@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.
Files changed (77) hide show
  1. package/dist/lib/browser/{chunk-5N5SWF3I.mjs → chunk-FEQD5TPI.mjs} +2 -2
  2. package/dist/lib/browser/{chunk-5N5SWF3I.mjs.map → chunk-FEQD5TPI.mjs.map} +2 -2
  3. package/dist/lib/browser/{chunk-SWWE4LUJ.mjs → chunk-GIRFSTHR.mjs} +4 -4
  4. package/dist/lib/browser/{chunk-SWWE4LUJ.mjs.map → chunk-GIRFSTHR.mjs.map} +3 -3
  5. package/dist/lib/browser/{clear-synced-videos-PIKJZET3.mjs → clear-synced-videos-PMP332H3.mjs} +8 -8
  6. package/dist/lib/browser/clear-synced-videos-PMP332H3.mjs.map +7 -0
  7. package/dist/lib/browser/index.mjs +5 -5
  8. package/dist/lib/browser/index.mjs.map +3 -3
  9. package/dist/lib/browser/meta.json +1 -1
  10. package/dist/lib/browser/{sync-II7O2LPG.mjs → sync-T34US6NO.mjs} +15 -15
  11. package/dist/lib/browser/sync-T34US6NO.mjs.map +7 -0
  12. package/dist/lib/node-esm/{chunk-RL46XZ2D.mjs → chunk-A3SKNJFU.mjs} +4 -4
  13. package/dist/lib/node-esm/{chunk-RL46XZ2D.mjs.map → chunk-A3SKNJFU.mjs.map} +3 -3
  14. package/dist/lib/node-esm/{chunk-BVKMXV2G.mjs → chunk-Q3TVMR5B.mjs} +2 -2
  15. package/dist/lib/node-esm/{chunk-BVKMXV2G.mjs.map → chunk-Q3TVMR5B.mjs.map} +2 -2
  16. package/dist/lib/node-esm/{clear-synced-videos-Q3MZO2CD.mjs → clear-synced-videos-SLEDJ5WI.mjs} +8 -8
  17. package/dist/lib/node-esm/clear-synced-videos-SLEDJ5WI.mjs.map +7 -0
  18. package/dist/lib/node-esm/index.mjs +5 -5
  19. package/dist/lib/node-esm/index.mjs.map +3 -3
  20. package/dist/lib/node-esm/meta.json +1 -1
  21. package/dist/lib/node-esm/{sync-BEXQNNSH.mjs → sync-RQYQ5LII.mjs} +15 -15
  22. package/dist/lib/node-esm/sync-RQYQ5LII.mjs.map +7 -0
  23. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  24. package/dist/types/src/capabilities/index.d.ts +1 -1
  25. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  26. package/dist/types/src/capabilities/migrations.d.ts +1 -1
  27. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  28. package/dist/types/src/containers/ChannelArticle/ChannelArticle.d.ts +1 -1
  29. package/dist/types/src/containers/ChannelArticle/ChannelArticle.d.ts.map +1 -1
  30. package/dist/types/src/containers/ChannelArticle/index.d.ts +1 -2
  31. package/dist/types/src/containers/ChannelArticle/index.d.ts.map +1 -1
  32. package/dist/types/src/containers/ChannelProperties/ChannelProperties.d.ts +7 -0
  33. package/dist/types/src/containers/ChannelProperties/ChannelProperties.d.ts.map +1 -0
  34. package/dist/types/src/containers/ChannelProperties/index.d.ts +2 -0
  35. package/dist/types/src/containers/ChannelProperties/index.d.ts.map +1 -0
  36. package/dist/types/src/containers/VideoArticle/VideoArticle.d.ts +2 -2
  37. package/dist/types/src/containers/VideoArticle/VideoArticle.d.ts.map +1 -1
  38. package/dist/types/src/containers/VideoArticle/index.d.ts +1 -2
  39. package/dist/types/src/containers/VideoArticle/index.d.ts.map +1 -1
  40. package/dist/types/src/containers/VideoCard/VideoCard.d.ts +2 -2
  41. package/dist/types/src/containers/VideoCard/VideoCard.d.ts.map +1 -1
  42. package/dist/types/src/containers/VideoCard/index.d.ts +1 -2
  43. package/dist/types/src/containers/VideoCard/index.d.ts.map +1 -1
  44. package/dist/types/src/containers/index.d.ts +3 -3
  45. package/dist/types/src/containers/index.d.ts.map +1 -1
  46. package/dist/types/src/operations/apis/youtube/api.d.ts.map +1 -1
  47. package/dist/types/src/operations/clear-synced-videos.d.ts.map +1 -1
  48. package/dist/types/src/operations/definitions.d.ts +3 -3
  49. package/dist/types/src/operations/definitions.d.ts.map +1 -1
  50. package/dist/types/src/operations/sync.d.ts.map +1 -1
  51. package/dist/types/tsconfig.tsbuildinfo +1 -1
  52. package/package.json +40 -40
  53. package/src/YouTubePlugin.tsx +3 -3
  54. package/src/capabilities/app-graph-builder.ts +13 -14
  55. package/src/capabilities/react-surface.tsx +20 -21
  56. package/src/containers/ChannelArticle/ChannelArticle.tsx +1 -1
  57. package/src/containers/ChannelArticle/index.ts +1 -3
  58. package/src/containers/{ChannelSettings/ChannelSettings.tsx → ChannelProperties/ChannelProperties.tsx} +2 -2
  59. package/src/containers/ChannelProperties/index.ts +5 -0
  60. package/src/containers/VideoArticle/VideoArticle.tsx +2 -2
  61. package/src/containers/VideoArticle/index.ts +1 -3
  62. package/src/containers/VideoCard/VideoCard.tsx +2 -2
  63. package/src/containers/VideoCard/index.ts +1 -3
  64. package/src/containers/index.ts +4 -4
  65. package/src/operations/apis/youtube/api.ts +0 -1
  66. package/src/operations/clear-synced-videos.ts +3 -4
  67. package/src/operations/definitions.ts +3 -3
  68. package/src/operations/sync.ts +0 -1
  69. package/dist/lib/browser/clear-synced-videos-PIKJZET3.mjs.map +0 -7
  70. package/dist/lib/browser/sync-II7O2LPG.mjs.map +0 -7
  71. package/dist/lib/node-esm/clear-synced-videos-Q3MZO2CD.mjs.map +0 -7
  72. package/dist/lib/node-esm/sync-BEXQNNSH.mjs.map +0 -7
  73. package/dist/types/src/containers/ChannelSettings/ChannelSettings.d.ts +0 -7
  74. package/dist/types/src/containers/ChannelSettings/ChannelSettings.d.ts.map +0 -1
  75. package/dist/types/src/containers/ChannelSettings/index.d.ts +0 -3
  76. package/dist/types/src/containers/ChannelSettings/index.d.ts.map +0 -1
  77. package/src/containers/ChannelSettings/index.ts +0 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/plugin-youtube",
