Add ability to set video thumbnail/preview
This commit is contained in:
parent
e883399fa6
commit
ac81d1a06d
22 changed files with 454 additions and 143 deletions
|
@ -1,4 +1,4 @@
|
|||
import { ElementRef, OnInit, ViewChild, ViewChildren } from '@angular/core'
|
||||
import { ElementRef, OnInit, ViewChild } from '@angular/core'
|
||||
import { ActivatedRoute, Router } from '@angular/router'
|
||||
import { isInMobileView } from '@app/shared/misc/utils'
|
||||
import { InfiniteScrollerDirective } from '@app/shared/video/infinite-scroller.directive'
|
||||
|
|
|
@ -158,7 +158,7 @@ app.use(function (req, res, next) {
|
|||
})
|
||||
|
||||
app.use(function (err, req, res, next) {
|
||||
logger.error(err, err)
|
||||
logger.error('Error in controller.', { error: err.stack || err.message || err })
|
||||
res.sendStatus(err.status || 500)
|
||||
})
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import * as express from 'express'
|
||||
import 'multer'
|
||||
import { extname, join } from 'path'
|
||||
import * as sharp from 'sharp'
|
||||
import * as uuidv4 from 'uuid/v4'
|
||||
import { UserCreate, UserRight, UserRole, UserUpdate, UserUpdateMe, UserVideoRate as FormattedUserVideoRate } from '../../../shared'
|
||||
import { unlinkPromise } from '../../helpers/core-utils'
|
||||
import { retryTransactionWrapper } from '../../helpers/database-utils'
|
||||
import { processImage } from '../../helpers/image-utils'
|
||||
import { logger } from '../../helpers/logger'
|
||||
import { createReqFiles, getFormattedObjects } from '../../helpers/utils'
|
||||
import { AVATAR_MIMETYPE_EXT, AVATARS_SIZE, CONFIG, sequelizeTypescript } from '../../initializers'
|
||||
import { AVATARS_SIZE, CONFIG, IMAGE_MIMETYPE_EXT, sequelizeTypescript } from '../../initializers'
|
||||
import { updateActorAvatarInstance } from '../../lib/activitypub'
|
||||
import { sendUpdateUser } from '../../lib/activitypub/send'
|
||||
import { Emailer } from '../../lib/emailer'
|
||||
|
@ -42,7 +42,7 @@ import { UserModel } from '../../models/account/user'
|
|||
import { OAuthTokenModel } from '../../models/oauth/oauth-token'
|
||||
import { VideoModel } from '../../models/video/video'
|
||||
|
||||
const reqAvatarFile = createReqFiles('avatarfile', CONFIG.STORAGE.AVATARS_DIR, AVATAR_MIMETYPE_EXT)
|
||||
const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.AVATARS_DIR })
|
||||
|
||||
const usersRouter = express.Router()
|
||||
|
||||
|
@ -288,17 +288,10 @@ async function updateMyAvatar (req: express.Request, res: express.Response, next
|
|||
const user = res.locals.oauth.token.user
|
||||
const actor = user.Account.Actor
|
||||
|
||||
const avatarDir = CONFIG.STORAGE.AVATARS_DIR
|
||||
const source = join(avatarDir, avatarPhysicalFile.filename)
|
||||
const extension = extname(avatarPhysicalFile.filename)
|
||||
const avatarName = uuidv4() + extension
|
||||
const destination = join(avatarDir, avatarName)
|
||||
|
||||
await sharp(source)
|
||||
.resize(AVATARS_SIZE.width, AVATARS_SIZE.height)
|
||||
.toFile(destination)
|
||||
|
||||
await unlinkPromise(source)
|
||||
const destination = join(CONFIG.STORAGE.AVATARS_DIR, avatarName)
|
||||
await processImage(avatarPhysicalFile, destination, AVATARS_SIZE)
|
||||
|
||||
const avatar = await sequelizeTypescript.transaction(async t => {
|
||||
const updatedActor = await updateActorAvatarInstance(actor, avatarName, t)
|
||||
|
|
|
@ -4,18 +4,36 @@ import { VideoCreate, VideoPrivacy, VideoUpdate } from '../../../../shared'
|
|||
import { renamePromise } from '../../../helpers/core-utils'
|
||||
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
||||
import { getVideoFileHeight } from '../../../helpers/ffmpeg-utils'
|
||||
import { processImage } from '../../../helpers/image-utils'
|
||||
import { logger } from '../../../helpers/logger'
|
||||
import { createReqFiles, getFormattedObjects, getServerActor, resetSequelizeInstance } from '../../../helpers/utils'
|
||||
import {
|
||||
CONFIG, sequelizeTypescript, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_MIMETYPE_EXT,
|
||||
CONFIG,
|
||||
IMAGE_MIMETYPE_EXT,
|
||||
PREVIEWS_SIZE,
|
||||
sequelizeTypescript,
|
||||
THUMBNAILS_SIZE,
|
||||
VIDEO_CATEGORIES,
|
||||
VIDEO_LANGUAGES,
|
||||
VIDEO_LICENCES,
|
||||
VIDEO_MIMETYPE_EXT,
|
||||
VIDEO_PRIVACIES
|
||||
} from '../../../initializers'
|
||||
import { fetchRemoteVideoDescription, getVideoActivityPubUrl, shareVideoByServerAndChannel } from '../../../lib/activitypub'
|
||||
import { sendCreateVideo, sendCreateViewToOrigin, sendCreateViewToVideoFollowers, sendUpdateVideo } from '../../../lib/activitypub/send'
|
||||
import { JobQueue } from '../../../lib/job-queue'
|
||||
import {
|
||||
asyncMiddleware, authenticate, paginationValidator, setDefaultSort, setDefaultPagination, videosAddValidator, videosGetValidator,
|
||||
videosRemoveValidator, videosSearchValidator, videosSortValidator, videosUpdateValidator
|
||||
asyncMiddleware,
|
||||
authenticate,
|
||||
paginationValidator,
|
||||
setDefaultPagination,
|
||||
setDefaultSort,
|
||||
videosAddValidator,
|
||||
videosGetValidator,
|
||||
videosRemoveValidator,
|
||||
videosSearchValidator,
|
||||
videosSortValidator,
|
||||
videosUpdateValidator
|
||||
} from '../../../middlewares'
|
||||
import { TagModel } from '../../../models/video/tag'
|
||||
import { VideoModel } from '../../../models/video/video'
|
||||
|
@ -28,7 +46,23 @@ import { rateVideoRouter } from './rate'
|
|||
|
||||
const videosRouter = express.Router()
|
||||
|
||||
const reqVideoFile = createReqFiles('videofile', CONFIG.STORAGE.VIDEOS_DIR, VIDEO_MIMETYPE_EXT)
|
||||
const reqVideoFileAdd = createReqFiles(
|
||||
[ 'videofile', 'thumbnailfile', 'previewfile' ],
|
||||
Object.assign({}, VIDEO_MIMETYPE_EXT, IMAGE_MIMETYPE_EXT),
|
||||
{
|
||||
videofile: CONFIG.STORAGE.VIDEOS_DIR,
|
||||
thumbnailfile: CONFIG.STORAGE.THUMBNAILS_DIR,
|
||||
previewfile: CONFIG.STORAGE.PREVIEWS_DIR
|
||||
}
|
||||
)
|
||||
const reqVideoFileUpdate = createReqFiles(
|
||||
[ 'thumbnailfile', 'previewfile' ],
|
||||
IMAGE_MIMETYPE_EXT,
|
||||
{
|
||||
thumbnailfile: CONFIG.STORAGE.THUMBNAILS_DIR,
|
||||
previewfile: CONFIG.STORAGE.PREVIEWS_DIR
|
||||
}
|
||||
)
|
||||
|
||||
videosRouter.use('/', abuseVideoRouter)
|
||||
videosRouter.use('/', blacklistRouter)
|
||||
|
@ -58,12 +92,13 @@ videosRouter.get('/search',
|
|||
)
|
||||
videosRouter.put('/:id',
|
||||
authenticate,
|
||||
reqVideoFileUpdate,
|
||||
asyncMiddleware(videosUpdateValidator),
|
||||
asyncMiddleware(updateVideoRetryWrapper)
|
||||
)
|
||||
videosRouter.post('/upload',
|
||||
authenticate,
|
||||
reqVideoFile,
|
||||
reqVideoFileAdd,
|
||||
asyncMiddleware(videosAddValidator),
|
||||
asyncMiddleware(addVideoRetryWrapper)
|
||||
)
|
||||
|
@ -150,8 +185,7 @@ async function addVideo (req: express.Request, res: express.Response, videoPhysi
|
|||
const video = new VideoModel(videoData)
|
||||
video.url = getVideoActivityPubUrl(video)
|
||||
|
||||
const videoFilePath = join(CONFIG.STORAGE.VIDEOS_DIR, videoPhysicalFile.filename)
|
||||
const videoFileHeight = await getVideoFileHeight(videoFilePath)
|
||||
const videoFileHeight = await getVideoFileHeight(videoPhysicalFile.path)
|
||||
|
||||
const videoFileData = {
|
||||
extname: extname(videoPhysicalFile.filename),
|
||||
|
@ -160,21 +194,28 @@ async function addVideo (req: express.Request, res: express.Response, videoPhysi
|
|||
}
|
||||
const videoFile = new VideoFileModel(videoFileData)
|
||||
const videoDir = CONFIG.STORAGE.VIDEOS_DIR
|
||||
const source = join(videoDir, videoPhysicalFile.filename)
|
||||
const destination = join(videoDir, video.getVideoFilename(videoFile))
|
||||
await renamePromise(videoPhysicalFile.path, destination)
|
||||
|
||||
await renamePromise(source, destination)
|
||||
// This is important in case if there is another attempt in the retry process
|
||||
videoPhysicalFile.filename = video.getVideoFilename(videoFile)
|
||||
// Process thumbnail or create it from the video
|
||||
const thumbnailField = req.files['thumbnailfile']
|
||||
if (thumbnailField) {
|
||||
const thumbnailPhysicalFile = thumbnailField[0]
|
||||
await processImage(thumbnailPhysicalFile, join(CONFIG.STORAGE.THUMBNAILS_DIR, video.getThumbnailName()), THUMBNAILS_SIZE)
|
||||
} else {
|
||||
await video.createThumbnail(videoFile)
|
||||
}
|
||||
|
||||
const tasks = []
|
||||
// Process preview or create it from the video
|
||||
const previewField = req.files['previewfile']
|
||||
if (previewField) {
|
||||
const previewPhysicalFile = previewField[0]
|
||||
await processImage(previewPhysicalFile, join(CONFIG.STORAGE.PREVIEWS_DIR, video.getPreviewName()), PREVIEWS_SIZE)
|
||||
} else {
|
||||
await video.createPreview(videoFile)
|
||||
}
|
||||
|
||||
tasks.push(
|
||||
video.createTorrentAndSetInfoHash(videoFile),
|
||||
video.createThumbnail(videoFile),
|
||||
video.createPreview(videoFile)
|
||||
)
|
||||
await Promise.all(tasks)
|
||||
await video.createTorrentAndSetInfoHash(videoFile)
|
||||
|
||||
const videoCreated = await sequelizeTypescript.transaction(async t => {
|
||||
const sequelizeOptions = { transaction: t }
|
||||
|
@ -237,6 +278,18 @@ async function updateVideo (req: express.Request, res: express.Response) {
|
|||
const videoInfoToUpdate: VideoUpdate = req.body
|
||||
const wasPrivateVideo = videoInstance.privacy === VideoPrivacy.PRIVATE
|
||||
|
||||
// Process thumbnail or create it from the video
|
||||
if (req.files && req.files['thumbnailfile']) {
|
||||
const thumbnailPhysicalFile = req.files['thumbnailfile'][0]
|
||||
await processImage(thumbnailPhysicalFile, join(CONFIG.STORAGE.THUMBNAILS_DIR, videoInstance.getThumbnailName()), THUMBNAILS_SIZE)
|
||||
}
|
||||
|
||||
// Process preview or create it from the video
|
||||
if (req.files && req.files['previewfile']) {
|
||||
const previewPhysicalFile = req.files['previewfile'][0]
|
||||
await processImage(previewPhysicalFile, join(CONFIG.STORAGE.PREVIEWS_DIR, videoInstance.getPreviewName()), PREVIEWS_SIZE)
|
||||
}
|
||||
|
||||
try {
|
||||
await sequelizeTypescript.transaction(async t => {
|
||||
const sequelizeOptions = {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'multer'
|
||||
import * as validator from 'validator'
|
||||
|
||||
function exists (value: any) {
|
||||
|
@ -28,6 +29,29 @@ function isBooleanValid (value: string) {
|
|||
return typeof value === 'boolean' || (typeof value === 'string' && validator.isBoolean(value))
|
||||
}
|
||||
|
||||
function isFileValid (
|
||||
files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[],
|
||||
mimeTypeRegex: string,
|
||||
field: string,
|
||||
optional = false
|
||||
) {
|
||||
// Should have files
|
||||
if (!files) return optional
|
||||
if (isArray(files)) return optional
|
||||
|
||||
// Should have a file
|
||||
const fileArray = files[ field ]
|
||||
if (!fileArray || fileArray.length === 0) {
|
||||
return optional
|
||||
}
|
||||
|
||||
// The file should exist
|
||||
const file = fileArray[ 0 ]
|
||||
if (!file || !file.originalname) return false
|
||||
|
||||
return new RegExp(`^${mimeTypeRegex}$`, 'i').test(file.mimetype)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
|
@ -37,5 +61,6 @@ export {
|
|||
isUUIDValid,
|
||||
isIdOrUUIDValid,
|
||||
isDateValid,
|
||||
isBooleanValid
|
||||
isBooleanValid,
|
||||
isFileValid
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import * as validator from 'validator'
|
||||
import 'express-validator'
|
||||
|
||||
import { exists, isArray } from './misc'
|
||||
import { CONSTRAINTS_FIELDS } from '../../initializers'
|
||||
import * as validator from 'validator'
|
||||
import { UserRole } from '../../../shared'
|
||||
import { CONSTRAINTS_FIELDS } from '../../initializers'
|
||||
|
||||
import { exists, isFileValid } from './misc'
|
||||
|
||||
const USERS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.USERS
|
||||
|
||||
|
@ -37,20 +37,12 @@ function isUserRoleValid (value: any) {
|
|||
return exists(value) && validator.isInt('' + value) && UserRole[value] !== undefined
|
||||
}
|
||||
|
||||
const avatarMimeTypes = CONSTRAINTS_FIELDS.ACTORS.AVATAR.EXTNAME
|
||||
.map(v => v.replace('.', ''))
|
||||
.join('|')
|
||||
const avatarMimeTypesRegex = `image/(${avatarMimeTypes})`
|
||||
function isAvatarFile (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[]) {
|
||||
// Should have files
|
||||
if (!files) return false
|
||||
if (isArray(files)) return false
|
||||
|
||||
// Should have videofile file
|
||||
const avatarfile = files['avatarfile']
|
||||
if (!avatarfile || avatarfile.length === 0) return false
|
||||
|
||||
// The file should exist
|
||||
const file = avatarfile[0]
|
||||
if (!file || !file.originalname) return false
|
||||
|
||||
return new RegExp('^image/(png|jpeg)$', 'i').test(file.mimetype)
|
||||
return isFileValid(files, avatarMimeTypesRegex, 'avatarfile')
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
@ -8,12 +8,12 @@ import {
|
|||
CONSTRAINTS_FIELDS,
|
||||
VIDEO_CATEGORIES,
|
||||
VIDEO_LANGUAGES,
|
||||
VIDEO_LICENCES,
|
||||
VIDEO_LICENCES, VIDEO_MIMETYPE_EXT,
|
||||
VIDEO_PRIVACIES,
|
||||
VIDEO_RATE_TYPES
|
||||
} from '../../initializers'
|
||||
import { VideoModel } from '../../models/video/video'
|
||||
import { exists, isArray } from './misc'
|
||||
import { exists, isArray, isFileValid } from './misc'
|
||||
|
||||
const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS
|
||||
const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES
|
||||
|
@ -68,20 +68,18 @@ function isVideoRatingTypeValid (value: string) {
|
|||
return value === 'none' || values(VIDEO_RATE_TYPES).indexOf(value as VideoRateType) !== -1
|
||||
}
|
||||
|
||||
const videoFileTypes = Object.keys(VIDEO_MIMETYPE_EXT).map(m => `(${m})`)
|
||||
const videoFileTypesRegex = videoFileTypes.join('|')
|
||||
function isVideoFile (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[]) {
|
||||
// Should have files
|
||||
if (!files) return false
|
||||
if (isArray(files)) return false
|
||||
return isFileValid(files, videoFileTypesRegex, 'videofile')
|
||||
}
|
||||
|
||||
// Should have videofile file
|
||||
const videofile = files['videofile']
|
||||
if (!videofile || videofile.length === 0) return false
|
||||
|
||||
// The file should exist
|
||||
const file = videofile[0]
|
||||
if (!file || !file.originalname) return false
|
||||
|
||||
return new RegExp('^video/(webm|mp4|ogg)$', 'i').test(file.mimetype)
|
||||
const videoImageTypes = CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME
|
||||
.map(v => v.replace('.', ''))
|
||||
.join('|')
|
||||
const videoImageTypesRegex = `image/(${videoImageTypes})`
|
||||
function isVideoImage (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[], field: string) {
|
||||
return isFileValid(files, videoImageTypesRegex, field, true)
|
||||
}
|
||||
|
||||
function isVideoPrivacyValid (value: string) {
|
||||
|
@ -141,5 +139,6 @@ export {
|
|||
isVideoPrivacyValid,
|
||||
isVideoFileResolutionValid,
|
||||
isVideoFileSizeValid,
|
||||
isVideoExist
|
||||
isVideoExist,
|
||||
isVideoImage
|
||||
}
|
||||
|
|
21
server/helpers/image-utils.ts
Normal file
21
server/helpers/image-utils.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import 'multer'
|
||||
import * as sharp from 'sharp'
|
||||
import { unlinkPromise } from './core-utils'
|
||||
|
||||
async function processImage (
|
||||
physicalFile: Express.Multer.File,
|
||||
destination: string,
|
||||
newSize: { width: number, height: number }
|
||||
) {
|
||||
await sharp(physicalFile.path)
|
||||
.resize(newSize.width, newSize.height)
|
||||
.toFile(destination)
|
||||
|
||||
await unlinkPromise(physicalFile.path)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
processImage
|
||||
}
|
|
@ -27,10 +27,14 @@ function badRequest (req: express.Request, res: express.Response, next: express.
|
|||
return res.type('json').status(400).end()
|
||||
}
|
||||
|
||||
function createReqFiles (fieldName: string, storageDir: string, mimeTypes: { [ id: string ]: string }) {
|
||||
function createReqFiles (
|
||||
fieldNames: string[],
|
||||
mimeTypes: { [ id: string ]: string },
|
||||
destinations: { [ fieldName: string ]: string }
|
||||
) {
|
||||
const storage = multer.diskStorage({
|
||||
destination: (req, file, cb) => {
|
||||
cb(null, storageDir)
|
||||
cb(null, destinations[file.fieldname])
|
||||
},
|
||||
|
||||
filename: async (req, file, cb) => {
|
||||
|
@ -48,7 +52,15 @@ function createReqFiles (fieldName: string, storageDir: string, mimeTypes: { [ i
|
|||
}
|
||||
})
|
||||
|
||||
return multer({ storage }).fields([{ name: fieldName, maxCount: 1 }])
|
||||
const fields = []
|
||||
for (const fieldName of fieldNames) {
|
||||
fields.push({
|
||||
name: fieldName,
|
||||
maxCount: 1
|
||||
})
|
||||
}
|
||||
|
||||
return multer({ storage }).fields(fields)
|
||||
}
|
||||
|
||||
async function generateRandomString (size: number) {
|
||||
|
|
|
@ -182,6 +182,12 @@ const CONSTRAINTS_FIELDS = {
|
|||
NAME: { min: 3, max: 120 }, // Length
|
||||
TRUNCATED_DESCRIPTION: { min: 3, max: 250 }, // Length
|
||||
DESCRIPTION: { min: 3, max: 3000 }, // Length
|
||||
IMAGE: {
|
||||
EXTNAME: [ '.jpg', '.jpeg' ],
|
||||
FILE_SIZE: {
|
||||
max: 2 * 1024 * 1024 // 2MB
|
||||
}
|
||||
},
|
||||
EXTNAME: [ '.mp4', '.ogv', '.webm' ],
|
||||
INFO_HASH: { min: 40, max: 40 }, // Length, info hash is 20 bytes length but we represent it in hexadecimal so 20 * 2
|
||||
DURATION: { min: 1 }, // Number
|
||||
|
@ -285,7 +291,7 @@ const VIDEO_MIMETYPE_EXT = {
|
|||
'video/mp4': '.mp4'
|
||||
}
|
||||
|
||||
const AVATAR_MIMETYPE_EXT = {
|
||||
const IMAGE_MIMETYPE_EXT = {
|
||||
'image/png': '.png',
|
||||
'image/jpg': '.jpg',
|
||||
'image/jpeg': '.jpg'
|
||||
|
@ -427,7 +433,7 @@ export {
|
|||
VIDEO_RATE_TYPES,
|
||||
VIDEO_MIMETYPE_EXT,
|
||||
USER_PASSWORD_RESET_LIFETIME,
|
||||
AVATAR_MIMETYPE_EXT,
|
||||
IMAGE_MIMETYPE_EXT,
|
||||
SCHEDULER_INTERVAL,
|
||||
JOB_COMPLETED_LIFETIME
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import { logger } from '../../helpers/logger'
|
|||
import { createPrivateAndPublicKeys } from '../../helpers/peertube-crypto'
|
||||
import { doRequest, doRequestAndSaveToFile } from '../../helpers/requests'
|
||||
import { getUrlFromWebfinger } from '../../helpers/webfinger'
|
||||
import { AVATAR_MIMETYPE_EXT, CONFIG, sequelizeTypescript } from '../../initializers'
|
||||
import { IMAGE_MIMETYPE_EXT, CONFIG, sequelizeTypescript } from '../../initializers'
|
||||
import { AccountModel } from '../../models/account/account'
|
||||
import { ActorModel } from '../../models/activitypub/actor'
|
||||
import { AvatarModel } from '../../models/avatar/avatar'
|
||||
|
@ -147,10 +147,10 @@ async function fetchActorTotalItems (url: string) {
|
|||
|
||||
async function fetchAvatarIfExists (actorJSON: ActivityPubActor) {
|
||||
if (
|
||||
actorJSON.icon && actorJSON.icon.type === 'Image' && AVATAR_MIMETYPE_EXT[actorJSON.icon.mediaType] !== undefined &&
|
||||
actorJSON.icon && actorJSON.icon.type === 'Image' && IMAGE_MIMETYPE_EXT[actorJSON.icon.mediaType] !== undefined &&
|
||||
isActivityPubUrlValid(actorJSON.icon.url)
|
||||
) {
|
||||
const extension = AVATAR_MIMETYPE_EXT[actorJSON.icon.mediaType]
|
||||
const extension = IMAGE_MIMETYPE_EXT[actorJSON.icon.mediaType]
|
||||
|
||||
const avatarName = uuidv4() + extension
|
||||
const destPath = join(CONFIG.STORAGE.AVATARS_DIR, avatarName)
|
||||
|
|
|
@ -4,8 +4,18 @@ import { body, param, query } from 'express-validator/check'
|
|||
import { UserRight, VideoPrivacy } from '../../../shared'
|
||||
import { isBooleanValid, isIdOrUUIDValid, isIdValid, isUUIDValid } from '../../helpers/custom-validators/misc'
|
||||
import {
|
||||
isVideoAbuseReasonValid, isVideoCategoryValid, isVideoDescriptionValid, isVideoExist, isVideoFile, isVideoLanguageValid,
|
||||
isVideoLicenceValid, isVideoNameValid, isVideoPrivacyValid, isVideoRatingTypeValid, isVideoTagsValid
|
||||
isVideoAbuseReasonValid,
|
||||
isVideoCategoryValid,
|
||||
isVideoDescriptionValid,
|
||||
isVideoExist,
|
||||
isVideoFile,
|
||||
isVideoImage,
|
||||
isVideoLanguageValid,
|
||||
isVideoLicenceValid,
|
||||
isVideoNameValid,
|
||||
isVideoPrivacyValid,
|
||||
isVideoRatingTypeValid,
|
||||
isVideoTagsValid
|
||||
} from '../../helpers/custom-validators/videos'
|
||||
import { getDurationFromVideoFile } from '../../helpers/ffmpeg-utils'
|
||||
import { logger } from '../../helpers/logger'
|
||||
|
@ -22,6 +32,14 @@ const videosAddValidator = [
|
|||
'This file is not supported. Please, make sure it is of the following type : '
|
||||
+ CONSTRAINTS_FIELDS.VIDEOS.EXTNAME.join(', ')
|
||||
),
|
||||
body('thumbnailfile').custom((value, { req }) => isVideoImage(req.files, 'thumbnailfile')).withMessage(
|
||||
'This thumbnail file is not supported. Please, make sure it is of the following type : '
|
||||
+ CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME.join(', ')
|
||||
),
|
||||
body('previewfile').custom((value, { req }) => isVideoImage(req.files, 'previewfile')).withMessage(
|
||||
'This preview file is not supported. Please, make sure it is of the following type : '
|
||||
+ CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME.join(', ')
|
||||
),
|
||||
body('name').custom(isVideoNameValid).withMessage('Should have a valid name'),
|
||||
body('category').optional().custom(isVideoCategoryValid).withMessage('Should have a valid category'),
|
||||
body('licence').optional().custom(isVideoLicenceValid).withMessage('Should have a valid licence'),
|
||||
|
@ -37,6 +55,7 @@ const videosAddValidator = [
|
|||
logger.debug('Checking videosAdd parameters', { parameters: req.body, files: req.files })
|
||||
|
||||
if (areValidationErrors(req, res)) return
|
||||
if (areErrorsInVideoImageFiles(req, res)) return
|
||||
|
||||
const videoFile: Express.Multer.File = req.files['videofile'][0]
|
||||
const user = res.locals.oauth.token.User
|
||||
|
@ -82,6 +101,14 @@ const videosAddValidator = [
|
|||
|
||||
const videosUpdateValidator = [
|
||||
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
|
||||
body('thumbnailfile').custom((value, { req }) => isVideoImage(req.files, 'thumbnailfile')).withMessage(
|
||||
'This thumbnail file is not supported. Please, make sure it is of the following type : '
|
||||
+ CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME.join(', ')
|
||||
),
|
||||
body('previewfile').custom((value, { req }) => isVideoImage(req.files, 'previewfile')).withMessage(
|
||||
'This preview file is not supported. Please, make sure it is of the following type : '
|
||||
+ CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME.join(', ')
|
||||
),
|
||||
body('name').optional().custom(isVideoNameValid).withMessage('Should have a valid name'),
|
||||
body('category').optional().custom(isVideoCategoryValid).withMessage('Should have a valid category'),
|
||||
body('licence').optional().custom(isVideoLicenceValid).withMessage('Should have a valid licence'),
|
||||
|
@ -96,6 +123,7 @@ const videosUpdateValidator = [
|
|||
logger.debug('Checking videosUpdate parameters', { parameters: req.body })
|
||||
|
||||
if (areValidationErrors(req, res)) return
|
||||
if (areErrorsInVideoImageFiles(req, res)) return
|
||||
if (!await isVideoExist(req.params.id, res)) return
|
||||
|
||||
const video = res.locals.video
|
||||
|
@ -274,3 +302,22 @@ function checkUserCanDeleteVideo (user: UserModel, video: VideoModel, res: expre
|
|||
|
||||
return true
|
||||
}
|
||||
|
||||
function areErrorsInVideoImageFiles (req: express.Request, res: express.Response) {
|
||||
// Files are optional
|
||||
if (!req.files) return false
|
||||
|
||||
for (const imageField of [ 'thumbnail', 'preview' ]) {
|
||||
if (!req.files[ imageField ]) continue
|
||||
|
||||
const imageFile = req.files[ imageField ][ 0 ] as Express.Multer.File
|
||||
if (imageFile.size > CONSTRAINTS_FIELDS.VIDEOS.IMAGE.FILE_SIZE.max) {
|
||||
res.status(400)
|
||||
.send({ error: `The size of the ${imageField} is too big (>${CONSTRAINTS_FIELDS.VIDEOS.IMAGE.FILE_SIZE.max}).` })
|
||||
.end()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import { UserRole } from '../../../../shared'
|
|||
|
||||
import {
|
||||
createUser, flushTests, getMyUserInformation, getMyUserVideoRating, getUsersList, immutableAssign, killallServers, makeGetRequest,
|
||||
makePostBodyRequest, makePostUploadRequest, makePutBodyRequest, registerUser, removeUser, runServer, ServerInfo, setAccessTokensToServers,
|
||||
makePostBodyRequest, makeUploadRequest, makePutBodyRequest, registerUser, removeUser, runServer, ServerInfo, setAccessTokensToServers,
|
||||
updateUser, uploadVideo, userLogin
|
||||
} from '../../utils'
|
||||
import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params'
|
||||
|
@ -273,7 +273,7 @@ describe('Test users API validators', function () {
|
|||
const attaches = {
|
||||
'avatarfile': join(__dirname, '..', 'fixtures', 'video_short.mp4')
|
||||
}
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/me/avatar/pick', token: server.accessToken, fields, attaches })
|
||||
await makeUploadRequest({ url: server.url, path: path + '/me/avatar/pick', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail with a big file', async function () {
|
||||
|
@ -281,7 +281,7 @@ describe('Test users API validators', function () {
|
|||
const attaches = {
|
||||
'avatarfile': join(__dirname, '..', 'fixtures', 'avatar-big.png')
|
||||
}
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/me/avatar/pick', token: server.accessToken, fields, attaches })
|
||||
await makeUploadRequest({ url: server.url, path: path + '/me/avatar/pick', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should succeed with the correct params', async function () {
|
||||
|
@ -289,7 +289,7 @@ describe('Test users API validators', function () {
|
|||
const attaches = {
|
||||
'avatarfile': join(__dirname, '..', 'fixtures', 'avatar.png')
|
||||
}
|
||||
await makePostUploadRequest({
|
||||
await makeUploadRequest({
|
||||
url: server.url,
|
||||
path: path + '/me/avatar/pick',
|
||||
token: server.accessToken,
|
||||
|
|
|
@ -7,7 +7,7 @@ import { join } from 'path'
|
|||
import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum'
|
||||
import {
|
||||
createUser, flushTests, getMyUserInformation, getVideo, getVideosList, immutableAssign, killallServers, makeDeleteRequest,
|
||||
makeGetRequest, makePostUploadRequest, makePutBodyRequest, removeVideo, runServer, ServerInfo, setAccessTokensToServers, userLogin
|
||||
makeGetRequest, makeUploadRequest, makePutBodyRequest, removeVideo, runServer, ServerInfo, setAccessTokensToServers, userLogin
|
||||
} from '../../utils'
|
||||
import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params'
|
||||
|
||||
|
@ -111,91 +111,91 @@ describe('Test videos API validator', function () {
|
|||
it('Should fail with nothing', async function () {
|
||||
const fields = {}
|
||||
const attaches = {}
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail without name', async function () {
|
||||
const fields = omit(baseCorrectParams, 'name')
|
||||
const attaches = baseCorrectAttaches
|
||||
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail with a long name', async function () {
|
||||
const fields = immutableAssign(baseCorrectParams, { name: 'super'.repeat(65) })
|
||||
const attaches = baseCorrectAttaches
|
||||
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail with a bad category', async function () {
|
||||
const fields = immutableAssign(baseCorrectParams, { category: 125 })
|
||||
const attaches = baseCorrectAttaches
|
||||
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail with a bad licence', async function () {
|
||||
const fields = immutableAssign(baseCorrectParams, { licence: 125 })
|
||||
const attaches = baseCorrectAttaches
|
||||
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail with a bad language', async function () {
|
||||
const fields = immutableAssign(baseCorrectParams, { language: 125 })
|
||||
const attaches = baseCorrectAttaches
|
||||
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail without nsfw attribute', async function () {
|
||||
const fields = omit(baseCorrectParams, 'nsfw')
|
||||
const attaches = baseCorrectAttaches
|
||||
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail with a bad nsfw attribute', async function () {
|
||||
const fields = immutableAssign(baseCorrectParams, { nsfw: 2 })
|
||||
const attaches = baseCorrectAttaches
|
||||
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail without commentsEnabled attribute', async function () {
|
||||
const fields = omit(baseCorrectParams, 'commentsEnabled')
|
||||
const attaches = baseCorrectAttaches
|
||||
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail with a bad commentsEnabled attribute', async function () {
|
||||
const fields = immutableAssign(baseCorrectParams, { commentsEnabled: 2 })
|
||||
const attaches = baseCorrectAttaches
|
||||
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail with a long description', async function () {
|
||||
const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(1500) })
|
||||
const attaches = baseCorrectAttaches
|
||||
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail without a channel', async function () {
|
||||
const fields = omit(baseCorrectParams, 'channelId')
|
||||
const attaches = baseCorrectAttaches
|
||||
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail with a bad channel', async function () {
|
||||
const fields = immutableAssign(baseCorrectParams, { channelId: 545454 })
|
||||
const attaches = baseCorrectAttaches
|
||||
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail with another user channel', async function () {
|
||||
|
@ -212,34 +212,34 @@ describe('Test videos API validator', function () {
|
|||
const fields = immutableAssign(baseCorrectParams, { channelId: customChannelId })
|
||||
const attaches = baseCorrectAttaches
|
||||
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail with too many tags', async function () {
|
||||
const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ] })
|
||||
const attaches = baseCorrectAttaches
|
||||
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail with a tag length too low', async function () {
|
||||
const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 't' ] })
|
||||
const attaches = baseCorrectAttaches
|
||||
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail with a tag length too big', async function () {
|
||||
const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 'my_super_tag_too_long_long_long_long_long_long' ] })
|
||||
const attaches = baseCorrectAttaches
|
||||
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail without an input file', async function () {
|
||||
const fields = baseCorrectParams
|
||||
const attaches = {}
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail without an incorrect input file', async function () {
|
||||
|
@ -247,7 +247,47 @@ describe('Test videos API validator', function () {
|
|||
const attaches = {
|
||||
'videofile': join(__dirname, '..', 'fixtures', 'video_short_fake.webm')
|
||||
}
|
||||
await makePostUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail with an incorrect thumbnail file', async function () {
|
||||
const fields = baseCorrectParams
|
||||
const attaches = {
|
||||
'thumbnailfile': join(__dirname, '..', 'fixtures', 'avatar.png'),
|
||||
'videofile': join(__dirname, '..', 'fixtures', 'video_short_fake.webm')
|
||||
}
|
||||
|
||||
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail with a big thumbnail file', async function () {
|
||||
const fields = baseCorrectParams
|
||||
const attaches = {
|
||||
'thumbnailfile': join(__dirname, '..', 'fixtures', 'avatar-big.png'),
|
||||
'videofile': join(__dirname, '..', 'fixtures', 'video_short_fake.webm')
|
||||
}
|
||||
|
||||
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail with an incorrect preview file', async function () {
|
||||
const fields = baseCorrectParams
|
||||
const attaches = {
|
||||
'previewfile': join(__dirname, '..', 'fixtures', 'avatar.png'),
|
||||
'videofile': join(__dirname, '..', 'fixtures', 'video_short_fake.webm')
|
||||
}
|
||||
|
||||
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should fail with a big preview file', async function () {
|
||||
const fields = baseCorrectParams
|
||||
const attaches = {
|
||||
'previewfile': join(__dirname, '..', 'fixtures', 'avatar-big.png'),
|
||||
'videofile': join(__dirname, '..', 'fixtures', 'video_short_fake.webm')
|
||||
}
|
||||
|
||||
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
|
||||
})
|
||||
|
||||
it('Should succeed with the correct parameters', async function () {
|
||||
|
@ -257,7 +297,7 @@ describe('Test videos API validator', function () {
|
|||
|
||||
{
|
||||
const attaches = baseCorrectAttaches
|
||||
await makePostUploadRequest({
|
||||
await makeUploadRequest({
|
||||
url: server.url,
|
||||
path: path + '/upload',
|
||||
token: server.accessToken,
|
||||
|
@ -272,7 +312,7 @@ describe('Test videos API validator', function () {
|
|||
videofile: join(__dirname, '..', 'fixtures', 'video_short.mp4')
|
||||
})
|
||||
|
||||
await makePostUploadRequest({
|
||||
await makeUploadRequest({
|
||||
url: server.url,
|
||||
path: path + '/upload',
|
||||
token: server.accessToken,
|
||||
|
@ -287,7 +327,7 @@ describe('Test videos API validator', function () {
|
|||
videofile: join(__dirname, '..', 'fixtures', 'video_short.ogv')
|
||||
})
|
||||
|
||||
await makePostUploadRequest({
|
||||
await makeUploadRequest({
|
||||
url: server.url,
|
||||
path: path + '/upload',
|
||||
token: server.accessToken,
|
||||
|
@ -400,6 +440,70 @@ describe('Test videos API validator', function () {
|
|||
await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields })
|
||||
})
|
||||
|
||||
it('Should fail with an incorrect thumbnail file', async function () {
|
||||
const fields = baseCorrectParams
|
||||
const attaches = {
|
||||
'thumbnailfile': join(__dirname, '..', 'fixtures', 'avatar.png')
|
||||
}
|
||||
|
||||
await makeUploadRequest({
|
||||
url: server.url,
|
||||
method: 'PUT',
|
||||
path: path + videoId,
|
||||
token: server.accessToken,
|
||||
fields,
|
||||
attaches
|
||||
})
|
||||
})
|
||||
|
||||
it('Should fail with a big thumbnail file', async function () {
|
||||
const fields = baseCorrectParams
|
||||
const attaches = {
|
||||
'thumbnailfile': join(__dirname, '..', 'fixtures', 'avatar-big.png')
|
||||
}
|
||||
|
||||
await makeUploadRequest({
|
||||
url: server.url,
|
||||
method: 'PUT',
|
||||
path: path + videoId,
|
||||
token: server.accessToken,
|
||||
fields,
|
||||
attaches
|
||||
})
|
||||
})
|
||||
|
||||
it('Should fail with an incorrect preview file', async function () {
|
||||
const fields = baseCorrectParams
|
||||
const attaches = {
|
||||
'previewfile': join(__dirname, '..', 'fixtures', 'avatar.png')
|
||||
}
|
||||
|
||||
await makeUploadRequest({
|
||||
url: server.url,
|
||||
method: 'PUT',
|
||||
path: path + videoId,
|
||||
token: server.accessToken,
|
||||
fields,
|
||||
attaches
|
||||
})
|
||||
})
|
||||
|
||||
it('Should fail with a big preview file', async function () {
|
||||
const fields = baseCorrectParams
|
||||
const attaches = {
|
||||
'previewfile': join(__dirname, '..', 'fixtures', 'avatar-big.png')
|
||||
}
|
||||
|
||||
await makeUploadRequest({
|
||||
url: server.url,
|
||||
method: 'PUT',
|
||||
path: path + videoId,
|
||||
token: server.accessToken,
|
||||
fields,
|
||||
attaches
|
||||
})
|
||||
})
|
||||
|
||||
it('Should fail with a video of another user')
|
||||
|
||||
it('Should fail with a video of another server')
|
||||
|
|
BIN
server/tests/api/fixtures/preview.jpg
Normal file
BIN
server/tests/api/fixtures/preview.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
BIN
server/tests/api/fixtures/thumbnail.jpg
Normal file
BIN
server/tests/api/fixtures/thumbnail.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
|
@ -137,7 +137,9 @@ describe('Test multiple servers', function () {
|
|||
nsfw: true,
|
||||
description: 'my super description for server 2',
|
||||
tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ],
|
||||
fixture: 'video_short2.webm'
|
||||
fixture: 'video_short2.webm',
|
||||
thumbnailfile: 'thumbnail.jpg',
|
||||
previewfile: 'preview.jpg'
|
||||
}
|
||||
await uploadVideo(servers[1].url, userAccessToken, videoAttributes)
|
||||
|
||||
|
@ -184,7 +186,9 @@ describe('Test multiple servers', function () {
|
|||
resolution: 720,
|
||||
size: 710000
|
||||
}
|
||||
]
|
||||
],
|
||||
thumbnailfile: 'thumbnail',
|
||||
previewfile: 'preview'
|
||||
}
|
||||
|
||||
const res = await getVideosList(server.url)
|
||||
|
@ -521,7 +525,9 @@ describe('Test multiple servers', function () {
|
|||
language: 13,
|
||||
nsfw: true,
|
||||
description: 'my super description updated',
|
||||
tags: [ 'tag_up_1', 'tag_up_2' ]
|
||||
tags: [ 'tag_up_1', 'tag_up_2' ],
|
||||
thumbnailfile: 'thumbnail.jpg',
|
||||
previewfile: 'preview.jpg'
|
||||
}
|
||||
|
||||
await updateVideo(servers[2].url, servers[2].accessToken, toRemove[0].id, attributes)
|
||||
|
@ -565,7 +571,9 @@ describe('Test multiple servers', function () {
|
|||
resolution: 720,
|
||||
size: 292677
|
||||
}
|
||||
]
|
||||
],
|
||||
thumbnailfile: 'thumbnail',
|
||||
previewfile: 'preview'
|
||||
}
|
||||
await completeVideoCheck(server.url, videoUpdated, checkAttributes)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* tslint:disable:no-unused-expression */
|
||||
|
||||
import { join } from 'path'
|
||||
import { isAbsolute, join } from 'path'
|
||||
import * as request from 'supertest'
|
||||
import * as WebTorrent from 'webtorrent'
|
||||
import { readFileBufferPromise } from '../../../helpers/core-utils'
|
||||
|
@ -45,8 +45,8 @@ async function testImage (url: string, imageName: string, imagePath: string, ext
|
|||
const body = res.body
|
||||
|
||||
const data = await readFileBufferPromise(join(__dirname, '..', '..', 'api', 'fixtures', imageName + extension))
|
||||
const minLength = body.length - ((50 * body.length) / 100)
|
||||
const maxLength = body.length + ((50 * body.length) / 100)
|
||||
const minLength = body.length - ((20 * body.length) / 100)
|
||||
const maxLength = body.length + ((20 * body.length) / 100)
|
||||
|
||||
return data.length > minLength && data.length < maxLength
|
||||
} else {
|
||||
|
@ -55,6 +55,14 @@ async function testImage (url: string, imageName: string, imagePath: string, ext
|
|||
}
|
||||
}
|
||||
|
||||
function buildAbsoluteFixturePath (path: string) {
|
||||
if (isAbsolute(path)) {
|
||||
return path
|
||||
}
|
||||
|
||||
return join(__dirname, '..', '..', 'api', 'fixtures', path)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
|
@ -63,5 +71,6 @@ export {
|
|||
webtorrentAdd,
|
||||
immutableAssign,
|
||||
testImage,
|
||||
buildAbsoluteFixturePath,
|
||||
root
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import * as request from 'supertest'
|
||||
import { buildAbsoluteFixturePath } from '../'
|
||||
|
||||
function makeGetRequest (options: {
|
||||
url: string,
|
||||
|
@ -40,8 +41,9 @@ function makeDeleteRequest (options: {
|
|||
.expect(options.statusCodeExpected)
|
||||
}
|
||||
|
||||
function makePostUploadRequest (options: {
|
||||
function makeUploadRequest (options: {
|
||||
url: string,
|
||||
method?: 'POST' | 'PUT',
|
||||
path: string,
|
||||
token: string,
|
||||
fields: { [ fieldName: string ]: any },
|
||||
|
@ -50,9 +52,14 @@ function makePostUploadRequest (options: {
|
|||
}) {
|
||||
if (!options.statusCodeExpected) options.statusCodeExpected = 400
|
||||
|
||||
const req = request(options.url)
|
||||
.post(options.path)
|
||||
.set('Accept', 'application/json')
|
||||
let req: request.Test
|
||||
if (options.method === 'PUT') {
|
||||
req = request(options.url).put(options.path)
|
||||
} else {
|
||||
req = request(options.url).post(options.path)
|
||||
}
|
||||
|
||||
req.set('Accept', 'application/json')
|
||||
|
||||
if (options.token) req.set('Authorization', 'Bearer ' + options.token)
|
||||
|
||||
|
@ -70,7 +77,7 @@ function makePostUploadRequest (options: {
|
|||
|
||||
Object.keys(options.attaches).forEach(attach => {
|
||||
const value = options.attaches[attach]
|
||||
req.attach(attach, value)
|
||||
req.attach(attach, buildAbsoluteFixturePath(value))
|
||||
})
|
||||
|
||||
return req.expect(options.statusCodeExpected)
|
||||
|
@ -119,7 +126,7 @@ function makePutBodyRequest (options: {
|
|||
|
||||
export {
|
||||
makeGetRequest,
|
||||
makePostUploadRequest,
|
||||
makeUploadRequest,
|
||||
makePostBodyRequest,
|
||||
makePutBodyRequest,
|
||||
makeDeleteRequest
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { isAbsolute, join } from 'path'
|
||||
import * as request from 'supertest'
|
||||
import { makePostBodyRequest, makePostUploadRequest, makePutBodyRequest } from '../'
|
||||
import { makePostBodyRequest, makeUploadRequest, makePutBodyRequest } from '../'
|
||||
|
||||
import { UserRole } from '../../../../shared/index'
|
||||
|
||||
|
@ -162,7 +162,7 @@ function updateMyAvatar (options: {
|
|||
filePath = join(__dirname, '..', '..', 'api', 'fixtures', options.fixture)
|
||||
}
|
||||
|
||||
return makePostUploadRequest({
|
||||
return makeUploadRequest({
|
||||
url: options.url,
|
||||
path,
|
||||
token: options.accessToken,
|
||||
|
|
|
@ -5,7 +5,16 @@ import { existsSync, readFile } from 'fs'
|
|||
import * as parseTorrent from 'parse-torrent'
|
||||
import { extname, isAbsolute, join } from 'path'
|
||||
import * as request from 'supertest'
|
||||
import { getMyUserInformation, makeGetRequest, root, ServerInfo, testImage } from '../'
|
||||
import {
|
||||
buildAbsoluteFixturePath,
|
||||
getMyUserInformation,
|
||||
makeGetRequest,
|
||||
makePutBodyRequest,
|
||||
makeUploadRequest,
|
||||
root,
|
||||
ServerInfo,
|
||||
testImage
|
||||
} from '../'
|
||||
import { VideoPrivacy } from '../../../../shared/models/videos'
|
||||
import { readdirPromise } from '../../../helpers/core-utils'
|
||||
import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../initializers'
|
||||
|
@ -23,6 +32,8 @@ type VideoAttributes = {
|
|||
channelId?: number
|
||||
privacy?: VideoPrivacy
|
||||
fixture?: string
|
||||
thumbnailfile?: string
|
||||
previewfile?: string
|
||||
}
|
||||
|
||||
function getVideoCategories (url: string) {
|
||||
|
@ -228,8 +239,8 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg
|
|||
defaultChannelId = res.body.videoChannels[0].id
|
||||
} catch (e) { /* empty */ }
|
||||
|
||||
// Default attributes
|
||||
let attributes = {
|
||||
// Override default attributes
|
||||
const attributes = Object.assign({
|
||||
name: 'my super video',
|
||||
category: 5,
|
||||
licence: 4,
|
||||
|
@ -241,8 +252,7 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg
|
|||
privacy: VideoPrivacy.PUBLIC,
|
||||
commentsEnabled: true,
|
||||
fixture: 'video_short.webm'
|
||||
}
|
||||
attributes = Object.assign(attributes, videoAttributesArg)
|
||||
}, videoAttributesArg)
|
||||
|
||||
const req = request(url)
|
||||
.post(path)
|
||||
|
@ -267,22 +277,22 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg
|
|||
req.field('licence', attributes.licence.toString())
|
||||
}
|
||||
|
||||
if (attributes.thumbnailfile !== undefined) {
|
||||
req.attach('thumbnailfile', buildAbsoluteFixturePath(attributes.thumbnailfile))
|
||||
}
|
||||
if (attributes.previewfile !== undefined) {
|
||||
req.attach('previewfile', buildAbsoluteFixturePath(attributes.previewfile))
|
||||
}
|
||||
|
||||
for (let i = 0; i < attributes.tags.length; i++) {
|
||||
req.field('tags[' + i + ']', attributes.tags[i])
|
||||
}
|
||||
|
||||
let filePath = ''
|
||||
if (isAbsolute(attributes.fixture)) {
|
||||
filePath = attributes.fixture
|
||||
} else {
|
||||
filePath = join(__dirname, '..', '..', 'api', 'fixtures', attributes.fixture)
|
||||
}
|
||||
|
||||
return req.attach('videofile', filePath)
|
||||
return req.attach('videofile', buildAbsoluteFixturePath(attributes.fixture))
|
||||
.expect(specialStatus)
|
||||
}
|
||||
|
||||
function updateVideo (url: string, accessToken: string, id: number | string, attributes: VideoAttributes, specialStatus = 204) {
|
||||
function updateVideo (url: string, accessToken: string, id: number | string, attributes: VideoAttributes, statusCodeExpected = 204) {
|
||||
const path = '/api/v1/videos/' + id
|
||||
const body = {}
|
||||
|
||||
|
@ -296,12 +306,30 @@ function updateVideo (url: string, accessToken: string, id: number | string, att
|
|||
if (attributes.tags) body['tags'] = attributes.tags
|
||||
if (attributes.privacy) body['privacy'] = attributes.privacy
|
||||
|
||||
return request(url)
|
||||
.put(path)
|
||||
.send(body)
|
||||
.set('Accept', 'application/json')
|
||||
.set('Authorization', 'Bearer ' + accessToken)
|
||||
.expect(specialStatus)
|
||||
// Upload request
|
||||
if (attributes.thumbnailfile || attributes.previewfile) {
|
||||
const attaches: any = {}
|
||||
if (attributes.thumbnailfile) attaches.thumbnailfile = attributes.thumbnailfile
|
||||
if (attributes.previewfile) attaches.previewfile = attributes.previewfile
|
||||
|
||||
return makeUploadRequest({
|
||||
url,
|
||||
method: 'PUT',
|
||||
path,
|
||||
token: accessToken,
|
||||
fields: body,
|
||||
attaches,
|
||||
statusCodeExpected
|
||||
})
|
||||
}
|
||||
|
||||
return makePutBodyRequest({
|
||||
url,
|
||||
path,
|
||||
fields: body,
|
||||
token: accessToken,
|
||||
statusCodeExpected
|
||||
})
|
||||
}
|
||||
|
||||
function rateVideo (url: string, accessToken: string, id: number, rating: string, specialStatus = 204) {
|
||||
|
@ -355,7 +383,9 @@ async function completeVideoCheck (
|
|||
files: {
|
||||
resolution: number
|
||||
size: number
|
||||
}[]
|
||||
}[],
|
||||
thumbnailfile?: string
|
||||
previewfile?: string
|
||||
}
|
||||
) {
|
||||
if (!attributes.likes) attributes.likes = 0
|
||||
|
@ -414,8 +444,15 @@ async function completeVideoCheck (
|
|||
const maxSize = attributeFile.size + ((10 * attributeFile.size) / 100)
|
||||
expect(file.size).to.be.above(minSize).and.below(maxSize)
|
||||
|
||||
const test = await testImage(url, attributes.fixture, videoDetails.thumbnailPath)
|
||||
expect(test).to.equal(true)
|
||||
{
|
||||
const test = await testImage(url, attributes.thumbnailfile || attributes.fixture, videoDetails.thumbnailPath)
|
||||
expect(test).to.equal(true)
|
||||
}
|
||||
|
||||
if (attributes.previewfile) {
|
||||
const test = await testImage(url, attributes.previewfile, videoDetails.previewPath)
|
||||
expect(test).to.equal(true)
|
||||
}
|
||||
|
||||
const torrent = await webtorrentAdd(magnetUri, true)
|
||||
expect(torrent.files).to.be.an('array')
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import * as program from 'commander'
|
||||
import { createWriteStream } from 'fs'
|
||||
import { join } from 'path'
|
||||
import { cursorTo } from 'readline'
|
||||
import * as youtubeDL from 'youtube-dl'
|
||||
import { VideoPrivacy } from '../../shared/models/videos'
|
||||
import { unlinkPromise } from '../helpers/core-utils'
|
||||
|
|
Loading…
Reference in a new issue