Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3a7c6dc7b3 | |||
| 1fb8543c62 | |||
| d185b0047e | |||
| 4e0060a3ce | |||
| 5f788998a3 | |||
| 60584b4dfb | |||
| eca7f5710b | |||
| ac508047bd | |||
| 5ef02c1e8e | |||
| 5ae07c01e7 | |||
| 5aa5da1a92 | |||
| d9da2d18f7 | |||
| 1297a07e37 | |||
| 5ca6c3861e | |||
| aa514032f5 | |||
| 3cb3930c1c | |||
| 34e2a89066 | |||
| f0ac96de94 | |||
| 00de89d02c | |||
| be97271559 | |||
| c06d43b3e7 | |||
| 48e4eae2be |
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
*.pyc
|
||||||
|
.spyderproject
|
||||||
|
|
||||||
5
EmailSettings.cfg
Normal file
5
EmailSettings.cfg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
SMTPServer = ""
|
||||||
|
SMTPUser = ""
|
||||||
|
SMTPPassword = ""
|
||||||
|
From = ""
|
||||||
|
To = ""
|
||||||
76
TVEncoder.py
76
TVEncoder.py
@@ -10,9 +10,14 @@ import getopt
|
|||||||
from libfilemanager import FileManager
|
from libfilemanager import FileManager
|
||||||
from libsettings import Settings
|
from libsettings import Settings
|
||||||
import libhandbrake
|
import libhandbrake
|
||||||
|
import libemail
|
||||||
from libtvdatasource import TVData
|
from libtvdatasource import TVData
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from termcolor import colored
|
from termcolor import colored
|
||||||
|
import logging
|
||||||
|
|
||||||
|
SETTINGS = "settings.cfg"
|
||||||
|
EMAIL_SETTINGS = "EmailSettings.cfg"
|
||||||
|
|
||||||
|
|
||||||
def showhelp():
|
def showhelp():
|
||||||
@@ -26,6 +31,7 @@ def showhelp():
|
|||||||
'files that will be processed without actually encoding them'
|
'files that will be processed without actually encoding them'
|
||||||
print 'TVEncoder.py -e - encode the files that have been processed'
|
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 -e -l - list the files that would be encoded'
|
||||||
|
print 'TVEncoder.py -c - check the output directories for duplicates'
|
||||||
|
|
||||||
|
|
||||||
def print_shows(shows):
|
def print_shows(shows):
|
||||||
@@ -69,9 +75,11 @@ def processarguments(options):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
inputoptions = namedtuple("inputoptions",
|
inputoptions = namedtuple("inputoptions",
|
||||||
"numfiles doencode readonly dolist")
|
"numfiles doencode readonly dolist "
|
||||||
|
"checkduplicates")
|
||||||
|
|
||||||
inputoptions.readonly = False
|
inputoptions.readonly = False
|
||||||
|
inputoptions.checkduplicates = False
|
||||||
|
|
||||||
for opt, arg in options:
|
for opt, arg in options:
|
||||||
if opt == '-h':
|
if opt == '-h':
|
||||||
@@ -85,6 +93,8 @@ def processarguments(options):
|
|||||||
inputoptions.numfiles = arg
|
inputoptions.numfiles = arg
|
||||||
elif opt == "-l":
|
elif opt == "-l":
|
||||||
inputoptions.readonly = True
|
inputoptions.readonly = True
|
||||||
|
elif opt == "-c":
|
||||||
|
inputoptions.checkduplicates = True
|
||||||
|
|
||||||
return inputoptions
|
return inputoptions
|
||||||
|
|
||||||
@@ -94,15 +104,25 @@ def main(argv):
|
|||||||
The main program for TVEncoder.
|
The main program for TVEncoder.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
opts, args = getopt.getopt(argv, "hlpen:")
|
opts, _ = getopt.getopt(argv, "hlpecn:")
|
||||||
except getopt.GetoptError:
|
except getopt.GetoptError:
|
||||||
showhelp()
|
showhelp()
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
inputoptions = processarguments(opts)
|
inputoptions = processarguments(opts)
|
||||||
|
|
||||||
settings = Settings("settings.cfg")
|
settings = Settings(SETTINGS)
|
||||||
filemanager = FileManager(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.readonly:
|
||||||
if inputoptions.doencode:
|
if inputoptions.doencode:
|
||||||
#Generate the list of files that would be encoded
|
#Generate the list of files that would be encoded
|
||||||
@@ -116,25 +136,69 @@ def main(argv):
|
|||||||
else:
|
else:
|
||||||
if inputoptions.doencode:
|
if inputoptions.doencode:
|
||||||
#Encode the files and move them to their final destination
|
#Encode the files and move them to their final destination
|
||||||
showdata = filemanager.getencodingfiles(inputoptions.readonly)
|
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
generallogger = createlogger("general", settings.generallogfile(),
|
||||||
|
logging.DEBUG)
|
||||||
|
actionlogger = createlogger("action", settings.actionlogfile(),
|
||||||
|
logging.INFO)
|
||||||
|
|
||||||
|
showdata = filemanager.getencodingfiles(inputoptions.readonly)
|
||||||
|
generallogger.info("There are {0} files to process."
|
||||||
|
.format(len(showdata)))
|
||||||
for show in showdata:
|
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):
|
if filemanager.checkfileexists(show.outputfile):
|
||||||
print "File {0} already exists. Cannot process." \
|
message = "File {0} already exists. Cannot process." \
|
||||||
.format(show.outputfile)
|
.format(show.outputfile)
|
||||||
|
generallogger.warning(message)
|
||||||
|
actionlogger.warning(message)
|
||||||
else:
|
else:
|
||||||
result = libhandbrake.encode(settings.handbrakecommand(),
|
result = libhandbrake.encode(settings.handbrakecommand(),
|
||||||
show.inputfile,
|
show.inputfile,
|
||||||
show.outputfile)
|
show.outputfile)
|
||||||
# TODO do something with the result
|
|
||||||
|
generallogger.info("Encode finished with result: {0}"
|
||||||
|
.format(result))
|
||||||
filemanager.performpostencodefileoperations(
|
filemanager.performpostencodefileoperations(
|
||||||
show.inputfile, show.outputfile)
|
show.inputfile, show.outputfile)
|
||||||
|
|
||||||
|
if filemanager.checkduplicates(show.outputfile):
|
||||||
|
actionlogger.info("There is an existing video file"
|
||||||
|
"present for {0}"
|
||||||
|
.format(show.outputfile))
|
||||||
|
|
||||||
|
generallogger.info("Processing finished.")
|
||||||
|
generallogger.info("==========================="
|
||||||
|
"=============\n\n")
|
||||||
|
|
||||||
|
libemail.sendemail(EMAIL_SETTINGS, "Encoding Complete",
|
||||||
|
"Finished encoding {0} shows."
|
||||||
|
.format(len(showdata)))
|
||||||
else:
|
else:
|
||||||
# Process files for encoding
|
# Process files for encoding
|
||||||
shows = filemanager.getfilestoprepare(inputoptions.numfiles)
|
shows = filemanager.getfilestoprepare(inputoptions.numfiles)
|
||||||
|
print "Preparing {0} files".format(len(shows))
|
||||||
tvdata = TVData(settings)
|
tvdata = TVData(settings)
|
||||||
tvdata.prepareepisodes(shows)
|
tvdata.prepareepisodes(shows)
|
||||||
|
|
||||||
|
|
||||||
|
def createlogger(name, filename, level):
|
||||||
|
"""
|
||||||
|
Create a logger named <name> that will write to the file <filename>
|
||||||
|
"""
|
||||||
|
|
||||||
|
logger = logging.getLogger(name)
|
||||||
|
handler = logging.FileHandler(filename, mode='w')
|
||||||
|
formatter = logging.Formatter('%(asctime)s %(message)s')
|
||||||
|
handler.setFormatter(formatter)
|
||||||
|
handler.setLevel(level)
|
||||||
|
logger.addHandler(handler)
|
||||||
|
return logger
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main(sys.argv[1:])
|
main(sys.argv[1:])
|
||||||
|
|||||||
32
libemail.py
Normal file
32
libemail.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Created on Sat Jul 20 20:48:10 2013
|
||||||
|
|
||||||
|
@author: shanef
|
||||||
|
"""
|
||||||
|
|
||||||
|
from libsettings import EmailSettings
|
||||||
|
|
||||||
|
import smtplib
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
|
||||||
|
|
||||||
|
def sendemail(settingsfilename, subject, body):
|
||||||
|
"""
|
||||||
|
Send an email using the settings defined in settingsfilename
|
||||||
|
"""
|
||||||
|
|
||||||
|
settings = EmailSettings(settingsfilename)
|
||||||
|
|
||||||
|
msg = MIMEText(body, "plain")
|
||||||
|
msg["Subject"] = subject
|
||||||
|
msg["From"] = settings.getfromaddress()
|
||||||
|
msg["To"] = settings.gettoaddress()
|
||||||
|
|
||||||
|
smtp = smtplib.SMTP(settings.getsmtpserver())
|
||||||
|
smtp.ehlo()
|
||||||
|
smtp.starttls()
|
||||||
|
smtp.login(settings.getsmtpuser(), settings.getsmtppassword())
|
||||||
|
smtp.sendmail(settings.getfromaddress(), [settings.gettoaddress()],
|
||||||
|
msg.as_string())
|
||||||
|
smtp.quit()
|
||||||
@@ -35,7 +35,12 @@ class EncodeData:
|
|||||||
|
|
||||||
errors = []
|
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 checkfileexists(tempoutfile, False):
|
||||||
errors.append("FILE_EXISTS")
|
errors.append("FILE_EXISTS")
|
||||||
|
|
||||||
return errors
|
return errors
|
||||||
@@ -107,13 +112,62 @@ class FileManager:
|
|||||||
#will reach here if there were less than numberofFiles found
|
#will reach here if there were less than numberofFiles found
|
||||||
return showstoprocess
|
return showstoprocess
|
||||||
|
|
||||||
|
def checkexistingduplicates(self):
|
||||||
|
"""
|
||||||
|
Check the existing files in the output directories for duplicate
|
||||||
|
files, typically in different formats
|
||||||
|
"""
|
||||||
|
|
||||||
|
duplicates = []
|
||||||
|
for show in self.__settings.getshownames():
|
||||||
|
outputdir = self.__settings.getshowoutputdirectory(show)
|
||||||
|
|
||||||
|
for rootdir, dirnames, filenames in os.walk(outputdir):
|
||||||
|
for fle in filenames:
|
||||||
|
filename = os.path.join(rootdir, fle)
|
||||||
|
if os.path.splitext(fle)[1].lower() in [".avi", ".mpg", ".mpeg",
|
||||||
|
"mp4", ".mkv"]:
|
||||||
|
if self.checkduplicates(filename):
|
||||||
|
duplicates.append(filename)
|
||||||
|
|
||||||
|
return sorted(duplicates)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def checkfileexists(filename):
|
def checkduplicates(filename):
|
||||||
|
"""
|
||||||
|
Check to see if there are any other video files existing for the
|
||||||
|
episode
|
||||||
|
"""
|
||||||
|
|
||||||
|
dirname = os.path.dirname(filename)
|
||||||
|
filename = os.path.basename(filename)
|
||||||
|
fileseasonepisode = filename[:6]
|
||||||
|
fileextension = os.path.splitext(filename)[1]
|
||||||
|
|
||||||
|
for _, _, filenames in os.walk(dirname):
|
||||||
|
for show in filenames:
|
||||||
|
extension = os.path.splitext(show)[1]
|
||||||
|
if (extension.lower() in [".avi", ".mpg", ".mpeg", "mp4", ".mkv"] and
|
||||||
|
show[:6] == fileseasonepisode
|
||||||
|
and fileextension != extension):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def checkfileexists(filename, casesensitive=True):
|
||||||
"""
|
"""
|
||||||
Check to see if a file currently exists
|
Check to see if a file currently exists
|
||||||
"""
|
"""
|
||||||
|
if casesensitive:
|
||||||
return os.path.exists(filename)
|
return os.path.exists(filename)
|
||||||
|
else:
|
||||||
|
filename = os.path.basename(filename)
|
||||||
|
for dirfile in os.listdir(os.path.dirname(filename)):
|
||||||
|
if (filename.lower() == dirfile.lower()):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def __getinputfilestoencode(self):
|
def __getinputfilestoencode(self):
|
||||||
"""
|
"""
|
||||||
@@ -123,7 +177,7 @@ class FileManager:
|
|||||||
filelist = []
|
filelist = []
|
||||||
|
|
||||||
for show in self.__settings.getshownames():
|
for show in self.__settings.getshownames():
|
||||||
for dirpath, dirnames, filenames in os.walk(
|
for dirpath, _, filenames in os.walk(
|
||||||
self.__settings.getshowinputdirectory(show)):
|
self.__settings.getshowinputdirectory(show)):
|
||||||
for inputfile in filenames:
|
for inputfile in filenames:
|
||||||
if inputfile.endswith(".mpg"):
|
if inputfile.endswith(".mpg"):
|
||||||
@@ -168,3 +222,23 @@ def findseason(path, filename, readonly):
|
|||||||
os.makedirs(seasonpath)
|
os.makedirs(seasonpath)
|
||||||
|
|
||||||
return seasonpath
|
return seasonpath
|
||||||
|
|
||||||
|
|
||||||
|
def checkfileexists(filename, casesensitive=True):
|
||||||
|
"""
|
||||||
|
Check to see if a file currently exists
|
||||||
|
"""
|
||||||
|
dirname = os.path.dirname(filename)
|
||||||
|
|
||||||
|
if casesensitive:
|
||||||
|
return os.path.exists(filename)
|
||||||
|
else:
|
||||||
|
if not os.path.exists(dirname):
|
||||||
|
return False
|
||||||
|
|
||||||
|
basename = os.path.basename(filename)
|
||||||
|
for dirfile in os.listdir(dirname):
|
||||||
|
if (basename.lower() == dirfile.lower()):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|||||||
@@ -8,19 +8,6 @@ Created on Fri Jul 5 20:14:15 2013
|
|||||||
from configobj import ConfigObj
|
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:
|
class Settings:
|
||||||
"""
|
"""
|
||||||
Accessor for the configuration file
|
Accessor for the configuration file
|
||||||
@@ -47,6 +34,27 @@ class Settings:
|
|||||||
|
|
||||||
return self.__config["HandbrakeCommand"]
|
return self.__config["HandbrakeCommand"]
|
||||||
|
|
||||||
|
def illegalcharacters(self):
|
||||||
|
"""
|
||||||
|
Get a list of illegal characters for filenames
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.__config["IllegalCharacters"]
|
||||||
|
|
||||||
|
def generallogfile(self):
|
||||||
|
"""
|
||||||
|
Get the filename to save general log messages to
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.__config["Logging"]["General"]
|
||||||
|
|
||||||
|
def actionlogfile(self):
|
||||||
|
"""
|
||||||
|
Get the filename to save the action log messages to
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.__config["Logging"]["Action"]
|
||||||
|
|
||||||
def mythtvaddress(self):
|
def mythtvaddress(self):
|
||||||
"""
|
"""
|
||||||
Get the MythTV/address setting
|
Get the MythTV/address setting
|
||||||
@@ -206,11 +214,9 @@ class Settings:
|
|||||||
else:
|
else:
|
||||||
return show["SickbeardPrefix"]
|
return show["SickbeardPrefix"]
|
||||||
|
|
||||||
# TODO check if this is actually doing anything. it seems like it
|
|
||||||
# just returns what is input
|
|
||||||
def getshow(self, showname):
|
def getshow(self, showname):
|
||||||
"""
|
"""
|
||||||
Get the InputDirectory setting for the show, showname.
|
Get the name of the show, showname.
|
||||||
"""
|
"""
|
||||||
showsection = self.__getshowsubsection(showname)
|
showsection = self.__getshowsubsection(showname)
|
||||||
if showsection is None:
|
if showsection is None:
|
||||||
@@ -231,3 +237,51 @@ class Settings:
|
|||||||
return self.__config["Shows"][show]
|
return self.__config["Shows"][show]
|
||||||
|
|
||||||
return None
|
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"]
|
||||||
|
|||||||
@@ -67,13 +67,14 @@ class Sickbeard:
|
|||||||
|
|
||||||
jsonurl = urlopen("{0}?cmd=show.seasons&tvdbid={1}".format(
|
jsonurl = urlopen("{0}?cmd=show.seasons&tvdbid={1}".format(
|
||||||
self.__getapiurl(), showid))
|
self.__getapiurl(), showid))
|
||||||
|
|
||||||
result = json.loads(jsonurl.read())
|
result = json.loads(jsonurl.read())
|
||||||
|
|
||||||
for season in result['data']:
|
for season in result['data']:
|
||||||
for episode in result['data'][season]:
|
for episode in result['data'][season]:
|
||||||
episodename = result['data'][season][episode]['name']
|
episodename = result['data'][season][episode]['name']
|
||||||
if name is not None and fuzz.partial_ratio(name.lower(),
|
if name is not None and fuzz.ratio(name.lower(),
|
||||||
episodename) > 90:
|
episodename.lower()) > 85:
|
||||||
return (season, episode, episodename)
|
return (season, episode, episodename)
|
||||||
elif description is not None:
|
elif description is not None:
|
||||||
descriptionqueryresult = \
|
descriptionqueryresult = \
|
||||||
@@ -84,14 +85,6 @@ class Sickbeard:
|
|||||||
|
|
||||||
return (0, 0, '')
|
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):
|
def fixepisodetitle(self, showname, episodetitle):
|
||||||
"""
|
"""
|
||||||
Check to see if there is a prefix specified for the show. If there is,
|
Check to see if there is a prefix specified for the show. If there is,
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ class TVData:
|
|||||||
seasonfolder = "Season {0}".format(show.season)
|
seasonfolder = "Season {0}".format(show.season)
|
||||||
season = "S{0}".format(show.season)
|
season = "S{0}".format(show.season)
|
||||||
episode = "E{0}".format(show.episode)
|
episode = "E{0}".format(show.episode)
|
||||||
renamedfile = "{0}{1} - {2} - SD TV_.mpg".format(season, episode,
|
renamedfile = self.getoutputfilename(season, episode,
|
||||||
show.subtitle)
|
show.subtitle)
|
||||||
|
|
||||||
directory = self.getdirectory(show.title, seasonfolder,
|
directory = self.getdirectory(show.title, seasonfolder,
|
||||||
@@ -102,20 +102,17 @@ class TVData:
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
#==============================================================================
|
def getoutputfilename(self, season, episode, name):
|
||||||
# def __determinetargetfilename(directory, filename, inputfilename):
|
"""
|
||||||
# """
|
Get the output filename, and remove any illegal characters
|
||||||
# Determine the filename for the input file. If the path does not
|
"""
|
||||||
# exist, it is created.
|
|
||||||
# """
|
filename = "{0}{1} - {2} - SD TV_.mpg".format(season, episode, name)
|
||||||
#
|
|
||||||
# inputdir = os.path.join(directory, inputfilename[:-4])
|
for illegalcharacter in self.__settings.illegalcharacters():
|
||||||
#
|
filename = filename.replace(illegalcharacter, "")
|
||||||
# if not os.path.exists(inputdir):
|
|
||||||
# os.makedirs(inputdir)
|
return filename
|
||||||
#
|
|
||||||
# return os.path.join(inputdir, filename)
|
|
||||||
#==============================================================================
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def processepisode(inputfile, outputfile):
|
def processepisode(inputfile, outputfile):
|
||||||
@@ -137,4 +134,11 @@ class TVData:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
for showdata in showsdata:
|
for showdata in showsdata:
|
||||||
|
print "========================================"
|
||||||
|
print "Copying {0} to {1}".format(showdata.inputfile,
|
||||||
|
showdata.outputfile)
|
||||||
|
|
||||||
self.processepisode(showdata.inputfile, showdata.outputfile)
|
self.processepisode(showdata.inputfile, showdata.outputfile)
|
||||||
|
|
||||||
|
print "Finished copy"
|
||||||
|
print "========================================\n\n"
|
||||||
|
|||||||
3
pep8.sh
Executable file
3
pep8.sh
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
pep8 libemail.py TVEncoder.py libfilemanager.py libhandbrake.py libmythtv.py libsettings.py libsickbeard.py libtvdatasource.py libtvshow.py
|
||||||
3
pylint.sh
Executable file
3
pylint.sh
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
pylint TVEncoder.py libfilemanager.py libhandbrake.py libmythtv.py libsettings.py libsickbeard.py libtvdatasource.py libtvshow.py libemail.py
|
||||||
52
settings.cfg
52
settings.cfg
@@ -1,5 +1,10 @@
|
|||||||
TVRecordings = "/Volumes/TV Recordings/"
|
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"
|
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" ]
|
[ "MythTV" ]
|
||||||
address = 192.168.0.2
|
address = 192.168.0.2
|
||||||
@@ -13,46 +18,55 @@ HandbrakeCommand = "HandBrakeCLI", "--verbose", "-i", "SUBSTITUTE WITH INPUT FIL
|
|||||||
APIKey = 3678177136222bf5002be209220ccb20
|
APIKey = 3678177136222bf5002be209220ccb20
|
||||||
|
|
||||||
[ "Shows" ]
|
[ "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" ]]
|
[[ "Thomas the Tank Engine & Friends" ]]
|
||||||
InputDirectory = "/srv/storage2/files/VideoProcessing/Thomas/Input/"
|
InputDirectory = "%(VideoProcessingDir)sThomas/"
|
||||||
UnknownDirectory = "/srv/storage2/files/VideoProcessing/Unknown/Thomas/"
|
UnknownDirectory = "%(UnknownInput)sThomas/"
|
||||||
OutputDirectory = "/srv/storage2/videos/Kids/TV/Thomas The Tank Engine & Friends/"
|
OutputDirectory = "%(KidsTVDir)sThomas The Tank Engine & Friends/"
|
||||||
alias = "Thomas and Friends",
|
alias = "Thomas and Friends",
|
||||||
MythTvEpisodePrefix = ,
|
MythTvEpisodePrefix = ,
|
||||||
SickbeardPrefix = ""
|
SickbeardPrefix = ""
|
||||||
[[ "Chuggington" ]]
|
[[ "Chuggington" ]]
|
||||||
InputDirectory = "/srv/storage2/files/VideoProcessing/Chuggington/Input/"
|
InputDirectory = "%(VideoProcessingDir)sChuggington/"
|
||||||
UnknownDirectory = "/srv/storage2/files/VideoProcessing/Unknown/Chuggington/"
|
UnknownDirectory = "%(UnknownInput)sChuggington/"
|
||||||
OutputDirectory = "/srv/storage2/videos/Kids/TV/Chuggington/"
|
OutputDirectory = "%(KidsTVDir)sChuggington/"
|
||||||
alias = ,
|
alias = ,
|
||||||
MythTvEpisodePrefix = ,
|
MythTvEpisodePrefix = ,
|
||||||
SickbeardPrefix = ""
|
SickbeardPrefix = ""
|
||||||
[[ "Mike the Knight" ]]
|
[[ "Mike the Knight" ]]
|
||||||
InputDirectory = "/srv/storage2/files/VideoProcessing/MikeTheKnight/Input/"
|
InputDirectory = "%(VideoProcessingDir)sMikeTheKnight/"
|
||||||
UnknownDirectory = "/srv/storage2/files/VideoProcessing/Unknown/MikeTheKnight/"
|
UnknownDirectory = "%(UnknownInput)sMikeTheKnight/"
|
||||||
OutputDirectory = "/srv/storage2/videos/Kids/TV/Mike the Knight/"
|
OutputDirectory = "%(KidsTVDir)sMike the Knight/"
|
||||||
alias = ,
|
alias = ,
|
||||||
MythTvEpisodePrefix = "Mike the Knight and the ", Mike the Knight and "
|
MythTvEpisodePrefix = "Mike the Knight and the ", Mike the Knight and "
|
||||||
SickbeardPrefix = ""
|
SickbeardPrefix = ""
|
||||||
[[ "Octonauts" ]]
|
[[ "Octonauts" ]]
|
||||||
InputDirectory = "/srv/storage2/files/VideoProcessing/Octonauts/Input/"
|
InputDirectory = "%(VideoProcessingDir)sOctonauts/"
|
||||||
UnknownDirectory = "/srv/storage2/files/VideoProcessing/Unknown/Octonauts/"
|
UnknownDirectory = "%(UnknownInput)sOctonauts/"
|
||||||
OutputDirectory = "/srv/storage2/videos/Kids/TV/Octonauts/"
|
OutputDirectory = "%(KidsTVDir)sOctonauts/"
|
||||||
alias = "The Octonauts",
|
alias = "The Octonauts",
|
||||||
MythTvEpisodePrefix = "The Octonauts and ",
|
MythTvEpisodePrefix = "The Octonauts and ",
|
||||||
SickbeardPrefix = "The"
|
SickbeardPrefix = "The"
|
||||||
[[ "In the Night Garden" ]]
|
[[ "In the Night Garden" ]]
|
||||||
InputDirectory = "/srv/storage2/files/VideoProcessing/InTheNightGarden/Input/"
|
InputDirectory = "%(VideoProcessingDir)sInTheNightGarden/"
|
||||||
UnknownDirectory = "/srv/storage2/files/VideoProcessing/Unknown/InTheNightGarden/"
|
UnknownDirectory = "%(UnknownInput)sInTheNightGarden/"
|
||||||
OutputDirectory = "/srv/storage2/videos/Kids/TV/In The Night Garden/"
|
OutputDirectory = "%(KidsTVDir)sIn The Night Garden/"
|
||||||
alias = ,
|
alias = ,
|
||||||
MythTvEpisodePrefix = ,
|
MythTvEpisodePrefix = ,
|
||||||
SickbeardPrefix = ""
|
SickbeardPrefix = ""
|
||||||
[[ "Raa Raa! The Noisy Lion" ]]
|
[[ "Raa Raa! The Noisy Lion" ]]
|
||||||
InputDirectory = "/srv/storage2/files/VideoProcessing/RaaRaa/Input/"
|
InputDirectory = "%(VideoProcessingDir)sRaaRaa/"
|
||||||
UnknownDirectory = "/srv/storage2/files/VideoProcessing/Unknown/RaaRaa/"
|
UnknownDirectory = "%(UnknownInput)sRaaRaa/"
|
||||||
OutputDirectory = "/srv/storage2/videos/Kids/TV/Raa Raa the Noisy Lion/"
|
OutputDirectory = "%(KidsTVDir)sRaa Raa the Noisy Lion/"
|
||||||
|
alias = ,
|
||||||
|
MythTvEpisodePrefix = ,
|
||||||
|
SickbeardPrefix = ""
|
||||||
|
[[ "Fireman Sam" ]]
|
||||||
|
InputDirectory = "%(VideoProcessingDir)sFiremanSam/"
|
||||||
|
UnknownDirectory = "%(UnknownInput)sFiremanSam/"
|
||||||
|
OutputDirectory = "%(KidsTVDir)sFireman Sam/"
|
||||||
alias = ,
|
alias = ,
|
||||||
MythTvEpisodePrefix = ,
|
MythTvEpisodePrefix = ,
|
||||||
SickbeardPrefix = ""
|
SickbeardPrefix = ""
|
||||||
46
tests/TVEncodertest.py
Normal file
46
tests/TVEncodertest.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# -*- 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)
|
||||||
33
tests/emailtest.py
Normal file
33
tests/emailtest.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Created on Fri Jul 19 23:31:16 2013
|
||||||
|
|
||||||
|
@author: shanef
|
||||||
|
"""
|
||||||
|
|
||||||
|
from minimock import Mock, mock
|
||||||
|
import unittest
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
sys.path.insert(0, parentdir)
|
||||||
|
import libemail
|
||||||
|
from libsettings import EmailSettings
|
||||||
|
import smtplib
|
||||||
|
|
||||||
|
|
||||||
|
class libemailtest(unittest.TestCase):
|
||||||
|
def test_SendEmail(self):
|
||||||
|
mock("EmailSettings.getfromaddress", returns="from@email.com")
|
||||||
|
mock("EmailSettings.gettoaddress", returns="to@email.com")
|
||||||
|
mock("EmailSettings.getsmtpserver", returns="smtp.test")
|
||||||
|
mock("EmailSettings.getsmtpuser", returns="user")
|
||||||
|
mock("EmailSettings.getsmtppassword", returns="password")
|
||||||
|
smtplib.SMTP = Mock('smtplib.SMTP')
|
||||||
|
smtplib.SMTP.mock_returns = Mock('smtp_connection')
|
||||||
|
|
||||||
|
libemail.sendemail("test", "subject", "body")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
suite = unittest.TestLoader().loadTestsFromTestCase(libemailtest)
|
||||||
|
unittest.TextTestRunner(verbosity=2).run(suite)
|
||||||
@@ -8,9 +8,11 @@ Created on Fri Jul 5 14:12:26 2013
|
|||||||
import unittest
|
import unittest
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import minimock
|
||||||
|
from minimock import mock, Mock
|
||||||
parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
sys.path.insert(0, parentdir)
|
sys.path.insert(0, parentdir)
|
||||||
from libfilemanager import EncodeData
|
from libfilemanager import EncodeData, FileManager
|
||||||
|
|
||||||
|
|
||||||
class libfilemanagertest(unittest.TestCase):
|
class libfilemanagertest(unittest.TestCase):
|
||||||
@@ -24,6 +26,92 @@ class libfilemanagertest(unittest.TestCase):
|
|||||||
"{2}\n".format(showname, inputname, outputname)
|
"{2}\n".format(showname, inputname, outputname)
|
||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_EncodeDataCheckProblemsFileExists(self):
|
||||||
|
showname = "test show"
|
||||||
|
inputname = "test input"
|
||||||
|
outputname = "test_output.mkv"
|
||||||
|
data = EncodeData(showname, inputname, outputname)
|
||||||
|
mock("os.path.exists", returns=True)
|
||||||
|
|
||||||
|
result = data.checkproblems()
|
||||||
|
|
||||||
|
self.assertIn("FILE_EXISTS", result)
|
||||||
|
minimock.restore()
|
||||||
|
|
||||||
|
def test_EncodeDataCheckProblemsFile_Exists(self):
|
||||||
|
showname = "test show"
|
||||||
|
inputname = "test input"
|
||||||
|
outputname = "test_output_.mkv"
|
||||||
|
data = EncodeData(showname, inputname, outputname)
|
||||||
|
mock("os.path.exists", returns_iter=[False, True])
|
||||||
|
result = data.checkproblems()
|
||||||
|
self.assertIn("FILE_EXISTS", result)
|
||||||
|
minimock.restore()
|
||||||
|
|
||||||
|
def test_checkfileexistscaseinsensitive(self):
|
||||||
|
settings = Mock('libsettings.Settings')
|
||||||
|
filemanager = FileManager(settings)
|
||||||
|
|
||||||
|
mock("os.listdir", returns=["filename.test"])
|
||||||
|
|
||||||
|
result = filemanager.checkfileexists("/path/to/fiLename.test", False)
|
||||||
|
|
||||||
|
self.assertTrue(result)
|
||||||
|
minimock.restore()
|
||||||
|
|
||||||
|
def test_checkduplicateavi(self):
|
||||||
|
settings = Mock('libsettings.Settings')
|
||||||
|
filemanager = FileManager(settings)
|
||||||
|
|
||||||
|
os.walk = dummywalk
|
||||||
|
|
||||||
|
result = filemanager.checkduplicates("/path/to/S03E14 - Test - SD TV.mkv")
|
||||||
|
|
||||||
|
self.assertTrue(result)
|
||||||
|
minimock.restore()
|
||||||
|
|
||||||
|
def test_checkduplicatethomas(self):
|
||||||
|
settings = Mock('libsettings.Settings')
|
||||||
|
filemanager = FileManager(settings)
|
||||||
|
|
||||||
|
os.walk = thomaswalk
|
||||||
|
|
||||||
|
result = filemanager.checkduplicates("/path/to/S12E05 - Henry Gets It Wrong - SD TV.mkv")
|
||||||
|
|
||||||
|
self.assertTrue(result)
|
||||||
|
minimock.restore()
|
||||||
|
|
||||||
|
def test_checkduplicatenomatch(self):
|
||||||
|
settings = Mock('libsettings.Settings')
|
||||||
|
filemanager = FileManager(settings)
|
||||||
|
|
||||||
|
os.walk = dummywalk
|
||||||
|
|
||||||
|
result = filemanager.checkduplicates("/path/to/S03E13 - Test - SD TV.mkv")
|
||||||
|
|
||||||
|
self.assertFalse(result)
|
||||||
|
minimock.restore()
|
||||||
|
|
||||||
|
def test_checkduplicatesameextension(self):
|
||||||
|
settings = Mock('libsettings.Settings')
|
||||||
|
filemanager = FileManager(settings)
|
||||||
|
|
||||||
|
os.walk = dummywalk
|
||||||
|
|
||||||
|
result = filemanager.checkduplicates("/path/to/S03E14 - Test - SD TV.avi")
|
||||||
|
|
||||||
|
self.assertFalse(result)
|
||||||
|
minimock.restore()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def dummywalk(arg):
|
||||||
|
return [("/path/to/", [], ["S03E14 - Test - SD TV.avi"])]
|
||||||
|
|
||||||
|
def thomaswalk(arg):
|
||||||
|
return [(("/path/to/", [], ["S12E05 - Henry Gets It Wrong - Unknown.AVI"]))]
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
suite = unittest.TestLoader().loadTestsFromTestCase(libfilemanagertest)
|
suite = unittest.TestLoader().loadTestsFromTestCase(libfilemanagertest)
|
||||||
unittest.TextTestRunner(verbosity=2).run(suite)
|
unittest.TextTestRunner(verbosity=2).run(suite)
|
||||||
|
minimock.restore()
|
||||||
|
|||||||
@@ -4,3 +4,48 @@ Created on Fri Jul 5 14:12:38 2013
|
|||||||
|
|
||||||
@author: shanef
|
@author: shanef
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from minimock import Mock
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
sys.path.insert(0, parentdir)
|
||||||
|
import libsickbeard
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
|
||||||
|
class SickbeardTest(unittest.TestCase):
|
||||||
|
def test_findepisodeCloseSubtitle(self):
|
||||||
|
settings = Mock('libsettings.Settings')
|
||||||
|
settings.sickbeardaddress.mock_returns = "test"
|
||||||
|
settings.sickbeardport.mock_returns = "123"
|
||||||
|
settings.sickbeardapikey.mock_returns = "test"
|
||||||
|
|
||||||
|
urllib.urlopen = dummy_urlopen
|
||||||
|
|
||||||
|
sickbeard = libsickbeard.Sickbeard(settings)
|
||||||
|
|
||||||
|
result = sickbeard.findepisode("78949", "Splish, Splash, Splosh")
|
||||||
|
|
||||||
|
self.assertEqual("13", result[0])
|
||||||
|
self.assertEqual("15", result[1])
|
||||||
|
self.assertEqual("Splish, Splash, Splosh!", result[2])
|
||||||
|
|
||||||
|
|
||||||
|
def dummy_urlopen(arg):
|
||||||
|
class TmpClass:
|
||||||
|
def read(arg):
|
||||||
|
jsonresult = '{ "data": {"13": { "15": { "airdate": "2010-02-12", ' \
|
||||||
|
'"name": "Splish, Splash, Splosh!", "quality": "N/A", ' \
|
||||||
|
'"status": "Wanted" } } }, "message": "", ' \
|
||||||
|
'"result": "success" }'
|
||||||
|
|
||||||
|
return jsonresult
|
||||||
|
|
||||||
|
return TmpClass()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
suite = unittest.TestLoader().loadTestsFromTestCase(SickbeardTest)
|
||||||
|
unittest.TextTestRunner(verbosity=2).run(suite)
|
||||||
|
|||||||
38
tests/libtvdatasourcetest.py
Normal file
38
tests/libtvdatasourcetest.py
Normal file
@@ -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)
|
||||||
Reference in New Issue
Block a user