diff options
Diffstat (limited to 'server/controllers/api/videos')
-rw-r--r-- | server/controllers/api/videos/files.ts | 34 | ||||
-rw-r--r-- | server/controllers/api/videos/import.ts | 7 | ||||
-rw-r--r-- | server/controllers/api/videos/index.ts | 6 | ||||
-rw-r--r-- | server/controllers/api/videos/live.ts | 11 | ||||
-rw-r--r-- | server/controllers/api/videos/passwords.ts | 105 | ||||
-rw-r--r-- | server/controllers/api/videos/storyboard.ts | 29 | ||||
-rw-r--r-- | server/controllers/api/videos/token.ts | 16 | ||||
-rw-r--r-- | server/controllers/api/videos/update.ts | 16 | ||||
-rw-r--r-- | server/controllers/api/videos/upload.ts | 26 |
9 files changed, 211 insertions, 39 deletions
diff --git a/server/controllers/api/videos/files.ts b/server/controllers/api/videos/files.ts index 6d9c0b843..67b60ff63 100644 --- a/server/controllers/api/videos/files.ts +++ b/server/controllers/api/videos/files.ts | |||
@@ -2,7 +2,8 @@ import express from 'express' | |||
2 | import toInt from 'validator/lib/toInt' | 2 | import toInt from 'validator/lib/toInt' |
3 | import { logger, loggerTagsFactory } from '@server/helpers/logger' | 3 | import { logger, loggerTagsFactory } from '@server/helpers/logger' |
4 | import { federateVideoIfNeeded } from '@server/lib/activitypub/videos' | 4 | import { federateVideoIfNeeded } from '@server/lib/activitypub/videos' |
5 | import { removeAllWebTorrentFiles, removeHLSFile, removeHLSPlaylist, removeWebTorrentFile } from '@server/lib/video-file' | 5 | import { updatePlaylistAfterFileChange } from '@server/lib/hls' |
6 | import { removeAllWebVideoFiles, removeHLSFile, removeHLSPlaylist, removeWebVideoFile } from '@server/lib/video-file' | ||
6 | import { VideoFileModel } from '@server/models/video/video-file' | 7 | import { VideoFileModel } from '@server/models/video/video-file' |
7 | import { HttpStatusCode, UserRight } from '@shared/models' | 8 | import { HttpStatusCode, UserRight } from '@shared/models' |
8 | import { | 9 | import { |
@@ -12,11 +13,10 @@ import { | |||
12 | videoFileMetadataGetValidator, | 13 | videoFileMetadataGetValidator, |
13 | videoFilesDeleteHLSFileValidator, | 14 | videoFilesDeleteHLSFileValidator, |
14 | videoFilesDeleteHLSValidator, | 15 | videoFilesDeleteHLSValidator, |
15 | videoFilesDeleteWebTorrentFileValidator, | 16 | videoFilesDeleteWebVideoFileValidator, |
16 | videoFilesDeleteWebTorrentValidator, | 17 | videoFilesDeleteWebVideoValidator, |
17 | videosGetValidator | 18 | videosGetValidator |
18 | } from '../../../middlewares' | 19 | } from '../../../middlewares' |
19 | import { updatePlaylistAfterFileChange } from '@server/lib/hls' | ||
20 | 20 | ||
21 | const lTags = loggerTagsFactory('api', 'video') | 21 | const lTags = loggerTagsFactory('api', 'video') |
22 | const filesRouter = express.Router() | 22 | const filesRouter = express.Router() |
@@ -40,17 +40,19 @@ filesRouter.delete('/:id/hls/:videoFileId', | |||
40 | asyncMiddleware(removeHLSFileController) | 40 | asyncMiddleware(removeHLSFileController) |
41 | ) | 41 | ) |
42 | 42 | ||
43 | filesRouter.delete('/:id/webtorrent', | 43 | filesRouter.delete( |
44 | [ '/:id/webtorrent', '/:id/web-videos' ], // TODO: remove webtorrent in V7 | ||
44 | authenticate, | 45 | authenticate, |
45 | ensureUserHasRight(UserRight.MANAGE_VIDEO_FILES), | 46 | ensureUserHasRight(UserRight.MANAGE_VIDEO_FILES), |
46 | asyncMiddleware(videoFilesDeleteWebTorrentValidator), | 47 | asyncMiddleware(videoFilesDeleteWebVideoValidator), |
47 | asyncMiddleware(removeAllWebTorrentFilesController) | 48 | asyncMiddleware(removeAllWebVideoFilesController) |
48 | ) | 49 | ) |
49 | filesRouter.delete('/:id/webtorrent/:videoFileId', | 50 | filesRouter.delete( |
51 | [ '/:id/webtorrent/:videoFileId', '/:id/web-videos/:videoFileId' ], // TODO: remove webtorrent in V7 | ||
50 | authenticate, | 52 | authenticate, |
51 | ensureUserHasRight(UserRight.MANAGE_VIDEO_FILES), | 53 | ensureUserHasRight(UserRight.MANAGE_VIDEO_FILES), |
52 | asyncMiddleware(videoFilesDeleteWebTorrentFileValidator), | 54 | asyncMiddleware(videoFilesDeleteWebVideoFileValidator), |
53 | asyncMiddleware(removeWebTorrentFileController) | 55 | asyncMiddleware(removeWebVideoFileController) |
54 | ) | 56 | ) |
55 | 57 | ||
56 | // --------------------------------------------------------------------------- | 58 | // --------------------------------------------------------------------------- |
@@ -96,24 +98,24 @@ async function removeHLSFileController (req: express.Request, res: express.Respo | |||
96 | 98 | ||
97 | // --------------------------------------------------------------------------- | 99 | // --------------------------------------------------------------------------- |
98 | 100 | ||
99 | async function removeAllWebTorrentFilesController (req: express.Request, res: express.Response) { | 101 | async function removeAllWebVideoFilesController (req: express.Request, res: express.Response) { |
100 | const video = res.locals.videoAll | 102 | const video = res.locals.videoAll |
101 | 103 | ||
102 | logger.info('Deleting WebTorrent files of %s.', video.url, lTags(video.uuid)) | 104 | logger.info('Deleting Web Video files of %s.', video.url, lTags(video.uuid)) |
103 | 105 | ||
104 | await removeAllWebTorrentFiles(video) | 106 | await removeAllWebVideoFiles(video) |
105 | await federateVideoIfNeeded(video, false, undefined) | 107 | await federateVideoIfNeeded(video, false, undefined) |
106 | 108 | ||
107 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 109 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) |
108 | } | 110 | } |
109 | 111 | ||
110 | async function removeWebTorrentFileController (req: express.Request, res: express.Response) { | 112 | async function removeWebVideoFileController (req: express.Request, res: express.Response) { |
111 | const video = res.locals.videoAll | 113 | const video = res.locals.videoAll |
112 | 114 | ||
113 | const videoFileId = +req.params.videoFileId | 115 | const videoFileId = +req.params.videoFileId |
114 | logger.info('Deleting WebTorrent file %d of %s.', videoFileId, video.url, lTags(video.uuid)) | 116 | logger.info('Deleting Web Video file %d of %s.', videoFileId, video.url, lTags(video.uuid)) |
115 | 117 | ||
116 | await removeWebTorrentFile(video, videoFileId) | 118 | await removeWebVideoFile(video, videoFileId) |
117 | await federateVideoIfNeeded(video, false, undefined) | 119 | await federateVideoIfNeeded(video, false, undefined) |
118 | 120 | ||
119 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 121 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) |
diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts index 6a50aaf4e..defe9efd4 100644 --- a/server/controllers/api/videos/import.ts +++ b/server/controllers/api/videos/import.ts | |||
@@ -14,7 +14,7 @@ import { getSecureTorrentName } from '../../../helpers/utils' | |||
14 | import { CONFIG } from '../../../initializers/config' | 14 | import { CONFIG } from '../../../initializers/config' |
15 | import { MIMETYPES } from '../../../initializers/constants' | 15 | import { MIMETYPES } from '../../../initializers/constants' |
16 | import { JobQueue } from '../../../lib/job-queue/job-queue' | 16 | import { JobQueue } from '../../../lib/job-queue/job-queue' |
17 | import { updateVideoMiniatureFromExisting } from '../../../lib/thumbnail' | 17 | import { updateLocalVideoMiniatureFromExisting } from '../../../lib/thumbnail' |
18 | import { | 18 | import { |
19 | asyncMiddleware, | 19 | asyncMiddleware, |
20 | asyncRetryTransactionMiddleware, | 20 | asyncRetryTransactionMiddleware, |
@@ -120,6 +120,7 @@ async function handleTorrentImport (req: express.Request, res: express.Response, | |||
120 | videoChannel: res.locals.videoChannel, | 120 | videoChannel: res.locals.videoChannel, |
121 | tags: body.tags || undefined, | 121 | tags: body.tags || undefined, |
122 | user, | 122 | user, |
123 | videoPasswords: body.videoPasswords, | ||
123 | videoImportAttributes: { | 124 | videoImportAttributes: { |
124 | magnetUri, | 125 | magnetUri, |
125 | torrentName, | 126 | torrentName, |
@@ -192,7 +193,7 @@ async function processThumbnail (req: express.Request, video: MVideoThumbnail) { | |||
192 | if (thumbnailField) { | 193 | if (thumbnailField) { |
193 | const thumbnailPhysicalFile = thumbnailField[0] | 194 | const thumbnailPhysicalFile = thumbnailField[0] |
194 | 195 | ||
195 | return updateVideoMiniatureFromExisting({ | 196 | return updateLocalVideoMiniatureFromExisting({ |
196 | inputPath: thumbnailPhysicalFile.path, | 197 | inputPath: thumbnailPhysicalFile.path, |
197 | video, | 198 | video, |
198 | type: ThumbnailType.MINIATURE, | 199 | type: ThumbnailType.MINIATURE, |
@@ -208,7 +209,7 @@ async function processPreview (req: express.Request, video: MVideoThumbnail): Pr | |||
208 | if (previewField) { | 209 | if (previewField) { |
209 | const previewPhysicalFile = previewField[0] | 210 | const previewPhysicalFile = previewField[0] |
210 | 211 | ||
211 | return updateVideoMiniatureFromExisting({ | 212 | return updateLocalVideoMiniatureFromExisting({ |
212 | inputPath: previewPhysicalFile.path, | 213 | inputPath: previewPhysicalFile.path, |
213 | video, | 214 | video, |
214 | type: ThumbnailType.PREVIEW, | 215 | type: ThumbnailType.PREVIEW, |
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index a34325e79..520d8cbbb 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts | |||
@@ -3,7 +3,6 @@ import { pickCommonVideoQuery } from '@server/helpers/query' | |||
3 | import { doJSONRequest } from '@server/helpers/requests' | 3 | import { doJSONRequest } from '@server/helpers/requests' |
4 | import { openapiOperationDoc } from '@server/middlewares/doc' | 4 | import { openapiOperationDoc } from '@server/middlewares/doc' |
5 | import { getServerActor } from '@server/models/application/application' | 5 | import { getServerActor } from '@server/models/application/application' |
6 | import { guessAdditionalAttributesFromQuery } from '@server/models/video/formatter/video-format-utils' | ||
7 | import { MVideoAccountLight } from '@server/types/models' | 6 | import { MVideoAccountLight } from '@server/types/models' |
8 | import { HttpStatusCode } from '../../../../shared/models' | 7 | import { HttpStatusCode } from '../../../../shared/models' |
9 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' | 8 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' |
@@ -31,6 +30,7 @@ import { | |||
31 | videosRemoveValidator, | 30 | videosRemoveValidator, |
32 | videosSortValidator | 31 | videosSortValidator |
33 | } from '../../../middlewares' | 32 | } from '../../../middlewares' |
33 | import { guessAdditionalAttributesFromQuery } from '../../../models/video/formatter' | ||
34 | import { VideoModel } from '../../../models/video/video' | 34 | import { VideoModel } from '../../../models/video/video' |
35 | import { blacklistRouter } from './blacklist' | 35 | import { blacklistRouter } from './blacklist' |
36 | import { videoCaptionsRouter } from './captions' | 36 | import { videoCaptionsRouter } from './captions' |
@@ -41,12 +41,14 @@ import { liveRouter } from './live' | |||
41 | import { ownershipVideoRouter } from './ownership' | 41 | import { ownershipVideoRouter } from './ownership' |
42 | import { rateVideoRouter } from './rate' | 42 | import { rateVideoRouter } from './rate' |
43 | import { statsRouter } from './stats' | 43 | import { statsRouter } from './stats' |
44 | import { storyboardRouter } from './storyboard' | ||
44 | import { studioRouter } from './studio' | 45 | import { studioRouter } from './studio' |
45 | import { tokenRouter } from './token' | 46 | import { tokenRouter } from './token' |
46 | import { transcodingRouter } from './transcoding' | 47 | import { transcodingRouter } from './transcoding' |
47 | import { updateRouter } from './update' | 48 | import { updateRouter } from './update' |
48 | import { uploadRouter } from './upload' | 49 | import { uploadRouter } from './upload' |
49 | import { viewRouter } from './view' | 50 | import { viewRouter } from './view' |
51 | import { videoPasswordRouter } from './passwords' | ||
50 | 52 | ||
51 | const auditLogger = auditLoggerFactory('videos') | 53 | const auditLogger = auditLoggerFactory('videos') |
52 | const videosRouter = express.Router() | 54 | const videosRouter = express.Router() |
@@ -68,6 +70,8 @@ videosRouter.use('/', updateRouter) | |||
68 | videosRouter.use('/', filesRouter) | 70 | videosRouter.use('/', filesRouter) |
69 | videosRouter.use('/', transcodingRouter) | 71 | videosRouter.use('/', transcodingRouter) |
70 | videosRouter.use('/', tokenRouter) | 72 | videosRouter.use('/', tokenRouter) |
73 | videosRouter.use('/', videoPasswordRouter) | ||
74 | videosRouter.use('/', storyboardRouter) | ||
71 | 75 | ||
72 | videosRouter.get('/categories', | 76 | videosRouter.get('/categories', |
73 | openapiOperationDoc({ operationId: 'getCategories' }), | 77 | openapiOperationDoc({ operationId: 'getCategories' }), |
diff --git a/server/controllers/api/videos/live.ts b/server/controllers/api/videos/live.ts index de047d4ec..e19e8c652 100644 --- a/server/controllers/api/videos/live.ts +++ b/server/controllers/api/videos/live.ts | |||
@@ -18,13 +18,14 @@ import { VideoLiveModel } from '@server/models/video/video-live' | |||
18 | import { VideoLiveSessionModel } from '@server/models/video/video-live-session' | 18 | import { VideoLiveSessionModel } from '@server/models/video/video-live-session' |
19 | import { MVideoDetails, MVideoFullLight, MVideoLive } from '@server/types/models' | 19 | import { MVideoDetails, MVideoFullLight, MVideoLive } from '@server/types/models' |
20 | import { buildUUID, uuidToShort } from '@shared/extra-utils' | 20 | import { buildUUID, uuidToShort } from '@shared/extra-utils' |
21 | import { HttpStatusCode, LiveVideoCreate, LiveVideoLatencyMode, LiveVideoUpdate, UserRight, VideoState } from '@shared/models' | 21 | import { HttpStatusCode, LiveVideoCreate, LiveVideoLatencyMode, LiveVideoUpdate, UserRight, VideoPrivacy, VideoState } from '@shared/models' |
22 | import { logger } from '../../../helpers/logger' | 22 | import { logger } from '../../../helpers/logger' |
23 | import { sequelizeTypescript } from '../../../initializers/database' | 23 | import { sequelizeTypescript } from '../../../initializers/database' |
24 | import { updateVideoMiniatureFromExisting } from '../../../lib/thumbnail' | 24 | import { updateLocalVideoMiniatureFromExisting } from '../../../lib/thumbnail' |
25 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, optionalAuthenticate } from '../../../middlewares' | 25 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, optionalAuthenticate } from '../../../middlewares' |
26 | import { VideoModel } from '../../../models/video/video' | 26 | import { VideoModel } from '../../../models/video/video' |
27 | import { VideoLiveReplaySettingModel } from '@server/models/video/video-live-replay-setting' | 27 | import { VideoLiveReplaySettingModel } from '@server/models/video/video-live-replay-setting' |
28 | import { VideoPasswordModel } from '@server/models/video/video-password' | ||
28 | 29 | ||
29 | const liveRouter = express.Router() | 30 | const liveRouter = express.Router() |
30 | 31 | ||
@@ -165,7 +166,7 @@ async function addLiveVideo (req: express.Request, res: express.Response) { | |||
165 | video, | 166 | video, |
166 | files: req.files, | 167 | files: req.files, |
167 | fallback: type => { | 168 | fallback: type => { |
168 | return updateVideoMiniatureFromExisting({ | 169 | return updateLocalVideoMiniatureFromExisting({ |
169 | inputPath: ASSETS_PATH.DEFAULT_LIVE_BACKGROUND, | 170 | inputPath: ASSETS_PATH.DEFAULT_LIVE_BACKGROUND, |
170 | video, | 171 | video, |
171 | type, | 172 | type, |
@@ -202,6 +203,10 @@ async function addLiveVideo (req: express.Request, res: express.Response) { | |||
202 | 203 | ||
203 | await federateVideoIfNeeded(videoCreated, true, t) | 204 | await federateVideoIfNeeded(videoCreated, true, t) |
204 | 205 | ||
206 | if (videoInfo.privacy === VideoPrivacy.PASSWORD_PROTECTED) { | ||
207 | await VideoPasswordModel.addPasswords(videoInfo.videoPasswords, video.id, t) | ||
208 | } | ||
209 | |||
205 | logger.info('Video live %s with uuid %s created.', videoInfo.name, videoCreated.uuid) | 210 | logger.info('Video live %s with uuid %s created.', videoInfo.name, videoCreated.uuid) |
206 | 211 | ||
207 | return { videoCreated } | 212 | return { videoCreated } |
diff --git a/server/controllers/api/videos/passwords.ts b/server/controllers/api/videos/passwords.ts new file mode 100644 index 000000000..d11cf5bcc --- /dev/null +++ b/server/controllers/api/videos/passwords.ts | |||
@@ -0,0 +1,105 @@ | |||
1 | import express from 'express' | ||
2 | |||
3 | import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes' | ||
4 | import { getFormattedObjects } from '../../../helpers/utils' | ||
5 | import { | ||
6 | asyncMiddleware, | ||
7 | asyncRetryTransactionMiddleware, | ||
8 | authenticate, | ||
9 | setDefaultPagination, | ||
10 | setDefaultSort | ||
11 | } from '../../../middlewares' | ||
12 | import { | ||
13 | listVideoPasswordValidator, | ||
14 | paginationValidator, | ||
15 | removeVideoPasswordValidator, | ||
16 | updateVideoPasswordListValidator, | ||
17 | videoPasswordsSortValidator | ||
18 | } from '../../../middlewares/validators' | ||
19 | import { VideoPasswordModel } from '@server/models/video/video-password' | ||
20 | import { logger, loggerTagsFactory } from '@server/helpers/logger' | ||
21 | import { Transaction } from 'sequelize' | ||
22 | import { getVideoWithAttributes } from '@server/helpers/video' | ||
23 | |||
24 | const lTags = loggerTagsFactory('api', 'video') | ||
25 | const videoPasswordRouter = express.Router() | ||
26 | |||
27 | videoPasswordRouter.get('/:videoId/passwords', | ||
28 | authenticate, | ||
29 | paginationValidator, | ||
30 | videoPasswordsSortValidator, | ||
31 | setDefaultSort, | ||
32 | setDefaultPagination, | ||
33 | asyncMiddleware(listVideoPasswordValidator), | ||
34 | asyncMiddleware(listVideoPasswords) | ||
35 | ) | ||
36 | |||
37 | videoPasswordRouter.put('/:videoId/passwords', | ||
38 | authenticate, | ||
39 | asyncMiddleware(updateVideoPasswordListValidator), | ||
40 | asyncMiddleware(updateVideoPasswordList) | ||
41 | ) | ||
42 | |||
43 | videoPasswordRouter.delete('/:videoId/passwords/:passwordId', | ||
44 | authenticate, | ||
45 | asyncMiddleware(removeVideoPasswordValidator), | ||
46 | asyncRetryTransactionMiddleware(removeVideoPassword) | ||
47 | ) | ||
48 | |||
49 | // --------------------------------------------------------------------------- | ||
50 | |||
51 | export { | ||
52 | videoPasswordRouter | ||
53 | } | ||
54 | |||
55 | // --------------------------------------------------------------------------- | ||
56 | |||
57 | async function listVideoPasswords (req: express.Request, res: express.Response) { | ||
58 | const options = { | ||
59 | videoId: res.locals.videoAll.id, | ||
60 | start: req.query.start, | ||
61 | count: req.query.count, | ||
62 | sort: req.query.sort | ||
63 | } | ||
64 | |||
65 | const resultList = await VideoPasswordModel.listPasswords(options) | ||
66 | |||
67 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | ||
68 | } | ||
69 | |||
70 | async function updateVideoPasswordList (req: express.Request, res: express.Response) { | ||
71 | const videoInstance = getVideoWithAttributes(res) | ||
72 | const videoId = videoInstance.id | ||
73 | |||
74 | const passwordArray = req.body.passwords as string[] | ||
75 | |||
76 | await VideoPasswordModel.sequelize.transaction(async (t: Transaction) => { | ||
77 | await VideoPasswordModel.deleteAllPasswords(videoId, t) | ||
78 | await VideoPasswordModel.addPasswords(passwordArray, videoId, t) | ||
79 | }) | ||
80 | |||
81 | logger.info( | ||
82 | `Video passwords for video with name %s and uuid %s have been updated`, | ||
83 | videoInstance.name, | ||
84 | videoInstance.uuid, | ||
85 | lTags(videoInstance.uuid) | ||
86 | ) | ||
87 | |||
88 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | ||
89 | } | ||
90 | |||
91 | async function removeVideoPassword (req: express.Request, res: express.Response) { | ||
92 | const videoInstance = getVideoWithAttributes(res) | ||
93 | const password = res.locals.videoPassword | ||
94 | |||
95 | await VideoPasswordModel.deletePassword(password.id) | ||
96 | logger.info( | ||
97 | 'Password with id %d of video named %s and uuid %s has been deleted.', | ||
98 | password.id, | ||
99 | videoInstance.name, | ||
100 | videoInstance.uuid, | ||
101 | lTags(videoInstance.uuid) | ||
102 | ) | ||
103 | |||
104 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | ||
105 | } | ||
diff --git a/server/controllers/api/videos/storyboard.ts b/server/controllers/api/videos/storyboard.ts new file mode 100644 index 000000000..47a22011d --- /dev/null +++ b/server/controllers/api/videos/storyboard.ts | |||
@@ -0,0 +1,29 @@ | |||
1 | import express from 'express' | ||
2 | import { getVideoWithAttributes } from '@server/helpers/video' | ||
3 | import { StoryboardModel } from '@server/models/video/storyboard' | ||
4 | import { asyncMiddleware, videosGetValidator } from '../../../middlewares' | ||
5 | |||
6 | const storyboardRouter = express.Router() | ||
7 | |||
8 | storyboardRouter.get('/:id/storyboards', | ||
9 | asyncMiddleware(videosGetValidator), | ||
10 | asyncMiddleware(listStoryboards) | ||
11 | ) | ||
12 | |||
13 | // --------------------------------------------------------------------------- | ||
14 | |||
15 | export { | ||
16 | storyboardRouter | ||
17 | } | ||
18 | |||
19 | // --------------------------------------------------------------------------- | ||
20 | |||
21 | async function listStoryboards (req: express.Request, res: express.Response) { | ||
22 | const video = getVideoWithAttributes(res) | ||
23 | |||
24 | const storyboards = await StoryboardModel.listStoryboardsOf(video) | ||
25 | |||
26 | return res.json({ | ||
27 | storyboards: storyboards.map(s => s.toFormattedJSON()) | ||
28 | }) | ||
29 | } | ||
diff --git a/server/controllers/api/videos/token.ts b/server/controllers/api/videos/token.ts index 22387c3e8..e961ffd9e 100644 --- a/server/controllers/api/videos/token.ts +++ b/server/controllers/api/videos/token.ts | |||
@@ -1,13 +1,14 @@ | |||
1 | import express from 'express' | 1 | import express from 'express' |
2 | import { VideoTokensManager } from '@server/lib/video-tokens-manager' | 2 | import { VideoTokensManager } from '@server/lib/video-tokens-manager' |
3 | import { VideoToken } from '@shared/models' | 3 | import { VideoPrivacy, VideoToken } from '@shared/models' |
4 | import { asyncMiddleware, authenticate, videosCustomGetValidator } from '../../../middlewares' | 4 | import { asyncMiddleware, optionalAuthenticate, videoFileTokenValidator, videosCustomGetValidator } from '../../../middlewares' |
5 | 5 | ||
6 | const tokenRouter = express.Router() | 6 | const tokenRouter = express.Router() |
7 | 7 | ||
8 | tokenRouter.post('/:id/token', | 8 | tokenRouter.post('/:id/token', |
9 | authenticate, | 9 | optionalAuthenticate, |
10 | asyncMiddleware(videosCustomGetValidator('only-video')), | 10 | asyncMiddleware(videosCustomGetValidator('only-video')), |
11 | videoFileTokenValidator, | ||
11 | generateToken | 12 | generateToken |
12 | ) | 13 | ) |
13 | 14 | ||
@@ -22,12 +23,11 @@ export { | |||
22 | function generateToken (req: express.Request, res: express.Response) { | 23 | function generateToken (req: express.Request, res: express.Response) { |
23 | const video = res.locals.onlyVideo | 24 | const video = res.locals.onlyVideo |
24 | 25 | ||
25 | const { token, expires } = VideoTokensManager.Instance.create({ videoUUID: video.uuid, user: res.locals.oauth.token.User }) | 26 | const files = video.privacy === VideoPrivacy.PASSWORD_PROTECTED |
27 | ? VideoTokensManager.Instance.createForPasswordProtectedVideo({ videoUUID: video.uuid }) | ||
28 | : VideoTokensManager.Instance.createForAuthUser({ videoUUID: video.uuid, user: res.locals.oauth.token.User }) | ||
26 | 29 | ||
27 | return res.json({ | 30 | return res.json({ |
28 | files: { | 31 | files |
29 | token, | ||
30 | expires | ||
31 | } | ||
32 | } as VideoToken) | 32 | } as VideoToken) |
33 | } | 33 | } |
diff --git a/server/controllers/api/videos/update.ts b/server/controllers/api/videos/update.ts index ddab428d4..28ec2cf37 100644 --- a/server/controllers/api/videos/update.ts +++ b/server/controllers/api/videos/update.ts | |||
@@ -2,13 +2,12 @@ import express from 'express' | |||
2 | import { Transaction } from 'sequelize/types' | 2 | import { Transaction } from 'sequelize/types' |
3 | import { changeVideoChannelShare } from '@server/lib/activitypub/share' | 3 | import { changeVideoChannelShare } from '@server/lib/activitypub/share' |
4 | import { addVideoJobsAfterUpdate, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video' | 4 | import { addVideoJobsAfterUpdate, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video' |
5 | import { VideoPathManager } from '@server/lib/video-path-manager' | ||
6 | import { setVideoPrivacy } from '@server/lib/video-privacy' | 5 | import { setVideoPrivacy } from '@server/lib/video-privacy' |
7 | import { openapiOperationDoc } from '@server/middlewares/doc' | 6 | import { openapiOperationDoc } from '@server/middlewares/doc' |
8 | import { FilteredModelAttributes } from '@server/types' | 7 | import { FilteredModelAttributes } from '@server/types' |
9 | import { MVideoFullLight } from '@server/types/models' | 8 | import { MVideoFullLight } from '@server/types/models' |
10 | import { forceNumber } from '@shared/core-utils' | 9 | import { forceNumber } from '@shared/core-utils' |
11 | import { HttpStatusCode, VideoUpdate } from '@shared/models' | 10 | import { HttpStatusCode, VideoPrivacy, VideoUpdate } from '@shared/models' |
12 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' | 11 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' |
13 | import { resetSequelizeInstance } from '../../../helpers/database-utils' | 12 | import { resetSequelizeInstance } from '../../../helpers/database-utils' |
14 | import { createReqFiles } from '../../../helpers/express-utils' | 13 | import { createReqFiles } from '../../../helpers/express-utils' |
@@ -20,6 +19,9 @@ import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' | |||
20 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videosUpdateValidator } from '../../../middlewares' | 19 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videosUpdateValidator } from '../../../middlewares' |
21 | import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update' | 20 | import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update' |
22 | import { VideoModel } from '../../../models/video/video' | 21 | import { VideoModel } from '../../../models/video/video' |
22 | import { VideoPathManager } from '@server/lib/video-path-manager' | ||
23 | import { VideoPasswordModel } from '@server/models/video/video-password' | ||
24 | import { exists } from '@server/helpers/custom-validators/misc' | ||
23 | 25 | ||
24 | const lTags = loggerTagsFactory('api', 'video') | 26 | const lTags = loggerTagsFactory('api', 'video') |
25 | const auditLogger = auditLoggerFactory('videos') | 27 | const auditLogger = auditLoggerFactory('videos') |
@@ -176,6 +178,16 @@ async function updateVideoPrivacy (options: { | |||
176 | const newPrivacy = forceNumber(videoInfoToUpdate.privacy) | 178 | const newPrivacy = forceNumber(videoInfoToUpdate.privacy) |
177 | setVideoPrivacy(videoInstance, newPrivacy) | 179 | setVideoPrivacy(videoInstance, newPrivacy) |
178 | 180 | ||
181 | // Delete passwords if video is not anymore password protected | ||
182 | if (videoInstance.privacy === VideoPrivacy.PASSWORD_PROTECTED && newPrivacy !== VideoPrivacy.PASSWORD_PROTECTED) { | ||
183 | await VideoPasswordModel.deleteAllPasswords(videoInstance.id, transaction) | ||
184 | } | ||
185 | |||
186 | if (newPrivacy === VideoPrivacy.PASSWORD_PROTECTED && exists(videoInfoToUpdate.videoPasswords)) { | ||
187 | await VideoPasswordModel.deleteAllPasswords(videoInstance.id, transaction) | ||
188 | await VideoPasswordModel.addPasswords(videoInfoToUpdate.videoPasswords, videoInstance.id, transaction) | ||
189 | } | ||
190 | |||
179 | // Unfederate the video if the new privacy is not compatible with federation | 191 | // Unfederate the video if the new privacy is not compatible with federation |
180 | if (hadPrivacyForFederation && !videoInstance.hasPrivacyForFederation()) { | 192 | if (hadPrivacyForFederation && !videoInstance.hasPrivacyForFederation()) { |
181 | await VideoModel.sendDelete(videoInstance, { transaction }) | 193 | await VideoModel.sendDelete(videoInstance, { transaction }) |
diff --git a/server/controllers/api/videos/upload.ts b/server/controllers/api/videos/upload.ts index 885ac8b81..27fef0b1a 100644 --- a/server/controllers/api/videos/upload.ts +++ b/server/controllers/api/videos/upload.ts | |||
@@ -14,14 +14,14 @@ import { openapiOperationDoc } from '@server/middlewares/doc' | |||
14 | import { VideoSourceModel } from '@server/models/video/video-source' | 14 | import { VideoSourceModel } from '@server/models/video/video-source' |
15 | import { MUserId, MVideoFile, MVideoFullLight } from '@server/types/models' | 15 | import { MUserId, MVideoFile, MVideoFullLight } from '@server/types/models' |
16 | import { uuidToShort } from '@shared/extra-utils' | 16 | import { uuidToShort } from '@shared/extra-utils' |
17 | import { HttpStatusCode, VideoCreate, VideoState } from '@shared/models' | 17 | import { HttpStatusCode, VideoCreate, VideoPrivacy, VideoState } from '@shared/models' |
18 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' | 18 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' |
19 | import { createReqFiles } from '../../../helpers/express-utils' | 19 | import { createReqFiles } from '../../../helpers/express-utils' |
20 | import { logger, loggerTagsFactory } from '../../../helpers/logger' | 20 | import { logger, loggerTagsFactory } from '../../../helpers/logger' |
21 | import { MIMETYPES } from '../../../initializers/constants' | 21 | import { MIMETYPES } from '../../../initializers/constants' |
22 | import { sequelizeTypescript } from '../../../initializers/database' | 22 | import { sequelizeTypescript } from '../../../initializers/database' |
23 | import { Hooks } from '../../../lib/plugins/hooks' | 23 | import { Hooks } from '../../../lib/plugins/hooks' |
24 | import { generateVideoMiniature } from '../../../lib/thumbnail' | 24 | import { generateLocalVideoMiniature } from '../../../lib/thumbnail' |
25 | import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' | 25 | import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' |
26 | import { | 26 | import { |
27 | asyncMiddleware, | 27 | asyncMiddleware, |
@@ -33,6 +33,7 @@ import { | |||
33 | } from '../../../middlewares' | 33 | } from '../../../middlewares' |
34 | import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update' | 34 | import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update' |
35 | import { VideoModel } from '../../../models/video/video' | 35 | import { VideoModel } from '../../../models/video/video' |
36 | import { VideoPasswordModel } from '@server/models/video/video-password' | ||
36 | 37 | ||
37 | const lTags = loggerTagsFactory('api', 'video') | 38 | const lTags = loggerTagsFactory('api', 'video') |
38 | const auditLogger = auditLoggerFactory('videos') | 39 | const auditLogger = auditLoggerFactory('videos') |
@@ -62,13 +63,13 @@ uploadRouter.post('/upload-resumable', | |||
62 | authenticate, | 63 | authenticate, |
63 | reqVideoFileAddResumable, | 64 | reqVideoFileAddResumable, |
64 | asyncMiddleware(videosAddResumableInitValidator), | 65 | asyncMiddleware(videosAddResumableInitValidator), |
65 | uploadx.upload | 66 | (req, res) => uploadx.upload(req, res) // Prevent next() call, explicitely tell to uploadx it's the end |
66 | ) | 67 | ) |
67 | 68 | ||
68 | uploadRouter.delete('/upload-resumable', | 69 | uploadRouter.delete('/upload-resumable', |
69 | authenticate, | 70 | authenticate, |
70 | asyncMiddleware(deleteUploadResumableCache), | 71 | asyncMiddleware(deleteUploadResumableCache), |
71 | uploadx.upload | 72 | (req, res) => uploadx.upload(req, res) // Prevent next() call, explicitely tell to uploadx it's the end |
72 | ) | 73 | ) |
73 | 74 | ||
74 | uploadRouter.put('/upload-resumable', | 75 | uploadRouter.put('/upload-resumable', |
@@ -110,7 +111,7 @@ async function addVideoLegacy (req: express.Request, res: express.Response) { | |||
110 | async function addVideoResumable (req: express.Request, res: express.Response) { | 111 | async function addVideoResumable (req: express.Request, res: express.Response) { |
111 | const videoPhysicalFile = res.locals.videoFileResumable | 112 | const videoPhysicalFile = res.locals.videoFileResumable |
112 | const videoInfo = videoPhysicalFile.metadata | 113 | const videoInfo = videoPhysicalFile.metadata |
113 | const files = { previewfile: videoInfo.previewfile } | 114 | const files = { previewfile: videoInfo.previewfile, thumbnailfile: videoInfo.thumbnailfile } |
114 | 115 | ||
115 | const response = await addVideo({ req, res, videoPhysicalFile, videoInfo, files }) | 116 | const response = await addVideo({ req, res, videoPhysicalFile, videoInfo, files }) |
116 | await Redis.Instance.setUploadSession(req.query.upload_id, response) | 117 | await Redis.Instance.setUploadSession(req.query.upload_id, response) |
@@ -152,7 +153,7 @@ async function addVideo (options: { | |||
152 | const [ thumbnailModel, previewModel ] = await buildVideoThumbnailsFromReq({ | 153 | const [ thumbnailModel, previewModel ] = await buildVideoThumbnailsFromReq({ |
153 | video, | 154 | video, |
154 | files, | 155 | files, |
155 | fallback: type => generateVideoMiniature({ video, videoFile, type }) | 156 | fallback: type => generateLocalVideoMiniature({ video, videoFile, type }) |
156 | }) | 157 | }) |
157 | 158 | ||
158 | const { videoCreated } = await sequelizeTypescript.transaction(async t => { | 159 | const { videoCreated } = await sequelizeTypescript.transaction(async t => { |
@@ -195,6 +196,10 @@ async function addVideo (options: { | |||
195 | transaction: t | 196 | transaction: t |
196 | }) | 197 | }) |
197 | 198 | ||
199 | if (videoInfo.privacy === VideoPrivacy.PASSWORD_PROTECTED) { | ||
200 | await VideoPasswordModel.addPasswords(videoInfo.videoPasswords, video.id, t) | ||
201 | } | ||
202 | |||
198 | auditLogger.create(getAuditIdFromRes(res), new VideoAuditView(videoCreated.toFormattedDetailsJSON())) | 203 | auditLogger.create(getAuditIdFromRes(res), new VideoAuditView(videoCreated.toFormattedDetailsJSON())) |
199 | logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid, lTags(videoCreated.uuid)) | 204 | logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid, lTags(videoCreated.uuid)) |
200 | 205 | ||
@@ -230,6 +235,15 @@ async function addVideoJobsAfterUpload (video: MVideoFullLight, videoFile: MVide | |||
230 | }, | 235 | }, |
231 | 236 | ||
232 | { | 237 | { |
238 | type: 'generate-video-storyboard' as 'generate-video-storyboard', | ||
239 | payload: { | ||
240 | videoUUID: video.uuid, | ||
241 | // No need to federate, we process these jobs sequentially | ||
242 | federate: false | ||
243 | } | ||
244 | }, | ||
245 | |||
246 | { | ||
233 | type: 'notify', | 247 | type: 'notify', |
234 | payload: { | 248 | payload: { |
235 | action: 'new-video', | 249 | action: 'new-video', |