Added database support, we now store all variables passed into our scripts so that in the event something fails and you need to perform a manual run it can find those variables by a database lookup and re-use them.

This commit is contained in:
echel0n 2014-04-22 06:22:37 -07:00
commit 3f137ae3b6
16 changed files with 514 additions and 81 deletions

1
.gitignore vendored
View file

@ -6,3 +6,4 @@
/userscripts/ /userscripts/
/logs/ /logs/
/.idea/ /.idea/
*.db

View file

@ -15,8 +15,8 @@ from nzbtomedia.autoProcess.autoProcessMusic import autoProcessMusic
from nzbtomedia.autoProcess.autoProcessTV import autoProcessTV from nzbtomedia.autoProcess.autoProcessTV import autoProcessTV
from nzbtomedia.nzbToMediaUtil import category_search, sanitizeFileName, copy_link, parse_args, flatten, get_dirnames, \ from nzbtomedia.nzbToMediaUtil import category_search, sanitizeFileName, copy_link, parse_args, flatten, get_dirnames, \
remove_read_only, pause_torrent, resume_torrent, listMediaFiles, joinPath, \ remove_read_only, pause_torrent, resume_torrent, listMediaFiles, joinPath, \
extractFiles, cleanProcDirs, append_downloadID extractFiles, cleanProcDirs, update_downloadInfoStatus, get_downloadInfo
from nzbtomedia import logger from nzbtomedia import logger, nzbToMediaDB
def processTorrent(inputDirectory, inputName, inputCategory, inputHash, inputID, clientAgent): def processTorrent(inputDirectory, inputName, inputCategory, inputHash, inputID, clientAgent):
status = 1 # 1 = failed | 0 = success status = 1 # 1 = failed | 0 = success
@ -25,12 +25,28 @@ def processTorrent(inputDirectory, inputName, inputCategory, inputHash, inputID,
foundFile = 0 foundFile = 0
copy_list = [] copy_list = []
if clientAgent != 'manual':
logger.debug('Adding TORRENT download info for directory %s to database' % (inputDirectory))
myDB = nzbToMediaDB.DBConnection()
controlValueDict = {"input_directory": inputDirectory}
newValueDict = {"input_name": inputName,
"input_hash": inputHash,
"input_id": inputID,
"client_agent": clientAgent,
"status": 0,
"last_update": datetime.date.today().toordinal()
}
myDB.upsert("downloads", newValueDict, controlValueDict)
logger.debug("Received Directory: %s | Name: %s | Category: %s" % (inputDirectory, inputName, inputCategory)) logger.debug("Received Directory: %s | Name: %s | Category: %s" % (inputDirectory, inputName, inputCategory))
inputDirectory, inputName, inputCategory, root, single = category_search(inputDirectory, inputName, inputCategory, root, nzbtomedia.CATEGORIES) # Confirm the category by parsing directory structure inputDirectory, inputName, inputCategory, root, single = category_search(inputDirectory, inputName, inputCategory, root, nzbtomedia.CATEGORIES) # Confirm the category by parsing directory structure
logger.debug("Determined Directory: %s | Name: %s | Category: %s" % (inputDirectory, inputName, inputCategory)) logger.debug("Determined Directory: %s | Name: %s | Category: %s" % (inputDirectory, inputName, inputCategory))
# Add torrent info hash to folder name incase we need it later on
section = nzbtomedia.CFG.findsection(inputCategory) section = nzbtomedia.CFG.findsection(inputCategory)
if not section: if not section:
logger.error( logger.error(
@ -49,11 +65,6 @@ def processTorrent(inputDirectory, inputName, inputCategory, inputHash, inputID,
inputCategory = "UNCAT" inputCategory = "UNCAT"
outputDestination = os.path.normpath(joinPath(nzbtomedia.OUTPUTDIRECTORY, inputCategory, sanitizeFileName(inputName))) outputDestination = os.path.normpath(joinPath(nzbtomedia.OUTPUTDIRECTORY, inputCategory, sanitizeFileName(inputName)))
# Add torrent info hash to folder name incase we need it later on
if clientAgent != 'manual':
logger.debug('Added torrent info hash %s to output directory %s' % (inputHash, outputDestination))
outputDestination = append_downloadID(outputDestination, inputHash)
logger.info("Output directory set to: %s" % (outputDestination)) logger.info("Output directory set to: %s" % (outputDestination))
processOnly = nzbtomedia.CFG[nzbtomedia.SECTIONS].sections processOnly = nzbtomedia.CFG[nzbtomedia.SECTIONS].sections
@ -185,6 +196,9 @@ def processTorrent(inputDirectory, inputName, inputCategory, inputHash, inputID,
logger.error("A problem was reported in the autoProcess* script. If torrent was paused we will resume seeding") logger.error("A problem was reported in the autoProcess* script. If torrent was paused we will resume seeding")
resume_torrent(clientAgent, inputHash, inputID, result, inputName) resume_torrent(clientAgent, inputHash, inputID, result, inputName)
else: else:
# update download status in our DB
update_downloadInfoStatus(inputDirectory, 1)
# cleanup our processing folders of any misc unwanted files and empty directories # cleanup our processing folders of any misc unwanted files and empty directories
cleanProcDirs() cleanProcDirs()
@ -289,16 +303,26 @@ def main(args):
if inputDirectory and inputName and inputHash and inputID: if inputDirectory and inputName and inputHash and inputID:
result = processTorrent(inputDirectory, inputName, inputCategory, inputHash, inputID, clientAgent) result = processTorrent(inputDirectory, inputName, inputCategory, inputHash, inputID, clientAgent)
else: else:
# Perform Manual Run # Perform Manual Post-Processing
logger.warning("Invalid number of arguments received from client, Switching to manual run mode ...") logger.warning("Invalid number of arguments received from client, Switching to manual run mode ...")
# Loop and auto-process
clientAgent = 'manual'
for section, subsection in nzbtomedia.SUBSECTIONS.items(): for section, subsection in nzbtomedia.SUBSECTIONS.items():
for category in subsection: for category in subsection:
if nzbtomedia.CFG[section][category].isenabled(): if nzbtomedia.CFG[section][category].isenabled():
dirNames = get_dirnames(section, category) dirNames = get_dirnames(section, category)
for dirName in dirNames: for dirName in dirNames:
clientAgent = 'manual'
inputHash = None
inputID = None
logger.info("Checking database for download info ...")
downloadInfo = get_downloadInfo(dirName, 0)
if downloadInfo:
clientAgent = downloadInfo['client_agent']
inputHash = downloadInfo['input_hash']
inputID = downloadInfo['input_id']
logger.info("Found download info for directory %s, setting variables now ..." % (dirName))
logger.info("Running %s:%s as a manual run for folder %s ..." % (section, category, dirName)) logger.info("Running %s:%s as a manual run for folder %s ..." % (section, category, dirName))
results = processTorrent(dirName, os.path.basename(dirName), category, inputHash, inputID, clientAgent) results = processTorrent(dirName, os.path.basename(dirName), category, inputHash, inputID, clientAgent)
if results != 0: if results != 0:

View file

@ -15,6 +15,8 @@
force_clean = 0 force_clean = 0
# Enable/Disable logging debug messages to nzbtomedia.log # Enable/Disable logging debug messages to nzbtomedia.log
log_debug = 0 log_debug = 0
# Enable/Disable logging database messages to nzbtomedia.log
log_db = 0
# Set to where your ffmpeg/ffprobe executables are located # Set to where your ffmpeg/ffprobe executables are located
ffmpeg_path = ffmpeg_path =

View file

@ -145,7 +145,7 @@ Impacts Torrents
Fixed an import error when extracting Fixed an import error when extracting
Impacts NZBs Impacts NZBs
Fixed passthrough of nzbName from NZBGet to pass the .nzb extension (required for SickBeard's failed fork) Fixed passthrough of inputName from NZBGet to pass the .nzb extension (required for SickBeard's failed fork)
V8.0 28/04/2013 V8.0 28/04/2013

View file

@ -275,36 +275,52 @@
############################################################################## ##############################################################################
import os import os
import sys import sys
import datetime
import nzbtomedia import nzbtomedia
from nzbtomedia.autoProcess.autoProcessComics import autoProcessComics from nzbtomedia.autoProcess.autoProcessComics import autoProcessComics
from nzbtomedia.autoProcess.autoProcessGames import autoProcessGames from nzbtomedia.autoProcess.autoProcessGames import autoProcessGames
from nzbtomedia.autoProcess.autoProcessMovie import autoProcessMovie from nzbtomedia.autoProcess.autoProcessMovie import autoProcessMovie
from nzbtomedia.autoProcess.autoProcessMusic import autoProcessMusic from nzbtomedia.autoProcess.autoProcessMusic import autoProcessMusic
from nzbtomedia.autoProcess.autoProcessTV import autoProcessTV from nzbtomedia.autoProcess.autoProcessTV import autoProcessTV
from nzbtomedia.nzbToMediaUtil import get_dirnames, extractFiles, cleanProcDirs from nzbtomedia.nzbToMediaUtil import get_dirnames, extractFiles, cleanProcDirs, update_downloadInfoStatus, get_downloadInfo
from nzbtomedia import logger from nzbtomedia import logger, nzbToMediaDB
# post-processing # post-processing
def process(nzbDir, inputName=None, status=0, clientAgent='manual', download_id=None, inputCategory=None): def process(inputDirectory, inputName=None, status=0, clientAgent='manual', download_id=None, inputCategory=None):
if clientAgent != 'manual':
logger.debug('Adding NZB download info for directory %s to database' % (inputDirectory))
myDB = nzbToMediaDB.DBConnection()
controlValueDict = {"input_directory": inputDirectory}
newValueDict = {"input_name": inputName,
"input_hash": download_id,
"input_id": download_id,
"client_agent": clientAgent,
"status": 0,
"last_update": datetime.date.today().toordinal()
}
myDB.upsert("downloads", newValueDict, controlValueDict)
# auto-detect section # auto-detect section
section = nzbtomedia.CFG.findsection(inputCategory) section = nzbtomedia.CFG.findsection(inputCategory)
if section: if section:
if nzbtomedia.CFG[section][inputCategory]['extract']: if nzbtomedia.CFG[section][inputCategory]['extract']:
logger.debug('Checking for archives to extract in directory: %s' % (nzbDir)) logger.debug('Checking for archives to extract in directory: %s' % (inputDirectory))
extractFiles(nzbDir) extractFiles(inputDirectory)
logger.info("Sending %s to %s for post-processing ..." % (inputName, str(section).upper())) logger.info("Sending %s to %s for post-processing ..." % (inputName, str(section).upper()))
if nzbtomedia.CFG["CouchPotato"][inputCategory]: if nzbtomedia.CFG["CouchPotato"][inputCategory]:
result = autoProcessMovie().process(nzbDir, inputName, status, clientAgent, download_id, inputCategory) result = autoProcessMovie().process(inputDirectory, inputName, status, clientAgent, download_id, inputCategory)
elif nzbtomedia.CFG["SickBeard", "NzbDrone"][inputCategory]: elif nzbtomedia.CFG["SickBeard", "NzbDrone"][inputCategory]:
result = autoProcessTV().processEpisode(nzbDir, inputName, status, clientAgent, inputCategory) result = autoProcessTV().processEpisode(inputDirectory, inputName, status, clientAgent, inputCategory)
elif nzbtomedia.CFG["HeadPhones"][inputCategory]: elif nzbtomedia.CFG["HeadPhones"][inputCategory]:
result = autoProcessMusic().process(nzbDir, inputName, status, clientAgent, inputCategory) result = autoProcessMusic().process(inputDirectory, inputName, status, clientAgent, inputCategory)
elif nzbtomedia.CFG["Mylar"][inputCategory]: elif nzbtomedia.CFG["Mylar"][inputCategory]:
result = autoProcessComics().processEpisode(nzbDir, inputName, status, clientAgent, inputCategory) result = autoProcessComics().processEpisode(inputDirectory, inputName, status, clientAgent, inputCategory)
elif nzbtomedia.CFG["Gamez"][inputCategory]: elif nzbtomedia.CFG["Gamez"][inputCategory]:
result = autoProcessGames().process(nzbDir, inputName, status, clientAgent, inputCategory) result = autoProcessGames().process(inputDirectory, inputName, status, clientAgent, inputCategory)
else: else:
result = -1 result = -1
else: else:
@ -312,6 +328,9 @@ def process(nzbDir, inputName=None, status=0, clientAgent='manual', download_id=
result = -1 result = -1
if result == 0: if result == 0:
# update download status in our DB
update_downloadInfoStatus(inputDirectory, 1)
# cleanup our processing folders of any misc unwanted files and empty directories # cleanup our processing folders of any misc unwanted files and empty directories
cleanProcDirs() cleanProcDirs()
@ -412,18 +431,26 @@ def main(args, section=None):
logger.info("Script triggered from SABnzbd 0.7.17+") 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='') result = process(args[1], inputName=args[2], status=args[7], inputCategory=args[5], clientAgent=clientAgent, download_id='')
else: else:
# Perform Manual Run # Perform Manual Post-Processing
logger.warning("Invalid number of arguments received from client, Switching to manual run mode ...") logger.warning("Invalid number of arguments received from client, Switching to manual run mode ...")
# Loop and auto-process
clientAgent = 'manual'
for section, subsection in nzbtomedia.SUBSECTIONS.items(): for section, subsection in nzbtomedia.SUBSECTIONS.items():
for category in subsection: for category in subsection:
if nzbtomedia.CFG[section][category].isenabled(): if nzbtomedia.CFG[section][category].isenabled():
dirNames = get_dirnames(section, category) dirNames = get_dirnames(section, category)
for dirName in dirNames: for dirName in dirNames:
clientAgent = 'manual'
download_id = None
logger.info("Checking database for download info ...")
downloadInfo = get_downloadInfo(dirName, 0)
if downloadInfo:
clientAgent = downloadInfo['client_agent']
download_id = downloadInfo['input_id']
logger.info("Found download info for directory %s, setting variables now ..." % (dirName))
logger.info("Starting manual run for %s:%s - Folder:%s" % (section, category, dirName)) logger.info("Starting manual run for %s:%s - Folder:%s" % (section, category, dirName))
results = process(dirName, os.path.basename(dirName), 0, clientAgent=clientAgent, inputCategory=category) results = process(dirName, os.path.basename(dirName), 0, clientAgent=clientAgent, download_id=download_id, inputCategory=category)
if results != 0: if results != 0:
logger.error("A problem was reported when trying to perform a manual run for %s:%s." % (section, category)) logger.error("A problem was reported when trying to perform a manual run for %s:%s." % (section, category))
result = results result = results

View file

@ -20,10 +20,11 @@ CONFIG_TV_FILE = os.path.join(PROGRAM_DIR, 'autoProcessTv.cfg')
# add our custom libs to the system path # add our custom libs to the system path
sys.path.insert(0, LIBS_DIR) sys.path.insert(0, LIBS_DIR)
from nzbtomedia import logger, versionCheck from nzbtomedia import logger, versionCheck, nzbToMediaDB
from nzbtomedia.nzbToMediaConfig import config from nzbtomedia.nzbToMediaConfig import config
from nzbtomedia.nzbToMediaUtil import WakeUp, makeDir, joinPath, cleanProcDirs, create_torrent_class, listMediaFiles from nzbtomedia.nzbToMediaUtil import WakeUp, makeDir, joinPath, cleanProcDirs, create_torrent_class, listMediaFiles
from nzbtomedia.transcoder import transcoder from nzbtomedia.transcoder import transcoder
from nzbtomedia.databases import mainDB
# sabnzbd constants # sabnzbd constants
SABNZB_NO_OF_ARGUMENTS = 8 SABNZB_NO_OF_ARGUMENTS = 8
@ -48,6 +49,7 @@ NZBGET_POSTPROCESS_NONE = 95
CFG = None CFG = None
LOG_DEBUG = None LOG_DEBUG = None
LOG_DB = None
SYS_ENCODING = None SYS_ENCODING = None
AUTO_UPDATE = None AUTO_UPDATE = None
@ -139,7 +141,7 @@ def initialize(section=None):
SECTIONS, SUBSECTIONS, USER_SCRIPT_CATEGORIES, __INITIALIZED__, AUTO_UPDATE, APP_FILENAME, USER_DELAY, USER_SCRIPT_RUNONCE, \ SECTIONS, SUBSECTIONS, USER_SCRIPT_CATEGORIES, __INITIALIZED__, AUTO_UPDATE, APP_FILENAME, USER_DELAY, USER_SCRIPT_RUNONCE, \
APP_NAME,USER_SCRIPT_MEDIAEXTENSIONS, USER_SCRIPT, USER_SCRIPT_PARAM, USER_SCRIPT_SUCCESSCODES, USER_SCRIPT_CLEAN, \ APP_NAME,USER_SCRIPT_MEDIAEXTENSIONS, USER_SCRIPT, USER_SCRIPT_PARAM, USER_SCRIPT_SUCCESSCODES, USER_SCRIPT_CLEAN, \
TRANSCODE, GIT_PATH, GIT_USER, GIT_BRANCH, GIT_REPO, SYS_ENCODING, NZB_CLIENTAGENT, SABNZBDHOST, SABNZBDPORT, SABNZBDAPIKEY, \ TRANSCODE, GIT_PATH, GIT_USER, GIT_BRANCH, GIT_REPO, SYS_ENCODING, NZB_CLIENTAGENT, SABNZBDHOST, SABNZBDPORT, SABNZBDAPIKEY, \
DUPLICATE, IGNOREEXTENSIONS, OUTPUTVIDEOEXTENSION, OUTPUTVIDEOCODEC, OUTPUTVIDEOPRESET, OUTPUTVIDEOFRAMERATE, \ DUPLICATE, IGNOREEXTENSIONS, OUTPUTVIDEOEXTENSION, OUTPUTVIDEOCODEC, OUTPUTVIDEOPRESET, OUTPUTVIDEOFRAMERATE, LOG_DB, \
OUTPUTVIDEOBITRATE, OUTPUTAUDIOCODEC, OUTPUTAUDIOBITRATE, OUTPUTSUBTITLECODEC, OUTPUTFASTSTART, OUTPUTQUALITYPERCENT, \ OUTPUTVIDEOBITRATE, OUTPUTAUDIOCODEC, OUTPUTAUDIOBITRATE, OUTPUTSUBTITLECODEC, OUTPUTFASTSTART, OUTPUTQUALITYPERCENT, \
NICENESS, LOG_DEBUG, FORCE_CLEAN, FFMPEG_PATH, FFMPEG, FFPROBE, AUDIOCONTAINER, EXTCONTAINER, TORRENT_CLASS, DELETE_ORIGINAL NICENESS, LOG_DEBUG, FORCE_CLEAN, FFMPEG_PATH, FFMPEG, FFPROBE, AUDIOCONTAINER, EXTCONTAINER, TORRENT_CLASS, DELETE_ORIGINAL
@ -184,13 +186,16 @@ def initialize(section=None):
if not config.addnzbget(): if not config.addnzbget():
logger.error("Unable to migrate NzbGet config file %s, exiting ..." % (CONFIG_FILE)) logger.error("Unable to migrate NzbGet config file %s, exiting ..." % (CONFIG_FILE))
sys.exit(-1) sys.exit(-1)
# load newly migrated config # load newly migrated config
logger.info("Loading config from [%s]" % (CONFIG_FILE)) logger.info("Loading config from [%s]" % (CONFIG_FILE))
CFG = config() CFG = config()
# Enable/Disable DEBUG Logging # Enable/Disable DEBUG Logging
LOG_DEBUG = int(CFG['General']['log_debug']) LOG_DEBUG = int(CFG['General']['log_debug'])
LOG_DB = int(CFG['General']['log_db'])
# initialize the main SB database
nzbToMediaDB.upgradeDatabase(nzbToMediaDB.DBConnection(), mainDB.InitialSchema)
# Set Version and GIT variables # Set Version and GIT variables
NZBTOMEDIA_VERSION = '9.3' NZBTOMEDIA_VERSION = '9.3'

View file

@ -6,7 +6,7 @@ from nzbtomedia.nzbToMediaUtil import convert_to_ascii, joinPath
from nzbtomedia import logger from nzbtomedia import logger
class autoProcessComics: class autoProcessComics:
def processEpisode(self, dirName, nzbName=None, status=0, clientAgent='manual', inputCategory=None): def processEpisode(self, dirName, inputName=None, status=0, clientAgent='manual', inputCategory=None):
# auto-detect correct section # auto-detect correct section
section = nzbtomedia.CFG.findsection(inputCategory) section = nzbtomedia.CFG.findsection(inputCategory)
if not section: if not section:
@ -34,15 +34,15 @@ class autoProcessComics:
except: except:
remote_path = None remote_path = None
nzbName, dirName = convert_to_ascii(nzbName, dirName) inputName, dirName = convert_to_ascii(inputName, dirName)
params = {} params = {}
params['nzb_folder'] = dirName params['nzb_folder'] = dirName
if remote_path: if remote_path:
params['nzb_folder'] = joinPath(remote_path, os.path.basename(dirName)) params['nzb_folder'] = joinPath(remote_path, os.path.basename(dirName))
if nzbName != None: if inputName != None:
params['nzb_name'] = nzbName params['nzb_name'] = inputName
if ssl: if ssl:
protocol = "https://" protocol = "https://"

View file

@ -4,7 +4,7 @@ from nzbtomedia.nzbToMediaUtil import convert_to_ascii
from nzbtomedia import logger from nzbtomedia import logger
class autoProcessGames: class autoProcessGames:
def process(self, dirName, nzbName=None, status=0, clientAgent='manual', inputCategory=None): def process(self, dirName, inputName=None, status=0, clientAgent='manual', 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
@ -37,11 +37,11 @@ class autoProcessGames:
else: else:
protocol = "http://" protocol = "http://"
nzbName, dirName = convert_to_ascii(nzbName, dirName) inputName, dirName = convert_to_ascii(inputName, dirName)
url = "%s%s:%s%s/api" % (protocol, host, port, web_root) url = "%s%s:%s%s/api" % (protocol, host, port, web_root)
fields = nzbName.split("-") fields = inputName.split("-")
gamezID = fields[0].replace("[","").replace("]","").replace(" ","") gamezID = fields[0].replace("[","").replace("]","").replace(" ","")

View file

@ -87,7 +87,7 @@ class autoProcessMovie:
return results return results
def process(self, dirName, nzbName=None, status=0, clientAgent="manual", download_id="", inputCategory=None): def process(self, dirName, inputName=None, status=0, clientAgent="manual", download_id="", inputCategory=None):
# auto-detect correct section # auto-detect correct section
section = nzbtomedia.CFG.findsection(inputCategory) section = nzbtomedia.CFG.findsection(inputCategory)
if not section: if not section:
@ -128,7 +128,7 @@ class autoProcessMovie:
baseURL = "%s%s:%s%s/api/%s" % (protocol, host, port, web_root, apikey) baseURL = "%s%s:%s%s/api/%s" % (protocol, host, port, web_root, apikey)
imdbid = find_imdbid(dirName, nzbName) imdbid = find_imdbid(dirName, inputName)
release = self.get_release(baseURL, imdbid, download_id) release = self.get_release(baseURL, imdbid, download_id)
# pull info from release found if available # pull info from release found if available
@ -146,8 +146,8 @@ class autoProcessMovie:
except: except:
pass pass
process_all_exceptions(nzbName.lower(), dirName) process_all_exceptions(inputName.lower(), dirName)
nzbName, dirName = convert_to_ascii(nzbName, dirName) inputName, dirName = convert_to_ascii(inputName, dirName)
if status == 0: if status == 0:
if nzbtomedia.TRANSCODE == 1: if nzbtomedia.TRANSCODE == 1:
@ -175,7 +175,7 @@ class autoProcessMovie:
logger.debug("Opening URL: %s" % (url), section) logger.debug("Opening URL: %s" % (url), section)
logger.postprocess("Starting %s scan for %s" % (method, nzbName), section) logger.postprocess("Starting %s scan for %s" % (method, inputName), section)
try: try:
r = requests.get(url, params=params) r = requests.get(url, params=params)
@ -195,18 +195,18 @@ class autoProcessMovie:
if not release: if not release:
return 0 return 0
else: else:
logger.postprocess("FAILED DOWNLOAD DETECTED FOR %s" % (nzbName), section) logger.postprocess("FAILED DOWNLOAD DETECTED FOR %s" % (inputName), section)
if delete_failed and os.path.isdir(dirName) and not os.path.dirname(dirName) == dirName: if delete_failed and os.path.isdir(dirName) and not os.path.dirname(dirName) == dirName:
logger.postprocess("Deleting failed files and folder %s" % dirName, section) logger.postprocess("Deleting failed files and folder %s" % dirName, section)
rmDir(dirName) rmDir(dirName)
if not download_id: if not download_id:
logger.error("Could not find a downloaded movie in the database matching %s, exiting!" % nzbName, logger.error("Could not find a downloaded movie in the database matching %s, exiting!" % inputName,
section) section)
return 1 # failure return 1 # failure
logger.postprocess("Setting failed release %s to ignored ..." % (nzbName), section) logger.postprocess("Setting failed release %s to ignored ..." % (inputName), section)
url = baseURL + "/release.ignore" url = baseURL + "/release.ignore"
logger.debug("Opening URL: %s" % (url), section) logger.debug("Opening URL: %s" % (url), section)
@ -219,9 +219,9 @@ class autoProcessMovie:
result = r.json() result = r.json()
if result['success']: if result['success']:
logger.postprocess("SUCCESS: %s has been set to ignored ..." % (nzbName), section) logger.postprocess("SUCCESS: %s has been set to ignored ..." % (inputName), section)
else: else:
logger.warning("FAILED: Unable to set %s to ignored!" % (nzbName), section) logger.warning("FAILED: Unable to set %s to ignored!" % (inputName), section)
logger.postprocess("Trying to snatch the next highest ranked release.", section) logger.postprocess("Trying to snatch the next highest ranked release.", section)
@ -239,7 +239,7 @@ class autoProcessMovie:
logger.postprocess("SUCCESS: Snatched the next highest release ...", section) logger.postprocess("SUCCESS: Snatched the next highest release ...", section)
return 0 return 0
else: else:
logger.postprocess("FAILED: Unable to find a higher ranked release then %s to snatch!" % (nzbName), logger.postprocess("FAILED: Unable to find a higher ranked release then %s to snatch!" % (inputName),
section) section)
return 1 return 1
@ -253,7 +253,7 @@ class autoProcessMovie:
release_status_new = release[release_id]['status'] release_status_new = release[release_id]['status']
if release_status_new != release_status_old: if release_status_new != release_status_old:
logger.postprocess("SUCCESS: Release %s has now been marked with a status of [%s]" % ( logger.postprocess("SUCCESS: Release %s has now been marked with a status of [%s]" % (
nzbName, str(release_status_new).upper()), section) inputName, str(release_status_new).upper()), section)
return 0 # success return 0 # success
except: except:
pass pass
@ -263,6 +263,6 @@ class autoProcessMovie:
# The status hasn't changed. we have waited 2 minutes which is more than enough. uTorrent can resule seeding now. # The status hasn't changed. we have waited 2 minutes which is more than enough. uTorrent can resule seeding now.
logger.warning( logger.warning(
"%s does not appear to have changed status after %s minutes, Please check your logs." % (nzbName, wait_for), "%s does not appear to have changed status after %s minutes, Please check your logs." % (inputName, wait_for),
section) section)
return 1 # failure return 1 # failure

View file

@ -30,7 +30,7 @@ class autoProcessMusic:
return album["Status"].lower() return album["Status"].lower()
except:pass except:pass
def process(self, dirName, nzbName=None, status=0, clientAgent="manual", inputCategory=None): def process(self, dirName, inputName=None, status=0, clientAgent="manual", inputCategory=None):
# auto-detect correct section # auto-detect correct section
section = nzbtomedia.CFG.findsection(inputCategory) section = nzbtomedia.CFG.findsection(inputCategory)
if len(section) == 0: if len(section) == 0:
@ -64,7 +64,7 @@ class autoProcessMusic:
else: else:
protocol = "http://" protocol = "http://"
nzbName, dirName = convert_to_ascii(nzbName, dirName) inputName, dirName = convert_to_ascii(inputName, dirName)
url = "%s%s:%s%s/api" % (protocol,host,port,web_root) url = "%s%s:%s%s/api" % (protocol,host,port,web_root)
@ -82,10 +82,10 @@ class autoProcessMusic:
if release_status: if release_status:
if release_status not in ["unprocessed", "snatched"]: if release_status not in ["unprocessed", "snatched"]:
logger.warning("%s is marked with a status of %s, skipping ..." % (nzbName, release_status),section) logger.warning("%s is marked with a status of %s, skipping ..." % (inputName, release_status),section)
return 0 return 0
else: else:
logger.error("Could not find a status for %s" % (nzbName),section) logger.error("Could not find a status for %s" % (inputName),section)
return 1 return 1
logger.debug("Opening URL: %s" % (url),section) logger.debug("Opening URL: %s" % (url),section)
@ -98,9 +98,9 @@ class autoProcessMusic:
logger.debug("Result: %s" % (r.text),section) logger.debug("Result: %s" % (r.text),section)
if r.text == "OK": if r.text == "OK":
logger.postprocess("SUCCESS: Post-Processing started for %s in folder %s ..." % (nzbName, dirName),section) logger.postprocess("SUCCESS: Post-Processing started for %s in folder %s ..." % (inputName, dirName),section)
else: else:
logger.error("FAILED: Post-Processing has NOT started for %s in folder %s. exiting!" % (nzbName, dirName),section) logger.error("FAILED: Post-Processing has NOT started for %s in folder %s. exiting!" % (inputName, dirName),section)
return 1 # failure return 1 # failure
else: else:

View file

@ -10,7 +10,7 @@ from nzbtomedia import logger
from nzbtomedia.transcoder import transcoder from nzbtomedia.transcoder import transcoder
class autoProcessTV: class autoProcessTV:
def processEpisode(self, dirName, nzbName=None, failed=False, clientAgent = "manual", inputCategory=None): def processEpisode(self, dirName, inputName=None, failed=False, clientAgent = "manual", inputCategory=None):
# auto-detect correct section # auto-detect correct section
section = nzbtomedia.CFG.findsection(inputCategory) section = nzbtomedia.CFG.findsection(inputCategory)
if not section: if not section:
@ -64,7 +64,7 @@ class autoProcessTV:
if not os.path.isdir(dirName) and os.path.isfile(dirName): # If the input directory is a file, assume single file download and split dir/name. if not os.path.isdir(dirName) and os.path.isfile(dirName): # If the input directory is a file, assume single file download and split dir/name.
dirName = os.path.split(os.path.normpath(dirName))[0] dirName = os.path.split(os.path.normpath(dirName))[0]
SpecificPath = joinPath(dirName, str(nzbName)) SpecificPath = joinPath(dirName, str(inputName))
cleanName = os.path.splitext(SpecificPath) cleanName = os.path.splitext(SpecificPath)
if cleanName[1] == ".nzb": if cleanName[1] == ".nzb":
SpecificPath = cleanName[0] SpecificPath = cleanName[0]
@ -72,9 +72,9 @@ class autoProcessTV:
dirName = SpecificPath dirName = SpecificPath
if fork not in nzbtomedia.SICKBEARD_TORRENT or (clientAgent in ['nzbget','sabnzbd'] and nzbExtractionBy != "Destination"): if fork not in nzbtomedia.SICKBEARD_TORRENT or (clientAgent in ['nzbget','sabnzbd'] and nzbExtractionBy != "Destination"):
if nzbName: if inputName:
process_all_exceptions(nzbName.lower(), dirName) process_all_exceptions(inputName.lower(), dirName)
nzbName, dirName = convert_to_ascii(nzbName, dirName) inputName, dirName = convert_to_ascii(inputName, dirName)
# Now check if tv files exist in destination. Eventually extraction may be done here if nzbExtractionBy == TorrentToMedia # Now check if tv files exist in destination. Eventually extraction may be done here if nzbExtractionBy == TorrentToMedia
video = 0 video = 0
@ -98,8 +98,8 @@ class autoProcessTV:
# configure SB params to pass # configure SB params to pass
fork_params['quiet'] = 1 fork_params['quiet'] = 1
if nzbName is not None: if inputName is not None:
fork_params['nzbName'] = nzbName fork_params['inputName'] = inputName
for param in copy.copy(fork_params): for param in copy.copy(fork_params):
if param == "failed": if param == "failed":

View file

@ -0,0 +1 @@
__all__ = ["mainDB"]

View file

@ -0,0 +1,49 @@
import nzbtomedia
from nzbtomedia import logger, nzbToMediaDB
from nzbtomedia.nzbToMediaUtil import backupVersionedFile
MIN_DB_VERSION = 1 # oldest db version we support migrating from
MAX_DB_VERSION = 1
def backupDatabase(version):
logger.info("Backing up database before upgrade")
if not backupVersionedFile(nzbToMediaDB.dbFilename(), version):
logger.log_error_and_exit("Database backup failed, abort upgrading database")
else:
logger.info("Proceeding with upgrade")
# ======================
# = Main DB Migrations =
# ======================
# Add new migrations at the bottom of the list; subclass the previous migration.
class InitialSchema(nzbToMediaDB.SchemaUpgrade):
def test(self):
return self.hasTable("db_version")
def execute(self):
if not self.hasTable("history") and not self.hasTable("db_version"):
queries = [
"CREATE TABLE db_version (db_version INTEGER);",
"CREATE TABLE downloads (input_directory TEXT PRIMARY KEY, input_name TEXT, input_hash TEXT, input_id TEXT, client_agent TEXT, status INTEGER, last_update NUMERIC);",
"INSERT INTO db_version (db_version) VALUES (1);"
]
for query in queries:
self.connection.action(query)
else:
cur_db_version = self.checkDBVersion()
if cur_db_version < MIN_DB_VERSION:
logger.log_error_and_exit("Your database version (" + str(
cur_db_version) + ") is too old to migrate from what this version of nzbToMedia supports (" + \
str(MIN_DB_VERSION) + ").\n" + \
"Please remove nzbtomedia.db file to begin fresh."
)
if cur_db_version > MAX_DB_VERSION:
logger.log_error_and_exit("Your database version (" + str(
cur_db_version) + ") has been incremented past what this version of nzbToMedia supports (" + \
str(MAX_DB_VERSION) + ").\n" + \
"If you have used other forks of nzbToMedia, your database may be unusable due to their modifications."
)

View file

@ -17,12 +17,14 @@ WARNING = logging.WARNING
MESSAGE = logging.INFO MESSAGE = logging.INFO
DEBUG = logging.DEBUG DEBUG = logging.DEBUG
POSTPROCESS = 21 POSTPROCESS = 21
DB = 5
reverseNames = {u'ERROR': ERROR, reverseNames = {u'ERROR': ERROR,
u'WARNING': WARNING, u'WARNING': WARNING,
u'INFO': MESSAGE, u'INFO': MESSAGE,
u'DEBUG': DEBUG, u'DEBUG': DEBUG,
u'POSTPROCESS': POSTPROCESS} u'POSTPROCESS': POSTPROCESS,
u'DB': DB}
class NTMRotatingLogHandler(object): class NTMRotatingLogHandler(object):
def __init__(self, log_file, num_files, num_bytes): def __init__(self, log_file, num_files, num_bytes):
@ -63,8 +65,9 @@ class NTMRotatingLogHandler(object):
if self.cur_handler: if self.cur_handler:
old_handler = self.cur_handler old_handler = self.cur_handler
else: else:
#Add a new logging level POSTPROCESS #Add a new logging levels
logging.addLevelName(21, 'POSTPROCESS') logging.addLevelName(21, 'POSTPROCESS')
logging.addLevelName(5, 'DB')
# only start consoleLogging on first initialize # only start consoleLogging on first initialize
if self.console_logging: if self.console_logging:
@ -77,13 +80,15 @@ class NTMRotatingLogHandler(object):
# set a format which is simpler for console use # set a format which is simpler for console use
console.setFormatter(DispatchingFormatter( console.setFormatter(DispatchingFormatter(
{'nzbtomedia': logging.Formatter('[%(asctime)s] [%(levelname)s]::%(message)s', '%H:%M:%S'), {'nzbtomedia': logging.Formatter('[%(asctime)s] [%(levelname)s]::%(message)s', '%H:%M:%S'),
'postprocess': logging.Formatter('[%(asctime)s] [%(levelname)s]::%(message)s', '%H:%M:%S') 'postprocess': logging.Formatter('[%(asctime)s] [%(levelname)s]::%(message)s', '%H:%M:%S'),
'db': logging.Formatter('[%(asctime)s] [%(levelname)s]::%(message)s', '%H:%M:%S')
}, },
logging.Formatter('%(message)s'), )) logging.Formatter('%(message)s'), ))
# add the handler to the root logger # add the handler to the root logger
logging.getLogger('nzbtomedia').addHandler(console) logging.getLogger('nzbtomedia').addHandler(console)
logging.getLogger('postprocess').addHandler(console) logging.getLogger('postprocess').addHandler(console)
logging.getLogger('db').addHandler(console)
self.log_file_path = os.path.join(nzbtomedia.LOG_DIR, self.log_file) self.log_file_path = os.path.join(nzbtomedia.LOG_DIR, self.log_file)
@ -91,9 +96,11 @@ class NTMRotatingLogHandler(object):
logging.getLogger('nzbtomedia').addHandler(self.cur_handler) logging.getLogger('nzbtomedia').addHandler(self.cur_handler)
logging.getLogger('postprocess').addHandler(self.cur_handler) logging.getLogger('postprocess').addHandler(self.cur_handler)
logging.getLogger('db').addHandler(self.cur_handler)
logging.getLogger('nzbtomedia').setLevel(logging.DEBUG) logging.getLogger('nzbtomedia').setLevel(logging.DEBUG)
logging.getLogger('postprocess').setLevel(POSTPROCESS) logging.getLogger('postprocess').setLevel(POSTPROCESS)
logging.getLogger('db').setLevel(POSTPROCESS)
# already logging in new log folder, close the old handler # already logging in new log folder, close the old handler
if old_handler: if old_handler:
@ -110,7 +117,8 @@ class NTMRotatingLogHandler(object):
file_handler.setFormatter(DispatchingFormatter( file_handler.setFormatter(DispatchingFormatter(
{'nzbtomedia': logging.Formatter('%(asctime)s %(levelname)-8s::%(message)s', '%Y-%m-%d %H:%M:%S'), {'nzbtomedia': logging.Formatter('%(asctime)s %(levelname)-8s::%(message)s', '%Y-%m-%d %H:%M:%S'),
'postprocess': logging.Formatter('%(asctime)s %(levelname)-8s::%(message)s', '%Y-%m-%d %H:%M:%S') 'postprocess': logging.Formatter('%(asctime)s %(levelname)-8s::%(message)s', '%Y-%m-%d %H:%M:%S'),
'db': logging.Formatter('%(asctime)s %(levelname)-8s::%(message)s', '%Y-%m-%d %H:%M:%S')
}, },
logging.Formatter('%(message)s'), )) logging.Formatter('%(message)s'), ))
@ -142,6 +150,7 @@ class NTMRotatingLogHandler(object):
ntm_logger = logging.getLogger('nzbtomedia') ntm_logger = logging.getLogger('nzbtomedia')
pp_logger = logging.getLogger('postprocess') pp_logger = logging.getLogger('postprocess')
db_logger = logging.getLogger('db')
# delete the old handler # delete the old handler
if self.cur_handler: if self.cur_handler:
@ -184,7 +193,9 @@ class NTMRotatingLogHandler(object):
ntm_logger = logging.getLogger('nzbtomedia') ntm_logger = logging.getLogger('nzbtomedia')
pp_logger = logging.getLogger('postprocess') pp_logger = logging.getLogger('postprocess')
db_logger = logging.getLogger('db')
setattr(pp_logger, 'postprocess', lambda *args: pp_logger.log(POSTPROCESS, *args)) setattr(pp_logger, 'postprocess', lambda *args: pp_logger.log(POSTPROCESS, *args))
setattr(db_logger, 'db', lambda *args: db_logger.log(DB, *args))
try: try:
if logLevel == DEBUG: if logLevel == DEBUG:
@ -198,6 +209,8 @@ class NTMRotatingLogHandler(object):
ntm_logger.error(out_line) ntm_logger.error(out_line)
elif logLevel == POSTPROCESS: elif logLevel == POSTPROCESS:
pp_logger.postprocess(out_line) pp_logger.postprocess(out_line)
elif logLevel == DB:
db_logger.db(out_line)
else: else:
ntm_logger.info(logLevel, out_line) ntm_logger.info(logLevel, out_line)
except ValueError: except ValueError:
@ -237,9 +250,12 @@ def warning(toLog, section='MAIN'):
def debug(toLog, section='MAIN'): def debug(toLog, section='MAIN'):
log(toLog, DEBUG, section) log(toLog, DEBUG, section)
def postprocess(toLog, section='MAIN'): def postprocess(toLog, section='POSTPROCESS'):
log(toLog, POSTPROCESS, section) log(toLog, POSTPROCESS, section)
def db(toLog, section='DB'):
log(toLog, DB, section)
def log_error_and_exit(error_msg): def log_error_and_exit(error_msg):
ntm_log_instance.log_error_and_exit(error_msg) ntm_log_instance.log_error_and_exit(error_msg)

266
nzbtomedia/nzbToMediaDB.py Normal file
View file

@ -0,0 +1,266 @@
from __future__ import with_statement
import re
import sqlite3
import time
import nzbtomedia
from nzbtomedia import logger
def dbFilename(filename="nzbtomedia.db", suffix=None):
"""
@param filename: The sqlite database filename to use. If not specified,
will be made to be nzbtomedia.db
@param suffix: The suffix to append to the filename. A '.' will be added
automatically, i.e. suffix='v0' will make dbfile.db.v0
@return: the correct location of the database file.
"""
if suffix:
filename = "%s.%s" % (filename, suffix)
return nzbtomedia.joinPath(nzbtomedia.PROGRAM_DIR, filename)
class DBConnection:
def __init__(self, filename="nzbtomedia.db", suffix=None, row_type=None):
self.filename = filename
self.connection = sqlite3.connect(dbFilename(filename), 20)
if row_type == "dict":
self.connection.row_factory = self._dict_factory
else:
self.connection.row_factory = sqlite3.Row
def checkDBVersion(self):
result = None
try:
result = self.select("SELECT db_version FROM db_version")
except sqlite3.OperationalError, e:
if "no such table: db_version" in e.args[0]:
return 0
if result:
return int(result[0]["db_version"])
else:
return 0
def fetch(self, query, args=None):
if query == None:
return
sqlResult = None
attempt = 0
while attempt < 5:
try:
if args == None:
logger.log(self.filename + ": " + query, logger.DB)
cursor = self.connection.cursor()
cursor.execute(query)
sqlResult = cursor.fetchone()[0]
else:
logger.log(self.filename + ": " + query + " with args " + str(args), logger.DB)
cursor = self.connection.cursor()
cursor.execute(query, args)
sqlResult = cursor.fetchone()[0]
# get out of the connection attempt loop since we were successful
break
except sqlite3.OperationalError, e:
if "unable to open database file" in e.args[0] or "database is locked" in e.args[0]:
logger.log(u"DB error: " + str(e), logger.WARNING)
attempt += 1
time.sleep(1)
else:
logger.log(u"DB error: " + str(e), logger.ERROR)
raise
except sqlite3.DatabaseError, e:
logger.log(u"Fatal error executing query: " + str(e), logger.ERROR)
raise
return sqlResult
def mass_action(self, querylist, logTransaction=False):
if querylist == None:
return
sqlResult = []
attempt = 0
while attempt < 5:
try:
for qu in querylist:
if len(qu) == 1:
if logTransaction:
logger.log(qu[0], logger.DEBUG)
sqlResult.append(self.connection.execute(qu[0]))
elif len(qu) > 1:
if logTransaction:
logger.log(qu[0] + " with args " + str(qu[1]), logger.DEBUG)
sqlResult.append(self.connection.execute(qu[0], qu[1]))
self.connection.commit()
logger.log(u"Transaction with " + str(len(querylist)) + u" query's executed", logger.DEBUG)
return sqlResult
except sqlite3.OperationalError, e:
sqlResult = []
if self.connection:
self.connection.rollback()
if "unable to open database file" in e.args[0] or "database is locked" in e.args[0]:
logger.log(u"DB error: " + str(e), logger.WARNING)
attempt += 1
time.sleep(1)
else:
logger.log(u"DB error: " + str(e), logger.ERROR)
raise
except sqlite3.DatabaseError, e:
sqlResult = []
if self.connection:
self.connection.rollback()
logger.log(u"Fatal error executing query: " + str(e), logger.ERROR)
raise
return sqlResult
def action(self, query, args=None):
if query == None:
return
sqlResult = None
attempt = 0
while attempt < 5:
try:
if args == None:
logger.log(self.filename + ": " + query, logger.DB)
sqlResult = self.connection.execute(query)
else:
logger.log(self.filename + ": " + query + " with args " + str(args), logger.DB)
sqlResult = self.connection.execute(query, args)
self.connection.commit()
# get out of the connection attempt loop since we were successful
break
except sqlite3.OperationalError, e:
if "unable to open database file" in e.args[0] or "database is locked" in e.args[0]:
logger.log(u"DB error: " + str(e), logger.WARNING)
attempt += 1
time.sleep(1)
else:
logger.log(u"DB error: " + str(e), logger.ERROR)
raise
except sqlite3.DatabaseError, e:
logger.log(u"Fatal error executing query: " + str(e), logger.ERROR)
raise
return sqlResult
def select(self, query, args=None):
sqlResults = self.action(query, args).fetchall()
if sqlResults == None:
return []
return sqlResults
def upsert(self, tableName, valueDict, keyDict):
changesBefore = self.connection.total_changes
genParams = lambda myDict: [x + " = ?" for x in myDict.keys()]
query = "UPDATE " + tableName + " SET " + ", ".join(genParams(valueDict)) + " WHERE " + " AND ".join(
genParams(keyDict))
self.action(query, valueDict.values() + keyDict.values())
if self.connection.total_changes == changesBefore:
query = "INSERT INTO " + tableName + " (" + ", ".join(valueDict.keys() + keyDict.keys()) + ")" + \
" VALUES (" + ", ".join(["?"] * len(valueDict.keys() + keyDict.keys())) + ")"
self.action(query, valueDict.values() + keyDict.values())
def tableInfo(self, tableName):
# FIXME ? binding is not supported here, but I cannot find a way to escape a string manually
cursor = self.connection.execute("PRAGMA table_info(%s)" % tableName)
columns = {}
for column in cursor:
columns[column['name']] = {'type': column['type']}
return columns
# http://stackoverflow.com/questions/3300464/how-can-i-get-dict-from-sqlite-query
def _dict_factory(self, cursor, row):
d = {}
for idx, col in enumerate(cursor.description):
d[col[0]] = row[idx]
return d
def sanityCheckDatabase(connection, sanity_check):
sanity_check(connection).check()
class DBSanityCheck(object):
def __init__(self, connection):
self.connection = connection
def check(self):
pass
# ===============
# = Upgrade API =
# ===============
def upgradeDatabase(connection, schema):
logger.log(u"Checking database structure...", logger.MESSAGE)
_processUpgrade(connection, schema)
def prettyName(class_name):
return ' '.join([x.group() for x in re.finditer("([A-Z])([a-z0-9]+)", class_name)])
def _processUpgrade(connection, upgradeClass):
instance = upgradeClass(connection)
logger.log(u"Checking " + prettyName(upgradeClass.__name__) + " database upgrade", logger.DEBUG)
if not instance.test():
logger.log(u"Database upgrade required: " + prettyName(upgradeClass.__name__), logger.MESSAGE)
try:
instance.execute()
except sqlite3.DatabaseError, e:
print "Error in " + str(upgradeClass.__name__) + ": " + str(e)
raise
logger.log(upgradeClass.__name__ + " upgrade completed", logger.DEBUG)
else:
logger.log(upgradeClass.__name__ + " upgrade not required", logger.DEBUG)
for upgradeSubClass in upgradeClass.__subclasses__():
_processUpgrade(connection, upgradeSubClass)
# Base migration class. All future DB changes should be subclassed from this class
class SchemaUpgrade(object):
def __init__(self, connection):
self.connection = connection
def hasTable(self, tableName):
return len(self.connection.action("SELECT 1 FROM sqlite_master WHERE name = ?;", (tableName, )).fetchall()) > 0
def hasColumn(self, tableName, column):
return column in self.connection.tableInfo(tableName)
def addColumn(self, table, column, type="NUMERIC", default=0):
self.connection.action("ALTER TABLE %s ADD %s %s" % (table, column, type))
self.connection.action("UPDATE %s SET %s = ?" % (table, column), (default,))
def checkDBVersion(self):
result = self.connection.select("SELECT db_version FROM db_version")
if result:
return int(result[0]["db_version"])
else:
return 0
def incDBVersion(self):
new_version = self.checkDBVersion() + 1
self.connection.action("UPDATE db_version SET db_version = ?", [new_version])
return new_version

View file

@ -5,18 +5,18 @@ import stat
import struct import struct
import shutil import shutil
import time import time
import datetime
import nzbtomedia import nzbtomedia
from lib import requests from lib import requests
from lib import guessit from lib import guessit
from nzbtomedia.extractor import extractor from nzbtomedia.extractor import extractor
from nzbtomedia.linktastic import linktastic from nzbtomedia.linktastic import linktastic
from nzbtomedia import logger from nzbtomedia import logger, nzbToMediaDB
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
def sanitizeFileName(name): def sanitizeFileName(name):
''' '''
>>> sanitizeFileName('a/b/c') >>> sanitizeFileName('a/b/c')
@ -268,12 +268,12 @@ def WakeUp():
logger.info("System with mac: %s has been woken. Continuing with the rest of the script." % (mac)) logger.info("System with mac: %s has been woken. Continuing with the rest of the script." % (mac))
def convert_to_ascii(nzbName, dirName): def convert_to_ascii(inputName, dirName):
ascii_convert = int(nzbtomedia.CFG["ASCII"]["convert"]) ascii_convert = int(nzbtomedia.CFG["ASCII"]["convert"])
if ascii_convert == 0 or os.name == 'nt': # just return if we don't want to convert or on windows os and "\" is replaced!. if ascii_convert == 0 or os.name == 'nt': # just return if we don't want to convert or on windows os and "\" is replaced!.
return nzbName, dirName return inputName, dirName
nzbName2 = str(nzbName.decode('ascii', 'replace').replace(u'\ufffd', '_')) inputName2 = str(inputName.decode('ascii', 'replace').replace(u'\ufffd', '_'))
dirName2 = str(dirName.decode('ascii', 'replace').replace(u'\ufffd', '_')) dirName2 = str(dirName.decode('ascii', 'replace').replace(u'\ufffd', '_'))
if dirName != dirName2: if dirName != dirName2:
logger.info("Renaming directory:%s to: %s." % (dirName, dirName2)) logger.info("Renaming directory:%s to: %s." % (dirName, dirName2))
@ -284,9 +284,9 @@ def convert_to_ascii(nzbName, dirName):
if filename != filename2: if filename != filename2:
logger.info("Renaming file:%s to: %s." % (filename, filename2)) logger.info("Renaming file:%s to: %s." % (filename, filename2))
shutil.move(filename, filename2) shutil.move(filename, filename2)
nzbName = nzbName2 inputName = inputName2
dirName = dirName2 dirName = dirName2
return nzbName, dirName return inputName, dirName
def parse_other(args): def parse_other(args):
@ -616,21 +616,21 @@ def listMediaFiles(path, media=True, audio=True, meta=True, archives=True, ignor
return files return files
def find_imdbid(dirName, nzbName): def find_imdbid(dirName, inputName):
imdbid = None imdbid = None
logger.info('Attemping imdbID lookup for %s' % (nzbName)) logger.info('Attemping imdbID lookup for %s' % (inputName))
# find imdbid in dirName # find imdbid in dirName
logger.info('Searching folder and file names for imdbID ...') logger.info('Searching folder and file names for imdbID ...')
m = re.search('(tt\d{7})', dirName+nzbName) m = re.search('(tt\d{7})', dirName+inputName)
if m: if m:
imdbid = m.group(1) imdbid = m.group(1)
logger.info("Found imdbID [%s]" % imdbid) logger.info("Found imdbID [%s]" % imdbid)
return imdbid return imdbid
logger.info('Searching IMDB for imdbID ...') logger.info('Searching IMDB for imdbID ...')
guess = guessit.guess_movie_info(nzbName) guess = guessit.guess_movie_info(inputName)
if guess: if guess:
# Movie Title # Movie Title
title = None title = None
@ -663,7 +663,7 @@ def find_imdbid(dirName, nzbName):
logger.info("Found imdbID [%s]" % imdbid) logger.info("Found imdbID [%s]" % imdbid)
return imdbid return imdbid
logger.warning('Unable to find a imdbID for %s' % (nzbName)) logger.warning('Unable to find a imdbID for %s' % (inputName))
def extractFiles(src, dst=None): def extractFiles(src, dst=None):
extracted_folder = [] extracted_folder = []
@ -702,5 +702,47 @@ def extractFiles(src, dst=None):
except: except:
logger.debug("Unable to remove file %s" % (inputFile)) logger.debug("Unable to remove file %s" % (inputFile))
def append_downloadID(dirName, download_id): def backupVersionedFile(old_file, version):
return '%s.downloadID(%s)' % (dirName,download_id) numTries = 0
new_file = old_file + '.' + 'v' + str(version)
while not os.path.isfile(new_file):
if not os.path.isfile(old_file):
logger.log(u"Not creating backup, " + old_file + " doesn't exist", logger.DEBUG)
break
try:
logger.log(u"Trying to back up " + old_file + " to " + new_file, logger.DEBUG)
shutil.copy(old_file, new_file)
logger.log(u"Backup done", logger.DEBUG)
break
except Exception, e:
logger.log(u"Error while trying to back up " + old_file + " to " + new_file + " : " + str(e), logger.WARNING)
numTries += 1
time.sleep(1)
logger.log(u"Trying again.", logger.DEBUG)
if numTries >= 10:
logger.log(u"Unable to back up " + old_file + " to " + new_file + " please do it manually.", logger.ERROR)
return False
return True
def update_downloadInfoStatus(inputDirectory, status):
logger.db("Updating status of our download in the DB to %s" % (status))
myDB = nzbToMediaDB.DBConnection()
myDB.action("UPDATE downloads SET status=?, last_update=? WHERE input_directory=?",
[status, datetime.date.today().toordinal(), inputDirectory])
def get_downloadInfo(inputDirectory, status):
logger.db("Getting download info from the DB for directory %s" % (inputDirectory))
myDB = nzbToMediaDB.DBConnection()
sqlResults = myDB.select("SELECT * FROM downloads WHERE input_directory=? AND status=?",
[inputDirectory, status])
return sqlResults