aboutsummaryrefslogtreecommitdiffhomepage
path: root/shared
diff options
context:
space:
mode:
Diffstat (limited to 'shared')
-rw-r--r--shared/models/activitypub/objects/video-object.ts2
-rw-r--r--shared/models/plugins/server/server-hook.model.ts4
-rw-r--r--shared/models/server/custom-config.model.ts6
-rw-r--r--shared/models/server/server-config.model.ts6
-rw-r--r--shared/models/videos/video-source.ts1
-rw-r--r--shared/models/videos/video.model.ts2
-rw-r--r--shared/server-commands/server/config-command.ts27
-rw-r--r--shared/server-commands/server/servers-command.ts8
-rw-r--r--shared/server-commands/videos/videos-command.ts71
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 @@
1export interface VideoSource { 1export 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 @@
1import { exec } from 'child_process' 1import { exec } from 'child_process'
2import { copy, ensureDir, readFile, remove } from 'fs-extra' 2import { copy, ensureDir, readFile, readdir, remove } from 'fs-extra'
3import { basename, join } from 'path' 3import { basename, join } from 'path'
4import { isGithubCI, root, wait } from '@shared/core-utils' 4import { isGithubCI, root, wait } from '@shared/core-utils'
5import { getFileSize } from '@shared/extra-utils' 5import { 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
34export class VideosCommand extends AbstractCommand { 34export 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 }) {