]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/controllers/download.ts
Add upload/import/go live video attributes hooks
[github/Chocobozzz/PeerTube.git] / server / controllers / download.ts
CommitLineData
41fb13c3
C
1import cors from 'cors'
2import express from 'express'
4bc45da3 3import { logger } from '@server/helpers/logger'
90a8bd30 4import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache'
4bc45da3 5import { Hooks } from '@server/lib/plugins/hooks'
0305db28 6import { VideoPathManager } from '@server/lib/video-path-manager'
4bc45da3 7import { MStreamingPlaylist, MVideo, MVideoFile, MVideoFullLight } from '@server/types/models'
0305db28 8import { HttpStatusCode, VideoStorage, VideoStreamingPlaylistType } from '@shared/models'
90a8bd30
C
9import { STATIC_DOWNLOAD_PATHS } from '../initializers/constants'
10import { asyncMiddleware, videosDownloadValidator } from '../middlewares'
11
12const downloadRouter = express.Router()
13
14downloadRouter.use(cors())
15
16downloadRouter.use(
17 STATIC_DOWNLOAD_PATHS.TORRENTS + ':filename',
4bc45da3 18 asyncMiddleware(downloadTorrent)
90a8bd30
C
19)
20
21downloadRouter.use(
22 STATIC_DOWNLOAD_PATHS.VIDEOS + ':id-:resolution([0-9]+).:extension',
23 asyncMiddleware(videosDownloadValidator),
4bc45da3 24 asyncMiddleware(downloadVideoFile)
90a8bd30
C
25)
26
27downloadRouter.use(
28 STATIC_DOWNLOAD_PATHS.HLS_VIDEOS + ':id-:resolution([0-9]+)-fragmented.:extension',
29 asyncMiddleware(videosDownloadValidator),
4bc45da3 30 asyncMiddleware(downloadHLSVideoFile)
90a8bd30
C
31)
32
33// ---------------------------------------------------------------------------
34
35export {
36 downloadRouter
37}
38
39// ---------------------------------------------------------------------------
40
41async function downloadTorrent (req: express.Request, res: express.Response) {
42 const result = await VideosTorrentCache.Instance.getFilePath(req.params.filename)
76148b27
RK
43 if (!result) {
44 return res.fail({
45 status: HttpStatusCode.NOT_FOUND_404,
46 message: 'Torrent file not found'
47 })
48 }
90a8bd30 49
4bc45da3
C
50 const allowParameters = { torrentPath: result.path, downloadName: result.downloadName }
51
52 const allowedResult = await Hooks.wrapFun(
53 isTorrentDownloadAllowed,
54 allowParameters,
55 'filter:api.download.torrent.allowed.result'
56 )
57
58 if (!checkAllowResult(res, allowParameters, allowedResult)) return
59
90a8bd30
C
60 return res.download(result.path, result.downloadName)
61}
62
4bc45da3 63async function downloadVideoFile (req: express.Request, res: express.Response) {
90a8bd30
C
64 const video = res.locals.videoAll
65
66 const videoFile = getVideoFile(req, video.VideoFiles)
76148b27
RK
67 if (!videoFile) {
68 return res.fail({
69 status: HttpStatusCode.NOT_FOUND_404,
70 message: 'Video file not found'
71 })
72 }
90a8bd30 73
4bc45da3
C
74 const allowParameters = { video, videoFile }
75
76 const allowedResult = await Hooks.wrapFun(
77 isVideoDownloadAllowed,
78 allowParameters,
79 'filter:api.download.video.allowed.result'
80 )
81
82 if (!checkAllowResult(res, allowParameters, allowedResult)) return
83
0305db28
JB
84 if (videoFile.storage === VideoStorage.OBJECT_STORAGE) {
85 return res.redirect(videoFile.getObjectStorageUrl())
86 }
87
ad5db104 88 await VideoPathManager.Instance.makeAvailableVideoFile(videoFile.withVideoOrPlaylist(video), path => {
0305db28
JB
89 const filename = `${video.name}-${videoFile.resolution}p${videoFile.extname}`
90
91 return res.download(path, filename)
92 })
90a8bd30
C
93}
94
4bc45da3 95async function downloadHLSVideoFile (req: express.Request, res: express.Response) {
90a8bd30 96 const video = res.locals.videoAll
4bc45da3
C
97 const streamingPlaylist = getHLSPlaylist(video)
98 if (!streamingPlaylist) return res.status(HttpStatusCode.NOT_FOUND_404).end
90a8bd30 99
4bc45da3 100 const videoFile = getVideoFile(req, streamingPlaylist.VideoFiles)
76148b27
RK
101 if (!videoFile) {
102 return res.fail({
103 status: HttpStatusCode.NOT_FOUND_404,
104 message: 'Video file not found'
105 })
106 }
90a8bd30 107
4bc45da3
C
108 const allowParameters = { video, streamingPlaylist, videoFile }
109
110 const allowedResult = await Hooks.wrapFun(
111 isVideoDownloadAllowed,
112 allowParameters,
113 'filter:api.download.video.allowed.result'
114 )
115
116 if (!checkAllowResult(res, allowParameters, allowedResult)) return
117
0305db28
JB
118 if (videoFile.storage === VideoStorage.OBJECT_STORAGE) {
119 return res.redirect(videoFile.getObjectStorageUrl())
120 }
121
ad5db104 122 await VideoPathManager.Instance.makeAvailableVideoFile(videoFile.withVideoOrPlaylist(streamingPlaylist), path => {
0305db28
JB
123 const filename = `${video.name}-${videoFile.resolution}p-${streamingPlaylist.getStringType()}${videoFile.extname}`
124
125 return res.download(path, filename)
126 })
90a8bd30
C
127}
128
129function getVideoFile (req: express.Request, files: MVideoFile[]) {
130 const resolution = parseInt(req.params.resolution, 10)
131 return files.find(f => f.resolution === resolution)
132}
133
134function getHLSPlaylist (video: MVideoFullLight) {
135 const playlist = video.VideoStreamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS)
136 if (!playlist) return undefined
137
138 return Object.assign(playlist, { Video: video })
139}
4bc45da3
C
140
141type AllowedResult = {
142 allowed: boolean
143 errorMessage?: string
144}
145
146function isTorrentDownloadAllowed (_object: {
147 torrentPath: string
148}): AllowedResult {
149 return { allowed: true }
150}
151
152function isVideoDownloadAllowed (_object: {
153 video: MVideo
154 videoFile: MVideoFile
155 streamingPlaylist?: MStreamingPlaylist
156}): AllowedResult {
157 return { allowed: true }
158}
159
160function checkAllowResult (res: express.Response, allowParameters: any, result?: AllowedResult) {
161 if (!result || result.allowed !== true) {
162 logger.info('Download is not allowed.', { result, allowParameters })
4bc45da3 163
76148b27
RK
164 res.fail({
165 status: HttpStatusCode.FORBIDDEN_403,
166 message: result?.errorMessage || 'Refused download'
167 })
4bc45da3
C
168 return false
169 }
170
171 return true
172}