94 lines
4.0 KiB
PHP
94 lines
4.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Plugin\AzuraCastOnDemandHls\Tests\Service;
|
|
|
|
use App\Entity\Station;
|
|
use App\Entity\StationMedia;
|
|
use App\Flysystem\StationFilesystems;
|
|
use PHPUnit\Framework\Attributes\CoversClass;
|
|
use Plugin\AzuraCastOnDemandHls\Config;
|
|
use Plugin\AzuraCastOnDemandHls\Exception\TranscodeException;
|
|
use Plugin\AzuraCastOnDemandHls\Service\CachePaths;
|
|
use Plugin\AzuraCastOnDemandHls\Service\HlsCache;
|
|
use Plugin\AzuraCastOnDemandHls\Tests\Support\TemporaryDirectoryTestCase;
|
|
use Psr\Log\NullLogger;
|
|
|
|
#[CoversClass(HlsCache::class)]
|
|
final class HlsCacheTest extends TemporaryDirectoryTestCase
|
|
{
|
|
public function testGeneratesAValidatedPackageAndReusesIt(): void
|
|
{
|
|
$source = $this->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;
|
|
}
|
|
}
|