]>
Commit | Line | Data |
---|---|---|
41fb13c3 C |
1 | import cors from 'cors' |
2 | import express from 'express' | |
71e3e879 C |
3 | import { readFile } from 'fs-extra' |
4 | import { join } from 'path' | |
5 | import { injectQueryToPlaylistUrls } from '@server/lib/hls' | |
3545e72c C |
6 | import { |
7 | asyncMiddleware, | |
8 | ensureCanAccessPrivateVideoHLSFiles, | |
9 | ensureCanAccessVideoPrivateWebTorrentFiles, | |
10 | handleStaticError, | |
11 | optionalAuthenticate | |
12 | } from '@server/middlewares' | |
71e3e879 | 13 | import { HttpStatusCode } from '@shared/models' |
ea8107bf | 14 | import { CONFIG } from '../initializers/config' |
3545e72c | 15 | import { DIRECTORIES, STATIC_MAX_AGE, STATIC_PATHS } from '../initializers/constants' |
71e3e879 | 16 | import { buildReinjectVideoFileTokenQuery, doReinjectVideoFileToken } from './shared/m3u8-playlist' |
65fcc311 C |
17 | |
18 | const staticRouter = express.Router() | |
19 | ||
ea8107bf | 20 | // Cors is very important to let other servers access torrent and video files |
62945f06 C |
21 | staticRouter.use(cors()) |
22 | ||
5a122ddd | 23 | // --------------------------------------------------------------------------- |
3545e72c | 24 | // WebTorrent/Classic videos |
5a122ddd C |
25 | // --------------------------------------------------------------------------- |
26 | ||
27 | const privateWebTorrentStaticMiddlewares = CONFIG.STATIC_FILES.PRIVATE_FILES_REQUIRE_AUTH === true | |
28 | ? [ optionalAuthenticate, asyncMiddleware(ensureCanAccessVideoPrivateWebTorrentFiles) ] | |
29 | : [] | |
30 | ||
3545e72c C |
31 | staticRouter.use( |
32 | STATIC_PATHS.PRIVATE_WEBSEED, | |
5a122ddd | 33 | ...privateWebTorrentStaticMiddlewares, |
3545e72c C |
34 | express.static(DIRECTORIES.VIDEOS.PRIVATE, { fallthrough: false }), |
35 | handleStaticError | |
36 | ) | |
65fcc311 C |
37 | staticRouter.use( |
38 | STATIC_PATHS.WEBSEED, | |
3545e72c | 39 | express.static(DIRECTORIES.VIDEOS.PUBLIC, { fallthrough: false }), |
f7ce623d | 40 | handleStaticError |
65fcc311 | 41 | ) |
3545e72c | 42 | |
6040f87d | 43 | staticRouter.use( |
b9fffa29 | 44 | STATIC_PATHS.REDUNDANCY, |
f7ce623d C |
45 | express.static(CONFIG.STORAGE.REDUNDANCY_DIR, { fallthrough: false }), |
46 | handleStaticError | |
6040f87d C |
47 | ) |
48 | ||
5a122ddd | 49 | // --------------------------------------------------------------------------- |
09209296 | 50 | // HLS |
5a122ddd C |
51 | // --------------------------------------------------------------------------- |
52 | ||
53 | const privateHLSStaticMiddlewares = CONFIG.STATIC_FILES.PRIVATE_FILES_REQUIRE_AUTH === true | |
54 | ? [ optionalAuthenticate, asyncMiddleware(ensureCanAccessPrivateVideoHLSFiles) ] | |
55 | : [] | |
56 | ||
71e3e879 C |
57 | staticRouter.use( |
58 | STATIC_PATHS.STREAMING_PLAYLISTS.PRIVATE_HLS + ':videoUUID/:playlistName.m3u8', | |
59 | ...privateHLSStaticMiddlewares, | |
60 | asyncMiddleware(servePrivateM3U8) | |
61 | ) | |
62 | ||
3545e72c C |
63 | staticRouter.use( |
64 | STATIC_PATHS.STREAMING_PLAYLISTS.PRIVATE_HLS, | |
5a122ddd | 65 | ...privateHLSStaticMiddlewares, |
3545e72c C |
66 | express.static(DIRECTORIES.HLS_STREAMING_PLAYLIST.PRIVATE, { fallthrough: false }), |
67 | handleStaticError | |
68 | ) | |
09209296 | 69 | staticRouter.use( |
9c6ca37f | 70 | STATIC_PATHS.STREAMING_PLAYLISTS.HLS, |
3545e72c | 71 | express.static(DIRECTORIES.HLS_STREAMING_PLAYLIST.PUBLIC, { fallthrough: false }), |
f7ce623d | 72 | handleStaticError |
09209296 C |
73 | ) |
74 | ||
65fcc311 C |
75 | // Thumbnails path for express |
76 | const thumbnailsPhysicalPath = CONFIG.STORAGE.THUMBNAILS_DIR | |
77 | staticRouter.use( | |
78 | STATIC_PATHS.THUMBNAILS, | |
f7ce623d C |
79 | express.static(thumbnailsPhysicalPath, { maxAge: STATIC_MAX_AGE.SERVER, fallthrough: false }), |
80 | handleStaticError | |
65fcc311 C |
81 | ) |
82 | ||
65fcc311 C |
83 | // --------------------------------------------------------------------------- |
84 | ||
85 | export { | |
86 | staticRouter | |
87 | } | |
71e3e879 C |
88 | |
89 | // --------------------------------------------------------------------------- | |
90 | ||
91 | async function servePrivateM3U8 (req: express.Request, res: express.Response) { | |
92 | const path = join(DIRECTORIES.HLS_STREAMING_PLAYLIST.PRIVATE, req.params.videoUUID, req.params.playlistName + '.m3u8') | |
73fb3dc5 | 93 | const filename = req.params.playlistName + '.m3u8' |
71e3e879 C |
94 | |
95 | let playlistContent: string | |
96 | ||
97 | try { | |
98 | playlistContent = await readFile(path, 'utf-8') | |
99 | } catch (err) { | |
100 | if (err.message.includes('ENOENT')) { | |
101 | return res.fail({ | |
102 | status: HttpStatusCode.NOT_FOUND_404, | |
103 | message: 'File not found' | |
104 | }) | |
105 | } | |
106 | ||
107 | throw err | |
108 | } | |
109 | ||
110 | // Inject token in playlist so players that cannot alter the HTTP request can still watch the video | |
111 | const transformedContent = doReinjectVideoFileToken(req) | |
73fb3dc5 | 112 | ? injectQueryToPlaylistUrls(playlistContent, buildReinjectVideoFileTokenQuery(req, filename.endsWith('master.m3u8'))) |
71e3e879 C |
113 | : playlistContent |
114 | ||
115 | return res.set('content-type', 'application/vnd.apple.mpegurl').send(transformedContent).end() | |
116 | } |