From 1ef65f4c034cc53ab5d55417e52d60e1f7fc1ddb Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 17 Sep 2020 10:00:46 +0200 Subject: Refactor video creation --- server/controllers/api/videos/import.ts | 72 +++++++++++++++--------------- server/controllers/api/videos/index.ts | 78 ++++++++++----------------------- server/controllers/api/videos/live.ts | 40 +++++++---------- 3 files changed, 76 insertions(+), 114 deletions(-) (limited to 'server/controllers/api/videos') diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts index 24a237304..5840cd063 100644 --- a/server/controllers/api/videos/import.ts +++ b/server/controllers/api/videos/import.ts @@ -1,30 +1,10 @@ +import * as Bluebird from 'bluebird' import * as express from 'express' +import { move, readFile } from 'fs-extra' import * as magnetUtil from 'magnet-uri' -import { auditLoggerFactory, getAuditIdFromRes, VideoImportAuditView } from '../../../helpers/audit-logger' -import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoImportAddValidator } from '../../../middlewares' -import { MIMETYPES } from '../../../initializers/constants' -import { getYoutubeDLInfo, YoutubeDLInfo, getYoutubeDLSubs } from '../../../helpers/youtube-dl' -import { createReqFiles } from '../../../helpers/express-utils' -import { logger } from '../../../helpers/logger' -import { VideoImportCreate, VideoImportState, VideoPrivacy, VideoState } from '../../../../shared' -import { VideoModel } from '../../../models/video/video' -import { VideoCaptionModel } from '../../../models/video/video-caption' -import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils' -import { getVideoActivityPubUrl } from '../../../lib/activitypub/url' -import { TagModel } from '../../../models/video/tag' -import { VideoImportModel } from '../../../models/video/video-import' -import { JobQueue } from '../../../lib/job-queue/job-queue' -import { join } from 'path' -import { isArray } from '../../../helpers/custom-validators/misc' -import * as Bluebird from 'bluebird' import * as parseTorrent from 'parse-torrent' -import { getSecureTorrentName } from '../../../helpers/utils' -import { move, readFile } from 'fs-extra' -import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' -import { CONFIG } from '../../../initializers/config' -import { sequelizeTypescript } from '../../../initializers/database' -import { createVideoMiniatureFromExisting, createVideoMiniatureFromUrl } from '../../../lib/thumbnail' -import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' +import { join } from 'path' +import { setVideoTags } from '@server/lib/video' import { MChannelAccountDefault, MThumbnail, @@ -36,6 +16,26 @@ import { MVideoWithBlacklistLight } from '@server/types/models' import { MVideoImport, MVideoImportFormattable } from '@server/types/models/video/video-import' +import { VideoImportCreate, VideoImportState, VideoPrivacy, VideoState } from '../../../../shared' +import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' +import { auditLoggerFactory, getAuditIdFromRes, VideoImportAuditView } from '../../../helpers/audit-logger' +import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils' +import { isArray } from '../../../helpers/custom-validators/misc' +import { createReqFiles } from '../../../helpers/express-utils' +import { logger } from '../../../helpers/logger' +import { getSecureTorrentName } from '../../../helpers/utils' +import { getYoutubeDLInfo, getYoutubeDLSubs, YoutubeDLInfo } from '../../../helpers/youtube-dl' +import { CONFIG } from '../../../initializers/config' +import { MIMETYPES } from '../../../initializers/constants' +import { sequelizeTypescript } from '../../../initializers/database' +import { getVideoActivityPubUrl } from '../../../lib/activitypub/url' +import { JobQueue } from '../../../lib/job-queue/job-queue' +import { createVideoMiniatureFromExisting, createVideoMiniatureFromUrl } from '../../../lib/thumbnail' +import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' +import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoImportAddValidator } from '../../../middlewares' +import { VideoModel } from '../../../models/video/video' +import { VideoCaptionModel } from '../../../models/video/video-caption' +import { VideoImportModel } from '../../../models/video/video-import' const auditLogger = auditLoggerFactory('video-imports') const videoImportsRouter = express.Router() @@ -260,7 +260,12 @@ async function processThumbnail (req: express.Request, video: VideoModel) { if (thumbnailField) { const thumbnailPhysicalFile = thumbnailField[0] - return createVideoMiniatureFromExisting(thumbnailPhysicalFile.path, video, ThumbnailType.MINIATURE, false) + return createVideoMiniatureFromExisting({ + inputPath: thumbnailPhysicalFile.path, + video, + type: ThumbnailType.MINIATURE, + automaticallyGenerated: false + }) } return undefined @@ -271,7 +276,12 @@ async function processPreview (req: express.Request, video: VideoModel) { if (previewField) { const previewPhysicalFile = previewField[0] - return createVideoMiniatureFromExisting(previewPhysicalFile.path, video, ThumbnailType.PREVIEW, false) + return createVideoMiniatureFromExisting({ + inputPath: previewPhysicalFile.path, + video, + type: ThumbnailType.PREVIEW, + automaticallyGenerated: false + }) } return undefined @@ -325,15 +335,7 @@ function insertIntoDB (parameters: { transaction: t }) - // Set tags to the video - if (tags) { - const tagInstances = await TagModel.findOrCreateTags(tags, t) - - await videoCreated.$set('Tags', tagInstances, sequelizeOptions) - videoCreated.Tags = tagInstances - } else { - videoCreated.Tags = [] - } + await setVideoTags({ video: videoCreated, tags, transaction: t }) // Create video import object in database const videoImport = await VideoImportModel.create( diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 94f0361ee..1539afc35 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts @@ -6,11 +6,11 @@ import { addOptimizeOrMergeAudioJob } from '@server/helpers/video' import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' import { changeVideoChannelShare } from '@server/lib/activitypub/share' import { getVideoActivityPubUrl } from '@server/lib/activitypub/url' +import { buildLocalVideoFromReq, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video' import { getVideoFilePath } from '@server/lib/video-paths' import { getServerActor } from '@server/models/application/application' import { MVideoDetails, MVideoFullLight } from '@server/types/models' -import { VideoCreate, VideoPrivacy, VideoState, VideoUpdate } from '../../../../shared' -import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' +import { VideoCreate, VideoState, VideoUpdate } from '../../../../shared' import { VideoFilter } from '../../../../shared/models/videos/video-query.type' import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' import { resetSequelizeInstance } from '../../../helpers/database-utils' @@ -34,7 +34,7 @@ import { JobQueue } from '../../../lib/job-queue' import { Notifier } from '../../../lib/notifier' import { Hooks } from '../../../lib/plugins/hooks' import { Redis } from '../../../lib/redis' -import { createVideoMiniatureFromExisting, generateVideoMiniature } from '../../../lib/thumbnail' +import { generateVideoMiniature } from '../../../lib/thumbnail' import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' import { asyncMiddleware, @@ -186,25 +186,9 @@ async function addVideo (req: express.Request, res: express.Response) { const videoPhysicalFile = req.files['videofile'][0] const videoInfo: VideoCreate = req.body - // Prepare data so we don't block the transaction - const videoData = { - name: videoInfo.name, - remote: false, - category: videoInfo.category, - licence: videoInfo.licence, - language: videoInfo.language, - commentsEnabled: videoInfo.commentsEnabled !== false, // If the value is not "false", the default is "true" - downloadEnabled: videoInfo.downloadEnabled !== false, - waitTranscoding: videoInfo.waitTranscoding || false, - state: CONFIG.TRANSCODING.ENABLED ? VideoState.TO_TRANSCODE : VideoState.PUBLISHED, - nsfw: videoInfo.nsfw || false, - description: videoInfo.description, - support: videoInfo.support, - privacy: videoInfo.privacy || VideoPrivacy.PRIVATE, - duration: videoPhysicalFile['duration'], // duration was added by a previous middleware - channelId: res.locals.videoChannel.id, - originallyPublishedAt: videoInfo.originallyPublishedAt - } + const videoData = buildLocalVideoFromReq(videoInfo, res.locals.videoChannel.id) + videoData.state = CONFIG.TRANSCODING.ENABLED ? VideoState.TO_TRANSCODE : VideoState.PUBLISHED + videoData.duration = videoPhysicalFile['duration'] // duration was added by a previous middleware const video = new VideoModel(videoData) as MVideoDetails video.url = getVideoActivityPubUrl(video) // We use the UUID, so set the URL after building the object @@ -230,17 +214,11 @@ async function addVideo (req: express.Request, res: express.Response) { videoPhysicalFile.filename = getVideoFilePath(video, videoFile) videoPhysicalFile.path = destination - // Process thumbnail or create it from the video - const thumbnailField = req.files['thumbnailfile'] - const thumbnailModel = thumbnailField - ? await createVideoMiniatureFromExisting(thumbnailField[0].path, video, ThumbnailType.MINIATURE, false) - : await generateVideoMiniature(video, videoFile, ThumbnailType.MINIATURE) - - // Process preview or create it from the video - const previewField = req.files['previewfile'] - const previewModel = previewField - ? await createVideoMiniatureFromExisting(previewField[0].path, video, ThumbnailType.PREVIEW, false) - : await generateVideoMiniature(video, videoFile, ThumbnailType.PREVIEW) + const [ thumbnailModel, previewModel ] = await buildVideoThumbnailsFromReq({ + video, + files: req.files, + fallback: type => generateVideoMiniature(video, videoFile, type) + }) // Create the torrent file await createTorrentAndSetInfoHash(video, videoFile) @@ -261,13 +239,7 @@ async function addVideo (req: express.Request, res: express.Response) { video.VideoFiles = [ videoFile ] - // Create tags - if (videoInfo.tags !== undefined) { - const tagInstances = await TagModel.findOrCreateTags(videoInfo.tags, t) - - await video.$set('Tags', tagInstances, sequelizeOptions) - video.Tags = tagInstances - } + await setVideoTags({ video, tags: videoInfo.tags, transaction: t }) // Schedule an update in the future? if (videoInfo.scheduleUpdate) { @@ -318,14 +290,12 @@ async function updateVideo (req: express.Request, res: express.Response) { const wasConfidentialVideo = videoInstance.isConfidential() const hadPrivacyForFederation = videoInstance.hasPrivacyForFederation() - // Process thumbnail or create it from the video - const thumbnailModel = req.files?.['thumbnailfile'] - ? await createVideoMiniatureFromExisting(req.files['thumbnailfile'][0].path, videoInstance, ThumbnailType.MINIATURE, false) - : undefined - - const previewModel = req.files?.['previewfile'] - ? await createVideoMiniatureFromExisting(req.files['previewfile'][0].path, videoInstance, ThumbnailType.PREVIEW, false) - : undefined + const [ thumbnailModel, previewModel ] = await buildVideoThumbnailsFromReq({ + video: videoInstance, + files: req.files, + fallback: () => Promise.resolve(undefined), + automaticallyGenerated: false + }) try { const videoInstanceUpdated = await sequelizeTypescript.transaction(async t => { @@ -366,12 +336,12 @@ async function updateVideo (req: express.Request, res: express.Response) { if (previewModel) await videoInstanceUpdated.addAndSaveThumbnail(previewModel, t) // Video tags update? - if (videoInfoToUpdate.tags !== undefined) { - const tagInstances = await TagModel.findOrCreateTags(videoInfoToUpdate.tags, t) - - await videoInstanceUpdated.$set('Tags', tagInstances, sequelizeOptions) - videoInstanceUpdated.Tags = tagInstances - } + await setVideoTags({ + video: videoInstanceUpdated, + tags: videoInfoToUpdate.tags, + transaction: t, + defaultValue: videoInstanceUpdated.Tags + }) // Video channel update? if (res.locals.videoChannel && videoInstanceUpdated.channelId !== res.locals.videoChannel.id) { diff --git a/server/controllers/api/videos/live.ts b/server/controllers/api/videos/live.ts index d08ef9869..97b135f96 100644 --- a/server/controllers/api/videos/live.ts +++ b/server/controllers/api/videos/live.ts @@ -4,18 +4,16 @@ import { createReqFiles } from '@server/helpers/express-utils' import { CONFIG } from '@server/initializers/config' import { ASSETS_PATH, MIMETYPES } from '@server/initializers/constants' import { getVideoActivityPubUrl } from '@server/lib/activitypub/url' +import { buildLocalVideoFromReq, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video' import { videoLiveAddValidator, videoLiveGetValidator } from '@server/middlewares/validators/videos/video-live' import { VideoLiveModel } from '@server/models/video/video-live' import { MVideoDetails, MVideoFullLight } from '@server/types/models' -import { VideoCreate, VideoPrivacy, VideoState } from '../../../../shared' -import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' +import { VideoCreate, VideoState } from '../../../../shared' import { logger } from '../../../helpers/logger' import { sequelizeTypescript } from '../../../initializers/database' import { createVideoMiniatureFromExisting } from '../../../lib/thumbnail' import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate } from '../../../middlewares' -import { TagModel } from '../../../models/video/tag' import { VideoModel } from '../../../models/video/video' -import { buildLocalVideoFromCreate } from '@server/lib/video' const liveRouter = express.Router() @@ -59,26 +57,24 @@ async function addLiveVideo (req: express.Request, res: express.Response) { const videoInfo: VideoCreate = req.body // Prepare data so we don't block the transaction - const videoData = buildLocalVideoFromCreate(videoInfo, res.locals.videoChannel.id) + const videoData = buildLocalVideoFromReq(videoInfo, res.locals.videoChannel.id) videoData.isLive = true - - const videoLive = new VideoLiveModel() - videoLive.streamKey = uuidv4() + videoData.state = VideoState.WAITING_FOR_LIVE + videoData.duration = 0 const video = new VideoModel(videoData) as MVideoDetails video.url = getVideoActivityPubUrl(video) // We use the UUID, so set the URL after building the object - // Process thumbnail or create it from the video - const thumbnailField = req.files ? req.files['thumbnailfile'] : null - const thumbnailModel = thumbnailField - ? await createVideoMiniatureFromExisting(thumbnailField[0].path, video, ThumbnailType.MINIATURE, false) - : await createVideoMiniatureFromExisting(ASSETS_PATH.DEFAULT_LIVE_BACKGROUND, video, ThumbnailType.MINIATURE, true) + const videoLive = new VideoLiveModel() + videoLive.streamKey = uuidv4() - // Process preview or create it from the video - const previewField = req.files ? req.files['previewfile'] : null - const previewModel = previewField - ? await createVideoMiniatureFromExisting(previewField[0].path, video, ThumbnailType.PREVIEW, false) - : await createVideoMiniatureFromExisting(ASSETS_PATH.DEFAULT_LIVE_BACKGROUND, video, ThumbnailType.PREVIEW, true) + const [ thumbnailModel, previewModel ] = await buildVideoThumbnailsFromReq({ + video, + files: req.files, + fallback: type => { + return createVideoMiniatureFromExisting({ inputPath: ASSETS_PATH.DEFAULT_LIVE_BACKGROUND, video, type, automaticallyGenerated: true }) + } + }) const { videoCreated } = await sequelizeTypescript.transaction(async t => { const sequelizeOptions = { transaction: t } @@ -94,13 +90,7 @@ async function addLiveVideo (req: express.Request, res: express.Response) { videoLive.videoId = videoCreated.id await videoLive.save(sequelizeOptions) - // Create tags - if (videoInfo.tags !== undefined) { - const tagInstances = await TagModel.findOrCreateTags(videoInfo.tags, t) - - await video.$set('Tags', tagInstances, sequelizeOptions) - video.Tags = tagInstances - } + await setVideoTags({ video, tags: videoInfo.tags, transaction: t }) logger.info('Video live %s with uuid %s created.', videoInfo.name, videoCreated.uuid) -- cgit v1.2.3