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