sintonia/legacy/application/services/PodcastService.php

523 lines
20 KiB
PHP
Raw Permalink Normal View History

2015-10-13 16:14:23 +02:00
<?php
class InvalidPodcastException extends Exception {}
2015-10-13 16:14:23 +02:00
class PodcastNotFoundException extends Exception {}
2015-10-13 16:14:23 +02:00
class Application_Service_PodcastService
2015-10-13 16:14:23 +02:00
{
// These fields should never be modified with POST/PUT data
2021-10-11 16:10:47 +02:00
private static $privateFields = [
'id',
'url',
'type',
'owner',
];
2015-10-13 16:14:23 +02:00
/**
2021-10-11 16:10:47 +02:00
* Returns parsed rss feed, or false if the given URL cannot be downloaded.
2015-10-13 16:14:23 +02:00
*
* @param string $feedUrl String containing the podcast feed URL
2015-10-13 16:14:23 +02:00
*
* @return mixed
*/
public static function getPodcastFeed($feedUrl)
{
try {
$feed = new SimplePie();
$feed->set_feed_url($feedUrl);
$feed->enable_cache(false);
$feed->init();
2021-10-11 16:10:47 +02:00
2015-10-13 16:14:23 +02:00
return $feed;
} catch (Exception $e) {
return false;
}
}
/** Creates a Podcast object from the given podcast URL.
2021-10-11 16:10:47 +02:00
* This is used by our Podcast REST API.
2015-10-13 16:14:23 +02:00
*
* @param string $feedUrl Podcast RSS Feed Url
2015-10-13 16:14:23 +02:00
*
2022-09-12 13:16:14 +02:00
* @return array Podcast Array with a full list of episodes
*
2015-10-13 16:14:23 +02:00
* @throws Exception
* @throws InvalidPodcastException
*/
public static function createFromFeedUrl($feedUrl)
{
// TODO: why is this so slow?
2015-10-13 16:14:23 +02:00
$rss = self::getPodcastFeed($feedUrl);
Vendorize ZF1, fix PHPUnit and configure travis This a a rather large commit due to the nature of the stuff it is touching. To get PHPUnit up and running again I had to update some deps and I did so by vendorizing them. The vendorizing of zf1 makes sense since distros are already considering to drop it from their repos. * [x] install vendorized zf1 with composer * [x] load composer autoloader before zf1 * [x] Implement headAction for all Zend_Rest_Controller based controllers * [x] switch to yml dataset to get around string only limitations of xml sets (also removed warning in readme) * [x] use year 2044 as hardcoded date for tests since it is in the future and has the same days like previously used 2016 * [x] make tests easier to run when accessing phpunit directly * [x] clean up test helper to always use airtime.conf * [x] switch test dbname to libretime_test * [x] test db username password switched to libretime/libretime * [x] install phpunit with composer in a clear version (make tests easier to reproduce on other platforms) * [x] remove local libs from airtime repo (most of airtime_mvc/library was not needed of in vendor already) * [x] configure composer autoloading and use it (also removed requires that are not needed anymore) * [x] add LibreTime prefix for FileNotFoundException (phing had a similar class and these are all pre-namespace style) * [x] add .travis.yml file * [x] make etc and logdir configurable with LIBRETIME_CONF_DIR and LIBRETIME_LOG_DIR env (so travis can change it) * [x] slight cleanup in config for travis not to fail * [x] add cloud_storage.conf for during test runs * [x] rewrite mvc testing docs and move them to docs/ folder * [x] don't use `static::class` in a class that does not have a parent class, use `__CLASS__` instead. * [x] don't use `<ClassName>::class`, since we already know what class we want `"<ClassName>"` ist just fine. * [x] fix "can't use method in write context" errors on 5.4 (also helps the optimizer) * [x] add build status badge on main README.md Fixes https://github.com/LibreTime/libretime/issues/4 The PHP parts of https://github.com/LibreTime/libretime/pull/10 get obsoleted by this change and it will need rebasing. This also contains https://github.com/LibreTime/libretime/pull/8, the late static binding compat code was broken for no reason and until CentOS drops php 5.4 there is no reason I'm aware of not to support it. I inlined #8 since the test would be failing on php 5.4 without the change. If you want to run tests you need to run `composer install` in the root directory and then `cd airtime_mvc/tests && ../../vendor/bin/phpunit`. For the tests to run the user `libretime` needs to be allowed to create the `libretime_test` database. See `docs/TESTING.md` for more info on getting set up.
2017-02-20 21:47:53 +01:00
if (!$rss) {
2015-10-13 16:14:23 +02:00
throw new InvalidPodcastException();
}
Vendorize ZF1, fix PHPUnit and configure travis This a a rather large commit due to the nature of the stuff it is touching. To get PHPUnit up and running again I had to update some deps and I did so by vendorizing them. The vendorizing of zf1 makes sense since distros are already considering to drop it from their repos. * [x] install vendorized zf1 with composer * [x] load composer autoloader before zf1 * [x] Implement headAction for all Zend_Rest_Controller based controllers * [x] switch to yml dataset to get around string only limitations of xml sets (also removed warning in readme) * [x] use year 2044 as hardcoded date for tests since it is in the future and has the same days like previously used 2016 * [x] make tests easier to run when accessing phpunit directly * [x] clean up test helper to always use airtime.conf * [x] switch test dbname to libretime_test * [x] test db username password switched to libretime/libretime * [x] install phpunit with composer in a clear version (make tests easier to reproduce on other platforms) * [x] remove local libs from airtime repo (most of airtime_mvc/library was not needed of in vendor already) * [x] configure composer autoloading and use it (also removed requires that are not needed anymore) * [x] add LibreTime prefix for FileNotFoundException (phing had a similar class and these are all pre-namespace style) * [x] add .travis.yml file * [x] make etc and logdir configurable with LIBRETIME_CONF_DIR and LIBRETIME_LOG_DIR env (so travis can change it) * [x] slight cleanup in config for travis not to fail * [x] add cloud_storage.conf for during test runs * [x] rewrite mvc testing docs and move them to docs/ folder * [x] don't use `static::class` in a class that does not have a parent class, use `__CLASS__` instead. * [x] don't use `<ClassName>::class`, since we already know what class we want `"<ClassName>"` ist just fine. * [x] fix "can't use method in write context" errors on 5.4 (also helps the optimizer) * [x] add build status badge on main README.md Fixes https://github.com/LibreTime/libretime/issues/4 The PHP parts of https://github.com/LibreTime/libretime/pull/10 get obsoleted by this change and it will need rebasing. This also contains https://github.com/LibreTime/libretime/pull/8, the late static binding compat code was broken for no reason and until CentOS drops php 5.4 there is no reason I'm aware of not to support it. I inlined #8 since the test would be failing on php 5.4 without the change. If you want to run tests you need to run `composer install` in the root directory and then `cd airtime_mvc/tests && ../../vendor/bin/phpunit`. For the tests to run the user `libretime` needs to be allowed to create the `libretime_test` database. See `docs/TESTING.md` for more info on getting set up.
2017-02-20 21:47:53 +01:00
$rssErr = $rss->error();
if (!empty($rssErr)) {
throw new InvalidPodcastException($rssErr);
}
2015-10-13 16:14:23 +02:00
// Ensure we are only creating Podcast with the given URL, and excluding
// any extra data fields that may have been POSTED
2021-10-11 16:10:47 +02:00
$podcastArray = [];
$podcastArray['url'] = $feedUrl;
2015-10-13 16:14:23 +02:00
2021-10-11 16:10:47 +02:00
$podcastArray['title'] = htmlspecialchars($rss->get_title());
$podcastArray['description'] = htmlspecialchars($rss->get_description());
$podcastArray['link'] = htmlspecialchars($rss->get_link());
$podcastArray['language'] = htmlspecialchars($rss->get_language());
$podcastArray['copyright'] = htmlspecialchars($rss->get_copyright());
Vagrant Debian support (and experimental CentOS) This changes the Vagrant setup to support multiple installations as multiple boxes. In addition to Ubuntu Vagrant can now be used to install on Debian as well as on CentOS. I took the chance to clean up the .deb install a bit and backported analyzer and celery to SysV proper so it runs there. Some of the distro specfics were moved to the install script from the python setup scripts to acheive this. For the CentOS support I added a rather involved OS prepare script. In the long term this will be added to the preparing-the-server docs we already have. I had to switch the default port to http-alt (8080). On CentOS 9080 is registered for ocsp and getting it to work for apache without hacking SELinux is hard. I think 8080 is the RFC way to go anyhow. If anyone want to override this it should be rather easy using the --web-port arg and by hacking Vagrantfile. The PyOpenSSL code has been refactored for all the distros that the Vagrantfile now supports. As far as my checks go, I tried this code with all the distros, uploaded a track and downloaded a unicode and a ssl podcast and was able to listen to them in each case. In the experimental CentOS case, the UI is not up to spec since services need to get scheduled through systemctl and the status overview (ie. on the /?config page) do not work properly. They need to be as follows: ``` sudo systemctl start airtime-playout sudo systemctl start airtime-liquidsoap sudo systemctl start airtime_analyzer.service sudo systemctl start airtime-celery.service ```
2017-03-08 12:39:59 +01:00
$author = $rss->get_author();
2021-10-11 16:10:47 +02:00
$name = empty($author) ? '' : $author->get_name();
$podcastArray['creator'] = htmlspecialchars($name);
2021-10-11 16:10:47 +02:00
$categories = [];
if (is_array($rss->get_categories())) {
foreach ($rss->get_categories() as $category) {
2021-10-11 16:10:47 +02:00
array_push($categories, $category->get_scheme() . ':' . $category->get_term());
}
}
$podcastArray['category'] = htmlspecialchars(implode('', $categories));
2015-10-13 16:14:23 +02:00
// TODO: put in constants
2021-10-11 16:10:47 +02:00
$itunesChannel = 'http://www.itunes.com/dtds/podcast-1.0.dtd';
2015-10-13 16:14:23 +02:00
$itunesSubtitle = $rss->get_channel_tags($itunesChannel, 'subtitle');
2021-10-11 16:10:47 +02:00
$podcastArray['itunes_subtitle'] = isset($itunesSubtitle[0]['data']) ? $itunesSubtitle[0]['data'] : '';
2015-10-13 16:14:23 +02:00
$itunesCategory = $rss->get_channel_tags($itunesChannel, 'category');
2021-10-11 16:10:47 +02:00
$categoryArray = [];
if (is_array($itunesCategory)) {
foreach ($itunesCategory as $c => $data) {
2021-10-11 16:10:47 +02:00
foreach ($data['attribs'] as $attrib) {
array_push($categoryArray, $attrib['text']);
}
2015-10-13 16:14:23 +02:00
}
}
2021-10-11 16:10:47 +02:00
$podcastArray['itunes_category'] = implode(',', $categoryArray);
2015-10-13 16:14:23 +02:00
$itunesAuthor = $rss->get_channel_tags($itunesChannel, 'author');
2021-10-11 16:10:47 +02:00
$podcastArray['itunes_author'] = isset($itunesAuthor[0]['data']) ? $itunesAuthor[0]['data'] : '';
2015-10-13 16:14:23 +02:00
$itunesSummary = $rss->get_channel_tags($itunesChannel, 'summary');
2021-10-11 16:10:47 +02:00
$podcastArray['itunes_summary'] = isset($itunesSummary[0]['data']) ? $itunesSummary[0]['data'] : '';
2015-10-13 16:14:23 +02:00
$itunesKeywords = $rss->get_channel_tags($itunesChannel, 'keywords');
2021-10-11 16:10:47 +02:00
$podcastArray['itunes_keywords'] = isset($itunesKeywords[0]['data']) ? $itunesKeywords[0]['data'] : '';
2015-10-13 16:14:23 +02:00
$itunesExplicit = $rss->get_channel_tags($itunesChannel, 'explicit');
2021-10-11 16:10:47 +02:00
$podcastArray['itunes_explicit'] = isset($itunesExplicit[0]['data']) ? $itunesExplicit[0]['data'] : '';
2015-10-13 16:14:23 +02:00
self::validatePodcastMetadata($podcastArray);
try {
// Base class
$podcast = new Podcast();
$podcast->fromArray($podcastArray, BasePeer::TYPE_FIELDNAME);
$podcast->setDbOwner(self::getOwnerId());
$podcast->save();
$importedPodcast = new ImportedPodcast();
$importedPodcast->fromArray($podcastArray, BasePeer::TYPE_FIELDNAME);
$importedPodcast->setPodcast($podcast);
$importedPodcast->setDbAutoIngest(true);
2015-10-13 16:14:23 +02:00
$importedPodcast->save();
// if the autosmartblock and album override are enabled then create a smartblock and playlist matching this podcast via the album name
if (Application_Model_Preference::GetPodcastAutoSmartblock() && Application_Model_Preference::GetPodcastAlbumOverride()) {
self::createPodcastSmartblockAndPlaylist($podcast);
}
return $podcast->toArray(BasePeer::TYPE_FIELDNAME);
2021-10-11 16:10:47 +02:00
} catch (Exception $e) {
2015-10-13 16:14:23 +02:00
$podcast->delete();
2021-10-11 16:10:47 +02:00
2015-10-13 16:14:23 +02:00
throw $e;
}
}
/**
* @param $title passed in directly from web UI input
fix(deps): update dependency friendsofphp/php-cs-fixer to <3.43.2 (main) (#2848) [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [friendsofphp/php-cs-fixer](https://togithub.com/PHP-CS-Fixer/PHP-CS-Fixer) | `<3.42.1` -> `<3.43.2` | [![age](https://developer.mend.io/api/mc/badges/age/packagist/friendsofphp%2fphp-cs-fixer/3.43.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/packagist/friendsofphp%2fphp-cs-fixer/3.43.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/packagist/friendsofphp%2fphp-cs-fixer/3.42.0/3.43.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/packagist/friendsofphp%2fphp-cs-fixer/3.42.0/3.43.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes <details> <summary>PHP-CS-Fixer/PHP-CS-Fixer (friendsofphp/php-cs-fixer)</summary> ### [`v3.43.1`](https://togithub.com/PHP-CS-Fixer/PHP-CS-Fixer/blob/HEAD/CHANGELOG.md#Changelog-for-v3431) [Compare Source](https://togithub.com/PHP-CS-Fixer/PHP-CS-Fixer/compare/v3.43.0...v3.43.1) - fix: Import only unique symbols' short names ([#&#8203;7635](https://togithub.com/PHP-CS-Fixer/PHP-CS-Fixer/issues/7635)) ### [`v3.43.0`](https://togithub.com/PHP-CS-Fixer/PHP-CS-Fixer/blob/HEAD/CHANGELOG.md#Changelog-for-v3430) [Compare Source](https://togithub.com/PHP-CS-Fixer/PHP-CS-Fixer/compare/v3.42.0...v3.43.0) - chore: change base of `@Symfony` set to `@PER-CS2.0` ([#&#8203;7627](https://togithub.com/PHP-CS-Fixer/PHP-CS-Fixer/issues/7627)) - chore: PHPUnit - allow for v10 ([#&#8203;7606](https://togithub.com/PHP-CS-Fixer/PHP-CS-Fixer/issues/7606)) - chore: Preg - rework catching the error ([#&#8203;7616](https://togithub.com/PHP-CS-Fixer/PHP-CS-Fixer/issues/7616)) - chore: Revert unneeded peer-dep-pin and re-gen lock file ([#&#8203;7618](https://togithub.com/PHP-CS-Fixer/PHP-CS-Fixer/issues/7618)) - docs: drop extra note about 8.0.0 bug in README.md ([#&#8203;7614](https://togithub.com/PHP-CS-Fixer/PHP-CS-Fixer/issues/7614)) - feat: add cast_spaces into `@PER-CS2.0` ([#&#8203;7625](https://togithub.com/PHP-CS-Fixer/PHP-CS-Fixer/issues/7625)) - feat: Configurable phpDoc tags for FQCN processing ([#&#8203;7628](https://togithub.com/PHP-CS-Fixer/PHP-CS-Fixer/issues/7628)) - feat: StatementIndentationFixer - introduce stick_comment_to_next_continuous_control_statement config ([#&#8203;7624](https://togithub.com/PHP-CS-Fixer/PHP-CS-Fixer/issues/7624)) - feat: UnaryOperatorSpacesFixer - introduce only_dec_inc config ([#&#8203;7626](https://togithub.com/PHP-CS-Fixer/PHP-CS-Fixer/issues/7626)) - fix: FullyQualifiedStrictTypesFixer - better support annotations in inline {} ([#&#8203;7633](https://togithub.com/PHP-CS-Fixer/PHP-CS-Fixer/issues/7633)) - fix: Improve how FQCN is handled in phpDoc ([#&#8203;7622](https://togithub.com/PHP-CS-Fixer/PHP-CS-Fixer/issues/7622)) - fix: phpdoc_align - fix multiline tag alignment issue ([#&#8203;7630](https://togithub.com/PHP-CS-Fixer/PHP-CS-Fixer/issues/7630)) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/libretime/libretime). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4xMDMuMSIsInVwZGF0ZWRJblZlciI6IjM3LjEwMy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiJ9--> --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: jo <ljonas@riseup.net>
2023-12-29 15:28:57 +01:00
* This will automatically create a smartblock and playlist for this podcast
* @param mixed $podcast
*/
public static function createPodcastSmartblockAndPlaylist($podcast, $title = null)
{
if (is_array($podcast)) {
$newpodcast = new Podcast();
$newpodcast->fromArray($podcast, BasePeer::TYPE_FIELDNAME);
$podcast = $newpodcast;
}
if ($title == null) {
$title = $podcast->getDbTitle();
}
// Base class
$newBl = new Application_Model_Block();
$newBl->setCreator(Application_Model_User::getCurrentUser()->getId());
$newBl->setName($title);
2021-10-11 16:10:47 +02:00
$newBl->setDescription(_('Auto-generated smartblock for podcast'));
$newBl->saveType('dynamic');
// limit the smartblock to 1 item
$row = new CcBlockcriteria();
$row->setDbCriteria('limit');
$row->setDbModifier('items');
$row->setDbValue(1);
$row->setDbBlockId($newBl->getId());
$row->save();
// sort so that it is the newest item
$row = new CcBlockcriteria();
$row->setDbCriteria('sort');
$row->setDbModifier('N/A');
$row->setDbValue('newest');
$row->setDbBlockId($newBl->getId());
$row->save();
// match the track by ensuring the album title matches the podcast
$row = new CcBlockcriteria();
$row->setDbCriteria('album_title');
$row->setDbModifier('is');
$row->setDbValue($title);
$row->setDbBlockId($newBl->getId());
$row->save();
$newPl = new Application_Model_Playlist();
$newPl->setName($title);
$newPl->setCreator(Application_Model_User::getCurrentUser()->getId());
$row = new CcPlaylistcontents();
$row->setDbBlockId($newBl->getId());
$row->setDbPlaylistId($newPl->getId());
$row->setDbType(2);
$row->save();
}
2015-10-19 17:54:53 +02:00
public static function createStationPodcast()
{
$podcast = new Podcast();
$podcast->setDbUrl(Config::getPublicUrl() . 'feeds/station-rss');
2015-10-19 17:54:53 +02:00
$title = Application_Model_Preference::GetStationName();
$title = empty($title) ? "My Station's Podcast" : $title;
$podcast->setDbTitle($title);
$podcast->setDbDescription(Application_Model_Preference::GetStationDescription());
$podcast->setDbLink(Config::getPublicUrl());
$podcast->setDbLanguage(explode('_', Application_Model_Preference::GetLocale())[0]);
2015-10-19 17:54:53 +02:00
$podcast->setDbCreator(Application_Model_Preference::GetStationName());
$podcast->setDbOwner(self::getOwnerId());
$podcast->save();
$stationPodcast = new StationPodcast();
$stationPodcast->setPodcast($podcast);
$stationPodcast->save();
Application_Model_Preference::setStationPodcastId($podcast->getDbId());
// Set the download key when we create the station podcast
// The value is randomly generated in the setter
Application_Model_Preference::setStationPodcastDownloadKey();
2021-10-11 16:10:47 +02:00
return $podcast->getDbId();
2015-10-19 17:54:53 +02:00
}
// TODO move this somewhere where it makes sense
2015-10-13 16:14:23 +02:00
private static function getOwnerId()
{
try {
if (Zend_Auth::getInstance()->hasIdentity()) {
$service_user = new Application_Service_UserService();
2021-10-11 16:10:47 +02:00
2015-10-13 16:14:23 +02:00
return $service_user->getCurrentUser()->getDbId();
}
2021-10-11 16:10:47 +02:00
$defaultOwner = CcSubjsQuery::create()
->filterByDbType('A')
->orderByDbId()
->findOne();
2021-10-11 16:10:47 +02:00
if (!$defaultOwner) {
// what to do if there is no admin user?
// should we handle this case?
return null;
}
return $defaultOwner->getDbId();
} catch (Exception $e) {
2015-10-13 16:14:23 +02:00
Logging::info($e->getMessage());
}
}
/**
2021-10-11 16:10:47 +02:00
* Trims the podcast metadata to fit the table's column max size.
2015-10-13 16:14:23 +02:00
*
2022-01-14 18:52:42 +01:00
* @param PodcastArray &$podcastArray
2015-10-13 16:14:23 +02:00
*/
private static function validatePodcastMetadata(&$podcastArray)
{
$podcastTable = PodcastPeer::getTableMap();
foreach ($podcastArray as $key => &$value) {
try {
// Make sure column exists in table
$columnMaxSize = $podcastTable->getColumn($key)->getSize();
} catch (PropelException $e) {
continue;
}
if (strlen($value) > $columnMaxSize) {
$value = substr($value, 0, $podcastTable->getColumn($key)->getSize());
}
}
}
/**
* Fetches a Podcast's rss feed and returns all its episodes with
2021-10-11 16:10:47 +02:00
* the Podcast object.
*
* @param mixed $podcastId
*
2022-09-12 13:16:14 +02:00
* @return array - Podcast Array with a full list of episodes
*
* @throws PodcastNotFoundException
* @throws InvalidPodcastException
*/
public static function getPodcastById($podcastId)
{
$podcast = PodcastQuery::create()->findPk($podcastId);
if (!$podcast) {
throw new PodcastNotFoundException();
}
2015-11-03 23:13:38 +01:00
$podcast = $podcast->toArray(BasePeer::TYPE_FIELDNAME);
2021-10-11 16:10:47 +02:00
$podcast['itunes_explicit'] = ($podcast['itunes_explicit'] == 'yes') ? true : false;
2015-11-03 23:13:38 +01:00
return $podcast;
}
/**
2021-10-11 16:10:47 +02:00
* Deletes a Podcast and its podcast episodes.
*
* @param mixed $podcastId
2021-10-11 16:10:47 +02:00
*
* @throws Exception
* @throws PodcastNotFoundException
*/
public static function deletePodcastById($podcastId)
{
$podcast = PodcastQuery::create()->findPk($podcastId);
if ($podcast) {
$podcast->delete();
2015-10-19 17:54:53 +02:00
// FIXME: I don't think we should be able to delete the station podcast...
2015-10-19 17:54:53 +02:00
if ($podcastId == Application_Model_Preference::getStationPodcastId()) {
Application_Model_Preference::setStationPodcastId(null);
}
} else {
throw new PodcastNotFoundException();
}
}
/**
2021-10-11 16:10:47 +02:00
* Build a response with podcast data and embedded HTML to load on the frontend.
*
2021-10-11 16:10:47 +02:00
* @param int $podcastId ID of the podcast to build a response for
* @param Zend_View_Interface $view Zend view object to render the response HTML
*
2021-10-11 16:10:47 +02:00
* @return array the response array containing the podcast data and editor HTML
2022-09-12 13:16:14 +02:00
*
* @throws PodcastNotFoundException
*/
2021-10-11 16:10:47 +02:00
public static function buildPodcastEditorResponse($podcastId, $view)
{
// Check the StationPodcast table rather than checking
// the station podcast ID key in preferences for extensibility
$podcast = StationPodcastQuery::create()->findOneByDbPodcastId($podcastId);
$path = $podcast ? 'podcast/station.phtml' : 'podcast/podcast.phtml';
$podcast = Application_Service_PodcastService::getPodcastById($podcastId);
2021-10-11 16:10:47 +02:00
return [
'podcast' => json_encode($podcast),
'html' => $view->render($path),
];
}
/**
2021-10-11 16:10:47 +02:00
* Updates a Podcast object with the given metadata.
*
* @param mixed $podcastId
* @param mixed $data
2021-10-11 16:10:47 +02:00
*
2022-09-12 13:16:14 +02:00
* @return array
*
* @throws Exception
* @throws PodcastNotFoundException
*/
public static function updatePodcastFromArray($podcastId, $data)
{
$podcast = PodcastQuery::create()->findPk($podcastId);
if (!$podcast) {
throw new PodcastNotFoundException();
}
2021-10-11 16:10:47 +02:00
self::removePrivateFields($data['podcast']);
self::validatePodcastMetadata($data['podcast']);
if (array_key_exists('auto_ingest', $data['podcast'])) {
2015-10-22 01:21:52 +02:00
self::_updateAutoIngestTimestamp($podcast, $data);
}
2021-10-11 16:10:47 +02:00
$data['podcast']['itunes_explicit'] = $data['podcast']['itunes_explicit'] ? 'yes' : 'clean';
$podcast->fromArray($data['podcast'], BasePeer::TYPE_FIELDNAME);
2015-10-15 14:06:01 +02:00
$podcast->save();
return $podcast->toArray(BasePeer::TYPE_FIELDNAME);
}
/**
2021-10-11 16:10:47 +02:00
* Update the automatic ingestion timestamp for the given Podcast.
*
2021-10-11 16:10:47 +02:00
* @param Podcast $podcast Podcast object to update
* @param array $data Podcast update data array
*/
2021-10-11 16:10:47 +02:00
private static function _updateAutoIngestTimestamp($podcast, $data)
{
// Get podcast data with lazy loaded columns since we can't directly call getDbAutoIngest()
$currData = $podcast->toArray(BasePeer::TYPE_FIELDNAME, true);
// Add an auto-ingest timestamp when turning auto-ingest on
2021-10-11 16:10:47 +02:00
if ($data['podcast']['auto_ingest'] == 1 && $currData['auto_ingest'] != 1) {
$data['podcast']['auto_ingest_timestamp'] = gmdate('r');
}
}
private static function removePrivateFields(&$data)
{
foreach (self::$privateFields as $key) {
unset($data[$key]);
}
}
2015-10-19 17:54:53 +02:00
2021-10-11 16:10:47 +02:00
private static function addEscapedChild($node, $name, $value = null, $namespace = null)
{
if (empty($value)) {
return null;
}
$child = $node->addChild($name, null, $namespace);
$child[0] = $value;
2021-10-11 16:10:47 +02:00
return $child;
}
2015-10-19 17:54:53 +02:00
public static function createStationRssFeed()
{
$stationPodcastId = Application_Model_Preference::getStationPodcastId();
2021-10-11 16:10:47 +02:00
2015-10-19 17:54:53 +02:00
try {
$podcast = PodcastQuery::create()->findPk($stationPodcastId);
if (!$podcast) {
throw new PodcastNotFoundException();
}
$xml = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"/>');
2021-10-11 16:10:47 +02:00
$channel = $xml->addChild('channel');
self::addEscapedChild($channel, 'title', $podcast->getDbTitle());
self::addEscapedChild($channel, 'link', $podcast->getDbLink());
self::addEscapedChild($channel, 'description', $podcast->getDbDescription());
self::addEscapedChild($channel, 'language', $podcast->getDbLanguage());
self::addEscapedChild($channel, 'copyright', $podcast->getDbCopyright());
2015-10-19 17:54:53 +02:00
2021-10-11 16:10:47 +02:00
$xml->addAttribute('xmlns:xmlns:atom', 'http://www.w3.org/2005/Atom');
2015-11-18 01:33:36 +01:00
2021-10-11 16:10:47 +02:00
$atomLink = $channel->addChild('xmlns:atom:link');
$atomLink->addAttribute('href', Config::getPublicUrl() . 'feeds/station-rss');
2021-10-11 16:10:47 +02:00
$atomLink->addAttribute('rel', 'self');
$atomLink->addAttribute('type', 'application/rss+xml');
2015-11-18 01:33:36 +01:00
$imageUrl = Config::getPublicUrl() . 'api/station-logo';
2021-10-11 16:10:47 +02:00
$image = $channel->addChild('image');
$image->addChild('title', htmlspecialchars($podcast->getDbTitle()));
self::addEscapedChild($image, 'url', $imageUrl);
self::addEscapedChild($image, 'link', Config::getPublicUrl());
2015-10-19 17:54:53 +02:00
$xml->addAttribute('xmlns:xmlns:itunes', ITUNES_XML_NAMESPACE_URL);
2021-10-11 16:10:47 +02:00
self::addEscapedChild($channel, 'xmlns:itunes:author', $podcast->getDbItunesAuthor());
self::addEscapedChild($channel, 'xmlns:itunes:keywords', $podcast->getDbItunesKeywords());
self::addEscapedChild($channel, 'xmlns:itunes:summary', $podcast->getDbItunesSummary());
self::addEscapedChild($channel, 'xmlns:itunes:subtitle', $podcast->getDbItunesSubtitle());
self::addEscapedChild($channel, 'xmlns:itunes:explicit', $podcast->getDbItunesExplicit());
$owner = $channel->addChild('xmlns:itunes:owner');
self::addEscapedChild($owner, 'xmlns:itunes:name', Application_Model_Preference::GetStationName());
self::addEscapedChild($owner, 'xmlns:itunes:email', Application_Model_Preference::GetEmail());
$itunesImage = $channel->addChild('xmlns:itunes:image');
$itunesImage->addAttribute('href', $imageUrl);
2015-10-19 17:54:53 +02:00
// Need to split categories into separate tags
2021-10-11 16:10:47 +02:00
$itunesCategories = explode(',', $podcast->getDbItunesCategory());
2015-10-19 17:54:53 +02:00
foreach ($itunesCategories as $c) {
if (!empty($c)) {
2021-10-11 16:10:47 +02:00
$category = $channel->addChild('xmlns:itunes:category');
$category->addAttribute('text', $c);
}
2015-10-19 17:54:53 +02:00
}
$episodes = PodcastEpisodesQuery::create()->filterByDbPodcastId($stationPodcastId)->find();
foreach ($episodes as $episode) {
2021-10-11 16:10:47 +02:00
$item = $channel->addChild('item');
2015-10-19 17:54:53 +02:00
$publishedFile = CcFilesQuery::create()->findPk($episode->getDbFileId());
// title
2021-10-11 16:10:47 +02:00
self::addEscapedChild($item, 'title', $publishedFile->getDbTrackTitle());
2015-10-19 17:54:53 +02:00
// link - do we need this?
2015-10-19 17:54:53 +02:00
// pubDate
2021-10-11 16:10:47 +02:00
self::addEscapedChild($item, 'pubDate', gmdate(DATE_RFC2822, strtotime($episode->getDbPublicationDate())));
2015-10-19 17:54:53 +02:00
// category
2021-10-11 16:10:47 +02:00
foreach ($itunesCategories as $c) {
if (!empty($c)) {
2021-10-11 16:10:47 +02:00
self::addEscapedChild($item, 'category', $c);
}
2015-10-19 17:54:53 +02:00
}
// guid
2021-10-11 16:10:47 +02:00
$guid = self::addEscapedChild($item, 'guid', $episode->getDbEpisodeGuid());
$guid->addAttribute('isPermaLink', 'false');
2015-10-19 17:54:53 +02:00
// description
2021-10-11 16:10:47 +02:00
self::addEscapedChild($item, 'description', $publishedFile->getDbDescription());
2015-10-19 17:54:53 +02:00
// encolsure - url, length, type attribs
2021-10-11 16:10:47 +02:00
$enclosure = $item->addChild('enclosure');
$enclosure->addAttribute('url', $episode->getDbDownloadUrl());
$enclosure->addAttribute('length', $publishedFile->getDbFilesize());
$enclosure->addAttribute('type', $publishedFile->getDbMime());
2015-10-19 17:54:53 +02:00
// itunes:subtitle
2022-08-25 16:25:54 +02:00
// From https://www.apple.com/ca/itunes/podcasts/specs.html#subtitle :
// 'The contents of the <itunes:subtitle> tag are displayed in the Description column in iTunes.'
// self::addEscapedChild($item, "xmlns:itunes:subtitle", $publishedFile->getDbTrackTitle());
2021-10-11 16:10:47 +02:00
self::addEscapedChild($item, 'xmlns:itunes:subtitle', $publishedFile->getDbDescription());
2015-10-19 17:54:53 +02:00
// itunes:summary
2021-10-11 16:10:47 +02:00
self::addEscapedChild($item, 'xmlns:itunes:summary', $publishedFile->getDbDescription());
2015-10-19 17:54:53 +02:00
// itunes:author
2021-10-11 16:10:47 +02:00
self::addEscapedChild($item, 'xmlns:itunes:author', $publishedFile->getDbArtistName());
2015-10-19 17:54:53 +02:00
// itunes:explicit - skip this?
2015-10-19 17:54:53 +02:00
// itunes:duration
2021-10-11 16:10:47 +02:00
self::addEscapedChild($item, 'xmlns:itunes:duration', explode('.', $publishedFile->getDbLength())[0]);
2015-10-19 17:54:53 +02:00
}
// Format it nicely with newlines...
2015-11-17 22:18:38 +01:00
$dom = new DOMDocument();
$dom->loadXML($xml->asXML());
$dom->formatOutput = true;
2015-10-19 17:54:53 +02:00
2021-10-11 16:10:47 +02:00
return $dom->saveXML();
2015-10-19 17:54:53 +02:00
} catch (FeedException $e) {
return false;
}
}
}