1 import express from 'express'
2 import { PassThrough, pipeline } from 'stream'
3 import { GetObjectCommandOutput } from '@aws-sdk/client-s3'
4 import { buildReinjectVideoFileTokenQuery } from '@server/controllers/shared/m3u8-playlist'
5 import { logger } from '@server/helpers/logger'
6 import { StreamReplacer } from '@server/helpers/stream-replacer'
7 import { MStreamingPlaylist, MVideo } from '@server/types/models'
8 import { HttpStatusCode } from '@shared/models'
9 import { injectQueryToPlaylistUrls } from '../hls'
10 import { getHLSFileReadStream, getWebTorrentFileReadStream } from './videos'
12 export async function proxifyWebTorrentFile (options: {
17 const { req, res, filename } = options
19 logger.debug('Proxifying WebTorrent file %s from object storage.', filename)
22 const { response: s3Response, stream } = await getWebTorrentFileReadStream({
24 rangeHeader: req.header('range')
27 setS3Headers(res, s3Response)
29 return stream.pipe(res)
31 return handleObjectStorageFailure(res, err)
35 export async function proxifyHLS (options: {
38 playlist: MStreamingPlaylist
41 reinjectVideoFileToken: boolean
43 const { req, res, playlist, video, filename, reinjectVideoFileToken } = options
45 logger.debug('Proxifying HLS file %s from object storage.', filename)
48 const { response: s3Response, stream } = await getHLSFileReadStream({
49 playlist: playlist.withVideo(video),
51 rangeHeader: req.header('range')
54 setS3Headers(res, s3Response)
56 const streamReplacer = reinjectVideoFileToken
57 ? new StreamReplacer(line => injectQueryToPlaylistUrls(line, buildReinjectVideoFileTokenQuery(req, filename.endsWith('master.m3u8'))))
67 handleObjectStorageFailure(res, err)
71 return handleObjectStorageFailure(res, err)
75 // ---------------------------------------------------------------------------
77 // ---------------------------------------------------------------------------
79 function 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)
86 status: HttpStatusCode.INTERNAL_SERVER_ERROR_500,
92 function 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)