diff options
author | Chocobozzz <me@florianbigard.com> | 2018-09-11 16:27:07 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2018-09-13 14:05:49 +0200 |
commit | c48e82b5e0478434de30626d14594a97f2402e7c (patch) | |
tree | a78e5272bd0fe4f5b41831e571e02d05f1515b82 /server/models/video | |
parent | a651038487faa838bda3ce04695b08bc65baff70 (diff) | |
download | PeerTube-c48e82b5e0478434de30626d14594a97f2402e7c.tar.gz PeerTube-c48e82b5e0478434de30626d14594a97f2402e7c.tar.zst PeerTube-c48e82b5e0478434de30626d14594a97f2402e7c.zip |
Basic video redundancy implementation
Diffstat (limited to 'server/models/video')
-rw-r--r-- | server/models/video/video-file.ts | 25 | ||||
-rw-r--r-- | server/models/video/video.ts | 73 |
2 files changed, 65 insertions, 33 deletions
diff --git a/server/models/video/video-file.ts b/server/models/video/video-file.ts index 3bc4855f3..0907ea569 100644 --- a/server/models/video/video-file.ts +++ b/server/models/video/video-file.ts | |||
@@ -1,5 +1,18 @@ | |||
1 | import { values } from 'lodash' | 1 | import { values } from 'lodash' |
2 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 2 | import { |
3 | AllowNull, | ||
4 | BelongsTo, | ||
5 | Column, | ||
6 | CreatedAt, | ||
7 | DataType, | ||
8 | Default, | ||
9 | ForeignKey, | ||
10 | HasMany, | ||
11 | Is, | ||
12 | Model, | ||
13 | Table, | ||
14 | UpdatedAt | ||
15 | } from 'sequelize-typescript' | ||
3 | import { | 16 | import { |
4 | isVideoFileInfoHashValid, | 17 | isVideoFileInfoHashValid, |
5 | isVideoFileResolutionValid, | 18 | isVideoFileResolutionValid, |
@@ -10,6 +23,7 @@ import { CONSTRAINTS_FIELDS } from '../../initializers' | |||
10 | import { throwIfNotValid } from '../utils' | 23 | import { throwIfNotValid } from '../utils' |
11 | import { VideoModel } from './video' | 24 | import { VideoModel } from './video' |
12 | import * as Sequelize from 'sequelize' | 25 | import * as Sequelize from 'sequelize' |
26 | import { VideoRedundancyModel } from '../redundancy/video-redundancy' | ||
13 | 27 | ||
14 | @Table({ | 28 | @Table({ |
15 | tableName: 'videoFile', | 29 | tableName: 'videoFile', |
@@ -70,6 +84,15 @@ export class VideoFileModel extends Model<VideoFileModel> { | |||
70 | }) | 84 | }) |
71 | Video: VideoModel | 85 | Video: VideoModel |
72 | 86 | ||
87 | @HasMany(() => VideoRedundancyModel, { | ||
88 | foreignKey: { | ||
89 | allowNull: false | ||
90 | }, | ||
91 | onDelete: 'CASCADE', | ||
92 | hooks: true | ||
93 | }) | ||
94 | RedundancyVideos: VideoRedundancyModel[] | ||
95 | |||
73 | static isInfohashExists (infoHash: string) { | 96 | static isInfohashExists (infoHash: string) { |
74 | const query = 'SELECT 1 FROM "videoFile" WHERE "infoHash" = $infoHash LIMIT 1' | 97 | const query = 'SELECT 1 FROM "videoFile" WHERE "infoHash" = $infoHash LIMIT 1' |
75 | const options = { | 98 | const options = { |
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 86316653f..27c631dcd 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -27,13 +27,13 @@ import { | |||
27 | Table, | 27 | Table, |
28 | UpdatedAt | 28 | UpdatedAt |
29 | } from 'sequelize-typescript' | 29 | } from 'sequelize-typescript' |
30 | import { VideoPrivacy, VideoResolution, VideoState } from '../../../shared' | 30 | import { ActivityUrlObject, VideoPrivacy, VideoResolution, VideoState } from '../../../shared' |
31 | import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' | 31 | import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' |
32 | import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos' | 32 | import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos' |
33 | import { VideoFilter } from '../../../shared/models/videos/video-query.type' | 33 | import { VideoFilter } from '../../../shared/models/videos/video-query.type' |
34 | import { createTorrentPromise, peertubeTruncate } from '../../helpers/core-utils' | 34 | import { createTorrentPromise, peertubeTruncate } from '../../helpers/core-utils' |
35 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 35 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
36 | import { isBooleanValid } from '../../helpers/custom-validators/misc' | 36 | import { isArray, isBooleanValid } from '../../helpers/custom-validators/misc' |
37 | import { | 37 | import { |
38 | isVideoCategoryValid, | 38 | isVideoCategoryValid, |
39 | isVideoDescriptionValid, | 39 | isVideoDescriptionValid, |
@@ -90,6 +90,7 @@ import { VideoCaptionModel } from './video-caption' | |||
90 | import { VideoBlacklistModel } from './video-blacklist' | 90 | import { VideoBlacklistModel } from './video-blacklist' |
91 | import { copy, remove, rename, stat, writeFile } from 'fs-extra' | 91 | import { copy, remove, rename, stat, writeFile } from 'fs-extra' |
92 | import { VideoViewModel } from './video-views' | 92 | import { VideoViewModel } from './video-views' |
93 | import { VideoRedundancyModel } from '../redundancy/video-redundancy' | ||
93 | 94 | ||
94 | // FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation | 95 | // FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation |
95 | const indexes: Sequelize.DefineIndexesOptions[] = [ | 96 | const indexes: Sequelize.DefineIndexesOptions[] = [ |
@@ -470,7 +471,13 @@ type AvailableForListIDsOptions = { | |||
470 | include: [ | 471 | include: [ |
471 | { | 472 | { |
472 | model: () => VideoFileModel.unscoped(), | 473 | model: () => VideoFileModel.unscoped(), |
473 | required: false | 474 | required: false, |
475 | include: [ | ||
476 | { | ||
477 | model: () => VideoRedundancyModel.unscoped(), | ||
478 | required: false | ||
479 | } | ||
480 | ] | ||
474 | } | 481 | } |
475 | ] | 482 | ] |
476 | }, | 483 | }, |
@@ -633,6 +640,7 @@ export class VideoModel extends Model<VideoModel> { | |||
633 | name: 'videoId', | 640 | name: 'videoId', |
634 | allowNull: false | 641 | allowNull: false |
635 | }, | 642 | }, |
643 | hooks: true, | ||
636 | onDelete: 'cascade' | 644 | onDelete: 'cascade' |
637 | }) | 645 | }) |
638 | VideoFiles: VideoFileModel[] | 646 | VideoFiles: VideoFileModel[] |
@@ -1325,9 +1333,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1325 | [ CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT + '/tracker/socket' ], | 1333 | [ CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT + '/tracker/socket' ], |
1326 | [ CONFIG.WEBSERVER.URL + '/tracker/announce' ] | 1334 | [ CONFIG.WEBSERVER.URL + '/tracker/announce' ] |
1327 | ], | 1335 | ], |
1328 | urlList: [ | 1336 | urlList: [ CONFIG.WEBSERVER.URL + STATIC_PATHS.WEBSEED + this.getVideoFilename(videoFile) ] |
1329 | CONFIG.WEBSERVER.URL + STATIC_PATHS.WEBSEED + this.getVideoFilename(videoFile) | ||
1330 | ] | ||
1331 | } | 1337 | } |
1332 | 1338 | ||
1333 | const torrent = await createTorrentPromise(this.getVideoFilePath(videoFile), options) | 1339 | const torrent = await createTorrentPromise(this.getVideoFilePath(videoFile), options) |
@@ -1535,11 +1541,11 @@ export class VideoModel extends Model<VideoModel> { | |||
1535 | } | 1541 | } |
1536 | } | 1542 | } |
1537 | 1543 | ||
1538 | const url = [] | 1544 | const url: ActivityUrlObject[] = [] |
1539 | for (const file of this.VideoFiles) { | 1545 | for (const file of this.VideoFiles) { |
1540 | url.push({ | 1546 | url.push({ |
1541 | type: 'Link', | 1547 | type: 'Link', |
1542 | mimeType: VIDEO_EXT_MIMETYPE[ file.extname ], | 1548 | mimeType: VIDEO_EXT_MIMETYPE[ file.extname ] as any, |
1543 | href: this.getVideoFileUrl(file, baseUrlHttp), | 1549 | href: this.getVideoFileUrl(file, baseUrlHttp), |
1544 | height: file.resolution, | 1550 | height: file.resolution, |
1545 | size: file.size, | 1551 | size: file.size, |
@@ -1548,14 +1554,14 @@ export class VideoModel extends Model<VideoModel> { | |||
1548 | 1554 | ||
1549 | url.push({ | 1555 | url.push({ |
1550 | type: 'Link', | 1556 | type: 'Link', |
1551 | mimeType: 'application/x-bittorrent', | 1557 | mimeType: 'application/x-bittorrent' as 'application/x-bittorrent', |
1552 | href: this.getTorrentUrl(file, baseUrlHttp), | 1558 | href: this.getTorrentUrl(file, baseUrlHttp), |
1553 | height: file.resolution | 1559 | height: file.resolution |
1554 | }) | 1560 | }) |
1555 | 1561 | ||
1556 | url.push({ | 1562 | url.push({ |
1557 | type: 'Link', | 1563 | type: 'Link', |
1558 | mimeType: 'application/x-bittorrent;x-scheme-handler/magnet', | 1564 | mimeType: 'application/x-bittorrent;x-scheme-handler/magnet' as 'application/x-bittorrent;x-scheme-handler/magnet', |
1559 | href: this.generateMagnetUri(file, baseUrlHttp, baseUrlWs), | 1565 | href: this.generateMagnetUri(file, baseUrlHttp, baseUrlWs), |
1560 | height: file.resolution | 1566 | height: file.resolution |
1561 | }) | 1567 | }) |
@@ -1796,7 +1802,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1796 | (now - updatedAtTime) > ACTIVITY_PUB.VIDEO_REFRESH_INTERVAL | 1802 | (now - updatedAtTime) > ACTIVITY_PUB.VIDEO_REFRESH_INTERVAL |
1797 | } | 1803 | } |
1798 | 1804 | ||
1799 | private getBaseUrls () { | 1805 | getBaseUrls () { |
1800 | let baseUrlHttp | 1806 | let baseUrlHttp |
1801 | let baseUrlWs | 1807 | let baseUrlWs |
1802 | 1808 | ||
@@ -1811,39 +1817,42 @@ export class VideoModel extends Model<VideoModel> { | |||
1811 | return { baseUrlHttp, baseUrlWs } | 1817 | return { baseUrlHttp, baseUrlWs } |
1812 | } | 1818 | } |
1813 | 1819 | ||
1814 | private getThumbnailUrl (baseUrlHttp: string) { | 1820 | generateMagnetUri (videoFile: VideoFileModel, baseUrlHttp: string, baseUrlWs: string) { |
1821 | const xs = this.getTorrentUrl(videoFile, baseUrlHttp) | ||
1822 | const announce = [ baseUrlWs + '/tracker/socket', baseUrlHttp + '/tracker/announce' ] | ||
1823 | let urlList = [ this.getVideoFileUrl(videoFile, baseUrlHttp) ] | ||
1824 | |||
1825 | const redundancies = videoFile.RedundancyVideos | ||
1826 | if (isArray(redundancies)) urlList = urlList.concat(redundancies.map(r => r.fileUrl)) | ||
1827 | |||
1828 | const magnetHash = { | ||
1829 | xs, | ||
1830 | announce, | ||
1831 | urlList, | ||
1832 | infoHash: videoFile.infoHash, | ||
1833 | name: this.name | ||
1834 | } | ||
1835 | |||
1836 | return magnetUtil.encode(magnetHash) | ||
1837 | } | ||
1838 | |||
1839 | getThumbnailUrl (baseUrlHttp: string) { | ||
1815 | return baseUrlHttp + STATIC_PATHS.THUMBNAILS + this.getThumbnailName() | 1840 | return baseUrlHttp + STATIC_PATHS.THUMBNAILS + this.getThumbnailName() |
1816 | } | 1841 | } |
1817 | 1842 | ||
1818 | private getTorrentUrl (videoFile: VideoFileModel, baseUrlHttp: string) { | 1843 | getTorrentUrl (videoFile: VideoFileModel, baseUrlHttp: string) { |
1819 | return baseUrlHttp + STATIC_PATHS.TORRENTS + this.getTorrentFileName(videoFile) | 1844 | return baseUrlHttp + STATIC_PATHS.TORRENTS + this.getTorrentFileName(videoFile) |
1820 | } | 1845 | } |
1821 | 1846 | ||
1822 | private getTorrentDownloadUrl (videoFile: VideoFileModel, baseUrlHttp: string) { | 1847 | getTorrentDownloadUrl (videoFile: VideoFileModel, baseUrlHttp: string) { |
1823 | return baseUrlHttp + STATIC_DOWNLOAD_PATHS.TORRENTS + this.getTorrentFileName(videoFile) | 1848 | return baseUrlHttp + STATIC_DOWNLOAD_PATHS.TORRENTS + this.getTorrentFileName(videoFile) |
1824 | } | 1849 | } |
1825 | 1850 | ||
1826 | private getVideoFileUrl (videoFile: VideoFileModel, baseUrlHttp: string) { | 1851 | getVideoFileUrl (videoFile: VideoFileModel, baseUrlHttp: string) { |
1827 | return baseUrlHttp + STATIC_PATHS.WEBSEED + this.getVideoFilename(videoFile) | 1852 | return baseUrlHttp + STATIC_PATHS.WEBSEED + this.getVideoFilename(videoFile) |
1828 | } | 1853 | } |
1829 | 1854 | ||
1830 | private getVideoFileDownloadUrl (videoFile: VideoFileModel, baseUrlHttp: string) { | 1855 | getVideoFileDownloadUrl (videoFile: VideoFileModel, baseUrlHttp: string) { |
1831 | return baseUrlHttp + STATIC_DOWNLOAD_PATHS.VIDEOS + this.getVideoFilename(videoFile) | 1856 | return baseUrlHttp + STATIC_DOWNLOAD_PATHS.VIDEOS + this.getVideoFilename(videoFile) |
1832 | } | 1857 | } |
1833 | |||
1834 | private generateMagnetUri (videoFile: VideoFileModel, baseUrlHttp: string, baseUrlWs: string) { | ||
1835 | const xs = this.getTorrentUrl(videoFile, baseUrlHttp) | ||
1836 | const announce = [ baseUrlWs + '/tracker/socket', baseUrlHttp + '/tracker/announce' ] | ||
1837 | const urlList = [ this.getVideoFileUrl(videoFile, baseUrlHttp) ] | ||
1838 | |||
1839 | const magnetHash = { | ||
1840 | xs, | ||
1841 | announce, | ||
1842 | urlList, | ||
1843 | infoHash: videoFile.infoHash, | ||
1844 | name: this.name | ||
1845 | } | ||
1846 | |||
1847 | return magnetUtil.encode(magnetHash) | ||
1848 | } | ||
1849 | } | 1858 | } |