diff options
author | Chocobozzz <me@florianbigard.com> | 2019-02-11 14:09:23 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2019-02-11 14:09:23 +0100 |
commit | b718fd22374d64534bcfe69932cf562894abed6a (patch) | |
tree | 311d3c67e2a4d1f33ebdd1dc163527de9d33d0f7 /server/models/video/video-streaming-playlist.ts | |
parent | adb115f5522bea4d52456a9fc5eb4140bb064476 (diff) | |
parent | 501e961199578129629cf0567033d13efced9904 (diff) | |
download | PeerTube-b718fd22374d64534bcfe69932cf562894abed6a.tar.gz PeerTube-b718fd22374d64534bcfe69932cf562894abed6a.tar.zst PeerTube-b718fd22374d64534bcfe69932cf562894abed6a.zip |
Merge branch 'develop' into pr/1285
Diffstat (limited to 'server/models/video/video-streaming-playlist.ts')
-rw-r--r-- | server/models/video/video-streaming-playlist.ts | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/server/models/video/video-streaming-playlist.ts b/server/models/video/video-streaming-playlist.ts new file mode 100644 index 000000000..bf6f7b0c4 --- /dev/null +++ b/server/models/video/video-streaming-playlist.ts | |||
@@ -0,0 +1,158 @@ | |||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | ||
2 | import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' | ||
3 | import { throwIfNotValid } from '../utils' | ||
4 | import { VideoModel } from './video' | ||
5 | import * as Sequelize from 'sequelize' | ||
6 | import { VideoRedundancyModel } from '../redundancy/video-redundancy' | ||
7 | import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' | ||
8 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | ||
9 | import { CONSTRAINTS_FIELDS, STATIC_PATHS } from '../../initializers' | ||
10 | import { VideoFileModel } from './video-file' | ||
11 | import { join } from 'path' | ||
12 | import { sha1 } from '../../helpers/core-utils' | ||
13 | import { isArrayOf } from '../../helpers/custom-validators/misc' | ||
14 | |||
15 | @Table({ | ||
16 | tableName: 'videoStreamingPlaylist', | ||
17 | indexes: [ | ||
18 | { | ||
19 | fields: [ 'videoId' ] | ||
20 | }, | ||
21 | { | ||
22 | fields: [ 'videoId', 'type' ], | ||
23 | unique: true | ||
24 | }, | ||
25 | { | ||
26 | fields: [ 'p2pMediaLoaderInfohashes' ], | ||
27 | using: 'gin' | ||
28 | } | ||
29 | ] | ||
30 | }) | ||
31 | export class VideoStreamingPlaylistModel extends Model<VideoStreamingPlaylistModel> { | ||
32 | @CreatedAt | ||
33 | createdAt: Date | ||
34 | |||
35 | @UpdatedAt | ||
36 | updatedAt: Date | ||
37 | |||
38 | @AllowNull(false) | ||
39 | @Column | ||
40 | type: VideoStreamingPlaylistType | ||
41 | |||
42 | @AllowNull(false) | ||
43 | @Is('PlaylistUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'playlist url')) | ||
44 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.URL.max)) | ||
45 | playlistUrl: string | ||
46 | |||
47 | @AllowNull(false) | ||
48 | @Is('VideoStreamingPlaylistInfoHashes', value => throwIfNotValid(value, v => isArrayOf(v, isVideoFileInfoHashValid), 'info hashes')) | ||
49 | @Column(DataType.ARRAY(DataType.STRING)) | ||
50 | p2pMediaLoaderInfohashes: string[] | ||
51 | |||
52 | @AllowNull(false) | ||
53 | @Is('VideoStreamingSegmentsSha256Url', value => throwIfNotValid(value, isActivityPubUrlValid, 'segments sha256 url')) | ||
54 | @Column | ||
55 | segmentsSha256Url: string | ||
56 | |||
57 | @ForeignKey(() => VideoModel) | ||
58 | @Column | ||
59 | videoId: number | ||
60 | |||
61 | @BelongsTo(() => VideoModel, { | ||
62 | foreignKey: { | ||
63 | allowNull: false | ||
64 | }, | ||
65 | onDelete: 'CASCADE' | ||
66 | }) | ||
67 | Video: VideoModel | ||
68 | |||
69 | @HasMany(() => VideoRedundancyModel, { | ||
70 | foreignKey: { | ||
71 | allowNull: false | ||
72 | }, | ||
73 | onDelete: 'CASCADE', | ||
74 | hooks: true | ||
75 | }) | ||
76 | RedundancyVideos: VideoRedundancyModel[] | ||
77 | |||
78 | static doesInfohashExist (infoHash: string) { | ||
79 | const query = 'SELECT 1 FROM "videoStreamingPlaylist" WHERE $infoHash = ANY("p2pMediaLoaderInfohashes") LIMIT 1' | ||
80 | const options = { | ||
81 | type: Sequelize.QueryTypes.SELECT, | ||
82 | bind: { infoHash }, | ||
83 | raw: true | ||
84 | } | ||
85 | |||
86 | return VideoModel.sequelize.query(query, options) | ||
87 | .then(results => { | ||
88 | return results.length === 1 | ||
89 | }) | ||
90 | } | ||
91 | |||
92 | static buildP2PMediaLoaderInfoHashes (playlistUrl: string, videoFiles: VideoFileModel[]) { | ||
93 | const hashes: string[] = [] | ||
94 | |||
95 | // https://github.com/Novage/p2p-media-loader/blob/master/p2p-media-loader-core/lib/p2p-media-manager.ts#L97 | ||
96 | for (let i = 0; i < videoFiles.length; i++) { | ||
97 | hashes.push(sha1(`1${playlistUrl}+V${i}`)) | ||
98 | } | ||
99 | |||
100 | return hashes | ||
101 | } | ||
102 | |||
103 | static loadWithVideo (id: number) { | ||
104 | const options = { | ||
105 | include: [ | ||
106 | { | ||
107 | model: VideoModel.unscoped(), | ||
108 | required: true | ||
109 | } | ||
110 | ] | ||
111 | } | ||
112 | |||
113 | return VideoStreamingPlaylistModel.findById(id, options) | ||
114 | } | ||
115 | |||
116 | static getHlsPlaylistFilename (resolution: number) { | ||
117 | return resolution + '.m3u8' | ||
118 | } | ||
119 | |||
120 | static getMasterHlsPlaylistFilename () { | ||
121 | return 'master.m3u8' | ||
122 | } | ||
123 | |||
124 | static getHlsSha256SegmentsFilename () { | ||
125 | return 'segments-sha256.json' | ||
126 | } | ||
127 | |||
128 | static getHlsVideoName (uuid: string, resolution: number) { | ||
129 | return `${uuid}-${resolution}-fragmented.mp4` | ||
130 | } | ||
131 | |||
132 | static getHlsMasterPlaylistStaticPath (videoUUID: string) { | ||
133 | return join(STATIC_PATHS.PLAYLISTS.HLS, videoUUID, VideoStreamingPlaylistModel.getMasterHlsPlaylistFilename()) | ||
134 | } | ||
135 | |||
136 | static getHlsPlaylistStaticPath (videoUUID: string, resolution: number) { | ||
137 | return join(STATIC_PATHS.PLAYLISTS.HLS, videoUUID, VideoStreamingPlaylistModel.getHlsPlaylistFilename(resolution)) | ||
138 | } | ||
139 | |||
140 | static getHlsSha256SegmentsStaticPath (videoUUID: string) { | ||
141 | return join(STATIC_PATHS.PLAYLISTS.HLS, videoUUID, VideoStreamingPlaylistModel.getHlsSha256SegmentsFilename()) | ||
142 | } | ||
143 | |||
144 | getStringType () { | ||
145 | if (this.type === VideoStreamingPlaylistType.HLS) return 'hls' | ||
146 | |||
147 | return 'unknown' | ||
148 | } | ||
149 | |||
150 | getVideoRedundancyUrl (baseUrlHttp: string) { | ||
151 | return baseUrlHttp + STATIC_PATHS.REDUNDANCY + this.getStringType() + '/' + this.Video.uuid | ||
152 | } | ||
153 | |||
154 | hasSameUniqueKeysThan (other: VideoStreamingPlaylistModel) { | ||
155 | return this.type === other.type && | ||
156 | this.videoId === other.videoId | ||
157 | } | ||
158 | } | ||