Fixed a bunch of issues that where present in TorrentToMedia and our config class including the migration code.

Added in manual run for TorrentToMedia.

All autoProcessing scripts now auto-detect the correct section based on sub-section.

NzbDrone support is 90% done but is not functional ... YET!

Lots more I'm sure but I wanted this released for those that where having issues.
This commit is contained in:
echel0n 2014-04-07 01:42:55 -07:00
parent 0ce4bb601d
commit 7d4ccf53cc
17 changed files with 715 additions and 305 deletions

View file

@ -20,7 +20,7 @@ from nzbtomedia.extractor import extractor
from nzbtomedia.nzbToMediaAutoFork import autoFork from nzbtomedia.nzbToMediaAutoFork import autoFork
from nzbtomedia.nzbToMediaConfig import config from nzbtomedia.nzbToMediaConfig import config
from nzbtomedia.nzbToMediaUtil import category_search, safeName, is_sample, copy_link, WakeUp, parse_args, flatten, \ from nzbtomedia.nzbToMediaUtil import category_search, safeName, is_sample, copy_link, WakeUp, parse_args, flatten, \
nzbtomedia_configure_logging nzbtomedia_configure_logging, get_dirnames
from nzbtomedia.synchronousdeluge.client import DelugeClient from nzbtomedia.synchronousdeluge.client import DelugeClient
from nzbtomedia.utorrent.client import UTorrentClient from nzbtomedia.utorrent.client import UTorrentClient
from nzbtomedia.transmissionrpc.client import Client as TransmissionClient from nzbtomedia.transmissionrpc.client import Client as TransmissionClient
@ -48,19 +48,16 @@ def main(inputDirectory, inputName, inputCategory, inputHash, inputID):
Logger.debug("MAIN: Determined Directory: %s | Name: %s | Category: %s", inputDirectory, inputName, inputCategory) Logger.debug("MAIN: Determined Directory: %s | Name: %s | Category: %s", inputDirectory, inputName, inputCategory)
if inputCategory in sections["SickBeard"]: if config.issubsection(inputCategory,["SickBeard","NzbDrone"]):
fork, fork_params = autoFork("SickBeard", inputCategory) Logger.info("MAIN: Calling autoProcessTV to post-process: %s",inputName)
Torrent_NoLink = int(config()["SickBeard"][inputCategory]["Torrent_NoLink"]) # 0 result = autoProcessTV().processEpisode(inputDirectory, inputName, 0, clientAgent=clientAgent, inputCategory=inputCategory)
if fork in config.SICKBEARD_TORRENT and Torrent_NoLink == 1: if result != 0:
Logger.info("MAIN: Calling SickBeard's %s branch to post-process: %s",fork ,inputName) Logger.info("MAIN: A problem was reported in the autoProcessTV script.")
result = autoProcessTV().processEpisode(inputDirectory, inputName, 0) Logger.info("MAIN: All done.")
if result != 0: sys.exit()
Logger.info("MAIN: A problem was reported in the autoProcess* script.")
Logger.info("MAIN: All done.")
sys.exit()
outputDestination = "" outputDestination = ""
for section, category in sections.items(): for category in categories:
if category == inputCategory: if category == inputCategory:
if os.path.basename(inputDirectory) == inputName and os.path.isdir(inputDirectory): if os.path.basename(inputDirectory) == inputName and os.path.isdir(inputDirectory):
Logger.info("MAIN: Download is a directory") Logger.info("MAIN: Download is a directory")
@ -72,6 +69,7 @@ def main(inputDirectory, inputName, inputCategory, inputHash, inputID):
break break
else: else:
continue continue
if outputDestination == "": if outputDestination == "":
if inputCategory == "": if inputCategory == "":
inputCategory = "UNCAT" inputCategory = "UNCAT"
@ -83,7 +81,7 @@ def main(inputDirectory, inputName, inputCategory, inputHash, inputID):
outputDestination = os.path.normpath(os.path.join(outputDirectory, inputCategory, os.path.splitext(safeName(inputName))[0])) outputDestination = os.path.normpath(os.path.join(outputDirectory, inputCategory, os.path.splitext(safeName(inputName))[0]))
Logger.info("MAIN: Output directory set to: %s", outputDestination) Logger.info("MAIN: Output directory set to: %s", outputDestination)
processOnly = list(chain.from_iterable(sections.values())) processOnly = list(chain.from_iterable(subsections.values()))
if not "NONE" in user_script_categories: # if None, we only process the 5 listed. if not "NONE" in user_script_categories: # if None, we only process the 5 listed.
if "ALL" in user_script_categories: # All defined categories if "ALL" in user_script_categories: # All defined categories
processOnly = categories processOnly = categories
@ -130,14 +128,14 @@ def main(inputDirectory, inputName, inputCategory, inputHash, inputID):
Logger.debug("MAIN: Scanning files in directory: %s", inputDirectory) Logger.debug("MAIN: Scanning files in directory: %s", inputDirectory)
noFlatten.extend(list(chain.from_iterable(config.get_sections(["HeadPhones"]).values()))) # Make sure we preserve folder structure for HeadPhones. if config.issubsection(inputCategory, "HeadPhones"):
noFlatten.extend(list(chain.from_iterable(config()[section].sections))) # Make sure we preserve folder structure for HeadPhones.
outputDestinationMaster = outputDestination # Save the original, so we can change this within the loop below, and reset afterwards. outputDestinationMaster = outputDestination # Save the original, so we can change this within the loop below, and reset afterwards.
now = datetime.datetime.now() now = datetime.datetime.now()
for dirpath, dirnames, filenames in os.walk(inputDirectory): for dirpath, dirnames, filenames in os.walk(inputDirectory):
Logger.debug("MAIN: Found %s files in %s", str(len(filenames)), dirpath) Logger.debug("MAIN: Found %s files in %s", str(len(filenames)), dirpath)
for file in filenames: for file in filenames:
filePath = os.path.join(dirpath, file) filePath = os.path.join(dirpath, file)
fileName, fileExtension = os.path.splitext(file) fileName, fileExtension = os.path.splitext(file)
if inputCategory in noFlatten: if inputCategory in noFlatten:
@ -172,7 +170,7 @@ def main(inputDirectory, inputName, inputCategory, inputHash, inputID):
continue # This file has not been recently moved or created, skip it continue # This file has not been recently moved or created, skip it
if fileExtension in mediaContainer: # If the file is a video file if fileExtension in mediaContainer: # If the file is a video file
if is_sample(filePath, inputName, minSampleSize, SampleIDs) and not inputCategory in config.get_sections(["HeadPhones"]).values(): # Ignore samples if is_sample(filePath, inputName, minSampleSize, SampleIDs) and not config.issubsection(inputCategory, ["HeadPhones"]): # Ignore samples
Logger.info("MAIN: Ignoring sample file: %s ", filePath) Logger.info("MAIN: Ignoring sample file: %s ", filePath)
continue continue
else: else:
@ -208,7 +206,7 @@ def main(inputDirectory, inputName, inputCategory, inputHash, inputID):
except: except:
Logger.exception("MAIN: Extraction failed for: %s", file) Logger.exception("MAIN: Extraction failed for: %s", file)
continue continue
elif not inputCategory in list(chain.from_iterable(config.get_sections(['CouchPotato','SickBeard']).values())): #process all for non-video categories. elif not config.issubsection(inputCategory,['CouchPotato','SickBeard','NzbDrone']): #process all for non-video categories.
Logger.info("MAIN: Found file %s for category %s", filePath, inputCategory) Logger.info("MAIN: Found file %s for category %s", filePath, inputCategory)
copy_link(filePath, targetDirectory, useLink, outputDestination) copy_link(filePath, targetDirectory, useLink, outputDestination)
copy_list.append([filePath, os.path.join(outputDestination, file)]) copy_list.append([filePath, os.path.join(outputDestination, file)])
@ -221,8 +219,8 @@ def main(inputDirectory, inputName, inputCategory, inputHash, inputID):
if not inputCategory in noFlatten: #don't flatten hp in case multi cd albums, and we need to copy this back later. if not inputCategory in noFlatten: #don't flatten hp in case multi cd albums, and we need to copy this back later.
flatten(outputDestination) flatten(outputDestination)
# Now check if movie files exist in destination: # Now check if video files exist in destination:
if inputCategory in list(chain.from_iterable(config.get_sections(['CouchPotato','SickBeard']).values())): if config.issubsection(inputCategory,["SickBeard","NzbDrone"]):
for dirpath, dirnames, filenames in os.walk(outputDestination): for dirpath, dirnames, filenames in os.walk(outputDestination):
for file in filenames: for file in filenames:
filePath = os.path.join(dirpath, file) filePath = os.path.join(dirpath, file)
@ -242,12 +240,12 @@ def main(inputDirectory, inputName, inputCategory, inputHash, inputID):
else: else:
Logger.debug("MAIN: Found %s media files in output. %s were found in input", str(video2), str(video)) Logger.debug("MAIN: Found %s media files in output. %s were found in input", str(video2), str(video))
processCategories = list(chain.from_iterable(sections.values())) processCategories = list(chain.from_iterable(subsections.values()))
if (inputCategory in user_script_categories and not "NONE" in user_script_categories) or ("ALL" in user_script_categories and not inputCategory in processCategories): if (inputCategory in user_script_categories and not "NONE" in user_script_categories) or ("ALL" in user_script_categories and not inputCategory in processCategories):
Logger.info("MAIN: Processing user script %s.", user_script) Logger.info("MAIN: Processing user script %s.", user_script)
result = external_script(outputDestination,inputName,inputCategory) result = external_script(outputDestination,inputName,inputCategory)
elif status == int(0) or (inputCategory in list(chain.from_iterable(config.get_sections(['HeadPhones','Mylar','Gamez']).values()))): # if movies linked/extracted or for other categories. elif status == int(0) or (config.issubsection(inputCategory,['HeadPhones','Mylar','Gamez'])): # if movies linked/extracted or for other categories.
Logger.debug("MAIN: Calling autoProcess script for successful download.") Logger.debug("MAIN: Calling autoProcess script for successful download.")
status = int(0) # hp, my, gz don't support failed. status = int(0) # hp, my, gz don't support failed.
else: else:
@ -255,21 +253,24 @@ def main(inputDirectory, inputName, inputCategory, inputHash, inputID):
sys.exit(-1) sys.exit(-1)
result = 0 result = 0
if inputCategory in sections['CouchPotato'].values(): if config.issubsection(inputCategory,['CouchPotato']):
Logger.info("MAIN: Calling CouchPotatoServer to post-process: %s", inputName) Logger.info("MAIN: Calling CouchPotato:" + inputCategory + " to post-process: %s", inputName)
download_id = inputHash download_id = inputHash
result = autoProcessMovie().process(outputDestination, inputName, status, clientAgent, download_id, inputCategory) result = autoProcessMovie().process(outputDestination, inputName, status, clientAgent, download_id, inputCategory)
elif inputCategory in sections['SickBeard'].values(): elif config.issubsection(inputCategory,['SickBeard']):
Logger.info("MAIN: Calling Sick-Beard to post-process: %s", inputName) Logger.info("MAIN: Calling Sick-Beard:" + inputCategory + " to post-process: %s", inputName)
result = autoProcessTV().processEpisode(outputDestination, inputName, status, clientAgent, inputCategory) result = autoProcessTV().processEpisode(outputDestination, inputName, status, clientAgent, inputCategory)
elif inputCategory in sections['HeadPhones'].values(): elif config.issubsection(inputCategory,['NzbDrone']):
Logger.info("MAIN: Calling HeadPhones to post-process: %s", inputName) Logger.info("MAIN: Calling NzbDrone:" + inputCategory + " to post-process: %s", inputName)
result = autoProcessTV().processEpisode(outputDestination, inputName, status, clientAgent, inputCategory)
elif config.issubsection(inputCategory,['HeadPhones']):
Logger.info("MAIN: Calling HeadPhones:" + inputCategory + " to post-process: %s", inputName)
result = autoProcessMusic().process(inputDirectory, inputName, status, clientAgent, inputCategory) result = autoProcessMusic().process(inputDirectory, inputName, status, clientAgent, inputCategory)
elif inputCategory in sections['Mylar'].values(): elif config.issubsection(inputCategory,['Mylar']):
Logger.info("MAIN: Calling Mylar to post-process: %s", inputName) Logger.info("MAIN: Calling Mylar:" + inputCategory + " to post-process: %s", inputName)
result = autoProcessComics().processEpisode(outputDestination, inputName, status, clientAgent, inputCategory) result = autoProcessComics().processEpisode(outputDestination, inputName, status, clientAgent, inputCategory)
elif inputCategory in sections['Gamez'].values(): elif config.issubsection(inputCategory,['Gamez']):
Logger.info("MAIN: Calling Gamez to post-process: %s", inputName) Logger.info("MAIN: Calling Gamez:" + inputCategory + " to post-process: %s", inputName)
result = autoProcessGames().process(outputDestination, inputName, status, clientAgent, inputCategory) result = autoProcessGames().process(outputDestination, inputName, status, clientAgent, inputCategory)
if result == 1: if result == 1:
@ -434,8 +435,8 @@ if __name__ == "__main__":
minSampleSize = int(config()["Extensions"]["minSampleSize"]) # 200 (in MB) minSampleSize = int(config()["Extensions"]["minSampleSize"]) # 200 (in MB)
SampleIDs = (config()["Extensions"]["SampleIDs"]) # sample,-s. SampleIDs = (config()["Extensions"]["SampleIDs"]) # sample,-s.
sections = config.get_sections(["CouchPotato", "SickBeard", "HeadPhones", "Mylar", "Gamez"]) subsections = config.get_subsections(["CouchPotato", "SickBeard", "NzbDrone", "HeadPhones", "Mylar", "Gamez"])
categories += list(chain.from_iterable(sections.values())) categories += list(chain.from_iterable(subsections.values()))
user_script_categories = config()["UserScript"]["user_script_categories"] # NONE user_script_categories = config()["UserScript"]["user_script_categories"] # NONE
if not "NONE" in user_script_categories: if not "NONE" in user_script_categories:
@ -460,4 +461,13 @@ if __name__ == "__main__":
Logger.exception("MAIN: There was a problem loading variables") Logger.exception("MAIN: There was a problem loading variables")
sys.exit(-1) sys.exit(-1)
main(inputDirectory, inputName, inputCategory, inputHash, inputID) # check if this is a manual run
if inputDirectory is None:
for section, subsection in subsections.iteritems():
for category in subsection:
dirNames = get_dirnames(section, category)
Logger.info("MAIN: TorrentToMedia running %s:%s as a manual run...", section, category)
for dirName in dirNames:
main(dirName, os.path.basename(dirName), category, inputHash, inputID)
else:
main(inputDirectory, inputName, inputCategory, inputHash, inputID)

