mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-09-26 04:02:51 +02:00
- Replace legacy in-project M3U parser with vendored Gemorroj/M3uParser - Initialize parser via bootstrap with proper namespacing - Remove obsolete src/domain/Stream/M3UEntry.php implementation - Update stream import/review mapping: - use logo as fallback when tvg-logo is missing - Add third-party compliance files for vendored parser: - ATTRIBUTION.md - LGPL LICENSE - Add full M3uParser unit test suite: - tag parsing - parser behavior - fixtures - Add StreamService integration tests for M3U parsing/import flow - Update test bootstrap: - add igbinary_serialize / igbinary_unserialize polyfills - explicitly load M3uParser bootstrap and custom test tag class
129 lines
4.3 KiB
PHP
129 lines
4.3 KiB
PHP
<?php
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
/**
|
|
* Integration tests for StreamService::parseM3U().
|
|
*
|
|
* Validates the parsing behaviour and the import array field extraction
|
|
* logic that the stream import pipeline relies on.
|
|
*/
|
|
final class StreamServiceParseM3UTest extends TestCase {
|
|
// ── helpers ────────────────────────────────────────────────────────────
|
|
|
|
/** Shortcut: parse inline M3U string via StreamService. */
|
|
private function parse(string $m3u): \M3uParser\M3uData {
|
|
return StreamService::parseM3U($m3u, false);
|
|
}
|
|
|
|
/** Build import array from a single parsed entry (mirrors the logic in process()). */
|
|
private function buildImportArray(\M3uParser\M3uData $data): ?array {
|
|
foreach ($data as $result) {
|
|
$tags = $result->getExtTags();
|
|
$tag = $tags[0] ?? null;
|
|
$url = $result->getPath();
|
|
|
|
if (!$url) {
|
|
return null;
|
|
}
|
|
|
|
return [
|
|
'stream_source' => [$url],
|
|
'stream_icon' => $tag ? ($tag->getAttribute('tvg-logo') ?: ($tag->getAttribute('logo') ?: '')) : '',
|
|
'stream_display_name' => $tag
|
|
? ($tag->getTitle() ?: basename(parse_url($url, PHP_URL_PATH) ?: $url))
|
|
: basename(parse_url($url, PHP_URL_PATH) ?: $url),
|
|
];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
// ── parseM3U tests ─────────────────────────────────────────────────────
|
|
|
|
public function testParsesBasicExtInfEntry(): void {
|
|
$m3u = "#EXTM3U\n#EXTINF:-1 tvg-id=\"ch1\",Channel One\nhttp://example.com/stream.m3u8\n";
|
|
|
|
$data = $this->parse($m3u);
|
|
|
|
$this->assertCount(1, $data);
|
|
}
|
|
|
|
public function testParsesUrlCorrectly(): void {
|
|
$m3u = "#EXTM3U\n#EXTINF:-1,Test\nhttp://example.com/live.m3u8\n";
|
|
|
|
$data = $this->parse($m3u);
|
|
$entry = iterator_to_array($data)[0];
|
|
|
|
$this->assertSame('http://example.com/live.m3u8', $entry->getPath());
|
|
}
|
|
|
|
public function testEmptyM3UReturnsEmptyResult(): void {
|
|
$data = $this->parse("#EXTM3U\n");
|
|
|
|
$this->assertCount(0, $data);
|
|
}
|
|
|
|
public function testParsesMultipleEntries(): void {
|
|
$m3u = "#EXTM3U\n"
|
|
. "#EXTINF:-1,Ch1\nhttp://example.com/1.m3u8\n"
|
|
. "#EXTINF:-1,Ch2\nhttp://example.com/2.m3u8\n"
|
|
. "#EXTINF:-1,Ch3\nhttp://example.com/3.m3u8\n";
|
|
|
|
$data = $this->parse($m3u);
|
|
|
|
$this->assertCount(3, $data);
|
|
}
|
|
|
|
// ── import array field extraction ──────────────────────────────────────
|
|
|
|
public function testImportArrayContainsTvgLogo(): void {
|
|
$m3u = "#EXTM3U\n#EXTINF:-1 tvg-logo=\"http://example.com/logo.png\",Channel\nhttp://example.com/stream.m3u8\n";
|
|
|
|
$arr = $this->buildImportArray($this->parse($m3u));
|
|
|
|
$this->assertSame('http://example.com/logo.png', $arr['stream_icon']);
|
|
}
|
|
|
|
public function testImportArrayFallsBackToLogoAttribute(): void {
|
|
$m3u = "#EXTM3U\n#EXTINF:-1 logo=\"http://example.com/fallback.png\",Channel\nhttp://example.com/stream.m3u8\n";
|
|
|
|
$arr = $this->buildImportArray($this->parse($m3u));
|
|
|
|
$this->assertSame('http://example.com/fallback.png', $arr['stream_icon']);
|
|
}
|
|
|
|
public function testImportArrayIconEmptyWhenNoLogoAttributes(): void {
|
|
$m3u = "#EXTM3U\n#EXTINF:-1,Channel\nhttp://example.com/stream.m3u8\n";
|
|
|
|
$arr = $this->buildImportArray($this->parse($m3u));
|
|
|
|
$this->assertSame('', $arr['stream_icon']);
|
|
}
|
|
|
|
public function testImportArrayUsesExtInfTitle(): void {
|
|
$m3u = "#EXTM3U\n#EXTINF:-1,My Channel Name\nhttp://example.com/stream.m3u8\n";
|
|
|
|
$arr = $this->buildImportArray($this->parse($m3u));
|
|
|
|
$this->assertSame('My Channel Name', $arr['stream_display_name']);
|
|
}
|
|
|
|
public function testImportArrayFallsBackToUrlBasenameWhenNoTitle(): void {
|
|
// No EXTINF — entry has no tags.
|
|
$m3u = "#EXTM3U\nhttp://example.com/channel-name.m3u8\n";
|
|
|
|
$arr = $this->buildImportArray($this->parse($m3u));
|
|
|
|
$this->assertSame('channel-name.m3u8', $arr['stream_display_name']);
|
|
}
|
|
|
|
public function testImportArrayTvgLogoTakesPrecedenceOverLogo(): void {
|
|
$m3u = "#EXTM3U\n#EXTINF:-1 tvg-logo=\"http://example.com/tvg.png\" logo=\"http://example.com/logo.png\",Channel\nhttp://example.com/stream.m3u8\n";
|
|
|
|
$arr = $this->buildImportArray($this->parse($m3u));
|
|
|
|
$this->assertSame('http://example.com/tvg.png', $arr['stream_icon']);
|
|
}
|
|
}
|