aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models/video/video-streaming-playlist.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/models/video/video-streaming-playlist.ts')
-rw-r--r--server/models/video/video-streaming-playlist.ts85
1 files changed, 55 insertions, 30 deletions
diff --git a/server/models/video/video-streaming-playlist.ts b/server/models/video/video-streaming-playlist.ts
index d627e8c9d..d591a3134 100644
--- a/server/models/video/video-streaming-playlist.ts
+++ b/server/models/video/video-streaming-playlist.ts
@@ -1,19 +1,27 @@
1import * as memoizee from 'memoizee' 1import * as memoizee from 'memoizee'
2import { join } from 'path' 2import { join } from 'path'
3import { Op, QueryTypes } from 'sequelize' 3import { Op } from 'sequelize'
4import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 4import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
5import { VideoFileModel } from '@server/models/video/video-file' 5import { VideoFileModel } from '@server/models/video/video-file'
6import { MStreamingPlaylist } from '@server/types/models' 6import { MStreamingPlaylist, MVideo } from '@server/types/models'
7import { AttributesOnly } from '@shared/core-utils'
7import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' 8import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
8import { sha1 } from '../../helpers/core-utils' 9import { sha1 } from '../../helpers/core-utils'
9import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 10import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
10import { isArrayOf } from '../../helpers/custom-validators/misc' 11import { isArrayOf } from '../../helpers/custom-validators/misc'
11import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' 12import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos'
12import { CONSTRAINTS_FIELDS, MEMOIZE_LENGTH, MEMOIZE_TTL, P2P_MEDIA_LOADER_PEER_VERSION, STATIC_PATHS } from '../../initializers/constants' 13import {
14 CONSTRAINTS_FIELDS,
15 MEMOIZE_LENGTH,
16 MEMOIZE_TTL,
17 P2P_MEDIA_LOADER_PEER_VERSION,
18 STATIC_PATHS,
19 WEBSERVER
20} from '../../initializers/constants'
13import { VideoRedundancyModel } from '../redundancy/video-redundancy' 21import { VideoRedundancyModel } from '../redundancy/video-redundancy'
22import { doesExist } from '../shared'
14import { throwIfNotValid } from '../utils' 23import { throwIfNotValid } from '../utils'
15import { VideoModel } from './video' 24import { VideoModel } from './video'
16import { AttributesOnly } from '@shared/core-utils'
17 25
18@Table({ 26@Table({
19 tableName: 'videoStreamingPlaylist', 27 tableName: 'videoStreamingPlaylist',
@@ -43,7 +51,11 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
43 type: VideoStreamingPlaylistType 51 type: VideoStreamingPlaylistType
44 52
45 @AllowNull(false) 53 @AllowNull(false)
46 @Is('PlaylistUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'playlist url')) 54 @Column
55 playlistFilename: string
56
57 @AllowNull(true)
58 @Is('PlaylistUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'playlist url', true))
47 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.URL.max)) 59 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.URL.max))
48 playlistUrl: string 60 playlistUrl: string
49 61
@@ -57,7 +69,11 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
57 p2pMediaLoaderPeerVersion: number 69 p2pMediaLoaderPeerVersion: number
58 70
59 @AllowNull(false) 71 @AllowNull(false)
60 @Is('VideoStreamingSegmentsSha256Url', value => throwIfNotValid(value, isActivityPubUrlValid, 'segments sha256 url')) 72 @Column
73 segmentsSha256Filename: string
74
75 @AllowNull(true)
76 @Is('VideoStreamingSegmentsSha256Url', value => throwIfNotValid(value, isActivityPubUrlValid, 'segments sha256 url', true))
61 @Column 77 @Column
62 segmentsSha256Url: string 78 segmentsSha256Url: string
63 79
@@ -98,14 +114,8 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
98 114
99 static doesInfohashExist (infoHash: string) { 115 static doesInfohashExist (infoHash: string) {
100 const query = 'SELECT 1 FROM "videoStreamingPlaylist" WHERE $infoHash = ANY("p2pMediaLoaderInfohashes") LIMIT 1' 116 const query = 'SELECT 1 FROM "videoStreamingPlaylist" WHERE $infoHash = ANY("p2pMediaLoaderInfohashes") LIMIT 1'
101 const options = {
102 type: QueryTypes.SELECT as QueryTypes.SELECT,
103 bind: { infoHash },
104 raw: true
105 }
106 117
107 return VideoModel.sequelize.query<object>(query, options) 118 return doesExist(query, { infoHash })
108 .then(results => results.length === 1)
109 } 119 }
110 120
111 static buildP2PMediaLoaderInfoHashes (playlistUrl: string, files: unknown[]) { 121 static buildP2PMediaLoaderInfoHashes (playlistUrl: string, files: unknown[]) {
@@ -125,7 +135,13 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
125 p2pMediaLoaderPeerVersion: { 135 p2pMediaLoaderPeerVersion: {
126 [Op.ne]: P2P_MEDIA_LOADER_PEER_VERSION 136 [Op.ne]: P2P_MEDIA_LOADER_PEER_VERSION
127 } 137 }
128 } 138 },
139 include: [
140 {
141 model: VideoModel.unscoped(),
142 required: true
143 }
144 ]
129 } 145 }
130 146
131 return VideoStreamingPlaylistModel.findAll(query) 147 return VideoStreamingPlaylistModel.findAll(query)
@@ -144,7 +160,7 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
144 return VideoStreamingPlaylistModel.findByPk(id, options) 160 return VideoStreamingPlaylistModel.findByPk(id, options)
145 } 161 }
146 162
147 static loadHLSPlaylistByVideo (videoId: number) { 163 static loadHLSPlaylistByVideo (videoId: number): Promise<MStreamingPlaylist> {
148 const options = { 164 const options = {
149 where: { 165 where: {
150 type: VideoStreamingPlaylistType.HLS, 166 type: VideoStreamingPlaylistType.HLS,
@@ -155,30 +171,29 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
155 return VideoStreamingPlaylistModel.findOne(options) 171 return VideoStreamingPlaylistModel.findOne(options)
156 } 172 }
157 173
158 static getHlsPlaylistFilename (resolution: number) { 174 static async loadOrGenerate (video: MVideo) {
159 return resolution + '.m3u8' 175 let playlist = await VideoStreamingPlaylistModel.loadHLSPlaylistByVideo(video.id)
160 } 176 if (!playlist) playlist = new VideoStreamingPlaylistModel()
161 177
162 static getMasterHlsPlaylistFilename () { 178 return Object.assign(playlist, { videoId: video.id, Video: video })
163 return 'master.m3u8'
164 } 179 }
165 180
166 static getHlsSha256SegmentsFilename () { 181 assignP2PMediaLoaderInfoHashes (video: MVideo, files: unknown[]) {
167 return 'segments-sha256.json' 182 const masterPlaylistUrl = this.getMasterPlaylistUrl(video)
168 }
169 183
170 static getHlsMasterPlaylistStaticPath (videoUUID: string) { 184 this.p2pMediaLoaderInfohashes = VideoStreamingPlaylistModel.buildP2PMediaLoaderInfoHashes(masterPlaylistUrl, files)
171 return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, videoUUID, VideoStreamingPlaylistModel.getMasterHlsPlaylistFilename())
172 } 185 }
173 186
174 static getHlsPlaylistStaticPath (videoUUID: string, resolution: number) { 187 getMasterPlaylistUrl (video: MVideo) {
175 return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, videoUUID, VideoStreamingPlaylistModel.getHlsPlaylistFilename(resolution)) 188 if (video.isOwned()) return WEBSERVER.URL + this.getMasterPlaylistStaticPath(video.uuid)
189
190 return this.playlistUrl
176 } 191 }
177 192
178 static getHlsSha256SegmentsStaticPath (videoUUID: string, isLive: boolean) { 193 getSha256SegmentsUrl (video: MVideo) {
179 if (isLive) return join('/live', 'segments-sha256', videoUUID) 194 if (video.isOwned()) return WEBSERVER.URL + this.getSha256SegmentsStaticPath(video.uuid, video.isLive)
180 195
181 return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, videoUUID, VideoStreamingPlaylistModel.getHlsSha256SegmentsFilename()) 196 return this.segmentsSha256Url
182 } 197 }
183 198
184 getStringType () { 199 getStringType () {
@@ -195,4 +210,14 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
195 return this.type === other.type && 210 return this.type === other.type &&
196 this.videoId === other.videoId 211 this.videoId === other.videoId
197 } 212 }
213
214 private getMasterPlaylistStaticPath (videoUUID: string) {
215 return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, videoUUID, this.playlistFilename)
216 }
217
218 private getSha256SegmentsStaticPath (videoUUID: string, isLive: boolean) {
219 if (isLive) return join('/live', 'segments-sha256', videoUUID)
220
221 return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, videoUUID, this.segmentsSha256Filename)
222 }
198} 223}