View file

@ -5,6 +5,7 @@
#### autoProcessing for Movies #### autoProcessing for Movies
#### movie - category that gets called for post-processing with CPS #### movie - category that gets called for post-processing with CPS
[[movie]] [[movie]]
enabled = 0
apikey = apikey =
host = localhost host = localhost
port = 5050 port = 5050
@ -24,6 +25,7 @@
#### autoProcessing for TV Series #### autoProcessing for TV Series
#### tv - category that gets called for post-processing with SB #### tv - category that gets called for post-processing with SB
[[tv]] [[tv]]
enabled = 0
host = localhost host = localhost
port = 8081 port = 8081
username = username =
@ -40,10 +42,30 @@
Torrent_NoLink = 0 Torrent_NoLink = 0
process_method = process_method =
[NzbDrone]
#### autoProcessing for TV Series
#### ndCategory - category that gets called for post-processing with NzbDrone
[[tv]]
enabled = 0
host = localhost
port = 8989
username =
password =
###### ADVANCED USE - ONLY EDIT IF YOU KNOW WHAT YOU'RE DOING ######
web_root =
ssl = 0
delay = 0
TimePerGiB = 60
watch_dir =
delete_failed = 0
nzbExtractionBy = Downloader
Torrent_NoLink = 0
[HeadPhones] [HeadPhones]
#### autoProcessing for Music #### autoProcessing for Music
#### music - category that gets called for post-processing with HP #### music - category that gets called for post-processing with HP
[[music]] [[music]]
enabled = 0
apikey = apikey =
host = localhost host = localhost
port = 8181 port = 8181
@ -58,6 +80,7 @@
#### autoProcessing for Comics #### autoProcessing for Comics
#### comics - category that gets called for post-processing with Mylar #### comics - category that gets called for post-processing with Mylar
[[comics]] [[comics]]
enabled = 0
host = localhost host = localhost
port= 8090 port= 8090
username= username=
@ -71,6 +94,7 @@
#### autoProcessing for Games #### autoProcessing for Games
#### games - category that gets called for post-processing with Gamez #### games - category that gets called for post-processing with Gamez
[[games]] [[games]]
enabled = 0
apikey = apikey =
host = localhost host = localhost
port = 8085 port = 8085
@ -172,4 +196,29 @@
[passwords] [passwords]
# enter the full path to a text file containing passwords to be used for extraction attempts. # enter the full path to a text file containing passwords to be used for extraction attempts.
# In the passwords file, every password should be on a new line # In the passwords file, every password should be on a new line
PassWordFile = PassWordFile =
# Logging configuration
[loggers]
keys = root
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = NOTSET
handlers = console
qualname =
[handler_console]
class = StreamHandler
args = (sys.stdout,)
level = INFO
formatter = generic
[formatter_generic]
format = %(asctime)s|%(levelname)-7.7s %(message)s
datefmt = %H:%M:%S

23
logging.cfg.sample Normal file
View file

@ -0,0 +1,23 @@
[loggers]
keys = root
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = NOTSET
handlers = console
qualname =
[handler_console]
class = StreamHandler
args = (sys.stdout,)
level = INFO
formatter = generic
[formatter_generic]
format = %(asctime)s|%(levelname)-7.7s %(message)s
datefmt = %H:%M:%S

View file