3
- "version": "0.8.4-main.bcb3aa67d6",
3
+ "version": "0.8.4-main.fcfe5033a5",
4
4
  "description": "DXOS YouTube feed plugin",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -61,35 +61,35 @@
61
61
  "@radix-ui/react-context": "1.1.1",
62
62
  "effect": "3.20.0",
63
63
  "youtube-caption-extractor": "^1.9.1",
64
- "@dxos/app-framework": "0.8.4-main.bcb3aa67d6",
65
- "@dxos/app-toolkit": "0.8.4-main.bcb3aa67d6",
66
- "@dxos/async": "0.8.4-main.bcb3aa67d6",
67
- "@dxos/client": "0.8.4-main.bcb3aa67d6",
68
- "@dxos/echo-atom": "0.8.4-main.bcb3aa67d6",
69
- "@dxos/debug": "0.8.4-main.bcb3aa67d6",
70
- "@dxos/echo": "0.8.4-main.bcb3aa67d6",
71
- "@dxos/echo-db": "0.8.4-main.bcb3aa67d6",
72
- "@dxos/errors": "0.8.4-main.bcb3aa67d6",
73
- "@dxos/effect": "0.8.4-main.bcb3aa67d6",
74
- "@dxos/invariant": "0.8.4-main.bcb3aa67d6",
75
- "@dxos/functions": "0.8.4-main.bcb3aa67d6",
76
- "@dxos/blueprints": "0.8.4-main.bcb3aa67d6",
77
- "@dxos/log": "0.8.4-main.bcb3aa67d6",
78
- "@dxos/operation": "0.8.4-main.bcb3aa67d6",
79
- "@dxos/plugin-automation": "0.8.4-main.bcb3aa67d6",
80
- "@dxos/plugin-attention": "0.8.4-main.bcb3aa67d6",
81
- "@dxos/plugin-deck": "0.8.4-main.bcb3aa67d6",
82
- "@dxos/plugin-client": "0.8.4-main.bcb3aa67d6",
83
- "@dxos/plugin-graph": "0.8.4-main.bcb3aa67d6",
84
- "@dxos/react-client": "0.8.4-main.bcb3aa67d6",
85
- "@dxos/react-ui": "0.8.4-main.bcb3aa67d6",
86
- "@dxos/react-ui-attention": "0.8.4-main.bcb3aa67d6",
87
- "@dxos/plugin-space": "0.8.4-main.bcb3aa67d6",
88
- "@dxos/react-ui-stack": "0.8.4-main.bcb3aa67d6",
89
- "@dxos/schema": "0.8.4-main.bcb3aa67d6",
90
- "@dxos/types": "0.8.4-main.bcb3aa67d6",
91
- "@dxos/keys": "0.8.4-main.bcb3aa67d6",
92
- "@dxos/util": "0.8.4-main.bcb3aa67d6"
64
+ "@dxos/app-framework": "0.8.4-main.fcfe5033a5",
65
+ "@dxos/app-toolkit": "0.8.4-main.fcfe5033a5",
66
+ "@dxos/blueprints": "0.8.4-main.fcfe5033a5",
67
+ "@dxos/client": "0.8.4-main.fcfe5033a5",
68
+ "@dxos/debug": "0.8.4-main.fcfe5033a5",
69
+ "@dxos/async": "0.8.4-main.fcfe5033a5",
70
+ "@dxos/echo": "0.8.4-main.fcfe5033a5",
71
+ "@dxos/echo-atom": "0.8.4-main.fcfe5033a5",
72
+ "@dxos/echo-db": "0.8.4-main.fcfe5033a5",
73
+ "@dxos/effect": "0.8.4-main.fcfe5033a5",
74
+ "@dxos/errors": "0.8.4-main.fcfe5033a5",
75
+ "@dxos/invariant": "0.8.4-main.fcfe5033a5",
76
+ "@dxos/functions": "0.8.4-main.fcfe5033a5",
77
+ "@dxos/keys": "0.8.4-main.fcfe5033a5",
78
+ "@dxos/log": "0.8.4-main.fcfe5033a5",
79
+ "@dxos/operation": "0.8.4-main.fcfe5033a5",
80
+ "@dxos/plugin-attention": "0.8.4-main.fcfe5033a5",
81
+ "@dxos/plugin-automation": "0.8.4-main.fcfe5033a5",
82
+ "@dxos/plugin-client": "0.8.4-main.fcfe5033a5",
83
+ "@dxos/plugin-deck": "0.8.4-main.fcfe5033a5",
84
+ "@dxos/react-client": "0.8.4-main.fcfe5033a5",
85
+ "@dxos/plugin-graph": "0.8.4-main.fcfe5033a5",
86
+ "@dxos/react-ui": "0.8.4-main.fcfe5033a5",
87
+ "@dxos/plugin-space": "0.8.4-main.fcfe5033a5",
88
+ "@dxos/react-ui-attention": "0.8.4-main.fcfe5033a5",
89
+ "@dxos/react-ui-stack": "0.8.4-main.fcfe5033a5",
90
+ "@dxos/schema": "0.8.4-main.fcfe5033a5",
91
+ "@dxos/util": "0.8.4-main.fcfe5033a5",
92
+ "@dxos/types": "0.8.4-main.fcfe5033a5"
93
93
  },
