From d63b2da06236f2dcd5e41e519e5eaabb9523cf68 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Fri, 5 Jul 2013 13:58:17 +1000 Subject: [PATCH 01/72] Removing old files --- DoEncode.py | 125 ------------------------------- ListFilesToEncode.py | 32 -------- ProcessRecordings.py | 154 --------------------------------------- ProcessRecordingsTest.py | 67 ----------------- sickbeard.py | 68 ----------------- 5 files changed, 446 deletions(-) delete mode 100755 DoEncode.py delete mode 100755 ListFilesToEncode.py delete mode 100755 ProcessRecordings.py delete mode 100755 ProcessRecordingsTest.py delete mode 100755 sickbeard.py diff --git a/DoEncode.py b/DoEncode.py deleted file mode 100755 index 846a726..0000000 --- a/DoEncode.py +++ /dev/null @@ -1,125 +0,0 @@ -# -*- 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/ListFilesToEncode.py b/ListFilesToEncode.py deleted file mode 100755 index d587abd..0000000 --- a/ListFilesToEncode.py +++ /dev/null @@ -1,32 +0,0 @@ -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 deleted file mode 100755 index abf40cf..0000000 --- a/ProcessRecordings.py +++ /dev/null @@ -1,154 +0,0 @@ -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 deleted file mode 100755 index 7f1b30b..0000000 --- a/ProcessRecordingsTest.py +++ /dev/null @@ -1,67 +0,0 @@ -# -*- 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/sickbeard.py b/sickbeard.py deleted file mode 100755 index 31a835d..0000000 --- a/sickbeard.py +++ /dev/null @@ -1,68 +0,0 @@ -# -*- 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 From d3440df49348a68babb7004f03c4c044ff922b75 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Fri, 5 Jul 2013 14:15:03 +1000 Subject: [PATCH 02/72] Added new files --- TVEncoder.py | 7 +++++++ libfilemanager.py | 7 +++++++ libhandbrake.py | 7 +++++++ libmythtv.py | 7 +++++++ libsickbeard.py | 7 +++++++ tests/libfilemanagertest.py | 7 +++++++ tests/libhandbraketest.py | 7 +++++++ tests/libmythtvtest.py | 7 +++++++ tests/libsickbeardtest.py | 7 +++++++ 9 files changed, 63 insertions(+) create mode 100644 TVEncoder.py create mode 100644 libfilemanager.py create mode 100644 libhandbrake.py create mode 100644 libmythtv.py create mode 100644 libsickbeard.py create mode 100644 tests/libfilemanagertest.py create mode 100644 tests/libhandbraketest.py create mode 100644 tests/libmythtvtest.py create mode 100644 tests/libsickbeardtest.py diff --git a/TVEncoder.py b/TVEncoder.py new file mode 100644 index 0000000..bb11bc6 --- /dev/null +++ b/TVEncoder.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Jul 5 14:14:22 2013 + +@author: shanef +""" + diff --git a/libfilemanager.py b/libfilemanager.py new file mode 100644 index 0000000..b055b8e --- /dev/null +++ b/libfilemanager.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Jul 5 14:11:31 2013 + +@author: shanef +""" + diff --git a/libhandbrake.py b/libhandbrake.py new file mode 100644 index 0000000..dd779f7 --- /dev/null +++ b/libhandbrake.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Jul 5 14:11:00 2013 + +@author: shanef +""" + diff --git a/libmythtv.py b/libmythtv.py new file mode 100644 index 0000000..a3ab093 --- /dev/null +++ b/libmythtv.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Jul 5 14:10:47 2013 + +@author: shanef +""" + diff --git a/libsickbeard.py b/libsickbeard.py new file mode 100644 index 0000000..a837534 --- /dev/null +++ b/libsickbeard.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Jul 5 14:10:37 2013 + +@author: shanef +""" + diff --git a/tests/libfilemanagertest.py b/tests/libfilemanagertest.py new file mode 100644 index 0000000..e7baae9 --- /dev/null +++ b/tests/libfilemanagertest.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Jul 5 14:12:26 2013 + +@author: shanef +""" + diff --git a/tests/libhandbraketest.py b/tests/libhandbraketest.py new file mode 100644 index 0000000..993692d --- /dev/null +++ b/tests/libhandbraketest.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Jul 5 14:13:19 2013 + +@author: shanef +""" + diff --git a/tests/libmythtvtest.py b/tests/libmythtvtest.py new file mode 100644 index 0000000..15505ca --- /dev/null +++ b/tests/libmythtvtest.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Jul 5 14:12:53 2013 + +@author: shanef +""" + diff --git a/tests/libsickbeardtest.py b/tests/libsickbeardtest.py new file mode 100644 index 0000000..3b0ee68 --- /dev/null +++ b/tests/libsickbeardtest.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Jul 5 14:12:38 2013 + +@author: shanef +""" + From 40ca301e0e10060542e1a9518a25db48c29ea61c Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Fri, 5 Jul 2013 14:39:25 +1000 Subject: [PATCH 03/72] Added handbrake logic --- libhandbrake.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/libhandbrake.py b/libhandbrake.py index dd779f7..fafd199 100644 --- a/libhandbrake.py +++ b/libhandbrake.py @@ -3,5 +3,33 @@ Created on Fri Jul 5 14:11:00 2013 @author: shanef + +Library to interface with handbrake to encode video files """ +import logging +import subprocess + +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'] + +class Encoder: + def Encode(input, output, waitForCompletion=True, logger=None): + HANDBRAKECOMMAND[3] = input + HANDBRAKECOMMAND[5] = output + + 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 \ No newline at end of file From 15952044f7effb5ff0863ddda475db51a6fcaf32 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Fri, 5 Jul 2013 14:48:21 +1000 Subject: [PATCH 04/72] Added mythtv logic --- libmythtv.py | 14 ++++++++++++++ libtvdatasource.py | 7 +++++++ 2 files changed, 21 insertions(+) create mode 100644 libtvdatasource.py diff --git a/libmythtv.py b/libmythtv.py index a3ab093..5789d8a 100644 --- a/libmythtv.py +++ b/libmythtv.py @@ -5,3 +5,17 @@ Created on Fri Jul 5 14:10:47 2013 @author: shanef """ +import MySQLdb as mdb +from libtvdatasource import TVShow + +class MythTV: + def RetrieveEpisodeData(serverAddress, user, password, database, inputFile): + con = mdb.connect(serverAddress, user, password, database) + + 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']) + \ No newline at end of file diff --git a/libtvdatasource.py b/libtvdatasource.py new file mode 100644 index 0000000..b73fc16 --- /dev/null +++ b/libtvdatasource.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Jul 5 14:42:47 2013 + +@author: shanef +""" + From ad5a3d48651add048a5540afeded8c9cbe7b8b35 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Fri, 5 Jul 2013 15:24:09 +1000 Subject: [PATCH 05/72] Added sickbeard logic --- libsickbeard.py | 57 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/libsickbeard.py b/libsickbeard.py index a837534..619363b 100644 --- a/libsickbeard.py +++ b/libsickbeard.py @@ -5,3 +5,60 @@ Created on Fri Jul 5 14:10:37 2013 @author: shanef """ +from libtvdatasource import TVShow +import json +from urllib import urlopen +from fuzzywuzzy import fuzz +from operator import itemgetter + +class Sickbeard: + def __init__(self, address, port, apikey): + self.address = address + self.port = port + self.apikey = apikey + + def __GetApiURL(self): + return "http://{0}:{1}/api/{2}/".format(self.address, self.port, self.apikey) + + def FindShowId(self, showName): + jsonurl = urlopen(self.__GetApiURL()+"?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 FindEpisodeByDescription(self, showId, season, episode, description): + jsonEpisodeUrl = urlopen("{0}?cmd=episode&tvdbid={1}&season={2}&episode={3}".format(self.__GetApiURL(), showId, season, episode)) + episodeResult = json.loads(jsonEpisodeUrl.read()) + + if fuzz.ratio(episodeResult['data']['description'].lower(), description.lower()) > 85: + return (season, episode) + + return None + + def FindEpisode(self, showId, name=None, description=None): + 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]: + if name is not None and name.lower() == result['data'][season][episode]['name'].lower(): + return (season, episode) + elif description is not None: + result = self.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 \ No newline at end of file From e006c455678b7e05b81d1f86af185e9d4b94a2e0 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Fri, 5 Jul 2013 15:35:12 +1000 Subject: [PATCH 06/72] Added main program arguments --- TVEncoder.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/TVEncoder.py b/TVEncoder.py index bb11bc6..6dc3e5a 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -5,3 +5,29 @@ Created on Fri Jul 5 14:14:22 2013 @author: shanef """ +import sys +import getopt + +def main(argv): + numFiles = 0 + doEncode = True + try: + opts, args = getopt.getopt(argv,"hn:l") + except getopt.GetoptError: + print 'TVEncoder.py -n - processes n recordings' + print 'TVEncoder.py -l -n - lists the files that will be processed without actually encoding them' + sys.exit(2) + for opt, arg in opts: + if opt == '-h': + print 'TVEncoder.py -n - processes n recordings' + print 'TVEncoder.py -l -n - lists the files that will be processed without actually encoding them' + sys.exit() + elif opt == "-n": + numFiles = arg + elif opt == "-l"): + doEncode = True + + print "Get to work" + +if __name__ == "__main__": + main(sys.argv[1:]) \ No newline at end of file From 864ef4d52539acdfb5f81d3f5b73f7da81fdef09 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Fri, 5 Jul 2013 21:27:53 +1000 Subject: [PATCH 07/72] Added settings library --- TVEncoder.py | 60 ++++++++++++++++++++++++++++++++++++++++++-------- libsettings.py | 7 ++++++ 2 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 libsettings.py diff --git a/TVEncoder.py b/TVEncoder.py index 6dc3e5a..e3a21c2 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -7,27 +7,69 @@ Created on Fri Jul 5 14:14:22 2013 import sys import getopt +import libfilemanager +import libsettings +import libhandbrake + +def ShowHelp(): + 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' + +def PrintShowsToEncode(showData): + print "/n".join(showData) def main(argv): numFiles = 0 - doEncode = True + doEncode = False + readOnly = True + doList = False + try: - opts, args = getopt.getopt(argv,"hn:l") + opts, args = getopt.getopt(argv,"hlpen:") except getopt.GetoptError: - print 'TVEncoder.py -n - processes n recordings' - print 'TVEncoder.py -l -n - lists the files that will be processed without actually encoding them' + ShowHelp() sys.exit(2) for opt, arg in opts: if opt == '-h': - print 'TVEncoder.py -n - processes n recordings' - print 'TVEncoder.py -l -n - lists the files that will be processed without actually encoding them' + ShowHelp() sys.exit() + elif opt == "-p": + doEncode = False + readOnly = False + elif opt == "-e": + readOnly = False + doEncode = True elif opt == "-n": numFiles = arg - elif opt == "-l"): - doEncode = True + elif opt == "-l": + readOnly = True + doList = True + + shows = Settings("") - print "Get to work" + if readOnly and doList: + if doEncode: + #Generate the list of files that would be encoded + showData = GetEncodingFiles(shows, readOnly) + PrintShowsToEncode(showData) + else: + # Generate the list of files to process + else: + if doEncode: + #Encode the files and move them to their final destination + showData = GetEncodingFiles(shows, readOnly) + + for show in showData: + if CheckFileExists(show.outputFile): + print "File {0} already exists. Cannot process.".format(show.outputFile) + else: + result = Encoder.Encode(show.inputFile, show.outputFile) + + PerformPostEncodeFileOperations(show.inputFile, show.outputFile) + else: + #Process files for encoding if __name__ == "__main__": main(sys.argv[1:]) \ No newline at end of file diff --git a/libsettings.py b/libsettings.py new file mode 100644 index 0000000..9d87b05 --- /dev/null +++ b/libsettings.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Jul 5 20:14:15 2013 + +@author: shanef +""" + From cb914cb65e9ade21e88004ead1591810b506ecc8 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Fri, 5 Jul 2013 21:30:01 +1000 Subject: [PATCH 08/72] interim commit --- libfilemanager.py | 67 ++++++++++++++++++++++++++++++++++++++++++++++ libhandbrake.py | 3 ++- libsettings.py | 15 +++++++++++ libtvdatasource.py | 7 +++++ 4 files changed, 91 insertions(+), 1 deletion(-) diff --git a/libfilemanager.py b/libfilemanager.py index b055b8e..1452e17 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -5,3 +5,70 @@ Created on Fri Jul 5 14:11:31 2013 @author: shanef """ +import os +import shutil + +#move this to settings +TVRECORDINGSDIR = "/srv/storage2/videos/TVRecordings/" + +class EncodeData: + inputFile = '' + show = None + outputFile = '' + + def ToString(): + return "Input: {0}/tOutput: {2}".format(inputFile, outputFile) + +def __GetInputFilesToEncode(shows): + fileList = [] + + for show in shows: + for r,d,f in os.walk(show.inputDirectory): + for files in f: + if files.endswith(".mpg"): + data = EncodeData() + data.show = show + data.inputFile = os.path.join(r,files) + fileList.append(data) + + return fileList + +def __FindSeason(path, fileName, readOnly): + 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 __GetEncodeOutputFile(showData, readOnly): + inFile = os.path.basename(showData.inputFile) + outFilename = inFile[:-3]+"mkv" + outPath = __FindSeason(showData.show.outputDirectory, outFilename) + showData.outputFile = os.path.join(outPath, outFilename) + + return showData + +def GetEncodingFiles(shows, readOnly=True): + showsData = __GetInputFilesToEncode(shows) + for showData in showsData: + showsData = __GetEncodeOutputFile(showData, readOnly) + + return showsData + +def CheckFileExists(file): + return os.path.isfile(file) + +def __GetRecordingFile(fileName): + return os.path.join(TVRECORDINGSDIR, os.path.dirname(fileName).split("/")[-1] + ".mpg") + +def PerformPostEncodeFileOperations(inputFileName, outputFileName): + shutil.rmtree(os.path.dirname(inputFileName)) + + linkAddress = __GetRecordingFile(inputFileName) + + os.remove(linkAddress) + + os.symlink(outputFileName, linkAddress) \ No newline at end of file diff --git a/libhandbrake.py b/libhandbrake.py index fafd199..f077fa0 100644 --- a/libhandbrake.py +++ b/libhandbrake.py @@ -32,4 +32,5 @@ class Encoder: logger.info("Handbrake completed with return code {0}".format(process.returncode)) return process.returncode - return None \ No newline at end of file + return None + \ No newline at end of file diff --git a/libsettings.py b/libsettings.py index 9d87b05..9227e8c 100644 --- a/libsettings.py +++ b/libsettings.py @@ -5,3 +5,18 @@ Created on Fri Jul 5 20:14:15 2013 @author: shanef """ +from libtvdatasource import TVShow +from xml.etree import ElementTree + +class Settings: + def __init__(self, settingsFile): + self.shows = self.LoadSettings(settingsFile) + + 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 \ No newline at end of file diff --git a/libtvdatasource.py b/libtvdatasource.py index b73fc16..b09eb23 100644 --- a/libtvdatasource.py +++ b/libtvdatasource.py @@ -5,3 +5,10 @@ Created on Fri Jul 5 14:42:47 2013 @author: shanef """ +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 \ No newline at end of file From 61a5eb976864c593826c506b5c2d5020c7313015 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Fri, 5 Jul 2013 22:40:17 +1000 Subject: [PATCH 09/72] interim commit --- TVEncoder.py | 7 ++++-- libfilemanager.py | 28 +++++++++++++++++++--- libtvdatasource.py | 59 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 87 insertions(+), 7 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index e3a21c2..18c2d77 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -11,6 +11,8 @@ import libfilemanager import libsettings import libhandbrake +TVRECORDINGSDIR = "/srv/storage2/videos/TVRecordings/" # TODO move this to settings + def ShowHelp(): 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' @@ -47,7 +49,7 @@ def main(argv): readOnly = True doList = True - shows = Settings("") + shows = Settings("") # TODO call actual settings file if readOnly and doList: if doEncode: @@ -56,6 +58,7 @@ def main(argv): PrintShowsToEncode(showData) else: # Generate the list of files to process + files = GetFilesToPrepare(TVRECORDINGSDIR, numFiles, shows) else: if doEncode: #Encode the files and move them to their final destination @@ -69,7 +72,7 @@ def main(argv): PerformPostEncodeFileOperations(show.inputFile, show.outputFile) else: - #Process files for encoding + # TODO Process files for encoding if __name__ == "__main__": main(sys.argv[1:]) \ No newline at end of file diff --git a/libfilemanager.py b/libfilemanager.py index 1452e17..9fbb3c0 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -5,6 +5,8 @@ Created on Fri Jul 5 14:11:31 2013 @author: shanef """ +import glob +import libtvdatasource as TVData import os import shutil @@ -16,8 +18,8 @@ class EncodeData: show = None outputFile = '' - def ToString(): - return "Input: {0}/tOutput: {2}".format(inputFile, outputFile) + def ToString(self): + return "Input: {0}/tOutput: {2}".format(self.inputFile, self.outputFile) def __GetInputFilesToEncode(shows): fileList = [] @@ -71,4 +73,24 @@ def PerformPostEncodeFileOperations(inputFileName, outputFileName): os.remove(linkAddress) - os.symlink(outputFileName, linkAddress) \ No newline at end of file + os.symlink(outputFileName, linkAddress) + +def GetFilesToPrepare(path, numberofFiles, shows): + files = glob.glob("{0}*.mpg".format(path)) + files = sorted(files, key=os.path.getctime) + files = filter(lambda file: not os.path.islink(file), files) + + #files is now a list of unprocessed files, but contains shows other than those we are interested in + + filesToProcess = [] + i = 0 + for file in files: + # TODO get these from settings + if TVData.CheckTitleIsInList('localhost', 'script', 'script', 'mythconverg', file): + filesToProcess.append(file) + i = i + 1 + if i == numberofFiles: + return filesToProcess + + return filesToProcess #will reach here if there were less than numberofFiles found + \ No newline at end of file diff --git a/libtvdatasource.py b/libtvdatasource.py index b09eb23..65c8c1b 100644 --- a/libtvdatasource.py +++ b/libtvdatasource.py @@ -5,10 +5,65 @@ Created on Fri Jul 5 14:42:47 2013 @author: shanef """ +import libmythtv as MythTV +from libsickbeard import Sickbeard + class TVShow: - def __init__(self, episode, season, title, subtitle, description): + def __init__(self, episode, season, title, subtitle, description, inputFile='', outputFile=''): self.episode = episode self.season = season self.title = title self.subtitle = subtitle - self.description = description \ No newline at end of file + self.description = description + self.inputFile = inputFile + self.outputFile = outputFile + +def FixEpisodeSeasonNumber(number): + if number < 10: + return "0{0}".format(number) + else: + return str(number) + +def RetrieveEpisodeData(serverAddress, user, password, database, inputFile, showsToProcess): + show = MythTV.RetrieveEpisodeData('localhost', 'script', 'script', 'mythconverg', file) + + if show.title: + if show.subtitle: + show.subtitle = GetEpisodeName(show.subtitle, show.title) + + if (show.season == '0' or show.episode == '0'): + showId = Sickbeard.FindShowId(show.title) + + result = Sickbeard.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) + +def CheckTitleIsInList(serverAddress, user, password, database, inputFile): + """Check that inputFile is a recording of a show that is to be processed.""" + show = MythTV.RetrieveEpisodeData('localhost', 'script', 'script', 'mythconverg', file) + + # TODO get this from settings + if show.title in ["Thomas and Friends", "Thomas the Tank Engine & Friends", + "Chuggington", "Mike the Knight", "Octonauts", + "The Octonauts", "In the Night Garden", + "Raa Raa! The Noisy Lion"]: + return True + else: + return False + +def GetEpisodeName(subtitle, showName): + if subtitle[:len(showName)].lower() == showName.lower(): + return subtitle[len(showName + ' and the '):] + else: + return subtitle \ No newline at end of file From b8acbcbdc164542b9f827f5bf3584b2cad29d4df Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 6 Jul 2013 20:18:52 +1000 Subject: [PATCH 10/72] finished initial coding --- TVEncoder.py | 11 ++++++-- libfilemanager.py | 13 ++++++---- libtvdatasource.py | 63 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 78 insertions(+), 9 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index 18c2d77..3adb279 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -8,8 +8,9 @@ Created on Fri Jul 5 14:14:22 2013 import sys import getopt import libfilemanager -import libsettings +from libsettings import Settings import libhandbrake +import libtvdatasource TVRECORDINGSDIR = "/srv/storage2/videos/TVRecordings/" # TODO move this to settings @@ -22,6 +23,9 @@ def ShowHelp(): def PrintShowsToEncode(showData): print "/n".join(showData) +def PrintShowsToPrepare(showData): + print "/n".join(showData) + def main(argv): numFiles = 0 doEncode = False @@ -58,7 +62,8 @@ def main(argv): PrintShowsToEncode(showData) else: # Generate the list of files to process - files = GetFilesToPrepare(TVRECORDINGSDIR, numFiles, shows) + shows = GetFilesToPrepare(TVRECORDINGSDIR, numFiles, shows) + PrintShowsToPrepare(shows) else: if doEncode: #Encode the files and move them to their final destination @@ -73,6 +78,8 @@ def main(argv): PerformPostEncodeFileOperations(show.inputFile, show.outputFile) else: # TODO Process files for encoding + shows = GetFilesToPrepare(TVRECORDINGSDIR, numFiles, shows) + PrepareEpisodes(shows) if __name__ == "__main__": main(sys.argv[1:]) \ No newline at end of file diff --git a/libfilemanager.py b/libfilemanager.py index 9fbb3c0..55878cf 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -21,6 +21,7 @@ class EncodeData: def ToString(self): return "Input: {0}/tOutput: {2}".format(self.inputFile, self.outputFile) + def __GetInputFilesToEncode(shows): fileList = [] @@ -82,15 +83,17 @@ def GetFilesToPrepare(path, numberofFiles, shows): #files is now a list of unprocessed files, but contains shows other than those we are interested in - filesToProcess = [] + showsToProcess = [] i = 0 for file in files: # TODO get these from settings - if TVData.CheckTitleIsInList('localhost', 'script', 'script', 'mythconverg', file): - filesToProcess.append(file) + #if TVData.CheckTitleIsInList('localhost', 'script', 'script', 'mythconverg', file): + showData = TVData.RetrieveEpisodeData('localhost', 'script', 'script', 'mythconverg', file, shows) + if showData: + showsToProcess.append(showData) i = i + 1 if i == numberofFiles: - return filesToProcess + return showsToProcess - return filesToProcess #will reach here if there were less than numberofFiles found + return showsToProcess #will reach here if there were less than numberofFiles found \ No newline at end of file diff --git a/libtvdatasource.py b/libtvdatasource.py index 65c8c1b..767204b 100644 --- a/libtvdatasource.py +++ b/libtvdatasource.py @@ -7,6 +7,18 @@ Created on Fri Jul 5 14:42:47 2013 import libmythtv as MythTV from libsickbeard import Sickbeard +import os +import shutil + +# TODO Move these to settings +PROCESSDIR="/srv/storage2/files/VideoProcessing/" +THOMAS="Thomas" +CHUGGINGTON="Chuggington" +MIKE="MikeTheKnight" +OCTONAUTS="Octonauts" +NIGHTGARDEN="InTheNightGarden" +RAARAA="RaaRaa" +INPUTDIR="Input" class TVShow: def __init__(self, episode, season, title, subtitle, description, inputFile='', outputFile=''): @@ -23,11 +35,31 @@ def FixEpisodeSeasonNumber(number): 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 RetrieveEpisodeData(serverAddress, user, password, database, inputFile, showsToProcess): + file = os.path.basename(inputFile) show = MythTV.RetrieveEpisodeData('localhost', 'script', 'script', 'mythconverg', file) - if show.title: + if show.title and show.title in showsToProcess: if show.subtitle: show.subtitle = GetEpisodeName(show.subtitle, show.title) @@ -48,9 +80,17 @@ def RetrieveEpisodeData(serverAddress, user, password, database, inputFile, show renamedFile = "{0}{1} - {2} - SD TV_.mpg".format(season, episode, show.subtitle) directory = GetDirectory(show.title, seasonFolder) + + show.outputFile = os.path.join(directory, inputFile[:-4], renamedFile) + show.inputFile = inputFile + + return show + else: + return None def CheckTitleIsInList(serverAddress, user, password, database, inputFile): """Check that inputFile is a recording of a show that is to be processed.""" + file = os.path.basename(inputFile) show = MythTV.RetrieveEpisodeData('localhost', 'script', 'script', 'mythconverg', file) # TODO get this from settings @@ -62,8 +102,27 @@ def CheckTitleIsInList(serverAddress, user, password, database, inputFile): else: return False +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 GetEpisodeName(subtitle, showName): if subtitle[:len(showName)].lower() == showName.lower(): return subtitle[len(showName + ' and the '):] else: - return subtitle \ No newline at end of file + return subtitle + +def ProcessEpisode(inputFile, outputFile): + outputdir = os.path.dirname(outputFile) + if not os.path.exists(outputdir): + os.makedirs(outputdir) + + shutil.move(inputFile, outputFile) + +def PrepareEpisodes(showsData): + for showData in showsData: + ProcessEpisode(showData.inputFile, showData.outputFile) \ No newline at end of file From 74b50ef3a27c0cab2f9894e28bbd0591daf2929c Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 6 Jul 2013 20:28:49 +1000 Subject: [PATCH 11/72] fixing errors --- TVEncoder.py | 16 ++++++++-------- libmythtv.py | 2 +- libsickbeard.py | 2 +- libtvdatasource.py | 11 +---------- libtvshow.py | 16 ++++++++++++++++ 5 files changed, 27 insertions(+), 20 deletions(-) create mode 100644 libtvshow.py diff --git a/TVEncoder.py b/TVEncoder.py index 3adb279..f3f5320 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -9,7 +9,7 @@ import sys import getopt import libfilemanager from libsettings import Settings -import libhandbrake +from libhandbrake import Encoder import libtvdatasource TVRECORDINGSDIR = "/srv/storage2/videos/TVRecordings/" # TODO move this to settings @@ -58,28 +58,28 @@ def main(argv): if readOnly and doList: if doEncode: #Generate the list of files that would be encoded - showData = GetEncodingFiles(shows, readOnly) + showData = libfilemanager.GetEncodingFiles(shows, readOnly) PrintShowsToEncode(showData) else: # Generate the list of files to process - shows = GetFilesToPrepare(TVRECORDINGSDIR, numFiles, shows) + shows = libfilemanager.GetFilesToPrepare(TVRECORDINGSDIR, numFiles, shows) PrintShowsToPrepare(shows) else: if doEncode: #Encode the files and move them to their final destination - showData = GetEncodingFiles(shows, readOnly) + showData = libfilemanager.GetEncodingFiles(shows, readOnly) for show in showData: - if CheckFileExists(show.outputFile): + if libfilemanager.CheckFileExists(show.outputFile): print "File {0} already exists. Cannot process.".format(show.outputFile) else: result = Encoder.Encode(show.inputFile, show.outputFile) - PerformPostEncodeFileOperations(show.inputFile, show.outputFile) + libfilemanager.PerformPostEncodeFileOperations(show.inputFile, show.outputFile) else: # TODO Process files for encoding - shows = GetFilesToPrepare(TVRECORDINGSDIR, numFiles, shows) - PrepareEpisodes(shows) + shows = libfilemanager.GetFilesToPrepare(TVRECORDINGSDIR, numFiles, shows) + libtvdatasource.PrepareEpisodes(shows) if __name__ == "__main__": main(sys.argv[1:]) \ No newline at end of file diff --git a/libmythtv.py b/libmythtv.py index 5789d8a..9872cb2 100644 --- a/libmythtv.py +++ b/libmythtv.py @@ -6,7 +6,7 @@ Created on Fri Jul 5 14:10:47 2013 """ import MySQLdb as mdb -from libtvdatasource import TVShow +from libtvshow import TVShow class MythTV: def RetrieveEpisodeData(serverAddress, user, password, database, inputFile): diff --git a/libsickbeard.py b/libsickbeard.py index 619363b..f3d4feb 100644 --- a/libsickbeard.py +++ b/libsickbeard.py @@ -5,7 +5,7 @@ Created on Fri Jul 5 14:10:37 2013 @author: shanef """ -from libtvdatasource import TVShow +from libtvshow import TVShow import json from urllib import urlopen from fuzzywuzzy import fuzz diff --git a/libtvdatasource.py b/libtvdatasource.py index 767204b..469603e 100644 --- a/libtvdatasource.py +++ b/libtvdatasource.py @@ -9,6 +9,7 @@ import libmythtv as MythTV from libsickbeard import Sickbeard import os import shutil +from libtvshow import TVShow # TODO Move these to settings PROCESSDIR="/srv/storage2/files/VideoProcessing/" @@ -20,16 +21,6 @@ NIGHTGARDEN="InTheNightGarden" RAARAA="RaaRaa" INPUTDIR="Input" -class TVShow: - def __init__(self, episode, season, title, subtitle, description, inputFile='', outputFile=''): - self.episode = episode - self.season = season - self.title = title - self.subtitle = subtitle - self.description = description - self.inputFile = inputFile - self.outputFile = outputFile - def FixEpisodeSeasonNumber(number): if number < 10: return "0{0}".format(number) diff --git a/libtvshow.py b/libtvshow.py new file mode 100644 index 0000000..84731fe --- /dev/null +++ b/libtvshow.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +""" +Created on Sat Jul 6 20:26:22 2013 + +@author: shanef +""" + +class TVShow: + def __init__(self, episode, season, title, subtitle, description, inputFile='', outputFile=''): + self.episode = episode + self.season = season + self.title = title + self.subtitle = subtitle + self.description = description + self.inputFile = inputFile + self.outputFile = outputFile \ No newline at end of file From d0d66fe9a891f16b807ff2d5a29456e0186bd94c Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 6 Jul 2013 21:10:16 +1000 Subject: [PATCH 12/72] move error fixing --- TVEncoder.py | 8 ++++---- libfilemanager.py | 2 +- libmythtv.py | 15 +++++++-------- libsettings.py | 11 ++++++++--- libtvdatasource.py | 10 +++++----- 5 files changed, 25 insertions(+), 21 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index f3f5320..47eacae 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -12,7 +12,7 @@ from libsettings import Settings from libhandbrake import Encoder import libtvdatasource -TVRECORDINGSDIR = "/srv/storage2/videos/TVRecordings/" # TODO move this to settings +TVRECORDINGSDIR = "/Volumes/TV Recordings/"#"/srv/storage2/videos/TVRecordings/" # TODO move this to settings def ShowHelp(): print 'TVEncoder.py -p -n - prepare n recordings' @@ -53,16 +53,16 @@ def main(argv): readOnly = True doList = True - shows = Settings("") # TODO call actual settings file + showSettings = Settings("settings.xml") # TODO call actual settings file if readOnly and doList: if doEncode: #Generate the list of files that would be encoded - showData = libfilemanager.GetEncodingFiles(shows, readOnly) + showData = libfilemanager.GetEncodingFiles(showSettings, readOnly) PrintShowsToEncode(showData) else: # Generate the list of files to process - shows = libfilemanager.GetFilesToPrepare(TVRECORDINGSDIR, numFiles, shows) + shows = libfilemanager.GetFilesToPrepare(TVRECORDINGSDIR, numFiles, showSettings) PrintShowsToPrepare(shows) else: if doEncode: diff --git a/libfilemanager.py b/libfilemanager.py index 55878cf..b3c3c7e 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -88,7 +88,7 @@ def GetFilesToPrepare(path, numberofFiles, shows): for file in files: # TODO get these from settings #if TVData.CheckTitleIsInList('localhost', 'script', 'script', 'mythconverg', file): - showData = TVData.RetrieveEpisodeData('localhost', 'script', 'script', 'mythconverg', file, shows) + showData = TVData.RetrieveEpisodeData('192.168.0.2', 'script', 'script', 'mythconverg', file, shows, '192.168.0.2', '8081', '3678177136222bf5002be209220ccb20') # TODO all these settings need to move to settings if showData: showsToProcess.append(showData) i = i + 1 diff --git a/libmythtv.py b/libmythtv.py index 9872cb2..9061232 100644 --- a/libmythtv.py +++ b/libmythtv.py @@ -8,14 +8,13 @@ Created on Fri Jul 5 14:10:47 2013 import MySQLdb as mdb from libtvshow import TVShow -class MythTV: - def RetrieveEpisodeData(serverAddress, user, password, database, inputFile): - con = mdb.connect(serverAddress, user, password, database) +def RetrieveEpisodeData(serverAddress, user, password, database, inputFile): + con = mdb.connect(serverAddress, user, password, database) - 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() + 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']) + return TVShow(result['episode'], result['season'], result['title'], result['subtitle'], result['description']) \ No newline at end of file diff --git a/libsettings.py b/libsettings.py index 9227e8c..c20a3b2 100644 --- a/libsettings.py +++ b/libsettings.py @@ -5,18 +5,23 @@ Created on Fri Jul 5 20:14:15 2013 @author: shanef """ -from libtvdatasource import TVShow from xml.etree import ElementTree +class ShowSettings: + def __init__(self, name, inputDirectory, outputDirectory): + self.name = name + self.inputDirectory = inputDirectory + self.outputDirectory = outputDirectory + class Settings: def __init__(self, settingsFile): self.shows = self.LoadSettings(settingsFile) - def LoadSettings(source): + def LoadSettings(self, source): shows = [] settingsXml = ElementTree.parse(source).getroot() for show in settingsXml.findall('show'): - newShow = TVShow(show[0].text, show[1].text, show[2].text) + newShow = ShowSettings(show[0].text, show[1].text, show[2].text) shows.append(newShow) return shows \ No newline at end of file diff --git a/libtvdatasource.py b/libtvdatasource.py index 469603e..d954d36 100644 --- a/libtvdatasource.py +++ b/libtvdatasource.py @@ -9,7 +9,6 @@ import libmythtv as MythTV from libsickbeard import Sickbeard import os import shutil -from libtvshow import TVShow # TODO Move these to settings PROCESSDIR="/srv/storage2/files/VideoProcessing/" @@ -46,18 +45,19 @@ def GetDirectory(title, season): return os.path.join(PROCESSDIR, directory, INPUTDIR, season) -def RetrieveEpisodeData(serverAddress, user, password, database, inputFile, showsToProcess): +def RetrieveEpisodeData(serverAddress, user, password, database, inputFile, showsToProcess, sickbeardAddress, sickbeardPort, sickbeardAPIKey): file = os.path.basename(inputFile) - show = MythTV.RetrieveEpisodeData('localhost', 'script', 'script', 'mythconverg', file) + show = MythTV.RetrieveEpisodeData(serverAddress, user, password, database, file) if show.title and show.title in showsToProcess: if show.subtitle: show.subtitle = GetEpisodeName(show.subtitle, show.title) if (show.season == '0' or show.episode == '0'): - showId = Sickbeard.FindShowId(show.title) + sickbeard = Sickbeard(sickbeardAddress, sickbeardPort, sickbeardAPIKey) + showId = sickbeard.FindShowId(show.title) - result = Sickbeard.FindEpisode(showId, show.subtitle, show.description) + result = sickbeard.FindEpisode(showId, show.subtitle, show.description) show.season = result[0] show.episode = result[1] From bb02b34f9e42dad40e549a6651efc7e1e9b9a1d3 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 6 Jul 2013 21:13:58 +1000 Subject: [PATCH 13/72] move error fixing --- TVEncoder.py | 1 + libtvdatasource.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/TVEncoder.py b/TVEncoder.py index 47eacae..1df07fa 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -63,6 +63,7 @@ def main(argv): else: # Generate the list of files to process shows = libfilemanager.GetFilesToPrepare(TVRECORDINGSDIR, numFiles, showSettings) + print len(shows) PrintShowsToPrepare(shows) else: if doEncode: diff --git a/libtvdatasource.py b/libtvdatasource.py index d954d36..a35eb97 100644 --- a/libtvdatasource.py +++ b/libtvdatasource.py @@ -48,7 +48,7 @@ def GetDirectory(title, season): def RetrieveEpisodeData(serverAddress, user, password, database, inputFile, showsToProcess, sickbeardAddress, sickbeardPort, sickbeardAPIKey): file = os.path.basename(inputFile) show = MythTV.RetrieveEpisodeData(serverAddress, user, password, database, file) - + print show if show.title and show.title in showsToProcess: if show.subtitle: show.subtitle = GetEpisodeName(show.subtitle, show.title) From 07d8a1903598f1420adeaf52b3882447fbd00f64 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 6 Jul 2013 21:15:46 +1000 Subject: [PATCH 14/72] move error fixing --- libtvdatasource.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libtvdatasource.py b/libtvdatasource.py index a35eb97..11885be 100644 --- a/libtvdatasource.py +++ b/libtvdatasource.py @@ -48,7 +48,8 @@ def GetDirectory(title, season): def RetrieveEpisodeData(serverAddress, user, password, database, inputFile, showsToProcess, sickbeardAddress, sickbeardPort, sickbeardAPIKey): file = os.path.basename(inputFile) show = MythTV.RetrieveEpisodeData(serverAddress, user, password, database, file) - print show + print "file: {0} mythtv returned show name {1}".format(file, show.title) + if show.title and show.title in showsToProcess: if show.subtitle: show.subtitle = GetEpisodeName(show.subtitle, show.title) From 9385b00bec1af885df0d04c5e06504ad99a70cbb Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 6 Jul 2013 21:16:57 +1000 Subject: [PATCH 15/72] move error fixing --- libmythtv.py | 1 + 1 file changed, 1 insertion(+) diff --git a/libmythtv.py b/libmythtv.py index 9061232..b909a88 100644 --- a/libmythtv.py +++ b/libmythtv.py @@ -15,6 +15,7 @@ def RetrieveEpisodeData(serverAddress, user, password, database, inputFile): 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() + print result return TVShow(result['episode'], result['season'], result['title'], result['subtitle'], result['description']) \ No newline at end of file From dee7b7bc149dbe1b83dee1fe93378ed4f3364445 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 6 Jul 2013 21:18:01 +1000 Subject: [PATCH 16/72] move error fixing --- libtvdatasource.py | 1 + 1 file changed, 1 insertion(+) diff --git a/libtvdatasource.py b/libtvdatasource.py index 11885be..ab659e1 100644 --- a/libtvdatasource.py +++ b/libtvdatasource.py @@ -47,6 +47,7 @@ def GetDirectory(title, season): def RetrieveEpisodeData(serverAddress, user, password, database, inputFile, showsToProcess, sickbeardAddress, sickbeardPort, sickbeardAPIKey): file = os.path.basename(inputFile) + print file show = MythTV.RetrieveEpisodeData(serverAddress, user, password, database, file) print "file: {0} mythtv returned show name {1}".format(file, show.title) From 27b132becbf7113a776c975bc8cf369720e9a8ba Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 6 Jul 2013 21:20:21 +1000 Subject: [PATCH 17/72] move error fixing --- TVEncoder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TVEncoder.py b/TVEncoder.py index 1df07fa..9d0dcaf 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -63,7 +63,7 @@ def main(argv): else: # Generate the list of files to process shows = libfilemanager.GetFilesToPrepare(TVRECORDINGSDIR, numFiles, showSettings) - print len(shows) + print "num results: {0}".format(len(shows)) PrintShowsToPrepare(shows) else: if doEncode: From 48beb3e6623180d3eb20d5cad68b91f3ba42d97e Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 6 Jul 2013 21:22:33 +1000 Subject: [PATCH 18/72] move error fixing --- libfilemanager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/libfilemanager.py b/libfilemanager.py index b3c3c7e..bcceee8 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -85,6 +85,7 @@ def GetFilesToPrepare(path, numberofFiles, shows): showsToProcess = [] i = 0 + print "Found {0} potential files".format(len(files)) for file in files: # TODO get these from settings #if TVData.CheckTitleIsInList('localhost', 'script', 'script', 'mythconverg', file): From 4a5cc6d0c64f91f2047c7e26de0fe05a3b9db266 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 6 Jul 2013 21:23:39 +1000 Subject: [PATCH 19/72] move error fixing --- TVEncoder.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/TVEncoder.py b/TVEncoder.py index 9d0dcaf..418b087 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -12,7 +12,8 @@ from libsettings import Settings from libhandbrake import Encoder import libtvdatasource -TVRECORDINGSDIR = "/Volumes/TV Recordings/"#"/srv/storage2/videos/TVRecordings/" # TODO move this to settings +#TVRECORDINGSDIR = "/Volumes/TV Recordings/" +TVRECORDINGSDIR = "/srv/storage2/videos/TVRecordings/" # TODO move this to settings def ShowHelp(): print 'TVEncoder.py -p -n - prepare n recordings' From a10bed2ddb588a81823ff72f489b7ebf09ce4d20 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 6 Jul 2013 21:59:33 +1000 Subject: [PATCH 20/72] move error fixing --- TVEncoder.py | 15 ++++++++++----- libfilemanager.py | 2 +- libmythtv.py | 2 +- libtvdatasource.py | 4 ++-- libtvshow.py | 5 ++++- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index 418b087..607dc0b 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -12,8 +12,8 @@ from libsettings import Settings from libhandbrake import Encoder import libtvdatasource -#TVRECORDINGSDIR = "/Volumes/TV Recordings/" -TVRECORDINGSDIR = "/srv/storage2/videos/TVRecordings/" # TODO move this to settings +TVRECORDINGSDIR = "/Volumes/TV Recordings/" +#TVRECORDINGSDIR = "/srv/storage2/videos/TVRecordings/" # TODO move this to settings def ShowHelp(): print 'TVEncoder.py -p -n - prepare n recordings' @@ -24,8 +24,9 @@ def ShowHelp(): def PrintShowsToEncode(showData): print "/n".join(showData) -def PrintShowsToPrepare(showData): - print "/n".join(showData) +def PrintShowsToPrepare(showsData): + for showData in showsData: + showData.Print() def main(argv): numFiles = 0 @@ -63,7 +64,11 @@ def main(argv): PrintShowsToEncode(showData) else: # Generate the list of files to process - shows = libfilemanager.GetFilesToPrepare(TVRECORDINGSDIR, numFiles, showSettings) + tempShowList = ["Thomas and Friends", "Thomas the Tank Engine & Friends", + "Chuggington", "Mike the Knight", "Octonauts", + "The Octonauts", "In the Night Garden", + "Raa Raa! The Noisy Lion"] # TODO get from settings + shows = libfilemanager.GetFilesToPrepare(TVRECORDINGSDIR, numFiles, tempShowList) print "num results: {0}".format(len(shows)) PrintShowsToPrepare(shows) else: diff --git a/libfilemanager.py b/libfilemanager.py index bcceee8..c87551e 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -93,7 +93,7 @@ def GetFilesToPrepare(path, numberofFiles, shows): if showData: showsToProcess.append(showData) i = i + 1 - if i == numberofFiles: + if i == int(numberofFiles): return showsToProcess return showsToProcess #will reach here if there were less than numberofFiles found diff --git a/libmythtv.py b/libmythtv.py index b909a88..5838849 100644 --- a/libmythtv.py +++ b/libmythtv.py @@ -15,7 +15,7 @@ def RetrieveEpisodeData(serverAddress, user, password, database, inputFile): 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() - print result + #print result return TVShow(result['episode'], result['season'], result['title'], result['subtitle'], result['description']) \ No newline at end of file diff --git a/libtvdatasource.py b/libtvdatasource.py index ab659e1..4fb545d 100644 --- a/libtvdatasource.py +++ b/libtvdatasource.py @@ -47,9 +47,9 @@ def GetDirectory(title, season): def RetrieveEpisodeData(serverAddress, user, password, database, inputFile, showsToProcess, sickbeardAddress, sickbeardPort, sickbeardAPIKey): file = os.path.basename(inputFile) - print file + #print file show = MythTV.RetrieveEpisodeData(serverAddress, user, password, database, file) - print "file: {0} mythtv returned show name {1}".format(file, show.title) + #print "file: {0} mythtv returned show name {1}".format(file, show.title) if show.title and show.title in showsToProcess: if show.subtitle: diff --git a/libtvshow.py b/libtvshow.py index 84731fe..8d261d8 100644 --- a/libtvshow.py +++ b/libtvshow.py @@ -13,4 +13,7 @@ class TVShow: self.subtitle = subtitle self.description = description self.inputFile = inputFile - self.outputFile = outputFile \ No newline at end of file + self.outputFile = outputFile + + def Print(self): + print "Input: {0} -> Output: {1}".format(self.inputFile, self.outputFile) \ No newline at end of file From 4f7d7db6789887294bf9058468c6e71ceaad374f Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 6 Jul 2013 21:59:49 +1000 Subject: [PATCH 21/72] move error fixing --- TVEncoder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index 607dc0b..13c5548 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -12,8 +12,8 @@ from libsettings import Settings from libhandbrake import Encoder import libtvdatasource -TVRECORDINGSDIR = "/Volumes/TV Recordings/" -#TVRECORDINGSDIR = "/srv/storage2/videos/TVRecordings/" # TODO move this to settings +#TVRECORDINGSDIR = "/Volumes/TV Recordings/" +TVRECORDINGSDIR = "/srv/storage2/videos/TVRecordings/" # TODO move this to settings def ShowHelp(): print 'TVEncoder.py -p -n - prepare n recordings' From ccb103ad36dbcd4e4438ef4f6f3450427df4d3dc Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 6 Jul 2013 23:14:41 +1000 Subject: [PATCH 22/72] move error fixing --- TVEncoder.py | 4 ++-- libsickbeard.py | 18 ++++++++++++------ libtvdatasource.py | 27 +++++++++++++-------------- libtvshow.py | 8 ++++---- 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index 13c5548..607dc0b 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -12,8 +12,8 @@ from libsettings import Settings from libhandbrake import Encoder import libtvdatasource -#TVRECORDINGSDIR = "/Volumes/TV Recordings/" -TVRECORDINGSDIR = "/srv/storage2/videos/TVRecordings/" # TODO move this to settings +TVRECORDINGSDIR = "/Volumes/TV Recordings/" +#TVRECORDINGSDIR = "/srv/storage2/videos/TVRecordings/" # TODO move this to settings def ShowHelp(): print 'TVEncoder.py -p -n - prepare n recordings' diff --git a/libsickbeard.py b/libsickbeard.py index f3d4feb..6fe27b0 100644 --- a/libsickbeard.py +++ b/libsickbeard.py @@ -24,6 +24,12 @@ class Sickbeard: jsonurl = urlopen(self.__GetApiURL()+"?cmd=shows") result = json.loads(jsonurl.read()) + # TODO find a better way to do this + if showName == "Thomas and Friends": + showName = "Thomas The Tank Engine & Friends" + elif showName == "The Octonauts": + showName = "Octonauts" + shows = [] for show in result['data']: shows.append((show, fuzz.partial_ratio(showName.lower(), result['data'][show]['show_name'].lower()))) @@ -38,7 +44,7 @@ class Sickbeard: episodeResult = json.loads(jsonEpisodeUrl.read()) if fuzz.ratio(episodeResult['data']['description'].lower(), description.lower()) > 85: - return (season, episode) + return (season, episode, episodeResult['data']['name']) return None @@ -49,13 +55,13 @@ class Sickbeard: 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) + return (season, episode, name) elif description is not None: - result = self.FindEpisodeByDescription(showId, season, episode, description) - if result is not None: - return result + descriptionQueryResult = self.FindEpisodeByDescription(showId, season, episode, description) + if descriptionQueryResult is not None: + return descriptionQueryResult - return (0, 0) + return (0, 0, '') def GetEpisodeName(subtitle, showName): if subtitle[:len(showName)].lower() == showName.lower(): diff --git a/libtvdatasource.py b/libtvdatasource.py index 4fb545d..b964852 100644 --- a/libtvdatasource.py +++ b/libtvdatasource.py @@ -21,10 +21,10 @@ RAARAA="RaaRaa" INPUTDIR="Input" def FixEpisodeSeasonNumber(number): - if number < 10: + if len(number) == 1: return "0{0}".format(number) else: - return str(number) + return number def GetDirectory(title, season): directory = "" @@ -47,35 +47,34 @@ def GetDirectory(title, season): def RetrieveEpisodeData(serverAddress, user, password, database, inputFile, showsToProcess, sickbeardAddress, sickbeardPort, sickbeardAPIKey): file = os.path.basename(inputFile) - #print file show = MythTV.RetrieveEpisodeData(serverAddress, user, password, database, file) - #print "file: {0} mythtv returned show name {1}".format(file, show.title) if show.title and show.title in showsToProcess: if show.subtitle: show.subtitle = GetEpisodeName(show.subtitle, show.title) - if (show.season == '0' or show.episode == '0'): + if (show.season == "0" or show.episode == "0"): sickbeard = Sickbeard(sickbeardAddress, sickbeardPort, sickbeardAPIKey) showId = sickbeard.FindShowId(show.title) result = sickbeard.FindEpisode(showId, show.subtitle, show.description) - show.season = result[0] - show.episode = result[1] + show.season = str(result[0]) + show.episode = str(result[1]) + show.subtitle = result[2] 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) + 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) + directory = GetDirectory(show.title, seasonFolder) - show.outputFile = os.path.join(directory, inputFile[:-4], renamedFile) - show.inputFile = inputFile + show.outputFile = os.path.join(directory, file[:-4], renamedFile) + show.inputFile = inputFile return show else: diff --git a/libtvshow.py b/libtvshow.py index 8d261d8..1fbb8a1 100644 --- a/libtvshow.py +++ b/libtvshow.py @@ -5,15 +5,15 @@ Created on Sat Jul 6 20:26:22 2013 @author: shanef """ -class TVShow: +class TVShow(object): def __init__(self, episode, season, title, subtitle, description, inputFile='', outputFile=''): - self.episode = episode - self.season = season + self.episode = str(episode) + self.season = str(season) self.title = title self.subtitle = subtitle self.description = description self.inputFile = inputFile self.outputFile = outputFile - + def Print(self): print "Input: {0} -> Output: {1}".format(self.inputFile, self.outputFile) \ No newline at end of file From 7a34fd01c83c9a1e7a4af4b0382ee107f010ba72 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Mon, 8 Jul 2013 17:21:42 +1000 Subject: [PATCH 23/72] added settings file --- TVEncoder.py | 33 ++++--- libfilemanager.py | 143 +++++++++++++++-------------- libhandbrake.py | 27 +++--- libmythtv.py | 27 ++++-- libsettings.py | 101 ++++++++++++++++++-- libsickbeard.py | 42 ++++++--- libtvdatasource.py | 223 ++++++++++++++++++++++++--------------------- settings.cfg | 52 +++++++++++ 8 files changed, 418 insertions(+), 230 deletions(-) create mode 100644 settings.cfg diff --git a/TVEncoder.py b/TVEncoder.py index 607dc0b..57ba313 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -7,12 +7,12 @@ Created on Fri Jul 5 14:14:22 2013 import sys import getopt -import libfilemanager +from libfilemanager import FileManager from libsettings import Settings from libhandbrake import Encoder import libtvdatasource -TVRECORDINGSDIR = "/Volumes/TV Recordings/" +#TVRECORDINGSDIR = "/Volumes/TV Recordings/" #TVRECORDINGSDIR = "/srv/storage2/videos/TVRecordings/" # TODO move this to settings def ShowHelp(): @@ -55,37 +55,42 @@ def main(argv): readOnly = True doList = True - showSettings = Settings("settings.xml") # TODO call actual settings file + settings = Settings("settings.cfg") if readOnly and doList: if doEncode: #Generate the list of files that would be encoded - showData = libfilemanager.GetEncodingFiles(showSettings, readOnly) + fileManager = FileManager(settings) + showData = fileManager.GetEncodingFiles(readOnly) PrintShowsToEncode(showData) else: # Generate the list of files to process - tempShowList = ["Thomas and Friends", "Thomas the Tank Engine & Friends", - "Chuggington", "Mike the Knight", "Octonauts", - "The Octonauts", "In the Night Garden", - "Raa Raa! The Noisy Lion"] # TODO get from settings - shows = libfilemanager.GetFilesToPrepare(TVRECORDINGSDIR, numFiles, tempShowList) +# tempShowList = ["Thomas and Friends", "Thomas the Tank Engine & Friends", +# "Chuggington", "Mike the Knight", "Octonauts", +# "The Octonauts", "In the Night Garden", +# "Raa Raa! The Noisy Lion"] # TODO get from settings + fileManager = FileManager(settings) + shows = fileManager.GetFilesToPrepare(numFiles) print "num results: {0}".format(len(shows)) PrintShowsToPrepare(shows) else: if doEncode: #Encode the files and move them to their final destination - showData = libfilemanager.GetEncodingFiles(shows, readOnly) + fileManager = FileManager(settings) + showData = fileManager.GetEncodingFiles(readOnly) for show in showData: - if libfilemanager.CheckFileExists(show.outputFile): + if fileManager.CheckFileExists(show.outputFile): print "File {0} already exists. Cannot process.".format(show.outputFile) else: - result = Encoder.Encode(show.inputFile, show.outputFile) + encoder = Encoder(settings.HandbrakeCommand) + result = encoder.Encode(show.inputFile, show.outputFile) - libfilemanager.PerformPostEncodeFileOperations(show.inputFile, show.outputFile) + fileManager.PerformPostEncodeFileOperations(show.inputFile, show.outputFile) else: # TODO Process files for encoding - shows = libfilemanager.GetFilesToPrepare(TVRECORDINGSDIR, numFiles, shows) + fileManager = FileManager(settings) + shows = fileManager.GetFilesToPrepare(numFiles) libtvdatasource.PrepareEpisodes(shows) if __name__ == "__main__": diff --git a/libfilemanager.py b/libfilemanager.py index c87551e..04c718b 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -6,12 +6,12 @@ Created on Fri Jul 5 14:11:31 2013 """ import glob -import libtvdatasource as TVData +from libtvdatasource import TVData import os import shutil #move this to settings -TVRECORDINGSDIR = "/srv/storage2/videos/TVRecordings/" +#TVRECORDINGSDIR = "/srv/storage2/videos/TVRecordings/" class EncodeData: inputFile = '' @@ -21,80 +21,85 @@ class EncodeData: def ToString(self): return "Input: {0}/tOutput: {2}".format(self.inputFile, self.outputFile) - -def __GetInputFilesToEncode(shows): - fileList = [] +class FileManager: + def __init__(self, settings): + self.settings = settings + + def __GetInputFilesToEncode(self): + fileList = [] + + for show in self.settings.GetShowNames(): + for r,d,f in os.walk(show.GetShowInputDirectory(show)): + for files in f: + if files.endswith(".mpg"): + data = EncodeData() + data.show = show + data.inputFile = os.path.join(r,files) + fileList.append(data) + + return fileList - for show in shows: - for r,d,f in os.walk(show.inputDirectory): - for files in f: - if files.endswith(".mpg"): - data = EncodeData() - data.show = show - data.inputFile = os.path.join(r,files) - fileList.append(data) + def __FindSeason(self, path, fileName, readOnly): + season = "Season {0}".format(fileName[1:3]) + seasonPath = os.path.join(path, season) - return fileList - -def __FindSeason(path, fileName, readOnly): - 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) + if not readOnly: + if not os.path.exists(seasonPath): + os.makedirs(seasonPath) + + return seasonPath - return seasonPath - -def __GetEncodeOutputFile(showData, readOnly): - inFile = os.path.basename(showData.inputFile) - outFilename = inFile[:-3]+"mkv" - outPath = __FindSeason(showData.show.outputDirectory, outFilename) - showData.outputFile = os.path.join(outPath, outFilename) + def __GetEncodeOutputFile(self, inputFile, showName, readOnly): + inFile = os.path.basename(inputFile) + outFilename = inFile[:-3]+"mkv" + outPath = self.__FindSeason(self.settings.GetShowOutputFile(showName), outFilename) + return os.path.join(outPath, outFilename) - return showData - -def GetEncodingFiles(shows, readOnly=True): - showsData = __GetInputFilesToEncode(shows) - for showData in showsData: - showsData = __GetEncodeOutputFile(showData, readOnly) + def GetEncodingFiles(self, readOnly=True): + showsData = self.__GetInputFilesToEncode(self.settings.GetShowNames()) + for showData in showsData: + showData.outputFile = self.__GetEncodeOutputFile(showData.inputFile, showData.name, readOnly) + + return showsData - return showsData - -def CheckFileExists(file): - return os.path.isfile(file) + def CheckFileExists(self, file): + return os.path.isfile(file) + + def __GetRecordingFile(self, fileName): + return os.path.join(self.settings.TVRecordingDirectory, os.path.dirname(fileName).split("/")[-1] + ".mpg") + + def PerformPostEncodeFileOperations(self, inputFileName, outputFileName): + shutil.rmtree(os.path.dirname(inputFileName)) -def __GetRecordingFile(fileName): - return os.path.join(TVRECORDINGSDIR, os.path.dirname(fileName).split("/")[-1] + ".mpg") + linkAddress = self.__GetRecordingFile(inputFileName) -def PerformPostEncodeFileOperations(inputFileName, outputFileName): - shutil.rmtree(os.path.dirname(inputFileName)) - - linkAddress = __GetRecordingFile(inputFileName) - - os.remove(linkAddress) - - os.symlink(outputFileName, linkAddress) - -def GetFilesToPrepare(path, numberofFiles, shows): - files = glob.glob("{0}*.mpg".format(path)) - files = sorted(files, key=os.path.getctime) - files = filter(lambda file: not os.path.islink(file), files) + os.remove(linkAddress) - #files is now a list of unprocessed files, but contains shows other than those we are interested in + os.symlink(outputFileName, linkAddress) - showsToProcess = [] - i = 0 - print "Found {0} potential files".format(len(files)) - for file in files: - # TODO get these from settings - #if TVData.CheckTitleIsInList('localhost', 'script', 'script', 'mythconverg', file): - showData = TVData.RetrieveEpisodeData('192.168.0.2', 'script', 'script', 'mythconverg', file, shows, '192.168.0.2', '8081', '3678177136222bf5002be209220ccb20') # TODO all these settings need to move to settings - if showData: - showsToProcess.append(showData) - i = i + 1 - if i == int(numberofFiles): - return showsToProcess - - return showsToProcess #will reach here if there were less than numberofFiles found + def GetFilesToPrepare(self, numberofFiles): + path = self.settings.TVRecordingDirectory() + files = glob.glob("{0}*.mpg".format(path)) + files = sorted(files, key=os.path.getctime) + files = filter(lambda file: not os.path.islink(file), files) + + #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(files)) + + tvData = TVData(self.settings) + + for file in files: + # TODO get these from settings + #if TVData.CheckTitleIsInList('localhost', 'script', 'script', 'mythconverg', file): + showData = tvData.RetrieveEpisodeData(file) + if showData: + showsToProcess.append(showData) + i = i + 1 + if i == int(numberofFiles): + return showsToProcess + + return showsToProcess #will reach here if there were less than numberofFiles found \ No newline at end of file diff --git a/libhandbrake.py b/libhandbrake.py index f077fa0..c32e940 100644 --- a/libhandbrake.py +++ b/libhandbrake.py @@ -10,20 +10,25 @@ Library to interface with handbrake to encode video files import logging import subprocess -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'] +#============================================================================== +# 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'] +#============================================================================== class Encoder: - def Encode(input, output, waitForCompletion=True, logger=None): - HANDBRAKECOMMAND[3] = input - HANDBRAKECOMMAND[5] = output + def __init__(self, handbrakeCommand): + self.handbrakeCommand = handbrakeCommand + + def Encode(self, input, output, waitForCompletion=True, logger=None): + self.handbrakeCommand[3] = input + self.handbrakeCommand[5] = output - logger.debug("Handbrake command is: {0}".format(HANDBRAKECOMMAND)) - process = subprocess.Popen(HANDBRAKECOMMAND) + logger.debug("Handbrake command is: {0}".format(self.handbrakeCommand)) + process = subprocess.Popen(self.handbrakeCommand) if waitForCompletion: process.wait() diff --git a/libmythtv.py b/libmythtv.py index 5838849..8d10a38 100644 --- a/libmythtv.py +++ b/libmythtv.py @@ -8,14 +8,25 @@ Created on Fri Jul 5 14:10:47 2013 import MySQLdb as mdb from libtvshow import TVShow -def RetrieveEpisodeData(serverAddress, user, password, database, inputFile): - con = mdb.connect(serverAddress, user, password, database) +class MythTV: + def __init__(self, settings): + self.settings = settings - 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() - #print result + def RetrieveEpisodeData(self, inputFile): + con = mdb.connect(self.settings.MythTVAddress(), self.settings.MythTVUser(), self.settings.MythTVPassword(), self.settings.MythTVDatabase()) - return TVShow(result['episode'], result['season'], result['title'], result['subtitle'], result['description']) + 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() + #print result + + return TVShow(result['episode'], result['season'], result['title'], result['subtitle'], result['description']) + + def FixMythTVEpisodeName(self, showName, episodeTitle): + for prefix in self.settings.GetShowMythTVEpisodePrefix(showName): + if episodeTitle.lower().startswith(prefix.lower()): + return episodeTitle[len(prefix):] + + return episodeTitle #didn't find anything so return the episode title \ No newline at end of file diff --git a/libsettings.py b/libsettings.py index c20a3b2..584696b 100644 --- a/libsettings.py +++ b/libsettings.py @@ -5,7 +5,7 @@ Created on Fri Jul 5 20:14:15 2013 @author: shanef """ -from xml.etree import ElementTree +from configobj import ConfigObj class ShowSettings: def __init__(self, name, inputDirectory, outputDirectory): @@ -13,15 +13,98 @@ class ShowSettings: self.inputDirectory = inputDirectory self.outputDirectory = outputDirectory + class Settings: def __init__(self, settingsFile): - self.shows = self.LoadSettings(settingsFile) + self.__config = ConfigObj(settingsFile) - def LoadSettings(self, source): - shows = [] - settingsXml = ElementTree.parse(source).getroot() + def TVRecordingDirectory(self): + return self.__config["TVRecordings"] - for show in settingsXml.findall('show'): - newShow = ShowSettings(show[0].text, show[1].text, show[2].text) - shows.append(newShow) - return shows \ No newline at end of file + def HandbrakeCommand(self): + return self.__config["HandbrakeCommand"] + + def MythTVAddress(self): + return self.__config["MythTV"]["address"] + + def MythTVUser(self): + return self.__config["MythTV"]["user"] + + def MythTVPassword(self): + return self.__config["MythTV"]["password"] + + def MythTVDatabase(self): + return self.__config["MythTV"]["database"] + + def SickbeardAddress(self): + return self.__config["Sickbeard"]["address"] + + def SickbeardPort(self): + return int(self.__config["Sickbeard"]["port"]) + + def SickbeardAPIKey(self): + return self.__config["Sickbeard"]["APIKey"] + + def UnknownDirectory(self): + return self.__config["Shows"]["UnknownInput"] + + def GetShowNames(self, includeAlias=False): + 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 GetShowInputDirectory(self, showName): + show = self.__GetShowSubsection(showName) + if show is None: + return "" + else: + return show["InputDirectory"] + + def GetShowOutputDirectory(self, showName): + show = self.__GetShowSubsection(showName) + if show is None: + return "" + else: + return show["OutputDirectory"] + + def GetShowAlias(self, showName): + show = self.__GetShowSubsection(showName) + if show is None: + return "" + else: + return show["alias"] + + def GetShowMythTVEpisodePrefix(self, showName): + show = self.__GetShowSubsection(showName) + if show is None: + return "" + else: + return show["MythTvEpisodePrefix"] + + def GetShowSickbeardEpisodePrefix(self, showName): + show = self.__GetShowSubsection(showName) + if show is None: + return "" + else: + return show["SickbeardPrefix"] + + def GetShow(self, showName): + showSection = self.__GetShowSubsection(showName) + if showSection is None: + return None + else: + return showSection.name + + def __GetShowSubsection(self, 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 \ No newline at end of file diff --git a/libsickbeard.py b/libsickbeard.py index 6fe27b0..d64bdde 100644 --- a/libsickbeard.py +++ b/libsickbeard.py @@ -12,13 +12,14 @@ from fuzzywuzzy import fuzz from operator import itemgetter class Sickbeard: - def __init__(self, address, port, apikey): - self.address = address - self.port = port - self.apikey = apikey + def __init__(self, settings): + self.__settings = settings + self.__address = settings.SickbeardAddress() + self.__port = settings.SickbeardPort() + self.__apikey = settings.SickbeardAPIKey() def __GetApiURL(self): - return "http://{0}:{1}/api/{2}/".format(self.address, self.port, self.apikey) + return "http://{0}:{1}/api/{2}/".format(self.__address, self.__port, self.__apikey) def FindShowId(self, showName): jsonurl = urlopen(self.__GetApiURL()+"?cmd=shows") @@ -42,10 +43,11 @@ class Sickbeard: def FindEpisodeByDescription(self, showId, season, episode, description): jsonEpisodeUrl = urlopen("{0}?cmd=episode&tvdbid={1}&season={2}&episode={3}".format(self.__GetApiURL(), showId, season, episode)) episodeResult = json.loads(jsonEpisodeUrl.read()) - - if fuzz.ratio(episodeResult['data']['description'].lower(), description.lower()) > 85: - return (season, episode, episodeResult['data']['name']) + sickbeardDescription = episodeResult['data']['description'] + if fuzz.ratio(sickbeardDescription.lower(), description.lower()) > 85 or fuzz.ratio(sickbeardDescription.lower()[:len(description)], description.lower()) > 85 or fuzz.ratio(sickbeardDescription.lower(), description.lower()[:len(sickbeardDescription)]) > 85: + return (season, episode, episodeResult['data']['name']) + return None def FindEpisode(self, showId, name=None, description=None): @@ -54,8 +56,8 @@ class Sickbeard: 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, name) + if name is not None and fuzz.partial_ratio(name.lower(), result['data'][season][episode]['name'].lower()) > 90: + return (season, episode, result['data'][season][episode]['name']) elif description is not None: descriptionQueryResult = self.FindEpisodeByDescription(showId, season, episode, description) if descriptionQueryResult is not None: @@ -63,8 +65,18 @@ class Sickbeard: return (0, 0, '') - def GetEpisodeName(subtitle, showName): - if subtitle[:len(showName)].lower() == showName.lower(): - return subtitle[len(showName + ' and the '):] - else: - return subtitle \ No newline at end of file +#============================================================================== +# def GetEpisodeName(subtitle, showName): +# if subtitle[:len(showName)].lower() == showName.lower(): +# return subtitle[len(showName + ' and the '):] +# else: +# return subtitle +#============================================================================== + + def FixEpisodeTitle(self, showName, episodeTitle): + sickbeardPrefix = self.__settings.GetShowSickbeardEpisodePrefix(showName) + if sickbeardPrefix != "": + if not episodeTitle.lower.startswith(sickbeardPrefix.lower()): + return "{0} {1}".format(sickbeardPrefix.rstrip(), episodeTitle.lstrip()) + + return episodeTitle \ No newline at end of file diff --git a/libtvdatasource.py b/libtvdatasource.py index b964852..339d6a9 100644 --- a/libtvdatasource.py +++ b/libtvdatasource.py @@ -5,116 +5,131 @@ Created on Fri Jul 5 14:42:47 2013 @author: shanef """ -import libmythtv as MythTV +from libmythtv import MythTV from libsickbeard import Sickbeard import os import shutil # TODO Move these to settings -PROCESSDIR="/srv/storage2/files/VideoProcessing/" -THOMAS="Thomas" -CHUGGINGTON="Chuggington" -MIKE="MikeTheKnight" -OCTONAUTS="Octonauts" -NIGHTGARDEN="InTheNightGarden" -RAARAA="RaaRaa" -INPUTDIR="Input" +#PROCESSDIR="/srv/storage2/files/VideoProcessing/" +#THOMAS="Thomas" +#CHUGGINGTON="Chuggington" +#MIKE="MikeTheKnight" +#OCTONAUTS="Octonauts" +#NIGHTGARDEN="InTheNightGarden" +#RAARAA="RaaRaa" +#INPUTDIR="Input" -def FixEpisodeSeasonNumber(number): - if len(number) == 1: - return "0{0}".format(number) - else: - return 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) +class TVData: + def __init__(self, settings): + self.settings = settings -def RetrieveEpisodeData(serverAddress, user, password, database, inputFile, showsToProcess, sickbeardAddress, sickbeardPort, sickbeardAPIKey): - file = os.path.basename(inputFile) - show = MythTV.RetrieveEpisodeData(serverAddress, user, password, database, file) + def FixEpisodeSeasonNumber(self, number): + if len(number) == 1: + return "0{0}".format(number) + else: + return number - if show.title and show.title in showsToProcess: - if show.subtitle: - show.subtitle = GetEpisodeName(show.subtitle, show.title) - - if (show.season == "0" or show.episode == "0"): - sickbeard = Sickbeard(sickbeardAddress, sickbeardPort, sickbeardAPIKey) - showId = sickbeard.FindShowId(show.title) - - result = sickbeard.FindEpisode(showId, show.subtitle, show.description) - show.season = str(result[0]) - show.episode = str(result[1]) - show.subtitle = result[2] - - 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) - - show.outputFile = os.path.join(directory, file[:-4], renamedFile) - show.inputFile = inputFile - - return show - else: - return None - -def CheckTitleIsInList(serverAddress, user, password, database, inputFile): - """Check that inputFile is a recording of a show that is to be processed.""" - file = os.path.basename(inputFile) - show = MythTV.RetrieveEpisodeData('localhost', 'script', 'script', 'mythconverg', file) - - # TODO get this from settings - if show.title in ["Thomas and Friends", "Thomas the Tank Engine & Friends", - "Chuggington", "Mike the Knight", "Octonauts", - "The Octonauts", "In the Night Garden", - "Raa Raa! The Noisy Lion"]: - return True - else: - return False - -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 GetEpisodeName(subtitle, showName): - if subtitle[:len(showName)].lower() == showName.lower(): - return subtitle[len(showName + ' and the '):] - else: - return subtitle - -def ProcessEpisode(inputFile, outputFile): - outputdir = os.path.dirname(outputFile) - if not os.path.exists(outputdir): - os.makedirs(outputdir) + def GetDirectory(self, title, season): + show = self.settings.GetShow(title) + if not show or show == "": + print "Couldn't find show for {0}".format(title) + return self.settings.UnknownDirectory() + else: + return os.path.join(self.settings.GetShowInputDirectory(show), season) +#============================================================================== +# 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" +#============================================================================== - shutil.move(inputFile, outputFile) - -def PrepareEpisodes(showsData): - for showData in showsData: - ProcessEpisode(showData.inputFile, showData.outputFile) \ No newline at end of file +# return os.path.join(PROCESSDIR, directory, INPUTDIR, season) + + def RetrieveEpisodeData(self, inputFile): + file = os.path.basename(inputFile) + + mythTv = MythTV(self.settings) + show = mythTv.RetrieveEpisodeData(file) + + 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.season != "0" and show.episode != "0": + show.season = self.FixEpisodeSeasonNumber(show.season) + show.episode = self.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 = self.GetDirectory(show.title, seasonFolder) + + show.outputFile = os.path.join(directory, file[:-4], renamedFile) + show.inputFile = inputFile + + return show + else: + return None + +#============================================================================== +# def CheckTitleIsInList(serverAddress, user, password, database, inputFile): +# """Check that inputFile is a recording of a show that is to be processed.""" +# file = os.path.basename(inputFile) +# show = MythTV.RetrieveEpisodeData('localhost', 'script', 'script', 'mythconverg', file) +# +# # TODO get this from settings +# if show.title in ["Thomas and Friends", "Thomas the Tank Engine & Friends", +# "Chuggington", "Mike the Knight", "Octonauts", +# "The Octonauts", "In the Night Garden", +# "Raa Raa! The Noisy Lion"]: +# return True +# else: +# return False +#============================================================================== + + 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 ProcessEpisode(inputFile, outputFile): + outputdir = os.path.dirname(outputFile) + if not os.path.exists(outputdir): + os.makedirs(outputdir) + + shutil.move(inputFile, outputFile) + + def PrepareEpisodes(self, showsData): + for showData in showsData: + self.ProcessEpisode(showData.inputFile, showData.outputFile) \ No newline at end of file diff --git a/settings.cfg b/settings.cfg new file mode 100644 index 0000000..8f142d5 --- /dev/null +++ b/settings.cfg @@ -0,0 +1,52 @@ +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" + +[ "MythTV" ] + address = 192.168.0.2 + user = script + password = script + database = mythconverg + +[ "Sickbeard" ] + address = 192.168.0.2 + port = 8081 + APIKey = 3678177136222bf5002be209220ccb20 + +[ "Shows" ] + UnknownInput = "/srv/storage2/files/VideoProcessing/Unknown/" + [[ "Thomas the Tank Engine & Friends" ]] + InputDirectory = "/srv/storage2/files/VideoProcessing/Thomas/Input/" + OutputDirectory = "/srv/storage2/videos/Kids/TV/Thomas The Tank Engine & Friends/" + alias = "Thomas and Friends", + MythTvEpisodePrefix = , + SickbeardPrefix = "" + [[ "Chuggington" ]] + InputDirectory = "/srv/storage2/files/VideoProcessing/Chuggington/Input/" + OutputDirectory = "/srv/storage2/videos/Kids/TV/Chuggington/" + alias = , + MythTvEpisodePrefix = , + SickbeardPrefix = "" + [[ "Mike the Knight" ]] + InputDirectory = "/srv/storage2/files/VideoProcessing/MikeTheKnight/Input/" + OutputDirectory = "/srv/storage2/videos/Kids/TV/Mike the Knight/" + alias = , + MythTvEpisodePrefix = "Mike the Knight and the ", Mike the Knight and " + SickbeardPrefix = "" + [[ "Octonauts" ]] + InputDirectory = "/srv/storage2/files/VideoProcessing/Octonauts/Input/" + OutputDirectory = "/srv/storage2/videos/Kids/TV/Octonauts/" + alias = "The Octonauts", + MythTvEpisodePrefix = "The Octonauts and ", + SickbeardPrefix = "The " + [[ "In the Night Garden" ]] + InputDirectory = "/srv/storage2/files/VideoProcessing/InTheNightGarden/Input/" + OutputDirectory = "/srv/storage2/videos/Kids/TV/In The Night Garden/" + alias = , + MythTvEpisodePrefix = , + SickbeardPrefix = "" + [[ "Raa Raa! The Noisy Lion" ]] + InputDirectory = "/srv/storage2/files/VideoProcessing/RaaRaa/Input/" + OutputDirectory = "/srv/storage2/videos/Kids/TV/Raa Raa the Noisy Lion/" + alias = , + MythTvEpisodePrefix = , + SickbeardPrefix = "" \ No newline at end of file From 6e2aff7ab80ab7c0a9392e69b4d0b3674f4a0b60 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Mon, 8 Jul 2013 17:40:55 +1000 Subject: [PATCH 24/72] bug fixing --- libsettings.py | 7 +++++++ libtvdatasource.py | 14 ++++++++------ settings.cfg | 8 +++++++- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/libsettings.py b/libsettings.py index 584696b..b8da13f 100644 --- a/libsettings.py +++ b/libsettings.py @@ -63,6 +63,13 @@ class Settings: return "" else: return show["InputDirectory"] + + def GetShowUnknownDirectory(self, showName): + show = self.__GetShowSubsection(showName) + if show is None: + return "" + else: + return show["UnknownDirectory"] def GetShowOutputDirectory(self, showName): show = self.__GetShowSubsection(showName) diff --git a/libtvdatasource.py b/libtvdatasource.py index 339d6a9..dd02c0e 100644 --- a/libtvdatasource.py +++ b/libtvdatasource.py @@ -30,13 +30,15 @@ class TVData: else: return number - def GetDirectory(self, title, season): + def GetDirectory(self, title, seasonFolder, season, episode): 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), season) + return os.path.join(self.settings.GetShowInputDirectory(show), seasonFolder) #============================================================================== # if title == "Thomas and Friends" or title == "Thomas the Tank Engine & Friends": # directory = THOMAS @@ -80,17 +82,17 @@ class TVData: show.episode = str(result[1]) show.subtitle = result[2] - if show.season != "0" and show.episode != "0": + #if show.season != "0" and show.episode != "0": show.season = self.FixEpisodeSeasonNumber(show.season) show.episode = self.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 = self.GetDirectory(show.title, seasonFolder) - + directory = self.GetDirectory(show.title, seasonFolder, season, episode) + show.outputFile = os.path.join(directory, file[:-4], renamedFile) show.inputFile = inputFile diff --git a/settings.cfg b/settings.cfg index 8f142d5..736ebde 100644 --- a/settings.cfg +++ b/settings.cfg @@ -16,36 +16,42 @@ HandbrakeCommand = "HandBrakeCLI", "--verbose", "-i", "SUBSTITUTE WITH INPUT FIL UnknownInput = "/srv/storage2/files/VideoProcessing/Unknown/" [[ "Thomas the Tank Engine & Friends" ]] InputDirectory = "/srv/storage2/files/VideoProcessing/Thomas/Input/" + UnknownDirectory = "/srv/storage2/files/VideoProcessing/Unknown/Thomas/" OutputDirectory = "/srv/storage2/videos/Kids/TV/Thomas The Tank Engine & Friends/" alias = "Thomas and Friends", MythTvEpisodePrefix = , SickbeardPrefix = "" [[ "Chuggington" ]] InputDirectory = "/srv/storage2/files/VideoProcessing/Chuggington/Input/" + UnknownDirectory = "/srv/storage2/files/VideoProcessing/Unknown/Chuggington/" OutputDirectory = "/srv/storage2/videos/Kids/TV/Chuggington/" alias = , MythTvEpisodePrefix = , SickbeardPrefix = "" [[ "Mike the Knight" ]] InputDirectory = "/srv/storage2/files/VideoProcessing/MikeTheKnight/Input/" + UnknownDirectory = "/srv/storage2/files/VideoProcessing/Unknown/MikeTheKnight/" OutputDirectory = "/srv/storage2/videos/Kids/TV/Mike the Knight/" alias = , MythTvEpisodePrefix = "Mike the Knight and the ", Mike the Knight and " SickbeardPrefix = "" [[ "Octonauts" ]] InputDirectory = "/srv/storage2/files/VideoProcessing/Octonauts/Input/" + UnknownDirectory = "/srv/storage2/files/VideoProcessing/Unknown/Octonauts/" OutputDirectory = "/srv/storage2/videos/Kids/TV/Octonauts/" alias = "The Octonauts", MythTvEpisodePrefix = "The Octonauts and ", - SickbeardPrefix = "The " + SickbeardPrefix = "The" [[ "In the Night Garden" ]] InputDirectory = "/srv/storage2/files/VideoProcessing/InTheNightGarden/Input/" + UnknownDirectory = "/srv/storage2/files/VideoProcessing/Unknown/InTheNightGarden/" OutputDirectory = "/srv/storage2/videos/Kids/TV/In The Night Garden/" alias = , MythTvEpisodePrefix = , SickbeardPrefix = "" [[ "Raa Raa! The Noisy Lion" ]] InputDirectory = "/srv/storage2/files/VideoProcessing/RaaRaa/Input/" + UnknownDirectory = "/srv/storage2/files/VideoProcessing/Unknown/RaaRaa/" OutputDirectory = "/srv/storage2/videos/Kids/TV/Raa Raa the Noisy Lion/" alias = , MythTvEpisodePrefix = , From e7fa1634808a303663517cd302b1448212830bdd Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Mon, 8 Jul 2013 23:49:54 +1000 Subject: [PATCH 25/72] added some unit tests --- libtvdatasource.py | 4 ++-- tests/libmythtvtest.py | 46 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/libtvdatasource.py b/libtvdatasource.py index dd02c0e..31e3343 100644 --- a/libtvdatasource.py +++ b/libtvdatasource.py @@ -125,12 +125,12 @@ class TVData: return os.path.join(dir, filename) - def ProcessEpisode(inputFile, outputFile): + def ProcessEpisode(self, inputFile, outputFile): outputdir = os.path.dirname(outputFile) if not os.path.exists(outputdir): os.makedirs(outputdir) - shutil.move(inputFile, outputFile) + shutil.copyfile(inputFile, outputFile) def PrepareEpisodes(self, showsData): for showData in showsData: diff --git a/tests/libmythtvtest.py b/tests/libmythtvtest.py index 15505ca..fc89d8f 100644 --- a/tests/libmythtvtest.py +++ b/tests/libmythtvtest.py @@ -5,3 +5,49 @@ Created on Fri Jul 5 14:12:53 2013 @author: shanef """ +import unittest +from minimock import Mock +import os,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 From 74d82516ae171cea8157beb67eb124effdb88b65 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Tue, 9 Jul 2013 12:14:02 +1000 Subject: [PATCH 26/72] fixed bug --- TVEncoder.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index 57ba313..182b72d 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -10,7 +10,7 @@ import getopt from libfilemanager import FileManager from libsettings import Settings from libhandbrake import Encoder -import libtvdatasource +from libtvdatasource import TVData #TVRECORDINGSDIR = "/Volumes/TV Recordings/" #TVRECORDINGSDIR = "/srv/storage2/videos/TVRecordings/" # TODO move this to settings @@ -91,7 +91,8 @@ def main(argv): # TODO Process files for encoding fileManager = FileManager(settings) shows = fileManager.GetFilesToPrepare(numFiles) - libtvdatasource.PrepareEpisodes(shows) + tvData = TVData(settings) + tvData.PrepareEpisodes(shows) if __name__ == "__main__": main(sys.argv[1:]) \ No newline at end of file From de54bfb8762d6f2cc4d8c779e98421e0ef8a14fe Mon Sep 17 00:00:00 2001 From: sfrischkorn Date: Tue, 9 Jul 2013 20:28:47 +1000 Subject: [PATCH 27/72] fixed some errors getting encoding files --- TVEncoder.py | 8 +++++--- libfilemanager.py | 12 ++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index 182b72d..c9c1cca 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -21,8 +21,10 @@ def ShowHelp(): print 'TVEncoder.py -e - encode the files that have been processed' print 'TVEncoder.py -e -l - list the files that would be encoded' -def PrintShowsToEncode(showData): - print "/n".join(showData) +def PrintShowsToEncode(showsData): +# print "/n".join(showData) + for showData in showsData: + print showData.ToString() def PrintShowsToPrepare(showsData): for showData in showsData: @@ -95,4 +97,4 @@ def main(argv): tvData.PrepareEpisodes(shows) if __name__ == "__main__": - main(sys.argv[1:]) \ No newline at end of file + main(sys.argv[1:]) diff --git a/libfilemanager.py b/libfilemanager.py index 04c718b..86173f2 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -19,7 +19,7 @@ class EncodeData: outputFile = '' def ToString(self): - return "Input: {0}/tOutput: {2}".format(self.inputFile, self.outputFile) + return "Show: {0}\nInput: {1}\nOutput: {2}\n".format(self.show, self.inputFile, self.outputFile) class FileManager: def __init__(self, settings): @@ -29,7 +29,7 @@ class FileManager: fileList = [] for show in self.settings.GetShowNames(): - for r,d,f in os.walk(show.GetShowInputDirectory(show)): + for r,d,f in os.walk(self.settings.GetShowInputDirectory(show)): for files in f: if files.endswith(".mpg"): data = EncodeData() @@ -52,13 +52,13 @@ class FileManager: def __GetEncodeOutputFile(self, inputFile, showName, readOnly): inFile = os.path.basename(inputFile) outFilename = inFile[:-3]+"mkv" - outPath = self.__FindSeason(self.settings.GetShowOutputFile(showName), outFilename) + outPath = self.__FindSeason(self.settings.GetShowOutputDirectory(showName), outFilename, readOnly) return os.path.join(outPath, outFilename) def GetEncodingFiles(self, readOnly=True): - showsData = self.__GetInputFilesToEncode(self.settings.GetShowNames()) + showsData = self.__GetInputFilesToEncode() for showData in showsData: - showData.outputFile = self.__GetEncodeOutputFile(showData.inputFile, showData.name, readOnly) + showData.outputFile = self.__GetEncodeOutputFile(showData.inputFile, showData.show, readOnly) return showsData @@ -102,4 +102,4 @@ class FileManager: return showsToProcess return showsToProcess #will reach here if there were less than numberofFiles found - \ No newline at end of file + From 1148dce0abe6510f6ac1b5c317d96690bf377802 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Tue, 9 Jul 2013 21:23:07 +1000 Subject: [PATCH 28/72] bug fixing --- libsickbeard.py | 8 ++++++++ libtvdatasource.py | 7 +++++-- tests/libsickbeardtest.py | 1 - 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/libsickbeard.py b/libsickbeard.py index d64bdde..994fd55 100644 --- a/libsickbeard.py +++ b/libsickbeard.py @@ -49,6 +49,14 @@ class Sickbeard: return (season, episode, episodeResult['data']['name']) return None + + def FindEpisodeName(self, showId, season, episode): + 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): jsonurl = urlopen("{0}?cmd=show.seasons&tvdbid={1}".format(self.__GetApiURL(), showId)) diff --git a/libtvdatasource.py b/libtvdatasource.py index 31e3343..f68364a 100644 --- a/libtvdatasource.py +++ b/libtvdatasource.py @@ -81,10 +81,13 @@ class TVData: 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) #if show.season != "0" and show.episode != "0": - show.season = self.FixEpisodeSeasonNumber(show.season) - show.episode = self.FixEpisodeSeasonNumber(show.episode) + show.season = self.FixEpisodeSeasonNumber(show.season) + show.episode = self.FixEpisodeSeasonNumber(show.episode) seasonFolder = "Season {0}".format(show.season) season = "S{0}".format(show.season) diff --git a/tests/libsickbeardtest.py b/tests/libsickbeardtest.py index 3b0ee68..0776083 100644 --- a/tests/libsickbeardtest.py +++ b/tests/libsickbeardtest.py @@ -4,4 +4,3 @@ Created on Fri Jul 5 14:12:38 2013 @author: shanef """ - From 493fc5cb8dce5ce3c55641a2916f50fab0eb0c8c Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Wed, 10 Jul 2013 11:13:42 +1000 Subject: [PATCH 29/72] Fixed bug in arguments --- TVEncoder.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index c9c1cca..7ba447b 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -33,7 +33,7 @@ def PrintShowsToPrepare(showsData): def main(argv): numFiles = 0 doEncode = False - readOnly = True + readOnly = False doList = False try: @@ -47,9 +47,7 @@ def main(argv): sys.exit() elif opt == "-p": doEncode = False - readOnly = False elif opt == "-e": - readOnly = False doEncode = True elif opt == "-n": numFiles = arg From e52c4a813792df8cb13ff3b803c925a522005e16 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Wed, 10 Jul 2013 11:14:45 +1000 Subject: [PATCH 30/72] Fixed bug calling settings --- TVEncoder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TVEncoder.py b/TVEncoder.py index 7ba447b..c822a84 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -83,7 +83,7 @@ def main(argv): if fileManager.CheckFileExists(show.outputFile): print "File {0} already exists. Cannot process.".format(show.outputFile) else: - encoder = Encoder(settings.HandbrakeCommand) + encoder = Encoder(settings.HandbrakeCommand()) result = encoder.Encode(show.inputFile, show.outputFile) fileManager.PerformPostEncodeFileOperations(show.inputFile, show.outputFile) From 745591cb8b3f800b1eaad51528b235cc3d9c6703 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Wed, 10 Jul 2013 11:16:15 +1000 Subject: [PATCH 31/72] fixed bug calling non-existant logger --- libhandbrake.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libhandbrake.py b/libhandbrake.py index c32e940..571a951 100644 --- a/libhandbrake.py +++ b/libhandbrake.py @@ -27,7 +27,9 @@ class Encoder: self.handbrakeCommand[3] = input self.handbrakeCommand[5] = output - logger.debug("Handbrake command is: {0}".format(self.handbrakeCommand)) + if logger: + logger.debug("Handbrake command is: {0}".format(self.handbrakeCommand)) + process = subprocess.Popen(self.handbrakeCommand) if waitForCompletion: From 57002e212455794515ee0244cd5267da5d9edf90 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Wed, 10 Jul 2013 14:29:23 +1000 Subject: [PATCH 32/72] fixed bug calling settings --- libfilemanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfilemanager.py b/libfilemanager.py index 86173f2..de3c404 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -66,7 +66,7 @@ class FileManager: return os.path.isfile(file) def __GetRecordingFile(self, fileName): - return os.path.join(self.settings.TVRecordingDirectory, os.path.dirname(fileName).split("/")[-1] + ".mpg") + return os.path.join(self.settings.TVRecordingDirectory(), os.path.dirname(fileName).split("/")[-1] + ".mpg") def PerformPostEncodeFileOperations(self, inputFileName, outputFileName): shutil.rmtree(os.path.dirname(inputFileName)) From e08a415617e806a2821a5524c1b4263759816b2e Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Wed, 10 Jul 2013 15:45:42 +1000 Subject: [PATCH 33/72] Fixing pep8 recomendations --- TVEncoder.py | 62 +++++++++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index c822a84..142ad62 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -12,49 +12,52 @@ from libsettings import Settings from libhandbrake import Encoder from libtvdatasource import TVData -#TVRECORDINGSDIR = "/Volumes/TV Recordings/" -#TVRECORDINGSDIR = "/srv/storage2/videos/TVRecordings/" # TODO move this to settings def ShowHelp(): - 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 -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' + def PrintShowsToEncode(showsData): # print "/n".join(showData) for showData in showsData: print showData.ToString() + def PrintShowsToPrepare(showsData): for showData in showsData: showData.Print() + def main(argv): numFiles = 0 doEncode = False readOnly = False doList = False - + try: - opts, args = getopt.getopt(argv,"hlpen:") + opts, args = getopt.getopt(argv, "hlpen:") except getopt.GetoptError: - ShowHelp() - sys.exit(2) + ShowHelp() + sys.exit(2) for opt, arg in opts: - if opt == '-h': - ShowHelp() - sys.exit() - elif opt == "-p": - doEncode = False - elif opt == "-e": - doEncode = True - elif opt == "-n": - numFiles = arg - elif opt == "-l": - readOnly = True - doList = True - + if opt == '-h': + ShowHelp() + sys.exit() + elif opt == "-p": + doEncode = False + elif opt == "-e": + doEncode = True + elif opt == "-n": + numFiles = arg + elif opt == "-l": + readOnly = True + doList = True + settings = Settings("settings.cfg") if readOnly and doList: @@ -65,10 +68,6 @@ def main(argv): PrintShowsToEncode(showData) else: # Generate the list of files to process -# tempShowList = ["Thomas and Friends", "Thomas the Tank Engine & Friends", -# "Chuggington", "Mike the Knight", "Octonauts", -# "The Octonauts", "In the Night Garden", -# "Raa Raa! The Noisy Lion"] # TODO get from settings fileManager = FileManager(settings) shows = fileManager.GetFilesToPrepare(numFiles) print "num results: {0}".format(len(shows)) @@ -78,21 +77,24 @@ def main(argv): #Encode the files and move them to their final destination fileManager = FileManager(settings) showData = fileManager.GetEncodingFiles(readOnly) - + for show in showData: if fileManager.CheckFileExists(show.outputFile): - print "File {0} already exists. Cannot process.".format(show.outputFile) + print "File {0} already exists. Cannot process." \ + .format(show.outputFile) else: encoder = Encoder(settings.HandbrakeCommand()) result = encoder.Encode(show.inputFile, show.outputFile) - - fileManager.PerformPostEncodeFileOperations(show.inputFile, show.outputFile) + + fileManager.PerformPostEncodeFileOperations( + show.inputFile, show.outputFile) else: # TODO Process files for encoding - fileManager = FileManager(settings) + fileManager = FileManager(settings) shows = fileManager.GetFilesToPrepare(numFiles) tvData = TVData(settings) tvData.PrepareEpisodes(shows) + if __name__ == "__main__": main(sys.argv[1:]) From 29388ba43f3f793a6b63d6d98e4b65740180db83 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Wed, 10 Jul 2013 23:13:44 +1000 Subject: [PATCH 34/72] fixing pylint and pep8 recommendations --- TVEncoder.py | 114 ++++++++++++++---------- libfilemanager.py | 223 ++++++++++++++++++++++++++++------------------ 2 files changed, 206 insertions(+), 131 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index 142ad62..251095f 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -11,9 +11,14 @@ from libfilemanager import FileManager from libsettings import Settings from libhandbrake import Encoder from libtvdatasource import TVData +from collections import namedtuple -def ShowHelp(): +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 ' \ @@ -22,78 +27,95 @@ def ShowHelp(): print 'TVEncoder.py -e -l - list the files that would be encoded' -def PrintShowsToEncode(showsData): -# print "/n".join(showData) - for showData in showsData: - print showData.ToString() +def print_shows_to_encode(shows): + """ + Prints he details of the shows that have been selected for encoding. + """ + + for showdata in shows: + print showdata -def PrintShowsToPrepare(showsData): - for showData in showsData: - showData.Print() +def print_shows_to_prepare(shows): + """ + Prints he details of the shows that have been selected for preparation. + """ + + for showdata in shows: + showdata.Print() + + +def processarguments(options): + """ + Determine the actions required from the input flags + """ + + inputoptions = namedtuple("inputoptions", + "numfiles doencode readonly dolist") + + 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 + inputoptions.dolist = True + + return inputoptions def main(argv): - numFiles = 0 - doEncode = False - readOnly = False - doList = False + """ + The main program for TVEncoder. + """ try: opts, args = getopt.getopt(argv, "hlpen:") except getopt.GetoptError: - ShowHelp() + showhelp() sys.exit(2) - for opt, arg in opts: - if opt == '-h': - ShowHelp() - sys.exit() - elif opt == "-p": - doEncode = False - elif opt == "-e": - doEncode = True - elif opt == "-n": - numFiles = arg - elif opt == "-l": - readOnly = True - doList = True + + inputoptions = processarguments(opts) settings = Settings("settings.cfg") + filemanager = FileManager(settings) - if readOnly and doList: - if doEncode: + if inputoptions.readonly and inputoptions.dolist: + if inputoptions.doencode: #Generate the list of files that would be encoded - fileManager = FileManager(settings) - showData = fileManager.GetEncodingFiles(readOnly) - PrintShowsToEncode(showData) + showdata = filemanager.getencodingfiles(inputoptions.readonly) + print_shows_to_encode(showdata) else: # Generate the list of files to process - fileManager = FileManager(settings) - shows = fileManager.GetFilesToPrepare(numFiles) + shows = filemanager.getfilestoprepare(inputoptions.numfiles) print "num results: {0}".format(len(shows)) - PrintShowsToPrepare(shows) + print_shows_to_prepare(shows) else: - if doEncode: + if inputoptions.doencode: #Encode the files and move them to their final destination - fileManager = FileManager(settings) - showData = fileManager.GetEncodingFiles(readOnly) + showdata = filemanager.getencodingfiles(inputoptions.readonly) - for show in showData: - if fileManager.CheckFileExists(show.outputFile): + for show in showdata: + if filemanager.checkfileexists(show.outputFile): print "File {0} already exists. Cannot process." \ .format(show.outputFile) else: encoder = Encoder(settings.HandbrakeCommand()) result = encoder.Encode(show.inputFile, show.outputFile) - - fileManager.PerformPostEncodeFileOperations( + # TODO do something with the result + filemanager.performpostencodefileoperations( show.inputFile, show.outputFile) else: - # TODO Process files for encoding - fileManager = FileManager(settings) - shows = fileManager.GetFilesToPrepare(numFiles) - tvData = TVData(settings) - tvData.PrepareEpisodes(shows) + # Process files for encoding + shows = filemanager.getfilestoprepare(inputoptions.numfiles) + tvdata = TVData(settings) + tvdata.PrepareEpisodes(shows) if __name__ == "__main__": diff --git a/libfilemanager.py b/libfilemanager.py index de3c404..4b13837 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -10,96 +10,149 @@ from libtvdatasource import TVData import os import shutil -#move this to settings -#TVRECORDINGSDIR = "/srv/storage2/videos/TVRecordings/" class EncodeData: - inputFile = '' - show = None - outputFile = '' - - def ToString(self): - return "Show: {0}\nInput: {1}\nOutput: {2}\n".format(self.show, self.inputFile, self.outputFile) + """ + Contains detais of files to encode. + inputfile - The source file + outputfile - The destination file + show - The name of the show + """ + + def __init__(self, inputfile='', show=None, 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) + class FileManager: + """ + Perform file operations + """ + def __init__(self, settings): self.settings = settings - - def __GetInputFilesToEncode(self): - fileList = [] - - for show in self.settings.GetShowNames(): - for r,d,f in os.walk(self.settings.GetShowInputDirectory(show)): - for files in f: - if files.endswith(".mpg"): - data = EncodeData() - data.show = show - data.inputFile = os.path.join(r,files) - fileList.append(data) - - return fileList - - def __FindSeason(self, path, fileName, readOnly): - 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 __GetEncodeOutputFile(self, inputFile, showName, readOnly): - inFile = os.path.basename(inputFile) - outFilename = inFile[:-3]+"mkv" - outPath = self.__FindSeason(self.settings.GetShowOutputDirectory(showName), outFilename, readOnly) - return os.path.join(outPath, outFilename) - - def GetEncodingFiles(self, readOnly=True): - showsData = self.__GetInputFilesToEncode() - for showData in showsData: - showData.outputFile = self.__GetEncodeOutputFile(showData.inputFile, showData.show, readOnly) - - return showsData - - def CheckFileExists(self, file): - return os.path.isfile(file) - - def __GetRecordingFile(self, fileName): - return os.path.join(self.settings.TVRecordingDirectory(), os.path.dirname(fileName).split("/")[-1] + ".mpg") - - def PerformPostEncodeFileOperations(self, inputFileName, outputFileName): - shutil.rmtree(os.path.dirname(inputFileName)) - - linkAddress = self.__GetRecordingFile(inputFileName) - - os.remove(linkAddress) - - os.symlink(outputFileName, linkAddress) - - def GetFilesToPrepare(self, numberofFiles): - path = self.settings.TVRecordingDirectory() - files = glob.glob("{0}*.mpg".format(path)) - files = sorted(files, key=os.path.getctime) - files = filter(lambda file: not os.path.islink(file), files) - - #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(files)) - tvData = TVData(self.settings) - - for file in files: - # TODO get these from settings - #if TVData.CheckTitleIsInList('localhost', 'script', 'script', 'mythconverg', file): - showData = tvData.RetrieveEpisodeData(file) - if showData: - showsToProcess.append(showData) + 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 - - return showsToProcess #will reach here if there were less than numberofFiles found - + if i == int(numberoffiles): + return showstoprocess + + #will reach here if there were less than numberofFiles found + return showstoprocess + + @staticmethod + def checkfileexists(filename): + """ + Check to see if a file currently exists + """ + + return os.path.exists(filename) + + def __getinputfilestoencode(self): + """ + Get the details of the files that are waiting to be encoded + """ + + filelist = [] + + for show in self.settings.GetShowNames(): + for dirpath, dirnames, 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 From ca4c95219a1f47e593131d93219a1bfe05dc460d Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Thu, 11 Jul 2013 00:30:00 +1000 Subject: [PATCH 35/72] fixing pep8 and pylint recomendations --- TVEncoder.py | 7 +-- libhandbrake.py | 51 +++++++++---------- libmythtv.py | 53 ++++++++++++++------ libsettings.py | 127 ++++++++++++++++++++++++++---------------------- libsickbeard.py | 96 +++++++++++++++++++++++++----------- 5 files changed, 200 insertions(+), 134 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index 251095f..ab01972 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -9,7 +9,7 @@ import sys import getopt from libfilemanager import FileManager from libsettings import Settings -from libhandbrake import Encoder +import libhandbrake from libtvdatasource import TVData from collections import namedtuple @@ -106,8 +106,9 @@ def main(argv): print "File {0} already exists. Cannot process." \ .format(show.outputFile) else: - encoder = Encoder(settings.HandbrakeCommand()) - result = encoder.Encode(show.inputFile, show.outputFile) + result = libhandbrake.encode(settings.handbrakecommand(), + show.inputFile, + show.outputFile) # TODO do something with the result filemanager.performpostencodefileoperations( show.inputFile, show.outputFile) diff --git a/libhandbrake.py b/libhandbrake.py index 571a951..0e865f7 100644 --- a/libhandbrake.py +++ b/libhandbrake.py @@ -7,37 +7,30 @@ Created on Fri Jul 5 14:11:00 2013 Library to interface with handbrake to encode video files """ -import logging import subprocess -#============================================================================== -# 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'] -#============================================================================== -class Encoder: - def __init__(self, handbrakeCommand): - self.handbrakeCommand = handbrakeCommand - - def Encode(self, input, output, waitForCompletion=True, logger=None): - self.handbrakeCommand[3] = input - self.handbrakeCommand[5] = output +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. + """ - if logger: - logger.debug("Handbrake command is: {0}".format(self.handbrakeCommand)) + handbrakecommand[3] = inputfile + handbrakecommand[5] = outputfile - process = subprocess.Popen(self.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 - \ No newline at end of file + 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 index 8d10a38..0036b08 100644 --- a/libmythtv.py +++ b/libmythtv.py @@ -8,25 +8,46 @@ Created on Fri Jul 5 14:10:47 2013 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): - con = mdb.connect(self.settings.MythTVAddress(), self.settings.MythTVUser(), self.settings.MythTVPassword(), self.settings.MythTVDatabase()) - + + 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)) + cur.execute("select episode, season, title, subtitle, " + "description from mythconverg.recorded where " + "basename = '{0}'".format(inputfile)) result = cur.fetchone() - #print result - - return TVShow(result['episode'], result['season'], result['title'], result['subtitle'], result['description']) - - def FixMythTVEpisodeName(self, showName, episodeTitle): - for prefix in self.settings.GetShowMythTVEpisodePrefix(showName): - if episodeTitle.lower().startswith(prefix.lower()): - return episodeTitle[len(prefix):] - - return episodeTitle #didn't find anything so return the episode title - \ No newline at end of file + + 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 index b8da13f..21394aa 100644 --- a/libsettings.py +++ b/libsettings.py @@ -7,111 +7,122 @@ Created on Fri Jul 5 20:14:15 2013 from configobj import ConfigObj -class ShowSettings: - def __init__(self, name, inputDirectory, outputDirectory): - self.name = name - self.inputDirectory = inputDirectory - self.outputDirectory = outputDirectory + +#============================================================================== +# class ShowSettings: +# """ +# Container for the settings for a show +# """ +# +# def __init__(self, name, inputdirectory, outputdirectory): +# self.name = name +# self.inputdirectory = inputdirectory +# self.outputdirectory = outputdirectory +#============================================================================== class Settings: - def __init__(self, settingsFile): - self.__config = ConfigObj(settingsFile) - - def TVRecordingDirectory(self): + """ + Accessor for the configuration file + """ + + def __init__(self, settingsfile): + self.__config = ConfigObj(settingsfile) + + def tvrecordingdirectory(self): return self.__config["TVRecordings"] - def HandbrakeCommand(self): + def handbrakecommand(self): return self.__config["HandbrakeCommand"] - - def MythTVAddress(self): - return self.__config["MythTV"]["address"] - - def MythTVUser(self): - return self.__config["MythTV"]["user"] - def MythTVPassword(self): + def mythtvaddress(self): + return self.__config["MythTV"]["address"] + + def mythtvuser(self): + return self.__config["MythTV"]["user"] + + def mythtvpassword(self): return self.__config["MythTV"]["password"] - def MythTVDatabase(self): + def mythtvdatabase(self): return self.__config["MythTV"]["database"] - - def SickbeardAddress(self): + + def sickbeardaddress(self): return self.__config["Sickbeard"]["address"] - - def SickbeardPort(self): + + def sickbeardport(self): return int(self.__config["Sickbeard"]["port"]) - def SickbeardAPIKey(self): + def sickbeardapikey(self): return self.__config["Sickbeard"]["APIKey"] - - def UnknownDirectory(self): + + def unknowndirectory(self): return self.__config["Shows"]["UnknownInput"] - - def GetShowNames(self, includeAlias=False): + + def getshownames(self, includealias=False): shows = self.__config["Shows"].sections result = shows[:] - if includeAlias: + if includealias: for show in shows: for alias in self.__config["Shows"][show]["alias"]: result.append(alias) return result - - def GetShowInputDirectory(self, showName): - show = self.__GetShowSubsection(showName) + + def getshowinputdirectory(self, showname): + show = self.__getshowsubsection(showname) if show is None: return "" else: return show["InputDirectory"] - def GetShowUnknownDirectory(self, showName): - show = self.__GetShowSubsection(showName) + def getshowunknowndirectory(self, showname): + show = self.__getshowsubsection(showname) if show is None: return "" else: return show["UnknownDirectory"] - - def GetShowOutputDirectory(self, showName): - show = self.__GetShowSubsection(showName) + + def getshowoutputdirectory(self, showname): + show = self.__getshowsubsection(showname) if show is None: return "" else: return show["OutputDirectory"] - - def GetShowAlias(self, showName): - show = self.__GetShowSubsection(showName) + + def getshowalias(self, showname): + show = self.__getshowsubsection(showname) if show is None: return "" else: return show["alias"] - - def GetShowMythTVEpisodePrefix(self, showName): - show = self.__GetShowSubsection(showName) + + def getshowmythtvepisodeprefix(self, showname): + show = self.__getshowsubsection(showname) if show is None: return "" else: return show["MythTvEpisodePrefix"] - def GetShowSickbeardEpisodePrefix(self, showName): - show = self.__GetShowSubsection(showName) + def getshowsickbearsepisodeprefix(self, showname): + show = self.__getshowsubsection(showname) if show is None: return "" else: return show["SickbeardPrefix"] - - def GetShow(self, showName): - showSection = self.__GetShowSubsection(showName) - if showSection is None: + + def getshow(self, showname): + showsection = self.__getshowsubsection(showname) + if showsection is None: return None else: - return showSection.name - - def __GetShowSubsection(self, 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 showsection.name + + def __getshowsubsection(self, 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 \ No newline at end of file + + return None diff --git a/libsickbeard.py b/libsickbeard.py index 994fd55..b8dd583 100644 --- a/libsickbeard.py +++ b/libsickbeard.py @@ -5,22 +5,23 @@ Created on Fri Jul 5 14:10:37 2013 @author: shanef """ -from libtvshow import TVShow import json from urllib import urlopen from fuzzywuzzy import fuzz from operator import itemgetter + class Sickbeard: def __init__(self, settings): self.__settings = settings self.__address = settings.SickbeardAddress() self.__port = settings.SickbeardPort() self.__apikey = settings.SickbeardAPIKey() - + def __GetApiURL(self): - return "http://{0}:{1}/api/{2}/".format(self.__address, self.__port, self.__apikey) - + return "http://{0}:{1}/api/{2}/".format(self.__address, self.__port, + self.__apikey) + def FindShowId(self, showName): jsonurl = urlopen(self.__GetApiURL()+"?cmd=shows") result = json.loads(jsonurl.read()) @@ -33,25 +34,32 @@ class Sickbeard: shows = [] for show in result['data']: - shows.append((show, fuzz.partial_ratio(showName.lower(), result['data'][show]['show_name'].lower()))) - + 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 FindEpisodeByDescription(self, showId, season, episode, 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 fuzz.ratio(sickbeardDescription.lower(), description.lower()) > 85 or fuzz.ratio(sickbeardDescription.lower()[:len(description)], description.lower()) > 85 or fuzz.ratio(sickbeardDescription.lower(), description.lower()[:len(sickbeardDescription)]) > 85: - return (season, episode, episodeResult['data']['name']) + 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 FindEpisodeName(self, showId, season, episode): - jsonurl = urlopen("{0}?cmd=episode&tvdbid={1}&season={2}&episode={3}".format(self.__GetApiURL(), showId, int(season), int(episode))) + 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 "" @@ -59,20 +67,25 @@ class Sickbeard: return result['data']['name'] def FindEpisode(self, showId, name=None, description=None): - jsonurl = urlopen("{0}?cmd=show.seasons&tvdbid={1}".format(self.__GetApiURL(), showId)) + 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]: - if name is not None and fuzz.partial_ratio(name.lower(), result['data'][season][episode]['name'].lower()) > 90: - return (season, episode, result['data'][season][episode]['name']) + episodename = result['data'][season][episode]['name'] + if name is not None and fuzz.partial_ratio(name.lower(), + episodename) > 90: + return (season, episode, episodename) elif description is not None: - descriptionQueryResult = self.FindEpisodeByDescription(showId, season, episode, description) + descriptionQueryResult = \ + self.FindEpisodeByDescription(showId, season, + episode, description) if descriptionQueryResult is not None: return descriptionQueryResult - + return (0, 0, '') - + #============================================================================== # def GetEpisodeName(subtitle, showName): # if subtitle[:len(showName)].lower() == showName.lower(): @@ -80,11 +93,38 @@ class Sickbeard: # else: # return subtitle #============================================================================== - + def FixEpisodeTitle(self, showName, episodeTitle): - sickbeardPrefix = self.__settings.GetShowSickbeardEpisodePrefix(showName) + sickbeardPrefix = \ + self.__settings.GetShowSickbeardEpisodePrefix(showName) + if sickbeardPrefix != "": if not episodeTitle.lower.startswith(sickbeardPrefix.lower()): - return "{0} {1}".format(sickbeardPrefix.rstrip(), episodeTitle.lstrip()) - - return episodeTitle \ No newline at end of file + return "{0} {1}".format(sickbeardPrefix.rstrip(), + episodeTitle.lstrip()) + + return episodeTitle + + +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 From 0e3e1a2b14e0b750e478c228351c888830915b16 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Fri, 12 Jul 2013 00:02:42 +1000 Subject: [PATCH 36/72] fixing for pylint and pep8 recommendatins --- TVEncoder.py | 19 ++-- libfilemanager.py | 16 ++-- libmythtv.py | 12 +-- libsettings.py | 105 ++++++++++++++++++++++ libsickbeard.py | 118 +++++++++++++++---------- libtvdatasource.py | 212 ++++++++++++++++++++++----------------------- libtvshow.py | 19 ++-- 7 files changed, 317 insertions(+), 184 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index ab01972..d80a1a4 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -27,24 +27,15 @@ def showhelp(): print 'TVEncoder.py -e -l - list the files that would be encoded' -def print_shows_to_encode(shows): +def print_shows(shows): """ - Prints he details of the shows that have been selected for encoding. + Prints he details of the shows. """ for showdata in shows: print showdata -def print_shows_to_prepare(shows): - """ - Prints he details of the shows that have been selected for preparation. - """ - - for showdata in shows: - showdata.Print() - - def processarguments(options): """ Determine the actions required from the input flags @@ -90,12 +81,12 @@ def main(argv): if inputoptions.doencode: #Generate the list of files that would be encoded showdata = filemanager.getencodingfiles(inputoptions.readonly) - print_shows_to_encode(showdata) + 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_to_prepare(shows) + print_shows(shows) else: if inputoptions.doencode: #Encode the files and move them to their final destination @@ -116,7 +107,7 @@ def main(argv): # Process files for encoding shows = filemanager.getfilestoprepare(inputoptions.numfiles) tvdata = TVData(settings) - tvdata.PrepareEpisodes(shows) + tvdata.prepareepisodes(shows) if __name__ == "__main__": diff --git a/libfilemanager.py b/libfilemanager.py index 4b13837..efe0d11 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -35,7 +35,7 @@ class FileManager: """ def __init__(self, settings): - self.settings = settings + self.__settings = settings def getencodingfiles(self, readonly=True): """ @@ -70,7 +70,7 @@ class FileManager: return the details of the number available. """ - path = self.settings.TVRecordingDirectory() + path = self.__settings.tvrecordingdirectory() potentialfiles = glob.glob("{0}*.mpg".format(path)) potentialfiles = sorted(potentialfiles, key=os.path.getctime) potentialfiles = [potentialfile for potentialfile in potentialfiles @@ -82,10 +82,10 @@ class FileManager: i = 0 print "Found {0} potential files".format(len(potentialfiles)) - tvdata = TVData(self.settings) + tvdata = TVData(self.__settings) for potentialfile in potentialfiles: - showdata = tvdata.RetrieveEpisodeData(potentialfile) + showdata = tvdata.retrieveepisodedata(potentialfile) if showdata: showstoprocess.append(showdata) i = i + 1 @@ -110,9 +110,9 @@ class FileManager: filelist = [] - for show in self.settings.GetShowNames(): + for show in self.__settings.getshownames(): for dirpath, dirnames, filenames in os.walk( - self.settings.GetShowInputDirectory(show)): + self.__settings.getshowinputdirectory(show)): for inputfile in filenames: if inputfile.endswith(".mpg"): data = EncodeData(show, os.path.join( @@ -128,7 +128,7 @@ class FileManager: infile = os.path.basename(inputfile) outfilename = infile[:-3]+"mkv" - outpath = findseason(self.settings.GetShowOutputDirectory( + outpath = findseason(self.__settings.GetShowOutputDirectory( showname), outfilename, readonly) return os.path.join(outpath, outfilename) @@ -139,7 +139,7 @@ class FileManager: final directory in it's path. """ - return os.path.join(self.settings.TVRecordingDirectory(), + return os.path.join(self.__settings.TVRecordingDirectory(), os.path.dirname(filename).split("/")[-1] + ".mpg") diff --git a/libmythtv.py b/libmythtv.py index 0036b08..a65cd7d 100644 --- a/libmythtv.py +++ b/libmythtv.py @@ -15,16 +15,16 @@ class MythTV: """ def __init__(self, settings): - self.settings = 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()) + con = mdb.connect(self.__settings.mythtvaddress(), + self.__settings.mythtvuser(), + self.__settings.mythtvpassword(), + self.__settings.mythtvdatabase()) with con: cur = con.cursor(mdb.cursors.DictCursor) @@ -45,7 +45,7 @@ class MythTV: prefixes are listed in the configuration file. """ - for prefix in self.settings.GetShowMythTVEpisodePrefix(showname): + for prefix in self.__settings.getshowmythtvepisodeprefix(showname): if episodetitle.lower().startswith(prefix.lower()): return episodetitle[len(prefix):] diff --git a/libsettings.py b/libsettings.py index 21394aa..598b729 100644 --- a/libsettings.py +++ b/libsettings.py @@ -27,39 +27,90 @@ class Settings: """ 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 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: @@ -68,7 +119,30 @@ class Settings: 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 "" @@ -76,6 +150,11 @@ class Settings: 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 "" @@ -83,6 +162,10 @@ class Settings: return show["UnknownDirectory"] def getshowoutputdirectory(self, showname): + """ + Get the OutputDirectory setting for the show, showname. + """ + show = self.__getshowsubsection(showname) if show is None: return "" @@ -90,6 +173,11 @@ class Settings: 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 "" @@ -97,6 +185,10 @@ class Settings: return show["alias"] def getshowmythtvepisodeprefix(self, showname): + """ + Get the MythTVEpisodePrefix setting for the show, showname. + """ + show = self.__getshowsubsection(showname) if show is None: return "" @@ -104,13 +196,22 @@ class Settings: return show["MythTvEpisodePrefix"] def getshowsickbearsepisodeprefix(self, showname): + """ + Get the SickbeardPrefix setting for the show, showname. + """ + show = self.__getshowsubsection(showname) if show is None: return "" else: return show["SickbeardPrefix"] + # TODO check if this is actually doing anything. it seems like it + # just returns what is input def getshow(self, showname): + """ + Get the InputDirectory setting for the show, showname. + """ showsection = self.__getshowsubsection(showname) if showsection is None: return None @@ -118,6 +219,10 @@ class Settings: 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 diff --git a/libsickbeard.py b/libsickbeard.py index b8dd583..a1b4abb 100644 --- a/libsickbeard.py +++ b/libsickbeard.py @@ -12,29 +12,29 @@ 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() + self.__address = settings.sickbeardaddress() + self.__port = settings.sickbeardport() + self.__apikey = settings.sickbeardapikey() - def __GetApiURL(self): - return "http://{0}:{1}/api/{2}/".format(self.__address, self.__port, - self.__apikey) + def findshowid(self, showname): + """ + Get the tvdb show id for the show + """ - def FindShowId(self, showName): - jsonurl = urlopen(self.__GetApiURL()+"?cmd=shows") + jsonurl = urlopen(self.__getapiurl()+"?cmd=shows") result = json.loads(jsonurl.read()) - # TODO find a better way to do this - if showName == "Thomas and Friends": - showName = "Thomas The Tank Engine & Friends" - elif showName == "The Octonauts": - showName = "Octonauts" + showname = self.__settings.findshownameforalias(showname) shows = [] for show in result['data']: - shows.append((show, fuzz.partial_ratio(showName.lower(), + shows.append((show, fuzz.partial_ratio(showname.lower(), result['data'][show] ['show_name'].lower()))) @@ -43,32 +43,30 @@ class Sickbeard: if shows[0][1] > 85: return shows[0][0] - def FindEpisodeByDescription(self, showId, season, episode, description): - jsonepisodeurl = urlopen("{0}?cmd=episode&tvdbid={1}&season={2}" - "&episode={3}".format(self.__GetApiURL(), - showId, season, episode)) - episoderesult = json.loads(jsonepisodeurl.read()) + def findepisodename(self, showid, season, episode): + """ + Get the name of an episode, given it's season and episode numbers + """ - sickbearddescription = episoderesult['data']['description'] - - if fuzzystringcompare(sickbearddescription, description): - return (season, episode, episoderesult['data']['name']) - - return None - - def FindEpisodeName(self, showId, season, episode): jsonurl = urlopen("{0}?cmd=episode&tvdbid={1}&season={2}" - "&episode={3}".format(self.__GetApiURL(), showId, - int(season), int(episode))) + "&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): + 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)) + self.__getapiurl(), showid)) result = json.loads(jsonurl.read()) for season in result['data']: @@ -78,11 +76,11 @@ class Sickbeard: episodename) > 90: return (season, episode, episodename) elif description is not None: - descriptionQueryResult = \ - self.FindEpisodeByDescription(showId, season, - episode, description) - if descriptionQueryResult is not None: - return descriptionQueryResult + descriptionqueryresult = \ + self.__findepisodebydescription(showid, season, + episode, description) + if descriptionqueryresult is not None: + return descriptionqueryresult return (0, 0, '') @@ -94,16 +92,48 @@ class Sickbeard: # return subtitle #============================================================================== - def FixEpisodeTitle(self, showName, episodeTitle): - sickbeardPrefix = \ - self.__settings.GetShowSickbeardEpisodePrefix(showName) + 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 + """ - if sickbeardPrefix != "": - if not episodeTitle.lower.startswith(sickbeardPrefix.lower()): - return "{0} {1}".format(sickbeardPrefix.rstrip(), - episodeTitle.lstrip()) + sickbeardprefix = \ + self.__settings.GetShowSickbeardEpisodePrefix(showname) - return episodeTitle + 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): diff --git a/libtvdatasource.py b/libtvdatasource.py index f68364a..e19fd2c 100644 --- a/libtvdatasource.py +++ b/libtvdatasource.py @@ -10,131 +10,131 @@ from libsickbeard import Sickbeard import os import shutil -# TODO Move these to settings -#PROCESSDIR="/srv/storage2/files/VideoProcessing/" -#THOMAS="Thomas" -#CHUGGINGTON="Chuggington" -#MIKE="MikeTheKnight" -#OCTONAUTS="Octonauts" -#NIGHTGARDEN="InTheNightGarden" -#RAARAA="RaaRaa" -#INPUTDIR="Input" + +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 FixEpisodeSeasonNumber(self, number): - if len(number) == 1: - return "0{0}".format(number) - else: - return number - - def GetDirectory(self, title, seasonFolder, season, episode): - show = self.settings.GetShow(title) + 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() + return self.__settings.unknowndirectory() elif season == "S00" or episode == "E00": - return self.settings.GetShowUnknownDirectory(show) + return self.__settings.getshowunknowndirectory(show) else: - return os.path.join(self.settings.GetShowInputDirectory(show), seasonFolder) -#============================================================================== -# 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 RetrieveEpisodeData(self, inputFile): - file = os.path.basename(inputFile) - - mythTv = MythTV(self.settings) - show = mythTv.RetrieveEpisodeData(file) - - showsToProcess = self.settings.GetShowNames(True) - - if show.title and show.title in showsToProcess: - show.title = self.settings.GetShow(show.title) - + 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) - + 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.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) - - #if show.season != "0" and show.episode != "0": - show.season = self.FixEpisodeSeasonNumber(show.season) - show.episode = self.FixEpisodeSeasonNumber(show.episode) - - seasonFolder = "Season {0}".format(show.season) + 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 = "{0}{1} - {2} - SD TV_.mpg".format(season, episode, show.subtitle) - - directory = self.GetDirectory(show.title, seasonFolder, season, episode) - - show.outputFile = os.path.join(directory, file[:-4], renamedFile) - show.inputFile = inputFile - + renamedfile = "{0}{1} - {2} - SD TV_.mpg".format(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 CheckTitleIsInList(serverAddress, user, password, database, inputFile): -# """Check that inputFile is a recording of a show that is to be processed.""" -# file = os.path.basename(inputFile) -# show = MythTV.RetrieveEpisodeData('localhost', 'script', 'script', 'mythconverg', file) -# -# # TODO get this from settings -# if show.title in ["Thomas and Friends", "Thomas the Tank Engine & Friends", -# "Chuggington", "Mike the Knight", "Octonauts", -# "The Octonauts", "In the Night Garden", -# "Raa Raa! The Noisy Lion"]: -# return True -# else: -# return False +# def __determinetargetfilename(directory, filename, inputfilename): +# """ +# Determine the filename for the input file. If the path does not +# exist, it is created. +# """ +# +# inputdir = os.path.join(directory, inputfilename[:-4]) +# +# if not os.path.exists(inputdir): +# os.makedirs(inputdir) +# +# return os.path.join(inputdir, filename) #============================================================================== - - 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 ProcessEpisode(self, inputFile, outputFile): - outputdir = os.path.dirname(outputFile) + + @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): - for showData in showsData: - self.ProcessEpisode(showData.inputFile, showData.outputFile) \ No newline at end of file + + 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: + self.processepisode(showdata.inputfile, showdata.outputfile) diff --git a/libtvshow.py b/libtvshow.py index 1fbb8a1..55ae750 100644 --- a/libtvshow.py +++ b/libtvshow.py @@ -5,15 +5,22 @@ Created on Sat Jul 6 20:26:22 2013 @author: shanef """ + class TVShow(object): - def __init__(self, episode, season, title, subtitle, description, inputFile='', outputFile=''): + """ + 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 Print(self): - print "Input: {0} -> Output: {1}".format(self.inputFile, self.outputFile) \ No newline at end of file + self.inputfile = inputfile + self.outputfile = outputfile + + def __str__(self): + return "Input: {0} -> Output: {1}".format(self.inputfile, + self.outputfile) From fde0324b6afcee1e27bdbb715b090df2f113f161 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 13 Jul 2013 10:17:12 +1000 Subject: [PATCH 37/72] Fixed error calling lower --- libsickbeard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsickbeard.py b/libsickbeard.py index a1b4abb..ee3f4ab 100644 --- a/libsickbeard.py +++ b/libsickbeard.py @@ -102,7 +102,7 @@ class Sickbeard: self.__settings.GetShowSickbeardEpisodePrefix(showname) if sickbeardprefix != "": - if not episodetitle.lower.startswith(sickbeardprefix.lower()): + if not episodetitle.lower().startswith(sickbeardprefix.lower()): return "{0} {1}".format(sickbeardprefix.rstrip(), episodetitle.lstrip()) From 22b94e372cc02b3a69246f4ca691297e4022c937 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 13 Jul 2013 10:53:03 +1000 Subject: [PATCH 38/72] Fixing call to renamed method --- libfilemanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfilemanager.py b/libfilemanager.py index efe0d11..cc7de55 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -128,7 +128,7 @@ class FileManager: infile = os.path.basename(inputfile) outfilename = infile[:-3]+"mkv" - outpath = findseason(self.__settings.GetShowOutputDirectory( + outpath = findseason(self.__settings.getshowoutputdirectory( showname), outfilename, readonly) return os.path.join(outpath, outfilename) From 3c2d87e6de3b624b38067c042854a148fdc87454 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 13 Jul 2013 11:55:36 +1000 Subject: [PATCH 39/72] Added a check for existing files --- TVEncoder.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index d80a1a4..7dc2614 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -12,6 +12,7 @@ from libsettings import Settings import libhandbrake from libtvdatasource import TVData from collections import namedtuple +from termcolor import colored def showhelp(): @@ -27,12 +28,16 @@ def showhelp(): print 'TVEncoder.py -e -l - list the files that would be encoded' -def print_shows(shows): +def print_shows(shows, filemanager): """ Prints he details of the shows. """ for showdata in shows: + if filemanager.checkfileexists(showdata.outputfile): + print colored("File {0} already exists!".format( + showdata.outputfile), 'red') + print showdata @@ -81,12 +86,12 @@ def main(argv): if inputoptions.doencode: #Generate the list of files that would be encoded showdata = filemanager.getencodingfiles(inputoptions.readonly) - print_shows(showdata) + print_shows(showdata, filemanager) else: # Generate the list of files to process shows = filemanager.getfilestoprepare(inputoptions.numfiles) print "num results: {0}".format(len(shows)) - print_shows(shows) + print_shows(shows, filemanager) else: if inputoptions.doencode: #Encode the files and move them to their final destination From b9df25ddf07deee8640847b99fbb45c0f9c2a3fe Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 13 Jul 2013 15:50:47 +1000 Subject: [PATCH 40/72] Added some unit tests and fixed the mythtv ones --- libfilemanager.py | 2 +- tests/libfilemanagertest.py | 19 +++++++++++++++++++ tests/libmythtvtest.py | 28 +++++++++++++++------------- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/libfilemanager.py b/libfilemanager.py index cc7de55..52f156a 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -19,7 +19,7 @@ class EncodeData: show - The name of the show """ - def __init__(self, inputfile='', show=None, outputfile=''): + def __init__(self, show=None, inputfile='', outputfile=''): self.inputfile = inputfile self.show = show self.outputfile = outputfile diff --git a/tests/libfilemanagertest.py b/tests/libfilemanagertest.py index e7baae9..155f274 100644 --- a/tests/libfilemanagertest.py +++ b/tests/libfilemanagertest.py @@ -5,3 +5,22 @@ Created on Fri Jul 5 14:12:26 2013 @author: shanef """ +import unittest +parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +sys.path.insert(0, parentdir) +from libfilemanager import EncodeData + +class libfilemanagertest(unittest.TestCase): + def test_EncodeDataPrint(self): + showname = "test show" + inputname = "test input" + outputname = "test output" + data = EncodeData(showname, inputname, outputname) + result = data.__str__() + expected = "Show: {0}\nInput: {1}\nOutput: " \ + "{2}\n".format(showname, inputname, outputname) + self.assertEqual(result, expected) + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(libfilemanagertest) + unittest.TextTestRunner(verbosity=2).run(suite) \ No newline at end of file diff --git a/tests/libmythtvtest.py b/tests/libmythtvtest.py index fc89d8f..893cdd4 100644 --- a/tests/libmythtvtest.py +++ b/tests/libmythtvtest.py @@ -7,45 +7,47 @@ Created on Fri Jul 5 14:12:53 2013 import unittest from minimock import Mock -import os,sys +import os +import sys parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -sys.path.insert(0,parentdir) +sys.path.insert(0, parentdir) import libmythtv + class MythTVTest(unittest.TestCase): def test_FixEpisodeNameNoPrefix(self): settings = Mock('libsettings.Settings') - settings.GetShowMythTVEpisodePrefix.mock_returns = "" + settings.getshowmythtvepisodeprefix.mock_returns = "" mythtv = libmythtv.MythTV(settings) - result = mythtv.FixMythTVEpisodeName("Show", "episode") + result = mythtv.fixmythtvepisodename("Show", "episode") self.assertEqual(result, "episode") def test_FixEpisodeNameNonMatchingPrefix(self): settings = Mock('libsettings.Settings') - settings.GetShowMythTVEpisodePrefix.mock_returns = [ "BloohBlah" ] + settings.getshowmythtvepisodeprefix.mock_returns = [ "BloohBlah" ] mythtv = libmythtv.MythTV(settings) - result = mythtv.FixMythTVEpisodeName("Show", "episode") + result = mythtv.fixmythtvepisodename("Show", "episode") self.assertEqual(result, "episode") def test_FixEpisodeNameMatchingPrefix(self): settings = Mock('libsettings.Settings') - settings.GetShowMythTVEpisodePrefix.mock_returns = [ "Match " ] + settings.getshowmythtvepisodeprefix.mock_returns = [ "Match " ] mythtv = libmythtv.MythTV(settings) - result = mythtv.FixMythTVEpisodeName("Show", "Match episode") + 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 " ] + settings.getshowmythtvepisodeprefix.mock_returns = [ "Match and ", "Match the " ] mythtv = libmythtv.MythTV(settings) - result = mythtv.FixMythTVEpisodeName("Show", "Match and episode") + 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 " ] + settings.getshowmythtvepisodeprefix.mock_returns = [ "Match and ", "Match the " ] mythtv = libmythtv.MythTV(settings) - result = mythtv.FixMythTVEpisodeName("Show", "Match the episode") + result = mythtv.fixmythtvepisodename("Show", "Match the episode") self.assertEqual(result, "episode") if __name__ == '__main__': From 263a45eeb5d4409ce37b87ef9a5ca06ba3a0492e Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 13 Jul 2013 16:07:45 +1000 Subject: [PATCH 41/72] fixing check for existing output files --- TVEncoder.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index 7dc2614..fc1393a 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -33,13 +33,20 @@ def print_shows(shows, filemanager): Prints he details of the shows. """ + existing = [] + for showdata in shows: if filemanager.checkfileexists(showdata.outputfile): - print colored("File {0} already exists!".format( - showdata.outputfile), 'red') + existing.append(showdata) print showdata + if len(existing) > 0: + print colored("The following shows have existing output files that " + "need to be fixed before proceeding:\n") + + for showdata in existing: + print colored(showdata) def processarguments(options): """ From f25bf65fdab6f1bbe267284a362d1c7632cdaf88 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 13 Jul 2013 16:18:14 +1000 Subject: [PATCH 42/72] fixing colored error --- TVEncoder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index fc1393a..4aed845 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -43,10 +43,10 @@ def print_shows(shows, filemanager): if len(existing) > 0: print colored("The following shows have existing output files that " - "need to be fixed before proceeding:\n") + "need to be fixed before proceeding:\n", 'red') for showdata in existing: - print colored(showdata) + print colored(showdata, 'red') def processarguments(options): """ From dc5691130fa4b02493ca623599c7d233601016cd Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 13 Jul 2013 16:19:30 +1000 Subject: [PATCH 43/72] fixing check for existing output files --- TVEncoder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index 4aed845..9432e1b 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -38,8 +38,8 @@ def print_shows(shows, filemanager): for showdata in shows: if filemanager.checkfileexists(showdata.outputfile): existing.append(showdata) - - print showdata + else: + print showdata if len(existing) > 0: print colored("The following shows have existing output files that " From 7e777a471994debc854351c19536bdd29ca007cb Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 13 Jul 2013 21:06:47 +1000 Subject: [PATCH 44/72] Fixed error parsing input options --- TVEncoder.py | 9 +++++---- tests/libfilemanagertest.py | 3 +++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index 9432e1b..1f74f07 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -56,6 +56,8 @@ def processarguments(options): inputoptions = namedtuple("inputoptions", "numfiles doencode readonly dolist") + inputoptions.readonly = False + for opt, arg in options: if opt == '-h': showhelp() @@ -68,7 +70,6 @@ def processarguments(options): inputoptions.numfiles = arg elif opt == "-l": inputoptions.readonly = True - inputoptions.dolist = True return inputoptions @@ -77,19 +78,19 @@ def main(argv): """ The main program for TVEncoder. """ - + print argv try: opts, args = getopt.getopt(argv, "hlpen:") except getopt.GetoptError: showhelp() sys.exit(2) - + print opts inputoptions = processarguments(opts) settings = Settings("settings.cfg") filemanager = FileManager(settings) - if inputoptions.readonly and inputoptions.dolist: + if inputoptions.readonly: if inputoptions.doencode: #Generate the list of files that would be encoded showdata = filemanager.getencodingfiles(inputoptions.readonly) diff --git a/tests/libfilemanagertest.py b/tests/libfilemanagertest.py index 155f274..4c569e9 100644 --- a/tests/libfilemanagertest.py +++ b/tests/libfilemanagertest.py @@ -6,10 +6,13 @@ Created on Fri Jul 5 14:12:26 2013 """ import unittest +import os +import sys parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, parentdir) from libfilemanager import EncodeData + class libfilemanagertest(unittest.TestCase): def test_EncodeDataPrint(self): showname = "test show" From 3d914c704adfc004c363b9ced65c7631ab1b2508 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 13 Jul 2013 21:09:14 +1000 Subject: [PATCH 45/72] Fixing renamed properties --- TVEncoder.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index 1f74f07..593c3b7 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -106,16 +106,16 @@ def main(argv): showdata = filemanager.getencodingfiles(inputoptions.readonly) for show in showdata: - if filemanager.checkfileexists(show.outputFile): + if filemanager.checkfileexists(show.outputfile): print "File {0} already exists. Cannot process." \ - .format(show.outputFile) + .format(show.outputfile) else: result = libhandbrake.encode(settings.handbrakecommand(), - show.inputFile, - show.outputFile) + show.inputfile, + show.outputfile) # TODO do something with the result filemanager.performpostencodefileoperations( - show.inputFile, show.outputFile) + show.inputfile, show.outputfile) else: # Process files for encoding shows = filemanager.getfilestoprepare(inputoptions.numfiles) From 9a35c1e1f4b33a080e1eb1a860d0faef19e6a2e5 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sun, 14 Jul 2013 08:46:50 +1000 Subject: [PATCH 46/72] fixing capitalisation error --- libfilemanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfilemanager.py b/libfilemanager.py index 52f156a..439719b 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -139,7 +139,7 @@ class FileManager: final directory in it's path. """ - return os.path.join(self.__settings.TVRecordingDirectory(), + return os.path.join(self.__settings.tvrecordingdirectory(), os.path.dirname(filename).split("/")[-1] + ".mpg") From 3c707b52acc450ef30179ae0a41a193acee96ff7 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sun, 14 Jul 2013 08:49:01 +1000 Subject: [PATCH 47/72] removing debug statements --- TVEncoder.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index 593c3b7..4626397 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -78,13 +78,11 @@ def main(argv): """ The main program for TVEncoder. """ - print argv try: opts, args = getopt.getopt(argv, "hlpen:") except getopt.GetoptError: showhelp() sys.exit(2) - print opts inputoptions = processarguments(opts) settings = Settings("settings.cfg") From 1ee1c1e988db224e053b8a364328e6fccc23c283 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Mon, 15 Jul 2013 08:31:45 +1000 Subject: [PATCH 48/72] fixing capitalisation error --- TVEncoder.py | 1 + libsickbeard.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/TVEncoder.py b/TVEncoder.py index 4626397..7888a0c 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -48,6 +48,7 @@ def print_shows(shows, filemanager): for showdata in existing: print colored(showdata, 'red') + def processarguments(options): """ Determine the actions required from the input flags diff --git a/libsickbeard.py b/libsickbeard.py index ee3f4ab..e356800 100644 --- a/libsickbeard.py +++ b/libsickbeard.py @@ -99,7 +99,7 @@ class Sickbeard: """ sickbeardprefix = \ - self.__settings.GetShowSickbeardEpisodePrefix(showname) + self.__settings.getshowsickbeardepisodeprefix(showname) if sickbeardprefix != "": if not episodetitle.lower().startswith(sickbeardprefix.lower()): From df4aefeca10b9a69c2fd1384afbfc7fcca4589d0 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Mon, 15 Jul 2013 08:33:32 +1000 Subject: [PATCH 49/72] fixing typo --- libsettings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsettings.py b/libsettings.py index 598b729..ff098bc 100644 --- a/libsettings.py +++ b/libsettings.py @@ -195,7 +195,7 @@ class Settings: else: return show["MythTvEpisodePrefix"] - def getshowsickbearsepisodeprefix(self, showname): + def getshowsickbeardepisodeprefix(self, showname): """ Get the SickbeardPrefix setting for the show, showname. """ From 35ca21e4e4cd36f7d069c2fc829c1227fb40a294 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Mon, 15 Jul 2013 22:27:06 +1000 Subject: [PATCH 50/72] Added checks for potential problems when listing files --- TVEncoder.py | 42 ++++++++++++++++++++++++------------- libfilemanager.py | 12 +++++++++++ libtvshow.py | 17 +++++++++++++++ tests/libfilemanagertest.py | 2 +- 4 files changed, 58 insertions(+), 15 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index 7888a0c..f2142a5 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -28,25 +28,39 @@ def showhelp(): print 'TVEncoder.py -e -l - list the files that would be encoded' -def print_shows(shows, filemanager): +def print_shows(shows): """ Prints he details of the shows. """ + okshows = [] + noepisodes = [] + existingfiles = [] - existing = [] + for show in shows: + showstr = str(show) - for showdata in shows: - if filemanager.checkfileexists(showdata.outputfile): - existing.append(showdata) - else: - print showdata + 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) - if len(existing) > 0: - print colored("The following shows have existing output files that " - "need to be fixed before proceeding:\n", 'red') + for show in okshows: + print show - for showdata in existing: - print colored(showdata, 'red') + 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): @@ -93,12 +107,12 @@ def main(argv): if inputoptions.doencode: #Generate the list of files that would be encoded showdata = filemanager.getencodingfiles(inputoptions.readonly) - print_shows(showdata, filemanager) + 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, filemanager) + print_shows(shows) else: if inputoptions.doencode: #Encode the files and move them to their final destination diff --git a/libfilemanager.py b/libfilemanager.py index 439719b..413e2b5 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -28,6 +28,18 @@ class EncodeData: 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 os.path.exists(self.outputfile): + errors.append("FILE_EXISTS") + + return errors + class FileManager: """ diff --git a/libtvshow.py b/libtvshow.py index 55ae750..73903a9 100644 --- a/libtvshow.py +++ b/libtvshow.py @@ -5,6 +5,9 @@ Created on Sat Jul 6 20:26:22 2013 @author: shanef """ +import os +#from libfilemanager import FileManager + class TVShow(object): """ @@ -24,3 +27,17 @@ class TVShow(object): 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/tests/libfilemanagertest.py b/tests/libfilemanagertest.py index 4c569e9..c986f73 100644 --- a/tests/libfilemanagertest.py +++ b/tests/libfilemanagertest.py @@ -19,7 +19,7 @@ class libfilemanagertest(unittest.TestCase): inputname = "test input" outputname = "test output" data = EncodeData(showname, inputname, outputname) - result = data.__str__() + result = str(data) expected = "Show: {0}\nInput: {1}\nOutput: " \ "{2}\n".format(showname, inputname, outputname) self.assertEqual(result, expected) From 12cd3bdeeaaf4b7a700dbc8b02445155fed7b200 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Tue, 16 Jul 2013 15:07:09 +1000 Subject: [PATCH 51/72] Update README --- README | 1 + 1 file changed, 1 insertion(+) diff --git a/README b/README index e69de29..f6c95b5 100644 --- a/README +++ b/README @@ -0,0 +1 @@ +Testing README file From 48e4eae2beac026b6aaa87dac7e6ae4caebb3e1d Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Thu, 18 Jul 2013 23:33:17 +1000 Subject: [PATCH 52/72] Ignore ? in filenames since they are illegal --- libsettings.py | 6 +++++ libtvdatasource.py | 29 +++++++++++------------ settings.cfg | 3 ++- tests/TVEncodertest.py | 45 ++++++++++++++++++++++++++++++++++++ tests/libtvdatasourcetest.py | 38 ++++++++++++++++++++++++++++++ 5 files changed, 104 insertions(+), 17 deletions(-) create mode 100644 tests/TVEncodertest.py create mode 100644 tests/libtvdatasourcetest.py diff --git a/libsettings.py b/libsettings.py index ff098bc..3cdedba 100644 --- a/libsettings.py +++ b/libsettings.py @@ -47,6 +47,12 @@ class Settings: return self.__config["HandbrakeCommand"] + def illegalcharacters(self): + """Get a list of illegal characters for filenames + """ + + return self.__config["IllegalCharacters"] + def mythtvaddress(self): """ Get the MythTV/address setting diff --git a/libtvdatasource.py b/libtvdatasource.py index e19fd2c..87ac3ba 100644 --- a/libtvdatasource.py +++ b/libtvdatasource.py @@ -88,8 +88,8 @@ class TVData: 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) + renamedfile = self.getoutputfilename(season, episode, + show.subtitle) directory = self.getdirectory(show.title, seasonfolder, season, episode) @@ -102,20 +102,17 @@ class TVData: else: return None -#============================================================================== -# def __determinetargetfilename(directory, filename, inputfilename): -# """ -# Determine the filename for the input file. If the path does not -# exist, it is created. -# """ -# -# inputdir = os.path.join(directory, inputfilename[:-4]) -# -# if not os.path.exists(inputdir): -# os.makedirs(inputdir) -# -# return os.path.join(inputdir, filename) -#============================================================================== + 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): diff --git a/settings.cfg b/settings.cfg index 736ebde..cf6505e 100644 --- a/settings.cfg +++ b/settings.cfg @@ -1,5 +1,6 @@ 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 = "?" [ "MythTV" ] address = 192.168.0.2 @@ -30,7 +31,7 @@ HandbrakeCommand = "HandBrakeCLI", "--verbose", "-i", "SUBSTITUTE WITH INPUT FIL SickbeardPrefix = "" [[ "Mike the Knight" ]] InputDirectory = "/srv/storage2/files/VideoProcessing/MikeTheKnight/Input/" - UnknownDirectory = "/srv/storage2/files/VideoProcessing/Unknown/MikeTheKnight/" + UnknownDirectory = "/srv/storage2/files/VideoProcessing/Unknown/MikeTheKnight/" OutputDirectory = "/srv/storage2/videos/Kids/TV/Mike the Knight/" alias = , MythTvEpisodePrefix = "Mike the Knight and the ", Mike the Knight and " diff --git a/tests/TVEncodertest.py b/tests/TVEncodertest.py new file mode 100644 index 0000000..85cf4ab --- /dev/null +++ b/tests/TVEncodertest.py @@ -0,0 +1,45 @@ +# -*- 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/libtvdatasourcetest.py b/tests/libtvdatasourcetest.py new file mode 100644 index 0000000..cba96bb --- /dev/null +++ b/tests/libtvdatasourcetest.py @@ -0,0 +1,38 @@ +# -*- 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 From be9727155951a176dd262145abfa96a64e3c9e4c Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Fri, 19 Jul 2013 22:36:46 +1000 Subject: [PATCH 53/72] added logging --- .gitignore | 3 +++ TVEncoder.py | 43 +++++++++++++++++++++++++++++++++++++++---- libsettings.py | 17 ++++++++++++++++- libtvdatasource.py | 7 +++++++ pep8.sh | 3 +++ pylint.sh | 3 +++ settings.cfg | 4 ++++ 7 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 .gitignore create mode 100755 pep8.sh create mode 100755 pylint.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..35755d6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.pyc +.spyderproject + diff --git a/TVEncoder.py b/TVEncoder.py index f2142a5..fcf78ea 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -13,6 +13,7 @@ import libhandbrake from libtvdatasource import TVData from collections import namedtuple from termcolor import colored +import logging def showhelp(): @@ -103,6 +104,12 @@ def main(argv): settings = Settings("settings.cfg") filemanager = FileManager(settings) + logging.basicConfig(level=logging.DEBUG) + generallogger = createlogger("general", settings.generallogfile, + logging.debug) + actionlogger = createlogger("action", settings.actionlogfile, + logging.info) + if inputoptions.readonly: if inputoptions.doencode: #Generate the list of files that would be encoded @@ -117,24 +124,52 @@ def main(argv): if inputoptions.doencode: #Encode the files and move them to their final destination 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): - print "File {0} already exists. Cannot process." \ - .format(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) - # TODO do something with the result + + generallogger.info("Encode finished with result: {0}" + .format(result)) filemanager.performpostencodefileoperations( show.inputfile, show.outputfile) + + generallogger.info("Processing finished.") + generallogger.info("===========================" + "=============\n\n") 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) + 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/libsettings.py b/libsettings.py index 3cdedba..0a5c9fb 100644 --- a/libsettings.py +++ b/libsettings.py @@ -48,11 +48,26 @@ class Settings: return self.__config["HandbrakeCommand"] def illegalcharacters(self): - """Get a list of illegal characters for filenames + """ + 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 diff --git a/libtvdatasource.py b/libtvdatasource.py index 87ac3ba..ddffeec 100644 --- a/libtvdatasource.py +++ b/libtvdatasource.py @@ -134,4 +134,11 @@ class TVData: """ 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/pep8.sh b/pep8.sh new file mode 100755 index 0000000..73bdc68 --- /dev/null +++ b/pep8.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +pep8 TVEncoder.py libfilemanager.py libhandbrake.py libmythtv.py libsettings.py libsickbeard.py libtvdatasource.py libtvshow.py \ No newline at end of file diff --git a/pylint.sh b/pylint.sh new file mode 100755 index 0000000..60da4bf --- /dev/null +++ b/pylint.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +pylint TVEncoder.py libfilemanager.py libhandbrake.py libmythtv.py libsettings.py libsickbeard.py libtvdatasource.py libtvshow.py \ No newline at end of file diff --git a/settings.cfg b/settings.cfg index cf6505e..9ff8c44 100644 --- a/settings.cfg +++ b/settings.cfg @@ -2,6 +2,10 @@ 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 From 00de89d02c41b21dcc657851666d79f0524cde5c Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Fri, 19 Jul 2013 23:18:51 +1000 Subject: [PATCH 54/72] Added string substitutions to config and corrected logging error --- TVEncoder.py | 8 ++++---- settings.cfg | 47 ++++++++++++++++++++++++++++------------------- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index fcf78ea..dad2e57 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -105,10 +105,10 @@ def main(argv): filemanager = FileManager(settings) logging.basicConfig(level=logging.DEBUG) - generallogger = createlogger("general", settings.generallogfile, - logging.debug) - actionlogger = createlogger("action", settings.actionlogfile, - logging.info) + generallogger = createlogger("general", settings.generallogfile(), + logging.DEBUG) + actionlogger = createlogger("action", settings.actionlogfile(), + logging.INFO) if inputoptions.readonly: if inputoptions.doencode: diff --git a/settings.cfg b/settings.cfg index 9ff8c44..59cecf1 100644 --- a/settings.cfg +++ b/settings.cfg @@ -18,46 +18,55 @@ IllegalCharacters = "?" APIKey = 3678177136222bf5002be209220ccb20 [ "Shows" ] - UnknownInput = "/srv/storage2/files/VideoProcessing/Unknown/" + VideoProcessingDir = "/srv/storage2/files/VideoProcessing/" + KidsTVDir = "/srv/storage2/videos/Kids/TV/" + UnknownInput = "%(VideoProcessingDir)sUnknown/" [[ "Thomas the Tank Engine & Friends" ]] - InputDirectory = "/srv/storage2/files/VideoProcessing/Thomas/Input/" - UnknownDirectory = "/srv/storage2/files/VideoProcessing/Unknown/Thomas/" - OutputDirectory = "/srv/storage2/videos/Kids/TV/Thomas The Tank Engine & Friends/" + InputDirectory = "%(VideoProcessingDir)sThomas/Input/" + UnknownDirectory = "%(UnknownInput)sThomas/" + OutputDirectory = "%(KidsTVDir)sThomas The Tank Engine & Friends/" alias = "Thomas and Friends", MythTvEpisodePrefix = , SickbeardPrefix = "" [[ "Chuggington" ]] - InputDirectory = "/srv/storage2/files/VideoProcessing/Chuggington/Input/" - UnknownDirectory = "/srv/storage2/files/VideoProcessing/Unknown/Chuggington/" - OutputDirectory = "/srv/storage2/videos/Kids/TV/Chuggington/" + InputDirectory = "%(VideoProcessingDir)sChuggington/Input/" + UnknownDirectory = "%(UnknownInput)sChuggington/" + OutputDirectory = "%(KidsTVDir)sChuggington/" alias = , MythTvEpisodePrefix = , SickbeardPrefix = "" [[ "Mike the Knight" ]] - InputDirectory = "/srv/storage2/files/VideoProcessing/MikeTheKnight/Input/" - UnknownDirectory = "/srv/storage2/files/VideoProcessing/Unknown/MikeTheKnight/" - OutputDirectory = "/srv/storage2/videos/Kids/TV/Mike the Knight/" + InputDirectory = "%(VideoProcessingDir)sMikeTheKnight/Input/" + UnknownDirectory = "%(UnknownInput)sMikeTheKnight/" + OutputDirectory = "%(KidsTVDir)sMike the Knight/" alias = , MythTvEpisodePrefix = "Mike the Knight and the ", Mike the Knight and " SickbeardPrefix = "" [[ "Octonauts" ]] - InputDirectory = "/srv/storage2/files/VideoProcessing/Octonauts/Input/" - UnknownDirectory = "/srv/storage2/files/VideoProcessing/Unknown/Octonauts/" - OutputDirectory = "/srv/storage2/videos/Kids/TV/Octonauts/" + InputDirectory = "%(VideoProcessingDir)sOctonauts/Input/" + UnknownDirectory = "%(UnknownInput)sOctonauts/" + OutputDirectory = "%(KidsTVDir)sOctonauts/" alias = "The Octonauts", MythTvEpisodePrefix = "The Octonauts and ", SickbeardPrefix = "The" [[ "In the Night Garden" ]] - InputDirectory = "/srv/storage2/files/VideoProcessing/InTheNightGarden/Input/" - UnknownDirectory = "/srv/storage2/files/VideoProcessing/Unknown/InTheNightGarden/" - OutputDirectory = "/srv/storage2/videos/Kids/TV/In The Night Garden/" + InputDirectory = "%(VideoProcessingDir)sInTheNightGarden/Input/" + UnknownDirectory = "%(UnknownInput)sInTheNightGarden/" + OutputDirectory = "%(KidsTVDir)sIn The Night Garden/" alias = , MythTvEpisodePrefix = , SickbeardPrefix = "" [[ "Raa Raa! The Noisy Lion" ]] - InputDirectory = "/srv/storage2/files/VideoProcessing/RaaRaa/Input/" - UnknownDirectory = "/srv/storage2/files/VideoProcessing/Unknown/RaaRaa/" - OutputDirectory = "/srv/storage2/videos/Kids/TV/Raa Raa the Noisy Lion/" + InputDirectory = "%(VideoProcessingDir)sRaaRaa/Input/" + UnknownDirectory = "%(UnknownInput)sRaaRaa/" + OutputDirectory = "%(KidsTVDir)sRaa Raa the Noisy Lion/" + alias = , + MythTvEpisodePrefix = , + SickbeardPrefix = "" + [[ "Fireman Sam" ]] + InputDirectory = "%(VideoProcessingDir)sFiremanSam/Input/" + UnknownDirectory = "%(UnknownInput)sFiremanSam/" + OutputDirectory = "%(KidsTVDir)sFireman Sam/" alias = , MythTvEpisodePrefix = , SickbeardPrefix = "" \ No newline at end of file From f0ac96de9408f19098838e05b304729e717b0530 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 20 Jul 2013 20:58:29 +1000 Subject: [PATCH 55/72] Added email messaging --- EmailSettings.cfg | 5 +++++ TVEncoder.py | 24 ++++++++++++++------- libemail.py | 28 +++++++++++++++++++++++++ libsettings.py | 50 +++++++++++++++++++++++++++++++++++++++++++- logs/encoding.log | 0 logs/needsaction.log | 0 tests/emailtest.py | 7 +++++++ 7 files changed, 105 insertions(+), 9 deletions(-) create mode 100644 EmailSettings.cfg create mode 100644 libemail.py create mode 100644 logs/encoding.log create mode 100644 logs/needsaction.log create mode 100644 tests/emailtest.py diff --git a/EmailSettings.cfg b/EmailSettings.cfg new file mode 100644 index 0000000..90c69ad --- /dev/null +++ b/EmailSettings.cfg @@ -0,0 +1,5 @@ +SMTPServer = "" +SMTPUser = "" +SMTPPassword = "" +From = "" +To = "" diff --git a/TVEncoder.py b/TVEncoder.py index dad2e57..abdad64 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -10,11 +10,14 @@ 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(): """ @@ -101,15 +104,9 @@ def main(argv): sys.exit(2) inputoptions = processarguments(opts) - settings = Settings("settings.cfg") + settings = Settings(SETTINGS) filemanager = FileManager(settings) - logging.basicConfig(level=logging.DEBUG) - generallogger = createlogger("general", settings.generallogfile(), - logging.DEBUG) - actionlogger = createlogger("action", settings.actionlogfile(), - logging.INFO) - if inputoptions.readonly: if inputoptions.doencode: #Generate the list of files that would be encoded @@ -123,6 +120,13 @@ def main(argv): 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))) @@ -149,6 +153,10 @@ def main(argv): 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) @@ -163,7 +171,7 @@ def createlogger(name, filename, level): """ logger = logging.getLogger(name) - handler = logging.FileHandler(filename) + handler = logging.FileHandler(filename, mode='w') formatter = logging.Formatter('%(asctime)s %(message)s') handler.setFormatter(formatter) handler.setLevel(level) diff --git a/libemail.py b/libemail.py new file mode 100644 index 0000000..6d3d383 --- /dev/null +++ b/libemail.py @@ -0,0 +1,28 @@ +# -*- 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): + settings = EmailSettings(settingsfilename) + + msg = MIMEText(body, "plain") + msg["Subject"] = subject + msg["From"] = settings.getfromaddress() + msg["To"] = settings.gettoaddress() + + s = smtplib.SMTP(settings.getsmtpserver()) + s.ehlo() + s.starttls() + s.login(settings.getsmtpuser(), settings.getsmtppassword()) + s.sendmail(settings.getfromaddress(), [settings.gettoaddress()], + msg.as_string()) + s.quit() diff --git a/libsettings.py b/libsettings.py index 0a5c9fb..771ad80 100644 --- a/libsettings.py +++ b/libsettings.py @@ -231,7 +231,7 @@ class Settings: # just returns what is input def getshow(self, showname): """ - Get the InputDirectory setting for the show, showname. + Get the name of the show, showname. """ showsection = self.__getshowsubsection(showname) if showsection is None: @@ -252,3 +252,51 @@ class Settings: 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"] \ No newline at end of file diff --git a/logs/encoding.log b/logs/encoding.log new file mode 100644 index 0000000..e69de29 diff --git a/logs/needsaction.log b/logs/needsaction.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/emailtest.py b/tests/emailtest.py new file mode 100644 index 0000000..e35724c --- /dev/null +++ b/tests/emailtest.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Jul 19 23:31:16 2013 + +@author: shanef +""" + From 34e2a890662ad0d878234b5d303b5eac85a2e8dc Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sat, 20 Jul 2013 22:26:43 +1000 Subject: [PATCH 56/72] Added unit test for email --- TVEncoder.py | 3 ++- libemail.py | 2 +- libsettings.py | 2 +- tests/emailtest.py | 31 +++++++++++++++++++++++++++++++ 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index abdad64..9411b15 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -19,6 +19,7 @@ import logging SETTINGS = "settings.cfg" EMAIL_SETTINGS = "EmailSettings.cfg" + def showhelp(): """ Prints the command lines switches that are valid for the program. @@ -154,7 +155,7 @@ def main(argv): generallogger.info("===========================" "=============\n\n") - libemail.SendEmail(EMAIL_SETTINGS, "Encoding Complete", + libemail.sendemail(EMAIL_SETTINGS, "Encoding Complete", "Finished encoding {0} shows." .format(len(showdata))) else: diff --git a/libemail.py b/libemail.py index 6d3d383..8e4c599 100644 --- a/libemail.py +++ b/libemail.py @@ -11,7 +11,7 @@ import smtplib from email.mime.text import MIMEText -def SendEmail(settingsfilename, subject, body): +def sendemail(settingsfilename, subject, body): settings = EmailSettings(settingsfilename) msg = MIMEText(body, "plain") diff --git a/libsettings.py b/libsettings.py index 771ad80..f5ef69a 100644 --- a/libsettings.py +++ b/libsettings.py @@ -299,4 +299,4 @@ class EmailSettings: Get the to address for emails """ - return self.__config["To"] \ No newline at end of file + return self.__config["To"] diff --git a/tests/emailtest.py b/tests/emailtest.py index e35724c..3f2c270 100644 --- a/tests/emailtest.py +++ b/tests/emailtest.py @@ -5,3 +5,34 @@ 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): + #EmailSettings = Mock('libsettings.EmailSettings') + #libsettings.EmailSettings.mock_returns = Mock('emailsettings') + + #EmailSettings.getfromaddress.mock_returns = "from@email.com" + #libsettings.EmailSettings.gettoaddress.mock_returns = "to@gmail.com" + 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) From 3cb3930c1c6b6956761fd285a7f6046858bda050 Mon Sep 17 00:00:00 2001 From: sfrischkorn Date: Sun, 21 Jul 2013 09:35:53 +1000 Subject: [PATCH 57/72] removing unneeded files --- logs/encoding.log | 0 logs/needsaction.log | 0 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 logs/encoding.log delete mode 100644 logs/needsaction.log diff --git a/logs/encoding.log b/logs/encoding.log deleted file mode 100644 index e69de29..0000000 diff --git a/logs/needsaction.log b/logs/needsaction.log deleted file mode 100644 index e69de29..0000000 From aa514032f5a8131607e5c0b1dc0f92852388b012 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sun, 21 Jul 2013 21:50:58 +1000 Subject: [PATCH 58/72] Also check for the output file without the _ in the filename to see if it exists --- libfilemanager.py | 5 +++++ tests/libfilemanagertest.py | 25 ++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/libfilemanager.py b/libfilemanager.py index 413e2b5..735b9f2 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -38,6 +38,11 @@ class EncodeData: if os.path.exists(self.outputfile): errors.append("FILE_EXISTS") + if self.outputfile[-5:-4] == "_": + tempoutfile = self.outputfile[:-5] + self.outputfile[-4:] + if os.path.exists(tempoutfile): + errors.append("FILE_EXISTS") + return errors diff --git a/tests/libfilemanagertest.py b/tests/libfilemanagertest.py index c986f73..0e22626 100644 --- a/tests/libfilemanagertest.py +++ b/tests/libfilemanagertest.py @@ -8,6 +8,8 @@ Created on Fri Jul 5 14:12:26 2013 import unittest import os import sys +import minimock +from minimock import mock parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, parentdir) from libfilemanager import EncodeData @@ -24,6 +26,27 @@ class libfilemanagertest(unittest.TestCase): "{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) + + 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) + if __name__ == '__main__': suite = unittest.TestLoader().loadTestsFromTestCase(libfilemanagertest) - unittest.TextTestRunner(verbosity=2).run(suite) \ No newline at end of file + unittest.TextTestRunner(verbosity=2).run(suite) + minimock.restore() From 5ca6c3861ee7bbb091f71197c1042d80a2941ed9 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sun, 21 Jul 2013 22:23:14 +1000 Subject: [PATCH 59/72] added check for pre-existing video files --- TVEncoder.py | 5 +++++ libfilemanager.py | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/TVEncoder.py b/TVEncoder.py index 9411b15..a1c58ca 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -151,6 +151,11 @@ def main(argv): 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") diff --git a/libfilemanager.py b/libfilemanager.py index 735b9f2..1171d5e 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -112,6 +112,24 @@ class FileManager: #will reach here if there were less than numberofFiles found return showstoprocess + def checkduplicates(self, 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)[:6] + + for dirpath, dirnames, filenames in os.walk(dirname): + for show in filenames: + extension = os.path.splitext(show)[1] + if (extension in [".avi", ".mpg", ".mpeg", "mp4"] and + show[:6] == filename): + return True + + return False + @staticmethod def checkfileexists(filename): """ From 1297a07e37e05861575bd89e68e3faec4ce88501 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sun, 21 Jul 2013 22:35:53 +1000 Subject: [PATCH 60/72] fixing pylint suggestions --- libemail.py | 18 +++++++++++------- libfilemanager.py | 5 +++-- libsettings.py | 2 -- pep8.sh | 2 +- pylint.sh | 2 +- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/libemail.py b/libemail.py index 8e4c599..a886880 100644 --- a/libemail.py +++ b/libemail.py @@ -12,6 +12,10 @@ 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") @@ -19,10 +23,10 @@ def sendemail(settingsfilename, subject, body): msg["From"] = settings.getfromaddress() msg["To"] = settings.gettoaddress() - s = smtplib.SMTP(settings.getsmtpserver()) - s.ehlo() - s.starttls() - s.login(settings.getsmtpuser(), settings.getsmtppassword()) - s.sendmail(settings.getfromaddress(), [settings.gettoaddress()], - msg.as_string()) - s.quit() + 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 index 1171d5e..0b03fb9 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -112,7 +112,8 @@ class FileManager: #will reach here if there were less than numberofFiles found return showstoprocess - def checkduplicates(self, filename): + @staticmethod + def checkduplicates(filename): """ Check to see if there are any other video files existing for the episode @@ -121,7 +122,7 @@ class FileManager: dirname = os.path.dirname(filename) filename = os.path.basename(filename)[:6] - for dirpath, dirnames, filenames in os.walk(dirname): + for filenames in os.walk(dirname)[2]: for show in filenames: extension = os.path.splitext(show)[1] if (extension in [".avi", ".mpg", ".mpeg", "mp4"] and diff --git a/libsettings.py b/libsettings.py index f5ef69a..7e69edf 100644 --- a/libsettings.py +++ b/libsettings.py @@ -227,8 +227,6 @@ class Settings: else: return show["SickbeardPrefix"] - # TODO check if this is actually doing anything. it seems like it - # just returns what is input def getshow(self, showname): """ Get the name of the show, showname. diff --git a/pep8.sh b/pep8.sh index 73bdc68..ce2b244 100755 --- a/pep8.sh +++ b/pep8.sh @@ -1,3 +1,3 @@ #!/bin/bash -pep8 TVEncoder.py libfilemanager.py libhandbrake.py libmythtv.py libsettings.py libsickbeard.py libtvdatasource.py libtvshow.py \ No newline at end of file +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 index 60da4bf..5204ecc 100755 --- a/pylint.sh +++ b/pylint.sh @@ -1,3 +1,3 @@ #!/bin/bash -pylint TVEncoder.py libfilemanager.py libhandbrake.py libmythtv.py libsettings.py libsickbeard.py libtvdatasource.py libtvshow.py \ No newline at end of file +pylint TVEncoder.py libfilemanager.py libhandbrake.py libmythtv.py libsettings.py libsickbeard.py libtvdatasource.py libtvshow.py libemail.py From d9da2d18f7ab3428d52b4eb99facf091986f9bcc Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Mon, 22 Jul 2013 22:34:36 +1000 Subject: [PATCH 61/72] Fixed fuzzy episode name matching logic --- libsickbeard.py | 14 ++++-------- tests/TVEncodertest.py | 1 + tests/emailtest.py | 7 +----- tests/libsickbeardtest.py | 45 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 16 deletions(-) diff --git a/libsickbeard.py b/libsickbeard.py index e356800..96d0142 100644 --- a/libsickbeard.py +++ b/libsickbeard.py @@ -67,13 +67,15 @@ class Sickbeard: jsonurl = urlopen("{0}?cmd=show.seasons&tvdbid={1}".format( self.__getapiurl(), showid)) + print jsonurl.__class__ + print jsonurl.__class__.__name__ 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.partial_ratio(name.lower(), - episodename) > 90: + if name is not None and fuzz.ratio(name.lower(), + episodename.lower()) > 85: return (season, episode, episodename) elif description is not None: descriptionqueryresult = \ @@ -84,14 +86,6 @@ class Sickbeard: return (0, 0, '') -#============================================================================== -# def GetEpisodeName(subtitle, showName): -# if subtitle[:len(showName)].lower() == showName.lower(): -# return subtitle[len(showName + ' and the '):] -# else: -# return subtitle -#============================================================================== - def fixepisodetitle(self, showname, episodetitle): """ Check to see if there is a prefix specified for the show. If there is, diff --git a/tests/TVEncodertest.py b/tests/TVEncodertest.py index 85cf4ab..d415f05 100644 --- a/tests/TVEncodertest.py +++ b/tests/TVEncodertest.py @@ -40,6 +40,7 @@ class TVEncoderTest(unittest.TestCase): 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 index 3f2c270..92cc000 100644 --- a/tests/emailtest.py +++ b/tests/emailtest.py @@ -12,17 +12,12 @@ import sys parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, parentdir) import libemail -#from libsettings import EmailSettings +from libsettings import EmailSettings import smtplib class libemailtest(unittest.TestCase): def test_SendEmail(self): - #EmailSettings = Mock('libsettings.EmailSettings') - #libsettings.EmailSettings.mock_returns = Mock('emailsettings') - - #EmailSettings.getfromaddress.mock_returns = "from@email.com" - #libsettings.EmailSettings.gettoaddress.mock_returns = "to@gmail.com" mock("EmailSettings.getfromaddress", returns="from@email.com") mock("EmailSettings.gettoaddress", returns="to@email.com") mock("EmailSettings.getsmtpserver", returns="smtp.test") diff --git a/tests/libsickbeardtest.py b/tests/libsickbeardtest.py index 0776083..f50a4a0 100644 --- a/tests/libsickbeardtest.py +++ b/tests/libsickbeardtest.py @@ -4,3 +4,48 @@ 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 = "test" + 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) From 5aa5da1a9238b51c1044033a82527847b8c9a96b Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Mon, 22 Jul 2013 23:43:58 +1000 Subject: [PATCH 62/72] fixed error finding duplicate file --- TVEncoder.py | 2 +- libfilemanager.py | 4 ++-- tests/libfilemanagertest.py | 28 ++++++++++++++++++++++++++-- tests/libsickbeardtest.py | 2 +- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index a1c58ca..c5b8f04 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -99,7 +99,7 @@ def main(argv): The main program for TVEncoder. """ try: - opts, args = getopt.getopt(argv, "hlpen:") + opts, _ = getopt.getopt(argv, "hlpen:") except getopt.GetoptError: showhelp() sys.exit(2) diff --git a/libfilemanager.py b/libfilemanager.py index 0b03fb9..e404438 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -122,7 +122,7 @@ class FileManager: dirname = os.path.dirname(filename) filename = os.path.basename(filename)[:6] - for filenames in os.walk(dirname)[2]: + for _, _, filenames in os.walk(dirname): for show in filenames: extension = os.path.splitext(show)[1] if (extension in [".avi", ".mpg", ".mpeg", "mp4"] and @@ -147,7 +147,7 @@ class FileManager: filelist = [] for show in self.__settings.getshownames(): - for dirpath, dirnames, filenames in os.walk( + for dirpath, _, filenames in os.walk( self.__settings.getshowinputdirectory(show)): for inputfile in filenames: if inputfile.endswith(".mpg"): diff --git a/tests/libfilemanagertest.py b/tests/libfilemanagertest.py index 0e22626..643670a 100644 --- a/tests/libfilemanagertest.py +++ b/tests/libfilemanagertest.py @@ -9,10 +9,10 @@ import unittest import os import sys import minimock -from minimock import mock +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 +from libfilemanager import EncodeData, FileManager class libfilemanagertest(unittest.TestCase): @@ -46,6 +46,30 @@ class libfilemanagertest(unittest.TestCase): result = data.checkproblems() self.assertIn("FILE_EXISTS", result) + 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) + + 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) + + +def dummywalk(arg): + return [("/path/to/", [], ["S03E14 - Test - SD TV.avi"])] + if __name__ == '__main__': suite = unittest.TestLoader().loadTestsFromTestCase(libfilemanagertest) unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/tests/libsickbeardtest.py b/tests/libsickbeardtest.py index f50a4a0..2015aaa 100644 --- a/tests/libsickbeardtest.py +++ b/tests/libsickbeardtest.py @@ -19,7 +19,7 @@ class SickbeardTest(unittest.TestCase): def test_findepisodeCloseSubtitle(self): settings = Mock('libsettings.Settings') settings.sickbeardaddress.mock_returns = "test" - settings.sickbeardport.mock_returns = "test" + settings.sickbeardport.mock_returns = "123" settings.sickbeardapikey.mock_returns = "test" urllib.urlopen = dummy_urlopen From 5ae07c01e72486177aa99b2d6f3edbd4417ac2c2 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Tue, 23 Jul 2013 23:07:41 +1000 Subject: [PATCH 63/72] Removed debugging lines --- libsickbeard.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libsickbeard.py b/libsickbeard.py index 96d0142..3ef2ad1 100644 --- a/libsickbeard.py +++ b/libsickbeard.py @@ -67,8 +67,7 @@ class Sickbeard: jsonurl = urlopen("{0}?cmd=show.seasons&tvdbid={1}".format( self.__getapiurl(), showid)) - print jsonurl.__class__ - print jsonurl.__class__.__name__ + result = json.loads(jsonurl.read()) for season in result['data']: From 5ef02c1e8ef151134daa97cd2af8221fb370c152 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Tue, 23 Jul 2013 23:59:30 +1000 Subject: [PATCH 64/72] Added a check to search the output directories for duplicate video files --- TVEncoder.py | 19 +++++++++++++++++-- libfilemanager.py | 29 ++++++++++++++++++++++++++--- libsettings.py | 13 ------------- tests/libfilemanagertest.py | 10 ++++++++++ 4 files changed, 53 insertions(+), 18 deletions(-) diff --git a/TVEncoder.py b/TVEncoder.py index c5b8f04..152a36d 100644 --- a/TVEncoder.py +++ b/TVEncoder.py @@ -31,6 +31,7 @@ def showhelp(): '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): @@ -74,9 +75,11 @@ def processarguments(options): """ inputoptions = namedtuple("inputoptions", - "numfiles doencode readonly dolist") + "numfiles doencode readonly dolist " + "checkduplicates") inputoptions.readonly = False + inputoptions.checkduplicates = False for opt, arg in options: if opt == '-h': @@ -90,6 +93,8 @@ def processarguments(options): inputoptions.numfiles = arg elif opt == "-l": inputoptions.readonly = True + elif opt == "-c": + inputoptions.checkduplicates = True return inputoptions @@ -99,7 +104,7 @@ def main(argv): The main program for TVEncoder. """ try: - opts, _ = getopt.getopt(argv, "hlpen:") + opts, _ = getopt.getopt(argv, "hlpecn:") except getopt.GetoptError: showhelp() sys.exit(2) @@ -108,6 +113,16 @@ def main(argv): 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 diff --git a/libfilemanager.py b/libfilemanager.py index e404438..3faa963 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -112,6 +112,26 @@ class FileManager: #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] in [".avi", ".mpg", ".mpeg", + "mp4", ".mkv"]: + if self.checkduplicates(filename): + duplicates.append(filename) + + return duplicates + @staticmethod def checkduplicates(filename): """ @@ -120,13 +140,16 @@ class FileManager: """ dirname = os.path.dirname(filename) - filename = os.path.basename(filename)[:6] + 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 in [".avi", ".mpg", ".mpeg", "mp4"] and - show[:6] == filename): + if (extension in [".avi", ".mpg", ".mpeg", "mp4", ".mkv"] and + show[:6] == fileseasonepisode + and fileextension != extension): return True return False diff --git a/libsettings.py b/libsettings.py index 7e69edf..efc5db6 100644 --- a/libsettings.py +++ b/libsettings.py @@ -8,19 +8,6 @@ Created on Fri Jul 5 20:14:15 2013 from configobj import ConfigObj -#============================================================================== -# class ShowSettings: -# """ -# Container for the settings for a show -# """ -# -# def __init__(self, name, inputdirectory, outputdirectory): -# self.name = name -# self.inputdirectory = inputdirectory -# self.outputdirectory = outputdirectory -#============================================================================== - - class Settings: """ Accessor for the configuration file diff --git a/tests/libfilemanagertest.py b/tests/libfilemanagertest.py index 643670a..94e0bd8 100644 --- a/tests/libfilemanagertest.py +++ b/tests/libfilemanagertest.py @@ -66,6 +66,16 @@ class libfilemanagertest(unittest.TestCase): self.assertFalse(result) + 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) + def dummywalk(arg): return [("/path/to/", [], ["S03E14 - Test - SD TV.avi"])] From ac508047bd19249d671109013db397c7c9f7a7f4 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Sun, 28 Jul 2013 10:12:38 +1000 Subject: [PATCH 65/72] Added : as an illegal character --- settings.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.cfg b/settings.cfg index 59cecf1..286ed6b 100644 --- a/settings.cfg +++ b/settings.cfg @@ -1,6 +1,6 @@ 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 = "?" +IllegalCharacters = "?", ":" [ "Logging" ] General = "logs/encoding.log" From eca7f5710b978af6b39491114a19c19228443160 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Mon, 29 Jul 2013 09:40:35 +1000 Subject: [PATCH 66/72] making looking for duplicates case insensitive --- libfilemanager.py | 29 +++++++++++++++++++++++++---- settings.cfg | 14 +++++++------- tests/libfilemanagertest.py | 17 +++++++++++++++++ 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/libfilemanager.py b/libfilemanager.py index 3faa963..b697c82 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -35,12 +35,12 @@ class EncodeData: errors = [] - if os.path.exists(self.outputfile): + if checkfileexists(self.outputfile, False): errors.append("FILE_EXISTS") if self.outputfile[-5:-4] == "_": tempoutfile = self.outputfile[:-5] + self.outputfile[-4:] - if os.path.exists(tempoutfile): + if checkfileexists(tempoutfile, False): errors.append("FILE_EXISTS") return errors @@ -155,12 +155,19 @@ class FileManager: return False @staticmethod - def checkfileexists(filename): + 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 os.path.exists(filename) + return False def __getinputfilestoencode(self): """ @@ -215,3 +222,17 @@ def findseason(path, filename, readonly): os.makedirs(seasonpath) return seasonpath + +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 diff --git a/settings.cfg b/settings.cfg index 286ed6b..b84c6ca 100644 --- a/settings.cfg +++ b/settings.cfg @@ -22,49 +22,49 @@ IllegalCharacters = "?", ":" KidsTVDir = "/srv/storage2/videos/Kids/TV/" UnknownInput = "%(VideoProcessingDir)sUnknown/" [[ "Thomas the Tank Engine & Friends" ]] - InputDirectory = "%(VideoProcessingDir)sThomas/Input/" + InputDirectory = "%(VideoProcessingDir)sThomas/" UnknownDirectory = "%(UnknownInput)sThomas/" OutputDirectory = "%(KidsTVDir)sThomas The Tank Engine & Friends/" alias = "Thomas and Friends", MythTvEpisodePrefix = , SickbeardPrefix = "" [[ "Chuggington" ]] - InputDirectory = "%(VideoProcessingDir)sChuggington/Input/" + InputDirectory = "%(VideoProcessingDir)sChuggington/" UnknownDirectory = "%(UnknownInput)sChuggington/" OutputDirectory = "%(KidsTVDir)sChuggington/" alias = , MythTvEpisodePrefix = , SickbeardPrefix = "" [[ "Mike the Knight" ]] - InputDirectory = "%(VideoProcessingDir)sMikeTheKnight/Input/" + 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/Input/" + 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/Input/" + InputDirectory = "%(VideoProcessingDir)sInTheNightGarden/" UnknownDirectory = "%(UnknownInput)sInTheNightGarden/" OutputDirectory = "%(KidsTVDir)sIn The Night Garden/" alias = , MythTvEpisodePrefix = , SickbeardPrefix = "" [[ "Raa Raa! The Noisy Lion" ]] - InputDirectory = "%(VideoProcessingDir)sRaaRaa/Input/" + InputDirectory = "%(VideoProcessingDir)sRaaRaa/" UnknownDirectory = "%(UnknownInput)sRaaRaa/" OutputDirectory = "%(KidsTVDir)sRaa Raa the Noisy Lion/" alias = , MythTvEpisodePrefix = , SickbeardPrefix = "" [[ "Fireman Sam" ]] - InputDirectory = "%(VideoProcessingDir)sFiremanSam/Input/" + InputDirectory = "%(VideoProcessingDir)sFiremanSam/" UnknownDirectory = "%(UnknownInput)sFiremanSam/" OutputDirectory = "%(KidsTVDir)sFireman Sam/" alias = , diff --git a/tests/libfilemanagertest.py b/tests/libfilemanagertest.py index 94e0bd8..0ebc2b8 100644 --- a/tests/libfilemanagertest.py +++ b/tests/libfilemanagertest.py @@ -36,6 +36,7 @@ class libfilemanagertest(unittest.TestCase): result = data.checkproblems() self.assertIn("FILE_EXISTS", result) + minimock.restore() def test_EncodeDataCheckProblemsFile_Exists(self): showname = "test show" @@ -45,6 +46,18 @@ class libfilemanagertest(unittest.TestCase): 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') @@ -55,6 +68,7 @@ class libfilemanagertest(unittest.TestCase): result = filemanager.checkduplicates("/path/to/S03E14 - Test - SD TV.mkv") self.assertTrue(result) + minimock.restore() def test_checkduplicatenomatch(self): settings = Mock('libsettings.Settings') @@ -65,6 +79,7 @@ class libfilemanagertest(unittest.TestCase): result = filemanager.checkduplicates("/path/to/S03E13 - Test - SD TV.mkv") self.assertFalse(result) + minimock.restore() def test_checkduplicatesameextension(self): settings = Mock('libsettings.Settings') @@ -75,6 +90,8 @@ class libfilemanagertest(unittest.TestCase): result = filemanager.checkduplicates("/path/to/S03E14 - Test - SD TV.avi") self.assertFalse(result) + minimock.restore() + def dummywalk(arg): From 60584b4dfb6e80126a8fd48bd6c5978faa853e72 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Tue, 30 Jul 2013 19:59:45 +1000 Subject: [PATCH 67/72] fixing bug --- libfilemanager.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libfilemanager.py b/libfilemanager.py index b697c82..2c68c1a 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -223,6 +223,7 @@ def findseason(path, filename, readonly): return seasonpath + def checkfileexists(filename, casesensitive=True): """ Check to see if a file currently exists @@ -230,9 +231,9 @@ def checkfileexists(filename, casesensitive=True): if casesensitive: return os.path.exists(filename) else: - filename = os.path.basename(filename) + basename = os.path.basename(filename) for dirfile in os.listdir(os.path.dirname(filename)): - if (filename.lower() == dirfile.lower()): + if (basename.lower() == dirfile.lower()): return True return False From 5f788998a3cd4d44ddac24bfada1dfffc363f166 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Thu, 1 Aug 2013 19:16:24 +1000 Subject: [PATCH 68/72] fixing bug looking for non-existant directories --- libfilemanager.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libfilemanager.py b/libfilemanager.py index 2c68c1a..ea051f0 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -228,11 +228,16 @@ 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(os.path.dirname(filename)): + for dirfile in os.listdir(dirname): if (basename.lower() == dirfile.lower()): return True From 4e0060a3cef4f7fd703b08445941937f6e90acb9 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Mon, 9 Sep 2013 17:16:24 +1000 Subject: [PATCH 69/72] Ignore case when comparing file extensions --- libfilemanager.py | 4 ++-- tests/libfilemanagertest.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/libfilemanager.py b/libfilemanager.py index ea051f0..c5607c0 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -125,7 +125,7 @@ class FileManager: for rootdir, dirnames, filenames in os.walk(outputdir): for fle in filenames: filename = os.path.join(rootdir, fle) - if os.path.splitext(fle)[1] in [".avi", ".mpg", ".mpeg", + if os.path.splitext(fle)[1].lower() in [".avi", ".mpg", ".mpeg", "mp4", ".mkv"]: if self.checkduplicates(filename): duplicates.append(filename) @@ -147,7 +147,7 @@ class FileManager: for _, _, filenames in os.walk(dirname): for show in filenames: extension = os.path.splitext(show)[1] - if (extension in [".avi", ".mpg", ".mpeg", "mp4", ".mkv"] and + if (extension.lower() in [".avi", ".mpg", ".mpeg", "mp4", ".mkv"] and show[:6] == fileseasonepisode and fileextension != extension): return True diff --git a/tests/libfilemanagertest.py b/tests/libfilemanagertest.py index 0ebc2b8..c8b8f9b 100644 --- a/tests/libfilemanagertest.py +++ b/tests/libfilemanagertest.py @@ -70,6 +70,17 @@ class libfilemanagertest(unittest.TestCase): 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) @@ -97,6 +108,9 @@ class libfilemanagertest(unittest.TestCase): 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) From d185b0047e8082117b5a1a9c6b8a967a63051028 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Mon, 9 Sep 2013 19:45:30 +1000 Subject: [PATCH 70/72] Sort the duplicate list --- libfilemanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfilemanager.py b/libfilemanager.py index c5607c0..c0f7aeb 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -130,7 +130,7 @@ class FileManager: if self.checkduplicates(filename): duplicates.append(filename) - return duplicates + return duplicates.sort() @staticmethod def checkduplicates(filename): From 1fb8543c625ffa82c6cbb463cda1c3388f959643 Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Mon, 9 Sep 2013 19:47:25 +1000 Subject: [PATCH 71/72] Sort the duplicate list --- libfilemanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfilemanager.py b/libfilemanager.py index c0f7aeb..ae196ae 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -130,7 +130,7 @@ class FileManager: if self.checkduplicates(filename): duplicates.append(filename) - return duplicates.sort() + return duplicates.sorted() @staticmethod def checkduplicates(filename): From 3a7c6dc7b3460c0f7eecaa7dd3f79a863192073f Mon Sep 17 00:00:00 2001 From: Shane Frischkorn Date: Mon, 9 Sep 2013 19:48:38 +1000 Subject: [PATCH 72/72] Sort the duplicate list --- libfilemanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfilemanager.py b/libfilemanager.py index ae196ae..db51349 100644 --- a/libfilemanager.py +++ b/libfilemanager.py @@ -130,7 +130,7 @@ class FileManager: if self.checkduplicates(filename): duplicates.append(filename) - return duplicates.sorted() + return sorted(duplicates) @staticmethod def checkduplicates(filename):