@ -147,9 +147,6 @@ if config.migrate():
else: else:
sys.exit(-1) sys.exit(-1)
# setup sections and categories
sections = config.get_sections(["CouchPotato"])
WakeUp() WakeUp()
# NZBGet V11+ # NZBGet V11+
@ -235,16 +232,20 @@ elif len(sys.argv) >= config.SABNZB_0717_NO_OF_ARGUMENTS:
else: else:
result = 0 result = 0
Logger.warn("MAIN: Invalid number of arguments received from client.") # init sub-sections
Logger.info("MAIN: Running autoProcessMovie as a manual run...") subsections = config.get_subsections(["CouchPotato"])
for section, categories in sections.items(): Logger.warn("MAIN: Invalid number of arguments received from client.")
for category in categories: for section, subsection in subsections.items():
for category in subsection:
dirNames = get_dirnames(section, category) dirNames = get_dirnames(section, category)
for dirName in dirNames: for dirName in dirNames:
Logger.info("MAIN: Calling " + section + ":" + category + " to post-process: %s", dirName) Logger.info("MAIN: nzbToCouchPotato running %s:%s as a manual run...", section, subsection)
results = autoProcessMovie().process(dirName, dirName, 0, inputCategory=category) results = autoProcessMovie(dirName, inputName=os.path.basename(dirName), status=0, clientAgent="manual",
if results != 0:result = results inputCategory=category)
if results != 0:
result = results
Logger.info("MAIN: A problem was reported when trying to manually run %s:%s.", section, subsection)
if result == 0: if result == 0:
Logger.info("MAIN: The autoProcessMovie script completed successfully.") Logger.info("MAIN: The autoProcessMovie script completed successfully.")

View file

@ -83,7 +83,7 @@ else:
sys.exit(-1) sys.exit(-1)
# gamez category # gamez category
sections = config.get_sections(['Gamez']) sections = config.get_subsections(['Gamez'])
WakeUp() WakeUp()
@ -162,16 +162,19 @@ elif len(sys.argv) >= config.SABNZB_0717_NO_OF_ARGUMENTS:
else: else:
result = 0 result = 0
Logger.warn("MAIN: Invalid number of arguments received from client. Exiting") # init sub-sections
Logger.info("MAIN: Running autoProcessGames as a manual run...") subsections = config.get_subsections(["Gamez"])
for section, categories in sections.items(): Logger.warn("MAIN: Invalid number of arguments received from client.")
for category in categories: for section, subsection in subsections.items():
for category in subsection:
dirNames = get_dirnames(section, category) dirNames = get_dirnames(section, category)
for dirName in dirNames: for dirName in dirNames:
Logger.info("MAIN: Calling " + section + ":" + category + " to post-process: %s", dirName) Logger.info("MAIN: nzbToGamez running %s:%s as a manual run...", section, category)
results = autoProcessGames().process(dirName, dirName, 0, inputCategory=category) results = autoProcessGames(dirName, inputName=os.path.basename(dirName), status=0, clientAgent = "manual", inputCategory=category)
if results != 0:result = results if results != 0:
result = results
Logger.info("MAIN: A problem was reported when trying to manually run %s:%s.", section, category)
if result == 0: if result == 0:
Logger.info("MAIN: The autoProcessGames script completed successfully.") Logger.info("MAIN: The autoProcessGames script completed successfully.")

View file

@ -95,9 +95,6 @@ if config.migrate():
else: else:
sys.exit(-1) sys.exit(-1)
# headphones category
sections = config.get_sections(["HeadPhones"])
WakeUp() WakeUp()
# NZBGet V11+ # NZBGet V11+
@ -175,16 +172,20 @@ elif len(sys.argv) >= config.SABNZB_0717_NO_OF_ARGUMENTS:
else: else:
result = 0 result = 0
Logger.warn("MAIN: Invalid number of arguments received from client.") # init sub-sections
Logger.info("MAIN: Running autoProcessMusic as a manual run...") subsections = config.get_subsections(["HeadPhones"])
for section, categories in sections.items(): Logger.warn("MAIN: Invalid number of arguments received from client.")
for category in categories: for section, subsection in subsections.items():
for category in subsection:
dirNames = get_dirnames(section, category) dirNames = get_dirnames(section, category)
for dirName in dirNames: for dirName in dirNames:
Logger.info("MAIN: Calling " + section + ":" + category + " to post-process: %s", dirName) Logger.info("MAIN: nzbToHeadPhones running %s:%s as a manual run...", section, subsection)
results = autoProcessMusic().process(dirName, dirName, 0, inputCategory=category) results = autoProcessMusic(dirName, inputName=os.path.basename(dirName), status=0, clientAgent="manual",
if results != 0:result = results inputCategory=category)
if results != 0:
result = results
Logger.info("MAIN: A problem was reported when trying to manually run %s:%s.", section, subsection)
if result == 0: if result == 0:
Logger.info("MAIN: The autoProcessMusic script completed successfully.") Logger.info("MAIN: The autoProcessMusic script completed successfully.")

View file

@ -9,7 +9,7 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'lib'
############################################################################## ##############################################################################
### NZBGET POST-PROCESSING SCRIPT ### ### NZBGET POST-PROCESSING SCRIPT ###
# Post-Process to CouchPotato, SickBeard, Mylar, Gamez, HeadPhones. # Post-Process to CouchPotato, SickBeard, NzbDrone, Mylar, Gamez, HeadPhones.
# #
# This script sends the download to your automated media management servers. # This script sends the download to your automated media management servers.
# #
@ -88,7 +88,7 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'lib'
#sbport=8081 #sbport=8081
# SickBeard username. # SickBeard username.
#sbusername= #sbusername=
# SickBeard password. # SickBeard password.
#sbpassword= #sbpassword=
@ -133,6 +133,35 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'lib'
# set this to move, copy, hardlin, symlink as appropriate if you want to over-ride SB defaults. Leave blank to use SB default. # set this to move, copy, hardlin, symlink as appropriate if you want to over-ride SB defaults. Leave blank to use SB default.
#sbprocess_method= #sbprocess_method=
## NzbDrone
# NzbDrone script category.
#
# category that gets called for post-processing with NzbDrone.
#ndCategory=tv
# NzbDrone host.
#ndHost=localhost
# NzbDrone port.
#ndPort=8989
# NzbDrone API key.
#ndAPIKey=
# NzbDrone uses SSL (0, 1).
#
# Set to 1 if using SSL, else set to 0.
#ndSSL=0
# NzbDrone web root.
#
# set this if using a reverse proxy.
#ndWebRoot=
# Prefer NzbDrone if categories clash (0, 1).
#ndPrefer=0
## HeadPhones ## HeadPhones
# HeadPhones script category. # HeadPhones script category.
@ -183,7 +212,7 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'lib'
#myport=8090 #myport=8090
# Mylar username. # Mylar username.
#myusername= #myusername=
# Mylar password. # Mylar password.
#mypassword= #mypassword=
@ -245,7 +274,7 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'lib'
# ignore extensions # ignore extensions
# #
# list of extensions that won't be transcoded. # list of extensions that won't be transcoded.
#ignoreExtensions=.avi,.mkv #ignoreExtensions=.avi,.mkv
# ffmpeg output settings. # ffmpeg output settings.
@ -287,58 +316,24 @@ from nzbtomedia.nzbToMediaUtil import nzbtomedia_configure_logging, WakeUp, get_
# post-processing # post-processing
def process(nzbDir, inputName=None, status=0, clientAgent='manual', download_id=None, inputCategory=None): def process(nzbDir, inputName=None, status=0, clientAgent='manual', download_id=None, inputCategory=None):
if inputCategory in sections["CouchPotato"]:
if isinstance(nzbDir, list): if section in ["CouchPotato"]:
for dirName in nzbDir: Logger.info("MAIN: Calling CouchPotatoServer to post-process: %s", inputName)
Logger.info("MAIN: Calling CouchPotatoServer to post-process: %s", inputName) return autoProcessMovie().process(nzbDir, inputName, status, clientAgent, download_id, inputCategory)
result = autoProcessMovie().process(dirName, dirName, status, clientAgent, download_id, inputCategory) elif section in ["SickBeard", "NzbDrone"]:
if result != 0: Logger.info("MAIN: Calling Sick-Beard to post-process: %s", inputName)
return result return autoProcessTV().processEpisode(nzbDir, inputName, status, clientAgent, inputCategory)
else: elif section in ["HeadPhones"]:
Logger.info("MAIN: Calling CouchPotatoServer to post-process: %s", inputName) Logger.info("MAIN: Calling HeadPhones to post-process: %s", inputName)
return autoProcessMovie().process(nzbDir, inputName, status, clientAgent, download_id, inputCategory) return autoProcessMusic().process(nzbDir, inputName, status, clientAgent, inputCategory)
elif inputCategory in sections["SickBeard"]: elif section in ["Mylar"]:
if isinstance(nzbDir, list): Logger.info("MAIN: Calling Mylar to post-process: %s", inputName)
for dirName in nzbDir: return autoProcessComics().processEpisode(nzbDir, inputName, status, clientAgent, inputCategory)
Logger.info("MAIN: Calling Sick-Beard to post-process: %s", inputName) elif section in ["Gamez"]:
result = autoProcessTV().processEpisode(dirName, dirName, status, clientAgent, inputCategory) Logger.info("MAIN: Calling Gamez to post-process: %s", inputName)
if result !=0: return autoProcessGames().process(nzbDir, inputName, status, clientAgent, inputCategory)
return result
else:
Logger.info("MAIN: Calling Sick-Beard to post-process: %s", inputName)
return autoProcessTV().processEpisode(nzbDir, inputName, status, clientAgent, inputCategory)
elif inputCategory in sections["HeadPhones"]:
if isinstance(nzbDir, list):
for dirName in nzbDir:
Logger.info("MAIN: Calling Headphones to post-process: %s", dirName)
result = autoProcessMusic().process(dirName, dirName, status, clientAgent, inputCategory)
if result != 0:
return result
else:
Logger.info("MAIN: Calling HeadPhones to post-process: %s", inputName)
return autoProcessMusic().process(nzbDir, inputName, status, clientAgent, inputCategory)
elif inputCategory in sections["Mylar"]:
if isinstance(nzbDir, list):
for dirName in nzbDir:
Logger.info("MAIN: Calling Mylar to post-process: %s", dirName)
result = autoProcessComics().processEpisode(dirName, dirName, status, clientAgent, inputCategory)
if result != 0:
return result
else:
Logger.info("MAIN: Calling Mylar to post-process: %s", inputName)
return autoProcessComics().processEpisode(nzbDir, inputName, status, clientAgent, inputCategory)
elif inputCategory in sections["Gamez"]:
if isinstance(nzbDir, list):
for dirName in nzbDir:
Logger.info("MAIN: Calling Gamez to post-process: %s", dirName)
result = autoProcessGames().process(dirName, dirName, status, clientAgent, inputCategory)
if result != 0:
return result
else:
Logger.info("MAIN: Calling Gamez to post-process: %s", inputName)
return autoProcessGames().process(nzbDir, inputName, status, clientAgent, inputCategory)
else: else:
Logger.warning("MAIN: The download category %s does not match any category defined in autoProcessMedia.cfg. Exiting.", inputCategory) Logger.warning("MAIN: We could not find the section %s with a download category of %s in your autoProcessMedia.cfg. Exiting.", section, inputCategory)
return -1 return -1
######################################################################################################################## ########################################################################################################################
@ -360,9 +355,6 @@ else:
print("Unable to find " + config.CONFIG_FILE + " or " + config.SAMPLE_CONFIG_FILE) print("Unable to find " + config.CONFIG_FILE + " or " + config.SAMPLE_CONFIG_FILE)
sys.exit(-1) sys.exit(-1)
# setup sections and categories
sections = config.get_sections(["CouchPotato","SickBeard","HeadPhones","Mylar","Gamez"])
WakeUp() WakeUp()
# Post-Processing Result # Post-Processing Result
@ -450,15 +442,20 @@ elif len(sys.argv) >= config.SABNZB_0717_NO_OF_ARGUMENTS:
else: else:
result = 0 result = 0
# init sub-sections
subsections = config.get_subsections(["CouchPotato", "SickBeard", "NzbDrone", "HeadPhones", "Mylar", "Gamez"])
Logger.warn("MAIN: Invalid number of arguments received from client.") Logger.warn("MAIN: Invalid number of arguments received from client.")
for section, categories in sections.items(): for section, subsection in subsections.items():
for category in categories: for category in subsection:
dirNames = get_dirnames(section, category) dirNames = get_dirnames(section, category)
Logger.info("MAIN: Running " + section + ":" + category + " as a manual run...") for dirName in dirNames:
results = process(dirNames, inputName=dirNames, status=0, inputCategory=category, clientAgent = "manual") Logger.info("MAIN: nzbToMedia running %s:%s as a manual run...", section, subsection)
if results != 0: results = process(dirName, inputName=os.path.basename(dirName), status=0, clientAgent="manual",
result = results inputCategory=category)
Logger.info("MAIN: A problem was reported when trying to manually run " + section + ":" + category + ".") if results != 0:
result = results
Logger.info("MAIN: A problem was reported when trying to manually run %s:%s.", section, subsection)
if result == 0: if result == 0:
Logger.info("MAIN: The nzbToMedia script completed successfully.") Logger.info("MAIN: The nzbToMedia script completed successfully.")

