add human-readable duration in feed

This commit is contained in:
dece 2024-02-25 22:51:24 +01:00
parent 5c2b34013f
commit 916d7baec2
3 changed files with 223 additions and 9 deletions

View file

@ -12,6 +12,7 @@
"doctrine/doctrine-bundle": "^2.9", "doctrine/doctrine-bundle": "^2.9",
"doctrine/doctrine-migrations-bundle": "^3.2", "doctrine/doctrine-migrations-bundle": "^3.2",
"doctrine/orm": "^2.15", "doctrine/orm": "^2.15",
"php-ffmpeg/php-ffmpeg": "^1.2",
"phpdocumentor/reflection-docblock": "^5.3", "phpdocumentor/reflection-docblock": "^5.3",
"phpstan/phpdoc-parser": "^1.20", "phpstan/phpdoc-parser": "^1.20",
"sensio/framework-extra-bundle": "^6.1", "sensio/framework-extra-bundle": "^6.1",

199
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "c241853e026884d05d25f49b85867c16", "content-hash": "8adb110d727d28dd8b1233bbe99e92de",
"packages": [ "packages": [
{ {
"name": "doctrine/annotations", "name": "doctrine/annotations",
@ -1463,6 +1463,53 @@
], ],
"time": "2023-01-14T14:17:03+00:00" "time": "2023-01-14T14:17:03+00:00"
}, },
{
"name": "evenement/evenement",
"version": "v3.0.2",
"source": {
"type": "git",
"url": "https://github.com/igorw/evenement.git",
"reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc",
"reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc",
"shasum": ""
},
"require": {
"php": ">=7.0"
},
"require-dev": {
"phpunit/phpunit": "^9 || ^6"
},
"type": "library",
"autoload": {
"psr-4": {
"Evenement\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Igor Wiedler",
"email": "igor@wiedler.ch"
}
],
"description": "Événement is a very simple event dispatching library for PHP",
"keywords": [
"event-dispatcher",
"event-emitter"
],
"support": {
"issues": "https://github.com/igorw/evenement/issues",
"source": "https://github.com/igorw/evenement/tree/v3.0.2"
},
"time": "2023-08-08T05:53:35+00:00"
},
{ {
"name": "monolog/monolog", "name": "monolog/monolog",
"version": "3.4.0", "version": "3.4.0",
@ -1564,6 +1611,95 @@
], ],
"time": "2023-06-21T08:46:11+00:00" "time": "2023-06-21T08:46:11+00:00"
}, },
{
"name": "php-ffmpeg/php-ffmpeg",
"version": "v1.2.0",
"source": {
"type": "git",
"url": "https://github.com/PHP-FFMpeg/PHP-FFMpeg.git",
"reference": "785a5ba05dd88b3b8146f85f18476b259b23917c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-FFMpeg/PHP-FFMpeg/zipball/785a5ba05dd88b3b8146f85f18476b259b23917c",
"reference": "785a5ba05dd88b3b8146f85f18476b259b23917c",
"shasum": ""
},
"require": {
"evenement/evenement": "^3.0",
"php": "^8.0 || ^8.1 || ^8.2 || ^8.3",
"psr/log": "^1.0 || ^2.0 || ^3.0",
"spatie/temporary-directory": "^2.0",
"symfony/cache": "^5.4 || ^6.0 || ^7.0",
"symfony/process": "^5.4 || ^6.0 || ^7.0"
},
"require-dev": {
"mockery/mockery": "^1.5",
"phpunit/phpunit": "^9.5.10"
},
"suggest": {
"php-ffmpeg/extras": "A compilation of common audio & video drivers for PHP-FFMpeg"
},
"type": "library",
"autoload": {
"psr-4": {
"FFMpeg\\": "src/FFMpeg",
"Alchemy\\BinaryDriver\\": "src/Alchemy/BinaryDriver"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Romain Neutron",
"email": "imprec@gmail.com",
"homepage": "http://www.lickmychip.com/"
},
{
"name": "Phraseanet Team",
"email": "info@alchemy.fr",
"homepage": "http://www.phraseanet.com/"
},
{
"name": "Patrik Karisch",
"email": "patrik@karisch.guru",
"homepage": "http://www.karisch.guru"
},
{
"name": "Romain Biard",
"email": "romain.biard@gmail.com",
"homepage": "https://www.strime.io/"
},
{
"name": "Jens Hausdorf",
"email": "hello@jens-hausdorf.de",
"homepage": "https://jens-hausdorf.de"
},
{
"name": "Pascal Baljet",
"email": "pascal@protone.media",
"homepage": "https://protone.media"
}
],
"description": "FFMpeg PHP, an Object Oriented library to communicate with AVconv / ffmpeg",
"keywords": [
"audio",
"audio processing",
"avconv",
"avprobe",
"ffmpeg",
"ffprobe",
"video",
"video processing"
],
"support": {
"issues": "https://github.com/PHP-FFMpeg/PHP-FFMpeg/issues",
"source": "https://github.com/PHP-FFMpeg/PHP-FFMpeg/tree/v1.2.0"
},
"time": "2024-01-02T10:37:01+00:00"
},
{ {
"name": "phpdocumentor/reflection-common", "name": "phpdocumentor/reflection-common",
"version": "2.2.0", "version": "2.2.0",
@ -2115,6 +2251,67 @@
"abandoned": "Symfony", "abandoned": "Symfony",
"time": "2023-02-24T14:57:12+00:00" "time": "2023-02-24T14:57:12+00:00"
}, },
{
"name": "spatie/temporary-directory",
"version": "2.2.1",
"source": {
"type": "git",
"url": "https://github.com/spatie/temporary-directory.git",
"reference": "76949fa18f8e1a7f663fd2eaa1d00e0bcea0752a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/temporary-directory/zipball/76949fa18f8e1a7f663fd2eaa1d00e0bcea0752a",
"reference": "76949fa18f8e1a7f663fd2eaa1d00e0bcea0752a",
"shasum": ""
},
"require": {
"php": "^8.0"
},
"require-dev": {
"phpunit/phpunit": "^9.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Spatie\\TemporaryDirectory\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Alex Vanderbist",
"email": "alex@spatie.be",
"homepage": "https://spatie.be",
"role": "Developer"
}
],
"description": "Easily create, use and destroy temporary directories",
"homepage": "https://github.com/spatie/temporary-directory",
"keywords": [
"php",
"spatie",
"temporary-directory"
],
"support": {
"issues": "https://github.com/spatie/temporary-directory/issues",
"source": "https://github.com/spatie/temporary-directory/tree/2.2.1"
},
"funding": [
{
"url": "https://spatie.be/open-source/support-us",
"type": "custom"
},
{
"url": "https://github.com/spatie",
"type": "github"
}
],
"time": "2023-12-25T11:46:58+00:00"
},
{ {
"name": "symfony/asset", "name": "symfony/asset",
"version": "v6.2.7", "version": "v6.2.7",

View file

@ -8,6 +8,7 @@ use DOMDocument;
use DOMElement; use DOMElement;
use DOMText; use DOMText;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use FFMpeg\FFProbe;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
@ -31,14 +32,20 @@ class FeedService
$this->baseImageUrl = $rootUrl . Constants::IMAGES_BASE_PATH; $this->baseImageUrl = $rootUrl . Constants::IMAGES_BASE_PATH;
} }
public const DOCUMENT_VERSION = '2.0'; public const XML_NS = 'http://www.w3.org/2000/xmlns/';
public const ATOM_NS = 'http://www.w3.org/2005/Atom';
public const ITUNES_NS = 'http://www.itunes.com/dtds/podcast-1.0.dtd';
public function generate(Podcast $podcast): string|false public function generate(Podcast $podcast): string|false
{ {
$document = new DOMDocument(); $document = new DOMDocument(version: '1.0', encoding: 'UTF-8');
$rssElement = new DOMElement('rss');
$rssElement = $document->createElement('rss');
$rssElement->setAttribute('version', '2.0');
$rssElement->setAttributeNS(self::XML_NS, 'xmlns:atom', self::ATOM_NS);
$rssElement->setAttributeNS(self::XML_NS, 'xmlns:itunes', self::ITUNES_NS);
$document->appendChild($rssElement); $document->appendChild($rssElement);
$rssElement->setAttribute('version', $this::DOCUMENT_VERSION);
$channelElement = new DOMElement('channel'); $channelElement = new DOMElement('channel');
$rssElement->appendChild($channelElement); $rssElement->appendChild($channelElement);
$titleElement = new DOMElement('title'); $titleElement = new DOMElement('title');
@ -70,15 +77,24 @@ class FeedService
$itemElement->appendChild(new DOMElement('description', $episode->getDescription())); $itemElement->appendChild(new DOMElement('description', $episode->getDescription()));
$itemElement->appendChild(new DOMElement('link', $audioUrl)); $itemElement->appendChild(new DOMElement('link', $audioUrl));
$itemElement->appendChild(new DOMElement('guid', $audioUrl)); $itemElement->appendChild(new DOMElement('guid', $audioUrl));
$enclosureElement = new DOMElement('enclosure');
$itemElement->appendChild($enclosureElement);
$enclosureElement->setAttribute('url', $audioUrl);
$filepath = $filepath =
$this->parameterBag->get('kernel.project_dir') ($this->parameterBag->get('kernel.project_dir') ?: '')
. '/' . '/'
. Constants::BASE_PUBLIC_DIR . Constants::BASE_PUBLIC_DIR
. Constants::FILES_BASE_PATH . Constants::FILES_BASE_PATH
. $episode->getAudioFilename(); . $episode->getAudioFilename();
// Use FFProbe to get the media duration, probably the easiest way but requires a working FFMpeg install.
$duration = floatval(FFProbe::create()->format($filepath)->get('duration'));
$duration_minutes = intval($duration / 60);
$duration_seconds = str_pad(strval(intval($duration % 60)), 2, '0', STR_PAD_LEFT);
$duration_str = "{$duration_minutes}:{$duration_seconds}";
$itemElement->appendChild(new DOMElement('itunes:duration', $duration_str, self::ITUNES_NS));
$enclosureElement = new DOMElement('enclosure');
$itemElement->appendChild($enclosureElement);
$enclosureElement->setAttribute('url', $audioUrl);
$enclosureElement->setAttribute('type', mime_content_type($filepath)); $enclosureElement->setAttribute('type', mime_content_type($filepath));
$enclosureElement->setAttribute('length', (string) filesize($filepath)); $enclosureElement->setAttribute('length', (string) filesize($filepath));
} }