X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fmodels%2Fvideo%2Fvideo-caption.ts;h=b68a6e99f01a1c9a1f03b6df6f3abe8808d44c74;hb=951b582f52d0694865f020f0e53ccfad2d2d6033;hp=9920dfc7c42816415bbd031033c6dc4d7dfffffd;hpb=40e87e9ecc54e3513fb586928330a7855eb192c6;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/models/video/video-caption.ts b/server/models/video/video-caption.ts index 9920dfc7c..b68a6e99f 100644 --- a/server/models/video/video-caption.ts +++ b/server/models/video/video-caption.ts @@ -1,10 +1,11 @@ -import * as Sequelize from 'sequelize' +import { OrderItem, Transaction } from 'sequelize' import { AllowNull, BeforeDestroy, BelongsTo, Column, CreatedAt, + DataType, ForeignKey, Is, Model, @@ -12,30 +13,34 @@ import { Table, UpdatedAt } from 'sequelize-typescript' -import { throwIfNotValid } from '../utils' +import { buildWhereIdOrUUID, throwIfNotValid } from '../utils' import { VideoModel } from './video' import { isVideoCaptionLanguageValid } from '../../helpers/custom-validators/video-captions' -import { VideoCaption } from '../../../shared/models/videos/video-caption.model' -import { CONFIG, STATIC_PATHS, VIDEO_LANGUAGES } from '../../initializers' +import { VideoCaption } from '../../../shared/models/videos/caption/video-caption.model' +import { CONSTRAINTS_FIELDS, LAZY_STATIC_PATHS, VIDEO_LANGUAGES, WEBSERVER } from '../../initializers/constants' import { join } from 'path' import { logger } from '../../helpers/logger' -import { unlinkPromise } from '../../helpers/core-utils' +import { remove } from 'fs-extra' +import { CONFIG } from '../../initializers/config' +import * as Bluebird from 'bluebird' +import { MVideoAccountLight, MVideoCaptionFormattable, MVideoCaptionVideo } from '@server/types/models' +import { buildRemoteVideoBaseUrl } from '@server/helpers/activitypub' export enum ScopeNames { WITH_VIDEO_UUID_AND_REMOTE = 'WITH_VIDEO_UUID_AND_REMOTE' } -@Scopes({ +@Scopes(() => ({ [ScopeNames.WITH_VIDEO_UUID_AND_REMOTE]: { include: [ { - attributes: [ 'uuid', 'remote' ], - model: () => VideoModel.unscoped(), + attributes: [ 'id', 'uuid', 'remote' ], + model: VideoModel.unscoped(), required: true } ] } -}) +})) @Table({ tableName: 'videoCaption', @@ -61,6 +66,10 @@ export class VideoCaptionModel extends Model { @Column language: string + @AllowNull(true) + @Column(DataType.STRING(CONSTRAINTS_FIELDS.COMMONS.URL.max)) + fileUrl: string + @ForeignKey(() => VideoModel) @Column videoId: number @@ -75,29 +84,30 @@ export class VideoCaptionModel extends Model { @BeforeDestroy static async removeFiles (instance: VideoCaptionModel) { + if (!instance.Video) { + instance.Video = await instance.$get('Video') + } if (instance.isOwned()) { - if (!instance.Video) { - instance.Video = await instance.$get('Video') as VideoModel - } + logger.info('Removing captions %s of video %s.', instance.Video.uuid, instance.language) - logger.debug('Removing captions %s of video %s.', instance.Video.uuid, instance.language) - return instance.removeCaptionFile() + try { + await instance.removeCaptionFile() + } catch (err) { + logger.error('Cannot remove caption file of video %s.', instance.Video.uuid) + } } return undefined } - static loadByVideoIdAndLanguage (videoId: string | number, language: string) { + static loadByVideoIdAndLanguage (videoId: string | number, language: string): Bluebird { const videoInclude = { model: VideoModel.unscoped(), attributes: [ 'id', 'remote', 'uuid' ], - where: { } + where: buildWhereIdOrUUID(videoId) } - if (typeof videoId === 'string') videoInclude.where['uuid'] = videoId - else videoInclude.where['id'] = videoId - const query = { where: { language @@ -110,18 +120,20 @@ export class VideoCaptionModel extends Model { return VideoCaptionModel.findOne(query) } - static insertOrReplaceLanguage (videoId: number, language: string, transaction: Sequelize.Transaction) { + static insertOrReplaceLanguage (videoId: number, language: string, fileUrl: string, transaction: Transaction) { const values = { videoId, - language + language, + fileUrl } - return VideoCaptionModel.upsert(values, { transaction }) + return VideoCaptionModel.upsert(values, { transaction, returning: true }) + .then(([ caption ]) => caption) } - static listVideoCaptions (videoId: number) { + static listVideoCaptions (videoId: number): Bluebird { const query = { - order: [ [ 'language', 'ASC' ] ], + order: [ [ 'language', 'ASC' ] ] as OrderItem[], where: { videoId } @@ -134,7 +146,7 @@ export class VideoCaptionModel extends Model { return VIDEO_LANGUAGES[language] || 'Unknown' } - static deleteAllCaptionsOfRemoteVideo (videoId: number, transaction: Sequelize.Transaction) { + static deleteAllCaptionsOfRemoteVideo (videoId: number, transaction: Transaction) { const query = { where: { videoId @@ -149,7 +161,7 @@ export class VideoCaptionModel extends Model { return this.Video.remote === false } - toFormattedJSON (): VideoCaption { + toFormattedJSON (this: MVideoCaptionFormattable): VideoCaption { return { language: { id: this.language, @@ -159,15 +171,25 @@ export class VideoCaptionModel extends Model { } } - getCaptionStaticPath () { - return join(STATIC_PATHS.VIDEO_CAPTIONS, this.getCaptionName()) + getCaptionStaticPath (this: MVideoCaptionFormattable) { + return join(LAZY_STATIC_PATHS.VIDEO_CAPTIONS, this.getCaptionName()) } - getCaptionName () { + getCaptionName (this: MVideoCaptionFormattable) { return `${this.Video.uuid}-${this.language}.vtt` } - removeCaptionFile () { - return unlinkPromise(CONFIG.STORAGE.CAPTIONS_DIR + this.getCaptionName()) + removeCaptionFile (this: MVideoCaptionFormattable) { + return remove(CONFIG.STORAGE.CAPTIONS_DIR + this.getCaptionName()) + } + + getFileUrl (video: MVideoAccountLight) { + if (!this.Video) this.Video = video as VideoModel + + if (video.isOwned()) return WEBSERVER.URL + this.getCaptionStaticPath() + if (this.fileUrl) return this.fileUrl + + // Fallback if we don't have a file URL + return buildRemoteVideoBaseUrl(video, this.getCaptionStaticPath()) } }