View file

@ -87,9 +87,6 @@ if config.migrate():
else: else:
sys.exit(-1) sys.exit(-1)
# mylar category
sections = config.get_sections(["Mylar"])
WakeUp() WakeUp()
# NZBGet V11+ # NZBGet V11+
@ -167,16 +164,20 @@ elif len(sys.argv) >= config.SABNZB_0717_NO_OF_ARGUMENTS:
else: else:
result = 0 result = 0
Logger.warn("MAIN: Invalid number of arguments received from client.") # init sub-sections
Logger.info("MAIN: Running autoProcessComics as a manual run...") subsections = config.get_subsections(["Mylar"])
for section, categories in sections.items(): Logger.warn("MAIN: Invalid number of arguments received from client.")
for category in categories: for section, subsection in subsections.items():
for category in subsection:
dirNames = get_dirnames(section, category) dirNames = get_dirnames(section, category)
for dirName in dirNames: for dirName in dirNames:
Logger.info("MAIN: Calling " + section + ":" + category + " to post-process: %s", dirName) Logger.info("MAIN: nzbToMylar running %s:%s as a manual run...", section, subsection)
results = autoProcessComics().processEpisode(dirName, dirName, 0, inputCategory=category) results = autoProcessComics(dirName, inputName=os.path.basename(dirName), status=0, clientAgent="manual",
if results != 0:result = results inputCategory=category)
if results != 0:
result = results
Logger.info("MAIN: A problem was reported when trying to manually run %s:%s.", section, subsection)
if result == 0: if result == 0:
Logger.info("MAIN: The autoProcessComics script completed successfully.") Logger.info("MAIN: The autoProcessComics script completed successfully.")

221
nzbToNzbDrone.py Executable file
View file

