diff options
Diffstat (limited to 'server/models/video')
-rw-r--r-- | server/models/video/thumbnail.ts | 4 | ||||
-rw-r--r-- | server/models/video/video-caption.ts | 6 | ||||
-rw-r--r-- | server/models/video/video-file.ts | 181 | ||||
-rw-r--r-- | server/models/video/video-format-utils.ts | 64 | ||||
-rw-r--r-- | server/models/video/video-query-builder.ts | 8 | ||||
-rw-r--r-- | server/models/video/video-streaming-playlist.ts | 58 | ||||
-rw-r--r-- | server/models/video/video.ts | 60 |
7 files changed, 225 insertions, 156 deletions
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 { | |||
17 | } from 'sequelize-typescript' | 17 | } from 'sequelize-typescript' |
18 | import { buildRemoteVideoBaseUrl } from '@server/helpers/activitypub' | 18 | import { buildRemoteVideoBaseUrl } from '@server/helpers/activitypub' |
19 | import { afterCommitIfTransaction } from '@server/helpers/database-utils' | 19 | import { afterCommitIfTransaction } from '@server/helpers/database-utils' |
20 | import { MThumbnail, MThumbnailVideo, MVideoAccountLight } from '@server/types/models' | 20 | import { MThumbnail, MThumbnailVideo, MVideoWithHost } from '@server/types/models' |
21 | import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' | 21 | import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' |
22 | import { logger } from '../../helpers/logger' | 22 | import { logger } from '../../helpers/logger' |
23 | import { CONFIG } from '../../initializers/config' | 23 | import { CONFIG } from '../../initializers/config' |
@@ -164,7 +164,7 @@ export class ThumbnailModel extends Model { | |||
164 | return join(directory, filename) | 164 | return join(directory, filename) |
165 | } | 165 | } |
166 | 166 | ||
167 | getFileUrl (video: MVideoAccountLight) { | 167 | getFileUrl (video: MVideoWithHost) { |
168 | const staticPath = ThumbnailModel.types[this.type].staticPath + this.filename | 168 | const staticPath = ThumbnailModel.types[this.type].staticPath + this.filename |
169 | 169 | ||
170 | if (video.isOwned()) return WEBSERVER.URL + staticPath | 170 | 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 { | |||
15 | Table, | 15 | Table, |
16 | UpdatedAt | 16 | UpdatedAt |
17 | } from 'sequelize-typescript' | 17 | } from 'sequelize-typescript' |
18 | import { v4 as uuidv4 } from 'uuid' | ||
18 | import { buildRemoteVideoBaseUrl } from '@server/helpers/activitypub' | 19 | import { buildRemoteVideoBaseUrl } from '@server/helpers/activitypub' |
19 | import { MVideoAccountLight, MVideoCaption, MVideoCaptionFormattable, MVideoCaptionVideo } from '@server/types/models' | 20 | import { MVideoCaption, MVideoCaptionFormattable, MVideoCaptionVideo, MVideoWithHost } from '@server/types/models' |
20 | import { VideoCaption } from '../../../shared/models/videos/caption/video-caption.model' | 21 | import { VideoCaption } from '../../../shared/models/videos/caption/video-caption.model' |
21 | import { isVideoCaptionLanguageValid } from '../../helpers/custom-validators/video-captions' | 22 | import { isVideoCaptionLanguageValid } from '../../helpers/custom-validators/video-captions' |
22 | import { logger } from '../../helpers/logger' | 23 | import { logger } from '../../helpers/logger' |
@@ -24,7 +25,6 @@ import { CONFIG } from '../../initializers/config' | |||
24 | import { CONSTRAINTS_FIELDS, LAZY_STATIC_PATHS, VIDEO_LANGUAGES, WEBSERVER } from '../../initializers/constants' | 25 | import { CONSTRAINTS_FIELDS, LAZY_STATIC_PATHS, VIDEO_LANGUAGES, WEBSERVER } from '../../initializers/constants' |
25 | import { buildWhereIdOrUUID, throwIfNotValid } from '../utils' | 26 | import { buildWhereIdOrUUID, throwIfNotValid } from '../utils' |
26 | import { VideoModel } from './video' | 27 | import { VideoModel } from './video' |
27 | import { v4 as uuidv4 } from 'uuid' | ||
28 | 28 | ||
29 | export enum ScopeNames { | 29 | export enum ScopeNames { |
30 | WITH_VIDEO_UUID_AND_REMOTE = 'WITH_VIDEO_UUID_AND_REMOTE' | 30 | WITH_VIDEO_UUID_AND_REMOTE = 'WITH_VIDEO_UUID_AND_REMOTE' |
@@ -204,7 +204,7 @@ export class VideoCaptionModel extends Model { | |||
204 | return remove(CONFIG.STORAGE.CAPTIONS_DIR + this.filename) | 204 | return remove(CONFIG.STORAGE.CAPTIONS_DIR + this.filename) |
205 | } | 205 | } |
206 | 206 | ||
207 | getFileUrl (video: MVideoAccountLight) { | 207 | getFileUrl (video: MVideoWithHost) { |
208 | if (!this.Video) this.Video = video as VideoModel | 208 | if (!this.Video) this.Video = video as VideoModel |
209 | 209 | ||
210 | if (video.isOwned()) return WEBSERVER.URL + this.getCaptionStaticPath() | 210 | 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 @@ | |||
1 | import { remove } from 'fs-extra' | ||
2 | import * as memoizee from 'memoizee' | ||
3 | import { join } from 'path' | ||
4 | import { FindOptions, Op, QueryTypes, Transaction } from 'sequelize' | ||
1 | import { | 5 | import { |
2 | AllowNull, | 6 | AllowNull, |
3 | BelongsTo, | 7 | BelongsTo, |
@@ -5,15 +9,22 @@ import { | |||
5 | CreatedAt, | 9 | CreatedAt, |
6 | DataType, | 10 | DataType, |
7 | Default, | 11 | Default, |
12 | DefaultScope, | ||
8 | ForeignKey, | 13 | ForeignKey, |
9 | HasMany, | 14 | HasMany, |
10 | Is, | 15 | Is, |
11 | Model, | 16 | Model, |
12 | Table, | ||
13 | UpdatedAt, | ||
14 | Scopes, | 17 | Scopes, |
15 | DefaultScope | 18 | Table, |
19 | UpdatedAt | ||
16 | } from 'sequelize-typescript' | 20 | } from 'sequelize-typescript' |
21 | import { Where } from 'sequelize/types/lib/utils' | ||
22 | import validator from 'validator' | ||
23 | import { buildRemoteVideoBaseUrl } from '@server/helpers/activitypub' | ||
24 | import { logger } from '@server/helpers/logger' | ||
25 | import { extractVideo } from '@server/helpers/video' | ||
26 | import { getTorrentFilePath } from '@server/lib/video-paths' | ||
27 | import { MStreamingPlaylistVideo, MVideo, MVideoWithHost } from '@server/types/models' | ||
17 | import { | 28 | import { |
18 | isVideoFileExtnameValid, | 29 | isVideoFileExtnameValid, |
19 | isVideoFileInfoHashValid, | 30 | isVideoFileInfoHashValid, |
@@ -21,20 +32,25 @@ import { | |||
21 | isVideoFileSizeValid, | 32 | isVideoFileSizeValid, |
22 | isVideoFPSResolutionValid | 33 | isVideoFPSResolutionValid |
23 | } from '../../helpers/custom-validators/videos' | 34 | } from '../../helpers/custom-validators/videos' |
35 | import { | ||
36 | LAZY_STATIC_PATHS, | ||
37 | MEMOIZE_LENGTH, | ||
38 | MEMOIZE_TTL, | ||
39 | MIMETYPES, | ||
40 | STATIC_DOWNLOAD_PATHS, | ||
41 | STATIC_PATHS, | ||
42 | WEBSERVER | ||
43 | } from '../../initializers/constants' | ||
44 | import { MVideoFile, MVideoFileStreamingPlaylistVideo, MVideoFileVideo } from '../../types/models/video/video-file' | ||
45 | import { VideoRedundancyModel } from '../redundancy/video-redundancy' | ||
24 | import { parseAggregateResult, throwIfNotValid } from '../utils' | 46 | import { parseAggregateResult, throwIfNotValid } from '../utils' |
25 | import { VideoModel } from './video' | 47 | import { VideoModel } from './video' |
26 | import { VideoRedundancyModel } from '../redundancy/video-redundancy' | ||
27 | import { VideoStreamingPlaylistModel } from './video-streaming-playlist' | 48 | import { VideoStreamingPlaylistModel } from './video-streaming-playlist' |
28 | import { FindOptions, Op, QueryTypes, Transaction } from 'sequelize' | ||
29 | import { MIMETYPES, MEMOIZE_LENGTH, MEMOIZE_TTL } from '../../initializers/constants' | ||
30 | import { MVideoFile, MVideoFileStreamingPlaylistVideo, MVideoFileVideo } from '../../types/models/video/video-file' | ||
31 | import { MStreamingPlaylistVideo, MVideo } from '@server/types/models' | ||
32 | import * as memoizee from 'memoizee' | ||
33 | import validator from 'validator' | ||
34 | 49 | ||
35 | export enum ScopeNames { | 50 | export enum ScopeNames { |
36 | WITH_VIDEO = 'WITH_VIDEO', | 51 | WITH_VIDEO = 'WITH_VIDEO', |
37 | WITH_METADATA = 'WITH_METADATA' | 52 | WITH_METADATA = 'WITH_METADATA', |
53 | WITH_VIDEO_OR_PLAYLIST = 'WITH_VIDEO_OR_PLAYLIST' | ||
38 | } | 54 | } |
39 | 55 | ||
40 | @DefaultScope(() => ({ | 56 | @DefaultScope(() => ({ |
@@ -51,6 +67,28 @@ export enum ScopeNames { | |||
51 | } | 67 | } |
52 | ] | 68 | ] |
53 | }, | 69 | }, |
70 | [ScopeNames.WITH_VIDEO_OR_PLAYLIST]: (options: { whereVideo?: Where } = {}) => { | ||
71 | return { | ||
72 | include: [ | ||
73 | { | ||
74 | model: VideoModel.unscoped(), | ||
75 | required: false, | ||
76 | where: options.whereVideo | ||
77 | }, | ||
78 | { | ||
79 | model: VideoStreamingPlaylistModel.unscoped(), | ||
80 | required: false, | ||
81 | include: [ | ||
82 | { | ||
83 | model: VideoModel.unscoped(), | ||
84 | required: true, | ||
85 | where: options.whereVideo | ||
86 | } | ||
87 | ] | ||
88 | } | ||
89 | ] | ||
90 | } | ||
91 | }, | ||
54 | [ScopeNames.WITH_METADATA]: { | 92 | [ScopeNames.WITH_METADATA]: { |
55 | attributes: { | 93 | attributes: { |
56 | include: [ 'metadata' ] | 94 | include: [ 'metadata' ] |
@@ -82,6 +120,16 @@ export enum ScopeNames { | |||
82 | }, | 120 | }, |
83 | 121 | ||
84 | { | 122 | { |
123 | fields: [ 'torrentFilename' ], | ||
124 | unique: true | ||
125 | }, | ||
126 | |||
127 | { | ||
128 | fields: [ 'filename' ], | ||
129 | unique: true | ||
130 | }, | ||
131 | |||
132 | { | ||
85 | fields: [ 'videoId', 'resolution', 'fps' ], | 133 | fields: [ 'videoId', 'resolution', 'fps' ], |
86 | unique: true, | 134 | unique: true, |
87 | where: { | 135 | where: { |
@@ -142,6 +190,24 @@ export class VideoFileModel extends Model { | |||
142 | @Column | 190 | @Column |
143 | metadataUrl: string | 191 | metadataUrl: string |
144 | 192 | ||
193 | @AllowNull(true) | ||
194 | @Column | ||
195 | fileUrl: string | ||
196 | |||
197 | // Could be null for live files | ||
198 | @AllowNull(true) | ||
199 | @Column | ||
200 | filename: string | ||
201 | |||
202 | @AllowNull(true) | ||
203 | @Column | ||
204 | torrentUrl: string | ||
205 | |||
206 | // Could be null for live files | ||
207 | @AllowNull(true) | ||
208 | @Column | ||
209 | torrentFilename: string | ||
210 | |||
145 | @ForeignKey(() => VideoModel) | 211 | @ForeignKey(() => VideoModel) |
146 | @Column | 212 | @Column |
147 | videoId: number | 213 | videoId: number |
@@ -199,6 +265,16 @@ export class VideoFileModel extends Model { | |||
199 | return !!videoFile | 265 | return !!videoFile |
200 | } | 266 | } |
201 | 267 | ||
268 | static loadWithVideoOrPlaylistByTorrentFilename (filename: string) { | ||
269 | const query = { | ||
270 | where: { | ||
271 | torrentFilename: filename | ||
272 | } | ||
273 | } | ||
274 | |||
275 | return VideoFileModel.scope(ScopeNames.WITH_VIDEO_OR_PLAYLIST).findOne(query) | ||
276 | } | ||
277 | |||
202 | static loadWithMetadata (id: number) { | 278 | static loadWithMetadata (id: number) { |
203 | return VideoFileModel.scope(ScopeNames.WITH_METADATA).findByPk(id) | 279 | return VideoFileModel.scope(ScopeNames.WITH_METADATA).findByPk(id) |
204 | } | 280 | } |
@@ -215,28 +291,11 @@ export class VideoFileModel extends Model { | |||
215 | const options = { | 291 | const options = { |
216 | where: { | 292 | where: { |
217 | id | 293 | id |
218 | }, | 294 | } |
219 | include: [ | ||
220 | { | ||
221 | model: VideoModel.unscoped(), | ||
222 | required: false, | ||
223 | where: whereVideo | ||
224 | }, | ||
225 | { | ||
226 | model: VideoStreamingPlaylistModel.unscoped(), | ||
227 | required: false, | ||
228 | include: [ | ||
229 | { | ||
230 | model: VideoModel.unscoped(), | ||
231 | required: true, | ||
232 | where: whereVideo | ||
233 | } | ||
234 | ] | ||
235 | } | ||
236 | ] | ||
237 | } | 295 | } |
238 | 296 | ||
239 | return VideoFileModel.findOne(options) | 297 | return VideoFileModel.scope({ method: [ ScopeNames.WITH_VIDEO_OR_PLAYLIST, whereVideo ] }) |
298 | .findOne(options) | ||
240 | .then(file => { | 299 | .then(file => { |
241 | // We used `required: false` so check we have at least a video or a streaming playlist | 300 | // We used `required: false` so check we have at least a video or a streaming playlist |
242 | if (!file.Video && !file.VideoStreamingPlaylist) return null | 301 | if (!file.Video && !file.VideoStreamingPlaylist) return null |
@@ -348,6 +407,10 @@ export class VideoFileModel extends Model { | |||
348 | return (this as MVideoFileStreamingPlaylistVideo).VideoStreamingPlaylist | 407 | return (this as MVideoFileStreamingPlaylistVideo).VideoStreamingPlaylist |
349 | } | 408 | } |
350 | 409 | ||
410 | getVideo (this: MVideoFileVideo | MVideoFileStreamingPlaylistVideo): MVideo { | ||
411 | return extractVideo(this.getVideoOrStreamingPlaylist()) | ||
412 | } | ||
413 | |||
351 | isAudio () { | 414 | isAudio () { |
352 | return !!MIMETYPES.AUDIO.EXT_MIMETYPE[this.extname] | 415 | return !!MIMETYPES.AUDIO.EXT_MIMETYPE[this.extname] |
353 | } | 416 | } |
@@ -360,6 +423,62 @@ export class VideoFileModel extends Model { | |||
360 | return !!this.videoStreamingPlaylistId | 423 | return !!this.videoStreamingPlaylistId |
361 | } | 424 | } |
362 | 425 | ||
426 | getFileUrl (video: MVideoWithHost) { | ||
427 | if (!this.Video) this.Video = video as VideoModel | ||
428 | |||
429 | if (video.isOwned()) return WEBSERVER.URL + this.getFileStaticPath(video) | ||
430 | if (this.fileUrl) return this.fileUrl | ||
431 | |||
432 | // Fallback if we don't have a file URL | ||
433 | return buildRemoteVideoBaseUrl(video, this.getFileStaticPath(video)) | ||
434 | } | ||
435 | |||
436 | getFileStaticPath (video: MVideo) { | ||
437 | if (this.isHLS()) return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, video.uuid, this.filename) | ||
438 | |||
439 | return join(STATIC_PATHS.WEBSEED, this.filename) | ||
440 | } | ||
441 | |||
442 | getFileDownloadUrl (video: MVideoWithHost) { | ||
443 | const basePath = this.isHLS() | ||
444 | ? STATIC_DOWNLOAD_PATHS.HLS_VIDEOS | ||
445 | : STATIC_DOWNLOAD_PATHS.VIDEOS | ||
446 | const path = join(basePath, this.filename) | ||
447 | |||
448 | if (video.isOwned()) return WEBSERVER.URL + path | ||
449 | |||
450 | // FIXME: don't guess remote URL | ||
451 | return buildRemoteVideoBaseUrl(video, path) | ||
452 | } | ||
453 | |||
454 | getRemoteTorrentUrl (video: MVideoWithHost) { | ||
455 | if (video.isOwned()) throw new Error(`Video ${video.url} is not a remote video`) | ||
456 | |||
457 | if (this.torrentUrl) return this.torrentUrl | ||
458 | |||
459 | // Fallback if we don't have a torrent URL | ||
460 | return buildRemoteVideoBaseUrl(video, this.getTorrentStaticPath()) | ||
461 | } | ||
462 | |||
463 | // We proxify torrent requests so use a local URL | ||
464 | getTorrentUrl () { | ||
465 | return WEBSERVER.URL + this.getTorrentStaticPath() | ||
466 | } | ||
467 | |||
468 | getTorrentStaticPath () { | ||
469 | return join(LAZY_STATIC_PATHS.TORRENTS, this.torrentFilename) | ||
470 | } | ||
471 | |||
472 | getTorrentDownloadUrl () { | ||
473 | return WEBSERVER.URL + join(STATIC_DOWNLOAD_PATHS.TORRENTS, this.torrentFilename) | ||
474 | } | ||
475 | |||
476 | removeTorrent () { | ||
477 | const torrentPath = getTorrentFilePath(this) | ||
478 | return remove(torrentPath) | ||
479 | .catch(err => logger.warn('Cannot delete torrent %s.', torrentPath, { err })) | ||
480 | } | ||
481 | |||
363 | hasSameUniqueKeysThan (other: MVideoFile) { | 482 | hasSameUniqueKeysThan (other: MVideoFile) { |
364 | return this.fps === other.fps && | 483 | return this.fps === other.fps && |
365 | this.resolution === other.resolution && | 484 | 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 @@ | |||
1 | import { Video, VideoDetails } from '../../../shared/models/videos' | 1 | import { generateMagnetUri } from '@server/helpers/webtorrent' |
2 | import { VideoModel } from './video' | 2 | import { getLocalVideoFileMetadataUrl } from '@server/lib/video-paths' |
3 | import { VideoFile } from '@shared/models/videos/video-file.model' | ||
3 | import { ActivityTagObject, ActivityUrlObject, VideoObject } from '../../../shared/models/activitypub/objects' | 4 | import { ActivityTagObject, ActivityUrlObject, VideoObject } from '../../../shared/models/activitypub/objects' |
5 | import { Video, VideoDetails } from '../../../shared/models/videos' | ||
6 | import { VideoStreamingPlaylist } from '../../../shared/models/videos/video-streaming-playlist.model' | ||
7 | import { isArray } from '../../helpers/custom-validators/misc' | ||
4 | import { MIMETYPES, WEBSERVER } from '../../initializers/constants' | 8 | import { MIMETYPES, WEBSERVER } from '../../initializers/constants' |
5 | import { VideoCaptionModel } from './video-caption' | ||
6 | import { | 9 | import { |
7 | getLocalVideoCommentsActivityPubUrl, | 10 | getLocalVideoCommentsActivityPubUrl, |
8 | getLocalVideoDislikesActivityPubUrl, | 11 | getLocalVideoDislikesActivityPubUrl, |
9 | getLocalVideoLikesActivityPubUrl, | 12 | getLocalVideoLikesActivityPubUrl, |
10 | getLocalVideoSharesActivityPubUrl | 13 | getLocalVideoSharesActivityPubUrl |
11 | } from '../../lib/activitypub/url' | 14 | } from '../../lib/activitypub/url' |
12 | import { isArray } from '../../helpers/custom-validators/misc' | ||
13 | import { VideoStreamingPlaylist } from '../../../shared/models/videos/video-streaming-playlist.model' | ||
14 | import { | 15 | import { |
15 | MStreamingPlaylistRedundanciesOpt, | 16 | MStreamingPlaylistRedundanciesOpt, |
16 | MStreamingPlaylistVideo, | 17 | MStreamingPlaylistVideo, |
@@ -18,12 +19,12 @@ import { | |||
18 | MVideoAP, | 19 | MVideoAP, |
19 | MVideoFile, | 20 | MVideoFile, |
20 | MVideoFormattable, | 21 | MVideoFormattable, |
21 | MVideoFormattableDetails | 22 | MVideoFormattableDetails, |
23 | MVideoWithHost | ||
22 | } from '../../types/models' | 24 | } from '../../types/models' |
23 | import { MVideoFileRedundanciesOpt } from '../../types/models/video/video-file' | 25 | import { MVideoFileRedundanciesOpt } from '../../types/models/video/video-file' |
24 | import { VideoFile } from '@shared/models/videos/video-file.model' | 26 | import { VideoModel } from './video' |
25 | import { generateMagnetUri } from '@server/helpers/webtorrent' | 27 | import { VideoCaptionModel } from './video-caption' |
26 | import { extractVideo } from '@server/helpers/video' | ||
27 | 28 | ||
28 | export type VideoFormattingJSONOptions = { | 29 | export type VideoFormattingJSONOptions = { |
29 | completeDescription?: boolean | 30 | completeDescription?: boolean |
@@ -153,12 +154,15 @@ function videoModelToFormattedDetailsJSON (video: MVideoFormattableDetails): Vid | |||
153 | } | 154 | } |
154 | 155 | ||
155 | // Format and sort video files | 156 | // Format and sort video files |
156 | detailsJson.files = videoFilesModelToFormattedJSON(video, baseUrlHttp, baseUrlWs, video.VideoFiles) | 157 | detailsJson.files = videoFilesModelToFormattedJSON(video, video, baseUrlHttp, baseUrlWs, video.VideoFiles) |
157 | 158 | ||
158 | return Object.assign(formattedJson, detailsJson) | 159 | return Object.assign(formattedJson, detailsJson) |
159 | } | 160 | } |
160 | 161 | ||
161 | function streamingPlaylistsModelToFormattedJSON (video: MVideo, playlists: MStreamingPlaylistRedundanciesOpt[]): VideoStreamingPlaylist[] { | 162 | function streamingPlaylistsModelToFormattedJSON ( |
163 | video: MVideoFormattableDetails, | ||
164 | playlists: MStreamingPlaylistRedundanciesOpt[] | ||
165 | ): VideoStreamingPlaylist[] { | ||
162 | if (isArray(playlists) === false) return [] | 166 | if (isArray(playlists) === false) return [] |
163 | 167 | ||
164 | const { baseUrlHttp, baseUrlWs } = video.getBaseUrls() | 168 | const { baseUrlHttp, baseUrlWs } = video.getBaseUrls() |
@@ -171,7 +175,7 @@ function streamingPlaylistsModelToFormattedJSON (video: MVideo, playlists: MStre | |||
171 | ? playlist.RedundancyVideos.map(r => ({ baseUrl: r.fileUrl })) | 175 | ? playlist.RedundancyVideos.map(r => ({ baseUrl: r.fileUrl })) |
172 | : [] | 176 | : [] |
173 | 177 | ||
174 | const files = videoFilesModelToFormattedJSON(playlistWithVideo, baseUrlHttp, baseUrlWs, playlist.VideoFiles) | 178 | const files = videoFilesModelToFormattedJSON(playlistWithVideo, video, baseUrlHttp, baseUrlWs, playlist.VideoFiles) |
175 | 179 | ||
176 | return { | 180 | return { |
177 | id: playlist.id, | 181 | id: playlist.id, |
@@ -190,14 +194,14 @@ function sortByResolutionDesc (fileA: MVideoFile, fileB: MVideoFile) { | |||
190 | return -1 | 194 | return -1 |
191 | } | 195 | } |
192 | 196 | ||
197 | // FIXME: refactor/merge model and video arguments | ||
193 | function videoFilesModelToFormattedJSON ( | 198 | function videoFilesModelToFormattedJSON ( |
194 | model: MVideo | MStreamingPlaylistVideo, | 199 | model: MVideo | MStreamingPlaylistVideo, |
200 | video: MVideoFormattableDetails, | ||
195 | baseUrlHttp: string, | 201 | baseUrlHttp: string, |
196 | baseUrlWs: string, | 202 | baseUrlWs: string, |
197 | videoFiles: MVideoFileRedundanciesOpt[] | 203 | videoFiles: MVideoFileRedundanciesOpt[] |
198 | ): VideoFile[] { | 204 | ): VideoFile[] { |
199 | const video = extractVideo(model) | ||
200 | |||
201 | return [ ...videoFiles ] | 205 | return [ ...videoFiles ] |
202 | .filter(f => !f.isLive()) | 206 | .filter(f => !f.isLive()) |
203 | .sort(sortByResolutionDesc) | 207 | .sort(sortByResolutionDesc) |
@@ -207,21 +211,29 @@ function videoFilesModelToFormattedJSON ( | |||
207 | id: videoFile.resolution, | 211 | id: videoFile.resolution, |
208 | label: videoFile.resolution + 'p' | 212 | label: videoFile.resolution + 'p' |
209 | }, | 213 | }, |
210 | magnetUri: generateMagnetUri(model, videoFile, baseUrlHttp, baseUrlWs), | 214 | |
215 | // FIXME: deprecated in 3.2 | ||
216 | magnetUri: generateMagnetUri(model, video, videoFile, baseUrlHttp, baseUrlWs), | ||
217 | |||
211 | size: videoFile.size, | 218 | size: videoFile.size, |
212 | fps: videoFile.fps, | 219 | fps: videoFile.fps, |
213 | torrentUrl: model.getTorrentUrl(videoFile, baseUrlHttp), | 220 | |
214 | torrentDownloadUrl: model.getTorrentDownloadUrl(videoFile, baseUrlHttp), | 221 | torrentUrl: videoFile.getTorrentUrl(), |
215 | fileUrl: model.getVideoFileUrl(videoFile, baseUrlHttp), | 222 | torrentDownloadUrl: videoFile.getTorrentDownloadUrl(), |
216 | fileDownloadUrl: model.getVideoFileDownloadUrl(videoFile, baseUrlHttp), | 223 | |
217 | metadataUrl: video.getVideoFileMetadataUrl(videoFile, baseUrlHttp) | 224 | fileUrl: videoFile.getFileUrl(video), |
225 | fileDownloadUrl: videoFile.getFileDownloadUrl(video), | ||
226 | |||
227 | metadataUrl: videoFile.metadataUrl ?? getLocalVideoFileMetadataUrl(video, videoFile) | ||
218 | } as VideoFile | 228 | } as VideoFile |
219 | }) | 229 | }) |
220 | } | 230 | } |
221 | 231 | ||
232 | // FIXME: refactor/merge model and video arguments | ||
222 | function addVideoFilesInAPAcc ( | 233 | function addVideoFilesInAPAcc ( |
223 | acc: ActivityUrlObject[] | ActivityTagObject[], | 234 | acc: ActivityUrlObject[] | ActivityTagObject[], |
224 | model: MVideoAP | MStreamingPlaylistVideo, | 235 | model: MVideoAP | MStreamingPlaylistVideo, |
236 | video: MVideoWithHost, | ||
225 | baseUrlHttp: string, | 237 | baseUrlHttp: string, |
226 | baseUrlWs: string, | 238 | baseUrlWs: string, |
227 | files: MVideoFile[] | 239 | files: MVideoFile[] |
@@ -234,7 +246,7 @@ function addVideoFilesInAPAcc ( | |||
234 | acc.push({ | 246 | acc.push({ |
235 | type: 'Link', | 247 | type: 'Link', |
236 | mediaType: MIMETYPES.VIDEO.EXT_MIMETYPE[file.extname] as any, | 248 | mediaType: MIMETYPES.VIDEO.EXT_MIMETYPE[file.extname] as any, |
237 | href: model.getVideoFileUrl(file, baseUrlHttp), | 249 | href: file.getFileUrl(video), |
238 | height: file.resolution, | 250 | height: file.resolution, |
239 | size: file.size, | 251 | size: file.size, |
240 | fps: file.fps | 252 | fps: file.fps |
@@ -244,7 +256,7 @@ function addVideoFilesInAPAcc ( | |||
244 | type: 'Link', | 256 | type: 'Link', |
245 | rel: [ 'metadata', MIMETYPES.VIDEO.EXT_MIMETYPE[file.extname] ], | 257 | rel: [ 'metadata', MIMETYPES.VIDEO.EXT_MIMETYPE[file.extname] ], |
246 | mediaType: 'application/json' as 'application/json', | 258 | mediaType: 'application/json' as 'application/json', |
247 | href: extractVideo(model).getVideoFileMetadataUrl(file, baseUrlHttp), | 259 | href: getLocalVideoFileMetadataUrl(video, file), |
248 | height: file.resolution, | 260 | height: file.resolution, |
249 | fps: file.fps | 261 | fps: file.fps |
250 | }) | 262 | }) |
@@ -252,14 +264,14 @@ function addVideoFilesInAPAcc ( | |||
252 | acc.push({ | 264 | acc.push({ |
253 | type: 'Link', | 265 | type: 'Link', |
254 | mediaType: 'application/x-bittorrent' as 'application/x-bittorrent', | 266 | mediaType: 'application/x-bittorrent' as 'application/x-bittorrent', |
255 | href: model.getTorrentUrl(file, baseUrlHttp), | 267 | href: file.getTorrentUrl(), |
256 | height: file.resolution | 268 | height: file.resolution |
257 | }) | 269 | }) |
258 | 270 | ||
259 | acc.push({ | 271 | acc.push({ |
260 | type: 'Link', | 272 | type: 'Link', |
261 | mediaType: 'application/x-bittorrent;x-scheme-handler/magnet' as 'application/x-bittorrent;x-scheme-handler/magnet', | 273 | mediaType: 'application/x-bittorrent;x-scheme-handler/magnet' as 'application/x-bittorrent;x-scheme-handler/magnet', |
262 | href: generateMagnetUri(model, file, baseUrlHttp, baseUrlWs), | 274 | href: generateMagnetUri(model, video, file, baseUrlHttp, baseUrlWs), |
263 | height: file.resolution | 275 | height: file.resolution |
264 | }) | 276 | }) |
265 | } | 277 | } |
@@ -307,7 +319,7 @@ function videoModelToActivityPubObject (video: MVideoAP): VideoObject { | |||
307 | } | 319 | } |
308 | ] | 320 | ] |
309 | 321 | ||
310 | addVideoFilesInAPAcc(url, video, baseUrlHttp, baseUrlWs, video.VideoFiles || []) | 322 | addVideoFilesInAPAcc(url, video, video, baseUrlHttp, baseUrlWs, video.VideoFiles || []) |
311 | 323 | ||
312 | for (const playlist of (video.VideoStreamingPlaylists || [])) { | 324 | for (const playlist of (video.VideoStreamingPlaylists || [])) { |
313 | const tag = playlist.p2pMediaLoaderInfohashes | 325 | const tag = playlist.p2pMediaLoaderInfohashes |
@@ -320,7 +332,7 @@ function videoModelToActivityPubObject (video: MVideoAP): VideoObject { | |||
320 | }) | 332 | }) |
321 | 333 | ||
322 | const playlistWithVideo = Object.assign(playlist, { Video: video }) | 334 | const playlistWithVideo = Object.assign(playlist, { Video: video }) |
323 | addVideoFilesInAPAcc(tag, playlistWithVideo, baseUrlHttp, baseUrlWs, playlist.VideoFiles || []) | 335 | addVideoFilesInAPAcc(tag, playlistWithVideo, video, baseUrlHttp, baseUrlWs, playlist.VideoFiles || []) |
324 | 336 | ||
325 | url.push({ | 337 | url.push({ |
326 | type: 'Link', | 338 | 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 | |||
516 | '"VideoFiles"."resolution"': '"VideoFiles.resolution"', | 516 | '"VideoFiles"."resolution"': '"VideoFiles.resolution"', |
517 | '"VideoFiles"."size"': '"VideoFiles.size"', | 517 | '"VideoFiles"."size"': '"VideoFiles.size"', |
518 | '"VideoFiles"."extname"': '"VideoFiles.extname"', | 518 | '"VideoFiles"."extname"': '"VideoFiles.extname"', |
519 | '"VideoFiles"."filename"': '"VideoFiles.filename"', | ||
520 | '"VideoFiles"."fileUrl"': '"VideoFiles.fileUrl"', | ||
521 | '"VideoFiles"."torrentFilename"': '"VideoFiles.torrentFilename"', | ||
522 | '"VideoFiles"."torrentUrl"': '"VideoFiles.torrentUrl"', | ||
519 | '"VideoFiles"."infoHash"': '"VideoFiles.infoHash"', | 523 | '"VideoFiles"."infoHash"': '"VideoFiles.infoHash"', |
520 | '"VideoFiles"."fps"': '"VideoFiles.fps"', | 524 | '"VideoFiles"."fps"': '"VideoFiles.fps"', |
521 | '"VideoFiles"."videoId"': '"VideoFiles.videoId"', | 525 | '"VideoFiles"."videoId"': '"VideoFiles.videoId"', |
@@ -529,6 +533,10 @@ function wrapForAPIResults (baseQuery: string, replacements: any, options: Build | |||
529 | '"VideoStreamingPlaylists->VideoFiles"."resolution"': '"VideoStreamingPlaylists.VideoFiles.resolution"', | 533 | '"VideoStreamingPlaylists->VideoFiles"."resolution"': '"VideoStreamingPlaylists.VideoFiles.resolution"', |
530 | '"VideoStreamingPlaylists->VideoFiles"."size"': '"VideoStreamingPlaylists.VideoFiles.size"', | 534 | '"VideoStreamingPlaylists->VideoFiles"."size"': '"VideoStreamingPlaylists.VideoFiles.size"', |
531 | '"VideoStreamingPlaylists->VideoFiles"."extname"': '"VideoStreamingPlaylists.VideoFiles.extname"', | 535 | '"VideoStreamingPlaylists->VideoFiles"."extname"': '"VideoStreamingPlaylists.VideoFiles.extname"', |
536 | '"VideoStreamingPlaylists->VideoFiles"."filename"': '"VideoStreamingPlaylists.VideoFiles.filename"', | ||
537 | '"VideoStreamingPlaylists->VideoFiles"."fileUrl"': '"VideoStreamingPlaylists.VideoFiles.fileUrl"', | ||
538 | '"VideoStreamingPlaylists->VideoFiles"."torrentFilename"': '"VideoStreamingPlaylists.VideoFiles.torrentFilename"', | ||
539 | '"VideoStreamingPlaylists->VideoFiles"."torrentUrl"': '"VideoStreamingPlaylists.VideoFiles.torrentUrl"', | ||
532 | '"VideoStreamingPlaylists->VideoFiles"."infoHash"': '"VideoStreamingPlaylists.VideoFiles.infoHash"', | 540 | '"VideoStreamingPlaylists->VideoFiles"."infoHash"': '"VideoStreamingPlaylists.VideoFiles.infoHash"', |
533 | '"VideoStreamingPlaylists->VideoFiles"."fps"': '"VideoStreamingPlaylists.VideoFiles.fps"', | 541 | '"VideoStreamingPlaylists->VideoFiles"."fps"': '"VideoStreamingPlaylists.VideoFiles.fps"', |
534 | '"VideoStreamingPlaylists->VideoFiles"."videoStreamingPlaylistId"': '"VideoStreamingPlaylists.VideoFiles.videoStreamingPlaylistId"', | 542 | '"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 @@ | |||
1 | import * as memoizee from 'memoizee' | ||
2 | import { join } from 'path' | ||
3 | import { Op, QueryTypes } from 'sequelize' | ||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 4 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' | 5 | import { VideoFileModel } from '@server/models/video/video-file' |
3 | import { throwIfNotValid } from '../utils' | 6 | import { MStreamingPlaylist } from '@server/types/models' |
4 | import { VideoModel } from './video' | ||
5 | import { VideoRedundancyModel } from '../redundancy/video-redundancy' | ||
6 | import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' | 7 | import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' |
7 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | ||
8 | import { | ||
9 | CONSTRAINTS_FIELDS, | ||
10 | MEMOIZE_LENGTH, | ||
11 | MEMOIZE_TTL, | ||
12 | P2P_MEDIA_LOADER_PEER_VERSION, | ||
13 | STATIC_DOWNLOAD_PATHS, | ||
14 | STATIC_PATHS | ||
15 | } from '../../initializers/constants' | ||
16 | import { join } from 'path' | ||
17 | import { sha1 } from '../../helpers/core-utils' | 8 | import { sha1 } from '../../helpers/core-utils' |
9 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | ||
18 | import { isArrayOf } from '../../helpers/custom-validators/misc' | 10 | import { isArrayOf } from '../../helpers/custom-validators/misc' |
19 | import { Op, QueryTypes } from 'sequelize' | 11 | import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' |
20 | import { MStreamingPlaylist, MStreamingPlaylistVideo, MVideoFile } from '@server/types/models' | 12 | import { CONSTRAINTS_FIELDS, MEMOIZE_LENGTH, MEMOIZE_TTL, P2P_MEDIA_LOADER_PEER_VERSION, STATIC_PATHS } from '../../initializers/constants' |
21 | import { VideoFileModel } from '@server/models/video/video-file' | 13 | import { VideoRedundancyModel } from '../redundancy/video-redundancy' |
22 | import { getTorrentFileName, getTorrentFilePath, getVideoFilename } from '@server/lib/video-paths' | 14 | import { throwIfNotValid } from '../utils' |
23 | import * as memoizee from 'memoizee' | 15 | import { VideoModel } from './video' |
24 | import { remove } from 'fs-extra' | ||
25 | import { logger } from '@server/helpers/logger' | ||
26 | 16 | ||
27 | @Table({ | 17 | @Table({ |
28 | tableName: 'videoStreamingPlaylist', | 18 | tableName: 'videoStreamingPlaylist', |
@@ -196,26 +186,6 @@ export class VideoStreamingPlaylistModel extends Model { | |||
196 | return 'unknown' | 186 | return 'unknown' |
197 | } | 187 | } |
198 | 188 | ||
199 | getVideoRedundancyUrl (baseUrlHttp: string) { | ||
200 | return baseUrlHttp + STATIC_PATHS.REDUNDANCY + this.getStringType() + '/' + this.Video.uuid | ||
201 | } | ||
202 | |||
203 | getTorrentDownloadUrl (videoFile: MVideoFile, baseUrlHttp: string) { | ||
204 | return baseUrlHttp + STATIC_DOWNLOAD_PATHS.TORRENTS + getTorrentFileName(this, videoFile) | ||
205 | } | ||
206 | |||
207 | getVideoFileDownloadUrl (videoFile: MVideoFile, baseUrlHttp: string) { | ||
208 | return baseUrlHttp + STATIC_DOWNLOAD_PATHS.HLS_VIDEOS + getVideoFilename(this, videoFile) | ||
209 | } | ||
210 | |||
211 | getVideoFileUrl (videoFile: MVideoFile, baseUrlHttp: string) { | ||
212 | return baseUrlHttp + join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, this.Video.uuid, getVideoFilename(this, videoFile)) | ||
213 | } | ||
214 | |||
215 | getTorrentUrl (videoFile: MVideoFile, baseUrlHttp: string) { | ||
216 | return baseUrlHttp + join(STATIC_PATHS.TORRENTS, getTorrentFileName(this, videoFile)) | ||
217 | } | ||
218 | |||
219 | getTrackerUrls (baseUrlHttp: string, baseUrlWs: string) { | 189 | getTrackerUrls (baseUrlHttp: string, baseUrlWs: string) { |
220 | return [ baseUrlWs + '/tracker/socket', baseUrlHttp + '/tracker/announce' ] | 190 | return [ baseUrlWs + '/tracker/socket', baseUrlHttp + '/tracker/announce' ] |
221 | } | 191 | } |
@@ -224,10 +194,4 @@ export class VideoStreamingPlaylistModel extends Model { | |||
224 | return this.type === other.type && | 194 | return this.type === other.type && |
225 | this.videoId === other.videoId | 195 | this.videoId === other.videoId |
226 | } | 196 | } |
227 | |||
228 | removeTorrent (this: MStreamingPlaylistVideo, videoFile: MVideoFile) { | ||
229 | const torrentPath = getTorrentFilePath(this, videoFile) | ||
230 | return remove(torrentPath) | ||
231 | .catch(err => logger.warn('Cannot delete torrent %s.', torrentPath, { err })) | ||
232 | } | ||
233 | } | 197 | } |
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 { | |||
24 | Table, | 24 | Table, |
25 | UpdatedAt | 25 | UpdatedAt |
26 | } from 'sequelize-typescript' | 26 | } from 'sequelize-typescript' |
27 | import { v4 as uuidv4 } from 'uuid' | ||
27 | import { buildNSFWFilter } from '@server/helpers/express-utils' | 28 | import { buildNSFWFilter } from '@server/helpers/express-utils' |
28 | import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video' | 29 | import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video' |
29 | import { LiveManager } from '@server/lib/live-manager' | 30 | import { LiveManager } from '@server/lib/live-manager' |
30 | import { getHLSDirectory, getTorrentFileName, getTorrentFilePath, getVideoFilename, getVideoFilePath } from '@server/lib/video-paths' | 31 | import { getHLSDirectory, getVideoFilePath } from '@server/lib/video-paths' |
31 | import { getServerActor } from '@server/models/application/application' | 32 | import { getServerActor } from '@server/models/application/application' |
32 | import { ModelCache } from '@server/models/model-cache' | 33 | import { ModelCache } from '@server/models/model-cache' |
33 | import { VideoFile } from '@shared/models/videos/video-file.model' | 34 | import { VideoFile } from '@shared/models/videos/video-file.model' |
@@ -60,7 +61,6 @@ import { | |||
60 | CONSTRAINTS_FIELDS, | 61 | CONSTRAINTS_FIELDS, |
61 | LAZY_STATIC_PATHS, | 62 | LAZY_STATIC_PATHS, |
62 | REMOTE_SCHEME, | 63 | REMOTE_SCHEME, |
63 | STATIC_DOWNLOAD_PATHS, | ||
64 | STATIC_PATHS, | 64 | STATIC_PATHS, |
65 | VIDEO_CATEGORIES, | 65 | VIDEO_CATEGORIES, |
66 | VIDEO_LANGUAGES, | 66 | VIDEO_LANGUAGES, |
@@ -78,6 +78,7 @@ import { | |||
78 | MStreamingPlaylistFilesVideo, | 78 | MStreamingPlaylistFilesVideo, |
79 | MUserAccountId, | 79 | MUserAccountId, |
80 | MUserId, | 80 | MUserId, |
81 | MVideo, | ||
81 | MVideoAccountLight, | 82 | MVideoAccountLight, |
82 | MVideoAccountLightBlacklistAllFiles, | 83 | MVideoAccountLightBlacklistAllFiles, |
83 | MVideoAP, | 84 | MVideoAP, |
@@ -130,7 +131,6 @@ import { VideoShareModel } from './video-share' | |||
130 | import { VideoStreamingPlaylistModel } from './video-streaming-playlist' | 131 | import { VideoStreamingPlaylistModel } from './video-streaming-playlist' |
131 | import { VideoTagModel } from './video-tag' | 132 | import { VideoTagModel } from './video-tag' |
132 | import { VideoViewModel } from './video-view' | 133 | import { VideoViewModel } from './video-view' |
133 | import { v4 as uuidv4 } from 'uuid' | ||
134 | 134 | ||
135 | export enum ScopeNames { | 135 | export enum ScopeNames { |
136 | AVAILABLE_FOR_LIST_IDS = 'AVAILABLE_FOR_LIST_IDS', | 136 | AVAILABLE_FOR_LIST_IDS = 'AVAILABLE_FOR_LIST_IDS', |
@@ -790,7 +790,7 @@ export class VideoModel extends Model { | |||
790 | // Remove physical files and torrents | 790 | // Remove physical files and torrents |
791 | instance.VideoFiles.forEach(file => { | 791 | instance.VideoFiles.forEach(file => { |
792 | tasks.push(instance.removeFile(file)) | 792 | tasks.push(instance.removeFile(file)) |
793 | tasks.push(instance.removeTorrent(file)) | 793 | tasks.push(file.removeTorrent()) |
794 | }) | 794 | }) |
795 | 795 | ||
796 | // Remove playlists file | 796 | // Remove playlists file |
@@ -853,18 +853,14 @@ export class VideoModel extends Model { | |||
853 | return undefined | 853 | return undefined |
854 | } | 854 | } |
855 | 855 | ||
856 | static listLocal (): Promise<MVideoWithAllFiles[]> { | 856 | static listLocal (): Promise<MVideo[]> { |
857 | const query = { | 857 | const query = { |
858 | where: { | 858 | where: { |
859 | remote: false | 859 | remote: false |
860 | } | 860 | } |
861 | } | 861 | } |
862 | 862 | ||
863 | return VideoModel.scope([ | 863 | return VideoModel.findAll(query) |
864 | ScopeNames.WITH_WEBTORRENT_FILES, | ||
865 | ScopeNames.WITH_STREAMING_PLAYLISTS, | ||
866 | ScopeNames.WITH_THUMBNAILS | ||
867 | ]).findAll(query) | ||
868 | } | 864 | } |
869 | 865 | ||
870 | static listAllAndSharedByActorForOutbox (actorId: number, start: number, count: number) { | 866 | static listAllAndSharedByActorForOutbox (actorId: number, start: number, count: number) { |
@@ -1623,6 +1619,10 @@ export class VideoModel extends Model { | |||
1623 | 'resolution', | 1619 | 'resolution', |
1624 | 'size', | 1620 | 'size', |
1625 | 'extname', | 1621 | 'extname', |
1622 | 'filename', | ||
1623 | 'fileUrl', | ||
1624 | 'torrentFilename', | ||
1625 | 'torrentUrl', | ||
1626 | 'infoHash', | 1626 | 'infoHash', |
1627 | 'fps', | 1627 | 'fps', |
1628 | 'videoId', | 1628 | 'videoId', |
@@ -1891,14 +1891,14 @@ export class VideoModel extends Model { | |||
1891 | let files: VideoFile[] = [] | 1891 | let files: VideoFile[] = [] |
1892 | 1892 | ||
1893 | if (Array.isArray(this.VideoFiles)) { | 1893 | if (Array.isArray(this.VideoFiles)) { |
1894 | const result = videoFilesModelToFormattedJSON(this, baseUrlHttp, baseUrlWs, this.VideoFiles) | 1894 | const result = videoFilesModelToFormattedJSON(this, this, baseUrlHttp, baseUrlWs, this.VideoFiles) |
1895 | files = files.concat(result) | 1895 | files = files.concat(result) |
1896 | } | 1896 | } |
1897 | 1897 | ||
1898 | for (const p of (this.VideoStreamingPlaylists || [])) { | 1898 | for (const p of (this.VideoStreamingPlaylists || [])) { |
1899 | p.Video = this | 1899 | p.Video = this |
1900 | 1900 | ||
1901 | const result = videoFilesModelToFormattedJSON(p, baseUrlHttp, baseUrlWs, p.VideoFiles) | 1901 | const result = videoFilesModelToFormattedJSON(p, this, baseUrlHttp, baseUrlWs, p.VideoFiles) |
1902 | files = files.concat(result) | 1902 | files = files.concat(result) |
1903 | } | 1903 | } |
1904 | 1904 | ||
@@ -1956,12 +1956,6 @@ export class VideoModel extends Model { | |||
1956 | .catch(err => logger.warn('Cannot delete file %s.', filePath, { err })) | 1956 | .catch(err => logger.warn('Cannot delete file %s.', filePath, { err })) |
1957 | } | 1957 | } |
1958 | 1958 | ||
1959 | removeTorrent (videoFile: MVideoFile) { | ||
1960 | const torrentPath = getTorrentFilePath(this, videoFile) | ||
1961 | return remove(torrentPath) | ||
1962 | .catch(err => logger.warn('Cannot delete torrent %s.', torrentPath, { err })) | ||
1963 | } | ||
1964 | |||
1965 | async removeStreamingPlaylistFiles (streamingPlaylist: MStreamingPlaylist, isRedundancy = false) { | 1959 | async removeStreamingPlaylistFiles (streamingPlaylist: MStreamingPlaylist, isRedundancy = false) { |
1966 | const directoryPath = getHLSDirectory(this, isRedundancy) | 1960 | const directoryPath = getHLSDirectory(this, isRedundancy) |
1967 | 1961 | ||
@@ -1977,7 +1971,7 @@ export class VideoModel extends Model { | |||
1977 | 1971 | ||
1978 | // Remove physical files and torrents | 1972 | // Remove physical files and torrents |
1979 | await Promise.all( | 1973 | await Promise.all( |
1980 | streamingPlaylistWithFiles.VideoFiles.map(file => streamingPlaylistWithFiles.removeTorrent(file)) | 1974 | streamingPlaylistWithFiles.VideoFiles.map(file => file.removeTorrent()) |
1981 | ) | 1975 | ) |
1982 | } | 1976 | } |
1983 | } | 1977 | } |
@@ -2054,34 +2048,6 @@ export class VideoModel extends Model { | |||
2054 | return [ baseUrlWs + '/tracker/socket', baseUrlHttp + '/tracker/announce' ] | 2048 | return [ baseUrlWs + '/tracker/socket', baseUrlHttp + '/tracker/announce' ] |
2055 | } | 2049 | } |
2056 | 2050 | ||
2057 | getTorrentUrl (videoFile: MVideoFile, baseUrlHttp: string) { | ||
2058 | return baseUrlHttp + STATIC_PATHS.TORRENTS + getTorrentFileName(this, videoFile) | ||
2059 | } | ||
2060 | |||
2061 | getTorrentDownloadUrl (videoFile: MVideoFile, baseUrlHttp: string) { | ||
2062 | return baseUrlHttp + STATIC_DOWNLOAD_PATHS.TORRENTS + getTorrentFileName(this, videoFile) | ||
2063 | } | ||
2064 | |||
2065 | getVideoFileUrl (videoFile: MVideoFile, baseUrlHttp: string) { | ||
2066 | return baseUrlHttp + STATIC_PATHS.WEBSEED + getVideoFilename(this, videoFile) | ||
2067 | } | ||
2068 | |||
2069 | getVideoFileMetadataUrl (videoFile: MVideoFile, baseUrlHttp: string) { | ||
2070 | const path = '/api/v1/videos/' | ||
2071 | |||
2072 | return this.isOwned() | ||
2073 | ? baseUrlHttp + path + this.uuid + '/metadata/' + videoFile.id | ||
2074 | : videoFile.metadataUrl | ||
2075 | } | ||
2076 | |||
2077 | getVideoRedundancyUrl (videoFile: MVideoFile, baseUrlHttp: string) { | ||
2078 | return baseUrlHttp + STATIC_PATHS.REDUNDANCY + getVideoFilename(this, videoFile) | ||
2079 | } | ||
2080 | |||
2081 | getVideoFileDownloadUrl (videoFile: MVideoFile, baseUrlHttp: string) { | ||
2082 | return baseUrlHttp + STATIC_DOWNLOAD_PATHS.VIDEOS + getVideoFilename(this, videoFile) | ||
2083 | } | ||
2084 | |||
2085 | getBandwidthBits (videoFile: MVideoFile) { | 2051 | getBandwidthBits (videoFile: MVideoFile) { |
2086 | return Math.ceil((videoFile.size * 8) / this.duration) | 2052 | return Math.ceil((videoFile.size * 8) / this.duration) |
2087 | } | 2053 | } |