temporaryDirectory . '/source.mp3'; $counter = $this->temporaryDirectory . '/ffmpeg-calls'; file_put_contents($source, 'input'); $script = $this->makeFfmpegScript($counter, false); $cache = $this->cache($source, $script); $station = new Station(7, $this->temporaryDirectory); $media = new StationMedia(2, 'abcdefghijklmnopqrstuvwx', 123, 'source.mp3'); $key = $cache->ensure($station, $media); self::assertSame($key, $cache->ensure($station, $media)); self::assertSame("#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-STREAM-INF:BANDWIDTH=140800,CODECS=\"mp4a.40.2\"\nmedia.m3u8\n", file_get_contents($this->temporaryDirectory . '/ondemand-hls/assets/' . $key . '/master.m3u8')); self::assertFileExists($this->temporaryDirectory . '/ondemand-hls/assets/' . $key . '/media.m3u8'); self::assertFileExists($this->temporaryDirectory . '/ondemand-hls/assets/' . $key . '/segment00000.ts'); self::assertSame("1\n", file_get_contents($counter)); } public function testRejectsMalformedFfmpegOutputWithoutPublishingIt(): void { $source = $this->temporaryDirectory . '/source.mp3'; file_put_contents($source, 'input'); $script = $this->makeFfmpegScript($this->temporaryDirectory . '/ffmpeg-calls', true); $cache = $this->cache($source, $script); $station = new Station(7, $this->temporaryDirectory); $media = new StationMedia(2, 'abcdefghijklmnopqrstuvwx', 123, 'source.mp3'); $this->expectException(TranscodeException::class); try { $cache->ensure($station, $media); } finally { self::assertDirectoryDoesNotExist($this->temporaryDirectory . '/ondemand-hls/assets/' . $cache->key($station, $media)); } } private function cache(string $source, string $ffmpeg): HlsCache { $config = new Config(cacheTtl: 1800, ffmpegBinary: $ffmpeg); $filesystem = new class($source) { public function __construct(private string $source) { } public function fileExists(string $path): bool { return 'source.mp3' === $path; } public function withLocalFile(string $path, callable $callback): mixed { return $callback($this->source); } }; return new HlsCache( $config, new CachePaths($config), new StationFilesystems($filesystem), new NullLogger(), ); } private function makeFfmpegScript(string $counter, bool $malformed): string { $script = $this->temporaryDirectory . '/fake-ffmpeg-' . bin2hex(random_bytes(4)) . '.sh'; $segment = $malformed ? "printf '#EXTM3U\\n../outside.ts\\n' > \"\$output\"\n" : "printf '#EXTM3U\\n#EXT-X-ENDLIST\\nsegment00000.ts\\n' > \"\$output\"\nsegment_file=\$(printf \"\$segment\" 0)\nprintf segment > \"\$segment_file\"\n"; file_put_contents($script, "#!/bin/sh\nset -eu\noutput=''\nsegment=''\nprevious=''\nfor argument in \"\$@\"; do\n if [ \"\$previous\" = '-hls_segment_filename' ]; then segment=\"\$argument\"; fi\n previous=\"\$argument\"\n output=\"\$argument\"\ndone\nprintf '1\\n' >> " . escapeshellarg($counter) . "\n" . $segment); chmod($script, 0700); return $script; } }