CONSTRAINTS_FIELDS,
HLS_REDUNDANCY_DIRECTORY,
HLS_STREAMING_PLAYLIST_DIRECTORY,
+ LAZY_STATIC_PATHS,
REMOTE_SCHEME,
STATIC_DOWNLOAD_PATHS,
STATIC_PATHS,
videoModelToFormattedJSON
} from './video-format-utils'
import { UserVideoHistoryModel } from '../account/user-video-history'
-import { UserModel } from '../account/user'
import { VideoImportModel } from './video-import'
import { VideoStreamingPlaylistModel } from './video-streaming-playlist'
import { VideoPlaylistElementModel } from './video-playlist-element'
import { ThumbnailModel } from './thumbnail'
import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
import { createTorrentPromise } from '../../helpers/webtorrent'
+import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
+import {
+ MChannel,
+ MChannelAccountDefault,
+ MChannelId,
+ MUserAccountId,
+ MUserId,
+ MVideoAccountLight,
+ MVideoAccountLightBlacklistAllFiles,
+ MVideoDetails,
+ MVideoForUser,
+ MVideoFullLight,
+ MVideoIdThumbnail,
+ MVideoThumbnail,
+ MVideoWithAllFiles, MVideoWithFile,
+ MVideoWithRights,
+ MVideoFormattable
+} from '../../typings/models'
+import { MVideoFile, MVideoFileRedundanciesOpt } from '../../typings/models/video/video-file'
+import { MThumbnail } from '../../typings/models/video/thumbnail'
// FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation
const indexes: (ModelIndexesOptions & { where?: WhereOptions })[] = [
videoPlaylistId?: number
trendingDays?: number
- user?: UserModel,
- historyOfUser?: UserModel
+ user?: MUserAccountId
+ historyOfUser?: MUserId
baseWhere?: WhereOptions[]
}
[ ScopeNames.WITH_BLACKLISTED ]: {
include: [
{
- attributes: [ 'id', 'reason' ],
+ attributes: [ 'id', 'reason', 'unfederated' ],
model: VideoBlacklistModel,
required: false
}
VideoCaptions: VideoCaptionModel[]
@BeforeDestroy
- static async sendDelete (instance: VideoModel, options) {
+ static async sendDelete (instance: MVideoAccountLight, options) {
if (instance.isOwned()) {
if (!instance.VideoChannel) {
instance.VideoChannel = await instance.$get('VideoChannel', {
include: [
- {
- model: AccountModel,
- include: [ ActorModel ]
- }
+ ActorModel,
+ AccountModel
],
transaction: options.transaction
- }) as VideoChannelModel
+ }) as MChannelAccountDefault
}
return sendDeleteVideo(instance, options.transaction)
return undefined
}
- static listLocal () {
+ static listLocal (): Bluebird<MVideoWithAllFiles[]> {
const query = {
where: {
remote: false
})
}
- static listUserVideosForApi (accountId: number, start: number, count: number, sort: string, withFiles = false) {
+ static listUserVideosForApi (accountId: number, start: number, count: number, sort: string) {
function buildBaseQuery (): FindOptions {
return {
offset: start,
ScopeNames.WITH_THUMBNAILS
]
- if (withFiles === true) {
- findQuery.include.push({
- model: VideoFileModel.unscoped(),
- required: true
- })
- }
-
return Promise.all([
VideoModel.count(countQuery),
- VideoModel.scope(findScopes).findAll(findQuery)
+ VideoModel.scope(findScopes).findAll<MVideoForUser>(findQuery)
]).then(([ count, rows ]) => {
return {
data: rows,
followerActorId?: number
videoPlaylistId?: number,
trendingDays?: number,
- user?: UserModel,
- historyOfUser?: UserModel
+ user?: MUserAccountId,
+ historyOfUser?: MUserId
}, countVideos = true) {
if (options.filter && options.filter === 'all-local' && !options.user.hasRight(UserRight.SEE_ALL_VIDEOS)) {
throw new Error('Try to filter all-local but no user has not the see all videos right')
tagsAllOf?: string[]
durationMin?: number // seconds
durationMax?: number // seconds
- user?: UserModel,
+ user?: MUserAccountId,
filter?: VideoFilter
}) {
const whereAnd = []
return VideoModel.getAvailableForApi(query, queryOptions)
}
- static load (id: number | string, t?: Transaction) {
+ static load (id: number | string, t?: Transaction): Bluebird<MVideoThumbnail> {
const where = buildWhereIdOrUUID(id)
const options = {
where,
return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options)
}
- static loadWithRights (id: number | string, t?: Transaction) {
+ static loadWithRights (id: number | string, t?: Transaction): Bluebird<MVideoWithRights> {
const where = buildWhereIdOrUUID(id)
const options = {
where,
]).findOne(options)
}
- static loadOnlyId (id: number | string, t?: Transaction) {
+ static loadOnlyId (id: number | string, t?: Transaction): Bluebird<MVideoIdThumbnail> {
const where = buildWhereIdOrUUID(id)
const options = {
return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options)
}
- static loadWithFiles (id: number, t?: Transaction, logging?: boolean) {
+ static loadWithFiles (id: number | string, t?: Transaction, logging?: boolean): Bluebird<MVideoWithAllFiles> {
+ const where = buildWhereIdOrUUID(id)
+
+ const query = {
+ where,
+ transaction: t,
+ logging
+ }
+
return VideoModel.scope([
ScopeNames.WITH_FILES,
ScopeNames.WITH_STREAMING_PLAYLISTS,
ScopeNames.WITH_THUMBNAILS
- ]).findByPk(id, { transaction: t, logging })
+ ]).findOne(query)
}
- static loadByUUIDWithFile (uuid: string) {
+ static loadByUUID (uuid: string): Bluebird<MVideoThumbnail> {
const options = {
where: {
uuid
return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options)
}
- static loadByUrl (url: string, transaction?: Transaction) {
+ static loadByUrl (url: string, transaction?: Transaction): Bluebird<MVideoThumbnail> {
const query: FindOptions = {
where: {
url
return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(query)
}
- static loadByUrlAndPopulateAccount (url: string, transaction?: Transaction) {
+ static loadByUrlAndPopulateAccount (url: string, transaction?: Transaction): Bluebird<MVideoAccountLightBlacklistAllFiles> {
const query: FindOptions = {
where: {
url
ScopeNames.WITH_ACCOUNT_DETAILS,
ScopeNames.WITH_FILES,
ScopeNames.WITH_STREAMING_PLAYLISTS,
- ScopeNames.WITH_THUMBNAILS
+ ScopeNames.WITH_THUMBNAILS,
+ ScopeNames.WITH_BLACKLISTED
]).findOne(query)
}
- static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Transaction, userId?: number) {
+ static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Transaction, userId?: number): Bluebird<MVideoFullLight> {
const where = buildWhereIdOrUUID(id)
const options = {
id: number | string,
t?: Transaction,
userId?: number
- }) {
+ }): Bluebird<MVideoDetails> {
const { id, t, userId } = parameters
const where = buildWhereIdOrUUID(id)
.then(results => results.length === 1)
}
- static bulkUpdateSupportField (videoChannel: VideoChannelModel, t: Transaction) {
+ static bulkUpdateSupportField (videoChannel: MChannel, t: Transaction) {
const options = {
where: {
channelId: videoChannel.id
return VideoModel.update({ support: videoChannel.support }, options)
}
- static getAllIdsFromChannel (videoChannel: VideoChannelModel) {
+ static getAllIdsFromChannel (videoChannel: MChannelId): Bluebird<number[]> {
const query = {
attributes: [ 'id' ],
where: {
this.VideoChannel.Account.isBlocked()
}
- getOriginalFile () {
+ getOriginalFile <T extends MVideoWithFile> (this: T) {
if (Array.isArray(this.VideoFiles) === false) return undefined
// The original file is the file that have the higher resolution
return maxBy(this.VideoFiles, file => file.resolution)
}
- async addAndSaveThumbnail (thumbnail: ThumbnailModel, transaction: Transaction) {
+ getFile <T extends MVideoWithFile> (this: T, resolution: number) {
+ if (Array.isArray(this.VideoFiles) === false) return undefined
+
+ return this.VideoFiles.find(f => f.resolution === resolution)
+ }
+
+ async addAndSaveThumbnail (thumbnail: MThumbnail, transaction: Transaction) {
thumbnail.videoId = this.id
const savedThumbnail = await thumbnail.save({ transaction })
this.Thumbnails.push(savedThumbnail)
}
- getVideoFilename (videoFile: VideoFileModel) {
+ getVideoFilename (videoFile: MVideoFile) {
return this.uuid + '-' + videoFile.resolution + videoFile.extname
}
return this.Thumbnails.find(t => t.type === ThumbnailType.PREVIEW)
}
- getTorrentFileName (videoFile: VideoFileModel) {
+ getTorrentFileName (videoFile: MVideoFile) {
const extension = '.torrent'
return this.uuid + '-' + videoFile.resolution + extension
}
return this.remote === false
}
- getTorrentFilePath (videoFile: VideoFileModel) {
+ getTorrentFilePath (videoFile: MVideoFile) {
return join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile))
}
- getVideoFilePath (videoFile: VideoFileModel) {
+ getVideoFilePath (videoFile: MVideoFile) {
return join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile))
}
- async createTorrentAndSetInfoHash (videoFile: VideoFileModel) {
+ async createTorrentAndSetInfoHash (videoFile: MVideoFile) {
const options = {
// Keep the extname, it's used by the client to stream the file inside a web browser
name: `${this.name} ${videoFile.resolution}p${videoFile.extname}`,
if (!preview) return null
// We use a local cache, so specify our cache endpoint instead of potential remote URL
- return join(STATIC_PATHS.PREVIEWS, preview.filename)
+ return join(LAZY_STATIC_PATHS.PREVIEWS, preview.filename)
}
- toFormattedJSON (options?: VideoFormattingJSONOptions): Video {
+ toFormattedJSON <T extends MVideoFormattable> (this: T, options?: VideoFormattingJSONOptions): Video {
return videoModelToFormattedJSON(this, options)
}
return `/api/${API_VERSION}/videos/${this.uuid}/description`
}
- removeFile (videoFile: VideoFileModel, isRedundancy = false) {
+ getHLSPlaylist () {
+ if (!this.VideoStreamingPlaylists) return undefined
+
+ return this.VideoStreamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS)
+ }
+
+ removeFile (videoFile: MVideoFile, isRedundancy = false) {
const baseDir = isRedundancy ? CONFIG.STORAGE.REDUNDANCY_DIR : CONFIG.STORAGE.VIDEOS_DIR
const filePath = join(baseDir, this.getVideoFilename(videoFile))
.catch(err => logger.warn('Cannot delete file %s.', filePath, { err }))
}
- removeTorrent (videoFile: VideoFileModel) {
+ removeTorrent (videoFile: MVideoFile) {
const torrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile))
return remove(torrentPath)
.catch(err => logger.warn('Cannot delete torrent %s.', torrentPath, { err }))
return { baseUrlHttp, baseUrlWs }
}
- generateMagnetUri (videoFile: VideoFileModel, baseUrlHttp: string, baseUrlWs: string) {
+ generateMagnetUri (videoFile: MVideoFileRedundanciesOpt, baseUrlHttp: string, baseUrlWs: string) {
const xs = this.getTorrentUrl(videoFile, baseUrlHttp)
const announce = this.getTrackerUrls(baseUrlHttp, baseUrlWs)
let urlList = [ this.getVideoFileUrl(videoFile, baseUrlHttp) ]
return [ baseUrlWs + '/tracker/socket', baseUrlHttp + '/tracker/announce' ]
}
- getTorrentUrl (videoFile: VideoFileModel, baseUrlHttp: string) {
+ getTorrentUrl (videoFile: MVideoFile, baseUrlHttp: string) {
return baseUrlHttp + STATIC_PATHS.TORRENTS + this.getTorrentFileName(videoFile)
}
- getTorrentDownloadUrl (videoFile: VideoFileModel, baseUrlHttp: string) {
+ getTorrentDownloadUrl (videoFile: MVideoFile, baseUrlHttp: string) {
return baseUrlHttp + STATIC_DOWNLOAD_PATHS.TORRENTS + this.getTorrentFileName(videoFile)
}
- getVideoFileUrl (videoFile: VideoFileModel, baseUrlHttp: string) {
+ getVideoFileUrl (videoFile: MVideoFile, baseUrlHttp: string) {
return baseUrlHttp + STATIC_PATHS.WEBSEED + this.getVideoFilename(videoFile)
}
- getVideoRedundancyUrl (videoFile: VideoFileModel, baseUrlHttp: string) {
+ getVideoRedundancyUrl (videoFile: MVideoFile, baseUrlHttp: string) {
return baseUrlHttp + STATIC_PATHS.REDUNDANCY + this.getVideoFilename(videoFile)
}
- getVideoFileDownloadUrl (videoFile: VideoFileModel, baseUrlHttp: string) {
+ getVideoFileDownloadUrl (videoFile: MVideoFile, baseUrlHttp: string) {
return baseUrlHttp + STATIC_DOWNLOAD_PATHS.VIDEOS + this.getVideoFilename(videoFile)
}
- getBandwidthBits (videoFile: VideoFileModel) {
+ getBandwidthBits (videoFile: MVideoFile) {
return Math.ceil((videoFile.size * 8) / this.duration)
}
}