diff options
Diffstat (limited to 'shared')
-rw-r--r-- | shared/models/activitypub/objects/video-object.ts | 2 | ||||
-rw-r--r-- | shared/models/plugins/server/server-hook.model.ts | 4 | ||||
-rw-r--r-- | shared/models/server/custom-config.model.ts | 6 | ||||
-rw-r--r-- | shared/models/server/server-config.model.ts | 6 | ||||
-rw-r--r-- | shared/models/videos/video-source.ts | 1 | ||||
-rw-r--r-- | shared/models/videos/video.model.ts | 2 | ||||
-rw-r--r-- | shared/server-commands/server/config-command.ts | 27 | ||||
-rw-r--r-- | shared/server-commands/server/servers-command.ts | 8 | ||||
-rw-r--r-- | shared/server-commands/videos/videos-command.ts | 71 |
9 files changed, 113 insertions, 14 deletions
diff --git a/shared/models/activitypub/objects/video-object.ts b/shared/models/activitypub/objects/video-object.ts index a252a2df0..409504f0f 100644 --- a/shared/models/activitypub/objects/video-object.ts +++ b/shared/models/activitypub/objects/video-object.ts | |||
@@ -31,9 +31,11 @@ export interface VideoObject { | |||
31 | downloadEnabled: boolean | 31 | downloadEnabled: boolean |
32 | waitTranscoding: boolean | 32 | waitTranscoding: boolean |
33 | state: VideoState | 33 | state: VideoState |
34 | |||
34 | published: string | 35 | published: string |
35 | originallyPublishedAt: string | 36 | originallyPublishedAt: string |
36 | updated: string | 37 | updated: string |
38 | uploadDate: string | ||
37 | 39 | ||
38 | mediaType: 'text/markdown' | 40 | mediaType: 'text/markdown' |
39 | content: string | 41 | content: string |
diff --git a/shared/models/plugins/server/server-hook.model.ts b/shared/models/plugins/server/server-hook.model.ts index 0ec62222d..cf387ffd7 100644 --- a/shared/models/plugins/server/server-hook.model.ts +++ b/shared/models/plugins/server/server-hook.model.ts | |||
@@ -64,6 +64,7 @@ export const serverFilterHookObject = { | |||
64 | 'filter:api.video.pre-import-torrent.accept.result': true, | 64 | 'filter:api.video.pre-import-torrent.accept.result': true, |
65 | 'filter:api.video.post-import-url.accept.result': true, | 65 | 'filter:api.video.post-import-url.accept.result': true, |
66 | 'filter:api.video.post-import-torrent.accept.result': true, | 66 | 'filter:api.video.post-import-torrent.accept.result': true, |
67 | 'filter:api.video.update-file.accept.result': true, | ||
67 | // Filter the result of the accept comment (thread or reply) functions | 68 | // Filter the result of the accept comment (thread or reply) functions |
68 | // If the functions return false then the user cannot post its comment | 69 | // If the functions return false then the user cannot post its comment |
69 | 'filter:api.video-thread.create.accept.result': true, | 70 | 'filter:api.video-thread.create.accept.result': true, |
@@ -155,6 +156,9 @@ export const serverActionHookObject = { | |||
155 | // Fired when a local video is viewed | 156 | // Fired when a local video is viewed |
156 | 'action:api.video.viewed': true, | 157 | 'action:api.video.viewed': true, |
157 | 158 | ||
159 | // Fired when a local video file has been replaced by a new one | ||
160 | 'action:api.video.file-updated': true, | ||
161 | |||
158 | // Fired when a video channel is created | 162 | // Fired when a video channel is created |
159 | 'action:api.video-channel.created': true, | 163 | 'action:api.video-channel.created': true, |
160 | // Fired when a video channel is updated | 164 | // Fired when a video channel is updated |
diff --git a/shared/models/server/custom-config.model.ts b/shared/models/server/custom-config.model.ts index 9aa66f2b8..0dbb46fa8 100644 --- a/shared/models/server/custom-config.model.ts +++ b/shared/models/server/custom-config.model.ts | |||
@@ -175,6 +175,12 @@ export interface CustomConfig { | |||
175 | } | 175 | } |
176 | } | 176 | } |
177 | 177 | ||
178 | videoFile: { | ||
179 | update: { | ||
180 | enabled: boolean | ||
181 | } | ||
182 | } | ||
183 | |||
178 | import: { | 184 | import: { |
179 | videos: { | 185 | videos: { |
180 | concurrency: number | 186 | concurrency: number |
diff --git a/shared/models/server/server-config.model.ts b/shared/models/server/server-config.model.ts index 288cf84cd..3f61e93b5 100644 --- a/shared/models/server/server-config.model.ts +++ b/shared/models/server/server-config.model.ts | |||
@@ -192,6 +192,12 @@ export interface ServerConfig { | |||
192 | } | 192 | } |
193 | } | 193 | } |
194 | 194 | ||
195 | videoFile: { | ||
196 | update: { | ||
197 | enabled: boolean | ||
198 | } | ||
199 | } | ||
200 | |||
195 | import: { | 201 | import: { |
196 | videos: { | 202 | videos: { |
197 | http: { | 203 | http: { |
diff --git a/shared/models/videos/video-source.ts b/shared/models/videos/video-source.ts index 57e54fc7f..bf4ad2453 100644 --- a/shared/models/videos/video-source.ts +++ b/shared/models/videos/video-source.ts | |||
@@ -1,3 +1,4 @@ | |||
1 | export interface VideoSource { | 1 | export interface VideoSource { |
2 | filename: string | 2 | filename: string |
3 | createdAt: string | Date | ||
3 | } | 4 | } |
diff --git a/shared/models/videos/video.model.ts b/shared/models/videos/video.model.ts index 9004efb35..7e5930067 100644 --- a/shared/models/videos/video.model.ts +++ b/shared/models/videos/video.model.ts | |||
@@ -94,4 +94,6 @@ export interface VideoDetails extends Video { | |||
94 | 94 | ||
95 | files: VideoFile[] | 95 | files: VideoFile[] |
96 | streamingPlaylists: VideoStreamingPlaylist[] | 96 | streamingPlaylists: VideoStreamingPlaylist[] |
97 | |||
98 | inputFileUpdatedAt: string | Date | ||
97 | } | 99 | } |
diff --git a/shared/server-commands/server/config-command.ts b/shared/server-commands/server/config-command.ts index 7f1e9d977..3521b2d69 100644 --- a/shared/server-commands/server/config-command.ts +++ b/shared/server-commands/server/config-command.ts | |||
@@ -74,6 +74,28 @@ export class ConfigCommand extends AbstractCommand { | |||
74 | 74 | ||
75 | // --------------------------------------------------------------------------- | 75 | // --------------------------------------------------------------------------- |
76 | 76 | ||
77 | disableFileUpdate () { | ||
78 | return this.setFileUpdateEnabled(false) | ||
79 | } | ||
80 | |||
81 | enableFileUpdate () { | ||
82 | return this.setFileUpdateEnabled(true) | ||
83 | } | ||
84 | |||
85 | private setFileUpdateEnabled (enabled: boolean) { | ||
86 | return this.updateExistingSubConfig({ | ||
87 | newConfig: { | ||
88 | videoFile: { | ||
89 | update: { | ||
90 | enabled | ||
91 | } | ||
92 | } | ||
93 | } | ||
94 | }) | ||
95 | } | ||
96 | |||
97 | // --------------------------------------------------------------------------- | ||
98 | |||
77 | enableChannelSync () { | 99 | enableChannelSync () { |
78 | return this.setChannelSyncEnabled(true) | 100 | return this.setChannelSyncEnabled(true) |
79 | } | 101 | } |
@@ -466,6 +488,11 @@ export class ConfigCommand extends AbstractCommand { | |||
466 | enabled: false | 488 | enabled: false |
467 | } | 489 | } |
468 | }, | 490 | }, |
491 | videoFile: { | ||
492 | update: { | ||
493 | enabled: false | ||
494 | } | ||
495 | }, | ||
469 | import: { | 496 | import: { |
470 | videos: { | 497 | videos: { |
471 | concurrency: 3, | 498 | concurrency: 3, |
diff --git a/shared/server-commands/server/servers-command.ts b/shared/server-commands/server/servers-command.ts index c91c2b008..54e586a18 100644 --- a/shared/server-commands/server/servers-command.ts +++ b/shared/server-commands/server/servers-command.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { exec } from 'child_process' | 1 | import { exec } from 'child_process' |
2 | import { copy, ensureDir, readFile, remove } from 'fs-extra' | 2 | import { copy, ensureDir, readFile, readdir, remove } from 'fs-extra' |
3 | import { basename, join } from 'path' | 3 | import { basename, join } from 'path' |
4 | import { isGithubCI, root, wait } from '@shared/core-utils' | 4 | import { isGithubCI, root, wait } from '@shared/core-utils' |
5 | import { getFileSize } from '@shared/extra-utils' | 5 | import { getFileSize } from '@shared/extra-utils' |
@@ -77,6 +77,12 @@ export class ServersCommand extends AbstractCommand { | |||
77 | return join(root(), 'test' + this.server.internalServerNumber, directory) | 77 | return join(root(), 'test' + this.server.internalServerNumber, directory) |
78 | } | 78 | } |
79 | 79 | ||
80 | async countFiles (directory: string) { | ||
81 | const files = await readdir(this.buildDirectory(directory)) | ||
82 | |||
83 | return files.length | ||
84 | } | ||
85 | |||
80 | buildWebVideoFilePath (fileUrl: string) { | 86 | buildWebVideoFilePath (fileUrl: string) { |
81 | return this.buildDirectory(join('web-videos', basename(fileUrl))) | 87 | return this.buildDirectory(join('web-videos', basename(fileUrl))) |
82 | } | 88 | } |
diff --git a/shared/server-commands/videos/videos-command.ts b/shared/server-commands/videos/videos-command.ts index 9602fa7da..6c38fa7ef 100644 --- a/shared/server-commands/videos/videos-command.ts +++ b/shared/server-commands/videos/videos-command.ts | |||
@@ -32,6 +32,7 @@ export type VideoEdit = Partial<Omit<VideoCreate, 'thumbnailfile' | 'previewfile | |||
32 | } | 32 | } |
33 | 33 | ||
34 | export class VideosCommand extends AbstractCommand { | 34 | export class VideosCommand extends AbstractCommand { |
35 | |||
35 | getCategories (options: OverrideCommandOptions = {}) { | 36 | getCategories (options: OverrideCommandOptions = {}) { |
36 | const path = '/api/v1/videos/categories' | 37 | const path = '/api/v1/videos/categories' |
37 | 38 | ||
@@ -424,7 +425,7 @@ export class VideosCommand extends AbstractCommand { | |||
424 | 425 | ||
425 | const created = mode === 'legacy' | 426 | const created = mode === 'legacy' |
426 | ? await this.buildLegacyUpload({ ...options, attributes }) | 427 | ? await this.buildLegacyUpload({ ...options, attributes }) |
427 | : await this.buildResumeUpload({ ...options, attributes }) | 428 | : await this.buildResumeUpload({ ...options, path: '/api/v1/videos/upload-resumable', attributes }) |
428 | 429 | ||
429 | // Wait torrent generation | 430 | // Wait torrent generation |
430 | const expectedStatus = this.buildExpectedStatus({ ...options, defaultExpectedStatus: HttpStatusCode.OK_200 }) | 431 | const expectedStatus = this.buildExpectedStatus({ ...options, defaultExpectedStatus: HttpStatusCode.OK_200 }) |
@@ -458,9 +459,10 @@ export class VideosCommand extends AbstractCommand { | |||
458 | } | 459 | } |
459 | 460 | ||
460 | async buildResumeUpload (options: OverrideCommandOptions & { | 461 | async buildResumeUpload (options: OverrideCommandOptions & { |
461 | attributes: VideoEdit | 462 | path: string |
463 | attributes: { fixture?: string } & { [id: string]: any } | ||
462 | }): Promise<VideoCreateResult> { | 464 | }): Promise<VideoCreateResult> { |
463 | const { attributes, expectedStatus } = options | 465 | const { path, attributes, expectedStatus } = options |
464 | 466 | ||
465 | let size = 0 | 467 | let size = 0 |
466 | let videoFilePath: string | 468 | let videoFilePath: string |
@@ -478,7 +480,15 @@ export class VideosCommand extends AbstractCommand { | |||
478 | } | 480 | } |
479 | 481 | ||
480 | // Do not check status automatically, we'll check it manually | 482 | // Do not check status automatically, we'll check it manually |
481 | const initializeSessionRes = await this.prepareResumableUpload({ ...options, expectedStatus: null, attributes, size, mimetype }) | 483 | const initializeSessionRes = await this.prepareResumableUpload({ |
484 | ...options, | ||
485 | |||
486 | path, | ||
487 | expectedStatus: null, | ||
488 | attributes, | ||
489 | size, | ||
490 | mimetype | ||
491 | }) | ||
482 | const initStatus = initializeSessionRes.status | 492 | const initStatus = initializeSessionRes.status |
483 | 493 | ||
484 | if (videoFilePath && initStatus === HttpStatusCode.CREATED_201) { | 494 | if (videoFilePath && initStatus === HttpStatusCode.CREATED_201) { |
@@ -487,10 +497,23 @@ export class VideosCommand extends AbstractCommand { | |||
487 | 497 | ||
488 | const pathUploadId = locationHeader.split('?')[1] | 498 | const pathUploadId = locationHeader.split('?')[1] |
489 | 499 | ||
490 | const result = await this.sendResumableChunks({ ...options, pathUploadId, videoFilePath, size }) | 500 | const result = await this.sendResumableChunks({ |
501 | ...options, | ||
502 | |||
503 | path, | ||
504 | pathUploadId, | ||
505 | videoFilePath, | ||
506 | size | ||
507 | }) | ||
491 | 508 | ||
492 | if (result.statusCode === HttpStatusCode.OK_200) { | 509 | if (result.statusCode === HttpStatusCode.OK_200) { |
493 | await this.endResumableUpload({ ...options, expectedStatus: HttpStatusCode.NO_CONTENT_204, pathUploadId }) | 510 | await this.endResumableUpload({ |
511 | ...options, | ||
512 | |||
513 | expectedStatus: HttpStatusCode.NO_CONTENT_204, | ||
514 | path, | ||
515 | pathUploadId | ||
516 | }) | ||
494 | } | 517 | } |
495 | 518 | ||
496 | return result.body?.video || result.body as any | 519 | return result.body?.video || result.body as any |
@@ -506,18 +529,19 @@ export class VideosCommand extends AbstractCommand { | |||
506 | } | 529 | } |
507 | 530 | ||
508 | async prepareResumableUpload (options: OverrideCommandOptions & { | 531 | async prepareResumableUpload (options: OverrideCommandOptions & { |
509 | attributes: VideoEdit | 532 | path: string |
533 | attributes: { fixture?: string } & { [id: string]: any } | ||
510 | size: number | 534 | size: number |
511 | mimetype: string | 535 | mimetype: string |
512 | 536 | ||
513 | originalName?: string | 537 | originalName?: string |
514 | lastModified?: number | 538 | lastModified?: number |
515 | }) { | 539 | }) { |
516 | const { attributes, originalName, lastModified, size, mimetype } = options | 540 | const { path, attributes, originalName, lastModified, size, mimetype } = options |
517 | 541 | ||
518 | const path = '/api/v1/videos/upload-resumable' | 542 | const attaches = this.buildUploadAttaches(omit(options.attributes, [ 'fixture' ])) |
519 | 543 | ||
520 | return this.postUploadRequest({ | 544 | const uploadOptions = { |
521 | ...options, | 545 | ...options, |
522 | 546 | ||
523 | path, | 547 | path, |
@@ -538,11 +562,16 @@ export class VideosCommand extends AbstractCommand { | |||
538 | implicitToken: true, | 562 | implicitToken: true, |
539 | 563 | ||
540 | defaultExpectedStatus: null | 564 | defaultExpectedStatus: null |
541 | }) | 565 | } |
566 | |||
567 | if (Object.keys(attaches).length === 0) return this.postBodyRequest(uploadOptions) | ||
568 | |||
569 | return this.postUploadRequest(uploadOptions) | ||
542 | } | 570 | } |
543 | 571 | ||
544 | sendResumableChunks (options: OverrideCommandOptions & { | 572 | sendResumableChunks (options: OverrideCommandOptions & { |
545 | pathUploadId: string | 573 | pathUploadId: string |
574 | path: string | ||
546 | videoFilePath: string | 575 | videoFilePath: string |
547 | size: number | 576 | size: number |
548 | contentLength?: number | 577 | contentLength?: number |
@@ -550,6 +579,7 @@ export class VideosCommand extends AbstractCommand { | |||
550 | digestBuilder?: (chunk: any) => string | 579 | digestBuilder?: (chunk: any) => string |
551 | }) { | 580 | }) { |
552 | const { | 581 | const { |
582 | path, | ||
553 | pathUploadId, | 583 | pathUploadId, |
554 | videoFilePath, | 584 | videoFilePath, |
555 | size, | 585 | size, |
@@ -559,7 +589,6 @@ export class VideosCommand extends AbstractCommand { | |||
559 | expectedStatus = HttpStatusCode.OK_200 | 589 | expectedStatus = HttpStatusCode.OK_200 |
560 | } = options | 590 | } = options |
561 | 591 | ||
562 | const path = '/api/v1/videos/upload-resumable' | ||
563 | let start = 0 | 592 | let start = 0 |
564 | 593 | ||
565 | const token = this.buildCommonRequestToken({ ...options, implicitToken: true }) | 594 | const token = this.buildCommonRequestToken({ ...options, implicitToken: true }) |
@@ -610,12 +639,13 @@ export class VideosCommand extends AbstractCommand { | |||
610 | } | 639 | } |
611 | 640 | ||
612 | endResumableUpload (options: OverrideCommandOptions & { | 641 | endResumableUpload (options: OverrideCommandOptions & { |
642 | path: string | ||
613 | pathUploadId: string | 643 | pathUploadId: string |
614 | }) { | 644 | }) { |
615 | return this.deleteRequest({ | 645 | return this.deleteRequest({ |
616 | ...options, | 646 | ...options, |
617 | 647 | ||
618 | path: '/api/v1/videos/upload-resumable', | 648 | path: options.path, |
619 | rawQuery: options.pathUploadId, | 649 | rawQuery: options.pathUploadId, |
620 | implicitToken: true, | 650 | implicitToken: true, |
621 | defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 | 651 | defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 |
@@ -657,6 +687,21 @@ export class VideosCommand extends AbstractCommand { | |||
657 | 687 | ||
658 | // --------------------------------------------------------------------------- | 688 | // --------------------------------------------------------------------------- |
659 | 689 | ||
690 | replaceSourceFile (options: OverrideCommandOptions & { | ||
691 | videoId: number | string | ||
692 | fixture: string | ||
693 | }) { | ||
694 | return this.buildResumeUpload({ | ||
695 | ...options, | ||
696 | |||
697 | path: '/api/v1/videos/' + options.videoId + '/source/replace-resumable', | ||
698 | attributes: { fixture: options.fixture }, | ||
699 | expectedStatus: HttpStatusCode.NO_CONTENT_204 | ||
700 | }) | ||
701 | } | ||
702 | |||
703 | // --------------------------------------------------------------------------- | ||
704 | |||
660 | removeHLSPlaylist (options: OverrideCommandOptions & { | 705 | removeHLSPlaylist (options: OverrideCommandOptions & { |
661 | videoId: number | string | 706 | videoId: number | string |
662 | }) { | 707 | }) { |