@ -0,0 +1,221 @@
#!/usr/bin/env python
# adds lib directory to system path
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'lib')))
#
##############################################################################
### NZBGET POST-PROCESSING SCRIPT ###
# Post-Process to NzbDrone.
#
# This script sends the download to your automated media management servers.
#
# NOTE: This script requires Python to be installed on your system.
##############################################################################
### OPTIONS ###
## NzbDrone
# NzbDrone script category.
#
# category that gets called for post-processing with NzbDrone.
#ndCategory=tv
# NzbDrone host.
#ndHost=localhost
# NzbDrone port.
#ndPort=8989
# NzbDrone API key.
#ndAPIKey=
# NzbDrone uses SSL (0, 1).
#
# Set to 1 if using SSL, else set to 0.
#ndSSL=0
# NzbDrone web root.
#
# set this if using a reverse proxy.
#ndWebRoot=
## Extensions
# Media Extensions
#
# This is a list of media extensions that are used to verify that the download does contain valid media.
#mediaExtensions=.mkv,.avi,.divx,.xvid,.mov,.wmv,.mp4,.mpg,.mpeg,.vob,.iso
## Transcoder
# Transcode (0, 1).
#
# set to 1 to transcode, otherwise set to 0.
#transcode=0
# create a duplicate, or replace the original (0, 1).
#
# set to 1 to cretae a new file or 0 to replace the original
#duplicate=1
# ignore extensions
#
# list of extensions that won't be transcoded.
#ignoreExtensions=.avi,.mkv
# ffmpeg output settings.
#outputVideoExtension=.mp4
#outputVideoCodec=libx264
#outputVideoPreset=medium
#outputVideoFramerate=24
#outputVideoBitrate=800k
#outputAudioCodec=libmp3lame
#outputAudioBitrate=128k
#outputSubtitleCodec=
## WakeOnLan
# use WOL (0, 1).
#
# set to 1 to send WOL broadcast to the mac and test the server (e.g. xbmc) on the host and port specified.
#wolwake=0
# WOL MAC
#
# enter the mac address of the system to be woken.
#wolmac=00:01:2e:2D:64:e1
# Set the Host and Port of a server to verify system has woken.
#wolhost=192.168.1.37
#wolport=80
### NZBGET POST-PROCESSING SCRIPT ###
##############################################################################
import logging
from nzbtomedia.autoProcess.autoProcessTV import autoProcessTV
from nzbtomedia.nzbToMediaConfig import config
from nzbtomedia.nzbToMediaUtil import nzbtomedia_configure_logging, WakeUp, get_dirnames
# run migrate to convert old cfg to new style cfg plus fix any cfg missing values/options.
if config.migrate():
# check to write settings from nzbGet UI to autoProcessMedia.cfg.
if os.environ.has_key('NZBOP_SCRIPTDIR'):
config.addnzbget()
nzbtomedia_configure_logging(config.LOG_FILE)
Logger = logging.getLogger(__name__)
Logger.info("====================") # Seperate old from new log
Logger.info("nzbToNzbDrone %s", config.NZBTOMEDIA_VERSION)
Logger.info("MAIN: Loading config from %s", config.CONFIG_FILE)
else:
sys.exit(-1)
WakeUp()
# NZBGet V11+
# Check if the script is called from nzbget 11.0 or later
if os.environ.has_key('NZBOP_SCRIPTDIR') and not os.environ['NZBOP_VERSION'][0:5] < '11.0':
Logger.info("MAIN: Script triggered from NZBGet (11.0 or later).")
# Check nzbget.conf options
status = 0
if os.environ['NZBOP_UNPACK'] != 'yes':
Logger.error("MAIN: Please enable option \"Unpack\" in nzbget configuration file, exiting")
sys.exit(config.NZBGET_POSTPROCESS_ERROR)
# Check par status
if os.environ['NZBPP_PARSTATUS'] == '3':
Logger.warning("MAIN: Par-check successful, but Par-repair disabled, exiting")
Logger.info("MAIN: Please check your Par-repair settings for future downloads.")
sys.exit(config.NZBGET_POSTPROCESS_NONE)
if os.environ['NZBPP_PARSTATUS'] == '1' or os.environ['NZBPP_PARSTATUS'] == '4':
Logger.warning("MAIN: Par-repair failed, setting status \"failed\"")
status = 1
# Check unpack status
if os.environ['NZBPP_UNPACKSTATUS'] == '1':
Logger.warning("MAIN: Unpack failed, setting status \"failed\"")
status = 1
if os.environ['NZBPP_UNPACKSTATUS'] == '0' and os.environ['NZBPP_PARSTATUS'] == '0':
# Unpack was skipped due to nzb-file properties or due to errors during par-check
if os.environ['NZBPP_HEALTH'] < 1000:
Logger.warning("MAIN: Download health is compromised and Par-check/repair disabled or no .par2 files found. Setting status \"failed\"")
Logger.info("MAIN: Please check your Par-check/repair settings for future downloads.")
status = 1
else:
Logger.info("MAIN: Par-check/repair disabled or no .par2 files found, and Unpack not required. Health is ok so handle as though download successful")
Logger.info("MAIN: Please check your Par-check/repair settings for future downloads.")
# Check if destination directory exists (important for reprocessing of history items)
if not os.path.isdir(os.environ['NZBPP_DIRECTORY']):
Logger.error("MAIN: Nothing to post-process: destination directory %s doesn't exist. Setting status \"failed\"", os.environ['NZBPP_DIRECTORY'])
status = 1
# All checks done, now launching the script.
Logger.info("MAIN: Script triggered from NZBGet, starting autoProcessTV...")
clientAgent = "nzbget"
result = autoProcessTV().processEpisode(os.environ['NZBPP_DIRECTORY'], os.environ['NZBPP_NZBFILENAME'], status, clientAgent, os.environ['NZBPP_CATEGORY'])
# SABnzbd Pre 0.7.17
elif len(sys.argv) == config.SABNZB_NO_OF_ARGUMENTS:
# SABnzbd argv:
# 1 The final directory of the job (full path)
# 2 The original name of the NZB file
# 3 Clean version of the job name (no path info and ".nzb" removed)
# 4 Indexer's report number (if supported)
# 5 User-defined category
# 6 Group that the NZB was posted in e.g. alt.binaries.x
# 7 Status of post processing. 0 = OK, 1=failed verification, 2=failed unpack, 3=1+2
Logger.info("MAIN: Script triggered from SABnzbd, starting autoProcessTV...")
clientAgent = "sabnzbd"
result = autoProcessTV().processEpisode(sys.argv[1], sys.argv[2], sys.argv[7], clientAgent, sys.argv[5])
# SABnzbd 0.7.17+
elif len(sys.argv) >= config.SABNZB_0717_NO_OF_ARGUMENTS:
# SABnzbd argv:
# 1 The final directory of the job (full path)
# 2 The original name of the NZB file
# 3 Clean version of the job name (no path info and ".nzb" removed)
# 4 Indexer's report number (if supported)
# 5 User-defined category
# 6 Group that the NZB was posted in e.g. alt.binaries.x
# 7 Status of post processing. 0 = OK, 1=failed verification, 2=failed unpack, 3=1+2
# 8 Failure URL
Logger.info("MAIN: Script triggered from SABnzbd 0.7.17+, starting autoProcessTV...")
clientAgent = "sabnzbd"
result = autoProcessTV().processEpisode(sys.argv[1], sys.argv[2], sys.argv[7], clientAgent, sys.argv[5])
else:
result = 0
# init sub-sections
subsections = config.get_subsections(["CouchPotato", "SickBeard", "NzbDrone", "HeadPhones", "Mylar", "Gamez"])
Logger.warn("MAIN: Invalid number of arguments received from client.")
for section, subsection in subsections.items():
for category in subsection:
dirNames = get_dirnames(section, category)
for dirName in dirNames:
Logger.info("MAIN: nzbToNzbDrone running %s:%s as a manual run...", section, subsection)
results = autoProcessTV(dirName, inputName=os.path.basename(dirName), status=0, clientAgent="manual",
inputCategory=category)
if results != 0:
result = results
Logger.info("MAIN: A problem was reported when trying to manually run %s:%s.", section, subsection)
if result == 0:
Logger.info("MAIN: The autoProcessTV script completed successfully.")
if os.environ.has_key('NZBOP_SCRIPTDIR'): # return code for nzbget v11
sys.exit(config.NZBGET_POSTPROCESS_SUCCESS)
else:
Logger.info("MAIN: A problem was reported in the autoProcessTV script.")
if os.environ.has_key('NZBOP_SCRIPTDIR'): # return code for nzbget v11
sys.exit(config.NZBGET_POSTPROCESS_ERROR)

View file

@ -149,9 +149,6 @@ if config.migrate():
else: else:
sys.exit(-1) sys.exit(-1)
# sickbeard category
sections = config.get_sections(["SickBeard"])
WakeUp() WakeUp()
# NZBGet V11+ # NZBGet V11+
@ -232,16 +229,20 @@ elif len(sys.argv) >= config.SABNZB_0717_NO_OF_ARGUMENTS:
else: else:
result = 0 result = 0
Logger.debug("MAIN: Invalid number of arguments received from client.") # init sub-sections
Logger.info("MAIN: Running autoProcessTV as a manual run...") subsections = config.get_subsections(["SickBeard", "NzbDrone"])
for section, categories in sections.items(): Logger.warn("MAIN: Invalid number of arguments received from client.")
for category in categories: for section, subsection in subsections.items():
for category in subsection:
dirNames = get_dirnames(section, category) dirNames = get_dirnames(section, category)
for dirName in dirNames: for dirName in dirNames:
Logger.info("MAIN: Calling " + section + ":" + category + " to post-process: %s", dirName) Logger.info("MAIN: nzbTo%s running %s:%s as a manual run...", section, section, subsection)
results = autoProcessTV().processEpisode(dirName, dirName, 0, inputCategory=category) results = autoProcessTV(dirName, inputName=os.path.basename(dirName), status=0, clientAgent="manual",
if results != 0:result = results inputCategory=category)
if results != 0:
result = results
Logger.info("MAIN: A problem was reported when trying to manually run %s:%s.", section, subsection)
if result == 0: if result == 0:
Logger.info("MAIN: The autoProcessTV script completed successfully.") Logger.info("MAIN: The autoProcessTV script completed successfully.")

View file

@ -14,11 +14,22 @@ class autoProcessComics:
Logger.error("No directory was given!") Logger.error("No directory was given!")
return 1 # failure return 1 # failure
# auto-detect correct section
section = [x for x in config.issubsection(inputCategory) if config()[x][inputCategory]['enabled'] == 1]
if len(section) > 1:
Logger.error(
"MAIN: You can't have multiple sub-sections with the same name enabled, fix your autoProcessMedia.cfg file.")
return 1
elif len(section) == 0:
Logger.error(
"MAIN: We were unable to find a processor for category %s that was enabled, please check your autoProcessMedia.cfg file.", inputCategory)
return 1
socket.setdefaulttimeout(int(config.NZBTOMEDIA_TIMEOUT)) #initialize socket timeout. socket.setdefaulttimeout(int(config.NZBTOMEDIA_TIMEOUT)) #initialize socket timeout.
Logger.info("Loading config from %s", config.CONFIG_FILE) Logger.info("Loading config from %s", config.CONFIG_FILE)
section = "Mylar"
host = config()[section][inputCategory]["host"] host = config()[section][inputCategory]["host"]
port = config()[section][inputCategory]["port"] port = config()[section][inputCategory]["port"]
username = config()[section][inputCategory]["username"] username = config()[section][inputCategory]["username"]

View file

@ -13,13 +13,24 @@ class autoProcessGames:
Logger.error("No directory was given!") Logger.error("No directory was given!")
return 1 # failure return 1 # failure
# auto-detect correct section
section = [x for x in config.issubsection(inputCategory) if config()[x][inputCategory]['enabled'] == 1]
if len(section) > 1:
Logger.error(
"MAIN: You can't have multiple sub-sections with the same name enabled, fix your autoProcessMedia.cfg file.")
return 1
elif len(section) == 0:
Logger.error(
"MAIN: We were unable to find a processor for category %s that was enabled, please check your autoProcessMedia.cfg file.", inputCategory)
return 1
socket.setdefaulttimeout(int(config.NZBTOMEDIA_TIMEOUT)) #initialize socket timeout. socket.setdefaulttimeout(int(config.NZBTOMEDIA_TIMEOUT)) #initialize socket timeout.
Logger.info("Loading config from %s", config.CONFIG_FILE) Logger.info("Loading config from %s", config.CONFIG_FILE)
status = int(status) status = int(status)
section = "Gamez"
host = config()[section][inputCategory]["host"] host = config()[section][inputCategory]["host"]
port = config()[section][inputCategory]["port"] port = config()[section][inputCategory]["port"]
apikey = config()[section][inputCategory]["apikey"] apikey = config()[section][inputCategory]["apikey"]

View file

