mirror of
https://github.com/Yetangitu/ampache
synced 2025-10-03 09:49:30 +02:00
481 lines
16 KiB
PHP
481 lines
16 KiB
PHP
<?php
|
|
/* vim:set softtabstop=4 shiftwidth=4 expandtab: */
|
|
/**
|
|
*
|
|
* LICENSE: GNU Affero General Public License, version 3 (AGPLv3)
|
|
* Copyright 2001 - 2016 Ampache.org
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
class Podcast extends database_object implements library_item
|
|
{
|
|
/* Variables from DB */
|
|
public $id;
|
|
public $catalog;
|
|
public $feed;
|
|
public $title;
|
|
public $website;
|
|
public $description;
|
|
public $language;
|
|
public $copyright;
|
|
public $generator;
|
|
public $lastbuilddate;
|
|
public $lastsync;
|
|
|
|
public $episodes;
|
|
public $f_title;
|
|
public $f_description;
|
|
public $f_language;
|
|
public $f_copyright;
|
|
public $f_generator;
|
|
public $f_lastbuilddate;
|
|
public $f_lastsync;
|
|
public $link;
|
|
public $f_link;
|
|
|
|
/**
|
|
* Podcast
|
|
* Takes the ID of the podcast and pulls the info from the db
|
|
*/
|
|
public function __construct($id='')
|
|
{
|
|
/* If they failed to pass in an id, just run for it */
|
|
if (!$id) {
|
|
return false;
|
|
}
|
|
|
|
/* Get the information from the db */
|
|
$info = $this->get_info($id);
|
|
|
|
foreach ($info as $key=>$value) {
|
|
$this->$key = $value;
|
|
} // foreach info
|
|
|
|
return true;
|
|
} //constructor
|
|
|
|
/**
|
|
* gc
|
|
*
|
|
* This cleans out unused podcasts
|
|
*/
|
|
public static function gc()
|
|
{
|
|
}
|
|
|
|
/**
|
|
* get_catalogs
|
|
*
|
|
* Get all catalog ids related to this item.
|
|
* @return int[]
|
|
*/
|
|
public function get_catalogs()
|
|
{
|
|
return array($this->catalog);
|
|
}
|
|
|
|
/**
|
|
* get_episodes
|
|
* gets all episodes for this podcast
|
|
*/
|
|
public function get_episodes($state_filter = '')
|
|
{
|
|
$params = array();
|
|
$sql = "SELECT `podcast_episode`.`id` FROM `podcast_episode` ";
|
|
if (AmpConfig::get('catalog_disable')) {
|
|
$sql .= "LEFT JOIN `podcast` ON `podcast`.`id` = `podcast_episode`.`podcast` ";
|
|
$sql .= "LEFT JOIN `catalog` ON `catalog`.`id` = `podcast`.`catalog` ";
|
|
}
|
|
$sql .= "WHERE `podcast_episode`.`podcast`='" . Dba::escape($this->id) . "' ";
|
|
if (!empty($state_filter)) {
|
|
$sql .= "AND `podcast_episode`.`state` = ? ";
|
|
$params[] = $state_filter;
|
|
}
|
|
if (AmpConfig::get('catalog_disable')) {
|
|
$sql .= "AND `catalog`.`enabled` = '1' ";
|
|
}
|
|
$sql .= "ORDER BY `podcast_episode`.`pubdate` DESC";
|
|
$db_results = Dba::read($sql, $params);
|
|
|
|
$results = array();
|
|
while ($r = Dba::fetch_assoc($db_results)) {
|
|
$results[] = $r['id'];
|
|
}
|
|
|
|
return $results;
|
|
} // get_episodes
|
|
|
|
/**
|
|
* _get_extra info
|
|
* This returns the extra information for the podcast, this means totals etc
|
|
*/
|
|
private function _get_extra_info()
|
|
{
|
|
// Try to find it in the cache and save ourselves the trouble
|
|
if (parent::is_cached('podcast_extra', $this->id)) {
|
|
$row = parent::get_from_cache('podcast_extra', $this->id);
|
|
} else {
|
|
$sql = "SELECT COUNT(`podcast_episode`.`id`) AS `episode_count` FROM `podcast_episode` " .
|
|
"WHERE `podcast_episode`.`podcast` = ?";
|
|
$db_results = Dba::read($sql, array($this->id));
|
|
$row = Dba::fetch_assoc($db_results);
|
|
|
|
parent::add_to_cache('podcast_extra', $this->id, $row);
|
|
}
|
|
|
|
/* Set Object Vars */
|
|
$this->episodes = $row['episode_count'];
|
|
|
|
return $row;
|
|
} // _get_extra_info
|
|
|
|
/**
|
|
* format
|
|
* this function takes the object and reformats some values
|
|
*/
|
|
public function format($details = true)
|
|
{
|
|
$this->f_title = scrub_out($this->title);
|
|
$this->f_description = scrub_out($this->description);
|
|
$this->f_language = scrub_out($this->language);
|
|
$this->f_copyright = scrub_out($this->copyright);
|
|
$this->f_generator = scrub_out($this->generator);
|
|
$this->f_website = scrub_out($this->website);
|
|
$this->f_lastbuilddate = date("m\/d\/Y - H:i", $this->lastbuilddate);
|
|
$this->f_lastsync = date("m\/d\/Y - H:i", $this->lastsync);
|
|
$this->link = AmpConfig::get('web_path') . '/podcast.php?action=show&podcast=' . $this->id;
|
|
$this->f_link = '<a href="' . $this->link . '" title="' . $this->f_title . '">' . $this->f_title . '</a>';
|
|
|
|
if ($details) {
|
|
$this->_get_extra_info();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function get_keywords()
|
|
{
|
|
$keywords = array();
|
|
$keywords['podcast'] = array('important' => true,
|
|
'label' => T_('Podcast'),
|
|
'value' => $this->f_title);
|
|
|
|
return $keywords;
|
|
}
|
|
|
|
public function get_fullname()
|
|
{
|
|
return $this->f_title;
|
|
}
|
|
|
|
public function get_parent()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public function get_childrens()
|
|
{
|
|
return array('podcast_episode' => $this->get_episodes());
|
|
}
|
|
|
|
public function search_childrens($name)
|
|
{
|
|
return array();
|
|
}
|
|
|
|
public function get_medias($filter_type = null)
|
|
{
|
|
$medias = array();
|
|
if (!$filter_type || $filter_type == 'podcast_episode') {
|
|
$episodes = $this->get_episodes('completed');
|
|
foreach ($episodes as $episode_id) {
|
|
$medias[] = array(
|
|
'object_type' => 'podcast_episode',
|
|
'object_id' => $episode_id
|
|
);
|
|
}
|
|
}
|
|
return $medias;
|
|
}
|
|
|
|
public function get_user_owner()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public function get_default_art_kind()
|
|
{
|
|
return 'default';
|
|
}
|
|
|
|
public function get_description()
|
|
{
|
|
return $this->f_description;
|
|
}
|
|
|
|
public function display_art($thumb = 2, $force = false)
|
|
{
|
|
if (Art::has_db($this->id, 'podcast') || $force) {
|
|
Art::display('podcast', $this->id, $this->get_fullname(), $thumb, $this->link);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* update
|
|
* This takes a key'd array of data and updates the current podcast
|
|
*/
|
|
public function update(array $data)
|
|
{
|
|
$feed = isset($data['feed']) ? $data['feed'] : $this->feed;
|
|
$title = isset($data['title']) ? scrub_in($data['title']) : $this->title;
|
|
$website = isset($data['website']) ? scrub_in($data['website']) : $this->website;
|
|
$description = isset($data['description']) ? scrub_in($data['description']) : $this->description;
|
|
$generator = isset($data['generator']) ? scrub_in($data['generator']) : $this->generator;
|
|
$copyright = isset($data['copyright']) ? scrub_in($data['copyright']) : $this->copyright;
|
|
|
|
if (strpos($feed, "http://") !== 0 && strpos($feed, "https://") !== 0) {
|
|
debug_event('podcast', 'Podcast update canceled, bad feed url.', 1);
|
|
return $this->id;
|
|
}
|
|
|
|
$sql = 'UPDATE `podcast` SET `feed` = ?, `title` = ?, `website` = ?, `description` = ?, `generator` = ?, `copyright` = ? WHERE `id` = ?';
|
|
Dba::write($sql, array($feed, $title, $website, $description, $generator, $copyright, $this->id));
|
|
|
|
$this->feed = $feed;
|
|
$this->title = $title;
|
|
$this->website = $website;
|
|
$this->description = $description;
|
|
$this->generator = $generator;
|
|
$this->copyright = $copyright;
|
|
|
|
return $this->id;
|
|
}
|
|
|
|
public static function create(array $data)
|
|
{
|
|
$feed = $data['feed'];
|
|
// Feed must be http/https
|
|
if (strpos($feed, "http://") !== 0 && strpos($feed, "https://") !== 0) {
|
|
AmpError::add('feed', T_('Wrong feed url'));
|
|
}
|
|
|
|
$catalog_id = intval($data['catalog']);
|
|
if ($catalog_id <= 0) {
|
|
AmpError::add('catalog', T_('Target catalog required'));
|
|
} else {
|
|
$catalog = Catalog::create_from_id($catalog_id);
|
|
if ($catalog->gather_types !== "podcast") {
|
|
AmpError::add('catalog', T_('Wrong target catalog type'));
|
|
}
|
|
}
|
|
|
|
if (AmpError::occurred()) {
|
|
return false;
|
|
}
|
|
|
|
$title = T_('Unknown');
|
|
$website = null;
|
|
$description = null;
|
|
$language = null;
|
|
$copyright = null;
|
|
$generator = null;
|
|
$lastbuilddate = 0;
|
|
$episodes = array();
|
|
$arturl = '';
|
|
|
|
$xmlstr = file_get_contents($feed);
|
|
if ($xmlstr === false) {
|
|
AmpError::add('feed', T_('Cannot access the feed.'));
|
|
} else {
|
|
$xml = simplexml_load_string($xmlstr);
|
|
if ($xml === false) {
|
|
AmpError::add('feed', T_('Cannot read the feed.'));
|
|
} else {
|
|
$title = html_entity_decode($xml->channel->title);
|
|
$website = $xml->channel->link;
|
|
$description = html_entity_decode($xml->channel->description);
|
|
$language = $xml->channel->language;
|
|
$copyright = html_entity_decode($xml->channel->copyright);
|
|
$generator = html_entity_decode($xml->channel->generator);
|
|
$lastbuilddatestr = $xml->channel->lastBuildDate;
|
|
if ($lastbuilddatestr) {
|
|
$lastbuilddate = strtotime($lastbuilddatestr);
|
|
}
|
|
|
|
if ($xml->channel->image) {
|
|
$arturl = $xml->channel->image->url;
|
|
}
|
|
|
|
$episodes = $xml->channel->item;
|
|
}
|
|
}
|
|
|
|
if (AmpError::occurred()) {
|
|
return false;
|
|
}
|
|
|
|
$sql = "INSERT INTO `podcast` (`feed`, `catalog`, `title`, `website`, `description`, `language`, `copyright`, `generator`, `lastbuilddate`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
|
$db_results = Dba::write($sql, array($feed, $catalog_id, $title, $website, $description, $language, $copyright, $generator, $lastbuilddate));
|
|
if ($db_results) {
|
|
$id = Dba::insert_id();
|
|
$podcast = new Podcast($id);
|
|
$dirpath = $podcast->get_root_path();
|
|
if (!is_dir($dirpath)) {
|
|
if (mkdir($dirpath) === false) {
|
|
debug_event('podcast', 'Cannot create directory ' . $dirpath, 1);
|
|
}
|
|
}
|
|
if (!empty($arturl)) {
|
|
$art = new Art($id, 'podcast');
|
|
$art->insert_url($arturl);
|
|
}
|
|
if (count($episodes) > 0) {
|
|
$podcast->add_episodes($episodes);
|
|
}
|
|
}
|
|
|
|
return $db_results;
|
|
}
|
|
|
|
public function add_episodes($episodes, $afterdate=0, $gather=false)
|
|
{
|
|
foreach ($episodes as $episode) {
|
|
$this->add_episode($episode, $afterdate);
|
|
}
|
|
|
|
// Select episodes to download
|
|
$dlnb = AmpConfig::get('podcast_new_download');
|
|
if ($dlnb != 0) {
|
|
$sql = "SELECT `podcast_episode`.`id` FROM `podcast_episode` INNER JOIN `podcast` ON `podcast`.`id` = `podcast_episode`.`podcast` " .
|
|
"WHERE `podcast`.`id` = ? AND `podcast_episode`.`addition_time` > `podcast`.`lastsync` " .
|
|
"ORDER BY `podcast_episode`.`pubdate` DESC";
|
|
if ($dlnb != -1) {
|
|
$sql .= " LIMIT " . $dlnb;
|
|
}
|
|
$db_results = Dba::read($sql, array($this->id));
|
|
while ($row = Dba::fetch_row($db_results)) {
|
|
$episode = new Podcast_Episode($row[0]);
|
|
$episode->change_state('pending');
|
|
if ($gather) {
|
|
$episode->gather();
|
|
}
|
|
}
|
|
}
|
|
// Remove items outside limit
|
|
$keepnb = AmpConfig::get('podcast_keep');
|
|
if ($keepnb > 0) {
|
|
$sql = "SELECT `podcast_episode`.`id` FROM `podcast_episode` WHERE `podcast_episode`.`podcast` = ? " .
|
|
"ORDER BY `podcast_episode`.`pubdate` DESC LIMIT " . $keepnb . ",18446744073709551615";
|
|
$db_results = Dba::read($sql, array($this->id));
|
|
while ($row = Dba::fetch_row($db_results)) {
|
|
$episode = new Podcast_Episode($row[0]);
|
|
$episode->remove();
|
|
}
|
|
}
|
|
$this->update_lastsync(time());
|
|
}
|
|
|
|
private function add_episode(SimpleXMLElement $episode, $afterdate=0)
|
|
{
|
|
debug_event('podcast', 'Adding new episode to podcast ' . $this->id . '...', 5);
|
|
|
|
$title = html_entity_decode($episode->title);
|
|
$website = $episode->link;
|
|
$guid = $episode->guid;
|
|
$description = html_entity_decode($episode->description);
|
|
$author = html_entity_decode($episode->author);
|
|
$category = html_entity_decode($episode->category);
|
|
$source = null;
|
|
$time = 0;
|
|
if ($episode->enclosure) {
|
|
$source = $episode->enclosure['url'];
|
|
}
|
|
$itunes = $episode->children('itunes', true);
|
|
if ($itunes) {
|
|
$ptime = date_parse($itunes->duration);
|
|
$time = $ptime['hour'] * 3600 + $ptime['minute'] * 60 + $ptime['second'];
|
|
}
|
|
$pubdate = 0;
|
|
$pubdatestr = $episode->pubDate;
|
|
if ($pubdatestr) {
|
|
$pubdate = strtotime($pubdatestr);
|
|
}
|
|
|
|
if ($pubdate <= 0) {
|
|
debug_event('podcast', 'Invalid episode publication date, skipped', 3);
|
|
return false;
|
|
}
|
|
|
|
if ($pubdate > $afterdate) {
|
|
$sql = "INSERT INTO `podcast_episode` (`title`, `guid`, `podcast`, `state`, `source`, `website`, `description`, `author`, `category`, `time`, `pubdate`, `addition_time`) " .
|
|
"VALUES (?, ?, ?, 'skipped', ?, ?, ?, ?, ?, ?, ?, ?)";
|
|
return Dba::write($sql, array($title, $guid, $this->id, $source, $website, $description, $author, $category, $time, $pubdate, time()));
|
|
} else {
|
|
debug_event('podcast', 'Episode published before ' . $afterdate . ' (' . $pubdate . '), skipped', 5);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private function update_lastsync($time)
|
|
{
|
|
$sql = "UPDATE `podcast` SET `lastsync` = ? WHERE `id` = ?";
|
|
return Dba::write($sql, array($time, $this->id));
|
|
}
|
|
|
|
public function sync_episodes($gather=false)
|
|
{
|
|
debug_event('podcast', 'Syncing feed ' . $this->feed . ' ...', 5);
|
|
|
|
$xmlstr = file_get_contents($this->feed);
|
|
if ($xmlstr === false) {
|
|
debug_event('podcast', 'Cannot access feed ' . $this->feed, 1);
|
|
return false;
|
|
}
|
|
$xml = simplexml_load_string($xmlstr);
|
|
if ($xml === false) {
|
|
debug_event('podcast', 'Cannot read feed ' . $this->feed, 1);
|
|
return false;
|
|
}
|
|
|
|
$this->add_episodes($xml->channel->item, $this->lastsync, $gather);
|
|
|
|
return true;
|
|
}
|
|
|
|
public function remove()
|
|
{
|
|
$episodes = $this->get_episodes();
|
|
foreach ($episodes as $episode_id) {
|
|
$episode = new Podcast_Episode($episode_id);
|
|
$episode->remove();
|
|
}
|
|
|
|
$sql = "DELETE FROM `podcast` WHERE `id` = ?";
|
|
return Dba::write($sql, array($this->id));
|
|
}
|
|
|
|
public function get_root_path()
|
|
{
|
|
$catalog = Catalog::create_from_id($this->catalog);
|
|
if (!$catalog->get_type() == 'local') {
|
|
debug_event('podcast', 'Bad catalog type.', 1);
|
|
return '';
|
|
}
|
|
|
|
$dirname = $this->title;
|
|
return $catalog->path . DIRECTORY_SEPARATOR . $dirname;
|
|
}
|
|
}
|