diff options
Diffstat (limited to 'server')
23 files changed, 224 insertions, 83 deletions
diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index d65e321e9..c75002aaf 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts | |||
@@ -172,7 +172,8 @@ async function updateCustomConfig (req: express.Request, res: express.Response, | |||
172 | 'instance.defaultClientRoute', | 172 | 'instance.defaultClientRoute', |
173 | 'instance.shortDescription', | 173 | 'instance.shortDescription', |
174 | 'cache.videoCaptions', | 174 | 'cache.videoCaptions', |
175 | 'signup.requiresEmailVerification' | 175 | 'signup.requiresEmailVerification', |
176 | 'transcoding.allowAdditionalExtensions' | ||
176 | ) | 177 | ) |
177 | toUpdateJSON.user['video_quota'] = toUpdate.user.videoQuota | 178 | toUpdateJSON.user['video_quota'] = toUpdate.user.videoQuota |
178 | toUpdateJSON.user['video_quota_daily'] = toUpdate.user.videoQuotaDaily | 179 | toUpdateJSON.user['video_quota_daily'] = toUpdate.user.videoQuotaDaily |
@@ -180,6 +181,7 @@ async function updateCustomConfig (req: express.Request, res: express.Response, | |||
180 | toUpdateJSON.instance['short_description'] = toUpdate.instance.shortDescription | 181 | toUpdateJSON.instance['short_description'] = toUpdate.instance.shortDescription |
181 | toUpdateJSON.instance['default_nsfw_policy'] = toUpdate.instance.defaultNSFWPolicy | 182 | toUpdateJSON.instance['default_nsfw_policy'] = toUpdate.instance.defaultNSFWPolicy |
182 | toUpdateJSON.signup['requires_email_verification'] = toUpdate.signup.requiresEmailVerification | 183 | toUpdateJSON.signup['requires_email_verification'] = toUpdate.signup.requiresEmailVerification |
184 | toUpdateJSON.transcoding['allow_additional_extensions'] = toUpdate.transcoding.allowAdditionalExtensions | ||
183 | 185 | ||
184 | await writeJSON(CONFIG.CUSTOM_FILE, toUpdateJSON, { spaces: 2 }) | 186 | await writeJSON(CONFIG.CUSTOM_FILE, toUpdateJSON, { spaces: 2 }) |
185 | 187 | ||
@@ -247,6 +249,7 @@ function customConfig (): CustomConfig { | |||
247 | }, | 249 | }, |
248 | transcoding: { | 250 | transcoding: { |
249 | enabled: CONFIG.TRANSCODING.ENABLED, | 251 | enabled: CONFIG.TRANSCODING.ENABLED, |
252 | allowAdditionalExtensions: CONFIG.TRANSCODING.ALLOW_ADDITIONAL_EXTENSIONS, | ||
250 | threads: CONFIG.TRANSCODING.THREADS, | 253 | threads: CONFIG.TRANSCODING.THREADS, |
251 | resolutions: { | 254 | resolutions: { |
252 | '240p': CONFIG.TRANSCODING.RESOLUTIONS[ '240p' ], | 255 | '240p': CONFIG.TRANSCODING.RESOLUTIONS[ '240p' ], |
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index d2456346b..f712b0f0b 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts | |||
@@ -2,7 +2,7 @@ import * as express from 'express' | |||
2 | import 'multer' | 2 | import 'multer' |
3 | import { UserUpdateMe, UserVideoRate as FormattedUserVideoRate } from '../../../../shared' | 3 | import { UserUpdateMe, UserVideoRate as FormattedUserVideoRate } from '../../../../shared' |
4 | import { getFormattedObjects } from '../../../helpers/utils' | 4 | import { getFormattedObjects } from '../../../helpers/utils' |
5 | import { CONFIG, IMAGE_MIMETYPE_EXT, sequelizeTypescript } from '../../../initializers' | 5 | import { CONFIG, MIMETYPES, sequelizeTypescript } from '../../../initializers' |
6 | import { sendUpdateActor } from '../../../lib/activitypub/send' | 6 | import { sendUpdateActor } from '../../../lib/activitypub/send' |
7 | import { | 7 | import { |
8 | asyncMiddleware, | 8 | asyncMiddleware, |
@@ -42,7 +42,7 @@ import { AccountModel } from '../../../models/account/account' | |||
42 | 42 | ||
43 | const auditLogger = auditLoggerFactory('users-me') | 43 | const auditLogger = auditLoggerFactory('users-me') |
44 | 44 | ||
45 | const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR }) | 45 | const reqAvatarFile = createReqFiles([ 'avatarfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR }) |
46 | 46 | ||
47 | const meRouter = express.Router() | 47 | const meRouter = express.Router() |
48 | 48 | ||
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts index fd143a139..3d6a6af7f 100644 --- a/server/controllers/api/video-channel.ts +++ b/server/controllers/api/video-channel.ts | |||
@@ -22,7 +22,7 @@ import { createVideoChannel } from '../../lib/video-channel' | |||
22 | import { buildNSFWFilter, createReqFiles, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' | 22 | import { buildNSFWFilter, createReqFiles, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' |
23 | import { setAsyncActorKeys } from '../../lib/activitypub' | 23 | import { setAsyncActorKeys } from '../../lib/activitypub' |
24 | import { AccountModel } from '../../models/account/account' | 24 | import { AccountModel } from '../../models/account/account' |
25 | import { CONFIG, IMAGE_MIMETYPE_EXT, sequelizeTypescript } from '../../initializers' | 25 | import { CONFIG, MIMETYPES, sequelizeTypescript } from '../../initializers' |
26 | import { logger } from '../../helpers/logger' | 26 | import { logger } from '../../helpers/logger' |
27 | import { VideoModel } from '../../models/video/video' | 27 | import { VideoModel } from '../../models/video/video' |
28 | import { updateAvatarValidator } from '../../middlewares/validators/avatar' | 28 | import { updateAvatarValidator } from '../../middlewares/validators/avatar' |
@@ -32,7 +32,7 @@ import { resetSequelizeInstance } from '../../helpers/database-utils' | |||
32 | import { UserModel } from '../../models/account/user' | 32 | import { UserModel } from '../../models/account/user' |
33 | 33 | ||
34 | const auditLogger = auditLoggerFactory('channels') | 34 | const auditLogger = auditLoggerFactory('channels') |
35 | const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR }) | 35 | const reqAvatarFile = createReqFiles([ 'avatarfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR }) |
36 | 36 | ||
37 | const videoChannelRouter = express.Router() | 37 | const videoChannelRouter = express.Router() |
38 | 38 | ||
diff --git a/server/controllers/api/videos/captions.ts b/server/controllers/api/videos/captions.ts index 3ba918189..9b3661368 100644 --- a/server/controllers/api/videos/captions.ts +++ b/server/controllers/api/videos/captions.ts | |||
@@ -2,7 +2,7 @@ import * as express from 'express' | |||
2 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate } from '../../../middlewares' | 2 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate } from '../../../middlewares' |
3 | import { addVideoCaptionValidator, deleteVideoCaptionValidator, listVideoCaptionsValidator } from '../../../middlewares/validators' | 3 | import { addVideoCaptionValidator, deleteVideoCaptionValidator, listVideoCaptionsValidator } from '../../../middlewares/validators' |
4 | import { createReqFiles } from '../../../helpers/express-utils' | 4 | import { createReqFiles } from '../../../helpers/express-utils' |
5 | import { CONFIG, sequelizeTypescript, VIDEO_CAPTIONS_MIMETYPE_EXT } from '../../../initializers' | 5 | import { CONFIG, MIMETYPES, sequelizeTypescript } from '../../../initializers' |
6 | import { getFormattedObjects } from '../../../helpers/utils' | 6 | import { getFormattedObjects } from '../../../helpers/utils' |
7 | import { VideoCaptionModel } from '../../../models/video/video-caption' | 7 | import { VideoCaptionModel } from '../../../models/video/video-caption' |
8 | import { VideoModel } from '../../../models/video/video' | 8 | import { VideoModel } from '../../../models/video/video' |
@@ -12,7 +12,7 @@ import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils' | |||
12 | 12 | ||
13 | const reqVideoCaptionAdd = createReqFiles( | 13 | const reqVideoCaptionAdd = createReqFiles( |
14 | [ 'captionfile' ], | 14 | [ 'captionfile' ], |
15 | VIDEO_CAPTIONS_MIMETYPE_EXT, | 15 | MIMETYPES.VIDEO_CAPTIONS.MIMETYPE_EXT, |
16 | { | 16 | { |
17 | captionfile: CONFIG.STORAGE.CAPTIONS_DIR | 17 | captionfile: CONFIG.STORAGE.CAPTIONS_DIR |
18 | } | 18 | } |
diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts index f27d648c7..099ab7b8d 100644 --- a/server/controllers/api/videos/import.ts +++ b/server/controllers/api/videos/import.ts | |||
@@ -3,14 +3,7 @@ import * as magnetUtil from 'magnet-uri' | |||
3 | import 'multer' | 3 | import 'multer' |
4 | import { auditLoggerFactory, getAuditIdFromRes, VideoImportAuditView } from '../../../helpers/audit-logger' | 4 | import { auditLoggerFactory, getAuditIdFromRes, VideoImportAuditView } from '../../../helpers/audit-logger' |
5 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoImportAddValidator } from '../../../middlewares' | 5 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoImportAddValidator } from '../../../middlewares' |
6 | import { | 6 | import { CONFIG, MIMETYPES, PREVIEWS_SIZE, sequelizeTypescript, THUMBNAILS_SIZE } from '../../../initializers' |
7 | CONFIG, | ||
8 | IMAGE_MIMETYPE_EXT, | ||
9 | PREVIEWS_SIZE, | ||
10 | sequelizeTypescript, | ||
11 | THUMBNAILS_SIZE, | ||
12 | TORRENT_MIMETYPE_EXT | ||
13 | } from '../../../initializers' | ||
14 | import { getYoutubeDLInfo, YoutubeDLInfo } from '../../../helpers/youtube-dl' | 7 | import { getYoutubeDLInfo, YoutubeDLInfo } from '../../../helpers/youtube-dl' |
15 | import { createReqFiles } from '../../../helpers/express-utils' | 8 | import { createReqFiles } from '../../../helpers/express-utils' |
16 | import { logger } from '../../../helpers/logger' | 9 | import { logger } from '../../../helpers/logger' |
@@ -35,7 +28,7 @@ const videoImportsRouter = express.Router() | |||
35 | 28 | ||
36 | const reqVideoFileImport = createReqFiles( | 29 | const reqVideoFileImport = createReqFiles( |
37 | [ 'thumbnailfile', 'previewfile', 'torrentfile' ], | 30 | [ 'thumbnailfile', 'previewfile', 'torrentfile' ], |
38 | Object.assign({}, TORRENT_MIMETYPE_EXT, IMAGE_MIMETYPE_EXT), | 31 | Object.assign({}, MIMETYPES.TORRENT.MIMETYPE_EXT, MIMETYPES.IMAGE.MIMETYPE_EXT), |
39 | { | 32 | { |
40 | thumbnailfile: CONFIG.STORAGE.TMP_DIR, | 33 | thumbnailfile: CONFIG.STORAGE.TMP_DIR, |
41 | previewfile: CONFIG.STORAGE.TMP_DIR, | 34 | previewfile: CONFIG.STORAGE.TMP_DIR, |
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 4e4697ef4..00a1302d1 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts | |||
@@ -7,15 +7,13 @@ import { logger } from '../../../helpers/logger' | |||
7 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' | 7 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' |
8 | import { getFormattedObjects, getServerActor } from '../../../helpers/utils' | 8 | import { getFormattedObjects, getServerActor } from '../../../helpers/utils' |
9 | import { | 9 | import { |
10 | CONFIG, | 10 | CONFIG, MIMETYPES, |
11 | IMAGE_MIMETYPE_EXT, | ||
12 | PREVIEWS_SIZE, | 11 | PREVIEWS_SIZE, |
13 | sequelizeTypescript, | 12 | sequelizeTypescript, |
14 | THUMBNAILS_SIZE, | 13 | THUMBNAILS_SIZE, |
15 | VIDEO_CATEGORIES, | 14 | VIDEO_CATEGORIES, |
16 | VIDEO_LANGUAGES, | 15 | VIDEO_LANGUAGES, |
17 | VIDEO_LICENCES, | 16 | VIDEO_LICENCES, |
18 | VIDEO_MIMETYPE_EXT, | ||
19 | VIDEO_PRIVACIES | 17 | VIDEO_PRIVACIES |
20 | } from '../../../initializers' | 18 | } from '../../../initializers' |
21 | import { | 19 | import { |
@@ -57,7 +55,7 @@ import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-u | |||
57 | import { videoCaptionsRouter } from './captions' | 55 | import { videoCaptionsRouter } from './captions' |
58 | import { videoImportsRouter } from './import' | 56 | import { videoImportsRouter } from './import' |
59 | import { resetSequelizeInstance } from '../../../helpers/database-utils' | 57 | import { resetSequelizeInstance } from '../../../helpers/database-utils' |
60 | import { rename } from 'fs-extra' | 58 | import { move } from 'fs-extra' |
61 | import { watchingRouter } from './watching' | 59 | import { watchingRouter } from './watching' |
62 | 60 | ||
63 | const auditLogger = auditLoggerFactory('videos') | 61 | const auditLogger = auditLoggerFactory('videos') |
@@ -65,7 +63,7 @@ const videosRouter = express.Router() | |||
65 | 63 | ||
66 | const reqVideoFileAdd = createReqFiles( | 64 | const reqVideoFileAdd = createReqFiles( |
67 | [ 'videofile', 'thumbnailfile', 'previewfile' ], | 65 | [ 'videofile', 'thumbnailfile', 'previewfile' ], |
68 | Object.assign({}, VIDEO_MIMETYPE_EXT, IMAGE_MIMETYPE_EXT), | 66 | Object.assign({}, MIMETYPES.VIDEO.MIMETYPE_EXT, MIMETYPES.IMAGE.MIMETYPE_EXT), |
69 | { | 67 | { |
70 | videofile: CONFIG.STORAGE.TMP_DIR, | 68 | videofile: CONFIG.STORAGE.TMP_DIR, |
71 | thumbnailfile: CONFIG.STORAGE.TMP_DIR, | 69 | thumbnailfile: CONFIG.STORAGE.TMP_DIR, |
@@ -74,7 +72,7 @@ const reqVideoFileAdd = createReqFiles( | |||
74 | ) | 72 | ) |
75 | const reqVideoFileUpdate = createReqFiles( | 73 | const reqVideoFileUpdate = createReqFiles( |
76 | [ 'thumbnailfile', 'previewfile' ], | 74 | [ 'thumbnailfile', 'previewfile' ], |
77 | IMAGE_MIMETYPE_EXT, | 75 | MIMETYPES.IMAGE.MIMETYPE_EXT, |
78 | { | 76 | { |
79 | thumbnailfile: CONFIG.STORAGE.TMP_DIR, | 77 | thumbnailfile: CONFIG.STORAGE.TMP_DIR, |
80 | previewfile: CONFIG.STORAGE.TMP_DIR | 78 | previewfile: CONFIG.STORAGE.TMP_DIR |
@@ -208,7 +206,7 @@ async function addVideo (req: express.Request, res: express.Response) { | |||
208 | // Move physical file | 206 | // Move physical file |
209 | const videoDir = CONFIG.STORAGE.VIDEOS_DIR | 207 | const videoDir = CONFIG.STORAGE.VIDEOS_DIR |
210 | const destination = join(videoDir, video.getVideoFilename(videoFile)) | 208 | const destination = join(videoDir, video.getVideoFilename(videoFile)) |
211 | await rename(videoPhysicalFile.path, destination) | 209 | await move(videoPhysicalFile.path, destination) |
212 | // This is important in case if there is another attempt in the retry process | 210 | // This is important in case if there is another attempt in the retry process |
213 | videoPhysicalFile.filename = video.getVideoFilename(videoFile) | 211 | videoPhysicalFile.filename = video.getVideoFilename(videoFile) |
214 | videoPhysicalFile.path = destination | 212 | videoPhysicalFile.path = destination |
diff --git a/server/helpers/custom-validators/video-captions.ts b/server/helpers/custom-validators/video-captions.ts index 177e9e86e..b33d90e18 100644 --- a/server/helpers/custom-validators/video-captions.ts +++ b/server/helpers/custom-validators/video-captions.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { CONSTRAINTS_FIELDS, VIDEO_CAPTIONS_MIMETYPE_EXT, VIDEO_LANGUAGES } from '../../initializers' | 1 | import { CONSTRAINTS_FIELDS, MIMETYPES, VIDEO_LANGUAGES } from '../../initializers' |
2 | import { exists, isFileValid } from './misc' | 2 | import { exists, isFileValid } from './misc' |
3 | import { Response } from 'express' | 3 | import { Response } from 'express' |
4 | import { VideoModel } from '../../models/video/video' | 4 | import { VideoModel } from '../../models/video/video' |
@@ -8,7 +8,7 @@ function isVideoCaptionLanguageValid (value: any) { | |||
8 | return exists(value) && VIDEO_LANGUAGES[ value ] !== undefined | 8 | return exists(value) && VIDEO_LANGUAGES[ value ] !== undefined |
9 | } | 9 | } |
10 | 10 | ||
11 | const videoCaptionTypes = Object.keys(VIDEO_CAPTIONS_MIMETYPE_EXT) | 11 | const videoCaptionTypes = Object.keys(MIMETYPES.VIDEO_CAPTIONS.MIMETYPE_EXT) |
12 | .concat([ 'application/octet-stream' ]) // MacOS sends application/octet-stream >< | 12 | .concat([ 'application/octet-stream' ]) // MacOS sends application/octet-stream >< |
13 | .map(m => `(${m})`) | 13 | .map(m => `(${m})`) |
14 | const videoCaptionTypesRegex = videoCaptionTypes.join('|') | 14 | const videoCaptionTypesRegex = videoCaptionTypes.join('|') |
diff --git a/server/helpers/custom-validators/video-imports.ts b/server/helpers/custom-validators/video-imports.ts index 4d6ab1fa4..ce9e9193c 100644 --- a/server/helpers/custom-validators/video-imports.ts +++ b/server/helpers/custom-validators/video-imports.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import 'express-validator' | 1 | import 'express-validator' |
2 | import 'multer' | 2 | import 'multer' |
3 | import * as validator from 'validator' | 3 | import * as validator from 'validator' |
4 | import { CONSTRAINTS_FIELDS, TORRENT_MIMETYPE_EXT, VIDEO_IMPORT_STATES } from '../../initializers' | 4 | import { CONSTRAINTS_FIELDS, MIMETYPES, VIDEO_IMPORT_STATES } from '../../initializers' |
5 | import { exists, isFileValid } from './misc' | 5 | import { exists, isFileValid } from './misc' |
6 | import * as express from 'express' | 6 | import * as express from 'express' |
7 | import { VideoImportModel } from '../../models/video/video-import' | 7 | import { VideoImportModel } from '../../models/video/video-import' |
@@ -24,7 +24,7 @@ function isVideoImportStateValid (value: any) { | |||
24 | return exists(value) && VIDEO_IMPORT_STATES[ value ] !== undefined | 24 | return exists(value) && VIDEO_IMPORT_STATES[ value ] !== undefined |
25 | } | 25 | } |
26 | 26 | ||
27 | const videoTorrentImportTypes = Object.keys(TORRENT_MIMETYPE_EXT).map(m => `(${m})`) | 27 | const videoTorrentImportTypes = Object.keys(MIMETYPES.TORRENT.MIMETYPE_EXT).map(m => `(${m})`) |
28 | const videoTorrentImportRegex = videoTorrentImportTypes.join('|') | 28 | const videoTorrentImportRegex = videoTorrentImportTypes.join('|') |
29 | function isVideoImportTorrentFile (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[]) { | 29 | function isVideoImportTorrentFile (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[]) { |
30 | return isFileValid(files, videoTorrentImportRegex, 'torrentfile', CONSTRAINTS_FIELDS.VIDEO_IMPORTS.TORRENT_FILE.FILE_SIZE.max, true) | 30 | return isFileValid(files, videoTorrentImportRegex, 'torrentfile', CONSTRAINTS_FIELDS.VIDEO_IMPORTS.TORRENT_FILE.FILE_SIZE.max, true) |
diff --git a/server/helpers/custom-validators/videos.ts b/server/helpers/custom-validators/videos.ts index a13b09ac8..e6f22e6c5 100644 --- a/server/helpers/custom-validators/videos.ts +++ b/server/helpers/custom-validators/videos.ts | |||
@@ -5,10 +5,9 @@ import 'multer' | |||
5 | import * as validator from 'validator' | 5 | import * as validator from 'validator' |
6 | import { UserRight, VideoFilter, VideoPrivacy, VideoRateType } from '../../../shared' | 6 | import { UserRight, VideoFilter, VideoPrivacy, VideoRateType } from '../../../shared' |
7 | import { | 7 | import { |
8 | CONSTRAINTS_FIELDS, | 8 | CONSTRAINTS_FIELDS, MIMETYPES, |
9 | VIDEO_CATEGORIES, | 9 | VIDEO_CATEGORIES, |
10 | VIDEO_LICENCES, | 10 | VIDEO_LICENCES, |
11 | VIDEO_MIMETYPE_EXT, | ||
12 | VIDEO_PRIVACIES, | 11 | VIDEO_PRIVACIES, |
13 | VIDEO_RATE_TYPES, | 12 | VIDEO_RATE_TYPES, |
14 | VIDEO_STATES | 13 | VIDEO_STATES |
@@ -83,10 +82,15 @@ function isVideoRatingTypeValid (value: string) { | |||
83 | return value === 'none' || values(VIDEO_RATE_TYPES).indexOf(value as VideoRateType) !== -1 | 82 | return value === 'none' || values(VIDEO_RATE_TYPES).indexOf(value as VideoRateType) !== -1 |
84 | } | 83 | } |
85 | 84 | ||
86 | const videoFileTypes = Object.keys(VIDEO_MIMETYPE_EXT).map(m => `(${m})`) | 85 | function isVideoFileExtnameValid (value: string) { |
87 | const videoFileTypesRegex = videoFileTypes.join('|') | 86 | return exists(value) && MIMETYPES.VIDEO.EXT_MIMETYPE[value] !== undefined |
87 | } | ||
88 | 88 | ||
89 | function isVideoFile (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[]) { | 89 | function isVideoFile (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[]) { |
90 | const videoFileTypesRegex = Object.keys(MIMETYPES.VIDEO.MIMETYPE_EXT) | ||
91 | .map(m => `(${m})`) | ||
92 | .join('|') | ||
93 | |||
90 | return isFileValid(files, videoFileTypesRegex, 'videofile', null) | 94 | return isFileValid(files, videoFileTypesRegex, 'videofile', null) |
91 | } | 95 | } |
92 | 96 | ||
@@ -221,6 +225,7 @@ export { | |||
221 | isVideoStateValid, | 225 | isVideoStateValid, |
222 | isVideoViewsValid, | 226 | isVideoViewsValid, |
223 | isVideoRatingTypeValid, | 227 | isVideoRatingTypeValid, |
228 | isVideoFileExtnameValid, | ||
224 | isVideoDurationValid, | 229 | isVideoDurationValid, |
225 | isVideoTagValid, | 230 | isVideoTagValid, |
226 | isVideoPrivacyValid, | 231 | isVideoPrivacyValid, |
diff --git a/server/initializers/checker-before-init.ts b/server/initializers/checker-before-init.ts index b51c7cfba..a7bc7eec8 100644 --- a/server/initializers/checker-before-init.ts +++ b/server/initializers/checker-before-init.ts | |||
@@ -19,7 +19,7 @@ function checkMissedConfig () { | |||
19 | 'signup.enabled', 'signup.limit', 'signup.requires_email_verification', | 19 | 'signup.enabled', 'signup.limit', 'signup.requires_email_verification', |
20 | 'signup.filters.cidr.whitelist', 'signup.filters.cidr.blacklist', | 20 | 'signup.filters.cidr.whitelist', 'signup.filters.cidr.blacklist', |
21 | 'redundancy.videos.strategies', 'redundancy.videos.check_interval', | 21 | 'redundancy.videos.strategies', 'redundancy.videos.check_interval', |
22 | 'transcoding.enabled', 'transcoding.threads', | 22 | 'transcoding.enabled', 'transcoding.threads', 'transcoding.allow_additional_extensions', |
23 | 'import.videos.http.enabled', 'import.videos.torrent.enabled', | 23 | 'import.videos.http.enabled', 'import.videos.torrent.enabled', |
24 | 'trending.videos.interval_days', | 24 | 'trending.videos.interval_days', |
25 | 'instance.name', 'instance.short_description', 'instance.description', 'instance.terms', 'instance.default_client_route', | 25 | 'instance.name', 'instance.short_description', 'instance.description', 'instance.terms', 'instance.default_client_route', |
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index d4496bc34..ad61bee73 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -16,7 +16,7 @@ let config: IConfig = require('config') | |||
16 | 16 | ||
17 | // --------------------------------------------------------------------------- | 17 | // --------------------------------------------------------------------------- |
18 | 18 | ||
19 | const LAST_MIGRATION_VERSION = 290 | 19 | const LAST_MIGRATION_VERSION = 295 |
20 | 20 | ||
21 | // --------------------------------------------------------------------------- | 21 | // --------------------------------------------------------------------------- |
22 | 22 | ||
@@ -246,6 +246,7 @@ const CONFIG = { | |||
246 | }, | 246 | }, |
247 | TRANSCODING: { | 247 | TRANSCODING: { |
248 | get ENABLED () { return config.get<boolean>('transcoding.enabled') }, | 248 | get ENABLED () { return config.get<boolean>('transcoding.enabled') }, |
249 | get ALLOW_ADDITIONAL_EXTENSIONS () { return config.get<boolean>('transcoding.allow_additional_extensions') }, | ||
249 | get THREADS () { return config.get<number>('transcoding.threads') }, | 250 | get THREADS () { return config.get<number>('transcoding.threads') }, |
250 | RESOLUTIONS: { | 251 | RESOLUTIONS: { |
251 | get '240p' () { return config.get<boolean>('transcoding.resolutions.240p') }, | 252 | get '240p' () { return config.get<boolean>('transcoding.resolutions.240p') }, |
@@ -298,7 +299,7 @@ const CONFIG = { | |||
298 | 299 | ||
299 | // --------------------------------------------------------------------------- | 300 | // --------------------------------------------------------------------------- |
300 | 301 | ||
301 | const CONSTRAINTS_FIELDS = { | 302 | let CONSTRAINTS_FIELDS = { |
302 | USERS: { | 303 | USERS: { |
303 | NAME: { min: 1, max: 50 }, // Length | 304 | NAME: { min: 1, max: 50 }, // Length |
304 | DESCRIPTION: { min: 3, max: 1000 }, // Length | 305 | DESCRIPTION: { min: 3, max: 1000 }, // Length |
@@ -357,7 +358,7 @@ const CONSTRAINTS_FIELDS = { | |||
357 | max: 2 * 1024 * 1024 // 2MB | 358 | max: 2 * 1024 * 1024 // 2MB |
358 | } | 359 | } |
359 | }, | 360 | }, |
360 | EXTNAME: [ '.mp4', '.ogv', '.webm' ], | 361 | EXTNAME: buildVideosExtname(), |
361 | INFO_HASH: { min: 40, max: 40 }, // Length, info hash is 20 bytes length but we represent it in hexadecimal so 20 * 2 | 362 | INFO_HASH: { min: 40, max: 40 }, // Length, info hash is 20 bytes length but we represent it in hexadecimal so 20 * 2 |
362 | DURATION: { min: 0 }, // Number | 363 | DURATION: { min: 0 }, // Number |
363 | TAGS: { min: 0, max: 5 }, // Number of total tags | 364 | TAGS: { min: 0, max: 5 }, // Number of total tags |
@@ -480,27 +481,31 @@ const VIDEO_ABUSE_STATES = { | |||
480 | [VideoAbuseState.ACCEPTED]: 'Accepted' | 481 | [VideoAbuseState.ACCEPTED]: 'Accepted' |
481 | } | 482 | } |
482 | 483 | ||
483 | const VIDEO_MIMETYPE_EXT = { | 484 | const MIMETYPES = { |
484 | 'video/webm': '.webm', | 485 | VIDEO: { |
485 | 'video/ogg': '.ogv', | 486 | MIMETYPE_EXT: buildVideoMimetypeExt(), |
486 | 'video/mp4': '.mp4' | 487 | EXT_MIMETYPE: null as { [ id: string ]: string } |
487 | } | 488 | }, |
488 | const VIDEO_EXT_MIMETYPE = invert(VIDEO_MIMETYPE_EXT) | 489 | IMAGE: { |
489 | 490 | MIMETYPE_EXT: { | |
490 | const IMAGE_MIMETYPE_EXT = { | 491 | 'image/png': '.png', |
491 | 'image/png': '.png', | 492 | 'image/jpg': '.jpg', |
492 | 'image/jpg': '.jpg', | 493 | 'image/jpeg': '.jpg' |
493 | 'image/jpeg': '.jpg' | 494 | } |
494 | } | 495 | }, |
495 | 496 | VIDEO_CAPTIONS: { | |
496 | const VIDEO_CAPTIONS_MIMETYPE_EXT = { | 497 | MIMETYPE_EXT: { |
497 | 'text/vtt': '.vtt', | 498 | 'text/vtt': '.vtt', |
498 | 'application/x-subrip': '.srt' | 499 | 'application/x-subrip': '.srt' |
499 | } | 500 | } |
500 | 501 | }, | |
501 | const TORRENT_MIMETYPE_EXT = { | 502 | TORRENT: { |
502 | 'application/x-bittorrent': '.torrent' | 503 | MIMETYPE_EXT: { |
504 | 'application/x-bittorrent': '.torrent' | ||
505 | } | ||
506 | } | ||
503 | } | 507 | } |
508 | MIMETYPES.VIDEO.EXT_MIMETYPE = invert(MIMETYPES.VIDEO.MIMETYPE_EXT) | ||
504 | 509 | ||
505 | // --------------------------------------------------------------------------- | 510 | // --------------------------------------------------------------------------- |
506 | 511 | ||
@@ -526,7 +531,7 @@ const ACTIVITY_PUB = { | |||
526 | COLLECTION_ITEMS_PER_PAGE: 10, | 531 | COLLECTION_ITEMS_PER_PAGE: 10, |
527 | FETCH_PAGE_LIMIT: 100, | 532 | FETCH_PAGE_LIMIT: 100, |
528 | URL_MIME_TYPES: { | 533 | URL_MIME_TYPES: { |
529 | VIDEO: Object.keys(VIDEO_MIMETYPE_EXT), | 534 | VIDEO: Object.keys(MIMETYPES.VIDEO.MIMETYPE_EXT), |
530 | TORRENT: [ 'application/x-bittorrent' ], | 535 | TORRENT: [ 'application/x-bittorrent' ], |
531 | MAGNET: [ 'application/x-bittorrent;x-scheme-handler/magnet' ] | 536 | MAGNET: [ 'application/x-bittorrent;x-scheme-handler/magnet' ] |
532 | }, | 537 | }, |
@@ -685,13 +690,12 @@ if (isTestInstance() === true) { | |||
685 | ROUTE_CACHE_LIFETIME.OVERVIEWS.VIDEOS = '0ms' | 690 | ROUTE_CACHE_LIFETIME.OVERVIEWS.VIDEOS = '0ms' |
686 | } | 691 | } |
687 | 692 | ||
688 | updateWebserverConfig() | 693 | updateWebserverUrls() |
689 | 694 | ||
690 | // --------------------------------------------------------------------------- | 695 | // --------------------------------------------------------------------------- |
691 | 696 | ||
692 | export { | 697 | export { |
693 | API_VERSION, | 698 | API_VERSION, |
694 | VIDEO_CAPTIONS_MIMETYPE_EXT, | ||
695 | AVATARS_SIZE, | 699 | AVATARS_SIZE, |
696 | ACCEPT_HEADERS, | 700 | ACCEPT_HEADERS, |
697 | BCRYPT_SALT_SIZE, | 701 | BCRYPT_SALT_SIZE, |
@@ -719,7 +723,6 @@ export { | |||
719 | FEEDS, | 723 | FEEDS, |
720 | JOB_TTL, | 724 | JOB_TTL, |
721 | NSFW_POLICY_TYPES, | 725 | NSFW_POLICY_TYPES, |
722 | TORRENT_MIMETYPE_EXT, | ||
723 | STATIC_MAX_AGE, | 726 | STATIC_MAX_AGE, |
724 | STATIC_PATHS, | 727 | STATIC_PATHS, |
725 | VIDEO_IMPORT_TIMEOUT, | 728 | VIDEO_IMPORT_TIMEOUT, |
@@ -732,7 +735,6 @@ export { | |||
732 | VIDEO_LICENCES, | 735 | VIDEO_LICENCES, |
733 | VIDEO_STATES, | 736 | VIDEO_STATES, |
734 | VIDEO_RATE_TYPES, | 737 | VIDEO_RATE_TYPES, |
735 | VIDEO_MIMETYPE_EXT, | ||
736 | VIDEO_TRANSCODING_FPS, | 738 | VIDEO_TRANSCODING_FPS, |
737 | FFMPEG_NICE, | 739 | FFMPEG_NICE, |
738 | VIDEO_ABUSE_STATES, | 740 | VIDEO_ABUSE_STATES, |
@@ -740,13 +742,12 @@ export { | |||
740 | USER_PASSWORD_RESET_LIFETIME, | 742 | USER_PASSWORD_RESET_LIFETIME, |
741 | MEMOIZE_TTL, | 743 | MEMOIZE_TTL, |
742 | USER_EMAIL_VERIFY_LIFETIME, | 744 | USER_EMAIL_VERIFY_LIFETIME, |
743 | IMAGE_MIMETYPE_EXT, | ||
744 | OVERVIEWS, | 745 | OVERVIEWS, |
745 | SCHEDULER_INTERVALS_MS, | 746 | SCHEDULER_INTERVALS_MS, |
746 | REPEAT_JOBS, | 747 | REPEAT_JOBS, |
747 | STATIC_DOWNLOAD_PATHS, | 748 | STATIC_DOWNLOAD_PATHS, |
748 | RATES_LIMIT, | 749 | RATES_LIMIT, |
749 | VIDEO_EXT_MIMETYPE, | 750 | MIMETYPES, |
750 | CRAWL_REQUEST_CONCURRENCY, | 751 | CRAWL_REQUEST_CONCURRENCY, |
751 | JOB_COMPLETED_LIFETIME, | 752 | JOB_COMPLETED_LIFETIME, |
752 | HTTP_SIGNATURE, | 753 | HTTP_SIGNATURE, |
@@ -768,11 +769,43 @@ function getLocalConfigFilePath () { | |||
768 | return join(dirname(configSources[ 0 ].name), filename + '.json') | 769 | return join(dirname(configSources[ 0 ].name), filename + '.json') |
769 | } | 770 | } |
770 | 771 | ||
771 | function updateWebserverConfig () { | 772 | function buildVideoMimetypeExt () { |
773 | const data = { | ||
774 | 'video/webm': '.webm', | ||
775 | 'video/ogg': '.ogv', | ||
776 | 'video/mp4': '.mp4' | ||
777 | } | ||
778 | |||
779 | if (CONFIG.TRANSCODING.ENABLED && CONFIG.TRANSCODING.ALLOW_ADDITIONAL_EXTENSIONS) { | ||
780 | Object.assign(data, { | ||
781 | 'video/quicktime': '.mov', | ||
782 | 'video/x-msvideo': '.avi', | ||
783 | 'video/x-flv': '.flv', | ||
784 | 'video/x-matroska': '.mkv' | ||
785 | }) | ||
786 | } | ||
787 | |||
788 | return data | ||
789 | } | ||
790 | |||
791 | function updateWebserverUrls () { | ||
772 | CONFIG.WEBSERVER.URL = sanitizeUrl(CONFIG.WEBSERVER.SCHEME + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT) | 792 | CONFIG.WEBSERVER.URL = sanitizeUrl(CONFIG.WEBSERVER.SCHEME + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT) |
773 | CONFIG.WEBSERVER.HOST = sanitizeHost(CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT, REMOTE_SCHEME.HTTP) | 793 | CONFIG.WEBSERVER.HOST = sanitizeHost(CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT, REMOTE_SCHEME.HTTP) |
774 | } | 794 | } |
775 | 795 | ||
796 | function updateWebserverConfig () { | ||
797 | CONSTRAINTS_FIELDS.VIDEOS.EXTNAME = buildVideosExtname() | ||
798 | |||
799 | MIMETYPES.VIDEO.MIMETYPE_EXT = buildVideoMimetypeExt() | ||
800 | MIMETYPES.VIDEO.EXT_MIMETYPE = invert(MIMETYPES.VIDEO.MIMETYPE_EXT) | ||
801 | } | ||
802 | |||
803 | function buildVideosExtname () { | ||
804 | return CONFIG.TRANSCODING.ENABLED && CONFIG.TRANSCODING.ALLOW_ADDITIONAL_EXTENSIONS | ||
805 | ? [ '.mp4', '.ogv', '.webm', '.mkv', '.mov', '.avi', '.flv' ] | ||
806 | : [ '.mp4', '.ogv', '.webm' ] | ||
807 | } | ||
808 | |||
776 | function buildVideosRedundancy (objs: any[]): VideosRedundancy[] { | 809 | function buildVideosRedundancy (objs: any[]): VideosRedundancy[] { |
777 | if (!objs) return [] | 810 | if (!objs) return [] |
778 | 811 | ||
@@ -854,4 +887,5 @@ export function reloadConfig () { | |||
854 | config = require('config') | 887 | config = require('config') |
855 | 888 | ||
856 | updateWebserverConfig() | 889 | updateWebserverConfig() |
890 | updateWebserverUrls() | ||
857 | } | 891 | } |
diff --git a/server/initializers/migrations/0295-video-file-extname.ts b/server/initializers/migrations/0295-video-file-extname.ts new file mode 100644 index 000000000..dbf249f66 --- /dev/null +++ b/server/initializers/migrations/0295-video-file-extname.ts | |||
@@ -0,0 +1,49 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | async function up (utils: { | ||
4 | transaction: Sequelize.Transaction, | ||
5 | queryInterface: Sequelize.QueryInterface, | ||
6 | sequelize: Sequelize.Sequelize, | ||
7 | db: any | ||
8 | }): Promise<void> { | ||
9 | { | ||
10 | await utils.queryInterface.renameColumn('videoFile', 'extname', 'extname_old') | ||
11 | } | ||
12 | |||
13 | { | ||
14 | const data = { | ||
15 | type: Sequelize.STRING, | ||
16 | defaultValue: null, | ||
17 | allowNull: true | ||
18 | } | ||
19 | |||
20 | await utils.queryInterface.addColumn('videoFile', 'extname', data) | ||
21 | } | ||
22 | |||
23 | { | ||
24 | const query = 'UPDATE "videoFile" SET "extname" = "extname_old"::text' | ||
25 | await utils.sequelize.query(query) | ||
26 | } | ||
27 | |||
28 | { | ||
29 | const data = { | ||
30 | type: Sequelize.STRING, | ||
31 | defaultValue: null, | ||
32 | allowNull: false | ||
33 | } | ||
34 | await utils.queryInterface.changeColumn('videoFile', 'extname', data) | ||
35 | } | ||
36 | |||
37 | { | ||
38 | await utils.queryInterface.removeColumn('videoFile', 'extname_old') | ||
39 | } | ||
40 | } | ||
41 | |||
42 | function down (options) { | ||
43 | throw new Error('Not implemented.') | ||
44 | } | ||
45 | |||
46 | export { | ||
47 | up, | ||
48 | down | ||
49 | } | ||
diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts index bbe48833d..f7bf7c65a 100644 --- a/server/lib/activitypub/actor.ts +++ b/server/lib/activitypub/actor.ts | |||
@@ -1,5 +1,4 @@ | |||
1 | import * as Bluebird from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import { join } from 'path' | ||
3 | import { Transaction } from 'sequelize' | 2 | import { Transaction } from 'sequelize' |
4 | import * as url from 'url' | 3 | import * as url from 'url' |
5 | import * as uuidv4 from 'uuid/v4' | 4 | import * as uuidv4 from 'uuid/v4' |
@@ -13,7 +12,7 @@ import { logger } from '../../helpers/logger' | |||
13 | import { createPrivateAndPublicKeys } from '../../helpers/peertube-crypto' | 12 | import { createPrivateAndPublicKeys } from '../../helpers/peertube-crypto' |
14 | import { doRequest, downloadImage } from '../../helpers/requests' | 13 | import { doRequest, downloadImage } from '../../helpers/requests' |
15 | import { getUrlFromWebfinger } from '../../helpers/webfinger' | 14 | import { getUrlFromWebfinger } from '../../helpers/webfinger' |
16 | import { AVATARS_SIZE, CONFIG, IMAGE_MIMETYPE_EXT, sequelizeTypescript } from '../../initializers' | 15 | import { AVATARS_SIZE, CONFIG, MIMETYPES, sequelizeTypescript } from '../../initializers' |
17 | import { AccountModel } from '../../models/account/account' | 16 | import { AccountModel } from '../../models/account/account' |
18 | import { ActorModel } from '../../models/activitypub/actor' | 17 | import { ActorModel } from '../../models/activitypub/actor' |
19 | import { AvatarModel } from '../../models/avatar/avatar' | 18 | import { AvatarModel } from '../../models/avatar/avatar' |
@@ -172,10 +171,10 @@ async function fetchActorTotalItems (url: string) { | |||
172 | 171 | ||
173 | async function fetchAvatarIfExists (actorJSON: ActivityPubActor) { | 172 | async function fetchAvatarIfExists (actorJSON: ActivityPubActor) { |
174 | if ( | 173 | if ( |
175 | actorJSON.icon && actorJSON.icon.type === 'Image' && IMAGE_MIMETYPE_EXT[actorJSON.icon.mediaType] !== undefined && | 174 | actorJSON.icon && actorJSON.icon.type === 'Image' && MIMETYPES.IMAGE.MIMETYPE_EXT[actorJSON.icon.mediaType] !== undefined && |
176 | isActivityPubUrlValid(actorJSON.icon.url) | 175 | isActivityPubUrlValid(actorJSON.icon.url) |
177 | ) { | 176 | ) { |
178 | const extension = IMAGE_MIMETYPE_EXT[actorJSON.icon.mediaType] | 177 | const extension = MIMETYPES.IMAGE.MIMETYPE_EXT[actorJSON.icon.mediaType] |
179 | 178 | ||
180 | const avatarName = uuidv4() + extension | 179 | const avatarName = uuidv4() + extension |
181 | await downloadImage(actorJSON.icon.url, CONFIG.STORAGE.AVATARS_DIR, avatarName, AVATARS_SIZE) | 180 | await downloadImage(actorJSON.icon.url, CONFIG.STORAGE.AVATARS_DIR, avatarName, AVATARS_SIZE) |
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts index 3d17e6846..379c2a0d7 100644 --- a/server/lib/activitypub/videos.ts +++ b/server/lib/activitypub/videos.ts | |||
@@ -1,7 +1,6 @@ | |||
1 | import * as Bluebird from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import * as sequelize from 'sequelize' | 2 | import * as sequelize from 'sequelize' |
3 | import * as magnetUtil from 'magnet-uri' | 3 | import * as magnetUtil from 'magnet-uri' |
4 | import { join } from 'path' | ||
5 | import * as request from 'request' | 4 | import * as request from 'request' |
6 | import { ActivityIconObject, ActivityUrlObject, ActivityVideoUrlObject, VideoState } from '../../../shared/index' | 5 | import { ActivityIconObject, ActivityUrlObject, ActivityVideoUrlObject, VideoState } from '../../../shared/index' |
7 | import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' | 6 | import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' |
@@ -11,7 +10,7 @@ import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos | |||
11 | import { resetSequelizeInstance, retryTransactionWrapper } from '../../helpers/database-utils' | 10 | import { resetSequelizeInstance, retryTransactionWrapper } from '../../helpers/database-utils' |
12 | import { logger } from '../../helpers/logger' | 11 | import { logger } from '../../helpers/logger' |
13 | import { doRequest, downloadImage } from '../../helpers/requests' | 12 | import { doRequest, downloadImage } from '../../helpers/requests' |
14 | import { ACTIVITY_PUB, CONFIG, REMOTE_SCHEME, sequelizeTypescript, THUMBNAILS_SIZE, VIDEO_MIMETYPE_EXT } from '../../initializers' | 13 | import { ACTIVITY_PUB, CONFIG, MIMETYPES, REMOTE_SCHEME, sequelizeTypescript, THUMBNAILS_SIZE } from '../../initializers' |
15 | import { ActorModel } from '../../models/activitypub/actor' | 14 | import { ActorModel } from '../../models/activitypub/actor' |
16 | import { TagModel } from '../../models/video/tag' | 15 | import { TagModel } from '../../models/video/tag' |
17 | import { VideoModel } from '../../models/video/video' | 16 | import { VideoModel } from '../../models/video/video' |
@@ -362,7 +361,7 @@ export { | |||
362 | // --------------------------------------------------------------------------- | 361 | // --------------------------------------------------------------------------- |
363 | 362 | ||
364 | function isActivityVideoUrlObject (url: ActivityUrlObject): url is ActivityVideoUrlObject { | 363 | function isActivityVideoUrlObject (url: ActivityUrlObject): url is ActivityVideoUrlObject { |
365 | const mimeTypes = Object.keys(VIDEO_MIMETYPE_EXT) | 364 | const mimeTypes = Object.keys(MIMETYPES.VIDEO.MIMETYPE_EXT) |
366 | 365 | ||
367 | const urlMediaType = url.mediaType || url.mimeType | 366 | const urlMediaType = url.mediaType || url.mimeType |
368 | return mimeTypes.indexOf(urlMediaType) !== -1 && urlMediaType.startsWith('video/') | 367 | return mimeTypes.indexOf(urlMediaType) !== -1 && urlMediaType.startsWith('video/') |
@@ -490,7 +489,7 @@ function videoFileActivityUrlToDBAttributes (video: VideoModel, videoObject: Vid | |||
490 | 489 | ||
491 | const mediaType = fileUrl.mediaType || fileUrl.mimeType | 490 | const mediaType = fileUrl.mediaType || fileUrl.mimeType |
492 | const attribute = { | 491 | const attribute = { |
493 | extname: VIDEO_MIMETYPE_EXT[ mediaType ], | 492 | extname: MIMETYPES.VIDEO.MIMETYPE_EXT[ mediaType ], |
494 | infoHash: parsed.infoHash, | 493 | infoHash: parsed.infoHash, |
495 | resolution: fileUrl.height, | 494 | resolution: fileUrl.height, |
496 | size: fileUrl.size, | 495 | size: fileUrl.size, |
diff --git a/server/models/redundancy/video-redundancy.ts b/server/models/redundancy/video-redundancy.ts index dd37dad22..8b6cd146a 100644 --- a/server/models/redundancy/video-redundancy.ts +++ b/server/models/redundancy/video-redundancy.ts | |||
@@ -15,7 +15,7 @@ import { | |||
15 | import { ActorModel } from '../activitypub/actor' | 15 | import { ActorModel } from '../activitypub/actor' |
16 | import { getVideoSort, throwIfNotValid } from '../utils' | 16 | import { getVideoSort, throwIfNotValid } from '../utils' |
17 | import { isActivityPubUrlValid, isUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 17 | import { isActivityPubUrlValid, isUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
18 | import { CONFIG, CONSTRAINTS_FIELDS, STATIC_PATHS, VIDEO_EXT_MIMETYPE } from '../../initializers' | 18 | import { CONFIG, CONSTRAINTS_FIELDS, MIMETYPES } from '../../initializers' |
19 | import { VideoFileModel } from '../video/video-file' | 19 | import { VideoFileModel } from '../video/video-file' |
20 | import { getServerActor } from '../../helpers/utils' | 20 | import { getServerActor } from '../../helpers/utils' |
21 | import { VideoModel } from '../video/video' | 21 | import { VideoModel } from '../video/video' |
@@ -415,8 +415,8 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
415 | expires: this.expiresOn.toISOString(), | 415 | expires: this.expiresOn.toISOString(), |
416 | url: { | 416 | url: { |
417 | type: 'Link', | 417 | type: 'Link', |
418 | mimeType: VIDEO_EXT_MIMETYPE[ this.VideoFile.extname ] as any, | 418 | mimeType: MIMETYPES.VIDEO.EXT_MIMETYPE[ this.VideoFile.extname ] as any, |
419 | mediaType: VIDEO_EXT_MIMETYPE[ this.VideoFile.extname ] as any, | 419 | mediaType: MIMETYPES.VIDEO.EXT_MIMETYPE[ this.VideoFile.extname ] as any, |
420 | href: this.fileUrl, | 420 | href: this.fileUrl, |
421 | height: this.VideoFile.resolution, | 421 | height: this.VideoFile.resolution, |
422 | size: this.VideoFile.size, | 422 | size: this.VideoFile.size, |
diff --git a/server/models/video/video-file.ts b/server/models/video/video-file.ts index adebdf0c7..3fd2d5a99 100644 --- a/server/models/video/video-file.ts +++ b/server/models/video/video-file.ts | |||
@@ -14,6 +14,7 @@ import { | |||
14 | UpdatedAt | 14 | UpdatedAt |
15 | } from 'sequelize-typescript' | 15 | } from 'sequelize-typescript' |
16 | import { | 16 | import { |
17 | isVideoFileExtnameValid, | ||
17 | isVideoFileInfoHashValid, | 18 | isVideoFileInfoHashValid, |
18 | isVideoFileResolutionValid, | 19 | isVideoFileResolutionValid, |
19 | isVideoFileSizeValid, | 20 | isVideoFileSizeValid, |
@@ -58,7 +59,8 @@ export class VideoFileModel extends Model<VideoFileModel> { | |||
58 | size: number | 59 | size: number |
59 | 60 | ||
60 | @AllowNull(false) | 61 | @AllowNull(false) |
61 | @Column(DataType.ENUM(values(CONSTRAINTS_FIELDS.VIDEOS.EXTNAME))) | 62 | @Is('VideoFileExtname', value => throwIfNotValid(value, isVideoFileExtnameValid, 'extname')) |
63 | @Column | ||
62 | extname: string | 64 | extname: string |
63 | 65 | ||
64 | @AllowNull(false) | 66 | @AllowNull(false) |
diff --git a/server/models/video/video-format-utils.ts b/server/models/video/video-format-utils.ts index e3f8d525b..de0747f22 100644 --- a/server/models/video/video-format-utils.ts +++ b/server/models/video/video-format-utils.ts | |||
@@ -2,7 +2,7 @@ import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos' | |||
2 | import { VideoModel } from './video' | 2 | import { VideoModel } from './video' |
3 | import { VideoFileModel } from './video-file' | 3 | import { VideoFileModel } from './video-file' |
4 | import { ActivityUrlObject, VideoTorrentObject } from '../../../shared/models/activitypub/objects' | 4 | import { ActivityUrlObject, VideoTorrentObject } from '../../../shared/models/activitypub/objects' |
5 | import { CONFIG, THUMBNAILS_SIZE, VIDEO_EXT_MIMETYPE } from '../../initializers' | 5 | import { CONFIG, MIMETYPES, THUMBNAILS_SIZE } from '../../initializers' |
6 | import { VideoCaptionModel } from './video-caption' | 6 | import { VideoCaptionModel } from './video-caption' |
7 | import { | 7 | import { |
8 | getVideoCommentsActivityPubUrl, | 8 | getVideoCommentsActivityPubUrl, |
@@ -207,8 +207,8 @@ function videoModelToActivityPubObject (video: VideoModel): VideoTorrentObject { | |||
207 | for (const file of video.VideoFiles) { | 207 | for (const file of video.VideoFiles) { |
208 | url.push({ | 208 | url.push({ |
209 | type: 'Link', | 209 | type: 'Link', |
210 | mimeType: VIDEO_EXT_MIMETYPE[ file.extname ] as any, | 210 | mimeType: MIMETYPES.VIDEO.EXT_MIMETYPE[ file.extname ] as any, |
211 | mediaType: VIDEO_EXT_MIMETYPE[ file.extname ] as any, | 211 | mediaType: MIMETYPES.VIDEO.EXT_MIMETYPE[ file.extname ] as any, |
212 | href: video.getVideoFileUrl(file, baseUrlHttp), | 212 | href: video.getVideoFileUrl(file, baseUrlHttp), |
213 | height: file.resolution, | 213 | height: file.resolution, |
214 | size: file.size, | 214 | size: file.size, |
diff --git a/server/tests/api/check-params/config.ts b/server/tests/api/check-params/config.ts index ffae380c1..b7bf41b58 100644 --- a/server/tests/api/check-params/config.ts +++ b/server/tests/api/check-params/config.ts | |||
@@ -54,6 +54,7 @@ describe('Test config API validators', function () { | |||
54 | }, | 54 | }, |
55 | transcoding: { | 55 | transcoding: { |
56 | enabled: true, | 56 | enabled: true, |
57 | allowAdditionalExtensions: true, | ||
57 | threads: 1, | 58 | threads: 1, |
58 | resolutions: { | 59 | resolutions: { |
59 | '240p': false, | 60 | '240p': false, |
diff --git a/server/tests/api/check-params/videos.ts b/server/tests/api/check-params/videos.ts index d94eccf8e..f26b91435 100644 --- a/server/tests/api/check-params/videos.ts +++ b/server/tests/api/check-params/videos.ts | |||
@@ -320,10 +320,15 @@ describe('Test videos API validator', function () { | |||
320 | 320 | ||
321 | it('Should fail without an incorrect input file', async function () { | 321 | it('Should fail without an incorrect input file', async function () { |
322 | const fields = baseCorrectParams | 322 | const fields = baseCorrectParams |
323 | const attaches = { | 323 | let attaches = { |
324 | 'videofile': join(__dirname, '..', '..', 'fixtures', 'video_short_fake.webm') | 324 | 'videofile': join(__dirname, '..', '..', 'fixtures', 'video_short_fake.webm') |
325 | } | 325 | } |
326 | await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) | 326 | await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) |
327 | |||
328 | attaches = { | ||
329 | 'videofile': join(__dirname, '..', '..', 'fixtures', 'video_short.mkv') | ||
330 | } | ||
331 | await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) | ||
327 | }) | 332 | }) |
328 | 333 | ||
329 | it('Should fail with an incorrect thumbnail file', async function () { | 334 | it('Should fail with an incorrect thumbnail file', async function () { |
diff --git a/server/tests/api/server/config.ts b/server/tests/api/server/config.ts index c5c360a17..4c163d47d 100644 --- a/server/tests/api/server/config.ts +++ b/server/tests/api/server/config.ts | |||
@@ -17,6 +17,7 @@ import { | |||
17 | setAccessTokensToServers, | 17 | setAccessTokensToServers, |
18 | updateCustomConfig | 18 | updateCustomConfig |
19 | } from '../../../../shared/utils' | 19 | } from '../../../../shared/utils' |
20 | import { ServerConfig } from '../../../../shared/models' | ||
20 | 21 | ||
21 | const expect = chai.expect | 22 | const expect = chai.expect |
22 | 23 | ||
@@ -43,6 +44,7 @@ function checkInitialConfig (data: CustomConfig) { | |||
43 | expect(data.user.videoQuota).to.equal(5242880) | 44 | expect(data.user.videoQuota).to.equal(5242880) |
44 | expect(data.user.videoQuotaDaily).to.equal(-1) | 45 | expect(data.user.videoQuotaDaily).to.equal(-1) |
45 | expect(data.transcoding.enabled).to.be.false | 46 | expect(data.transcoding.enabled).to.be.false |
47 | expect(data.transcoding.allowAdditionalExtensions).to.be.false | ||
46 | expect(data.transcoding.threads).to.equal(2) | 48 | expect(data.transcoding.threads).to.equal(2) |
47 | expect(data.transcoding.resolutions['240p']).to.be.true | 49 | expect(data.transcoding.resolutions['240p']).to.be.true |
48 | expect(data.transcoding.resolutions['360p']).to.be.true | 50 | expect(data.transcoding.resolutions['360p']).to.be.true |
@@ -74,6 +76,7 @@ function checkUpdatedConfig (data: CustomConfig) { | |||
74 | expect(data.user.videoQuotaDaily).to.equal(318742) | 76 | expect(data.user.videoQuotaDaily).to.equal(318742) |
75 | expect(data.transcoding.enabled).to.be.true | 77 | expect(data.transcoding.enabled).to.be.true |
76 | expect(data.transcoding.threads).to.equal(1) | 78 | expect(data.transcoding.threads).to.equal(1) |
79 | expect(data.transcoding.allowAdditionalExtensions).to.be.true | ||
77 | expect(data.transcoding.resolutions['240p']).to.be.false | 80 | expect(data.transcoding.resolutions['240p']).to.be.false |
78 | expect(data.transcoding.resolutions['360p']).to.be.true | 81 | expect(data.transcoding.resolutions['360p']).to.be.true |
79 | expect(data.transcoding.resolutions['480p']).to.be.true | 82 | expect(data.transcoding.resolutions['480p']).to.be.true |
@@ -96,7 +99,7 @@ describe('Test config', function () { | |||
96 | 99 | ||
97 | it('Should have a correct config on a server with registration enabled', async function () { | 100 | it('Should have a correct config on a server with registration enabled', async function () { |
98 | const res = await getConfig(server.url) | 101 | const res = await getConfig(server.url) |
99 | const data = res.body | 102 | const data: ServerConfig = res.body |
100 | 103 | ||
101 | expect(data.signup.allowed).to.be.true | 104 | expect(data.signup.allowed).to.be.true |
102 | }) | 105 | }) |
@@ -111,11 +114,21 @@ describe('Test config', function () { | |||
111 | ]) | 114 | ]) |
112 | 115 | ||
113 | const res = await getConfig(server.url) | 116 | const res = await getConfig(server.url) |
114 | const data = res.body | 117 | const data: ServerConfig = res.body |
115 | 118 | ||
116 | expect(data.signup.allowed).to.be.false | 119 | expect(data.signup.allowed).to.be.false |
117 | }) | 120 | }) |
118 | 121 | ||
122 | it('Should have the correct video allowed extensions', async function () { | ||
123 | const res = await getConfig(server.url) | ||
124 | const data: ServerConfig = res.body | ||
125 | |||
126 | expect(data.video.file.extensions).to.have.lengthOf(3) | ||
127 | expect(data.video.file.extensions).to.contain('.mp4') | ||
128 | expect(data.video.file.extensions).to.contain('.webm') | ||
129 | expect(data.video.file.extensions).to.contain('.ogv') | ||
130 | }) | ||
131 | |||
119 | it('Should get the customized configuration', async function () { | 132 | it('Should get the customized configuration', async function () { |
120 | const res = await getCustomConfig(server.url, server.accessToken) | 133 | const res = await getCustomConfig(server.url, server.accessToken) |
121 | const data = res.body as CustomConfig | 134 | const data = res.body as CustomConfig |
@@ -165,6 +178,7 @@ describe('Test config', function () { | |||
165 | }, | 178 | }, |
166 | transcoding: { | 179 | transcoding: { |
167 | enabled: true, | 180 | enabled: true, |
181 | allowAdditionalExtensions: true, | ||
168 | threads: 1, | 182 | threads: 1, |
169 | resolutions: { | 183 | resolutions: { |
170 | '240p': false, | 184 | '240p': false, |
@@ -193,6 +207,18 @@ describe('Test config', function () { | |||
193 | checkUpdatedConfig(data) | 207 | checkUpdatedConfig(data) |
194 | }) | 208 | }) |
195 | 209 | ||
210 | it('Should have the correct updated video allowed extensions', async function () { | ||
211 | const res = await getConfig(server.url) | ||
212 | const data: ServerConfig = res.body | ||
213 | |||
214 | expect(data.video.file.extensions).to.have.length.above(3) | ||
215 | expect(data.video.file.extensions).to.contain('.mp4') | ||
216 | expect(data.video.file.extensions).to.contain('.webm') | ||
217 | expect(data.video.file.extensions).to.contain('.ogv') | ||
218 | expect(data.video.file.extensions).to.contain('.flv') | ||
219 | expect(data.video.file.extensions).to.contain('.mkv') | ||
220 | }) | ||
221 | |||
196 | it('Should have the configuration updated after a restart', async function () { | 222 | it('Should have the configuration updated after a restart', async function () { |
197 | this.timeout(10000) | 223 | this.timeout(10000) |
198 | 224 | ||
diff --git a/server/tests/api/videos/video-transcoder.ts b/server/tests/api/videos/video-transcoder.ts index 68cf00194..eefd32ef8 100644 --- a/server/tests/api/videos/video-transcoder.ts +++ b/server/tests/api/videos/video-transcoder.ts | |||
@@ -20,9 +20,8 @@ import { | |||
20 | uploadVideo, | 20 | uploadVideo, |
21 | webtorrentAdd | 21 | webtorrentAdd |
22 | } from '../../../../shared/utils' | 22 | } from '../../../../shared/utils' |
23 | import { join } from 'path' | 23 | import { extname, join } from 'path' |
24 | import { waitJobs } from '../../../../shared/utils/server/jobs' | 24 | import { waitJobs } from '../../../../shared/utils/server/jobs' |
25 | import { pathExists } from 'fs-extra' | ||
26 | import { VIDEO_TRANSCODING_FPS } from '../../../../server/initializers/constants' | 25 | import { VIDEO_TRANSCODING_FPS } from '../../../../server/initializers/constants' |
27 | 26 | ||
28 | const expect = chai.expect | 27 | const expect = chai.expect |
@@ -322,6 +321,34 @@ describe('Test video transcoding', function () { | |||
322 | } | 321 | } |
323 | }) | 322 | }) |
324 | 323 | ||
324 | it('Should accept and transcode additional extensions', async function () { | ||
325 | this.timeout(300000) | ||
326 | |||
327 | for (const fixture of [ 'video_short.mkv', 'video_short.avi' ]) { | ||
328 | const videoAttributes = { | ||
329 | name: fixture, | ||
330 | fixture | ||
331 | } | ||
332 | |||
333 | await uploadVideo(servers[ 1 ].url, servers[ 1 ].accessToken, videoAttributes) | ||
334 | |||
335 | await waitJobs(servers) | ||
336 | |||
337 | for (const server of servers) { | ||
338 | const res = await getVideosList(server.url) | ||
339 | |||
340 | const video = res.body.data.find(v => v.name === videoAttributes.name) | ||
341 | const res2 = await getVideo(server.url, video.id) | ||
342 | const videoDetails = res2.body | ||
343 | |||
344 | expect(videoDetails.files).to.have.lengthOf(4) | ||
345 | |||
346 | const magnetUri = videoDetails.files[ 0 ].magnetUri | ||
347 | expect(magnetUri).to.contain('.mp4') | ||
348 | } | ||
349 | } | ||
350 | }) | ||
351 | |||
325 | after(async function () { | 352 | after(async function () { |
326 | killallServers(servers) | 353 | killallServers(servers) |
327 | }) | 354 | }) |
diff --git a/server/tests/fixtures/video_short.avi b/server/tests/fixtures/video_short.avi new file mode 100644 index 000000000..88979cab2 --- /dev/null +++ b/server/tests/fixtures/video_short.avi | |||
Binary files differ | |||
diff --git a/server/tests/fixtures/video_short.mkv b/server/tests/fixtures/video_short.mkv new file mode 100644 index 000000000..a67f4f806 --- /dev/null +++ b/server/tests/fixtures/video_short.mkv | |||
Binary files differ | |||