From 90a8bd305de4153ec21137a73ff482dcc2e3e19b Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 16 Feb 2021 16:25:53 +0100 Subject: Dissociate video file names and video uuid --- server/models/video/thumbnail.ts | 4 +- server/models/video/video-caption.ts | 6 +- server/models/video/video-file.ts | 181 ++++++++++++++++++++---- server/models/video/video-format-utils.ts | 64 +++++---- server/models/video/video-query-builder.ts | 8 ++ server/models/video/video-streaming-playlist.ts | 58 ++------ server/models/video/video.ts | 60 ++------ 7 files changed, 225 insertions(+), 156 deletions(-) (limited to 'server/models/video') diff --git a/server/models/video/thumbnail.ts b/server/models/video/thumbnail.ts index 4185ec5f2..9533c8d19 100644 --- a/server/models/video/thumbnail.ts +++ b/server/models/video/thumbnail.ts @@ -17,7 +17,7 @@ import { } from 'sequelize-typescript' import { buildRemoteVideoBaseUrl } from '@server/helpers/activitypub' import { afterCommitIfTransaction } from '@server/helpers/database-utils' -import { MThumbnail, MThumbnailVideo, MVideoAccountLight } from '@server/types/models' +import { MThumbnail, MThumbnailVideo, MVideoWithHost } from '@server/types/models' import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' import { logger } from '../../helpers/logger' import { CONFIG } from '../../initializers/config' @@ -164,7 +164,7 @@ export class ThumbnailModel extends Model { return join(directory, filename) } - getFileUrl (video: MVideoAccountLight) { + getFileUrl (video: MVideoWithHost) { const staticPath = ThumbnailModel.types[this.type].staticPath + this.filename if (video.isOwned()) return WEBSERVER.URL + staticPath diff --git a/server/models/video/video-caption.ts b/server/models/video/video-caption.ts index a1553ea15..71b067335 100644 --- a/server/models/video/video-caption.ts +++ b/server/models/video/video-caption.ts @@ -15,8 +15,9 @@ import { Table, UpdatedAt } from 'sequelize-typescript' +import { v4 as uuidv4 } from 'uuid' import { buildRemoteVideoBaseUrl } from '@server/helpers/activitypub' -import { MVideoAccountLight, MVideoCaption, MVideoCaptionFormattable, MVideoCaptionVideo } from '@server/types/models' +import { MVideoCaption, MVideoCaptionFormattable, MVideoCaptionVideo, MVideoWithHost } from '@server/types/models' import { VideoCaption } from '../../../shared/models/videos/caption/video-caption.model' import { isVideoCaptionLanguageValid } from '../../helpers/custom-validators/video-captions' import { logger } from '../../helpers/logger' @@ -24,7 +25,6 @@ import { CONFIG } from '../../initializers/config' import { CONSTRAINTS_FIELDS, LAZY_STATIC_PATHS, VIDEO_LANGUAGES, WEBSERVER } from '../../initializers/constants' import { buildWhereIdOrUUID, throwIfNotValid } from '../utils' import { VideoModel } from './video' -import { v4 as uuidv4 } from 'uuid' export enum ScopeNames { WITH_VIDEO_UUID_AND_REMOTE = 'WITH_VIDEO_UUID_AND_REMOTE' @@ -204,7 +204,7 @@ export class VideoCaptionModel extends Model { return remove(CONFIG.STORAGE.CAPTIONS_DIR + this.filename) } - getFileUrl (video: MVideoAccountLight) { + getFileUrl (video: MVideoWithHost) { if (!this.Video) this.Video = video as VideoModel if (video.isOwned()) return WEBSERVER.URL + this.getCaptionStaticPath() diff --git a/server/models/video/video-file.ts b/server/models/video/video-file.ts index 48b337c68..57807cbfd 100644 --- a/server/models/video/video-file.ts +++ b/server/models/video/video-file.ts @@ -1,3 +1,7 @@ +import { remove } from 'fs-extra' +import * as memoizee from 'memoizee' +import { join } from 'path' +import { FindOptions, Op, QueryTypes, Transaction } from 'sequelize' import { AllowNull, BelongsTo, @@ -5,15 +9,22 @@ import { CreatedAt, DataType, Default, + DefaultScope, ForeignKey, HasMany, Is, Model, - Table, - UpdatedAt, Scopes, - DefaultScope + Table, + UpdatedAt } from 'sequelize-typescript' +import { Where } from 'sequelize/types/lib/utils' +import validator from 'validator' +import { buildRemoteVideoBaseUrl } from '@server/helpers/activitypub' +import { logger } from '@server/helpers/logger' +import { extractVideo } from '@server/helpers/video' +import { getTorrentFilePath } from '@server/lib/video-paths' +import { MStreamingPlaylistVideo, MVideo, MVideoWithHost } from '@server/types/models' import { isVideoFileExtnameValid, isVideoFileInfoHashValid, @@ -21,20 +32,25 @@ import { isVideoFileSizeValid, isVideoFPSResolutionValid } from '../../helpers/custom-validators/videos' +import { + LAZY_STATIC_PATHS, + MEMOIZE_LENGTH, + MEMOIZE_TTL, + MIMETYPES, + STATIC_DOWNLOAD_PATHS, + STATIC_PATHS, + WEBSERVER +} from '../../initializers/constants' +import { MVideoFile, MVideoFileStreamingPlaylistVideo, MVideoFileVideo } from '../../types/models/video/video-file' +import { VideoRedundancyModel } from '../redundancy/video-redundancy' import { parseAggregateResult, throwIfNotValid } from '../utils' import { VideoModel } from './video' -import { VideoRedundancyModel } from '../redundancy/video-redundancy' import { VideoStreamingPlaylistModel } from './video-streaming-playlist' -import { FindOptions, Op, QueryTypes, Transaction } from 'sequelize' -import { MIMETYPES, MEMOIZE_LENGTH, MEMOIZE_TTL } from '../../initializers/constants' -import { MVideoFile, MVideoFileStreamingPlaylistVideo, MVideoFileVideo } from '../../types/models/video/video-file' -import { MStreamingPlaylistVideo, MVideo } from '@server/types/models' -import * as memoizee from 'memoizee' -import validator from 'validator' export enum ScopeNames { WITH_VIDEO = 'WITH_VIDEO', - WITH_METADATA = 'WITH_METADATA' + WITH_METADATA = 'WITH_METADATA', + WITH_VIDEO_OR_PLAYLIST = 'WITH_VIDEO_OR_PLAYLIST' } @DefaultScope(() => ({ @@ -51,6 +67,28 @@ export enum ScopeNames { } ] }, + [ScopeNames.WITH_VIDEO_OR_PLAYLIST]: (options: { whereVideo?: Where } = {}) => { + return { + include: [ + { + model: VideoModel.unscoped(), + required: false, + where: options.whereVideo + }, + { + model: VideoStreamingPlaylistModel.unscoped(), + required: false, + include: [ + { + model: VideoModel.unscoped(), + required: true, + where: options.whereVideo + } + ] + } + ] + } + }, [ScopeNames.WITH_METADATA]: { attributes: { include: [ 'metadata' ] @@ -81,6 +119,16 @@ export enum ScopeNames { fields: [ 'infoHash' ] }, + { + fields: [ 'torrentFilename' ], + unique: true + }, + + { + fields: [ 'filename' ], + unique: true + }, + { fields: [ 'videoId', 'resolution', 'fps' ], unique: true, @@ -142,6 +190,24 @@ export class VideoFileModel extends Model { @Column metadataUrl: string + @AllowNull(true) + @Column + fileUrl: string + + // Could be null for live files + @AllowNull(true) + @Column + filename: string + + @AllowNull(true) + @Column + torrentUrl: string + + // Could be null for live files + @AllowNull(true) + @Column + torrentFilename: string + @ForeignKey(() => VideoModel) @Column videoId: number @@ -199,6 +265,16 @@ export class VideoFileModel extends Model { return !!videoFile } + static loadWithVideoOrPlaylistByTorrentFilename (filename: string) { + const query = { + where: { + torrentFilename: filename + } + } + + return VideoFileModel.scope(ScopeNames.WITH_VIDEO_OR_PLAYLIST).findOne(query) + } + static loadWithMetadata (id: number) { return VideoFileModel.scope(ScopeNames.WITH_METADATA).findByPk(id) } @@ -215,28 +291,11 @@ export class VideoFileModel extends Model { const options = { where: { id - }, - include: [ - { - model: VideoModel.unscoped(), - required: false, - where: whereVideo - }, - { - model: VideoStreamingPlaylistModel.unscoped(), - required: false, - include: [ - { - model: VideoModel.unscoped(), - required: true, - where: whereVideo - } - ] - } - ] + } } - return VideoFileModel.findOne(options) + return VideoFileModel.scope({ method: [ ScopeNames.WITH_VIDEO_OR_PLAYLIST, whereVideo ] }) + .findOne(options) .then(file => { // We used `required: false` so check we have at least a video or a streaming playlist if (!file.Video && !file.VideoStreamingPlaylist) return null @@ -348,6 +407,10 @@ export class VideoFileModel extends Model { return (this as MVideoFileStreamingPlaylistVideo).VideoStreamingPlaylist } + getVideo (this: MVideoFileVideo | MVideoFileStreamingPlaylistVideo): MVideo { + return extractVideo(this.getVideoOrStreamingPlaylist()) + } + isAudio () { return !!MIMETYPES.AUDIO.EXT_MIMETYPE[this.extname] } @@ -360,6 +423,62 @@ export class VideoFileModel extends Model { return !!this.videoStreamingPlaylistId } + getFileUrl (video: MVideoWithHost) { + if (!this.Video) this.Video = video as VideoModel + + if (video.isOwned()) return WEBSERVER.URL + this.getFileStaticPath(video) + if (this.fileUrl) return this.fileUrl + + // Fallback if we don't have a file URL + return buildRemoteVideoBaseUrl(video, this.getFileStaticPath(video)) + } + + getFileStaticPath (video: MVideo) { + if (this.isHLS()) return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, video.uuid, this.filename) + + return join(STATIC_PATHS.WEBSEED, this.filename) + } + + getFileDownloadUrl (video: MVideoWithHost) { + const basePath = this.isHLS() + ? STATIC_DOWNLOAD_PATHS.HLS_VIDEOS + : STATIC_DOWNLOAD_PATHS.VIDEOS + const path = join(basePath, this.filename) + + if (video.isOwned()) return WEBSERVER.URL + path + + // FIXME: don't guess remote URL + return buildRemoteVideoBaseUrl(video, path) + } + + getRemoteTorrentUrl (video: MVideoWithHost) { + if (video.isOwned()) throw new Error(`Video ${video.url} is not a remote video`) + + if (this.torrentUrl) return this.torrentUrl + + // Fallback if we don't have a torrent URL + return buildRemoteVideoBaseUrl(video, this.getTorrentStaticPath()) + } + + // We proxify torrent requests so use a local URL + getTorrentUrl () { + return WEBSERVER.URL + this.getTorrentStaticPath() + } + + getTorrentStaticPath () { + return join(LAZY_STATIC_PATHS.TORRENTS, this.torrentFilename) + } + + getTorrentDownloadUrl () { + return WEBSERVER.URL + join(STATIC_DOWNLOAD_PATHS.TORRENTS, this.torrentFilename) + } + + removeTorrent () { + const torrentPath = getTorrentFilePath(this) + return remove(torrentPath) + .catch(err => logger.warn('Cannot delete torrent %s.', torrentPath, { err })) + } + hasSameUniqueKeysThan (other: MVideoFile) { return this.fps === other.fps && this.resolution === other.resolution && diff --git a/server/models/video/video-format-utils.ts b/server/models/video/video-format-utils.ts index 77b8bcfe3..adf460734 100644 --- a/server/models/video/video-format-utils.ts +++ b/server/models/video/video-format-utils.ts @@ -1,16 +1,17 @@ -import { Video, VideoDetails } from '../../../shared/models/videos' -import { VideoModel } from './video' +import { generateMagnetUri } from '@server/helpers/webtorrent' +import { getLocalVideoFileMetadataUrl } from '@server/lib/video-paths' +import { VideoFile } from '@shared/models/videos/video-file.model' import { ActivityTagObject, ActivityUrlObject, VideoObject } from '../../../shared/models/activitypub/objects' +import { Video, VideoDetails } from '../../../shared/models/videos' +import { VideoStreamingPlaylist } from '../../../shared/models/videos/video-streaming-playlist.model' +import { isArray } from '../../helpers/custom-validators/misc' import { MIMETYPES, WEBSERVER } from '../../initializers/constants' -import { VideoCaptionModel } from './video-caption' import { getLocalVideoCommentsActivityPubUrl, getLocalVideoDislikesActivityPubUrl, getLocalVideoLikesActivityPubUrl, getLocalVideoSharesActivityPubUrl } from '../../lib/activitypub/url' -import { isArray } from '../../helpers/custom-validators/misc' -import { VideoStreamingPlaylist } from '../../../shared/models/videos/video-streaming-playlist.model' import { MStreamingPlaylistRedundanciesOpt, MStreamingPlaylistVideo, @@ -18,12 +19,12 @@ import { MVideoAP, MVideoFile, MVideoFormattable, - MVideoFormattableDetails + MVideoFormattableDetails, + MVideoWithHost } from '../../types/models' import { MVideoFileRedundanciesOpt } from '../../types/models/video/video-file' -import { VideoFile } from '@shared/models/videos/video-file.model' -import { generateMagnetUri } from '@server/helpers/webtorrent' -import { extractVideo } from '@server/helpers/video' +import { VideoModel } from './video' +import { VideoCaptionModel } from './video-caption' export type VideoFormattingJSONOptions = { completeDescription?: boolean @@ -153,12 +154,15 @@ function videoModelToFormattedDetailsJSON (video: MVideoFormattableDetails): Vid } // Format and sort video files - detailsJson.files = videoFilesModelToFormattedJSON(video, baseUrlHttp, baseUrlWs, video.VideoFiles) + detailsJson.files = videoFilesModelToFormattedJSON(video, video, baseUrlHttp, baseUrlWs, video.VideoFiles) return Object.assign(formattedJson, detailsJson) } -function streamingPlaylistsModelToFormattedJSON (video: MVideo, playlists: MStreamingPlaylistRedundanciesOpt[]): VideoStreamingPlaylist[] { +function streamingPlaylistsModelToFormattedJSON ( + video: MVideoFormattableDetails, + playlists: MStreamingPlaylistRedundanciesOpt[] +): VideoStreamingPlaylist[] { if (isArray(playlists) === false) return [] const { baseUrlHttp, baseUrlWs } = video.getBaseUrls() @@ -171,7 +175,7 @@ function streamingPlaylistsModelToFormattedJSON (video: MVideo, playlists: MStre ? playlist.RedundancyVideos.map(r => ({ baseUrl: r.fileUrl })) : [] - const files = videoFilesModelToFormattedJSON(playlistWithVideo, baseUrlHttp, baseUrlWs, playlist.VideoFiles) + const files = videoFilesModelToFormattedJSON(playlistWithVideo, video, baseUrlHttp, baseUrlWs, playlist.VideoFiles) return { id: playlist.id, @@ -190,14 +194,14 @@ function sortByResolutionDesc (fileA: MVideoFile, fileB: MVideoFile) { return -1 } +// FIXME: refactor/merge model and video arguments function videoFilesModelToFormattedJSON ( model: MVideo | MStreamingPlaylistVideo, + video: MVideoFormattableDetails, baseUrlHttp: string, baseUrlWs: string, videoFiles: MVideoFileRedundanciesOpt[] ): VideoFile[] { - const video = extractVideo(model) - return [ ...videoFiles ] .filter(f => !f.isLive()) .sort(sortByResolutionDesc) @@ -207,21 +211,29 @@ function videoFilesModelToFormattedJSON ( id: videoFile.resolution, label: videoFile.resolution + 'p' }, - magnetUri: generateMagnetUri(model, videoFile, baseUrlHttp, baseUrlWs), + + // FIXME: deprecated in 3.2 + magnetUri: generateMagnetUri(model, video, videoFile, baseUrlHttp, baseUrlWs), + size: videoFile.size, fps: videoFile.fps, - torrentUrl: model.getTorrentUrl(videoFile, baseUrlHttp), - torrentDownloadUrl: model.getTorrentDownloadUrl(videoFile, baseUrlHttp), - fileUrl: model.getVideoFileUrl(videoFile, baseUrlHttp), - fileDownloadUrl: model.getVideoFileDownloadUrl(videoFile, baseUrlHttp), - metadataUrl: video.getVideoFileMetadataUrl(videoFile, baseUrlHttp) + + torrentUrl: videoFile.getTorrentUrl(), + torrentDownloadUrl: videoFile.getTorrentDownloadUrl(), + + fileUrl: videoFile.getFileUrl(video), + fileDownloadUrl: videoFile.getFileDownloadUrl(video), + + metadataUrl: videoFile.metadataUrl ?? getLocalVideoFileMetadataUrl(video, videoFile) } as VideoFile }) } +// FIXME: refactor/merge model and video arguments function addVideoFilesInAPAcc ( acc: ActivityUrlObject[] | ActivityTagObject[], model: MVideoAP | MStreamingPlaylistVideo, + video: MVideoWithHost, baseUrlHttp: string, baseUrlWs: string, files: MVideoFile[] @@ -234,7 +246,7 @@ function addVideoFilesInAPAcc ( acc.push({ type: 'Link', mediaType: MIMETYPES.VIDEO.EXT_MIMETYPE[file.extname] as any, - href: model.getVideoFileUrl(file, baseUrlHttp), + href: file.getFileUrl(video), height: file.resolution, size: file.size, fps: file.fps @@ -244,7 +256,7 @@ function addVideoFilesInAPAcc ( type: 'Link', rel: [ 'metadata', MIMETYPES.VIDEO.EXT_MIMETYPE[file.extname] ], mediaType: 'application/json' as 'application/json', - href: extractVideo(model).getVideoFileMetadataUrl(file, baseUrlHttp), + href: getLocalVideoFileMetadataUrl(video, file), height: file.resolution, fps: file.fps }) @@ -252,14 +264,14 @@ function addVideoFilesInAPAcc ( acc.push({ type: 'Link', mediaType: 'application/x-bittorrent' as 'application/x-bittorrent', - href: model.getTorrentUrl(file, baseUrlHttp), + href: file.getTorrentUrl(), height: file.resolution }) acc.push({ type: 'Link', mediaType: 'application/x-bittorrent;x-scheme-handler/magnet' as 'application/x-bittorrent;x-scheme-handler/magnet', - href: generateMagnetUri(model, file, baseUrlHttp, baseUrlWs), + href: generateMagnetUri(model, video, file, baseUrlHttp, baseUrlWs), height: file.resolution }) } @@ -307,7 +319,7 @@ function videoModelToActivityPubObject (video: MVideoAP): VideoObject { } ] - addVideoFilesInAPAcc(url, video, baseUrlHttp, baseUrlWs, video.VideoFiles || []) + addVideoFilesInAPAcc(url, video, video, baseUrlHttp, baseUrlWs, video.VideoFiles || []) for (const playlist of (video.VideoStreamingPlaylists || [])) { const tag = playlist.p2pMediaLoaderInfohashes @@ -320,7 +332,7 @@ function videoModelToActivityPubObject (video: MVideoAP): VideoObject { }) const playlistWithVideo = Object.assign(playlist, { Video: video }) - addVideoFilesInAPAcc(tag, playlistWithVideo, baseUrlHttp, baseUrlWs, playlist.VideoFiles || []) + addVideoFilesInAPAcc(tag, playlistWithVideo, video, baseUrlHttp, baseUrlWs, playlist.VideoFiles || []) url.push({ type: 'Link', diff --git a/server/models/video/video-query-builder.ts b/server/models/video/video-query-builder.ts index 822d0c89b..af1878e7a 100644 --- a/server/models/video/video-query-builder.ts +++ b/server/models/video/video-query-builder.ts @@ -516,6 +516,10 @@ function wrapForAPIResults (baseQuery: string, replacements: any, options: Build '"VideoFiles"."resolution"': '"VideoFiles.resolution"', '"VideoFiles"."size"': '"VideoFiles.size"', '"VideoFiles"."extname"': '"VideoFiles.extname"', + '"VideoFiles"."filename"': '"VideoFiles.filename"', + '"VideoFiles"."fileUrl"': '"VideoFiles.fileUrl"', + '"VideoFiles"."torrentFilename"': '"VideoFiles.torrentFilename"', + '"VideoFiles"."torrentUrl"': '"VideoFiles.torrentUrl"', '"VideoFiles"."infoHash"': '"VideoFiles.infoHash"', '"VideoFiles"."fps"': '"VideoFiles.fps"', '"VideoFiles"."videoId"': '"VideoFiles.videoId"', @@ -529,6 +533,10 @@ function wrapForAPIResults (baseQuery: string, replacements: any, options: Build '"VideoStreamingPlaylists->VideoFiles"."resolution"': '"VideoStreamingPlaylists.VideoFiles.resolution"', '"VideoStreamingPlaylists->VideoFiles"."size"': '"VideoStreamingPlaylists.VideoFiles.size"', '"VideoStreamingPlaylists->VideoFiles"."extname"': '"VideoStreamingPlaylists.VideoFiles.extname"', + '"VideoStreamingPlaylists->VideoFiles"."filename"': '"VideoStreamingPlaylists.VideoFiles.filename"', + '"VideoStreamingPlaylists->VideoFiles"."fileUrl"': '"VideoStreamingPlaylists.VideoFiles.fileUrl"', + '"VideoStreamingPlaylists->VideoFiles"."torrentFilename"': '"VideoStreamingPlaylists.VideoFiles.torrentFilename"', + '"VideoStreamingPlaylists->VideoFiles"."torrentUrl"': '"VideoStreamingPlaylists.VideoFiles.torrentUrl"', '"VideoStreamingPlaylists->VideoFiles"."infoHash"': '"VideoStreamingPlaylists.VideoFiles.infoHash"', '"VideoStreamingPlaylists->VideoFiles"."fps"': '"VideoStreamingPlaylists.VideoFiles.fps"', '"VideoStreamingPlaylists->VideoFiles"."videoStreamingPlaylistId"': '"VideoStreamingPlaylists.VideoFiles.videoStreamingPlaylistId"', diff --git a/server/models/video/video-streaming-playlist.ts b/server/models/video/video-streaming-playlist.ts index 148768c21..c9375b433 100644 --- a/server/models/video/video-streaming-playlist.ts +++ b/server/models/video/video-streaming-playlist.ts @@ -1,28 +1,18 @@ +import * as memoizee from 'memoizee' +import { join } from 'path' +import { Op, QueryTypes } from 'sequelize' import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' -import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' -import { throwIfNotValid } from '../utils' -import { VideoModel } from './video' -import { VideoRedundancyModel } from '../redundancy/video-redundancy' +import { VideoFileModel } from '@server/models/video/video-file' +import { MStreamingPlaylist } from '@server/types/models' import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' -import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' -import { - CONSTRAINTS_FIELDS, - MEMOIZE_LENGTH, - MEMOIZE_TTL, - P2P_MEDIA_LOADER_PEER_VERSION, - STATIC_DOWNLOAD_PATHS, - STATIC_PATHS -} from '../../initializers/constants' -import { join } from 'path' import { sha1 } from '../../helpers/core-utils' +import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' import { isArrayOf } from '../../helpers/custom-validators/misc' -import { Op, QueryTypes } from 'sequelize' -import { MStreamingPlaylist, MStreamingPlaylistVideo, MVideoFile } from '@server/types/models' -import { VideoFileModel } from '@server/models/video/video-file' -import { getTorrentFileName, getTorrentFilePath, getVideoFilename } from '@server/lib/video-paths' -import * as memoizee from 'memoizee' -import { remove } from 'fs-extra' -import { logger } from '@server/helpers/logger' +import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' +import { CONSTRAINTS_FIELDS, MEMOIZE_LENGTH, MEMOIZE_TTL, P2P_MEDIA_LOADER_PEER_VERSION, STATIC_PATHS } from '../../initializers/constants' +import { VideoRedundancyModel } from '../redundancy/video-redundancy' +import { throwIfNotValid } from '../utils' +import { VideoModel } from './video' @Table({ tableName: 'videoStreamingPlaylist', @@ -196,26 +186,6 @@ export class VideoStreamingPlaylistModel extends Model { return 'unknown' } - getVideoRedundancyUrl (baseUrlHttp: string) { - return baseUrlHttp + STATIC_PATHS.REDUNDANCY + this.getStringType() + '/' + this.Video.uuid - } - - getTorrentDownloadUrl (videoFile: MVideoFile, baseUrlHttp: string) { - return baseUrlHttp + STATIC_DOWNLOAD_PATHS.TORRENTS + getTorrentFileName(this, videoFile) - } - - getVideoFileDownloadUrl (videoFile: MVideoFile, baseUrlHttp: string) { - return baseUrlHttp + STATIC_DOWNLOAD_PATHS.HLS_VIDEOS + getVideoFilename(this, videoFile) - } - - getVideoFileUrl (videoFile: MVideoFile, baseUrlHttp: string) { - return baseUrlHttp + join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, this.Video.uuid, getVideoFilename(this, videoFile)) - } - - getTorrentUrl (videoFile: MVideoFile, baseUrlHttp: string) { - return baseUrlHttp + join(STATIC_PATHS.TORRENTS, getTorrentFileName(this, videoFile)) - } - getTrackerUrls (baseUrlHttp: string, baseUrlWs: string) { return [ baseUrlWs + '/tracker/socket', baseUrlHttp + '/tracker/announce' ] } @@ -224,10 +194,4 @@ export class VideoStreamingPlaylistModel extends Model { return this.type === other.type && this.videoId === other.videoId } - - removeTorrent (this: MStreamingPlaylistVideo, videoFile: MVideoFile) { - const torrentPath = getTorrentFilePath(this, videoFile) - return remove(torrentPath) - .catch(err => logger.warn('Cannot delete torrent %s.', torrentPath, { err })) - } } diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 3321deed3..2e6b6aeec 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts @@ -24,10 +24,11 @@ import { Table, UpdatedAt } from 'sequelize-typescript' +import { v4 as uuidv4 } from 'uuid' import { buildNSFWFilter } from '@server/helpers/express-utils' import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video' import { LiveManager } from '@server/lib/live-manager' -import { getHLSDirectory, getTorrentFileName, getTorrentFilePath, getVideoFilename, getVideoFilePath } from '@server/lib/video-paths' +import { getHLSDirectory, getVideoFilePath } from '@server/lib/video-paths' import { getServerActor } from '@server/models/application/application' import { ModelCache } from '@server/models/model-cache' import { VideoFile } from '@shared/models/videos/video-file.model' @@ -60,7 +61,6 @@ import { CONSTRAINTS_FIELDS, LAZY_STATIC_PATHS, REMOTE_SCHEME, - STATIC_DOWNLOAD_PATHS, STATIC_PATHS, VIDEO_CATEGORIES, VIDEO_LANGUAGES, @@ -78,6 +78,7 @@ import { MStreamingPlaylistFilesVideo, MUserAccountId, MUserId, + MVideo, MVideoAccountLight, MVideoAccountLightBlacklistAllFiles, MVideoAP, @@ -130,7 +131,6 @@ import { VideoShareModel } from './video-share' import { VideoStreamingPlaylistModel } from './video-streaming-playlist' import { VideoTagModel } from './video-tag' import { VideoViewModel } from './video-view' -import { v4 as uuidv4 } from 'uuid' export enum ScopeNames { AVAILABLE_FOR_LIST_IDS = 'AVAILABLE_FOR_LIST_IDS', @@ -790,7 +790,7 @@ export class VideoModel extends Model { // Remove physical files and torrents instance.VideoFiles.forEach(file => { tasks.push(instance.removeFile(file)) - tasks.push(instance.removeTorrent(file)) + tasks.push(file.removeTorrent()) }) // Remove playlists file @@ -853,18 +853,14 @@ export class VideoModel extends Model { return undefined } - static listLocal (): Promise { + static listLocal (): Promise { const query = { where: { remote: false } } - return VideoModel.scope([ - ScopeNames.WITH_WEBTORRENT_FILES, - ScopeNames.WITH_STREAMING_PLAYLISTS, - ScopeNames.WITH_THUMBNAILS - ]).findAll(query) + return VideoModel.findAll(query) } static listAllAndSharedByActorForOutbox (actorId: number, start: number, count: number) { @@ -1623,6 +1619,10 @@ export class VideoModel extends Model { 'resolution', 'size', 'extname', + 'filename', + 'fileUrl', + 'torrentFilename', + 'torrentUrl', 'infoHash', 'fps', 'videoId', @@ -1891,14 +1891,14 @@ export class VideoModel extends Model { let files: VideoFile[] = [] if (Array.isArray(this.VideoFiles)) { - const result = videoFilesModelToFormattedJSON(this, baseUrlHttp, baseUrlWs, this.VideoFiles) + const result = videoFilesModelToFormattedJSON(this, this, baseUrlHttp, baseUrlWs, this.VideoFiles) files = files.concat(result) } for (const p of (this.VideoStreamingPlaylists || [])) { p.Video = this - const result = videoFilesModelToFormattedJSON(p, baseUrlHttp, baseUrlWs, p.VideoFiles) + const result = videoFilesModelToFormattedJSON(p, this, baseUrlHttp, baseUrlWs, p.VideoFiles) files = files.concat(result) } @@ -1956,12 +1956,6 @@ export class VideoModel extends Model { .catch(err => logger.warn('Cannot delete file %s.', filePath, { err })) } - removeTorrent (videoFile: MVideoFile) { - const torrentPath = getTorrentFilePath(this, videoFile) - return remove(torrentPath) - .catch(err => logger.warn('Cannot delete torrent %s.', torrentPath, { err })) - } - async removeStreamingPlaylistFiles (streamingPlaylist: MStreamingPlaylist, isRedundancy = false) { const directoryPath = getHLSDirectory(this, isRedundancy) @@ -1977,7 +1971,7 @@ export class VideoModel extends Model { // Remove physical files and torrents await Promise.all( - streamingPlaylistWithFiles.VideoFiles.map(file => streamingPlaylistWithFiles.removeTorrent(file)) + streamingPlaylistWithFiles.VideoFiles.map(file => file.removeTorrent()) ) } } @@ -2054,34 +2048,6 @@ export class VideoModel extends Model { return [ baseUrlWs + '/tracker/socket', baseUrlHttp + '/tracker/announce' ] } - getTorrentUrl (videoFile: MVideoFile, baseUrlHttp: string) { - return baseUrlHttp + STATIC_PATHS.TORRENTS + getTorrentFileName(this, videoFile) - } - - getTorrentDownloadUrl (videoFile: MVideoFile, baseUrlHttp: string) { - return baseUrlHttp + STATIC_DOWNLOAD_PATHS.TORRENTS + getTorrentFileName(this, videoFile) - } - - getVideoFileUrl (videoFile: MVideoFile, baseUrlHttp: string) { - return baseUrlHttp + STATIC_PATHS.WEBSEED + getVideoFilename(this, videoFile) - } - - getVideoFileMetadataUrl (videoFile: MVideoFile, baseUrlHttp: string) { - const path = '/api/v1/videos/' - - return this.isOwned() - ? baseUrlHttp + path + this.uuid + '/metadata/' + videoFile.id - : videoFile.metadataUrl - } - - getVideoRedundancyUrl (videoFile: MVideoFile, baseUrlHttp: string) { - return baseUrlHttp + STATIC_PATHS.REDUNDANCY + getVideoFilename(this, videoFile) - } - - getVideoFileDownloadUrl (videoFile: MVideoFile, baseUrlHttp: string) { - return baseUrlHttp + STATIC_DOWNLOAD_PATHS.VIDEOS + getVideoFilename(this, videoFile) - } - getBandwidthBits (videoFile: MVideoFile) { return Math.ceil((videoFile.size * 8) / this.duration) } -- cgit v1.2.3