aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/videos/+video-watch/modal/video-download.component.ts4
-rw-r--r--server/controllers/static.ts38
-rw-r--r--server/initializers/constants.ts5
-rw-r--r--server/models/video/video.ts18
-rw-r--r--shared/models/videos/video.model.ts2
5 files changed, 61 insertions, 6 deletions
diff --git a/client/src/app/videos/+video-watch/modal/video-download.component.ts b/client/src/app/videos/+video-watch/modal/video-download.component.ts
index b06a7eef1..12f31b011 100644
--- a/client/src/app/videos/+video-watch/modal/video-download.component.ts
+++ b/client/src/app/videos/+video-watch/modal/video-download.component.ts
@@ -41,7 +41,7 @@ export class VideoDownloadComponent implements OnInit {
41 return 41 return
42 } 42 }
43 43
44 const link = this.downloadType === 'direct' ? file.fileUrl : file.torrentUrl 44 const link = this.downloadType === 'direct' ? file.fileDownloadUrl : file.torrentDownloadUrl
45 window.open(link) 45 window.location.assign(link)
46 } 46 }
47} 47}
diff --git a/server/controllers/static.ts b/server/controllers/static.ts
index c1bf384a4..8bebe6fa7 100644
--- a/server/controllers/static.ts
+++ b/server/controllers/static.ts
@@ -1,8 +1,9 @@
1import * as cors from 'cors' 1import * as cors from 'cors'
2import * as express from 'express' 2import * as express from 'express'
3import { CONFIG, STATIC_MAX_AGE, STATIC_PATHS } from '../initializers' 3import { CONFIG, STATIC_DOWNLOAD_PATHS, STATIC_MAX_AGE, STATIC_PATHS } from '../initializers'
4import { VideosPreviewCache } from '../lib/cache' 4import { VideosPreviewCache } from '../lib/cache'
5import { asyncMiddleware } from '../middlewares' 5import { asyncMiddleware, videosGetValidator } from '../middlewares'
6import { VideoModel } from '../models/video/video'
6 7
7const staticRouter = express.Router() 8const staticRouter = express.Router()
8 9
@@ -16,6 +17,11 @@ staticRouter.use(
16 cors(), 17 cors(),
17 express.static(torrentsPhysicalPath, { maxAge: 0 }) // Don't cache because we could regenerate the torrent file 18 express.static(torrentsPhysicalPath, { maxAge: 0 }) // Don't cache because we could regenerate the torrent file
18) 19)
20staticRouter.use(
21 STATIC_DOWNLOAD_PATHS.TORRENTS + ':id-:resolution([0-9]+).torrent',
22 asyncMiddleware(videosGetValidator),
23 asyncMiddleware(downloadTorrent)
24)
19 25
20// Videos path for webseeding 26// Videos path for webseeding
21const videosPhysicalPath = CONFIG.STORAGE.VIDEOS_DIR 27const videosPhysicalPath = CONFIG.STORAGE.VIDEOS_DIR
@@ -24,6 +30,11 @@ staticRouter.use(
24 cors(), 30 cors(),
25 express.static(videosPhysicalPath, { maxAge: STATIC_MAX_AGE }) 31 express.static(videosPhysicalPath, { maxAge: STATIC_MAX_AGE })
26) 32)
33staticRouter.use(
34 STATIC_DOWNLOAD_PATHS.VIDEOS + ':id-:resolution([0-9]+).:extension',
35 asyncMiddleware(videosGetValidator),
36 asyncMiddleware(downloadVideoFile)
37)
27 38
28// Thumbnails path for express 39// Thumbnails path for express
29const thumbnailsPhysicalPath = CONFIG.STORAGE.THUMBNAILS_DIR 40const thumbnailsPhysicalPath = CONFIG.STORAGE.THUMBNAILS_DIR
@@ -64,3 +75,26 @@ async function getPreview (req: express.Request, res: express.Response, next: ex
64 75
65 return res.sendFile(path, { maxAge: STATIC_MAX_AGE }) 76 return res.sendFile(path, { maxAge: STATIC_MAX_AGE })
66} 77}
78
79async function downloadTorrent (req: express.Request, res: express.Response, next: express.NextFunction) {
80 const { video, videoFile } = getVideoAndFileOr404(req, res)
81 if (!videoFile) return res.status(404).end()
82
83 return res.download(video.getTorrentFilePath(videoFile), `${video.name}-${videoFile.resolution}p.torrent`)
84}
85
86async function downloadVideoFile (req: express.Request, res: express.Response, next: express.NextFunction) {
87 const { video, videoFile } = getVideoAndFileOr404(req, res)
88 if (!videoFile) return res.status(404).end()
89
90 return res.download(video.getVideoFilePath(videoFile), `${video.name}-${videoFile.resolution}p${videoFile.extname}`)
91}
92
93function getVideoAndFileOr404 (req: express.Request, res: express.Response) {
94 const resolution = parseInt(req.params.resolution, 10)
95 const video: VideoModel = res.locals.video
96
97 const videoFile = video.VideoFiles.find(f => f.resolution === resolution)
98
99 return { video, videoFile }
100}
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index a35306730..26ee3db47 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -389,6 +389,10 @@ const STATIC_PATHS = {
389 WEBSEED: '/static/webseed/', 389 WEBSEED: '/static/webseed/',
390 AVATARS: '/static/avatars/' 390 AVATARS: '/static/avatars/'
391} 391}
392const STATIC_DOWNLOAD_PATHS = {
393 TORRENTS: '/download/torrents/',
394 VIDEOS: '/download/videos/'
395}
392 396
393// Cache control 397// Cache control
394let STATIC_MAX_AGE = '30d' 398let STATIC_MAX_AGE = '30d'
@@ -493,6 +497,7 @@ export {
493 USER_PASSWORD_RESET_LIFETIME, 497 USER_PASSWORD_RESET_LIFETIME,
494 IMAGE_MIMETYPE_EXT, 498 IMAGE_MIMETYPE_EXT,
495 SCHEDULER_INTERVAL, 499 SCHEDULER_INTERVAL,
500 STATIC_DOWNLOAD_PATHS,
496 RATES_LIMIT, 501 RATES_LIMIT,
497 JOB_COMPLETED_LIFETIME, 502 JOB_COMPLETED_LIFETIME,
498 VIDEO_VIEW_LIFETIME 503 VIDEO_VIEW_LIFETIME
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 1640cd57f..5821ea397 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -29,7 +29,6 @@ import { VideoPrivacy, VideoResolution } from '../../../shared'
29import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' 29import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
30import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos' 30import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos'
31import { VideoFilter } from '../../../shared/models/videos/video-query.type' 31import { VideoFilter } from '../../../shared/models/videos/video-query.type'
32import { activityPubCollectionPagination } from '../../helpers/activitypub'
33import { 32import {
34 createTorrentPromise, 33 createTorrentPromise,
35 peertubeTruncate, 34 peertubeTruncate,
@@ -59,6 +58,7 @@ import {
59 CONSTRAINTS_FIELDS, 58 CONSTRAINTS_FIELDS,
60 PREVIEWS_SIZE, 59 PREVIEWS_SIZE,
61 REMOTE_SCHEME, 60 REMOTE_SCHEME,
61 STATIC_DOWNLOAD_PATHS,
62 STATIC_PATHS, 62 STATIC_PATHS,
63 THUMBNAILS_SIZE, 63 THUMBNAILS_SIZE,
64 VIDEO_CATEGORIES, 64 VIDEO_CATEGORIES,
@@ -979,6 +979,10 @@ export class VideoModel extends Model<VideoModel> {
979 ) 979 )
980 } 980 }
981 981
982 getTorrentFilePath (videoFile: VideoFileModel) {
983 return join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile))
984 }
985
982 getVideoFilePath (videoFile: VideoFileModel) { 986 getVideoFilePath (videoFile: VideoFileModel) {
983 return join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile)) 987 return join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile))
984 } 988 }
@@ -1112,7 +1116,9 @@ export class VideoModel extends Model<VideoModel> {
1112 magnetUri: this.generateMagnetUri(videoFile, baseUrlHttp, baseUrlWs), 1116 magnetUri: this.generateMagnetUri(videoFile, baseUrlHttp, baseUrlWs),
1113 size: videoFile.size, 1117 size: videoFile.size,
1114 torrentUrl: this.getTorrentUrl(videoFile, baseUrlHttp), 1118 torrentUrl: this.getTorrentUrl(videoFile, baseUrlHttp),
1115 fileUrl: this.getVideoFileUrl(videoFile, baseUrlHttp) 1119 torrentDownloadUrl: this.getTorrentDownloadUrl(videoFile, baseUrlHttp),
1120 fileUrl: this.getVideoFileUrl(videoFile, baseUrlHttp),
1121 fileDownloadUrl: this.getVideoFileDownloadUrl(videoFile, baseUrlHttp)
1116 } as VideoFile 1122 } as VideoFile
1117 }) 1123 })
1118 .sort((a, b) => { 1124 .sort((a, b) => {
@@ -1367,10 +1373,18 @@ export class VideoModel extends Model<VideoModel> {
1367 return baseUrlHttp + STATIC_PATHS.TORRENTS + this.getTorrentFileName(videoFile) 1373 return baseUrlHttp + STATIC_PATHS.TORRENTS + this.getTorrentFileName(videoFile)
1368 } 1374 }
1369 1375
1376 private getTorrentDownloadUrl (videoFile: VideoFileModel, baseUrlHttp: string) {
1377 return baseUrlHttp + STATIC_DOWNLOAD_PATHS.TORRENTS + this.getTorrentFileName(videoFile)
1378 }
1379
1370 private getVideoFileUrl (videoFile: VideoFileModel, baseUrlHttp: string) { 1380 private getVideoFileUrl (videoFile: VideoFileModel, baseUrlHttp: string) {
1371 return baseUrlHttp + STATIC_PATHS.WEBSEED + this.getVideoFilename(videoFile) 1381 return baseUrlHttp + STATIC_PATHS.WEBSEED + this.getVideoFilename(videoFile)
1372 } 1382 }
1373 1383
1384 private getVideoFileDownloadUrl (videoFile: VideoFileModel, baseUrlHttp: string) {
1385 return baseUrlHttp + STATIC_DOWNLOAD_PATHS.VIDEOS + this.getVideoFilename(videoFile)
1386 }
1387
1374 private generateMagnetUri (videoFile: VideoFileModel, baseUrlHttp: string, baseUrlWs: string) { 1388 private generateMagnetUri (videoFile: VideoFileModel, baseUrlHttp: string, baseUrlWs: string) {
1375 const xs = this.getTorrentUrl(videoFile, baseUrlHttp) 1389 const xs = this.getTorrentUrl(videoFile, baseUrlHttp)
1376 const announce = [ baseUrlWs + '/tracker/socket', baseUrlHttp + '/tracker/announce' ] 1390 const announce = [ baseUrlWs + '/tracker/socket', baseUrlHttp + '/tracker/announce' ]
diff --git a/shared/models/videos/video.model.ts b/shared/models/videos/video.model.ts
index eb40e82de..1c86545d3 100644
--- a/shared/models/videos/video.model.ts
+++ b/shared/models/videos/video.model.ts
@@ -14,7 +14,9 @@ export interface VideoFile {
14 resolution: VideoConstant<VideoResolution> 14 resolution: VideoConstant<VideoResolution>
15 size: number // Bytes 15 size: number // Bytes
16 torrentUrl: string 16 torrentUrl: string
17 torrentDownloadUrl: string
17 fileUrl: string 18 fileUrl: string
19 fileDownloadUrl: string
18} 20}
19 21
20export interface Video { 22export interface Video {