@dracoonghost/trndup-sdk 1.3.16 → 1.3.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -594,6 +594,566 @@ declare namespace Insights {
594
594
  /** Max number of data points (default: 30, max: 90) */
595
595
  limit?: number;
596
596
  }
597
+ /**
598
+ * How we calculated this insight - shown to user for transparency
599
+ */
600
+ interface CalculationExplanation {
601
+ /** Short description of the methodology */
602
+ method: string;
603
+ /** Time range analyzed */
604
+ timeRange: string;
605
+ /** Key factors that influenced the result */
606
+ factors: string[];
607
+ /** Data quality/reliability indicator */
608
+ dataQuality: 'high' | 'medium' | 'low';
609
+ /** Why data quality might be limited */
610
+ dataQualityNote?: string;
611
+ }
612
+ /**
613
+ * Common metadata for all insights
614
+ */
615
+ interface InsightMeta {
616
+ /** When this insight was calculated */
617
+ calculatedAt: string;
618
+ /** When this insight expires (should be recalculated) */
619
+ validUntil: string;
620
+ /** Algorithm version for tracking changes */
621
+ algorithmVersion: string;
622
+ /** Whether result came from cache */
623
+ fromCache: boolean;
624
+ /** How this was calculated (for transparency) */
625
+ howWeCalculatedThis: CalculationExplanation;
626
+ }
627
+ /**
628
+ * Response when there's not enough data for an insight
629
+ */
630
+ interface InsufficientDataResponse {
631
+ hasData: false;
632
+ reason: string;
633
+ minimumRequired: string;
634
+ currentAmount: string;
635
+ suggestion: string;
636
+ }
637
+ interface TopVideoData {
638
+ videoId: string;
639
+ title: string;
640
+ thumbnailUrl: string | null;
641
+ publishedAt: string;
642
+ /** Link to watch on YouTube */
643
+ watchUrl: string;
644
+ }
645
+ interface TopVideoMetrics {
646
+ views: number;
647
+ likes: number;
648
+ comments: number;
649
+ estimatedMinutesWatched: number;
650
+ engagementRate: number;
651
+ }
652
+ interface TopVideoComparison {
653
+ /** Performance vs user's average video */
654
+ vsChannelAverage: {
655
+ views: number;
656
+ engagement: number;
657
+ description: string;
658
+ };
659
+ /** Why this video performed well */
660
+ standoutReasons: string[];
661
+ }
662
+ interface BestPerformingVideoData {
663
+ /** Time range analyzed */
664
+ period: {
665
+ start: string;
666
+ end: string;
667
+ days: number;
668
+ };
669
+ /** The top performing video */
670
+ topVideo: TopVideoData;
671
+ /** Video's metrics for this period */
672
+ metrics: TopVideoMetrics;
673
+ /** How it compares to your other content */
674
+ comparison: TopVideoComparison;
675
+ /** Human-readable summary */
676
+ insight: string;
677
+ /** Runner-up videos (for context) */
678
+ runnersUp: Array<{
679
+ videoId: string;
680
+ title: string;
681
+ views: number;
682
+ thumbnailUrl: string | null;
683
+ }>;
684
+ }
685
+ type BestPerformingVideoResponse = (InsufficientDataResponse) | {
686
+ hasData: true;
687
+ data: BestPerformingVideoData;
688
+ _meta: InsightMeta;
689
+ };
690
+ interface DecayingVideo {
691
+ videoId: string;
692
+ title: string;
693
+ thumbnailUrl: string | null;
694
+ publishedAt: string;
695
+ watchUrl: string;
696
+ /** Age of video in days */
697
+ ageInDays: number;
698
+ /** Views in current period */
699
+ currentViews: number;
700
+ /** Views in previous period */
701
+ previousViews: number;
702
+ /** Decay rate as percentage (negative = declining) */
703
+ decayRate: number;
704
+ /** Severity of decay */
705
+ severity: 'severe' | 'moderate' | 'mild';
706
+ /** Actionable suggestion */
707
+ suggestion: string;
708
+ }
709
+ interface ContentDecayData {
710
+ /** Time range analyzed */
711
+ period: {
712
+ current: {
713
+ start: string;
714
+ end: string;
715
+ };
716
+ previous: {
717
+ start: string;
718
+ end: string;
719
+ };
720
+ days: number;
721
+ };
722
+ /** Number of videos analyzed */
723
+ videosAnalyzed: number;
724
+ /** Videos experiencing significant decay */
725
+ decayingVideos: DecayingVideo[];
726
+ /** Summary stats */
727
+ summary: {
728
+ totalDecaying: number;
729
+ severeCount: number;
730
+ moderateCount: number;
731
+ mildCount: number;
732
+ /** Combined view loss from all decaying videos */
733
+ totalViewsLost: number;
734
+ };
735
+ /** Human-readable summary */
736
+ insight: string;
737
+ /** What content decay means */
738
+ whatThisMeans: string;
739
+ }
740
+ type ContentDecayResponse = (InsufficientDataResponse) | {
741
+ hasData: true;
742
+ data: ContentDecayData;
743
+ _meta: InsightMeta;
744
+ };
745
+ interface DayPerformance {
746
+ day: string;
747
+ avgFirstDayViews: number;
748
+ avgEngagement: number;
749
+ videoCount: number;
750
+ /** Confidence in this data */
751
+ confidence: 'high' | 'medium' | 'low';
752
+ }
753
+ interface TimeSlotPerformance {
754
+ slot: string;
755
+ label: string;
756
+ avgFirstDayViews: number;
757
+ videoCount: number;
758
+ confidence: 'high' | 'medium' | 'low';
759
+ }
760
+ interface AudienceActivityData {
761
+ /** How many videos were analyzed */
762
+ videosAnalyzed: number;
763
+ /** Day when your audience is most active */
764
+ mostActiveDay: {
765
+ day: string;
766
+ confidence: 'high' | 'medium' | 'low';
767
+ /** Why this day shows highest activity */
768
+ reason: string;
769
+ };
770
+ /** Time slot when your audience is most active */
771
+ mostActiveTimeSlot: {
772
+ slot: string;
773
+ label: string;
774
+ confidence: 'high' | 'medium' | 'low';
775
+ reason: string;
776
+ };
777
+ /** Full breakdown by day */
778
+ dayBreakdown: DayPerformance[];
779
+ /** Full breakdown by time slot (if enough data) */
780
+ timeBreakdown: TimeSlotPerformance[] | null;
781
+ /** Human-readable summary */
782
+ insight: string;
783
+ /** Disclaimer about the data */
784
+ disclaimer: string;
785
+ }
786
+ type AudienceActivityResponse = (InsufficientDataResponse) | {
787
+ hasData: true;
788
+ data: AudienceActivityData;
789
+ _meta: InsightMeta;
790
+ };
791
+ /** @deprecated Use AudienceActivityData instead */
792
+ type BestUploadTimeData = AudienceActivityData;
793
+ /** @deprecated Use AudienceActivityResponse instead */
794
+ type BestUploadTimeResponse = AudienceActivityResponse;
795
+ interface FadingHitVideo {
796
+ videoId: string;
797
+ title: string;
798
+ thumbnailUrl: string | null;
799
+ publishedAt: string;
800
+ watchUrl: string;
801
+ /** How old the video is in days */
802
+ ageInDays: number;
803
+ /** Performance during peak period (actual peak, not just first 14 days) */
804
+ peakPerformance: {
805
+ /** Average daily views during peak window */
806
+ dailyViews: number;
807
+ /** Comparison to channel average (e.g., "45% above average") */
808
+ vsChannelAverage: string;
809
+ /** Was this a top performer (2x+ channel average)? */
810
+ wasTopPerformer: boolean;
811
+ /** When the actual peak occurred */
812
+ peakPeriod: {
813
+ start: string;
814
+ end: string;
815
+ /** How many days after upload the peak occurred */
816
+ daysAfterUpload: number;
817
+ /** True if video went viral later (not at launch) - delayed virality */
818
+ wasDelayedViral: boolean;
819
+ };
820
+ };
821
+ /** Performance in recent period (last 14 days) */
822
+ currentPerformance: {
823
+ /** Current average daily views */
824
+ dailyViews: number;
825
+ /** Percentage decline from peak (negative number) */
826
+ declineFromPeak: number;
827
+ };
828
+ /** How likely revival efforts would work */
829
+ revivalPotential: 'high' | 'medium' | 'low';
830
+ /** Why this video might be worth reviving */
831
+ whyRevive: string;
832
+ /** Specific suggestions to revive this video */
833
+ suggestions: string[];
834
+ }
835
+ interface FadingHitsData {
836
+ /** Time period for "current" performance check */
837
+ period: {
838
+ current: {
839
+ start: string;
840
+ end: string;
841
+ };
842
+ label: string;
843
+ };
844
+ /** Number of videos analyzed */
845
+ videosAnalyzed: number;
846
+ /** Videos that were performing above average but are now declining */
847
+ fadingHits: FadingHitVideo[];
848
+ /** Summary stats */
849
+ summary: {
850
+ totalFading: number;
851
+ highPotentialCount: number;
852
+ mediumPotentialCount: number;
853
+ lowPotentialCount: number;
854
+ /** Combined daily views being lost */
855
+ totalDailyViewsLost: number;
856
+ };
857
+ /** Human-readable summary */
858
+ insight: string;
859
+ /** What fading hits means */
860
+ whatThisMeans: string;
861
+ }
862
+ type FadingHitsResponse = (InsufficientDataResponse) | {
863
+ hasData: true;
864
+ data: FadingHitsData;
865
+ _meta: InsightMeta;
866
+ };
867
+ type SubscriberQualityLabel = 'excellent' | 'good' | 'fair' | 'needs_attention' | 'concerning';
868
+ interface SubscriberQualityData {
869
+ /** Time range analyzed */
870
+ period: {
871
+ start: string;
872
+ end: string;
873
+ days: number;
874
+ };
875
+ /** Overall quality score (0-100) */
876
+ qualityScore: number;
877
+ /** Score interpretation */
878
+ scoreLabel: SubscriberQualityLabel;
879
+ /** Core metrics */
880
+ metrics: {
881
+ /** Total subscribers gained */
882
+ subscribersGained: number;
883
+ /** Total subscribers lost */
884
+ subscribersLost: number;
885
+ /** Net subscriber change */
886
+ netSubscribers: number;
887
+ /** Retention rate (how many stay) */
888
+ retentionRate: number;
889
+ /** Average watch time per view */
890
+ avgWatchTimeMinutes: number;
891
+ /** Views per subscriber (engagement proxy) */
892
+ viewsPerSubscriber: number;
893
+ };
894
+ /** Comparison to previous period */
895
+ trend: {
896
+ scoreChange: number;
897
+ direction: 'improving' | 'stable' | 'declining';
898
+ description: string;
899
+ };
900
+ /** Human-readable summary */
901
+ insight: string;
902
+ /** Specific recommendations */
903
+ recommendations: string[];
904
+ }
905
+ type SubscriberQualityResponse = (InsufficientDataResponse) | {
906
+ hasData: true;
907
+ data: SubscriberQualityData;
908
+ _meta: InsightMeta;
909
+ };
910
+ type FatigueLevel = 'none' | 'low' | 'moderate' | 'high' | 'critical';
911
+ interface FatigueSignal {
912
+ /** Name of the signal */
913
+ name: string;
914
+ /** Current period value */
915
+ current: number;
916
+ /** Previous period value */
917
+ previous: number;
918
+ /** Change percentage (negative = declining) */
919
+ changePercent: number;
920
+ /** Whether this signal indicates fatigue */
921
+ indicatesFatigue: boolean;
922
+ /** Description of what this means */
923
+ interpretation: string;
924
+ }
925
+ interface AudienceFatigueData {
926
+ /** Time periods compared */
927
+ period: {
928
+ current: {
929
+ start: string;
930
+ end: string;
931
+ };
932
+ previous: {
933
+ start: string;
934
+ end: string;
935
+ };
936
+ daysPerPeriod: number;
937
+ };
938
+ /** Overall fatigue index (0-100, higher = more fatigue) */
939
+ fatigueIndex: number;
940
+ /** Human-readable fatigue level */
941
+ fatigueLevel: FatigueLevel;
942
+ /** Individual signals that contribute to the score */
943
+ signals: {
944
+ watchTimePerView: FatigueSignal;
945
+ engagementRate: FatigueSignal;
946
+ subscriberChurn: FatigueSignal;
947
+ viewsPerVideo: FatigueSignal;
948
+ };
949
+ /** Summary of signals */
950
+ summary: {
951
+ signalsIndicatingFatigue: number;
952
+ totalSignals: number;
953
+ primaryConcern: string | null;
954
+ };
955
+ /** Human-readable insight */
956
+ insight: string;
957
+ /** What to do about it */
958
+ recommendations: string[];
959
+ }
960
+ type AudienceFatigueResponse = (InsufficientDataResponse) | {
961
+ hasData: true;
962
+ data: AudienceFatigueData;
963
+ _meta: InsightMeta;
964
+ };
965
+ interface LengthBucketPerformance {
966
+ /** Bucket name (e.g., "5-10 minutes") */
967
+ bucket: string;
968
+ /** Bucket range in seconds */
969
+ range: {
970
+ min: number;
971
+ max: number | null;
972
+ };
973
+ /** Number of videos in this bucket */
974
+ videoCount: number;
975
+ /** Average retention as percentage */
976
+ avgRetention: number;
977
+ /** Average engagement rate */
978
+ avgEngagement: number;
979
+ /** Average views per video */
980
+ avgViews: number;
981
+ /** Combined performance score (0-100) */
982
+ performanceScore: number;
983
+ /** Is this the optimal bucket? */
984
+ isOptimal: boolean;
985
+ }
986
+ interface OptimalVideoLengthData {
987
+ /** How many videos were analyzed */
988
+ videosAnalyzed: number;
989
+ /** The recommended optimal length range */
990
+ optimalLength: {
991
+ bucket: string;
992
+ range: {
993
+ min: number;
994
+ max: number | null;
995
+ };
996
+ confidence: 'high' | 'medium' | 'low';
997
+ reason: string;
998
+ };
999
+ /** Performance breakdown by length bucket */
1000
+ breakdown: LengthBucketPerformance[];
1001
+ /** Key findings */
1002
+ findings: {
1003
+ bestRetention: {
1004
+ bucket: string;
1005
+ value: number;
1006
+ };
1007
+ bestEngagement: {
1008
+ bucket: string;
1009
+ value: number;
1010
+ };
1011
+ mostCommon: {
1012
+ bucket: string;
1013
+ count: number;
1014
+ };
1015
+ };
1016
+ /** Human-readable insight */
1017
+ insight: string;
1018
+ /** Specific recommendations */
1019
+ recommendations: string[];
1020
+ }
1021
+ type OptimalVideoLengthResponse = (InsufficientDataResponse) | {
1022
+ hasData: true;
1023
+ data: OptimalVideoLengthData;
1024
+ _meta: InsightMeta;
1025
+ };
1026
+ interface AllInsightsData {
1027
+ bestPerformingVideo: BestPerformingVideoResponse;
1028
+ contentDecay: ContentDecayResponse;
1029
+ audienceActivity: AudienceActivityResponse;
1030
+ subscriberQuality: SubscriberQualityResponse;
1031
+ fadingHits: FadingHitsResponse;
1032
+ audienceFatigue: AudienceFatigueResponse;
1033
+ optimalLength: OptimalVideoLengthResponse;
1034
+ }
1035
+ interface AllInsightsResponse {
1036
+ data: AllInsightsData;
1037
+ _meta: {
1038
+ calculatedAt: string;
1039
+ insightsAvailable: string[];
1040
+ };
1041
+ }
1042
+ interface GetBestVideoParams {
1043
+ /** Number of days to analyze (1-90, default: 30) */
1044
+ period?: number;
1045
+ }
1046
+ interface GetContentDecayParams {
1047
+ /** Days per comparison period (3-14, default: 7) */
1048
+ period?: number;
1049
+ }
1050
+ interface GetSubscriberQualityParams {
1051
+ /** Number of days to analyze (14-180, default: 90) */
1052
+ period?: number;
1053
+ }
1054
+ interface GetAudienceFatigueParams {
1055
+ /** Days per comparison period (14-90, default: 30) */
1056
+ period?: number;
1057
+ }
1058
+ }
1059
+ declare namespace Activity {
1060
+ /**
1061
+ * Job types for sync operations
1062
+ */
1063
+ type JobType = 'videos_sync' | 'channel_analytics' | 'video_analytics';
1064
+ /**
1065
+ * Job status values
1066
+ */
1067
+ type JobStatus = 'started' | 'completed' | 'failed';
1068
+ /**
1069
+ * Job trigger source
1070
+ */
1071
+ type JobTrigger = 'scheduler' | 'api' | 'manual';
1072
+ /**
1073
+ * Job data details (varies by job type)
1074
+ */
1075
+ interface JobData {
1076
+ videosFound?: number;
1077
+ newVideos?: number;
1078
+ updatedVideos?: number;
1079
+ totalVideos?: number;
1080
+ daysProcessed?: number;
1081
+ metricsUpdated?: number;
1082
+ syncedDays?: number;
1083
+ dateRange?: {
1084
+ start: string;
1085
+ end: string;
1086
+ };
1087
+ videoId?: string;
1088
+ videoTitle?: string;
1089
+ recordsCreated?: number;
1090
+ recordsUpdated?: number;
1091
+ queued?: number;
1092
+ skipped?: number;
1093
+ message?: string;
1094
+ [key: string]: unknown;
1095
+ }
1096
+ /**
1097
+ * A single activity log entry
1098
+ */
1099
+ interface LogEntry {
1100
+ /** Unique log ID */
1101
+ id: string;
1102
+ /** BullMQ job ID */
1103
+ jobId: string;
1104
+ /** Type of sync job */
1105
+ type: JobType;
1106
+ /** Current status */
1107
+ status: JobStatus;
1108
+ /** When the job started */
1109
+ startedAt: string;
1110
+ /** When the job completed (if finished) */
1111
+ completedAt?: string;
1112
+ /** Duration in milliseconds */
1113
+ durationMs?: number;
1114
+ /** What triggered this job */
1115
+ triggeredBy: JobTrigger;
1116
+ /** Job result data */
1117
+ data?: JobData;
1118
+ /** Error message (if failed) */
1119
+ error?: string;
1120
+ }
1121
+ /**
1122
+ * Response for activity logs list
1123
+ */
1124
+ interface LogsResponse {
1125
+ /** List of log entries */
1126
+ logs: LogEntry[];
1127
+ /** Number of entries returned */
1128
+ count: number;
1129
+ }
1130
+ /**
1131
+ * Activity summary with counts
1132
+ */
1133
+ interface SummaryResponse {
1134
+ /** Total jobs ever run */
1135
+ totalJobs: number;
1136
+ /** Successfully completed jobs */
1137
+ completed: number;
1138
+ /** Failed jobs */
1139
+ failed: number;
1140
+ /** Breakdown by job type */
1141
+ byType: Record<JobType, {
1142
+ total: number;
1143
+ lastRun?: string;
1144
+ }>;
1145
+ }
1146
+ /**
1147
+ * Parameters for fetching activity logs
1148
+ */
1149
+ interface GetLogsParams {
1150
+ /** Filter by job type */
1151
+ type?: JobType;
1152
+ /** Filter by status */
1153
+ status?: JobStatus;
1154
+ /** Max entries to return (default: 50, max: 100) */
1155
+ limit?: number;
1156
+ }
597
1157
  }
