// npm import bluebird from 'bluebird'; import * as path from 'path'; import { v4 as uuid } from 'uuid'; import { S3 } from 'aws-sdk'; // @ownzones import { s3 as ownS3 } from '@ownzones/lib'; // app import { wrappedFs as fs } from '../../utils/async-wrappers'; import { Honeycomb, TracedHttpsAgent } from '../tracing'; import { config, log } from '../../config'; import { SegmentBuilderHelper } from './segment-builder-helper'; import { ISegment } from '../playlist-builder'; import { ISegmentTranscoder, ISegmentTranscodeResult } from './segment-transcoder'; const s3 = new S3({ // logger: console, httpOptions: { agent: new TracedHttpsAgent(), }, }); /** * Concatenates multiple subsegments into the final hls segment */ export class ConcatSegment implements ISegmentTranscoder { public static async concatSegments(urls: string[], totalStartTime: number): Promise { const localPaths = await bluebird.map(urls, async (url) => Honeycomb.startAsyncSpan( path.basename(__filename), 'downloadFromS3', async (segmentLoadSpan) => { const { bucket, key } = ownS3.parseUrl(url); const request = s3.getObject({ Bucket: bucket, Key: key }); const content = (await request.promise()).Body; const writeSpan = Honeycomb.startSpan(path.basename(__filename), 'writeToDisk', { url }); const filePath = path.join(config.mediaPath, `${uuid()}.ts`); await fs.writeFileAsync(filePath, content); Honeycomb.endSpan(writeSpan); Honeycomb.endSpan(segmentLoadSpan); return filePath; }, { url }, )); const txtFilePath = path.join(config.mediaPath, `${uuid()}.txt`); try { await fs.writeFileAsync(txtFilePath, localPaths.map((x) => `file '${x}'`).join('\n')); return await SegmentBuilderHelper.ffmpegSetOffset( txtFilePath, Number(totalStartTime) + 1, true, ); } catch (err) { log.error(`ConcatSegment failed for segment at time: ${totalStartTime}`); throw err; } finally { await fs.unlinkAsync(txtFilePath); } } /** * Given a segment path, it offsets the totalStartTime of said segment and returns the data. * @param segmentPath {string} * @param totalStartTime {number} */ public static async offsetSegmentStartTime(segmentPath: string, totalStartTime: number): Promise { return Honeycomb.startAsyncSpan(path.basename(__filename), 'offsetBigCacheSegment', async (offsetBigCacheSegment) => { const signedUrl = await ownS3.signUrl(segmentPath); const offsetSegmentPath = await SegmentBuilderHelper.ffmpegSetOffset( signedUrl, Number(totalStartTime) + 1, false, ); const segmentData = fs.readFileSync(offsetSegmentPath); Honeycomb.endSpan(offsetBigCacheSegment); return segmentData; }); } public segment: ISegment; public constructor(segment: ISegment) { this.segment = segment; } public async transcode(): Promise { const tsFilePath = await ConcatSegment.concatSegments(this.segment.urls as string[], this.segment.totalStartTime); return { segmentData: fs.readFileSync(tsFilePath) }; } }