From 0c9668f77901e7540e2c7045eb0f2974a4842a69 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 21 Apr 2023 14:55:10 +0200 Subject: Implement remote runner jobs in server Move ffmpeg functions to @shared --- server/lib/object-storage/index.ts | 1 + server/lib/object-storage/proxy.ts | 97 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 server/lib/object-storage/proxy.ts (limited to 'server/lib/object-storage') 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 @@ export * from './keys' +export * from './proxy' export * from './urls' export * 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 @@ +import express from 'express' +import { PassThrough, pipeline } from 'stream' +import { GetObjectCommandOutput } from '@aws-sdk/client-s3' +import { buildReinjectVideoFileTokenQuery } from '@server/controllers/shared/m3u8-playlist' +import { logger } from '@server/helpers/logger' +import { StreamReplacer } from '@server/helpers/stream-replacer' +import { MStreamingPlaylist, MVideo } from '@server/types/models' +import { HttpStatusCode } from '@shared/models' +import { injectQueryToPlaylistUrls } from '../hls' +import { getHLSFileReadStream, getWebTorrentFileReadStream } from './videos' + +export async function proxifyWebTorrentFile (options: { + req: express.Request + res: express.Response + filename: string +}) { + const { req, res, filename } = options + + logger.debug('Proxifying WebTorrent file %s from object storage.', filename) + + try { + const { response: s3Response, stream } = await getWebTorrentFileReadStream({ + filename, + rangeHeader: req.header('range') + }) + + setS3Headers(res, s3Response) + + return stream.pipe(res) + } catch (err) { + return handleObjectStorageFailure(res, err) + } +} + +export async function proxifyHLS (options: { + req: express.Request + res: express.Response + playlist: MStreamingPlaylist + video: MVideo + filename: string + reinjectVideoFileToken: boolean +}) { + const { req, res, playlist, video, filename, reinjectVideoFileToken } = options + + logger.debug('Proxifying HLS file %s from object storage.', filename) + + try { + const { response: s3Response, stream } = await getHLSFileReadStream({ + playlist: playlist.withVideo(video), + filename, + rangeHeader: req.header('range') + }) + + setS3Headers(res, s3Response) + + const streamReplacer = reinjectVideoFileToken + ? new StreamReplacer(line => injectQueryToPlaylistUrls(line, buildReinjectVideoFileTokenQuery(req, filename.endsWith('master.m3u8')))) + : new PassThrough() + + return pipeline( + stream, + streamReplacer, + res, + err => { + if (!err) return + + handleObjectStorageFailure(res, err) + } + ) + } catch (err) { + return handleObjectStorageFailure(res, err) + } +} + +// --------------------------------------------------------------------------- +// Private +// --------------------------------------------------------------------------- + +function handleObjectStorageFailure (res: express.Response, err: Error) { + if (err.name === 'NoSuchKey') { + logger.debug('Could not find key in object storage to proxify private HLS video file.', { err }) + return res.sendStatus(HttpStatusCode.NOT_FOUND_404) + } + + return res.fail({ + status: HttpStatusCode.INTERNAL_SERVER_ERROR_500, + message: err.message, + type: err.name + }) +} + +function setS3Headers (res: express.Response, s3Response: GetObjectCommandOutput) { + if (s3Response.$metadata.httpStatusCode === HttpStatusCode.PARTIAL_CONTENT_206) { + res.setHeader('Content-Range', s3Response.ContentRange) + res.status(HttpStatusCode.PARTIAL_CONTENT_206) + } +} -- cgit v1.2.3