X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fmodels%2Fvideo%2Fvideo-streaming-playlist.ts;h=b15d20cf92417450e44ed5caa0d4bb7437e5e431;hb=ac27887774e63d99f4e227fbe18846f143cc4b3c;hp=148768c210a8c73183fbc6288651970cbb1254ba;hpb=b49f22d8f9a52ab75fd38db2d377249eb58fa678;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/models/video/video-streaming-playlist.ts b/server/models/video/video-streaming-playlist.ts index 148768c21..b15d20cf9 100644 --- a/server/models/video/video-streaming-playlist.ts +++ b/server/models/video/video-streaming-playlist.ts @@ -1,28 +1,27 @@ +import * as memoizee from 'memoizee' +import { join } from 'path' +import { Op } 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 { doesExist } from '@server/helpers/database-utils' +import { VideoFileModel } from '@server/models/video/video-file' +import { MStreamingPlaylist, MVideo } from '@server/types/models' +import { AttributesOnly } from '@shared/core-utils' import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' +import { sha1 } from '../../helpers/core-utils' import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' +import { isArrayOf } from '../../helpers/custom-validators/misc' +import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' import { CONSTRAINTS_FIELDS, MEMOIZE_LENGTH, MEMOIZE_TTL, P2P_MEDIA_LOADER_PEER_VERSION, - STATIC_DOWNLOAD_PATHS, - STATIC_PATHS + STATIC_PATHS, + WEBSERVER } from '../../initializers/constants' -import { join } from 'path' -import { sha1 } from '../../helpers/core-utils' -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 { VideoRedundancyModel } from '../redundancy/video-redundancy' +import { throwIfNotValid } from '../utils' +import { VideoModel } from './video' @Table({ tableName: 'videoStreamingPlaylist', @@ -40,7 +39,7 @@ import { logger } from '@server/helpers/logger' } ] }) -export class VideoStreamingPlaylistModel extends Model { +export class VideoStreamingPlaylistModel extends Model>> { @CreatedAt createdAt: Date @@ -52,7 +51,11 @@ export class VideoStreamingPlaylistModel extends Model { type: VideoStreamingPlaylistType @AllowNull(false) - @Is('PlaylistUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'playlist url')) + @Column + playlistFilename: string + + @AllowNull(true) + @Is('PlaylistUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'playlist url', true)) @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.URL.max)) playlistUrl: string @@ -66,7 +69,11 @@ export class VideoStreamingPlaylistModel extends Model { p2pMediaLoaderPeerVersion: number @AllowNull(false) - @Is('VideoStreamingSegmentsSha256Url', value => throwIfNotValid(value, isActivityPubUrlValid, 'segments sha256 url')) + @Column + segmentsSha256Filename: string + + @AllowNull(true) + @Is('VideoStreamingSegmentsSha256Url', value => throwIfNotValid(value, isActivityPubUrlValid, 'segments sha256 url', true)) @Column segmentsSha256Url: string @@ -107,14 +114,8 @@ export class VideoStreamingPlaylistModel extends Model { static doesInfohashExist (infoHash: string) { const query = 'SELECT 1 FROM "videoStreamingPlaylist" WHERE $infoHash = ANY("p2pMediaLoaderInfohashes") LIMIT 1' - const options = { - type: QueryTypes.SELECT as QueryTypes.SELECT, - bind: { infoHash }, - raw: true - } - return VideoModel.sequelize.query(query, options) - .then(results => results.length === 1) + return doesExist(query, { infoHash }) } static buildP2PMediaLoaderInfoHashes (playlistUrl: string, files: unknown[]) { @@ -134,7 +135,13 @@ export class VideoStreamingPlaylistModel extends Model { p2pMediaLoaderPeerVersion: { [Op.ne]: P2P_MEDIA_LOADER_PEER_VERSION } - } + }, + include: [ + { + model: VideoModel.unscoped(), + required: true + } + ] } return VideoStreamingPlaylistModel.findAll(query) @@ -153,7 +160,7 @@ export class VideoStreamingPlaylistModel extends Model { return VideoStreamingPlaylistModel.findByPk(id, options) } - static loadHLSPlaylistByVideo (videoId: number) { + static loadHLSPlaylistByVideo (videoId: number): Promise { const options = { where: { type: VideoStreamingPlaylistType.HLS, @@ -164,30 +171,29 @@ export class VideoStreamingPlaylistModel extends Model { return VideoStreamingPlaylistModel.findOne(options) } - static getHlsPlaylistFilename (resolution: number) { - return resolution + '.m3u8' - } + static async loadOrGenerate (video: MVideo) { + let playlist = await VideoStreamingPlaylistModel.loadHLSPlaylistByVideo(video.id) + if (!playlist) playlist = new VideoStreamingPlaylistModel() - static getMasterHlsPlaylistFilename () { - return 'master.m3u8' + return Object.assign(playlist, { videoId: video.id, Video: video }) } - static getHlsSha256SegmentsFilename () { - return 'segments-sha256.json' - } + assignP2PMediaLoaderInfoHashes (video: MVideo, files: unknown[]) { + const masterPlaylistUrl = this.getMasterPlaylistUrl(video) - static getHlsMasterPlaylistStaticPath (videoUUID: string) { - return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, videoUUID, VideoStreamingPlaylistModel.getMasterHlsPlaylistFilename()) + this.p2pMediaLoaderInfohashes = VideoStreamingPlaylistModel.buildP2PMediaLoaderInfoHashes(masterPlaylistUrl, files) } - static getHlsPlaylistStaticPath (videoUUID: string, resolution: number) { - return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, videoUUID, VideoStreamingPlaylistModel.getHlsPlaylistFilename(resolution)) + getMasterPlaylistUrl (video: MVideo) { + if (video.isOwned()) return WEBSERVER.URL + this.getMasterPlaylistStaticPath(video.uuid) + + return this.playlistUrl } - static getHlsSha256SegmentsStaticPath (videoUUID: string, isLive: boolean) { - if (isLive) return join('/live', 'segments-sha256', videoUUID) + getSha256SegmentsUrl (video: MVideo) { + if (video.isOwned()) return WEBSERVER.URL + this.getSha256SegmentsStaticPath(video.uuid, video.isLive) - return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, videoUUID, VideoStreamingPlaylistModel.getHlsSha256SegmentsFilename()) + return this.segmentsSha256Url } getStringType () { @@ -196,26 +202,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' ] } @@ -225,9 +211,13 @@ export class VideoStreamingPlaylistModel extends Model { 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 })) + private getMasterPlaylistStaticPath (videoUUID: string) { + return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, videoUUID, this.playlistFilename) + } + + private getSha256SegmentsStaticPath (videoUUID: string, isLive: boolean) { + if (isLive) return join('/live', 'segments-sha256', videoUUID) + + return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, videoUUID, this.segmentsSha256Filename) } }