aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/initializers
diff options
context:
space:
mode:
authorFlorent <florent.git@zeteo.me>2022-08-10 09:53:39 +0200
committerGitHub <noreply@github.com>2022-08-10 09:53:39 +0200
commit2a491182e483b97afb1b65c908b23cb48d591807 (patch)
treeec13503216ad72a3ea8f1ce3b659899f8167fb47 /server/initializers
parent06ac128958c489efe1008eeca1df683819bd2f18 (diff)
downloadPeerTube-2a491182e483b97afb1b65c908b23cb48d591807.tar.gz
PeerTube-2a491182e483b97afb1b65c908b23cb48d591807.tar.zst
PeerTube-2a491182e483b97afb1b65c908b23cb48d591807.zip
Channel sync (#5135)
* Add external channel URL for channel update / creation (#754) * Disallow synchronisation if user has no video quota (#754) * More constraints serverside (#754) * Disable sync if server configuration does not allow HTTP import (#754) * Working version synchronizing videos with a job (#754) TODO: refactoring, too much code duplication * More logs and try/catch (#754) * Fix eslint error (#754) * WIP: support synchronization time change (#754) * New frontend #754 * WIP: Create sync front (#754) * Enhance UI, sync creation form (#754) * Warning message when HTTP upload is disallowed * More consistent names (#754) * Binding Front with API (#754) * Add a /me API (#754) * Improve list UI (#754) * Implement creation and deletion routes (#754) * Lint (#754) * Lint again (#754) * WIP: UI for triggering import existing videos (#754) * Implement jobs for syncing and importing channels * Don't sync videos before sync creation + avoid concurrency issue (#754) * Cleanup (#754) * Cleanup: OpenAPI + API rework (#754) * Remove dead code (#754) * Eslint (#754) * Revert the mess with whitespaces in constants.ts (#754) * Some fixes after rebase (#754) * Several fixes after PR remarks (#754) * Front + API: Rename video-channels-sync to video-channel-syncs (#754) * Allow enabling channel sync through UI (#754) * getChannelInfo (#754) * Minor fixes: openapi + model + sql (#754) * Simplified API validators (#754) * Rename MChannelSync to MChannelSyncChannel (#754) * Add command for VideoChannelSync (#754) * Use synchronization.enabled config (#754) * Check parameters test + some fixes (#754) * Fix conflict mistake (#754) * Restrict access to video channel sync list API (#754) * Start adding unit test for synchronization (#754) * Continue testing (#754) * Tests finished + convertion of job to scheduler (#754) * Add lastSyncAt field (#754) * Fix externalRemoteUrl sort + creation date not well formatted (#754) * Small fix (#754) * Factorize addYoutubeDLImport and buildVideo (#754) * Check duplicates on channel not on users (#754) * factorize thumbnail generation (#754) * Fetch error should return status 400 (#754) * Separate video-channel-import and video-channel-sync-latest (#754) * Bump DB migration version after rebase (#754) * Prettier states in UI table (#754) * Add DefaultScope in VideoChannelSyncModel (#754) * Fix audit logs (#754) * Ensure user can upload when importing channel + minor fixes (#754) * Mark synchronization as failed on exception + typos (#754) * Change REST API for importing videos into channel (#754) * Add option for fully synchronize a chnanel (#754) * Return a whole sync object on creation to avoid tricks in Front (#754) * Various remarks (#754) * Single quotes by default (#754) * Rename synchronization to video_channel_synchronization * Add check.latest_videos_count and max_per_user options (#754) * Better channel rendering in list #754 * Allow sorting with channel name and state (#754) * Add missing tests for channel imports (#754) * Prefer using a parent job for channel sync * Styling * Client styling Co-authored-by: Chocobozzz <me@florianbigard.com>
Diffstat (limited to 'server/initializers')
-rw-r--r--server/initializers/checker-after-init.ts7
-rw-r--r--server/initializers/checker-before-init.ts2
-rw-r--r--server/initializers/config.ts9
-rw-r--r--server/initializers/constants.ts29
-rw-r--r--server/initializers/database.ts4
-rw-r--r--server/initializers/migrations/0730-video-channel-sync.ts36
6 files changed, 82 insertions, 5 deletions
diff --git a/server/initializers/checker-after-init.ts b/server/initializers/checker-after-init.ts
index f0f16d9bd..74c82541e 100644
--- a/server/initializers/checker-after-init.ts
+++ b/server/initializers/checker-after-init.ts
@@ -48,6 +48,7 @@ function checkConfig () {
48 checkRemoteRedundancyConfig() 48 checkRemoteRedundancyConfig()
49 checkStorageConfig() 49 checkStorageConfig()
50 checkTranscodingConfig() 50 checkTranscodingConfig()
51 checkImportConfig()
51 checkBroadcastMessageConfig() 52 checkBroadcastMessageConfig()
52 checkSearchConfig() 53 checkSearchConfig()
53 checkLiveConfig() 54 checkLiveConfig()
@@ -200,6 +201,12 @@ function checkTranscodingConfig () {
200 } 201 }
201} 202}
202 203
204function checkImportConfig () {
205 if (CONFIG.IMPORT.VIDEO_CHANNEL_SYNCHRONIZATION.ENABLED && !CONFIG.IMPORT.VIDEOS.HTTP) {
206 throw new Error('You need to enable HTTP import to allow synchronization')
207 }
208}
209
203function checkBroadcastMessageConfig () { 210function checkBroadcastMessageConfig () {
204 if (CONFIG.BROADCAST_MESSAGE.ENABLED) { 211 if (CONFIG.BROADCAST_MESSAGE.ENABLED) {
205 const currentLevel = CONFIG.BROADCAST_MESSAGE.LEVEL 212 const currentLevel = CONFIG.BROADCAST_MESSAGE.LEVEL
diff --git a/server/initializers/checker-before-init.ts b/server/initializers/checker-before-init.ts
index f4057b81b..3188903be 100644
--- a/server/initializers/checker-before-init.ts
+++ b/server/initializers/checker-before-init.ts
@@ -32,6 +32,8 @@ function checkMissedConfig () {
32 'transcoding.resolutions.480p', 'transcoding.resolutions.720p', 'transcoding.resolutions.1080p', 'transcoding.resolutions.1440p', 32 'transcoding.resolutions.480p', 'transcoding.resolutions.720p', 'transcoding.resolutions.1080p', 'transcoding.resolutions.1440p',
33 'transcoding.resolutions.2160p', 'transcoding.always_transcode_original_resolution', 'video_studio.enabled', 33 'transcoding.resolutions.2160p', 'transcoding.always_transcode_original_resolution', 'video_studio.enabled',
34 'import.videos.http.enabled', 'import.videos.torrent.enabled', 'import.videos.concurrency', 'import.videos.timeout', 34 'import.videos.http.enabled', 'import.videos.torrent.enabled', 'import.videos.concurrency', 'import.videos.timeout',
35 'import.video_channel_synchronization.enabled', 'import.video_channel_synchronization.max_per_user',
36 'import.video_channel_synchronization.check_interval', 'import.video_channel_synchronization.videos_limit_per_synchronization',
35 'auto_blacklist.videos.of_users.enabled', 'trending.videos.interval_days', 37 'auto_blacklist.videos.of_users.enabled', 'trending.videos.interval_days',
36 'client.videos.miniature.display_author_avatar', 38 'client.videos.miniature.display_author_avatar',
37 'client.videos.miniature.prefer_author_display_name', 'client.menu.login.redirect_on_single_external_auth', 39 'client.videos.miniature.prefer_author_display_name', 'client.menu.login.redirect_on_single_external_auth',
diff --git a/server/initializers/config.ts b/server/initializers/config.ts
index 1a0b8942c..2c92bea22 100644
--- a/server/initializers/config.ts
+++ b/server/initializers/config.ts
@@ -398,6 +398,14 @@ const CONFIG = {
398 TORRENT: { 398 TORRENT: {
399 get ENABLED () { return config.get<boolean>('import.videos.torrent.enabled') } 399 get ENABLED () { return config.get<boolean>('import.videos.torrent.enabled') }
400 } 400 }
401 },
402 VIDEO_CHANNEL_SYNCHRONIZATION: {
403 get ENABLED () { return config.get<boolean>('import.video_channel_synchronization.enabled') },
404 get MAX_PER_USER () { return config.get<number>('import.video_channel_synchronization.max_per_user') },
405 get CHECK_INTERVAL () { return parseDurationToMs(config.get<string>('import.video_channel_synchronization.check_interval')) },
406 get VIDEOS_LIMIT_PER_SYNCHRONIZATION () {
407 return config.get<number>('import.video_channel_synchronization.videos_limit_per_synchronization')
408 }
401 } 409 }
402 }, 410 },
403 AUTO_BLACKLIST: { 411 AUTO_BLACKLIST: {
@@ -499,6 +507,7 @@ const CONFIG = {
499 get IS_DEFAULT_SEARCH () { return config.get<boolean>('search.search_index.is_default_search') } 507 get IS_DEFAULT_SEARCH () { return config.get<boolean>('search.search_index.is_default_search') }
500 } 508 }
501 } 509 }
510
502} 511}
503 512
504function registerConfigChangedHandler (fun: Function) { 513function registerConfigChangedHandler (fun: Function) {
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 5a5f2d666..697a64d42 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -6,6 +6,7 @@ import { randomInt, root } from '@shared/core-utils'
6import { 6import {
7 AbuseState, 7 AbuseState,
8 JobType, 8 JobType,
9 VideoChannelSyncState,
9 VideoImportState, 10 VideoImportState,
10 VideoPrivacy, 11 VideoPrivacy,
11 VideoRateType, 12 VideoRateType,
@@ -24,7 +25,7 @@ import { CONFIG, registerConfigChangedHandler } from './config'
24 25
25// --------------------------------------------------------------------------- 26// ---------------------------------------------------------------------------
26 27
27const LAST_MIGRATION_VERSION = 725 28const LAST_MIGRATION_VERSION = 730
28 29
29// --------------------------------------------------------------------------- 30// ---------------------------------------------------------------------------
30 31
@@ -64,6 +65,7 @@ const SORTABLE_COLUMNS = {
64 JOBS: [ 'createdAt' ], 65 JOBS: [ 'createdAt' ],
65 VIDEO_CHANNELS: [ 'id', 'name', 'updatedAt', 'createdAt' ], 66 VIDEO_CHANNELS: [ 'id', 'name', 'updatedAt', 'createdAt' ],
66 VIDEO_IMPORTS: [ 'createdAt' ], 67 VIDEO_IMPORTS: [ 'createdAt' ],
68 VIDEO_CHANNEL_SYNCS: [ 'externalChannelUrl', 'videoChannel', 'createdAt', 'lastSyncAt', 'state' ],
67 69
68 VIDEO_COMMENT_THREADS: [ 'createdAt', 'totalReplies' ], 70 VIDEO_COMMENT_THREADS: [ 'createdAt', 'totalReplies' ],
69 VIDEO_COMMENTS: [ 'createdAt' ], 71 VIDEO_COMMENTS: [ 'createdAt' ],
@@ -156,6 +158,8 @@ const JOB_ATTEMPTS: { [id in JobType]: number } = {
156 'video-live-ending': 1, 158 'video-live-ending': 1,
157 'video-studio-edition': 1, 159 'video-studio-edition': 1,
158 'manage-video-torrent': 1, 160 'manage-video-torrent': 1,
161 'video-channel-import': 1,
162 'after-video-channel-import': 1,
159 'move-to-object-storage': 3, 163 'move-to-object-storage': 3,
160 'notify': 1, 164 'notify': 1,
161 'federate-video': 1 165 'federate-video': 1
@@ -178,6 +182,8 @@ const JOB_CONCURRENCY: { [id in Exclude<JobType, 'video-transcoding' | 'video-im
178 'video-studio-edition': 1, 182 'video-studio-edition': 1,
179 'manage-video-torrent': 1, 183 'manage-video-torrent': 1,
180 'move-to-object-storage': 1, 184 'move-to-object-storage': 1,
185 'video-channel-import': 1,
186 'after-video-channel-import': 1,
181 'notify': 5, 187 'notify': 5,
182 'federate-video': 3 188 'federate-video': 3
183} 189}
@@ -199,9 +205,11 @@ const JOB_TTL: { [id in JobType]: number } = {
199 'video-redundancy': 1000 * 3600 * 3, // 3 hours 205 'video-redundancy': 1000 * 3600 * 3, // 3 hours
200 'video-live-ending': 1000 * 60 * 10, // 10 minutes 206 'video-live-ending': 1000 * 60 * 10, // 10 minutes
201 'manage-video-torrent': 1000 * 3600 * 3, // 3 hours 207 'manage-video-torrent': 1000 * 3600 * 3, // 3 hours
208 'move-to-object-storage': 1000 * 60 * 60 * 3, // 3 hours
209 'video-channel-import': 1000 * 60 * 60 * 4, // 4 hours
210 'after-video-channel-import': 60000 * 5, // 5 minutes
202 'notify': 60000 * 5, // 5 minutes 211 'notify': 60000 * 5, // 5 minutes
203 'federate-video': 60000 * 5, // 5 minutes 212 'federate-video': 60000 * 5 // 5 minutes
204 'move-to-object-storage': 1000 * 60 * 60 * 3 // 3 hours
205} 213}
206const REPEAT_JOBS: { [ id in JobType ]?: RepeatOptions } = { 214const REPEAT_JOBS: { [ id in JobType ]?: RepeatOptions } = {
207 'videos-views-stats': { 215 'videos-views-stats': {
@@ -246,7 +254,8 @@ const SCHEDULER_INTERVALS_MS = {
246 REMOVE_OLD_VIEWS: 60000 * 60 * 24, // 1 day 254 REMOVE_OLD_VIEWS: 60000 * 60 * 24, // 1 day
247 REMOVE_OLD_HISTORY: 60000 * 60 * 24, // 1 day 255 REMOVE_OLD_HISTORY: 60000 * 60 * 24, // 1 day
248 UPDATE_INBOX_STATS: 1000 * 60, // 1 minute 256 UPDATE_INBOX_STATS: 1000 * 60, // 1 minute
249 REMOVE_DANGLING_RESUMABLE_UPLOADS: 60000 * 60 // 1 hour 257 REMOVE_DANGLING_RESUMABLE_UPLOADS: 60000 * 60, // 1 hour
258 CHANNEL_SYNC_CHECK_INTERVAL: CONFIG.IMPORT.VIDEO_CHANNEL_SYNCHRONIZATION.CHECK_INTERVAL
250} 259}
251 260
252// --------------------------------------------------------------------------- 261// ---------------------------------------------------------------------------
@@ -276,8 +285,12 @@ const CONSTRAINTS_FIELDS = {
276 NAME: { min: 1, max: 120 }, // Length 285 NAME: { min: 1, max: 120 }, // Length
277 DESCRIPTION: { min: 3, max: 1000 }, // Length 286 DESCRIPTION: { min: 3, max: 1000 }, // Length
278 SUPPORT: { min: 3, max: 1000 }, // Length 287 SUPPORT: { min: 3, max: 1000 }, // Length
288 EXTERNAL_CHANNEL_URL: { min: 3, max: 2000 }, // Length
279 URL: { min: 3, max: 2000 } // Length 289 URL: { min: 3, max: 2000 } // Length
280 }, 290 },
291 VIDEO_CHANNEL_SYNCS: {
292 EXTERNAL_CHANNEL_URL: { min: 3, max: 2000 } // Length
293 },
281 VIDEO_CAPTIONS: { 294 VIDEO_CAPTIONS: {
282 CAPTION_FILE: { 295 CAPTION_FILE: {
283 EXTNAME: [ '.vtt', '.srt' ], 296 EXTNAME: [ '.vtt', '.srt' ],
@@ -478,6 +491,13 @@ const VIDEO_IMPORT_STATES: { [ id in VideoImportState ]: string } = {
478 [VideoImportState.PROCESSING]: 'Processing' 491 [VideoImportState.PROCESSING]: 'Processing'
479} 492}
480 493
494const VIDEO_CHANNEL_SYNC_STATE: { [ id in VideoChannelSyncState ]: string } = {
495 [VideoChannelSyncState.FAILED]: 'Failed',
496 [VideoChannelSyncState.SYNCED]: 'Synchronized',
497 [VideoChannelSyncState.PROCESSING]: 'Processing',
498 [VideoChannelSyncState.WAITING_FIRST_RUN]: 'Waiting first run'
499}
500
481const ABUSE_STATES: { [ id in AbuseState ]: string } = { 501const ABUSE_STATES: { [ id in AbuseState ]: string } = {
482 [AbuseState.PENDING]: 'Pending', 502 [AbuseState.PENDING]: 'Pending',
483 [AbuseState.REJECTED]: 'Rejected', 503 [AbuseState.REJECTED]: 'Rejected',
@@ -1005,6 +1025,7 @@ export {
1005 JOB_COMPLETED_LIFETIME, 1025 JOB_COMPLETED_LIFETIME,
1006 HTTP_SIGNATURE, 1026 HTTP_SIGNATURE,
1007 VIDEO_IMPORT_STATES, 1027 VIDEO_IMPORT_STATES,
1028 VIDEO_CHANNEL_SYNC_STATE,
1008 VIEW_LIFETIME, 1029 VIEW_LIFETIME,
1009 CONTACT_FORM_LIFETIME, 1030 CONTACT_FORM_LIFETIME,
1010 VIDEO_PLAYLIST_PRIVACIES, 1031 VIDEO_PLAYLIST_PRIVACIES,
diff --git a/server/initializers/database.ts b/server/initializers/database.ts
index 91286241b..f55f40df0 100644
--- a/server/initializers/database.ts
+++ b/server/initializers/database.ts
@@ -50,6 +50,7 @@ import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-pla
50import { VideoTagModel } from '../models/video/video-tag' 50import { VideoTagModel } from '../models/video/video-tag'
51import { VideoViewModel } from '../models/view/video-view' 51import { VideoViewModel } from '../models/view/video-view'
52import { CONFIG } from './config' 52import { CONFIG } from './config'
53import { VideoChannelSyncModel } from '@server/models/video/video-channel-sync'
53 54
54require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string 55require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string
55 56
@@ -153,7 +154,8 @@ async function initDatabaseModels (silent: boolean) {
153 VideoTrackerModel, 154 VideoTrackerModel,
154 PluginModel, 155 PluginModel,
155 ActorCustomPageModel, 156 ActorCustomPageModel,
156 VideoJobInfoModel 157 VideoJobInfoModel,
158 VideoChannelSyncModel
157 ]) 159 ])
158 160
159 // Check extensions exist in the database 161 // Check extensions exist in the database
diff --git a/server/initializers/migrations/0730-video-channel-sync.ts b/server/initializers/migrations/0730-video-channel-sync.ts
new file mode 100644
index 000000000..a2fe8211f
--- /dev/null
+++ b/server/initializers/migrations/0730-video-channel-sync.ts
@@ -0,0 +1,36 @@
1import * as Sequelize from 'sequelize'
2
3async function up (utils: {
4 transaction: Sequelize.Transaction
5 queryInterface: Sequelize.QueryInterface
6 sequelize: Sequelize.Sequelize
7 db: any
8}): Promise<void> {
9 const query = `
10 CREATE TABLE IF NOT EXISTS "videoChannelSync" (
11 "id" SERIAL,
12 "externalChannelUrl" VARCHAR(2000) NOT NULL DEFAULT NULL,
13 "videoChannelId" INTEGER NOT NULL REFERENCES "videoChannel" ("id")
14 ON DELETE CASCADE
15 ON UPDATE CASCADE,
16 "state" INTEGER NOT NULL DEFAULT 1,
17 "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL,
18 "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
19 "lastSyncAt" TIMESTAMP WITH TIME ZONE,
20 PRIMARY KEY ("id")
21 );
22 `
23 await utils.sequelize.query(query, { transaction: utils.transaction })
24}
25
26async function down (utils: {
27 queryInterface: Sequelize.QueryInterface
28 transaction: Sequelize.Transaction
29}) {
30 await utils.queryInterface.dropTable('videoChannelSync', { transaction: utils.transaction })
31}
32
33export {
34 up,
35 down
36}