def main(args, section=None): # Initialize the config core.initialize(section) logger.info("#########################################################") logger.info("## ..::[{0}]::.. ##".format(os.path.basename(__file__))) logger.info("#########################################################") # debug command line options logger.debug("Options passed into nzbToMedia: {0}".format(args)) # Post-Processing Result result = [0, ""] status = 0 # NZBGet if 'NZBOP_SCRIPTDIR' in os.environ: # Check if the script is called from nzbget 11.0 or later if os.environ['NZBOP_VERSION'][0:5] < '11.0': logger.error("NZBGet Version {0} is not supported. Please update NZBGet.".format(os.environ['NZBOP_VERSION'])) sys.exit(core.NZBGET_POSTPROCESS_ERROR) logger.info("Script triggered from NZBGet Version {0}.".format(os.environ['NZBOP_VERSION'])) # Check if the script is called from nzbget 13.0 or later if 'NZBPP_TOTALSTATUS' in os.environ: if not os.environ['NZBPP_TOTALSTATUS'] == 'SUCCESS': logger.info("Download failed with status {0}.".format(os.environ['NZBPP_STATUS'])) status = 1 else: # Check par status if os.environ['NZBPP_PARSTATUS'] == '1' or os.environ['NZBPP_PARSTATUS'] == '4': logger.warning("Par-repair failed, setting status \"failed\"") status = 1 # Check unpack status if os.environ['NZBPP_UNPACKSTATUS'] == '1': logger.warning("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( "Download health is compromised and Par-check/repair disabled or no .par2 files found. Setting status \"failed\"") logger.info("Please check your Par-check/repair settings for future downloads.") status = 1 else: logger.info( "Par-check/repair disabled or no .par2 files found, and Unpack not required. Health is ok so handle as though download successful") logger.info("Please check your Par-check/repair settings for future downloads.") # Check for download_id to pass to CouchPotato download_id = "" failureLink = None if 'NZBPR_COUCHPOTATO' in os.environ: download_id = os.environ['NZBPR_COUCHPOTATO'] elif 'NZBPR_DRONE' in os.environ: download_id = os.environ['NZBPR_DRONE'] elif 'NZBPR_SONARR' in os.environ: download_id = os.environ['NZBPR_SONARR'] if 'NZBPR__DNZB_FAILURE' in os.environ: failureLink = os.environ['NZBPR__DNZB_FAILURE'] # All checks done, now launching the script. clientAgent = 'nzbget' result = process(os.environ['NZBPP_DIRECTORY'], inputName=os.environ['NZBPP_NZBNAME'], status=status, clientAgent=clientAgent, download_id=download_id, inputCategory=os.environ['NZBPP_CATEGORY'], failureLink=failureLink) # SABnzbd Pre 0.7.17 elif len(args) == core.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 clientAgent = 'sabnzbd' logger.info("Script triggered from SABnzbd") result = process(args[1], inputName=args[2], status=args[7], inputCategory=args[5], clientAgent=clientAgent, download_id='') # SABnzbd 0.7.17+ elif len(args) >= core.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 clientAgent = 'sabnzbd' logger.info("Script triggered from SABnzbd 0.7.17+") result = process(args[1], inputName=args[2], status=args[7], inputCategory=args[5], clientAgent=clientAgent, download_id='', failureLink=''.join(args[8:])) # Generic program elif len(args) > 5 and args[5] == 'generic': logger.info("Script triggered from generic program") result = process(args[1], inputName=args[2], inputCategory=args[3], download_id=args[4]) else: # Perform Manual Post-Processing logger.warning("Invalid number of arguments received from client, Switching to manual run mode ...") for section, subsections in core.SECTIONS.items(): for subsection in subsections: if not core.CFG[section][subsection].isenabled(): continue for dirName in getDirs(section, subsection, link='move'): logger.info("Starting manual run for {0}:{1} - Folder: {2}".format(section, subsection, dirName)) logger.info("Checking database for download info for {0} ...".format(os.path.basename(dirName))) core.DOWNLOADINFO = get_downloadInfo(os.path.basename(dirName), 0) if core.DOWNLOADINFO: logger.info("Found download info for {0}, " "setting variables now ...".format (os.path.basename(dirName))) clientAgent = text_type(core.DOWNLOADINFO[0].get('client_agent', 'manual')) download_id = text_type(core.DOWNLOADINFO[0].get('input_id', '')) else: logger.info('Unable to locate download info for {0}, ' 'continuing to try and process this release ...'.format (os.path.basename(dirName))) clientAgent = 'manual' download_id = '' if clientAgent and clientAgent.lower() not in core.NZB_CLIENTS: continue try: dirName = dirName.encode(core.SYS_ENCODING) except UnicodeError: pass inputName = os.path.basename(dirName) try: inputName = inputName.encode(core.SYS_ENCODING) except UnicodeError: pass results = process(dirName, inputName, 0, clientAgent=clientAgent, download_id=download_id or None, inputCategory=subsection) if results[0] != 0: logger.error("A problem was reported when trying to perform a manual run for {0}:{1}.".format (section, subsection)) result = results if result[0] == 0: logger.info("The {0} script completed successfully.".format(args[0])) if result[1]: print(result[1] + "!") if 'NZBOP_SCRIPTDIR' in os.environ: # return code for nzbget v11 del core.MYAPP return core.NZBGET_POSTPROCESS_SUCCESS else: logger.error("A problem was reported in the {0} script.".format(args[0])) if result[1]: print(result[1] + "!") if 'NZBOP_SCRIPTDIR' in os.environ: # return code for nzbget v11 del core.MYAPP return core.NZBGET_POSTPROCESS_ERROR del core.MYAPP return result[0]
def startproc(inputDirectory, inputName=None, status=0, clientAgent='manual', download_id=None, inputCategory=None, failureLink=None): if core.SAFE_MODE and inputDirectory == core.NZB_DEFAULTDIR: logger.error( 'The input directory:[{0}] is the Default Download Directory. Please configure category directories to prevent processing of other media.' .format(inputDirectory)) return [-1, ""] if not download_id and clientAgent == 'sabnzbd': download_id = get_nzoid(inputName) if clientAgent != 'manual' and not core.DOWNLOADINFO: logger.debug( 'Adding NZB download info for directory {0} to database'.format( inputDirectory)) myDB = nzbToMediaDB.DBConnection() inputDirectory1 = inputDirectory inputName1 = inputName try: encoded, inputDirectory1 = CharReplace(inputDirectory) encoded, inputName1 = CharReplace(inputName) except: pass controlValueDict = {"input_directory": text_type(inputDirectory1)} newValueDict = { "input_name": text_type(inputName1), "input_hash": text_type(download_id), "input_id": text_type(download_id), "client_agent": text_type(clientAgent), "status": 0, "last_update": datetime.date.today().toordinal() } myDB.upsert("downloads", newValueDict, controlValueDict) # auto-detect section if inputCategory is None: inputCategory = 'UNCAT' usercat = inputCategory section = core.CFG.findsection(inputCategory).isenabled() if section is None: section = core.CFG.findsection("ALL").isenabled() if section is None: logger.error( 'Category:[{0}] is not defined or is not enabled. Please rename it or ensure it is enabled for the appropriate section in your autoProcessMedia.cfg and try again.' .format(inputCategory)) return [-1, ""] else: usercat = "ALL" if len(section) > 1: logger.error( 'Category:[{0}] is not unique, {1} are using it. Please rename it or disable all other sections using the same category name in your autoProcessMedia.cfg and try again.' .format(inputCategory, section.keys())) return [-1, ""] if section: sectionName = section.keys()[0] logger.info('Auto-detected SECTION:{0}'.format(sectionName)) else: logger.error( "Unable to locate a section with subsection:{0} enabled in your autoProcessMedia.cfg, exiting!" .format(inputCategory)) return [-1, ""] cfg = dict(core.CFG[sectionName][usercat]) extract = int(cfg.get("extract", 0)) try: if int(cfg.get("remote_path")) and not core.REMOTEPATHS: logger.error( 'Remote Path is enabled for {0}:{1} but no Network mount points are defined. Please check your autoProcessMedia.cfg, exiting!' .format(sectionName, inputCategory)) return [-1, ""] except: logger.error( 'Remote Path {0} is not valid for {1}:{2} Please set this to either 0 to disable or 1 to enable!' .format(core.get("remote_path"), sectionName, inputCategory)) inputName, inputDirectory = convert_to_ascii(inputName, inputDirectory) if extract == 1: logger.debug( 'Checking for archives to extract in directory: {0}'.format( inputDirectory)) extractFiles(inputDirectory) logger.info("Calling Checker to check the Files and Convert them to mp4") result = Filechecker().checkfiles(sectionName, inputDirectory, inputName, status, clientAgent, download_id, inputCategory, failureLink) return result
def process(inputDirectory, inputName=None, status=0, clientAgent='manual', download_id=None, inputCategory=None, failureLink=None): if core.SAFE_MODE and inputDirectory == core.NZB_DEFAULTDIR: logger.error( 'The input directory:[{0}] is the Default Download Directory. Please configure category directories to prevent processing of other media.'.format( inputDirectory)) return [-1, ""] if not download_id and clientAgent == 'sabnzbd': download_id = get_nzoid(inputName) if clientAgent != 'manual' and not core.DOWNLOADINFO: logger.debug('Adding NZB download info for directory {0} to database'.format(inputDirectory)) myDB = nzbToMediaDB.DBConnection() inputDirectory1 = inputDirectory inputName1 = inputName try: encoded, inputDirectory1 = CharReplace(inputDirectory) encoded, inputName1 = CharReplace(inputName) except: pass controlValueDict = {"input_directory": text_type(inputDirectory1)} newValueDict = {"input_name": text_type(inputName1), "input_hash": text_type(download_id), "input_id": text_type(download_id), "client_agent": text_type(clientAgent), "status": 0, "last_update": datetime.date.today().toordinal() } myDB.upsert("downloads", newValueDict, controlValueDict) # auto-detect section if inputCategory is None: inputCategory = 'UNCAT' usercat = inputCategory section = core.CFG.findsection(inputCategory).isenabled() if section is None: section = core.CFG.findsection("ALL").isenabled() if section is None: logger.error( 'Category:[{0}] is not defined or is not enabled. Please rename it or ensure it is enabled for the appropriate section in your autoProcessMedia.cfg and try again.'.format( inputCategory)) return [-1, ""] else: usercat = "ALL" if len(section) > 1: logger.error( 'Category:[{0}] is not unique, {1} are using it. Please rename it or disable all other sections using the same category name in your autoProcessMedia.cfg and try again.'.format( inputCategory, section.keys())) return [-1, ""] if section: sectionName = section.keys()[0] logger.info('Auto-detected SECTION:{0}'.format(sectionName)) else: logger.error("Unable to locate a section with subsection:{0} enabled in your autoProcessMedia.cfg, exiting!".format( inputCategory)) return [-1, ""] cfg = dict(core.CFG[sectionName][usercat]) extract = int(cfg.get("extract", 0)) try: if int(cfg.get("remote_path")) and not core.REMOTEPATHS: logger.error('Remote Path is enabled for {0}:{1} but no Network mount points are defined. Please check your autoProcessMedia.cfg, exiting!'.format( sectionName, inputCategory)) return [-1, ""] except: logger.error('Remote Path {0} is not valid for {1}:{2} Please set this to either 0 to disable or 1 to enable!'.format( core.get("remote_path"), sectionName, inputCategory)) inputName, inputDirectory = convert_to_ascii(inputName, inputDirectory) if extract == 1: logger.debug('Checking for archives to extract in directory: {0}'.format(inputDirectory)) extractFiles(inputDirectory) logger.info("Calling {0}:{1} to post-process:{2}".format(sectionName, inputCategory, inputName)) if sectionName == "CouchPotato": result = autoProcessMovie().process(sectionName, inputDirectory, inputName, status, clientAgent, download_id, inputCategory, failureLink) elif sectionName in ["SickBeard", "NzbDrone"]: result = autoProcessTV().processEpisode(sectionName, inputDirectory, inputName, status, clientAgent, download_id, inputCategory, failureLink) elif sectionName == "HeadPhones": result = autoProcessMusic().process(sectionName, inputDirectory, inputName, status, clientAgent, inputCategory) elif sectionName == "Mylar": result = autoProcessComics().processEpisode(sectionName, inputDirectory, inputName, status, clientAgent, inputCategory) elif sectionName == "Gamez": result = autoProcessGames().process(sectionName, inputDirectory, inputName, status, clientAgent, inputCategory) elif sectionName == 'UserScript': result = external_script(inputDirectory, inputName, inputCategory, section[usercat]) else: result = [-1, ""] plex_update(inputCategory) if result[0] == 0: if clientAgent != 'manual': # update download status in our DB update_downloadInfoStatus(inputName, 1) if sectionName not in ['UserScript', 'NzbDrone']: # cleanup our processing folders of any misc unwanted files and empty directories cleanDir(inputDirectory, sectionName, inputCategory) return result
def main(args): # Initialize the config core.initialize() # clientAgent for Torrents clientAgent = core.TORRENT_CLIENTAGENT logger.info("#########################################################") logger.info("## ..::[{0}]::.. ##".format(os.path.basename(__file__))) logger.info("#########################################################") # debug command line options logger.debug("Options passed into TorrentToMedia: {0}".format(args)) # Post-Processing Result result = [0, ""] try: inputDirectory, inputName, inputCategory, inputHash, inputID = core.parse_args( clientAgent, args) except: logger.error("There was a problem loading variables") return -1 if inputDirectory and inputName and inputHash and inputID: result = processTorrent(inputDirectory, inputName, inputCategory, inputHash, inputID, clientAgent) else: # Perform Manual Post-Processing logger.warning( "Invalid number of arguments received from client, Switching to manual run mode ..." ) for section, subsections in core.SECTIONS.items(): for subsection in subsections: if not core.CFG[section][subsection].isenabled(): continue for dirName in core.getDirs(section, subsection, link='hard'): logger.info( "Starting manual run for {0}:{1} - Folder:{2}".format( section, subsection, dirName)) logger.info( "Checking database for download info for {0} ...". format(os.path.basename(dirName))) core.DOWNLOADINFO = core.get_downloadInfo( os.path.basename(dirName), 0) if core.DOWNLOADINFO: clientAgent = text_type(core.DOWNLOADINFO[0].get( 'client_agent', 'manual')) inputHash = text_type(core.DOWNLOADINFO[0].get( 'input_hash', '')) inputID = text_type(core.DOWNLOADINFO[0].get( 'input_id', '')) logger.info("Found download info for {0}, " "setting variables now ...".format( os.path.basename(dirName))) else: logger.info( 'Unable to locate download info for {0}, ' 'continuing to try and process this release ...'. format(os.path.basename(dirName))) clientAgent = 'manual' inputHash = '' inputID = '' if clientAgent.lower() not in core.TORRENT_CLIENTS: continue try: dirName = dirName.encode(core.SYS_ENCODING) except UnicodeError: pass inputName = os.path.basename(dirName) try: inputName = inputName.encode(core.SYS_ENCODING) except UnicodeError: pass results = processTorrent(dirName, inputName, subsection, inputHash or None, inputID or None, clientAgent) if results[0] != 0: logger.error( "A problem was reported when trying to perform a manual run for {0}:{1}." .format(section, subsection)) result = results if result[0] == 0: logger.info("The {0} script completed successfully.".format(args[0])) else: logger.error("A problem was reported in the {0} script.".format( args[0])) del core.MYAPP return result[0]
def processTorrent(inputDirectory, inputName, inputCategory, inputHash, inputID, clientAgent): status = 1 # 1 = failed | 0 = success root = 0 foundFile = 0 if clientAgent != 'manual' and not core.DOWNLOADINFO: logger.debug( 'Adding TORRENT download info for directory {0} to database'. format(inputDirectory)) myDB = nzbToMediaDB.DBConnection() inputDirectory1 = inputDirectory inputName1 = inputName try: encoded, inputDirectory1 = CharReplace(inputDirectory) encoded, inputName1 = CharReplace(inputName) except: pass controlValueDict = {"input_directory": text_type(inputDirectory1)} newValueDict = { "input_name": text_type(inputName1), "input_hash": text_type(inputHash), "input_id": text_type(inputID), "client_agent": text_type(clientAgent), "status": 0, "last_update": datetime.date.today().toordinal() } myDB.upsert("downloads", newValueDict, controlValueDict) logger.debug("Received Directory: {0} | Name: {1} | Category: {2}".format( inputDirectory, inputName, inputCategory)) # Confirm the category by parsing directory structure inputDirectory, inputName, inputCategory, root = core.category_search( inputDirectory, inputName, inputCategory, root, core.CATEGORIES) if inputCategory == "": inputCategory = "UNCAT" usercat = inputCategory try: inputName = inputName.encode(core.SYS_ENCODING) except UnicodeError: pass try: inputDirectory = inputDirectory.encode(core.SYS_ENCODING) except UnicodeError: pass logger.debug( "Determined Directory: {0} | Name: {1} | Category: {2}".format( inputDirectory, inputName, inputCategory)) # auto-detect section section = core.CFG.findsection(inputCategory).isenabled() if section is None: section = core.CFG.findsection("ALL").isenabled() if section is None: logger.error( 'Category:[{0}] is not defined or is not enabled. ' 'Please rename it or ensure it is enabled for the appropriate section ' 'in your autoProcessMedia.cfg and try again.'.format( inputCategory)) return [-1, ""] else: usercat = "ALL" if len(section) > 1: logger.error( 'Category:[{0}] is not unique, {1} are using it. ' 'Please rename it or disable all other sections using the same category name ' 'in your autoProcessMedia.cfg and try again.'.format( usercat, section.keys())) return [-1, ""] if section: sectionName = section.keys()[0] logger.info('Auto-detected SECTION:{0}'.format(sectionName)) else: logger.error("Unable to locate a section with subsection:{0} " "enabled in your autoProcessMedia.cfg, exiting!".format( inputCategory)) return [-1, ""] section = dict( section[sectionName] [usercat]) # Type cast to dict() to allow effective usage of .get() Torrent_NoLink = int(section.get("Torrent_NoLink", 0)) keep_archive = int(section.get("keep_archive", 0)) extract = int(section.get('extract', 0)) extensions = section.get('user_script_mediaExtensions', "").lower().split(',') uniquePath = int(section.get("unique_path", 1)) if clientAgent != 'manual': core.pause_torrent(clientAgent, inputHash, inputID, inputName) # In case input is not directory, make sure to create one. # This way Processing is isolated. if not os.path.isdir(os.path.join(inputDirectory, inputName)): basename = os.path.basename(inputDirectory) basename = core.sanitizeName(inputName) \ if inputName == basename else os.path.splitext(core.sanitizeName(inputName))[0] outputDestination = os.path.join(core.OUTPUTDIRECTORY, inputCategory, basename) elif uniquePath: outputDestination = os.path.normpath( core.os.path.join(core.OUTPUTDIRECTORY, inputCategory, core.sanitizeName(inputName).replace(" ", "."))) else: outputDestination = os.path.normpath( core.os.path.join(core.OUTPUTDIRECTORY, inputCategory)) try: outputDestination = outputDestination.encode(core.SYS_ENCODING) except UnicodeError: pass if outputDestination in inputDirectory: outputDestination = inputDirectory logger.info("Output directory set to: {0}".format(outputDestination)) if core.SAFE_MODE and outputDestination == core.TORRENT_DEFAULTDIR: logger.error( 'The output directory:[{0}] is the Download Directory. ' 'Edit outputDirectory in autoProcessMedia.cfg. Exiting'.format( inputDirectory)) return [-1, ""] logger.debug("Scanning files in directory: {0}".format(inputDirectory)) if sectionName in ['HeadPhones', 'Lidarr']: core.NOFLATTEN.extend( inputCategory ) # Make sure we preserve folder structure for HeadPhones. now = datetime.datetime.now() if extract == 1: inputFiles = core.listMediaFiles(inputDirectory, archives=False, other=True, otherext=extensions) else: inputFiles = core.listMediaFiles(inputDirectory, other=True, otherext=extensions) if len(inputFiles) == 0 and os.path.isfile(inputDirectory): inputFiles = [inputDirectory] logger.debug("Found 1 file to process: {0}".format(inputDirectory)) else: logger.debug("Found {0} files in {1}".format(len(inputFiles), inputDirectory)) for inputFile in inputFiles: filePath = os.path.dirname(inputFile) fileName, fileExt = os.path.splitext(os.path.basename(inputFile)) fullFileName = os.path.basename(inputFile) targetFile = core.os.path.join(outputDestination, fullFileName) if inputCategory in core.NOFLATTEN: if not os.path.basename(filePath) in outputDestination: targetFile = core.os.path.join( core.os.path.join(outputDestination, os.path.basename(filePath)), fullFileName) logger.debug( "Setting outputDestination to {0} to preserve folder structure" .format(os.path.dirname(targetFile))) try: targetFile = targetFile.encode(core.SYS_ENCODING) except UnicodeError: pass if root == 1: if not foundFile: logger.debug("Looking for {0} in: {1}".format( inputName, inputFile)) if any([ core.sanitizeName(inputName) in core.sanitizeName(inputFile), core.sanitizeName(fileName) in core.sanitizeName(inputName) ]): foundFile = True logger.debug( "Found file {0} that matches Torrent Name {1}".format( fullFileName, inputName)) else: continue if root == 2: mtime_lapse = now - datetime.datetime.fromtimestamp( os.path.getmtime(inputFile)) ctime_lapse = now - datetime.datetime.fromtimestamp( os.path.getctime(inputFile)) if not foundFile: logger.debug( "Looking for files with modified/created dates less than 5 minutes old." ) if (mtime_lapse < datetime.timedelta(minutes=5)) or ( ctime_lapse < datetime.timedelta(minutes=5)): foundFile = True logger.debug( "Found file {0} with date modified/created less than 5 minutes ago." .format(fullFileName)) else: continue # This file has not been recently moved or created, skip it if Torrent_NoLink == 0: try: core.copy_link(inputFile, targetFile, core.USELINK) core.rmReadOnly(targetFile) except: logger.error("Failed to link: {0} to {1}".format( inputFile, targetFile)) inputName, outputDestination = convert_to_ascii(inputName, outputDestination) if extract == 1: logger.debug( 'Checking for archives to extract in directory: {0}'.format( inputDirectory)) core.extractFiles(inputDirectory, outputDestination, keep_archive) if inputCategory not in core.NOFLATTEN: # don't flatten hp in case multi cd albums, and we need to copy this back later. core.flatten(outputDestination) # Now check if video files exist in destination: if sectionName in [ "SickBeard", "NzbDrone", "Sonarr", "CouchPotato", "Radarr" ]: numVideos = len( core.listMediaFiles(outputDestination, media=True, audio=False, meta=False, archives=False)) if numVideos > 0: logger.info("Found {0} media files in {1}".format( numVideos, outputDestination)) status = 0 elif extract != 1: logger.info( "Found no media files in {0}. Sending to {1} to process". format(outputDestination, sectionName)) status = 0 else: logger.warning( "Found no media files in {0}".format(outputDestination)) # Only these sections can handling failed downloads # so make sure everything else gets through without the check for failed if sectionName not in [ 'CouchPotato', 'Radarr', 'SickBeard', 'NzbDrone', 'Sonarr' ]: status = 0 logger.info("Calling {0}:{1} to post-process:{2}".format( sectionName, usercat, inputName)) if core.TORRENT_CHMOD_DIRECTORY: core.rchmod(outputDestination, core.TORRENT_CHMOD_DIRECTORY) result = [0, ""] if sectionName == 'UserScript': result = external_script(outputDestination, inputName, inputCategory, section) elif sectionName in ['CouchPotato', 'Radarr']: result = core.autoProcessMovie().process(sectionName, outputDestination, inputName, status, clientAgent, inputHash, inputCategory) elif sectionName in ['SickBeard', 'NzbDrone', 'Sonarr']: if inputHash: inputHash = inputHash.upper() result = core.autoProcessTV().processEpisode(sectionName, outputDestination, inputName, status, clientAgent, inputHash, inputCategory) elif sectionName in ['HeadPhones', 'Lidarr']: result = core.autoProcessMusic().process(sectionName, outputDestination, inputName, status, clientAgent, inputCategory) elif sectionName == 'Mylar': result = core.autoProcessComics().processEpisode( sectionName, outputDestination, inputName, status, clientAgent, inputCategory) elif sectionName == 'Gamez': result = core.autoProcessGames().process(sectionName, outputDestination, inputName, status, clientAgent, inputCategory) plex_update(inputCategory) if result[0] != 0: if not core.TORRENT_RESUME_ON_FAILURE: logger.error("A problem was reported in the autoProcess* script. " "Torrent won't resume seeding (settings)") elif clientAgent != 'manual': logger.error("A problem was reported in the autoProcess* script. " "If torrent was paused we will resume seeding") core.resume_torrent(clientAgent, inputHash, inputID, inputName) else: if clientAgent != 'manual': # update download status in our DB core.update_downloadInfoStatus(inputName, 1) # remove torrent if core.USELINK == 'move-sym' and not core.DELETE_ORIGINAL == 1: logger.debug( 'Checking for sym-links to re-direct in: {0}'.format( inputDirectory)) for dirpath, dirs, files in os.walk(inputDirectory): for file in files: logger.debug('Checking symlink: {0}'.format( os.path.join(dirpath, file))) replace_links(os.path.join(dirpath, file)) core.remove_torrent(clientAgent, inputHash, inputID, inputName) if not sectionName == 'UserScript': # for user script, we assume this is cleaned by the script or option USER_SCRIPT_CLEAN # cleanup our processing folders of any misc unwanted files and empty directories core.cleanDir(outputDestination, sectionName, inputCategory) return result
def main(args): # Initialize the config core.initialize() # clientAgent for Torrents clientAgent = core.TORRENT_CLIENTAGENT logger.info("#########################################################") logger.info("## ..::[{0}]::.. ##".format(os.path.basename(__file__))) logger.info("#########################################################") # debug command line options logger.debug("Options passed into TorrentToMedia: {0}".format(args)) # Post-Processing Result result = [0, ""] try: inputDirectory, inputName, inputCategory, inputHash, inputID = core.parse_args(clientAgent, args) except: logger.error("There was a problem loading variables") return -1 if inputDirectory and inputName and inputHash and inputID: result = processTorrent(inputDirectory, inputName, inputCategory, inputHash, inputID, clientAgent) else: # Perform Manual Post-Processing logger.warning("Invalid number of arguments received from client, Switching to manual run mode ...") for section, subsections in core.SECTIONS.items(): for subsection in subsections: if not core.CFG[section][subsection].isenabled(): continue for dirName in core.getDirs(section, subsection, link='hard'): logger.info("Starting manual run for {0}:{1} - Folder:{2}".format (section, subsection, dirName)) logger.info("Checking database for download info for {0} ...".format (os.path.basename(dirName))) core.DOWNLOADINFO = core.get_downloadInfo(os.path.basename(dirName), 0) if core.DOWNLOADINFO: clientAgent = text_type(core.DOWNLOADINFO[0].get('client_agent', 'manual')) inputHash = text_type(core.DOWNLOADINFO[0].get('input_hash', '')) inputID = text_type(core.DOWNLOADINFO[0].get('input_id', '')) logger.info("Found download info for {0}, " "setting variables now ...".format(os.path.basename(dirName))) else: logger.info('Unable to locate download info for {0}, ' 'continuing to try and process this release ...'.format (os.path.basename(dirName))) clientAgent = 'manual' inputHash = '' inputID = '' if clientAgent.lower() not in core.TORRENT_CLIENTS: continue try: dirName = dirName.encode(core.SYS_ENCODING) except UnicodeError: pass inputName = os.path.basename(dirName) try: inputName = inputName.encode(core.SYS_ENCODING) except UnicodeError: pass results = processTorrent(dirName, inputName, subsection, inputHash or None, inputID or None, clientAgent) if results[0] != 0: logger.error("A problem was reported when trying to perform a manual run for {0}:{1}.".format (section, subsection)) result = results if result[0] == 0: logger.info("The {0} script completed successfully.".format(args[0])) else: logger.error("A problem was reported in the {0} script.".format(args[0])) del core.MYAPP return result[0]
def processTorrent(inputDirectory, inputName, inputCategory, inputHash, inputID, clientAgent): status = 1 # 1 = failed | 0 = success root = 0 foundFile = 0 if clientAgent != 'manual' and not core.DOWNLOADINFO: logger.debug('Adding TORRENT download info for directory {0} to database'.format(inputDirectory)) myDB = nzbToMediaDB.DBConnection() inputDirectory1 = inputDirectory inputName1 = inputName try: encoded, inputDirectory1 = CharReplace(inputDirectory) encoded, inputName1 = CharReplace(inputName) except: pass controlValueDict = {"input_directory": text_type(inputDirectory1)} newValueDict = {"input_name": text_type(inputName1), "input_hash": text_type(inputHash), "input_id": text_type(inputID), "client_agent": text_type(clientAgent), "status": 0, "last_update": datetime.date.today().toordinal() } myDB.upsert("downloads", newValueDict, controlValueDict) logger.debug("Received Directory: {0} | Name: {1} | Category: {2}".format(inputDirectory, inputName, inputCategory)) # Confirm the category by parsing directory structure inputDirectory, inputName, inputCategory, root = core.category_search(inputDirectory, inputName, inputCategory, root, core.CATEGORIES) if inputCategory == "": inputCategory = "UNCAT" usercat = inputCategory try: inputName = inputName.encode(core.SYS_ENCODING) except UnicodeError: pass try: inputDirectory = inputDirectory.encode(core.SYS_ENCODING) except UnicodeError: pass logger.debug("Determined Directory: {0} | Name: {1} | Category: {2}".format (inputDirectory, inputName, inputCategory)) # auto-detect section section = core.CFG.findsection(inputCategory).isenabled() if section is None: section = core.CFG.findsection("ALL").isenabled() if section is None: logger.error('Category:[{0}] is not defined or is not enabled. ' 'Please rename it or ensure it is enabled for the appropriate section ' 'in your autoProcessMedia.cfg and try again.'.format (inputCategory)) return [-1, ""] else: usercat = "ALL" if len(section) > 1: logger.error('Category:[{0}] is not unique, {1} are using it. ' 'Please rename it or disable all other sections using the same category name ' 'in your autoProcessMedia.cfg and try again.'.format (usercat, section.keys())) return [-1, ""] if section: sectionName = section.keys()[0] logger.info('Auto-detected SECTION:{0}'.format(sectionName)) else: logger.error("Unable to locate a section with subsection:{0} " "enabled in your autoProcessMedia.cfg, exiting!".format (inputCategory)) return [-1, ""] section = dict(section[sectionName][usercat]) # Type cast to dict() to allow effective usage of .get() Torrent_NoLink = int(section.get("Torrent_NoLink", 0)) keep_archive = int(section.get("keep_archive", 0)) extract = int(section.get('extract', 0)) uniquePath = int(section.get("unique_path", 1)) if clientAgent != 'manual': core.pause_torrent(clientAgent, inputHash, inputID, inputName) # In case input is not directory, make sure to create one. # This way Processing is isolated. if not os.path.isdir(os.path.join(inputDirectory, inputName)): basename = os.path.basename(inputDirectory) basename = core.sanitizeName(inputName) \ if inputName == basename else os.path.splitext(core.sanitizeName(inputName))[0] outputDestination = os.path.join(core.OUTPUTDIRECTORY, inputCategory, basename) elif uniquePath: outputDestination = os.path.normpath( core.os.path.join(core.OUTPUTDIRECTORY, inputCategory, core.sanitizeName(inputName))) else: outputDestination = os.path.normpath( core.os.path.join(core.OUTPUTDIRECTORY, inputCategory)) try: outputDestination = outputDestination.encode(core.SYS_ENCODING) except UnicodeError: pass if outputDestination in inputDirectory: outputDestination = inputDirectory logger.info("Output directory set to: {0}".format(outputDestination)) if core.SAFE_MODE and outputDestination == core.TORRENT_DEFAULTDIR: logger.error('The output directory:[{0}] is the Download Directory. ' 'Edit outputDirectory in autoProcessMedia.cfg. Exiting'.format (inputDirectory)) return [-1, ""] logger.debug("Scanning files in directory: {0}".format(inputDirectory)) if sectionName == 'HeadPhones': core.NOFLATTEN.extend( inputCategory) # Make sure we preserve folder structure for HeadPhones. now = datetime.datetime.now() if extract == 1: inputFiles = core.listMediaFiles(inputDirectory, archives=False) else: inputFiles = core.listMediaFiles(inputDirectory) logger.debug("Found {0} files in {1}".format(len(inputFiles), inputDirectory)) for inputFile in inputFiles: filePath = os.path.dirname(inputFile) fileName, fileExt = os.path.splitext(os.path.basename(inputFile)) fullFileName = os.path.basename(inputFile) targetFile = core.os.path.join(outputDestination, fullFileName) if inputCategory in core.NOFLATTEN: if not os.path.basename(filePath) in outputDestination: targetFile = core.os.path.join( core.os.path.join(outputDestination, os.path.basename(filePath)), fullFileName) logger.debug("Setting outputDestination to {0} to preserve folder structure".format (os.path.dirname(targetFile))) try: targetFile = targetFile.encode(core.SYS_ENCODING) except UnicodeError: pass if root == 1: if not foundFile: logger.debug("Looking for {0} in: {1}".format(inputName, inputFile)) if any([core.sanitizeName(inputName) in core.sanitizeName(inputFile), core.sanitizeName(fileName) in core.sanitizeName(inputName)]): foundFile = True logger.debug("Found file {0} that matches Torrent Name {1}".format (fullFileName, inputName)) else: continue if root == 2: mtime_lapse = now - datetime.datetime.fromtimestamp(os.path.getmtime(inputFile)) ctime_lapse = now - datetime.datetime.fromtimestamp(os.path.getctime(inputFile)) if not foundFile: logger.debug("Looking for files with modified/created dates less than 5 minutes old.") if (mtime_lapse < datetime.timedelta(minutes=5)) or (ctime_lapse < datetime.timedelta(minutes=5)): foundFile = True logger.debug("Found file {0} with date modified/created less than 5 minutes ago.".format (fullFileName)) else: continue # This file has not been recently moved or created, skip it if Torrent_NoLink == 0: try: core.copy_link(inputFile, targetFile, core.USELINK) core.rmReadOnly(targetFile) except: logger.error("Failed to link: {0} to {1}".format(inputFile, targetFile)) inputName, outputDestination = convert_to_ascii(inputName, outputDestination) if extract == 1: logger.debug('Checking for archives to extract in directory: {0}'.format(inputDirectory)) core.extractFiles(inputDirectory, outputDestination, keep_archive) if inputCategory not in core.NOFLATTEN: # don't flatten hp in case multi cd albums, and we need to copy this back later. core.flatten(outputDestination) # Now check if video files exist in destination: if sectionName in ["SickBeard", "NzbDrone", "CouchPotato"]: numVideos = len( core.listMediaFiles(outputDestination, media=True, audio=False, meta=False, archives=False)) if numVideos > 0: logger.info("Found {0} media files in {1}".format(numVideos, outputDestination)) status = 0 elif extract != 1: logger.info("Found no media files in {0}. Sending to {1} to process".format(outputDestination, sectionName)) status = 0 else: logger.warning("Found no media files in {0}".format(outputDestination)) # Only these sections can handling failed downloads # so make sure everything else gets through without the check for failed if sectionName not in ['CouchPotato', 'SickBeard', 'NzbDrone']: status = 0 logger.info("Calling {0}:{1} to post-process:{2}".format(sectionName, usercat, inputName)) if core.TORRENT_CHMOD_DIRECTORY: core.rchmod(outputDestination, core.TORRENT_CHMOD_DIRECTORY) result = [0, ""] if sectionName == 'UserScript': result = external_script(outputDestination, inputName, inputCategory, section) elif sectionName == 'CouchPotato': result = core.autoProcessMovie().process(sectionName, outputDestination, inputName, status, clientAgent, inputHash, inputCategory) elif sectionName in ['SickBeard', 'NzbDrone']: if inputHash: inputHash = inputHash.upper() result = core.autoProcessTV().processEpisode(sectionName, outputDestination, inputName, status, clientAgent, inputHash, inputCategory) elif sectionName == 'HeadPhones': result = core.autoProcessMusic().process(sectionName, outputDestination, inputName, status, clientAgent, inputCategory) elif sectionName == 'Mylar': result = core.autoProcessComics().processEpisode(sectionName, outputDestination, inputName, status, clientAgent, inputCategory) elif sectionName == 'Gamez': result = core.autoProcessGames().process(sectionName, outputDestination, inputName, status, clientAgent, inputCategory) plex_update(inputCategory) if result[0] != 0: if not core.TORRENT_RESUME_ON_FAILURE: logger.error("A problem was reported in the autoProcess* script. " "Torrent won't resume seeding (settings)") elif clientAgent != 'manual': logger.error("A problem was reported in the autoProcess* script. " "If torrent was paused we will resume seeding") core.resume_torrent(clientAgent, inputHash, inputID, inputName) else: if clientAgent != 'manual': # update download status in our DB core.update_downloadInfoStatus(inputName, 1) # remove torrent if core.USELINK == 'move-sym' and not core.DELETE_ORIGINAL == 1: logger.debug('Checking for sym-links to re-direct in: {0}'.format(inputDirectory)) for dirpath, dirs, files in os.walk(inputDirectory): for file in files: logger.debug('Checking symlink: {0}'.format(os.path.join(dirpath, file))) replace_links(os.path.join(dirpath, file)) core.remove_torrent(clientAgent, inputHash, inputID, inputName) if not sectionName == 'UserScript': # for user script, we assume this is cleaned by the script or option USER_SCRIPT_CLEAN # cleanup our processing folders of any misc unwanted files and empty directories core.cleanDir(outputDestination, sectionName, inputCategory) return result