my-youtube-api 1.0.0 → 1.0.2

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.
Files changed (25) hide show
  1. package/dist/errors/youtube-api-errors.d.ts +30 -0
  2. package/dist/errors/youtube-api-errors.js +59 -0
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.js +1 -0
  5. package/dist/youtube-handlers/youtube-video-poster.d.ts +39 -3
  6. package/dist/youtube-handlers/youtube-video-poster.js +291 -10
  7. package/dist/youtube-handlers copy/youtube-account-handler.d.ts +21 -0
  8. package/dist/youtube-handlers copy/youtube-account-handler.js +33 -0
  9. package/dist/youtube-handlers copy/youtube-channel-handler.d.ts +65 -0
  10. package/dist/youtube-handlers copy/youtube-channel-handler.js +193 -0
  11. package/dist/youtube-handlers copy/youtube-channel-handler2.d.ts +65 -0
  12. package/dist/youtube-handlers copy/youtube-channel-handler2.js +177 -0
  13. package/dist/youtube-handlers copy/youtube-video-handler.d.ts +30 -0
  14. package/dist/youtube-handlers copy/youtube-video-handler.js +55 -0
  15. package/dist/youtube-handlers copy/youtube-video-poster.d.ts +23 -0
  16. package/dist/youtube-handlers copy/youtube-video-poster.js +47 -0
  17. package/package.json +1 -1
  18. package/src/errors/youtube-api-errors.ts +72 -0
  19. package/src/index.ts +1 -1
  20. package/src/youtube-handlers/youtube-video-poster.ts +361 -12
  21. package/src/youtube-handlers copy/youtube-account-handler.ts +51 -0
  22. package/src/youtube-handlers copy/youtube-channel-handler.ts +275 -0
  23. package/src/youtube-handlers copy/youtube-channel-handler2.ts +249 -0
  24. package/src/youtube-handlers copy/youtube-video-handler.ts +87 -0
  25. package/src/youtube-handlers copy/youtube-video-poster.ts +65 -0
