1 import { Transaction } from 'sequelize/types'
2 import { sequelizeTypescript } from '@server/initializers/database'
3 import { TagModel } from '@server/models/video/tag'
4 import { VideoModel } from '@server/models/video/video'
5 import { FilteredModelAttributes } from '@server/types'
6 import { MTag, MThumbnail, MVideoTag, MVideoThumbnail, MVideoUUID } from '@server/types/models'
7 import { ThumbnailType, VideoCreate, VideoPrivacy } from '@shared/models'
8 import { federateVideoIfNeeded } from './activitypub/videos'
9 import { Notifier } from './notifier'
10 import { createVideoMiniatureFromExisting } from './thumbnail'
12 function buildLocalVideoFromReq (videoInfo: VideoCreate, channelId: number): FilteredModelAttributes<VideoModel> {
16 category: videoInfo.category,
17 licence: videoInfo.licence,
18 language: videoInfo.language,
19 commentsEnabled: videoInfo.commentsEnabled !== false, // If the value is not "false", the default is "true"
20 downloadEnabled: videoInfo.downloadEnabled !== false,
21 waitTranscoding: videoInfo.waitTranscoding || false,
22 nsfw: videoInfo.nsfw || false,
23 description: videoInfo.description,
24 support: videoInfo.support,
25 privacy: videoInfo.privacy || VideoPrivacy.PRIVATE,
27 originallyPublishedAt: videoInfo.originallyPublishedAt
31 async function buildVideoThumbnailsFromReq (options: {
32 video: MVideoThumbnail
33 files: { [fieldname: string]: Express.Multer.File[] } | Express.Multer.File[]
34 fallback: (type: ThumbnailType) => Promise<MThumbnail>
35 automaticallyGenerated?: boolean
37 const { video, files, fallback, automaticallyGenerated } = options
41 type: ThumbnailType.MINIATURE,
42 fieldName: 'thumbnailfile'
45 type: ThumbnailType.PREVIEW,
46 fieldName: 'previewfile'
49 const fields = files?.[p.fieldName]
52 return createVideoMiniatureFromExisting({
53 inputPath: fields[0].path,
56 automaticallyGenerated: automaticallyGenerated || false
60 return fallback(p.type)
63 return Promise.all(promises)
66 async function setVideoTags (options: {
69 transaction?: Transaction
72 const { video, tags, transaction, defaultValue } = options
73 // Set tags to the video
75 const tagInstances = await TagModel.findOrCreateTags(tags, transaction)
77 await video.$set('Tags', tagInstances, { transaction })
78 video.Tags = tagInstances
80 video.Tags = defaultValue || []
84 async function publishAndFederateIfNeeded (video: MVideoUUID, wasLive = false) {
85 const result = await sequelizeTypescript.transaction(async t => {
86 // Maybe the video changed in database, refresh it
87 const videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t)
88 // Video does not exist anymore
89 if (!videoDatabase) return undefined
91 // We transcoded the video file in another format, now we can publish it
92 const videoPublished = await videoDatabase.publishIfNeededAndSave(t)
94 // If the video was not published, we consider it is a new one for other instances
95 // Live videos are always federated, so it's not a new video
96 await federateVideoIfNeeded(videoDatabase, !wasLive && videoPublished, t)
98 return { videoDatabase, videoPublished }
101 if (result?.videoPublished) {
102 Notifier.Instance.notifyOnNewVideoIfNeeded(result.videoDatabase)
103 Notifier.Instance.notifyOnVideoPublishedAfterTranscoding(result.videoDatabase)
107 // ---------------------------------------------------------------------------
110 buildLocalVideoFromReq,
111 publishAndFederateIfNeeded,
112 buildVideoThumbnailsFromReq,