@dxos/plugin-youtube 0.8.3 → 0.8.4-main.74a063c4e0
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/blueprints/index.mjs +49 -8
- package/dist/lib/browser/blueprints/index.mjs.map +4 -4
- package/dist/lib/browser/{chunk-C26XKDK2.mjs → chunk-FEQD5TPI.mjs} +4 -4
- package/dist/lib/browser/{chunk-C26XKDK2.mjs.map → chunk-FEQD5TPI.mjs.map} +2 -2
- package/dist/lib/browser/{chunk-P67QEKBQ.mjs → chunk-GIRFSTHR.mjs} +6 -8
- package/dist/lib/browser/chunk-GIRFSTHR.mjs.map +7 -0
- package/dist/lib/browser/chunk-GTIWG45H.mjs +157 -0
- package/dist/lib/browser/chunk-GTIWG45H.mjs.map +7 -0
- package/dist/lib/browser/{clear-synced-videos-EVMJIZPD.mjs → clear-synced-videos-NZIWAVGL.mjs} +7 -8
- package/dist/lib/browser/clear-synced-videos-NZIWAVGL.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +86 -81
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{sync-423Q4BDD.mjs → sync-T34US6NO.mjs} +18 -19
- package/dist/lib/browser/sync-T34US6NO.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +2 -4
- package/dist/lib/node-esm/blueprints/index.mjs +49 -8
- package/dist/lib/node-esm/blueprints/index.mjs.map +4 -4
- package/dist/lib/node-esm/{chunk-JSGRZMG3.mjs → chunk-A3SKNJFU.mjs} +6 -8
- package/dist/lib/node-esm/chunk-A3SKNJFU.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-JM5SBBP5.mjs → chunk-Q3TVMR5B.mjs} +4 -4
- package/dist/lib/node-esm/{chunk-JM5SBBP5.mjs.map → chunk-Q3TVMR5B.mjs.map} +2 -2
- package/dist/lib/node-esm/chunk-YOE54ALJ.mjs +158 -0
- package/dist/lib/node-esm/chunk-YOE54ALJ.mjs.map +7 -0
- package/dist/lib/node-esm/{clear-synced-videos-5UCH6XHL.mjs → clear-synced-videos-ZX7KBPGS.mjs} +7 -8
- package/dist/lib/{browser/clear-synced-videos-EVMJIZPD.mjs.map → node-esm/clear-synced-videos-ZX7KBPGS.mjs.map} +2 -2
- package/dist/lib/node-esm/index.mjs +86 -81
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/{sync-CEF5DX2J.mjs → sync-RQYQ5LII.mjs} +18 -19
- package/dist/lib/node-esm/sync-RQYQ5LII.mjs.map +7 -0
- package/dist/lib/node-esm/types/index.mjs +2 -4
- package/dist/types/src/YouTubePlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -0
- package/dist/types/src/capabilities/blueprint-definition.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +5 -3
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/migrations.d.ts +5 -0
- package/dist/types/src/capabilities/migrations.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
- 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/ChannelSettings/index.d.ts +1 -2
- package/dist/types/src/containers/ChannelSettings/index.d.ts.map +1 -1
- package/dist/types/src/containers/VideoArticle/VideoArticle.d.ts +2 -7
- 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/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 +9 -5
- 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/src/translations.d.ts +38 -38
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/Channel.d.ts +28 -3
- package/dist/types/src/types/Channel.d.ts.map +1 -1
- package/dist/types/src/types/Video.d.ts +22 -0
- package/dist/types/src/types/Video.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +48 -40
- package/src/YouTubePlugin.tsx +12 -6
- package/src/blueprints/youtube.ts +1 -1
- package/src/capabilities/{app-graph-builder/app-graph-builder.ts → app-graph-builder.ts} +29 -33
- package/src/capabilities/{blueprint-definition/blueprint-definition.ts → blueprint-definition.ts} +1 -1
- package/src/capabilities/index.ts +7 -4
- package/src/capabilities/migrations.ts +35 -0
- package/src/capabilities/{react-surface/react-surface.tsx → react-surface.tsx} +16 -18
- package/src/containers/ChannelArticle/ChannelArticle.tsx +1 -1
- package/src/containers/ChannelArticle/index.ts +1 -3
- package/src/containers/ChannelSettings/index.ts +1 -3
- package/src/containers/VideoArticle/VideoArticle.tsx +2 -7
- 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/operations/apis/youtube/api.ts +0 -1
- package/src/operations/clear-synced-videos.ts +0 -1
- package/src/operations/definitions.ts +3 -3
- package/src/operations/sync.ts +0 -1
- package/src/translations.ts +38 -38
- package/src/types/Channel.ts +18 -1
- package/src/types/Video.ts +25 -1
- package/dist/lib/browser/ChannelArticle-CDQR4BBY.mjs +0 -90
- package/dist/lib/browser/ChannelArticle-CDQR4BBY.mjs.map +0 -7
- package/dist/lib/browser/ChannelSettings-ZYUNW3VS.mjs +0 -28
- package/dist/lib/browser/ChannelSettings-ZYUNW3VS.mjs.map +0 -7
- package/dist/lib/browser/VideoArticle-FC4A6E7B.mjs +0 -76
- package/dist/lib/browser/VideoArticle-FC4A6E7B.mjs.map +0 -7
- package/dist/lib/browser/VideoCard-CCPXDCB7.mjs +0 -64
- package/dist/lib/browser/VideoCard-CCPXDCB7.mjs.map +0 -7
- package/dist/lib/browser/app-graph-builder-MJY6A6SN.mjs +0 -195
- package/dist/lib/browser/app-graph-builder-MJY6A6SN.mjs.map +0 -7
- package/dist/lib/browser/blueprint-definition-FRYUYJ22.mjs +0 -22
- package/dist/lib/browser/blueprint-definition-FRYUYJ22.mjs.map +0 -7
- package/dist/lib/browser/chunk-DFRSBBSO.mjs +0 -21
- package/dist/lib/browser/chunk-DFRSBBSO.mjs.map +0 -7
- package/dist/lib/browser/chunk-GFRR4TTX.mjs +0 -72
- package/dist/lib/browser/chunk-GFRR4TTX.mjs.map +0 -7
- package/dist/lib/browser/chunk-MUE22YUM.mjs +0 -57
- package/dist/lib/browser/chunk-MUE22YUM.mjs.map +0 -7
- package/dist/lib/browser/chunk-P67QEKBQ.mjs.map +0 -7
- package/dist/lib/browser/chunk-YMDT37TA.mjs +0 -62
- package/dist/lib/browser/chunk-YMDT37TA.mjs.map +0 -7
- package/dist/lib/browser/chunk-Z3DGTMKC.mjs +0 -8
- package/dist/lib/browser/chunk-Z3DGTMKC.mjs.map +0 -7
- package/dist/lib/browser/react-surface-EDA5VYDC.mjs +0 -77
- package/dist/lib/browser/react-surface-EDA5VYDC.mjs.map +0 -7
- package/dist/lib/browser/sync-423Q4BDD.mjs.map +0 -7
- package/dist/lib/node-esm/ChannelArticle-GQ64BO7V.mjs +0 -91
- package/dist/lib/node-esm/ChannelArticle-GQ64BO7V.mjs.map +0 -7
- package/dist/lib/node-esm/ChannelSettings-DM2HWNKO.mjs +0 -29
- package/dist/lib/node-esm/ChannelSettings-DM2HWNKO.mjs.map +0 -7
- package/dist/lib/node-esm/VideoArticle-WLTWZO3K.mjs +0 -77
- package/dist/lib/node-esm/VideoArticle-WLTWZO3K.mjs.map +0 -7
- package/dist/lib/node-esm/VideoCard-FOWQZK75.mjs +0 -65
- package/dist/lib/node-esm/VideoCard-FOWQZK75.mjs.map +0 -7
- package/dist/lib/node-esm/app-graph-builder-IU5TBAXN.mjs +0 -196
- package/dist/lib/node-esm/app-graph-builder-IU5TBAXN.mjs.map +0 -7
- package/dist/lib/node-esm/blueprint-definition-W264MZ3D.mjs +0 -23
- package/dist/lib/node-esm/blueprint-definition-W264MZ3D.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-5KNC2JMP.mjs +0 -58
- package/dist/lib/node-esm/chunk-5KNC2JMP.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-6BUJ2DQX.mjs +0 -73
- package/dist/lib/node-esm/chunk-6BUJ2DQX.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-CX6MV3QM.mjs +0 -23
- package/dist/lib/node-esm/chunk-CX6MV3QM.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-CZSLL3XQ.mjs +0 -63
- package/dist/lib/node-esm/chunk-CZSLL3XQ.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-JSGRZMG3.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-M4S6BE47.mjs +0 -10
- package/dist/lib/node-esm/chunk-M4S6BE47.mjs.map +0 -7
- package/dist/lib/node-esm/clear-synced-videos-5UCH6XHL.mjs.map +0 -7
- package/dist/lib/node-esm/react-surface-5DJAQPHJ.mjs +0 -78
- package/dist/lib/node-esm/react-surface-5DJAQPHJ.mjs.map +0 -7
- package/dist/lib/node-esm/sync-CEF5DX2J.mjs.map +0 -7
- package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts.map +0 -1
- package/dist/types/src/capabilities/app-graph-builder/index.d.ts +0 -3
- package/dist/types/src/capabilities/app-graph-builder/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/blueprint-definition/blueprint-definition.d.ts.map +0 -1
- package/dist/types/src/capabilities/blueprint-definition/index.d.ts +0 -3
- package/dist/types/src/capabilities/blueprint-definition/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/react-surface/index.d.ts +0 -3
- package/dist/types/src/capabilities/react-surface/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +0 -1
- package/src/capabilities/app-graph-builder/index.ts +0 -7
- package/src/capabilities/blueprint-definition/index.ts +0 -7
- package/src/capabilities/react-surface/index.ts +0 -7
- /package/dist/types/src/capabilities/{app-graph-builder/app-graph-builder.d.ts → app-graph-builder.d.ts} +0 -0
- /package/dist/types/src/capabilities/{blueprint-definition/blueprint-definition.d.ts → blueprint-definition.d.ts} +0 -0
- /package/dist/types/src/capabilities/{react-surface/react-surface.d.ts → react-surface.d.ts} +0 -0
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/capabilities/react-surface/react-surface.tsx", "../../../src/containers/index.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport React from 'react';\n\nimport { Capabilities, Capability } from '@dxos/app-framework';\nimport { Surface } from '@dxos/app-framework/ui';\n\nimport { ChannelArticle, ChannelSettings, VideoArticle, VideoCard } from '../../containers';\nimport { meta } from '../../meta';\nimport { Channel, Video } from '../../types';\n\nexport default Capability.makeModule(() =>\n Effect.succeed(\n Capability.contributes(Capabilities.ReactSurface, [\n Surface.create({\n id: `${meta.id}.channel`,\n role: ['article'],\n filter: (data): data is { attendableId?: string; subject: Channel.YouTubeChannel } =>\n Channel.instanceOf(data.subject),\n component: ({ data }) => {\n return <ChannelArticle subject={data.subject} attendableId={data.attendableId} />;\n },\n }),\n Surface.create({\n id: `${meta.id}.video`,\n role: ['article', 'section'],\n filter: (\n data,\n ): data is { attendableId: string; subject: Video.YouTubeVideo; companionTo: Channel.YouTubeChannel } =>\n typeof data.attendableId === 'string' &&\n Video.instanceOf(data.subject) &&\n Channel.instanceOf(data.companionTo),\n component: ({ data: { attendableId, companionTo, subject }, role }) => {\n return <VideoArticle role={role} subject={subject} channel={companionTo} attendableId={attendableId} />;\n },\n }),\n Surface.create({\n id: `${meta.id}.video-card`,\n role: 'card--content',\n filter: (data): data is { subject: Video.YouTubeVideo } => Video.instanceOf(data?.subject),\n component: ({ data: { subject }, role }) => <VideoCard subject={subject} role={role} />,\n }),\n Surface.create({\n id: `${meta.id}.channel.companion.settings`,\n role: 'object-settings',\n filter: (data): data is { subject: Channel.YouTubeChannel } => Channel.instanceOf(data.subject),\n component: ({ data }) => <ChannelSettings subject={data.subject} />,\n }),\n ]),\n ),\n);\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { type ComponentType, lazy, type LazyExoticComponent } from 'react';\n\nimport type { ChannelArticleProps } from './ChannelArticle/ChannelArticle';\nimport type { ChannelSettingsProps } from './ChannelSettings/ChannelSettings';\nimport type { VideoArticleProps } from './VideoArticle/VideoArticle';\nimport type { VideoCardProps } from './VideoCard/VideoCard';\n\nexport type { ChannelArticleProps, ChannelSettingsProps, VideoArticleProps, VideoCardProps };\n\nexport const ChannelArticle: LazyExoticComponent<ComponentType<ChannelArticleProps>> = lazy(\n () => import('./ChannelArticle'),\n);\n\nexport const ChannelSettings: LazyExoticComponent<ComponentType<ChannelSettingsProps>> = lazy(\n () => import('./ChannelSettings'),\n);\n\nexport const VideoArticle: LazyExoticComponent<ComponentType<VideoArticleProps>> = lazy(() => import('./VideoArticle'));\n\nexport const VideoCard: LazyExoticComponent<ComponentType<VideoCardProps>> = lazy(() => import('./VideoCard'));\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;AAIA,YAAYA,YAAY;AACxB,OAAOC,WAAW;AAElB,SAASC,cAAcC,kBAAkB;AACzC,SAASC,eAAe;;;ACJxB,SAA6BC,YAAsC;AAS5D,IAAMC,iBAA0EC,KACrF,MAAM,OAAO,+BAAA,CAAA;AAGR,IAAMC,kBAA4ED,KACvF,MAAM,OAAO,gCAAA,CAAA;AAGR,IAAME,eAAsEF,KAAK,MAAM,OAAO,6BAAA,CAAA;AAE9F,IAAMG,YAAgEH,KAAK,MAAM,OAAO,0BAAA,CAAA;;;ADT/F,IAAA,wBAAeI,WAAWC,WAAW,MAC5BC,eACLF,WAAWG,YAAYC,aAAaC,cAAc;EAChDC,QAAQC,OAAO;IACbC,IAAI,GAAGC,KAAKD,EAAE;IACdE,MAAM;MAAC;;IACPC,QAAQ,CAACC,SACPC,gBAAQC,WAAWF,KAAKG,OAAO;IACjCC,WAAW,CAAC,EAAEJ,KAAI,MAAE;AAClB,aAAO,sBAAA,cAACK,gBAAAA;QAAeF,SAASH,KAAKG;QAASG,cAAcN,KAAKM;;IACnE;EACF,CAAA;EACAZ,QAAQC,OAAO;IACbC,IAAI,GAAGC,KAAKD,EAAE;IACdE,MAAM;MAAC;MAAW;;IAClBC,QAAQ,CACNC,SAEA,OAAOA,KAAKM,iBAAiB,YAC7BC,cAAML,WAAWF,KAAKG,OAAO,KAC7BF,gBAAQC,WAAWF,KAAKQ,WAAW;IACrCJ,WAAW,CAAC,EAAEJ,MAAM,EAAEM,cAAcE,aAAaL,QAAO,GAAIL,KAAI,MAAE;AAChE,aAAO,sBAAA,cAACW,cAAAA;QAAaX;QAAYK;QAAkBO,SAASF;QAAaF;;IAC3E;EACF,CAAA;EACAZ,QAAQC,OAAO;IACbC,IAAI,GAAGC,KAAKD,EAAE;IACdE,MAAM;IACNC,QAAQ,CAACC,SAAkDO,cAAML,WAAWF,MAAMG,OAAAA;IAClFC,WAAW,CAAC,EAAEJ,MAAM,EAAEG,QAAO,GAAIL,KAAI,MAAO,sBAAA,cAACa,WAAAA;MAAUR;MAAkBL;;EAC3E,CAAA;EACAJ,QAAQC,OAAO;IACbC,IAAI,GAAGC,KAAKD,EAAE;IACdE,MAAM;IACNC,QAAQ,CAACC,SAAsDC,gBAAQC,WAAWF,KAAKG,OAAO;IAC9FC,WAAW,CAAC,EAAEJ,KAAI,MAAO,sBAAA,cAACY,iBAAAA;MAAgBT,SAASH,KAAKG;;EAC1D,CAAA;CACD,CAAA,CAAA;",
|
|
6
|
-
"names": ["Effect", "React", "Capabilities", "Capability", "Surface", "lazy", "ChannelArticle", "lazy", "ChannelSettings", "VideoArticle", "VideoCard", "Capability", "makeModule", "succeed", "contributes", "Capabilities", "ReactSurface", "Surface", "create", "id", "meta", "role", "filter", "data", "Channel", "instanceOf", "subject", "component", "ChannelArticle", "attendableId", "Video", "companionTo", "VideoArticle", "channel", "VideoCard", "ChannelSettings"]
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
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';\n\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
|
-
}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
|
|
2
|
-
import {
|
|
3
|
-
YouTubeVideo
|
|
4
|
-
} from "./chunk-CZSLL3XQ.mjs";
|
|
5
|
-
import "./chunk-HSLMI22Q.mjs";
|
|
6
|
-
|
|
7
|
-
// src/containers/ChannelArticle/ChannelArticle.tsx
|
|
8
|
-
import React, { useMemo } from "react";
|
|
9
|
-
import { Obj, Query } from "@dxos/echo";
|
|
10
|
-
import { Filter, useObject, useQuery } from "@dxos/react-client/echo";
|
|
11
|
-
import { Icon, Panel } from "@dxos/react-ui";
|
|
12
|
-
var ChannelArticle = ({ subject: channel }) => {
|
|
13
|
-
useObject(channel);
|
|
14
|
-
const feed = channel.feed?.target;
|
|
15
|
-
const db = Obj.getDatabase(channel);
|
|
16
|
-
const videos = useQuery(db, feed ? Query.select(Filter.type(YouTubeVideo)).from(feed) : Query.select(Filter.nothing()));
|
|
17
|
-
const sortedVideos = useMemo(() => [
|
|
18
|
-
...videos
|
|
19
|
-
].sort((videoA, videoB) => new Date(videoB.publishedAt).getTime() - new Date(videoA.publishedAt).getTime()), [
|
|
20
|
-
videos
|
|
21
|
-
]);
|
|
22
|
-
return /* @__PURE__ */ React.createElement(Panel.Root, null, /* @__PURE__ */ React.createElement(Panel.Content, {
|
|
23
|
-
className: "overflow-auto"
|
|
24
|
-
}, /* @__PURE__ */ React.createElement("div", {
|
|
25
|
-
className: "flex flex-col gap-4 p-4"
|
|
26
|
-
}, /* @__PURE__ */ React.createElement("div", {
|
|
27
|
-
className: "flex items-center justify-between"
|
|
28
|
-
}, /* @__PURE__ */ React.createElement("div", {
|
|
29
|
-
className: "flex items-center gap-2"
|
|
30
|
-
}, /* @__PURE__ */ React.createElement(Icon, {
|
|
31
|
-
icon: "ph--youtube-logo--regular",
|
|
32
|
-
size: 6
|
|
33
|
-
}), /* @__PURE__ */ React.createElement("h2", {
|
|
34
|
-
className: "text-lg font-semibold"
|
|
35
|
-
}, channel.name ?? "YouTube Channel")), channel.lastSyncedAt && /* @__PURE__ */ React.createElement("span", {
|
|
36
|
-
className: "text-xs text-description"
|
|
37
|
-
}, "Last synced: ", new Date(channel.lastSyncedAt).toLocaleString())), channel.channelUrl && /* @__PURE__ */ React.createElement("div", {
|
|
38
|
-
className: "text-sm text-description"
|
|
39
|
-
}, /* @__PURE__ */ React.createElement("a", {
|
|
40
|
-
href: channel.channelUrl.startsWith("http") ? channel.channelUrl : `https://www.youtube.com/@${channel.channelUrl}`,
|
|
41
|
-
target: "_blank",
|
|
42
|
-
rel: "noopener noreferrer",
|
|
43
|
-
className: "hover:underline"
|
|
44
|
-
}, channel.channelUrl)), /* @__PURE__ */ React.createElement("div", {
|
|
45
|
-
className: "flex flex-col gap-2"
|
|
46
|
-
}, /* @__PURE__ */ React.createElement("h3", {
|
|
47
|
-
className: "text-md font-medium"
|
|
48
|
-
}, "Videos (", sortedVideos.length, ")"), /* @__PURE__ */ React.createElement("div", {
|
|
49
|
-
className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"
|
|
50
|
-
}, sortedVideos.map((video) => /* @__PURE__ */ React.createElement("div", {
|
|
51
|
-
key: video.videoId,
|
|
52
|
-
className: "flex flex-col gap-2 p-2 rounded hover:bg-surface-hover"
|
|
53
|
-
}, video.thumbnailUrl ? /* @__PURE__ */ React.createElement("a", {
|
|
54
|
-
href: video.url,
|
|
55
|
-
target: "_blank",
|
|
56
|
-
rel: "noopener noreferrer",
|
|
57
|
-
className: "relative aspect-video group"
|
|
58
|
-
}, /* @__PURE__ */ React.createElement("img", {
|
|
59
|
-
src: video.thumbnailUrl,
|
|
60
|
-
alt: video.title,
|
|
61
|
-
className: "h-full w-full object-cover rounded"
|
|
62
|
-
}), /* @__PURE__ */ React.createElement("div", {
|
|
63
|
-
className: "absolute inset-0 flex items-center justify-center bg-black/0 group-hover:bg-black/30 rounded transition-colors"
|
|
64
|
-
}, /* @__PURE__ */ React.createElement("div", {
|
|
65
|
-
className: "opacity-0 group-hover:opacity-100 bg-red-600 text-white rounded-full p-2 transition-opacity"
|
|
66
|
-
}, /* @__PURE__ */ React.createElement(Icon, {
|
|
67
|
-
icon: "ph--play--fill",
|
|
68
|
-
size: 4
|
|
69
|
-
})))) : /* @__PURE__ */ React.createElement("div", {
|
|
70
|
-
className: "aspect-video bg-surface-hover rounded flex items-center justify-center"
|
|
71
|
-
}, /* @__PURE__ */ React.createElement(Icon, {
|
|
72
|
-
icon: "ph--video--regular",
|
|
73
|
-
size: 8
|
|
74
|
-
})), /* @__PURE__ */ React.createElement("div", {
|
|
75
|
-
className: "flex flex-col gap-1"
|
|
76
|
-
}, /* @__PURE__ */ React.createElement("span", {
|
|
77
|
-
className: "font-medium line-clamp-2",
|
|
78
|
-
title: video.title
|
|
79
|
-
}, video.title), /* @__PURE__ */ React.createElement("span", {
|
|
80
|
-
className: "text-xs text-description"
|
|
81
|
-
}, new Date(video.publishedAt).toLocaleDateString(), video.transcript && " \u2022 Transcript available")))), sortedVideos.length === 0 && /* @__PURE__ */ React.createElement("div", {
|
|
82
|
-
className: "col-span-full text-sm text-description p-4 text-center"
|
|
83
|
-
}, "No videos synced yet. Sync the channel to fetch videos."))))));
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
// src/containers/ChannelArticle/index.ts
|
|
87
|
-
var ChannelArticle_default = ChannelArticle;
|
|
88
|
-
export {
|
|
89
|
-
ChannelArticle_default as default
|
|
90
|
-
};
|
|
91
|
-
//# sourceMappingURL=ChannelArticle-GQ64BO7V.mjs.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/containers/ChannelArticle/ChannelArticle.tsx", "../../../src/containers/ChannelArticle/index.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport React, { useMemo } from 'react';\n\nimport { type Feed, Obj, Query } from '@dxos/echo';\nimport { Filter, useObject, useQuery } from '@dxos/react-client/echo';\nimport { Icon, Panel } from '@dxos/react-ui';\n\nimport * as Channel from '../../types/Channel';\nimport * as Video from '../../types/Video';\n\nexport type ChannelArticleProps = {\n subject: Channel.YouTubeChannel;\n attendableId?: string;\n};\n\nexport const ChannelArticle = ({ subject: channel }: ChannelArticleProps) => {\n useObject(channel);\n const feed = channel.feed?.target as Feed.Feed | undefined;\n const db = Obj.getDatabase(channel);\n const videos = useQuery(\n db,\n feed ? Query.select(Filter.type(Video.YouTubeVideo)).from(feed) : Query.select(Filter.nothing()),\n ) as Video.YouTubeVideo[];\n\n const sortedVideos = useMemo(\n () =>\n [...videos].sort(\n (videoA, videoB) => new Date(videoB.publishedAt).getTime() - new Date(videoA.publishedAt).getTime(),\n ),\n [videos],\n );\n\n return (\n <Panel.Root>\n <Panel.Content className='overflow-auto'>\n <div className='flex flex-col gap-4 p-4'>\n <div className='flex items-center justify-between'>\n <div className='flex items-center gap-2'>\n <Icon icon='ph--youtube-logo--regular' size={6} />\n <h2 className='text-lg font-semibold'>{channel.name ?? 'YouTube Channel'}</h2>\n </div>\n {channel.lastSyncedAt && (\n <span className='text-xs text-description'>\n Last synced: {new Date(channel.lastSyncedAt).toLocaleString()}\n </span>\n )}\n </div>\n\n {channel.channelUrl && (\n <div className='text-sm text-description'>\n <a\n href={\n channel.channelUrl.startsWith('http')\n ? channel.channelUrl\n : `https://www.youtube.com/@${channel.channelUrl}`\n }\n target='_blank'\n rel='noopener noreferrer'\n className='hover:underline'\n >\n {channel.channelUrl}\n </a>\n </div>\n )}\n\n <div className='flex flex-col gap-2'>\n <h3 className='text-md font-medium'>Videos ({sortedVideos.length})</h3>\n <div className='grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4'>\n {sortedVideos.map((video) => (\n <div key={video.videoId} className='flex flex-col gap-2 p-2 rounded hover:bg-surface-hover'>\n {video.thumbnailUrl ? (\n <a\n href={video.url}\n target='_blank'\n rel='noopener noreferrer'\n className='relative aspect-video group'\n >\n <img src={video.thumbnailUrl} alt={video.title} className='h-full w-full object-cover rounded' />\n <div className='absolute inset-0 flex items-center justify-center bg-black/0 group-hover:bg-black/30 rounded transition-colors'>\n <div className='opacity-0 group-hover:opacity-100 bg-red-600 text-white rounded-full p-2 transition-opacity'>\n <Icon icon='ph--play--fill' size={4} />\n </div>\n </div>\n </a>\n ) : (\n <div className='aspect-video bg-surface-hover rounded flex items-center justify-center'>\n <Icon icon='ph--video--regular' size={8} />\n </div>\n )}\n <div className='flex flex-col gap-1'>\n <span className='font-medium line-clamp-2' title={video.title}>\n {video.title}\n </span>\n <span className='text-xs text-description'>\n {new Date(video.publishedAt).toLocaleDateString()}\n {video.transcript && ' • Transcript available'}\n </span>\n </div>\n </div>\n ))}\n {sortedVideos.length === 0 && (\n <div className='col-span-full text-sm text-description p-4 text-center'>\n No videos synced yet. Sync the channel to fetch videos.\n </div>\n )}\n </div>\n </div>\n </div>\n </Panel.Content>\n </Panel.Root>\n );\n};\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { ChannelArticle } from './ChannelArticle';\n\nexport default ChannelArticle;\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["React", "useMemo", "Obj", "Query", "Filter", "useObject", "useQuery", "Icon", "Panel", "ChannelArticle", "subject", "channel", "useObject", "feed", "target", "db", "Obj", "getDatabase", "videos", "useQuery", "Query", "select", "Filter", "type", "YouTubeVideo", "from", "nothing", "sortedVideos", "useMemo", "sort", "videoA", "videoB", "Date", "publishedAt", "getTime", "Panel", "Root", "Content", "className", "div", "Icon", "icon", "size", "h2", "name", "lastSyncedAt", "span", "toLocaleString", "channelUrl", "a", "href", "startsWith", "rel", "h3", "length", "map", "video", "key", "videoId", "thumbnailUrl", "url", "img", "src", "alt", "title", "toLocaleDateString", "transcript", "ChannelArticle"]
|
|
7
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
|
|
2
|
-
import "./chunk-HSLMI22Q.mjs";
|
|
3
|
-
|
|
4
|
-
// src/containers/ChannelSettings/ChannelSettings.tsx
|
|
5
|
-
import React from "react";
|
|
6
|
-
var ChannelSettings = ({ subject: channel }) => {
|
|
7
|
-
return /* @__PURE__ */ React.createElement("div", {
|
|
8
|
-
className: "flex flex-col gap-4 p-4"
|
|
9
|
-
}, /* @__PURE__ */ React.createElement("h3", {
|
|
10
|
-
className: "text-md font-medium"
|
|
11
|
-
}, "Channel Settings"), /* @__PURE__ */ React.createElement("div", {
|
|
12
|
-
className: "flex flex-col gap-2 text-sm"
|
|
13
|
-
}, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", {
|
|
14
|
-
className: "text-description"
|
|
15
|
-
}, "Name:"), " ", /* @__PURE__ */ React.createElement("span", null, channel.name ?? "Not set")), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", {
|
|
16
|
-
className: "text-description"
|
|
17
|
-
}, "Channel URL:"), " ", /* @__PURE__ */ React.createElement("span", null, channel.channelUrl ?? "Not set")), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", {
|
|
18
|
-
className: "text-description"
|
|
19
|
-
}, "Channel ID:"), " ", /* @__PURE__ */ React.createElement("span", null, channel.channelId ?? "Not set")), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", {
|
|
20
|
-
className: "text-description"
|
|
21
|
-
}, "Last Synced:"), " ", /* @__PURE__ */ React.createElement("span", null, channel.lastSyncedAt ? new Date(channel.lastSyncedAt).toLocaleString() : "Never"))));
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
// src/containers/ChannelSettings/index.ts
|
|
25
|
-
var ChannelSettings_default = ChannelSettings;
|
|
26
|
-
export {
|
|
27
|
-
ChannelSettings_default as default
|
|
28
|
-
};
|
|
29
|
-
//# sourceMappingURL=ChannelSettings-DM2HWNKO.mjs.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/containers/ChannelSettings/ChannelSettings.tsx", "../../../src/containers/ChannelSettings/index.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport React from 'react';\n\nimport * as Channel from '../../types/Channel';\n\nexport type ChannelSettingsProps = {\n subject: Channel.YouTubeChannel;\n};\n\nexport const ChannelSettings = ({ subject: channel }: ChannelSettingsProps) => {\n return (\n <div className='flex flex-col gap-4 p-4'>\n <h3 className='text-md font-medium'>Channel Settings</h3>\n <div className='flex flex-col gap-2 text-sm'>\n <div>\n <span className='text-description'>Name:</span> <span>{channel.name ?? 'Not set'}</span>\n </div>\n <div>\n <span className='text-description'>Channel URL:</span> <span>{channel.channelUrl ?? 'Not set'}</span>\n </div>\n <div>\n <span className='text-description'>Channel ID:</span> <span>{channel.channelId ?? 'Not set'}</span>\n </div>\n <div>\n <span className='text-description'>Last Synced:</span>{' '}\n <span>{channel.lastSyncedAt ? new Date(channel.lastSyncedAt).toLocaleString() : 'Never'}</span>\n </div>\n </div>\n </div>\n );\n};\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { ChannelSettings } from './ChannelSettings';\n\nexport default ChannelSettings;\n"],
|
|
5
|
-
"mappings": ";;;;AAIA,OAAOA,WAAW;AAQX,IAAMC,kBAAkB,CAAC,EAAEC,SAASC,QAAO,MAAwB;AACxE,SACE,sBAAA,cAACC,OAAAA;IAAIC,WAAU;KACb,sBAAA,cAACC,MAAAA;IAAGD,WAAU;KAAsB,kBAAA,GACpC,sBAAA,cAACD,OAAAA;IAAIC,WAAU;KACb,sBAAA,cAACD,OAAAA,MACC,sBAAA,cAACG,QAAAA;IAAKF,WAAU;KAAmB,OAAA,GAAY,KAAC,sBAAA,cAACE,QAAAA,MAAMJ,QAAQK,QAAQ,SAAA,CAAA,GAEzE,sBAAA,cAACJ,OAAAA,MACC,sBAAA,cAACG,QAAAA;IAAKF,WAAU;KAAmB,cAAA,GAAmB,KAAC,sBAAA,cAACE,QAAAA,MAAMJ,QAAQM,cAAc,SAAA,CAAA,GAEtF,sBAAA,cAACL,OAAAA,MACC,sBAAA,cAACG,QAAAA;IAAKF,WAAU;KAAmB,aAAA,GAAkB,KAAC,sBAAA,cAACE,QAAAA,MAAMJ,QAAQO,aAAa,SAAA,CAAA,GAEpF,sBAAA,cAACN,OAAAA,MACC,sBAAA,cAACG,QAAAA;IAAKF,WAAU;KAAmB,cAAA,GAAoB,KACvD,sBAAA,cAACE,QAAAA,MAAMJ,QAAQQ,eAAe,IAAIC,KAAKT,QAAQQ,YAAY,EAAEE,eAAc,IAAK,OAAA,CAAA,CAAA,CAAA;AAK1F;;;AC3BA,IAAA,0BAAeC;",
|
|
6
|
-
"names": ["React", "ChannelSettings", "subject", "channel", "div", "className", "h3", "span", "name", "channelUrl", "channelId", "lastSyncedAt", "Date", "toLocaleString", "ChannelSettings"]
|
|
7
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
|
|
2
|
-
import "./chunk-HSLMI22Q.mjs";
|
|
3
|
-
|
|
4
|
-
// src/containers/VideoArticle/VideoArticle.tsx
|
|
5
|
-
import React, { useState } from "react";
|
|
6
|
-
import { Icon, Panel } from "@dxos/react-ui";
|
|
7
|
-
var VideoArticle = ({ subject: video, role }) => {
|
|
8
|
-
const [showPlayer, setShowPlayer] = useState(false);
|
|
9
|
-
const publishedDate = new Date(video.publishedAt).toLocaleDateString();
|
|
10
|
-
const hasTranscript = Boolean(video.transcript);
|
|
11
|
-
const embedUrl = `https://www.youtube.com/embed/${video.videoId}`;
|
|
12
|
-
return /* @__PURE__ */ React.createElement(Panel.Root, null, /* @__PURE__ */ React.createElement(Panel.Content, {
|
|
13
|
-
className: role === "section" ? "overflow-auto" : ""
|
|
14
|
-
}, /* @__PURE__ */ React.createElement("div", {
|
|
15
|
-
className: "flex flex-col gap-4 p-4 max-w-4xl"
|
|
16
|
-
}, /* @__PURE__ */ React.createElement("div", {
|
|
17
|
-
className: "flex flex-col gap-2"
|
|
18
|
-
}, /* @__PURE__ */ React.createElement("h2", {
|
|
19
|
-
className: "text-lg font-semibold"
|
|
20
|
-
}, video.title), /* @__PURE__ */ React.createElement("div", {
|
|
21
|
-
className: "flex items-center gap-2 text-sm text-description"
|
|
22
|
-
}, /* @__PURE__ */ React.createElement("span", null, video.channelTitle), /* @__PURE__ */ React.createElement("span", null, "\u2022"), /* @__PURE__ */ React.createElement("span", null, publishedDate), video.viewCount !== void 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", null, "\u2022"), /* @__PURE__ */ React.createElement("span", null, video.viewCount.toLocaleString(), " views")), video.likeCount !== void 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", null, "\u2022"), /* @__PURE__ */ React.createElement("span", null, video.likeCount.toLocaleString(), " likes")))), /* @__PURE__ */ React.createElement("div", {
|
|
23
|
-
className: "aspect-video w-full max-w-2xl"
|
|
24
|
-
}, showPlayer ? /* @__PURE__ */ React.createElement("iframe", {
|
|
25
|
-
src: embedUrl,
|
|
26
|
-
title: video.title,
|
|
27
|
-
allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
|
|
28
|
-
allowFullScreen: true,
|
|
29
|
-
className: "h-full w-full rounded-lg"
|
|
30
|
-
}) : /* @__PURE__ */ React.createElement("button", {
|
|
31
|
-
type: "button",
|
|
32
|
-
onClick: () => setShowPlayer(true),
|
|
33
|
-
className: "relative h-full w-full group cursor-pointer"
|
|
34
|
-
}, video.thumbnailUrl ? /* @__PURE__ */ React.createElement("img", {
|
|
35
|
-
src: video.thumbnailUrl,
|
|
36
|
-
alt: video.title,
|
|
37
|
-
className: "h-full w-full object-cover rounded-lg"
|
|
38
|
-
}) : /* @__PURE__ */ React.createElement("div", {
|
|
39
|
-
className: "h-full w-full bg-surface-hover rounded-lg flex items-center justify-center"
|
|
40
|
-
}, /* @__PURE__ */ React.createElement(Icon, {
|
|
41
|
-
icon: "ph--play--fill",
|
|
42
|
-
size: 12
|
|
43
|
-
})), /* @__PURE__ */ React.createElement("div", {
|
|
44
|
-
className: "absolute inset-0 flex items-center justify-center bg-black/30 group-hover:bg-black/50 rounded-lg transition-colors"
|
|
45
|
-
}, /* @__PURE__ */ React.createElement("div", {
|
|
46
|
-
className: "bg-red-600 text-white rounded-full p-4 group-hover:scale-110 transition-transform"
|
|
47
|
-
}, /* @__PURE__ */ React.createElement(Icon, {
|
|
48
|
-
icon: "ph--play--fill",
|
|
49
|
-
size: 8
|
|
50
|
-
}))))), video.description && /* @__PURE__ */ React.createElement("div", {
|
|
51
|
-
className: "max-w-2xl"
|
|
52
|
-
}, /* @__PURE__ */ React.createElement("h3", {
|
|
53
|
-
className: "text-md font-medium mb-2"
|
|
54
|
-
}, "Description"), /* @__PURE__ */ React.createElement("p", {
|
|
55
|
-
className: "whitespace-pre-wrap text-sm"
|
|
56
|
-
}, video.description)), hasTranscript && /* @__PURE__ */ React.createElement("div", {
|
|
57
|
-
className: "max-w-2xl"
|
|
58
|
-
}, /* @__PURE__ */ React.createElement("h3", {
|
|
59
|
-
className: "text-md font-medium mb-2"
|
|
60
|
-
}, "Transcript"), /* @__PURE__ */ React.createElement("div", {
|
|
61
|
-
className: "bg-surface-input p-4 rounded-lg max-h-96 overflow-auto"
|
|
62
|
-
}, /* @__PURE__ */ React.createElement("p", {
|
|
63
|
-
className: "whitespace-pre-wrap text-sm"
|
|
64
|
-
}, video.transcript))), /* @__PURE__ */ React.createElement("a", {
|
|
65
|
-
href: video.url,
|
|
66
|
-
target: "_blank",
|
|
67
|
-
rel: "noopener noreferrer",
|
|
68
|
-
className: "inline-flex items-center gap-2 text-sm text-primary hover:underline"
|
|
69
|
-
}, "Watch on YouTube \u2192"))));
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
// src/containers/VideoArticle/index.ts
|
|
73
|
-
var VideoArticle_default = VideoArticle;
|
|
74
|
-
export {
|
|
75
|
-
VideoArticle_default as default
|
|
76
|
-
};
|
|
77
|
-
//# sourceMappingURL=VideoArticle-WLTWZO3K.mjs.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/containers/VideoArticle/VideoArticle.tsx", "../../../src/containers/VideoArticle/index.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport React, { useState } from 'react';\n\nimport { Icon, Panel } from '@dxos/react-ui';\n\nimport * as Channel from '../../types/Channel';\nimport * as Video from '../../types/Video';\n\nexport type VideoArticleProps = {\n role: string | string[];\n subject: Video.YouTubeVideo;\n channel: Channel.YouTubeChannel;\n attendableId?: string;\n};\n\nexport const VideoArticle = ({ subject: video, role }: VideoArticleProps) => {\n const [showPlayer, setShowPlayer] = useState(false);\n const publishedDate = new Date(video.publishedAt).toLocaleDateString();\n const hasTranscript = Boolean(video.transcript);\n const embedUrl = `https://www.youtube.com/embed/${video.videoId}`;\n\n return (\n <Panel.Root>\n <Panel.Content className={role === 'section' ? 'overflow-auto' : ''}>\n <div className='flex flex-col gap-4 p-4 max-w-4xl'>\n <div className='flex flex-col gap-2'>\n <h2 className='text-lg font-semibold'>{video.title}</h2>\n <div className='flex items-center gap-2 text-sm text-description'>\n <span>{video.channelTitle}</span>\n <span>•</span>\n <span>{publishedDate}</span>\n {video.viewCount !== undefined && (\n <>\n <span>•</span>\n <span>{video.viewCount.toLocaleString()} views</span>\n </>\n )}\n {video.likeCount !== undefined && (\n <>\n <span>•</span>\n <span>{video.likeCount.toLocaleString()} likes</span>\n </>\n )}\n </div>\n </div>\n\n <div className='aspect-video w-full max-w-2xl'>\n {showPlayer ? (\n <iframe\n src={embedUrl}\n title={video.title}\n allow='accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture'\n allowFullScreen\n className='h-full w-full rounded-lg'\n />\n ) : (\n <button\n type='button'\n onClick={() => setShowPlayer(true)}\n className='relative h-full w-full group cursor-pointer'\n >\n {video.thumbnailUrl ? (\n <img src={video.thumbnailUrl} alt={video.title} className='h-full w-full object-cover rounded-lg' />\n ) : (\n <div className='h-full w-full bg-surface-hover rounded-lg flex items-center justify-center'>\n <Icon icon='ph--play--fill' size={12} />\n </div>\n )}\n <div className='absolute inset-0 flex items-center justify-center bg-black/30 group-hover:bg-black/50 rounded-lg transition-colors'>\n <div className='bg-red-600 text-white rounded-full p-4 group-hover:scale-110 transition-transform'>\n <Icon icon='ph--play--fill' size={8} />\n </div>\n </div>\n </button>\n )}\n </div>\n\n {video.description && (\n <div className='max-w-2xl'>\n <h3 className='text-md font-medium mb-2'>Description</h3>\n <p className='whitespace-pre-wrap text-sm'>{video.description}</p>\n </div>\n )}\n\n {hasTranscript && (\n <div className='max-w-2xl'>\n <h3 className='text-md font-medium mb-2'>Transcript</h3>\n <div className='bg-surface-input p-4 rounded-lg max-h-96 overflow-auto'>\n <p className='whitespace-pre-wrap text-sm'>{video.transcript}</p>\n </div>\n </div>\n )}\n\n <a\n href={video.url}\n target='_blank'\n rel='noopener noreferrer'\n className='inline-flex items-center gap-2 text-sm text-primary hover:underline'\n >\n Watch on YouTube →\n </a>\n </div>\n </Panel.Content>\n </Panel.Root>\n );\n};\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { VideoArticle } from './VideoArticle';\n\nexport default VideoArticle;\n"],
|
|
5
|
-
"mappings": ";;;;AAIA,OAAOA,SAASC,gBAAgB;AAEhC,SAASC,MAAMC,aAAa;AAYrB,IAAMC,eAAe,CAAC,EAAEC,SAASC,OAAOC,KAAI,MAAqB;AACtE,QAAM,CAACC,YAAYC,aAAAA,IAAiBC,SAAS,KAAA;AAC7C,QAAMC,gBAAgB,IAAIC,KAAKN,MAAMO,WAAW,EAAEC,mBAAkB;AACpE,QAAMC,gBAAgBC,QAAQV,MAAMW,UAAU;AAC9C,QAAMC,WAAW,iCAAiCZ,MAAMa,OAAO;AAE/D,SACE,sBAAA,cAACC,MAAMC,MAAI,MACT,sBAAA,cAACD,MAAME,SAAO;IAACC,WAAWhB,SAAS,YAAY,kBAAkB;KAC/D,sBAAA,cAACiB,OAAAA;IAAID,WAAU;KACb,sBAAA,cAACC,OAAAA;IAAID,WAAU;KACb,sBAAA,cAACE,MAAAA;IAAGF,WAAU;KAAyBjB,MAAMoB,KAAK,GAClD,sBAAA,cAACF,OAAAA;IAAID,WAAU;KACb,sBAAA,cAACI,QAAAA,MAAMrB,MAAMsB,YAAY,GACzB,sBAAA,cAACD,QAAAA,MAAK,QAAA,GACN,sBAAA,cAACA,QAAAA,MAAMhB,aAAAA,GACNL,MAAMuB,cAAcC,UACnB,sBAAA,cAAA,MAAA,UAAA,MACE,sBAAA,cAACH,QAAAA,MAAK,QAAA,GACN,sBAAA,cAACA,QAAAA,MAAMrB,MAAMuB,UAAUE,eAAc,GAAG,QAAA,CAAA,GAG3CzB,MAAM0B,cAAcF,UACnB,sBAAA,cAAA,MAAA,UAAA,MACE,sBAAA,cAACH,QAAAA,MAAK,QAAA,GACN,sBAAA,cAACA,QAAAA,MAAMrB,MAAM0B,UAAUD,eAAc,GAAG,QAAA,CAAA,CAAA,CAAA,GAMhD,sBAAA,cAACP,OAAAA;IAAID,WAAU;KACZf,aACC,sBAAA,cAACyB,UAAAA;IACCC,KAAKhB;IACLQ,OAAOpB,MAAMoB;IACbS,OAAM;IACNC,iBAAAA;IACAb,WAAU;OAGZ,sBAAA,cAACc,UAAAA;IACCC,MAAK;IACLC,SAAS,MAAM9B,cAAc,IAAA;IAC7Bc,WAAU;KAETjB,MAAMkC,eACL,sBAAA,cAACC,OAAAA;IAAIP,KAAK5B,MAAMkC;IAAcE,KAAKpC,MAAMoB;IAAOH,WAAU;OAE1D,sBAAA,cAACC,OAAAA;IAAID,WAAU;KACb,sBAAA,cAACoB,MAAAA;IAAKC,MAAK;IAAiBC,MAAM;OAGtC,sBAAA,cAACrB,OAAAA;IAAID,WAAU;KACb,sBAAA,cAACC,OAAAA;IAAID,WAAU;KACb,sBAAA,cAACoB,MAAAA;IAAKC,MAAK;IAAiBC,MAAM;UAO3CvC,MAAMwC,eACL,sBAAA,cAACtB,OAAAA;IAAID,WAAU;KACb,sBAAA,cAACwB,MAAAA;IAAGxB,WAAU;KAA2B,aAAA,GACzC,sBAAA,cAACyB,KAAAA;IAAEzB,WAAU;KAA+BjB,MAAMwC,WAAW,CAAA,GAIhE/B,iBACC,sBAAA,cAACS,OAAAA;IAAID,WAAU;KACb,sBAAA,cAACwB,MAAAA;IAAGxB,WAAU;KAA2B,YAAA,GACzC,sBAAA,cAACC,OAAAA;IAAID,WAAU;KACb,sBAAA,cAACyB,KAAAA;IAAEzB,WAAU;KAA+BjB,MAAMW,UAAU,CAAA,CAAA,GAKlE,sBAAA,cAACgC,KAAAA;IACCC,MAAM5C,MAAM6C;IACZC,QAAO;IACPC,KAAI;IACJ9B,WAAU;KACX,yBAAA,CAAA,CAAA,CAAA;AAOX;;;ACtGA,IAAA,uBAAe+B;",
|
|
6
|
-
"names": ["React", "useState", "Icon", "Panel", "VideoArticle", "subject", "video", "role", "showPlayer", "setShowPlayer", "useState", "publishedDate", "Date", "publishedAt", "toLocaleDateString", "hasTranscript", "Boolean", "transcript", "embedUrl", "videoId", "Panel", "Root", "Content", "className", "div", "h2", "title", "span", "channelTitle", "viewCount", "undefined", "toLocaleString", "likeCount", "iframe", "src", "allow", "allowFullScreen", "button", "type", "onClick", "thumbnailUrl", "img", "alt", "Icon", "icon", "size", "description", "h3", "p", "a", "href", "url", "target", "rel", "VideoArticle"]
|
|
7
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
|
|
2
|
-
import "./chunk-HSLMI22Q.mjs";
|
|
3
|
-
|
|
4
|
-
// src/containers/VideoCard/VideoCard.tsx
|
|
5
|
-
import React, { useState } from "react";
|
|
6
|
-
import { Card, Icon } from "@dxos/react-ui";
|
|
7
|
-
var VideoCard = ({ subject: video }) => {
|
|
8
|
-
const [showPlayer, setShowPlayer] = useState(false);
|
|
9
|
-
const publishedDate = new Date(video.publishedAt).toLocaleDateString();
|
|
10
|
-
const hasTranscript = Boolean(video.transcript);
|
|
11
|
-
const embedUrl = `https://www.youtube.com/embed/${video.videoId}?autoplay=1`;
|
|
12
|
-
return /* @__PURE__ */ React.createElement(Card.Content, null, showPlayer ? /* @__PURE__ */ React.createElement("div", {
|
|
13
|
-
className: "aspect-video w-full"
|
|
14
|
-
}, /* @__PURE__ */ React.createElement("iframe", {
|
|
15
|
-
src: embedUrl,
|
|
16
|
-
title: video.title,
|
|
17
|
-
allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
|
|
18
|
-
allowFullScreen: true,
|
|
19
|
-
className: "h-full w-full rounded"
|
|
20
|
-
})) : /* @__PURE__ */ React.createElement("button", {
|
|
21
|
-
type: "button",
|
|
22
|
-
onClick: () => setShowPlayer(true),
|
|
23
|
-
className: "relative aspect-video w-full group cursor-pointer"
|
|
24
|
-
}, video.thumbnailUrl ? /* @__PURE__ */ React.createElement("img", {
|
|
25
|
-
src: video.thumbnailUrl,
|
|
26
|
-
alt: video.title,
|
|
27
|
-
className: "h-full w-full object-cover rounded"
|
|
28
|
-
}) : /* @__PURE__ */ React.createElement("div", {
|
|
29
|
-
className: "h-full w-full bg-surface-hover rounded flex items-center justify-center"
|
|
30
|
-
}, /* @__PURE__ */ React.createElement(Icon, {
|
|
31
|
-
icon: "ph--play--fill",
|
|
32
|
-
size: 12
|
|
33
|
-
})), /* @__PURE__ */ React.createElement("div", {
|
|
34
|
-
className: "absolute inset-0 flex items-center justify-center bg-black/30 group-hover:bg-black/50 rounded transition-colors"
|
|
35
|
-
}, /* @__PURE__ */ React.createElement("div", {
|
|
36
|
-
className: "bg-red-600 text-white rounded-full p-3 group-hover:scale-110 transition-transform"
|
|
37
|
-
}, /* @__PURE__ */ React.createElement(Icon, {
|
|
38
|
-
icon: "ph--play--fill",
|
|
39
|
-
size: 6
|
|
40
|
-
})))), /* @__PURE__ */ React.createElement(Card.Toolbar, null, /* @__PURE__ */ React.createElement(Card.IconBlock, null, /* @__PURE__ */ React.createElement(Icon, {
|
|
41
|
-
icon: "ph--youtube-logo--regular",
|
|
42
|
-
size: 5
|
|
43
|
-
})), /* @__PURE__ */ React.createElement("div", {
|
|
44
|
-
className: "flex gap-3 items-center justify-between col-span-2"
|
|
45
|
-
}, /* @__PURE__ */ React.createElement("span", {
|
|
46
|
-
className: "grow truncate font-medium"
|
|
47
|
-
}, video.title), /* @__PURE__ */ React.createElement("span", {
|
|
48
|
-
className: "text-xs text-description text-right whitespace-nowrap pe-2"
|
|
49
|
-
}, publishedDate))), /* @__PURE__ */ React.createElement(Card.Row, null, /* @__PURE__ */ React.createElement("span", {
|
|
50
|
-
className: "text-xs text-description"
|
|
51
|
-
}, video.channelTitle)), video.description && /* @__PURE__ */ React.createElement(Card.Row, null, /* @__PURE__ */ React.createElement(Card.Text, {
|
|
52
|
-
variant: "description"
|
|
53
|
-
}, video.description.slice(0, 150), "...")), /* @__PURE__ */ React.createElement(Card.Row, null, /* @__PURE__ */ React.createElement("div", {
|
|
54
|
-
className: "flex gap-2 items-center text-xs text-description"
|
|
55
|
-
}, video.viewCount !== void 0 && /* @__PURE__ */ React.createElement("span", null, video.viewCount.toLocaleString(), " views"), hasTranscript && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", null, "\u2022"), /* @__PURE__ */ React.createElement("span", {
|
|
56
|
-
className: "text-green-600"
|
|
57
|
-
}, "Transcript available")))));
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
// src/containers/VideoCard/index.ts
|
|
61
|
-
var VideoCard_default = VideoCard;
|
|
62
|
-
export {
|
|
63
|
-
VideoCard_default as default
|
|
64
|
-
};
|
|
65
|
-
//# sourceMappingURL=VideoCard-FOWQZK75.mjs.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/containers/VideoCard/VideoCard.tsx", "../../../src/containers/VideoCard/index.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport React, { useState } from 'react';\n\nimport { type SurfaceComponentProps } from '@dxos/app-toolkit/ui';\nimport { Card, Icon } from '@dxos/react-ui';\n\nimport * as Video from '../../types/Video';\n\nexport type VideoCardProps = SurfaceComponentProps<Video.YouTubeVideo>;\n\n/**\n * YouTube video card with embedded player.\n */\nexport const VideoCard = ({ subject: video }: VideoCardProps) => {\n const [showPlayer, setShowPlayer] = useState(false);\n const publishedDate = new Date(video.publishedAt).toLocaleDateString();\n const hasTranscript = Boolean(video.transcript);\n\n const embedUrl = `https://www.youtube.com/embed/${video.videoId}?autoplay=1`;\n\n return (\n <Card.Content>\n {showPlayer ? (\n <div className='aspect-video w-full'>\n <iframe\n src={embedUrl}\n title={video.title}\n allow='accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture'\n allowFullScreen\n className='h-full w-full rounded'\n />\n </div>\n ) : (\n <button\n type='button'\n onClick={() => setShowPlayer(true)}\n className='relative aspect-video w-full group cursor-pointer'\n >\n {video.thumbnailUrl ? (\n <img src={video.thumbnailUrl} alt={video.title} className='h-full w-full object-cover rounded' />\n ) : (\n <div className='h-full w-full bg-surface-hover rounded flex items-center justify-center'>\n <Icon icon='ph--play--fill' size={12} />\n </div>\n )}\n <div className='absolute inset-0 flex items-center justify-center bg-black/30 group-hover:bg-black/50 rounded transition-colors'>\n <div className='bg-red-600 text-white rounded-full p-3 group-hover:scale-110 transition-transform'>\n <Icon icon='ph--play--fill' size={6} />\n </div>\n </div>\n </button>\n )}\n <Card.Toolbar>\n <Card.IconBlock>\n <Icon icon='ph--youtube-logo--regular' size={5} />\n </Card.IconBlock>\n <div className='flex gap-3 items-center justify-between col-span-2'>\n <span className='grow truncate font-medium'>{video.title}</span>\n <span className='text-xs text-description text-right whitespace-nowrap pe-2'>{publishedDate}</span>\n </div>\n </Card.Toolbar>\n <Card.Row>\n <span className='text-xs text-description'>{video.channelTitle}</span>\n </Card.Row>\n {video.description && (\n <Card.Row>\n <Card.Text variant='description'>{video.description.slice(0, 150)}...</Card.Text>\n </Card.Row>\n )}\n <Card.Row>\n <div className='flex gap-2 items-center text-xs text-description'>\n {video.viewCount !== undefined && <span>{video.viewCount.toLocaleString()} views</span>}\n {hasTranscript && (\n <>\n <span>•</span>\n <span className='text-green-600'>Transcript available</span>\n </>\n )}\n </div>\n </Card.Row>\n </Card.Content>\n );\n};\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { VideoCard } from './VideoCard';\n\nexport default VideoCard;\n"],
|
|
5
|
-
"mappings": ";;;;AAIA,OAAOA,SAASC,gBAAgB;AAGhC,SAASC,MAAMC,YAAY;AASpB,IAAMC,YAAY,CAAC,EAAEC,SAASC,MAAK,MAAkB;AAC1D,QAAM,CAACC,YAAYC,aAAAA,IAAiBC,SAAS,KAAA;AAC7C,QAAMC,gBAAgB,IAAIC,KAAKL,MAAMM,WAAW,EAAEC,mBAAkB;AACpE,QAAMC,gBAAgBC,QAAQT,MAAMU,UAAU;AAE9C,QAAMC,WAAW,iCAAiCX,MAAMY,OAAO;AAE/D,SACE,sBAAA,cAACC,KAAKC,SAAO,MACVb,aACC,sBAAA,cAACc,OAAAA;IAAIC,WAAU;KACb,sBAAA,cAACC,UAAAA;IACCC,KAAKP;IACLQ,OAAOnB,MAAMmB;IACbC,OAAM;IACNC,iBAAAA;IACAL,WAAU;QAId,sBAAA,cAACM,UAAAA;IACCC,MAAK;IACLC,SAAS,MAAMtB,cAAc,IAAA;IAC7Bc,WAAU;KAEThB,MAAMyB,eACL,sBAAA,cAACC,OAAAA;IAAIR,KAAKlB,MAAMyB;IAAcE,KAAK3B,MAAMmB;IAAOH,WAAU;OAE1D,sBAAA,cAACD,OAAAA;IAAIC,WAAU;KACb,sBAAA,cAACY,MAAAA;IAAKC,MAAK;IAAiBC,MAAM;OAGtC,sBAAA,cAACf,OAAAA;IAAIC,WAAU;KACb,sBAAA,cAACD,OAAAA;IAAIC,WAAU;KACb,sBAAA,cAACY,MAAAA;IAAKC,MAAK;IAAiBC,MAAM;SAK1C,sBAAA,cAACjB,KAAKkB,SAAO,MACX,sBAAA,cAAClB,KAAKmB,WAAS,MACb,sBAAA,cAACJ,MAAAA;IAAKC,MAAK;IAA4BC,MAAM;OAE/C,sBAAA,cAACf,OAAAA;IAAIC,WAAU;KACb,sBAAA,cAACiB,QAAAA;IAAKjB,WAAU;KAA6BhB,MAAMmB,KAAK,GACxD,sBAAA,cAACc,QAAAA;IAAKjB,WAAU;KAA8DZ,aAAAA,CAAAA,CAAAA,GAGlF,sBAAA,cAACS,KAAKqB,KAAG,MACP,sBAAA,cAACD,QAAAA;IAAKjB,WAAU;KAA4BhB,MAAMmC,YAAY,CAAA,GAE/DnC,MAAMoC,eACL,sBAAA,cAACvB,KAAKqB,KAAG,MACP,sBAAA,cAACrB,KAAKwB,MAAI;IAACC,SAAQ;KAAetC,MAAMoC,YAAYG,MAAM,GAAG,GAAA,GAAK,KAAA,CAAA,GAGtE,sBAAA,cAAC1B,KAAKqB,KAAG,MACP,sBAAA,cAACnB,OAAAA;IAAIC,WAAU;KACZhB,MAAMwC,cAAcC,UAAa,sBAAA,cAACR,QAAAA,MAAMjC,MAAMwC,UAAUE,eAAc,GAAG,QAAA,GACzElC,iBACC,sBAAA,cAAA,MAAA,UAAA,MACE,sBAAA,cAACyB,QAAAA,MAAK,QAAA,GACN,sBAAA,cAACA,QAAAA;IAAKjB,WAAU;KAAiB,sBAAA,CAAA,CAAA,CAAA,CAAA;AAO/C;;;AC/EA,IAAA,oBAAe2B;",
|
|
6
|
-
"names": ["React", "useState", "Card", "Icon", "VideoCard", "subject", "video", "showPlayer", "setShowPlayer", "useState", "publishedDate", "Date", "publishedAt", "toLocaleDateString", "hasTranscript", "Boolean", "transcript", "embedUrl", "videoId", "Card", "Content", "div", "className", "iframe", "src", "title", "allow", "allowFullScreen", "button", "type", "onClick", "thumbnailUrl", "img", "alt", "Icon", "icon", "size", "Toolbar", "IconBlock", "span", "Row", "channelTitle", "description", "Text", "variant", "slice", "viewCount", "undefined", "toLocaleString", "VideoCard"]
|
|
7
|
-
}
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
|
|
2
|
-
import {
|
|
3
|
-
meta
|
|
4
|
-
} from "./chunk-CX6MV3QM.mjs";
|
|
5
|
-
import "./chunk-M4S6BE47.mjs";
|
|
6
|
-
import "./chunk-JM5SBBP5.mjs";
|
|
7
|
-
import {
|
|
8
|
-
ClearSyncedVideos,
|
|
9
|
-
Sync
|
|
10
|
-
} from "./chunk-JSGRZMG3.mjs";
|
|
11
|
-
import {
|
|
12
|
-
Channel_exports
|
|
13
|
-
} from "./chunk-6BUJ2DQX.mjs";
|
|
14
|
-
import "./chunk-CZSLL3XQ.mjs";
|
|
15
|
-
import "./chunk-HSLMI22Q.mjs";
|
|
16
|
-
|
|
17
|
-
// src/capabilities/app-graph-builder/app-graph-builder.ts
|
|
18
|
-
import { Atom } from "@effect-atom/atom-react";
|
|
19
|
-
import * as Effect from "effect/Effect";
|
|
20
|
-
import * as Option from "effect/Option";
|
|
21
|
-
import { Capability } from "@dxos/app-framework";
|
|
22
|
-
import { AppCapabilities, companionSegment, LayoutOperation } from "@dxos/app-toolkit";
|
|
23
|
-
import { Filter, Obj, Query, Ref } from "@dxos/echo";
|
|
24
|
-
import { AtomQuery, AtomRef } from "@dxos/echo-atom";
|
|
25
|
-
import { invariant } from "@dxos/invariant";
|
|
26
|
-
import { log } from "@dxos/log";
|
|
27
|
-
import { Operation } from "@dxos/operation";
|
|
28
|
-
import { AttentionCapabilities } from "@dxos/plugin-attention";
|
|
29
|
-
import { AutomationCapabilities, invokeFunctionWithTracing } from "@dxos/plugin-automation";
|
|
30
|
-
import { PLANK_COMPANION_TYPE } from "@dxos/plugin-deck/types";
|
|
31
|
-
import { GraphBuilder, NodeMatcher } from "@dxos/plugin-graph";
|
|
32
|
-
var __dxlog_file = "/Users/mykola/dev/dxos/packages/plugins/plugin-youtube/src/capabilities/app-graph-builder/app-graph-builder.ts";
|
|
33
|
-
var app_graph_builder_default = Capability.makeModule(Effect.fnUntraced(function* () {
|
|
34
|
-
const selectionManager = yield* Capability.get(AttentionCapabilities.Selection);
|
|
35
|
-
const selectedId = Atom.family((nodeId) => Atom.make((get) => {
|
|
36
|
-
const state = get(selectionManager.state);
|
|
37
|
-
const selection = state.selections[nodeId];
|
|
38
|
-
return selection?.mode === "single" ? selection.id : void 0;
|
|
39
|
-
}));
|
|
40
|
-
const whenYouTubeChannel = NodeMatcher.whenEchoType(Channel_exports.YouTubeChannel);
|
|
41
|
-
const extensions = yield* Effect.all([
|
|
42
|
-
GraphBuilder.createExtension({
|
|
43
|
-
id: `${meta.id}.channel-video`,
|
|
44
|
-
match: (node) => Channel_exports.instanceOf(node.data) ? Option.some({
|
|
45
|
-
channel: node.data,
|
|
46
|
-
nodeId: node.id
|
|
47
|
-
}) : Option.none(),
|
|
48
|
-
connector: (matched, get) => {
|
|
49
|
-
const channel = matched.channel;
|
|
50
|
-
const db = Obj.getDatabase(channel);
|
|
51
|
-
const feed = channel.feed ? get(AtomRef.make(channel.feed)) : void 0;
|
|
52
|
-
if (!db || !feed) {
|
|
53
|
-
return Effect.succeed([]);
|
|
54
|
-
}
|
|
55
|
-
const videoId = get(selectedId(matched.nodeId));
|
|
56
|
-
const video = get(AtomQuery.make(db, Query.select(videoId ? Filter.id(videoId) : Filter.nothing()).from(feed)))[0];
|
|
57
|
-
return Effect.succeed([
|
|
58
|
-
{
|
|
59
|
-
id: companionSegment("video"),
|
|
60
|
-
type: PLANK_COMPANION_TYPE,
|
|
61
|
-
data: video ?? "video",
|
|
62
|
-
properties: {
|
|
63
|
-
label: [
|
|
64
|
-
"video label",
|
|
65
|
-
{
|
|
66
|
-
ns: meta.id
|
|
67
|
-
}
|
|
68
|
-
],
|
|
69
|
-
icon: "ph--play--regular",
|
|
70
|
-
disposition: "hidden"
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
]);
|
|
74
|
-
}
|
|
75
|
-
}),
|
|
76
|
-
GraphBuilder.createExtension({
|
|
77
|
-
id: `${meta.id}.sync-channel`,
|
|
78
|
-
match: whenYouTubeChannel,
|
|
79
|
-
actions: (channel) => Effect.succeed([
|
|
80
|
-
{
|
|
81
|
-
id: "sync",
|
|
82
|
-
data: Effect.fnUntraced(function* () {
|
|
83
|
-
const computeRuntime = yield* Capability.get(AutomationCapabilities.ComputeRuntime);
|
|
84
|
-
const db = Obj.getDatabase(channel);
|
|
85
|
-
invariant(db, void 0, {
|
|
86
|
-
F: __dxlog_file,
|
|
87
|
-
L: 83,
|
|
88
|
-
S: this,
|
|
89
|
-
A: [
|
|
90
|
-
"db",
|
|
91
|
-
""
|
|
92
|
-
]
|
|
93
|
-
});
|
|
94
|
-
const runtime = computeRuntime.getRuntime(db.spaceId);
|
|
95
|
-
yield* Effect.tryPromise(() => runtime.runPromise(invokeFunctionWithTracing(Sync, {
|
|
96
|
-
channel: Ref.make(channel)
|
|
97
|
-
}))).pipe(Effect.catchAll((error) => {
|
|
98
|
-
log.catch(error, void 0, {
|
|
99
|
-
F: __dxlog_file,
|
|
100
|
-
L: 93,
|
|
101
|
-
S: this,
|
|
102
|
-
C: (f, a) => f(...a)
|
|
103
|
-
});
|
|
104
|
-
return Operation.invoke(LayoutOperation.AddToast, {
|
|
105
|
-
id: `${meta.id}.sync-channel-error`,
|
|
106
|
-
icon: "ph--warning--regular",
|
|
107
|
-
duration: 5e3,
|
|
108
|
-
title: [
|
|
109
|
-
"sync channel error title",
|
|
110
|
-
{
|
|
111
|
-
ns: meta.id
|
|
112
|
-
}
|
|
113
|
-
],
|
|
114
|
-
closeLabel: [
|
|
115
|
-
"close label",
|
|
116
|
-
{
|
|
117
|
-
ns: meta.id
|
|
118
|
-
}
|
|
119
|
-
]
|
|
120
|
-
});
|
|
121
|
-
}));
|
|
122
|
-
}),
|
|
123
|
-
properties: {
|
|
124
|
-
label: [
|
|
125
|
-
"sync channel label",
|
|
126
|
-
{
|
|
127
|
-
ns: meta.id
|
|
128
|
-
}
|
|
129
|
-
],
|
|
130
|
-
icon: "ph--arrows-clockwise--regular",
|
|
131
|
-
disposition: "list-item"
|
|
132
|
-
}
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
id: "clear-synced-videos",
|
|
136
|
-
data: Effect.fnUntraced(function* () {
|
|
137
|
-
const computeRuntime = yield* Capability.get(AutomationCapabilities.ComputeRuntime);
|
|
138
|
-
const db = Obj.getDatabase(channel);
|
|
139
|
-
invariant(db, void 0, {
|
|
140
|
-
F: __dxlog_file,
|
|
141
|
-
L: 115,
|
|
142
|
-
S: this,
|
|
143
|
-
A: [
|
|
144
|
-
"db",
|
|
145
|
-
""
|
|
146
|
-
]
|
|
147
|
-
});
|
|
148
|
-
const runtime = computeRuntime.getRuntime(db.spaceId);
|
|
149
|
-
yield* Effect.tryPromise(() => runtime.runPromise(invokeFunctionWithTracing(ClearSyncedVideos, {
|
|
150
|
-
channel: Ref.make(channel)
|
|
151
|
-
}))).pipe(Effect.catchAll((error) => {
|
|
152
|
-
log.catch(error, void 0, {
|
|
153
|
-
F: __dxlog_file,
|
|
154
|
-
L: 125,
|
|
155
|
-
S: this,
|
|
156
|
-
C: (f, a) => f(...a)
|
|
157
|
-
});
|
|
158
|
-
return Operation.invoke(LayoutOperation.AddToast, {
|
|
159
|
-
id: `${meta.id}.clear-synced-videos-error`,
|
|
160
|
-
icon: "ph--warning--regular",
|
|
161
|
-
duration: 5e3,
|
|
162
|
-
title: [
|
|
163
|
-
"clear synced videos error title",
|
|
164
|
-
{
|
|
165
|
-
ns: meta.id
|
|
166
|
-
}
|
|
167
|
-
],
|
|
168
|
-
closeLabel: [
|
|
169
|
-
"close label",
|
|
170
|
-
{
|
|
171
|
-
ns: meta.id
|
|
172
|
-
}
|
|
173
|
-
]
|
|
174
|
-
});
|
|
175
|
-
}));
|
|
176
|
-
}),
|
|
177
|
-
properties: {
|
|
178
|
-
label: [
|
|
179
|
-
"clear synced videos label",
|
|
180
|
-
{
|
|
181
|
-
ns: meta.id
|
|
182
|
-
}
|
|
183
|
-
],
|
|
184
|
-
icon: "ph--trash--regular",
|
|
185
|
-
disposition: "list-item"
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
])
|
|
189
|
-
})
|
|
190
|
-
]);
|
|
191
|
-
return Capability.contributes(AppCapabilities.AppGraphBuilder, extensions);
|
|
192
|
-
}));
|
|
193
|
-
export {
|
|
194
|
-
app_graph_builder_default as default
|
|
195
|
-
};
|
|
196
|
-
//# sourceMappingURL=app-graph-builder-IU5TBAXN.mjs.map
|