@ -165,12 +165,22 @@ class autoProcessMovie:
Logger.error("No directory was given!") Logger.error("No directory was given!")
return 1 # failure return 1 # failure
# auto-detect correct section
section = [x for x in config.issubsection(inputCategory) if config()[x][inputCategory]['enabled'] == 1]
if len(section) > 1:
Logger.error(
"MAIN: You can't have multiple sub-sections with the same name enabled, fix your autoProcessMedia.cfg file.")
return 1
elif len(section) == 0:
Logger.error(
"MAIN: We were unable to find a processor for category %s that was enabled, please check your autoProcessMedia.cfg file.", inputCategory)
return 1
socket.setdefaulttimeout(int(config.NZBTOMEDIA_TIMEOUT)) #initialize socket timeout. socket.setdefaulttimeout(int(config.NZBTOMEDIA_TIMEOUT)) #initialize socket timeout.
Logger.info("Loading config from %s", config.CONFIG_FILE) Logger.info("Loading config from %s", config.CONFIG_FILE)
status = int(status) status = int(status)
section = "CouchPotato"
host = config()[section][inputCategory]["host"] host = config()[section][inputCategory]["host"]
port = config()[section][inputCategory]["port"] port = config()[section][inputCategory]["port"]
apikey = config()[section][inputCategory]["apikey"] apikey = config()[section][inputCategory]["apikey"]
@ -178,6 +188,7 @@ class autoProcessMovie:
method = config()[section][inputCategory]["method"] method = config()[section][inputCategory]["method"]
delete_failed = int(config()[section][inputCategory]["delete_failed"]) delete_failed = int(config()[section][inputCategory]["delete_failed"])
wait_for = int(config()[section][inputCategory]["wait_for"]) wait_for = int(config()[section][inputCategory]["wait_for"])
try: try:
TimePerGiB = int(config()[section][inputCategory]["TimePerGiB"]) TimePerGiB = int(config()[section][inputCategory]["TimePerGiB"])
except: except:

View file

@ -14,13 +14,23 @@ class autoProcessMusic:
Logger.error("No directory was given!") Logger.error("No directory was given!")
return 1 # failure return 1 # failure
# auto-detect correct section
section = [x for x in config.issubsection(inputCategory) if config()[x][inputCategory]['enabled'] == 1]
if len(section) > 1:
Logger.error(
"MAIN: You can't have multiple sub-sections with the same name enabled, fix your autoProcessMedia.cfg file.")
return 1
elif len(section) == 0:
Logger.error(
"MAIN: We were unable to find a processor for category %s that was enabled, please check your autoProcessMedia.cfg file.", inputCategory)
return 1
socket.setdefaulttimeout(int(config.NZBTOMEDIA_TIMEOUT)) #initialize socket timeout. socket.setdefaulttimeout(int(config.NZBTOMEDIA_TIMEOUT)) #initialize socket timeout.
Logger.info("Loading config from %s", config.CONFIG_FILE) Logger.info("Loading config from %s", config.CONFIG_FILE)
status = int(status) status = int(status)
section = "HeadPhones"
host = config()[section][inputCategory]["host"] host = config()[section][inputCategory]["host"]
port = config()[section][inputCategory]["port"] port = config()[section][inputCategory]["port"]
apikey = config()[section][inputCategory]["apikey"] apikey = config()[section][inputCategory]["apikey"]

View file

@ -14,18 +14,34 @@ from nzbtomedia.nzbToMediaUtil import convert_to_ascii, is_sample, flatten, getD
Logger = logging.getLogger() Logger = logging.getLogger()
class autoProcessTV: class autoProcessTV:
def processEpisode(self, dirName, nzbName=None, failed=False, clientAgent = "manual", inputCategory=None): def processEpisode(self, dirName, nzbName=None, failed=False, clientAgent = "manual", section=None, inputCategory=None):
if dirName is None: if dirName is None:
Logger.error("No directory was given!") Logger.error("No directory was given!")
return 1 # failure return 1 # failure
# auto-detect correct section
section = [x for x in config.issubsection(inputCategory) if config()[x][inputCategory]['enabled'] == 1]
if len(section) > 1:
Logger.error(
"MAIN: You can't have multiple sub-sections with the same name enabled, fix your autoProcessMedia.cfg file.")
return 1
elif len(section) == 0:
Logger.error(
"MAIN: We were unable to find a processor for category %s that was enabled, please check your autoProcessMedia.cfg file.", inputCategory)
return 1
fork, fork_params = autoFork(section, inputCategory)
Torrent_NoLink = int(config()[section][inputCategory]["Torrent_NoLink"]) # 0
if not fork in config.SICKBEARD_TORRENT and not Torrent_NoLink == 1:
if clientAgent in ['utorrent', 'transmission', 'deluge']:
return 1
socket.setdefaulttimeout(int(config.NZBTOMEDIA_TIMEOUT)) #initialize socket timeout. socket.setdefaulttimeout(int(config.NZBTOMEDIA_TIMEOUT)) #initialize socket timeout.
Logger.info("Loading config from %s", config.CONFIG_FILE) Logger.info("Loading config from %s", config.CONFIG_FILE)
status = int(failed) status = int(failed)
section = "SickBeard"
host = config()[section][inputCategory]["host"] host = config()[section][inputCategory]["host"]
port = config()[section][inputCategory]["port"] port = config()[section][inputCategory]["port"]
username = config()[section][inputCategory]["username"] username = config()[section][inputCategory]["username"]
@ -90,9 +106,6 @@ class autoProcessTV:
if os.path.isdir(SpecificPath): if os.path.isdir(SpecificPath):
dirName = SpecificPath dirName = SpecificPath
# auto-detect fork type
fork, params = autoFork(section, inputCategory)
if fork not in config.SICKBEARD_TORRENT or (clientAgent in ['nzbget','sabnzbd'] and nzbExtractionBy != "Destination"): if fork not in config.SICKBEARD_TORRENT or (clientAgent in ['nzbget','sabnzbd'] and nzbExtractionBy != "Destination"):
if nzbName: if nzbName:
process_all_exceptions(nzbName.lower(), dirName) process_all_exceptions(nzbName.lower(), dirName)

View file

