diff options
11 files changed, 88 insertions, 28 deletions
diff --git a/client/src/app/+admin/overview/videos/video-list.component.ts b/client/src/app/+admin/overview/videos/video-list.component.ts index 52f02d8d0..2792a2d8a 100644 --- a/client/src/app/+admin/overview/videos/video-list.component.ts +++ b/client/src/app/+admin/overview/videos/video-list.component.ts | |||
@@ -3,7 +3,7 @@ import { finalize } from 'rxjs/operators' | |||
3 | import { Component, OnInit, ViewChild } from '@angular/core' | 3 | import { Component, OnInit, ViewChild } from '@angular/core' |
4 | import { ActivatedRoute, Router } from '@angular/router' | 4 | import { ActivatedRoute, Router } from '@angular/router' |
5 | import { AuthService, ConfirmService, Notifier, RestPagination, RestTable } from '@app/core' | 5 | import { AuthService, ConfirmService, Notifier, RestPagination, RestTable } from '@app/core' |
6 | import { formatICU } from '@app/helpers' | 6 | import { formatICU, getAbsoluteAPIUrl } from '@app/helpers' |
7 | import { AdvancedInputFilter } from '@app/shared/shared-forms' | 7 | import { AdvancedInputFilter } from '@app/shared/shared-forms' |
8 | import { DropdownAction, Video, VideoService } from '@app/shared/shared-main' | 8 | import { DropdownAction, Video, VideoService } from '@app/shared/shared-main' |
9 | import { VideoBlockComponent, VideoBlockService } from '@app/shared/shared-moderation' | 9 | import { VideoBlockComponent, VideoBlockService } from '@app/shared/shared-moderation' |
@@ -166,7 +166,7 @@ export class VideoListComponent extends RestTable <Video> implements OnInit { | |||
166 | 166 | ||
167 | const files = getAllFiles(video) | 167 | const files = getAllFiles(video) |
168 | 168 | ||
169 | return files.some(f => !f.fileUrl.startsWith(window.location.origin)) | 169 | return files.some(f => !f.fileUrl.startsWith(getAbsoluteAPIUrl())) |
170 | } | 170 | } |
171 | 171 | ||
172 | canRemoveOneFile (video: Video) { | 172 | canRemoveOneFile (video: Video) { |
@@ -294,7 +294,7 @@ export class VideoListComponent extends RestTable <Video> implements OnInit { | |||
294 | } | 294 | } |
295 | 295 | ||
296 | private runTranscoding (videos: Video[], type: 'hls' | 'web-video') { | 296 | private runTranscoding (videos: Video[], type: 'hls' | 'web-video') { |
297 | this.videoService.runTranscoding(videos.map(v => v.id), type) | 297 | this.videoService.runTranscoding({ videoIds: videos.map(v => v.id), type, askForForceTranscodingIfNeeded: false }) |
298 | .subscribe({ | 298 | .subscribe({ |
299 | next: () => { | 299 | next: () => { |
300 | this.notifier.success($localize`Transcoding jobs created.`) | 300 | this.notifier.success($localize`Transcoding jobs created.`) |
diff --git a/client/src/app/shared/shared-main/video/video.model.ts b/client/src/app/shared/shared-main/video/video.model.ts index a5bf1db8b..392dcadd0 100644 --- a/client/src/app/shared/shared-main/video/video.model.ts +++ b/client/src/app/shared/shared-main/video/video.model.ts | |||
@@ -257,9 +257,12 @@ export class Video implements VideoServerModel { | |||
257 | } | 257 | } |
258 | 258 | ||
259 | canRunTranscoding (user: AuthUser) { | 259 | canRunTranscoding (user: AuthUser) { |
260 | return this.canRunForcedTranscoding(user) && this.state.id !== VideoState.TO_TRANSCODE | ||
261 | } | ||
262 | |||
263 | canRunForcedTranscoding (user: AuthUser) { | ||
260 | return this.isLocal && | 264 | return this.isLocal && |
261 | user && user.hasRight(UserRight.RUN_VIDEO_TRANSCODING) && | 265 | user && user.hasRight(UserRight.RUN_VIDEO_TRANSCODING) |
262 | this.state.id !== VideoState.TO_TRANSCODE | ||
263 | } | 266 | } |
264 | 267 | ||
265 | hasHLS () { | 268 | hasHLS () { |
diff --git a/client/src/app/shared/shared-main/video/video.service.ts b/client/src/app/shared/shared-main/video/video.service.ts index 20145b9c5..cd0a300e2 100644 --- a/client/src/app/shared/shared-main/video/video.service.ts +++ b/client/src/app/shared/shared-main/video/video.service.ts | |||
@@ -1,9 +1,9 @@ | |||
1 | import { SortMeta } from 'primeng/api' | 1 | import { SortMeta } from 'primeng/api' |
2 | import { from, Observable, of } from 'rxjs' | 2 | import { from, Observable, of, throwError } from 'rxjs' |
3 | import { catchError, concatMap, map, switchMap, toArray } from 'rxjs/operators' | 3 | import { catchError, concatMap, map, switchMap, toArray } from 'rxjs/operators' |
4 | import { HttpClient, HttpParams, HttpRequest } from '@angular/common/http' | 4 | import { HttpClient, HttpParams, HttpRequest } from '@angular/common/http' |
5 | import { Injectable } from '@angular/core' | 5 | import { Injectable } from '@angular/core' |
6 | import { AuthService, ComponentPaginationLight, RestExtractor, RestService, ServerService, UserService } from '@app/core' | 6 | import { AuthService, ComponentPaginationLight, ConfirmService, RestExtractor, RestService, ServerService, UserService } from '@app/core' |
7 | import { objectToFormData } from '@app/helpers' | 7 | import { objectToFormData } from '@app/helpers' |
8 | import { arrayify } from '@shared/core-utils' | 8 | import { arrayify } from '@shared/core-utils' |
9 | import { | 9 | import { |
@@ -11,6 +11,7 @@ import { | |||
11 | FeedFormat, | 11 | FeedFormat, |
12 | NSFWPolicyType, | 12 | NSFWPolicyType, |
13 | ResultList, | 13 | ResultList, |
14 | ServerErrorCode, | ||
14 | Storyboard, | 15 | Storyboard, |
15 | UserVideoRate, | 16 | UserVideoRate, |
16 | UserVideoRateType, | 17 | UserVideoRateType, |
@@ -33,8 +34,8 @@ import { AccountService } from '../account/account.service' | |||
33 | import { VideoChannel, VideoChannelService } from '../video-channel' | 34 | import { VideoChannel, VideoChannelService } from '../video-channel' |
34 | import { VideoDetails } from './video-details.model' | 35 | import { VideoDetails } from './video-details.model' |
35 | import { VideoEdit } from './video-edit.model' | 36 | import { VideoEdit } from './video-edit.model' |
36 | import { Video } from './video.model' | ||
37 | import { VideoPasswordService } from './video-password.service' | 37 | import { VideoPasswordService } from './video-password.service' |
38 | import { Video } from './video.model' | ||
38 | 39 | ||
39 | export type CommonVideoParams = { | 40 | export type CommonVideoParams = { |
40 | videoPagination?: ComponentPaginationLight | 41 | videoPagination?: ComponentPaginationLight |
@@ -64,7 +65,8 @@ export class VideoService { | |||
64 | private authHttp: HttpClient, | 65 | private authHttp: HttpClient, |
65 | private restExtractor: RestExtractor, | 66 | private restExtractor: RestExtractor, |
66 | private restService: RestService, | 67 | private restService: RestService, |
67 | private serverService: ServerService | 68 | private serverService: ServerService, |
69 | private confirmService: ConfirmService | ||
68 | ) {} | 70 | ) {} |
69 | 71 | ||
70 | getVideoViewUrl (uuid: string) { | 72 | getVideoViewUrl (uuid: string) { |
@@ -325,17 +327,53 @@ export class VideoService { | |||
325 | .pipe(catchError(err => this.restExtractor.handleError(err))) | 327 | .pipe(catchError(err => this.restExtractor.handleError(err))) |
326 | } | 328 | } |
327 | 329 | ||
328 | runTranscoding (videoIds: (number | string)[], type: 'hls' | 'web-video') { | 330 | runTranscoding (options: { |
329 | const body: VideoTranscodingCreate = { transcodingType: type } | 331 | videoIds: (number | string)[] |
332 | type: 'hls' | 'web-video' | ||
333 | askForForceTranscodingIfNeeded: boolean | ||
334 | forceTranscoding?: boolean | ||
335 | }): Observable<any> { | ||
336 | const { videoIds, type, askForForceTranscodingIfNeeded, forceTranscoding } = options | ||
337 | |||
338 | if (askForForceTranscodingIfNeeded && videoIds.length !== 1) { | ||
339 | throw new Error('Cannot ask to force transcoding on multiple videos') | ||
340 | } | ||
341 | |||
342 | const body: VideoTranscodingCreate = { transcodingType: type, forceTranscoding } | ||
330 | 343 | ||
331 | return from(videoIds) | 344 | return from(videoIds) |
332 | .pipe( | 345 | .pipe( |
333 | concatMap(id => this.authHttp.post(VideoService.BASE_VIDEO_URL + '/' + id + '/transcoding', body)), | 346 | concatMap(id => { |
347 | return this.authHttp.post(VideoService.BASE_VIDEO_URL + '/' + id + '/transcoding', body) | ||
348 | .pipe( | ||
349 | catchError(err => { | ||
350 | if (askForForceTranscodingIfNeeded && err.error?.code === ServerErrorCode.VIDEO_ALREADY_BEING_TRANSCODED) { | ||
351 | const message = $localize`PeerTube considers this video is already being transcoded.` + | ||
352 | // eslint-disable-next-line max-len | ||
353 | $localize` If you think PeerTube is wrong (video in broken state after a crash etc.), you can force transcoding on this video.` + | ||
354 | ` Do you still want to run transcoding?` | ||
355 | |||
356 | return from(this.confirmService.confirm(message, $localize`Force transcoding`)) | ||
357 | .pipe( | ||
358 | switchMap(res => { | ||
359 | if (res === false) return throwError(() => err) | ||
360 | |||
361 | return this.runTranscoding({ videoIds, type, askForForceTranscodingIfNeeded: false, forceTranscoding: true }) | ||
362 | }) | ||
363 | ) | ||
364 | } | ||
365 | |||
366 | return throwError(() => err) | ||
367 | }) | ||
368 | ) | ||
369 | }), | ||
334 | toArray(), | 370 | toArray(), |
335 | catchError(err => this.restExtractor.handleError(err)) | 371 | catchError(err => this.restExtractor.handleError(err)) |
336 | ) | 372 | ) |
337 | } | 373 | } |
338 | 374 | ||
375 | // --------------------------------------------------------------------------- | ||
376 | |||
339 | loadCompleteDescription (descriptionPath: string) { | 377 | loadCompleteDescription (descriptionPath: string) { |
340 | return this.authHttp | 378 | return this.authHttp |
341 | .get<{ description: string }>(environment.apiUrl + descriptionPath) | 379 | .get<{ description: string }>(environment.apiUrl + descriptionPath) |
diff --git a/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts b/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts index 0a3ada711..9891aae2e 100644 --- a/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts +++ b/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts | |||
@@ -198,8 +198,8 @@ export class VideoActionsDropdownComponent implements OnChanges { | |||
198 | return this.video.canRemoveFiles(this.user) | 198 | return this.video.canRemoveFiles(this.user) |
199 | } | 199 | } |
200 | 200 | ||
201 | canRunTranscoding () { | 201 | canRunForcedTranscoding () { |
202 | return this.video.canRunTranscoding(this.user) | 202 | return this.video.canRunForcedTranscoding(this.user) |
203 | } | 203 | } |
204 | 204 | ||
205 | /* Action handlers */ | 205 | /* Action handlers */ |
@@ -291,10 +291,10 @@ export class VideoActionsDropdownComponent implements OnChanges { | |||
291 | } | 291 | } |
292 | 292 | ||
293 | runTranscoding (video: Video, type: 'hls' | 'web-video') { | 293 | runTranscoding (video: Video, type: 'hls' | 'web-video') { |
294 | this.videoService.runTranscoding([ video.id ], type) | 294 | this.videoService.runTranscoding({ videoIds: [ video.id ], type, askForForceTranscodingIfNeeded: true }) |
295 | .subscribe({ | 295 | .subscribe({ |
296 | next: () => { | 296 | next: () => { |
297 | this.notifier.success($localize`Transcoding jobs created for ${video.name}.`) | 297 | this.notifier.success($localize`Transcoding jobs created for "${video.name}".`) |
298 | this.transcodingCreated.emit() | 298 | this.transcodingCreated.emit() |
299 | }, | 299 | }, |
300 | 300 | ||
@@ -390,13 +390,13 @@ export class VideoActionsDropdownComponent implements OnChanges { | |||
390 | { | 390 | { |
391 | label: $localize`Run HLS transcoding`, | 391 | label: $localize`Run HLS transcoding`, |
392 | handler: ({ video }) => this.runTranscoding(video, 'hls'), | 392 | handler: ({ video }) => this.runTranscoding(video, 'hls'), |
393 | isDisplayed: () => this.displayOptions.transcoding && this.canRunTranscoding(), | 393 | isDisplayed: () => this.displayOptions.transcoding && this.canRunForcedTranscoding(), |
394 | iconName: 'cog' | 394 | iconName: 'cog' |
395 | }, | 395 | }, |
396 | { | 396 | { |
397 | label: $localize`Run Web Video transcoding`, | 397 | label: $localize`Run Web Video transcoding`, |
398 | handler: ({ video }) => this.runTranscoding(video, 'web-video'), | 398 | handler: ({ video }) => this.runTranscoding(video, 'web-video'), |
399 | isDisplayed: () => this.displayOptions.transcoding && this.canRunTranscoding(), | 399 | isDisplayed: () => this.displayOptions.transcoding && this.canRunForcedTranscoding(), |
400 | iconName: 'cog' | 400 | iconName: 'cog' |
401 | }, | 401 | }, |
402 | { | 402 | { |
diff --git a/server/controllers/api/videos/transcoding.ts b/server/controllers/api/videos/transcoding.ts index 54f484b2b..c0b93742f 100644 --- a/server/controllers/api/videos/transcoding.ts +++ b/server/controllers/api/videos/transcoding.ts | |||
@@ -3,6 +3,7 @@ import { logger, loggerTagsFactory } from '@server/helpers/logger' | |||
3 | import { Hooks } from '@server/lib/plugins/hooks' | 3 | import { Hooks } from '@server/lib/plugins/hooks' |
4 | import { createTranscodingJobs } from '@server/lib/transcoding/create-transcoding-job' | 4 | import { createTranscodingJobs } from '@server/lib/transcoding/create-transcoding-job' |
5 | import { computeResolutionsToTranscode } from '@server/lib/transcoding/transcoding-resolutions' | 5 | import { computeResolutionsToTranscode } from '@server/lib/transcoding/transcoding-resolutions' |
6 | import { VideoJobInfoModel } from '@server/models/video/video-job-info' | ||
6 | import { HttpStatusCode, UserRight, VideoState, VideoTranscodingCreate } from '@shared/models' | 7 | import { HttpStatusCode, UserRight, VideoState, VideoTranscodingCreate } from '@shared/models' |
7 | import { asyncMiddleware, authenticate, createTranscodingValidator, ensureUserHasRight } from '../../../middlewares' | 8 | import { asyncMiddleware, authenticate, createTranscodingValidator, ensureUserHasRight } from '../../../middlewares' |
8 | 9 | ||
@@ -30,6 +31,8 @@ async function createTranscoding (req: express.Request, res: express.Response) { | |||
30 | 31 | ||
31 | const body: VideoTranscodingCreate = req.body | 32 | const body: VideoTranscodingCreate = req.body |
32 | 33 | ||
34 | await VideoJobInfoModel.abortAllTasks(video.uuid, 'pendingTranscode') | ||
35 | |||
33 | const { resolution: maxResolution, hasAudio } = await video.probeMaxQualityFile() | 36 | const { resolution: maxResolution, hasAudio } = await video.probeMaxQualityFile() |
34 | 37 | ||
35 | const resolutions = await Hooks.wrapObject( | 38 | const resolutions = await Hooks.wrapObject( |
diff --git a/server/middlewares/validators/videos/video-transcoding.ts b/server/middlewares/validators/videos/video-transcoding.ts index 3eb2d3141..2f99ff42c 100644 --- a/server/middlewares/validators/videos/video-transcoding.ts +++ b/server/middlewares/validators/videos/video-transcoding.ts | |||
@@ -1,9 +1,10 @@ | |||
1 | import express from 'express' | 1 | import express from 'express' |
2 | import { body } from 'express-validator' | 2 | import { body } from 'express-validator' |
3 | import { isBooleanValid, toBooleanOrNull } from '@server/helpers/custom-validators/misc' | ||
3 | import { isValidCreateTranscodingType } from '@server/helpers/custom-validators/video-transcoding' | 4 | import { isValidCreateTranscodingType } from '@server/helpers/custom-validators/video-transcoding' |
4 | import { CONFIG } from '@server/initializers/config' | 5 | import { CONFIG } from '@server/initializers/config' |
5 | import { VideoJobInfoModel } from '@server/models/video/video-job-info' | 6 | import { VideoJobInfoModel } from '@server/models/video/video-job-info' |
6 | import { HttpStatusCode } from '@shared/models' | 7 | import { HttpStatusCode, ServerErrorCode, VideoTranscodingCreate } from '@shared/models' |
7 | import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from '../shared' | 8 | import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from '../shared' |
8 | 9 | ||
9 | const createTranscodingValidator = [ | 10 | const createTranscodingValidator = [ |
@@ -12,6 +13,11 @@ const createTranscodingValidator = [ | |||
12 | body('transcodingType') | 13 | body('transcodingType') |
13 | .custom(isValidCreateTranscodingType), | 14 | .custom(isValidCreateTranscodingType), |
14 | 15 | ||
16 | body('forceTranscoding') | ||
17 | .optional() | ||
18 | .customSanitizer(toBooleanOrNull) | ||
19 | .custom(isBooleanValid), | ||
20 | |||
15 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 21 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
16 | if (areValidationErrors(req, res)) return | 22 | if (areValidationErrors(req, res)) return |
17 | if (!await doesVideoExist(req.params.videoId, res, 'all')) return | 23 | if (!await doesVideoExist(req.params.videoId, res, 'all')) return |
@@ -32,11 +38,14 @@ const createTranscodingValidator = [ | |||
32 | }) | 38 | }) |
33 | } | 39 | } |
34 | 40 | ||
35 | // Prefer using job info table instead of video state because before 4.0 failed transcoded video were stuck in "TO_TRANSCODE" state | 41 | const body = req.body as VideoTranscodingCreate |
42 | if (body.forceTranscoding === true) return next() | ||
43 | |||
36 | const info = await VideoJobInfoModel.load(video.id) | 44 | const info = await VideoJobInfoModel.load(video.id) |
37 | if (info && info.pendingTranscode > 0) { | 45 | if (info && info.pendingTranscode > 0) { |
38 | return res.fail({ | 46 | return res.fail({ |
39 | status: HttpStatusCode.CONFLICT_409, | 47 | status: HttpStatusCode.CONFLICT_409, |
48 | type: ServerErrorCode.VIDEO_ALREADY_BEING_TRANSCODED, | ||
40 | message: 'This video is already being transcoded' | 49 | message: 'This video is already being transcoded' |
41 | }) | 50 | }) |
42 | } | 51 | } |
diff --git a/server/tests/api/check-params/transcoding.ts b/server/tests/api/check-params/transcoding.ts index 4bebcb528..d5899e11b 100644 --- a/server/tests/api/check-params/transcoding.ts +++ b/server/tests/api/check-params/transcoding.ts | |||
@@ -93,15 +93,17 @@ describe('Test transcoding API validators', function () { | |||
93 | await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'hls' }) | 93 | await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'hls' }) |
94 | await waitJobs(servers) | 94 | await waitJobs(servers) |
95 | 95 | ||
96 | await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'web-video' }) | 96 | await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'web-video', forceTranscoding: true }) |
97 | await waitJobs(servers) | 97 | await waitJobs(servers) |
98 | }) | 98 | }) |
99 | 99 | ||
100 | it('Should not run transcoding on a video that is already being transcoded', async function () { | 100 | it('Should not run transcoding on a video that is already being transcoded if forceTranscoding is not set', async function () { |
101 | await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'web-video' }) | 101 | await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'web-video' }) |
102 | 102 | ||
103 | const expectedStatus = HttpStatusCode.CONFLICT_409 | 103 | const expectedStatus = HttpStatusCode.CONFLICT_409 |
104 | await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'web-video', expectedStatus }) | 104 | await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'web-video', expectedStatus }) |
105 | |||
106 | await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'web-video', forceTranscoding: true }) | ||
105 | }) | 107 | }) |
106 | 108 | ||
107 | after(async function () { | 109 | after(async function () { |
diff --git a/shared/models/server/server-error-code.enum.ts b/shared/models/server/server-error-code.enum.ts index 77d1e1d3f..583e8245f 100644 --- a/shared/models/server/server-error-code.enum.ts +++ b/shared/models/server/server-error-code.enum.ts | |||
@@ -52,7 +52,9 @@ export const enum ServerErrorCode { | |||
52 | UNKNOWN_RUNNER_TOKEN = 'unknown_runner_token', | 52 | UNKNOWN_RUNNER_TOKEN = 'unknown_runner_token', |
53 | 53 | ||
54 | VIDEO_REQUIRES_PASSWORD = 'video_requires_password', | 54 | VIDEO_REQUIRES_PASSWORD = 'video_requires_password', |
55 | INCORRECT_VIDEO_PASSWORD = 'incorrect_video_password' | 55 | INCORRECT_VIDEO_PASSWORD = 'incorrect_video_password', |
56 | |||
57 | VIDEO_ALREADY_BEING_TRANSCODED = 'video_already_being_transcoded' | ||
56 | } | 58 | } |
57 | 59 | ||
58 | /** | 60 | /** |
diff --git a/shared/models/videos/transcoding/video-transcoding-create.model.ts b/shared/models/videos/transcoding/video-transcoding-create.model.ts index c6e756a0a..6c2dbefa6 100644 --- a/shared/models/videos/transcoding/video-transcoding-create.model.ts +++ b/shared/models/videos/transcoding/video-transcoding-create.model.ts | |||
@@ -1,3 +1,5 @@ | |||
1 | export interface VideoTranscodingCreate { | 1 | export interface VideoTranscodingCreate { |
2 | transcodingType: 'hls' | 'webtorrent' | 'web-video' // TODO: remove webtorrent in v7 | 2 | transcodingType: 'hls' | 'webtorrent' | 'web-video' // TODO: remove webtorrent in v7 |
3 | |||
4 | forceTranscoding?: boolean // Default false | ||
3 | } | 5 | } |
diff --git a/shared/server-commands/videos/videos-command.ts b/shared/server-commands/videos/videos-command.ts index a58f1c545..4c3513ed4 100644 --- a/shared/server-commands/videos/videos-command.ts +++ b/shared/server-commands/videos/videos-command.ts | |||
@@ -775,19 +775,16 @@ export class VideosCommand extends AbstractCommand { | |||
775 | }) | 775 | }) |
776 | } | 776 | } |
777 | 777 | ||
778 | runTranscoding (options: OverrideCommandOptions & { | 778 | runTranscoding (options: OverrideCommandOptions & VideoTranscodingCreate & { |
779 | videoId: number | string | 779 | videoId: number | string |
780 | transcodingType: 'hls' | 'webtorrent' | 'web-video' | ||
781 | }) { | 780 | }) { |
782 | const path = '/api/v1/videos/' + options.videoId + '/transcoding' | 781 | const path = '/api/v1/videos/' + options.videoId + '/transcoding' |
783 | 782 | ||
784 | const fields: VideoTranscodingCreate = pick(options, [ 'transcodingType' ]) | ||
785 | |||
786 | return this.postBodyRequest({ | 783 | return this.postBodyRequest({ |
787 | ...options, | 784 | ...options, |
788 | 785 | ||
789 | path, | 786 | path, |
790 | fields, | 787 | fields: pick(options, [ 'transcodingType', 'forceTranscoding' ]), |
791 | implicitToken: true, | 788 | implicitToken: true, |
792 | defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 | 789 | defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 |
793 | }) | 790 | }) |
diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml index 44daecf85..0cbc58678 100644 --- a/support/doc/api/openapi.yaml +++ b/support/doc/api/openapi.yaml | |||
@@ -4914,6 +4914,10 @@ paths: | |||
4914 | enum: | 4914 | enum: |
4915 | - hls | 4915 | - hls |
4916 | - web-video | 4916 | - web-video |
4917 | forceTranscoding: | ||
4918 | type: boolean | ||
4919 | default: false | ||
4920 | description: If the video is stuck in transcoding state, do it anyway | ||
4917 | required: | 4921 | required: |
4918 | - transcodingType | 4922 | - transcodingType |
4919 | responses: | 4923 | responses: |