diff options
author | Chocobozzz <florian.bigard@gmail.com> | 2017-10-19 14:58:28 +0200 |
---|---|---|
committer | Chocobozzz <florian.bigard@gmail.com> | 2017-10-19 14:58:28 +0200 |
commit | a96aed15188174c50885dda0df3164a67295e11f (patch) | |
tree | 7e3941324614e52a2f6ab7755224ab8ae8f8c19f | |
parent | bda65bdc9f3ce7d4b6e97cb9afaca25b71added3 (diff) | |
download | PeerTube-a96aed15188174c50885dda0df3164a67295e11f.tar.gz PeerTube-a96aed15188174c50885dda0df3164a67295e11f.tar.zst PeerTube-a96aed15188174c50885dda0df3164a67295e11f.zip |
Add ability to download a video from direct link or torrent file
-rw-r--r-- | client/src/app/videos/+video-watch/video-download.component.html | 30 | ||||
-rw-r--r-- | client/src/app/videos/+video-watch/video-download.component.ts (renamed from client/src/app/videos/+video-watch/video-magnet.component.ts) | 7 | ||||
-rw-r--r-- | client/src/app/videos/+video-watch/video-magnet.component.html | 20 | ||||
-rw-r--r-- | client/src/app/videos/+video-watch/video-watch.component.html | 6 | ||||
-rw-r--r-- | client/src/app/videos/+video-watch/video-watch.component.ts | 8 | ||||
-rw-r--r-- | client/src/app/videos/+video-watch/video-watch.module.ts | 4 | ||||
-rw-r--r-- | client/src/assets/player/peertube-videojs-plugin.ts | 7 | ||||
-rw-r--r-- | server.ts | 44 | ||||
-rw-r--r-- | server/models/video/video-interface.ts | 2 | ||||
-rw-r--r-- | server/models/video/video.ts | 75 | ||||
-rw-r--r-- | server/tests/api/multiple-pods.ts | 2 | ||||
-rw-r--r-- | server/tests/api/single-pod.ts | 2 | ||||
-rw-r--r-- | shared/models/videos/video.model.ts | 2 |
13 files changed, 123 insertions, 86 deletions
diff --git a/client/src/app/videos/+video-watch/video-download.component.html b/client/src/app/videos/+video-watch/video-download.component.html new file mode 100644 index 000000000..ddc57e999 --- /dev/null +++ b/client/src/app/videos/+video-watch/video-download.component.html | |||
@@ -0,0 +1,30 @@ | |||
1 | <div bsModal #modal="bs-modal" class="modal" tabindex="-1"> | ||
2 | <div class="modal-dialog"> | ||
3 | <div class="modal-content modal-lg"> | ||
4 | |||
5 | <div class="modal-header"> | ||
6 | <button type="button" class="close" aria-label="Close" (click)="hide()"> | ||
7 | <span aria-hidden="true">×</span> | ||
8 | </button> | ||
9 | <h4 class="modal-title">Download</h4> | ||
10 | </div> | ||
11 | |||
12 | <div class="modal-body"> | ||
13 | <div *ngFor="let file of video.files" class="resolution-block"> | ||
14 | <label>{{ file.resolutionLabel }}</label> | ||
15 | <a class="btn btn-default " target="_blank" [href]="file.torrentUrl"> | ||
16 | <span class="glyphicon glyphicon-download"></span> | ||
17 | Torrent file | ||
18 | </a> | ||
19 | <a class="btn btn-default" target="_blank" [href]="file.fileUrl"> | ||
20 | <span class="glyphicon glyphicon-download"></span> | ||
21 | Download | ||
22 | </a> | ||
23 | |||
24 | <!-- Don't display magnet URI for now, this is not compatible with most torrent clients --> | ||
25 | <!--<input #magnetUriInput (click)="magnetUriInput.select()" type="text" class="form-control input-sm readonly" readonly [value]="file.magnetUri" />--> | ||
26 | </div> | ||
27 | </div> | ||
28 | </div> | ||
29 | </div> | ||
30 | </div> | ||
diff --git a/client/src/app/videos/+video-watch/video-magnet.component.ts b/client/src/app/videos/+video-watch/video-download.component.ts index f9432e92c..22149aa6b 100644 --- a/client/src/app/videos/+video-watch/video-magnet.component.ts +++ b/client/src/app/videos/+video-watch/video-download.component.ts | |||
@@ -5,10 +5,11 @@ import { ModalDirective } from 'ngx-bootstrap/modal' | |||
5 | import { Video } from '../shared' | 5 | import { Video } from '../shared' |
6 | 6 | ||
7 | @Component({ | 7 | @Component({ |
8 | selector: 'my-video-magnet', | 8 | selector: 'my-video-download', |
9 | templateUrl: './video-magnet.component.html' | 9 | templateUrl: './video-download.component.html', |
10 | styles: [ '.resolution-block { margin-top: 20px; }' ] | ||
10 | }) | 11 | }) |
11 | export class VideoMagnetComponent { | 12 | export class VideoDownloadComponent { |
12 | @Input() video: Video = null | 13 | @Input() video: Video = null |
13 | 14 | ||
14 | @ViewChild('modal') modal: ModalDirective | 15 | @ViewChild('modal') modal: ModalDirective |
diff --git a/client/src/app/videos/+video-watch/video-magnet.component.html b/client/src/app/videos/+video-watch/video-magnet.component.html deleted file mode 100644 index 484280c45..000000000 --- a/client/src/app/videos/+video-watch/video-magnet.component.html +++ /dev/null | |||
@@ -1,20 +0,0 @@ | |||
1 | <div bsModal #modal="bs-modal" class="modal" tabindex="-1"> | ||
2 | <div class="modal-dialog"> | ||
3 | <div class="modal-content modal-lg"> | ||
4 | |||
5 | <div class="modal-header"> | ||
6 | <button type="button" class="close" aria-label="Close" (click)="hide()"> | ||
7 | <span aria-hidden="true">×</span> | ||
8 | </button> | ||
9 | <h4 class="modal-title">Magnet Uri</h4> | ||
10 | </div> | ||
11 | |||
12 | <div class="modal-body"> | ||
13 | <div *ngFor="let file of video.files"> | ||
14 | <label>{{ file.resolutionLabel }}</label> | ||
15 | <input #magnetUriInput (click)="magnetUriInput.select()" type="text" class="form-control input-sm readonly" readonly [value]="file.magnetUri" /> | ||
16 | </div> | ||
17 | </div> | ||
18 | </div> | ||
19 | </div> | ||
20 | </div> | ||
diff --git a/client/src/app/videos/+video-watch/video-watch.component.html b/client/src/app/videos/+video-watch/video-watch.component.html index 88863131a..5d5827344 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.html +++ b/client/src/app/videos/+video-watch/video-watch.component.html | |||
@@ -71,8 +71,8 @@ | |||
71 | </li> | 71 | </li> |
72 | 72 | ||
73 | <li role="menuitem"> | 73 | <li role="menuitem"> |
74 | <a class="dropdown-item" title="Get magnet URI" href="#" (click)="showMagnetUriModal($event)"> | 74 | <a class="dropdown-item" title="Download the video" href="#" (click)="showDownloadModal($event)"> |
75 | <span class="glyphicon glyphicon-magnet"></span> Magnet | 75 | <span class="glyphicon glyphicon-download-alt"></span> Download |
76 | </a> | 76 | </a> |
77 | </li> | 77 | </li> |
78 | 78 | ||
@@ -179,6 +179,6 @@ | |||
179 | 179 | ||
180 | <ng-template [ngIf]="video !== null"> | 180 | <ng-template [ngIf]="video !== null"> |
181 | <my-video-share #videoShareModal [video]="video"></my-video-share> | 181 | <my-video-share #videoShareModal [video]="video"></my-video-share> |
182 | <my-video-magnet #videoMagnetModal [video]="video"></my-video-magnet> | 182 | <my-video-download #videoDownloadModal [video]="video"></my-video-download> |
183 | <my-video-report #videoReportModal [video]="video"></my-video-report> | 183 | <my-video-report #videoReportModal [video]="video"></my-video-report> |
184 | </ng-template> | 184 | </ng-template> |
diff --git a/client/src/app/videos/+video-watch/video-watch.component.ts b/client/src/app/videos/+video-watch/video-watch.component.ts index bd98e877c..651298c14 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.ts +++ b/client/src/app/videos/+video-watch/video-watch.component.ts | |||
@@ -10,7 +10,7 @@ import { MetaService } from '@ngx-meta/core' | |||
10 | import { NotificationsService } from 'angular2-notifications' | 10 | import { NotificationsService } from 'angular2-notifications' |
11 | 11 | ||
12 | import { AuthService, ConfirmService } from '../../core' | 12 | import { AuthService, ConfirmService } from '../../core' |
13 | import { VideoMagnetComponent } from './video-magnet.component' | 13 | import { VideoDownloadComponent } from './video-download.component' |
14 | import { VideoShareComponent } from './video-share.component' | 14 | import { VideoShareComponent } from './video-share.component' |
15 | import { VideoReportComponent } from './video-report.component' | 15 | import { VideoReportComponent } from './video-report.component' |
16 | import { Video, VideoService } from '../shared' | 16 | import { Video, VideoService } from '../shared' |
@@ -23,7 +23,7 @@ import { UserVideoRateType, VideoRateType } from '../../../../../shared' | |||
23 | styleUrls: [ './video-watch.component.scss' ] | 23 | styleUrls: [ './video-watch.component.scss' ] |
24 | }) | 24 | }) |
25 | export class VideoWatchComponent implements OnInit, OnDestroy { | 25 | export class VideoWatchComponent implements OnInit, OnDestroy { |
26 | @ViewChild('videoMagnetModal') videoMagnetModal: VideoMagnetComponent | 26 | @ViewChild('videoDownloadModal') videoDownloadModal: VideoDownloadComponent |
27 | @ViewChild('videoShareModal') videoShareModal: VideoShareComponent | 27 | @ViewChild('videoShareModal') videoShareModal: VideoShareComponent |
28 | @ViewChild('videoReportModal') videoReportModal: VideoReportComponent | 28 | @ViewChild('videoReportModal') videoReportModal: VideoReportComponent |
29 | 29 | ||
@@ -160,9 +160,9 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
160 | this.videoShareModal.show() | 160 | this.videoShareModal.show() |
161 | } | 161 | } |
162 | 162 | ||
163 | showMagnetUriModal (event: Event) { | 163 | showDownloadModal (event: Event) { |
164 | event.preventDefault() | 164 | event.preventDefault() |
165 | this.videoMagnetModal.show() | 165 | this.videoDownloadModal.show() |
166 | } | 166 | } |
167 | 167 | ||
168 | isUserLoggedIn () { | 168 | isUserLoggedIn () { |
diff --git a/client/src/app/videos/+video-watch/video-watch.module.ts b/client/src/app/videos/+video-watch/video-watch.module.ts index 5f20b171e..c6c1344ce 100644 --- a/client/src/app/videos/+video-watch/video-watch.module.ts +++ b/client/src/app/videos/+video-watch/video-watch.module.ts | |||
@@ -7,7 +7,7 @@ import { SharedModule } from '../../shared' | |||
7 | import { VideoWatchComponent } from './video-watch.component' | 7 | import { VideoWatchComponent } from './video-watch.component' |
8 | import { VideoReportComponent } from './video-report.component' | 8 | import { VideoReportComponent } from './video-report.component' |
9 | import { VideoShareComponent } from './video-share.component' | 9 | import { VideoShareComponent } from './video-share.component' |
10 | import { VideoMagnetComponent } from './video-magnet.component' | 10 | import { VideoDownloadComponent } from './video-download.component' |
11 | 11 | ||
12 | @NgModule({ | 12 | @NgModule({ |
13 | imports: [ | 13 | imports: [ |
@@ -18,7 +18,7 @@ import { VideoMagnetComponent } from './video-magnet.component' | |||
18 | declarations: [ | 18 | declarations: [ |
19 | VideoWatchComponent, | 19 | VideoWatchComponent, |
20 | 20 | ||
21 | VideoMagnetComponent, | 21 | VideoDownloadComponent, |
22 | VideoShareComponent, | 22 | VideoShareComponent, |
23 | VideoReportComponent | 23 | VideoReportComponent |
24 | ], | 24 | ], |
diff --git a/client/src/assets/player/peertube-videojs-plugin.ts b/client/src/assets/player/peertube-videojs-plugin.ts index 7cf3ea6cc..19490baf2 100644 --- a/client/src/assets/player/peertube-videojs-plugin.ts +++ b/client/src/assets/player/peertube-videojs-plugin.ts | |||
@@ -158,7 +158,12 @@ const peertubePlugin = function (options: PeertubePluginOptions) { | |||
158 | }) | 158 | }) |
159 | 159 | ||
160 | player.torrent.on('error', err => handleError(err)) | 160 | player.torrent.on('error', err => handleError(err)) |
161 | player.torrent.on('warning', err => handleError(err)) | 161 | player.torrent.on('warning', err => { |
162 | // We don't support HTTP tracker but we don't care -> we use the web socket tracker | ||
163 | if (err.message.indexOf('Unsupported tracker protocol: http://') !== -1) return | ||
164 | |||
165 | return handleError(err) | ||
166 | }) | ||
162 | 167 | ||
163 | player.trigger('videoFileUpdate') | 168 | player.trigger('videoFileUpdate') |
164 | 169 | ||
@@ -79,26 +79,6 @@ app.use(morgan('combined', { | |||
79 | app.use(bodyParser.json({ limit: '500kb' })) | 79 | app.use(bodyParser.json({ limit: '500kb' })) |
80 | app.use(bodyParser.urlencoded({ extended: false })) | 80 | app.use(bodyParser.urlencoded({ extended: false })) |
81 | 81 | ||
82 | // ----------- Views, routes and static files ----------- | ||
83 | |||
84 | // API | ||
85 | const apiRoute = '/api/' + API_VERSION | ||
86 | app.use(apiRoute, apiRouter) | ||
87 | |||
88 | // Services (oembed...) | ||
89 | app.use('/services', servicesRouter) | ||
90 | |||
91 | // Client files | ||
92 | app.use('/', clientsRouter) | ||
93 | |||
94 | // Static files | ||
95 | app.use('/', staticRouter) | ||
96 | |||
97 | // Always serve index client page (the client is a single page application, let it handle routing) | ||
98 | app.use('/*', function (req, res, next) { | ||
99 | res.sendFile(path.join(__dirname, '../client/dist/index.html')) | ||
100 | }) | ||
101 | |||
102 | // ----------- Tracker ----------- | 82 | // ----------- Tracker ----------- |
103 | 83 | ||
104 | const trackerServer = new TrackerServer({ | 84 | const trackerServer = new TrackerServer({ |
@@ -122,6 +102,30 @@ wss.on('connection', function (ws) { | |||
122 | trackerServer.onWebSocketConnection(ws) | 102 | trackerServer.onWebSocketConnection(ws) |
123 | }) | 103 | }) |
124 | 104 | ||
105 | const onHttpRequest = trackerServer.onHttpRequest.bind(trackerServer) | ||
106 | app.get('/tracker/announce', (req, res) => onHttpRequest(req, res, { action: 'announce' })) | ||
107 | app.get('/tracker/scrape', (req, res) => onHttpRequest(req, res, { action: 'scrape' })) | ||
108 | |||
109 | // ----------- Views, routes and static files ----------- | ||
110 | |||
111 | // API | ||
112 | const apiRoute = '/api/' + API_VERSION | ||
113 | app.use(apiRoute, apiRouter) | ||
114 | |||
115 | // Services (oembed...) | ||
116 | app.use('/services', servicesRouter) | ||
117 | |||
118 | // Client files | ||
119 | app.use('/', clientsRouter) | ||
120 | |||
121 | // Static files | ||
122 | app.use('/', staticRouter) | ||
123 | |||
124 | // Always serve index client page (the client is a single page application, let it handle routing) | ||
125 | app.use('/*', function (req, res) { | ||
126 | res.sendFile(path.join(__dirname, '../client/dist/index.html')) | ||
127 | }) | ||
128 | |||
125 | // ----------- Errors ----------- | 129 | // ----------- Errors ----------- |
126 | 130 | ||
127 | // Catch 404 and forward to error handler | 131 | // Catch 404 and forward to error handler |
diff --git a/server/models/video/video-interface.ts b/server/models/video/video-interface.ts index 1402df26a..86ce84dd9 100644 --- a/server/models/video/video-interface.ts +++ b/server/models/video/video-interface.ts | |||
@@ -18,7 +18,6 @@ export namespace VideoMethods { | |||
18 | export type ToFormattedJSON = (this: VideoInstance) => FormattedVideo | 18 | export type ToFormattedJSON = (this: VideoInstance) => FormattedVideo |
19 | 19 | ||
20 | export type GetOriginalFile = (this: VideoInstance) => VideoFileInstance | 20 | export type GetOriginalFile = (this: VideoInstance) => VideoFileInstance |
21 | export type GenerateMagnetUri = (this: VideoInstance, videoFile: VideoFileInstance) => string | ||
22 | export type GetTorrentFileName = (this: VideoInstance, videoFile: VideoFileInstance) => string | 21 | export type GetTorrentFileName = (this: VideoInstance, videoFile: VideoFileInstance) => string |
23 | export type GetVideoFilename = (this: VideoInstance, videoFile: VideoFileInstance) => string | 22 | export type GetVideoFilename = (this: VideoInstance, videoFile: VideoFileInstance) => string |
24 | export type CreatePreview = (this: VideoInstance, videoFile: VideoFileInstance) => Promise<string> | 23 | export type CreatePreview = (this: VideoInstance, videoFile: VideoFileInstance) => Promise<string> |
@@ -108,7 +107,6 @@ export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.In | |||
108 | createThumbnail: VideoMethods.CreateThumbnail | 107 | createThumbnail: VideoMethods.CreateThumbnail |
109 | createTorrentAndSetInfoHash: VideoMethods.CreateTorrentAndSetInfoHash | 108 | createTorrentAndSetInfoHash: VideoMethods.CreateTorrentAndSetInfoHash |
110 | getOriginalFile: VideoMethods.GetOriginalFile | 109 | getOriginalFile: VideoMethods.GetOriginalFile |
111 | generateMagnetUri: VideoMethods.GenerateMagnetUri | ||
112 | getPreviewName: VideoMethods.GetPreviewName | 110 | getPreviewName: VideoMethods.GetPreviewName |
113 | getPreviewPath: VideoMethods.GetPreviewPath | 111 | getPreviewPath: VideoMethods.GetPreviewPath |
114 | getThumbnailName: VideoMethods.GetThumbnailName | 112 | getThumbnailName: VideoMethods.GetThumbnailName |
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 4bd8eb98f..0b1af4d21 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -52,7 +52,6 @@ import { PREVIEWS_SIZE } from '../../initializers/constants' | |||
52 | 52 | ||
53 | let Video: Sequelize.Model<VideoInstance, VideoAttributes> | 53 | let Video: Sequelize.Model<VideoInstance, VideoAttributes> |
54 | let getOriginalFile: VideoMethods.GetOriginalFile | 54 | let getOriginalFile: VideoMethods.GetOriginalFile |
55 | let generateMagnetUri: VideoMethods.GenerateMagnetUri | ||
56 | let getVideoFilename: VideoMethods.GetVideoFilename | 55 | let getVideoFilename: VideoMethods.GetVideoFilename |
57 | let getThumbnailName: VideoMethods.GetThumbnailName | 56 | let getThumbnailName: VideoMethods.GetThumbnailName |
58 | let getThumbnailPath: VideoMethods.GetThumbnailPath | 57 | let getThumbnailPath: VideoMethods.GetThumbnailPath |
@@ -254,7 +253,6 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da | |||
254 | createPreview, | 253 | createPreview, |
255 | createThumbnail, | 254 | createThumbnail, |
256 | createTorrentAndSetInfoHash, | 255 | createTorrentAndSetInfoHash, |
257 | generateMagnetUri, | ||
258 | getPreviewName, | 256 | getPreviewName, |
259 | getPreviewPath, | 257 | getPreviewPath, |
260 | getThumbnailName, | 258 | getThumbnailName, |
@@ -426,33 +424,6 @@ createTorrentAndSetInfoHash = function (this: VideoInstance, videoFile: VideoFil | |||
426 | }) | 424 | }) |
427 | } | 425 | } |
428 | 426 | ||
429 | generateMagnetUri = function (this: VideoInstance, videoFile: VideoFileInstance) { | ||
430 | let baseUrlHttp | ||
431 | let baseUrlWs | ||
432 | |||
433 | if (this.isOwned()) { | ||
434 | baseUrlHttp = CONFIG.WEBSERVER.URL | ||
435 | baseUrlWs = CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT | ||
436 | } else { | ||
437 | baseUrlHttp = REMOTE_SCHEME.HTTP + '://' + this.Author.Pod.host | ||
438 | baseUrlWs = REMOTE_SCHEME.WS + '://' + this.Author.Pod.host | ||
439 | } | ||
440 | |||
441 | const xs = baseUrlHttp + STATIC_PATHS.TORRENTS + this.getTorrentFileName(videoFile) | ||
442 | const announce = [ baseUrlWs + '/tracker/socket' ] | ||
443 | const urlList = [ baseUrlHttp + STATIC_PATHS.WEBSEED + this.getVideoFilename(videoFile) ] | ||
444 | |||
445 | const magnetHash = { | ||
446 | xs, | ||
447 | announce, | ||
448 | urlList, | ||
449 | infoHash: videoFile.infoHash, | ||
450 | name: this.name | ||
451 | } | ||
452 | |||
453 | return magnetUtil.encode(magnetHash) | ||
454 | } | ||
455 | |||
456 | getEmbedPath = function (this: VideoInstance) { | 427 | getEmbedPath = function (this: VideoInstance) { |
457 | return '/videos/embed/' + this.uuid | 428 | return '/videos/embed/' + this.uuid |
458 | } | 429 | } |
@@ -516,6 +487,7 @@ toFormattedJSON = function (this: VideoInstance) { | |||
516 | } | 487 | } |
517 | 488 | ||
518 | // Format and sort video files | 489 | // Format and sort video files |
490 | const { baseUrlHttp, baseUrlWs } = getBaseUrls(this) | ||
519 | json.files = this.VideoFiles | 491 | json.files = this.VideoFiles |
520 | .map(videoFile => { | 492 | .map(videoFile => { |
521 | let resolutionLabel = videoFile.resolution + 'p' | 493 | let resolutionLabel = videoFile.resolution + 'p' |
@@ -523,8 +495,10 @@ toFormattedJSON = function (this: VideoInstance) { | |||
523 | const videoFileJson = { | 495 | const videoFileJson = { |
524 | resolution: videoFile.resolution, | 496 | resolution: videoFile.resolution, |
525 | resolutionLabel, | 497 | resolutionLabel, |
526 | magnetUri: this.generateMagnetUri(videoFile), | 498 | magnetUri: generateMagnetUri(this, videoFile, baseUrlHttp, baseUrlWs), |
527 | size: videoFile.size | 499 | size: videoFile.size, |
500 | torrentUrl: getTorrentUrl(this, videoFile, baseUrlHttp), | ||
501 | fileUrl: getVideoFileUrl(this, videoFile, baseUrlHttp) | ||
528 | } | 502 | } |
529 | 503 | ||
530 | return videoFileJson | 504 | return videoFileJson |
@@ -972,3 +946,42 @@ function createBaseVideosWhere () { | |||
972 | } | 946 | } |
973 | } | 947 | } |
974 | } | 948 | } |
949 | |||
950 | function getBaseUrls (video: VideoInstance) { | ||
951 | let baseUrlHttp | ||
952 | let baseUrlWs | ||
953 | |||
954 | if (video.isOwned()) { | ||
955 | baseUrlHttp = CONFIG.WEBSERVER.URL | ||
956 | baseUrlWs = CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT | ||
957 | } else { | ||
958 | baseUrlHttp = REMOTE_SCHEME.HTTP + '://' + video.Author.Pod.host | ||
959 | baseUrlWs = REMOTE_SCHEME.WS + '://' + video.Author.Pod.host | ||
960 | } | ||
961 | |||
962 | return { baseUrlHttp, baseUrlWs } | ||
963 | } | ||
964 | |||
965 | function getTorrentUrl (video: VideoInstance, videoFile: VideoFileInstance, baseUrlHttp: string) { | ||
966 | return baseUrlHttp + STATIC_PATHS.TORRENTS + video.getTorrentFileName(videoFile) | ||
967 | } | ||
968 | |||
969 | function getVideoFileUrl (video: VideoInstance, videoFile: VideoFileInstance, baseUrlHttp: string) { | ||
970 | return baseUrlHttp + STATIC_PATHS.WEBSEED + video.getVideoFilename(videoFile) | ||
971 | } | ||
972 | |||
973 | function generateMagnetUri (video: VideoInstance, videoFile: VideoFileInstance, baseUrlHttp: string, baseUrlWs: string) { | ||
974 | const xs = getTorrentUrl(video, videoFile, baseUrlHttp) | ||
975 | const announce = [ baseUrlWs + '/tracker/socket', baseUrlHttp + '/tracker/announce' ] | ||
976 | const urlList = [ getVideoFileUrl(video, videoFile, baseUrlHttp) ] | ||
977 | |||
978 | const magnetHash = { | ||
979 | xs, | ||
980 | announce, | ||
981 | urlList, | ||
982 | infoHash: videoFile.infoHash, | ||
983 | name: video.name | ||
984 | } | ||
985 | |||
986 | return magnetUtil.encode(magnetHash) | ||
987 | } | ||
diff --git a/server/tests/api/multiple-pods.ts b/server/tests/api/multiple-pods.ts index 6c11aace5..e0ccb3058 100644 --- a/server/tests/api/multiple-pods.ts +++ b/server/tests/api/multiple-pods.ts | |||
@@ -106,6 +106,8 @@ describe('Test multiple pods', function () { | |||
106 | const file = video.files[0] | 106 | const file = video.files[0] |
107 | const magnetUri = file.magnetUri | 107 | const magnetUri = file.magnetUri |
108 | expect(file.magnetUri).to.have.lengthOf.above(2) | 108 | expect(file.magnetUri).to.have.lengthOf.above(2) |
109 | expect(file.torrentUrl).to.equal(`http://${video.podHost}/static/torrents/${video.uuid}-${file.resolution}.torrent`) | ||
110 | expect(file.fileUrl).to.equal(`http://${video.podHost}/static/webseed/${video.uuid}-${file.resolution}.webm`) | ||
109 | expect(file.resolution).to.equal(720) | 111 | expect(file.resolution).to.equal(720) |
110 | expect(file.resolutionLabel).to.equal('720p') | 112 | expect(file.resolutionLabel).to.equal('720p') |
111 | expect(file.size).to.equal(572456) | 113 | expect(file.size).to.equal(572456) |
diff --git a/server/tests/api/single-pod.ts b/server/tests/api/single-pod.ts index 82bc51a3e..71017b2b3 100644 --- a/server/tests/api/single-pod.ts +++ b/server/tests/api/single-pod.ts | |||
@@ -127,6 +127,8 @@ describe('Test a single pod', function () { | |||
127 | const file = video.files[0] | 127 | const file = video.files[0] |
128 | const magnetUri = file.magnetUri | 128 | const magnetUri = file.magnetUri |
129 | expect(file.magnetUri).to.have.lengthOf.above(2) | 129 | expect(file.magnetUri).to.have.lengthOf.above(2) |
130 | expect(file.torrentUrl).to.equal(`${server.url}/static/torrents/${video.uuid}-${file.resolution}.torrent`) | ||
131 | expect(file.fileUrl).to.equal(`${server.url}/static/webseed/${video.uuid}-${file.resolution}.webm`) | ||
130 | expect(file.resolution).to.equal(720) | 132 | expect(file.resolution).to.equal(720) |
131 | expect(file.resolutionLabel).to.equal('720p') | 133 | expect(file.resolutionLabel).to.equal('720p') |
132 | expect(file.size).to.equal(218910) | 134 | expect(file.size).to.equal(218910) |
diff --git a/shared/models/videos/video.model.ts b/shared/models/videos/video.model.ts index bbcada845..8e47ac069 100644 --- a/shared/models/videos/video.model.ts +++ b/shared/models/videos/video.model.ts | |||
@@ -3,6 +3,8 @@ export interface VideoFile { | |||
3 | resolution: number | 3 | resolution: number |
4 | resolutionLabel: string | 4 | resolutionLabel: string |
5 | size: number // Bytes | 5 | size: number // Bytes |
6 | torrentUrl: string | ||
7 | fileUrl: string | ||
6 | } | 8 | } |
7 | 9 | ||
8 | export interface Video { | 10 | export interface Video { |