598
1158
 
599
1159
  /**
@@ -993,6 +1553,301 @@ declare class InsightsModule {
993
1553
  * GET /v1/insights/youtube/momentum/history
994
1554
  */
995
1555
  getYouTubeMomentumHistory(params?: Insights.GetMomentumHistoryParams): Promise<Insights.MomentumHistoryResponse>;
1556
+ /**
1557
+ * Get the best performing video in a time period
1558
+ *
1559
+ * Identifies your top video by views and explains WHY it performed well.
1560
+ *
1561
+ * @param params - Query parameters
1562
+ * @param params.period - Days to analyze (1-90, default: 30)
1563
+ *
1564
+ * @example
1565
+ * ```typescript
1566
+ * const result = await client.insights.getYouTubeBestVideo({ period: 30 });
1567
+ *
1568
+ * if (result.hasData) {
1569
+ * console.log(`🏆 ${result.data.topVideo.title}`);
1570
+ * console.log(`Views: ${result.data.metrics.views}`);
1571
+ * console.log(`${result.data.comparison.vsChannelAverage.description}`);
1572
+ * console.log(result.data.insight);
1573
+ *
1574
+ * // Show how it was calculated
1575
+ * console.log(result._meta.howWeCalculatedThis.method);
1576
+ * } else {
1577
+ * console.log(result.suggestion);
1578
+ * }
1579
+ * ```
1580
+ *
1581
+ * GET /v1/insights/youtube/best-video
1582
+ */
1583
+ getYouTubeBestVideo(params?: Insights.GetBestVideoParams): Promise<Insights.BestPerformingVideoResponse>;
1584
+ /**
1585
+ * Find videos that are losing momentum
1586
+ *
1587
+ * Compares current period vs previous period to identify declining videos.
1588
+ * Provides actionable suggestions for each decaying video.
1589
+ *
1590
+ * @param params - Query parameters
1591
+ * @param params.period - Days per comparison period (3-14, default: 7)
1592
+ *
1593
+ * @example
1594
+ * ```typescript
1595
+ * const result = await client.insights.getYouTubeContentDecay({ period: 7 });
1596
+ *
1597
+ * if (result.hasData) {
1598
+ * console.log(result.data.insight);
1599
+ * console.log(`${result.data.summary.totalDecaying} videos declining`);
1600
+ *
1601
+ * for (const video of result.data.decayingVideos) {
1602
+ * console.log(`📉 ${video.title}: ${video.decayRate}%`);
1603
+ * console.log(` ${video.suggestion}`);
1604
+ * }
1605
+ * }
1606
+ * ```
1607
+ *
1608
+ * GET /v1/insights/youtube/content-decay
1609
+ */
1610
+ getYouTubeContentDecay(params?: Insights.GetContentDecayParams): Promise<Insights.ContentDecayResponse>;
1611
+ /**
1612
+ * Get when your audience is most active
1613
+ *
1614
+ * Unlike generic advice, this is based on YOUR audience's behavior.
1615
+ * Analyzes first-day performance by publish day and time to identify
1616
+ * when your audience is most engaged.
1617
+ *
1618
+ * @example
1619
+ * ```typescript
1620
+ * const result = await client.insights.getYouTubeAudienceActivity();
1621
+ *
1622
+ * if (result.hasData) {
1623
+ * console.log(`📅 Most active day: ${result.data.mostActiveDay.day}`);
1624
+ * console.log(`⏰ Peak time: ${result.data.mostActiveTimeSlot.label}`);
1625
+ * console.log(result.data.insight);
1626
+ *
1627
+ * // Show day breakdown
1628
+ * for (const day of result.data.dayBreakdown) {
1629
+ * console.log(`${day.day}: ${day.avgFirstDayViews} avg views`);
1630
+ * }
1631
+ * }
1632
+ * ```
1633
+ *
1634
+ * GET /v1/insights/youtube/audience-activity
1635
+ */
1636
+ getYouTubeAudienceActivity(): Promise<Insights.AudienceActivityResponse>;
1637
+ /**
1638
+ * @deprecated Use `getYouTubeAudienceActivity()` instead
1639
+ */
1640
+ getYouTubeBestUploadTime(): Promise<Insights.BestUploadTimeResponse>;
1641
+ /**
1642
+ * Find videos that were hits but are now declining
1643
+ *
1644
+ * Identifies videos that performed above your channel average initially
1645
+ * but have since lost momentum. These are revival opportunities.
1646
+ *
1647
+ * @example
1648
+ * ```typescript
1649
+ * const result = await client.insights.getYouTubeFadingHits();
1650
+ *
1651
+ * if (result.hasData) {
1652
+ * console.log(result.data.insight);
1653
+ * console.log(`${result.data.summary.totalFading} videos could use a boost`);
1654
+ *
1655
+ * for (const video of result.data.fadingHits) {
1656
+ * console.log(`📉 ${video.title}`);
1657
+ * console.log(` Peak: ${video.peakPeriod.avgDailyViews} views/day`);
1658
+ * console.log(` Now: ${video.currentPeriod.avgDailyViews} views/day`);
1659
+ * console.log(` Decline: ${video.decline.percentage}%`);
1660
+ * console.log(` Revival potential: ${video.revivalPotential}`);
1661
+ * console.log(` ${video.whyRevive}`);
1662
+ * for (const suggestion of video.suggestions) {
1663
+ * console.log(` 💡 ${suggestion}`);
1664
+ * }
1665
+ * }
1666
+ * }
1667
+ * ```
1668
+ *
1669
+ * GET /v1/insights/youtube/fading-hits
1670
+ */
1671
+ getYouTubeFadingHits(): Promise<Insights.FadingHitsResponse>;
1672
+ /**
1673
+ * Measure subscriber quality score (0-100)
1674
+ *
1675
+ * Analyzes whether new subscribers are engaged or just numbers.
1676
+ * Helps identify fake growth or low-quality audiences.
1677
+ *
1678
+ * @param params - Query parameters
1679
+ * @param params.period - Days to analyze (7-90, default: 30)
1680
+ *
1681
+ * @example
1682
+ * ```typescript
1683
+ * const result = await client.insights.getYouTubeSubscriberQuality({ period: 90 });
1684
+ *
1685
+ * if (result.hasData) {
1686
+ * console.log(`Quality Score: ${result.data.qualityScore}/100 (${result.data.scoreLabel})`);
1687
+ * console.log(`Retention: ${result.data.metrics.retentionRate}%`);
1688
+ * console.log(result.data.insight);
1689
+ *
1690
+ * // Show recommendations
1691
+ * for (const rec of result.data.recommendations) {
1692
+ * console.log(`💡 ${rec}`);
1693
+ * }
1694
+ * }
1695
+ * ```
1696
+ *
1697
+ * GET /v1/insights/youtube/subscriber-quality
1698
+ */
1699
+ getYouTubeSubscriberQuality(params?: Insights.GetSubscriberQualityParams): Promise<Insights.SubscriberQualityResponse>;
1700
+ /**
1701
+ * Detect if your audience is getting tired of your content
1702
+ *
1703
+ * Compares key engagement metrics between recent and previous periods
1704
+ * to identify declining audience interest before it becomes critical.
1705
+ *
1706
+ * @param params - Query parameters
1707
+ * @param params.period - Days per comparison period (14-90, default: 30)
1708
+ *
1709
+ * @example
1710
+ * ```typescript
1711
+ * const result = await client.insights.getYouTubeAudienceFatigue({ period: 30 });
1712
+ *
1713
+ * if (result.hasData) {
1714
+ * console.log(`Fatigue Index: ${result.data.fatigueIndex}/100`);
1715
+ * console.log(`Level: ${result.data.fatigueLevel}`);
1716
+ * console.log(result.data.insight);
1717
+ *
1718
+ * // Check individual signals
1719
+ * if (result.data.signals.watchTimePerView.indicatesFatigue) {
1720
+ * console.log('⚠️ Watch time is declining');
1721
+ * }
1722
+ *
1723
+ * // Get recommendations
1724
+ * for (const rec of result.data.recommendations) {
1725
+ * console.log(`💡 ${rec}`);
1726
+ * }
1727
+ * }
1728
+ * ```
1729
+ *
1730
+ * GET /v1/insights/youtube/audience-fatigue
1731
+ */
1732
+ getYouTubeAudienceFatigue(params?: Insights.GetAudienceFatigueParams): Promise<Insights.AudienceFatigueResponse>;
1733
+ /**
1734
+ * Find the ideal video length for YOUR audience
1735
+ *
1736
+ * Unlike generic "8-12 minutes is best" advice, this is personalized
1737
+ * based on how YOUR videos perform at different lengths.
1738
+ *
1739
+ * @example
1740
+ * ```typescript
1741
+ * const result = await client.insights.getYouTubeOptimalLength();
1742
+ *
1743
+ * if (result.hasData) {
1744
+ * console.log(`Optimal: ${result.data.optimalLength.bucket}`);
1745
+ * console.log(`Confidence: ${result.data.optimalLength.confidence}`);
1746
+ * console.log(result.data.insight);
1747
+ *
1748
+ * // Show breakdown by length
1749
+ * for (const bucket of result.data.breakdown) {
1750
+ * const marker = bucket.isOptimal ? '🏆' : ' ';
1751
+ * console.log(`${marker} ${bucket.bucket}: ${bucket.avgRetention}% retention, ${bucket.avgEngagement}% engagement`);
1752
+ * }
1753
+ *
1754
+ * // Recommendations
1755
+ * for (const rec of result.data.recommendations) {
1756
+ * console.log(`💡 ${rec}`);
1757
+ * }
1758
+ * }
1759
+ * ```
1760
+ *
1761
+ * GET /v1/insights/youtube/optimal-length
1762
+ */
1763
+ getYouTubeOptimalLength(): Promise<Insights.OptimalVideoLengthResponse>;
1764
+ /**
1765
+ * Get all YouTube insights in one call
1766
+ *
1767
+ * Efficient for dashboard display - fetches all insights in parallel.
1768
+ * Each insight has `hasData: boolean` to check availability.
1769
+ *
1770
+ * @example
1771
+ * ```typescript
1772
+ * const all = await client.insights.getYouTubeAllInsights();
1773
+ *
1774
+ * // Check which insights are available
1775
+ * console.log('Available:', all._meta.insightsAvailable);
1776
+ *
1777
+ * // Use each insight
1778
+ * if (all.data.bestPerformingVideo.hasData) {
1779
+ * console.log(`Best: ${all.data.bestPerformingVideo.data.topVideo.title}`);
1780
+ * }
1781
+ *
1782
+ * if (all.data.audienceFatigue.hasData) {
1783
+ * console.log(`Fatigue: ${all.data.audienceFatigue.data.fatigueLevel}`);
1784
+ * }
1785
+ *
1786
+ * if (all.data.optimalLength.hasData) {
1787
+ * console.log(`Optimal Length: ${all.data.optimalLength.data.optimalLength.bucket}`);
1788
+ * }
1789
+ * ```
1790
+ *
1791
+ * GET /v1/insights/youtube/all
1792
+ */
1793
+ getYouTubeAllInsights(): Promise<Insights.AllInsightsResponse>;
1794
+ }
1795
+
1796
+ /**
1797
+ * Activity Module
1798
+ *
1799
+ * View sync job activity and logs
1800
+ */
1801
+
1802
+ declare class ActivityModule {
1803
+ private client;
1804
+ constructor(client: TrndUpClient);
1805
+ /**
1806
+ * Get recent activity logs
1807
+ *
1808
+ * @param params - Optional filters
1809
+ * @returns List of activity log entries
1810
+ *
1811
+ * @example
1812
+ * ```typescript
1813
+ * // Get all recent logs
1814
+ * const { logs } = await client.activity.getLogs();
1815
+ *
1816
+ * // Get only failed jobs
1817
+ * const { logs } = await client.activity.getLogs({ status: 'failed' });
1818
+ *
1819
+ * // Get video sync jobs only
1820
+ * const { logs } = await client.activity.getLogs({ type: 'videos_sync' });
1821
+ * ```
1822
+ */
1823
+ getLogs(params?: Activity.GetLogsParams): Promise<Activity.LogsResponse>;
1824
+ /**
1825
+ * Get activity summary with counts by type
1826
+ *
1827
+ * @returns Summary with total/completed/failed counts
1828
+ *
1829
+ * @example
1830
+ * ```typescript
1831
+ * const summary = await client.activity.getSummary();
1832
+ * console.log(`Total: ${summary.totalJobs}, Failed: ${summary.failed}`);
1833
+ * ```
1834
+ */
1835
+ getSummary(): Promise<Activity.SummaryResponse>;
1836
+ /**
1837
+ * Get the latest log entry for a specific job type
1838
+ *
1839
+ * @param jobType - The type of job to get
1840
+ * @returns Latest log entry or null if none exists
1841
+ *
1842
+ * @example
1843
+ * ```typescript
1844
+ * const latestVideoSync = await client.activity.getLatest('videos_sync');
1845
+ * if (latestVideoSync) {
1846
+ * console.log(`Last sync: ${latestVideoSync.completedAt}`);
1847
+ * }
1848
+ * ```
1849
+ */
1850
+ getLatest(jobType: Activity.JobType): Promise<Activity.LogEntry | null>;
996
1851
  }
997
1852
 
998
1853
  /**
@@ -1039,9 +1894,10 @@ declare class TrndUpSDK extends TrndUpClient {
1039
1894
  instagram: InstagramModule;
1040
1895
  social: SocialModule;
1041
1896
  insights: InsightsModule;
1897
+ activity: ActivityModule;
1042
1898
  constructor(config: TrndUpClientConfig);
1043
1899
  }
1044
1900
 
1045
1901
  declare const SDK_VERSION = "1.0.0";
1046
1902
 
1047
- export { Auth, INSTAGRAM_SCOPES, Insights, Instagram, type RequestOptions, SDK_VERSION, Social, TrndUpApiError, type TrndUpClientConfig, TrndUpNetworkError, TrndUpSDK, YOUTUBE_SCOPES, YouTube };
1903
+ export { Activity, Auth, INSTAGRAM_SCOPES, Insights, Instagram, type RequestOptions, SDK_VERSION, Social, TrndUpApiError, type TrndUpClientConfig, TrndUpNetworkError, TrndUpSDK, YOUTUBE_SCOPES, YouTube };