@ -1,7 +1,8 @@
import logging
import os import os
import shutil import shutil
from itertools import chain
from lib import configobj from lib import configobj
from itertools import chain
class config(object): class config(object):
# constants for nzbtomedia # constants for nzbtomedia
@ -37,157 +38,191 @@ class config(object):
TV_CONFIG_FILE = os.path.join(PROGRAM_DIR, "autoProcessTv.cfg") TV_CONFIG_FILE = os.path.join(PROGRAM_DIR, "autoProcessTv.cfg")
LOG_FILE = os.path.join(PROGRAM_DIR, "postprocess.log") LOG_FILE = os.path.join(PROGRAM_DIR, "postprocess.log")
LOG_CONFIG = os.path.join(PROGRAM_DIR, "logging.cfg") LOG_CONFIG = os.path.join(PROGRAM_DIR, "logging.cfg")
SAMPLE_LOG_CONFIG = os.path.join(PROGRAM_DIR, "logging.cfg.sample")
def __new__(cls, *config_file): def __new__(cls, *config_file):
try: try:
# load config # load config
if not config_file: if not config_file:
return configobj.ConfigObj(cls.CONFIG_FILE) return configobj.ConfigObj(cls.CONFIG_FILE, interpolation=False)
else: else:
return configobj.ConfigObj(*config_file) return configobj.ConfigObj(*config_file, interpolation=False)
except Exception, e: except Exception, e:
return return
@staticmethod @staticmethod
def get_sections(section): def issubsection(subsection, sections=None):
sections = {} if not sections:
sections = config.__get_sections(subsection)
if not isinstance(sections, list):
sections = [sections]
results = set()
for section in sections:
if config()[section].has_key(subsection):
results.add(section)
return results if results.issubset(sections) else False
@staticmethod
def __get_sections(subsections):
# check and return categories if section does exist # check and return categories if section does exist
if not isinstance(section, list): if not isinstance(subsections, list):
section = [section] subsections = [subsections]
for x in section: to_return = []
if config().has_key(x): for subsection in subsections:
sections.update({x: config()[x].sections}) for section in config().sections:
return sections if config()[section].has_key(subsection):
to_return.append(section)
return to_return
@staticmethod
def get_subsections(sections):
# check and return categories if section does exist
if not isinstance(sections, list):
sections = [sections]
to_return = {}
for section in sections:
if section in config().sections:
for subsection in config()[section].sections:
if not isinstance(subsection, list):
subsection = [subsection]
to_return.update({section: subsection})
return to_return
@staticmethod @staticmethod
def migrate(): def migrate():
categories = {} global config_new, config_old
confignew = None config_new = config_old = None
configold = None
try: try:
# check for autoProcessMedia.cfg and create if it does not exist # check for autoProcessMedia.cfg and create if it does not exist
if not config(config.CONFIG_FILE): if not config(config.CONFIG_FILE):
shutil.copyfile(config.SAMPLE_CONFIG_FILE, config.CONFIG_FILE) shutil.copyfile(config.SAMPLE_CONFIG_FILE, config.CONFIG_FILE)
configold = config(config.CONFIG_FILE) config_old = config(config.CONFIG_FILE)
except:pass except:
pass
try: try:
# check for autoProcessMedia.cfg.sample and create if it does not exist # check for autoProcessMedia.cfg.sample and create if it does not exist
if not config(config.SAMPLE_CONFIG_FILE): if not config(config.SAMPLE_CONFIG_FILE):
shutil.copyfile(config.CONFIG_FILE, config.SAMPLE_CONFIG_FILE) shutil.copyfile(config.CONFIG_FILE, config.SAMPLE_CONFIG_FILE)
confignew = config(config.SAMPLE_CONFIG_FILE) config_new = config(config.SAMPLE_CONFIG_FILE)
except:pass except:
pass
# check for autoProcessMedia.cfg and autoProcessMedia.cfg.sample and if they don't exist return and fail # check for autoProcessMedia.cfg and autoProcessMedia.cfg.sample and if they don't exist return and fail
if not config() and not config(config.SAMPLE_CONFIG_FILE) or not confignew or not configold: if not config() and not config(config.SAMPLE_CONFIG_FILE) or not config_new or not config_old:
return False return False
for section in configold.sections:
for option, value in configold[section].items(): subsections = {}
if section == "CouchPotato": # gather all new-style and old-style sub-sections
if option == "category": # change this old format for newsection, newitems in config_new.iteritems():
option = "cpsCategory" if config_new[newsection].sections:
if section == "SickBeard": subsections.update({newsection: config_new[newsection].sections})
if option == "category": # change this old format for section, items in config_old.iteritems():
option = "sbCategory" if config_old[section].sections:
if option in ["cpsCategory","sbCategory","hpCategory","mlCategory","gzCategory"]: subsections.update({section: config_old[section].sections})
for option, value in config_old[section].items():
if option in ["category", "cpsCategory", "sbCategory", "hpCategory", "mlCategory", "gzCategory"]:
if not isinstance(value, list): if not isinstance(value, list):
value = [value] value = [value]
categories.update({section: value}) # add subsection
subsections.update({section: value})
config_old[section].pop(option)
continue continue
try: def cleanup_values(values, section):
for section in configold.sections: for option, value in values.iteritems():
subsection = None if section in ['CouchPotato']:
if section in list(chain.from_iterable(categories.values())): if option == ['outputDirectory']:
subsection = section config_new['Torrent'][option] = os.path.split(os.path.normpath(value))[0]
section = ''.join([k for k, v in categories.iteritems() if subsection in v]) values.pop(option)
elif section in categories.keys(): if section in ['CouchPotato', 'HeadPhones', 'Gamez']:
subsection = categories[section][0] if option in ['username', 'password']:
values.pop(option)
if section in ["SickBeard", "NzbDrone"]:
if option == "wait_for": # remove old format
values.pop(option)
if option == "failed_fork": # change this old format
values['failed'] = 'auto'
values.pop(option)
if option == "Torrent_ForceLink":
values['Torrent_NoLink'] = value
values.pop(option)
if option == "outputDirectory": # move this to new location format
config_new['Torrent'][option] = os.path.split(os.path.normpath(value))[0]
values.pop(option)
if section in ["Torrent"]:
if option in ["compressedExtensions", "mediaExtensions", "metaExtensions", "minSampleSize"]:
config_new['Extensions'][option] = value
values.pop(option)
if option == "useLink": # Sym links supported now as well.
if isinstance(value, int):
num_value = int(value)
if num_value == 1:
value = 'hard'
else:
value = 'no'
return values
# create subsection if it does not exist def process_section(section, subsections=None):
if subsection and subsection not in confignew[section].sections: if subsections:
confignew[section][subsection] = {} for subsection in subsections:
if subsection in config_old.sections:
values = config_old[subsection]
if subsection not in config_new[section].sections:
config_new[section][subsection] = {}
for option, value in values.items():
config_new[section][subsection][option] = value
elif subsection in config_old[section].sections:
values = config_old[section][subsection]
if subsection not in config_new[section].sections:
config_new[section][subsection] = {}
for option, value in values.items():
config_new[section][subsection][option] = value
else:
values = config_old[section]
if section not in config_new.sections:
config_new[section] = {}
for option, value in values.items():
config_new[section][option] = value
for option, value in configold[section].items(): # convert old-style categories to new-style sub-sections
if section == "CouchPotato": for section in config_old.keys():
if option == "outputDirectory": # move this to new location format subsection = None
value = os.path.split(os.path.normpath(value))[0] if section in list(chain.from_iterable(subsections.values())):
confignew['Torrent'][option] = value subsection = section
continue section = ''.join([k for k,v in subsections.iteritems() if subsection in v])
if option in ["username", "password"]: # these are no-longer needed. process_section(section, subsection)
continue #[[v.remove(c) for c in v if c in subsection] for k, v in subsections.items() if k == section]
if option in ["category","cpsCategory"]: elif section in subsections.keys():
continue subsection = subsections[section]
process_section(section, subsection)
if section == "SickBeard": #[[v.remove(c) for c in v if c in subsection] for k,v in subsections.items() if k == section]
if option == "wait_for": # remove old format elif section in config_old.keys():
continue process_section(section, subsection)
if option == "failed_fork": # change this old format
option = "fork"
value = "auto"
if option == "Torrent_ForceLink":
continue
if option == "outputDirectory": # move this to new location format
value = os.path.split(os.path.normpath(value))[0]
confignew['Torrent'][option] = value
continue
if option in ["category", "sbCategory"]:
continue
if section == "HeadPhones":
if option in ["username", "password" ]:
continue
if option == "hpCategory":
continue
if section == "Mylar":
if option in "mlCategory":
continue
if section == "Gamez":
if option in ["username", "password"]: # these are no-longer needed.
continue
if option == "gzCategory":
continue
if section == "Torrent":
if option in ["compressedExtensions", "mediaExtensions", "metaExtensions", "minSampleSize"]:
section = "Extensions" # these were moved
if option == "useLink": # Sym links supported now as well.
if isinstance(value, int):
num_value = int(value)
if num_value == 1:
value = "hard"
else:
value = "no"
if subsection:
confignew[section][subsection][option] = value
else:
confignew[section][option] = value
except:pass
# create a backup of our old config # create a backup of our old config
if os.path.isfile(config.CONFIG_FILE): if os.path.isfile(config.CONFIG_FILE):
cfgbak_name = config.CONFIG_FILE + ".old" cfgbak_name = config.CONFIG_FILE + ".old"
if os.path.isfile(cfgbak_name): # remove older backups if os.path.isfile(cfgbak_name): # remove older backups
os.unlink(cfgbak_name) os.unlink(cfgbak_name)
os.rename(config.CONFIG_FILE, cfgbak_name) os.rename(config.CONFIG_FILE, cfgbak_name)
# writing our configuration file to 'autoProcessMedia.cfg' # writing our configuration file to 'autoProcessMedia.cfg'
with open(config.CONFIG_FILE, 'wb') as configFile: with open(config.CONFIG_FILE, 'wb') as configFile:
confignew.write(configFile) config_new.write(configFile)
return True return True
@staticmethod @staticmethod
def addnzbget(): def addnzbget():
confignew = config() config_new = config()
section = "CouchPotato" section = "CouchPotato"
envCatKey = 'NZBPO_CPSCATEGORY' envCatKey = 'NZBPO_CPSCATEGORY'
envKeys = ['APIKEY', 'HOST', 'PORT', 'SSL', 'WEB_ROOT', 'DELAY', 'METHOD', 'DELETE_FAILED', 'REMOTECPS', 'WAIT_FOR', 'TIMEPERGIB'] envKeys = ['APIKEY', 'HOST', 'PORT', 'SSL', 'WEB_ROOT', 'DELAY', 'METHOD', 'DELETE_FAILED', 'REMOTECPS', 'WAIT_FOR', 'TIMEPERGIB']
@ -198,9 +233,9 @@ class config(object):
if os.environ.has_key(key): if os.environ.has_key(key):
option = cfgKeys[index] option = cfgKeys[index]
value = os.environ[key] value = os.environ[key]
if os.environ[envCatKey] not in confignew[section].sections: if os.environ[envCatKey] not in config_new[section].sections:
confignew[section][os.environ[envCatKey]] = {} config_new[section][os.environ[envCatKey]] = {}
confignew[section][os.environ[envCatKey]][option] = value config_new[section][os.environ[envCatKey]][option] = value
section = "SickBeard" section = "SickBeard"
envCatKey = 'NZBPO_SBCATEGORY' envCatKey = 'NZBPO_SBCATEGORY'
@ -212,9 +247,9 @@ class config(object):
if os.environ.has_key(key): if os.environ.has_key(key):
option = cfgKeys[index] option = cfgKeys[index]
value = os.environ[key] value = os.environ[key]
if os.environ[envCatKey] not in confignew[section].sections: if os.environ[envCatKey] not in config_new[section].sections:
confignew[section][os.environ[envCatKey]] = {} config_new[section][os.environ[envCatKey]] = {}
confignew[section][os.environ[envCatKey]][option] = value config_new[section][os.environ[envCatKey]][option] = value
section = "HeadPhones" section = "HeadPhones"
envCatKey = 'NZBPO_HPCATEGORY' envCatKey = 'NZBPO_HPCATEGORY'
@ -226,9 +261,9 @@ class config(object):
if os.environ.has_key(key): if os.environ.has_key(key):
option = cfgKeys[index] option = cfgKeys[index]
value = os.environ[key] value = os.environ[key]
if os.environ[envCatKey] not in confignew[section].sections: if os.environ[envCatKey] not in config_new[section].sections:
confignew[section][os.environ[envCatKey]] = {} config_new[section][os.environ[envCatKey]] = {}
confignew[section][os.environ[envCatKey]][option] = value config_new[section][os.environ[envCatKey]][option] = value
section = "Mylar" section = "Mylar"
envCatKey = 'NZBPO_MYCATEGORY' envCatKey = 'NZBPO_MYCATEGORY'
@ -240,9 +275,9 @@ class config(object):
if os.environ.has_key(key): if os.environ.has_key(key):
option = cfgKeys[index] option = cfgKeys[index]
value = os.environ[key] value = os.environ[key]
if os.environ[envCatKey] not in confignew[section].sections: if os.environ[envCatKey] not in config_new[section].sections:
confignew[section][os.environ[envCatKey]] = {} config_new[section][os.environ[envCatKey]] = {}
confignew[section][os.environ[envCatKey]][option] = value config_new[section][os.environ[envCatKey]][option] = value
section = "Gamez" section = "Gamez"
envCatKey = 'NZBPO_GZCATEGORY' envCatKey = 'NZBPO_GZCATEGORY'
@ -254,9 +289,9 @@ class config(object):
if os.environ.has_key(key): if os.environ.has_key(key):
option = cfgKeys[index] option = cfgKeys[index]
value = os.environ[key] value = os.environ[key]
if os.environ[envCatKey] not in confignew[section].sections: if os.environ[envCatKey] not in config_new[section].sections:
confignew[section][os.environ[envCatKey]] = {} config_new[section][os.environ[envCatKey]] = {}
confignew[section][os.environ[envCatKey]][option] = value config_new[section][os.environ[envCatKey]][option] = value
section = "Extensions" section = "Extensions"
envKeys = ['COMPRESSEDEXTENSIONS', 'MEDIAEXTENSIONS', 'METAEXTENSIONS'] envKeys = ['COMPRESSEDEXTENSIONS', 'MEDIAEXTENSIONS', 'METAEXTENSIONS']
@ -266,7 +301,7 @@ class config(object):
if os.environ.has_key(key): if os.environ.has_key(key):
option = cfgKeys[index] option = cfgKeys[index]
value = os.environ[key] value = os.environ[key]
confignew[section][option] = value config_new[section][option] = value
section = "Transcoder" section = "Transcoder"
envKeys = ['TRANSCODE', 'DUPLICATE', 'IGNOREEXTENSIONS', 'OUTPUTVIDEOEXTENSION', 'OUTPUTVIDEOCODEC', 'OUTPUTVIDEOPRESET', 'OUTPUTVIDEOFRAMERATE', 'OUTPUTVIDEOBITRATE', 'OUTPUTAUDIOCODEC', 'OUTPUTAUDIOBITRATE', 'OUTPUTSUBTITLECODEC'] envKeys = ['TRANSCODE', 'DUPLICATE', 'IGNOREEXTENSIONS', 'OUTPUTVIDEOEXTENSION', 'OUTPUTVIDEOCODEC', 'OUTPUTVIDEOPRESET', 'OUTPUTVIDEOFRAMERATE', 'OUTPUTVIDEOBITRATE', 'OUTPUTAUDIOCODEC', 'OUTPUTAUDIOBITRATE', 'OUTPUTSUBTITLECODEC']
@ -276,7 +311,7 @@ class config(object):
if os.environ.has_key(key): if os.environ.has_key(key):
option = cfgKeys[index] option = cfgKeys[index]
value = os.environ[key] value = os.environ[key]
confignew[section][option] = value config_new[section][option] = value
section = "WakeOnLan" section = "WakeOnLan"
envKeys = ['WAKE', 'HOST', 'PORT', 'MAC'] envKeys = ['WAKE', 'HOST', 'PORT', 'MAC']
@ -286,7 +321,7 @@ class config(object):
if os.environ.has_key(key): if os.environ.has_key(key):
option = cfgKeys[index] option = cfgKeys[index]
value = os.environ[key] value = os.environ[key]
confignew[section][option] = value config_new[section][option] = value
# create a backup of our old config # create a backup of our old config
if os.path.isfile(config.CONFIG_FILE): if os.path.isfile(config.CONFIG_FILE):
@ -297,4 +332,4 @@ class config(object):
# writing our configuration file to 'autoProcessMedia.cfg' # writing our configuration file to 'autoProcessMedia.cfg'
with open(config.CONFIG_FILE, 'wb') as configFile: with open(config.CONFIG_FILE, 'wb') as configFile:
confignew.write(configFile) config_new.write(configFile)

