diff options
-rw-r--r-- | server/helpers/youtube-dl/youtube-dl-info-builder.ts | 4 | ||||
-rw-r--r-- | server/lib/job-queue/handlers/video-channel-import.ts | 4 | ||||
-rw-r--r-- | server/lib/sync-channel.ts | 5 | ||||
-rw-r--r-- | server/lib/video-import.ts | 2 | ||||
-rw-r--r-- | server/tests/api/videos/video-channel-syncs.ts | 138 |
5 files changed, 106 insertions, 47 deletions
diff --git a/server/helpers/youtube-dl/youtube-dl-info-builder.ts b/server/helpers/youtube-dl/youtube-dl-info-builder.ts index 303e4051f..a74904e43 100644 --- a/server/helpers/youtube-dl/youtube-dl-info-builder.ts +++ b/server/helpers/youtube-dl/youtube-dl-info-builder.ts | |||
@@ -12,7 +12,7 @@ type YoutubeDLInfo = { | |||
12 | tags?: string[] | 12 | tags?: string[] |
13 | thumbnailUrl?: string | 13 | thumbnailUrl?: string |
14 | ext?: string | 14 | ext?: string |
15 | originallyPublishedAt?: Date | 15 | originallyPublishedAtWithoutTime?: Date |
16 | webpageUrl?: string | 16 | webpageUrl?: string |
17 | 17 | ||
18 | urls?: string[] | 18 | urls?: string[] |
@@ -81,7 +81,7 @@ class YoutubeDLInfoBuilder { | |||
81 | tags: this.getTags(obj.tags), | 81 | tags: this.getTags(obj.tags), |
82 | thumbnailUrl: obj.thumbnail || undefined, | 82 | thumbnailUrl: obj.thumbnail || undefined, |
83 | urls: this.buildAvailableUrl(obj), | 83 | urls: this.buildAvailableUrl(obj), |
84 | originallyPublishedAt: this.buildOriginallyPublishedAt(obj), | 84 | originallyPublishedAtWithoutTime: this.buildOriginallyPublishedAt(obj), |
85 | ext: obj.ext, | 85 | ext: obj.ext, |
86 | webpageUrl: obj.webpage_url | 86 | webpageUrl: obj.webpage_url |
87 | } | 87 | } |
diff --git a/server/lib/job-queue/handlers/video-channel-import.ts b/server/lib/job-queue/handlers/video-channel-import.ts index 9aaad659e..600292844 100644 --- a/server/lib/job-queue/handlers/video-channel-import.ts +++ b/server/lib/job-queue/handlers/video-channel-import.ts | |||
@@ -5,7 +5,7 @@ import { synchronizeChannel } from '@server/lib/sync-channel' | |||
5 | import { VideoChannelModel } from '@server/models/video/video-channel' | 5 | import { VideoChannelModel } from '@server/models/video/video-channel' |
6 | import { VideoChannelSyncModel } from '@server/models/video/video-channel-sync' | 6 | import { VideoChannelSyncModel } from '@server/models/video/video-channel-sync' |
7 | import { MChannelSync } from '@server/types/models' | 7 | import { MChannelSync } from '@server/types/models' |
8 | import { VideoChannelImportPayload } from '@shared/models' | 8 | import { VideoChannelImportPayload, VideoChannelSyncState } from '@shared/models' |
9 | 9 | ||
10 | export async function processVideoChannelImport (job: Job) { | 10 | export async function processVideoChannelImport (job: Job) { |
11 | const payload = job.data as VideoChannelImportPayload | 11 | const payload = job.data as VideoChannelImportPayload |
@@ -42,5 +42,7 @@ export async function processVideoChannelImport (job: Job) { | |||
42 | }) | 42 | }) |
43 | } catch (err) { | 43 | } catch (err) { |
44 | logger.error(`Failed to import channel ${videoChannel.name}`, { err }) | 44 | logger.error(`Failed to import channel ${videoChannel.name}`, { err }) |
45 | channelSync.state = VideoChannelSyncState.FAILED | ||
46 | await channelSync.save() | ||
45 | } | 47 | } |
46 | } | 48 | } |
diff --git a/server/lib/sync-channel.ts b/server/lib/sync-channel.ts index 3a81daac0..6cd1fc26d 100644 --- a/server/lib/sync-channel.ts +++ b/server/lib/sync-channel.ts | |||
@@ -93,7 +93,10 @@ async function skipImport (channel: MChannel, targetUrl: string, onlyAfter?: Dat | |||
93 | 93 | ||
94 | const videoInfo = await youtubeDL.getInfoForDownload() | 94 | const videoInfo = await youtubeDL.getInfoForDownload() |
95 | 95 | ||
96 | if (videoInfo.originallyPublishedAt.getTime() < onlyAfter.getTime()) { | 96 | const onlyAfterWithoutTime = new Date(onlyAfter) |
97 | onlyAfterWithoutTime.setHours(0, 0, 0) | ||
98 | |||
99 | if (videoInfo.originallyPublishedAtWithoutTime.getTime() >= onlyAfterWithoutTime.getTime()) { | ||
97 | return true | 100 | return true |
98 | } | 101 | } |
99 | } | 102 | } |
diff --git a/server/lib/video-import.ts b/server/lib/video-import.ts index de95116aa..d9da09040 100644 --- a/server/lib/video-import.ts +++ b/server/lib/video-import.ts | |||
@@ -125,7 +125,7 @@ async function buildVideoFromImport ({ channelId, importData, importDataOverride | |||
125 | channelId, | 125 | channelId, |
126 | originallyPublishedAt: importDataOverride?.originallyPublishedAt | 126 | originallyPublishedAt: importDataOverride?.originallyPublishedAt |
127 | ? new Date(importDataOverride?.originallyPublishedAt) | 127 | ? new Date(importDataOverride?.originallyPublishedAt) |
128 | : importData.originallyPublishedAt | 128 | : importData.originallyPublishedAtWithoutTime |
129 | } | 129 | } |
130 | 130 | ||
131 | videoData = await Hooks.wrapObject( | 131 | videoData = await Hooks.wrapObject( |
diff --git a/server/tests/api/videos/video-channel-syncs.ts b/server/tests/api/videos/video-channel-syncs.ts index 4e18ddfc7..65b143a4f 100644 --- a/server/tests/api/videos/video-channel-syncs.ts +++ b/server/tests/api/videos/video-channel-syncs.ts | |||
@@ -3,9 +3,9 @@ import { FIXTURE_URLS } from '@server/tests/shared' | |||
3 | import { areHttpImportTestsDisabled } from '@shared/core-utils' | 3 | import { areHttpImportTestsDisabled } from '@shared/core-utils' |
4 | import { HttpStatusCode, VideoChannelSyncState, VideoInclude, VideoPrivacy } from '@shared/models' | 4 | import { HttpStatusCode, VideoChannelSyncState, VideoInclude, VideoPrivacy } from '@shared/models' |
5 | import { | 5 | import { |
6 | ChannelSyncsCommand, | 6 | createMultipleServers, |
7 | createSingleServer, | ||
8 | getServerImportConfig, | 7 | getServerImportConfig, |
8 | killallServers, | ||
9 | PeerTubeServer, | 9 | PeerTubeServer, |
10 | setAccessTokensToServers, | 10 | setAccessTokensToServers, |
11 | setDefaultAccountAvatar, | 11 | setDefaultAccountAvatar, |
@@ -20,8 +20,7 @@ describe('Test channel synchronizations', function () { | |||
20 | function runSuite (mode: 'youtube-dl' | 'yt-dlp') { | 20 | function runSuite (mode: 'youtube-dl' | 'yt-dlp') { |
21 | 21 | ||
22 | describe('Sync using ' + mode, function () { | 22 | describe('Sync using ' + mode, function () { |
23 | let server: PeerTubeServer | 23 | let servers: PeerTubeServer[] |
24 | let command: ChannelSyncsCommand | ||
25 | 24 | ||
26 | let startTestDate: Date | 25 | let startTestDate: Date |
27 | 26 | ||
@@ -35,7 +34,7 @@ describe('Test channel synchronizations', function () { | |||
35 | } | 34 | } |
36 | 35 | ||
37 | async function changeDateForSync (channelSyncId: number, newDate: string) { | 36 | async function changeDateForSync (channelSyncId: number, newDate: string) { |
38 | await server.sql.updateQuery( | 37 | await servers[0].sql.updateQuery( |
39 | `UPDATE "videoChannelSync" ` + | 38 | `UPDATE "videoChannelSync" ` + |
40 | `SET "createdAt"='${newDate}', "lastSyncAt"='${newDate}' ` + | 39 | `SET "createdAt"='${newDate}', "lastSyncAt"='${newDate}' ` + |
41 | `WHERE id=${channelSyncId}` | 40 | `WHERE id=${channelSyncId}` |
@@ -43,25 +42,23 @@ describe('Test channel synchronizations', function () { | |||
43 | } | 42 | } |
44 | 43 | ||
45 | before(async function () { | 44 | before(async function () { |
46 | this.timeout(120_000) | 45 | this.timeout(240_000) |
47 | 46 | ||
48 | startTestDate = new Date() | 47 | startTestDate = new Date() |
49 | 48 | ||
50 | server = await createSingleServer(1, getServerImportConfig(mode)) | 49 | servers = await createMultipleServers(2, getServerImportConfig(mode)) |
51 | |||
52 | await setAccessTokensToServers([ server ]) | ||
53 | await setDefaultVideoChannel([ server ]) | ||
54 | await setDefaultChannelAvatar([ server ]) | ||
55 | await setDefaultAccountAvatar([ server ]) | ||
56 | 50 | ||
57 | await server.config.enableChannelSync() | 51 | await setAccessTokensToServers(servers) |
52 | await setDefaultVideoChannel(servers) | ||
53 | await setDefaultChannelAvatar(servers) | ||
54 | await setDefaultAccountAvatar(servers) | ||
58 | 55 | ||
59 | command = server.channelSyncs | 56 | await servers[0].config.enableChannelSync() |
60 | 57 | ||
61 | { | 58 | { |
62 | userInfo.accessToken = await server.users.generateUserAndToken(userInfo.username) | 59 | userInfo.accessToken = await servers[0].users.generateUserAndToken(userInfo.username) |
63 | 60 | ||
64 | const { videoChannels } = await server.users.getMyInfo({ token: userInfo.accessToken }) | 61 | const { videoChannels } = await servers[0].users.getMyInfo({ token: userInfo.accessToken }) |
65 | userInfo.channelId = videoChannels[0].id | 62 | userInfo.channelId = videoChannels[0].id |
66 | } | 63 | } |
67 | }) | 64 | }) |
@@ -70,9 +67,9 @@ describe('Test channel synchronizations', function () { | |||
70 | this.timeout(120_000) | 67 | this.timeout(120_000) |
71 | 68 | ||
72 | { | 69 | { |
73 | const { video } = await server.imports.importVideo({ | 70 | const { video } = await servers[0].imports.importVideo({ |
74 | attributes: { | 71 | attributes: { |
75 | channelId: server.store.channel.id, | 72 | channelId: servers[0].store.channel.id, |
76 | privacy: VideoPrivacy.PUBLIC, | 73 | privacy: VideoPrivacy.PUBLIC, |
77 | targetUrl: FIXTURE_URLS.youtube | 74 | targetUrl: FIXTURE_URLS.youtube |
78 | } | 75 | } |
@@ -80,16 +77,16 @@ describe('Test channel synchronizations', function () { | |||
80 | 77 | ||
81 | expect(video.name).to.equal('small video - youtube') | 78 | expect(video.name).to.equal('small video - youtube') |
82 | 79 | ||
83 | const { total } = await server.videos.listByChannel({ handle: 'root_channel', include: VideoInclude.NOT_PUBLISHED_STATE }) | 80 | const { total } = await servers[0].videos.listByChannel({ handle: 'root_channel', include: VideoInclude.NOT_PUBLISHED_STATE }) |
84 | expect(total).to.equal(1) | 81 | expect(total).to.equal(1) |
85 | } | 82 | } |
86 | 83 | ||
87 | const { videoChannelSync } = await command.create({ | 84 | const { videoChannelSync } = await servers[0].channelSyncs.create({ |
88 | attributes: { | 85 | attributes: { |
89 | externalChannelUrl: FIXTURE_URLS.youtubeChannel, | 86 | externalChannelUrl: FIXTURE_URLS.youtubeChannel, |
90 | videoChannelId: server.store.channel.id | 87 | videoChannelId: servers[0].store.channel.id |
91 | }, | 88 | }, |
92 | token: server.accessToken, | 89 | token: servers[0].accessToken, |
93 | expectedStatus: HttpStatusCode.OK_200 | 90 | expectedStatus: HttpStatusCode.OK_200 |
94 | }) | 91 | }) |
95 | rootChannelSyncId = videoChannelSync.id | 92 | rootChannelSyncId = videoChannelSync.id |
@@ -97,16 +94,19 @@ describe('Test channel synchronizations', function () { | |||
97 | // Ensure any missing video not already fetched will be considered as new | 94 | // Ensure any missing video not already fetched will be considered as new |
98 | await changeDateForSync(videoChannelSync.id, '1970-01-01') | 95 | await changeDateForSync(videoChannelSync.id, '1970-01-01') |
99 | 96 | ||
100 | await server.debug.sendCommand({ | 97 | await servers[0].debug.sendCommand({ |
101 | body: { | 98 | body: { |
102 | command: 'process-video-channel-sync-latest' | 99 | command: 'process-video-channel-sync-latest' |
103 | } | 100 | } |
104 | }) | 101 | }) |
105 | 102 | ||
106 | { | 103 | { |
107 | await waitJobs(server) | 104 | await waitJobs(servers) |
108 | 105 | ||
109 | const { total, data } = await server.videos.listByChannel({ handle: 'root_channel', include: VideoInclude.NOT_PUBLISHED_STATE }) | 106 | const { total, data } = await servers[0].videos.listByChannel({ |
107 | handle: 'root_channel', | ||
108 | include: VideoInclude.NOT_PUBLISHED_STATE | ||
109 | }) | ||
110 | expect(total).to.equal(2) | 110 | expect(total).to.equal(2) |
111 | expect(data[0].name).to.equal('test') | 111 | expect(data[0].name).to.equal('test') |
112 | } | 112 | } |
@@ -115,18 +115,18 @@ describe('Test channel synchronizations', function () { | |||
115 | it('Should add another synchronization', async function () { | 115 | it('Should add another synchronization', async function () { |
116 | const externalChannelUrl = FIXTURE_URLS.youtubeChannel + '?foo=bar' | 116 | const externalChannelUrl = FIXTURE_URLS.youtubeChannel + '?foo=bar' |
117 | 117 | ||
118 | const { videoChannelSync } = await command.create({ | 118 | const { videoChannelSync } = await servers[0].channelSyncs.create({ |
119 | attributes: { | 119 | attributes: { |
120 | externalChannelUrl, | 120 | externalChannelUrl, |
121 | videoChannelId: server.store.channel.id | 121 | videoChannelId: servers[0].store.channel.id |
122 | }, | 122 | }, |
123 | token: server.accessToken, | 123 | token: servers[0].accessToken, |
124 | expectedStatus: HttpStatusCode.OK_200 | 124 | expectedStatus: HttpStatusCode.OK_200 |
125 | }) | 125 | }) |
126 | 126 | ||
127 | expect(videoChannelSync.externalChannelUrl).to.equal(externalChannelUrl) | 127 | expect(videoChannelSync.externalChannelUrl).to.equal(externalChannelUrl) |
128 | expect(videoChannelSync.channel).to.include({ | 128 | expect(videoChannelSync.channel).to.include({ |
129 | id: server.store.channel.id, | 129 | id: servers[0].store.channel.id, |
130 | name: 'root_channel' | 130 | name: 'root_channel' |
131 | }) | 131 | }) |
132 | expect(videoChannelSync.state.id).to.equal(VideoChannelSyncState.WAITING_FIRST_RUN) | 132 | expect(videoChannelSync.state.id).to.equal(VideoChannelSyncState.WAITING_FIRST_RUN) |
@@ -134,7 +134,7 @@ describe('Test channel synchronizations', function () { | |||
134 | }) | 134 | }) |
135 | 135 | ||
136 | it('Should add a synchronization for another user', async function () { | 136 | it('Should add a synchronization for another user', async function () { |
137 | const { videoChannelSync } = await command.create({ | 137 | const { videoChannelSync } = await servers[0].channelSyncs.create({ |
138 | attributes: { | 138 | attributes: { |
139 | externalChannelUrl: FIXTURE_URLS.youtubeChannel + '?baz=qux', | 139 | externalChannelUrl: FIXTURE_URLS.youtubeChannel + '?baz=qux', |
140 | videoChannelId: userInfo.channelId | 140 | videoChannelId: userInfo.channelId |
@@ -145,9 +145,9 @@ describe('Test channel synchronizations', function () { | |||
145 | }) | 145 | }) |
146 | 146 | ||
147 | it('Should not import a channel if not asked', async function () { | 147 | it('Should not import a channel if not asked', async function () { |
148 | await waitJobs(server) | 148 | await waitJobs(servers) |
149 | 149 | ||
150 | const { data } = await command.listByAccount({ accountName: userInfo.username }) | 150 | const { data } = await servers[0].channelSyncs.listByAccount({ accountName: userInfo.username }) |
151 | 151 | ||
152 | expect(data[0].state).to.contain({ | 152 | expect(data[0].state).to.contain({ |
153 | id: VideoChannelSyncState.WAITING_FIRST_RUN, | 153 | id: VideoChannelSyncState.WAITING_FIRST_RUN, |
@@ -160,15 +160,15 @@ describe('Test channel synchronizations', function () { | |||
160 | 160 | ||
161 | await changeDateForSync(userInfo.syncId, '2019-03-01') | 161 | await changeDateForSync(userInfo.syncId, '2019-03-01') |
162 | 162 | ||
163 | await server.debug.sendCommand({ | 163 | await servers[0].debug.sendCommand({ |
164 | body: { | 164 | body: { |
165 | command: 'process-video-channel-sync-latest' | 165 | command: 'process-video-channel-sync-latest' |
166 | } | 166 | } |
167 | }) | 167 | }) |
168 | 168 | ||
169 | await waitJobs(server) | 169 | await waitJobs(servers) |
170 | 170 | ||
171 | const { data, total } = await server.videos.listByChannel({ | 171 | const { data, total } = await servers[0].videos.listByChannel({ |
172 | handle: userInfo.channelName, | 172 | handle: userInfo.channelName, |
173 | include: VideoInclude.NOT_PUBLISHED_STATE | 173 | include: VideoInclude.NOT_PUBLISHED_STATE |
174 | }) | 174 | }) |
@@ -180,7 +180,7 @@ describe('Test channel synchronizations', function () { | |||
180 | it('Should list channel synchronizations', async function () { | 180 | it('Should list channel synchronizations', async function () { |
181 | // Root | 181 | // Root |
182 | { | 182 | { |
183 | const { total, data } = await command.listByAccount({ accountName: 'root' }) | 183 | const { total, data } = await servers[0].channelSyncs.listByAccount({ accountName: 'root' }) |
184 | expect(total).to.equal(2) | 184 | expect(total).to.equal(2) |
185 | 185 | ||
186 | expect(data[0]).to.deep.contain({ | 186 | expect(data[0]).to.deep.contain({ |
@@ -193,13 +193,13 @@ describe('Test channel synchronizations', function () { | |||
193 | 193 | ||
194 | expect(new Date(data[0].lastSyncAt)).to.be.greaterThan(startTestDate) | 194 | expect(new Date(data[0].lastSyncAt)).to.be.greaterThan(startTestDate) |
195 | 195 | ||
196 | expect(data[0].channel).to.contain({ id: server.store.channel.id }) | 196 | expect(data[0].channel).to.contain({ id: servers[0].store.channel.id }) |
197 | expect(data[1]).to.contain({ externalChannelUrl: FIXTURE_URLS.youtubeChannel + '?foo=bar' }) | 197 | expect(data[1]).to.contain({ externalChannelUrl: FIXTURE_URLS.youtubeChannel + '?foo=bar' }) |
198 | } | 198 | } |
199 | 199 | ||
200 | // User | 200 | // User |
201 | { | 201 | { |
202 | const { total, data } = await command.listByAccount({ accountName: userInfo.username }) | 202 | const { total, data } = await servers[0].channelSyncs.listByAccount({ accountName: userInfo.username }) |
203 | expect(total).to.equal(1) | 203 | expect(total).to.equal(1) |
204 | expect(data[0]).to.deep.contain({ | 204 | expect(data[0]).to.deep.contain({ |
205 | externalChannelUrl: FIXTURE_URLS.youtubeChannel + '?baz=qux', | 205 | externalChannelUrl: FIXTURE_URLS.youtubeChannel + '?baz=qux', |
@@ -212,7 +212,7 @@ describe('Test channel synchronizations', function () { | |||
212 | }) | 212 | }) |
213 | 213 | ||
214 | it('Should list imports of a channel synchronization', async function () { | 214 | it('Should list imports of a channel synchronization', async function () { |
215 | const { total, data } = await server.imports.getMyVideoImports({ videoChannelSyncId: rootChannelSyncId }) | 215 | const { total, data } = await servers[0].imports.getMyVideoImports({ videoChannelSyncId: rootChannelSyncId }) |
216 | 216 | ||
217 | expect(total).to.equal(1) | 217 | expect(total).to.equal(1) |
218 | expect(data).to.have.lengthOf(1) | 218 | expect(data).to.have.lengthOf(1) |
@@ -220,14 +220,68 @@ describe('Test channel synchronizations', function () { | |||
220 | }) | 220 | }) |
221 | 221 | ||
222 | it('Should remove user\'s channel synchronizations', async function () { | 222 | it('Should remove user\'s channel synchronizations', async function () { |
223 | await command.delete({ channelSyncId: userInfo.syncId }) | 223 | await servers[0].channelSyncs.delete({ channelSyncId: userInfo.syncId }) |
224 | 224 | ||
225 | const { total } = await command.listByAccount({ accountName: userInfo.username }) | 225 | const { total } = await servers[0].channelSyncs.listByAccount({ accountName: userInfo.username }) |
226 | expect(total).to.equal(0) | 226 | expect(total).to.equal(0) |
227 | }) | 227 | }) |
228 | 228 | ||
229 | // FIXME: youtube-dl doesn't work when speicifying a port after the hostname | ||
230 | // it('Should import a remote PeerTube channel', async function () { | ||
231 | // this.timeout(240_000) | ||
232 | |||
233 | // await servers[1].videos.quickUpload({ name: 'remote 1' }) | ||
234 | // await waitJobs(servers) | ||
235 | |||
236 | // const { videoChannelSync } = await servers[0].channelSyncs.create({ | ||
237 | // attributes: { | ||
238 | // externalChannelUrl: servers[1].url + '/c/root_channel', | ||
239 | // videoChannelId: userInfo.channelId | ||
240 | // }, | ||
241 | // token: userInfo.accessToken | ||
242 | // }) | ||
243 | // await servers[0].channels.importVideos({ | ||
244 | // channelName: userInfo.channelName, | ||
245 | // externalChannelUrl: servers[1].url + '/c/root_channel', | ||
246 | // videoChannelSyncId: videoChannelSync.id, | ||
247 | // token: userInfo.accessToken | ||
248 | // }) | ||
249 | |||
250 | // await waitJobs(servers) | ||
251 | |||
252 | // const { data, total } = await servers[0].videos.listByChannel({ | ||
253 | // handle: userInfo.channelName, | ||
254 | // include: VideoInclude.NOT_PUBLISHED_STATE | ||
255 | // }) | ||
256 | |||
257 | // expect(total).to.equal(2) | ||
258 | // expect(data[0].name).to.equal('remote 1') | ||
259 | // }) | ||
260 | |||
261 | // it('Should keep synced a remote PeerTube channel', async function () { | ||
262 | // this.timeout(240_000) | ||
263 | |||
264 | // await servers[1].videos.quickUpload({ name: 'remote 2' }) | ||
265 | // await waitJobs(servers) | ||
266 | |||
267 | // await servers[0].debug.sendCommand({ | ||
268 | // body: { | ||
269 | // command: 'process-video-channel-sync-latest' | ||
270 | // } | ||
271 | // }) | ||
272 | |||
273 | // await waitJobs(servers) | ||
274 | |||
275 | // const { data, total } = await servers[0].videos.listByChannel({ | ||
276 | // handle: userInfo.channelName, | ||
277 | // include: VideoInclude.NOT_PUBLISHED_STATE | ||
278 | // }) | ||
279 | // expect(total).to.equal(2) | ||
280 | // expect(data[0].name).to.equal('remote 2') | ||
281 | // }) | ||
282 | |||
229 | after(async function () { | 283 | after(async function () { |
230 | await server?.kill() | 284 | await killallServers(servers) |
231 | }) | 285 | }) |
232 | }) | 286 | }) |
233 | } | 287 | } |