#!/usr/bin/env python """ tvcli This is a command line program to manage your favourite TV programs and output air times/program names. This is the first project I've created in python, feedback is welcome. Copyright (c) 2011 """ import copy, getopt, os.path, pickle, urllib, sys, time from datetime import date, timedelta, datetime from xml.dom.minidom import parse, parseString from colorama import init, Fore, Back, Style init() APIKEY = 'AB4A43DCDF3A99B5' def updateAll(): shows = getData() if not shows: print "You have no favourite TV programs to update!" return 1 for show in shows: print "Updating episode list for "+show['SeriesName']+"..." updateEpisodes(show['id']) def getData(): if not os.path.isfile('data/favourites.pk'): return 0 f = open('data/favourites.pk','r') data = pickle.load(f) f.close() return data def getEpData(pID): if not os.path.isfile('data/episodes/'+pID+'.pk'): return 0 f = open('data/episodes/'+pID+'.pk','r') data = pickle.load(f) f.close() return data def listProgs(favs): if not favs: print "Your favourites list is empty :(" return 1 for i in range(len(favs)): print Fore.CYAN+Style.BRIGHT+`i+1`+': '+Style.RESET_ALL+favs[i]['SeriesName'] return 0 def getNextEp(pID): episodes = getEpData(pID) d = date.today() airDate = d.isoformat() offset = -99999999 for episode in episodes: if 'FirstAired' in episode: o = int(airDate.replace('-','')) - int(episode['FirstAired'].replace('-','')) if o < 0 and o > offset: offset = o airDate = episode['FirstAired'] epName = "["+episode['SeasonNumber']+"x"+episode['EpisodeNumber']+"] "+episode['EpisodeName'] try: epName except NameError: epName = None if epName == None: return "No data available." else: return date.fromtimestamp(time.mktime(time.strptime(airDate,"%Y-%m-%d"))).strftime("%d %B %Y")+" - "+epName def showToday(): today = date.today().strftime("%d %B %Y") d = getData() programs = [] for i in range(len(d)): p = d[i] n = getNextEp(p['id']) if n.find(today) > -1: p['cid'] = i programs.append(p) if len(programs)==0: print "No programs airing today." return 1 print Fore.CYAN+Style.BRIGHT+"Airing Today:"+Style.RESET_ALL for p in programs: info(p['cid']+1) def showTomorrow(): t = date.today()+timedelta(days=2) tomorrow = t.strftime("%d %B %Y") d = getData() programs = [] for i in range(len(d)): p = d[i] n = getNextEp(p['id']) if n.find(tomorrow) > -1: p['cid'] = i programs.append(p) if len(programs)==0: print "No programs airing tomorrow." return 1 print Fore.CYAN+Style.BRIGHT+"Airing Tomorrow:"+Style.RESET_ALL for p in programs: info(p['cid']+1) def showInDays(days): td = date.today().isoformat() d = getData() days = int(days) progs = [] for i in range(len(d)): p = d[i] episodes = getEpData(p['id']) for episode in episodes: if 'FirstAired' in episode: o = int(episode['FirstAired'].replace('-','')) - int(td.replace('-','')) if o <= days and o > 0: p['cid'] = i progs.append(p) if len(progs)==0: print "No programs airing in the next "+`days`+" days." return 1 print Fore.CYAN+Style.BRIGHT+"Airing in the next "+`days`+" Days:"+Style.RESET_ALL for p in progs: info(p['cid']+1) def getLastEp(pID): episodes = getEpData(pID) d = date.today() now = d.isoformat() for episode in episodes: if 'FirstAired' in episode: o = int(now.replace('-','')) - int(episode['FirstAired'].replace('-','')) try: offset except NameError: offset = o if o > 0 and o < offset: offset = o airDate = episode['FirstAired'] epName = "["+episode['SeasonNumber']+"x"+episode['EpisodeNumber']+"] "+episode['EpisodeName'] try: airDate except NameError: airDate = None if airDate == None: return "No data available." else: return date.fromtimestamp(time.mktime(time.strptime(airDate,"%Y-%m-%d"))).strftime("%d %B %Y")+" - "+epName def search(program): sock = urllib.urlopen("http://www.thetvdb.com/api/GetSeries.php?seriesname="+program) f = sock.read() sock.close() dom = parseString(f) if len(dom.getElementsByTagName('Series')) == 0: print "No episodes found with that name." return 1 for series in dom.getElementsByTagName('Series'): name = series.getElementsByTagName('SeriesName') ID = series.getElementsByTagName('seriesid') fa = series.getElementsByTagName('FirstAired') overview = series.getElementsByTagName('Overview') if len(name) < 0: break s = getSeriesData(ID[0].childNodes[0].data) print "\n"+Style.BRIGHT+Fore.BLUE+ID[0].childNodes[0].data+" - "+name[0].childNodes[0].data+Style.RESET_ALL if len(overview) > 0: print Style.BRIGHT+" Overview:\t"+Style.NORMAL+overview[0].childNodes[0].data[:60].rsplit(' ',1)[0]+"..." if len(fa) > 0: print Style.BRIGHT+" First Aired:\t"+Style.NORMAL+date.fromtimestamp(time.mktime(time.strptime(fa[0].childNodes[0].data,"%Y-%m-%d"))).strftime("%d %B %Y") if 'Genre' in s: genres = s['Genre'].split('|') print Style.BRIGHT+" Genre(s):\t"+Style.NORMAL, for i in range(len(genres)): genre = genres[i] if len(genre.strip()) > 0: sys.stdout.write(genre) if len(genres) != (i+2): print ", ", print if 'Network' in s: print Style.BRIGHT+" Network:\t"+Style.NORMAL+s['Network'] if 'Rating' in s: print Style.BRIGHT+" Rating:\t"+Style.NORMAL+s['Rating'] if 'Status' in s: print Style.BRIGHT+" Status:\t"+Style.NORMAL+s['Status'] def updateEpisodes(pID): url = "http://www.thetvdb.com/api/"+APIKEY+"/series/"+pID+"/all" sock = urllib.urlopen(url) if sock.getcode() == 200: f = sock.read() else: return 1 sock.close() dom = parseString(f) episode = {} episodes = [] for episodeData in dom.getElementsByTagName('Episode'): for tag in episodeData.childNodes: if tag.nodeName != "#text" and tag.nodeValue !="\n": if len(tag.childNodes) > 0: episode[tag.nodeName] = tag.childNodes[0].data episodes.append(episode.copy()) episode.clear() f = open('data/episodes/'+pID+'.pk','w') pickle.dump(episodes,f) f.close() def delete(pID): d = getData() print Style.BRIGHT+"Are you sure you want to delete "+d[int(pID)-1]['SeriesName']+"?"+Style.NORMAL i = raw_input('(y/n): ') if i == "n" or i=="N": print "Quitting..." sys.exit(1) elif i!="y" and i!="Y": print "Invalid." sys.exit(1) del d[int(pID)-1] f = open('data/favourites.pk','w') pickle.dump(d,f) f.close() print "Deleted." def getSeriesData(pID): url = "http://www.thetvdb.com/api/"+APIKEY+"/series/"+pID+"/all" sock = urllib.urlopen(url) if sock.getcode() == 200: f = sock.read() else: print "Error: Series not found or API key incorrect." return 1 sock.close() dom = parseString(f) prog = {} for sInfo in dom.getElementsByTagName('Series'): for tag in sInfo.childNodes: if tag.nodeName != "#text" and tag.nodeValue != "\n": if len(tag.childNodes) > 0: prog[tag.nodeName] = tag.childNodes[0].data return prog def add(pID): print "Searching for "+pID+" in the TVDB..." prog = getSeriesData(pID) progs = [] if os.path.isfile('data/favourites.pk'): current = getData() current.append(prog) progs = current else: progs = [ prog ] f = open('data/favourites.pk','w') pickle.dump(progs,f) f.close() print "Added "+prog['SeriesName']+" to favourites." print "Updating episodes..." updateEpisodes(pID) print "Done" def info(fID): fID = int(fID)-1 d = getData() if len(d) < fID: print "Bad program ID. Please use the ID for the program given by the --list option." sys.exit(1) program = d[fID] print Fore.BLUE+Style.BRIGHT+`fID+1`+": "+program['SeriesName']+Style.RESET_ALL print Style.BRIGHT+" Airs: "+Style.NORMAL+program['Airs_DayOfWeek']+"s "+program['Airs_Time']+" (Network's local time)" print Style.BRIGHT+" Latest Episode: "+Style.NORMAL+getLastEp(program['id']) print Style.BRIGHT+" Next Episode: "+Style.NORMAL+getNextEp(program['id']) def usage(): print "Usage: tvcli " print " -s,\t--search=PROGRAM\tSearch for a program's ID in the TvDB." print " -i,\t--info=PID\t\tRetrive info for program using ID (from --list)" print " -a,\t--add=TVDBID\t\tAdd program (identified by TvDB ID) to favourites list." print " -d,\t--days=NUM\t\tList programs airing in the next NUM days." print " -t,\t--today\t\t\tList programs airing today." print " -T,\t--tomorrow\t\tList programs airing tomorrow." print " -u,\t--update\t\tUpdate episode lists." print " -l,\t--list\t\t\tList the programs being tracked." print " -D,\t--delete=PID\t\tDelete a program using ID from --list." def main(argv): if len(argv) == 0: usage() sys.exit(1) try: opts, args = getopt.getopt(argv, "s:i:a:d:D:tTul", ["search=","info=","add=","days=","delete=","today","tomorrow","update","list"]) except getopt.GetoptError: usage() sys.exit(2) for o, a in opts: if o in ("-l", "--list"): listProgs(getData()) if o in ("-s", "--search"): search(a) if o in ("-u", "--update"): updateAll() if o in ("-a", "--add"): add(a) if o in ("-i", "--info"): info(a) if o in ("-D", "--delete"): delete(a) if o in ("-t", "--today"): showToday() if o in ("-T", "--tomorrow"): showTomorrow() if o in ("-d", "--days"): showInDays(a) main(sys.argv[1:])