94
94
  "devDependencies": {
95
95
  "@types/react": "~19.2.7",
@@ -97,14 +97,14 @@
97
97
  "react": "~19.2.3",
98
98
  "react-dom": "~19.2.3",
99
99
  "vite": "^7.1.11",
100
- "@dxos/plugin-theme": "0.8.4-main.bcb3aa67d6",
101
- "@dxos/protocols": "0.8.4-main.bcb3aa67d6",
102
- "@dxos/random": "0.8.4-main.bcb3aa67d6",
103
- "@dxos/config": "0.8.4-main.bcb3aa67d6",
104
- "@dxos/storybook-utils": "0.8.4-main.bcb3aa67d6",
105
- "@dxos/ui-theme": "0.8.4-main.bcb3aa67d6",
106
- "@dxos/react-ui": "0.8.4-main.bcb3aa67d6",
107
- "@dxos/plugin-testing": "0.8.4-main.bcb3aa67d6"
100
+ "@dxos/config": "0.8.4-main.fcfe5033a5",
101
+ "@dxos/plugin-testing": "0.8.4-main.fcfe5033a5",
102
+ "@dxos/plugin-theme": "0.8.4-main.fcfe5033a5",
103
+ "@dxos/protocols": "0.8.4-main.fcfe5033a5",
104
+ "@dxos/random": "0.8.4-main.fcfe5033a5",
105
+ "@dxos/storybook-utils": "0.8.4-main.fcfe5033a5",
106
+ "@dxos/react-ui": "0.8.4-main.fcfe5033a5",
107
+ "@dxos/ui-theme": "0.8.4-main.fcfe5033a5"
108
108
  },