@@ -0,0 +1,30 @@
1
+ export declare class YouTubeApiError extends Error {
2
+ readonly code: string;
3
+ readonly status?: number;
4
+ readonly details?: any;
5
+ constructor(message: string, code?: string, status?: number, details?: any);
6
+ }
7
+ export declare class YouTubeAuthenticationError extends YouTubeApiError {
8
+ constructor(message: string, details?: any);
9
+ }
10
+ export declare class YouTubeQuotaError extends YouTubeApiError {
11
+ constructor(message: string, details?: any);
12
+ }
13
+ export declare class YouTubeRateLimitError extends YouTubeApiError {
14
+ constructor(message: string, details?: any);
15
+ }
16
+ export declare class YouTubeMediaError extends YouTubeApiError {
17
+ constructor(message: string, details?: any);
18
+ }
19
+ export declare class YouTubeUploadError extends YouTubeApiError {
20
+ constructor(message: string, details?: any);
21
+ }
22
+ export declare class YouTubeValidationError extends YouTubeApiError {
23
+ constructor(message: string, details?: any);
24
+ }
25
+ export declare class YouTubeProcessingError extends YouTubeApiError {
26
+ constructor(message: string, details?: any);
27
+ }
28
+ export declare class YouTubeTimeoutError extends YouTubeApiError {
29
+ constructor(message: string, details?: any);
30
+ }
@@ -0,0 +1,59 @@
1
+ // src/errors/youtube-api-errors.ts
2
+ export class YouTubeApiError extends Error {
3
+ constructor(message, code = 'YOUTUBE_API_ERROR', status, details) {
4
+ super(message);
5
+ this.name = 'YouTubeApiError';
6
+ this.code = code;
7
+ this.status = status;
8
+ this.details = details;
9
+ Object.setPrototypeOf(this, YouTubeApiError.prototype);
10
+ }
11
+ }
12
+ export class YouTubeAuthenticationError extends YouTubeApiError {
13
+ constructor(message, details) {
14
+ super(message, 'AUTHENTICATION_FAILED', 401, details);
15
+ this.name = 'YouTubeAuthenticationError';
16
+ }
17
+ }
18
+ export class YouTubeQuotaError extends YouTubeApiError {
19
+ constructor(message, details) {
20
+ super(message, 'QUOTA_EXCEEDED', 429, details);
21
+ this.name = 'YouTubeQuotaError';
22
+ }
23
+ }
24
+ export class YouTubeRateLimitError extends YouTubeApiError {
25
+ constructor(message, details) {
26
+ super(message, 'RATE_LIMIT_EXCEEDED', 429, details);
27
+ this.name = 'YouTubeRateLimitError';
28
+ }
29
+ }
30
+ export class YouTubeMediaError extends YouTubeApiError {
31
+ constructor(message, details) {
32
+ super(message, 'MEDIA_ERROR', 400, details);
33
+ this.name = 'YouTubeMediaError';
34
+ }
35
+ }
36
+ export class YouTubeUploadError extends YouTubeApiError {
37
+ constructor(message, details) {
38
+ super(message, 'UPLOAD_FAILED', 500, details);
39
+ this.name = 'YouTubeUploadError';
40
+ }
41
+ }
42
+ export class YouTubeValidationError extends YouTubeApiError {
43
+ constructor(message, details) {
44
+ super(message, 'VALIDATION_ERROR', 400, details);
45
+ this.name = 'YouTubeValidationError';
46
+ }
47
+ }
48
+ export class YouTubeProcessingError extends YouTubeApiError {
49
+ constructor(message, details) {
50
+ super(message, 'PROCESSING_ERROR', 500, details);
51
+ this.name = 'YouTubeProcessingError';
52
+ }
53
+ }
54
+ export class YouTubeTimeoutError extends YouTubeApiError {
55
+ constructor(message, details) {
56
+ super(message, 'PROCESSING_TIMEOUT', 408, details);
57
+ this.name = 'YouTubeTimeoutError';
58
+ }
59
+ }
package/dist/index.d.ts CHANGED
@@ -2,5 +2,6 @@ import { YoutubeAccountHandler } from "./youtube-handlers/youtube-account-handle
2
2
  import { YoutubeChannelHandler } from "./youtube-handlers/youtube-channel-handler";
3
3
  import { YoutubeVideoHandler } from "./youtube-handlers/youtube-video-handler";
4
4
  import { YoutubeVideoPoster } from "./youtube-handlers/youtube-video-poster";
5
+ export * from "./errors/youtube-api-errors";
5
6
  import { isValidAccessToken, fetchNewAccessToken } from './utils';
6
7
  export { YoutubeAccountHandler, YoutubeChannelHandler, YoutubeVideoHandler, YoutubeVideoPoster, isValidAccessToken, fetchNewAccessToken };
package/dist/index.js CHANGED
@@ -8,5 +8,6 @@ npm pack
8
8
 
9
9
  in nextjs go to this package/"my-youtube-api-1.0.0.tgz"
10
10
  */
11
+ export * from "./errors/youtube-api-errors";
11
12
  import { isValidAccessToken, fetchNewAccessToken } from './utils';
12
13
  export { YoutubeAccountHandler, YoutubeChannelHandler, YoutubeVideoHandler, YoutubeVideoPoster, isValidAccessToken, fetchNewAccessToken };
@@ -9,15 +9,51 @@ export interface VideoMetadata {
9
9
  license?: "youtube" | "creativeCommon";
10
10
  embeddable?: boolean;
11
11
  publicStatsViewable?: boolean;
12
+ playlistId?: string;
13
+ notifySubscribers?: boolean;
14
+ }
15
+ export interface YouTubeUploadResult {
16
+ id: string;
17
+ status: string;
18
+ url: string;
19
+ title: string;
20
+ description?: string;
21
+ thumbnailUrl?: string;
12
22
  }
