From 78f4aeb7cb1dd8951088cf38d952bfcd580bc404 Mon Sep 17 00:00:00 2001 From: Justin Emter Date: Sat, 22 Jul 2017 19:38:05 -0700 Subject: [PATCH 01/20] Cleanup / added cli flags for info gathering / debugging. --- PseudoChannel.py | 962 ++++++++++++++------------- pseudo_config.py | 35 +- pseudo_schedule.xml | 82 +-- src/Commercial.py | 50 +- src/Episode.py | 66 +- src/Media.py | 56 +- src/Movie.py | 50 +- src/Music.py | 50 +- src/PseudoChannelDatabase.py | 720 ++++++++++---------- src/PseudoDailyScheduleController.py | 438 ++++++------ src/Video.py | 50 +- 11 files changed, 1318 insertions(+), 1241 deletions(-) diff --git a/PseudoChannel.py b/PseudoChannel.py index cd072b3..5c46a78 100644 --- a/PseudoChannel.py +++ b/PseudoChannel.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python # -*- coding: utf-8 -*- from src import PseudoChannelDatabase @@ -28,704 +29,763 @@ sys.setdefaultencoding('utf-8') class PseudoChannel(): - PLEX = PlexServer(config.baseurl, config.token) + PLEX = PlexServer(config.baseurl, config.token) + MEDIA = [] - MEDIA = [] + def __init__(self): - def __init__(self): + self.db = PseudoChannelDatabase("pseudo-channel.db") - self.db = PseudoChannelDatabase("pseudo-channel.db") + self.controller = PseudoDailyScheduleController(config.baseurl, config.token, config.plexClients) - self.controller = PseudoDailyScheduleController(config.baseurl, config.token, config.plexClients) + """Database functions. - """Database functions. + update_db(): Grab the media from the Plex DB and store it in the local pseudo-channel.db. - update_db(): Grab the media from the Plex DB and store it in the local pseudo-channel.db. + drop_db(): Drop the local database. Fresh start. - drop_db(): Drop the local database. Fresh start. + update_schedule(): Update schedule with user defined times. - update_schedule(): Update schedule with user defined times. + drop_schedule(): Drop the user defined schedule table. - drop_schedule(): Drop the user defined schedule table. + generate_daily_schedule(): Generates daily schedule based on the "schedule" table. + """ - generate_daily_schedule(): Generates daily schedule based on the "schedule" table. - """ + # Print iterations progress + def print_progress(self, iteration, total, prefix='', suffix='', decimals=1, bar_length=100): + """ + Call in a loop to create terminal progress bar + @params: + iteration - Required : current iteration (Int) + total - Required : total iterations (Int) + prefix - Optional : prefix string (Str) + suffix - Optional : suffix string (Str) + decimals - Optional : positive number of decimals in percent complete (Int) + bar_length - Optional : character length of bar (Int) + """ + str_format = "{0:." + str(decimals) + "f}" + percents = str_format.format(100 * (iteration / float(total))) + filled_length = int(round(bar_length * iteration / float(total))) + bar = '█' * filled_length + '-' * (bar_length - filled_length) - # Print iterations progress - def print_progress(self, iteration, total, prefix='', suffix='', decimals=1, bar_length=100): - """ - Call in a loop to create terminal progress bar - @params: - iteration - Required : current iteration (Int) - total - Required : total iterations (Int) - prefix - Optional : prefix string (Str) - suffix - Optional : suffix string (Str) - decimals - Optional : positive number of decimals in percent complete (Int) - bar_length - Optional : character length of bar (Int) - """ - str_format = "{0:." + str(decimals) + "f}" - percents = str_format.format(100 * (iteration / float(total))) - filled_length = int(round(bar_length * iteration / float(total))) - bar = '█' * filled_length + '-' * (bar_length - filled_length) + sys.stdout.write('\r%s |%s| %s%s %s' % (prefix, bar, percents, '%', suffix)), - sys.stdout.write('\r%s |%s| %s%s %s' % (prefix, bar, percents, '%', suffix)), + if iteration == total: + sys.stdout.write('\n') + sys.stdout.flush() - if iteration == total: - sys.stdout.write('\n') - sys.stdout.flush() + def update_db(self): - def update_db(self): + print("#### Updating Local Database") - print("#### Updating Local Database") + self.db.create_tables() - self.db.create_tables() + libs_dict = config.plexLibraries - libs_dict = config.plexLibraries + sections = self.PLEX.library.sections() - sections = self.PLEX.library.sections() + for section in sections: - for section in sections: + for correct_lib_name, user_lib_name in libs_dict.items(): - for correct_lib_name, user_lib_name in libs_dict.items(): + if section.title.lower() in [x.lower() for x in user_lib_name]: - if section.title.lower() in [x.lower() for x in user_lib_name]: + if correct_lib_name == "Movies": - if correct_lib_name == "Movies": + sectionMedia = self.PLEX.library.section(section.title).all() - sectionMedia = self.PLEX.library.section(section.title).all() + for i, media in enumerate(sectionMedia): - for i, media in enumerate(sectionMedia): + self.db.add_movies_to_db(1, media.title, media.duration) - self.db.add_movies_to_db(1, media.title, media.duration) + self.print_progress( + i + 1, + len(sectionMedia), + prefix = 'Progress '+section.title+": ", + suffix = 'Complete', + bar_length = 40 + ) - self.print_progress( - i + 1, - len(sectionMedia), - prefix = 'Progress '+section.title+": ", - suffix = 'Complete', - bar_length = 40 - ) + elif correct_lib_name == "TV Shows": - elif correct_lib_name == "TV Shows": + sectionMedia = self.PLEX.library.section(section.title).all() - sectionMedia = self.PLEX.library.section(section.title).all() + for i, media in enumerate(sectionMedia): - for i, media in enumerate(sectionMedia): + backgroundImagePath = self.PLEX.library.section(section.title).get(media.title) - backgroundImagePath = self.PLEX.library.section(section.title).get(media.title) + backgroundImgURL = '' - backgroundImgURL = '' + if isinstance(backgroundImagePath.art, str): - if isinstance(backgroundImagePath.art, str): + backgroundImgURL = config.baseurl+backgroundImagePath.art+"?X-Plex-Token="+config.token - backgroundImgURL = config.baseurl+backgroundImagePath.art+"?X-Plex-Token="+config.token + self.db.add_shows_to_db(2, media.title, media.duration, '', backgroundImgURL) - self.db.add_shows_to_db(2, media.title, media.duration, '', backgroundImgURL) + self.print_progress( + i + 1, + len(sectionMedia), + prefix = 'Progress '+section.title+": ", + suffix = 'Complete', + bar_length = 40 + ) - self.print_progress( - i + 1, - len(sectionMedia), - prefix = 'Progress '+section.title+": ", - suffix = 'Complete', - bar_length = 40 - ) + #add all episodes of each tv show to episodes table + episodes = self.PLEX.library.section(section.title).get(media.title).episodes() - #add all episodes of each tv show to episodes table - episodes = self.PLEX.library.section(section.title).get(media.title).episodes() + for episode in episodes: - for episode in episodes: + duration = episode.duration - duration = episode.duration + if duration: - if duration: + self.db.add_episodes_to_db( + 4, + episode.title, + duration, + episode.index, + episode.parentIndex, + media.title + ) - self.db.add_episodes_to_db( - 4, - episode.title, - duration, - episode.index, - episode.parentIndex, - media.title - ) + else: - else: + self.db.add_episodes_to_db( + 4, + episode.title, + 0, + episode.index, + episode.parentIndex, + media.title + ) - self.db.add_episodes_to_db( - 4, - episode.title, - 0, - episode.index, - episode.parentIndex, - media.title - ) + elif correct_lib_name == "Commercials": - elif correct_lib_name == "Commercials": + sectionMedia = self.PLEX.library.section(section.title).all() - sectionMedia = self.PLEX.library.section(section.title).all() + media_length = len(sectionMedia) - media_length = len(sectionMedia) + for i, media in enumerate(sectionMedia): - for i, media in enumerate(sectionMedia): + self.db.add_commercials_to_db(3, media.title, media.duration) - self.db.add_commercials_to_db(3, media.title, media.duration) + self.print_progress( + i + 1, + media_length, + prefix = 'Progress '+section.title+":", + suffix = 'Complete', + bar_length = 40 + ) - self.print_progress( - i + 1, - media_length, - prefix = 'Progress '+section.title+":", - suffix = 'Complete', - bar_length = 40 - ) + def update_schedule(self): - def update_schedule(self): + self.db.create_tables() - self.db.create_tables() + self.db.remove_all_scheduled_items() - self.db.remove_all_scheduled_items() + scheduled_days_list = [ + "mondays", + "tuesdays", + "wednesdays", + "thursdays", + "fridays", + "saturdays", + "sundays", + "weekdays", + "weekends", + "everyday" + ] - scheduled_days_list = [ - "mondays", - "tuesdays", - "wednesdays", - "thursdays", - "fridays", - "saturdays", - "sundays", - "weekdays", - "weekends", - "everyday" - ] + section_dict = { + "TV Shows" : ["series", "shows", "tv", "episodes", "tv shows", "show"], + "Movies" : ["movie", "movies", "films", "film"], + "Videos" : ["video", "videos", "vid"], + "Music" : ["music", "songs", "song", "tune", "tunes"] + } - section_dict = { - "TV Shows" : ["series", "shows", "tv", "episodes", "tv shows", "show"], - "Movies" : ["movie", "movies", "films", "film"], - "Videos" : ["video", "videos", "vid"], - "Music" : ["music", "songs", "song", "tune", "tunes"] - } + tree = ET.parse('pseudo_schedule.xml') - tree = ET.parse('pseudo_schedule.xml') + root = tree.getroot() - root = tree.getroot() + for child in root: - for child in root: + if child.tag in scheduled_days_list: - if child.tag in scheduled_days_list: + for time in child.iter("time"): - for time in child.iter("time"): + for key, value in section_dict.items(): - for key, value in section_dict.items(): + if time.attrib['type'] == key or time.attrib['type'] in value: - if time.attrib['type'] == key or time.attrib['type'] in value: + title = time.attrib['title'] - title = time.attrib['title'] + natural_start_time = self.translate_time(time.text) - natural_start_time = self.translate_time(time.text) + natural_end_time = 0 - natural_end_time = 0 + section = key - section = key + day_of_week = child.tag - day_of_week = child.tag + strict_time = time.attrib['strict-time'] - strict_time = time.attrib['strict-time'] + time_shift = time.attrib['time-shift'] - time_shift = time.attrib['time-shift'] + overlap_max = time.attrib['overlap-max'] - overlap_max = time.attrib['overlap-max'] + start_time_unix = datetime.datetime.strptime( + self.translate_time(time.text), + '%I:%M %p').strftime('%Y-%m-%d %H:%M:%S') - start_time_unix = datetime.datetime.strptime( - self.translate_time(time.text), - '%I:%M %p').strftime('%Y-%m-%d %H:%M:%S') + print "Adding: ", time.tag, section, time.text, time.attrib['title'] - print "Adding: ", time.tag, section, time.text, time.attrib['title'] + self.db.add_schedule_to_db( + 0, # mediaID + title, # title + 0, # duration + natural_start_time, # startTime + natural_end_time, # endTime + day_of_week, # dayOfWeek + start_time_unix, # startTimeUnix + section, # section + strict_time, # strictTime + time_shift, # timeShift + overlap_max, # overlapMax + ) - self.db.add_schedule_to_db( - 0, # mediaID - title, # title - 0, # duration - natural_start_time, # startTime - natural_end_time, # endTime - day_of_week, # dayOfWeek - start_time_unix, # startTimeUnix - section, # section - strict_time, # strictTime - time_shift, # timeShift - overlap_max, # overlapMax - ) + def drop_db(self): - def drop_db(self): + self.db.drop_db() - self.db.drop_db() + def drop_schedule(self): - def drop_schedule(self): + self.db.drop_schedule() - self.db.drop_schedule() + def remove_all_scheduled_items(): - def remove_all_scheduled_items(): + self.db.remove_all_scheduled_items() - self.db.remove_all_scheduled_items() + """App functions. - """App functions. + generate_daily_schedule(): Generate the daily_schedule table. + """ - generate_daily_schedule(): Generate the daily_schedule table. - """ + ''' + * + * Using datetime to figure out when the media item will end based on the scheduled start time or the offset + * generated by the previous media item. - ''' - * - * Using datetime to figure out when the media item will end based on the scheduled start time or the offset - * generated by the previous media item. + * Returns time + * + ''' + ''' + * + * Returns time difference in minutes + * + ''' - * Returns time - * - ''' - ''' - * - * Returns time difference in minutes - * - ''' + def translate_time(self, timestr): - def translate_time(self, timestr): + try: - try: + return datetime.datetime.strptime(timestr, "%I:%M %p").strftime("%-I:%M %p") - return datetime.datetime.strptime(timestr, "%I:%M %p").strftime("%-I:%M %p") + except ValueError as e: - except ValueError as e: + pass - pass + try: - try: + return datetime.datetime.strptime(timestr, "%H:%M").strftime("%-I:%M %p") - return datetime.datetime.strptime(timestr, "%H:%M").strftime("%-I:%M %p") + except ValueError as e: - except ValueError as e: + pass - pass + def time_diff(self, time1,time2): + ''' + * + * Getting the offest by comparing both times from the unix epoch time and getting the difference. + * + ''' + timeA = datetime.datetime.strptime(time1, "%I:%M %p") + timeB = datetime.datetime.strptime(time2, "%I:%M %p") + + timeAEpoch = calendar.timegm(timeA.timetuple()) + timeBEpoch = calendar.timegm(timeB.timetuple()) - def time_diff(self, time1,time2): - ''' - * - * Getting the offest by comparing both times from the unix epoch time and getting the difference. - * - ''' - timeA = datetime.datetime.strptime(time1, "%I:%M %p") - timeB = datetime.datetime.strptime(time2, "%I:%M %p") - - timeAEpoch = calendar.timegm(timeA.timetuple()) - timeBEpoch = calendar.timegm(timeB.timetuple()) + tdelta = abs(timeAEpoch) - abs(timeBEpoch) - tdelta = abs(timeAEpoch) - abs(timeBEpoch) + return int(tdelta/60) - return int(tdelta/60) + ''' + * + * Passing in the endtime from the prev episode and desired start time of this episode, calculate the best start time - ''' - * - * Passing in the endtime from the prev episode and desired start time of this episode, calculate the best start time + * Returns time - for new start time + * + ''' + def calculate_start_time(self, prevEndTime, intendedStartTime, timeGap, overlapMax): - * Returns time - for new start time - * - ''' - def calculate_start_time(self, prevEndTime, intendedStartTime, timeGap, overlapMax): + self.TIME_GAP = timeGap - self.TIME_GAP = timeGap + self.OVERLAP_GAP = timeGap - self.OVERLAP_GAP = timeGap + self.OVERLAP_MAX = overlapMax - self.OVERLAP_MAX = overlapMax + time1 = prevEndTime.strftime('%-I:%M %p') - time1 = prevEndTime.strftime('%-I:%M %p') + timeB = datetime.datetime.strptime(intendedStartTime, '%I:%M %p').strftime('%-I:%M %p') - timeB = datetime.datetime.strptime(intendedStartTime, '%I:%M %p').strftime('%-I:%M %p') + print "++++ Previous End Time: ", time1, "Intended start time: ", timeB - print "++++ Previous End Time: ", time1, "Intended start time: ", timeB + timeDiff = self.time_diff(time1, timeB) - timeDiff = self.time_diff(time1, timeB) + """print("timeDiff "+ str(timeDiff)) + print("startTimeUNIX: "+ str(intendedStartTime))""" - """print("timeDiff "+ str(timeDiff)) - print("startTimeUNIX: "+ str(intendedStartTime))""" + newTimeObj = timeB - newTimeObj = timeB + newStartTime = timeB - newStartTime = timeB + ''' + * + * If time difference is negative, then we know there is overlap + * + ''' + if timeDiff < 0: + ''' + * + * If there is an overlap, then the overlapGap var in config will determine the next increment. If it is set to "15", then the show will will bump up to the next 15 minute interval past the hour. + * + ''' + timeset=[datetime.time(h,m).strftime("%H:%M") for h,m in itertools.product(xrange(0,24),xrange(0,60,int(self.OVERLAP_GAP)))] + + #print(timeset) - ''' - * - * If time difference is negative, then we know there is overlap - * - ''' - if timeDiff < 0: - ''' - * - * If there is an overlap, then the overlapGap var in config will determine the next increment. If it is set to "15", then the show will will bump up to the next 15 minute interval past the hour. - * - ''' - timeset=[datetime.time(h,m).strftime("%H:%M") for h,m in itertools.product(xrange(0,24),xrange(0,60,int(self.OVERLAP_GAP)))] - - #print(timeset) + timeSetToUse = None - timeSetToUse = None + for time in timeset: - for time in timeset: + #print(time) + theTimeSetInterval = datetime.datetime.strptime(time, '%H:%M') - #print(time) - theTimeSetInterval = datetime.datetime.strptime(time, '%H:%M') + # print(theTimeSetInterval) - # print(theTimeSetInterval) + # print(prevEndTime) - # print(prevEndTime) + if theTimeSetInterval >= prevEndTime: - if theTimeSetInterval >= prevEndTime: + print "++++ There is overlap. Setting new time-interval:", theTimeSetInterval - print "++++ There is overlap. Setting new time-interval:", theTimeSetInterval + newStartTime = theTimeSetInterval - newStartTime = theTimeSetInterval + break - break + #newStartTime = newTimeObj + datetime.timedelta(minutes=abs(timeDiff + overlapGap)) - #newStartTime = newTimeObj + datetime.timedelta(minutes=abs(timeDiff + overlapGap)) + elif (timeDiff >= 0) and (self.TIME_GAP != -1): - elif (timeDiff >= 0) and (self.TIME_GAP != -1): + ''' + * + * If there this value is configured, then the timeGap var in config will determine the next increment. + * If it is set to "15", then the show will will bump up to the next 15 minute interval past the hour. + * + ''' + timeset=[datetime.time(h,m).strftime("%H:%M") for h,m in itertools.product(xrange(0,24),xrange(0,60,int(self.TIME_GAP)))] + + # print(timeset) - ''' - * - * If there this value is configured, then the timeGap var in config will determine the next increment. - * If it is set to "15", then the show will will bump up to the next 15 minute interval past the hour. - * - ''' - timeset=[datetime.time(h,m).strftime("%H:%M") for h,m in itertools.product(xrange(0,24),xrange(0,60,int(self.TIME_GAP)))] - - # print(timeset) + for time in timeset: - for time in timeset: + theTimeSetInterval = datetime.datetime.strptime(time, '%H:%M') - theTimeSetInterval = datetime.datetime.strptime(time, '%H:%M') + tempTimeTwoStr = datetime.datetime.strptime(time1, '%I:%M %p').strftime('%H:%M') - tempTimeTwoStr = datetime.datetime.strptime(time1, '%I:%M %p').strftime('%H:%M') + formatted_time_two = datetime.datetime.strptime(tempTimeTwoStr, '%H:%M') - formatted_time_two = datetime.datetime.strptime(tempTimeTwoStr, '%H:%M') + if theTimeSetInterval >= formatted_time_two: - if theTimeSetInterval >= formatted_time_two: + print "++++ Setting new time-interval:", theTimeSetInterval - print "++++ Setting new time-interval:", theTimeSetInterval + newStartTime = theTimeSetInterval - newStartTime = theTimeSetInterval + break - break + else: - else: + print("Not sure what to do here") - print("Not sure what to do here") + return newStartTime.strftime('%-I:%M %p') - return newStartTime.strftime('%-I:%M %p') + def get_end_time_from_duration(self, startTime, duration): - def get_end_time_from_duration(self, startTime, duration): + time = datetime.datetime.strptime(startTime, '%I:%M %p') - time = datetime.datetime.strptime(startTime, '%I:%M %p') + show_time_plus_duration = time + datetime.timedelta(milliseconds=duration) - show_time_plus_duration = time + datetime.timedelta(milliseconds=duration) + #print(show_time_plus_duration.minute) - #print(show_time_plus_duration.minute) + return show_time_plus_duration - return show_time_plus_duration + def generate_daily_schedule(self): - def generate_daily_schedule(self): + print("#### Generating Daily Schedule") - print("#### Generating Daily Schedule") + schedule = self.db.get_schedule() - schedule = self.db.get_schedule() + weekday_dict = { + "0" : ["mondays", "weekdays", "everyday"], + "1" : ["tuesdays", "weekdays", "everyday"], + "2" : ["wednesdays", "weekdays", "everyday"], + "3" : ["thursdays", "weekdays", "everyday"], + "4" : ["fridays", "weekdays", "everyday"], + "5" : ["saturdays", "weekends", "everyday"], + "6" : ["sundays", "weekends", "everyday"], + } - weekday_dict = { - "0" : ["mondays", "weekdays", "everyday"], - "1" : ["tuesdays", "weekdays", "everyday"], - "2" : ["wednesdays", "weekdays", "everyday"], - "3" : ["thursdays", "weekdays", "everyday"], - "4" : ["fridays", "weekdays", "everyday"], - "5" : ["saturdays", "weekends", "everyday"], - "6" : ["sundays", "weekends", "everyday"], - } + weekno = datetime.datetime.today().weekday() - weekno = datetime.datetime.today().weekday() + schedule_advance_watcher = 0 - schedule_advance_watcher = 0 + for entry in schedule: - for entry in schedule: + schedule_advance_watcher += 1 - schedule_advance_watcher += 1 + section = entry[9] - section = entry[9] + for key, val in weekday_dict.iteritems(): - for key, val in weekday_dict.iteritems(): + if str(entry[7]) in str(val) and int(weekno) == int(key): - if str(entry[7]) in str(val) and int(weekno) == int(key): + if section == "TV Shows": - if section == "TV Shows": + if entry[3] == "random": - if entry[3] == "random": + next_episode = self.db.get_random_episode() - next_episode = self.db.get_random_episode() + else: - else: + next_episode = self.db.get_next_episode(entry[3]) - next_episode = self.db.get_next_episode(entry[3]) + if next_episode != None: + + episode = Episode( + section, # section_type + next_episode[3], # title + entry[5], # natural_start_time + self.get_end_time_from_duration(entry[5], next_episode[4]), # natural_end_time + next_episode[4], # duration + entry[7], # day_of_week + entry[10], # is_strict_time + entry[11], # time_shift + entry[12], # overlap_max + entry[3], # show_series_title + next_episode[5], # episode_number + next_episode[6] # season_number + ) - if next_episode != None: - - episode = Episode( - section, # section_type - next_episode[3], # title - entry[5], # natural_start_time - self.get_end_time_from_duration(entry[5], next_episode[4]), # natural_end_time - next_episode[4], # duration - entry[7], # day_of_week - entry[10], # is_strict_time - entry[11], # time_shift - entry[12], # overlap_max - entry[3], # show_series_title - next_episode[5], # episode_number - next_episode[6] # season_number - ) + self.MEDIA.append(episode) - self.MEDIA.append(episode) + else: - else: + print("Cannot find TV Show Episode, {} in the local db".format(entry[3])) - print("Cannot find TV Show Episode, {} in the local db".format(entry[3])) + #print(episode) - #print(episode) + elif section == "Movies": - elif section == "Movies": + if entry[3] == "random": - if entry[3] == "random": + the_movie = self.db.get_random_movie() - the_movie = self.db.get_random_movie() + else: - else: + the_movie = self.db.get_movie(entry[3]) - the_movie = self.db.get_movie(entry[3]) + if the_movie != None: - if the_movie != None: + movie = Movie( + section, # section_type + the_movie[3], # title + entry[5], # natural_start_time + self.get_end_time_from_duration(entry[5], the_movie[4]), # natural_end_time + the_movie[4], # duration + entry[7], # day_of_week + entry[10], # is_strict_time + entry[11], # time_shift + entry[12] # overlap_max + ) - movie = Movie( - section, # section_type - the_movie[3], # title - entry[5], # natural_start_time - self.get_end_time_from_duration(entry[5], the_movie[4]), # natural_end_time - the_movie[4], # duration - entry[7], # day_of_week - entry[10], # is_strict_time - entry[11], # time_shift - entry[12] # overlap_max - ) + #print(movie.natural_end_time) - #print(movie.natural_end_time) + self.MEDIA.append(movie) - self.MEDIA.append(movie) + else: - else: + print("Cannot find Movie, {} in the local db".format(entry[3])) - print("Cannot find Movie, {} in the local db".format(entry[3])) + elif section == "Music": - elif section == "Music": + the_music = self.db.get_music(entry[3]) - the_music = self.db.get_music(entry[3]) + if the_music != None: - if the_music != None: + music = Music( + section, # section_type + the_music[3], # title + entry[5], # natural_start_time + self.get_end_time_from_duration(entry[5], the_music[4]), # natural_end_time + the_music[4], # duration + entry[7], # day_of_week + entry[10], # is_strict_time + entry[11], # time_shift + entry[12] # overlap_max + ) - music = Music( - section, # section_type - the_music[3], # title - entry[5], # natural_start_time - self.get_end_time_from_duration(entry[5], the_music[4]), # natural_end_time - the_music[4], # duration - entry[7], # day_of_week - entry[10], # is_strict_time - entry[11], # time_shift - entry[12] # overlap_max - ) + #print(music.natural_end_time) - #print(music.natural_end_time) + self.MEDIA.append(music) - self.MEDIA.append(music) + else: - else: + print("Cannot find Music, {} in the local db".format(entry[3])) - print("Cannot find Music, {} in the local db".format(entry[3])) + elif section == "Video": - elif section == "Video": + the_video = self.db.get_video(entry[3]) - the_video = self.db.get_video(entry[3]) + if the_music != None: - if the_music != None: + video = Video( + section, # section_type + the_video[3], # title + entry[5], # natural_start_time + self.get_end_time_from_duration(entry[5], the_video[4]), # natural_end_time + the_video[4], # duration + entry[7], # day_of_week + entry[10], # is_strict_time + entry[11], # time_shift + entry[12] # overlap_max + ) - video = Video( - section, # section_type - the_video[3], # title - entry[5], # natural_start_time - self.get_end_time_from_duration(entry[5], the_video[4]), # natural_end_time - the_video[4], # duration - entry[7], # day_of_week - entry[10], # is_strict_time - entry[11], # time_shift - entry[12] # overlap_max - ) + #print(music.natural_end_time) - #print(music.natural_end_time) + self.MEDIA.append(video) - self.MEDIA.append(video) + else: - else: + print("Cannot find Video, {} in the local db".format(entry[3])) - print("Cannot find Video, {} in the local db".format(entry[3])) + else: - else: + pass - pass + """If we reached the end of the scheduled items for today, add them to the daily schedule - """If we reached the end of the scheduled items for today, add them to the daily schedule + """ + if schedule_advance_watcher >= len(schedule): - """ - if schedule_advance_watcher >= len(schedule): + print "+++++ Finished processing time entries, recreating daily_schedule" - print "+++++ Finished processing time entries, recreating daily_schedule" + previous_episode = None - previous_episode = None + self.db.remove_all_daily_scheduled_items() - self.db.remove_all_daily_scheduled_items() + for entry in self.MEDIA: - for entry in self.MEDIA: + #print entry.natural_end_time - #print entry.natural_end_time + if previous_episode != None: - if previous_episode != None: + natural_start_time = datetime.datetime.strptime(entry.natural_start_time, '%I:%M %p') - natural_start_time = datetime.datetime.strptime(entry.natural_start_time, '%I:%M %p') + natural_end_time = entry.natural_end_time - natural_end_time = entry.natural_end_time + if entry.is_strict_time.lower() == "true": - if entry.is_strict_time.lower() == "true": + print "++++ Strict-time: {}".format(str(entry.title)) - print "++++ Strict-time: {}".format(str(entry.title)) + entry.end_time = self.get_end_time_from_duration( + self.translate_time(entry.start_time), + entry.duration + ) - entry.end_time = self.get_end_time_from_duration( - self.translate_time(entry.start_time), - entry.duration - ) + self.db.add_media_to_daily_schedule(entry) - self.db.add_media_to_daily_schedule(entry) + previous_episode = entry - previous_episode = entry + else: - else: + print "++++ NOT strict-time: {}".format(str(entry.title).encode(sys.stdout.encoding, errors='replace')) - print "++++ NOT strict-time: {}".format(str(entry.title).encode(sys.stdout.encoding, errors='replace')) + new_starttime = self.calculate_start_time( + previous_episode.end_time, + entry.natural_start_time, + previous_episode.time_shift, + previous_episode.overlap_max + ) - new_starttime = self.calculate_start_time( - previous_episode.end_time, - entry.natural_start_time, - previous_episode.time_shift, - previous_episode.overlap_max - ) + print "++++ New start time:", new_starttime - print "++++ New start time:", new_starttime + entry.start_time = datetime.datetime.strptime(new_starttime, '%I:%M %p').strftime('%-I:%M %p') - entry.start_time = datetime.datetime.strptime(new_starttime, '%I:%M %p').strftime('%-I:%M %p') + entry.end_time = self.get_end_time_from_duration(entry.start_time, entry.duration) - entry.end_time = self.get_end_time_from_duration(entry.start_time, entry.duration) + self.db.add_media_to_daily_schedule(entry) - self.db.add_media_to_daily_schedule(entry) + previous_episode = entry - previous_episode = entry + else: - else: + self.db.add_media_to_daily_schedule(entry) - self.db.add_media_to_daily_schedule(entry) + previous_episode = entry - previous_episode = entry + def show_clients(self): + + print "##### Connected Clients:" + + for i, client in enumerate(self.PLEX.clients()): + + print "+++++", str(i + 1)+".", "Client:", client.title + + def show_schedule(self): + + print "##### Daily Pseudo Schedule:" + + daily_schedule = self.db.get_daily_schedule() + + for i , entry in enumerate(daily_schedule): + + print "+++++", str(i + 1)+".", entry[11], entry[8], entry[6], " - ", entry[3] + + def exit_app(self): + + print "Exiting Pseudo TV & cleaning up." + + for i in self.MEDIA: + + del i + + self.MEDIA = None + + self.controller = None + + self.db = None + + sleep(1) if __name__ == '__main__': - pseudo_channel = PseudoChannel() + pseudo_channel = PseudoChannel() - #pseudo_channel.db.create_tables() + #pseudo_channel.db.create_tables() - #pseudo_channel.update_db() + #pseudo_channel.update_db() - #pseudo_channel.update_schedule() + #pseudo_channel.update_schedule() - #pseudo_channel.generate_daily_schedule() + #pseudo_channel.generate_daily_schedule() - parser = argparse.ArgumentParser( - description="Pseudo Channel for Plex. Update pseduo_config.py & pseudo_schedule.xml before this step.", - usage="PseudoChannel.py [-u] update local db with plex db [-xml] update db with xml schedule data [-g] generate daily schedule [-r] run the app" + parser = argparse.ArgumentParser( + description="Pseudo Channel for Plex. Update pseduo_config.py & pseudo_schedule.xml before this step.", + usage="PseudoChannel.py [-u] update local db with plex db [-xml] update db " \ + "with xml schedule data [-g] generate daily schedule [-r] run the app" ) - parser.add_argument('-u', action='store_true') - parser.add_argument('-xml', action='store_true') - parser.add_argument('-g', action='store_true') - parser.add_argument('-r', action='store_true') + ''' + * + * Primary arguments: "python PseudoChannel.py -u -xml -g -r" + * + ''' - ''' - * - * Show connected clients: "python PseudoChannel.py -u -xml -g -r" - * - ''' - parser.add_argument('-sc', action='store_true') + parser.add_argument('-u', action='store_true') + parser.add_argument('-xml', action='store_true') + parser.add_argument('-g', action='store_true') + parser.add_argument('-r', action='store_true') - globals().update(vars(parser.parse_args())) + ''' + * + * Show connected clients: "python PseudoChannel.py -c" + * + ''' + parser.add_argument('-c', action='store_true') - args = parser.parse_args() + ''' + * + * Show schedule (daily): "python PseudoChannel.py -s" + * + ''' + parser.add_argument('-s', action='store_true') - print(args) + globals().update(vars(parser.parse_args())) - if args.u: + args = parser.parse_args() - pseudo_channel.update_db() + #print(args) - if args.xml: + if args.u: - pseudo_channel.update_schedule() + pseudo_channel.update_db() - if args.g: + if args.xml: - pseudo_channel.generate_daily_schedule() + pseudo_channel.update_schedule() - if args.r: + if args.g: - try: + pseudo_channel.generate_daily_schedule() - print "++++ Running TV Controller" - - """Every minute on the minute check the DB startTimes of all media to - determine whether or not to play. Also, check the now_time to - see if it's midnight (or 23.59), if so then generate a new daily_schedule - - """ - while True: + if args.c: - now = datetime.datetime.now() + pseudo_channel.show_clients() - now_time = now.time() + if args.s: - if now_time == time(23,59): + pseudo_channel.show_schedule() - pseudo_channel.generate_daily_schedule() + if args.r: - pseudo_channel.controller.tv_controller(pseudo_channel.db.get_daily_schedule()) + try: - t = datetime.datetime.utcnow() + print "##### Running TV Controller" + print "+++++ To run this in the background:" + print "+++++", "screen -d -m bash -c 'python PseudoChannel.py -r; exec sh'" + + """Every minute on the minute check the DB startTimes of all media to + determine whether or not to play. Also, check the now_time to + see if it's midnight (or 23.59), if so then generate a new daily_schedule + + """ + while True: - sleeptime = 60 - (t.second + t.microsecond/1000000.0) + now = datetime.datetime.now() - sleep(sleeptime) + now_time = now.time() - except KeyboardInterrupt, e: + if now_time == time(23,59): - pass - + pseudo_channel.generate_daily_schedule() + + pseudo_channel.controller.tv_controller(pseudo_channel.db.get_daily_schedule()) + + t = datetime.datetime.utcnow() + + sleeptime = 60 - (t.second + t.microsecond/1000000.0) + + sleep(sleeptime) + + except KeyboardInterrupt, e: + + pseudo_channel.exit_app() + + del pseudo_channel + diff --git a/pseudo_config.py b/pseudo_config.py index bd79793..07f3c50 100644 --- a/pseudo_config.py +++ b/pseudo_config.py @@ -1,23 +1,24 @@ -#!/usr/bin/python +#!/usr/bin/env python + """ - 1) Create a file outside of this proj dir called "plex_token.py": + 1) Create a file outside of this proj dir called "plex_token.py": - touch ../plex_token.py - - 2) add this line to the newly created file: + touch ../plex_token.py + + 2) add this line to the newly created file: - token = 'your plex token' + token = 'your plex token' - 3) Edit the "basurl" variable below to point to your Plex server + 3) Edit the "basurl" variable below to point to your Plex server - 4) Edit the "plexClients" variable to include the name of your plex client(s) this app will control. + 4) Edit the "plexClients" variable to include the name of your plex client(s) this app will control. - 5) Edit the "plexLibraries" variable to remap your specific library names to the app specific names. - ...for instance, if your Plex "Movies" are located in your Plex library as "Films", update that - line so it looks like: + 5) Edit the "plexLibraries" variable to remap your specific library names to the app specific names. + ...for instance, if your Plex "Movies" are located in your Plex library as "Films", update that + line so it looks like: - "Movies" : ["Films"], - + "Movies" : ["Films"], + """ import os, sys @@ -36,8 +37,8 @@ token = plex_token.token plexClients = ['RasPlex'] plexLibraries = { - "TV Shows" : ["TV Shows"], - "Movies" : ["Movies"], - "Music" : ["Music"], - "Commercials" : ["Commercials"], + "TV Shows" : ["TV Shows"], + "Movies" : ["Movies"], + "Music" : ["Music"], + "Commercials" : ["Commercials"], } diff --git a/pseudo_schedule.xml b/pseudo_schedule.xml index f5e7ac8..9c0e60f 100644 --- a/pseudo_schedule.xml +++ b/pseudo_schedule.xml @@ -1,65 +1,65 @@ - - - - - - + + + + + + - - + + - - - - + + + + - - + + - + - - - - - + + + + + - + - - + + - - + + - + - - + + - + - - + + - - - - - + + + + + - - - + + + @@ -92,7 +92,7 @@ - + - + diff --git a/src/Commercial.py b/src/Commercial.py index 10b9a92..254c4da 100644 --- a/src/Commercial.py +++ b/src/Commercial.py @@ -1,8 +1,10 @@ +#!/usr/bin/env python + from Media import Media class Commercial(Media): - """Inherits Media. + """Inherits Media. Attributes: section_type: The type of library this is (i.e. "TV Shows") @@ -14,27 +16,27 @@ class Commercial(Media): is_strict_time: If strict time, then anchor to "natural_start_time" """ - def __init__( - self, - section_type, - title, - natural_start_time, - natural_end_time, - duration, - day_of_week, - is_strict_time, - time_shift, - overlap_max - ): + def __init__( + self, + section_type, + title, + natural_start_time, + natural_end_time, + duration, + day_of_week, + is_strict_time, + time_shift, + overlap_max + ): - super(Commercial, self).__init__( - section_type, - title, - natural_start_time, - natural_end_time, - duration, - day_of_week, - is_strict_time, - time_shift, - overlap_max - ) + super(Commercial, self).__init__( + section_type, + title, + natural_start_time, + natural_end_time, + duration, + day_of_week, + is_strict_time, + time_shift, + overlap_max + ) diff --git a/src/Episode.py b/src/Episode.py index 3c579d5..a9f902e 100644 --- a/src/Episode.py +++ b/src/Episode.py @@ -1,8 +1,10 @@ +#!/usr/bin/env python + from Media import Media class Episode(Media): - """Inherits Media. + """Inherits Media. Attributes: section_type: The type of library this is (i.e. "TV Shows") @@ -13,38 +15,38 @@ class Episode(Media): day_of_week: When the content is scheduled to play is_strict_time: If strict time, then anchor to "natural_start_time" show_series_title: The series title (i.e. "Friends") - episode_number: The episode number in the Season - season_number: The number of season in the series. + episode_number: The episode number in the Season + season_number: The number of season in the series. """ - def __init__( - self, - section_type, - title, - natural_start_time, - natural_end_time, - duration, - day_of_week, - is_strict_time, - time_shift, - overlap_max, - show_series_title, - episode_number, - season_number - ): + def __init__( + self, + section_type, + title, + natural_start_time, + natural_end_time, + duration, + day_of_week, + is_strict_time, + time_shift, + overlap_max, + show_series_title, + episode_number, + season_number + ): - super(Episode, self).__init__( - section_type, - title, - natural_start_time, - natural_end_time, - duration, - day_of_week, - is_strict_time, - time_shift, - overlap_max - ) + super(Episode, self).__init__( + section_type, + title, + natural_start_time, + natural_end_time, + duration, + day_of_week, + is_strict_time, + time_shift, + overlap_max + ) - self.show_series_title = show_series_title - self.episode_number = episode_number - self.season_number = season_number + self.show_series_title = show_series_title + self.episode_number = episode_number + self.season_number = season_number diff --git a/src/Media.py b/src/Media.py index 66e72d7..992261f 100644 --- a/src/Media.py +++ b/src/Media.py @@ -1,13 +1,15 @@ +#!/usr/bin/env python + """ *** Inherited by Commercial, Episode & Movie """ class Media(object): - plex_server_url = '' - plex_server_token = '' - media_image = '' + plex_server_url = '' + plex_server_token = '' + media_image = '' - """A base class for media objects. + """A base class for media objects. Attributes: section_type: The type of library this is (i.e. "TV Shows") @@ -19,29 +21,29 @@ class Media(object): is_strict_time: If strict time, then anchor to "natural_start_time" """ - def __init__( - self, - section_type, - title, - natural_start_time, - natural_end_time, - duration, - day_of_week, - is_strict_time, - time_shift, - overlap_max - ): + def __init__( + self, + section_type, + title, + natural_start_time, + natural_end_time, + duration, + day_of_week, + is_strict_time, + time_shift, + overlap_max + ): - self.section_type = section_type - self.title = title - self.natural_start_time = natural_start_time - self.natural_end_time = natural_end_time - self.duration = duration - self.day_of_week = day_of_week - self.is_strict_time = is_strict_time - self.time_shift = time_shift - self.overlap_max = overlap_max + self.section_type = section_type + self.title = title + self.natural_start_time = natural_start_time + self.natural_end_time = natural_end_time + self.duration = duration + self.day_of_week = day_of_week + self.is_strict_time = is_strict_time + self.time_shift = time_shift + self.overlap_max = overlap_max - self.start_time = natural_start_time - self.end_time = natural_end_time + self.start_time = natural_start_time + self.end_time = natural_end_time diff --git a/src/Movie.py b/src/Movie.py index a8258b0..d2975d8 100644 --- a/src/Movie.py +++ b/src/Movie.py @@ -1,8 +1,10 @@ +#!/usr/bin/env python + from Media import Media class Movie(Media): - """Inherits Media. + """Inherits Media. Attributes: section_type: The type of library this is (i.e. "TV Shows") @@ -14,27 +16,27 @@ class Movie(Media): is_strict_time: If strict time, then anchor to "natural_start_time" """ - def __init__( - self, - section_type, - title, - natural_start_time, - natural_end_time, - duration, - day_of_week, - is_strict_time, - time_shift, - overlap_max - ): + def __init__( + self, + section_type, + title, + natural_start_time, + natural_end_time, + duration, + day_of_week, + is_strict_time, + time_shift, + overlap_max + ): - super(Movie, self).__init__( - section_type, - title, - natural_start_time, - natural_end_time, - duration, - day_of_week, - is_strict_time, - time_shift, - overlap_max - ) + super(Movie, self).__init__( + section_type, + title, + natural_start_time, + natural_end_time, + duration, + day_of_week, + is_strict_time, + time_shift, + overlap_max + ) diff --git a/src/Music.py b/src/Music.py index 0ac53bb..4b8ee25 100644 --- a/src/Music.py +++ b/src/Music.py @@ -1,8 +1,10 @@ +#!/usr/bin/env python + from Media import Media class Music(Media): - """Inherits Media. + """Inherits Media. Attributes: section_type: The type of library this is (i.e. "TV Shows") @@ -14,27 +16,27 @@ class Music(Media): is_strict_time: If strict time, then anchor to "natural_start_time" """ - def __init__( - self, - section_type, - title, - natural_start_time, - natural_end_time, - duration, - day_of_week, - is_strict_time, - time_shift, - overlap_max - ): + def __init__( + self, + section_type, + title, + natural_start_time, + natural_end_time, + duration, + day_of_week, + is_strict_time, + time_shift, + overlap_max + ): - super(Music, self).__init__( - section_type, - title, - natural_start_time, - natural_end_time, - duration, - day_of_week, - is_strict_time, - time_shift, - overlap_max - ) + super(Music, self).__init__( + section_type, + title, + natural_start_time, + natural_end_time, + duration, + day_of_week, + is_strict_time, + time_shift, + overlap_max + ) diff --git a/src/PseudoChannelDatabase.py b/src/PseudoChannelDatabase.py index bed59f8..5391edd 100644 --- a/src/PseudoChannelDatabase.py +++ b/src/PseudoChannelDatabase.py @@ -1,472 +1,474 @@ +#!/usr/bin/env python + import sqlite3 import datetime import time class PseudoChannelDatabase(): - def __init__(self, db): + def __init__(self, db): - self.db = db + self.db = db - self.conn = sqlite3.connect(self.db) + self.conn = sqlite3.connect(self.db) - self.cursor = self.conn.cursor() + self.cursor = self.conn.cursor() - """Database functions. + """Database functions. - Utilities, etc. - """ + Utilities, etc. + """ - def create_tables(self): + def create_tables(self): - self.cursor.execute('CREATE TABLE IF NOT EXISTS ' - 'movies(id INTEGER PRIMARY KEY AUTOINCREMENT, ' - 'unix INTEGER, mediaID INTEGER, title TEXT, duration INTEGER, lastPlayedDate TEXT)') + self.cursor.execute('CREATE TABLE IF NOT EXISTS ' + 'movies(id INTEGER PRIMARY KEY AUTOINCREMENT, ' + 'unix INTEGER, mediaID INTEGER, title TEXT, duration INTEGER, lastPlayedDate TEXT)') - self.cursor.execute('CREATE TABLE IF NOT EXISTS ' - 'videos(id INTEGER PRIMARY KEY AUTOINCREMENT, ' - 'unix INTEGER, mediaID INTEGER, title TEXT, duration INTEGER)') + self.cursor.execute('CREATE TABLE IF NOT EXISTS ' + 'videos(id INTEGER PRIMARY KEY AUTOINCREMENT, ' + 'unix INTEGER, mediaID INTEGER, title TEXT, duration INTEGER)') - self.cursor.execute('CREATE TABLE IF NOT EXISTS ' - 'music(id INTEGER PRIMARY KEY AUTOINCREMENT, ' - 'unix INTEGER, mediaID INTEGER, title TEXT, duration INTEGER)') + self.cursor.execute('CREATE TABLE IF NOT EXISTS ' + 'music(id INTEGER PRIMARY KEY AUTOINCREMENT, ' + 'unix INTEGER, mediaID INTEGER, title TEXT, duration INTEGER)') - self.cursor.execute('CREATE TABLE IF NOT EXISTS ' - 'shows(id INTEGER PRIMARY KEY AUTOINCREMENT, ' - 'unix INTEGER, mediaID INTEGER, title TEXT, duration INTEGER, ' - 'lastEpisodeTitle TEXT, fullImageURL TEXT)') + self.cursor.execute('CREATE TABLE IF NOT EXISTS ' + 'shows(id INTEGER PRIMARY KEY AUTOINCREMENT, ' + 'unix INTEGER, mediaID INTEGER, title TEXT, duration INTEGER, ' + 'lastEpisodeTitle TEXT, fullImageURL TEXT)') - self.cursor.execute('CREATE TABLE IF NOT EXISTS ' - 'episodes(id INTEGER PRIMARY KEY AUTOINCREMENT, ' - 'unix INTEGER, mediaID INTEGER, title TEXT, duration INTEGER, ' - 'episodeNumber INTEGER, seasonNumber INTEGER, showTitle TEXT)') + self.cursor.execute('CREATE TABLE IF NOT EXISTS ' + 'episodes(id INTEGER PRIMARY KEY AUTOINCREMENT, ' + 'unix INTEGER, mediaID INTEGER, title TEXT, duration INTEGER, ' + 'episodeNumber INTEGER, seasonNumber INTEGER, showTitle TEXT)') - self.cursor.execute('CREATE TABLE IF NOT EXISTS ' - 'commercials(id INTEGER PRIMARY KEY AUTOINCREMENT, unix INTEGER, ' - 'mediaID INTEGER, title TEXT, duration INTEGER)') + self.cursor.execute('CREATE TABLE IF NOT EXISTS ' + 'commercials(id INTEGER PRIMARY KEY AUTOINCREMENT, unix INTEGER, ' + 'mediaID INTEGER, title TEXT, duration INTEGER)') - self.cursor.execute('CREATE TABLE IF NOT EXISTS ' - 'schedule(id INTEGER PRIMARY KEY AUTOINCREMENT, unix INTEGER, ' - 'mediaID INTEGER, title TEXT, duration INTEGER, startTime INTEGER, ' - 'endTime INTEGER, dayOfWeek TEXT, startTimeUnix INTEGER, section TEXT, ' - 'strictTime TEXT, timeShift TEXT, overlapMax TEXT)') + self.cursor.execute('CREATE TABLE IF NOT EXISTS ' + 'schedule(id INTEGER PRIMARY KEY AUTOINCREMENT, unix INTEGER, ' + 'mediaID INTEGER, title TEXT, duration INTEGER, startTime INTEGER, ' + 'endTime INTEGER, dayOfWeek TEXT, startTimeUnix INTEGER, section TEXT, ' + 'strictTime TEXT, timeShift TEXT, overlapMax TEXT)') - self.cursor.execute('CREATE TABLE IF NOT EXISTS ' - 'daily_schedule(id INTEGER PRIMARY KEY AUTOINCREMENT, unix INTEGER, ' - 'mediaID INTEGER, title TEXT, episodeNumber INTEGER, seasonNumber INTEGER, ' - 'showTitle TEXT, duration INTEGER, startTime INTEGER, endTime INTEGER, ' - 'dayOfWeek TEXT, sectionType TEXT)') + self.cursor.execute('CREATE TABLE IF NOT EXISTS ' + 'daily_schedule(id INTEGER PRIMARY KEY AUTOINCREMENT, unix INTEGER, ' + 'mediaID INTEGER, title TEXT, episodeNumber INTEGER, seasonNumber INTEGER, ' + 'showTitle TEXT, duration INTEGER, startTime INTEGER, endTime INTEGER, ' + 'dayOfWeek TEXT, sectionType TEXT)') - self.cursor.execute('CREATE TABLE IF NOT EXISTS ' - 'app_settings(id INTEGER PRIMARY KEY AUTOINCREMENT, version TEXT)') + self.cursor.execute('CREATE TABLE IF NOT EXISTS ' + 'app_settings(id INTEGER PRIMARY KEY AUTOINCREMENT, version TEXT)') - #index - self.cursor.execute('CREATE UNIQUE INDEX IF NOT EXISTS idx_episode_title ON episodes (title);') + #index + self.cursor.execute('CREATE UNIQUE INDEX IF NOT EXISTS idx_episode_title ON episodes (title);') - self.cursor.execute('CREATE UNIQUE INDEX IF NOT EXISTS idx_movie_title ON movies (title);') + self.cursor.execute('CREATE UNIQUE INDEX IF NOT EXISTS idx_movie_title ON movies (title);') - self.cursor.execute('CREATE UNIQUE INDEX IF NOT EXISTS idx_movie_title ON videos (title);') + self.cursor.execute('CREATE UNIQUE INDEX IF NOT EXISTS idx_movie_title ON videos (title);') - self.cursor.execute('CREATE UNIQUE INDEX IF NOT EXISTS idx_music_title ON music (title);') + self.cursor.execute('CREATE UNIQUE INDEX IF NOT EXISTS idx_music_title ON music (title);') - self.cursor.execute('CREATE UNIQUE INDEX IF NOT EXISTS idx_commercial_title ON commercials (title);') + self.cursor.execute('CREATE UNIQUE INDEX IF NOT EXISTS idx_commercial_title ON commercials (title);') - self.cursor.execute('CREATE UNIQUE INDEX IF NOT EXISTS idx_settings_version ON app_settings (version);') + self.cursor.execute('CREATE UNIQUE INDEX IF NOT EXISTS idx_settings_version ON app_settings (version);') - """Setting Basic Settings - - """ - try: - self.cursor.execute("INSERT OR REPLACE INTO app_settings " - "(version) VALUES (?)", - ("0.1",)) + """Setting Basic Settings + + """ + try: + self.cursor.execute("INSERT OR REPLACE INTO app_settings " + "(version) VALUES (?)", + ("0.1",)) - self.conn.commit() - # Catch the exception - except Exception as e: - # Roll back any change if something goes wrong - self.conn.rollback() - raise e - - def drop_db(self): - - pass - - def drop_schedule(self): - - pass - - def remove_all_scheduled_items(self): - - sql = "DELETE FROM schedule WHERE id > -1" - - self.cursor.execute(sql) - - self.conn.commit() - - def remove_all_daily_scheduled_items(self): - - sql = "DELETE FROM daily_schedule WHERE id > -1" - - self.cursor.execute(sql) - - self.conn.commit() - - """Database functions. - - Setters, etc. - """ - - def add_movies_to_db(self, mediaID, title, duration): - unix = int(time.time()) - try: - self.cursor.execute("INSERT OR REPLACE INTO movies " - "(unix, mediaID, title, duration) VALUES (?, ?, ?, ?)", - (unix, mediaID, title, duration)) - - self.conn.commit() - # Catch the exception - except Exception as e: - # Roll back any change if something goes wrong - self.conn.rollback() - raise e - - def add_videos_to_db(self, mediaID, title, duration): - unix = int(time.time()) - try: - self.cursor.execute("INSERT OR REPLACE INTO videos " - "(unix, mediaID, title, duration) VALUES (?, ?, ?, ?)", - (unix, mediaID, title, duration)) - - self.conn.commit() - # Catch the exception - except Exception as e: - # Roll back any change if something goes wrong - self.conn.rollback() - raise e - - def add_shows_to_db(self, mediaID, title, duration, lastEpisodeTitle, fullImageURL): - unix = int(time.time()) - try: - self.cursor.execute("INSERT OR REPLACE INTO shows " - "(unix, mediaID, title, duration, lastEpisodeTitle, fullImageURL) VALUES (?, ?, ?, ?, ?, ?)", - (unix, mediaID, title, duration, lastEpisodeTitle, fullImageURL)) - self.conn.commit() - # Catch the exception - except Exception as e: - # Roll back any change if something goes wrong - self.conn.rollback() - raise e - - def add_episodes_to_db(self, mediaID, title, duration, episodeNumber, seasonNumber, showTitle): - unix = int(time.time()) - try: - self.cursor.execute("INSERT OR REPLACE INTO episodes " - "(unix, mediaID, title, duration, episodeNumber, seasonNumber, showTitle) VALUES (?, ?, ?, ?, ?, ?, ?)", - (unix, mediaID, title, duration, episodeNumber, seasonNumber, showTitle)) - self.conn.commit() - # Catch the exception - except Exception as e: - # Roll back any change if something goes wrong - self.conn.rollback() - raise e - - def add_commercials_to_db(self, mediaID, title, duration): - unix = int(time.time()) - try: - self.cursor.execute("INSERT OR REPLACE INTO commercials " - "(unix, mediaID, title, duration) VALUES (?, ?, ?, ?)", - (unix, mediaID, title, duration)) - self.conn.commit() - # Catch the exception - except Exception as e: - # Roll back any change if something goes wrong - self.conn.rollback() - raise e - - def add_schedule_to_db(self, mediaID, title, duration, startTime, endTime, dayOfWeek, startTimeUnix, section, strictTime, timeShift, overlapMax): - unix = int(time.time()) - try: - self.cursor.execute("INSERT OR REPLACE INTO schedule " - "(unix, mediaID, title, duration, startTime, endTime, dayOfWeek, startTimeUnix, section, strictTime, timeShift, overlapMax) " - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", - (unix, mediaID, title, duration, startTime, endTime, dayOfWeek, startTimeUnix, section, strictTime, timeShift, overlapMax)) - self.conn.commit() - # Catch the exception - except Exception as e: - # Roll back any change if something goes wrong - self.conn.rollback() - raise e - - def add_daily_schedule_to_db( - self, - mediaID, - title, - episodeNumber, - seasonNumber, - showTitle, - duration, - startTime, - endTime, - dayOfWeek, - sectionType - ): - - unix = int(time.time()) - - try: - - self.cursor.execute("INSERT OR REPLACE INTO daily_schedule " - "(unix, mediaID, title, episodeNumber, seasonNumber, " - "showTitle, duration, startTime, endTime, dayOfWeek, sectionType) " - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", - ( - unix, - mediaID, - title, - episodeNumber, - seasonNumber, - showTitle, - duration, - startTime, - endTime, - dayOfWeek, - sectionType - )) + self.conn.commit() + # Catch the exception + except Exception as e: + # Roll back any change if something goes wrong + self.conn.rollback() + raise e + + def drop_db(self): + + pass + + def drop_schedule(self): + + pass + + def remove_all_scheduled_items(self): + + sql = "DELETE FROM schedule WHERE id > -1" + + self.cursor.execute(sql) + + self.conn.commit() + + def remove_all_daily_scheduled_items(self): + + sql = "DELETE FROM daily_schedule WHERE id > -1" + + self.cursor.execute(sql) + + self.conn.commit() + + """Database functions. + + Setters, etc. + """ + + def add_movies_to_db(self, mediaID, title, duration): + unix = int(time.time()) + try: + self.cursor.execute("INSERT OR REPLACE INTO movies " + "(unix, mediaID, title, duration) VALUES (?, ?, ?, ?)", + (unix, mediaID, title, duration)) + + self.conn.commit() + # Catch the exception + except Exception as e: + # Roll back any change if something goes wrong + self.conn.rollback() + raise e + + def add_videos_to_db(self, mediaID, title, duration): + unix = int(time.time()) + try: + self.cursor.execute("INSERT OR REPLACE INTO videos " + "(unix, mediaID, title, duration) VALUES (?, ?, ?, ?)", + (unix, mediaID, title, duration)) + + self.conn.commit() + # Catch the exception + except Exception as e: + # Roll back any change if something goes wrong + self.conn.rollback() + raise e + + def add_shows_to_db(self, mediaID, title, duration, lastEpisodeTitle, fullImageURL): + unix = int(time.time()) + try: + self.cursor.execute("INSERT OR REPLACE INTO shows " + "(unix, mediaID, title, duration, lastEpisodeTitle, fullImageURL) VALUES (?, ?, ?, ?, ?, ?)", + (unix, mediaID, title, duration, lastEpisodeTitle, fullImageURL)) + self.conn.commit() + # Catch the exception + except Exception as e: + # Roll back any change if something goes wrong + self.conn.rollback() + raise e + + def add_episodes_to_db(self, mediaID, title, duration, episodeNumber, seasonNumber, showTitle): + unix = int(time.time()) + try: + self.cursor.execute("INSERT OR REPLACE INTO episodes " + "(unix, mediaID, title, duration, episodeNumber, seasonNumber, showTitle) VALUES (?, ?, ?, ?, ?, ?, ?)", + (unix, mediaID, title, duration, episodeNumber, seasonNumber, showTitle)) + self.conn.commit() + # Catch the exception + except Exception as e: + # Roll back any change if something goes wrong + self.conn.rollback() + raise e + + def add_commercials_to_db(self, mediaID, title, duration): + unix = int(time.time()) + try: + self.cursor.execute("INSERT OR REPLACE INTO commercials " + "(unix, mediaID, title, duration) VALUES (?, ?, ?, ?)", + (unix, mediaID, title, duration)) + self.conn.commit() + # Catch the exception + except Exception as e: + # Roll back any change if something goes wrong + self.conn.rollback() + raise e + + def add_schedule_to_db(self, mediaID, title, duration, startTime, endTime, dayOfWeek, startTimeUnix, section, strictTime, timeShift, overlapMax): + unix = int(time.time()) + try: + self.cursor.execute("INSERT OR REPLACE INTO schedule " + "(unix, mediaID, title, duration, startTime, endTime, dayOfWeek, startTimeUnix, section, strictTime, timeShift, overlapMax) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + (unix, mediaID, title, duration, startTime, endTime, dayOfWeek, startTimeUnix, section, strictTime, timeShift, overlapMax)) + self.conn.commit() + # Catch the exception + except Exception as e: + # Roll back any change if something goes wrong + self.conn.rollback() + raise e + + def add_daily_schedule_to_db( + self, + mediaID, + title, + episodeNumber, + seasonNumber, + showTitle, + duration, + startTime, + endTime, + dayOfWeek, + sectionType + ): + + unix = int(time.time()) + + try: + + self.cursor.execute("INSERT OR REPLACE INTO daily_schedule " + "(unix, mediaID, title, episodeNumber, seasonNumber, " + "showTitle, duration, startTime, endTime, dayOfWeek, sectionType) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + ( + unix, + mediaID, + title, + episodeNumber, + seasonNumber, + showTitle, + duration, + startTime, + endTime, + dayOfWeek, + sectionType + )) - self.conn.commit() + self.conn.commit() - # Catch the exception - except Exception as e: + # Catch the exception + except Exception as e: - # Roll back any change if something goes wrong + # Roll back any change if something goes wrong - self.conn.rollback() + self.conn.rollback() - raise e + raise e - def add_media_to_daily_schedule(self, media): + def add_media_to_daily_schedule(self, media): - print "#### Adding media to db", media.title, media.start_time + print "#### Adding media to db", media.title, media.start_time - self.add_daily_schedule_to_db( - 0, - media.title, - media.episode_number if media.__class__.__name__ == "Episode" else 0, - media.season_number if media.__class__.__name__ == "Episode" else 0, - media.show_series_title if media.__class__.__name__ == "Episode" else '', - media.duration, - media.start_time, - media.end_time, - media.day_of_week, - media.section_type - ) + self.add_daily_schedule_to_db( + 0, + media.title, + media.episode_number if media.__class__.__name__ == "Episode" else 0, + media.season_number if media.__class__.__name__ == "Episode" else 0, + media.show_series_title if media.__class__.__name__ == "Episode" else '', + media.duration, + media.start_time, + media.end_time, + media.day_of_week, + media.section_type + ) - """Database functions. + """Database functions. - Getters, etc. - """ - def get_media(self, title, mediaType): + Getters, etc. + """ + def get_media(self, title, mediaType): - media = mediaType + media = mediaType - sql = "SELECT * FROM "+media+" WHERE (title LIKE ?) COLLATE NOCASE" - self.cursor.execute(sql, ("%"+title+"%", )) - media_item = self.cursor.fetchone() + sql = "SELECT * FROM "+media+" WHERE (title LIKE ?) COLLATE NOCASE" + self.cursor.execute(sql, ("%"+title+"%", )) + media_item = self.cursor.fetchone() - return media_item + return media_item - def get_schedule(self): + def get_schedule(self): - self.cursor.execute("SELECT * FROM schedule ORDER BY datetime(startTimeUnix) ASC") + self.cursor.execute("SELECT * FROM schedule ORDER BY datetime(startTimeUnix) ASC") - datalist = list(self.cursor.fetchall()) + datalist = list(self.cursor.fetchall()) - return datalist + return datalist - def get_daily_schedule(self): + def get_daily_schedule(self): - self.cursor.execute("SELECT * FROM daily_schedule ORDER BY datetime(startTime) ASC") + self.cursor.execute("SELECT * FROM daily_schedule ORDER BY datetime(startTime) ASC") - datalist = list(self.cursor.fetchall()) + datalist = list(self.cursor.fetchall()) - return datalist + return datalist - def get_movie(self, title): + def get_movie(self, title): - media = "movies" + media = "movies" - return self.get_media(title, media) + return self.get_media(title, media) - def get_shows(self, title): + def get_shows(self, title): - media = "shows" + media = "shows" - return self.get_media(title, media) + return self.get_media(title, media) - def get_music(self, title): + def get_music(self, title): - media = "music" + media = "music" - return self.get_media(title, media) + return self.get_media(title, media) - def get_video(self, title): + def get_video(self, title): - media = "videos" + media = "videos" - return self.get_media(title, media) + return self.get_media(title, media) - def get_episodes(self, title): + def get_episodes(self, title): - media = "episodes" + media = "episodes" - return self.get_media(title, media) + return self.get_media(title, media) - def update_shows_table_with_last_episode(self, showTitle, lastEpisodeTitle): + def update_shows_table_with_last_episode(self, showTitle, lastEpisodeTitle): - sql1 = "UPDATE shows SET lastEpisodeTitle = ? WHERE title LIKE ? COLLATE NOCASE" + sql1 = "UPDATE shows SET lastEpisodeTitle = ? WHERE title LIKE ? COLLATE NOCASE" - self.cursor.execute(sql1, (lastEpisodeTitle, showTitle, )) + self.cursor.execute(sql1, (lastEpisodeTitle, showTitle, )) - self.conn.commit() + self.conn.commit() - def get_first_episode(self, tvshow): + def get_first_episode(self, tvshow): - sql = ("SELECT id, unix, mediaID, title, duration, MIN(episodeNumber), MIN(seasonNumber), " - "showTitle FROM episodes WHERE ( showTitle LIKE ?) COLLATE NOCASE") + sql = ("SELECT id, unix, mediaID, title, duration, MIN(episodeNumber), MIN(seasonNumber), " + "showTitle FROM episodes WHERE ( showTitle LIKE ?) COLLATE NOCASE") - self.cursor.execute(sql, (tvshow, )) + self.cursor.execute(sql, (tvshow, )) - first_episode = self.cursor.fetchone() + first_episode = self.cursor.fetchone() - return first_episode + return first_episode - ''' - * - * When incrementing episodes in a series I am advancing by "id" - * - ''' - def get_episode_id(self, episodeTitle): + ''' + * + * When incrementing episodes in a series I am advancing by "id" + * + ''' + def get_episode_id(self, episodeTitle): - sql = "SELECT id FROM episodes WHERE ( title LIKE ?) COLLATE NOCASE" + sql = "SELECT id FROM episodes WHERE ( title LIKE ?) COLLATE NOCASE" - self.cursor.execute(sql, (episodeTitle, )) + self.cursor.execute(sql, (episodeTitle, )) - episode_id = self.cursor.fetchone() + episode_id = self.cursor.fetchone() - return episode_id + return episode_id - def get_random_episode(self): + def get_random_episode(self): - sql = "SELECT * FROM episodes WHERE id IN (SELECT id FROM episodes ORDER BY RANDOM() LIMIT 1)" + sql = "SELECT * FROM episodes WHERE id IN (SELECT id FROM episodes ORDER BY RANDOM() LIMIT 1)" - self.cursor.execute(sql) + self.cursor.execute(sql) - return self.cursor.fetchone() + return self.cursor.fetchone() - def get_random_movie(self): + def get_random_movie(self): - sql = "SELECT * FROM movies WHERE id IN (SELECT id FROM movies ORDER BY RANDOM() LIMIT 1)" + sql = "SELECT * FROM movies WHERE id IN (SELECT id FROM movies ORDER BY RANDOM() LIMIT 1)" - self.cursor.execute(sql) + self.cursor.execute(sql) - return self.cursor.fetchone() + return self.cursor.fetchone() - def get_next_episode(self, series): + def get_next_episode(self, series): - #print(series) - ''' - * - * As a way of storing a "queue", I am storing the *next episode title in the "shows" table so I can - * determine what has been previously scheduled for each show - * - ''' - self.cursor.execute("SELECT lastEpisodeTitle FROM shows WHERE title LIKE ? COLLATE NOCASE", (series, )) + #print(series) + ''' + * + * As a way of storing a "queue", I am storing the *next episode title in the "shows" table so I can + * determine what has been previously scheduled for each show + * + ''' + self.cursor.execute("SELECT lastEpisodeTitle FROM shows WHERE title LIKE ? COLLATE NOCASE", (series, )) - last_title_list = self.cursor.fetchone() - ''' - * - * If the last episode stored in the "shows" table is empty, then this is probably a first run... - * - ''' - if last_title_list and last_title_list[0] == '': + last_title_list = self.cursor.fetchone() + ''' + * + * If the last episode stored in the "shows" table is empty, then this is probably a first run... + * + ''' + if last_title_list and last_title_list[0] == '': - ''' - * - * Find the first episode of the series - * - ''' - first_episode = self.get_first_episode(series) + ''' + * + * Find the first episode of the series + * + ''' + first_episode = self.get_first_episode(series) - first_episode_title = first_episode[3] + first_episode_title = first_episode[3] - #print(first_episode_title) - ''' - * - * Add this episdoe title to the "shows" table for the queue functionality to work - * - ''' - self.update_shows_table_with_last_episode(series, first_episode_title) + #print(first_episode_title) + ''' + * + * Add this episdoe title to the "shows" table for the queue functionality to work + * + ''' + self.update_shows_table_with_last_episode(series, first_episode_title) - return first_episode + return first_episode - elif last_title_list: - ''' - * - * The last episode stored in the "shows" table was not empty... get the next episode in the series - * - ''' - #print("First episode already set in shows, advancing episodes forward") + elif last_title_list: + ''' + * + * The last episode stored in the "shows" table was not empty... get the next episode in the series + * + ''' + #print("First episode already set in shows, advancing episodes forward") - #print(str(self.get_episode_id(last_title_list[0]))) + #print(str(self.get_episode_id(last_title_list[0]))) - """ - * - * If this isn't a first run, then grabbing the next episode by incrementing id - * - """ - sql = ("SELECT * FROM episodes WHERE ( id > "+str(self.get_episode_id(last_title_list[0])[0])+ - " AND showTitle LIKE ? ) ORDER BY seasonNumber LIMIT 1 COLLATE NOCASE") + """ + * + * If this isn't a first run, then grabbing the next episode by incrementing id + * + """ + sql = ("SELECT * FROM episodes WHERE ( id > "+str(self.get_episode_id(last_title_list[0])[0])+ + " AND showTitle LIKE ? ) ORDER BY seasonNumber LIMIT 1 COLLATE NOCASE") - self.cursor.execute(sql, (series, )) - ''' - * - * Try and advance to the next episode in the series, if it returns None then that means it reached the end... - * - ''' - next_episode = self.cursor.fetchone() + self.cursor.execute(sql, (series, )) + ''' + * + * Try and advance to the next episode in the series, if it returns None then that means it reached the end... + * + ''' + next_episode = self.cursor.fetchone() - if next_episode != None: + if next_episode != None: - #print(next_episode[3]) + #print(next_episode[3]) - self.update_shows_table_with_last_episode(series, next_episode[3]) + self.update_shows_table_with_last_episode(series, next_episode[3]) - return next_episode + return next_episode - else: + else: - print("Not grabbing next episode restarting series, series must be over. Restarting from episode 1.") + print("Not grabbing next episode restarting series, series must be over. Restarting from episode 1.") - first_episode = self.get_first_episode(series) + first_episode = self.get_first_episode(series) - self.update_shows_table_with_last_episode(series, first_episode[3]) + self.update_shows_table_with_last_episode(series, first_episode[3]) - return first_episode - - def get_commercials(self, title): + return first_episode + + def get_commercials(self, title): - media = "commercials" + media = "commercials" - sql = "SELECT * FROM "+media+" WHERE (title LIKE ?) COLLATE NOCASE" - self.cursor.execute(sql, (title, )) - datalist = list(self.cursor.fetchone()) - if datalist > 0: - print(datalist) + sql = "SELECT * FROM "+media+" WHERE (title LIKE ?) COLLATE NOCASE" + self.cursor.execute(sql, (title, )) + datalist = list(self.cursor.fetchone()) + if datalist > 0: + print(datalist) - return datalist + return datalist - else: + else: - return None + return None diff --git a/src/PseudoDailyScheduleController.py b/src/PseudoDailyScheduleController.py index 08e8448..dde5d78 100644 --- a/src/PseudoDailyScheduleController.py +++ b/src/PseudoDailyScheduleController.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python + from plexapi.server import PlexServer from datetime import datetime import sqlite3 @@ -10,328 +12,328 @@ import logging.handlers class PseudoDailyScheduleController(): - def __init__(self, server, token, clients): + def __init__(self, server, token, clients): - self.PLEX = PlexServer(server, token) + self.PLEX = PlexServer(server, token) - self.BASE_URL = server + self.BASE_URL = server - self.TOKEN = token + self.TOKEN = token - self.PLEX_CLIENTS = clients + self.PLEX_CLIENTS = clients - self.my_logger = logging.getLogger('MyLogger') - self.my_logger.setLevel(logging.DEBUG) + self.my_logger = logging.getLogger('MyLogger') + self.my_logger.setLevel(logging.DEBUG) - self.handler = logging.handlers.SysLogHandler(address = '/dev/log') + self.handler = logging.handlers.SysLogHandler(address = '/dev/log') - self.my_logger.addHandler(self.handler) + self.my_logger.addHandler(self.handler) - ''' - * - * Get the full image url (including plex token) from the local db. - * @param seriesTitle: case-unsensitive string of the series title - * @return string: full path of to the show image - * - ''' - def get_show_photo(self, section, title): + ''' + * + * Get the full image url (including plex token) from the local db. + * @param seriesTitle: case-unsensitive string of the series title + * @return string: full path of to the show image + * + ''' + def get_show_photo(self, section, title): - backgroundImagePath = None + backgroundImagePath = None - backgroundImgURL = '' + backgroundImgURL = '' - try: + try: - backgroundImagePath = self.PLEX.library.section(section).get(title) + backgroundImagePath = self.PLEX.library.section(section).get(title) - except: + except: - return backgroundImgURL + return backgroundImgURL - if backgroundImagePath != None and isinstance(backgroundImagePath.art, str): + if backgroundImagePath != None and isinstance(backgroundImagePath.art, str): - backgroundImgURL = self.BASE_URL+backgroundImagePath.art+"?X-Plex-Token="+self.TOKEN + backgroundImgURL = self.BASE_URL+backgroundImagePath.art+"?X-Plex-Token="+self.TOKEN - return backgroundImgURL + return backgroundImgURL - ''' - * - * Get the generated html for the .html file that is the schedule. - * ...This is used whenever a show starts or stops in order to add and remove various styles. - * @param currentTime: datetime object - * @param bgImageURL: str of the image used for the background - * @return string: the generated html content - * - ''' - def get_html_from_daily_schedule(self, currentTime, bgImageURL, datalist): + ''' + * + * Get the generated html for the .html file that is the schedule. + * ...This is used whenever a show starts or stops in order to add and remove various styles. + * @param currentTime: datetime object + * @param bgImageURL: str of the image used for the background + * @return string: the generated html content + * + ''' + def get_html_from_daily_schedule(self, currentTime, bgImageURL, datalist): - now = datetime.now() + now = datetime.now() - time = now.strftime("%B %d, %Y") + time = now.strftime("%B %d, %Y") - doc, tag, text, line = Doc( + doc, tag, text, line = Doc( - ).ttl() + ).ttl() - doc.asis('') + doc.asis('') - with tag('html'): + with tag('html'): - with tag('head'): + with tag('head'): - with tag('title'): + with tag('title'): - text(time + " - Daily Pseudo Schedule") + text(time + " - Daily Pseudo Schedule") - doc.asis('') - doc.asis('') + doc.asis('') + doc.asis('') - if bgImageURL != None: - doc.asis('') + if bgImageURL != None: + doc.asis('') - with tag('body'): + with tag('body'): - with tag('div', klass='container mt-3'): + with tag('div', klass='container mt-3'): - with tag('div', klass='row make-white'): + with tag('div', klass='row make-white'): - with tag('div'): + with tag('div'): - with tag('div'): + with tag('div'): - line('h1', "Daily Pseudo Schedule", klass='col-12 pl-0') + line('h1', "Daily Pseudo Schedule", klass='col-12 pl-0') - with tag('div'): + with tag('div'): - line('h3', time, klass='col-12 pl-1') + line('h3', time, klass='col-12 pl-1') - with tag('table', klass='col-12 table table-bordered table-hover'): + with tag('table', klass='col-12 table table-bordered table-hover'): - with tag('thead', klass='table-info'): - with tag('tr'): - with tag('th'): - text('#') - with tag('th'): - text('Type') - with tag('th'): - text('Series') - with tag('th'): - text('Title') - with tag('th'): - text('Start Time') + with tag('thead', klass='table-info'): + with tag('tr'): + with tag('th'): + text('#') + with tag('th'): + text('Type') + with tag('th'): + text('Series') + with tag('th'): + text('Title') + with tag('th'): + text('Start Time') - numberIncrease = 0 + numberIncrease = 0 - for row in datalist: + for row in datalist: - numberIncrease += 1 + numberIncrease += 1 - with tag('tbody'): + with tag('tbody'): - timeB = datetime.strptime(row[8], '%I:%M %p') + timeB = datetime.strptime(row[8], '%I:%M %p') - if currentTime == None: + if currentTime == None: - with tag('tr'): - with tag('th', scope='row'): - text(numberIncrease) - with tag('td'): - text(row[11]) - with tag('td'): - text(row[6]) - with tag('td'): - text(row[3]) - with tag('td'): - text(row[8]) + with tag('tr'): + with tag('th', scope='row'): + text(numberIncrease) + with tag('td'): + text(row[11]) + with tag('td'): + text(row[6]) + with tag('td'): + text(row[3]) + with tag('td'): + text(row[8]) - elif currentTime.hour == timeB.hour and currentTime.minute == timeB.minute: + elif currentTime.hour == timeB.hour and currentTime.minute == timeB.minute: - with tag('tr', klass='bg-info'): + with tag('tr', klass='bg-info'): - with tag('th', scope='row'): - text(numberIncrease) - with tag('td'): - text(row[11]) - with tag('td'): - text(row[6]) - with tag('td'): - text(row[3]) - with tag('td'): - text(row[8]) + with tag('th', scope='row'): + text(numberIncrease) + with tag('td'): + text(row[11]) + with tag('td'): + text(row[6]) + with tag('td'): + text(row[3]) + with tag('td'): + text(row[8]) - else: + else: - with tag('tr'): - with tag('th', scope='row'): - text(numberIncrease) - with tag('td'): - text(row[11]) - with tag('td'): - text(row[6]) - with tag('td'): - text(row[3]) - with tag('td'): - text(row[8]) + with tag('tr'): + with tag('th', scope='row'): + text(numberIncrease) + with tag('td'): + text(row[11]) + with tag('td'): + text(row[6]) + with tag('td'): + text(row[3]) + with tag('td'): + text(row[8]) - return doc.getvalue() + return doc.getvalue() - ''' - * - * Create 'schedules' dir & write the generated html to .html file. - * @param data: html string - * @return null - * - ''' - def write_schedule_to_file(self, data): + ''' + * + * Create 'schedules' dir & write the generated html to .html file. + * @param data: html string + * @return null + * + ''' + def write_schedule_to_file(self, data): - now = datetime.now() + now = datetime.now() - fileName = "index.html" + fileName = "index.html" - writepath = './schedules/' + writepath = './schedules/' - if not os.path.exists(writepath): + if not os.path.exists(writepath): - os.makedirs(writepath) + os.makedirs(writepath) - if os.path.exists(writepath+fileName): - - os.remove(writepath+fileName) + if os.path.exists(writepath+fileName): + + os.remove(writepath+fileName) - mode = 'a' if os.path.exists(writepath) else 'w' + mode = 'a' if os.path.exists(writepath) else 'w' - with open(writepath+fileName, mode) as f: + with open(writepath+fileName, mode) as f: - f.write(data) + f.write(data) - ''' - * - * Trigger "playMedia()" on the Python Plex API for specified media. - * @param mediaType: str: "TV Shows" - * @param mediaParentTitle: str: "Seinfeld" - * @param mediaTitle: str: "The Soup Nazi" - * @return null - * - ''' - def play_media(self, mediaType, mediaParentTitle, mediaTitle): + ''' + * + * Trigger "playMedia()" on the Python Plex API for specified media. + * @param mediaType: str: "TV Shows" + * @param mediaParentTitle: str: "Seinfeld" + * @param mediaTitle: str: "The Soup Nazi" + * @return null + * + ''' + def play_media(self, mediaType, mediaParentTitle, mediaTitle): - if mediaType == "TV Shows": + if mediaType == "TV Shows": - mediaItems = self.PLEX.library.section(mediaType).get(mediaParentTitle).episodes() + mediaItems = self.PLEX.library.section(mediaType).get(mediaParentTitle).episodes() - for item in mediaItems: + for item in mediaItems: - # print(part.title) + # print(part.title) - if item.title == mediaTitle: + if item.title == mediaTitle: - for client in self.PLEX_CLIENTS: + for client in self.PLEX_CLIENTS: - clientItem = self.PLEX.client(client) + clientItem = self.PLEX.client(client) - clientItem.playMedia(item) - - break + clientItem.playMedia(item) + + break - elif mediaType == "Movies": + elif mediaType == "Movies": - movie = self.PLEX.library.section(mediaType).get(mediaTitle) + movie = self.PLEX.library.section(mediaType).get(mediaTitle) - for client in self.PLEX_CLIENTS: + for client in self.PLEX_CLIENTS: - clientItem = self.PLEX.client(client) + clientItem = self.PLEX.client(client) - clientItem.playMedia(movie) + clientItem.playMedia(movie) - else: + else: - print("Not sure how to play {}".format(mediaType)) - - ''' - * - * If tv_controller() does not find a "startTime" for scheduled media, search for an "endTime" match for now time. - * ...This is useful for clearing the generated html schedule when media ends and there is a gap before the next media. - * @param null - * @return null - * - ''' - def check_for_end_time(self, datalist): + print("Not sure how to play {}".format(mediaType)) + + ''' + * + * If tv_controller() does not find a "startTime" for scheduled media, search for an "endTime" match for now time. + * ...This is useful for clearing the generated html schedule when media ends and there is a gap before the next media. + * @param null + * @return null + * + ''' + def check_for_end_time(self, datalist): - currentTime = datetime.now() + currentTime = datetime.now() - """c.execute("SELECT * FROM daily_schedule") + """c.execute("SELECT * FROM daily_schedule") - datalist = list(c.fetchall()) - """ - for row in datalist: + datalist = list(c.fetchall()) + """ + for row in datalist: - try: - - endTime = datetime.strptime(row[9], '%Y-%m-%d %H:%M:%S.%f') + try: + + endTime = datetime.strptime(row[9], '%Y-%m-%d %H:%M:%S.%f') - except ValueError: + except ValueError: - endTime = datetime.strptime(row[9], '%Y-%m-%d %H:%M:%S') + endTime = datetime.strptime(row[9], '%Y-%m-%d %H:%M:%S') - if currentTime.hour == endTime.hour: + if currentTime.hour == endTime.hour: - if currentTime.minute == endTime.minute: + if currentTime.minute == endTime.minute: - print("Ok end time found") + print("Ok end time found") - self.write_schedule_to_file(self.get_html_from_daily_schedule(None, None, datalist)) + self.write_schedule_to_file(self.get_html_from_daily_schedule(None, None, datalist)) - break - ''' - * - * Check DB / current time. If that matches a scheduled shows startTime then trigger play via Plex API - * @param null - * @return null - * - ''' - def tv_controller(self, datalist): + break + ''' + * + * Check DB / current time. If that matches a scheduled shows startTime then trigger play via Plex API + * @param null + * @return null + * + ''' + def tv_controller(self, datalist): - datalistLengthMonitor = 0; + datalistLengthMonitor = 0; - currentTime = datetime.now() + currentTime = datetime.now() - """c.execute("SELECT * FROM daily_schedule ORDER BY datetime(startTimeUnix) ASC") + """c.execute("SELECT * FROM daily_schedule ORDER BY datetime(startTimeUnix) ASC") - datalist = list(c.fetchall())""" + datalist = list(c.fetchall())""" - self.my_logger.debug('TV Controller') + self.my_logger.debug('TV Controller') - for row in datalist: + for row in datalist: - timeB = datetime.strptime(row[8], '%I:%M %p') + timeB = datetime.strptime(row[8], '%I:%M %p') - if currentTime.hour == timeB.hour: + if currentTime.hour == timeB.hour: - if currentTime.minute == timeB.minute: + if currentTime.minute == timeB.minute: - print("Starting Epsisode: " + row[3]) - print(row) + print("Starting Epsisode: " + row[3]) + print(row) - self.play_media(row[11], row[6], row[3]) + self.play_media(row[11], row[6], row[3]) - self.write_schedule_to_file( - self.get_html_from_daily_schedule( - timeB, - self.get_show_photo( - row[11], - row[6] if row[11] == "TV Shows" else row[3] - ), - datalist - ) - ) + self.write_schedule_to_file( + self.get_html_from_daily_schedule( + timeB, + self.get_show_photo( + row[11], + row[6] if row[11] == "TV Shows" else row[3] + ), + datalist + ) + ) - self.my_logger.debug('Trying to play: ' + row[3]) + self.my_logger.debug('Trying to play: ' + row[3]) - break + break - datalistLengthMonitor += 1 + datalistLengthMonitor += 1 - if datalistLengthMonitor >= len(datalist): + if datalistLengthMonitor >= len(datalist): - self.check_for_end_time(datalist) \ No newline at end of file + self.check_for_end_time(datalist) \ No newline at end of file diff --git a/src/Video.py b/src/Video.py index d9f6a5d..d09fa8d 100644 --- a/src/Video.py +++ b/src/Video.py @@ -1,8 +1,10 @@ +#!/usr/bin/env python + from Media import Media class Video(Media): - """Inherits Media. + """Inherits Media. Attributes: section_type: The type of library this is (i.e. "TV Shows") @@ -14,27 +16,27 @@ class Video(Media): is_strict_time: If strict time, then anchor to "natural_start_time" """ - def __init__( - self, - section_type, - title, - natural_start_time, - natural_end_time, - duration, - day_of_week, - is_strict_time, - time_shift, - overlap_max - ): + def __init__( + self, + section_type, + title, + natural_start_time, + natural_end_time, + duration, + day_of_week, + is_strict_time, + time_shift, + overlap_max + ): - super(Video, self).__init__( - section_type, - title, - natural_start_time, - natural_end_time, - duration, - day_of_week, - is_strict_time, - time_shift, - overlap_max - ) + super(Video, self).__init__( + section_type, + title, + natural_start_time, + natural_end_time, + duration, + day_of_week, + is_strict_time, + time_shift, + overlap_max + ) From b90c05754c4ea1ad8d52a57197f254f687859b6d Mon Sep 17 00:00:00 2001 From: Justin Emter Date: Sat, 22 Jul 2017 20:49:57 -0700 Subject: [PATCH 02/20] Added help for cli args. --- PseudoChannel.py | 44 ++++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/PseudoChannel.py b/PseudoChannel.py index 5c46a78..e8f8670 100644 --- a/PseudoChannel.py +++ b/PseudoChannel.py @@ -17,6 +17,7 @@ from datetime import time import calendar import itertools import argparse +import textwrap from xml.dom import minidom import xml.etree.ElementTree as ET @@ -662,7 +663,7 @@ class PseudoChannel(): for i , entry in enumerate(daily_schedule): - print "+++++", str(i + 1)+".", entry[11], entry[8], entry[6], " - ", entry[3] + print "+++++", str(i + 1)+".", entry[8], entry[11], entry[6], " - ", entry[3] def exit_app(self): @@ -692,11 +693,18 @@ if __name__ == '__main__': #pseudo_channel.generate_daily_schedule() + banner = textwrap.dedent('''\ +# __ __ +# |__)_ _ _| _ / |_ _ _ _ _| _ +# | _)(-|_|(_|(_)\__| )(_|| )| )(-|. |_)\/ +# | / + + A Custom TV Channel for Plex +''') + parser = argparse.ArgumentParser( - description="Pseudo Channel for Plex. Update pseduo_config.py & pseudo_schedule.xml before this step.", - usage="PseudoChannel.py [-u] update local db with plex db [-xml] update db " \ - "with xml schedule data [-g] generate daily schedule [-r] run the app" - ) + formatter_class=argparse.RawDescriptionHelpFormatter, + description = banner) ''' * @@ -704,24 +712,36 @@ if __name__ == '__main__': * ''' - parser.add_argument('-u', action='store_true') - parser.add_argument('-xml', action='store_true') - parser.add_argument('-g', action='store_true') - parser.add_argument('-r', action='store_true') + parser.add_argument('-u', + action='store_true', + help='Update the local database with Plex linraries.') + parser.add_argument('-xml', + action='store_true', + help='Update the local database with the pseudo_schedule.xml.') + parser.add_argument('-g', + action='store_true', + help='Generate the daily schedule.') + parser.add_argument('-r', + action='store_true', + help='Run this program.') ''' * * Show connected clients: "python PseudoChannel.py -c" * ''' - parser.add_argument('-c', action='store_true') + parser.add_argument('-c', + action='store_true', + help='Show Plex clients.') ''' * * Show schedule (daily): "python PseudoChannel.py -s" * ''' - parser.add_argument('-s', action='store_true') + parser.add_argument('-s', + action='store_true', + help='Show scheduled media for today.') globals().update(vars(parser.parse_args())) @@ -753,7 +773,7 @@ if __name__ == '__main__': try: - print "##### Running TV Controller" + print banner print "+++++ To run this in the background:" print "+++++", "screen -d -m bash -c 'python PseudoChannel.py -r; exec sh'" From 1641602ed8449417930169eeba73361f4bc64aae Mon Sep 17 00:00:00 2001 From: Justin Emter Date: Sat, 22 Jul 2017 20:51:58 -0700 Subject: [PATCH 03/20] Indents. --- PseudoChannel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PseudoChannel.py b/PseudoChannel.py index e8f8670..f2e1258 100644 --- a/PseudoChannel.py +++ b/PseudoChannel.py @@ -667,7 +667,7 @@ class PseudoChannel(): def exit_app(self): - print "Exiting Pseudo TV & cleaning up." + print " - Exiting Pseudo TV & cleaning up." for i in self.MEDIA: From 64e4485718e4c38dff86a115981476b7218ba5f6 Mon Sep 17 00:00:00 2001 From: Justin Emter Date: Mon, 24 Jul 2017 16:08:38 -0700 Subject: [PATCH 04/20] Added google calendar integration... Create a calendar event with a comma dilineated title like, 'Show, Seinfeld, false' where the third value is for strict-time. --- PseudoChannel.py | 148 +++++++++++++++++++++++++++++++++ pseudo_config.py | 8 ++ src/GoogleCalendar.py | 97 +++++++++++++++++++++ src/PseudoChannelCommercial.py | 0 src/__init__.py | 3 +- 5 files changed, 255 insertions(+), 1 deletion(-) create mode 100644 src/GoogleCalendar.py create mode 100644 src/PseudoChannelCommercial.py diff --git a/PseudoChannel.py b/PseudoChannel.py index f2e1258..26f79b3 100644 --- a/PseudoChannel.py +++ b/PseudoChannel.py @@ -8,6 +8,7 @@ from src import Episode from src import Music from src import Video from src import PseudoDailyScheduleController +from src import GoogleCalendar from plexapi.server import PlexServer @@ -32,6 +33,8 @@ class PseudoChannel(): PLEX = PlexServer(config.baseurl, config.token) MEDIA = [] + USING_GCALENDAR = config.useGoogleCalendar + GKEY = config.gkey def __init__(self): @@ -179,6 +182,107 @@ class PseudoChannel(): bar_length = 40 ) + def update_schedule_from_google_calendar(self): + + self.gcal = GoogleCalendar(self.GKEY) + + events = self.gcal.get_entries() + + self.db.create_tables() + + self.db.remove_all_scheduled_items() + + scheduled_days_list = [ + "mondays", + "tuesdays", + "wednesdays", + "thursdays", + "fridays", + "saturdays", + "sundays", + "weekdays", + "weekends", + "everyday" + ] + + section_dict = { + "TV Shows" : ["series", "shows", "tv", "episodes", "tv shows", "show"], + "Movies" : ["movie", "movies", "films", "film"], + "Videos" : ["video", "videos", "vid"], + "Music" : ["music", "songs", "song", "tune", "tunes"] + } + + weekday_dict = { + "0" : ["mondays", "weekdays", "everyday"], + "1" : ["tuesdays", "weekdays", "everyday"], + "2" : ["wednesdays", "weekdays", "everyday"], + "3" : ["thursdays", "weekdays", "everyday"], + "4" : ["fridays", "weekdays", "everyday"], + "5" : ["saturdays", "weekends", "everyday"], + "6" : ["sundays", "weekends", "everyday"], + } + + for event in events: + + titlelist = [x.strip() for x in event['summary'].split(',')] + + start = event['start'].get('dateTime', event['start'].get('date')) + + s = datetime.datetime.strptime(start,"%Y-%m-%dT%H:%M:%S-07:00") + + weekno = s.weekday() + + for key, value in section_dict.items(): + + if str(titlelist[0]).lower() == key or str(titlelist[0]).lower() in value: + + print "Adding {} to schedule.".format(titlelist[1]) + + title = titlelist[1] + + # s.strftime('%I:%M'), event["summary"] + natural_start_time = self.translate_time(s.strftime('%I:%M %p')) + + natural_end_time = 0 + + section = key + + for dnum, daylist in weekday_dict.items(): + + #print int(weekno), int(dnum) + + if int(weekno) == int(dnum): + + day_of_week = daylist[0] + + strict_time = titlelist[2] if len(titlelist) > 2 else "true" + + time_shift = "5" + + overlap_max = "" + + print natural_start_time + + start_time_unix = datetime.datetime.strptime( + self.translate_time(natural_start_time), + '%I:%M %p').strftime('%Y-%m-%d %H:%M:%S') + + #print "Adding: ", time.tag, section, time.text, time.attrib['title'] + + self.db.add_schedule_to_db( + 0, # mediaID + title, # title + 0, # duration + natural_start_time, # startTime + natural_end_time, # endTime + day_of_week, # dayOfWeek + start_time_unix, # startTimeUnix + section, # section + strict_time, # strictTime + time_shift, # timeShift + overlap_max, # overlapMax + ) + def update_schedule(self): self.db.create_tables() @@ -647,6 +751,37 @@ class PseudoChannel(): previous_episode = entry + def get_daily_schedule_as_media_object_list(self): + + for i, item in enumerate(self.db.get_daily_schedule(), start=0): + + if item[11] == "TV Shows": + + """episode = Episode( + + )""" + pass + + elif item[11] == "Movies": + + pass + + elif item[11] == "Music": + + pass + + elif item[11] == "Commercials": + + pass + + elif item[11] == "Videos": + + pass + + else: + + pass + def show_clients(self): print "##### Connected Clients:" @@ -743,6 +878,15 @@ if __name__ == '__main__': action='store_true', help='Show scheduled media for today.') + ''' + * + * Update Schedule based on Google Cal: "python PseudoChannel.py -gc" + * + ''' + parser.add_argument('-gc', + action='store_true', + help='Updates the schedule based on entries in the google calendar.') + globals().update(vars(parser.parse_args())) args = parser.parse_args() @@ -757,6 +901,10 @@ if __name__ == '__main__': pseudo_channel.update_schedule() + if args.gc: + + pseudo_channel.update_schedule_from_google_calendar() + if args.g: pseudo_channel.generate_daily_schedule() diff --git a/pseudo_config.py b/pseudo_config.py index 07f3c50..ab0435b 100644 --- a/pseudo_config.py +++ b/pseudo_config.py @@ -18,6 +18,11 @@ line so it looks like: "Movies" : ["Films"], + + 6) For Google Calendar integration add you "gkey" to the "plex_token.py" file + ...(https://docs.simplecalendar.io/find-google-calendar-id/): + + gkey = "the key" """ @@ -28,6 +33,7 @@ import plex_token as plex_token baseurl = 'http://media.home:32400' token = plex_token.token +gkey = plex_token.gkey ''' * @@ -42,3 +48,5 @@ plexLibraries = { "Music" : ["Music"], "Commercials" : ["Commercials"], } + +useGoogleCalendar = True diff --git a/src/GoogleCalendar.py b/src/GoogleCalendar.py new file mode 100644 index 0000000..216ebaa --- /dev/null +++ b/src/GoogleCalendar.py @@ -0,0 +1,97 @@ +from __future__ import print_function +import httplib2 +import os + +from apiclient import discovery +from oauth2client import client +from oauth2client import tools +from oauth2client.file import Storage + +import datetime + +"""try: + import argparse + flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args() +except ImportError: + flags = None""" + + +class GoogleCalendar(): + + # If modifying these scopes, delete your previously saved credentials + # at ~/.credentials/calendar-python-quickstart.json + SCOPES = 'https://www.googleapis.com/auth/calendar.readonly' + CLIENT_SECRET_FILE = 'client_secret.json' + APPLICATION_NAME = 'Google Calendar API Python Quickstart' + + KEY = '' + + def __init__(self, key): + + self.KEY = key + + def get_credentials(self): + """Gets valid user credentials from storage. + + If nothing has been stored, or if the stored credentials are invalid, + the OAuth2 flow is completed to obtain the new credentials. + + Returns: + Credentials, the obtained credential. + """ + home_dir = os.path.expanduser('~') + credential_dir = os.path.join(home_dir, '.credentials') + if not os.path.exists(credential_dir): + os.makedirs(credential_dir) + credential_path = os.path.join(credential_dir, + 'calendar-python-quickstart.json') + + store = Storage(credential_path) + credentials = store.get() + if not credentials or credentials.invalid: + flow = client.flow_from_clientsecrets(self.CLIENT_SECRET_FILE, self.SCOPES) + flow.user_agent = self.APPLICATION_NAME + if flags: + credentials = tools.run_flow(flow, store, flags) + else: # Needed only for compatibility with Python 2.6 + credentials = tools.run(flow, store) + print('Storing credentials to ' + credential_path) + return credentials + + def get_entries(self): + """Shows basic usage of the Google Calendar API. + + Creates a Google Calendar API service object and outputs a list of the next + 10 events on the user's calendar. + """ + credentials = self.get_credentials() + http = credentials.authorize(httplib2.Http()) + service = discovery.build('calendar', 'v3', http=http) + + now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time + + end = (datetime.datetime.now() + datetime.timedelta(days=1)) + + end = end.isoformat() + 'Z' # 'Z' indicates UTC time + + #print(now) + + #print(end) + + print('Getting the upcoming 10 events') + eventsResult = service.events().list( + calendarId=self.KEY, timeMin=now, timeMax=end, maxResults=250, singleEvents=True, + orderBy='startTime').execute() + events = eventsResult.get('items', []) + + if not events: + print('No upcoming events found.') + for event in events: + #start = event['start'].get('dateTime', event['start'].get('date')) + #print(start, event['summary']) + pass + return events + + +if __name__ == '__main__': + pass \ No newline at end of file diff --git a/src/PseudoChannelCommercial.py b/src/PseudoChannelCommercial.py new file mode 100644 index 0000000..e69de29 diff --git a/src/__init__.py b/src/__init__.py index 21d061f..2c68fd8 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -5,4 +5,5 @@ from Movie import Movie from Media import Media from Music import Music from Video import Video -from PseudoDailyScheduleController import PseudoDailyScheduleController \ No newline at end of file +from PseudoDailyScheduleController import PseudoDailyScheduleController +from GoogleCalendar import GoogleCalendar \ No newline at end of file From d716176210388d71bc703a947b5d4b7f28749d84 Mon Sep 17 00:00:00 2001 From: Justin Emter Date: Mon, 24 Jul 2017 17:26:38 -0700 Subject: [PATCH 05/20] Added a flag in the config that uses google calendar to update / regenerate the daily schedule at midnight. Also incorporated random movies in calendar scheduling: 'Movie, Random, true' --- PseudoChannel.py | 15 +++++++++++++-- pseudo_config.py | 4 ++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/PseudoChannel.py b/PseudoChannel.py index 26f79b3..e53df1f 100644 --- a/PseudoChannel.py +++ b/PseudoChannel.py @@ -35,9 +35,12 @@ class PseudoChannel(): MEDIA = [] USING_GCALENDAR = config.useGoogleCalendar GKEY = config.gkey + USING_GOOGLE_CALENDAR = False def __init__(self): + self.USING_GOOGLE_CALENDAR = config.useGoogleCalendar + self.db = PseudoChannelDatabase("pseudo-channel.db") self.controller = PseudoDailyScheduleController(config.baseurl, config.token, config.plexClients) @@ -569,7 +572,7 @@ class PseudoChannel(): if section == "TV Shows": - if entry[3] == "random": + if str(entry[3]).lower() == "random": next_episode = self.db.get_random_episode() @@ -604,7 +607,7 @@ class PseudoChannel(): elif section == "Movies": - if entry[3] == "random": + if str(entry[3]).lower() == "random": the_movie = self.db.get_random_movie() @@ -938,6 +941,14 @@ if __name__ == '__main__': if now_time == time(23,59): + if pseudo_channel.USING_GOOGLE_CALENDAR: + + pseudo_channel.update_schedule_from_google_calendar() + + else: + + pass + pseudo_channel.generate_daily_schedule() pseudo_channel.controller.tv_controller(pseudo_channel.db.get_daily_schedule()) diff --git a/pseudo_config.py b/pseudo_config.py index ab0435b..ab0b02b 100644 --- a/pseudo_config.py +++ b/pseudo_config.py @@ -23,6 +23,10 @@ ...(https://docs.simplecalendar.io/find-google-calendar-id/): gkey = "the key" + + 7) If using the Google Calendar integration exclusively, set this to true below: + + useGoogleCalendar """ From f9b7286f76f72247320cfa98579199d7fa7e7f3c Mon Sep 17 00:00:00 2001 From: Pseudo Channel Date: Mon, 24 Jul 2017 17:48:57 -0700 Subject: [PATCH 06/20] Need to generate credentials via the google python tut then move the .credentials folder to home folder of controller. --- src/GoogleCalendar.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/GoogleCalendar.py b/src/GoogleCalendar.py index 216ebaa..64fa2f4 100644 --- a/src/GoogleCalendar.py +++ b/src/GoogleCalendar.py @@ -7,6 +7,9 @@ from oauth2client import client from oauth2client import tools from oauth2client.file import Storage +import os.path as path +import sys + import datetime """try: @@ -20,8 +23,11 @@ class GoogleCalendar(): # If modifying these scopes, delete your previously saved credentials # at ~/.credentials/calendar-python-quickstart.json + two_up = path.abspath(path.join(__file__ ,"../../../")) + home_dir = os.path.expanduser('~') + credential_dir = os.path.join(home_dir, 'client_secret.json') SCOPES = 'https://www.googleapis.com/auth/calendar.readonly' - CLIENT_SECRET_FILE = 'client_secret.json' + CLIENT_SECRET_FILE = credential_dir APPLICATION_NAME = 'Google Calendar API Python Quickstart' KEY = '' @@ -48,6 +54,7 @@ class GoogleCalendar(): store = Storage(credential_path) credentials = store.get() + if not credentials or credentials.invalid: flow = client.flow_from_clientsecrets(self.CLIENT_SECRET_FILE, self.SCOPES) flow.user_agent = self.APPLICATION_NAME From 8049ac7be815a2be49e93a3cb186fe1879eb0f29 Mon Sep 17 00:00:00 2001 From: Justin Emter Date: Tue, 25 Jul 2017 08:47:50 -0700 Subject: [PATCH 07/20] Fixed the auto daily sched generate bug. --- PseudoChannel.py | 9 +++------ pseudo_config.py | 9 +++++++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/PseudoChannel.py b/PseudoChannel.py index e53df1f..b7916d3 100644 --- a/PseudoChannel.py +++ b/PseudoChannel.py @@ -33,14 +33,11 @@ class PseudoChannel(): PLEX = PlexServer(config.baseurl, config.token) MEDIA = [] - USING_GCALENDAR = config.useGoogleCalendar GKEY = config.gkey - USING_GOOGLE_CALENDAR = False + USING_GOOGLE_CALENDAR = config.useGoogleCalendar def __init__(self): - self.USING_GOOGLE_CALENDAR = config.useGoogleCalendar - self.db = PseudoChannelDatabase("pseudo-channel.db") self.controller = PseudoDailyScheduleController(config.baseurl, config.token, config.plexClients) @@ -937,9 +934,9 @@ if __name__ == '__main__': now = datetime.datetime.now() - now_time = now.time() + now_time = now.time().replace(microsecond=0) - if now_time == time(23,59): + if now_time == time(11,59): if pseudo_channel.USING_GOOGLE_CALENDAR: diff --git a/pseudo_config.py b/pseudo_config.py index ab0b02b..03a83b9 100644 --- a/pseudo_config.py +++ b/pseudo_config.py @@ -19,7 +19,7 @@ "Movies" : ["Films"], - 6) For Google Calendar integration add you "gkey" to the "plex_token.py" file + 6) For Google Calendar integration add your "gkey" to the "plex_token.py" file ...(https://docs.simplecalendar.io/find-google-calendar-id/): gkey = "the key" @@ -33,7 +33,12 @@ import os, sys sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # import ../plex_token.py -import plex_token as plex_token + +try: + import plex_token as plex_token +except ImportError as e: + print "+++++ Cannot find plex_token file. Make sure you create a plex_token.py file with the appropriate data." + raise e baseurl = 'http://media.home:32400' token = plex_token.token From bc1dbbde35f558673f7192d37eeba3f9fa0a026b Mon Sep 17 00:00:00 2001 From: Justin Emter Date: Tue, 25 Jul 2017 14:14:20 -0700 Subject: [PATCH 08/20] Added in XML output alongside HTML. Also should be generating XML/HTML upon the daily generation of the schedule. --- PseudoChannel.py | 41 ++++++++-- src/Commercial.py | 6 +- src/Episode.py | 6 +- src/Media.py | 4 +- src/Movie.py | 6 +- src/Music.py | 6 +- src/PseudoChannelDatabase.py | 59 +++++++------- src/PseudoDailyScheduleController.py | 112 ++++++++++++++++++++++++++- src/Video.py | 6 +- 9 files changed, 198 insertions(+), 48 deletions(-) diff --git a/PseudoChannel.py b/PseudoChannel.py index b7916d3..0556578 100644 --- a/PseudoChannel.py +++ b/PseudoChannel.py @@ -100,7 +100,7 @@ class PseudoChannel(): for i, media in enumerate(sectionMedia): - self.db.add_movies_to_db(1, media.title, media.duration) + self.db.add_movies_to_db(1, media.title, media.duration, media.key) self.print_progress( i + 1, @@ -125,7 +125,7 @@ class PseudoChannel(): backgroundImgURL = config.baseurl+backgroundImagePath.art+"?X-Plex-Token="+config.token - self.db.add_shows_to_db(2, media.title, media.duration, '', backgroundImgURL) + self.db.add_shows_to_db(2, media.title, media.duration, '', backgroundImgURL, media.key) self.print_progress( i + 1, @@ -150,7 +150,8 @@ class PseudoChannel(): duration, episode.index, episode.parentIndex, - media.title + media.title, + episode.key ) else: @@ -161,7 +162,8 @@ class PseudoChannel(): 0, episode.index, episode.parentIndex, - media.title + media.title, + episode.key ) elif correct_lib_name == "Commercials": @@ -172,7 +174,7 @@ class PseudoChannel(): for i, media in enumerate(sectionMedia): - self.db.add_commercials_to_db(3, media.title, media.duration) + self.db.add_commercials_to_db(3, media.title, media.duration, media.key) self.print_progress( i + 1, @@ -589,6 +591,7 @@ class PseudoChannel(): entry[10], # is_strict_time entry[11], # time_shift entry[12], # overlap_max + next_episode[8], # plex id entry[3], # show_series_title next_episode[5], # episode_number next_episode[6] # season_number @@ -623,7 +626,8 @@ class PseudoChannel(): entry[7], # day_of_week entry[10], # is_strict_time entry[11], # time_shift - entry[12] # overlap_max + entry[12], # overlap_max + the_movie[6] # plex id ) #print(movie.natural_end_time) @@ -649,7 +653,8 @@ class PseudoChannel(): entry[7], # day_of_week entry[10], # is_strict_time entry[11], # time_shift - entry[12] # overlap_max + entry[12], # overlap_max + the_music[6], # plex id ) #print(music.natural_end_time) @@ -675,7 +680,8 @@ class PseudoChannel(): entry[7], # day_of_week entry[10], # is_strict_time entry[11], # time_shift - entry[12] # overlap_max + entry[12], # overlap_max + the_video[6] # plex id ) #print(music.natural_end_time) @@ -751,6 +757,10 @@ class PseudoChannel(): previous_episode = entry + def make_xml_schedule(self): + + self.controller.make_xml_schedule(self.db.get_daily_schedule()) + def get_daily_schedule_as_media_object_list(self): for i, item in enumerate(self.db.get_daily_schedule(), start=0): @@ -887,6 +897,15 @@ if __name__ == '__main__': action='store_true', help='Updates the schedule based on entries in the google calendar.') + ''' + * + * Make XML / HTML Schedule: "python PseudoChannel.py -m" + * + ''' + parser.add_argument('-m', + action='store_true', + help='Makes the XML / HTML schedule based on the daily_schedule table.') + globals().update(vars(parser.parse_args())) args = parser.parse_args() @@ -917,6 +936,10 @@ if __name__ == '__main__': pseudo_channel.show_schedule() + if args.m: + + pseudo_channel.make_xml_schedule() + if args.r: try: @@ -948,6 +971,8 @@ if __name__ == '__main__': pseudo_channel.generate_daily_schedule() + pseudo_channel.make_xml_schedule() + pseudo_channel.controller.tv_controller(pseudo_channel.db.get_daily_schedule()) t = datetime.datetime.utcnow() diff --git a/src/Commercial.py b/src/Commercial.py index 254c4da..800f217 100644 --- a/src/Commercial.py +++ b/src/Commercial.py @@ -26,7 +26,8 @@ class Commercial(Media): day_of_week, is_strict_time, time_shift, - overlap_max + overlap_max, + plex_media_id ): super(Commercial, self).__init__( @@ -38,5 +39,6 @@ class Commercial(Media): day_of_week, is_strict_time, time_shift, - overlap_max + overlap_max, + plex_media_id ) diff --git a/src/Episode.py b/src/Episode.py index a9f902e..7286184 100644 --- a/src/Episode.py +++ b/src/Episode.py @@ -30,9 +30,10 @@ class Episode(Media): is_strict_time, time_shift, overlap_max, + plex_media_id, show_series_title, episode_number, - season_number + season_number, ): super(Episode, self).__init__( @@ -44,7 +45,8 @@ class Episode(Media): day_of_week, is_strict_time, time_shift, - overlap_max + overlap_max, + plex_media_id ) self.show_series_title = show_series_title diff --git a/src/Media.py b/src/Media.py index 992261f..9c37abe 100644 --- a/src/Media.py +++ b/src/Media.py @@ -31,7 +31,8 @@ class Media(object): day_of_week, is_strict_time, time_shift, - overlap_max + overlap_max, + plex_media_id ): self.section_type = section_type @@ -43,6 +44,7 @@ class Media(object): self.is_strict_time = is_strict_time self.time_shift = time_shift self.overlap_max = overlap_max + self.plex_media_id = plex_media_id self.start_time = natural_start_time self.end_time = natural_end_time diff --git a/src/Movie.py b/src/Movie.py index d2975d8..37644fe 100644 --- a/src/Movie.py +++ b/src/Movie.py @@ -26,7 +26,8 @@ class Movie(Media): day_of_week, is_strict_time, time_shift, - overlap_max + overlap_max, + plex_media_id ): super(Movie, self).__init__( @@ -38,5 +39,6 @@ class Movie(Media): day_of_week, is_strict_time, time_shift, - overlap_max + overlap_max, + plex_media_id ) diff --git a/src/Music.py b/src/Music.py index 4b8ee25..e63bdd7 100644 --- a/src/Music.py +++ b/src/Music.py @@ -26,7 +26,8 @@ class Music(Media): day_of_week, is_strict_time, time_shift, - overlap_max + overlap_max, + plex_media_id ): super(Music, self).__init__( @@ -38,5 +39,6 @@ class Music(Media): day_of_week, is_strict_time, time_shift, - overlap_max + overlap_max, + plex_media_id ) diff --git a/src/PseudoChannelDatabase.py b/src/PseudoChannelDatabase.py index 5391edd..6c424bf 100644 --- a/src/PseudoChannelDatabase.py +++ b/src/PseudoChannelDatabase.py @@ -23,29 +23,30 @@ class PseudoChannelDatabase(): self.cursor.execute('CREATE TABLE IF NOT EXISTS ' 'movies(id INTEGER PRIMARY KEY AUTOINCREMENT, ' - 'unix INTEGER, mediaID INTEGER, title TEXT, duration INTEGER, lastPlayedDate TEXT)') + 'unix INTEGER, mediaID INTEGER, title TEXT, duration INTEGER, ' + 'lastPlayedDate TEXT, plexMediaID TEXT)') self.cursor.execute('CREATE TABLE IF NOT EXISTS ' 'videos(id INTEGER PRIMARY KEY AUTOINCREMENT, ' - 'unix INTEGER, mediaID INTEGER, title TEXT, duration INTEGER)') + 'unix INTEGER, mediaID INTEGER, title TEXT, duration INTEGER, plexMediaID TEXT)') self.cursor.execute('CREATE TABLE IF NOT EXISTS ' 'music(id INTEGER PRIMARY KEY AUTOINCREMENT, ' - 'unix INTEGER, mediaID INTEGER, title TEXT, duration INTEGER)') + 'unix INTEGER, mediaID INTEGER, title TEXT, duration INTEGER, plexMediaID TEXT)') self.cursor.execute('CREATE TABLE IF NOT EXISTS ' 'shows(id INTEGER PRIMARY KEY AUTOINCREMENT, ' 'unix INTEGER, mediaID INTEGER, title TEXT, duration INTEGER, ' - 'lastEpisodeTitle TEXT, fullImageURL TEXT)') + 'lastEpisodeTitle TEXT, fullImageURL TEXT, plexMediaID TEXT)') self.cursor.execute('CREATE TABLE IF NOT EXISTS ' 'episodes(id INTEGER PRIMARY KEY AUTOINCREMENT, ' 'unix INTEGER, mediaID INTEGER, title TEXT, duration INTEGER, ' - 'episodeNumber INTEGER, seasonNumber INTEGER, showTitle TEXT)') + 'episodeNumber INTEGER, seasonNumber INTEGER, showTitle TEXT, plexMediaID TEXT)') self.cursor.execute('CREATE TABLE IF NOT EXISTS ' 'commercials(id INTEGER PRIMARY KEY AUTOINCREMENT, unix INTEGER, ' - 'mediaID INTEGER, title TEXT, duration INTEGER)') + 'mediaID INTEGER, title TEXT, duration INTEGER, plexMediaID TEXT)') self.cursor.execute('CREATE TABLE IF NOT EXISTS ' 'schedule(id INTEGER PRIMARY KEY AUTOINCREMENT, unix INTEGER, ' @@ -57,7 +58,7 @@ class PseudoChannelDatabase(): 'daily_schedule(id INTEGER PRIMARY KEY AUTOINCREMENT, unix INTEGER, ' 'mediaID INTEGER, title TEXT, episodeNumber INTEGER, seasonNumber INTEGER, ' 'showTitle TEXT, duration INTEGER, startTime INTEGER, endTime INTEGER, ' - 'dayOfWeek TEXT, sectionType TEXT)') + 'dayOfWeek TEXT, sectionType TEXT, plexMediaID TEXT)') self.cursor.execute('CREATE TABLE IF NOT EXISTS ' 'app_settings(id INTEGER PRIMARY KEY AUTOINCREMENT, version TEXT)') @@ -119,12 +120,12 @@ class PseudoChannelDatabase(): Setters, etc. """ - def add_movies_to_db(self, mediaID, title, duration): + def add_movies_to_db(self, mediaID, title, duration, plexMediaID): unix = int(time.time()) try: self.cursor.execute("INSERT OR REPLACE INTO movies " - "(unix, mediaID, title, duration) VALUES (?, ?, ?, ?)", - (unix, mediaID, title, duration)) + "(unix, mediaID, title, duration, plexMediaID) VALUES (?, ?, ?, ?, ?)", + (unix, mediaID, title, duration, plexMediaID)) self.conn.commit() # Catch the exception @@ -133,12 +134,12 @@ class PseudoChannelDatabase(): self.conn.rollback() raise e - def add_videos_to_db(self, mediaID, title, duration): + def add_videos_to_db(self, mediaID, title, duration, plexMediaID): unix = int(time.time()) try: self.cursor.execute("INSERT OR REPLACE INTO videos " - "(unix, mediaID, title, duration) VALUES (?, ?, ?, ?)", - (unix, mediaID, title, duration)) + "(unix, mediaID, title, duration, plexMediaID) VALUES (?, ?, ?, ?, ?)", + (unix, mediaID, title, duration, plexMediaID)) self.conn.commit() # Catch the exception @@ -147,12 +148,12 @@ class PseudoChannelDatabase(): self.conn.rollback() raise e - def add_shows_to_db(self, mediaID, title, duration, lastEpisodeTitle, fullImageURL): + def add_shows_to_db(self, mediaID, title, duration, lastEpisodeTitle, fullImageURL, plexMediaID): unix = int(time.time()) try: self.cursor.execute("INSERT OR REPLACE INTO shows " - "(unix, mediaID, title, duration, lastEpisodeTitle, fullImageURL) VALUES (?, ?, ?, ?, ?, ?)", - (unix, mediaID, title, duration, lastEpisodeTitle, fullImageURL)) + "(unix, mediaID, title, duration, lastEpisodeTitle, fullImageURL, plexMediaID) VALUES (?, ?, ?, ?, ?, ?, ?)", + (unix, mediaID, title, duration, lastEpisodeTitle, fullImageURL, plexMediaID)) self.conn.commit() # Catch the exception except Exception as e: @@ -160,12 +161,12 @@ class PseudoChannelDatabase(): self.conn.rollback() raise e - def add_episodes_to_db(self, mediaID, title, duration, episodeNumber, seasonNumber, showTitle): + def add_episodes_to_db(self, mediaID, title, duration, episodeNumber, seasonNumber, showTitle, plexMediaID): unix = int(time.time()) try: self.cursor.execute("INSERT OR REPLACE INTO episodes " - "(unix, mediaID, title, duration, episodeNumber, seasonNumber, showTitle) VALUES (?, ?, ?, ?, ?, ?, ?)", - (unix, mediaID, title, duration, episodeNumber, seasonNumber, showTitle)) + "(unix, mediaID, title, duration, episodeNumber, seasonNumber, showTitle, plexMediaID) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", + (unix, mediaID, title, duration, episodeNumber, seasonNumber, showTitle, plexMediaID)) self.conn.commit() # Catch the exception except Exception as e: @@ -173,15 +174,16 @@ class PseudoChannelDatabase(): self.conn.rollback() raise e - def add_commercials_to_db(self, mediaID, title, duration): + def add_commercials_to_db(self, mediaID, title, duration, plexMediaID): unix = int(time.time()) try: self.cursor.execute("INSERT OR REPLACE INTO commercials " - "(unix, mediaID, title, duration) VALUES (?, ?, ?, ?)", - (unix, mediaID, title, duration)) + "(unix, mediaID, title, duration, plexMediaID) VALUES (?, ?, ?, ?, ?)", + (unix, mediaID, title, duration, plexMediaID)) self.conn.commit() # Catch the exception except Exception as e: + print plexMediaID # Roll back any change if something goes wrong self.conn.rollback() raise e @@ -211,7 +213,8 @@ class PseudoChannelDatabase(): startTime, endTime, dayOfWeek, - sectionType + sectionType, + plexMediaID ): unix = int(time.time()) @@ -220,8 +223,8 @@ class PseudoChannelDatabase(): self.cursor.execute("INSERT OR REPLACE INTO daily_schedule " "(unix, mediaID, title, episodeNumber, seasonNumber, " - "showTitle, duration, startTime, endTime, dayOfWeek, sectionType) " - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + "showTitle, duration, startTime, endTime, dayOfWeek, sectionType, plexMediaID) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", ( unix, mediaID, @@ -233,7 +236,8 @@ class PseudoChannelDatabase(): startTime, endTime, dayOfWeek, - sectionType + sectionType, + plexMediaID )) self.conn.commit() @@ -261,7 +265,8 @@ class PseudoChannelDatabase(): media.start_time, media.end_time, media.day_of_week, - media.section_type + media.section_type, + media.plex_media_id ) """Database functions. diff --git a/src/PseudoDailyScheduleController.py b/src/PseudoDailyScheduleController.py index dde5d78..5b81c5e 100644 --- a/src/PseudoDailyScheduleController.py +++ b/src/PseudoDailyScheduleController.py @@ -5,6 +5,7 @@ from datetime import datetime import sqlite3 from yattag import Doc +from yattag import indent import os, sys import logging @@ -56,6 +57,63 @@ class PseudoDailyScheduleController(): return backgroundImgURL + def get_xml_from_daily_schedule(self, currentTime, bgImageURL, datalist): + + now = datetime.now() + + time = now.strftime("%B %d, %Y") + + doc, tag, text, line = Doc( + + ).ttl() + + doc.asis('') + + with tag('schedule', currently_playing_bg_image=bgImageURL if bgImageURL != None else ''): + + for row in datalist: + + timeB = datetime.strptime(row[8], '%I:%M %p') + + if currentTime == None: + + with tag('time', + ('data-key', str(row[12])), + ('data-current', 'false'), + ('data-type', str(row[11])), + ('data-title', str(row[3])), + ('data-start-time', str(row[8])), + ): + + text(row[8]) + + elif currentTime.hour == timeB.hour and currentTime.minute == timeB.minute: + + with tag('time', + ('data-key', str(row[12])), + ('data-current', 'true'), + ('data-type', str(row[11])), + ('data-title', str(row[3])), + ('data-start-time', str(row[8])), + ): + + text(row[8]) + + else: + + with tag('time', + ('data-key', str(row[12])), + ('data-current', 'false'), + ('data-type', str(row[11])), + ('data-title', str(row[3])), + ('data-start-time', str(row[8])), + ): + + text(row[8]) + + return indent(doc.getvalue()) + + ''' * * Get the generated html for the .html file that is the schedule. @@ -176,7 +234,7 @@ class PseudoDailyScheduleController(): text(row[8]) - return doc.getvalue() + return indent(doc.getvalue()) ''' * @@ -207,6 +265,35 @@ class PseudoDailyScheduleController(): f.write(data) + ''' + * + * Create 'schedules' dir & write the generated xml to .xml file. + * @param data: xml string + * @return null + * + ''' + def write_xml_to_file(self, data): + + now = datetime.now() + + fileName = "pseudo_schedule.xml" + + writepath = './schedules/' + + if not os.path.exists(writepath): + + os.makedirs(writepath) + + if os.path.exists(writepath+fileName): + + os.remove(writepath+fileName) + + mode = 'a' if os.path.exists(writepath) else 'w' + + with open(writepath+fileName, mode) as f: + + f.write(data) + ''' * * Trigger "playMedia()" on the Python Plex API for specified media. @@ -283,6 +370,7 @@ class PseudoDailyScheduleController(): print("Ok end time found") self.write_schedule_to_file(self.get_html_from_daily_schedule(None, None, datalist)) + self.write_xml_to_file(self.get_xml_from_daily_schedule(None, None, datalist)) break ''' @@ -328,6 +416,19 @@ class PseudoDailyScheduleController(): ) ) + """Generate / write XML to file + """ + self.write_xml_to_file( + self.get_xml_from_daily_schedule( + timeB, + self.get_show_photo( + row[11], + row[6] if row[11] == "TV Shows" else row[3] + ), + datalist + ) + ) + self.my_logger.debug('Trying to play: ' + row[3]) break @@ -336,4 +437,11 @@ class PseudoDailyScheduleController(): if datalistLengthMonitor >= len(datalist): - self.check_for_end_time(datalist) \ No newline at end of file + self.check_for_end_time(datalist) + + def make_xml_schedule(self, datalist): + + print "+++++ ", "Writing XML / HTML to file." + + self.write_schedule_to_file(self.get_html_from_daily_schedule(None, None, datalist)) + self.write_xml_to_file(self.get_xml_from_daily_schedule(None, None, datalist)) \ No newline at end of file diff --git a/src/Video.py b/src/Video.py index d09fa8d..6507255 100644 --- a/src/Video.py +++ b/src/Video.py @@ -26,7 +26,8 @@ class Video(Media): day_of_week, is_strict_time, time_shift, - overlap_max + overlap_max, + plex_media_id ): super(Video, self).__init__( @@ -38,5 +39,6 @@ class Video(Media): day_of_week, is_strict_time, time_shift, - overlap_max + overlap_max, + plex_media_id ) From e83361857cb66fae3d91d71e25ca46dde82d95f1 Mon Sep 17 00:00:00 2001 From: Justin Emter Date: Tue, 25 Jul 2017 14:32:17 -0700 Subject: [PATCH 09/20] Fixed out-of-range bug when generating daily schedule. --- PseudoChannel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PseudoChannel.py b/PseudoChannel.py index 0556578..0fc7116 100644 --- a/PseudoChannel.py +++ b/PseudoChannel.py @@ -591,7 +591,7 @@ class PseudoChannel(): entry[10], # is_strict_time entry[11], # time_shift entry[12], # overlap_max - next_episode[8], # plex id + next_episode[8] if len(next_episode) >= 9 else '', # plex id entry[3], # show_series_title next_episode[5], # episode_number next_episode[6] # season_number From ab9f69de5f9cea51b8d657da116c9b8db756b556 Mon Sep 17 00:00:00 2001 From: Justin Emter Date: Tue, 25 Jul 2017 14:50:26 -0700 Subject: [PATCH 10/20] Updated cli. --- PseudoChannel.py | 48 +++++++++++++++++++++---------------------- src/GoogleCalendar.py | 2 +- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/PseudoChannel.py b/PseudoChannel.py index 0fc7116..64fcd37 100644 --- a/PseudoChannel.py +++ b/PseudoChannel.py @@ -857,16 +857,25 @@ if __name__ == '__main__': * ''' - parser.add_argument('-u', + parser.add_argument('-u', '--update', action='store_true', - help='Update the local database with Plex linraries.') - parser.add_argument('-xml', + help='Update the local database with Plex libraries.') + parser.add_argument('-xml', '--xml', action='store_true', help='Update the local database with the pseudo_schedule.xml.') - parser.add_argument('-g', + ''' + * + * Update Schedule based on Google Cal: "python PseudoChannel.py -gc" + * + ''' + parser.add_argument('-gc', '--google_calendar', + action='store_true', + help='Update the local database with entries in the google calendar.') + + parser.add_argument('-g', '--generate_schedule', action='store_true', help='Generate the daily schedule.') - parser.add_argument('-r', + parser.add_argument('-r', '--run', action='store_true', help='Run this program.') @@ -875,7 +884,7 @@ if __name__ == '__main__': * Show connected clients: "python PseudoChannel.py -c" * ''' - parser.add_argument('-c', + parser.add_argument('-c', '--show_clients', action='store_true', help='Show Plex clients.') @@ -884,25 +893,16 @@ if __name__ == '__main__': * Show schedule (daily): "python PseudoChannel.py -s" * ''' - parser.add_argument('-s', + parser.add_argument('-s', '--show_schedule', action='store_true', help='Show scheduled media for today.') - ''' - * - * Update Schedule based on Google Cal: "python PseudoChannel.py -gc" - * - ''' - parser.add_argument('-gc', - action='store_true', - help='Updates the schedule based on entries in the google calendar.') - ''' * * Make XML / HTML Schedule: "python PseudoChannel.py -m" * ''' - parser.add_argument('-m', + parser.add_argument('-m', '--make_html', action='store_true', help='Makes the XML / HTML schedule based on the daily_schedule table.') @@ -912,7 +912,7 @@ if __name__ == '__main__': #print(args) - if args.u: + if args.update: pseudo_channel.update_db() @@ -920,27 +920,27 @@ if __name__ == '__main__': pseudo_channel.update_schedule() - if args.gc: + if args.google_calendar: pseudo_channel.update_schedule_from_google_calendar() - if args.g: + if args.generate_schedule: pseudo_channel.generate_daily_schedule() - if args.c: + if args.show_clients: pseudo_channel.show_clients() - if args.s: + if args.show_schedule: pseudo_channel.show_schedule() - if args.m: + if args.make_html: pseudo_channel.make_xml_schedule() - if args.r: + if args.run: try: diff --git a/src/GoogleCalendar.py b/src/GoogleCalendar.py index 64fa2f4..8ef2164 100644 --- a/src/GoogleCalendar.py +++ b/src/GoogleCalendar.py @@ -85,7 +85,7 @@ class GoogleCalendar(): #print(end) - print('Getting the upcoming 10 events') + print('##### Getting the upcoming calendar events') eventsResult = service.events().list( calendarId=self.KEY, timeMin=now, timeMax=end, maxResults=250, singleEvents=True, orderBy='startTime').execute() From 8b2e69fc3ec84e4e56d3dd263ef60430e36fdd7e Mon Sep 17 00:00:00 2001 From: Justin Emter Date: Tue, 25 Jul 2017 18:32:22 -0700 Subject: [PATCH 11/20] Added commercial support. Currently seems to be working ok, but needs adjusting / testing. Currently am using it with a time-shift of '5'. --- PseudoChannel.py | 92 ++++++++++++++---- pseudo_config.py | 2 + src/PseudoChannelCommercial.py | 133 +++++++++++++++++++++++++++ src/PseudoChannelDatabase.py | 10 +- src/PseudoDailyScheduleController.py | 78 +++++++++------- src/__init__.py | 3 +- 6 files changed, 268 insertions(+), 50 deletions(-) diff --git a/PseudoChannel.py b/PseudoChannel.py index 64fcd37..352eae2 100644 --- a/PseudoChannel.py +++ b/PseudoChannel.py @@ -9,6 +9,7 @@ from src import Music from src import Video from src import PseudoDailyScheduleController from src import GoogleCalendar +from src import PseudoChannelCommercial from plexapi.server import PlexServer @@ -36,6 +37,8 @@ class PseudoChannel(): GKEY = config.gkey USING_GOOGLE_CALENDAR = config.useGoogleCalendar + USING_COMMERCIAL_INJECTION = config.useCommercialInjection + def __init__(self): self.db = PseudoChannelDatabase("pseudo-channel.db") @@ -243,7 +246,7 @@ class PseudoChannel(): title = titlelist[1] # s.strftime('%I:%M'), event["summary"] - natural_start_time = self.translate_time(s.strftime('%I:%M %p')) + natural_start_time = self.translate_time(s.strftime('%I:%M:%S %p')) natural_end_time = 0 @@ -259,6 +262,8 @@ class PseudoChannel(): strict_time = titlelist[2] if len(titlelist) > 2 else "true" + #strict_time = "true" + time_shift = "5" overlap_max = "" @@ -267,7 +272,7 @@ class PseudoChannel(): start_time_unix = datetime.datetime.strptime( self.translate_time(natural_start_time), - '%I:%M %p').strftime('%Y-%m-%d %H:%M:%S') + '%I:%M:%S %p').strftime('%Y-%m-%d %H:%M:%S') #print "Adding: ", time.tag, section, time.text, time.attrib['title'] @@ -343,7 +348,7 @@ class PseudoChannel(): start_time_unix = datetime.datetime.strptime( self.translate_time(time.text), - '%I:%M %p').strftime('%Y-%m-%d %H:%M:%S') + '%I:%M:%S %p').strftime('%Y-%m-%d %H:%M:%S') print "Adding: ", time.tag, section, time.text, time.attrib['title'] @@ -398,7 +403,7 @@ class PseudoChannel(): try: - return datetime.datetime.strptime(timestr, "%I:%M %p").strftime("%-I:%M %p") + return datetime.datetime.strptime(timestr, '%I:%M:%S %p').strftime('%I:%M:%S %p') except ValueError as e: @@ -406,7 +411,7 @@ class PseudoChannel(): try: - return datetime.datetime.strptime(timestr, "%H:%M").strftime("%-I:%M %p") + return datetime.datetime.strptime(timestr, '%I:%M:%S %p').strftime('%I:%M:%S %p') except ValueError as e: @@ -418,8 +423,8 @@ class PseudoChannel(): * Getting the offest by comparing both times from the unix epoch time and getting the difference. * ''' - timeA = datetime.datetime.strptime(time1, "%I:%M %p") - timeB = datetime.datetime.strptime(time2, "%I:%M %p") + timeA = datetime.datetime.strptime(time1, '%I:%M:%S %p') + timeB = datetime.datetime.strptime(time2, '%I:%M:%S %p') timeAEpoch = calendar.timegm(timeA.timetuple()) timeBEpoch = calendar.timegm(timeB.timetuple()) @@ -444,9 +449,9 @@ class PseudoChannel(): self.OVERLAP_MAX = overlapMax - time1 = prevEndTime.strftime('%-I:%M %p') + time1 = prevEndTime.strftime('%I:%M:%S %p') - timeB = datetime.datetime.strptime(intendedStartTime, '%I:%M %p').strftime('%-I:%M %p') + timeB = datetime.datetime.strptime(intendedStartTime, '%I:%M:%S %p').strftime('%I:%M:%S %p') print "++++ Previous End Time: ", time1, "Intended start time: ", timeB @@ -511,7 +516,7 @@ class PseudoChannel(): theTimeSetInterval = datetime.datetime.strptime(time, '%H:%M') - tempTimeTwoStr = datetime.datetime.strptime(time1, '%I:%M %p').strftime('%H:%M') + tempTimeTwoStr = datetime.datetime.strptime(time1, '%I:%M:%S %p').strftime('%H:%M') formatted_time_two = datetime.datetime.strptime(tempTimeTwoStr, '%H:%M') @@ -527,11 +532,11 @@ class PseudoChannel(): print("Not sure what to do here") - return newStartTime.strftime('%-I:%M %p') + return newStartTime.strftime('%I:%M:%S %p') def get_end_time_from_duration(self, startTime, duration): - time = datetime.datetime.strptime(startTime, '%I:%M %p') + time = datetime.datetime.strptime(startTime, '%I:%M:%S %p') show_time_plus_duration = time + datetime.timedelta(milliseconds=duration) @@ -543,6 +548,11 @@ class PseudoChannel(): print("#### Generating Daily Schedule") + if self.USING_COMMERCIAL_INJECTION: + self.commercials = PseudoChannelCommercial( + self.db.get_commercials() + ) + schedule = self.db.get_schedule() weekday_dict = { @@ -713,7 +723,7 @@ class PseudoChannel(): if previous_episode != None: - natural_start_time = datetime.datetime.strptime(entry.natural_start_time, '%I:%M %p') + natural_start_time = datetime.datetime.strptime(entry.natural_start_time, '%I:%M:%S %p') natural_end_time = entry.natural_end_time @@ -726,6 +736,19 @@ class PseudoChannel(): entry.duration ) + """Get List of Commercials to inject""" + + if self.USING_COMMERCIAL_INJECTION: + + list_of_commercials = self.commercials.get_commercials_to_place_between_media( + previous_episode, + entry + ) + + for commercial in list_of_commercials: + + self.db.add_media_to_daily_schedule(commercial) + self.db.add_media_to_daily_schedule(entry) previous_episode = entry @@ -743,10 +766,21 @@ class PseudoChannel(): print "++++ New start time:", new_starttime - entry.start_time = datetime.datetime.strptime(new_starttime, '%I:%M %p').strftime('%-I:%M %p') + entry.start_time = datetime.datetime.strptime(new_starttime, '%I:%M:%S %p').strftime('%I:%M:%S %p') entry.end_time = self.get_end_time_from_duration(entry.start_time, entry.duration) + """Get List of Commercials to inject""" + if self.USING_COMMERCIAL_INJECTION: + list_of_commercials = self.commercials.get_commercials_to_place_between_media( + previous_episode, + entry + ) + + for commercial in list_of_commercials: + + self.db.add_media_to_daily_schedule(commercial) + self.db.add_media_to_daily_schedule(entry) previous_episode = entry @@ -757,6 +791,19 @@ class PseudoChannel(): previous_episode = entry + def run_commercial_injection(self): + + print "#### Running commercial injection." + + self.commercials = PseudoChannelCommercial( + self.db.get_commercials(), + self.db.get_daily_schedule() + ) + + commercials_to_inject = self.commercials.get_commercials_to_inject() + + print commercials_to_inject + def make_xml_schedule(self): self.controller.make_xml_schedule(self.db.get_daily_schedule()) @@ -906,6 +953,15 @@ if __name__ == '__main__': action='store_true', help='Makes the XML / HTML schedule based on the daily_schedule table.') + ''' + * + * Make XML / HTML Schedule: "python PseudoChannel.py -i" + * + ''' + parser.add_argument('-i', '--inject_commercials', + action='store_true', + help='Squeeze commercials in any media gaps if possible.') + globals().update(vars(parser.parse_args())) args = parser.parse_args() @@ -940,6 +996,10 @@ if __name__ == '__main__': pseudo_channel.make_xml_schedule() + if args.inject_commercials: + + pseudo_channel.run_commercial_injection() + if args.run: try: @@ -977,9 +1037,9 @@ if __name__ == '__main__': t = datetime.datetime.utcnow() - sleeptime = 60 - (t.second + t.microsecond/1000000.0) + #sleeptime = 60 - (t.second + t.microsecond/1000000.0) - sleep(sleeptime) + sleep(.5) except KeyboardInterrupt, e: diff --git a/pseudo_config.py b/pseudo_config.py index 03a83b9..8f9dd9c 100644 --- a/pseudo_config.py +++ b/pseudo_config.py @@ -59,3 +59,5 @@ plexLibraries = { } useGoogleCalendar = True + +useCommercialInjection = True diff --git a/src/PseudoChannelCommercial.py b/src/PseudoChannelCommercial.py index e69de29..ca16405 100644 --- a/src/PseudoChannelCommercial.py +++ b/src/PseudoChannelCommercial.py @@ -0,0 +1,133 @@ +"""Commercial Functionality +""" +from random import shuffle +import random +import copy +import datetime +from src import Commercial + +class PseudoChannelCommercial(): + + MIN_DURATION_FOR_COMMERCIAL = 10 #seconds + daily_schedule = [] + + def __init__(self, commercials): + + self.commercials = commercials + + def get_commercials_to_inject(self): + + self.go() + + return None + + def get_random_commercial(self): + + random_commercial = random.choice(self.commercials) + + random_commercial_dur_seconds = (int(random_commercial[4])/1000)%60 + + while random_commercial_dur_seconds < self.MIN_DURATION_FOR_COMMERCIAL: + + random_commercial = random.choice(self.commercials) + + random_commercial_dur_seconds = (int(random_commercial[4])/1000)%60 + + return random_commercial + + def go(self): + + shuffled_commercial_list = copy.deepcopy(self.commercials) + + random.shuffle(self.commercials, random.random) + + #print shuffled_commercial_list + + prev_item = None + + for entry in self.daily_schedule: + + """First Episode""" + if prev_item == None: + + prev_item = entry + + else: + + prev_item_end_time = datetime.datetime.strptime(prev_item[9], '%Y-%m-%d %H:%M:%S.%f') + + curr_item_start_time = datetime.datetime.strptime(entry[8], '%I:%M:%S %p') + + time_diff = (curr_item_start_time - prev_item_end_time) + + days, hours, minutes = time_diff.days, time_diff.seconds // 3600, time_diff.seconds // 60 % 60 + + count = 0 + + commercial_list = [] + + commercial_dur_sum = 0 + + while int(time_diff.total_seconds()) >= commercial_dur_sum and count < len(self.commercials): + + + random_commercial = self.get_random_commercial() + + commercial_list.append(random_commercial) + + commercial_dur_sum += int(random_commercial[4]) + + print commercial_list + + prev_item = entry + + def get_commercials_to_place_between_media(self, last_ep, now_ep): + + print last_ep.end_time, now_ep.start_time + + prev_item_end_time = datetime.datetime.strptime(last_ep.end_time.strftime('%Y-%m-%d %H:%M:%S.%f'), '%Y-%m-%d %H:%M:%S.%f') + + curr_item_start_time = datetime.datetime.strptime(now_ep.start_time, '%I:%M:%S %p') + + time_diff = (curr_item_start_time - prev_item_end_time) + + days, hours, minutes = time_diff.days, time_diff.seconds // 3600, time_diff.seconds // 60 % 60 + + count = 0 + + commercial_list = [] + + commercial_dur_sum = 0 + + while int(time_diff.total_seconds()) >= commercial_dur_sum and count < len(self.commercials): + + random_commercial = self.get_random_commercial() + + new_commercial_seconds = (int(random_commercial[4])/1000)%60 + + commercial_dur_sum += new_commercial_seconds + + new_commercial_start_time = prev_item_end_time + datetime.timedelta(seconds=commercial_dur_sum) + + new_commercial_end_time = new_commercial_start_time + datetime.timedelta(seconds=int(new_commercial_seconds)) + + formatted_time_for_new_commercial = new_commercial_start_time.strftime('%I:%M:%S %p') + + new_commercial = Commercial( + "Commercials", + random_commercial[3], + formatted_time_for_new_commercial, # natural_start_time + new_commercial_end_time, + random_commercial[4], + "everyday", # day_of_week + "true", # is_strict_time + "1", # time_shift + "0", # overlap_max + "", # plex_media_id + ) + + commercial_list.append(new_commercial) + + #print "here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + + return commercial_list \ No newline at end of file diff --git a/src/PseudoChannelDatabase.py b/src/PseudoChannelDatabase.py index 6c424bf..f04030f 100644 --- a/src/PseudoChannelDatabase.py +++ b/src/PseudoChannelDatabase.py @@ -329,6 +329,14 @@ class PseudoChannelDatabase(): return self.get_media(title, media) + def get_commercials(self): + + self.cursor.execute("SELECT * FROM commercials ORDER BY duration ASC") + + datalist = list(self.cursor.fetchall()) + + return datalist + def update_shows_table_with_last_episode(self, showTitle, lastEpisodeTitle): sql1 = "UPDATE shows SET lastEpisodeTitle = ? WHERE title LIKE ? COLLATE NOCASE" @@ -461,7 +469,7 @@ class PseudoChannelDatabase(): return first_episode - def get_commercials(self, title): + def get_commercial(self, title): media = "commercials" diff --git a/src/PseudoDailyScheduleController.py b/src/PseudoDailyScheduleController.py index 5b81c5e..59e7556 100644 --- a/src/PseudoDailyScheduleController.py +++ b/src/PseudoDailyScheduleController.py @@ -73,7 +73,7 @@ class PseudoDailyScheduleController(): for row in datalist: - timeB = datetime.strptime(row[8], '%I:%M %p') + timeB = datetime.strptime(row[8], '%I:%M:%S %p') if currentTime == None: @@ -188,7 +188,7 @@ class PseudoDailyScheduleController(): with tag('tbody'): - timeB = datetime.strptime(row[8], '%I:%M %p') + timeB = datetime.strptime(row[8], '%I:%M:%S %p') if currentTime == None: @@ -333,6 +333,16 @@ class PseudoDailyScheduleController(): clientItem.playMedia(movie) + elif mediaType == "Commercials": + + movie = self.PLEX.library.section(mediaType).get(mediaTitle) + + for client in self.PLEX_CLIENTS: + + clientItem = self.PLEX.client(client) + + clientItem.playMedia(movie) + else: print("Not sure how to play {}".format(mediaType)) @@ -367,12 +377,14 @@ class PseudoDailyScheduleController(): if currentTime.minute == endTime.minute: - print("Ok end time found") + if currentTime.second == endTime.second: - self.write_schedule_to_file(self.get_html_from_daily_schedule(None, None, datalist)) - self.write_xml_to_file(self.get_xml_from_daily_schedule(None, None, datalist)) + print("Ok end time found") - break + self.write_schedule_to_file(self.get_html_from_daily_schedule(None, None, datalist)) + self.write_xml_to_file(self.get_xml_from_daily_schedule(None, None, datalist)) + + break ''' * * Check DB / current time. If that matches a scheduled shows startTime then trigger play via Plex API @@ -394,44 +406,46 @@ class PseudoDailyScheduleController(): for row in datalist: - timeB = datetime.strptime(row[8], '%I:%M %p') + timeB = datetime.strptime(row[8], '%I:%M:%S %p') if currentTime.hour == timeB.hour: if currentTime.minute == timeB.minute: - print("Starting Epsisode: " + row[3]) - print(row) + if currentTime.second == timeB.second: - self.play_media(row[11], row[6], row[3]) + print("Starting Media: " + row[3]) + print(row) - self.write_schedule_to_file( - self.get_html_from_daily_schedule( - timeB, - self.get_show_photo( - row[11], - row[6] if row[11] == "TV Shows" else row[3] - ), - datalist + self.play_media(row[11], row[6], row[3]) + + self.write_schedule_to_file( + self.get_html_from_daily_schedule( + timeB, + self.get_show_photo( + row[11], + row[6] if row[11] == "TV Shows" else row[3] + ), + datalist + ) ) - ) - """Generate / write XML to file - """ - self.write_xml_to_file( - self.get_xml_from_daily_schedule( - timeB, - self.get_show_photo( - row[11], - row[6] if row[11] == "TV Shows" else row[3] - ), - datalist + """Generate / write XML to file + """ + self.write_xml_to_file( + self.get_xml_from_daily_schedule( + timeB, + self.get_show_photo( + row[11], + row[6] if row[11] == "TV Shows" else row[3] + ), + datalist + ) ) - ) - self.my_logger.debug('Trying to play: ' + row[3]) + self.my_logger.debug('Trying to play: ' + row[3]) - break + break datalistLengthMonitor += 1 diff --git a/src/__init__.py b/src/__init__.py index 2c68fd8..027281c 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -6,4 +6,5 @@ from Media import Media from Music import Music from Video import Video from PseudoDailyScheduleController import PseudoDailyScheduleController -from GoogleCalendar import GoogleCalendar \ No newline at end of file +from GoogleCalendar import GoogleCalendar +from PseudoChannelCommercial import PseudoChannelCommercial \ No newline at end of file From 6aa420766054a4f3c74c58cdc08013050f3044ec Mon Sep 17 00:00:00 2001 From: Justin Emter Date: Tue, 25 Jul 2017 20:29:59 -0700 Subject: [PATCH 12/20] Trying to work out commercial logic / fixed XML error. --- PseudoChannel.py | 18 +++++++++++++----- src/PseudoChannelCommercial.py | 6 +++++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/PseudoChannel.py b/PseudoChannel.py index 352eae2..a3ff7a2 100644 --- a/PseudoChannel.py +++ b/PseudoChannel.py @@ -346,9 +346,7 @@ class PseudoChannel(): overlap_max = time.attrib['overlap-max'] - start_time_unix = datetime.datetime.strptime( - self.translate_time(time.text), - '%I:%M:%S %p').strftime('%Y-%m-%d %H:%M:%S') + start_time_unix = self.translate_time(time.text) print "Adding: ", time.tag, section, time.text, time.attrib['title'] @@ -403,7 +401,7 @@ class PseudoChannel(): try: - return datetime.datetime.strptime(timestr, '%I:%M:%S %p').strftime('%I:%M:%S %p') + return datetime.datetime.strptime(timestr, '%I:%M %p').strftime('%I:%M:%S %p') except ValueError as e: @@ -417,6 +415,16 @@ class PseudoChannel(): pass + try: + + return datetime.datetime.strptime(timestr, '%H:%M').strftime('%I:%M:%S %p') + + except ValueError as e: + + pass + + return timestr + def time_diff(self, time1,time2): ''' * @@ -595,7 +603,7 @@ class PseudoChannel(): section, # section_type next_episode[3], # title entry[5], # natural_start_time - self.get_end_time_from_duration(entry[5], next_episode[4]), # natural_end_time + self.get_end_time_from_duration(self.translate_time(entry[5]), next_episode[4]), # natural_end_time next_episode[4], # duration entry[7], # day_of_week entry[10], # is_strict_time diff --git a/src/PseudoChannelCommercial.py b/src/PseudoChannelCommercial.py index ca16405..e9e2f3c 100644 --- a/src/PseudoChannelCommercial.py +++ b/src/PseudoChannelCommercial.py @@ -99,7 +99,7 @@ class PseudoChannelCommercial(): commercial_dur_sum = 0 - while int(time_diff.total_seconds()) >= commercial_dur_sum and count < len(self.commercials): + while int(time_diff.total_seconds()) >= commercial_dur_sum and (count*10000) < len(self.commercials): random_commercial = self.get_random_commercial() @@ -126,6 +126,10 @@ class PseudoChannelCommercial(): "", # plex_media_id ) + if int(time_diff.total_seconds()) < commercial_dur_sum: + + break + commercial_list.append(new_commercial) #print "here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" From 920190626a871cb60f23f0c7ce856a39166a738b Mon Sep 17 00:00:00 2001 From: Justin Emter Date: Tue, 25 Jul 2017 22:00:45 -0700 Subject: [PATCH 13/20] Adjusted logic to calculate start / stop times for commercials. This should solve the commercials getting cut off. I do hoever see some issues, pushing anyway. --- src/PseudoChannelCommercial.py | 157 ++++++++++++++++++--------------- 1 file changed, 87 insertions(+), 70 deletions(-) diff --git a/src/PseudoChannelCommercial.py b/src/PseudoChannelCommercial.py index e9e2f3c..bd0fb53 100644 --- a/src/PseudoChannelCommercial.py +++ b/src/PseudoChannelCommercial.py @@ -8,130 +8,147 @@ from src import Commercial class PseudoChannelCommercial(): - MIN_DURATION_FOR_COMMERCIAL = 10 #seconds - daily_schedule = [] + MIN_DURATION_FOR_COMMERCIAL = 10 #seconds + daily_schedule = [] - def __init__(self, commercials): + def __init__(self, commercials): - self.commercials = commercials + self.commercials = commercials - def get_commercials_to_inject(self): + def get_commercials_to_inject(self): - self.go() + self.go() - return None + return None - def get_random_commercial(self): + def get_random_commercial(self): - random_commercial = random.choice(self.commercials) + random_commercial = random.choice(self.commercials) - random_commercial_dur_seconds = (int(random_commercial[4])/1000)%60 + random_commercial_dur_seconds = (int(random_commercial[4])/1000)%60 - while random_commercial_dur_seconds < self.MIN_DURATION_FOR_COMMERCIAL: + while random_commercial_dur_seconds < self.MIN_DURATION_FOR_COMMERCIAL: - random_commercial = random.choice(self.commercials) + random_commercial = random.choice(self.commercials) - random_commercial_dur_seconds = (int(random_commercial[4])/1000)%60 + random_commercial_dur_seconds = (int(random_commercial[4])/1000)%60 - return random_commercial + return random_commercial - def go(self): + def go(self): - shuffled_commercial_list = copy.deepcopy(self.commercials) + shuffled_commercial_list = copy.deepcopy(self.commercials) - random.shuffle(self.commercials, random.random) + random.shuffle(self.commercials, random.random) - #print shuffled_commercial_list + #print shuffled_commercial_list - prev_item = None + prev_item = None - for entry in self.daily_schedule: + for entry in self.daily_schedule: - """First Episode""" - if prev_item == None: + """First Episode""" + if prev_item == None: - prev_item = entry + prev_item = entry - else: + else: - prev_item_end_time = datetime.datetime.strptime(prev_item[9], '%Y-%m-%d %H:%M:%S.%f') + prev_item_end_time = datetime.datetime.strptime(prev_item[9], '%Y-%m-%d %H:%M:%S.%f') - curr_item_start_time = datetime.datetime.strptime(entry[8], '%I:%M:%S %p') + curr_item_start_time = datetime.datetime.strptime(entry[8], '%I:%M:%S %p') - time_diff = (curr_item_start_time - prev_item_end_time) + time_diff = (curr_item_start_time - prev_item_end_time) - days, hours, minutes = time_diff.days, time_diff.seconds // 3600, time_diff.seconds // 60 % 60 + days, hours, minutes = time_diff.days, time_diff.seconds // 3600, time_diff.seconds // 60 % 60 - count = 0 + count = 0 - commercial_list = [] + commercial_list = [] - commercial_dur_sum = 0 + commercial_dur_sum = 0 - while int(time_diff.total_seconds()) >= commercial_dur_sum and count < len(self.commercials): + while int(time_diff.total_seconds()) >= commercial_dur_sum and count < len(self.commercials): - random_commercial = self.get_random_commercial() + random_commercial = self.get_random_commercial() - commercial_list.append(random_commercial) + commercial_list.append(random_commercial) - commercial_dur_sum += int(random_commercial[4]) + commercial_dur_sum += int(random_commercial[4]) - print commercial_list + print commercial_list - prev_item = entry + prev_item = entry - def get_commercials_to_place_between_media(self, last_ep, now_ep): + def timedelta_milliseconds(self, td): + return td.days*86400000 + td.seconds*1000 + td.microseconds/1000 - print last_ep.end_time, now_ep.start_time + def get_commercials_to_place_between_media(self, last_ep, now_ep): - prev_item_end_time = datetime.datetime.strptime(last_ep.end_time.strftime('%Y-%m-%d %H:%M:%S.%f'), '%Y-%m-%d %H:%M:%S.%f') + print last_ep.end_time, now_ep.start_time - curr_item_start_time = datetime.datetime.strptime(now_ep.start_time, '%I:%M:%S %p') + prev_item_end_time = datetime.datetime.strptime(last_ep.end_time.strftime('%Y-%m-%d %H:%M:%S.%f'), '%Y-%m-%d %H:%M:%S.%f') - time_diff = (curr_item_start_time - prev_item_end_time) + curr_item_start_time = datetime.datetime.strptime(now_ep.start_time, '%I:%M:%S %p') - days, hours, minutes = time_diff.days, time_diff.seconds // 3600, time_diff.seconds // 60 % 60 + time_diff = (curr_item_start_time - prev_item_end_time) - count = 0 + count = 0 - commercial_list = [] + commercial_list = [] - commercial_dur_sum = 0 + commercial_dur_sum = 0 - while int(time_diff.total_seconds()) >= commercial_dur_sum and (count*10000) < len(self.commercials): + time_diff_milli = self.timedelta_milliseconds(time_diff) - random_commercial = self.get_random_commercial() + last_commercial = None - new_commercial_seconds = (int(random_commercial[4])/1000)%60 + time_watch = prev_item_end_time - commercial_dur_sum += new_commercial_seconds + print "here", time_diff.seconds - new_commercial_start_time = prev_item_end_time + datetime.timedelta(seconds=commercial_dur_sum) + while curr_item_start_time > time_watch and (count) < len(self.commercials): - new_commercial_end_time = new_commercial_start_time + datetime.timedelta(seconds=int(new_commercial_seconds)) + random_commercial = self.get_random_commercial() - formatted_time_for_new_commercial = new_commercial_start_time.strftime('%I:%M:%S %p') + last_commercial = random_commercial - new_commercial = Commercial( - "Commercials", - random_commercial[3], - formatted_time_for_new_commercial, # natural_start_time - new_commercial_end_time, - random_commercial[4], - "everyday", # day_of_week - "true", # is_strict_time - "1", # time_shift - "0", # overlap_max - "", # plex_media_id - ) + #new_commercial_seconds = (int(random_commercial[4])/1000)%60 - if int(time_diff.total_seconds()) < commercial_dur_sum: + new_commercial_milli = int(random_commercial[4]) - break + commercial_dur_sum += new_commercial_milli - commercial_list.append(new_commercial) + new_commercial_start_time = time_watch - #print "here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + new_commercial_end_time = new_commercial_start_time - return commercial_list \ No newline at end of file + new_commercial_end_time += datetime.timedelta(milliseconds=int(new_commercial_milli)) + + formatted_time_for_new_commercial = time_watch.strftime('%I:%M:%S %p') + + new_commercial = Commercial( + "Commercials", + random_commercial[3], + formatted_time_for_new_commercial, # natural_start_time + new_commercial_end_time, + random_commercial[4], + "everyday", # day_of_week + "true", # is_strict_time + "1", # time_shift + "0", # overlap_max + "", # plex_media_id + ) + + time_watch += datetime.timedelta(milliseconds=commercial_dur_sum) + + if time_diff_milli < commercial_dur_sum or new_commercial_end_time > curr_item_start_time: + + break + + commercial_list.append(new_commercial) + + #print "here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + + return commercial_list \ No newline at end of file From 17a277a72ce5fa12833a21fddeedb9c900e8bbf9 Mon Sep 17 00:00:00 2001 From: Justin Emter Date: Wed, 26 Jul 2017 00:07:35 -0700 Subject: [PATCH 14/20] Fixed the issue where the daily_sched wasn't generating at midnight. --- PseudoChannel.py | 6 +++++- src/PseudoChannelCommercial.py | 12 +++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/PseudoChannel.py b/PseudoChannel.py index a3ff7a2..cfd5949 100644 --- a/PseudoChannel.py +++ b/PseudoChannel.py @@ -1027,12 +1027,16 @@ if __name__ == '__main__': now_time = now.time().replace(microsecond=0) - if now_time == time(11,59): + #print time(11,59,00), now_time + + if now_time == time(00,00,00): if pseudo_channel.USING_GOOGLE_CALENDAR: pseudo_channel.update_schedule_from_google_calendar() + sleep(.5) + else: pass diff --git a/src/PseudoChannelCommercial.py b/src/PseudoChannelCommercial.py index bd0fb53..0bb5a7f 100644 --- a/src/PseudoChannelCommercial.py +++ b/src/PseudoChannelCommercial.py @@ -106,6 +106,8 @@ class PseudoChannelCommercial(): time_watch = prev_item_end_time + new_commercial_start_time = prev_item_end_time + print "here", time_diff.seconds while curr_item_start_time > time_watch and (count) < len(self.commercials): @@ -118,9 +120,11 @@ class PseudoChannelCommercial(): new_commercial_milli = int(random_commercial[4]) - commercial_dur_sum += new_commercial_milli + new_commercial_start_time += datetime.timedelta(milliseconds=commercial_dur_sum) - new_commercial_start_time = time_watch + time_watch += datetime.timedelta(milliseconds=commercial_dur_sum) + + commercial_dur_sum += new_commercial_milli new_commercial_end_time = new_commercial_start_time @@ -141,9 +145,7 @@ class PseudoChannelCommercial(): "", # plex_media_id ) - time_watch += datetime.timedelta(milliseconds=commercial_dur_sum) - - if time_diff_milli < commercial_dur_sum or new_commercial_end_time > curr_item_start_time: + if new_commercial_end_time > curr_item_start_time: break From 56fa06326093ccfa194e5aac6756ec1a0420a92b Mon Sep 17 00:00:00 2001 From: Justin Emter Date: Wed, 26 Jul 2017 15:16:55 -0700 Subject: [PATCH 15/20] I think this might have solved the commercial logic. Testing here first. --- src/PseudoChannelCommercial.py | 36 ++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/PseudoChannelCommercial.py b/src/PseudoChannelCommercial.py index 0bb5a7f..42fd0cb 100644 --- a/src/PseudoChannelCommercial.py +++ b/src/PseudoChannelCommercial.py @@ -3,7 +3,9 @@ from random import shuffle import random import copy -import datetime +from datetime import datetime +from datetime import timedelta +from dateutil.relativedelta import relativedelta from src import Commercial class PseudoChannelCommercial(): @@ -88,9 +90,9 @@ class PseudoChannelCommercial(): print last_ep.end_time, now_ep.start_time - prev_item_end_time = datetime.datetime.strptime(last_ep.end_time.strftime('%Y-%m-%d %H:%M:%S.%f'), '%Y-%m-%d %H:%M:%S.%f') + prev_item_end_time = datetime.strptime(last_ep.end_time.strftime('%Y-%m-%d %H:%M:%S.%f'), '%Y-%m-%d %H:%M:%S.%f') - curr_item_start_time = datetime.datetime.strptime(now_ep.start_time, '%I:%M:%S %p') + curr_item_start_time = datetime.strptime(now_ep.start_time, '%I:%M:%S %p') time_diff = (curr_item_start_time - prev_item_end_time) @@ -110,27 +112,33 @@ class PseudoChannelCommercial(): print "here", time_diff.seconds - while curr_item_start_time > time_watch and (count) < len(self.commercials): + while curr_item_start_time > new_commercial_start_time and (count) < len(self.commercials): random_commercial = self.get_random_commercial() - last_commercial = random_commercial - #new_commercial_seconds = (int(random_commercial[4])/1000)%60 new_commercial_milli = int(random_commercial[4]) - new_commercial_start_time += datetime.timedelta(milliseconds=commercial_dur_sum) + if last_commercial != None: - time_watch += datetime.timedelta(milliseconds=commercial_dur_sum) + #print last_commercial[3] + + new_commercial_start_time = last_commercial.end_time + + new_commercial_end_time = new_commercial_start_time + \ + timedelta(milliseconds=int(new_commercial_milli)) + + else: + + new_commercial_start_time = prev_item_end_time + + new_commercial_end_time = new_commercial_start_time + \ + timedelta(milliseconds=int(new_commercial_milli)) commercial_dur_sum += new_commercial_milli - new_commercial_end_time = new_commercial_start_time - - new_commercial_end_time += datetime.timedelta(milliseconds=int(new_commercial_milli)) - - formatted_time_for_new_commercial = time_watch.strftime('%I:%M:%S %p') + formatted_time_for_new_commercial = new_commercial_start_time.strftime('%I:%M:%S %p') new_commercial = Commercial( "Commercials", @@ -145,6 +153,8 @@ class PseudoChannelCommercial(): "", # plex_media_id ) + last_commercial = new_commercial + if new_commercial_end_time > curr_item_start_time: break From 8c4411d5a664f29b55ea3bc655ea8f57b377f3b9 Mon Sep 17 00:00:00 2001 From: Justin Emter Date: Wed, 26 Jul 2017 16:02:30 -0700 Subject: [PATCH 16/20] Looping through commercials over 100 times trying to fill up time. --- src/PseudoChannelCommercial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PseudoChannelCommercial.py b/src/PseudoChannelCommercial.py index 42fd0cb..779fff0 100644 --- a/src/PseudoChannelCommercial.py +++ b/src/PseudoChannelCommercial.py @@ -112,7 +112,7 @@ class PseudoChannelCommercial(): print "here", time_diff.seconds - while curr_item_start_time > new_commercial_start_time and (count) < len(self.commercials): + while curr_item_start_time > new_commercial_start_time and (count) < len(self.commercials)*100: random_commercial = self.get_random_commercial() From 7d3028e6fafcfd24016df7647396b7ed8f43c5f7 Mon Sep 17 00:00:00 2001 From: Justin Emter Date: Wed, 26 Jul 2017 19:53:44 -0700 Subject: [PATCH 17/20] Commercial support seems to work & also the duplicating DB entries seems to be fixed. --- src/PseudoChannelDatabase.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/PseudoChannelDatabase.py b/src/PseudoChannelDatabase.py index f04030f..c8e7596 100644 --- a/src/PseudoChannelDatabase.py +++ b/src/PseudoChannelDatabase.py @@ -68,7 +68,9 @@ class PseudoChannelDatabase(): self.cursor.execute('CREATE UNIQUE INDEX IF NOT EXISTS idx_movie_title ON movies (title);') - self.cursor.execute('CREATE UNIQUE INDEX IF NOT EXISTS idx_movie_title ON videos (title);') + self.cursor.execute('CREATE UNIQUE INDEX IF NOT EXISTS idx_shows_title ON shows (title);') + + self.cursor.execute('CREATE UNIQUE INDEX IF NOT EXISTS idx_video_title ON videos (title);') self.cursor.execute('CREATE UNIQUE INDEX IF NOT EXISTS idx_music_title ON music (title);') @@ -123,7 +125,7 @@ class PseudoChannelDatabase(): def add_movies_to_db(self, mediaID, title, duration, plexMediaID): unix = int(time.time()) try: - self.cursor.execute("INSERT OR REPLACE INTO movies " + self.cursor.execute("REPLACE INTO movies " "(unix, mediaID, title, duration, plexMediaID) VALUES (?, ?, ?, ?, ?)", (unix, mediaID, title, duration, plexMediaID)) @@ -137,7 +139,7 @@ class PseudoChannelDatabase(): def add_videos_to_db(self, mediaID, title, duration, plexMediaID): unix = int(time.time()) try: - self.cursor.execute("INSERT OR REPLACE INTO videos " + self.cursor.execute("REPLACE INTO videos " "(unix, mediaID, title, duration, plexMediaID) VALUES (?, ?, ?, ?, ?)", (unix, mediaID, title, duration, plexMediaID)) @@ -151,7 +153,7 @@ class PseudoChannelDatabase(): def add_shows_to_db(self, mediaID, title, duration, lastEpisodeTitle, fullImageURL, plexMediaID): unix = int(time.time()) try: - self.cursor.execute("INSERT OR REPLACE INTO shows " + self.cursor.execute("REPLACE INTO shows " "(unix, mediaID, title, duration, lastEpisodeTitle, fullImageURL, plexMediaID) VALUES (?, ?, ?, ?, ?, ?, ?)", (unix, mediaID, title, duration, lastEpisodeTitle, fullImageURL, plexMediaID)) self.conn.commit() @@ -164,7 +166,7 @@ class PseudoChannelDatabase(): def add_episodes_to_db(self, mediaID, title, duration, episodeNumber, seasonNumber, showTitle, plexMediaID): unix = int(time.time()) try: - self.cursor.execute("INSERT OR REPLACE INTO episodes " + self.cursor.execute("REPLACE INTO episodes " "(unix, mediaID, title, duration, episodeNumber, seasonNumber, showTitle, plexMediaID) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", (unix, mediaID, title, duration, episodeNumber, seasonNumber, showTitle, plexMediaID)) self.conn.commit() @@ -177,7 +179,7 @@ class PseudoChannelDatabase(): def add_commercials_to_db(self, mediaID, title, duration, plexMediaID): unix = int(time.time()) try: - self.cursor.execute("INSERT OR REPLACE INTO commercials " + self.cursor.execute("REPLACE INTO commercials " "(unix, mediaID, title, duration, plexMediaID) VALUES (?, ?, ?, ?, ?)", (unix, mediaID, title, duration, plexMediaID)) self.conn.commit() @@ -191,7 +193,7 @@ class PseudoChannelDatabase(): def add_schedule_to_db(self, mediaID, title, duration, startTime, endTime, dayOfWeek, startTimeUnix, section, strictTime, timeShift, overlapMax): unix = int(time.time()) try: - self.cursor.execute("INSERT OR REPLACE INTO schedule " + self.cursor.execute("REPLACE INTO schedule " "(unix, mediaID, title, duration, startTime, endTime, dayOfWeek, startTimeUnix, section, strictTime, timeShift, overlapMax) " "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", (unix, mediaID, title, duration, startTime, endTime, dayOfWeek, startTimeUnix, section, strictTime, timeShift, overlapMax)) From 99e4b9838f11b0d2de35d192f05e92fcddedf06d Mon Sep 17 00:00:00 2001 From: Justin Emter Date: Thu, 27 Jul 2017 10:50:03 -0700 Subject: [PATCH 18/20] Fixed the resource hogging issue. Also added custom daily update var to config. --- PseudoChannel.py | 109 +++++++++++++++++++++++++++------ pseudo_config.py | 2 + src/PseudoChannelCommercial.py | 4 +- src/PseudoChannelDatabase.py | 4 +- 4 files changed, 97 insertions(+), 22 deletions(-) diff --git a/PseudoChannel.py b/PseudoChannel.py index cfd5949..c5712c1 100644 --- a/PseudoChannel.py +++ b/PseudoChannel.py @@ -23,6 +23,11 @@ import textwrap from xml.dom import minidom import xml.etree.ElementTree as ET +import sched + +from threading import Timer +import signal + from time import sleep import pseudo_config as config @@ -39,6 +44,10 @@ class PseudoChannel(): USING_COMMERCIAL_INJECTION = config.useCommercialInjection + DAILY_UPDATE_TIME = config.dailyUpdateTime + + APP_TIME_FORMAT_STR = '%I:%M:%S %p' + def __init__(self): self.db = PseudoChannelDatabase("pseudo-channel.db") @@ -246,7 +255,7 @@ class PseudoChannel(): title = titlelist[1] # s.strftime('%I:%M'), event["summary"] - natural_start_time = self.translate_time(s.strftime('%I:%M:%S %p')) + natural_start_time = self.translate_time(s.strftime(self.APP_TIME_FORMAT_STR)) natural_end_time = 0 @@ -401,7 +410,7 @@ class PseudoChannel(): try: - return datetime.datetime.strptime(timestr, '%I:%M %p').strftime('%I:%M:%S %p') + return datetime.datetime.strptime(timestr, '%I:%M %p').strftime(self.APP_TIME_FORMAT_STR) except ValueError as e: @@ -409,7 +418,7 @@ class PseudoChannel(): try: - return datetime.datetime.strptime(timestr, '%I:%M:%S %p').strftime('%I:%M:%S %p') + return datetime.datetime.strptime(timestr, '%I:%M:%S %p').strftime(self.APP_TIME_FORMAT_STR) except ValueError as e: @@ -417,7 +426,7 @@ class PseudoChannel(): try: - return datetime.datetime.strptime(timestr, '%H:%M').strftime('%I:%M:%S %p') + return datetime.datetime.strptime(timestr, '%H:%M').strftime(self.APP_TIME_FORMAT_STR) except ValueError as e: @@ -459,7 +468,7 @@ class PseudoChannel(): time1 = prevEndTime.strftime('%I:%M:%S %p') - timeB = datetime.datetime.strptime(intendedStartTime, '%I:%M:%S %p').strftime('%I:%M:%S %p') + timeB = datetime.datetime.strptime(intendedStartTime, '%I:%M:%S %p').strftime(self.APP_TIME_FORMAT_STR) print "++++ Previous End Time: ", time1, "Intended start time: ", timeB @@ -524,7 +533,7 @@ class PseudoChannel(): theTimeSetInterval = datetime.datetime.strptime(time, '%H:%M') - tempTimeTwoStr = datetime.datetime.strptime(time1, '%I:%M:%S %p').strftime('%H:%M') + tempTimeTwoStr = datetime.datetime.strptime(time1, self.APP_TIME_FORMAT_STR).strftime('%H:%M') formatted_time_two = datetime.datetime.strptime(tempTimeTwoStr, '%H:%M') @@ -731,7 +740,7 @@ class PseudoChannel(): if previous_episode != None: - natural_start_time = datetime.datetime.strptime(entry.natural_start_time, '%I:%M:%S %p') + natural_start_time = datetime.datetime.strptime(entry.natural_start_time, self.APP_TIME_FORMAT_STR) natural_end_time = entry.natural_end_time @@ -774,7 +783,7 @@ class PseudoChannel(): print "++++ New start time:", new_starttime - entry.start_time = datetime.datetime.strptime(new_starttime, '%I:%M:%S %p').strftime('%I:%M:%S %p') + entry.start_time = datetime.datetime.strptime(new_starttime, self.APP_TIME_FORMAT_STR).strftime('%I:%M:%S %p') entry.end_time = self.get_end_time_from_duration(entry.start_time, entry.duration) @@ -812,6 +821,12 @@ class PseudoChannel(): print commercials_to_inject + def run(self): + + """print datetime.datetime.now() + threading.Timer(1, self.run()).start()""" + pass + def make_xml_schedule(self): self.controller.make_xml_schedule(self.db.get_daily_schedule()) @@ -1010,17 +1025,73 @@ if __name__ == '__main__': if args.run: - try: - - print banner - print "+++++ To run this in the background:" - print "+++++", "screen -d -m bash -c 'python PseudoChannel.py -r; exec sh'" + print banner + print "+++++ To run this in the background:" + print "+++++", "screen -d -m bash -c 'python PseudoChannel.py -r; exec sh'" + + """Every minute on the minute check the DB startTimes of all media to + determine whether or not to play. Also, check the now_time to + see if it's midnight (or 23.59), if so then generate a new daily_schedule + + """ + + s = sched.scheduler(time, sleep) + + the_daily_schedule = pseudo_channel.db.get_daily_schedule() + + daily_update_time = datetime.datetime.strptime( + pseudo_channel.translate_time( + pseudo_channel.DAILY_UPDATE_TIME + ) + , pseudo_channel.APP_TIME_FORMAT_STR) + + try: + + def run_task(): + + now = datetime.datetime.now() + + now_time = now.time().replace(microsecond=0) + + #print time(11,59,00), now_time + + if now_time == time( + daily_update_time.hour, + daily_update_time.minute, + daily_update_time.second + ): + + if pseudo_channel.USING_GOOGLE_CALENDAR: + + pseudo_channel.update_schedule_from_google_calendar() + + else: + + pass + + pseudo_channel.generate_daily_schedule() + + pseudo_channel.make_xml_schedule() + + pseudo_channel.controller.tv_controller(the_daily_schedule) + + t = Timer(1, run_task, ()) + + t.start() + + print '{}'.format(datetime.datetime.now(), end="\r") + + except KeyboardInterrupt: + + print('Manual break by user') + + run_task() + + + + """try: + - """Every minute on the minute check the DB startTimes of all media to - determine whether or not to play. Also, check the now_time to - see if it's midnight (or 23.59), if so then generate a new daily_schedule - - """ while True: now = datetime.datetime.now() @@ -1057,7 +1128,7 @@ if __name__ == '__main__': pseudo_channel.exit_app() - del pseudo_channel + del pseudo_channel""" diff --git a/pseudo_config.py b/pseudo_config.py index 8f9dd9c..ba9b068 100644 --- a/pseudo_config.py +++ b/pseudo_config.py @@ -61,3 +61,5 @@ plexLibraries = { useGoogleCalendar = True useCommercialInjection = True + +dailyUpdateTime = "12:00 AM" diff --git a/src/PseudoChannelCommercial.py b/src/PseudoChannelCommercial.py index 779fff0..866bbe1 100644 --- a/src/PseudoChannelCommercial.py +++ b/src/PseudoChannelCommercial.py @@ -88,7 +88,7 @@ class PseudoChannelCommercial(): def get_commercials_to_place_between_media(self, last_ep, now_ep): - print last_ep.end_time, now_ep.start_time + #print last_ep.end_time, now_ep.start_time prev_item_end_time = datetime.strptime(last_ep.end_time.strftime('%Y-%m-%d %H:%M:%S.%f'), '%Y-%m-%d %H:%M:%S.%f') @@ -110,7 +110,7 @@ class PseudoChannelCommercial(): new_commercial_start_time = prev_item_end_time - print "here", time_diff.seconds + #print "here", time_diff.seconds while curr_item_start_time > new_commercial_start_time and (count) < len(self.commercials)*100: diff --git a/src/PseudoChannelDatabase.py b/src/PseudoChannelDatabase.py index c8e7596..30eae25 100644 --- a/src/PseudoChannelDatabase.py +++ b/src/PseudoChannelDatabase.py @@ -10,7 +10,7 @@ class PseudoChannelDatabase(): self.db = db - self.conn = sqlite3.connect(self.db) + self.conn = sqlite3.connect(self.db, check_same_thread=False) self.cursor = self.conn.cursor() @@ -299,6 +299,8 @@ class PseudoChannelDatabase(): datalist = list(self.cursor.fetchall()) + print "##### Getting Daily Schedule from DB." + return datalist def get_movie(self, title): From acda7fca9b363d2137d086e51a93d404bfea262b Mon Sep 17 00:00:00 2001 From: Justin Emter Date: Thu, 27 Jul 2017 11:34:23 -0700 Subject: [PATCH 19/20] Added another config flag for debug mode. If set to false then commercials are not included in the html / xml. Will soon effect other input. --- PseudoChannel.py | 21 +++++++++++++-------- pseudo_config.py | 2 ++ src/PseudoDailyScheduleController.py | 12 +++++++++++- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/PseudoChannel.py b/PseudoChannel.py index c5712c1..ce37f0b 100644 --- a/PseudoChannel.py +++ b/PseudoChannel.py @@ -23,8 +23,6 @@ import textwrap from xml.dom import minidom import xml.etree.ElementTree as ET -import sched - from threading import Timer import signal @@ -40,6 +38,7 @@ class PseudoChannel(): PLEX = PlexServer(config.baseurl, config.token) MEDIA = [] GKEY = config.gkey + USING_GOOGLE_CALENDAR = config.useGoogleCalendar USING_COMMERCIAL_INJECTION = config.useCommercialInjection @@ -48,11 +47,18 @@ class PseudoChannel(): APP_TIME_FORMAT_STR = '%I:%M:%S %p' + DEBUG = config.debug_mode + def __init__(self): self.db = PseudoChannelDatabase("pseudo-channel.db") - self.controller = PseudoDailyScheduleController(config.baseurl, config.token, config.plexClients) + self.controller = PseudoDailyScheduleController( + config.baseurl, + config.token, + config.plexClients, + self.DEBUG + ) """Database functions. @@ -1035,15 +1041,14 @@ if __name__ == '__main__': """ - s = sched.scheduler(time, sleep) - the_daily_schedule = pseudo_channel.db.get_daily_schedule() daily_update_time = datetime.datetime.strptime( pseudo_channel.translate_time( pseudo_channel.DAILY_UPDATE_TIME - ) - , pseudo_channel.APP_TIME_FORMAT_STR) + ), + pseudo_channel.APP_TIME_FORMAT_STR + ) try: @@ -1059,7 +1064,7 @@ if __name__ == '__main__': daily_update_time.hour, daily_update_time.minute, daily_update_time.second - ): + ): if pseudo_channel.USING_GOOGLE_CALENDAR: diff --git a/pseudo_config.py b/pseudo_config.py index ba9b068..1b51cc4 100644 --- a/pseudo_config.py +++ b/pseudo_config.py @@ -63,3 +63,5 @@ useGoogleCalendar = True useCommercialInjection = True dailyUpdateTime = "12:00 AM" + +debug_mode = False diff --git a/src/PseudoDailyScheduleController.py b/src/PseudoDailyScheduleController.py index 59e7556..a7dbf7a 100644 --- a/src/PseudoDailyScheduleController.py +++ b/src/PseudoDailyScheduleController.py @@ -13,7 +13,7 @@ import logging.handlers class PseudoDailyScheduleController(): - def __init__(self, server, token, clients): + def __init__(self, server, token, clients, debugMode = False): self.PLEX = PlexServer(server, token) @@ -23,6 +23,8 @@ class PseudoDailyScheduleController(): self.PLEX_CLIENTS = clients + self.DEBUG = debugMode + self.my_logger = logging.getLogger('MyLogger') self.my_logger.setLevel(logging.DEBUG) @@ -73,6 +75,10 @@ class PseudoDailyScheduleController(): for row in datalist: + if str(row[11]) == "Commercials" and self.DEBUG == False: + + continue + timeB = datetime.strptime(row[8], '%I:%M:%S %p') if currentTime == None: @@ -184,6 +190,10 @@ class PseudoDailyScheduleController(): for row in datalist: + if str(row[11]) == "Commercials" and self.DEBUG == False: + + continue + numberIncrease += 1 with tag('tbody'): From 6542cfacda9ef0e63a59cf3d369704606043a2ed Mon Sep 17 00:00:00 2001 From: Justin Emter Date: Thu, 27 Jul 2017 14:15:56 -0700 Subject: [PATCH 20/20] Moved baseurl from config to plex_toke.py file so it doesn't get overwritten on updates. --- pseudo_config.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pseudo_config.py b/pseudo_config.py index 1b51cc4..2fb5681 100644 --- a/pseudo_config.py +++ b/pseudo_config.py @@ -7,6 +7,7 @@ 2) add this line to the newly created file: + baseurl = 'the url to your server' token = 'your plex token' 3) Edit the "basurl" variable below to point to your Plex server @@ -40,7 +41,7 @@ except ImportError as e: print "+++++ Cannot find plex_token file. Make sure you create a plex_token.py file with the appropriate data." raise e -baseurl = 'http://media.home:32400' +baseurl = plex_token.baseurl token = plex_token.token gkey = plex_token.gkey