109
109
  "peerDependencies": {
110
110
  "@effect-atom/atom-react": "^0.5.0",
@@ -112,8 +112,8 @@
112
112
  "effect": "3.20.0",
113
113
  "react": "~19.2.3",
114
114
  "react-dom": "~19.2.3",
115
- "@dxos/ui-theme": "0.8.4-main.bcb3aa67d6",
116
- "@dxos/react-ui": "0.8.4-main.bcb3aa67d6"
115
+ "@dxos/react-ui": "0.8.4-main.fcfe5033a5",
116
+ "@dxos/ui-theme": "0.8.4-main.fcfe5033a5"
117
117
  },
118
118
  "publishConfig": {
119
119
  "access": "public"
@@ -11,15 +11,15 @@ import { Annotation } from '@dxos/echo';
11
11
  import { Operation } from '@dxos/operation';
12
12
  import { AttentionEvents } from '@dxos/plugin-attention/types';
13
13
  import { ClientEvents } from '@dxos/plugin-client/types';
14
- import { type CreateObject } from '@dxos/plugin-space/types';
15
14
  import { SpaceOperation } from '@dxos/plugin-space/operations';
15
+ import { type CreateObject } from '@dxos/plugin-space/types';
16
16
 
17
17
  import { YouTubeBlueprint } from '#blueprints';
18
+ import { AppGraphBuilder, BlueprintDefinition, Migrations, ReactSurface } from '#capabilities';
18
19
  import { meta } from '#meta';
19
- import { translations } from './translations';
20
20
  import { Channel, Video } from '#types';
21
21
 
22
- import { AppGraphBuilder, BlueprintDefinition, Migrations, ReactSurface } from '#capabilities';
22
+ import { translations } from './translations';
23
23
 
24
24
  export const YouTubePlugin = Plugin.define(meta).pipe(
25
25
  AppPlugin.addAppGraphModule({
@@ -8,19 +8,18 @@ import * as Option from 'effect/Option';
8
8
 
9
9
  import { Capability } from '@dxos/app-framework';
10
10
  import { AppCapabilities, AppNode, LayoutOperation } from '@dxos/app-toolkit';
11
- import { linkedSegment } from '@dxos/react-ui-attention';
12
11
  import { type Feed, Filter, Obj, Query, Ref } from '@dxos/echo';
13
12
  import { AtomQuery, AtomRef } from '@dxos/echo-atom';
14
13
  import { invariant } from '@dxos/invariant';
15
14
  import { log } from '@dxos/log';
16
15
  import { Operation } from '@dxos/operation';
17
16
  import { AttentionCapabilities } from '@dxos/plugin-attention/types';
18
- import { invokeFunctionWithTracing } from '@dxos/plugin-automation/hooks';
19
17
  import { AutomationCapabilities } from '@dxos/plugin-automation/types';
20
- import { GraphBuilder, NodeMatcher } from '@dxos/plugin-graph';
18
+ import { GraphBuilder, Node, NodeMatcher } from '@dxos/plugin-graph';
19
+ import { linkedSegment } from '@dxos/react-ui-attention';
21
20
 
22
- import { ClearSyncedVideos, Sync } from '#operations';
23
21
  import { meta } from '#meta';
22
+ import { ClearSyncedVideos, Sync } from '#operations';
24
23
  import { Channel, Video } from '#types';
25
24
 
26
25
  export default Capability.makeModule(
@@ -38,7 +37,7 @@ export default Capability.makeModule(
38
37
 
39
38
  const extensions = yield* Effect.all([
40
39
  GraphBuilder.createExtension({
41
- id: `${meta.id}.channel-video`,
40
+ id: 'channel-video',
42
41
  match: (node) =>
43
42
  Channel.instanceOf(node.data) ? Option.some({ channel: node.data, nodeId: node.id }) : Option.none(),
44
43
  connector: (matched, get) => {
@@ -68,11 +67,11 @@ export default Capability.makeModule(
68
67
  }),
69
68
 
70
69
  GraphBuilder.createExtension({
71
- id: `${meta.id}.sync-channel`,
70
+ id: 'sync-channel',
72
71
  match: whenYouTubeChannel,
73
72
  actions: (channel) =>
74
73
  Effect.succeed([
75
- {
74
+ Node.makeAction({
76
75
  id: 'sync',
77
76
  data: Effect.fnUntraced(function* () {
78
77
  const computeRuntime = yield* Capability.get(AutomationCapabilities.ComputeRuntime);
@@ -81,7 +80,7 @@ export default Capability.makeModule(
81
80
  const runtime = computeRuntime.getRuntime(db.spaceId);
82
81
  yield* Effect.tryPromise(() =>
83
82
  runtime.runPromise(
84
- invokeFunctionWithTracing(Sync, {
83
+ Operation.invoke(Sync, {
85
84
  channel: Ref.make(channel),
86
85
  }),
87
86
  ),
@@ -89,7 +88,7 @@ export default Capability.makeModule(
89
88
  Effect.catchAll((error) => {
90
89
  log.catch(error);
91
90
  return Operation.invoke(LayoutOperation.AddToast, {
92
- id: `${meta.id}.sync-channel-error`,
91
+ id: 'sync-channel-error',
93
92
  icon: 'ph--warning--regular',
94
93
  duration: 5_000,
95
94
  title: ['sync-channel-error.title', { ns: meta.id }],
@@ -103,8 +102,8 @@ export default Capability.makeModule(
103
102
  icon: 'ph--arrows-clockwise--regular',
104
103
  disposition: 'list-item',
105
104
  },
106
- },
107
- {
105
+ }),
106
+ Node.makeAction({
108
107
  id: 'clear-synced-videos',
109
108
  data: Effect.fnUntraced(function* () {
110
109
  const computeRuntime = yield* Capability.get(AutomationCapabilities.ComputeRuntime);
@@ -113,7 +112,7 @@ export default Capability.makeModule(
113
112
  const runtime = computeRuntime.getRuntime(db.spaceId);
114
113
  yield* Effect.tryPromise(() =>
115
114
  runtime.runPromise(
116
- invokeFunctionWithTracing(ClearSyncedVideos, {
115
+ Operation.invoke(ClearSyncedVideos, {
117
116
  channel: Ref.make(channel),
118
117
  }),
119
118
  ),
@@ -121,7 +120,7 @@ export default Capability.makeModule(
121
120
  Effect.catchAll((error) => {
122
121
  log.catch(error);
123
122
  return Operation.invoke(LayoutOperation.AddToast, {
124
- id: `${meta.id}.clear-synced-videos-error`,
123
+ id: 'clear-synced-videos-error',
125
124
  icon: 'ph--warning--regular',
126
125
  duration: 5_000,
127
126
  title: ['clear-synced-videos-error.title', { ns: meta.id }],
@@ -135,7 +134,7 @@ export default Capability.makeModule(
135
134
  icon: 'ph--trash--regular',
136
135
  disposition: 'list-item',
137
136
  },
138
- },
137
+ }),
139
138
  ]),
140
139
  }),
141
140
  ]);
@@ -7,47 +7,46 @@ import React from 'react';
7
7
 
8
8
  import { Capabilities, Capability } from '@dxos/app-framework';
9
9
  import { Surface } from '@dxos/app-framework/ui';
10
+ import { AppSurface } from '@dxos/app-toolkit/ui';
10
11
 
11
- import { ChannelArticle, ChannelSettings, VideoArticle, VideoCard } from '#containers';
12
- import { meta } from '#meta';
12
+ import { ChannelArticle, ChannelProperties, VideoArticle, VideoCard } from '#containers';
13
13
  import { Channel, Video } from '#types';
14
14
 
15
15
  export default Capability.makeModule(() =>
16
16
  Effect.succeed(
17
17
  Capability.contributes(Capabilities.ReactSurface, [
18
18
  Surface.create({
19
- id: `${meta.id}.channel`,
20
- role: ['article'],
21
- filter: (data): data is { attendableId?: string; subject: Channel.YouTubeChannel } =>
22
- Channel.instanceOf(data.subject),
19
+ id: 'channel',
20
+ filter: AppSurface.object(AppSurface.Article, Channel.YouTubeChannel),
23
21
  component: ({ data }) => {
24
22
  return <ChannelArticle subject={data.subject} attendableId={data.attendableId} />;
25
23
  },
26
24
  }),
27
25
  Surface.create({
28
- id: `${meta.id}.video`,
29
- role: ['article', 'section'],
30
- filter: (
31
- data,
32
- ): data is { attendableId: string; subject: Video.YouTubeVideo; companionTo: Channel.YouTubeChannel } =>
33
- typeof data.attendableId === 'string' &&
34
- Video.instanceOf(data.subject) &&
35
- Channel.instanceOf(data.companionTo),
26
+ id: 'video',
27
+ filter: AppSurface.oneOf(
28
+ AppSurface.allOf(
29
+ AppSurface.object(AppSurface.Article, Video.YouTubeVideo),
30
+ AppSurface.companion(AppSurface.Article, Channel.YouTubeChannel),
31
+ ),
32
+ AppSurface.allOf(
33
+ AppSurface.object(AppSurface.Section, Video.YouTubeVideo),
34
+ AppSurface.companion(AppSurface.Section, Channel.YouTubeChannel),
35
+ ),
36
+ ),
36
37
  component: ({ data: { attendableId, companionTo, subject }, role }) => {
37
38
  return <VideoArticle role={role} subject={subject} companionTo={companionTo} attendableId={attendableId} />;
38
39
  },
39
40
  }),
40
41
  Surface.create({
41
- id: `${meta.id}.video-card`,
42
- role: 'card--content',
43
- filter: (data): data is { subject: Video.YouTubeVideo } => Video.instanceOf(data?.subject),
42
+ id: 'video-card',
43
+ filter: AppSurface.object(AppSurface.Card, Video.YouTubeVideo),
44
44
  component: ({ data: { subject }, role }) => <VideoCard subject={subject} role={role} />,
45
45
  }),
46
46
  Surface.create({
47
- id: `${meta.id}.channel.companion.settings`,
48
- role: 'object-settings',
49
- filter: (data): data is { subject: Channel.YouTubeChannel } => Channel.instanceOf(data.subject),
50
- component: ({ data }) => <ChannelSettings subject={data.subject} />,
47
+ id: 'channel-properties',
48
+ filter: AppSurface.object(AppSurface.ObjectProperties, Channel.YouTubeChannel),
49
+ component: ({ data }) => <ChannelProperties subject={data.subject} />,
51
50
  }),
52
51
  ]),
53
52
  ),
@@ -13,7 +13,7 @@ import * as Video from '../../types/Video';
13
13
 
14
14
  export type ChannelArticleProps = {
15
15
  subject: Channel.YouTubeChannel;
16
- attendableId?: string;
16
+ attendableId: string;
17
17
  };
18
18
 
19
19
  export const ChannelArticle = ({ subject: channel }: ChannelArticleProps) => {
@@ -2,6 +2,4 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { ChannelArticle } from './ChannelArticle';
6
-
7
- export default ChannelArticle;
5
+ export { ChannelArticle as default } from './ChannelArticle';
@@ -6,11 +6,11 @@ import React from 'react';
6
6
 
7
7
  import * as Channel from '../../types/Channel';
8
8
 
9
- export type ChannelSettingsProps = {
9
+ export type ChannelPropertiesProps = {
10
10
  subject: Channel.YouTubeChannel;
11
11
  };
12
12
 
13
- export const ChannelSettings = ({ subject: channel }: ChannelSettingsProps) => {
13
+ export const ChannelProperties = ({ subject: channel }: ChannelPropertiesProps) => {
14
14
  return (
15
15
  <div className='flex flex-col gap-4 p-4'>
16
16
  <h3 className='text-md font-medium'>Channel Settings</h3>
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export { ChannelProperties as default } from './ChannelProperties';
@@ -4,12 +4,12 @@
4
4
 
5
5
  import React, { useState } from 'react';
6
6
 
7
- import { type CompanionSurfaceProps } from '@dxos/app-toolkit/ui';
7
+ import { type AppSurface } from '@dxos/app-toolkit/ui';
8
8
  import { Icon, Panel } from '@dxos/react-ui';
9
9
 
10
10
  import * as Video from '../../types/Video';
11
11
 
12
- export type VideoArticleProps = CompanionSurfaceProps<Video.YouTubeVideo>;
12
+ export type VideoArticleProps = AppSurface.ObjectArticleProps<Video.YouTubeVideo>;
13
13
 
14
14
  export const VideoArticle = ({ subject: video, role }: VideoArticleProps) => {
15
15
  const [showPlayer, setShowPlayer] = useState(false);
@@ -2,6 +2,4 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { VideoArticle } from './VideoArticle';
6
-
7
- export default VideoArticle;
5
+ export { VideoArticle as default } from './VideoArticle';
@@ -4,12 +4,12 @@
4
4
 
5
5
  import React, { useState } from 'react';
6
6
 
7
- import { type ObjectSurfaceProps } from '@dxos/app-toolkit/ui';
7
+ import { type AppSurface } from '@dxos/app-toolkit/ui';
8
8
  import { Card, Icon } from '@dxos/react-ui';
9
9
 
10
10
  import * as Video from '../../types/Video';
11
11
 
12
- export type VideoCardProps = ObjectSurfaceProps<Video.YouTubeVideo>;
12
+ export type VideoCardProps = AppSurface.ObjectCardProps<Video.YouTubeVideo>;
13
13
 
14
14
  /**
15
15
  * YouTube video card with embedded player.
@@ -2,6 +2,4 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { VideoCard } from './VideoCard';
6
-
7
- export default VideoCard;
5
+ export { VideoCard as default } from './VideoCard';
@@ -5,18 +5,18 @@
5
5
  import { type ComponentType, lazy, type LazyExoticComponent } from 'react';
6
6
 
7
7
  import type { ChannelArticleProps } from './ChannelArticle/ChannelArticle';
8
- import type { ChannelSettingsProps } from './ChannelSettings/ChannelSettings';
8
+ import type { ChannelPropertiesProps } from './ChannelProperties/ChannelProperties';
9
9
  import type { VideoArticleProps } from './VideoArticle/VideoArticle';
10
10
  import type { VideoCardProps } from './VideoCard/VideoCard';
11
11
 
12
- export type { ChannelArticleProps, ChannelSettingsProps, VideoArticleProps, VideoCardProps };
12
+ export type { ChannelArticleProps, ChannelPropertiesProps, VideoArticleProps, VideoCardProps };
13
13
 
14
14
  export const ChannelArticle: LazyExoticComponent<ComponentType<ChannelArticleProps>> = lazy(
15
15
  () => import('./ChannelArticle'),
16
16
  );
17
17
 
18
- export const ChannelSettings: LazyExoticComponent<ComponentType<ChannelSettingsProps>> = lazy(
19
- () => import('./ChannelSettings'),
18
+ export const ChannelProperties: LazyExoticComponent<ComponentType<ChannelPropertiesProps>> = lazy(
19
+ () => import('./ChannelProperties'),
20
20
  );
21
21
 
22
22
  export const VideoArticle: LazyExoticComponent<ComponentType<VideoArticleProps>> = lazy(() => import('./VideoArticle'));
@@ -13,7 +13,6 @@ import { withAuthorization } from '@dxos/functions';
13
13
  import { log } from '@dxos/log';
14
14
 
15
15
  import { GoogleCredentials } from '../../services/google-credentials';
16
-
17
16
  import {
18
17
  ChannelsResponse,
19
18
  ErrorResponse,
@@ -9,7 +9,6 @@ import { log } from '@dxos/log';
9
9
  import { Operation } from '@dxos/operation';
10
10
 
11
11
  import { Channel, Video } from '../types';
12
-
13
12
  import { ClearSyncedVideos } from './definitions';
14
13
 
15
14
  const handler: Operation.WithHandler<typeof ClearSyncedVideos> = ClearSyncedVideos.pipe(
@@ -26,9 +25,9 @@ const handler: Operation.WithHandler<typeof ClearSyncedVideos> = ClearSyncedVide
26
25
  yield* Database.add(newFeed);
27
26
  Obj.setParent(newFeed, channel);
28
27
 
29
- Obj.change(channel, (mutable) => {
30
- mutable.feed = Ref.make(newFeed);
31
- delete mutable.lastSyncedAt;
28
+ Obj.change(channel, (channel) => {
29
+ channel.feed = Ref.make(newFeed);
30
+ delete channel.lastSyncedAt;
32
31
  });
33
32
 
34
33
  if (videos.length > 0) {
@@ -2,11 +2,11 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Operation } from '@dxos/operation';
6
5
  import * as Schema from 'effect/Schema';
7
6
 
8
7
  import { Database, Feed, Ref } from '@dxos/echo';
9
8
  import { CredentialsService } from '@dxos/functions';
9
+ import { Operation } from '@dxos/operation';
10
10
 
11
11
  import { Channel, Video } from '../types';
12
12
 
@@ -40,7 +40,7 @@ export const Sync = Operation.make({
40
40
  channelTitle: Schema.String.pipe(Schema.optional),
41
41
  }),
42
42
  types: [Channel.YouTubeChannel, Video.YouTubeVideo],
43
- services: [Database.Service, Feed.Service, CredentialsService],
43
+ services: [Database.Service, Feed.FeedService, CredentialsService],
44
44
  });
45
45
 
46
46
  export const ClearSyncedVideos = Operation.make({
@@ -58,5 +58,5 @@ export const ClearSyncedVideos = Operation.make({
58
58
  removedVideos: Schema.Number,
59
59
  }),
60
60
  types: [Channel.YouTubeChannel, Video.YouTubeVideo],
61
- services: [Database.Service, Feed.Service],
61
+ services: [Database.Service, Feed.FeedService],
62
62
  });
@@ -16,7 +16,6 @@ import { log } from '@dxos/log';
16
16
  import { Operation } from '@dxos/operation';
17
17
 
18
18
  import { Channel, Video } from '../types';
19
-
20
19
  import { YouTube } from './apis';
21
20
  import { Sync } from './definitions';
22
21
  import { GoogleCredentials } from './services/google-credentials';
@@ -1,7 +0,0 @@
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';\n\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, (mutable) => {\n mutable.feed = Ref.make(newFeed);\n delete mutable.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;;AAM1B,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,CAACyB,YAAAA;AACnBA,YAAQhB,OAAOiB,IAAIN,KAAKD,OAAAA;AACxB,WAAOM,QAAQE;EACjB,CAAA;AAEA,MAAIjB,OAAOQ,SAAS,GAAG;AACrB,WAAOP,KAAKiB,OAAOpB,SAASE,MAAAA;EAC9B;AAEA,aAAWmB,SAASnB,QAAQ;AAC1B,WAAOJ,SAASsB,OAAOC,KAAAA;EACzB;AAEA,SAAOvB,SAASsB,OAAOpB,OAAAA;AAEvBL,MAAI,iCAAiC;IAAE2B,eAAepB,OAAOQ;EAAO,GAAA;;;;;;AACpE,SAAO;IAAEY,eAAepB,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", "mutable", "Ref", "lastSyncedAt", "remove", "video", "removedVideos"]
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,7 +0,0 @@
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';\n\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, (mutable) => {\n mutable.feed = Ref.make(newFeed);\n delete mutable.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;;AAM1B,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,CAACyB,YAAAA;AACnBA,YAAQhB,OAAOiB,IAAIN,KAAKD,OAAAA;AACxB,WAAOM,QAAQE;EACjB,CAAA;AAEA,MAAIjB,OAAOQ,SAAS,GAAG;AACrB,WAAOP,KAAKiB,OAAOpB,SAASE,MAAAA;EAC9B;AAEA,aAAWmB,SAASnB,QAAQ;AAC1B,WAAOJ,SAASsB,OAAOC,KAAAA;EACzB;AAEA,SAAOvB,SAASsB,OAAOpB,OAAAA;AAEvBL,MAAI,iCAAiC;IAAE2B,eAAepB,OAAOQ;EAAO,GAAA;;;;;;AACpE,SAAO;IAAEY,eAAepB,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", "mutable", "Ref", "lastSyncedAt", "remove", "video", "removedVideos"]
7
- }