@dxos/plugin-youtube 0.8.4-main.bcb3aa67d6 → 0.8.4-main.fcfe5033a5
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/lib/browser/{chunk-5N5SWF3I.mjs → chunk-FEQD5TPI.mjs} +2 -2
- package/dist/lib/browser/{chunk-5N5SWF3I.mjs.map → chunk-FEQD5TPI.mjs.map} +2 -2
- package/dist/lib/browser/{chunk-SWWE4LUJ.mjs → chunk-GIRFSTHR.mjs} +4 -4
- package/dist/lib/browser/{chunk-SWWE4LUJ.mjs.map → chunk-GIRFSTHR.mjs.map} +3 -3
- package/dist/lib/browser/{clear-synced-videos-PIKJZET3.mjs → clear-synced-videos-PMP332H3.mjs} +8 -8
- package/dist/lib/browser/clear-synced-videos-PMP332H3.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +5 -5
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{sync-II7O2LPG.mjs → sync-T34US6NO.mjs} +15 -15
- package/dist/lib/browser/sync-T34US6NO.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-RL46XZ2D.mjs → chunk-A3SKNJFU.mjs} +4 -4
- package/dist/lib/node-esm/{chunk-RL46XZ2D.mjs.map → chunk-A3SKNJFU.mjs.map} +3 -3
- package/dist/lib/node-esm/{chunk-BVKMXV2G.mjs → chunk-Q3TVMR5B.mjs} +2 -2
- package/dist/lib/node-esm/{chunk-BVKMXV2G.mjs.map → chunk-Q3TVMR5B.mjs.map} +2 -2
- package/dist/lib/node-esm/{clear-synced-videos-Q3MZO2CD.mjs → clear-synced-videos-SLEDJ5WI.mjs} +8 -8
- package/dist/lib/node-esm/clear-synced-videos-SLEDJ5WI.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +5 -5
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/{sync-BEXQNNSH.mjs → sync-RQYQ5LII.mjs} +15 -15
- package/dist/lib/node-esm/sync-RQYQ5LII.mjs.map +7 -0
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +1 -1
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/migrations.d.ts +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/containers/ChannelArticle/ChannelArticle.d.ts +1 -1
- package/dist/types/src/containers/ChannelArticle/ChannelArticle.d.ts.map +1 -1
- package/dist/types/src/containers/ChannelArticle/index.d.ts +1 -2
- package/dist/types/src/containers/ChannelArticle/index.d.ts.map +1 -1
- package/dist/types/src/containers/ChannelProperties/ChannelProperties.d.ts +7 -0
- package/dist/types/src/containers/ChannelProperties/ChannelProperties.d.ts.map +1 -0
- package/dist/types/src/containers/ChannelProperties/index.d.ts +2 -0
- package/dist/types/src/containers/ChannelProperties/index.d.ts.map +1 -0
- package/dist/types/src/containers/VideoArticle/VideoArticle.d.ts +2 -2
- package/dist/types/src/containers/VideoArticle/VideoArticle.d.ts.map +1 -1
- package/dist/types/src/containers/VideoArticle/index.d.ts +1 -2
- package/dist/types/src/containers/VideoArticle/index.d.ts.map +1 -1
- package/dist/types/src/containers/VideoCard/VideoCard.d.ts +2 -2
- package/dist/types/src/containers/VideoCard/VideoCard.d.ts.map +1 -1
- package/dist/types/src/containers/VideoCard/index.d.ts +1 -2
- package/dist/types/src/containers/VideoCard/index.d.ts.map +1 -1
- package/dist/types/src/containers/index.d.ts +3 -3
- package/dist/types/src/containers/index.d.ts.map +1 -1
- package/dist/types/src/operations/apis/youtube/api.d.ts.map +1 -1
- package/dist/types/src/operations/clear-synced-videos.d.ts.map +1 -1
- package/dist/types/src/operations/definitions.d.ts +3 -3
- package/dist/types/src/operations/definitions.d.ts.map +1 -1
- package/dist/types/src/operations/sync.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +40 -40
- package/src/YouTubePlugin.tsx +3 -3
- package/src/capabilities/app-graph-builder.ts +13 -14
- package/src/capabilities/react-surface.tsx +20 -21
- package/src/containers/ChannelArticle/ChannelArticle.tsx +1 -1
- package/src/containers/ChannelArticle/index.ts +1 -3
- package/src/containers/{ChannelSettings/ChannelSettings.tsx → ChannelProperties/ChannelProperties.tsx} +2 -2
- package/src/containers/ChannelProperties/index.ts +5 -0
- package/src/containers/VideoArticle/VideoArticle.tsx +2 -2
- package/src/containers/VideoArticle/index.ts +1 -3
- package/src/containers/VideoCard/VideoCard.tsx +2 -2
- package/src/containers/VideoCard/index.ts +1 -3
- package/src/containers/index.ts +4 -4
- package/src/operations/apis/youtube/api.ts +0 -1
- package/src/operations/clear-synced-videos.ts +3 -4
- package/src/operations/definitions.ts +3 -3
- package/src/operations/sync.ts +0 -1
- package/dist/lib/browser/clear-synced-videos-PIKJZET3.mjs.map +0 -7
- package/dist/lib/browser/sync-II7O2LPG.mjs.map +0 -7
- package/dist/lib/node-esm/clear-synced-videos-Q3MZO2CD.mjs.map +0 -7
- package/dist/lib/node-esm/sync-BEXQNNSH.mjs.map +0 -7
- package/dist/types/src/containers/ChannelSettings/ChannelSettings.d.ts +0 -7
- package/dist/types/src/containers/ChannelSettings/ChannelSettings.d.ts.map +0 -1
- package/dist/types/src/containers/ChannelSettings/index.d.ts +0 -3
- package/dist/types/src/containers/ChannelSettings/index.d.ts.map +0 -1
- package/src/containers/ChannelSettings/index.ts +0 -7
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
GoogleCredentials,
|
|
3
3
|
youtube_exports
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-FEQD5TPI.mjs";
|
|
5
5
|
import {
|
|
6
6
|
Sync
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-GIRFSTHR.mjs";
|
|
8
8
|
import {
|
|
9
9
|
Video_exports
|
|
10
10
|
} from "./chunk-GTIWG45H.mjs";
|
|
@@ -71,7 +71,7 @@ var handler = Sync.pipe(Operation.withHandler(({ channel: channelRef, restricted
|
|
|
71
71
|
includeTranscripts
|
|
72
72
|
}, {
|
|
73
73
|
F: __dxlog_file2,
|
|
74
|
-
L:
|
|
74
|
+
L: 27,
|
|
75
75
|
S: this,
|
|
76
76
|
C: (f, a) => f(...a)
|
|
77
77
|
});
|
|
@@ -83,7 +83,7 @@ var handler = Sync.pipe(Operation.withHandler(({ channel: channelRef, restricted
|
|
|
83
83
|
const channelInfo = extractChannelInfo(channelUrl);
|
|
84
84
|
log2("extracted channel info", channelInfo, {
|
|
85
85
|
F: __dxlog_file2,
|
|
86
|
-
L:
|
|
86
|
+
L: 37,
|
|
87
87
|
S: this,
|
|
88
88
|
C: (f, a) => f(...a)
|
|
89
89
|
});
|
|
@@ -94,7 +94,7 @@ var handler = Sync.pipe(Operation.withHandler(({ channel: channelRef, restricted
|
|
|
94
94
|
uploadsPlaylistId
|
|
95
95
|
}, {
|
|
96
96
|
F: __dxlog_file2,
|
|
97
|
-
L:
|
|
97
|
+
L: 40,
|
|
98
98
|
S: this,
|
|
99
99
|
C: (f, a) => f(...a)
|
|
100
100
|
});
|
|
@@ -111,7 +111,7 @@ var handler = Sync.pipe(Operation.withHandler(({ channel: channelRef, restricted
|
|
|
111
111
|
count: existingVideoIds.size
|
|
112
112
|
}, {
|
|
113
113
|
F: __dxlog_file2,
|
|
114
|
-
L:
|
|
114
|
+
L: 53,
|
|
115
115
|
S: this,
|
|
116
116
|
C: (f, a) => f(...a)
|
|
117
117
|
});
|
|
@@ -124,7 +124,7 @@ var handler = Sync.pipe(Operation.withHandler(({ channel: channelRef, restricted
|
|
|
124
124
|
channelTitle
|
|
125
125
|
}, {
|
|
126
126
|
F: __dxlog_file2,
|
|
127
|
-
L:
|
|
127
|
+
L: 67,
|
|
128
128
|
S: this,
|
|
129
129
|
C: (f, a) => f(...a)
|
|
130
130
|
});
|
|
@@ -237,7 +237,7 @@ var fetchPlaylistVideos = (uploadsPlaylistId, maxVideos) => {
|
|
|
237
237
|
hasMore: Boolean(response.nextPageToken)
|
|
238
238
|
}, {
|
|
239
239
|
F: __dxlog_file2,
|
|
240
|
-
L:
|
|
240
|
+
L: 186,
|
|
241
241
|
S: this,
|
|
242
242
|
C: (f, a) => f(...a)
|
|
243
243
|
});
|
|
@@ -278,7 +278,7 @@ var streamVideosToFeed = Effect2.fn(function* (uploadsPlaylistId, feed, existing
|
|
|
278
278
|
videoId
|
|
279
279
|
}, {
|
|
280
280
|
F: __dxlog_file2,
|
|
281
|
-
L:
|
|
281
|
+
L: 250,
|
|
282
282
|
S: this,
|
|
283
283
|
C: (f, a) => f(...a)
|
|
284
284
|
});
|
|
@@ -290,7 +290,7 @@ var streamVideosToFeed = Effect2.fn(function* (uploadsPlaylistId, feed, existing
|
|
|
290
290
|
count: videoIds.length
|
|
291
291
|
}, {
|
|
292
292
|
F: __dxlog_file2,
|
|
293
|
-
L:
|
|
293
|
+
L: 260,
|
|
294
294
|
S: this,
|
|
295
295
|
C: (f, a) => f(...a)
|
|
296
296
|
});
|
|
@@ -305,7 +305,7 @@ var streamVideosToFeed = Effect2.fn(function* (uploadsPlaylistId, feed, existing
|
|
|
305
305
|
videoId: item.id
|
|
306
306
|
}, {
|
|
307
307
|
F: __dxlog_file2,
|
|
308
|
-
L:
|
|
308
|
+
L: 274,
|
|
309
309
|
S: this,
|
|
310
310
|
C: (f, a) => f(...a)
|
|
311
311
|
});
|
|
@@ -317,7 +317,7 @@ var streamVideosToFeed = Effect2.fn(function* (uploadsPlaylistId, feed, existing
|
|
|
317
317
|
length: transcript.fullText.length
|
|
318
318
|
}, {
|
|
319
319
|
F: __dxlog_file2,
|
|
320
|
-
L:
|
|
320
|
+
L: 278,
|
|
321
321
|
S: this,
|
|
322
322
|
C: (f, a) => f(...a)
|
|
323
323
|
});
|
|
@@ -326,7 +326,7 @@ var streamVideosToFeed = Effect2.fn(function* (uploadsPlaylistId, feed, existing
|
|
|
326
326
|
videoId: item.id
|
|
327
327
|
}, {
|
|
328
328
|
F: __dxlog_file2,
|
|
329
|
-
L:
|
|
329
|
+
L: 280,
|
|
330
330
|
S: this,
|
|
331
331
|
C: (f, a) => f(...a)
|
|
332
332
|
});
|
|
@@ -342,7 +342,7 @@ var streamVideosToFeed = Effect2.fn(function* (uploadsPlaylistId, feed, existing
|
|
|
342
342
|
count: videos.length
|
|
343
343
|
}, {
|
|
344
344
|
F: __dxlog_file2,
|
|
345
|
-
L:
|
|
345
|
+
L: 296,
|
|
346
346
|
S: this,
|
|
347
347
|
C: (f, a) => f(...a)
|
|
348
348
|
});
|
|
@@ -356,4 +356,4 @@ var sync_default = handler;
|
|
|
356
356
|
export {
|
|
357
357
|
sync_default as default
|
|
358
358
|
};
|
|
359
|
-
//# sourceMappingURL=sync-
|
|
359
|
+
//# sourceMappingURL=sync-T34US6NO.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/operations/sync.ts", "../../../src/operations/transcript.ts"],
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport * as FetchHttpClient from '@effect/platform/FetchHttpClient';\nimport * as Chunk from 'effect/Chunk';\nimport * as Effect from 'effect/Effect';\nimport * as Function from 'effect/Function';\nimport * as Layer from 'effect/Layer';\nimport * as Option from 'effect/Option';\nimport * as Predicate from 'effect/Predicate';\nimport * as Stream from 'effect/Stream';\n\nimport { Database, Feed, Filter, Obj, Ref } from '@dxos/echo';\nimport { log } from '@dxos/log';\nimport { Operation } from '@dxos/operation';\n\nimport { Channel, Video } from '../types';\nimport { YouTube } from './apis';\nimport { Sync } from './definitions';\nimport { GoogleCredentials } from './services/google-credentials';\nimport { fetchTranscript } from './transcript';\n\nconst handler: Operation.WithHandler<typeof Sync> = Sync.pipe(\n Operation.withHandler(({ channel: channelRef, restrictedMode = false, includeTranscripts = true }) =>\n Effect.gen(function* () {\n log('syncing youtube channel', { channel: channelRef.dxn.toString(), restrictedMode, includeTranscripts });\n const channel = yield* Database.load(channelRef);\n\n const channelUrl =\n (channel as Channel.YouTubeChannel).channelUrl ?? (channel as Channel.YouTubeChannel).channelId;\n if (!channelUrl) {\n return yield* Effect.fail(new Error('No channel URL or ID configured'));\n }\n\n const channelInfo = extractChannelInfo(channelUrl);\n log('extracted channel info', channelInfo);\n\n const { channelId, channelTitle, uploadsPlaylistId } = yield* getUploadsPlaylistId(channelInfo);\n log('found channel', { channelId, channelTitle, uploadsPlaylistId });\n\n Obj.change(channel as Channel.YouTubeChannel, (channelObj) => {\n channelObj.channelId = channelId;\n if (!channelObj.name) {\n channelObj.name = channelTitle;\n }\n });\n\n // Get the feed and query for existing videos.\n const feed = yield* Database.load((channel as Channel.YouTubeChannel).feed as Ref.Ref<Feed.Feed>);\n const existingVideos = yield* Feed.runQuery(feed, Filter.type(Video.YouTubeVideo));\n const existingVideoIds = new Set(existingVideos.map((video: Video.YouTubeVideo) => video.videoId));\n log('existing videos', { count: existingVideoIds.size });\n\n const newVideosCount = yield* streamVideosToFeed(\n uploadsPlaylistId,\n feed,\n existingVideoIds,\n restrictedMode,\n includeTranscripts,\n );\n\n Obj.change(channel as Channel.YouTubeChannel, (channelObj) => {\n channelObj.lastSyncedAt = new Date().toISOString();\n });\n\n log('sync complete', { newVideos: newVideosCount, channelTitle });\n return {\n newVideos: newVideosCount,\n channelTitle,\n };\n }).pipe(Effect.provide(Layer.mergeAll(FetchHttpClient.layer, GoogleCredentials.fromChannelRef(channelRef)))),\n ),\n);\n\nconst STREAMING_CONFIG = {\n /** Videos per page from YouTube API. */\n maxResults: 50,\n /** Parallel transcript fetches. */\n transcriptFetchConcurrency: 3,\n /** In-flight video buffer. */\n bufferSize: 10,\n /** Videos per feed append. */\n feedBatchSize: 10,\n /** Max videos in restricted mode. */\n restrictedMax: 20,\n} as const;\n\n/**\n * Extracts channel ID from various YouTube URL formats.\n */\nconst extractChannelInfo = (\n urlOrHandle: string,\n): { type: 'id'; value: string } | { type: 'handle'; value: string } | { type: 'url'; value: string } => {\n const trimmed = urlOrHandle.trim();\n\n if (trimmed.startsWith('@')) {\n return { type: 'handle', value: trimmed.slice(1) };\n }\n\n if (trimmed.startsWith('UC') && trimmed.length === 24) {\n return { type: 'id', value: trimmed };\n }\n\n try {\n const url = new URL(trimmed);\n const pathname = url.pathname;\n\n if (pathname.startsWith('/channel/')) {\n const channelId = pathname.split('/')[2];\n if (channelId) {\n return { type: 'id', value: channelId };\n }\n }\n\n if (pathname.startsWith('/@')) {\n return { type: 'handle', value: pathname.slice(2) };\n }\n\n if (pathname.startsWith('/c/') || pathname.startsWith('/user/')) {\n const name = pathname.split('/')[2];\n if (name) {\n return { type: 'handle', value: name };\n }\n }\n } catch {\n if (/^[a-zA-Z0-9_-]+$/.test(trimmed)) {\n return { type: 'handle', value: trimmed };\n }\n }\n\n return { type: 'url', value: trimmed };\n};\n\n/**\n * Gets the uploads playlist ID for a channel.\n */\nconst getUploadsPlaylistId = Effect.fn(function* (channelInfo: { type: string; value: string }) {\n let channelResponse;\n\n if (channelInfo.type === 'id') {\n channelResponse = yield* YouTube.getChannel(channelInfo.value);\n } else {\n channelResponse = yield* YouTube.getChannelByHandle(channelInfo.value);\n }\n\n const channel = channelResponse.items[0];\n if (!channel) {\n return yield* Effect.fail(new Error(`Channel not found: ${channelInfo.value}`));\n }\n\n const uploadsPlaylistId = channel.contentDetails?.relatedPlaylists?.uploads;\n if (!uploadsPlaylistId) {\n return yield* Effect.fail(new Error(`No uploads playlist found for channel: ${channelInfo.value}`));\n }\n\n return {\n channelId: channel.id,\n channelTitle: channel.snippet?.title ?? '',\n uploadsPlaylistId,\n };\n});\n\n/**\n * Fetches videos from an uploads playlist, returning them from newest to oldest.\n */\nconst fetchPlaylistVideos = (uploadsPlaylistId: string, maxVideos?: number) => {\n let videoCount = 0;\n\n return Stream.unfoldChunkEffect({ pageToken: Option.none<string>(), done: false }, (state) =>\n Effect.gen(function* () {\n if (state.done || (maxVideos && videoCount >= maxVideos)) {\n return Option.none();\n }\n\n const response = yield* YouTube.listPlaylistItems(\n uploadsPlaylistId,\n STREAMING_CONFIG.maxResults,\n Option.getOrUndefined(state.pageToken),\n );\n\n const videoIds = response.items\n .map((item) => item.snippet?.resourceId?.videoId)\n .filter((id): id is string => Boolean(id));\n\n log('fetched playlist items', {\n count: videoIds.length,\n pageToken: Option.getOrUndefined(state.pageToken),\n hasMore: Boolean(response.nextPageToken),\n });\n\n videoCount += videoIds.length;\n\n const nextState = {\n pageToken: Option.fromNullable(response.nextPageToken),\n done: !response.nextPageToken || (maxVideos !== undefined && videoCount >= maxVideos),\n };\n\n return Option.some([Chunk.fromIterable(videoIds), nextState]);\n }),\n );\n};\n\ntype TranscriptData = { segments: Video.TranscriptSegment[]; fullText: string };\n\n/**\n * Maps YouTube API video item to YouTubeVideo data.\n */\nconst mapVideoData = (\n item: YouTube.VideoItem,\n transcript: TranscriptData | undefined,\n includeTranscripts: boolean,\n): Omit<Video.YouTubeVideo, 'id' | '~@dxos/echo/Kind'> => {\n const hasTranscript = Boolean(transcript?.fullText?.trim());\n return {\n title: item.snippet?.title ?? 'Untitled',\n videoId: item.id,\n description: item.snippet?.description,\n url: `https://www.youtube.com/watch?v=${item.id}`,\n thumbnailUrl:\n item.snippet?.thumbnails?.high?.url ??\n item.snippet?.thumbnails?.medium?.url ??\n item.snippet?.thumbnails?.default?.url,\n channelTitle: item.snippet?.channelTitle,\n publishedAt: item.snippet?.publishedAt ?? new Date().toISOString(),\n duration: item.contentDetails?.duration,\n viewCount: item.statistics?.viewCount ? parseInt(item.statistics.viewCount, 10) : undefined,\n likeCount: item.statistics?.likeCount ? parseInt(item.statistics.likeCount, 10) : undefined,\n transcript: transcript && hasTranscript ? transcript.fullText : undefined,\n transcriptSegments: transcript && hasTranscript ? transcript.segments : undefined,\n transcriptFetched: includeTranscripts ? hasTranscript : false,\n };\n};\n\n/**\n * Stream videos with transcripts into a DXOS feed.\n */\nconst streamVideosToFeed = Effect.fn(function* (\n uploadsPlaylistId: string,\n feed: Feed.Feed,\n existingVideoIds: Set<string>,\n restricted: boolean,\n includeTranscripts: boolean,\n) {\n const count = yield* Function.pipe(\n fetchPlaylistVideos(uploadsPlaylistId, restricted ? STREAMING_CONFIG.restrictedMax : undefined),\n Stream.filter((videoId) => {\n const isDuplicate = existingVideoIds.has(videoId);\n if (isDuplicate) {\n log('skipping duplicate video', { videoId });\n }\n return !isDuplicate;\n }),\n restricted ? Stream.take(STREAMING_CONFIG.restrictedMax) : Function.identity,\n Stream.grouped(10),\n Stream.flatMap(\n (videoIdChunk) =>\n Effect.gen(function* () {\n const videoIds = Chunk.toArray(videoIdChunk);\n log('fetching video details', { count: videoIds.length });\n\n const response = yield* YouTube.getVideoDetails(videoIds);\n return response.items;\n }),\n { concurrency: 1 },\n ),\n Stream.flatMap((items) => Stream.fromIterable(items)),\n Stream.flatMap(\n (item) =>\n Effect.gen(function* () {\n let transcript: TranscriptData | undefined;\n\n if (includeTranscripts) {\n log('fetching transcript', { videoId: item.id });\n const result = yield* fetchTranscript(item.id);\n if (result) {\n transcript = result;\n log('transcript fetched', { videoId: item.id, length: transcript.fullText.length });\n } else {\n log('no transcript available', { videoId: item.id });\n }\n }\n\n return mapVideoData(item, transcript, includeTranscripts);\n }),\n {\n concurrency: STREAMING_CONFIG.transcriptFetchConcurrency,\n bufferSize: STREAMING_CONFIG.bufferSize,\n },\n ),\n Stream.filter(Predicate.isNotNullable),\n Stream.grouped(STREAMING_CONFIG.feedBatchSize),\n Stream.mapEffect((batch) =>\n Effect.gen(function* () {\n const videos = Chunk.toArray(batch);\n log('appending batch to feed', { count: videos.length });\n const videoObjects = videos.map((video) => Obj.make(Video.YouTubeVideo, video));\n yield* Feed.append(feed, videoObjects);\n return videos.length;\n }),\n ),\n Stream.runFold(0, (acc, batchCount) => acc + batchCount),\n );\n\n return count;\n});\n\nexport default handler;\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport { log } from '@dxos/log';\n\nimport type { TranscriptSegment } from '../types/Video';\n\nexport type TranscriptResult = {\n segments: TranscriptSegment[];\n fullText: string;\n};\n\n/**\n * Fetches captions for a YouTube video using youtube-caption-extractor.\n * Works in both browser and Node/edge environments.\n */\nexport const fetchTranscript = (videoId: string, lang?: string): Effect.Effect<TranscriptResult | undefined> =>\n Effect.tryPromise({\n try: async () => {\n const { getSubtitles } = await import('youtube-caption-extractor');\n const subtitles = await getSubtitles({ videoID: videoId, lang: lang ?? 'en' });\n\n if (!subtitles || subtitles.length === 0) {\n return undefined;\n }\n\n const segments: TranscriptSegment[] = subtitles.map((sub) => ({\n text: sub.text,\n offset: parseFloat(sub.start),\n duration: parseFloat(sub.dur),\n }));\n\n const fullText = segments.map((segment) => segment.text).join(' ');\n return { segments, fullText };\n },\n catch: (error) => {\n log('failed to fetch transcript', { videoId, error });\n return undefined;\n },\n }).pipe(\n Effect.catchAll(() => Effect.succeed(undefined)),\n Effect.timeout('30 seconds'),\n Effect.catchAll(() => Effect.succeed(undefined)),\n );\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["FetchHttpClient", "Chunk", "Effect", "Function", "Layer", "Option", "Predicate", "Stream", "Database", "Feed", "Filter", "Obj", "log", "Operation", "Effect", "log", "fetchTranscript", "videoId", "lang", "tryPromise", "try", "getSubtitles", "subtitles", "videoID", "length", "undefined", "segments", "map", "sub", "text", "offset", "parseFloat", "start", "duration", "dur", "fullText", "segment", "join", "catch", "error", "pipe", "catchAll", "succeed", "timeout", "handler", "Sync", "pipe", "Operation", "withHandler", "channel", "channelRef", "restrictedMode", "includeTranscripts", "gen", "log", "dxn", "toString", "Database", "load", "channelUrl", "channelId", "fail", "Error", "channelInfo", "extractChannelInfo", "channelTitle", "uploadsPlaylistId", "getUploadsPlaylistId", "Obj", "change", "channelObj", "name", "feed", "existingVideos", "Feed", "runQuery", "Filter", "type", "Video", "YouTubeVideo", "existingVideoIds", "Set", "map", "video", "videoId", "count", "size", "newVideosCount", "streamVideosToFeed", "lastSyncedAt", "Date", "toISOString", "newVideos", "provide", "mergeAll", "layer", "GoogleCredentials", "fromChannelRef", "STREAMING_CONFIG", "maxResults", "transcriptFetchConcurrency", "bufferSize", "feedBatchSize", "restrictedMax", "urlOrHandle", "trimmed", "trim", "startsWith", "value", "slice", "length", "url", "URL", "pathname", "split", "test", "fn", "channelResponse", "YouTube", "getChannel", "getChannelByHandle", "items", "contentDetails", "relatedPlaylists", "uploads", "id", "snippet", "title", "fetchPlaylistVideos", "maxVideos", "videoCount", "unfoldChunkEffect", "pageToken", "none", "done", "state", "response", "listPlaylistItems", "getOrUndefined", "videoIds", "item", "resourceId", "filter", "Boolean", "hasMore", "nextPageToken", "nextState", "fromNullable", "undefined", "some", "fromIterable", "mapVideoData", "transcript", "hasTranscript", "fullText", "description", "thumbnailUrl", "thumbnails", "high", "medium", "default", "publishedAt", "duration", "viewCount", "statistics", "parseInt", "likeCount", "transcriptSegments", "segments", "transcriptFetched", "restricted", "isDuplicate", "has", "take", "identity", "grouped", "flatMap", "videoIdChunk", "toArray", "getVideoDetails", "concurrency", "result", "fetchTranscript", "isNotNullable", "mapEffect", "batch", "videos", "videoObjects", "make", "append", "runFold", "acc", "batchCount"]
|
|
7
|
+
}
|
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
} from "./chunk-YOE54ALJ.mjs";
|
|
6
6
|
|
|
7
7
|
// src/operations/definitions.ts
|
|
8
|
-
import { Operation } from "@dxos/operation";
|
|
9
8
|
import * as Schema from "effect/Schema";
|
|
10
9
|
import { Database, Feed, Ref } from "@dxos/echo";
|
|
11
10
|
import { CredentialsService } from "@dxos/functions";
|
|
11
|
+
import { Operation } from "@dxos/operation";
|
|
12
12
|
var Sync = Operation.make({
|
|
13
13
|
meta: {
|
|
14
14
|
key: "dxos.org/function/youtube/sync",
|
|
@@ -36,7 +36,7 @@ var Sync = Operation.make({
|
|
|
36
36
|
],
|
|
37
37
|
services: [
|
|
38
38
|
Database.Service,
|
|
39
|
-
Feed.
|
|
39
|
+
Feed.FeedService,
|
|
40
40
|
CredentialsService
|
|
41
41
|
]
|
|
42
42
|
});
|
|
@@ -60,7 +60,7 @@ var ClearSyncedVideos = Operation.make({
|
|
|
60
60
|
],
|
|
61
61
|
services: [
|
|
62
62
|
Database.Service,
|
|
63
|
-
Feed.
|
|
63
|
+
Feed.FeedService
|
|
64
64
|
]
|
|
65
65
|
});
|
|
66
66
|
|
|
@@ -68,4 +68,4 @@ export {
|
|
|
68
68
|
Sync,
|
|
69
69
|
ClearSyncedVideos
|
|
70
70
|
};
|
|
71
|
-
//# sourceMappingURL=chunk-
|
|
71
|
+
//# sourceMappingURL=chunk-A3SKNJFU.mjs.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/operations/definitions.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport
|
|
5
|
-
"mappings": ";;;;;;;AAIA,
|
|
6
|
-
"names": ["
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport * as Schema from 'effect/Schema';\n\nimport { Database, Feed, Ref } from '@dxos/echo';\nimport { CredentialsService } from '@dxos/functions';\nimport { Operation } from '@dxos/operation';\n\nimport { Channel, Video } from '../types';\n\nexport const Sync = Operation.make({\n meta: {\n key: 'dxos.org/function/youtube/sync',\n name: 'Sync YouTube Channel',\n description: 'Sync videos from a YouTube channel to the feed.',\n },\n input: Schema.Struct({\n channel: Ref.Ref(Channel.YouTubeChannel).annotations({\n description: 'Reference to the YouTube channel to sync videos from.',\n }),\n restrictedMode: Schema.Boolean.pipe(\n Schema.annotations({\n description:\n 'Use restricted mode to limit to max 20 videos. Reduces API calls. Useful for testing or quick syncs.',\n }),\n Schema.optional,\n ),\n includeTranscripts: Schema.Boolean.pipe(\n Schema.annotations({\n description:\n 'Whether to fetch transcripts: Data API when your account can manage the video, otherwise watch-page captions (browser uses a public CORS proxy for YouTube fetches).',\n }),\n Schema.optional,\n ),\n }),\n output: Schema.Struct({\n newVideos: Schema.Number,\n channelTitle: Schema.String.pipe(Schema.optional),\n }),\n types: [Channel.YouTubeChannel, Video.YouTubeVideo],\n services: [Database.Service, Feed.FeedService, CredentialsService],\n});\n\nexport const ClearSyncedVideos = Operation.make({\n meta: {\n key: 'dxos.org/function/youtube/clear-synced-videos',\n name: 'Clear Synced YouTube Videos',\n description: 'Remove all synced videos from the channel by replacing its feed with a new empty feed.',\n },\n input: Schema.Struct({\n channel: Ref.Ref(Channel.YouTubeChannel).annotations({\n description: 'Reference to the YouTube channel whose synced videos should be cleared.',\n }),\n }),\n output: Schema.Struct({\n removedVideos: Schema.Number,\n }),\n types: [Channel.YouTubeChannel, Video.YouTubeVideo],\n services: [Database.Service, Feed.FeedService],\n});\n"],
|
|
5
|
+
"mappings": ";;;;;;;AAIA,YAAYA,YAAY;AAExB,SAASC,UAAUC,MAAMC,WAAW;AACpC,SAASC,0BAA0B;AACnC,SAASC,iBAAiB;AAInB,IAAMC,OAAOC,UAAUC,KAAK;EACjCC,MAAM;IACJC,KAAK;IACLC,MAAM;IACNC,aAAa;EACf;EACAC,OAAcC,cAAO;IACnBC,SAASC,IAAIA,IAAIC,gBAAQC,cAAc,EAAEC,YAAY;MACnDP,aAAa;IACf,CAAA;IACAQ,gBAAuBC,eAAQC,KACtBH,mBAAY;MACjBP,aACE;IACJ,CAAA,GACOW,eAAQ;IAEjBC,oBAA2BH,eAAQC,KAC1BH,mBAAY;MACjBP,aACE;IACJ,CAAA,GACOW,eAAQ;EAEnB,CAAA;EACAE,QAAeX,cAAO;IACpBY,WAAkBC;IAClBC,cAAqBC,cAAOP,KAAYC,eAAQ;EAClD,CAAA;EACAO,OAAO;IAACb,gBAAQC;IAAgBa,cAAMC;;EACtCC,UAAU;IAACC,SAASC;IAASC,KAAKC;IAAaC;;AACjD,CAAA;AAEO,IAAMC,oBAAoBhC,UAAUC,KAAK;EAC9CC,MAAM;IACJC,KAAK;IACLC,MAAM;IACNC,aAAa;EACf;EACAC,OAAcC,cAAO;IACnBC,SAASC,IAAIA,IAAIC,gBAAQC,cAAc,EAAEC,YAAY;MACnDP,aAAa;IACf,CAAA;EACF,CAAA;EACAa,QAAeX,cAAO;IACpB0B,eAAsBb;EACxB,CAAA;EACAG,OAAO;IAACb,gBAAQC;IAAgBa,cAAMC;;EACtCC,UAAU;IAACC,SAASC;IAASC,KAAKC;;AACpC,CAAA;",
|
|
6
|
+
"names": ["Schema", "Database", "Feed", "Ref", "CredentialsService", "Operation", "Sync", "Operation", "make", "meta", "key", "name", "description", "input", "Struct", "channel", "Ref", "Channel", "YouTubeChannel", "annotations", "restrictedMode", "Boolean", "pipe", "optional", "includeTranscripts", "output", "newVideos", "Number", "channelTitle", "String", "types", "Video", "YouTubeVideo", "services", "Database", "Service", "Feed", "FeedService", "CredentialsService", "ClearSyncedVideos", "removedVideos"]
|
|
7
7
|
}
|
|
@@ -287,7 +287,7 @@ var makeYouTubeApiRequest = Effect2.fn("makeYouTubeApiRequest")(function* (url)
|
|
|
287
287
|
if (response.error) {
|
|
288
288
|
log2.catch(response.error, void 0, {
|
|
289
289
|
F: __dxlog_file2,
|
|
290
|
-
L:
|
|
290
|
+
L: 80,
|
|
291
291
|
S: this,
|
|
292
292
|
C: (f, a) => f(...a)
|
|
293
293
|
});
|
|
@@ -353,4 +353,4 @@ export {
|
|
|
353
353
|
GoogleCredentials,
|
|
354
354
|
youtube_exports
|
|
355
355
|
};
|
|
356
|
-
//# sourceMappingURL=chunk-
|
|
356
|
+
//# sourceMappingURL=chunk-Q3TVMR5B.mjs.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/operations/services/google-credentials.ts", "../../../src/operations/apis/youtube/index.ts", "../../../src/operations/apis/youtube/api.ts", "../../../src/operations/apis/youtube/types.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport * as Context from 'effect/Context';\nimport * as Effect from 'effect/Effect';\nimport * as Layer from 'effect/Layer';\n\nimport { Database, type Ref } from '@dxos/echo';\nimport { CredentialsService } from '@dxos/functions';\nimport { log } from '@dxos/log';\nimport { type AccessToken } from '@dxos/types';\n\nimport type * as Channel from '../../types/Channel';\n\n/**\n * Creates the service interface from a cached token.\n * Falls back to database credentials if no cached token is provided.\n */\nconst makeService = (cachedToken: string | undefined): Context.Tag.Service<GoogleCredentials> => ({\n get: () =>\n cachedToken\n ? Effect.succeed(cachedToken)\n : Effect.map(CredentialsService.getCredential({ service: 'google.com' }), (credential) => credential.apiKey!),\n});\n\n/**\n * Loads access token from a ref if available.\n */\nconst loadAccessToken = (accessTokenRef: Ref.Ref<AccessToken.AccessToken> | undefined, label: string) =>\n Effect.gen(function* () {\n if (accessTokenRef) {\n const accessToken = yield* Database.load(accessTokenRef);\n if (accessToken?.token) {\n log(`using ${label}-specific access token`, { note: accessToken.note });\n return accessToken.token;\n }\n }\n return undefined;\n });\n\n/**\n * Service for accessing Google API credentials.\n * Provides the Google API token either from an object's access token or falls back to database credentials.\n */\n// TODO(dmaretskyi): Remove this service.\nexport class GoogleCredentials extends Context.Tag('GoogleCredentials')<\n GoogleCredentials,\n {\n /** Returns the Google API token. */\n get: () => Effect.Effect<string, never, CredentialsService>;\n }\n>() {\n /**\n * Creates a credentials layer from a channel.\n * Pre-loads the access token during layer construction.\n */\n static fromChannel = (channel: Channel.YouTubeChannel) =>\n Layer.effect(GoogleCredentials, Effect.map(loadAccessToken(channel.accessToken, 'channel'), makeService));\n\n /**\n * Creates a credentials layer from a channel ref.\n * Loads the channel and pre-loads the access token during layer construction.\n */\n static fromChannelRef = (channelRef: Ref.Ref<Channel.YouTubeChannel>) =>\n Layer.effect(\n GoogleCredentials,\n Effect.flatMap(Database.load(channelRef), (channel) =>\n Effect.map(loadAccessToken(channel.accessToken, 'channel'), makeService),\n ),\n );\n\n /**\n * Default layer that uses database credentials.\n * Use this for operations that don't have an associated channel.\n */\n static default = Layer.succeed(GoogleCredentials, makeService(undefined));\n\n /** Convenience accessor - returns the Google API token. */\n static get = () => Effect.flatMap(GoogleCredentials, (service) => service.get());\n}\n", "//\n// Copyright 2024 DXOS.org\n//\n\nexport * from './api';\nexport * from './types';\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport * as HttpClient from '@effect/platform/HttpClient';\nimport * as HttpClientRequest from '@effect/platform/HttpClientRequest';\nimport * as Effect from 'effect/Effect';\nimport type * as ParseResult from 'effect/ParseResult';\nimport * as Schedule from 'effect/Schedule';\nimport * as Schema from 'effect/Schema';\n\nimport { withAuthorization } from '@dxos/functions';\nimport { log } from '@dxos/log';\n\nimport { GoogleCredentials } from '../../services/google-credentials';\n\nimport {\n ChannelsResponse,\n ErrorResponse,\n PlaylistItemsResponse,\n SearchResponse,\n VideosResponse,\n YouTubeError,\n} from './types';\n\nconst API_URL = 'https://www.googleapis.com/youtube/v3';\n\n/**\n * Creates a URL from path segments and query parameters.\n */\nconst createUrl = (parts: (string | undefined)[], params?: Record<string, unknown>): URL => {\n const url = new URL(parts.filter(Boolean).join('/'));\n if (params) {\n Object.entries(params)\n .filter(([_, value]) => value != null)\n .forEach(([key, value]) => url.searchParams.set(key, String(value)));\n }\n return url;\n};\n\n/**\n * Decode response and handle YouTube API errors.\n */\nconst decodeAndHandleErrors =\n <S extends Schema.Schema.Any>(schema: S) =>\n (\n data: unknown,\n ): Effect.Effect<Schema.Schema.Type<S>, YouTubeError | ParseResult.ParseError, Schema.Schema.Context<S>> =>\n Schema.decodeUnknown(Schema.Union(schema, ErrorResponse))(data).pipe(\n Effect.flatMap((response) => {\n if ('error' in response) {\n return Effect.fail(YouTubeError.fromErrorResponse(response));\n } else {\n return Effect.succeed(response);\n }\n }),\n );\n\n/**\n * Makes an authenticated HTTP request to a YouTube API endpoint.\n */\nconst makeYouTubeApiRequest = Effect.fn('makeYouTubeApiRequest')(function* (url: string) {\n const token = yield* GoogleCredentials.get();\n\n const httpClient = yield* HttpClient.HttpClient.pipe(Effect.map(withAuthorization(token, 'Bearer')));\n const httpClientWithTracerDisabled = httpClient.pipe(HttpClient.withTracerDisabledWhen(() => true));\n\n const request = HttpClientRequest.get(url);\n\n const response = yield* request.pipe(\n HttpClientRequest.setHeader('accept', 'application/json'),\n httpClientWithTracerDisabled.execute,\n Effect.flatMap((res) => res.json),\n Effect.timeout('10 seconds'),\n Effect.retry(Schedule.exponential(1_000).pipe(Schedule.compose(Schedule.recurs(3)))),\n Effect.scoped,\n Effect.withSpan('YouTubeApiRequest'),\n );\n\n if ((response as Record<string, unknown>).error) {\n log.catch((response as Record<string, unknown>).error);\n }\n\n return response;\n});\n\n/**\n * Get channel details by channel ID.\n * https://developers.google.com/youtube/v3/docs/channels/list\n */\nexport const getChannel = Effect.fn(function* (channelId: string) {\n const url = createUrl([API_URL, 'channels'], {\n part: 'snippet,contentDetails',\n id: channelId,\n }).toString();\n return yield* makeYouTubeApiRequest(url).pipe(Effect.flatMap(decodeAndHandleErrors(ChannelsResponse)));\n});\n\n/**\n * Get channel by username/handle.\n * https://developers.google.com/youtube/v3/docs/channels/list\n */\nexport const getChannelByHandle = Effect.fn(function* (handle: string) {\n const url = createUrl([API_URL, 'channels'], {\n part: 'snippet,contentDetails',\n forHandle: handle.startsWith('@') ? handle.slice(1) : handle,\n }).toString();\n return yield* makeYouTubeApiRequest(url).pipe(Effect.flatMap(decodeAndHandleErrors(ChannelsResponse)));\n});\n\n/**\n * Search for channels by query.\n * https://developers.google.com/youtube/v3/docs/search/list\n */\nexport const searchChannels = Effect.fn(function* (query: string, maxResults = 10) {\n const url = createUrl([API_URL, 'search'], {\n part: 'snippet',\n type: 'channel',\n q: query,\n maxResults,\n }).toString();\n return yield* makeYouTubeApiRequest(url).pipe(Effect.flatMap(decodeAndHandleErrors(SearchResponse)));\n});\n\n/**\n * List videos from a playlist (used to get uploads from a channel's uploads playlist).\n * https://developers.google.com/youtube/v3/docs/playlistItems/list\n */\nexport const listPlaylistItems = Effect.fn(function* (playlistId: string, maxResults = 50, pageToken?: string) {\n const url = createUrl([API_URL, 'playlistItems'], {\n part: 'snippet,contentDetails',\n playlistId,\n maxResults,\n pageToken,\n }).toString();\n return yield* makeYouTubeApiRequest(url).pipe(Effect.flatMap(decodeAndHandleErrors(PlaylistItemsResponse)));\n});\n\n/**\n * Get detailed video information including statistics and duration.\n * https://developers.google.com/youtube/v3/docs/videos/list\n */\nexport const getVideoDetails = Effect.fn(function* (videoIds: string[]) {\n const url = createUrl([API_URL, 'videos'], {\n part: 'snippet,contentDetails,statistics',\n id: videoIds.join(','),\n }).toString();\n return yield* makeYouTubeApiRequest(url).pipe(Effect.flatMap(decodeAndHandleErrors(VideosResponse)));\n});\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport * as Schema from 'effect/Schema';\n\n/**\n * YouTube API error response.\n */\nexport class YouTubeError extends Schema.TaggedError<YouTubeError>()('YouTubeError', {\n code: Schema.Number,\n message: Schema.String,\n status: Schema.optional(Schema.String),\n}) {\n static fromErrorResponse(response: ErrorResponse) {\n return new YouTubeError({\n code: response.error.code,\n message: response.error.message,\n status: response.error.status,\n });\n }\n}\n\nexport const ErrorResponse = Schema.Struct({\n error: Schema.Struct({\n code: Schema.Number,\n message: Schema.String,\n status: Schema.optional(Schema.String),\n }),\n});\n\nexport type ErrorResponse = Schema.Schema.Type<typeof ErrorResponse>;\n\n/**\n * YouTube channel snippet from search/channel API.\n */\nexport const ChannelSnippet = Schema.Struct({\n title: Schema.String,\n description: Schema.optional(Schema.String),\n customUrl: Schema.optional(Schema.String),\n publishedAt: Schema.optional(Schema.String),\n thumbnails: Schema.optional(\n Schema.Struct({\n default: Schema.optional(Schema.Struct({ url: Schema.String })),\n medium: Schema.optional(Schema.Struct({ url: Schema.String })),\n high: Schema.optional(Schema.Struct({ url: Schema.String })),\n }),\n ),\n});\n\n/**\n * YouTube channel item from channels API.\n */\nexport const ChannelItem = Schema.Struct({\n kind: Schema.String,\n etag: Schema.String,\n id: Schema.String,\n snippet: Schema.optional(ChannelSnippet),\n contentDetails: Schema.optional(\n Schema.Struct({\n relatedPlaylists: Schema.optional(\n Schema.Struct({\n uploads: Schema.optional(Schema.String),\n }),\n ),\n }),\n ),\n});\n\n/**\n * YouTube channels list response.\n */\nexport const ChannelsResponse = Schema.Struct({\n kind: Schema.String,\n etag: Schema.String,\n pageInfo: Schema.optional(\n Schema.Struct({\n totalResults: Schema.Number,\n resultsPerPage: Schema.Number,\n }),\n ),\n items: Schema.Array(ChannelItem),\n});\n\nexport type ChannelsResponse = Schema.Schema.Type<typeof ChannelsResponse>;\n\n/**\n * Video snippet from search/playlistItems API.\n */\nexport const VideoSnippet = Schema.Struct({\n publishedAt: Schema.String,\n channelId: Schema.optional(Schema.String),\n title: Schema.String,\n description: Schema.optional(Schema.String),\n thumbnails: Schema.optional(\n Schema.Struct({\n default: Schema.optional(Schema.Struct({ url: Schema.String })),\n medium: Schema.optional(Schema.Struct({ url: Schema.String })),\n high: Schema.optional(Schema.Struct({ url: Schema.String })),\n standard: Schema.optional(Schema.Struct({ url: Schema.String })),\n maxres: Schema.optional(Schema.Struct({ url: Schema.String })),\n }),\n ),\n channelTitle: Schema.optional(Schema.String),\n playlistId: Schema.optional(Schema.String),\n position: Schema.optional(Schema.Number),\n resourceId: Schema.optional(\n Schema.Struct({\n kind: Schema.String,\n videoId: Schema.String,\n }),\n ),\n});\n\n/**\n * Content details for a video.\n */\nexport const VideoContentDetails = Schema.Struct({\n videoId: Schema.optional(Schema.String),\n videoPublishedAt: Schema.optional(Schema.String),\n duration: Schema.optional(Schema.String),\n});\n\n/**\n * Video statistics.\n */\nexport const VideoStatistics = Schema.Struct({\n viewCount: Schema.optional(Schema.String),\n likeCount: Schema.optional(Schema.String),\n commentCount: Schema.optional(Schema.String),\n});\n\n/**\n * Video item from videos API.\n */\nexport const VideoItem = Schema.Struct({\n kind: Schema.String,\n etag: Schema.String,\n id: Schema.String,\n snippet: Schema.optional(VideoSnippet),\n contentDetails: Schema.optional(VideoContentDetails),\n statistics: Schema.optional(VideoStatistics),\n});\n\nexport type VideoItem = Schema.Schema.Type<typeof VideoItem>;\n\n/**\n * YouTube videos list response.\n */\nexport const VideosResponse = Schema.Struct({\n kind: Schema.String,\n etag: Schema.String,\n nextPageToken: Schema.optional(Schema.String),\n prevPageToken: Schema.optional(Schema.String),\n pageInfo: Schema.optional(\n Schema.Struct({\n totalResults: Schema.Number,\n resultsPerPage: Schema.Number,\n }),\n ),\n items: Schema.Array(VideoItem),\n});\n\nexport type VideosResponse = Schema.Schema.Type<typeof VideosResponse>;\n\n/**\n * Playlist item from playlistItems API.\n */\nexport const PlaylistItem = Schema.Struct({\n kind: Schema.String,\n etag: Schema.String,\n id: Schema.String,\n snippet: Schema.optional(VideoSnippet),\n contentDetails: Schema.optional(VideoContentDetails),\n});\n\n/**\n * YouTube playlist items response.\n */\nexport const PlaylistItemsResponse = Schema.Struct({\n kind: Schema.String,\n etag: Schema.String,\n nextPageToken: Schema.optional(Schema.String),\n prevPageToken: Schema.optional(Schema.String),\n pageInfo: Schema.optional(\n Schema.Struct({\n totalResults: Schema.Number,\n resultsPerPage: Schema.Number,\n }),\n ),\n items: Schema.Array(PlaylistItem),\n});\n\nexport type PlaylistItemsResponse = Schema.Schema.Type<typeof PlaylistItemsResponse>;\n\n/**\n * Search result item.\n */\nexport const SearchItem = Schema.Struct({\n kind: Schema.String,\n etag: Schema.String,\n id: Schema.Struct({\n kind: Schema.String,\n videoId: Schema.optional(Schema.String),\n channelId: Schema.optional(Schema.String),\n playlistId: Schema.optional(Schema.String),\n }),\n snippet: Schema.optional(VideoSnippet),\n});\n\n/**\n * YouTube search response.\n */\nexport const SearchResponse = Schema.Struct({\n kind: Schema.String,\n etag: Schema.String,\n nextPageToken: Schema.optional(Schema.String),\n prevPageToken: Schema.optional(Schema.String),\n pageInfo: Schema.optional(\n Schema.Struct({\n totalResults: Schema.Number,\n resultsPerPage: Schema.Number,\n }),\n ),\n items: Schema.Array(SearchItem),\n});\n\nexport type SearchResponse = Schema.Schema.Type<typeof SearchResponse>;\n\n/**\n * Caption track from captions.list.\n */\nexport const CaptionResource = Schema.Struct({\n id: Schema.String,\n snippet: Schema.optional(\n Schema.Struct({\n videoId: Schema.optional(Schema.String),\n language: Schema.optional(Schema.String),\n name: Schema.optional(Schema.String),\n trackKind: Schema.optional(Schema.String),\n }),\n ),\n});\n\n/**\n * YouTube captions.list response.\n */\nexport const CaptionsListResponse = Schema.Struct({\n kind: Schema.String,\n etag: Schema.String,\n items: Schema.optional(Schema.Array(CaptionResource)),\n});\n\nexport type CaptionsListResponse = Schema.Schema.Type<typeof CaptionsListResponse>;\n"],
|
|
5
|
-
"mappings": ";;;;;;AAIA,YAAYA,aAAa;AACzB,YAAYC,YAAY;AACxB,YAAYC,WAAW;AAEvB,SAASC,gBAA0B;AACnC,SAASC,0BAA0B;AACnC,SAASC,WAAW;;AASpB,IAAMC,cAAc,CAACC,iBAA6E;EAChGC,KAAK,MACHD,cACWE,eAAQF,WAAAA,IACRG,WAAIN,mBAAmBO,cAAc;IAAEC,SAAS;EAAa,CAAA,GAAI,CAACC,eAAeA,WAAWC,MAAM;AACjH;AAKA,IAAMC,kBAAkB,CAACC,gBAA8DC,UAC9EC,WAAI,aAAA;AACT,MAAIF,gBAAgB;AAClB,UAAMG,cAAc,OAAOhB,SAASiB,KAAKJ,cAAAA;AACzC,QAAIG,aAAaE,OAAO;AACtBhB,UAAI,SAASY,KAAAA,0BAA+B;QAAEK,MAAMH,YAAYG;MAAK,GAAA;;;;;;AACrE,aAAOH,YAAYE;IACrB;EACF;AACA,SAAOE;AACT,CAAA;AAOK,IAAMC,oBAAN,MAAMA,2BAAkCC,YAAI,mBAAA,EAAA,EAAA;;;;;EAWjD,OAAOC,cAAc,CAACC,YACdC,aAAOJ,oBAA0Bd,WAAIK,gBAAgBY,QAAQR,aAAa,SAAA,GAAYb,WAAAA,CAAAA;;;;;EAM9F,OAAOuB,iBAAiB,CAACC,eACjBF,aACJJ,oBACOO,eAAQ5B,SAASiB,KAAKU,UAAAA,GAAa,CAACH,YAClCjB,WAAIK,gBAAgBY,QAAQR,aAAa,SAAA,GAAYb,WAAAA,CAAAA,CAAAA;;;;;EAQlE,OAAO0B,UAAgBvB,cAAQe,oBAAmBlB,YAAYiB,MAAAA,CAAAA;;EAG9D,OAAOf,MAAM,MAAauB,eAAQP,oBAAmB,CAACZ,YAAYA,QAAQJ,IAAG,CAAA;AAC/E;;;AChFA;;;;;;;;;;;;;;;;;;;;;;;;;;ACIA,YAAYyB,gBAAgB;AAC5B,YAAYC,uBAAuB;AACnC,YAAYC,aAAY;AAExB,YAAYC,cAAc;AAC1B,YAAYC,aAAY;AAExB,SAASC,yBAAyB;AAClC,SAASC,OAAAA,YAAW;;;ACRpB,YAAYC,YAAY;AAKjB,IAAMC,eAAN,MAAMA,sBAA4BC,mBAAW,EAAiB,gBAAgB;EACnFC,MAAaC;EACbC,SAAgBC;EAChBC,QAAeC,gBAAgBF,aAAM;AACvC,CAAA,EAAA;EACE,OAAOG,kBAAkBC,UAAyB;AAChD,WAAO,IAAIT,cAAa;MACtBE,MAAMO,SAASC,MAAMR;MACrBE,SAASK,SAASC,MAAMN;MACxBE,QAAQG,SAASC,MAAMJ;IACzB,CAAA;EACF;AACF;AAEO,IAAMK,gBAAuBC,cAAO;EACzCF,OAAcE,cAAO;IACnBV,MAAaC;IACbC,SAAgBC;IAChBC,QAAeC,gBAAgBF,aAAM;EACvC,CAAA;AACF,CAAA;AAOO,IAAMQ,iBAAwBD,cAAO;EAC1CE,OAAcT;EACdU,aAAoBR,gBAAgBF,aAAM;EAC1CW,WAAkBT,gBAAgBF,aAAM;EACxCY,aAAoBV,gBAAgBF,aAAM;EAC1Ca,YAAmBX,gBACVK,cAAO;IACZO,SAAgBZ,gBAAgBK,cAAO;MAAEQ,KAAYf;IAAO,CAAA,CAAA;IAC5DgB,QAAed,gBAAgBK,cAAO;MAAEQ,KAAYf;IAAO,CAAA,CAAA;IAC3DiB,MAAaf,gBAAgBK,cAAO;MAAEQ,KAAYf;IAAO,CAAA,CAAA;EAC3D,CAAA,CAAA;AAEJ,CAAA;AAKO,IAAMkB,cAAqBX,cAAO;EACvCY,MAAanB;EACboB,MAAapB;EACbqB,IAAWrB;EACXsB,SAAgBpB,gBAASM,cAAAA;EACzBe,gBAAuBrB,gBACdK,cAAO;IACZiB,kBAAyBtB,gBAChBK,cAAO;MACZkB,SAAgBvB,gBAAgBF,aAAM;IACxC,CAAA,CAAA;EAEJ,CAAA,CAAA;AAEJ,CAAA;AAKO,IAAM0B,mBAA0BnB,cAAO;EAC5CY,MAAanB;EACboB,MAAapB;EACb2B,UAAiBzB,gBACRK,cAAO;IACZqB,cAAqB9B;IACrB+B,gBAAuB/B;EACzB,CAAA,CAAA;EAEFgC,OAAcC,aAAMb,WAAAA;AACtB,CAAA;AAOO,IAAMc,eAAsBzB,cAAO;EACxCK,aAAoBZ;EACpBiC,WAAkB/B,gBAAgBF,aAAM;EACxCS,OAAcT;EACdU,aAAoBR,gBAAgBF,aAAM;EAC1Ca,YAAmBX,gBACVK,cAAO;IACZO,SAAgBZ,gBAAgBK,cAAO;MAAEQ,KAAYf;IAAO,CAAA,CAAA;IAC5DgB,QAAed,gBAAgBK,cAAO;MAAEQ,KAAYf;IAAO,CAAA,CAAA;IAC3DiB,MAAaf,gBAAgBK,cAAO;MAAEQ,KAAYf;IAAO,CAAA,CAAA;IACzDkC,UAAiBhC,gBAAgBK,cAAO;MAAEQ,KAAYf;IAAO,CAAA,CAAA;IAC7DmC,QAAejC,gBAAgBK,cAAO;MAAEQ,KAAYf;IAAO,CAAA,CAAA;EAC7D,CAAA,CAAA;EAEFoC,cAAqBlC,gBAAgBF,aAAM;EAC3CqC,YAAmBnC,gBAAgBF,aAAM;EACzCsC,UAAiBpC,gBAAgBJ,aAAM;EACvCyC,YAAmBrC,gBACVK,cAAO;IACZY,MAAanB;IACbwC,SAAgBxC;EAClB,CAAA,CAAA;AAEJ,CAAA;AAKO,IAAMyC,sBAA6BlC,cAAO;EAC/CiC,SAAgBtC,gBAAgBF,aAAM;EACtC0C,kBAAyBxC,gBAAgBF,aAAM;EAC/C2C,UAAiBzC,gBAAgBF,aAAM;AACzC,CAAA;AAKO,IAAM4C,kBAAyBrC,cAAO;EAC3CsC,WAAkB3C,gBAAgBF,aAAM;EACxC8C,WAAkB5C,gBAAgBF,aAAM;EACxC+C,cAAqB7C,gBAAgBF,aAAM;AAC7C,CAAA;AAKO,IAAMgD,YAAmBzC,cAAO;EACrCY,MAAanB;EACboB,MAAapB;EACbqB,IAAWrB;EACXsB,SAAgBpB,gBAAS8B,YAAAA;EACzBT,gBAAuBrB,gBAASuC,mBAAAA;EAChCQ,YAAmB/C,gBAAS0C,eAAAA;AAC9B,CAAA;AAOO,IAAMM,iBAAwB3C,cAAO;EAC1CY,MAAanB;EACboB,MAAapB;EACbmD,eAAsBjD,gBAAgBF,aAAM;EAC5CoD,eAAsBlD,gBAAgBF,aAAM;EAC5C2B,UAAiBzB,gBACRK,cAAO;IACZqB,cAAqB9B;IACrB+B,gBAAuB/B;EACzB,CAAA,CAAA;EAEFgC,OAAcC,aAAMiB,SAAAA;AACtB,CAAA;AAOO,IAAMK,eAAsB9C,cAAO;EACxCY,MAAanB;EACboB,MAAapB;EACbqB,IAAWrB;EACXsB,SAAgBpB,gBAAS8B,YAAAA;EACzBT,gBAAuBrB,gBAASuC,mBAAAA;AAClC,CAAA;AAKO,IAAMa,wBAA+B/C,cAAO;EACjDY,MAAanB;EACboB,MAAapB;EACbmD,eAAsBjD,gBAAgBF,aAAM;EAC5CoD,eAAsBlD,gBAAgBF,aAAM;EAC5C2B,UAAiBzB,gBACRK,cAAO;IACZqB,cAAqB9B;IACrB+B,gBAAuB/B;EACzB,CAAA,CAAA;EAEFgC,OAAcC,aAAMsB,YAAAA;AACtB,CAAA;AAOO,IAAME,aAAoBhD,cAAO;EACtCY,MAAanB;EACboB,MAAapB;EACbqB,IAAWd,cAAO;IAChBY,MAAanB;IACbwC,SAAgBtC,gBAAgBF,aAAM;IACtCiC,WAAkB/B,gBAAgBF,aAAM;IACxCqC,YAAmBnC,gBAAgBF,aAAM;EAC3C,CAAA;EACAsB,SAAgBpB,gBAAS8B,YAAAA;AAC3B,CAAA;AAKO,IAAMwB,iBAAwBjD,cAAO;EAC1CY,MAAanB;EACboB,MAAapB;EACbmD,eAAsBjD,gBAAgBF,aAAM;EAC5CoD,eAAsBlD,gBAAgBF,aAAM;EAC5C2B,UAAiBzB,gBACRK,cAAO;IACZqB,cAAqB9B;IACrB+B,gBAAuB/B;EACzB,CAAA,CAAA;EAEFgC,OAAcC,aAAMwB,UAAAA;AACtB,CAAA;AAOO,IAAME,kBAAyBlD,cAAO;EAC3Cc,IAAWrB;EACXsB,SAAgBpB,gBACPK,cAAO;IACZiC,SAAgBtC,gBAAgBF,aAAM;IACtC0D,UAAiBxD,gBAAgBF,aAAM;IACvC2D,MAAazD,gBAAgBF,aAAM;IACnC4D,WAAkB1D,gBAAgBF,aAAM;EAC1C,CAAA,CAAA;AAEJ,CAAA;AAKO,IAAM6D,uBAA8BtD,cAAO;EAChDY,MAAanB;EACboB,MAAapB;EACb8B,OAAc5B,gBAAgB6B,aAAM0B,eAAAA,CAAAA;AACtC,CAAA;;;;
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport * as Context from 'effect/Context';\nimport * as Effect from 'effect/Effect';\nimport * as Layer from 'effect/Layer';\n\nimport { Database, type Ref } from '@dxos/echo';\nimport { CredentialsService } from '@dxos/functions';\nimport { log } from '@dxos/log';\nimport { type AccessToken } from '@dxos/types';\n\nimport type * as Channel from '../../types/Channel';\n\n/**\n * Creates the service interface from a cached token.\n * Falls back to database credentials if no cached token is provided.\n */\nconst makeService = (cachedToken: string | undefined): Context.Tag.Service<GoogleCredentials> => ({\n get: () =>\n cachedToken\n ? Effect.succeed(cachedToken)\n : Effect.map(CredentialsService.getCredential({ service: 'google.com' }), (credential) => credential.apiKey!),\n});\n\n/**\n * Loads access token from a ref if available.\n */\nconst loadAccessToken = (accessTokenRef: Ref.Ref<AccessToken.AccessToken> | undefined, label: string) =>\n Effect.gen(function* () {\n if (accessTokenRef) {\n const accessToken = yield* Database.load(accessTokenRef);\n if (accessToken?.token) {\n log(`using ${label}-specific access token`, { note: accessToken.note });\n return accessToken.token;\n }\n }\n return undefined;\n });\n\n/**\n * Service for accessing Google API credentials.\n * Provides the Google API token either from an object's access token or falls back to database credentials.\n */\n// TODO(dmaretskyi): Remove this service.\nexport class GoogleCredentials extends Context.Tag('GoogleCredentials')<\n GoogleCredentials,\n {\n /** Returns the Google API token. */\n get: () => Effect.Effect<string, never, CredentialsService>;\n }\n>() {\n /**\n * Creates a credentials layer from a channel.\n * Pre-loads the access token during layer construction.\n */\n static fromChannel = (channel: Channel.YouTubeChannel) =>\n Layer.effect(GoogleCredentials, Effect.map(loadAccessToken(channel.accessToken, 'channel'), makeService));\n\n /**\n * Creates a credentials layer from a channel ref.\n * Loads the channel and pre-loads the access token during layer construction.\n */\n static fromChannelRef = (channelRef: Ref.Ref<Channel.YouTubeChannel>) =>\n Layer.effect(\n GoogleCredentials,\n Effect.flatMap(Database.load(channelRef), (channel) =>\n Effect.map(loadAccessToken(channel.accessToken, 'channel'), makeService),\n ),\n );\n\n /**\n * Default layer that uses database credentials.\n * Use this for operations that don't have an associated channel.\n */\n static default = Layer.succeed(GoogleCredentials, makeService(undefined));\n\n /** Convenience accessor - returns the Google API token. */\n static get = () => Effect.flatMap(GoogleCredentials, (service) => service.get());\n}\n", "//\n// Copyright 2024 DXOS.org\n//\n\nexport * from './api';\nexport * from './types';\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport * as HttpClient from '@effect/platform/HttpClient';\nimport * as HttpClientRequest from '@effect/platform/HttpClientRequest';\nimport * as Effect from 'effect/Effect';\nimport type * as ParseResult from 'effect/ParseResult';\nimport * as Schedule from 'effect/Schedule';\nimport * as Schema from 'effect/Schema';\n\nimport { withAuthorization } from '@dxos/functions';\nimport { log } from '@dxos/log';\n\nimport { GoogleCredentials } from '../../services/google-credentials';\nimport {\n ChannelsResponse,\n ErrorResponse,\n PlaylistItemsResponse,\n SearchResponse,\n VideosResponse,\n YouTubeError,\n} from './types';\n\nconst API_URL = 'https://www.googleapis.com/youtube/v3';\n\n/**\n * Creates a URL from path segments and query parameters.\n */\nconst createUrl = (parts: (string | undefined)[], params?: Record<string, unknown>): URL => {\n const url = new URL(parts.filter(Boolean).join('/'));\n if (params) {\n Object.entries(params)\n .filter(([_, value]) => value != null)\n .forEach(([key, value]) => url.searchParams.set(key, String(value)));\n }\n return url;\n};\n\n/**\n * Decode response and handle YouTube API errors.\n */\nconst decodeAndHandleErrors =\n <S extends Schema.Schema.Any>(schema: S) =>\n (\n data: unknown,\n ): Effect.Effect<Schema.Schema.Type<S>, YouTubeError | ParseResult.ParseError, Schema.Schema.Context<S>> =>\n Schema.decodeUnknown(Schema.Union(schema, ErrorResponse))(data).pipe(\n Effect.flatMap((response) => {\n if ('error' in response) {\n return Effect.fail(YouTubeError.fromErrorResponse(response));\n } else {\n return Effect.succeed(response);\n }\n }),\n );\n\n/**\n * Makes an authenticated HTTP request to a YouTube API endpoint.\n */\nconst makeYouTubeApiRequest = Effect.fn('makeYouTubeApiRequest')(function* (url: string) {\n const token = yield* GoogleCredentials.get();\n\n const httpClient = yield* HttpClient.HttpClient.pipe(Effect.map(withAuthorization(token, 'Bearer')));\n const httpClientWithTracerDisabled = httpClient.pipe(HttpClient.withTracerDisabledWhen(() => true));\n\n const request = HttpClientRequest.get(url);\n\n const response = yield* request.pipe(\n HttpClientRequest.setHeader('accept', 'application/json'),\n httpClientWithTracerDisabled.execute,\n Effect.flatMap((res) => res.json),\n Effect.timeout('10 seconds'),\n Effect.retry(Schedule.exponential(1_000).pipe(Schedule.compose(Schedule.recurs(3)))),\n Effect.scoped,\n Effect.withSpan('YouTubeApiRequest'),\n );\n\n if ((response as Record<string, unknown>).error) {\n log.catch((response as Record<string, unknown>).error);\n }\n\n return response;\n});\n\n/**\n * Get channel details by channel ID.\n * https://developers.google.com/youtube/v3/docs/channels/list\n */\nexport const getChannel = Effect.fn(function* (channelId: string) {\n const url = createUrl([API_URL, 'channels'], {\n part: 'snippet,contentDetails',\n id: channelId,\n }).toString();\n return yield* makeYouTubeApiRequest(url).pipe(Effect.flatMap(decodeAndHandleErrors(ChannelsResponse)));\n});\n\n/**\n * Get channel by username/handle.\n * https://developers.google.com/youtube/v3/docs/channels/list\n */\nexport const getChannelByHandle = Effect.fn(function* (handle: string) {\n const url = createUrl([API_URL, 'channels'], {\n part: 'snippet,contentDetails',\n forHandle: handle.startsWith('@') ? handle.slice(1) : handle,\n }).toString();\n return yield* makeYouTubeApiRequest(url).pipe(Effect.flatMap(decodeAndHandleErrors(ChannelsResponse)));\n});\n\n/**\n * Search for channels by query.\n * https://developers.google.com/youtube/v3/docs/search/list\n */\nexport const searchChannels = Effect.fn(function* (query: string, maxResults = 10) {\n const url = createUrl([API_URL, 'search'], {\n part: 'snippet',\n type: 'channel',\n q: query,\n maxResults,\n }).toString();\n return yield* makeYouTubeApiRequest(url).pipe(Effect.flatMap(decodeAndHandleErrors(SearchResponse)));\n});\n\n/**\n * List videos from a playlist (used to get uploads from a channel's uploads playlist).\n * https://developers.google.com/youtube/v3/docs/playlistItems/list\n */\nexport const listPlaylistItems = Effect.fn(function* (playlistId: string, maxResults = 50, pageToken?: string) {\n const url = createUrl([API_URL, 'playlistItems'], {\n part: 'snippet,contentDetails',\n playlistId,\n maxResults,\n pageToken,\n }).toString();\n return yield* makeYouTubeApiRequest(url).pipe(Effect.flatMap(decodeAndHandleErrors(PlaylistItemsResponse)));\n});\n\n/**\n * Get detailed video information including statistics and duration.\n * https://developers.google.com/youtube/v3/docs/videos/list\n */\nexport const getVideoDetails = Effect.fn(function* (videoIds: string[]) {\n const url = createUrl([API_URL, 'videos'], {\n part: 'snippet,contentDetails,statistics',\n id: videoIds.join(','),\n }).toString();\n return yield* makeYouTubeApiRequest(url).pipe(Effect.flatMap(decodeAndHandleErrors(VideosResponse)));\n});\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport * as Schema from 'effect/Schema';\n\n/**\n * YouTube API error response.\n */\nexport class YouTubeError extends Schema.TaggedError<YouTubeError>()('YouTubeError', {\n code: Schema.Number,\n message: Schema.String,\n status: Schema.optional(Schema.String),\n}) {\n static fromErrorResponse(response: ErrorResponse) {\n return new YouTubeError({\n code: response.error.code,\n message: response.error.message,\n status: response.error.status,\n });\n }\n}\n\nexport const ErrorResponse = Schema.Struct({\n error: Schema.Struct({\n code: Schema.Number,\n message: Schema.String,\n status: Schema.optional(Schema.String),\n }),\n});\n\nexport type ErrorResponse = Schema.Schema.Type<typeof ErrorResponse>;\n\n/**\n * YouTube channel snippet from search/channel API.\n */\nexport const ChannelSnippet = Schema.Struct({\n title: Schema.String,\n description: Schema.optional(Schema.String),\n customUrl: Schema.optional(Schema.String),\n publishedAt: Schema.optional(Schema.String),\n thumbnails: Schema.optional(\n Schema.Struct({\n default: Schema.optional(Schema.Struct({ url: Schema.String })),\n medium: Schema.optional(Schema.Struct({ url: Schema.String })),\n high: Schema.optional(Schema.Struct({ url: Schema.String })),\n }),\n ),\n});\n\n/**\n * YouTube channel item from channels API.\n */\nexport const ChannelItem = Schema.Struct({\n kind: Schema.String,\n etag: Schema.String,\n id: Schema.String,\n snippet: Schema.optional(ChannelSnippet),\n contentDetails: Schema.optional(\n Schema.Struct({\n relatedPlaylists: Schema.optional(\n Schema.Struct({\n uploads: Schema.optional(Schema.String),\n }),\n ),\n }),\n ),\n});\n\n/**\n * YouTube channels list response.\n */\nexport const ChannelsResponse = Schema.Struct({\n kind: Schema.String,\n etag: Schema.String,\n pageInfo: Schema.optional(\n Schema.Struct({\n totalResults: Schema.Number,\n resultsPerPage: Schema.Number,\n }),\n ),\n items: Schema.Array(ChannelItem),\n});\n\nexport type ChannelsResponse = Schema.Schema.Type<typeof ChannelsResponse>;\n\n/**\n * Video snippet from search/playlistItems API.\n */\nexport const VideoSnippet = Schema.Struct({\n publishedAt: Schema.String,\n channelId: Schema.optional(Schema.String),\n title: Schema.String,\n description: Schema.optional(Schema.String),\n thumbnails: Schema.optional(\n Schema.Struct({\n default: Schema.optional(Schema.Struct({ url: Schema.String })),\n medium: Schema.optional(Schema.Struct({ url: Schema.String })),\n high: Schema.optional(Schema.Struct({ url: Schema.String })),\n standard: Schema.optional(Schema.Struct({ url: Schema.String })),\n maxres: Schema.optional(Schema.Struct({ url: Schema.String })),\n }),\n ),\n channelTitle: Schema.optional(Schema.String),\n playlistId: Schema.optional(Schema.String),\n position: Schema.optional(Schema.Number),\n resourceId: Schema.optional(\n Schema.Struct({\n kind: Schema.String,\n videoId: Schema.String,\n }),\n ),\n});\n\n/**\n * Content details for a video.\n */\nexport const VideoContentDetails = Schema.Struct({\n videoId: Schema.optional(Schema.String),\n videoPublishedAt: Schema.optional(Schema.String),\n duration: Schema.optional(Schema.String),\n});\n\n/**\n * Video statistics.\n */\nexport const VideoStatistics = Schema.Struct({\n viewCount: Schema.optional(Schema.String),\n likeCount: Schema.optional(Schema.String),\n commentCount: Schema.optional(Schema.String),\n});\n\n/**\n * Video item from videos API.\n */\nexport const VideoItem = Schema.Struct({\n kind: Schema.String,\n etag: Schema.String,\n id: Schema.String,\n snippet: Schema.optional(VideoSnippet),\n contentDetails: Schema.optional(VideoContentDetails),\n statistics: Schema.optional(VideoStatistics),\n});\n\nexport type VideoItem = Schema.Schema.Type<typeof VideoItem>;\n\n/**\n * YouTube videos list response.\n */\nexport const VideosResponse = Schema.Struct({\n kind: Schema.String,\n etag: Schema.String,\n nextPageToken: Schema.optional(Schema.String),\n prevPageToken: Schema.optional(Schema.String),\n pageInfo: Schema.optional(\n Schema.Struct({\n totalResults: Schema.Number,\n resultsPerPage: Schema.Number,\n }),\n ),\n items: Schema.Array(VideoItem),\n});\n\nexport type VideosResponse = Schema.Schema.Type<typeof VideosResponse>;\n\n/**\n * Playlist item from playlistItems API.\n */\nexport const PlaylistItem = Schema.Struct({\n kind: Schema.String,\n etag: Schema.String,\n id: Schema.String,\n snippet: Schema.optional(VideoSnippet),\n contentDetails: Schema.optional(VideoContentDetails),\n});\n\n/**\n * YouTube playlist items response.\n */\nexport const PlaylistItemsResponse = Schema.Struct({\n kind: Schema.String,\n etag: Schema.String,\n nextPageToken: Schema.optional(Schema.String),\n prevPageToken: Schema.optional(Schema.String),\n pageInfo: Schema.optional(\n Schema.Struct({\n totalResults: Schema.Number,\n resultsPerPage: Schema.Number,\n }),\n ),\n items: Schema.Array(PlaylistItem),\n});\n\nexport type PlaylistItemsResponse = Schema.Schema.Type<typeof PlaylistItemsResponse>;\n\n/**\n * Search result item.\n */\nexport const SearchItem = Schema.Struct({\n kind: Schema.String,\n etag: Schema.String,\n id: Schema.Struct({\n kind: Schema.String,\n videoId: Schema.optional(Schema.String),\n channelId: Schema.optional(Schema.String),\n playlistId: Schema.optional(Schema.String),\n }),\n snippet: Schema.optional(VideoSnippet),\n});\n\n/**\n * YouTube search response.\n */\nexport const SearchResponse = Schema.Struct({\n kind: Schema.String,\n etag: Schema.String,\n nextPageToken: Schema.optional(Schema.String),\n prevPageToken: Schema.optional(Schema.String),\n pageInfo: Schema.optional(\n Schema.Struct({\n totalResults: Schema.Number,\n resultsPerPage: Schema.Number,\n }),\n ),\n items: Schema.Array(SearchItem),\n});\n\nexport type SearchResponse = Schema.Schema.Type<typeof SearchResponse>;\n\n/**\n * Caption track from captions.list.\n */\nexport const CaptionResource = Schema.Struct({\n id: Schema.String,\n snippet: Schema.optional(\n Schema.Struct({\n videoId: Schema.optional(Schema.String),\n language: Schema.optional(Schema.String),\n name: Schema.optional(Schema.String),\n trackKind: Schema.optional(Schema.String),\n }),\n ),\n});\n\n/**\n * YouTube captions.list response.\n */\nexport const CaptionsListResponse = Schema.Struct({\n kind: Schema.String,\n etag: Schema.String,\n items: Schema.optional(Schema.Array(CaptionResource)),\n});\n\nexport type CaptionsListResponse = Schema.Schema.Type<typeof CaptionsListResponse>;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["Context", "Effect", "Layer", "Database", "CredentialsService", "log", "makeService", "cachedToken", "get", "succeed", "map", "getCredential", "service", "credential", "apiKey", "loadAccessToken", "accessTokenRef", "label", "gen", "accessToken", "load", "token", "note", "undefined", "GoogleCredentials", "Tag", "fromChannel", "channel", "effect", "fromChannelRef", "channelRef", "flatMap", "default", "HttpClient", "HttpClientRequest", "Effect", "Schedule", "Schema", "withAuthorization", "log", "Schema", "YouTubeError", "TaggedError", "code", "Number", "message", "String", "status", "optional", "fromErrorResponse", "response", "error", "ErrorResponse", "Struct", "ChannelSnippet", "title", "description", "customUrl", "publishedAt", "thumbnails", "default", "url", "medium", "high", "ChannelItem", "kind", "etag", "id", "snippet", "contentDetails", "relatedPlaylists", "uploads", "ChannelsResponse", "pageInfo", "totalResults", "resultsPerPage", "items", "Array", "VideoSnippet", "channelId", "standard", "maxres", "channelTitle", "playlistId", "position", "resourceId", "videoId", "VideoContentDetails", "videoPublishedAt", "duration", "VideoStatistics", "viewCount", "likeCount", "commentCount", "VideoItem", "statistics", "VideosResponse", "nextPageToken", "prevPageToken", "PlaylistItem", "PlaylistItemsResponse", "SearchItem", "SearchResponse", "CaptionResource", "language", "name", "trackKind", "CaptionsListResponse", "API_URL", "createUrl", "parts", "params", "url", "URL", "filter", "Boolean", "join", "Object", "entries", "_", "value", "forEach", "key", "searchParams", "set", "String", "decodeAndHandleErrors", "schema", "data", "decodeUnknown", "Union", "ErrorResponse", "pipe", "flatMap", "response", "fail", "YouTubeError", "fromErrorResponse", "succeed", "makeYouTubeApiRequest", "fn", "token", "GoogleCredentials", "get", "httpClient", "HttpClient", "map", "withAuthorization", "httpClientWithTracerDisabled", "withTracerDisabledWhen", "request", "setHeader", "execute", "res", "json", "timeout", "retry", "exponential", "compose", "recurs", "scoped", "withSpan", "error", "log", "catch", "getChannel", "channelId", "part", "id", "toString", "ChannelsResponse", "getChannelByHandle", "handle", "forHandle", "startsWith", "slice", "searchChannels", "query", "maxResults", "type", "q", "SearchResponse", "listPlaylistItems", "playlistId", "pageToken", "PlaylistItemsResponse", "getVideoDetails", "videoIds", "VideosResponse"]
|
|
7
7
|
}
|
package/dist/lib/node-esm/{clear-synced-videos-Q3MZO2CD.mjs → clear-synced-videos-SLEDJ5WI.mjs}
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
ClearSyncedVideos
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-A3SKNJFU.mjs";
|
|
5
5
|
import {
|
|
6
6
|
Video_exports
|
|
7
7
|
} from "./chunk-YOE54ALJ.mjs";
|
|
@@ -18,7 +18,7 @@ var handler = ClearSyncedVideos.pipe(Operation.withHandler(({ channel: channelRe
|
|
|
18
18
|
channel: channelRef.dxn.toString()
|
|
19
19
|
}, {
|
|
20
20
|
F: __dxlog_file,
|
|
21
|
-
L:
|
|
21
|
+
L: 17,
|
|
22
22
|
S: this,
|
|
23
23
|
C: (f, a) => f(...a)
|
|
24
24
|
});
|
|
@@ -29,16 +29,16 @@ var handler = ClearSyncedVideos.pipe(Operation.withHandler(({ channel: channelRe
|
|
|
29
29
|
count: videos.length
|
|
30
30
|
}, {
|
|
31
31
|
F: __dxlog_file,
|
|
32
|
-
L:
|
|
32
|
+
L: 22,
|
|
33
33
|
S: this,
|
|
34
34
|
C: (f, a) => f(...a)
|
|
35
35
|
});
|
|
36
36
|
const newFeed = Feed.make();
|
|
37
37
|
yield* Database.add(newFeed);
|
|
38
38
|
Obj.setParent(newFeed, channel);
|
|
39
|
-
Obj.change(channel, (
|
|
40
|
-
|
|
41
|
-
delete
|
|
39
|
+
Obj.change(channel, (channel2) => {
|
|
40
|
+
channel2.feed = Ref.make(newFeed);
|
|
41
|
+
delete channel2.lastSyncedAt;
|
|
42
42
|
});
|
|
43
43
|
if (videos.length > 0) {
|
|
44
44
|
yield* Feed.remove(oldFeed, videos);
|
|
@@ -51,7 +51,7 @@ var handler = ClearSyncedVideos.pipe(Operation.withHandler(({ channel: channelRe
|
|
|
51
51
|
removedVideos: videos.length
|
|
52
52
|
}, {
|
|
53
53
|
F: __dxlog_file,
|
|
54
|
-
L:
|
|
54
|
+
L: 43,
|
|
55
55
|
S: this,
|
|
56
56
|
C: (f, a) => f(...a)
|
|
57
57
|
});
|
|
@@ -63,4 +63,4 @@ var clear_synced_videos_default = handler;
|
|
|
63
63
|
export {
|
|
64
64
|
clear_synced_videos_default as default
|
|
65
65
|
};
|
|
66
|
-
//# sourceMappingURL=clear-synced-videos-
|
|
66
|
+
//# sourceMappingURL=clear-synced-videos-SLEDJ5WI.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/operations/clear-synced-videos.ts"],
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport { Database, Feed, Filter, Obj, Ref } from '@dxos/echo';\nimport { log } from '@dxos/log';\nimport { Operation } from '@dxos/operation';\n\nimport { Channel, Video } from '../types';\nimport { ClearSyncedVideos } from './definitions';\n\nconst handler: Operation.WithHandler<typeof ClearSyncedVideos> = ClearSyncedVideos.pipe(\n Operation.withHandler(({ channel: channelRef }) =>\n Effect.gen(function* () {\n log('clearing youtube channel synced videos', { channel: channelRef.dxn.toString() });\n const channel = (yield* Database.load(channelRef)) as Channel.YouTubeChannel;\n const oldFeed = yield* Database.load(channel.feed as Ref.Ref<Feed.Feed>);\n\n const videos = yield* Feed.runQuery(oldFeed, Filter.type(Video.YouTubeVideo));\n log('removing synced videos', { count: videos.length });\n\n const newFeed = Feed.make();\n yield* Database.add(newFeed);\n Obj.setParent(newFeed, channel);\n\n Obj.change(channel, (channel) => {\n channel.feed = Ref.make(newFeed);\n delete channel.lastSyncedAt;\n });\n\n if (videos.length > 0) {\n yield* Feed.remove(oldFeed, videos);\n }\n\n for (const video of videos) {\n yield* Database.remove(video);\n }\n\n yield* Database.remove(oldFeed);\n\n log('replaced youtube channel feed', { removedVideos: videos.length });\n return { removedVideos: videos.length };\n }),\n ),\n);\n\nexport default handler;\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;AAIA,YAAYA,YAAY;AAExB,SAASC,UAAUC,MAAMC,QAAQC,KAAKC,WAAW;AACjD,SAASC,WAAW;AACpB,SAASC,iBAAiB;;AAK1B,IAAMC,UAA2DC,kBAAkBC,KACjFC,UAAUC,YAAY,CAAC,EAAEC,SAASC,WAAU,MACnCC,WAAI,aAAA;AACTC,MAAI,0CAA0C;IAAEH,SAASC,WAAWG,IAAIC,SAAQ;EAAG,GAAA;;;;;;AACnF,QAAML,UAAW,OAAOM,SAASC,KAAKN,UAAAA;AACtC,QAAMO,UAAU,OAAOF,SAASC,KAAKP,QAAQS,IAAI;AAEjD,QAAMC,SAAS,OAAOC,KAAKC,SAASJ,SAASK,OAAOC,KAAKC,cAAMC,YAAY,CAAA;AAC3Eb,MAAI,0BAA0B;IAAEc,OAAOP,OAAOQ;EAAO,GAAA;;;;;;AAErD,QAAMC,UAAUR,KAAKS,KAAI;AACzB,SAAOd,SAASe,IAAIF,OAAAA;AACpBG,MAAIC,UAAUJ,SAASnB,OAAAA;AAEvBsB,MAAIE,OAAOxB,SAAS,CAACA,aAAAA;AACnBA,IAAAA,SAAQS,OAAOgB,IAAIL,KAAKD,OAAAA;AACxB,WAAOnB,SAAQ0B;EACjB,CAAA;AAEA,MAAIhB,OAAOQ,SAAS,GAAG;AACrB,WAAOP,KAAKgB,OAAOnB,SAASE,MAAAA;EAC9B;AAEA,aAAWkB,SAASlB,QAAQ;AAC1B,WAAOJ,SAASqB,OAAOC,KAAAA;EACzB;AAEA,SAAOtB,SAASqB,OAAOnB,OAAAA;AAEvBL,MAAI,iCAAiC;IAAE0B,eAAenB,OAAOQ;EAAO,GAAA;;;;;;AACpE,SAAO;IAAEW,eAAenB,OAAOQ;EAAO;AACxC,CAAA,CAAA,CAAA;AAIJ,IAAA,8BAAevB;",
|
|
6
|
+
"names": ["Effect", "Database", "Feed", "Filter", "Obj", "Ref", "log", "Operation", "handler", "ClearSyncedVideos", "pipe", "Operation", "withHandler", "channel", "channelRef", "gen", "log", "dxn", "toString", "Database", "load", "oldFeed", "feed", "videos", "Feed", "runQuery", "Filter", "type", "Video", "YouTubeVideo", "count", "length", "newFeed", "make", "add", "Obj", "setParent", "change", "Ref", "lastSyncedAt", "remove", "video", "removedVideos"]
|
|
7
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
youtube_exports
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-Q3TVMR5B.mjs";
|
|
5
5
|
import {
|
|
6
6
|
ClearSyncedVideos,
|
|
7
7
|
Sync
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-A3SKNJFU.mjs";
|
|
9
9
|
import "./chunk-YOE54ALJ.mjs";
|
|
10
10
|
import "./chunk-HSLMI22Q.mjs";
|
|
11
11
|
|
|
@@ -28,7 +28,7 @@ var meta = {
|
|
|
28
28
|
|
|
29
29
|
// src/operations/index.ts
|
|
30
30
|
import { OperationHandlerSet } from "@dxos/operation";
|
|
31
|
-
var YouTubeHandlers = OperationHandlerSet.lazy(() => import("./clear-synced-videos-
|
|
31
|
+
var YouTubeHandlers = OperationHandlerSet.lazy(() => import("./clear-synced-videos-SLEDJ5WI.mjs"), () => import("./sync-RQYQ5LII.mjs"));
|
|
32
32
|
|
|
33
33
|
// src/YouTubePlugin.tsx
|
|
34
34
|
import * as Effect from "effect/Effect";
|
|
@@ -41,7 +41,9 @@ import { AttentionEvents } from "@dxos/plugin-attention/types";
|
|
|
41
41
|
import { ClientEvents } from "@dxos/plugin-client/types";
|
|
42
42
|
import { SpaceOperation } from "@dxos/plugin-space/operations";
|
|
43
43
|
import { YouTubeBlueprint } from "#blueprints";
|
|
44
|
+
import { AppGraphBuilder, BlueprintDefinition, Migrations, ReactSurface } from "#capabilities";
|
|
44
45
|
import { meta as meta3 } from "#meta";
|
|
46
|
+
import { Channel as Channel2, Video as Video2 } from "#types";
|
|
45
47
|
|
|
46
48
|
// src/translations.ts
|
|
47
49
|
import { meta as meta2 } from "#meta";
|
|
@@ -96,8 +98,6 @@ var translations = [
|
|
|
96
98
|
];
|
|
97
99
|
|
|
98
100
|
// src/YouTubePlugin.tsx
|
|
99
|
-
import { Channel as Channel2, Video as Video2 } from "#types";
|
|
100
|
-
import { AppGraphBuilder, BlueprintDefinition, Migrations, ReactSurface } from "#capabilities";
|
|
101
101
|
var YouTubePlugin = Plugin.define(meta3).pipe(AppPlugin.addAppGraphModule({
|
|
102
102
|
activatesOn: ActivationEvent.allOf(AppActivationEvents.SetupAppGraph, AttentionEvents.AttentionReady),
|
|
103
103
|
activate: AppGraphBuilder
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/meta.ts", "../../../src/operations/index.ts", "../../../src/YouTubePlugin.tsx", "../../../src/translations.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport { type Plugin } from '@dxos/app-framework';\nimport { trim } from '@dxos/util';\n\nexport const meta: Plugin.Meta = {\n id: 'org.dxos.plugin.youtube',\n name: 'YouTube',\n description: trim`\n YouTube channel subscription and video feed management.\n Sync videos from channels and access transcripts for analysis.\n `,\n icon: 'ph--youtube-logo--regular',\n iconHue: 'red',\n source: 'https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-youtube',\n tags: ['labs'],\n};\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { OperationHandlerSet } from '@dxos/operation';\n\nexport { YouTube } from './apis';\nexport * from './definitions';\n\nexport const YouTubeHandlers = OperationHandlerSet.lazy(\n () => import('./clear-synced-videos'),\n () => import('./sync'),\n);\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport * as Option from 'effect/Option';\n\nimport { ActivationEvent, Plugin } from '@dxos/app-framework';\nimport { AppActivationEvents, AppPlugin } from '@dxos/app-toolkit';\nimport { Annotation } from '@dxos/echo';\nimport { Operation } from '@dxos/operation';\nimport { AttentionEvents } from '@dxos/plugin-attention/types';\nimport { ClientEvents } from '@dxos/plugin-client/types';\nimport {
|
|
5
|
-
"mappings": ";;;;;;;;;;;;AAKA,SAASA,YAAY;AAEd,IAAMC,OAAoB;EAC/BC,IAAI;EACJC,MAAM;EACNC,aAAaC;;;;EAIbC,MAAM;EACNC,SAAS;EACTC,QAAQ;EACRC,MAAM;IAAC;;AACT;;;ACdA,SAASC,2BAA2B;AAK7B,IAAMC,kBAAkBC,oBAAoBC,KACjD,MAAM,OAAO,oCAAA,GACb,MAAM,OAAO,qBAAA,CAAA;;;ACPf,YAAYC,YAAY;AACxB,YAAYC,YAAY;AAExB,SAASC,iBAAiBC,cAAc;AACxC,SAASC,qBAAqBC,iBAAiB;AAC/C,SAASC,kBAAkB;AAC3B,SAASC,iBAAiB;AAC1B,SAASC,uBAAuB;AAChC,SAASC,oBAAoB;
|
|
6
|
-
"names": ["trim", "meta", "id", "name", "description", "trim", "icon", "iconHue", "source", "tags", "OperationHandlerSet", "YouTubeHandlers", "OperationHandlerSet", "lazy", "Effect", "Option", "ActivationEvent", "Plugin", "AppActivationEvents", "AppPlugin", "Annotation", "Operation", "AttentionEvents", "ClientEvents", "SpaceOperation", "YouTubeBlueprint", "
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport { type Plugin } from '@dxos/app-framework';\nimport { trim } from '@dxos/util';\n\nexport const meta: Plugin.Meta = {\n id: 'org.dxos.plugin.youtube',\n name: 'YouTube',\n description: trim`\n YouTube channel subscription and video feed management.\n Sync videos from channels and access transcripts for analysis.\n `,\n icon: 'ph--youtube-logo--regular',\n iconHue: 'red',\n source: 'https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-youtube',\n tags: ['labs'],\n};\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { OperationHandlerSet } from '@dxos/operation';\n\nexport { YouTube } from './apis';\nexport * from './definitions';\n\nexport const YouTubeHandlers = OperationHandlerSet.lazy(\n () => import('./clear-synced-videos'),\n () => import('./sync'),\n);\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport * as Option from 'effect/Option';\n\nimport { ActivationEvent, Plugin } from '@dxos/app-framework';\nimport { AppActivationEvents, AppPlugin } from '@dxos/app-toolkit';\nimport { Annotation } from '@dxos/echo';\nimport { Operation } from '@dxos/operation';\nimport { AttentionEvents } from '@dxos/plugin-attention/types';\nimport { ClientEvents } from '@dxos/plugin-client/types';\nimport { SpaceOperation } from '@dxos/plugin-space/operations';\nimport { type CreateObject } from '@dxos/plugin-space/types';\n\nimport { YouTubeBlueprint } from '#blueprints';\nimport { AppGraphBuilder, BlueprintDefinition, Migrations, ReactSurface } from '#capabilities';\nimport { meta } from '#meta';\nimport { Channel, Video } from '#types';\n\nimport { translations } from './translations';\n\nexport const YouTubePlugin = Plugin.define(meta).pipe(\n AppPlugin.addAppGraphModule({\n activatesOn: ActivationEvent.allOf(AppActivationEvents.SetupAppGraph, AttentionEvents.AttentionReady),\n activate: AppGraphBuilder,\n }),\n AppPlugin.addBlueprintDefinitionModule({ activate: BlueprintDefinition }),\n AppPlugin.addMetadataModule({\n metadata: [\n {\n id: Channel.YouTubeChannel.typename,\n metadata: {\n label: (object: Channel.YouTubeChannel) => object.name ?? object.channelUrl ?? object.channelId ?? 'YouTube',\n icon: Annotation.IconAnnotation.get(Channel.YouTubeChannel).pipe(Option.getOrThrow).icon,\n iconHue: Annotation.IconAnnotation.get(Channel.YouTubeChannel).pipe(Option.getOrThrow).hue ?? 'white',\n blueprints: [YouTubeBlueprint.key],\n inputSchema: Channel.CreateYouTubeChannelSchema,\n createObject: ((props, options) =>\n Effect.gen(function* () {\n const object = Channel.make(props);\n return yield* Operation.invoke(SpaceOperation.AddObject, {\n object,\n target: options.target,\n hidden: true,\n targetNodeId: options.targetNodeId,\n });\n })) satisfies CreateObject,\n },\n },\n {\n id: Video.YouTubeVideo.typename,\n metadata: {\n label: (object: Video.YouTubeVideo) => object.title,\n icon: Annotation.IconAnnotation.get(Video.YouTubeVideo).pipe(Option.getOrThrow).icon,\n iconHue: Annotation.IconAnnotation.get(Video.YouTubeVideo).pipe(Option.getOrThrow).hue ?? 'white',\n },\n },\n ],\n }),\n AppPlugin.addSchemaModule({\n schema: [Channel.YouTubeChannel, Video.YouTubeVideo],\n }),\n AppPlugin.addSurfaceModule({ activate: ReactSurface }),\n AppPlugin.addTranslationsModule({ translations }),\n Plugin.addModule({\n activatesOn: ClientEvents.SetupMigration,\n activate: Migrations,\n }),\n Plugin.make,\n);\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { type Resource } from '@dxos/react-ui';\n\nimport { meta } from '#meta';\nimport { Channel, Video } from '#types';\n\nexport const translations = [\n {\n 'en-US': {\n [Channel.YouTubeChannel.typename]: {\n 'typename.label': 'YouTube Channel',\n 'typename.label_zero': 'YouTube Channels',\n 'typename.label_one': 'YouTube Channel',\n 'typename.label_other': 'YouTube Channels',\n 'object-name.placeholder': 'New YouTube channel',\n 'add-object.label': 'Add YouTube channel',\n 'rename-object.label': 'Rename YouTube channel',\n 'delete-object.label': 'Delete YouTube channel',\n 'object-deleted.label': 'YouTube channel deleted',\n },\n [Video.YouTubeVideo.typename]: {\n 'typename.label': 'Video',\n 'typename.label_zero': 'Videos',\n 'typename.label_one': 'Video',\n 'typename.label_other': 'Videos',\n 'object-name.placeholder': 'New video',\n 'add-object.label': 'Add video',\n 'rename-object.label': 'Rename video',\n 'delete-object.label': 'Delete video',\n 'object-deleted.label': 'Video deleted',\n },\n [meta.id]: {\n 'plugin.name': 'YouTube',\n 'channel.label': 'YouTube Channel',\n 'video.label': 'Video',\n 'sync-channel.label': 'Sync channel',\n 'sync-channel-error.title': 'Failed to sync channel',\n 'clear-synced-videos.label': 'Clear synced videos',\n 'clear-synced-videos-error.title': 'Failed to clear synced videos',\n 'close.label': 'Close',\n 'new-channel.label': 'New YouTube Channel',\n 'empty-channel.message': 'No videos synced yet',\n 'no-video.message': 'Select a video to view it',\n\n 'channel-account.label': 'Account',\n 'channel-account.placeholder': 'Select account...',\n 'channel-sync.label': 'Channel Sync',\n\n 'enable-background-sync.label': 'Enable background sync',\n 'enabling-background-sync.label': 'Enabling...',\n 'disable-background-sync.label': 'Disable background sync',\n 'view-trigger.label': 'View trigger',\n },\n },\n },\n] as const satisfies Resource[];\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["trim", "meta", "id", "name", "description", "trim", "icon", "iconHue", "source", "tags", "OperationHandlerSet", "YouTubeHandlers", "OperationHandlerSet", "lazy", "Effect", "Option", "ActivationEvent", "Plugin", "AppActivationEvents", "AppPlugin", "Annotation", "Operation", "AttentionEvents", "ClientEvents", "SpaceOperation", "YouTubeBlueprint", "AppGraphBuilder", "BlueprintDefinition", "Migrations", "ReactSurface", "meta", "Channel", "Video", "meta", "Channel", "Video", "translations", "Channel", "YouTubeChannel", "typename", "Video", "YouTubeVideo", "meta", "id", "YouTubePlugin", "Plugin", "define", "meta", "pipe", "AppPlugin", "addAppGraphModule", "activatesOn", "ActivationEvent", "allOf", "AppActivationEvents", "SetupAppGraph", "AttentionEvents", "AttentionReady", "activate", "AppGraphBuilder", "addBlueprintDefinitionModule", "BlueprintDefinition", "addMetadataModule", "metadata", "id", "Channel", "YouTubeChannel", "typename", "label", "object", "name", "channelUrl", "channelId", "icon", "Annotation", "IconAnnotation", "get", "getOrThrow", "iconHue", "hue", "blueprints", "YouTubeBlueprint", "key", "inputSchema", "CreateYouTubeChannelSchema", "createObject", "props", "options", "gen", "make", "Operation", "invoke", "SpaceOperation", "AddObject", "target", "hidden", "targetNodeId", "Video", "YouTubeVideo", "title", "addSchemaModule", "schema", "addSurfaceModule", "ReactSurface", "addTranslationsModule", "translations", "addModule", "ClientEvents", "SetupMigration", "Migrations"]
|
|
7
7
|
}
|