From b4564345ea9fb4ca8b538180bf881aa2b53339e5 Mon Sep 17 00:00:00 2001 From: mutto233 <39921339+mutto233@users.noreply.github.com> Date: Tue, 10 Jul 2018 01:23:02 -0400 Subject: [PATCH] PlexClient Override feature added New feature! Now, if your directory is named "channels_" your script will override and always play to the client . This is going to be helpful for when we add mutli-box support. --- both-dir/src/PseudoDailyScheduleController.py | 1459 +++++++++-------- 1 file changed, 733 insertions(+), 726 deletions(-) diff --git a/both-dir/src/PseudoDailyScheduleController.py b/both-dir/src/PseudoDailyScheduleController.py index e4bd0ed..78b9f6d 100644 --- a/both-dir/src/PseudoDailyScheduleController.py +++ b/both-dir/src/PseudoDailyScheduleController.py @@ -1,727 +1,734 @@ -#!/usr/bin/env python - -import os, sys -import socket -import logging -import logging.handlers -from datetime import datetime -import sqlite3 -import thread,SocketServer,SimpleHTTPServer -from plexapi.server import PlexServer -from yattag import Doc -from yattag import indent -import pseudo_config as config - -class PseudoDailyScheduleController(): - - def __init__(self, - server, - token, - clients, - controllerServerPath = '', - controllerServerPort = '8000', - debugMode = False, - htmlPseudoTitle = "Daily PseudoChannel" - ): - - self.PLEX = PlexServer(server, token) - self.BASE_URL = server - self.TOKEN = token - self.PLEX_CLIENTS = clients - self.CONTROLLER_SERVER_PATH = controllerServerPath - self.CONTROLLER_SERVER_PORT = controllerServerPort if controllerServerPort != '' else '80' - self.DEBUG = debugMode - self.webserverStarted = False - self.HTML_PSEUDO_TITLE = htmlPseudoTitle - try: - self.my_logger = logging.getLogger('MyLogger') - self.my_logger.setLevel(logging.DEBUG) - self.handler = logging.handlers.SysLogHandler(address = '/dev/log') - self.my_logger.addHandler(self.handler) - except: - pass - - ''' - * - * 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, showtitle, durationAmount): - - backgroundImagePath = None - backgroundImgURL = '' - # First, we are going to search for movies/commercials - try: - movies = self.PLEX.library.section(section).search(title=showtitle) - #print "+++++ Checking Duration for a Match: " - for item in movies: -# print item.duration - if item.duration == durationAmount: - #print "+++++ MATCH FOUND in %s" % item - backgroundImagePath = item - break - except: - return backgroundImgURL - - if backgroundImagePath == None: - # We will try the old way, for the sake of TV Shows - try: - backgroundImagePath = self.PLEX.library.section(section).get(showtitle) - except: - return backgroundImgURL - - if backgroundImagePath != None and isinstance(backgroundImagePath.art, str): - backgroundImgURL = self.BASE_URL+backgroundImagePath.art+"?X-Plex-Token="+self.TOKEN - return backgroundImgURL - - def start_server(self): - - if self.webserverStarted == False and self.CONTROLLER_SERVER_PATH != '': - """Changing dir to the schedules dir.""" - web_dir = os.path.abspath(os.path.join(os.path.dirname( __file__ ), '..', 'schedules')) - os.chdir(web_dir) - PORT = int(self.CONTROLLER_SERVER_PORT) - class MyHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): - - def log_message(self, format, *args): - return - - global httpd - try: - #print "Starting webserver at port: ", PORT - # create the httpd handler for the simplehttpserver - # we set the allow_reuse_address incase something hangs can still bind to port - class ReusableTCPServer(SocketServer.TCPServer): allow_reuse_address=True - # specify the httpd service on 0.0.0.0 (all interfaces) on port 80 - httpd = ReusableTCPServer(("0.0.0.0", PORT),MyHandler) - # thread this mofo - thread.start_new_thread(httpd.serve_forever,()) - # handle keyboard interrupts - except KeyboardInterrupt: - core.print_info("Exiting the SET web server...") - httpd.socket.close() - except socket.error, exc: - print "Caught exception socket.error : %s" % exc - # handle the rest - #except Exception: - # print "[*] Exiting the SET web server...\n" - # httpd.socket.close() - self.webserverStarted = True - - 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: - if str(row[11]) == "Commercials" and self.DEBUG == False: - continue - timeB = datetime.strptime(row[8], '%I:%M:%S %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. - * ...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, nowPlayingTitle): - - now = datetime.now() - now = now.replace(year=1900, month=1, day=1) - midnight = now.replace(hour=23, minute=59, second=59) - #mutto233 put this here, because to be honest, isn't the current time always now? - if currentTime != None: - currentTime=now - - - time = now.strftime("%B %d, %Y") - doc, tag, text, line = Doc( - - ).ttl() - doc.asis('') - with tag('html'): - with tag('head'): - with tag('title'): - text(time + " - Daily Pseudo Schedule") - doc.asis('') - doc.asis('') - doc.asis(""" - - """) - if bgImageURL != None: - doc.asis('') - with tag('body'): - with tag('div', klass='container mt-3'): - with tag('div', klass='row make-white'): - with tag('div'): - with tag('div'): - line('h1', self.HTML_PSEUDO_TITLE, klass='col-12 pl-0') - with tag('div'): - line('h3', time, klass='col-12 pl-1') - line('h3', - "Now Playing: "+nowPlayingTitle, - klass='col-12 pl-1', - style="color:red;") - 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') - numberIncrease = 0 - for row in datalist: - if str(row[11]) == "Commercials" and self.DEBUG == False: - continue - numberIncrease += 1 - with tag('tbody'): - if currentTime != None: - currentTime = currentTime.replace(year=1900, month=1, day=1) - timeBStart = datetime.strptime(row[8], '%I:%M:%S %p') - timeBStart = timeBStart.replace(year=1900, month=1, day=1) - try: - timeBEnd = datetime.strptime(row[9], '%Y-%m-%d %H:%M:%S.%f') - timeBEnd = timeBEnd.replace(day=1) - except: - timeBEnd = datetime.strptime(row[9], '%Y-%m-%d %H:%M:%S') - timeBEnd = timeBEnd.replace(day=1) - #print timeBStart - #print "%s" % row[3] - #print "%s, %s, %s, %s, %s" % (currentTime, timeBStart, timeBEnd, midnight,midnight.replace(hour=0,minute=0,second=0)) - #print "%s" % (timeBStart - timeBEnd).total_seconds() - #print "%s, %s, %s, %s" % ((currentTime-timeBStart).total_seconds(),(midnight-currentTime).total_seconds(),(currentTime-midnight.replace(hour=0,minute=0,second=0)).total_seconds(),(timeBEnd-currentTime).total_seconds()) - 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]) - elif ((currentTime - timeBStart).total_seconds() >= 0 and \ - (timeBEnd - currentTime).total_seconds() >= 0) or \ - ((timeBStart - timeBEnd).total_seconds() >= 0 and \ - (((currentTime-midnight.replace(hour=0,minute=0,second=0)).total_seconds() >= 0 and \ - (timeBEnd-currentTime).total_seconds() >= 0) or - ((currentTime-timeBStart).total_seconds() >= 0 and \ - (midnight-currentTime).total_seconds() >= 0))): - - print "+++++ Currently Playing:", row[3] - - 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]) - 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]) - return indent(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): - - now = datetime.now() - fileName = "index.html" - writepath = './' if os.path.basename(os.getcwd()) == "schedules" else "./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) - self.start_server() - - ''' - * - * 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 = './' if os.path.basename(os.getcwd()) == "schedules" else "./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) - - ''' - * - * Write 0 or 1 to file for the ajax in the schedule.html to know when to refresh - * @param data: xml string - * @return null - * - ''' - def write_refresh_bool_to_file(self): - - fileName = "pseudo_refresh.txt" - writepath = './' if os.path.basename(os.getcwd()) == "schedules" else "./schedules/" - first_line = '' - if not os.path.exists(writepath): - os.makedirs(writepath) - if not os.path.exists(writepath+fileName): - file(writepath+fileName, 'w').close() - mode = 'r+' - with open(writepath+fileName, mode) as f: - f.seek(0) - first_line = f.read() - if self.DEBUG: - print "+++++ Html refresh flag: {}".format(first_line) - if first_line == '' or first_line == "0": - f.seek(0) - f.truncate() - f.write("1") - else: - f.seek(0) - f.truncate() - f.write("0") - - ''' - * - * 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, offset, customSectionName, durationAmount): - - try: - if mediaType == "TV Shows": -# print "Here, Trying to play custom type: ", customSectionName - print "+++++ Checking Duration for a Match: " - mediaItems = self.PLEX.library.section(customSectionName).get(mediaParentTitle).episodes() - for item in mediaItems: -# print item.duration - if item.title == mediaTitle and item.duration == durationAmount: - print "+++++ MATCH FOUND in %s" % item - for client in self.PLEX_CLIENTS: - clientItem = self.PLEX.client(client) - clientItem.playMedia(item, offset=offset) - break - elif mediaType == "Movies": - movies = self.PLEX.library.section(customSectionName).search(title=mediaTitle) - print "+++++ Checking Duration for a Match: " - for item in movies: -# print item.duration - if item.duration == durationAmount: - print "+++++ MATCH FOUND in %s" % item - movie = item - break - for client in self.PLEX_CLIENTS: - clientItem = self.PLEX.client(client) - clientItem.playMedia(movie, offset=offset) - elif mediaType == "Commercials": - # This one is a bit more complicated, since we have the dirty gap fix possible - # Basically, we are going to just assume it isn't a dirty gap fix, and if it is, - # We will just play the first value - COMMERCIAL_PADDING = config.commercialPadding - movies = self.PLEX.library.section(customSectionName).search(title=mediaTitle) - print "+++++ Checking Duration for a Match: " - for item in movies: - #print item - #print item.duration - if (item.duration+1000*COMMERCIAL_PADDING) == durationAmount or item.duration == durationAmount: - print "+++++ MATCH FOUND in %s" % item - movie = item - break - try: - movie - except: - print "+++++ Commercial is NOT FOUND, my guess is this is the dirty gap. Picking first one" - movie = movies[0] - - for client in self.PLEX_CLIENTS: - clientItem = self.PLEX.client(client) - clientItem.playMedia(movie, offset=offset) - else: - print("##### Not sure how to play {}".format(customSectionName)) - print "+++++ Done." - except Exception as e: - print e.__doc__ - print e.message - print "##### There was an error trying to play the media." - pass - - def stop_media(self): - - try: - self.my_logger.debug('Trying to stop media.') - for client in self.PLEX_CLIENTS: - clientItem = self.PLEX.client(client) - clientItem.stop(mtype='video') - self.my_logger.debug('Done.') - except Exception as e: - self.my_logger.debug('stop_media - except.', e) - pass - ''' - * - * 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() - """c.execute("SELECT * FROM daily_schedule") - datalist = list(c.fetchall()) - """ - for row in datalist: - try: - endTime = datetime.strptime(row[9], '%Y-%m-%d %H:%M:%S.%f') - except ValueError: - endTime = datetime.strptime(row[9], '%Y-%m-%d %H:%M:%S') - if currentTime.hour == endTime.hour: - if currentTime.minute == endTime.minute: - if currentTime.second == endTime.second: - if self.DEBUG: - 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)) - self.write_refresh_bool_to_file() - break - - def play(self, row, datalist, offset=0): - - print str("##### Starting Media: '{}'".format(row[3])).encode('UTF-8') - print str("##### Media Offset: '{}' seconds.".format(int(offset / 1000))).encode('UTF-8') - if self.DEBUG: - print str(row).encode('UTF-8') - timeB = datetime.strptime(row[8], '%I:%M:%S %p') - - print "Here, row[13]", row[13] - - self.play_media(row[11], row[6], row[3], offset, row[13], row[7]) - self.write_schedule_to_file( - self.get_html_from_daily_schedule( - timeB, - self.get_show_photo( - row[13], - row[6] if row[11] == "TV Shows" else row[3], row[7] - ), - datalist, - row[6] + " - " + row[3] if row[11] == "TV Shows" else row[3] - ) - ) - self.write_refresh_bool_to_file() - """Generate / write XML to file - """ - self.write_xml_to_file( - self.get_xml_from_daily_schedule( - timeB, - self.get_show_photo( - row[13], - row[6] if row[11] == "TV Shows" else row[3], row[7] - ), - datalist - ) - ) - try: - self.my_logger.debug('Trying to play: ' + row[3]) - except: - pass - - ''' - * - * 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; - currentTime = datetime.now() - """c.execute("SELECT * FROM daily_schedule ORDER BY datetime(startTimeUnix) ASC") - datalist = list(c.fetchall())""" - try: - self.my_logger.debug('TV Controller') - except: - pass - for row in datalist: - timeB = datetime.strptime(row[8], '%I:%M:%S %p') - if currentTime.hour == timeB.hour: - if currentTime.minute == timeB.minute: - if currentTime.second == timeB.second: - print("Starting Media: " + row[3]) - print(row) - self.play_media(row[11], row[6], row[3], row[13], row[7]) - self.write_schedule_to_file( - self.get_html_from_daily_schedule( - timeB, - self.get_show_photo( - row[13], - row[6] if row[11] == "TV Shows" else row[3], row[7] - ), - datalist, - row[6] + " - " + row[3] if row[11] == "TV Shows" else row[3] - ) - ) - self.write_refresh_bool_to_file() - """Generate / write XML to file - """ - self.write_xml_to_file( - self.get_xml_from_daily_schedule( - timeB, - self.get_show_photo( - row[13], - row[6] if row[11] == "TV Shows" else row[3], row[7] - ), - datalist - ) - ) - try: - self.my_logger.debug('Trying to play: ' + row[3]) - except: - pass - break - datalistLengthMonitor += 1 - if datalistLengthMonitor >= len(datalist): - self.check_for_end_time(datalist) - - def manually_get_now_playing_bg_image(self, currentTime, datalist): - now = datetime.now() - now = now.replace(year=1900, month=1, day=1) - midnight = now.replace(hour=23, minute=59, second=59) - increase_var = 0 - - for row in datalist: - #print row[8] - #print row[9] - if str(row[11]) == "Commercials" and self.DEBUG == False: - continue - timeBStart = datetime.strptime(row[8], '%I:%M:%S %p') - timeBStart = timeBStart.replace(year=1900, month=1, day=1) - try: - timeBEnd = datetime.strptime(row[9], '%Y-%m-%d %H:%M:%S.%f') - timeBEnd = timeBEnd.replace(day=1) - except: - timeBEnd = datetime.strptime(row[9], '%Y-%m-%d %H:%M:%S') - timeBEnd = timeBEnd.replace(day=1) - - #print ((currentTime - timeBStart).total_seconds() >= 0 and \ - # (timeBEnd - currentTime).total_seconds() >= 0) - - #print currentTime.minute - #print timeBStart.minute - - if ((currentTime - timeBStart).total_seconds() >= 0 and \ - (timeBEnd - currentTime).total_seconds() >= 0) or \ - ((timeBStart - timeBEnd).total_seconds() >= 0 and \ - (((currentTime-midnight.replace(hour=0,minute=0,second=0)).total_seconds() >= 0 and \ - (timeBEnd-currentTime).total_seconds() >= 0) or - ((currentTime-timeBStart).total_seconds() >= 0 and \ - (midnight-currentTime).total_seconds() >= 0))): - - print "+++++ Made the conditional & found item: {}".format(row[6]) - - return self.get_show_photo( - row[13], - row[6] if row[11] == "TV Shows" else row[3], row[7] - ) - - else: - - pass - - increase_var += 1 - - if len(datalist) >= increase_var: - print("+++++ In 'manually_get_now_playing_bg_image()'. " - "Reached the end of the schedule. No bgImages found.") - return None - - def manually_get_now_playing_title(self, currentTime, datalist): - now = datetime.now() - now = now.replace(year=1900, month=1, day=1) - midnight = now.replace(hour=23, minute=59, second=59) - increase_var = 0 - - for row in datalist: - #print row[8] - #print row[9] - """if str(row[11]) == "Commercials" and self.DEBUG == False: - continue""" - timeBStart = datetime.strptime(row[8], '%I:%M:%S %p') - timeBStart = timeBStart.replace(year=1900, month=1, day=1) - try: - timeBEnd = datetime.strptime(row[9], '%Y-%m-%d %H:%M:%S.%f') - timeBEnd = timeBEnd.replace(day=1) - except: - timeBEnd = datetime.strptime(row[9], '%Y-%m-%d %H:%M:%S') - timeBEnd = timeBEnd.replace(day=1) - - #print ((currentTime - timeBStart).total_seconds() >= 0 and \ - # (timeBEnd - currentTime).total_seconds() >= 0) - - #print currentTime.minute - #print timeBStart.minute - - if ((currentTime - timeBStart).total_seconds() >= 0 and \ - (timeBEnd - currentTime).total_seconds() >= 0) or \ - ((timeBStart - timeBEnd).total_seconds() >= 0 and \ - (((currentTime-midnight.replace(hour=0,minute=0,second=0)).total_seconds() >= 0 and \ - (timeBEnd-currentTime).total_seconds() >= 0) or - ((currentTime-timeBStart).total_seconds() >= 0 and \ - (midnight-currentTime).total_seconds() >= 0))): - - print "+++++ Made the conditional & found item: {}".format(row[6]) - - return row[6] + " - " + row[3] if row[11] == "TV Shows" else row[3] - - else: - - pass - - increase_var += 1 - - if len(datalist) >= increase_var: - print("+++++ In 'manually_get_now_playing_title()'. " - "Reached the end of the schedule. No bgImages found.") - return '' - - def make_xml_schedule(self, datalist): - - print "##### ", "Writing XML / HTML to file." - now = datetime.now() - now = now.replace(year=1900, month=1, day=1) - - bgImage = self.manually_get_now_playing_bg_image(now, datalist) - - itemTitle = self.manually_get_now_playing_title(now, datalist) - - print "+++++ The path to the bgImage: {}".format(bgImage) - - self.write_refresh_bool_to_file() - self.write_schedule_to_file(self.get_html_from_daily_schedule(now, bgImage, datalist, itemTitle)) +#!/usr/bin/env python + +import os, sys +import socket +import logging +import logging.handlers +from datetime import datetime +import sqlite3 +import thread,SocketServer,SimpleHTTPServer +from plexapi.server import PlexServer +from yattag import Doc +from yattag import indent +import pseudo_config as config + +class PseudoDailyScheduleController(): + + def __init__(self, + server, + token, + clients, + controllerServerPath = '', + controllerServerPort = '8000', + debugMode = False, + htmlPseudoTitle = "Daily PseudoChannel" + ): + + self.PLEX = PlexServer(server, token) + self.BASE_URL = server + self.TOKEN = token + self.PLEX_CLIENTS = clients + self.CONTROLLER_SERVER_PATH = controllerServerPath + self.CONTROLLER_SERVER_PORT = controllerServerPort if controllerServerPort != '' else '80' + self.DEBUG = debugMode + self.webserverStarted = False + self.HTML_PSEUDO_TITLE = htmlPseudoTitle + try: + self.my_logger = logging.getLogger('MyLogger') + self.my_logger.setLevel(logging.DEBUG) + self.handler = logging.handlers.SysLogHandler(address = '/dev/log') + self.my_logger.addHandler(self.handler) + except: + pass + + ''' + * + * 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, showtitle, durationAmount): + + backgroundImagePath = None + backgroundImgURL = '' + # First, we are going to search for movies/commercials + try: + movies = self.PLEX.library.section(section).search(title=showtitle) + #print "+++++ Checking Duration for a Match: " + for item in movies: +# print item.duration + if item.duration == durationAmount: + #print "+++++ MATCH FOUND in %s" % item + backgroundImagePath = item + break + except: + return backgroundImgURL + + if backgroundImagePath == None: + # We will try the old way, for the sake of TV Shows + try: + backgroundImagePath = self.PLEX.library.section(section).get(showtitle) + except: + return backgroundImgURL + + if backgroundImagePath != None and isinstance(backgroundImagePath.art, str): + backgroundImgURL = self.BASE_URL+backgroundImagePath.art+"?X-Plex-Token="+self.TOKEN + return backgroundImgURL + + def start_server(self): + + if self.webserverStarted == False and self.CONTROLLER_SERVER_PATH != '': + """Changing dir to the schedules dir.""" + web_dir = os.path.abspath(os.path.join(os.path.dirname( __file__ ), '..', 'schedules')) + os.chdir(web_dir) + PORT = int(self.CONTROLLER_SERVER_PORT) + class MyHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): + + def log_message(self, format, *args): + return + + global httpd + try: + #print "Starting webserver at port: ", PORT + # create the httpd handler for the simplehttpserver + # we set the allow_reuse_address incase something hangs can still bind to port + class ReusableTCPServer(SocketServer.TCPServer): allow_reuse_address=True + # specify the httpd service on 0.0.0.0 (all interfaces) on port 80 + httpd = ReusableTCPServer(("0.0.0.0", PORT),MyHandler) + # thread this mofo + thread.start_new_thread(httpd.serve_forever,()) + # handle keyboard interrupts + except KeyboardInterrupt: + core.print_info("Exiting the SET web server...") + httpd.socket.close() + except socket.error, exc: + print "Caught exception socket.error : %s" % exc + # handle the rest + #except Exception: + # print "[*] Exiting the SET web server...\n" + # httpd.socket.close() + self.webserverStarted = True + + 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: + if str(row[11]) == "Commercials" and self.DEBUG == False: + continue + timeB = datetime.strptime(row[8], '%I:%M:%S %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. + * ...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, nowPlayingTitle): + + now = datetime.now() + now = now.replace(year=1900, month=1, day=1) + midnight = now.replace(hour=23, minute=59, second=59) + #mutto233 put this here, because to be honest, isn't the current time always now? + if currentTime != None: + currentTime=now + + + time = now.strftime("%B %d, %Y") + doc, tag, text, line = Doc( + + ).ttl() + doc.asis('') + with tag('html'): + with tag('head'): + with tag('title'): + text(time + " - Daily Pseudo Schedule") + doc.asis('') + doc.asis('') + doc.asis(""" + + """) + if bgImageURL != None: + doc.asis('') + with tag('body'): + with tag('div', klass='container mt-3'): + with tag('div', klass='row make-white'): + with tag('div'): + with tag('div'): + line('h1', self.HTML_PSEUDO_TITLE, klass='col-12 pl-0') + with tag('div'): + line('h3', time, klass='col-12 pl-1') + line('h3', + "Now Playing: "+nowPlayingTitle, + klass='col-12 pl-1', + style="color:red;") + 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') + numberIncrease = 0 + for row in datalist: + if str(row[11]) == "Commercials" and self.DEBUG == False: + continue + numberIncrease += 1 + with tag('tbody'): + if currentTime != None: + currentTime = currentTime.replace(year=1900, month=1, day=1) + timeBStart = datetime.strptime(row[8], '%I:%M:%S %p') + timeBStart = timeBStart.replace(year=1900, month=1, day=1) + try: + timeBEnd = datetime.strptime(row[9], '%Y-%m-%d %H:%M:%S.%f') + timeBEnd = timeBEnd.replace(day=1) + except: + timeBEnd = datetime.strptime(row[9], '%Y-%m-%d %H:%M:%S') + timeBEnd = timeBEnd.replace(day=1) + #print timeBStart + #print "%s" % row[3] + #print "%s, %s, %s, %s, %s" % (currentTime, timeBStart, timeBEnd, midnight,midnight.replace(hour=0,minute=0,second=0)) + #print "%s" % (timeBStart - timeBEnd).total_seconds() + #print "%s, %s, %s, %s" % ((currentTime-timeBStart).total_seconds(),(midnight-currentTime).total_seconds(),(currentTime-midnight.replace(hour=0,minute=0,second=0)).total_seconds(),(timeBEnd-currentTime).total_seconds()) + 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]) + elif ((currentTime - timeBStart).total_seconds() >= 0 and \ + (timeBEnd - currentTime).total_seconds() >= 0) or \ + ((timeBStart - timeBEnd).total_seconds() >= 0 and \ + (((currentTime-midnight.replace(hour=0,minute=0,second=0)).total_seconds() >= 0 and \ + (timeBEnd-currentTime).total_seconds() >= 0) or + ((currentTime-timeBStart).total_seconds() >= 0 and \ + (midnight-currentTime).total_seconds() >= 0))): + + print "+++++ Currently Playing:", row[3] + + 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]) + 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]) + return indent(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): + + now = datetime.now() + fileName = "index.html" + writepath = './' if os.path.basename(os.getcwd()) == "schedules" else "./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) + self.start_server() + + ''' + * + * 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 = './' if os.path.basename(os.getcwd()) == "schedules" else "./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) + + ''' + * + * Write 0 or 1 to file for the ajax in the schedule.html to know when to refresh + * @param data: xml string + * @return null + * + ''' + def write_refresh_bool_to_file(self): + + fileName = "pseudo_refresh.txt" + writepath = './' if os.path.basename(os.getcwd()) == "schedules" else "./schedules/" + first_line = '' + if not os.path.exists(writepath): + os.makedirs(writepath) + if not os.path.exists(writepath+fileName): + file(writepath+fileName, 'w').close() + mode = 'r+' + with open(writepath+fileName, mode) as f: + f.seek(0) + first_line = f.read() + if self.DEBUG: + print "+++++ Html refresh flag: {}".format(first_line) + if first_line == '' or first_line == "0": + f.seek(0) + f.truncate() + f.write("1") + else: + f.seek(0) + f.truncate() + f.write("0") + + ''' + * + * 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, offset, customSectionName, durationAmount): + # Check for necessary client override, if we have a folder of "channels_" + cwd = os.getcwd() + if "channels_" in cwd: + head,sep,tail = cwd.partition('channels_') + head,sep,tail = tail.partition('/') + self.PLEX_CLIENTS = [head] + print "CLIENT OVERRIDE: %s" % self.PLEX_CLIENTS + + try: + if mediaType == "TV Shows": +# print "Here, Trying to play custom type: ", customSectionName + print "+++++ Checking Duration for a Match: " + mediaItems = self.PLEX.library.section(customSectionName).get(mediaParentTitle).episodes() + for item in mediaItems: +# print item.duration + if item.title == mediaTitle and item.duration == durationAmount: + print "+++++ MATCH FOUND in %s" % item + for client in self.PLEX_CLIENTS: + clientItem = self.PLEX.client(client) + clientItem.playMedia(item, offset=offset) + break + elif mediaType == "Movies": + movies = self.PLEX.library.section(customSectionName).search(title=mediaTitle) + print "+++++ Checking Duration for a Match: " + for item in movies: +# print item.duration + if item.duration == durationAmount: + print "+++++ MATCH FOUND in %s" % item + movie = item + break + for client in self.PLEX_CLIENTS: + clientItem = self.PLEX.client(client) + clientItem.playMedia(movie, offset=offset) + elif mediaType == "Commercials": + # This one is a bit more complicated, since we have the dirty gap fix possible + # Basically, we are going to just assume it isn't a dirty gap fix, and if it is, + # We will just play the first value + COMMERCIAL_PADDING = config.commercialPadding + movies = self.PLEX.library.section(customSectionName).search(title=mediaTitle) + print "+++++ Checking Duration for a Match: " + for item in movies: + #print item + #print item.duration + if (item.duration+1000*COMMERCIAL_PADDING) == durationAmount or item.duration == durationAmount: + print "+++++ MATCH FOUND in %s" % item + movie = item + break + try: + movie + except: + print "+++++ Commercial is NOT FOUND, my guess is this is the dirty gap. Picking first one" + movie = movies[0] + + for client in self.PLEX_CLIENTS: + clientItem = self.PLEX.client(client) + clientItem.playMedia(movie, offset=offset) + else: + print("##### Not sure how to play {}".format(customSectionName)) + print "+++++ Done." + except Exception as e: + print e.__doc__ + print e.message + print "##### There was an error trying to play the media." + pass + + def stop_media(self): + + try: + self.my_logger.debug('Trying to stop media.') + for client in self.PLEX_CLIENTS: + clientItem = self.PLEX.client(client) + clientItem.stop(mtype='video') + self.my_logger.debug('Done.') + except Exception as e: + self.my_logger.debug('stop_media - except.', e) + pass + ''' + * + * 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() + """c.execute("SELECT * FROM daily_schedule") + datalist = list(c.fetchall()) + """ + for row in datalist: + try: + endTime = datetime.strptime(row[9], '%Y-%m-%d %H:%M:%S.%f') + except ValueError: + endTime = datetime.strptime(row[9], '%Y-%m-%d %H:%M:%S') + if currentTime.hour == endTime.hour: + if currentTime.minute == endTime.minute: + if currentTime.second == endTime.second: + if self.DEBUG: + 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)) + self.write_refresh_bool_to_file() + break + + def play(self, row, datalist, offset=0): + + print str("##### Starting Media: '{}'".format(row[3])).encode('UTF-8') + print str("##### Media Offset: '{}' seconds.".format(int(offset / 1000))).encode('UTF-8') + if self.DEBUG: + print str(row).encode('UTF-8') + timeB = datetime.strptime(row[8], '%I:%M:%S %p') + + print "Here, row[13]", row[13] + + self.play_media(row[11], row[6], row[3], offset, row[13], row[7]) + self.write_schedule_to_file( + self.get_html_from_daily_schedule( + timeB, + self.get_show_photo( + row[13], + row[6] if row[11] == "TV Shows" else row[3], row[7] + ), + datalist, + row[6] + " - " + row[3] if row[11] == "TV Shows" else row[3] + ) + ) + self.write_refresh_bool_to_file() + """Generate / write XML to file + """ + self.write_xml_to_file( + self.get_xml_from_daily_schedule( + timeB, + self.get_show_photo( + row[13], + row[6] if row[11] == "TV Shows" else row[3], row[7] + ), + datalist + ) + ) + try: + self.my_logger.debug('Trying to play: ' + row[3]) + except: + pass + + ''' + * + * 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; + currentTime = datetime.now() + """c.execute("SELECT * FROM daily_schedule ORDER BY datetime(startTimeUnix) ASC") + datalist = list(c.fetchall())""" + try: + self.my_logger.debug('TV Controller') + except: + pass + for row in datalist: + timeB = datetime.strptime(row[8], '%I:%M:%S %p') + if currentTime.hour == timeB.hour: + if currentTime.minute == timeB.minute: + if currentTime.second == timeB.second: + print("Starting Media: " + row[3]) + print(row) + self.play_media(row[11], row[6], row[3], row[13], row[7]) + self.write_schedule_to_file( + self.get_html_from_daily_schedule( + timeB, + self.get_show_photo( + row[13], + row[6] if row[11] == "TV Shows" else row[3], row[7] + ), + datalist, + row[6] + " - " + row[3] if row[11] == "TV Shows" else row[3] + ) + ) + self.write_refresh_bool_to_file() + """Generate / write XML to file + """ + self.write_xml_to_file( + self.get_xml_from_daily_schedule( + timeB, + self.get_show_photo( + row[13], + row[6] if row[11] == "TV Shows" else row[3], row[7] + ), + datalist + ) + ) + try: + self.my_logger.debug('Trying to play: ' + row[3]) + except: + pass + break + datalistLengthMonitor += 1 + if datalistLengthMonitor >= len(datalist): + self.check_for_end_time(datalist) + + def manually_get_now_playing_bg_image(self, currentTime, datalist): + now = datetime.now() + now = now.replace(year=1900, month=1, day=1) + midnight = now.replace(hour=23, minute=59, second=59) + increase_var = 0 + + for row in datalist: + #print row[8] + #print row[9] + if str(row[11]) == "Commercials" and self.DEBUG == False: + continue + timeBStart = datetime.strptime(row[8], '%I:%M:%S %p') + timeBStart = timeBStart.replace(year=1900, month=1, day=1) + try: + timeBEnd = datetime.strptime(row[9], '%Y-%m-%d %H:%M:%S.%f') + timeBEnd = timeBEnd.replace(day=1) + except: + timeBEnd = datetime.strptime(row[9], '%Y-%m-%d %H:%M:%S') + timeBEnd = timeBEnd.replace(day=1) + + #print ((currentTime - timeBStart).total_seconds() >= 0 and \ + # (timeBEnd - currentTime).total_seconds() >= 0) + + #print currentTime.minute + #print timeBStart.minute + + if ((currentTime - timeBStart).total_seconds() >= 0 and \ + (timeBEnd - currentTime).total_seconds() >= 0) or \ + ((timeBStart - timeBEnd).total_seconds() >= 0 and \ + (((currentTime-midnight.replace(hour=0,minute=0,second=0)).total_seconds() >= 0 and \ + (timeBEnd-currentTime).total_seconds() >= 0) or + ((currentTime-timeBStart).total_seconds() >= 0 and \ + (midnight-currentTime).total_seconds() >= 0))): + + print "+++++ Made the conditional & found item: {}".format(row[6]) + + return self.get_show_photo( + row[13], + row[6] if row[11] == "TV Shows" else row[3], row[7] + ) + + else: + + pass + + increase_var += 1 + + if len(datalist) >= increase_var: + print("+++++ In 'manually_get_now_playing_bg_image()'. " + "Reached the end of the schedule. No bgImages found.") + return None + + def manually_get_now_playing_title(self, currentTime, datalist): + now = datetime.now() + now = now.replace(year=1900, month=1, day=1) + midnight = now.replace(hour=23, minute=59, second=59) + increase_var = 0 + + for row in datalist: + #print row[8] + #print row[9] + """if str(row[11]) == "Commercials" and self.DEBUG == False: + continue""" + timeBStart = datetime.strptime(row[8], '%I:%M:%S %p') + timeBStart = timeBStart.replace(year=1900, month=1, day=1) + try: + timeBEnd = datetime.strptime(row[9], '%Y-%m-%d %H:%M:%S.%f') + timeBEnd = timeBEnd.replace(day=1) + except: + timeBEnd = datetime.strptime(row[9], '%Y-%m-%d %H:%M:%S') + timeBEnd = timeBEnd.replace(day=1) + + #print ((currentTime - timeBStart).total_seconds() >= 0 and \ + # (timeBEnd - currentTime).total_seconds() >= 0) + + #print currentTime.minute + #print timeBStart.minute + + if ((currentTime - timeBStart).total_seconds() >= 0 and \ + (timeBEnd - currentTime).total_seconds() >= 0) or \ + ((timeBStart - timeBEnd).total_seconds() >= 0 and \ + (((currentTime-midnight.replace(hour=0,minute=0,second=0)).total_seconds() >= 0 and \ + (timeBEnd-currentTime).total_seconds() >= 0) or + ((currentTime-timeBStart).total_seconds() >= 0 and \ + (midnight-currentTime).total_seconds() >= 0))): + + print "+++++ Made the conditional & found item: {}".format(row[6]) + + return row[6] + " - " + row[3] if row[11] == "TV Shows" else row[3] + + else: + + pass + + increase_var += 1 + + if len(datalist) >= increase_var: + print("+++++ In 'manually_get_now_playing_title()'. " + "Reached the end of the schedule. No bgImages found.") + return '' + + def make_xml_schedule(self, datalist): + + print "##### ", "Writing XML / HTML to file." + now = datetime.now() + now = now.replace(year=1900, month=1, day=1) + + bgImage = self.manually_get_now_playing_bg_image(now, datalist) + + itemTitle = self.manually_get_now_playing_title(now, datalist) + + print "+++++ The path to the bgImage: {}".format(bgImage) + + self.write_refresh_bool_to_file() + self.write_schedule_to_file(self.get_html_from_daily_schedule(now, bgImage, datalist, itemTitle)) self.write_xml_to_file(self.get_xml_from_daily_schedule(None, None, datalist)) \ No newline at end of file