diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 35755d6..0000000 --- a/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.pyc -.spyderproject - diff --git a/DoEncode.py b/DoEncode.py new file mode 100755 index 0000000..846a726 --- /dev/null +++ b/DoEncode.py @@ -0,0 +1,125 @@ +# -*- coding: utf-8 -*- +""" +Created on Sat Jun 29 23:25:55 2013 + +@author: shane +""" + +import glob +import logging +import os +import shutil +import subprocess +from xml.etree import ElementTree + +SETTINGSFILE = "settings.xml" +HANDBRAKECOMMAND = ['HandBrakeCLI', '--verbose', '-i', '"{0}"', '-o', '"{1}"', + '-f', 'mkv', '-e', 'x264', '-x264-preset', 'slower', + '-x264-tune', 'animation', '-q', '20', + '--loose-anamorphic', '--decomb', '--detelecine', + '--denoise="2:1:2:3"', '--deblock'] + +TVRECORDINGSDIR = "/srv/storage2/videos/TVRecordings/" + +LOGFILE = "encoding.log" +ACTIONLOG = "needsAction.log" + +class TVShow: + def __init__(self, name, inputDirectory, outputDirectory): + self.name = name + self.inputDirectory = inputDirectory + self.outputDirectory = outputDirectory + +def LoadSettings(source): + shows = [] + settingsXml = ElementTree.parse(source).getroot() + + for show in settingsXml.findall('show'): + newShow = TVShow(show[0].text, show[1].text, show[2].text) + shows.append(newShow) + return shows + +def FindSeason(path, fileName): + season = "Season {0}".format(fileName[1:3]) + seasonPath = os.path.join(path, season) + if not os.path.exists(seasonPath): + os.makedirs(seasonPath) + + return seasonPath + +def GetRecordingFile(file): + return os.path.join(TVRECORDINGSDIR, os.path.dirname(inputFile).split("/")[-1] + ".mpg") + +def CheckOldOutputFileExists(file, myLogger): + myLogger.debug("Searching for existing file: {0}".format(file[:-3]+"*")) + return glob.glob(file[:-3]+"*") + +def CreateLogger(name, filename, level): + logger = logging.getLogger(name) + handler = logging.FileHandler(filename) + formatter = logging.Formatter('%(asctime)s %(message)s') + handler.setFormatter(formatter) + handler.setLevel(level) + logger.addHandler(handler) + return logger + +#generalLogger.basicConfig(filename=LOGFILE, level=logging.DEBUG, format='%(asctime)s %(message)s') + +#actionLogger = logging.getLogger("action") +#actionLogger.basicConfig(filename=ACTIONLOG, level=logging.INFO, format='%(asctime)s %(message)s') + +logging.basicConfig(level=logging.DEBUG) +generalLogger = CreateLogger("general", LOGFILE, logging.DEBUG) +actionLogger = CreateLogger("action", ACTIONLOG, logging.INFO) + +generalLogger.debug("Loading settings from {0}".format(SETTINGSFILE)) +shows = LoadSettings(SETTINGSFILE) + +for show in shows: + generalLogger.info("Processing {0}".format(show.name)) + fileList = [] + + for r,d,f in os.walk(show.inputDirectory): + for files in f: + if files.endswith(".mpg"): + fileList.append(os.path.join(r,files)) + + for inputFile in fileList: + generalLogger.info("Processing file {0}".format(inputFile)) + + inFile = os.path.basename(inputFile) + outFilename = inFile[:-3]+"mkv" + outPath = FindSeason(show.outputDirectory, outFilename) + outFile = os.path.join(outPath, outFilename) + generalLogger.debug("Output file is {0}".format(outFile)) + + if os.path.isfile(outFile): + message = "File {0} already exists. Not processing any further.".format(outFile) + generalLogger.warning(message) + actionLogger.info(message) + else: + existingFile = CheckOldOutputFileExists(outFile, generalLogger) + generalLogger.debug("Search returned {0}".format(existingFile)) + if len(existingFile) > 0: + message = "There is an existing version of {0} at {1}.".format(outFilename, existingFile[0]) + generalLogger.info(message) + actionLogger.info(message) + + HANDBRAKECOMMAND[3] = inputFile + HANDBRAKECOMMAND[5] = outFile + generalLogger.debug("Handbrake command is: {0}".format(HANDBRAKECOMMAND)) + po = subprocess.Popen(HANDBRAKECOMMAND) + po.wait() + generalLogger.info("Handbrake completed with return code {0}".format(po.returncode)) + + generalLogger.info("Deleting input files from {0}".format(os.path.dirname(inputFile))) + shutil.rmtree(os.path.dirname(inputFile)) + + linkAddress = GetRecordingFile(inputFile) + generalLogger.info("Deleting original file from {0}".format(linkAddress)) + os.remove(linkAddress) + + generalLogger.info("Creating symlink from {0} to {1}".format(linkAddress, outFile)) + os.symlink(outFile, linkAddress) + + generalLogger.info("Processing completed for {0}".format(inputFile)) diff --git a/EmailSettings.cfg b/EmailSettings.cfg deleted file mode 100644 index 90c69ad..0000000 --- a/EmailSettings.cfg +++ /dev/null @@ -1,5 +0,0 @@ -SMTPServer = "" -SMTPUser = "" -SMTPPassword = "" -From = "" -To = "" diff --git a/ListFilesToEncode.py b/ListFilesToEncode.py new file mode 100755 index 0000000..d587abd --- /dev/null +++ b/ListFilesToEncode.py @@ -0,0 +1,32 @@ +import os +from xml.etree import ElementTree + +SETTINGSFILE = "settings.xml" + +class TVShow: + def __init__(self, name, inputDirectory, outputDirectory): + self.name = name + self.inputDirectory = inputDirectory + self.outputDirectory = outputDirectory + +def LoadSettings(source): + shows = [] + settingsXml = ElementTree.parse(source).getroot() + + for show in settingsXml.findall('show'): + newShow = TVShow(show[0].text, show[1].text, show[2].text) + shows.append(newShow) + return shows + +shows = LoadSettings(SETTINGSFILE) + +for show in shows: + fileList = [] + + for r,d,f in os.walk(show.inputDirectory): + for files in f: + if files.endswith(".mpg"): + fileList.append(os.path.join(r,files)) + + for inputFile in fileList: + print inputFile diff --git a/ProcessRecordings.py b/ProcessRecordings.py new file mode 100755 index 0000000..abf40cf --- /dev/null +++ b/ProcessRecordings.py @@ -0,0 +1,154 @@ +import os +import shutil +import MySQLdb as mdb +import glob +import json +from urllib import urlopen +from fuzzywuzzy import fuzz +from operator import itemgetter + +PROCESSDIR="/srv/storage2/files/VideoProcessing/" +THOMAS="Thomas" +CHUGGINGTON="Chuggington" +MIKE="MikeTheKnight" +OCTONAUTS="Octonauts" +NIGHTGARDEN="InTheNightGarden" +RAARAA="RaaRaa" +INPUTDIR="Input" +SICKBEARDAPI="http://192.168.0.2:8081/api/3678177136222bf5002be209220ccb20/" + +class TVShow: + def __init__(self, episode, season, title, subtitle, description): + self.episode = episode + self.season = season + self.title = title + self.subtitle = subtitle + self.description = description + +def FindShowId(showName): + jsonurl = urlopen(SICKBEARDAPI+"?cmd=shows") + result = json.loads(jsonurl.read()) + + shows = [] + for show in result['data']: + shows.append((show, fuzz.partial_ratio(showName.lower(), result['data'][show]['show_name'].lower()))) + + shows = sorted(shows, key=itemgetter(1), reverse=True) + + if shows[0][1] > 85: + return shows[0][0] + +def FindEpisode(showId, name=None, description=None): + jsonurl = urlopen("{0}?cmd=show.seasons&tvdbid={1}".format(SICKBEARDAPI, showId)) + result = json.loads(jsonurl.read()) + + for season in result['data']: + for episode in result['data'][season]: + if name is not None and name.lower() == result['data'][season][episode]['name'].lower(): + return (season, episode) + elif description is not None: + result = FindEpisodeByDescription(showId, season, episode, description) + if result is not None: + return result + + return (0, 0) + +def GetEpisodeName(subtitle, showName): + if subtitle[:len(showName)].lower() == showName.lower(): + return subtitle[len(showName + ' and the '):] + else: + return subtitle + +def FindEpisodeByDescription(showId, season, episode, description): + jsonEpisodeUrl = urlopen("{0}?cmd=episode&tvdbid={1}&season={2}&episode={3}".format(SICKBEARDAPI, showId, season, episode)) + episodeResult = json.loads(jsonEpisodeUrl.read()) + + if fuzz.ratio(episodeResult['data']['description'].lower(), description.lower()) > 85: + return (season, episode) + + return None + +def DetermineTargetFilename(directory, filename, inputFilename): + dir = os.path.join(directory, inputFilename[:-4]) + + if not os.path.exists(dir): + os.makedirs(dir) + + return os.path.join(dir, filename) + +def ProcessKnownEpisode(directory, filename, inputFilename): + target = DetermineTargetFilename(directory, filename, inputFilename) + shutil.move(inputFilename, target) + +def ProcessUnknownEpisode(inputFilename): + print "do this" + +def RetrieveEpisodeData(inputFile): + con = mdb.connect('localhost', 'script', 'script', 'mythconverg') + + with con: + cur = con.cursor(mdb.cursors.DictCursor) + cur.execute("select episode, season, title, subtitle, description from mythconverg.recorded where basename = '{0}'".format(inputFile)) + result = cur.fetchone() + + return TVShow(result['episode'], result['season'], result['title'], result['subtitle'], result['description']) + +def FixEpisodeSeasonNumber(number): + if number < 10: + return "0{0}".format(number) + else: + return str(number) + +def GetDirectory(title, season): + directory = "" + if title == "Thomas and Friends" or title == "Thomas the Tank Engine & Friends": + directory = THOMAS + elif title == "Chuggington": + directory = CHUGGINGTON + elif title == "Mike the Knight": + directory = MIKE + elif title == "Octonauts" or title == "The Octonauts": + directory = OCTONAUTS + elif title == "In the Night Garden": + directory = NIGHTGARDEN + elif title == "Raa Raa! The Noisy Lion": + directory = RAARAA + else: + print "Didn't match" + + return os.path.join(PROCESSDIR, directory, INPUTDIR, season) + + +def ProcessEpisode(inputFile): + show = RetrieveEpisodeData(inputFile) + + if show.title: + if show.subtitle: + show.subtitle = GetEpisodeName(show.subtitle, show.title) + + if (show.season == '0' or show.episode == '0'): + showId = FindShowId(show.title) + + result = FindEpisode(showId, show.subtitle, show.description) + show.season = result[0] + show.episode = result[1] + + if show.season != "0" and show.episode != "0": + show.season = FixEpisodeSeasonNumber(show.season) + show.episode = FixEpisodeSeasonNumber(show.episode) + + seasonFolder = "Season {0}".format(show.season) + season = "S{0}".format(show.season) + episode = "E{0}".format(show.episode) + renamedFile = "{0}{1} - {2} - SD TV_.mpg".format(season, episode, show.subtitle) + + directory = GetDirectory(show.title, seasonFolder) + ProcessKnownEpisode(directory, renamedFile, os.path.basename(inputFile)) + else: + print "no show name" + +def GetFilesToProcess(): + return glob.glob("*.mpg") + +for file in GetFilesToProcess(): + ProcessEpisode(file) diff --git a/ProcessRecordingsTest.py b/ProcessRecordingsTest.py new file mode 100755 index 0000000..7f1b30b --- /dev/null +++ b/ProcessRecordingsTest.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +""" +Created on Thu Jul 04 14:40:38 2013 + +@author: shane +""" +import ProcessRecordings +import unittest +from minimock import mock +import os.path + + +class ProcessRecordingsTest(unittest.TestCase): + def test_fixnumber(self): + result = ProcessRecordings.FixEpisodeSeasonNumber(1) + self.assertEqual("01", result) + + def test_fixnumber2(self): + result = ProcessRecordings.FixEpisodeSeasonNumber(9) + self.assertEqual("09", result) + + def test_fixnumber3(self): + result = ProcessRecordings.FixEpisodeSeasonNumber(11) + self.assertEqual("11", result) + + def test_episodeName(self): + subtitle = 'Mike the Knight and the Test Case' + title = 'Mike the Knight' + result = ProcessRecordings.GetEpisodeName(subtitle, title) + self.assertEqual('Test Case', result) + + def test_episodeName2(self): + subtitle = 'Test Case 2' + title = 'Mike the Knight' + result = ProcessRecordings.GetEpisodeName(subtitle, title) + self.assertEqual('Test Case 2', result) + + def test_GetDirectoryThomas(self): + title = 'Thomas and Friends' + season = 'Season 01' + result = ProcessRecordings.GetDirectory(title, season) + self.assertEqual("/srv/storage2/files/VideoProcessing/Thomas/Input/Season 01", result) + + def test_GetDirectoryThomas2(self): + title = 'Thomas the Tank Engine & Friends' + season = 'Season 01' + result = ProcessRecordings.GetDirectory(title, season) + self.assertEqual("/srv/storage2/files/VideoProcessing/Thomas/Input/Season 01", result) + + def test_GetDirectoryChuggington(self): + title = 'Chuggington' + season = 'Season 02' + result = ProcessRecordings.GetDirectory(title, season) + self.assertEqual("/srv/storage2/files/VideoProcessing/Chuggington/Input/Season 02", result) + + def test_DetermineTargetFilename(self): + directory = '/srv/storage2/test/Input' + filename = 'S01E02 - test episode - SD TV_.mpg' + inputFilename = '123456.mpg' + + mock('os.path.exists', returns=True) + result = ProcessRecordings.DetermineTargetFilename(directory, filename, inputFilename) + self.assertEqual('/srv/storage2/test/Input/123456/S01E02 - test episode - SD TV_.mpg', result) + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(ProcessRecordingsTest) + unittest.TextTestRunner(verbosity=2).run(suite) \ No newline at end of file diff --git a/README b/README index f6c95b5..e69de29 100644 --- a/README +++ b/README @@ -1 +0,0 @@ -Testing README file diff --git a/TVEncoder.py b/TVEncoder.py deleted file mode 100644 index 152a36d..0000000 --- a/TVEncoder.py +++ /dev/null @@ -1,204 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Jul 5 14:14:22 2013 - -@author: shanef -""" - -import sys -import getopt -from libfilemanager import FileManager -from libsettings import Settings -import libhandbrake -import libemail -from libtvdatasource import TVData -from collections import namedtuple -from termcolor import colored -import logging - -SETTINGS = "settings.cfg" -EMAIL_SETTINGS = "EmailSettings.cfg" - - -def showhelp(): - """ - Prints the command lines switches that are valid for the program. - """ - - print 'TVEncoder.py -p -n ' \ - '- prepare n recordings' - print 'TVEncoder.py -p -l -n - lists the ' \ - 'files that will be processed without actually encoding them' - print 'TVEncoder.py -e - encode the files that have been processed' - print 'TVEncoder.py -e -l - list the files that would be encoded' - print 'TVEncoder.py -c - check the output directories for duplicates' - - -def print_shows(shows): - """ - Prints he details of the shows. - """ - okshows = [] - noepisodes = [] - existingfiles = [] - - for show in shows: - showstr = str(show) - - errors = show.checkproblems() - if not errors: - okshows.append(showstr) - elif "NO_EPISODE" in errors: - noepisodes.append(showstr) - elif "FILE_EXISTS" in errors: - existingfiles.append(showstr) - - for show in okshows: - print show - - if noepisodes: - print colored("\nDetails of the episode could not be determined for " - "the following shows:", 'red') - for show in noepisodes: - print colored(show, 'red') - - if existingfiles: - print colored("\nThe following shows have a pre-existing " - "output file:", 'red') - for show in existingfiles: - print colored(show, 'red') - - -def processarguments(options): - """ - Determine the actions required from the input flags - """ - - inputoptions = namedtuple("inputoptions", - "numfiles doencode readonly dolist " - "checkduplicates") - - inputoptions.readonly = False - inputoptions.checkduplicates = False - - for opt, arg in options: - if opt == '-h': - showhelp() - sys.exit() - elif opt == "-p": - inputoptions.doencode = False - elif opt == "-e": - inputoptions.doencode = True - elif opt == "-n": - inputoptions.numfiles = arg - elif opt == "-l": - inputoptions.readonly = True - elif opt == "-c": - inputoptions.checkduplicates = True - - return inputoptions - - -def main(argv): - """ - The main program for TVEncoder. - """ - try: - opts, _ = getopt.getopt(argv, "hlpecn:") - except getopt.GetoptError: - showhelp() - sys.exit(2) - inputoptions = processarguments(opts) - - settings = Settings(SETTINGS) - filemanager = FileManager(settings) - - if inputoptions.checkduplicates: - print "Searching for duplicates..." - duplicates = filemanager.checkexistingduplicates() - if duplicates: - for duplicate in duplicates: - print duplicate - else: - print "No duplicates found." - return - - if inputoptions.readonly: - if inputoptions.doencode: - #Generate the list of files that would be encoded - showdata = filemanager.getencodingfiles(inputoptions.readonly) - print_shows(showdata) - else: - # Generate the list of files to process - shows = filemanager.getfilestoprepare(inputoptions.numfiles) - print "num results: {0}".format(len(shows)) - print_shows(shows) - else: - if inputoptions.doencode: - #Encode the files and move them to their final destination - - logging.basicConfig(level=logging.DEBUG) - generallogger = createlogger("general", settings.generallogfile(), - logging.DEBUG) - actionlogger = createlogger("action", settings.actionlogfile(), - logging.INFO) - - showdata = filemanager.getencodingfiles(inputoptions.readonly) - generallogger.info("There are {0} files to process." - .format(len(showdata))) - for show in showdata: - generallogger.info("========================================") - generallogger.info("Processing {0} of {1}, {2}".format( - showdata.index(show) + 1, len(showdata), str(show))) - - if filemanager.checkfileexists(show.outputfile): - message = "File {0} already exists. Cannot process." \ - .format(show.outputfile) - generallogger.warning(message) - actionlogger.warning(message) - else: - result = libhandbrake.encode(settings.handbrakecommand(), - show.inputfile, - show.outputfile) - - generallogger.info("Encode finished with result: {0}" - .format(result)) - filemanager.performpostencodefileoperations( - show.inputfile, show.outputfile) - - if filemanager.checkduplicates(show.outputfile): - actionlogger.info("There is an existing video file" - "present for {0}" - .format(show.outputfile)) - - generallogger.info("Processing finished.") - generallogger.info("===========================" - "=============\n\n") - - libemail.sendemail(EMAIL_SETTINGS, "Encoding Complete", - "Finished encoding {0} shows." - .format(len(showdata))) - else: - # Process files for encoding - shows = filemanager.getfilestoprepare(inputoptions.numfiles) - print "Preparing {0} files".format(len(shows)) - tvdata = TVData(settings) - tvdata.prepareepisodes(shows) - - -def createlogger(name, filename, level): - """ - Create a logger named that will write to the file - """ - - logger = logging.getLogger(name) - handler = logging.FileHandler(filename, mode='w') - formatter = logging.Formatter('%(asctime)s %(message)s') - handler.setFormatter(formatter) - handler.setLevel(level) - logger.addHandler(handler) - return logger - - -if __name__ == "__main__": - main(sys.argv[1:]) diff --git a/libemail.py b/libemail.py deleted file mode 100644 index a886880..0000000 --- a/libemail.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Sat Jul 20 20:48:10 2013 - -@author: shanef -""" - -from libsettings import EmailSettings - -import smtplib -from email.mime.text import MIMEText - - -def sendemail(settingsfilename, subject, body): - """ - Send an email using the settings defined in settingsfilename - """ - - settings = EmailSettings(settingsfilename) - - msg = MIMEText(body, "plain") - msg["Subject"] = subject - msg["From"] = settings.getfromaddress() - msg["To"] = settings.gettoaddress() - - smtp = smtplib.SMTP(settings.getsmtpserver()) - smtp.ehlo() - smtp.starttls() - smtp.login(settings.getsmtpuser(), settings.getsmtppassword()) - smtp.sendmail(settings.getfromaddress(), [settings.gettoaddress()], - msg.as_string()) - smtp.quit() diff --git a/libfilemanager.py b/libfilemanager.py deleted file mode 100644 index db51349..0000000 --- a/libfilemanager.py +++ /dev/null @@ -1,244 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Jul 5 14:11:31 2013 - -@author: shanef -""" - -import glob -from libtvdatasource import TVData -import os -import shutil - - -class EncodeData: - """ - Contains detais of files to encode. - inputfile - The source file - outputfile - The destination file - show - The name of the show - """ - - def __init__(self, show=None, inputfile='', outputfile=''): - self.inputfile = inputfile - self.show = show - self.outputfile = outputfile - - def __str__(self): - return "Show: {0}\nInput: {1}\nOutput: " \ - "{2}\n".format(self.show, self.inputfile, self.outputfile) - - def checkproblems(self): - """ - Check the EncodeData object for any potential problems. - """ - - errors = [] - - if checkfileexists(self.outputfile, False): - errors.append("FILE_EXISTS") - - if self.outputfile[-5:-4] == "_": - tempoutfile = self.outputfile[:-5] + self.outputfile[-4:] - if checkfileexists(tempoutfile, False): - errors.append("FILE_EXISTS") - - return errors - - -class FileManager: - """ - Perform file operations - """ - - def __init__(self, settings): - self.__settings = settings - - def getencodingfiles(self, readonly=True): - """ - Get the details of the shows that are ready for encoding - """ - - showsdata = self.__getinputfilestoencode() - for showdata in showsdata: - showdata.outputfile = self.__getencodeoutputfile( - showdata.inputfile, showdata.show, readonly) - - return showsdata - - def performpostencodefileoperations(self, inputfilename, outputfilename): - """ - Delete the input file, and the original recorded file. Then create a - symlink from the new encoded file to the original mythtv file. - """ - - shutil.rmtree(os.path.dirname(inputfilename)) - - linkaddress = self.__getrecordingfile(inputfilename) - - os.remove(linkaddress) - - os.symlink(outputfilename, linkaddress) - - def getfilestoprepare(self, numberoffiles): - """ - Get the details of the first to prepare for encoding. - If there are less files than available, it will - return the details of the number available. - """ - - path = self.__settings.tvrecordingdirectory() - potentialfiles = glob.glob("{0}*.mpg".format(path)) - potentialfiles = sorted(potentialfiles, key=os.path.getctime) - potentialfiles = [potentialfile for potentialfile in potentialfiles - if not os.path.islink(potentialfile)] - - #files is now a list of unprocessed files, but contains shows other - #than those we are interested in - showstoprocess = [] - i = 0 - print "Found {0} potential files".format(len(potentialfiles)) - - tvdata = TVData(self.__settings) - - for potentialfile in potentialfiles: - showdata = tvdata.retrieveepisodedata(potentialfile) - if showdata: - showstoprocess.append(showdata) - i = i + 1 - if i == int(numberoffiles): - return showstoprocess - - #will reach here if there were less than numberofFiles found - return showstoprocess - - def checkexistingduplicates(self): - """ - Check the existing files in the output directories for duplicate - files, typically in different formats - """ - - duplicates = [] - for show in self.__settings.getshownames(): - outputdir = self.__settings.getshowoutputdirectory(show) - - for rootdir, dirnames, filenames in os.walk(outputdir): - for fle in filenames: - filename = os.path.join(rootdir, fle) - if os.path.splitext(fle)[1].lower() in [".avi", ".mpg", ".mpeg", - "mp4", ".mkv"]: - if self.checkduplicates(filename): - duplicates.append(filename) - - return sorted(duplicates) - - @staticmethod - def checkduplicates(filename): - """ - Check to see if there are any other video files existing for the - episode - """ - - dirname = os.path.dirname(filename) - filename = os.path.basename(filename) - fileseasonepisode = filename[:6] - fileextension = os.path.splitext(filename)[1] - - for _, _, filenames in os.walk(dirname): - for show in filenames: - extension = os.path.splitext(show)[1] - if (extension.lower() in [".avi", ".mpg", ".mpeg", "mp4", ".mkv"] and - show[:6] == fileseasonepisode - and fileextension != extension): - return True - - return False - - @staticmethod - def checkfileexists(filename, casesensitive=True): - """ - Check to see if a file currently exists - """ - if casesensitive: - return os.path.exists(filename) - else: - filename = os.path.basename(filename) - for dirfile in os.listdir(os.path.dirname(filename)): - if (filename.lower() == dirfile.lower()): - return True - - return False - - def __getinputfilestoencode(self): - """ - Get the details of the files that are waiting to be encoded - """ - - filelist = [] - - for show in self.__settings.getshownames(): - for dirpath, _, filenames in os.walk( - self.__settings.getshowinputdirectory(show)): - for inputfile in filenames: - if inputfile.endswith(".mpg"): - data = EncodeData(show, os.path.join( - dirpath, inputfile)) - filelist.append(data) - - return filelist - - def __getencodeoutputfile(self, inputfile, showname, readonly): - """ - Get the full path of the output filename to save the encoded video to - """ - - infile = os.path.basename(inputfile) - outfilename = infile[:-3]+"mkv" - outpath = findseason(self.__settings.getshowoutputdirectory( - showname), outfilename, readonly) - return os.path.join(outpath, outfilename) - - def __getrecordingfile(self, filename): - """ - Get the name of the mythtv recording based on the filename. The - filename contains the name of the mythtv recording as the - final directory in it's path. - """ - - return os.path.join(self.__settings.tvrecordingdirectory(), - os.path.dirname(filename).split("/")[-1] + ".mpg") - - -def findseason(path, filename, readonly): - """ - Get the name of the season folder. eg. Season 01 - """ - - season = "Season {0}".format(filename[1:3]) - seasonpath = os.path.join(path, season) - - if not readonly: - if not os.path.exists(seasonpath): - os.makedirs(seasonpath) - - return seasonpath - - -def checkfileexists(filename, casesensitive=True): - """ - Check to see if a file currently exists - """ - dirname = os.path.dirname(filename) - - if casesensitive: - return os.path.exists(filename) - else: - if not os.path.exists(dirname): - return False - - basename = os.path.basename(filename) - for dirfile in os.listdir(dirname): - if (basename.lower() == dirfile.lower()): - return True - - return False diff --git a/libhandbrake.py b/libhandbrake.py deleted file mode 100644 index 0e865f7..0000000 --- a/libhandbrake.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Jul 5 14:11:00 2013 - -@author: shanef - -Library to interface with handbrake to encode video files -""" - -import subprocess - - -def encode(handbrakecommand, inputfile, outputfile, waitforcompletion=True, - logger=None): - """ - Encode inputfile and save the result to outputfile. handbrakecommand is - a list of strings containing the arguments to handbrakecli. - """ - - handbrakecommand[3] = inputfile - handbrakecommand[5] = outputfile - - if logger: - logger.debug("Handbrake command is: {0}".format(handbrakecommand)) - - process = subprocess.Popen(handbrakecommand) - - if waitforcompletion: - process.wait() - - if logger is not None: - logger.info("Handbrake completed with return code {0}".format( - process.returncode)) - return process.returncode - - return None diff --git a/libmythtv.py b/libmythtv.py deleted file mode 100644 index a65cd7d..0000000 --- a/libmythtv.py +++ /dev/null @@ -1,53 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Jul 5 14:10:47 2013 - -@author: shanef -""" - -import MySQLdb as mdb -from libtvshow import TVShow - - -class MythTV: - """ - Contains methods used for interacting with mythtv - """ - - def __init__(self, settings): - self.__settings = settings - - def retrieveepisodedata(self, inputfile): - """ - Retrieve the data that mythtv knows about the recorded file. - """ - con = mdb.connect(self.__settings.mythtvaddress(), - self.__settings.mythtvuser(), - self.__settings.mythtvpassword(), - self.__settings.mythtvdatabase()) - - with con: - cur = con.cursor(mdb.cursors.DictCursor) - cur.execute("select episode, season, title, subtitle, " - "description from mythconverg.recorded where " - "basename = '{0}'".format(inputfile)) - result = cur.fetchone() - - return TVShow(result['episode'], result['season'], - result['title'], result['subtitle'], - result['description']) - - def fixmythtvepisodename(self, showname, episodetitle): - """ - Look for any prefixes listed in the configuration file. If there are - any and the episide title starts with the prefix, remove the prefix - from the episode title. The searching is done in the order that the - prefixes are listed in the configuration file. - """ - - for prefix in self.__settings.getshowmythtvepisodeprefix(showname): - if episodetitle.lower().startswith(prefix.lower()): - return episodetitle[len(prefix):] - - #didn't find anything so return the episode title - return episodetitle diff --git a/libsettings.py b/libsettings.py deleted file mode 100644 index efc5db6..0000000 --- a/libsettings.py +++ /dev/null @@ -1,287 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Jul 5 20:14:15 2013 - -@author: shanef -""" - -from configobj import ConfigObj - - -class Settings: - """ - Accessor for the configuration file - """ - - def __init__(self, settingsfile): - """ - Initialise settingsfile as a configobj - """ - - self.__config = ConfigObj(settingsfile) - - def tvrecordingdirectory(self): - """ - Get the TVRecordings setting - """ - - return self.__config["TVRecordings"] - - def handbrakecommand(self): - """ - Get the HandbrakeCommand setting - """ - - return self.__config["HandbrakeCommand"] - - def illegalcharacters(self): - """ - Get a list of illegal characters for filenames - """ - - return self.__config["IllegalCharacters"] - - def generallogfile(self): - """ - Get the filename to save general log messages to - """ - - return self.__config["Logging"]["General"] - - def actionlogfile(self): - """ - Get the filename to save the action log messages to - """ - - return self.__config["Logging"]["Action"] - - def mythtvaddress(self): - """ - Get the MythTV/address setting - """ - - return self.__config["MythTV"]["address"] - - def mythtvuser(self): - """ - Get the MythTV/user setting - """ - - return self.__config["MythTV"]["user"] - - def mythtvpassword(self): - """ - Get the MythTV/password setting - """ - - return self.__config["MythTV"]["password"] - - def mythtvdatabase(self): - """ - Get the MythTV/database setting - """ - - return self.__config["MythTV"]["database"] - - def sickbeardaddress(self): - """ - Get the Sickbeard/address setting - """ - - return self.__config["Sickbeard"]["address"] - - def sickbeardport(self): - """ - Get the Sickbeard/port setting - """ - - return int(self.__config["Sickbeard"]["port"]) - - def sickbeardapikey(self): - """ - Get the Sickbeard/APIKey setting - """ - - return self.__config["Sickbeard"]["APIKey"] - - def unknowndirectory(self): - """ - Get the Shows/UnknownInput directory. It is the directory used for - episodes where nothing is known about it - """ - - return self.__config["Shows"]["UnknownInput"] - - def getshownames(self, includealias=False): - """ - Get a list of the names of the shows that are specified in the - settings file. If includealias is True, it will also include any - defined aliases in the list. - """ - - shows = self.__config["Shows"].sections - result = shows[:] - if includealias: - for show in shows: - for alias in self.__config["Shows"][show]["alias"]: - result.append(alias) - return result - - def findshownameforalias(self, aliasname): - """ - Find the name of the show. If the supplied aliasname is an alias, it - will return the show name. If aliasname is the name of a show, it will - return aliasname - """ - - if aliasname in self.getshownames(): - # aliasname is the name of an actual show - return aliasname - - # search for the show that the alias belongs to - for showsettings in self.__config["Shows"]: - if aliasname in showsettings["alias"]: - return showsettings.name - - # Could not find it anywhere - return None - - def getshowinputdirectory(self, showname): - """ - Get the InputDirectory setting for the show, showname. - """ - - show = self.__getshowsubsection(showname) - if show is None: - return "" - else: - return show["InputDirectory"] - - def getshowunknowndirectory(self, showname): - """ - Get the UnknownDirectory setting for the show, showname. It is used - when the show is known, but the season or episode are not. - """ - - show = self.__getshowsubsection(showname) - if show is None: - return "" - else: - return show["UnknownDirectory"] - - def getshowoutputdirectory(self, showname): - """ - Get the OutputDirectory setting for the show, showname. - """ - - show = self.__getshowsubsection(showname) - if show is None: - return "" - else: - return show["OutputDirectory"] - - def getshowalias(self, showname): - """ - Get the alias setting for the show, showname. It returns a list of - aliases. - """ - - show = self.__getshowsubsection(showname) - if show is None: - return "" - else: - return show["alias"] - - def getshowmythtvepisodeprefix(self, showname): - """ - Get the MythTVEpisodePrefix setting for the show, showname. - """ - - show = self.__getshowsubsection(showname) - if show is None: - return "" - else: - return show["MythTvEpisodePrefix"] - - def getshowsickbeardepisodeprefix(self, showname): - """ - Get the SickbeardPrefix setting for the show, showname. - """ - - show = self.__getshowsubsection(showname) - if show is None: - return "" - else: - return show["SickbeardPrefix"] - - def getshow(self, showname): - """ - Get the name of the show, showname. - """ - showsection = self.__getshowsubsection(showname) - if showsection is None: - return None - else: - return showsection.name - - def __getshowsubsection(self, showname): - """ - Get the configuration options for the show, showname. - """ - - if showname in self.getshownames(): - return self.__config["Shows"][showname] - else: # check liases - for show in self.getshownames(): - if showname in self.__config["Shows"][show]["alias"]: - return self.__config["Shows"][show] - - return None - - -class EmailSettings: - """ - Accessor for the email configuration file - """ - - def __init__(self, settingsfile): - """ - Initialise settingsfile as a configobj - """ - - self.__config = ConfigObj(settingsfile) - - def getsmtpserver(self): - """ - Get the address of the smtp server - """ - - return self.__config["SMTPServer"] - - def getsmtpuser(self): - """ - Get the username for the smtp server - """ - - return self.__config["SMTPUser"] - - def getsmtppassword(self): - """ - Get the username for the smtp server - """ - - return self.__config["SMTPPassword"] - - def getfromaddress(self): - """ - Get the from address for emails - """ - - return self.__config["From"] - - def gettoaddress(self): - """ - Get the to address for emails - """ - - return self.__config["To"] diff --git a/libsickbeard.py b/libsickbeard.py deleted file mode 100644 index 3ef2ad1..0000000 --- a/libsickbeard.py +++ /dev/null @@ -1,153 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Jul 5 14:10:37 2013 - -@author: shanef -""" - -import json -from urllib import urlopen -from fuzzywuzzy import fuzz -from operator import itemgetter - - -class Sickbeard: - """ - Contains operations used to interact with sickbeard - """ - - def __init__(self, settings): - self.__settings = settings - self.__address = settings.sickbeardaddress() - self.__port = settings.sickbeardport() - self.__apikey = settings.sickbeardapikey() - - def findshowid(self, showname): - """ - Get the tvdb show id for the show - """ - - jsonurl = urlopen(self.__getapiurl()+"?cmd=shows") - result = json.loads(jsonurl.read()) - - showname = self.__settings.findshownameforalias(showname) - - shows = [] - for show in result['data']: - shows.append((show, fuzz.partial_ratio(showname.lower(), - result['data'][show] - ['show_name'].lower()))) - - shows = sorted(shows, key=itemgetter(1), reverse=True) - - if shows[0][1] > 85: - return shows[0][0] - - def findepisodename(self, showid, season, episode): - """ - Get the name of an episode, given it's season and episode numbers - """ - - jsonurl = urlopen("{0}?cmd=episode&tvdbid={1}&season={2}" - "&episode={3}".format(self.__getapiurl(), showid, - int(season), int(episode))) - - result = json.loads(jsonurl.read()) - - if result['result'] == 'error': - return "" - else: - return result['data']['name'] - - def findepisode(self, showid, name=None, description=None): - """ - Find an episode, either by it's name or it's description. This is used - when the season and episode numbers are not known - """ - - jsonurl = urlopen("{0}?cmd=show.seasons&tvdbid={1}".format( - self.__getapiurl(), showid)) - - result = json.loads(jsonurl.read()) - - for season in result['data']: - for episode in result['data'][season]: - episodename = result['data'][season][episode]['name'] - if name is not None and fuzz.ratio(name.lower(), - episodename.lower()) > 85: - return (season, episode, episodename) - elif description is not None: - descriptionqueryresult = \ - self.__findepisodebydescription(showid, season, - episode, description) - if descriptionqueryresult is not None: - return descriptionqueryresult - - return (0, 0, '') - - def fixepisodetitle(self, showname, episodetitle): - """ - Check to see if there is a prefix specified for the show. If there is, - add the prefix to the start of the episode title - """ - - sickbeardprefix = \ - self.__settings.getshowsickbeardepisodeprefix(showname) - - if sickbeardprefix != "": - if not episodetitle.lower().startswith(sickbeardprefix.lower()): - return "{0} {1}".format(sickbeardprefix.rstrip(), - episodetitle.lstrip()) - - return episodetitle - - def __getapiurl(self): - """ - Get the url of the sickbeard api, substituting the values from the - settings - """ - - return "http://{0}:{1}/api/{2}/".format(self.__address, self.__port, - self.__apikey) - - def __findepisodebydescription(self, showid, season, episode, description): - """ - Find the details of an episode by searching for it's description - """ - - jsonepisodeurl = urlopen("{0}?cmd=episode&tvdbid={1}&season={2}" - "&episode={3}".format(self.__getapiurl(), - showid, season, - episode)) - episoderesult = json.loads(jsonepisodeurl.read()) - - sickbearddescription = episoderesult['data']['description'] - - if fuzzystringcompare(sickbearddescription, description): - return (season, episode, episoderesult['data']['name']) - - return None - - -def fuzzystringcompare(string1, string2, matchvalue=85, casesensitive=False): - """ - Compare two strings to see if they match it first does a straight - comparison. Secondly, it concatenates the longer string to the length of - the shorter one, and tries to compare them again. - """ - - if not casesensitive: - string1 = string1.lower() - string2 = string2.lower() - - if fuzz.ratio(string1, string2) > matchvalue: - return True - - if len(string1) > len(string2): - if fuzz.ratio(string1[:len(string2)], string2) > matchvalue: - return True - elif len(string2) > len(string1): - if fuzz.ratio(string1, string2[:len(string1)]) > matchvalue: - return True - - return False diff --git a/libtvdatasource.py b/libtvdatasource.py deleted file mode 100644 index ddffeec..0000000 --- a/libtvdatasource.py +++ /dev/null @@ -1,144 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Jul 5 14:42:47 2013 - -@author: shanef -""" - -from libmythtv import MythTV -from libsickbeard import Sickbeard -import os -import shutil - - -def fixepisodeseasonnumber(number): - """ - If the number is single digit, return a string with 0 in front of it. - """ - - if len(number) == 1: - return "0{0}".format(number) - else: - return number - - -class TVData: - """ - Class contains logic for processing information about tv episodes - """ - - def __init__(self, settings): - self.__settings = settings - - def getdirectory(self, title, seasonfolder, season, episode): - """ - Get the directory where prepared episodes will be located. - """ - - show = self.__settings.getshow(title) - if not show or show == "": - print "Couldn't find show for {0}".format(title) - return self.__settings.unknowndirectory() - elif season == "S00" or episode == "E00": - return self.__settings.getshowunknowndirectory(show) - else: - return os.path.join(self.__settings.getshowinputdirectory(show), - seasonfolder) - - def retrieveepisodedata(self, inputfile): - """ - Retrieve the details of an episode. It first looks up the details that - mythtv recorded about it, then looks up sickbeard to attempt to find - any missing details. Finally it determined the output file for it. - """ - - inputfilename = os.path.basename(inputfile) - - mythtv = MythTV(self.__settings) - show = mythtv.retrieveepisodedata(inputfilename) - - showstoprocess = self.__settings.getshownames(True) - - if show.title and show.title in showstoprocess: - show.title = self.__settings.getshow(show.title) - - if (show.season == "0" or show.episode == "0"): - sickbeard = Sickbeard(self.__settings) - showid = sickbeard.findshowid(show.title) - - if show.subtitle is not None and show.subtitle: - show.subtitle = mythtv.fixmythtvepisodename(show.title, - show.subtitle) - show.subtitle = sickbeard.fixepisodetitle(show.title, - show.subtitle) - - result = sickbeard.findepisode(showid, show.subtitle, - show.description) - show.season = str(result[0]) - show.episode = str(result[1]) - show.subtitle = result[2] - - if show.subtitle is None or show.subtitle == "": - show.subtitle = sickbeard.findepisodename(showid, show.season, - show.episode) - - show.season = fixepisodeseasonnumber(show.season) - show.episode = fixepisodeseasonnumber(show.episode) - - seasonfolder = "Season {0}".format(show.season) - season = "S{0}".format(show.season) - episode = "E{0}".format(show.episode) - renamedfile = self.getoutputfilename(season, episode, - show.subtitle) - - directory = self.getdirectory(show.title, seasonfolder, - season, episode) - - show.outputfile = os.path.join(directory, inputfilename[:-4], - renamedfile) - show.inputfile = inputfile - - return show - else: - return None - - def getoutputfilename(self, season, episode, name): - """ - Get the output filename, and remove any illegal characters - """ - - filename = "{0}{1} - {2} - SD TV_.mpg".format(season, episode, name) - - for illegalcharacter in self.__settings.illegalcharacters(): - filename = filename.replace(illegalcharacter, "") - - return filename - - @staticmethod - def processepisode(inputfile, outputfile): - """ - Copy inputfile to outputfile, creating the path for outputfile if - required. - """ - - outputdir = os.path.dirname(outputfile) - if not os.path.exists(outputdir): - os.makedirs(outputdir) - - shutil.copyfile(inputfile, outputfile) - - def prepareepisodes(self, showsdata): - """ - Copy the files in showsdata from their input directory to their output - directory. - """ - - for showdata in showsdata: - print "========================================" - print "Copying {0} to {1}".format(showdata.inputfile, - showdata.outputfile) - - self.processepisode(showdata.inputfile, showdata.outputfile) - - print "Finished copy" - print "========================================\n\n" diff --git a/libtvshow.py b/libtvshow.py deleted file mode 100644 index 73903a9..0000000 --- a/libtvshow.py +++ /dev/null @@ -1,43 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Sat Jul 6 20:26:22 2013 - -@author: shanef -""" - -import os -#from libfilemanager import FileManager - - -class TVShow(object): - """ - Describes the details of a tv episode - """ - - def __init__(self, episode, season, title, subtitle, description, - inputfile='', outputfile=''): - self.episode = str(episode) - self.season = str(season) - self.title = title - self.subtitle = subtitle - self.description = description - self.inputfile = inputfile - self.outputfile = outputfile - - def __str__(self): - return "Input: {0} -> Output: {1}".format(self.inputfile, - self.outputfile) - - def checkproblems(self): - """ - Check the TVShow object for any potential problems. - """ - - errors = [] - if self.episode == "E00" or self.season == "S00" or not self.subtitle: - errors.append("NO_EPISODE") - - if os.path.exists(self.outputfile): - errors.append("FILE_EXISTS") - - return errors diff --git a/pep8.sh b/pep8.sh deleted file mode 100755 index ce2b244..0000000 --- a/pep8.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -pep8 libemail.py TVEncoder.py libfilemanager.py libhandbrake.py libmythtv.py libsettings.py libsickbeard.py libtvdatasource.py libtvshow.py diff --git a/pylint.sh b/pylint.sh deleted file mode 100755 index 5204ecc..0000000 --- a/pylint.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -pylint TVEncoder.py libfilemanager.py libhandbrake.py libmythtv.py libsettings.py libsickbeard.py libtvdatasource.py libtvshow.py libemail.py diff --git a/settings.cfg b/settings.cfg deleted file mode 100644 index b84c6ca..0000000 --- a/settings.cfg +++ /dev/null @@ -1,72 +0,0 @@ -TVRecordings = "/Volumes/TV Recordings/" -HandbrakeCommand = "HandBrakeCLI", "--verbose", "-i", "SUBSTITUTE WITH INPUT FILE", "-o", "SUBSTITUDE WITH OUTPUT FILE", "-f", "mkv", "-e", "x264", "-x264-preset", "slower", "-x264-tune", "animation", "-q", "20", "--loose-anamorphic", "--decomb", "--detelecine", '--denoise="2:1:2:3"', "--deblock" -IllegalCharacters = "?", ":" - -[ "Logging" ] - General = "logs/encoding.log" - Action = "logs/needsaction.log" - -[ "MythTV" ] - address = 192.168.0.2 - user = script - password = script - database = mythconverg - -[ "Sickbeard" ] - address = 192.168.0.2 - port = 8081 - APIKey = 3678177136222bf5002be209220ccb20 - -[ "Shows" ] - VideoProcessingDir = "/srv/storage2/files/VideoProcessing/" - KidsTVDir = "/srv/storage2/videos/Kids/TV/" - UnknownInput = "%(VideoProcessingDir)sUnknown/" - [[ "Thomas the Tank Engine & Friends" ]] - InputDirectory = "%(VideoProcessingDir)sThomas/" - UnknownDirectory = "%(UnknownInput)sThomas/" - OutputDirectory = "%(KidsTVDir)sThomas The Tank Engine & Friends/" - alias = "Thomas and Friends", - MythTvEpisodePrefix = , - SickbeardPrefix = "" - [[ "Chuggington" ]] - InputDirectory = "%(VideoProcessingDir)sChuggington/" - UnknownDirectory = "%(UnknownInput)sChuggington/" - OutputDirectory = "%(KidsTVDir)sChuggington/" - alias = , - MythTvEpisodePrefix = , - SickbeardPrefix = "" - [[ "Mike the Knight" ]] - InputDirectory = "%(VideoProcessingDir)sMikeTheKnight/" - UnknownDirectory = "%(UnknownInput)sMikeTheKnight/" - OutputDirectory = "%(KidsTVDir)sMike the Knight/" - alias = , - MythTvEpisodePrefix = "Mike the Knight and the ", Mike the Knight and " - SickbeardPrefix = "" - [[ "Octonauts" ]] - InputDirectory = "%(VideoProcessingDir)sOctonauts/" - UnknownDirectory = "%(UnknownInput)sOctonauts/" - OutputDirectory = "%(KidsTVDir)sOctonauts/" - alias = "The Octonauts", - MythTvEpisodePrefix = "The Octonauts and ", - SickbeardPrefix = "The" - [[ "In the Night Garden" ]] - InputDirectory = "%(VideoProcessingDir)sInTheNightGarden/" - UnknownDirectory = "%(UnknownInput)sInTheNightGarden/" - OutputDirectory = "%(KidsTVDir)sIn The Night Garden/" - alias = , - MythTvEpisodePrefix = , - SickbeardPrefix = "" - [[ "Raa Raa! The Noisy Lion" ]] - InputDirectory = "%(VideoProcessingDir)sRaaRaa/" - UnknownDirectory = "%(UnknownInput)sRaaRaa/" - OutputDirectory = "%(KidsTVDir)sRaa Raa the Noisy Lion/" - alias = , - MythTvEpisodePrefix = , - SickbeardPrefix = "" - [[ "Fireman Sam" ]] - InputDirectory = "%(VideoProcessingDir)sFiremanSam/" - UnknownDirectory = "%(UnknownInput)sFiremanSam/" - OutputDirectory = "%(KidsTVDir)sFireman Sam/" - alias = , - MythTvEpisodePrefix = , - SickbeardPrefix = "" \ No newline at end of file diff --git a/sickbeard.py b/sickbeard.py new file mode 100755 index 0000000..31a835d --- /dev/null +++ b/sickbeard.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +""" +Created on Tue Jul 2 21:00:35 2013 + +@author: shane +""" +import json +from urllib import urlopen +from fuzzywuzzy import fuzz +from operator import itemgetter + +def FindShowId(showName): + jsonurl = urlopen("http://192.168.0.2:8081/api/3678177136222bf5002be209220ccb20/?cmd=shows") + result = json.loads(jsonurl.read()) + + shows = [] + for show in result['data']: + shows.append((show, fuzz.partial_ratio(showName.lower(), result['data'][show]['show_name'].lower()))) + + shows = sorted(shows, key=itemgetter(1), reverse=True) + + if shows[0][1] > 85: + return shows[0][0] + +def FindEpisode(showId, name=None, description=None): + jsonurl = urlopen("http://192.168.0.2:8081/api/3678177136222bf5002be209220ccb20/?cmd=show.seasons&tvdbid={0}".format(showId)) + result = json.loads(jsonurl.read()) + + for season in result['data']: + for episode in result['data'][season]: + if name is not None and name.lower() == result['data'][season][episode]['name'].lower(): + return (season, episode) + elif description is not None: + result = FindEpisodeByDescription(showId, season, episode, description) + if result is not None: + return result + + return (0, 0) + +def GetEpisodeName(subtitle, showName): + if subtitle[:len(showName)].lower() == showName.lower(): + return subtitle[len(showName + ' and the '):] + else: + return subtitle + +def FindEpisodeByDescription(showId, season, episode, description): + jsonEpisodeUrl = urlopen("http://192.168.0.2:8081/api/3678177136222bf5002be209220ccb20/?cmd=episode&tvdbid={0}&season={1}&episode={2}".format(showId, season, episode)) + episodeResult = json.loads(jsonEpisodeUrl.read()) + + if fuzz.ratio(episodeResult['data']['description'].lower(), description.lower()) > 85: + return (season, episode) + + return None + +showId = FindShowId('Mike the Knight') +#showId = FindShowId("Octonauts") +#print showId + +subtitle = 'Mike the Knight and the Knightly Campout' + +description = "When the Octopod's waterworks are flooded with frightened Humuhumu fish, the Octonauts have to find a way to flush them out!" + +episodeName = GetEpisodeName(subtitle, 'Mike the Knight') + +result = FindEpisode(showId, episodeName) +#result = FindEpisodeByDescription(showId, description) +print result[0] +print result[1] \ No newline at end of file diff --git a/tests/TVEncodertest.py b/tests/TVEncodertest.py deleted file mode 100644 index d415f05..0000000 --- a/tests/TVEncodertest.py +++ /dev/null @@ -1,46 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Sat Jul 13 20:37:47 2013 - -@author: shanef -""" - -import unittest -import os -import sys -parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -sys.path.insert(0, parentdir) -import TVEncoder - - -class TVEncoderTest(unittest.TestCase): - def test_processarguments_encodereadonly(self): - args = [] - args.append(('-e', '')) - args.append(('-l', '')) - result = TVEncoder.processarguments(args) - - self.assertTrue(result.doencode) - self.assertTrue(result.readonly) - - def test_processarguments_encodereadonlyreverse(self): - args = [] - args.append(('-l', '')) - args.append(('-e', '')) - result = TVEncoder.processarguments(args) - - self.assertTrue(result.doencode) - self.assertTrue(result.readonly) - - def test_processarguments_encode(self): - args = [] - args.append(('-e', '')) - result = TVEncoder.processarguments(args) - - self.assertTrue(result.doencode) - self.assertFalse(result.readonly) - - -if __name__ == '__main__': - suite = unittest.TestLoader().loadTestsFromTestCase(TVEncoderTest) - unittest.TextTestRunner(verbosity=2).run(suite) \ No newline at end of file diff --git a/tests/emailtest.py b/tests/emailtest.py deleted file mode 100644 index 92cc000..0000000 --- a/tests/emailtest.py +++ /dev/null @@ -1,33 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Jul 19 23:31:16 2013 - -@author: shanef -""" - -from minimock import Mock, mock -import unittest -import os -import sys -parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -sys.path.insert(0, parentdir) -import libemail -from libsettings import EmailSettings -import smtplib - - -class libemailtest(unittest.TestCase): - def test_SendEmail(self): - mock("EmailSettings.getfromaddress", returns="from@email.com") - mock("EmailSettings.gettoaddress", returns="to@email.com") - mock("EmailSettings.getsmtpserver", returns="smtp.test") - mock("EmailSettings.getsmtpuser", returns="user") - mock("EmailSettings.getsmtppassword", returns="password") - smtplib.SMTP = Mock('smtplib.SMTP') - smtplib.SMTP.mock_returns = Mock('smtp_connection') - - libemail.sendemail("test", "subject", "body") - -if __name__ == '__main__': - suite = unittest.TestLoader().loadTestsFromTestCase(libemailtest) - unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/tests/libfilemanagertest.py b/tests/libfilemanagertest.py deleted file mode 100644 index c8b8f9b..0000000 --- a/tests/libfilemanagertest.py +++ /dev/null @@ -1,117 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Jul 5 14:12:26 2013 - -@author: shanef -""" - -import unittest -import os -import sys -import minimock -from minimock import mock, Mock -parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -sys.path.insert(0, parentdir) -from libfilemanager import EncodeData, FileManager - - -class libfilemanagertest(unittest.TestCase): - def test_EncodeDataPrint(self): - showname = "test show" - inputname = "test input" - outputname = "test output" - data = EncodeData(showname, inputname, outputname) - result = str(data) - expected = "Show: {0}\nInput: {1}\nOutput: " \ - "{2}\n".format(showname, inputname, outputname) - self.assertEqual(result, expected) - - def test_EncodeDataCheckProblemsFileExists(self): - showname = "test show" - inputname = "test input" - outputname = "test_output.mkv" - data = EncodeData(showname, inputname, outputname) - mock("os.path.exists", returns=True) - - result = data.checkproblems() - - self.assertIn("FILE_EXISTS", result) - minimock.restore() - - def test_EncodeDataCheckProblemsFile_Exists(self): - showname = "test show" - inputname = "test input" - outputname = "test_output_.mkv" - data = EncodeData(showname, inputname, outputname) - mock("os.path.exists", returns_iter=[False, True]) - result = data.checkproblems() - self.assertIn("FILE_EXISTS", result) - minimock.restore() - - def test_checkfileexistscaseinsensitive(self): - settings = Mock('libsettings.Settings') - filemanager = FileManager(settings) - - mock("os.listdir", returns=["filename.test"]) - - result = filemanager.checkfileexists("/path/to/fiLename.test", False) - - self.assertTrue(result) - minimock.restore() - - def test_checkduplicateavi(self): - settings = Mock('libsettings.Settings') - filemanager = FileManager(settings) - - os.walk = dummywalk - - result = filemanager.checkduplicates("/path/to/S03E14 - Test - SD TV.mkv") - - self.assertTrue(result) - minimock.restore() - - def test_checkduplicatethomas(self): - settings = Mock('libsettings.Settings') - filemanager = FileManager(settings) - - os.walk = thomaswalk - - result = filemanager.checkduplicates("/path/to/S12E05 - Henry Gets It Wrong - SD TV.mkv") - - self.assertTrue(result) - minimock.restore() - - def test_checkduplicatenomatch(self): - settings = Mock('libsettings.Settings') - filemanager = FileManager(settings) - - os.walk = dummywalk - - result = filemanager.checkduplicates("/path/to/S03E13 - Test - SD TV.mkv") - - self.assertFalse(result) - minimock.restore() - - def test_checkduplicatesameextension(self): - settings = Mock('libsettings.Settings') - filemanager = FileManager(settings) - - os.walk = dummywalk - - result = filemanager.checkduplicates("/path/to/S03E14 - Test - SD TV.avi") - - self.assertFalse(result) - minimock.restore() - - - -def dummywalk(arg): - return [("/path/to/", [], ["S03E14 - Test - SD TV.avi"])] - -def thomaswalk(arg): - return [(("/path/to/", [], ["S12E05 - Henry Gets It Wrong - Unknown.AVI"]))] - -if __name__ == '__main__': - suite = unittest.TestLoader().loadTestsFromTestCase(libfilemanagertest) - unittest.TextTestRunner(verbosity=2).run(suite) - minimock.restore() diff --git a/tests/libhandbraketest.py b/tests/libhandbraketest.py deleted file mode 100644 index 993692d..0000000 --- a/tests/libhandbraketest.py +++ /dev/null @@ -1,7 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Jul 5 14:13:19 2013 - -@author: shanef -""" - diff --git a/tests/libmythtvtest.py b/tests/libmythtvtest.py deleted file mode 100644 index 893cdd4..0000000 --- a/tests/libmythtvtest.py +++ /dev/null @@ -1,55 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Jul 5 14:12:53 2013 - -@author: shanef -""" - -import unittest -from minimock import Mock -import os -import sys -parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -sys.path.insert(0, parentdir) -import libmythtv - - -class MythTVTest(unittest.TestCase): - def test_FixEpisodeNameNoPrefix(self): - settings = Mock('libsettings.Settings') - settings.getshowmythtvepisodeprefix.mock_returns = "" - mythtv = libmythtv.MythTV(settings) - result = mythtv.fixmythtvepisodename("Show", "episode") - self.assertEqual(result, "episode") - - def test_FixEpisodeNameNonMatchingPrefix(self): - settings = Mock('libsettings.Settings') - settings.getshowmythtvepisodeprefix.mock_returns = [ "BloohBlah" ] - mythtv = libmythtv.MythTV(settings) - result = mythtv.fixmythtvepisodename("Show", "episode") - self.assertEqual(result, "episode") - - def test_FixEpisodeNameMatchingPrefix(self): - settings = Mock('libsettings.Settings') - settings.getshowmythtvepisodeprefix.mock_returns = [ "Match " ] - mythtv = libmythtv.MythTV(settings) - result = mythtv.fixmythtvepisodename("Show", "Match episode") - self.assertEqual(result, "episode") - - def test_FixEpisodeNameMatchingFirstPrefix(self): - settings = Mock('libsettings.Settings') - settings.getshowmythtvepisodeprefix.mock_returns = [ "Match and ", "Match the " ] - mythtv = libmythtv.MythTV(settings) - result = mythtv.fixmythtvepisodename("Show", "Match and episode") - self.assertEqual(result, "episode") - - def test_FixEpisodeNameMatchingSecondPrefix(self): - settings = Mock('libsettings.Settings') - settings.getshowmythtvepisodeprefix.mock_returns = [ "Match and ", "Match the " ] - mythtv = libmythtv.MythTV(settings) - result = mythtv.fixmythtvepisodename("Show", "Match the episode") - self.assertEqual(result, "episode") - -if __name__ == '__main__': - suite = unittest.TestLoader().loadTestsFromTestCase(MythTVTest) - unittest.TextTestRunner(verbosity=2).run(suite) \ No newline at end of file diff --git a/tests/libsickbeardtest.py b/tests/libsickbeardtest.py deleted file mode 100644 index 2015aaa..0000000 --- a/tests/libsickbeardtest.py +++ /dev/null @@ -1,51 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Jul 5 14:12:38 2013 - -@author: shanef -""" - -import unittest -from minimock import Mock -import os -import sys -parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -sys.path.insert(0, parentdir) -import libsickbeard -import urllib - - -class SickbeardTest(unittest.TestCase): - def test_findepisodeCloseSubtitle(self): - settings = Mock('libsettings.Settings') - settings.sickbeardaddress.mock_returns = "test" - settings.sickbeardport.mock_returns = "123" - settings.sickbeardapikey.mock_returns = "test" - - urllib.urlopen = dummy_urlopen - - sickbeard = libsickbeard.Sickbeard(settings) - - result = sickbeard.findepisode("78949", "Splish, Splash, Splosh") - - self.assertEqual("13", result[0]) - self.assertEqual("15", result[1]) - self.assertEqual("Splish, Splash, Splosh!", result[2]) - - -def dummy_urlopen(arg): - class TmpClass: - def read(arg): - jsonresult = '{ "data": {"13": { "15": { "airdate": "2010-02-12", ' \ - '"name": "Splish, Splash, Splosh!", "quality": "N/A", ' \ - '"status": "Wanted" } } }, "message": "", ' \ - '"result": "success" }' - - return jsonresult - - return TmpClass() - - -if __name__ == '__main__': - suite = unittest.TestLoader().loadTestsFromTestCase(SickbeardTest) - unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/tests/libtvdatasourcetest.py b/tests/libtvdatasourcetest.py deleted file mode 100644 index cba96bb..0000000 --- a/tests/libtvdatasourcetest.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Thu Jul 18 23:13:15 2013 - -@author: shanef -""" - -import unittest -from minimock import Mock -import os -import sys -parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -sys.path.insert(0, parentdir) -import libtvdatasource - - -class tvdatasourceTest(unittest.TestCase): - def test_GetOutputFilenameNoIllegals(self): - result = self._dooutputfilenametest("S01", "E02", "test name", "") - self.assertEqual(result, "S01E02 - test name - SD TV_.mpg") - - def test_GetOutputFilenameOneIllegals(self): - result = self._dooutputfilenametest("S01", "E02", "test name?", "?") - self.assertEqual(result, "S01E02 - test name - SD TV_.mpg") - - def test_GetOutputFilenameTwoIllegals(self): - result = self._dooutputfilenametest("S01", "E02", "tes>t name?", ["?", ">"]) - self.assertEqual(result, "S01E02 - test name - SD TV_.mpg") - - def _dooutputfilenametest(self, season, episode, name, illegals): - settings = Mock('libsettings.Settings') - settings.illegalcharacters.mock_returns = illegals - tvdatasource = libtvdatasource.TVData(settings) - return tvdatasource.getoutputfilename(season, episode, name) - -if __name__ == '__main__': - suite = unittest.TestLoader().loadTestsFromTestCase(tvdatasourceTest) - unittest.TextTestRunner(verbosity=2).run(suite) \ No newline at end of file