Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions formats/M3uFormat.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

/**
* M3U
*
*/
class M3uFormat extends FormatAbstract
{
const MIME_TYPE = 'application/mpegurl';
private $item_duration = null;
private $item_title = null;
private $item_url = null;
private $item_bytes = null;

private function resetItem()
{
$this->item_duration = null;
$this->item_title = null;
$this->item_url = null;
$this->item_bytes = null;
}
private function itemIsEmpty(): bool
{
return $this->item_url === null;
}
private function itemRender(): string
{
if ($this->itemIsEmpty()) {
return '';
}
$text = "\n";
$commentParts = [];
if ($this->item_duration !== null && $this->item_duration > 0) {
$commentParts[] = $this->item_duration;
}
if ($this->item_title !== null) {
$commentParts[] = $this->item_title;
}

if (count($commentParts) !== 0) {
$text .= '#EXTINF:' . implode(',', $commentParts) . "\n";
}

$text .= $this->item_url . "\n";
return $text;
}

public function stringify()
{
$contents = "#EXTM3U\n";

foreach ($this->getItems() as $item) {
$this->resetItem();
$itemArray = $item->toArray();

if (isset($itemArray['enclosure'])) {
$this->item_url = $itemArray['enclosure']['url'];
$this->item_bytes = $itemArray['enclosure']['length'];
if (isset($itemArray['itunes']) && isset($itemArray['itunes']['duration'])) {
$this->item_duration = self::parseDuration($itemArray['itunes']['duration']);
}
}
if (isset($itemArray['title'])) {
$item->item_title = $itemArray['title'];
}
if (! $this->itemIsEmpty()) {
$contents .= $this->itemRender();
} else {
foreach ($item->enclosures as $url) {
$this->resetItem();
$this->item_url = $url;
if (isset($itemArray['title'])) {
$this->item_title = $itemArray['title'];
}
$contents .= $this->itemRender();
}
}
}
return mb_convert_encoding($contents, $this->getCharset(), 'UTF-8');
}
/*
* parseDuration converts a string like "00:4:20" to 260
* allowing to convert duration as used in the itunes:duration tag to the number of seconds
*/
private static function parseDuration(string $duration_string): int
{
$seconds = 0;
$parts = explode(':', $duration_string);
for ($i = 0; $i < count($parts); $i++) {
$seconds += intval($parts[count($parts) - $i - 1]) * pow(60, $i);
}
return $seconds;
}
}
29 changes: 29 additions & 0 deletions tests/Formats/M3uFormatTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

/**
* M3uFormat
*/

namespace RssBridge\Tests\Formats;

require_once __DIR__ . '/BaseFormatTest.php';

use PHPUnit\Framework\TestCase;

class M3uFormatTest extends BaseFormatTest
{
private const PATH_EXPECTED = self::PATH_SAMPLES . 'expectedM3uFormat/';

/**
* @dataProvider sampleProvider
* @runInSeparateProcess
*/
public function testOutput(string $name, string $path)
{
$data = $this->formatData('M3u', $this->loadSample($path));

$expected = file_get_contents(self::PATH_EXPECTED . $name . '.m3u');
$this->assertEquals($expected, $data);
}
}

4 changes: 4 additions & 0 deletions tests/Formats/samples/expectedM3uFormat/feed.common.m3u
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#EXTM3U

#EXTINF:Atom draft-07 snapshot
http://example.org/audio/ph34r_my_podcast.mp3
1 change: 1 addition & 0 deletions tests/Formats/samples/expectedM3uFormat/feed.empty.m3u
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#EXTM3U
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#EXTM3U
1 change: 1 addition & 0 deletions tests/Formats/samples/expectedM3uFormat/feed.microblog.m3u
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#EXTM3U