13
23
  export declare class YoutubeVideoPoster {
14
24
  private access_token;
15
25
  private youtube;
16
26
  constructor(access_token: string);
27
+ /**
28
+ * Handle YouTube API errors and throw appropriate custom errors
29
+ */
30
+ private handleYouTubeError;
31
+ /**
32
+ * Validate video metadata
33
+ */
34
+ private validateMetadata;
35
+ /**
36
+ * Fetch video stream from URL with proper error handling
37
+ */
38
+ private fetchVideoStream;
39
+ /**
40
+ * Wait for video processing to complete
41
+ */
42
+ private waitForVideoProcessing;
43
+ /**
44
+ * Add video to playlist if specified
45
+ */
46
+ private addToPlaylist;
17
47
  /**
18
48
  * Upload a video from a Cloudinary URL to YouTube
19
- * @param videoUrl URL of the video on Cloudinary
20
- * @param metadata Video metadata
21
49
  */
22
- uploadFromCloudUrl(videoUrl: string, metadata: VideoMetadata): Promise<youtube_v3.Schema$Video>;
50
+ uploadFromCloudUrl(videoUrl: string, metadata: VideoMetadata): Promise<YouTubeUploadResult>;
51
+ /**
52
+ * Get video details
53
+ */
54
+ getVideoDetails(videoId: string): Promise<youtube_v3.Schema$Video>;
55
+ /**
56
+ * Delete a video
57
+ */
58
+ deleteVideo(videoId: string): Promise<void>;
23
59
  }
@@ -1,28 +1,248 @@
1
+ // src/youtube-handlers/youtube-video-poster.ts
1
2
  import { google } from "googleapis";
2
3
  import axios from "axios";
