aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib/object-storage
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib/object-storage')
-rw-r--r--server/lib/object-storage/index.ts1
-rw-r--r--server/lib/object-storage/proxy.ts97
2 files changed, 98 insertions, 0 deletions
diff --git a/server/lib/object-storage/index.ts b/server/lib/object-storage/index.ts
index 8b413a40e..6525f8dfb 100644
--- a/server/lib/object-storage/index.ts
+++ b/server/lib/object-storage/index.ts
@@ -1,3 +1,4 @@
1export * from './keys' 1export * from './keys'
2export * from './proxy'
2export * from './urls' 3export * from './urls'
3export * from './videos' 4export * from './videos'
diff --git a/server/lib/object-storage/proxy.ts b/server/lib/object-storage/proxy.ts
new file mode 100644
index 000000000..c782a8a25
--- /dev/null
+++ b/server/lib/object-storage/proxy.ts
@@ -0,0 +1,97 @@
1import express from 'express'
2import { PassThrough, pipeline } from 'stream'
3import { GetObjectCommandOutput } from '@aws-sdk/client-s3'
4import { buildReinjectVideoFileTokenQuery } from '@server/controllers/shared/m3u8-playlist'
5import { logger } from '@server/helpers/logger'
6import { StreamReplacer } from '@server/helpers/stream-replacer'
7import { MStreamingPlaylist, MVideo } from '@server/types/models'
8import { HttpStatusCode } from '@shared/models'
9import { injectQueryToPlaylistUrls } from '../hls'
10import { getHLSFileReadStream, getWebTorrentFileReadStream } from './videos'
11
12export async function proxifyWebTorrentFile (options: {
13 req: express.Request
14 res: express.Response
15 filename: string
16}) {
17 const { req, res, filename } = options
18
19 logger.debug('Proxifying WebTorrent file %s from object storage.', filename)
20
21 try {
22 const { response: s3Response, stream } = await getWebTorrentFileReadStream({
23 filename,
24 rangeHeader: req.header('range')
25 })
26
27 setS3Headers(res, s3Response)
28
29 return stream.pipe(res)
30 } catch (err) {
31 return handleObjectStorageFailure(res, err)
32 }
33}
34
35export async function proxifyHLS (options: {
36 req: express.Request
37 res: express.Response
38 playlist: MStreamingPlaylist
39 video: MVideo
40 filename: string
41 reinjectVideoFileToken: boolean
42}) {
43 const { req, res, playlist, video, filename, reinjectVideoFileToken } = options
44
45 logger.debug('Proxifying HLS file %s from object storage.', filename)
46
47 try {
48 const { response: s3Response, stream } = await getHLSFileReadStream({
49 playlist: playlist.withVideo(video),
50 filename,
51 rangeHeader: req.header('range')
52 })
53
54 setS3Headers(res, s3Response)
55
56 const streamReplacer = reinjectVideoFileToken
57 ? new StreamReplacer(line => injectQueryToPlaylistUrls(line, buildReinjectVideoFileTokenQuery(req, filename.endsWith('master.m3u8'))))
58 : new PassThrough()
59
60 return pipeline(
61 stream,
62 streamReplacer,
63 res,
64 err => {
65 if (!err) return
66
67 handleObjectStorageFailure(res, err)
68 }
69 )
70 } catch (err) {
71 return handleObjectStorageFailure(res, err)
72 }
73}
74
75// ---------------------------------------------------------------------------
76// Private
77// ---------------------------------------------------------------------------
78
79function handleObjectStorageFailure (res: express.Response, err: Error) {
80 if (err.name === 'NoSuchKey') {
81 logger.debug('Could not find key in object storage to proxify private HLS video file.', { err })
82 return res.sendStatus(HttpStatusCode.NOT_FOUND_404)
83 }
84
85 return res.fail({
86 status: HttpStatusCode.INTERNAL_SERVER_ERROR_500,
87 message: err.message,
88 type: err.name
89 })
90}
91
92function setS3Headers (res: express.Response, s3Response: GetObjectCommandOutput) {
93 if (s3Response.$metadata.httpStatusCode === HttpStatusCode.PARTIAL_CONTENT_206) {
94 res.setHeader('Content-Range', s3Response.ContentRange)
95 res.status(HttpStatusCode.PARTIAL_CONTENT_206)
96 }
97}