. * */ 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 = '' . $this->f_title . ''; 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; } }