4
+ import { Readable } from "stream";
5
+ import { YouTubeApiError, YouTubeAuthenticationError, YouTubeQuotaError, YouTubeRateLimitError, YouTubeMediaError, YouTubeUploadError, YouTubeValidationError, YouTubeProcessingError, YouTubeTimeoutError } from "../errors/youtube-api-errors";
3
6
  export class YoutubeVideoPoster {
4
7
  constructor(access_token) {
5
8
  this.access_token = access_token;
9
+ if (!access_token) {
10
+ throw new YouTubeAuthenticationError('Access token is required');
11
+ }
6
12
  this.youtube = google.youtube({ version: "v3", auth: this.access_token });
7
13
  }
14
+ /**
15
+ * Handle YouTube API errors and throw appropriate custom errors
16
+ */
17
+ handleYouTubeError(error, context) {
18
+ // Handle Google API errors
19
+ if (error.errors && Array.isArray(error.errors)) {
20
+ const apiError = error.errors[0];
21
+ const message = `${context}: ${apiError.message}`;
22
+ const reason = apiError.reason;
23
+ const domain = apiError.domain;
24
+ switch (reason) {
25
+ case 'authError':
26
+ case 'invalidCredentials':
27
+ case 'required':
28
+ throw new YouTubeAuthenticationError(message, apiError);
29
+ case 'quotaExceeded':
30
+ case 'dailyLimitExceeded':
31
+ case 'userRateLimitExceeded':
32
+ throw new YouTubeQuotaError(message, apiError);
33
+ case 'rateLimitExceeded':
34
+ case 'userRateLimitExceeded':
35
+ throw new YouTubeRateLimitError(message, apiError);
36
+ case 'invalidValue':
37
+ case 'invalid':
38
+ throw new YouTubeValidationError(message, apiError);
39
+ case 'processingFailed':
40
+ case 'failed':
41
+ throw new YouTubeProcessingError(message, apiError);
42
+ default:
43
+ throw new YouTubeApiError(message, `YOUTUBE_${reason?.toUpperCase()}`, error.code, apiError);
44
+ }
45
+ }
46
+ // Handle axios/network errors
47
+ if (error.response?.data?.error) {
48
+ const youtubeError = error.response.data.error;
49
+ const message = `${context}: ${youtubeError.message}`;
50
+ const code = youtubeError.code;
51
+ switch (code) {
52
+ case 401:
53
+ case 403:
54
+ throw new YouTubeAuthenticationError(message, youtubeError);
55
+ case 429:
56
+ throw new YouTubeRateLimitError(message, youtubeError);
57
+ case 400:
58
+ throw new YouTubeValidationError(message, youtubeError);
59
+ case 500:
60
+ case 503:
61
+ throw new YouTubeProcessingError(message, youtubeError);
62
+ default:
63
+ throw new YouTubeApiError(message, `YOUTUBE_${code}`, code, youtubeError);
64
+ }
65
+ }
66
+ // Handle timeout errors
67
+ if (error.code === 'ECONNABORTED' || error.message?.includes('timeout')) {
68
+ throw new YouTubeTimeoutError(`${context}: Request timed out`, error);
69
+ }
70
+ // Handle network errors
71
+ if (error.code === 'ENOTFOUND' || error.code === 'ECONNREFUSED') {
72
+ throw new YouTubeApiError(`${context}: Network error - ${error.message}`, 'NETWORK_ERROR', 503);
73
+ }
74
+ // Handle media/upload specific errors
75
+ if (error.message?.includes('stream') || error.message?.includes('video') || error.message?.includes('upload')) {
76
+ throw new YouTubeUploadError(`${context}: ${error.message}`, error);
77
+ }
78
+ // Default to generic API error
79
+ if (error instanceof YouTubeApiError) {
80
+ throw error;
81
+ }
82
+ throw new YouTubeApiError(`${context}: ${error.message}`, 'UNKNOWN_ERROR', error.response?.status);
83
+ }
84
+ /**
85
+ * Validate video metadata
86
+ */
87
+ validateMetadata(metadata) {
88
+ if (!metadata.title || metadata.title.trim().length === 0) {
89
+ throw new YouTubeValidationError('Video title is required');
90
+ }
91
+ if (metadata.title.length > 100) {
92
+ throw new YouTubeValidationError('Video title must be 100 characters or less');
93
+ }
94
+ if (metadata.description && metadata.description.length > 5000) {
95
+ throw new YouTubeValidationError('Video description must be 5000 characters or less');
96
+ }
97
+ if (metadata.tags && metadata.tags.length > 500) {
98
+ throw new YouTubeValidationError('Maximum 500 tags allowed');
99
+ }
100
+ if (metadata.tags) {
101
+ for (const tag of metadata.tags) {
102
+ if (tag.length > 30) {
103
+ throw new YouTubeValidationError(`Tag "${tag}" must be 30 characters or less`);
104
+ }
105
+ }
106
+ }
107
+ const validPrivacyStatuses = ['public', 'private', 'unlisted'];
108
+ if (metadata.privacyStatus && !validPrivacyStatuses.includes(metadata.privacyStatus)) {
109
+ throw new YouTubeValidationError(`Privacy status must be one of: ${validPrivacyStatuses.join(', ')}`);
110
+ }
111
+ const validLicenses = ['youtube', 'creativeCommon'];
112
+ if (metadata.license && !validLicenses.includes(metadata.license)) {
113
+ throw new YouTubeValidationError(`License must be one of: ${validLicenses.join(', ')}`);
114
+ }
115
+ }
116
+ /**
117
+ * Fetch video stream from URL with proper error handling
118
+ */
119
+ async fetchVideoStream(videoUrl) {
120
+ try {
121
+ console.log(`📹 Fetching video from: ${videoUrl}`);
122
+ const response = await axios.get(videoUrl, {
123
+ responseType: "stream",
124
+ timeout: 30000, // 30 second timeout
125
+ maxContentLength: 1024 * 1024 * 1024, // 1GB max file size
126
+ validateStatus: (status) => status === 200
127
+ });
128
+ if (!response.data || !(response.data instanceof Readable)) {
129
+ throw new YouTubeMediaError('Invalid video stream received from URL');
130
+ }
131
+ return response.data;
132
+ }
133
+ catch (error) {
134
+ if (error instanceof YouTubeApiError) {
135
+ throw error;
136
+ }
137
+ if (error.response?.status === 404) {
138
+ throw new YouTubeMediaError(`Video not found at URL: ${videoUrl}`);
139
+ }
140
+ else if (error.response?.status === 403) {
141
+ throw new YouTubeMediaError(`Access denied to video URL: ${videoUrl}`);
142
+ }
143
+ else if (error.code === 'ECONNABORTED') {
144
+ throw new YouTubeTimeoutError(`Timeout while fetching video from URL: ${videoUrl}`);
145
+ }
146
+ else {
147
+ throw new YouTubeMediaError(`Failed to fetch video from URL: ${error.message}`);
148
+ }
149
+ }
150
+ }
151
+ /**
152
+ * Wait for video processing to complete
153
+ */
154
+ async waitForVideoProcessing(videoId, timeoutMs = 300000 // 5 minutes
155
+ ) {
156
+ const startTime = Date.now();
157
+ const maxAttempts = 30;
158
+ let attempts = 0;
159
+ console.log(`⏳ Waiting for video processing: ${videoId}`);
160
+ while (attempts < maxAttempts && Date.now() - startTime < timeoutMs) {
161
+ attempts++;
162
+ try {
163
+ const statusResponse = await this.youtube.videos.list({
164
+ part: ['status'],
165
+ id: [videoId]
166
+ });
167
+ const video = statusResponse.data.items?.[0];
168
+ if (!video) {
169
+ throw new YouTubeProcessingError(`Video ${videoId} not found after upload`);
170
+ }
171
+ const status = video.status?.uploadStatus;
172
+ switch (status) {
173
+ case 'processed':
174
+ console.log(`✅ Video processing completed: ${videoId}`);
175
+ return;
176
+ case 'failed':
177
+ const failureReason = video.status?.failureReason || 'Unknown failure reason';
178
+ throw new YouTubeProcessingError(`Video processing failed: ${failureReason}`);
179
+ case 'rejected':
180
+ const rejectionReason = video.status?.rejectionReason || 'Unknown rejection reason';
181
+ throw new YouTubeProcessingError(`Video was rejected: ${rejectionReason}`);
182
+ case 'uploaded':
183
+ case 'processing':
184
+ // Wait and check again
185
+ const waitTime = Math.min(1000 * Math.pow(2, attempts), 10000); // Exponential backoff, max 10s
186
+ console.log(`⏳ Video status: ${status}, waiting ${waitTime}ms...`);
187
+ await new Promise(resolve => setTimeout(resolve, waitTime));
188
+ break;
189
+ default:
190
+ await new Promise(resolve => setTimeout(resolve, 5000));
191
+ break;
192
+ }
193
+ }
194
+ catch (error) {
195
+ if (error instanceof YouTubeApiError) {
196
+ throw error;
197
+ }
198
+ this.handleYouTubeError(error, "Failed to check video processing status");
199
+ }
200
+ }
201
+ throw new YouTubeTimeoutError(`Video processing timed out after ${timeoutMs}ms`);
202
+ }
203
+ /**
204
+ * Add video to playlist if specified
205
+ */
206
+ async addToPlaylist(videoId, playlistId) {
207
+ try {
208
+ await this.youtube.playlistItems.insert({
209
+ part: ['snippet'],
210
+ requestBody: {
211
+ snippet: {
212
+ playlistId: playlistId,
213
+ resourceId: {
214
+ kind: 'youtube#video',
215
+ videoId: videoId
216
+ }
217
+ }
218
+ }
219
+ });
220
+ console.log(`✅ Video ${videoId} added to playlist ${playlistId}`);
221
+ }
222
+ catch (error) {
223
+ console.warn(`⚠️ Failed to add video to playlist: ${error.message}`);
224
+ // Don't throw error for playlist addition failure as video upload was successful
225
+ }
226
+ }
8
227
  /**
9
228
  * Upload a video from a Cloudinary URL to YouTube
10
- * @param videoUrl URL of the video on Cloudinary
11
- * @param metadata Video metadata
12
229
  */
13
230
  async uploadFromCloudUrl(videoUrl, metadata) {
231
+ let videoStream = null;
14
232
  try {
15
- // Fetch video as stream from Cloudinary
16
- const response = await axios.get(videoUrl, { responseType: "stream" });
17
- const videoStream = response.data;
233
+ // Validate metadata first
234
+ this.validateMetadata(metadata);
235
+ // Fetch video stream
236
+ videoStream = await this.fetchVideoStream(videoUrl);
18
237
  // Prepare metadata payload
19
238
  const metadataPayload = {
20
239
  part: ["snippet", "status"],
240
+ notifySubscribers: metadata.notifySubscribers !== false,
21
241
  requestBody: {
22
242
  snippet: {
23
243
  title: metadata.title.substring(0, 100),
24
244
  description: (metadata.description || "").substring(0, 5000),
25
- tags: (metadata.tags || []).slice(0, 5),
245
+ tags: (metadata.tags || []).slice(0, 500),
26
246
  categoryId: metadata.categoryId || "22"
27
247
  },
28
248
  status: {
@@ -35,13 +255,74 @@ export class YoutubeVideoPoster {
35
255
  },
36
256
  media: { body: videoStream }
37
257
  };
258
+ console.log(`🚀 Uploading video to YouTube: "${metadata.title}"`);
38
259
  // Upload video
39
- const res = await this.youtube.videos.insert(metadataPayload);
40
- return res.data;
260
+ const uploadResponse = await this.youtube.videos.insert(metadataPayload)
261
+ .catch(error => this.handleYouTubeError(error, "Failed to upload video to YouTube"));
262
+ const videoId = uploadResponse.data.id;
263
+ if (!videoId) {
264
+ throw new YouTubeUploadError('Video ID not returned from YouTube API');
265
+ }
266
+ console.log(`✅ Video uploaded successfully: ${videoId}`);
267
+ // Wait for processing to complete
268
+ await this.waitForVideoProcessing(videoId);
269
+ // Add to playlist if specified
270
+ if (metadata.playlistId) {
271
+ await this.addToPlaylist(videoId, metadata.playlistId);
272
+ }
273
+ const result = {
274
+ id: videoId,
275
+ status: 'processed',
276
+ url: `https://www.youtube.com/watch?v=${videoId}`,
277
+ title: metadata.title,
278
+ description: metadata.description,
279
+ // You can add thumbnail URL here if needed
280
+ };
281
+ console.log(`🎉 YouTube video published successfully: ${result.url}`);
282
+ return result;
283
+ }
284
+ catch (error) {
285
+ // Clean up stream if it exists
286
+ if (videoStream) {
287
+ videoStream.destroy();
288
+ }
289
+ if (error instanceof YouTubeApiError) {
290
+ throw error;
291
+ }
292
+ this.handleYouTubeError(error, "Unexpected error during YouTube video upload");
293
+ }
294
+ }
295
+ /**
296
+ * Get video details
297
+ */
298
+ async getVideoDetails(videoId) {
299
+ try {
300
+ const response = await this.youtube.videos.list({
301
+ part: ['snippet', 'status', 'contentDetails', 'statistics'],
302
+ id: [videoId]
303
+ });
304
+ const video = response.data.items?.[0];
305
+ if (!video) {
306
+ throw new YouTubeValidationError(`Video not found: ${videoId}`);
307
+ }
308
+ return video;
309
+ }
310
+ catch (error) {
311
+ this.handleYouTubeError(error, "Failed to get video details");
312
+ }
313
+ }
314
+ /**
315
+ * Delete a video
316
+ */
317
+ async deleteVideo(videoId) {
318
+ try {
319
+ await this.youtube.videos.delete({
320
+ id: videoId
321
+ });
322
+ console.log(`✅ Video deleted successfully: ${videoId}`);
41
323
  }
42
324
  catch (error) {
43
- console.error("YouTube upload failed:", error.response?.data || error.message);
44
- throw new Error(`YouTube upload failed: ${error.response?.data?.error?.message || error.message}`);
325
+ this.handleYouTubeError(error, "Failed to delete video");
45
326
  }
46
327
  }
47
328
  }
@@ -0,0 +1,21 @@
1
+ interface Channel {
2
+ channelId: string;
3
+ title: string;
4
+ description: string;
5
+ thumbnail: string;
6
+ }
7
+ export declare class YoutubeAccountHandler {
8
+ private account_access_token;
9
+ /**
10
+ * Creates a new YoutubeAccountHandler instance
11
+ * @param account_access_token - The current access token for YouTube API authentication
12
+ */
13
+ constructor(account_access_token: string);
14
+ /**
15
+ * Fetches the list of YouTube channels associated with the authenticated account
16
+ * @returns Promise resolving to an array of Channel objects containing channel details
17
+ * @throws {AxiosError} If the API request fails due to authentication or other errors
18
+ */
19
+ fetchChannels(): Promise<Channel[]>;
20
+ }
21
+ export {};
@@ -0,0 +1,33 @@
1
+ import axios from "axios";
2
+ import { YOUTUBE_BASE_URL } from "../error-utils";
3
+ export class YoutubeAccountHandler {
4
+ /**
5
+ * Creates a new YoutubeAccountHandler instance
6
+ * @param account_access_token - The current access token for YouTube API authentication
7
+ */
8
+ constructor(account_access_token) {
9
+ this.account_access_token = account_access_token;
10
+ }
11
+ /**
12
+ * Fetches the list of YouTube channels associated with the authenticated account
13
+ * @returns Promise resolving to an array of Channel objects containing channel details
14
+ * @throws {AxiosError} If the API request fails due to authentication or other errors
15
+ */
16
+ async fetchChannels() {
17
+ const url = `${YOUTUBE_BASE_URL}/youtube/v3/channels`;
18
+ const params = {
19
+ part: "snippet,contentDetails,statistics",
20
+ mine: true,
21
+ };
22
+ const access_token = this.account_access_token;
23
+ const headers = { Authorization: `Bearer ${access_token}` };
24
+ const res = await axios.get(url, { params, headers });
25
+ const channels = res.data.items.map((ch) => ({
26
+ channelId: ch.id,
27
+ title: ch.snippet.title,
28
+ description: ch.snippet.description,
29
+ thumbnail: ch.snippet.thumbnails?.default?.url,
30
+ }));
31
+ return channels;
32
+ }
33
+ }
@@ -0,0 +1,65 @@
1
+ interface VideoAnalytics {
2
+ videoId: string;
3
+ title: string;
4
+ views: number;
5
+ likes: number;
6
+ comments: number;
7
+ publishedAt: string;
8
+ thumbnail: string;
9
+ }
10
+ interface ChannelAnalytics {
11
+ channelId: string;
12
+ title: string;
13
+ description: string;
14
+ thumbnail: string;
15
+ subscriberCount: number;
16
+ viewCount: number;
17
+ videoCount: number;
18
+ hiddenSubscriberCount: boolean;
19
+ }
20
+ interface AudienceAnalytics {
21
+ views: number;
22
+ estimatedMinutesWatched: number;
23
+ averageViewDuration: number;
24
+ subscribersGained: number;
25
+ subscribersLost: number;
26
+ }
27
+ interface VideoAnalytics {
28
+ videoId: string;
29
+ title: string;
30
+ views: number;
31
+ likes: number;
32
+ comments: number;
33
+ publishedAt: string;
34
+ thumbneil?: string;
35
+ videoType: "short" | "regular";
36
+ }
37
+ interface ChannelRevenue {
38
+ estimatedRevenue: number;
39
+ currency: string;
40
+ startDate: string;
41
+ endDate: string;
42
+ }
43
+ export declare class YoutubeChannelHandler {
44
+ private access_token;
45
+ private channel_id;
46
+ constructor(access_token: string, channel_id: string);
47
+ /**
48
+ * Fetches basic channel statistics and information
49
+ * @returns Channel analytics including subscriber count, view count, and video count
50
+ */
51
+ fetchChannelAnalytics(): Promise<ChannelAnalytics>;
52
+ /**
53
+ * Fetches analytics for all videos in the channel
54
+ * @param maxResults Maximum number of videos to fetch (default: 50, max: 50)
55
+ * @returns Array of video analytics including views, likes, and comments
56
+ */
57
+ fetchVideos(maxResults?: number): Promise<VideoAnalytics[]>;
58
+ /**
59
+ * Fetches audience analytics and engagement metrics for the channel
60
+ * @returns Audience analytics including view duration and subscriber changes
61
+ */
62
+ fetchAudienceAnalytics(): Promise<AudienceAnalytics>;
63
+ fetchChannelRevenue(startDate?: string, endDate?: string): Promise<ChannelRevenue>;
64
+ }
65
+ export {};