-import * as Bluebird from 'bluebird'
+import Bluebird from 'bluebird'
import { remove } from 'fs-extra'
import { maxBy, minBy } from 'lodash'
import { join } from 'path'
import { uuidToShort } from '@server/helpers/uuid'
import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video'
import { LiveManager } from '@server/lib/live/live-manager'
-import { getHLSDirectory, getVideoFilePath } from '@server/lib/video-paths'
+import { removeHLSObjectStorage, removeWebTorrentObjectStorage } from '@server/lib/object-storage'
+import { getHLSDirectory, getHLSRedundancyDirectory } from '@server/lib/paths'
+import { VideoPathManager } from '@server/lib/video-path-manager'
import { getServerActor } from '@server/models/application/application'
import { ModelCache } from '@server/models/model-cache'
-import { AttributesOnly, buildVideoEmbedPath, buildVideoWatchPath } from '@shared/core-utils'
+import { AttributesOnly, buildVideoEmbedPath, buildVideoWatchPath, pick } from '@shared/core-utils'
import { VideoFile } from '@shared/models/videos/video-file.model'
import { ResultList, UserRight, VideoPrivacy, VideoState } from '../../../shared'
import { VideoObject } from '../../../shared/models/activitypub/objects'
-import { Video, VideoDetails, VideoRateType } from '../../../shared/models/videos'
+import { Video, VideoDetails, VideoRateType, VideoStorage } from '../../../shared/models/videos'
import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
import { VideoFilter } from '../../../shared/models/videos/video-query.type'
import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
import { VideoCommentModel } from './video-comment'
import { VideoFileModel } from './video-file'
import { VideoImportModel } from './video-import'
+import { VideoJobInfoModel } from './video-job-info'
import { VideoLiveModel } from './video-live'
import { VideoPlaylistElementModel } from './video-playlist-element'
import { VideoShareModel } from './video-share'
})
VideoCaptions: VideoCaptionModel[]
+ @HasOne(() => VideoJobInfoModel, {
+ foreignKey: {
+ name: 'videoId',
+ allowNull: false
+ },
+ onDelete: 'cascade'
+ })
+ VideoJobInfo: VideoJobInfoModel
+
@BeforeDestroy
static async sendDelete (instance: MVideoAccountLight, options) {
if (!instance.isOwned()) return undefined
const trendingDays = options.sort.endsWith('trending')
? CONFIG.TRENDING.VIDEOS.INTERVAL_DAYS
: undefined
- let trendingAlgorithm
+
+ let trendingAlgorithm: string
if (options.sort.endsWith('hot')) trendingAlgorithm = 'hot'
if (options.sort.endsWith('best')) trendingAlgorithm = 'best'
: serverActor.id
const queryOptions = {
- start: options.start,
- count: options.count,
- sort: options.sort,
+ ...pick(options, [
+ 'start',
+ 'count',
+ 'sort',
+ 'nsfw',
+ 'isLive',
+ 'categoryOneOf',
+ 'licenceOneOf',
+ 'languageOneOf',
+ 'tagsOneOf',
+ 'tagsAllOf',
+ 'filter',
+ 'withFiles',
+ 'accountId',
+ 'videoChannelId',
+ 'videoPlaylistId',
+ 'includeLocalVideos',
+ 'user',
+ 'historyOfUser',
+ 'search'
+ ]),
+
followerActorId,
serverAccountId: serverActor.Account.id,
- nsfw: options.nsfw,
- isLive: options.isLive,
- categoryOneOf: options.categoryOneOf,
- licenceOneOf: options.licenceOneOf,
- languageOneOf: options.languageOneOf,
- tagsOneOf: options.tagsOneOf,
- tagsAllOf: options.tagsAllOf,
- filter: options.filter,
- withFiles: options.withFiles,
- accountId: options.accountId,
- videoChannelId: options.videoChannelId,
- videoPlaylistId: options.videoPlaylistId,
- includeLocalVideos: options.includeLocalVideos,
- user: options.user,
- historyOfUser: options.historyOfUser,
trendingDays,
- trendingAlgorithm,
- search: options.search
+ trendingAlgorithm
}
return VideoModel.getAvailableForApi(queryOptions, options.countVideos)
}
static async searchAndPopulateAccountAndServer (options: {
+ start: number
+ count: number
+ sort: string
includeLocalVideos: boolean
search?: string
host?: string
- start?: number
- count?: number
- sort?: string
startDate?: string // ISO 8601
endDate?: string // ISO 8601
originallyPublishedStartDate?: string
durationMax?: number // seconds
user?: MUserAccountId
filter?: VideoFilter
+ uuids?: string[]
}) {
const serverActor = await getServerActor()
const queryOptions = {
- followerActorId: serverActor.id,
- serverAccountId: serverActor.Account.id,
-
- includeLocalVideos: options.includeLocalVideos,
- nsfw: options.nsfw,
- isLive: options.isLive,
-
- categoryOneOf: options.categoryOneOf,
- licenceOneOf: options.licenceOneOf,
- languageOneOf: options.languageOneOf,
+ ...pick(options, [
+ 'includeLocalVideos',
+ 'nsfw',
+ 'isLive',
+ 'categoryOneOf',
+ 'licenceOneOf',
+ 'languageOneOf',
+ 'tagsOneOf',
+ 'tagsAllOf',
+ 'user',
+ 'filter',
+ 'host',
+ 'start',
+ 'count',
+ 'sort',
+ 'startDate',
+ 'endDate',
+ 'originallyPublishedStartDate',
+ 'originallyPublishedEndDate',
+ 'durationMin',
+ 'durationMax',
+ 'uuids',
+ 'search'
+ ]),
- tagsOneOf: options.tagsOneOf,
- tagsAllOf: options.tagsAllOf,
-
- user: options.user,
- filter: options.filter,
- host: options.host,
-
- start: options.start,
- count: options.count,
- sort: options.sort,
-
- startDate: options.startDate,
- endDate: options.endDate,
-
- originallyPublishedStartDate: options.originallyPublishedStartDate,
- originallyPublishedEndDate: options.originallyPublishedEndDate,
-
- durationMin: options.durationMin,
- durationMax: options.durationMax,
-
- search: options.search
+ followerActorId: serverActor.id,
+ serverAccountId: serverActor.Account.id
}
return VideoModel.getAvailableForApi(queryOptions)
getMaxQualityResolution () {
const file = this.getMaxQualityFile()
const videoOrPlaylist = file.getVideoOrStreamingPlaylist()
- const originalFilePath = getVideoFilePath(videoOrPlaylist, file)
- return getVideoFileResolution(originalFilePath)
+ return VideoPathManager.Instance.makeAvailableVideoFile(videoOrPlaylist, file, originalFilePath => {
+ return getVideoFileResolution(originalFilePath)
+ })
}
getDescriptionAPIPath () {
}
removeFileAndTorrent (videoFile: MVideoFile, isRedundancy = false) {
- const filePath = getVideoFilePath(this, videoFile, isRedundancy)
+ const filePath = isRedundancy
+ ? VideoPathManager.Instance.getFSRedundancyVideoFilePath(this, videoFile)
+ : VideoPathManager.Instance.getFSVideoFileOutputPath(this, videoFile)
const promises: Promise<any>[] = [ remove(filePath) ]
if (!isRedundancy) promises.push(videoFile.removeTorrent())
+ if (videoFile.storage === VideoStorage.OBJECT_STORAGE) {
+ promises.push(removeWebTorrentObjectStorage(videoFile))
+ }
+
return Promise.all(promises)
}
async removeStreamingPlaylistFiles (streamingPlaylist: MStreamingPlaylist, isRedundancy = false) {
- const directoryPath = getHLSDirectory(this, isRedundancy)
+ const directoryPath = isRedundancy
+ ? getHLSRedundancyDirectory(this)
+ : getHLSDirectory(this)
await remove(directoryPath)
await Promise.all(
streamingPlaylistWithFiles.VideoFiles.map(file => file.removeTorrent())
)
+
+ if (streamingPlaylist.storage === VideoStorage.OBJECT_STORAGE) {
+ await removeHLSObjectStorage(streamingPlaylist, this)
+ }
}
}
this.privacy === VideoPrivacy.INTERNAL
}
- async publishIfNeededAndSave (t: Transaction) {
- if (this.state !== VideoState.PUBLISHED) {
- this.state = VideoState.PUBLISHED
- this.publishedAt = new Date()
- await this.save({ transaction: t })
+ async setNewState (newState: VideoState, transaction: Transaction) {
+ if (this.state === newState) throw new Error('Cannot use same state ' + newState)
+
+ this.state = newState
- return true
+ if (this.state === VideoState.PUBLISHED) {
+ this.publishedAt = new Date()
}
- return false
+ await this.save({ transaction })
}
getBandwidthBits (videoFile: MVideoFile) {