diff options
9 files changed, 64 insertions, 16 deletions
diff --git a/client/src/app/+videos/+video-watch/shared/information/video-alert.component.html b/client/src/app/+videos/+video-watch/shared/information/video-alert.component.html index 6e5d0bcad..0c4d46714 100644 --- a/client/src/app/+videos/+video-watch/shared/information/video-alert.component.html +++ b/client/src/app/+videos/+video-watch/shared/information/video-alert.component.html | |||
@@ -2,6 +2,10 @@ | |||
2 | Transcoding failed, this video may not work properly. | 2 | Transcoding failed, this video may not work properly. |
3 | </div> | 3 | </div> |
4 | 4 | ||
5 | <div i18n class="alert alert-warning" *ngIf="isVideoMoveToObjectStorageFailed()"> | ||
6 | Move to external storage failed, this video may not work properly. | ||
7 | </div> | ||
8 | |||
5 | <div i18n class="alert alert-warning" *ngIf="isVideoToImport()"> | 9 | <div i18n class="alert alert-warning" *ngIf="isVideoToImport()"> |
6 | The video is being imported, it will be available when the import is finished. | 10 | The video is being imported, it will be available when the import is finished. |
7 | </div> | 11 | </div> |
diff --git a/client/src/app/+videos/+video-watch/shared/information/video-alert.component.ts b/client/src/app/+videos/+video-watch/shared/information/video-alert.component.ts index addea53c0..a3d3fa6fb 100644 --- a/client/src/app/+videos/+video-watch/shared/information/video-alert.component.ts +++ b/client/src/app/+videos/+video-watch/shared/information/video-alert.component.ts | |||
@@ -18,6 +18,10 @@ export class VideoAlertComponent { | |||
18 | return this.video && this.video.state.id === VideoState.TRANSCODING_FAILED | 18 | return this.video && this.video.state.id === VideoState.TRANSCODING_FAILED |
19 | } | 19 | } |
20 | 20 | ||
21 | isVideoMoveToObjectStorageFailed () { | ||
22 | return this.video && this.video.state.id === VideoState.TO_MOVE_TO_EXTERNAL_STORAGE_FAILED | ||
23 | } | ||
24 | |||
21 | isVideoToImport () { | 25 | isVideoToImport () { |
22 | return this.video && this.video.state.id === VideoState.TO_IMPORT | 26 | return this.video && this.video.state.id === VideoState.TO_IMPORT |
23 | } | 27 | } |
diff --git a/client/src/app/shared/shared-video-miniature/video-miniature.component.ts b/client/src/app/shared/shared-video-miniature/video-miniature.component.ts index f387c38c2..847e401ed 100644 --- a/client/src/app/shared/shared-video-miniature/video-miniature.component.ts +++ b/client/src/app/shared/shared-video-miniature/video-miniature.component.ts | |||
@@ -179,6 +179,10 @@ export class VideoMiniatureComponent implements OnInit { | |||
179 | return $localize`Transcoding failed` | 179 | return $localize`Transcoding failed` |
180 | } | 180 | } |
181 | 181 | ||
182 | if (video.state.id === VideoState.TO_MOVE_TO_EXTERNAL_STORAGE_FAILED) { | ||
183 | return $localize`Move to external storage failed` | ||
184 | } | ||
185 | |||
182 | if (video.state.id === VideoState.TO_TRANSCODE && video.waitTranscoding === true) { | 186 | if (video.state.id === VideoState.TO_TRANSCODE && video.waitTranscoding === true) { |
183 | return $localize`Waiting transcoding` | 187 | return $localize`Waiting transcoding` |
184 | } | 188 | } |
diff --git a/scripts/create-move-video-storage-job.ts b/scripts/create-move-video-storage-job.ts index 699487f72..90c84b1d7 100644 --- a/scripts/create-move-video-storage-job.ts +++ b/scripts/create-move-video-storage-job.ts | |||
@@ -4,7 +4,7 @@ registerTSPaths() | |||
4 | import { program } from 'commander' | 4 | import { program } from 'commander' |
5 | import { VideoModel } from '@server/models/video/video' | 5 | import { VideoModel } from '@server/models/video/video' |
6 | import { initDatabaseModels } from '@server/initializers/database' | 6 | import { initDatabaseModels } from '@server/initializers/database' |
7 | import { VideoStorage } from '@shared/models' | 7 | import { VideoState, VideoStorage } from '@shared/models' |
8 | import { moveToExternalStorageState } from '@server/lib/video-state' | 8 | import { moveToExternalStorageState } from '@server/lib/video-state' |
9 | import { JobQueue } from '@server/lib/job-queue' | 9 | import { JobQueue } from '@server/lib/job-queue' |
10 | import { CONFIG } from '@server/initializers/config' | 10 | import { CONFIG } from '@server/initializers/config' |
@@ -62,6 +62,11 @@ async function run () { | |||
62 | process.exit(-1) | 62 | process.exit(-1) |
63 | } | 63 | } |
64 | 64 | ||
65 | if (video.state === VideoState.TO_MOVE_TO_EXTERNAL_STORAGE) { | ||
66 | console.error('This video is already being moved to external storage') | ||
67 | process.exit(-1) | ||
68 | } | ||
69 | |||
65 | ids.push(video.id) | 70 | ids.push(video.id) |
66 | } else { | 71 | } else { |
67 | ids = await VideoModel.listLocalIds() | 72 | ids = await VideoModel.listLocalIds() |
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 57f7af789..7816561fd 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -427,7 +427,8 @@ const VIDEO_STATES: { [ id in VideoState ]: string } = { | |||
427 | [VideoState.WAITING_FOR_LIVE]: 'Waiting for livestream', | 427 | [VideoState.WAITING_FOR_LIVE]: 'Waiting for livestream', |
428 | [VideoState.LIVE_ENDED]: 'Livestream ended', | 428 | [VideoState.LIVE_ENDED]: 'Livestream ended', |
429 | [VideoState.TO_MOVE_TO_EXTERNAL_STORAGE]: 'To move to an external storage', | 429 | [VideoState.TO_MOVE_TO_EXTERNAL_STORAGE]: 'To move to an external storage', |
430 | [VideoState.TRANSCODING_FAILED]: 'Transcoding failed' | 430 | [VideoState.TRANSCODING_FAILED]: 'Transcoding failed', |
431 | [VideoState.TO_MOVE_TO_EXTERNAL_STORAGE_FAILED]: 'External storage move failed' | ||
431 | } | 432 | } |
432 | 433 | ||
433 | const VIDEO_IMPORT_STATES: { [ id in VideoImportState ]: string } = { | 434 | const VIDEO_IMPORT_STATES: { [ id in VideoImportState ]: string } = { |
diff --git a/server/lib/job-queue/handlers/move-to-object-storage.ts b/server/lib/job-queue/handlers/move-to-object-storage.ts index b5eea0184..d9c415b2d 100644 --- a/server/lib/job-queue/handlers/move-to-object-storage.ts +++ b/server/lib/job-queue/handlers/move-to-object-storage.ts | |||
@@ -7,7 +7,7 @@ import { CONFIG } from '@server/initializers/config' | |||
7 | import { P2P_MEDIA_LOADER_PEER_VERSION } from '@server/initializers/constants' | 7 | import { P2P_MEDIA_LOADER_PEER_VERSION } from '@server/initializers/constants' |
8 | import { storeHLSFile, storeWebTorrentFile } from '@server/lib/object-storage' | 8 | import { storeHLSFile, storeWebTorrentFile } from '@server/lib/object-storage' |
9 | import { getHLSDirectory, getHlsResolutionPlaylistFilename } from '@server/lib/paths' | 9 | import { getHLSDirectory, getHlsResolutionPlaylistFilename } from '@server/lib/paths' |
10 | import { moveToNextState } from '@server/lib/video-state' | 10 | import { moveToFailedMoveToObjectStorageState, moveToNextState } from '@server/lib/video-state' |
11 | import { VideoModel } from '@server/models/video/video' | 11 | import { VideoModel } from '@server/models/video/video' |
12 | import { VideoJobInfoModel } from '@server/models/video/video-job-info' | 12 | import { VideoJobInfoModel } from '@server/models/video/video-job-info' |
13 | import { MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoWithAllFiles } from '@server/types/models' | 13 | import { MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoWithAllFiles } from '@server/types/models' |
@@ -24,18 +24,25 @@ export async function processMoveToObjectStorage (job: Job) { | |||
24 | return undefined | 24 | return undefined |
25 | } | 25 | } |
26 | 26 | ||
27 | if (video.VideoFiles) { | 27 | try { |
28 | await moveWebTorrentFiles(video) | 28 | if (video.VideoFiles) { |
29 | } | 29 | await moveWebTorrentFiles(video) |
30 | } | ||
30 | 31 | ||
31 | if (video.VideoStreamingPlaylists) { | 32 | if (video.VideoStreamingPlaylists) { |
32 | await moveHLSFiles(video) | 33 | await moveHLSFiles(video) |
33 | } | 34 | } |
35 | |||
36 | const pendingMove = await VideoJobInfoModel.decrease(video.uuid, 'pendingMove') | ||
37 | if (pendingMove === 0) { | ||
38 | logger.info('Running cleanup after moving files to object storage (video %s in job %d)', video.uuid, job.id) | ||
39 | await doAfterLastJob(video, payload.isNewVideo) | ||
40 | } | ||
41 | } catch (err) { | ||
42 | logger.error('Cannot move video %s to object storage.', video.url, { err }) | ||
34 | 43 | ||
35 | const pendingMove = await VideoJobInfoModel.decrease(video.uuid, 'pendingMove') | 44 | await moveToFailedMoveToObjectStorageState(video) |
36 | if (pendingMove === 0) { | 45 | await VideoJobInfoModel.abortAllTasks(video.uuid, 'pendingMove') |
37 | logger.info('Running cleanup after moving files to object storage (video %s in job %d)', video.uuid, job.id) | ||
38 | await doAfterLastJob(video, payload.isNewVideo) | ||
39 | } | 46 | } |
40 | 47 | ||
41 | return payload.videoUUID | 48 | return payload.videoUUID |
diff --git a/server/lib/video-state.ts b/server/lib/video-state.ts index e420991cd..97ff540ed 100644 --- a/server/lib/video-state.ts +++ b/server/lib/video-state.ts | |||
@@ -4,7 +4,7 @@ import { CONFIG } from '@server/initializers/config' | |||
4 | import { sequelizeTypescript } from '@server/initializers/database' | 4 | import { sequelizeTypescript } from '@server/initializers/database' |
5 | import { VideoModel } from '@server/models/video/video' | 5 | import { VideoModel } from '@server/models/video/video' |
6 | import { VideoJobInfoModel } from '@server/models/video/video-job-info' | 6 | import { VideoJobInfoModel } from '@server/models/video/video-job-info' |
7 | import { MVideoFullLight, MVideoUUID } from '@server/types/models' | 7 | import { MVideo, MVideoFullLight, MVideoUUID } from '@server/types/models' |
8 | import { VideoState } from '@shared/models' | 8 | import { VideoState } from '@shared/models' |
9 | import { federateVideoIfNeeded } from './activitypub/videos' | 9 | import { federateVideoIfNeeded } from './activitypub/videos' |
10 | import { Notifier } from './notifier' | 10 | import { Notifier } from './notifier' |
@@ -79,18 +79,25 @@ async function moveToExternalStorageState (video: MVideoFullLight, isNewVideo: b | |||
79 | } | 79 | } |
80 | } | 80 | } |
81 | 81 | ||
82 | function moveToFailedTranscodingState (video: MVideoFullLight) { | 82 | function moveToFailedTranscodingState (video: MVideo) { |
83 | if (video.state === VideoState.TRANSCODING_FAILED) return | 83 | if (video.state === VideoState.TRANSCODING_FAILED) return |
84 | 84 | ||
85 | return video.setNewState(VideoState.TRANSCODING_FAILED, false, undefined) | 85 | return video.setNewState(VideoState.TRANSCODING_FAILED, false, undefined) |
86 | } | 86 | } |
87 | 87 | ||
88 | function moveToFailedMoveToObjectStorageState (video: MVideo) { | ||
89 | if (video.state === VideoState.TO_MOVE_TO_EXTERNAL_STORAGE_FAILED) return | ||
90 | |||
91 | return video.setNewState(VideoState.TO_MOVE_TO_EXTERNAL_STORAGE_FAILED, false, undefined) | ||
92 | } | ||
93 | |||
88 | // --------------------------------------------------------------------------- | 94 | // --------------------------------------------------------------------------- |
89 | 95 | ||
90 | export { | 96 | export { |
91 | buildNextVideoState, | 97 | buildNextVideoState, |
92 | moveToExternalStorageState, | 98 | moveToExternalStorageState, |
93 | moveToFailedTranscodingState, | 99 | moveToFailedTranscodingState, |
100 | moveToFailedMoveToObjectStorageState, | ||
94 | moveToNextState | 101 | moveToNextState |
95 | } | 102 | } |
96 | 103 | ||
diff --git a/server/models/video/video-job-info.ts b/server/models/video/video-job-info.ts index 6a67a214c..7497addf1 100644 --- a/server/models/video/video-job-info.ts +++ b/server/models/video/video-job-info.ts | |||
@@ -99,4 +99,19 @@ export class VideoJobInfoModel extends Model<Partial<AttributesOnly<VideoJobInfo | |||
99 | 99 | ||
100 | return pendingMove | 100 | return pendingMove |
101 | } | 101 | } |
102 | |||
103 | static async abortAllTasks (videoUUID: string, column: VideoJobInfoColumnType): Promise<void> { | ||
104 | const options = { type: QueryTypes.UPDATE as QueryTypes.UPDATE, bind: { videoUUID } } | ||
105 | |||
106 | await VideoJobInfoModel.sequelize.query(` | ||
107 | UPDATE | ||
108 | "videoJobInfo" | ||
109 | SET | ||
110 | "${column}" = 0, | ||
111 | "updatedAt" = NOW() | ||
112 | FROM "video" | ||
113 | WHERE | ||
114 | "video"."id" = "videoJobInfo"."videoId" AND "video"."uuid" = $videoUUID | ||
115 | `, options) | ||
116 | } | ||
102 | } | 117 | } |
diff --git a/shared/models/videos/video-state.enum.ts b/shared/models/videos/video-state.enum.ts index 6112b6e16..09268d2ff 100644 --- a/shared/models/videos/video-state.enum.ts +++ b/shared/models/videos/video-state.enum.ts | |||
@@ -5,5 +5,6 @@ export const enum VideoState { | |||
5 | WAITING_FOR_LIVE = 4, | 5 | WAITING_FOR_LIVE = 4, |
6 | LIVE_ENDED = 5, | 6 | LIVE_ENDED = 5, |
7 | TO_MOVE_TO_EXTERNAL_STORAGE = 6, | 7 | TO_MOVE_TO_EXTERNAL_STORAGE = 6, |
8 | TRANSCODING_FAILED = 7 | 8 | TRANSCODING_FAILED = 7, |
9 | TO_MOVE_TO_EXTERNAL_STORAGE_FAILED = 8 | ||
9 | } | 10 | } |