Merge branch 'develop'

Merging various bug fixes and enhancements, namely, Commercial Injection, the multiple DB entry issue on update (-u), using 'threading' Timer for the clock ticks versus an endles while loop (for performance / precision), Google Calendar integration, etc.
This commit is contained in:
Justin Emter
2017-07-27 14:17:43 -07:00
14 changed files with 2128 additions and 1245 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,32 +1,49 @@
#!/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'
baseurl = 'the url to your server'
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"],
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"
7) If using the Google Calendar integration exclusively, set this to true below:
useGoogleCalendar
"""
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
baseurl = 'http://media.home:32400'
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 = plex_token.baseurl
token = plex_token.token
gkey = plex_token.gkey
'''
*
@@ -36,8 +53,16 @@ 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"],
}
useGoogleCalendar = True
useCommercialInjection = True
dailyUpdateTime = "12:00 AM"
debug_mode = False

View File

@@ -1,65 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<schedule>
<everyday>
<time title="Looney Tunes" type="series" strict-time="true" time-shift="1" overlap-max="">6:00 AM</time>
<time title="Looney Tunes" type="series" strict-time="false" time-shift="1" overlap-max="">6:30 AM</time>
<time title="Looney Tunes" type="series" strict-time="false" time-shift="1" overlap-max="">7:00 AM</time>
<time title="Looney Tunes" type="series" strict-time="false" time-shift="1" overlap-max="">7:30 AM</time>
</everyday>
<everyday>
<time title="Looney Tunes" type="series" strict-time="true" time-shift="1" overlap-max="">6:00 AM</time>
<time title="Looney Tunes" type="series" strict-time="false" time-shift="1" overlap-max="">6:30 AM</time>
<time title="Looney Tunes" type="series" strict-time="false" time-shift="1" overlap-max="">7:00 AM</time>
<time title="Looney Tunes" type="series" strict-time="false" time-shift="1" overlap-max="">7:30 AM</time>
</everyday>
<mondays></mondays>
<tuesdays></tuesdays>
<wednesdays></wednesdays>
<thursdays></thursdays>
<fridays>
<time title="Looney Tunes" type="series" strict-time="true" time-shift="1" overlap-max="">12:30 AM</time>
</fridays>
<time title="Looney Tunes" type="series" strict-time="true" time-shift="1" overlap-max="">12:30 AM</time>
</fridays>
<saturdays></saturdays>
<sundays></sundays>
<weekends>
<time title="Garfield &#38; Friends" type="series" strict-time="false" time-shift="5" overlap-max="">8:00 AM</time>
<time title="Garfield &#38; Friends" type="series" strict-time="false" time-shift="5" overlap-max="">8:30 AM</time>
<time title="Garfield &#38; Friends" type="series" strict-time="false" time-shift="5" overlap-max="">9:00 AM</time>
<time title="Garfield &#38; Friends" type="series" strict-time="false" time-shift="5" overlap-max="">9:30 AM</time>
<time title="Garfield &#38; Friends" type="series" strict-time="false" time-shift="5" overlap-max="">8:00 AM</time>
<time title="Garfield &#38; Friends" type="series" strict-time="false" time-shift="5" overlap-max="">8:30 AM</time>
<time title="Garfield &#38; Friends" type="series" strict-time="false" time-shift="5" overlap-max="">9:00 AM</time>
<time title="Garfield &#38; Friends" type="series" strict-time="false" time-shift="5" overlap-max="">9:30 AM</time>
<time title="Batman" type="series" strict-time="false" time-shift="5" overlap-max="">10:00 AM</time>
<time title="Batman" type="series" strict-time="false" time-shift="5" overlap-max="">10:30 AM</time>
<time title="Batman" type="series" strict-time="false" time-shift="5" overlap-max="">10:00 AM</time>
<time title="Batman" type="series" strict-time="false" time-shift="5" overlap-max="">10:30 AM</time>
<time title="random" type="movie" strict-time="false" time-shift="5" overlap-max="">11:00 AM</time>
<time title="random" type="movie" strict-time="false" time-shift="5" overlap-max="">11:00 AM</time>
<time title="The Adventures of Pete &#38; Pete" type="series" strict-time="false" time-shift="5" overlap-max="">12:30 PM</time>
<time title="The Adventures of Pete &#38; Pete" type="series" strict-time="false" time-shift="5" overlap-max="">1:00 PM</time>
<time title="Gilligan's Island" type="series" strict-time="false" time-shift="5" overlap-max="">1:30 PM</time>
<time title="Gilligan's Island" type="series" strict-time="false" time-shift="5" overlap-max="">1:40 PM</time>
<time title="The Jetsons" type="series" strict-time="false" time-shift="5" overlap-max="">2:00 PM</time>
<time title="The Adventures of Pete &#38; Pete" type="series" strict-time="false" time-shift="5" overlap-max="">12:30 PM</time>
<time title="The Adventures of Pete &#38; Pete" type="series" strict-time="false" time-shift="5" overlap-max="">1:00 PM</time>
<time title="Gilligan's Island" type="series" strict-time="false" time-shift="5" overlap-max="">1:30 PM</time>
<time title="Gilligan's Island" type="series" strict-time="false" time-shift="5" overlap-max="">1:40 PM</time>
<time title="The Jetsons" type="series" strict-time="false" time-shift="5" overlap-max="">2:00 PM</time>
<time title="random" type="movie" strict-time="true" time-shift="5" overlap-max="">2:30 PM</time>
<time title="random" type="movie" strict-time="true" time-shift="5" overlap-max="">2:30 PM</time>
<time title="The Wonder Years" type="series" strict-time="false" time-shift="5" overlap-max="">4:30 PM</time>
<time title="The Wonder Years" type="series" strict-time="false" time-shift="5" overlap-max="">5:00 PM</time>
<time title="The Wonder Years" type="series" strict-time="false" time-shift="5" overlap-max="">4:30 PM</time>
<time title="The Wonder Years" type="series" strict-time="false" time-shift="5" overlap-max="">5:00 PM</time>
<time title="The Simpsons" type="series" strict-time="true" time-shift="5" overlap-max="">5:30 PM</time>
<time title="The Simpsons" type="series" strict-time="false" time-shift="5" overlap-max="">6:00 PM</time>
<time title="The Simpsons" type="series" strict-time="true" time-shift="5" overlap-max="">5:30 PM</time>
<time title="The Simpsons" type="series" strict-time="false" time-shift="5" overlap-max="">6:00 PM</time>
<time title="Sherlock" type="series" strict-time="false" time-shift="5" overlap-max="">6:30 PM</time>
<time title="Sherlock" type="series" strict-time="false" time-shift="5" overlap-max="">6:30 PM</time>
<time title="Arrested Development" type="series" strict-time="false" time-shift="5" overlap-max="">7:00 PM</time>
<time title="Arrested Development" type="series" strict-time="false" time-shift="5" overlap-max="">7:30 PM</time>
<time title="Arrested Development" type="series" strict-time="false" time-shift="5" overlap-max="">7:00 PM</time>
<time title="Arrested Development" type="series" strict-time="false" time-shift="5" overlap-max="">7:30 PM</time>
<time title="random" type="movie" strict-time="false" time-shift="5" overlap-max="">8:00 PM</time>
<time title="random" type="movie" strict-time="false" time-shift="5" overlap-max="">8:00 PM</time>
<time title="Band of Brothers" type="show" strict-time="false" time-shift="5" overlap-max="">10:00 PM</time>
<time title="Beverly Hills, 90210" type="show" strict-time="false" time-shift="5" overlap-max="">10:30 PM</time>
<time title="Band of Brothers" type="show" strict-time="false" time-shift="5" overlap-max="">10:00 PM</time>
<time title="Beverly Hills, 90210" type="show" strict-time="false" time-shift="5" overlap-max="">10:30 PM</time>
<time title="The Flintstones" type="show" strict-time="false" time-shift="5" overlap-max="">11:00 PM</time>
<time title="The Flintstones" type="show" strict-time="false" time-shift="5" overlap-max="">11:30 PM</time>
</weekends>
<weekdays>
<default title="Seinfeld" type="series" ></default>
<time title="The Flintstones" type="show" strict-time="false" time-shift="5" overlap-max="">11:00 PM</time>
<time title="The Flintstones" type="show" strict-time="false" time-shift="5" overlap-max="">11:30 PM</time>
</weekends>
<weekdays>
<default title="Seinfeld" type="series" ></default>
<time title="Garfield &#38; Friends" type="series" strict-time="false" time-shift="5" overlap-max="">8:00 AM</time>
<time title="Garfield &#38; Friends" type="series" strict-time="false" time-shift="5" overlap-max="">8:30 AM</time>
<time title="Garfield &#38; Friends" type="series" strict-time="false" time-shift="5" overlap-max="">9:00 AM</time>
<time title="Garfield &#38; Friends" type="series" strict-time="false" time-shift="5" overlap-max="">9:30 AM</time>
<time title="Garfield &#38; Friends" type="series" strict-time="false" time-shift="5" overlap-max="">8:30 AM</time>
<time title="Garfield &#38; Friends" type="series" strict-time="false" time-shift="5" overlap-max="">9:00 AM</time>
<time title="Garfield &#38; Friends" type="series" strict-time="false" time-shift="5" overlap-max="">9:30 AM</time>
<time title="talespin" type="series" strict-time="false" time-shift="5" overlap-max="">10:00 AM</time>
<time title="talespin" type="series" strict-time="false" time-shift="5" overlap-max="">10:30 AM</time>
@@ -92,7 +92,7 @@
<time title="new girl" type="series" strict-time="false" time-shift="5" overlap-max="">7:30 PM</time>
<time title="the trip" type="series" strict-time="false" time-shift="5" overlap-max="">8:30 PM</time>
<time title="the trip" type="series" strict-time="false" time-shift="5" overlap-max="">8:30 PM</time>
<time title="the trip" type="series" strict-time="false" time-shift="5" overlap-max="">9:00 PM</time>
</weekdays>
</weekdays>
</schedule>

View File

@@ -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,29 @@ 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,
plex_media_id
):
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,
plex_media_id
)

View File

@@ -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,40 @@ 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,
plex_media_id,
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,
plex_media_id
)
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

104
src/GoogleCalendar.py Normal file
View File

@@ -0,0 +1,104 @@
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 os.path as path
import sys
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
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 = credential_dir
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 calendar 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

View File

@@ -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,31 @@ 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,
plex_media_id
):
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.plex_media_id = plex_media_id
self.start_time = natural_start_time
self.end_time = natural_end_time
self.start_time = natural_start_time
self.end_time = natural_end_time

View File

@@ -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,29 @@ 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,
plex_media_id
):
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,
plex_media_id
)

View File

@@ -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,29 @@ 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,
plex_media_id
):
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,
plex_media_id
)

View File

@@ -0,0 +1,166 @@
"""Commercial Functionality
"""
from random import shuffle
import random
import copy
from datetime import datetime
from datetime import timedelta
from dateutil.relativedelta import relativedelta
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 timedelta_milliseconds(self, td):
return td.days*86400000 + td.seconds*1000 + td.microseconds/1000
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.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.strptime(now_ep.start_time, '%I:%M:%S %p')
time_diff = (curr_item_start_time - prev_item_end_time)
count = 0
commercial_list = []
commercial_dur_sum = 0
time_diff_milli = self.timedelta_milliseconds(time_diff)
last_commercial = None
time_watch = prev_item_end_time
new_commercial_start_time = prev_item_end_time
#print "here", time_diff.seconds
while curr_item_start_time > new_commercial_start_time and (count) < len(self.commercials)*100:
random_commercial = self.get_random_commercial()
#new_commercial_seconds = (int(random_commercial[4])/1000)%60
new_commercial_milli = int(random_commercial[4])
if last_commercial != None:
#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
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
)
last_commercial = new_commercial
if new_commercial_end_time > curr_item_start_time:
break
commercial_list.append(new_commercial)
#print "here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
return commercial_list

View File

@@ -1,472 +1,491 @@
#!/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, check_same_thread=False)
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, plexMediaID 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, plexMediaID TEXT)')
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, 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)')
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, 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)')
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, plexMediaID 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, plexMediaID 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 '
'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, plexMediaID 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_shows_title ON shows (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_video_title ON videos (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_music_title ON music (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_commercial_title ON commercials (title);')
"""Setting Basic Settings
"""
try:
self.cursor.execute("INSERT OR REPLACE INTO app_settings "
"(version) VALUES (?)",
("0.1",))
self.cursor.execute('CREATE UNIQUE INDEX IF NOT EXISTS idx_settings_version ON app_settings (version);')
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
))
"""Setting Basic Settings
"""
try:
self.cursor.execute("INSERT OR REPLACE INTO app_settings "
"(version) VALUES (?)",
("0.1",))
self.conn.commit()
self.conn.commit()
# Catch the exception
except Exception as e:
# Roll back any change if something goes wrong
self.conn.rollback()
raise e
# Catch the exception
except Exception as 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, plexMediaID):
unix = int(time.time())
try:
self.cursor.execute("REPLACE INTO movies "
"(unix, mediaID, title, duration, plexMediaID) VALUES (?, ?, ?, ?, ?)",
(unix, mediaID, title, duration, plexMediaID))
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, plexMediaID):
unix = int(time.time())
try:
self.cursor.execute("REPLACE INTO videos "
"(unix, mediaID, title, duration, plexMediaID) VALUES (?, ?, ?, ?, ?)",
(unix, mediaID, title, duration, plexMediaID))
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, plexMediaID):
unix = int(time.time())
try:
self.cursor.execute("REPLACE INTO shows "
"(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:
# 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, plexMediaID):
unix = int(time.time())
try:
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()
# 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, plexMediaID):
unix = int(time.time())
try:
self.cursor.execute("REPLACE INTO commercials "
"(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
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("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,
plexMediaID
):
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, plexMediaID) "
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
(
unix,
mediaID,
title,
episodeNumber,
seasonNumber,
showTitle,
duration,
startTime,
endTime,
dayOfWeek,
sectionType,
plexMediaID
))
# Roll back any change if something goes wrong
self.conn.commit()
self.conn.rollback()
# Catch the exception
except Exception as e:
raise e
# Roll back any change if something goes wrong
def add_media_to_daily_schedule(self, media):
self.conn.rollback()
print "#### Adding media to db", media.title, media.start_time
raise e
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
)
def add_media_to_daily_schedule(self, media):
"""Database functions.
print "#### Adding media to db", media.title, media.start_time
Getters, etc.
"""
def get_media(self, title, mediaType):
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,
media.plex_media_id
)
media = mediaType
"""Database functions.
sql = "SELECT * FROM "+media+" WHERE (title LIKE ?) COLLATE NOCASE"
self.cursor.execute(sql, ("%"+title+"%", ))
media_item = self.cursor.fetchone()
Getters, etc.
"""
def get_media(self, title, mediaType):
return media_item
media = mediaType
def get_schedule(self):
sql = "SELECT * FROM "+media+" WHERE (title LIKE ?) COLLATE NOCASE"
self.cursor.execute(sql, ("%"+title+"%", ))
media_item = self.cursor.fetchone()
self.cursor.execute("SELECT * FROM schedule ORDER BY datetime(startTimeUnix) ASC")
return media_item
datalist = list(self.cursor.fetchall())
def get_schedule(self):
return datalist
self.cursor.execute("SELECT * FROM schedule ORDER BY datetime(startTimeUnix) ASC")
def get_daily_schedule(self):
datalist = list(self.cursor.fetchall())
self.cursor.execute("SELECT * FROM daily_schedule ORDER BY datetime(startTime) ASC")
return datalist
datalist = list(self.cursor.fetchall())
def get_daily_schedule(self):
return datalist
self.cursor.execute("SELECT * FROM daily_schedule ORDER BY datetime(startTime) ASC")
def get_movie(self, title):
datalist = list(self.cursor.fetchall())
media = "movies"
print "##### Getting Daily Schedule from DB."
return self.get_media(title, media)
return datalist
def get_shows(self, title):
def get_movie(self, title):
media = "shows"
media = "movies"
return self.get_media(title, media)
return self.get_media(title, media)
def get_music(self, title):
def get_shows(self, title):
media = "music"
media = "shows"
return self.get_media(title, media)
return self.get_media(title, media)
def get_video(self, title):
def get_music(self, title):
media = "videos"
media = "music"
return self.get_media(title, media)
return self.get_media(title, media)
def get_episodes(self, title):
def get_video(self, title):
media = "episodes"
media = "videos"
return self.get_media(title, media)
return self.get_media(title, media)
def update_shows_table_with_last_episode(self, showTitle, lastEpisodeTitle):
def get_episodes(self, title):
sql1 = "UPDATE shows SET lastEpisodeTitle = ? WHERE title LIKE ? COLLATE NOCASE"
media = "episodes"
self.cursor.execute(sql1, (lastEpisodeTitle, showTitle, ))
return self.get_media(title, media)
self.conn.commit()
def get_commercials(self):
def get_first_episode(self, tvshow):
self.cursor.execute("SELECT * FROM commercials ORDER BY duration ASC")
sql = ("SELECT id, unix, mediaID, title, duration, MIN(episodeNumber), MIN(seasonNumber), "
"showTitle FROM episodes WHERE ( showTitle LIKE ?) COLLATE NOCASE")
datalist = list(self.cursor.fetchall())
self.cursor.execute(sql, (tvshow, ))
return datalist
first_episode = self.cursor.fetchone()
def update_shows_table_with_last_episode(self, showTitle, lastEpisodeTitle):
return first_episode
sql1 = "UPDATE shows SET lastEpisodeTitle = ? WHERE title LIKE ? COLLATE NOCASE"
'''
*
* When incrementing episodes in a series I am advancing by "id"
*
'''
def get_episode_id(self, episodeTitle):
self.cursor.execute(sql1, (lastEpisodeTitle, showTitle, ))
sql = "SELECT id FROM episodes WHERE ( title LIKE ?) COLLATE NOCASE"
self.conn.commit()
self.cursor.execute(sql, (episodeTitle, ))
def get_first_episode(self, tvshow):
episode_id = self.cursor.fetchone()
sql = ("SELECT id, unix, mediaID, title, duration, MIN(episodeNumber), MIN(seasonNumber), "
"showTitle FROM episodes WHERE ( showTitle LIKE ?) COLLATE NOCASE")
return episode_id
self.cursor.execute(sql, (tvshow, ))
def get_random_episode(self):
first_episode = self.cursor.fetchone()
sql = "SELECT * FROM episodes WHERE id IN (SELECT id FROM episodes ORDER BY RANDOM() LIMIT 1)"
return first_episode
self.cursor.execute(sql)
'''
*
* When incrementing episodes in a series I am advancing by "id"
*
'''
def get_episode_id(self, episodeTitle):
return self.cursor.fetchone()
sql = "SELECT id FROM episodes WHERE ( title LIKE ?) COLLATE NOCASE"
def get_random_movie(self):
self.cursor.execute(sql, (episodeTitle, ))
sql = "SELECT * FROM movies WHERE id IN (SELECT id FROM movies ORDER BY RANDOM() LIMIT 1)"
episode_id = self.cursor.fetchone()
self.cursor.execute(sql)
return episode_id
return self.cursor.fetchone()
def get_random_episode(self):
def get_next_episode(self, series):
sql = "SELECT * FROM episodes WHERE id IN (SELECT id FROM episodes ORDER BY RANDOM() LIMIT 1)"
#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, ))
self.cursor.execute(sql)
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] == '':
return self.cursor.fetchone()
'''
*
* Find the first episode of the series
*
'''
first_episode = self.get_first_episode(series)
def get_random_movie(self):
first_episode_title = first_episode[3]
sql = "SELECT * FROM movies WHERE id IN (SELECT id FROM movies ORDER BY RANDOM() LIMIT 1)"
#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)
self.cursor.execute(sql)
return first_episode
return self.cursor.fetchone()
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")
def get_next_episode(self, series):
#print(str(self.get_episode_id(last_title_list[0])))
#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, ))
"""
*
* 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")
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] == '':
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()
'''
*
* Find the first episode of the series
*
'''
first_episode = self.get_first_episode(series)
if next_episode != None:
first_episode_title = first_episode[3]
#print(next_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)
self.update_shows_table_with_last_episode(series, next_episode[3])
return first_episode
return next_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")
else:
#print(str(self.get_episode_id(last_title_list[0])))
print("Not grabbing next episode restarting series, series must be over. Restarting from episode 1.")
"""
*
* 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")
first_episode = self.get_first_episode(series)
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.update_shows_table_with_last_episode(series, first_episode[3])
if next_episode != None:
return first_episode
def get_commercials(self, title):
#print(next_episode[3])
media = "commercials"
self.update_shows_table_with_last_episode(series, next_episode[3])
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 next_episode
return datalist
else:
else:
print("Not grabbing next episode restarting series, series must be over. Restarting from episode 1.")
return None
first_episode = self.get_first_episode(series)
self.update_shows_table_with_last_episode(series, first_episode[3])
return first_episode
def get_commercial(self, title):
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)
return datalist
else:
return None

View File

@@ -1,8 +1,11 @@
#!/usr/bin/env python
from plexapi.server import PlexServer
from datetime import datetime
import sqlite3
from yattag import Doc
from yattag import indent
import os, sys
import logging
@@ -10,328 +13,459 @@ import logging.handlers
class PseudoDailyScheduleController():
def __init__(self, server, token, clients):
def __init__(self, server, token, clients, debugMode = False):
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.DEBUG = debugMode
self.handler = logging.handlers.SysLogHandler(address = '/dev/log')
self.my_logger = logging.getLogger('MyLogger')
self.my_logger.setLevel(logging.DEBUG)
self.my_logger.addHandler(self.handler)
self.handler = logging.handlers.SysLogHandler(address = '/dev/log')
'''
*
* 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):
self.my_logger.addHandler(self.handler)
backgroundImagePath = None
'''
*
* 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):
backgroundImgURL = ''
backgroundImagePath = None
try:
backgroundImgURL = ''
backgroundImagePath = self.PLEX.library.section(section).get(title)
try:
except:
backgroundImagePath = self.PLEX.library.section(section).get(title)
return backgroundImgURL
except:
if backgroundImagePath != None and isinstance(backgroundImagePath.art, str):
return backgroundImgURL
backgroundImgURL = self.BASE_URL+backgroundImagePath.art+"?X-Plex-Token="+self.TOKEN
if backgroundImagePath != None and isinstance(backgroundImagePath.art, str):
return backgroundImgURL
backgroundImgURL = self.BASE_URL+backgroundImagePath.art+"?X-Plex-Token="+self.TOKEN
'''
*
* 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):
return backgroundImgURL
now = datetime.now()
def get_xml_from_daily_schedule(self, currentTime, bgImageURL, datalist):
time = now.strftime("%B %d, %Y")
now = datetime.now()
doc, tag, text, line = Doc(
time = now.strftime("%B %d, %Y")
).ttl()
doc, tag, text, line = Doc(
doc.asis('<!DOCTYPE html>')
).ttl()
with tag('html'):
doc.asis('<?xml version="1.0" encoding="UTF-8"?>')
with tag('head'):
with tag('schedule', currently_playing_bg_image=bgImageURL if bgImageURL != None else ''):
with tag('title'):
for row in datalist:
text(time + " - Daily Pseudo Schedule")
if str(row[11]) == "Commercials" and self.DEBUG == False:
doc.asis('<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet">')
doc.asis('<script>setTimeout(function() {location.reload();}, 30000);</script>')
continue
if bgImageURL != None:
doc.asis('<style>body{ background:transparent!important; } html { background: url('+bgImageURL+') no-repeat center center fixed; -webkit-background-size: cover;-moz-background-size: cover;-o-background-size: cover;background-size: cover;}.make-white { padding: 24px; background:rgba(255,255,255, 0.9); }</style>')
timeB = datetime.strptime(row[8], '%I:%M:%S %p')
with tag('body'):
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])),
):
with tag('div', klass='container mt-3'):
text(row[8])
with tag('div', klass='row make-white'):
elif currentTime.hour == timeB.hour and currentTime.minute == timeB.minute:
with tag('div'):
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])),
):
with tag('div'):
text(row[8])
line('h1', "Daily Pseudo Schedule", klass='col-12 pl-0')
else:
with tag('div'):
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])),
):
line('h3', time, klass='col-12 pl-1')
text(row[8])
with tag('table', klass='col-12 table table-bordered table-hover'):
return indent(doc.getvalue())
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
'''
*
* 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):
for row in datalist:
now = datetime.now()
numberIncrease += 1
time = now.strftime("%B %d, %Y")
with tag('tbody'):
doc, tag, text, line = Doc(
timeB = datetime.strptime(row[8], '%I:%M %p')
).ttl()
if currentTime == None:
doc.asis('<!DOCTYPE html>')
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('html'):
elif currentTime.hour == timeB.hour and currentTime.minute == timeB.minute:
with tag('head'):
with tag('tr', klass='bg-info'):
with tag('title'):
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])
text(time + " - Daily Pseudo Schedule")
else:
doc.asis('<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet">')
doc.asis('<script>setTimeout(function() {location.reload();}, 30000);</script>')
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])
if bgImageURL != None:
doc.asis('<style>body{ background:transparent!important; } html { background: url('+bgImageURL+') no-repeat center center fixed; -webkit-background-size: cover;-moz-background-size: cover;-o-background-size: cover;background-size: cover;}.make-white { padding: 24px; background:rgba(255,255,255, 0.9); }</style>')
with tag('body'):
return doc.getvalue()
with tag('div', klass='container mt-3'):
'''
*
* Create 'schedules' dir & write the generated html to .html file.
* @param data: html string
* @return null
*
'''
def write_schedule_to_file(self, data):
with tag('div', klass='row make-white'):
now = datetime.now()
with tag('div'):
fileName = "index.html"
with tag('div'):
writepath = './schedules/'
line('h1', "Daily Pseudo Schedule", klass='col-12 pl-0')
if not os.path.exists(writepath):
with tag('div'):
os.makedirs(writepath)
line('h3', time, klass='col-12 pl-1')
if os.path.exists(writepath+fileName):
os.remove(writepath+fileName)
with tag('table', klass='col-12 table table-bordered table-hover'):
mode = 'a' if os.path.exists(writepath) else 'w'
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 open(writepath+fileName, mode) as f:
numberIncrease = 0
f.write(data)
for row in datalist:
'''
*
* 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 str(row[11]) == "Commercials" and self.DEBUG == False:
if mediaType == "TV Shows":
continue
mediaItems = self.PLEX.library.section(mediaType).get(mediaParentTitle).episodes()
numberIncrease += 1
for item in mediaItems:
with tag('tbody'):
# print(part.title)
timeB = datetime.strptime(row[8], '%I:%M:%S %p')
if item.title == mediaTitle:
if currentTime == None:
for client in self.PLEX_CLIENTS:
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])
clientItem = self.PLEX.client(client)
elif currentTime.hour == timeB.hour and currentTime.minute == timeB.minute:
clientItem.playMedia(item)
break
with tag('tr', klass='bg-info'):
elif mediaType == "Movies":
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])
movie = self.PLEX.library.section(mediaType).get(mediaTitle)
else:
for client in self.PLEX_CLIENTS:
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])
clientItem = self.PLEX.client(client)
clientItem.playMedia(movie)
return indent(doc.getvalue())
else:
'''
*
* Create 'schedules' dir & write the generated html to .html file.
* @param data: html string
* @return null
*
'''
def write_schedule_to_file(self, data):
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):
now = datetime.now()
currentTime = datetime.now()
fileName = "index.html"
"""c.execute("SELECT * FROM daily_schedule")
writepath = './schedules/'
datalist = list(c.fetchall())
"""
for row in datalist:
if not os.path.exists(writepath):
try:
endTime = datetime.strptime(row[9], '%Y-%m-%d %H:%M:%S.%f')
os.makedirs(writepath)
except ValueError:
if os.path.exists(writepath+fileName):
os.remove(writepath+fileName)
endTime = datetime.strptime(row[9], '%Y-%m-%d %H:%M:%S')
mode = 'a' if os.path.exists(writepath) else 'w'
if currentTime.hour == endTime.hour:
with open(writepath+fileName, mode) as f:
if currentTime.minute == endTime.minute:
f.write(data)
print("Ok end time found")
'''
*
* Create 'schedules' dir & write the generated xml to .xml file.
* @param data: xml string
* @return null
*
'''
def write_xml_to_file(self, data):
self.write_schedule_to_file(self.get_html_from_daily_schedule(None, None, datalist))
now = datetime.now()
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):
fileName = "pseudo_schedule.xml"
datalistLengthMonitor = 0;
writepath = './schedules/'
currentTime = datetime.now()
if not os.path.exists(writepath):
"""c.execute("SELECT * FROM daily_schedule ORDER BY datetime(startTimeUnix) ASC")
os.makedirs(writepath)
datalist = list(c.fetchall())"""
if os.path.exists(writepath+fileName):
os.remove(writepath+fileName)
self.my_logger.debug('TV Controller')
mode = 'a' if os.path.exists(writepath) else 'w'
for row in datalist:
with open(writepath+fileName, mode) as f:
timeB = datetime.strptime(row[8], '%I:%M %p')
f.write(data)
if currentTime.hour == timeB.hour:
'''
*
* 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 currentTime.minute == timeB.minute:
if mediaType == "TV Shows":
print("Starting Epsisode: " + row[3])
print(row)
mediaItems = self.PLEX.library.section(mediaType).get(mediaParentTitle).episodes()
self.play_media(row[11], row[6], row[3])
for item in mediaItems:
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
)
)
# print(part.title)
self.my_logger.debug('Trying to play: ' + row[3])
if item.title == mediaTitle:
break
for client in self.PLEX_CLIENTS:
datalistLengthMonitor += 1
clientItem = self.PLEX.client(client)
if datalistLengthMonitor >= len(datalist):
clientItem.playMedia(item)
break
self.check_for_end_time(datalist)
elif mediaType == "Movies":
movie = self.PLEX.library.section(mediaType).get(mediaTitle)
for client in self.PLEX_CLIENTS:
clientItem = self.PLEX.client(client)
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))
'''
*
* 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:
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
'''
*
* 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())"""
self.my_logger.debug('TV Controller')
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])
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
)
)
self.my_logger.debug('Trying to play: ' + row[3])
break
datalistLengthMonitor += 1
if datalistLengthMonitor >= len(datalist):
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))

View File

@@ -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,29 @@ 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,
plex_media_id
):
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,
plex_media_id
)

View File

@@ -5,4 +5,6 @@ from Movie import Movie
from Media import Media
from Music import Music
from Video import Video
from PseudoDailyScheduleController import PseudoDailyScheduleController
from PseudoDailyScheduleController import PseudoDailyScheduleController
from GoogleCalendar import GoogleCalendar
from PseudoChannelCommercial import PseudoChannelCommercial