View file

@ -51,6 +51,9 @@ def create_destination(outputDestination):
sys.exit(-1) sys.exit(-1)
def category_search(inputDirectory, inputName, inputCategory, root, categories): def category_search(inputDirectory, inputName, inputCategory, root, categories):
if inputDirectory is None:
return inputDirectory, inputName, inputCategory, root
if not os.path.isdir(inputDirectory) and os.path.isfile(inputDirectory): # If the input directory is a file, assume single file downlaod and split dir/name. if not os.path.isdir(inputDirectory) and os.path.isfile(inputDirectory): # If the input directory is a file, assume single file downlaod and split dir/name.
inputDirectory,inputName = os.path.split(os.path.normpath(inputDirectory)) inputDirectory,inputName = os.path.split(os.path.normpath(inputDirectory))
@ -327,14 +330,13 @@ def TestCon(host, port):
def WakeUp(): def WakeUp():
if not config(): if not config():
Logger.error("You need an autoProcessMedia.config() file - did you rename and edit the .sample?") Logger.error("You need an autoProcessMedia.cfg file - did you rename and edit the .sample?")
return return
wake = int(config()["WakeOnLan"]["wake"]) wake = int(config()["WakeOnLan"]["wake"])
if wake == 0: # just return if we don't need to wake anything. if wake == 0: # just return if we don't need to wake anything.
return return
Logger.info("Loading WakeOnLan config from %s", config.CONFIG_FILE) Logger.info("Loading WakeOnLan config from %s", config.CONFIG_FILE)
config()["WakeOnLan"]["host"]
host = config()["WakeOnLan"]["host"] host = config()["WakeOnLan"]["host"]
port = int(config()["WakeOnLan"]["port"]) port = int(config()["WakeOnLan"]["port"])
mac = config()["WakeOnLan"]["mac"] mac = config()["WakeOnLan"]["mac"]
@ -376,6 +378,7 @@ def convert_to_ascii(nzbName, dirName):
return nzbName, dirName return nzbName, dirName
def parse_other(args): def parse_other(args):
return os.path.normpath(args[1]), '', '', '', '' return os.path.normpath(args[1]), '', '', '', ''
def parse_rtorrent(args): def parse_rtorrent(args):
@ -449,40 +452,49 @@ def parse_args(clientAgent):
'transmission': parse_transmission, 'transmission': parse_transmission,
} }
return clients[clientAgent](sys.argv) try:
return clients[clientAgent](sys.argv)
except:return None, None, None, None, None
def get_dirnames(section, inputCategory): def get_dirnames(section, subsections=None):
dirNames = [] dirNames = []
try: if subsections is None:
watch_dir = config()[section][inputCategory]["watch_dir"] subsections = config.get_subsections(section).values()
if not os.path.exists(watch_dir):
watch_dir = ""
except:
watch_dir = ""
try: if not isinstance(subsections, list):
outputDirectory = os.path.join(config()["Torrent"]["outputDirectory"], inputCategory) subsections = [subsections]
if not os.path.exists(watch_dir):
outputDirectory = ""
except:
outputDirectory = ""
if watch_dir != "": for subsection in subsections:
dirNames.extend([os.path.join(watch_dir, o) for o in os.listdir(watch_dir) if try:
os.path.isdir(os.path.join(watch_dir, o))]) watch_dir = config()[section][subsection]["watch_dir"]
if not dirNames: if not os.path.exists(watch_dir):
Logger.warn("No Directories identified to Scan inside " + watch_dir) watch_dir = None
except:
watch_dir = None
if outputDirectory != "": try:
dirNames.extend([os.path.join(outputDirectory, o) for o in os.listdir(outputDirectory) if outputDirectory = os.path.join(config()["Torrent"]["outputDirectory"], subsection)
os.path.isdir(os.path.join(outputDirectory, o))]) if not os.path.exists(outputDirectory):
if not dirNames: outputDirectory = None
Logger.warn("No Directories identified to Scan inside " + outputDirectory) except:
outputDirectory = None
if watch_dir == "" and outputDirectory == "": if watch_dir:
Logger.warn("No watch_dir or outputDirectory setup to be Scanned, go fix you autoProcessMedia.cfg file.") dirNames.extend([os.path.join(watch_dir, o) for o in os.listdir(watch_dir) if
os.path.isdir(os.path.join(watch_dir, o))])
if not dirNames:
Logger.warn("%s:%s has no directories identified to scan inside %s", section, subsection, watch_dir)
if outputDirectory:
dirNames.extend([os.path.join(outputDirectory, o) for o in os.listdir(outputDirectory) if
os.path.isdir(os.path.join(outputDirectory, o))])
if not dirNames:
Logger.warn("%s:%s has no directories identified to scan inside %s", section, subsection, outputDirectory)
if watch_dir is None and outputDirectory is None:
Logger.warn("%s:%s has no watch_dir or outputDirectory setup to be Scanned, go fix you autoProcessMedia.cfg file.", section, subsection)
return dirNames return dirNames