aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models/video
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-09-11 16:27:07 +0200
committerChocobozzz <me@florianbigard.com>2018-09-13 14:05:49 +0200
commitc48e82b5e0478434de30626d14594a97f2402e7c (patch)
treea78e5272bd0fe4f5b41831e571e02d05f1515b82 /server/models/video
parenta651038487faa838bda3ce04695b08bc65baff70 (diff)
downloadPeerTube-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.ts25
-rw-r--r--server/models/video/video.ts73
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 @@
1import { values } from 'lodash' 1import { values } from 'lodash'
2import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 2import {
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'
3import { 16import {
4 isVideoFileInfoHashValid, 17 isVideoFileInfoHashValid,
5 isVideoFileResolutionValid, 18 isVideoFileResolutionValid,
@@ -10,6 +23,7 @@ import { CONSTRAINTS_FIELDS } from '../../initializers'
10import { throwIfNotValid } from '../utils' 23import { throwIfNotValid } from '../utils'
11import { VideoModel } from './video' 24import { VideoModel } from './video'
12import * as Sequelize from 'sequelize' 25import * as Sequelize from 'sequelize'
26import { 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'
30import { VideoPrivacy, VideoResolution, VideoState } from '../../../shared' 30import { ActivityUrlObject, VideoPrivacy, VideoResolution, VideoState } from '../../../shared'
31import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' 31import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
32import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos' 32import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos'
33import { VideoFilter } from '../../../shared/models/videos/video-query.type' 33import { VideoFilter } from '../../../shared/models/videos/video-query.type'
34import { createTorrentPromise, peertubeTruncate } from '../../helpers/core-utils' 34import { createTorrentPromise, peertubeTruncate } from '../../helpers/core-utils'
35import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 35import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
36import { isBooleanValid } from '../../helpers/custom-validators/misc' 36import { isArray, isBooleanValid } from '../../helpers/custom-validators/misc'
37import { 37import {
38 isVideoCategoryValid, 38 isVideoCategoryValid,
39 isVideoDescriptionValid, 39 isVideoDescriptionValid,
@@ -90,6 +90,7 @@ import { VideoCaptionModel } from './video-caption'
90import { VideoBlacklistModel } from './video-blacklist' 90import { VideoBlacklistModel } from './video-blacklist'
91import { copy, remove, rename, stat, writeFile } from 'fs-extra' 91import { copy, remove, rename, stat, writeFile } from 'fs-extra'
92import { VideoViewModel } from './video-views' 92import { VideoViewModel } from './video-views'
93import { 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
95const indexes: Sequelize.DefineIndexesOptions[] = [ 96const 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}