def __init__(self, options): self._logger = logging.getLogger("sd4tvh") # type: logging.Logger self._encoding = u"utf-8" # type: str self._sd = SchedulesDirect(options.username, options.password) # type: SchedulesDirect self._status = None # type: Status self._output_path = options.output_path # type: str self._days = options.days # type: int self._hdhomerun_ip = options.hdhomerun # type: str self._enable_filter = options.filter # type: bool self._filter_path = options.filter_path # type: str self._channels_path = options.channels_path # type: str self._episode_title_in_description = False # type: bool self._content_rating_preference_order = \ [u"Motion Picture Association of America", u"USA Parental Rating", u"Canadian Parental Rating"]
def __init__(self, options): self._sd = SchedulesDirect(options.username, options.password) self._status = None self._logger = logging.getLogger(__name__) self._xmltv_document = XmltvDocument() self._output_path = options.output_path self._days = options.days self._callsign_whitelist = None self._callsign_blacklist = None self._content_rating_preference_order = [u'Motion Picture Association of America', u'USA Parental Rating', u'Canadian Parental Rating'] self._include_credits = False self._include_see_also = False self._genres = set()
class sd4tvh: def __init__(self, options): self._logger = logging.getLogger("sd4tvh") # type: logging.Logger self._encoding = u"utf-8" # type: str self._sd = SchedulesDirect(options.username, options.password) # type: SchedulesDirect self._status = None # type: Status self._output_path = options.output_path # type: str self._days = options.days # type: int self._hdhomerun_ip = options.hdhomerun # type: str self._enable_filter = options.filter # type: bool self._filter_path = options.filter_path # type: str self._channels_path = options.channels_path # type: str self._episode_title_in_description = False # type: bool self._content_rating_preference_order = \ [u"Motion Picture Association of America", u"USA Parental Rating", u"Canadian Parental Rating"] # type: List[str] def login(self): self._logger.info(u"Getting SchedulesDirect token.") self._sd.get_token() self._logger.info(u"Getting SchedulesDirect status.") self._status = self._sd.get_status() if not self._sd.is_online(): raise Exception(u"System is not online.") expiry_delta = self._status.account.expires - datetime.utcnow() expiry_days = int(expiry_delta.total_seconds() // 86400) if expiry_days > 14: self._logger.info(u"Account will expire on %s (%s days).", self._status.account.expires, expiry_days) else: self._logger.warn(u"Account will expire on %s (%s days).", self._status.account.expires, expiry_days) time_delta = datetime.utcnow() - self._status.last_data_update hours, minutes = int(time_delta.total_seconds() // 3600), int((time_delta.total_seconds() % 3600) // 60) self._logger.info(u"SchedulesDirect last update at %s (%s hours %s minutes ago).", self._status.last_data_update, hours, minutes) if self._status.system_status.message is not None: self._logger.info(u"Message: %s", self._status.system_status.message) def manage(self): self._sd.manage() def channels(self): if len(self._status.lineups) == 0: self._logger.info(u"Not subscribed to any lineups, exiting.") return self._logger.info(u"Getting station/channel mappings for %s lineups.", len(self._status.lineups)) lineup_map_list = self._sd.get_lineup_map_list(self._status.lineups) # Setup channel filter(s) channel_filter = MetaChannelFilter() if self._hdhomerun_ip is not None: cf = HdhomerunChannelFilter(self._hdhomerun_ip) channel_filter.add_channel_filter(cf) if self._enable_filter: cf = FileChannelFilter(config_path=self._channels_path, lineup_map_list=lineup_map_list) channel_filter.add_channel_filter(cf) def process(self): if len(self._status.lineups) == 0: self._logger.info(u"Not subscribed to any lineups, exiting.") return self._logger.info(u"Getting station/channel mappings for %s lineups.", len(self._status.lineups)) lineup_map_list = self._sd.get_lineup_map_list(self._status.lineups) # Setup channel filter(s) channel_filter = MetaChannelFilter() if self._hdhomerun_ip is not None: cf = HdhomerunChannelFilter(self._hdhomerun_ip) channel_filter.add_channel_filter(cf) if self._enable_filter: cf = FileChannelFilter(config_path=self._filter_path, lineup_map_list=lineup_map_list) channel_filter.add_channel_filter(cf) station_ids = [station.station_id for station in lineup_map_list.unique_stations(channel_filter)] self._logger.info(u"Getting schedule hashes...") schedule_hash_list = self._sd.get_schedule_hash_list(station_ids) self._sd.refresh_cache(schedule_hash_list) with XmltvWriter(self._output_path) as f: f.write(u"<?xml version=\"1.0\" encoding=\"{0}\" ?>\n".format(self._encoding).encode(self._encoding)) f.write(u"<tv>\n".encode(self._encoding)) self._logger.info(u"Adding channels to xmltv document...") for channel in lineup_map_list.unique_channels(channel_filter): self._add_channel(f, channel) self._logger.info(u"Adding programs to xmltv document...") total_programs_added = 0 for channel in lineup_map_list.unique_channels(channel_filter): channel_programs_added = 0 schedule_list = self._sd.get_cached_schedules([(channel.station_id, None)]) program_lookup = self._sd.get_cached_programs(schedule_list.get_program_ids()) artwork_ids = {program.artwork_id for program in program_lookup.values() if program.has_image_artwork} program_artwork_lookup = self._sd.get_cached_artwork(list(artwork_ids)) for date in islice(schedule_list.schedule_dates(), self._days): programs_added = 0 schedule = schedule_list.get_schedule(channel.station_id, date) if schedule is None: continue # check for error statuses from SD - for example 7020 SCHEDULE RANGE EXCEEDED # TODO: better handling of error responses if schedule.response_status is not None and schedule.response_status.code != 0: self._logger.warn(u"Skipping day due to: %s", schedule.response_status.message) continue for broadcast in schedule.broadcasts: programs_added += 1 program = program_lookup[broadcast.program_id] program_artwork = program_artwork_lookup.get(program.artwork_id, None) self._add_programme(f, program, channel, broadcast, program_artwork) self._logger.debug(u"Added %s programs for channel %s on %s.", programs_added, next(channel.get_display_names()), date) channel_programs_added += programs_added self._logger.info(u"Added %s programs for channel %s.", channel_programs_added, next(channel.get_display_names())) total_programs_added += channel_programs_added self._logger.info(u"Added %s total programs.", total_programs_added) f.write(u"</tv>\n".encode(self._encoding)) self._logger.info(u"Finished.") def _get_program_categories(self, program): # type: (Program) -> Set[str] """ :param program: :return: """ categories = set() if program.is_sports_entity: categories.add(u"Sports") elif program.is_movie_entity: categories.add(u"Movie / Drama") elif program.is_show_entity or program.is_episode_entity: if u"Children" in program.genres: categories.add(u"Children's / Youth programs") if u"Educational" in program.genres: categories.add(u"Education / Science / Factual topics") if u"Science" in program.genres: categories.add(u"Education / Science / Factual topics") if u"Newsmagazine" in program.genres: categories.add(u"News magazine") if u"Documentary" in program.genres: categories.add(u"Documentary") if u"News" in program.genres: categories.add(u"News / Current affairs") if u"Music" in program.genres: categories.add(u"Music / Ballet / Dance") else: self._logger.warn(u"Unknown entity type: %s", program.entity_type) return categories def _add_programme(self, fp, program, channel, broadcast, program_artwork): """ :param fp: :param program: :type program: Program :param channel: :type channel: Channel :param broadcast: :type broadcast: Broadcast :param program_artwork: :type program_artwork: ProgramArtwork :return: """ p = XmltvProgramme(broadcast.air_date_time, broadcast.end_date_time, channel.get_unique_id()) p.add_title(program.titles.title120) if program.episode_title is not None: p.add_subtitle(program.episode_title) for category in self._get_program_categories(program): p.add_category(category) for genre in program.genres: p.add_category(genre) p.add_episode_num_dd_progid(program.program_id) if program.is_episode_entity: if program.metadata is not None and \ program.metadata.season_episode is not None and \ program.metadata.season_episode.has_season_episode: if broadcast.multipart is None: p.add_episode_num_xmltv_ns( season_num=program.metadata.season_episode.season, episode_num=program.metadata.season_episode.episode) else: p.add_episode_num_xmltv_ns( season_num=program.metadata.season_episode.season, episode_num=program.metadata.season_episode.episode, part_num=broadcast.multipart.part_number, total_parts=broadcast.multipart.total_parts) elif program.episode_num is not None: if broadcast.multipart is not None: p.add_episode_num_xmltv_ns( part_num=broadcast.multipart.part_number, total_parts=broadcast.multipart.total_parts) p.add_episode_num_onscreen(u"E{0}".format(program.episode_num)) if program.descriptions is None: self._add_programme_description(broadcast, p, program, None) else: for description_language in program.descriptions.languages(): self._add_programme_description(broadcast, p, program, description_language) if program.movie is not None: for quality_rating in program.movie.quality_ratings: p.add_star_rating(quality_rating.rating, quality_rating.max_rating, quality_rating.ratings_body) if program.is_episode_entity and not broadcast.is_new and program.original_air_date is not None: p.add_previously_shown(start=program.original_air_date) if program_artwork is not None: # "Banner-L2", "Banner-L3", "Iconic", "Staple" image_list = program_artwork.image_list.aspect_preference(u"4x3", u"16x9", u"3x4", u"2x3").size_preference(u"Md").category_preference(u"Poster Art", u"Box Art", u"Banner", u"Banner-L1", u"Banner-LO", u"Banner-L2", u"Logo").tier_preference(u"Series", u"Season", u"Sport", u"Team", u"Organization", u"College", None) image = image_list[0] if image_list else None if image is not None: p.add_icon(image.url) p.save(fp, encoding=self._encoding) def _add_programme_description(self, broadcast, p, program, description_language): description_elements = [] #if self._episode_title_in_description and program.episode_title is not None: # description_elements.append(u"\"{0}\"".format(program.episode_title)) program_attributes = [] #if program.show_type is not None: # program_attributes.append(program.show_type) airdate = True if program.movie is not None and program.movie.year is not None: program_attributes.append(u"Released: " + program.movie.year) elif broadcast.is_live: program_attributes.append(u"LIVE") airdate = False elif broadcast.is_new: program_attributes.append(u"NEW") airdate = False if program.is_episode_entity: if program.metadata is not None and \ program.metadata.season_episode is not None and \ program.metadata.season_episode.has_season_episode: program_attributes.append(u"Season {0.season} - Episode {0.episode}".format(program.metadata.season_episode)) elif program.episode_num is not None: program_attributes.append(u"Episode {0}".format(program.episode_num)) if broadcast.multipart is not None: program_attributes.append(u"{0.part_number} of {0.total_parts}".format(broadcast.multipart)) if airdate is True and program.original_air_date is not None: program_attributes.append(u"Originally Aired: " + str(program.original_air_date.strftime("%B %d, %Y"))) if len(program.content_ratings) != 0: for preference in self._content_rating_preference_order: rating = program.get_content_rating(preference) if rating is None: continue program_attributes.append(rating.code) break if program.movie is not None and len(program.movie.quality_ratings) != 0: selected_quality_rating = next((quality_rating for quality_rating in program.movie.quality_ratings if quality_rating.max_rating == u"4"), None) if selected_quality_rating is not None: program_attributes.append(selected_quality_rating.get_stars()) if description_language is not None: longest_text = program.descriptions.get_longest_text(language=description_language) description_elements.append(longest_text) if len(program_attributes) != 0: description_elements.append(u" | ".join(program_attributes)) if len(program.recommendations) != 0: description_elements.append(u"See also: {0}".format(u", ".join([pr.title120 for pr in program.recommendations]))) p.add_description("\n".join(description_elements), lang=description_language) def _add_channel(self, fp, channel): channel_id = channel.get_unique_id() self._logger.info(u"Adding channel %s to xmltv document.", channel_id) xmltv_channel = XmltvChannel(channel_id) [xmltv_channel.add_display_name(display_name) for display_name in channel.get_display_names()] xmltv_channel.save(fp, encoding=self._encoding) def _export_icons(self, stations): import urllib import os from urlparse import urlparse for station in stations: if station.logo is None: continue url = station.logo.url path = urlparse(url) (path, filename) = os.path.split(path[2]) (filename, extension) = os.path.splitext(filename) image_on_web = urllib.urlopen(url) if image_on_web.headers.maintype != "image": image_on_web.close() continue f = open(station.station_id + extension, "wb") f.write(image_on_web.read()) f.close() image_on_web.close()
#!/usr/bin/python
class Sd2Xmltv: def __init__(self, options): self._sd = SchedulesDirect(options.username, options.password) self._status = None self._logger = logging.getLogger(__name__) self._xmltv_document = XmltvDocument() self._output_path = options.output_path self._days = options.days self._callsign_whitelist = None self._callsign_blacklist = None self._content_rating_preference_order = [u'Motion Picture Association of America', u'USA Parental Rating', u'Canadian Parental Rating'] self._include_credits = False self._include_see_also = False self._genres = set() def login(self): self._logger.info('Getting SchedulesDirect token.') self._sd.get_token() self._logger.info('Getting SchedulesDirect status.') self._status = Status.decode(self._sd.get_status()) if not self._sd.is_online(): raise Exception('System is not online.') expiry_delta = self._status.account.expires - datetime.utcnow() self._logger.info('Account will expire on {0} ({1} days).'.format(self._status.account.expires, int(expiry_delta.total_seconds() // 86400))) time_delta = datetime.utcnow() - self._status.last_data_update hours, minutes = int(time_delta.total_seconds() // 3600), int((time_delta.total_seconds() % 3600) // 60) self._logger.info('SchedulesDirect last update at {0} ({1} hours {2} minutes ago).'.format(self._status.last_data_update, hours, minutes)) if self._status.system_status.message is not None: self._logger.info('Message: {0}'.format(self._status.system_status.message)) def process(self): if len(self._status.lineups) == 0: self._logger.info('Not subscribed to any lineups, exiting.') return self._logger.info('Getting station/channel mappings for {0} lineups.'.format(len(self._status.lineups))) lineup_mappings = self._sd.get_lineup_mappings([(lineup.lineup_id, lineup.modified) for lineup in self._status.lineups]) #self._callsign_whitelist = ['CBLTDT', 'CHCHDT', 'CKCODT', 'CICADT', 'CHCJDT', 'CJMTDT', 'CIIID41', 'CFMTDT', 'CITYDT'] station_ids = {channel.station.station_id for channel in self._enumerate_channels(lineup_mappings)} current_date = datetime.utcnow().date() schedule_dates = [(current_date + timedelta(days = x)).strftime('%Y-%m-%d') for x in range(0, self._days)] self._logger.info('Getting schedules for {0} stations.'.format(len(station_ids))) schedules = self._sd.get_schedules(station_ids, schedule_dates) for channel in self._enumerate_channels(lineup_mappings): channel.station.schedules = [schedule for schedule in schedules if channel.station.station_id == schedule.station_id] self._logger.info('Caching new or changed programs...') programs = [(airing.program_id, airing.md5) for airing in self._enumerate_airings(lineup_mappings)] self._sd.cache_programs(programs) self._logger.info('Adding channels to xmltv document...') for channel in self._enumerate_channels(lineup_mappings): self._add_channel(channel) self._logger.info('Adding programs to xmltv document...') total_programs_added = 0 for channel in self._enumerate_channels(lineup_mappings): programs_added = 0 for schedule in channel.station.schedules: self._logger.info('Adding programs on %s for channel %s.' % (schedule.metadata.start_date.strftime("%Y-%m-%d"), channel.get_display_names().next())) for airing in schedule.airings: programs_added += 1 self._add_programme(channel, airing) self._logger.info('Added %s programs for channel %s.' % (programs_added, channel.get_display_names().next())) total_programs_added += programs_added self._logger.info('Added %s total programs.' % total_programs_added) self._logger.info('Saving ' + self._output_path) if self._output_path[-2:] == 'gz': f = gzip.open(self._output_path, 'wb') self._xmltv_document.save(f) f.close() else: self._xmltv_document.save(self._output_path) return def manage(self): while True: print('\nManage Account Options:\n') print('1. List subscribed lineups') print('2. Add lineup') print('3. Remove lineup') print('4. List lineup channels.') print('\nChoose an option or \'x\' to exit.') choice = raw_input('> ') if choice == 'x': break elif choice == '1': self._list_subscribed_lineups() elif choice == '2': self._add_lineup() elif choice == '3': self._remove_lineup() elif choice == '4': self._list_lineup_channels() def _list_subscribed_lineups(self): lineups = self._sd.get_subscribed_lineups() print '\nSubscribed Lineups:\n' for lineup in lineups: print 'Lineup:\t%s' % lineup.lineup_id print 'Name:\t%s' % lineup.name print 'Transport:\t%s' % lineup.transport print 'Location:\t%s' % lineup.location print '' def _add_lineup(self): while True: print '\nAdd Lineup\n' print 'Enter 5-digit zip or postal code or \'x\' to cancel:' postal_code = raw_input('> ') if postal_code == 'x': break country = 'USA' if postal_code[0].isalpha(): country = 'CAN' headends = self._sd.get_headends_by_postal_code(country, postal_code) while True: subscribed_lineups = self._sd.get_subscribed_lineups() subscribed_lineup_ids = [lineup.lineup_id for lineup in subscribed_lineups] headend_lineups = [(headend, lineup) for headend in headends for lineup in headend.lineups if lineup.lineup_id not in subscribed_lineup_ids] transport_set = set() for (headend, lineup) in headend_lineups: transport_set.add(headend.type) options = [] count = 0 for transport in transport_set: print '\nTransport: %s\n' % transport for (headend, lineup) in [(headend, lineup) for (headend, lineup) in headend_lineups if headend.type == transport]: options.append((headend, lineup)) count += 1 print '\t%s. %s (%s)' % (count, lineup.name, headend.location) print '\nChoose a lineup to add or \'x\' to cancel.' choice = raw_input('> ') if choice == 'x': break choice = int(choice) - 1 (headend, lineup) = options[choice] print 'Are you sure you want to add \'%s (%s)\'? (y/n)' % (lineup.name, headend.location) if raw_input('> ') != 'y': continue response = self._sd.add_lineup(lineup.lineup_id) print 'Schedules Direct returned \'%s\'.' % response.message print '%s lineup changes remaining.\n' % response.changes_remaining def _list_lineup_channels(self): while True: print '\nList Lineup Channels\n' subscribed_lineups = self._sd.get_subscribed_lineups() options = [] count = 0 for lineup in subscribed_lineups: count += 1 options.append(lineup) print '%s. %s (%s)' % (count, lineup.name, lineup.location) print '\nChoose a lineup to list channels or \'x\' to cancel.' choice = raw_input('> ') if choice == 'x': break choice = int(choice) - 1 lineup = options[choice] lineup_mapping = self._sd.get_lineup_mapping(lineup.lineup_id) for channel in lineup_mapping.channels: print '%s\t%s.%s\t%s\t%s "%s"' % (channel.uhf_vhf, channel.atsc_major, channel.atsc_minor, channel.channel, channel.station.callsign, channel.station.name) def _remove_lineup(self): while True: print '\nRemove Lineup\n' subscribed_lineups = self._sd.get_subscribed_lineups() options = [] count = 0 for lineup in subscribed_lineups: count += 1 options.append(lineup) print '%s. %s (%s)' % (count, lineup.name, lineup.location) print '\nChoose a lineup to remove or \'x\' to cancel.' choice = raw_input('> ') if choice == 'x': break choice = int(choice) - 1 lineup = options[choice] print 'Are you sure you want to remove \'%s (%s)\'? (y/n)' % (lineup.name, lineup.location) if raw_input('> ') != 'y': continue response = self._sd.remove_lineup(lineup.lineup_id) print '\nSchedules Direct returned \'%s\'.' % response.message print '%s lineup changes remaining.\n' % response.changes_remaining def _enumerate_channels(self, lineups): result = [channel for lineup in lineups for channel in lineup.channels] if self._callsign_whitelist is not None: result = [channel for channel in result if channel.station.callsign in self._callsign_whitelist] if self._callsign_blacklist is not None: result = [channel for channel in result if channel.station.callsign not in self._callsign_blacklist] yielded_channels = set() for channel in result: unique_id = channel.get_unique_id() if unique_id in yielded_channels: continue yield channel yielded_channels.add(unique_id) def _enumerate_airings(self, lineups): for channel in self._enumerate_channels(lineups): for schedule in channel.station.schedules: for airing in schedule.airings: yield airing def _add_program_categories(self, programme, channel, airing, program): """ :param programme: :type programme: XmltvProgramme :param channel: :type channel: Channel :param airing: :type airing: Airing :param program: :type program: Program :return: """ categories = set() if program.program_id[:2] == 'SP': categories.add('Sports') elif program.program_id[:2] == 'MV': categories.add('Movie / Drama') elif program.program_id[:2] == 'SH' or program.program_id[:2] == 'EP': if 'Children' in program.genres: categories.add("Children's / Youth programmes") if 'Educational' in program.genres: categories.add('Education / Science / Factual topics') if 'Science' in program.genres: categories.add('Education / Science / Factual topics') if 'Newsmagazine' in program.genres: categories.add('News magazine') if 'Documentary' in program.genres: categories.add('Documentary') if 'News' in program.genres: categories.add('News / Current affairs') if 'Music' in program.genres: categories.add('Music / Ballet / Dance') for category in categories: programme.add_category(category) def _add_programme(self, channel, airing): """ :param channel: :type channel: Channel :param airing: :type airing: Airing :return: """ program = self._sd.get_program(airing.program_id, airing.md5) if program is None: self._logger.warning('Program id {0} with md5 {1} was not found in cache, trying again with only id.'.format(airing.program_id, airing.md5)) program = self._sd.get_program(airing.program_id) if program is None: self._logger.error('Program id {0} not found in cache, skipping adding program.'.format(airing.program_id)) return self._logger.warning('Found Program id {0} \'{1}\' with md5 {2} in cache.'.format(program.program_id, program.titles.title120, program.md5)) start_time = airing.air_date_time stop_time = airing.end_date_time channel_id = channel.get_unique_id() p = self._xmltv_document.add_programme(start_time, stop_time, channel_id) p.add_title(program.titles.title120) if program.episode_title is not None: p.add_subtitle(program.episode_title) self._add_program_categories(p, channel, airing, program) #for genre in program.genres: # p.add_category('sd: ' + genre) # was airing.is_new #if program.program_id[:2] == 'EP' or program.program_id[:2] == 'MV' or program.program_id[:2] == 'SP': # tvheadend now supports series subscription filtering so add to all shows p.add_episode_num_dd_progid(program.program_id) if program.metadata.season_episode is not None: if airing.multipart is None: p.add_episode_num_xmltv_ns( season_num = program.metadata.season_episode.season, episode_num = program.metadata.season_episode.episode) else: p.add_episode_num_xmltv_ns( season_num = program.metadata.season_episode.season, episode_num = program.metadata.season_episode.episode, part_num = airing.multipart.part_number, total_parts = airing.multipart.total_parts) elif program.episode_num is not None: p.add_episode_num_onscreen('E' + str(program.episode_num)) description_prefix = '' if program.episode_title is not None: description_prefix = '"' + program.episode_title + '" ' program_attributes = [] if program.show_type is not None: program_attributes.append(program.show_type) if program.movie is not None and program.movie.year is not None: program_attributes.append(program.movie.year) elif airing.is_live == True: program_attributes.append('Live') elif airing.is_new == True: program_attributes.append('New') elif program.original_air_date is not None: program_attributes.append(program.original_air_date.strftime('%Y-%m-%d')) if program.metadata.season_episode is not None: program_attributes.append('S%sE%s' % (program.metadata.season_episode.season, program.metadata.season_episode.episode)) elif program.episode_num is not None: program_attributes.append('E' + str(program.episode_num)) if airing.multipart is not None: program_attributes.append('%s of %s' % (airing.multipart.part_number, airing.multipart.total_parts)) if len(program.content_ratings) != 0 and len(self._content_rating_preference_order) != 0: for preference in self._content_rating_preference_order: rating = program.get_content_rating(preference) if rating is None: continue program_attributes.append(rating) break if len(program_attributes) != 0: description_prefix = description_prefix + '(' + '; '.join(program_attributes) + ') ' see_also = '' if len(program.recommendations) != 0: see_also = ' See also: ' + ', '.join([pr.title120 for pr in program.recommendations]) if len(program.descriptions.description1000) != 0: for description in program.descriptions.description1000: p.add_description(description_prefix + description.description + see_also, description.language) elif len(program.descriptions.description100) != 0: for description in program.descriptions.description100: p.add_description(description_prefix + description.description + see_also, description.language) else: if description_prefix != '': p.add_description(description_prefix.strip() + see_also) if program.movie is not None: for quality_rating in program.movie.quality_ratings: p.add_star_rating(quality_rating.rating, quality_rating.max_rating, quality_rating.ratings_body) if self._include_credits == False: return for cast in program.cast: if cast['role'] in ['Actor', 'Guest', 'Guest Star', 'Judge', 'Voice', 'Guest Voice', 'Host', 'Narrator', 'Anchor', 'Contestant', 'Correspondent', 'Musical Guest']: continue self._logger.info('cast: ' + cast['role']) actors = program.get_cast(['Actor']) guests = program.get_cast(['Guest']) guest_stars = program.get_cast(['Guest Star']) judges = program.get_cast(['Judge']) voices = program.get_cast(['Voice']) guest_voices = program.get_cast(['Guest Voice']) cast_hosts = program.get_cast(['Host']) narrators = program.get_cast(['Narrator']) anchors = program.get_cast(['Anchor']) contestants = program.get_cast(['Contestant']) correspondents = program.get_cast(['Correspondent']) musical_guests = program.get_cast(['Musical Guest']) for actor in actors: p.add_credit_actor(actor.name) for guest_star in guest_stars: p.add_credit_guest(guest_star) directors = program.get_crew(['Director']) writers = program.get_crew(['Writer']) producers = program.get_crew(['Producer']) for director in directors: p.add_credit_director(director) for writer in writers: p.add_credit_writer(writer) for producer in producers: p.add_credit_producer(producer) def _add_channel(self, channel): channel_id = channel.get_unique_id() if self._xmltv_document.has_channel(channel_id): self._logger.info('Skipping channel %s, already added.' % (channel_id)) return self._logger.info('Adding channel %s to xmltv document.' % (channel_id)) xmltv_channel = self._xmltv_document.add_channel(channel_id) [xmltv_channel.add_display_name(display_name) for display_name in channel.get_display_names()]