mirror of
https://github.com/clinton-hall/nzbToMedia.git
synced 2025-08-14 02:26:53 -07:00
PEP8: Fix formatting
* Remove redundant backslash between brackets * Fix multiple statements on one line * Fix missing/excess whitespace * Fix comments not starting with a single # and a space * Convert tabs to spaces * Use triple-quoted docstring
This commit is contained in:
parent
81ffe0456d
commit
8cd0e76ef8
35 changed files with 1342 additions and 947 deletions
469
core/__init__.py
469
core/__init__.py
|
@ -34,14 +34,14 @@ from core.autoProcess.autoProcessTV import autoProcessTV
|
||||||
from core import logger, versionCheck, nzbToMediaDB
|
from core import logger, versionCheck, nzbToMediaDB
|
||||||
from core.nzbToMediaConfig import config
|
from core.nzbToMediaConfig import config
|
||||||
from core.nzbToMediaUtil import category_search, sanitizeName, copy_link, parse_args, flatten, getDirs, \
|
from core.nzbToMediaUtil import category_search, sanitizeName, copy_link, parse_args, flatten, getDirs, \
|
||||||
rmReadOnly,rmDir, pause_torrent, resume_torrent, remove_torrent, listMediaFiles, \
|
rmReadOnly, rmDir, pause_torrent, resume_torrent, remove_torrent, listMediaFiles, \
|
||||||
extractFiles, cleanDir, update_downloadInfoStatus, get_downloadInfo, WakeUp, makeDir, cleanDir, \
|
extractFiles, cleanDir, update_downloadInfoStatus, get_downloadInfo, WakeUp, makeDir, cleanDir, \
|
||||||
create_torrent_class, listMediaFiles, RunningProcess
|
create_torrent_class, listMediaFiles, RunningProcess
|
||||||
from core.transcoder import transcoder
|
from core.transcoder import transcoder
|
||||||
from core.databases import mainDB
|
from core.databases import mainDB
|
||||||
|
|
||||||
# Client Agents
|
# Client Agents
|
||||||
NZB_CLIENTS = ['sabnzbd','nzbget']
|
NZB_CLIENTS = ['sabnzbd', 'nzbget']
|
||||||
TORRENT_CLIENTS = ['transmission', 'deluge', 'utorrent', 'rtorrent', 'other']
|
TORRENT_CLIENTS = ['transmission', 'deluge', 'utorrent', 'rtorrent', 'other']
|
||||||
|
|
||||||
# sabnzbd constants
|
# sabnzbd constants
|
||||||
|
@ -62,7 +62,8 @@ FORKS[FORK_FAILED_TORRENT] = {"dir": None, "failed": None, "process_method": Non
|
||||||
FORKS[FORK_SICKRAGETV] = {"proc_dir": None, "failed": None, "process_method": None, "force": None, "delete_on": None}
|
FORKS[FORK_SICKRAGETV] = {"proc_dir": None, "failed": None, "process_method": None, "force": None, "delete_on": None}
|
||||||
FORKS[FORK_SICKRAGE] = {"proc_dir": None, "failed": None, "process_method": None, "force": None, "delete_on": None}
|
FORKS[FORK_SICKRAGE] = {"proc_dir": None, "failed": None, "process_method": None, "force": None, "delete_on": None}
|
||||||
FORKS[FORK_SICKGEAR] = {"dir": None, "failed": None, "process_method": None, "force": None}
|
FORKS[FORK_SICKGEAR] = {"dir": None, "failed": None, "process_method": None, "force": None}
|
||||||
ALL_FORKS = {"dir": None, "dirName": None, "proc_dir": None, "failed": None, "process_method": None, "force": None, "delete_on": None}
|
ALL_FORKS = {"dir": None, "dirName": None, "proc_dir": None, "failed": None, "process_method": None, "force": None,
|
||||||
|
"delete_on": None}
|
||||||
|
|
||||||
# NZBGet Exit Codes
|
# NZBGet Exit Codes
|
||||||
NZBGET_POSTPROCESS_PARCHECK = 92
|
NZBGET_POSTPROCESS_PARCHECK = 92
|
||||||
|
@ -202,6 +203,7 @@ USER_SCRIPT_RUNONCE = None
|
||||||
|
|
||||||
__INITIALIZED__ = False
|
__INITIALIZED__ = False
|
||||||
|
|
||||||
|
|
||||||
def initialize(section=None):
|
def initialize(section=None):
|
||||||
global NZBGET_POSTPROCESS_ERROR, NZBGET_POSTPROCESS_NONE, NZBGET_POSTPROCESS_PARCHECK, NZBGET_POSTPROCESS_SUCCESS, \
|
global NZBGET_POSTPROCESS_ERROR, NZBGET_POSTPROCESS_NONE, NZBGET_POSTPROCESS_PARCHECK, NZBGET_POSTPROCESS_SUCCESS, \
|
||||||
NZBTOMEDIA_TIMEOUT, FORKS, FORK_DEFAULT, FORK_FAILED_TORRENT, FORK_FAILED, \
|
NZBTOMEDIA_TIMEOUT, FORKS, FORK_DEFAULT, FORK_FAILED_TORRENT, FORK_FAILED, \
|
||||||
|
@ -224,7 +226,7 @@ def initialize(section=None):
|
||||||
|
|
||||||
if __INITIALIZED__:
|
if __INITIALIZED__:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if os.environ.has_key('NTM_LOGFILE'):
|
if os.environ.has_key('NTM_LOGFILE'):
|
||||||
LOG_FILE = os.environ['NTM_LOGFILE']
|
LOG_FILE = os.environ['NTM_LOGFILE']
|
||||||
LOG_DIR = os.path.split(LOG_FILE)[0]
|
LOG_DIR = os.path.split(LOG_FILE)[0]
|
||||||
|
@ -316,7 +318,8 @@ def initialize(section=None):
|
||||||
# restart nzbToMedia
|
# restart nzbToMedia
|
||||||
try:
|
try:
|
||||||
del MYAPP
|
del MYAPP
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
restart()
|
restart()
|
||||||
else:
|
else:
|
||||||
logger.error("Update wasn't successful, not restarting. Check your log for more information.")
|
logger.error("Update wasn't successful, not restarting. Check your log for more information.")
|
||||||
|
@ -334,8 +337,10 @@ def initialize(section=None):
|
||||||
SABNZBDAPIKEY = CFG["Nzb"]["sabnzbd_apikey"]
|
SABNZBDAPIKEY = CFG["Nzb"]["sabnzbd_apikey"]
|
||||||
NZB_DEFAULTDIR = CFG["Nzb"]["default_downloadDirectory"]
|
NZB_DEFAULTDIR = CFG["Nzb"]["default_downloadDirectory"]
|
||||||
GROUPS = CFG["Custom"]["remove_group"]
|
GROUPS = CFG["Custom"]["remove_group"]
|
||||||
if isinstance(GROUPS, str): GROUPS = GROUPS.split(',')
|
if isinstance(GROUPS, str):
|
||||||
if GROUPS == ['']: GROUPS = None
|
GROUPS = GROUPS.split(',')
|
||||||
|
if GROUPS == ['']:
|
||||||
|
GROUPS = None
|
||||||
|
|
||||||
TORRENT_CLIENTAGENT = CFG["Torrent"]["clientAgent"] # utorrent | deluge | transmission | rtorrent | vuze |other
|
TORRENT_CLIENTAGENT = CFG["Torrent"]["clientAgent"] # utorrent | deluge | transmission | rtorrent | vuze |other
|
||||||
USELINK = CFG["Torrent"]["useLink"] # no | hard | sym
|
USELINK = CFG["Torrent"]["useLink"] # no | hard | sym
|
||||||
|
@ -343,8 +348,10 @@ def initialize(section=None):
|
||||||
TORRENT_DEFAULTDIR = CFG["Torrent"]["default_downloadDirectory"]
|
TORRENT_DEFAULTDIR = CFG["Torrent"]["default_downloadDirectory"]
|
||||||
CATEGORIES = (CFG["Torrent"]["categories"]) # music,music_videos,pictures,software
|
CATEGORIES = (CFG["Torrent"]["categories"]) # music,music_videos,pictures,software
|
||||||
NOFLATTEN = (CFG["Torrent"]["noFlatten"])
|
NOFLATTEN = (CFG["Torrent"]["noFlatten"])
|
||||||
if isinstance(NOFLATTEN, str): NOFLATTEN = NOFLATTEN.split(',')
|
if isinstance(NOFLATTEN, str):
|
||||||
if isinstance(CATEGORIES, str): CATEGORIES = CATEGORIES.split(',')
|
NOFLATTEN = NOFLATTEN.split(',')
|
||||||
|
if isinstance(CATEGORIES, str):
|
||||||
|
CATEGORIES = CATEGORIES.split(',')
|
||||||
DELETE_ORIGINAL = int(CFG["Torrent"]["deleteOriginal"])
|
DELETE_ORIGINAL = int(CFG["Torrent"]["deleteOriginal"])
|
||||||
TORRENT_CHMOD_DIRECTORY = int(str(CFG["Torrent"]["chmodDirectory"]), 8)
|
TORRENT_CHMOD_DIRECTORY = int(str(CFG["Torrent"]["chmodDirectory"]), 8)
|
||||||
TORRENT_RESUME_ON_FAILURE = int(CFG["Torrent"]["resumeOnFailure"])
|
TORRENT_RESUME_ON_FAILURE = int(CFG["Torrent"]["resumeOnFailure"])
|
||||||
|
@ -365,9 +372,12 @@ def initialize(section=None):
|
||||||
|
|
||||||
REMOTEPATHS = CFG["Network"]["mount_points"] or []
|
REMOTEPATHS = CFG["Network"]["mount_points"] or []
|
||||||
if REMOTEPATHS:
|
if REMOTEPATHS:
|
||||||
if isinstance(REMOTEPATHS, list): REMOTEPATHS = ','.join(REMOTEPATHS) # fix in case this imported as list.
|
if isinstance(REMOTEPATHS, list):
|
||||||
REMOTEPATHS = [ tuple(item.split(',')) for item in REMOTEPATHS.split('|') ] # /volume1/Public/,E:\|/volume2/share/,\\NAS\
|
REMOTEPATHS = ','.join(REMOTEPATHS) # fix in case this imported as list.
|
||||||
REMOTEPATHS = [ (local.strip(), remote.strip()) for local, remote in REMOTEPATHS ] # strip trailing and leading whitespaces
|
REMOTEPATHS = [tuple(item.split(',')) for item in
|
||||||
|
REMOTEPATHS.split('|')] # /volume1/Public/,E:\|/volume2/share/,\\NAS\
|
||||||
|
REMOTEPATHS = [(local.strip(), remote.strip()) for local, remote in
|
||||||
|
REMOTEPATHS] # strip trailing and leading whitespaces
|
||||||
|
|
||||||
PLEXSSL = int(CFG["Plex"]["plex_ssl"])
|
PLEXSSL = int(CFG["Plex"]["plex_ssl"])
|
||||||
PLEXHOST = CFG["Plex"]["plex_host"]
|
PLEXHOST = CFG["Plex"]["plex_host"]
|
||||||
|
@ -375,62 +385,79 @@ def initialize(section=None):
|
||||||
PLEXTOKEN = CFG["Plex"]["plex_token"]
|
PLEXTOKEN = CFG["Plex"]["plex_token"]
|
||||||
PLEXSEC = CFG["Plex"]["plex_sections"] or []
|
PLEXSEC = CFG["Plex"]["plex_sections"] or []
|
||||||
if PLEXSEC:
|
if PLEXSEC:
|
||||||
if isinstance(PLEXSEC, list): PLEXSEC = ','.join(PLEXSEC) # fix in case this imported as list.
|
if isinstance(PLEXSEC, list):
|
||||||
PLEXSEC = [ tuple(item.split(',')) for item in PLEXSEC.split('|') ]
|
PLEXSEC = ','.join(PLEXSEC) # fix in case this imported as list.
|
||||||
|
PLEXSEC = [tuple(item.split(',')) for item in PLEXSEC.split('|')]
|
||||||
|
|
||||||
devnull = open(os.devnull, 'w')
|
devnull = open(os.devnull, 'w')
|
||||||
try:
|
try:
|
||||||
subprocess.Popen(["nice"], stdout=devnull, stderr=devnull).communicate()
|
subprocess.Popen(["nice"], stdout=devnull, stderr=devnull).communicate()
|
||||||
NICENESS.extend(['nice', '-n%s' % (int(CFG["Posix"]["niceness"]))])
|
NICENESS.extend(['nice', '-n%s' % (int(CFG["Posix"]["niceness"]))])
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
try:
|
try:
|
||||||
subprocess.Popen(["ionice"], stdout=devnull, stderr=devnull).communicate()
|
subprocess.Popen(["ionice"], stdout=devnull, stderr=devnull).communicate()
|
||||||
try:
|
try:
|
||||||
NICENESS.extend(['ionice', '-c%s' % (int(CFG["Posix"]["ionice_class"]))])
|
NICENESS.extend(['ionice', '-c%s' % (int(CFG["Posix"]["ionice_class"]))])
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
try:
|
try:
|
||||||
if 'ionice' in NICENESS:
|
if 'ionice' in NICENESS:
|
||||||
NICENESS.extend(['-n%s' % (int(CFG["Posix"]["ionice_classdata"]))])
|
NICENESS.extend(['-n%s' % (int(CFG["Posix"]["ionice_classdata"]))])
|
||||||
else:
|
else:
|
||||||
NICENESS.extend(['ionice', '-n%s' % (int(CFG["Posix"]["ionice_classdata"]))])
|
NICENESS.extend(['ionice', '-n%s' % (int(CFG["Posix"]["ionice_classdata"]))])
|
||||||
except: pass
|
except:
|
||||||
except: pass
|
pass
|
||||||
|
except:
|
||||||
|
pass
|
||||||
devnull.close()
|
devnull.close()
|
||||||
|
|
||||||
COMPRESSEDCONTAINER = [re.compile('.r\d{2}$', re.I),
|
COMPRESSEDCONTAINER = [re.compile('.r\d{2}$', re.I),
|
||||||
re.compile('.part\d+.rar$', re.I),
|
re.compile('.part\d+.rar$', re.I),
|
||||||
re.compile('.rar$', re.I)]
|
re.compile('.rar$', re.I)]
|
||||||
COMPRESSEDCONTAINER += [re.compile('%s$' % ext, re.I) for ext in CFG["Extensions"]["compressedExtensions"]]
|
COMPRESSEDCONTAINER += [re.compile('%s$' % ext, re.I) for ext in CFG["Extensions"]["compressedExtensions"]]
|
||||||
MEDIACONTAINER = CFG["Extensions"]["mediaExtensions"]
|
MEDIACONTAINER = CFG["Extensions"]["mediaExtensions"]
|
||||||
AUDIOCONTAINER = CFG["Extensions"]["audioExtensions"]
|
AUDIOCONTAINER = CFG["Extensions"]["audioExtensions"]
|
||||||
METACONTAINER = CFG["Extensions"]["metaExtensions"] # .nfo,.sub,.srt
|
METACONTAINER = CFG["Extensions"]["metaExtensions"] # .nfo,.sub,.srt
|
||||||
if isinstance(COMPRESSEDCONTAINER, str): COMPRESSEDCONTAINER = COMPRESSEDCONTAINER.split(',')
|
if isinstance(COMPRESSEDCONTAINER, str):
|
||||||
if isinstance(MEDIACONTAINER, str): MEDIACONTAINER = MEDIACONTAINER.split(',')
|
COMPRESSEDCONTAINER = COMPRESSEDCONTAINER.split(',')
|
||||||
if isinstance(AUDIOCONTAINER, str): AUDIOCONTAINER = AUDIOCONTAINER.split(',')
|
if isinstance(MEDIACONTAINER, str):
|
||||||
if isinstance(METACONTAINER, str): METACONTAINER = METACONTAINER.split(',')
|
MEDIACONTAINER = MEDIACONTAINER.split(',')
|
||||||
|
if isinstance(AUDIOCONTAINER, str):
|
||||||
|
AUDIOCONTAINER = AUDIOCONTAINER.split(',')
|
||||||
|
if isinstance(METACONTAINER, str):
|
||||||
|
METACONTAINER = METACONTAINER.split(',')
|
||||||
|
|
||||||
GETSUBS = int(CFG["Transcoder"]["getSubs"])
|
GETSUBS = int(CFG["Transcoder"]["getSubs"])
|
||||||
TRANSCODE = int(CFG["Transcoder"]["transcode"])
|
TRANSCODE = int(CFG["Transcoder"]["transcode"])
|
||||||
DUPLICATE = int(CFG["Transcoder"]["duplicate"])
|
DUPLICATE = int(CFG["Transcoder"]["duplicate"])
|
||||||
CONCAT = int(CFG["Transcoder"]["concat"])
|
CONCAT = int(CFG["Transcoder"]["concat"])
|
||||||
IGNOREEXTENSIONS = (CFG["Transcoder"]["ignoreExtensions"])
|
IGNOREEXTENSIONS = (CFG["Transcoder"]["ignoreExtensions"])
|
||||||
if isinstance(IGNOREEXTENSIONS, str): IGNOREEXTENSIONS = IGNOREEXTENSIONS.split(',')
|
if isinstance(IGNOREEXTENSIONS, str):
|
||||||
|
IGNOREEXTENSIONS = IGNOREEXTENSIONS.split(',')
|
||||||
OUTPUTFASTSTART = int(CFG["Transcoder"]["outputFastStart"])
|
OUTPUTFASTSTART = int(CFG["Transcoder"]["outputFastStart"])
|
||||||
GENERALOPTS = (CFG["Transcoder"]["generalOptions"])
|
GENERALOPTS = (CFG["Transcoder"]["generalOptions"])
|
||||||
if isinstance(GENERALOPTS, str): GENERALOPTS = GENERALOPTS.split(',')
|
if isinstance(GENERALOPTS, str):
|
||||||
if GENERALOPTS == ['']: GENERALOPTS = []
|
GENERALOPTS = GENERALOPTS.split(',')
|
||||||
if not '-fflags' in GENERALOPTS: GENERALOPTS.append('-fflags')
|
if GENERALOPTS == ['']:
|
||||||
if not '+genpts' in GENERALOPTS: GENERALOPTS.append('+genpts')
|
GENERALOPTS = []
|
||||||
|
if not '-fflags' in GENERALOPTS:
|
||||||
|
GENERALOPTS.append('-fflags')
|
||||||
|
if not '+genpts' in GENERALOPTS:
|
||||||
|
GENERALOPTS.append('+genpts')
|
||||||
try:
|
try:
|
||||||
OUTPUTQUALITYPERCENT = int(CFG["Transcoder"]["outputQualityPercent"])
|
OUTPUTQUALITYPERCENT = int(CFG["Transcoder"]["outputQualityPercent"])
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
OUTPUTVIDEOPATH = CFG["Transcoder"]["outputVideoPath"]
|
OUTPUTVIDEOPATH = CFG["Transcoder"]["outputVideoPath"]
|
||||||
PROCESSOUTPUT = int(CFG["Transcoder"]["processOutput"])
|
PROCESSOUTPUT = int(CFG["Transcoder"]["processOutput"])
|
||||||
ALANGUAGE = CFG["Transcoder"]["audioLanguage"]
|
ALANGUAGE = CFG["Transcoder"]["audioLanguage"]
|
||||||
AINCLUDE = int(CFG["Transcoder"]["allAudioLanguages"])
|
AINCLUDE = int(CFG["Transcoder"]["allAudioLanguages"])
|
||||||
SLANGUAGES = CFG["Transcoder"]["subLanguages"]
|
SLANGUAGES = CFG["Transcoder"]["subLanguages"]
|
||||||
if isinstance(SLANGUAGES, str): SLANGUAGES = SLANGUAGES.split(',')
|
if isinstance(SLANGUAGES, str):
|
||||||
if SLANGUAGES == ['']: SLANGUAGES = []
|
SLANGUAGES = SLANGUAGES.split(',')
|
||||||
|
if SLANGUAGES == ['']:
|
||||||
|
SLANGUAGES = []
|
||||||
SINCLUDE = int(CFG["Transcoder"]["allSubLanguages"])
|
SINCLUDE = int(CFG["Transcoder"]["allSubLanguages"])
|
||||||
SEXTRACT = int(CFG["Transcoder"]["extractSubs"])
|
SEXTRACT = int(CFG["Transcoder"]["extractSubs"])
|
||||||
SEMBED = int(CFG["Transcoder"]["embedSubs"])
|
SEMBED = int(CFG["Transcoder"]["embedSubs"])
|
||||||
|
@ -438,169 +465,215 @@ def initialize(section=None):
|
||||||
VEXTENSION = CFG["Transcoder"]["outputVideoExtension"].strip()
|
VEXTENSION = CFG["Transcoder"]["outputVideoExtension"].strip()
|
||||||
VCODEC = CFG["Transcoder"]["outputVideoCodec"].strip()
|
VCODEC = CFG["Transcoder"]["outputVideoCodec"].strip()
|
||||||
VCODEC_ALLOW = CFG["Transcoder"]["VideoCodecAllow"].strip()
|
VCODEC_ALLOW = CFG["Transcoder"]["VideoCodecAllow"].strip()
|
||||||
if isinstance(VCODEC_ALLOW, str): VCODEC_ALLOW = VCODEC_ALLOW.split(',')
|
if isinstance(VCODEC_ALLOW, str):
|
||||||
if VCODEC_ALLOW == ['']: VCODEC_ALLOW = []
|
VCODEC_ALLOW = VCODEC_ALLOW.split(',')
|
||||||
|
if VCODEC_ALLOW == ['']:
|
||||||
|
VCODEC_ALLOW = []
|
||||||
VPRESET = CFG["Transcoder"]["outputVideoPreset"].strip()
|
VPRESET = CFG["Transcoder"]["outputVideoPreset"].strip()
|
||||||
try:
|
try:
|
||||||
VFRAMERATE = float(CFG["Transcoder"]["outputVideoFramerate"].strip())
|
VFRAMERATE = float(CFG["Transcoder"]["outputVideoFramerate"].strip())
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
try:
|
try:
|
||||||
VCRF = int(CFG["Transcoder"]["outputVideoCRF"].strip())
|
VCRF = int(CFG["Transcoder"]["outputVideoCRF"].strip())
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
try:
|
try:
|
||||||
VLEVEL = CFG["Transcoder"]["outputVideoLevel"].strip()
|
VLEVEL = CFG["Transcoder"]["outputVideoLevel"].strip()
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
try:
|
try:
|
||||||
VBITRATE = int((CFG["Transcoder"]["outputVideoBitrate"].strip()).replace('k','000'))
|
VBITRATE = int((CFG["Transcoder"]["outputVideoBitrate"].strip()).replace('k', '000'))
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
VRESOLUTION = CFG["Transcoder"]["outputVideoResolution"]
|
VRESOLUTION = CFG["Transcoder"]["outputVideoResolution"]
|
||||||
ACODEC = CFG["Transcoder"]["outputAudioCodec"].strip()
|
ACODEC = CFG["Transcoder"]["outputAudioCodec"].strip()
|
||||||
ACODEC_ALLOW = CFG["Transcoder"]["AudioCodecAllow"].strip()
|
ACODEC_ALLOW = CFG["Transcoder"]["AudioCodecAllow"].strip()
|
||||||
if isinstance(ACODEC_ALLOW, str): ACODEC_ALLOW = ACODEC_ALLOW.split(',')
|
if isinstance(ACODEC_ALLOW, str):
|
||||||
if ACODEC_ALLOW == ['']: ACODEC_ALLOW = []
|
ACODEC_ALLOW = ACODEC_ALLOW.split(',')
|
||||||
|
if ACODEC_ALLOW == ['']:
|
||||||
|
ACODEC_ALLOW = []
|
||||||
try:
|
try:
|
||||||
ACHANNELS = int(CFG["Transcoder"]["outputAudioChannels"].strip())
|
ACHANNELS = int(CFG["Transcoder"]["outputAudioChannels"].strip())
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
try:
|
try:
|
||||||
ABITRATE = int((CFG["Transcoder"]["outputAudioBitrate"].strip()).replace('k','000'))
|
ABITRATE = int((CFG["Transcoder"]["outputAudioBitrate"].strip()).replace('k', '000'))
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
ACODEC2 = CFG["Transcoder"]["outputAudioTrack2Codec"].strip()
|
ACODEC2 = CFG["Transcoder"]["outputAudioTrack2Codec"].strip()
|
||||||
ACODEC2_ALLOW = CFG["Transcoder"]["AudioCodec2Allow"].strip()
|
ACODEC2_ALLOW = CFG["Transcoder"]["AudioCodec2Allow"].strip()
|
||||||
if isinstance(ACODEC2_ALLOW, str): ACODEC2_ALLOW = ACODEC2_ALLOW.split(',')
|
if isinstance(ACODEC2_ALLOW, str):
|
||||||
if ACODEC2_ALLOW == ['']: ACODEC2_ALLOW = []
|
ACODEC2_ALLOW = ACODEC2_ALLOW.split(',')
|
||||||
|
if ACODEC2_ALLOW == ['']:
|
||||||
|
ACODEC2_ALLOW = []
|
||||||
try:
|
try:
|
||||||
ACHANNELS2 = int(CFG["Transcoder"]["outputAudioTrack2Channels"].strip())
|
ACHANNELS2 = int(CFG["Transcoder"]["outputAudioTrack2Channels"].strip())
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
try:
|
try:
|
||||||
ABITRATE2 = int((CFG["Transcoder"]["outputAudioTrack2Bitrate"].strip()).replace('k','000'))
|
ABITRATE2 = int((CFG["Transcoder"]["outputAudioTrack2Bitrate"].strip()).replace('k', '000'))
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
ACODEC3 = CFG["Transcoder"]["outputAudioOtherCodec"].strip()
|
ACODEC3 = CFG["Transcoder"]["outputAudioOtherCodec"].strip()
|
||||||
ACODEC3_ALLOW = CFG["Transcoder"]["AudioOtherCodecAllow"].strip()
|
ACODEC3_ALLOW = CFG["Transcoder"]["AudioOtherCodecAllow"].strip()
|
||||||
if isinstance(ACODEC3_ALLOW, str): ACODEC3_ALLOW = ACODEC3_ALLOW.split(',')
|
if isinstance(ACODEC3_ALLOW, str):
|
||||||
if ACODEC3_ALLOW == ['']: ACODEC3_ALLOW = []
|
ACODEC3_ALLOW = ACODEC3_ALLOW.split(',')
|
||||||
|
if ACODEC3_ALLOW == ['']:
|
||||||
|
ACODEC3_ALLOW = []
|
||||||
try:
|
try:
|
||||||
ACHANNELS3 = int(CFG["Transcoder"]["outputAudioOtherChannels"].strip())
|
ACHANNELS3 = int(CFG["Transcoder"]["outputAudioOtherChannels"].strip())
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
try:
|
try:
|
||||||
ABITRATE3 = int((CFG["Transcoder"]["outputAudioOtherBitrate"].strip()).replace('k','000'))
|
ABITRATE3 = int((CFG["Transcoder"]["outputAudioOtherBitrate"].strip()).replace('k', '000'))
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
SCODEC = CFG["Transcoder"]["outputSubtitleCodec"].strip()
|
SCODEC = CFG["Transcoder"]["outputSubtitleCodec"].strip()
|
||||||
BURN = int(CFG["Transcoder"]["burnInSubtitle"].strip())
|
BURN = int(CFG["Transcoder"]["burnInSubtitle"].strip())
|
||||||
DEFAULTS = CFG["Transcoder"]["outputDefault"].strip()
|
DEFAULTS = CFG["Transcoder"]["outputDefault"].strip()
|
||||||
HWACCEL = int(CFG["Transcoder"]["hwAccel"])
|
HWACCEL = int(CFG["Transcoder"]["hwAccel"])
|
||||||
|
|
||||||
allow_subs = ['.mkv','.mp4', '.m4v', 'asf', 'wma', 'wmv']
|
allow_subs = ['.mkv', '.mp4', '.m4v', 'asf', 'wma', 'wmv']
|
||||||
codec_alias = {
|
codec_alias = {
|
||||||
'libx264':['libx264', 'h264', 'h.264', 'AVC', 'MPEG-4'],
|
'libx264': ['libx264', 'h264', 'h.264', 'AVC', 'MPEG-4'],
|
||||||
'libmp3lame':['libmp3lame', 'mp3'],
|
'libmp3lame': ['libmp3lame', 'mp3'],
|
||||||
'libfaac':['libfaac', 'aac', 'faac']
|
'libfaac': ['libfaac', 'aac', 'faac']
|
||||||
}
|
}
|
||||||
transcode_defaults = {
|
transcode_defaults = {
|
||||||
'iPad':{
|
'iPad': {
|
||||||
'VEXTENSION':'.mp4','VCODEC':'libx264','VPRESET':None,'VFRAMERATE':None,'VBITRATE':None,'VCRF':None,'VLEVEL':None,
|
'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None,
|
||||||
'VRESOLUTION':None,'VCODEC_ALLOW':['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'],
|
'VCRF': None, 'VLEVEL': None,
|
||||||
'ACODEC':'aac','ACODEC_ALLOW':['libfaac'],'ABITRATE':None, 'ACHANNELS':2,
|
'VRESOLUTION': None,
|
||||||
'ACODEC2':'ac3','ACODEC2_ALLOW':['ac3'],'ABITRATE2':None, 'ACHANNELS2':6,
|
'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'],
|
||||||
'ACODEC3':None,'ACODEC3_ALLOW':[],'ABITRATE3':None, 'ACHANNELS3':None,
|
'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': None, 'ACHANNELS': 2,
|
||||||
'SCODEC':'mov_text'
|
'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6,
|
||||||
},
|
'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None,
|
||||||
'iPad-1080p':{
|
'SCODEC': 'mov_text'
|
||||||
'VEXTENSION':'.mp4','VCODEC':'libx264','VPRESET':None,'VFRAMERATE':None,'VBITRATE':None,'VCRF':None,'VLEVEL':None,
|
},
|
||||||
'VRESOLUTION':'1920:1080','VCODEC_ALLOW':['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'],
|
'iPad-1080p': {
|
||||||
'ACODEC':'aac','ACODEC_ALLOW':['libfaac'],'ABITRATE':None, 'ACHANNELS':2,
|
'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None,
|
||||||
'ACODEC2':'ac3','ACODEC2_ALLOW':['ac3'],'ABITRATE2':None, 'ACHANNELS2':6,
|
'VCRF': None, 'VLEVEL': None,
|
||||||
'ACODEC3':None,'ACODEC3_ALLOW':[],'ABITRATE3':None, 'ACHANNELS3':None,
|
'VRESOLUTION': '1920:1080',
|
||||||
'SCODEC':'mov_text'
|
'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'],
|
||||||
},
|
'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': None, 'ACHANNELS': 2,
|
||||||
'iPad-720p':{
|
'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6,
|
||||||
'VEXTENSION':'.mp4','VCODEC':'libx264','VPRESET':None,'VFRAMERATE':None,'VBITRATE':None,'VCRF':None,'VLEVEL':None,
|
'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None,
|
||||||
'VRESOLUTION':'1280:720','VCODEC_ALLOW':['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'],
|
'SCODEC': 'mov_text'
|
||||||
'ACODEC':'aac','ACODEC_ALLOW':['libfaac'],'ABITRATE':None, 'ACHANNELS':2,
|
},
|
||||||
'ACODEC2':'ac3','ACODEC2_ALLOW':['ac3'],'ABITRATE2':None, 'ACHANNELS2':6,
|
'iPad-720p': {
|
||||||
'ACODEC3':None,'ACODEC3_ALLOW':[],'ABITRATE3':None, 'ACHANNELS3':None,
|
'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None,
|
||||||
'SCODEC':'mov_text'
|
'VCRF': None, 'VLEVEL': None,
|
||||||
},
|
'VRESOLUTION': '1280:720',
|
||||||
'Apple-TV':{
|
'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'],
|
||||||
'VEXTENSION':'.mp4','VCODEC':'libx264','VPRESET':None,'VFRAMERATE':None,'VBITRATE':None,'VCRF':None,'VLEVEL':None,
|
'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': None, 'ACHANNELS': 2,
|
||||||
'VRESOLUTION':'1280:720','VCODEC_ALLOW':['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'],
|
'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6,
|
||||||
'ACODEC':'ac3','ACODEC_ALLOW':['ac3'],'ABITRATE':None, 'ACHANNELS':6,
|
'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None,
|
||||||
'ACODEC2':'aac','ACODEC2_ALLOW':['libfaac'],'ABITRATE2':None, 'ACHANNELS2':2,
|
'SCODEC': 'mov_text'
|
||||||
'ACODEC3':None,'ACODEC3_ALLOW':[],'ABITRATE3':None, 'ACHANNELS3':None,
|
},
|
||||||
'SCODEC':'mov_text'
|
'Apple-TV': {
|
||||||
},
|
'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None,
|
||||||
'iPod':{
|
'VCRF': None, 'VLEVEL': None,
|
||||||
'VEXTENSION':'.mp4','VCODEC':'libx264','VPRESET':None,'VFRAMERATE':None,'VBITRATE':None,'VCRF':None,'VLEVEL':None,
|
'VRESOLUTION': '1280:720',
|
||||||
'VRESOLUTION':'1280:720','VCODEC_ALLOW':['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'],
|
'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'],
|
||||||
'ACODEC':'aac','ACODEC_ALLOW':['libfaac'],'ABITRATE':128000, 'ACHANNELS':2,
|
'ACODEC': 'ac3', 'ACODEC_ALLOW': ['ac3'], 'ABITRATE': None, 'ACHANNELS': 6,
|
||||||
'ACODEC2':None,'ACODEC2_ALLOW':[],'ABITRATE2':None, 'ACHANNELS2':None,
|
'ACODEC2': 'aac', 'ACODEC2_ALLOW': ['libfaac'], 'ABITRATE2': None, 'ACHANNELS2': 2,
|
||||||
'ACODEC3':None,'ACODEC3_ALLOW':[],'ABITRATE3':None, 'ACHANNELS3':None,
|
'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None,
|
||||||
'SCODEC':'mov_text'
|
'SCODEC': 'mov_text'
|
||||||
},
|
},
|
||||||
'iPhone':{
|
'iPod': {
|
||||||
'VEXTENSION':'.mp4','VCODEC':'libx264','VPRESET':None,'VFRAMERATE':None,'VBITRATE':None,'VCRF':None,'VLEVEL':None,
|
'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None,
|
||||||
'VRESOLUTION':'460:320','VCODEC_ALLOW':['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'],
|
'VCRF': None, 'VLEVEL': None,
|
||||||
'ACODEC':'aac','ACODEC_ALLOW':['libfaac'],'ABITRATE':128000, 'ACHANNELS':2,
|
'VRESOLUTION': '1280:720',
|
||||||
'ACODEC2':None,'ACODEC2_ALLOW':[],'ABITRATE2':None, 'ACHANNELS2':None,
|
'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'],
|
||||||
'ACODEC3':None,'ACODEC3_ALLOW':[],'ABITRATE3':None, 'ACHANNELS3':None,
|
'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2,
|
||||||
'SCODEC':'mov_text'
|
'ACODEC2': None, 'ACODEC2_ALLOW': [], 'ABITRATE2': None, 'ACHANNELS2': None,
|
||||||
},
|
'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None,
|
||||||
'PS3':{
|
'SCODEC': 'mov_text'
|
||||||
'VEXTENSION':'.mp4','VCODEC':'libx264','VPRESET':None,'VFRAMERATE':None,'VBITRATE':None,'VCRF':None,'VLEVEL':None,
|
},
|
||||||
'VRESOLUTION':None,'VCODEC_ALLOW':['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'],
|
'iPhone': {
|
||||||
'ACODEC':'ac3','ACODEC_ALLOW':['ac3'],'ABITRATE':None, 'ACHANNELS':6,
|
'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None,
|
||||||
'ACODEC2':'aac','ACODEC2_ALLOW':['libfaac'],'ABITRATE2':None, 'ACHANNELS2':2,
|
'VCRF': None, 'VLEVEL': None,
|
||||||
'ACODEC3':None,'ACODEC3_ALLOW':[],'ABITRATE3':None, 'ACHANNELS3':None,
|
'VRESOLUTION': '460:320',
|
||||||
'SCODEC':'mov_text'
|
'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'],
|
||||||
},
|
'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2,
|
||||||
'xbox':{
|
'ACODEC2': None, 'ACODEC2_ALLOW': [], 'ABITRATE2': None, 'ACHANNELS2': None,
|
||||||
'VEXTENSION':'.mp4','VCODEC':'libx264','VPRESET':None,'VFRAMERATE':None,'VBITRATE':None,'VCRF':None,'VLEVEL':None,
|
'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None,
|
||||||
'VRESOLUTION':None,'VCODEC_ALLOW':['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'],
|
'SCODEC': 'mov_text'
|
||||||
'ACODEC':'ac3','ACODEC_ALLOW':['ac3'],'ABITRATE':None, 'ACHANNELS':6,
|
},
|
||||||
'ACODEC2':None,'ACODEC2_ALLOW':[],'ABITRATE2':None, 'ACHANNELS2':None,
|
'PS3': {
|
||||||
'ACODEC3':None,'ACODEC3_ALLOW':[],'ABITRATE3':None, 'ACHANNELS3':None,
|
'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None,
|
||||||
'SCODEC':'mov_text'
|
'VCRF': None, 'VLEVEL': None,
|
||||||
},
|
'VRESOLUTION': None,
|
||||||
'Roku-480p':{
|
'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'],
|
||||||
'VEXTENSION':'.mp4','VCODEC':'libx264','VPRESET':None,'VFRAMERATE':None,'VBITRATE':None,'VCRF':None,'VLEVEL':None,
|
'ACODEC': 'ac3', 'ACODEC_ALLOW': ['ac3'], 'ABITRATE': None, 'ACHANNELS': 6,
|
||||||
'VRESOLUTION':None,'VCODEC_ALLOW':['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'],
|
'ACODEC2': 'aac', 'ACODEC2_ALLOW': ['libfaac'], 'ABITRATE2': None, 'ACHANNELS2': 2,
|
||||||
'ACODEC':'aac','ACODEC_ALLOW':['libfaac'],'ABITRATE':128000, 'ACHANNELS':2,
|
'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None,
|
||||||
'ACODEC2':'ac3','ACODEC2_ALLOW':['ac3'],'ABITRATE2':None, 'ACHANNELS2':6,
|
'SCODEC': 'mov_text'
|
||||||
'ACODEC3':None,'ACODEC3_ALLOW':[],'ABITRATE3':None, 'ACHANNELS3':None,
|
},
|
||||||
'SCODEC':'mov_text'
|
'xbox': {
|
||||||
},
|
'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None,
|
||||||
'Roku-720p':{
|
'VCRF': None, 'VLEVEL': None,
|
||||||
'VEXTENSION':'.mp4','VCODEC':'libx264','VPRESET':None,'VFRAMERATE':None,'VBITRATE':None,'VCRF':None,'VLEVEL':None,
|
'VRESOLUTION': None,
|
||||||
'VRESOLUTION':None,'VCODEC_ALLOW':['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'],
|
'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'],
|
||||||
'ACODEC':'aac','ACODEC_ALLOW':['libfaac'],'ABITRATE':128000, 'ACHANNELS':2,
|
'ACODEC': 'ac3', 'ACODEC_ALLOW': ['ac3'], 'ABITRATE': None, 'ACHANNELS': 6,
|
||||||
'ACODEC2':'ac3','ACODEC2_ALLOW':['ac3'],'ABITRATE2':None, 'ACHANNELS2':6,
|
'ACODEC2': None, 'ACODEC2_ALLOW': [], 'ABITRATE2': None, 'ACHANNELS2': None,
|
||||||
'ACODEC3':None,'ACODEC3_ALLOW':[],'ABITRATE3':None, 'ACHANNELS3':None,
|
'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None,
|
||||||
'SCODEC':'mov_text'
|
'SCODEC': 'mov_text'
|
||||||
},
|
},
|
||||||
'Roku-1080p':{
|
'Roku-480p': {
|
||||||
'VEXTENSION':'.mp4','VCODEC':'libx264','VPRESET':None,'VFRAMERATE':None,'VBITRATE':None,'VCRF':None,'VLEVEL':None,
|
'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None,
|
||||||
'VRESOLUTION':None,'VCODEC_ALLOW':['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'],
|
'VCRF': None, 'VLEVEL': None,
|
||||||
'ACODEC':'aac','ACODEC_ALLOW':['libfaac'],'ABITRATE':160000, 'ACHANNELS':2,
|
'VRESOLUTION': None,
|
||||||
'ACODEC2':'ac3','ACODEC2_ALLOW':['ac3'],'ABITRATE2':None, 'ACHANNELS2':6,
|
'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'],
|
||||||
'ACODEC3':None,'ACODEC3_ALLOW':[],'ABITRATE3':None, 'ACHANNELS3':None,
|
'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2,
|
||||||
'SCODEC':'mov_text'
|
'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6,
|
||||||
},
|
'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None,
|
||||||
'mkv':{
|
'SCODEC': 'mov_text'
|
||||||
'VEXTENSION':'.mkv','VCODEC':'libx264','VPRESET':None,'VFRAMERATE':None,'VBITRATE':None,'VCRF':None,'VLEVEL':None,
|
},
|
||||||
'VRESOLUTION':None,'VCODEC_ALLOW':['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4', 'mpeg2video'],
|
'Roku-720p': {
|
||||||
'ACODEC':'dts','ACODEC_ALLOW':['libfaac', 'dts', 'ac3', 'mp2', 'mp3'],'ABITRATE':None, 'ACHANNELS':8,
|
'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None,
|
||||||
'ACODEC2':None,'ACODEC2_ALLOW':[],'ABITRATE2':None, 'ACHANNELS2':None,
|
'VCRF': None, 'VLEVEL': None,
|
||||||
'ACODEC3':'ac3','ACODEC3_ALLOW':['libfaac', 'dts', 'ac3', 'mp2', 'mp3'],'ABITRATE3':None, 'ACHANNELS3':8,
|
'VRESOLUTION': None,
|
||||||
'SCODEC':'mov_text'
|
'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'],
|
||||||
},
|
'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2,
|
||||||
'mp4-scene-release':{
|
'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6,
|
||||||
'VEXTENSION':'.mp4','VCODEC':'libx264','VPRESET':None,'VFRAMERATE':None,'VBITRATE':None,'VCRF':19,'VLEVEL':'3.1',
|
'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None,
|
||||||
'VRESOLUTION':None,'VCODEC_ALLOW':['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4', 'mpeg2video'],
|
'SCODEC': 'mov_text'
|
||||||
'ACODEC':'dts','ACODEC_ALLOW':['libfaac', 'dts', 'ac3', 'mp2', 'mp3'],'ABITRATE':None, 'ACHANNELS':8,
|
},
|
||||||
'ACODEC2':None,'ACODEC2_ALLOW':[],'ABITRATE2':None, 'ACHANNELS2':None,
|
'Roku-1080p': {
|
||||||
'ACODEC3':'ac3','ACODEC3_ALLOW':['libfaac', 'dts', 'ac3', 'mp2', 'mp3'],'ABITRATE3':None, 'ACHANNELS3':8,
|
'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None,
|
||||||
'SCODEC':'mov_text'
|
'VCRF': None, 'VLEVEL': None,
|
||||||
}
|
'VRESOLUTION': None,
|
||||||
|
'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'],
|
||||||
|
'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 160000, 'ACHANNELS': 2,
|
||||||
|
'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6,
|
||||||
|
'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None,
|
||||||
|
'SCODEC': 'mov_text'
|
||||||
|
},
|
||||||
|
'mkv': {
|
||||||
|
'VEXTENSION': '.mkv', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None,
|
||||||
|
'VCRF': None, 'VLEVEL': None,
|
||||||
|
'VRESOLUTION': None,
|
||||||
|
'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4', 'mpeg2video'],
|
||||||
|
'ACODEC': 'dts', 'ACODEC_ALLOW': ['libfaac', 'dts', 'ac3', 'mp2', 'mp3'], 'ABITRATE': None, 'ACHANNELS': 8,
|
||||||
|
'ACODEC2': None, 'ACODEC2_ALLOW': [], 'ABITRATE2': None, 'ACHANNELS2': None,
|
||||||
|
'ACODEC3': 'ac3', 'ACODEC3_ALLOW': ['libfaac', 'dts', 'ac3', 'mp2', 'mp3'], 'ABITRATE3': None,
|
||||||
|
'ACHANNELS3': 8,
|
||||||
|
'SCODEC': 'mov_text'
|
||||||
|
},
|
||||||
|
'mp4-scene-release': {
|
||||||
|
'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None,
|
||||||
|
'VCRF': 19, 'VLEVEL': '3.1',
|
||||||
|
'VRESOLUTION': None,
|
||||||
|
'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4', 'mpeg2video'],
|
||||||
|
'ACODEC': 'dts', 'ACODEC_ALLOW': ['libfaac', 'dts', 'ac3', 'mp2', 'mp3'], 'ABITRATE': None, 'ACHANNELS': 8,
|
||||||
|
'ACODEC2': None, 'ACODEC2_ALLOW': [], 'ABITRATE2': None, 'ACHANNELS2': None,
|
||||||
|
'ACODEC3': 'ac3', 'ACODEC3_ALLOW': ['libfaac', 'dts', 'ac3', 'mp2', 'mp3'], 'ABITRATE3': None,
|
||||||
|
'ACHANNELS3': 8,
|
||||||
|
'SCODEC': 'mov_text'
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if DEFAULTS and DEFAULTS in transcode_defaults:
|
if DEFAULTS and DEFAULTS in transcode_defaults:
|
||||||
VEXTENSION = transcode_defaults[DEFAULTS]['VEXTENSION']
|
VEXTENSION = transcode_defaults[DEFAULTS]['VEXTENSION']
|
||||||
VCODEC = transcode_defaults[DEFAULTS]['VCODEC']
|
VCODEC = transcode_defaults[DEFAULTS]['VCODEC']
|
||||||
|
@ -630,25 +703,29 @@ def initialize(section=None):
|
||||||
|
|
||||||
if VEXTENSION in allow_subs:
|
if VEXTENSION in allow_subs:
|
||||||
ALLOWSUBS = 1
|
ALLOWSUBS = 1
|
||||||
if not VCODEC_ALLOW and VCODEC: VCODEC_ALLOW.extend([VCODEC])
|
if not VCODEC_ALLOW and VCODEC:
|
||||||
|
VCODEC_ALLOW.extend([VCODEC])
|
||||||
for codec in VCODEC_ALLOW:
|
for codec in VCODEC_ALLOW:
|
||||||
if codec in codec_alias:
|
if codec in codec_alias:
|
||||||
extra = [ item for item in codec_alias[codec] if item not in VCODEC_ALLOW ]
|
extra = [item for item in codec_alias[codec] if item not in VCODEC_ALLOW]
|
||||||
VCODEC_ALLOW.extend(extra)
|
VCODEC_ALLOW.extend(extra)
|
||||||
if not ACODEC_ALLOW and ACODEC: ACODEC_ALLOW.extend([ACODEC])
|
if not ACODEC_ALLOW and ACODEC:
|
||||||
|
ACODEC_ALLOW.extend([ACODEC])
|
||||||
for codec in ACODEC_ALLOW:
|
for codec in ACODEC_ALLOW:
|
||||||
if codec in codec_alias:
|
if codec in codec_alias:
|
||||||
extra = [ item for item in codec_alias[codec] if item not in ACODEC_ALLOW ]
|
extra = [item for item in codec_alias[codec] if item not in ACODEC_ALLOW]
|
||||||
ACODEC_ALLOW.extend(extra)
|
ACODEC_ALLOW.extend(extra)
|
||||||
if not ACODEC2_ALLOW and ACODEC2: ACODEC2_ALLOW.extend([ACODEC2])
|
if not ACODEC2_ALLOW and ACODEC2:
|
||||||
|
ACODEC2_ALLOW.extend([ACODEC2])
|
||||||
for codec in ACODEC2_ALLOW:
|
for codec in ACODEC2_ALLOW:
|
||||||
if codec in codec_alias:
|
if codec in codec_alias:
|
||||||
extra = [ item for item in codec_alias[codec] if item not in ACODEC2_ALLOW ]
|
extra = [item for item in codec_alias[codec] if item not in ACODEC2_ALLOW]
|
||||||
ACODEC2_ALLOW.extend(extra)
|
ACODEC2_ALLOW.extend(extra)
|
||||||
if not ACODEC3_ALLOW and ACODEC3: ACODEC3_ALLOW.extend([ACODEC3])
|
if not ACODEC3_ALLOW and ACODEC3:
|
||||||
|
ACODEC3_ALLOW.extend([ACODEC3])
|
||||||
for codec in ACODEC3_ALLOW:
|
for codec in ACODEC3_ALLOW:
|
||||||
if codec in codec_alias:
|
if codec in codec_alias:
|
||||||
extra = [ item for item in codec_alias[codec] if item not in ACODEC3_ALLOW ]
|
extra = [item for item in codec_alias[codec] if item not in ACODEC3_ALLOW]
|
||||||
ACODEC3_ALLOW.extend(extra)
|
ACODEC3_ALLOW.extend(extra)
|
||||||
codec_alias = {} # clear memory
|
codec_alias = {} # clear memory
|
||||||
|
|
||||||
|
@ -674,47 +751,59 @@ def initialize(section=None):
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
SEVENZIP = subprocess.Popen(['which', '7z'], stdout=subprocess.PIPE).communicate()[0].strip()
|
SEVENZIP = subprocess.Popen(['which', '7z'], stdout=subprocess.PIPE).communicate()[0].strip()
|
||||||
except: pass
|
except:
|
||||||
if not SEVENZIP:
|
pass
|
||||||
|
if not SEVENZIP:
|
||||||
try:
|
try:
|
||||||
SEVENZIP = subprocess.Popen(['which', '7zr'], stdout=subprocess.PIPE).communicate()[0].strip()
|
SEVENZIP = subprocess.Popen(['which', '7zr'], stdout=subprocess.PIPE).communicate()[0].strip()
|
||||||
except: pass
|
except:
|
||||||
if not SEVENZIP:
|
pass
|
||||||
|
if not SEVENZIP:
|
||||||
try:
|
try:
|
||||||
SEVENZIP = subprocess.Popen(['which', '7za'], stdout=subprocess.PIPE).communicate()[0].strip()
|
SEVENZIP = subprocess.Popen(['which', '7za'], stdout=subprocess.PIPE).communicate()[0].strip()
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
if not SEVENZIP:
|
if not SEVENZIP:
|
||||||
SEVENZIP = None
|
SEVENZIP = None
|
||||||
logger.warning("Failed to locate 7zip. Transcosing of disk images and extraction of .7z files will not be possible!")
|
logger.warning(
|
||||||
if os.path.isfile(os.path.join(FFMPEG_PATH, 'ffmpeg')) or os.access(os.path.join(FFMPEG_PATH, 'ffmpeg'), os.X_OK):
|
"Failed to locate 7zip. Transcosing of disk images and extraction of .7z files will not be possible!")
|
||||||
|
if os.path.isfile(os.path.join(FFMPEG_PATH, 'ffmpeg')) or os.access(os.path.join(FFMPEG_PATH, 'ffmpeg'),
|
||||||
|
os.X_OK):
|
||||||
FFMPEG = os.path.join(FFMPEG_PATH, 'ffmpeg')
|
FFMPEG = os.path.join(FFMPEG_PATH, 'ffmpeg')
|
||||||
elif os.path.isfile(os.path.join(FFMPEG_PATH, 'avconv')) or os.access(os.path.join(FFMPEG_PATH, 'avconv'), os.X_OK):
|
elif os.path.isfile(os.path.join(FFMPEG_PATH, 'avconv')) or os.access(os.path.join(FFMPEG_PATH, 'avconv'),
|
||||||
|
os.X_OK):
|
||||||
FFMPEG = os.path.join(FFMPEG_PATH, 'avconv')
|
FFMPEG = os.path.join(FFMPEG_PATH, 'avconv')
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
FFMPEG = subprocess.Popen(['which', 'ffmpeg'], stdout=subprocess.PIPE).communicate()[0].strip()
|
FFMPEG = subprocess.Popen(['which', 'ffmpeg'], stdout=subprocess.PIPE).communicate()[0].strip()
|
||||||
except: pass
|
except:
|
||||||
if not FFMPEG:
|
pass
|
||||||
|
if not FFMPEG:
|
||||||
try:
|
try:
|
||||||
FFMPEG = subprocess.Popen(['which', 'avconv'], stdout=subprocess.PIPE).communicate()[0].strip()
|
FFMPEG = subprocess.Popen(['which', 'avconv'], stdout=subprocess.PIPE).communicate()[0].strip()
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
if not FFMPEG:
|
if not FFMPEG:
|
||||||
FFMPEG = None
|
FFMPEG = None
|
||||||
logger.warning("Failed to locate ffmpeg. Transcoding disabled!")
|
logger.warning("Failed to locate ffmpeg. Transcoding disabled!")
|
||||||
logger.warning("Install ffmpeg with x264 support to enable this feature ...")
|
logger.warning("Install ffmpeg with x264 support to enable this feature ...")
|
||||||
|
|
||||||
if os.path.isfile(os.path.join(FFMPEG_PATH, 'ffprobe')) or os.access(os.path.join(FFMPEG_PATH, 'ffprobe'), os.X_OK):
|
if os.path.isfile(os.path.join(FFMPEG_PATH, 'ffprobe')) or os.access(os.path.join(FFMPEG_PATH, 'ffprobe'),
|
||||||
|
os.X_OK):
|
||||||
FFPROBE = os.path.join(FFMPEG_PATH, 'ffprobe')
|
FFPROBE = os.path.join(FFMPEG_PATH, 'ffprobe')
|
||||||
elif os.path.isfile(os.path.join(FFMPEG_PATH, 'avprobe')) or os.access(os.path.join(FFMPEG_PATH, 'avprobe'), os.X_OK):
|
elif os.path.isfile(os.path.join(FFMPEG_PATH, 'avprobe')) or os.access(os.path.join(FFMPEG_PATH, 'avprobe'),
|
||||||
|
os.X_OK):
|
||||||
FFPROBE = os.path.join(FFMPEG_PATH, 'avprobe')
|
FFPROBE = os.path.join(FFMPEG_PATH, 'avprobe')
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
FFPROBE = subprocess.Popen(['which', 'ffprobe'], stdout=subprocess.PIPE).communicate()[0].strip()
|
FFPROBE = subprocess.Popen(['which', 'ffprobe'], stdout=subprocess.PIPE).communicate()[0].strip()
|
||||||
except: pass
|
except:
|
||||||
if not FFPROBE:
|
pass
|
||||||
|
if not FFPROBE:
|
||||||
try:
|
try:
|
||||||
FFPROBE = subprocess.Popen(['which', 'avprobe'], stdout=subprocess.PIPE).communicate()[0].strip()
|
FFPROBE = subprocess.Popen(['which', 'avprobe'], stdout=subprocess.PIPE).communicate()[0].strip()
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
if not FFPROBE:
|
if not FFPROBE:
|
||||||
FFPROBE = None
|
FFPROBE = None
|
||||||
if CHECK_MEDIA:
|
if CHECK_MEDIA:
|
||||||
|
@ -723,7 +812,7 @@ def initialize(section=None):
|
||||||
|
|
||||||
# check for script-defied section and if None set to allow sections
|
# check for script-defied section and if None set to allow sections
|
||||||
SECTIONS = CFG[tuple(x for x in CFG if CFG[x].sections and CFG[x].isenabled()) if not section else (section,)]
|
SECTIONS = CFG[tuple(x for x in CFG if CFG[x].sections and CFG[x].isenabled()) if not section else (section,)]
|
||||||
for section,subsections in SECTIONS.items():
|
for section, subsections in SECTIONS.items():
|
||||||
CATEGORIES.extend([subsection for subsection in subsections if CFG[section][subsection].isenabled()])
|
CATEGORIES.extend([subsection for subsection in subsections if CFG[section][subsection].isenabled()])
|
||||||
CATEGORIES = list(set(CATEGORIES))
|
CATEGORIES = list(set(CATEGORIES))
|
||||||
|
|
||||||
|
@ -733,6 +822,7 @@ def initialize(section=None):
|
||||||
# finished initalizing
|
# finished initalizing
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def restart():
|
def restart():
|
||||||
install_type = versionCheck.CheckVersion().install_type
|
install_type = versionCheck.CheckVersion().install_type
|
||||||
|
|
||||||
|
@ -752,11 +842,12 @@ def restart():
|
||||||
|
|
||||||
os._exit(status)
|
os._exit(status)
|
||||||
|
|
||||||
|
|
||||||
def rchmod(path, mod):
|
def rchmod(path, mod):
|
||||||
logger.log("Changing file mode of %s to %s" % (path, oct(mod)))
|
logger.log("Changing file mode of %s to %s" % (path, oct(mod)))
|
||||||
os.chmod(path, mod)
|
os.chmod(path, mod)
|
||||||
if not os.path.isdir(path):
|
if not os.path.isdir(path):
|
||||||
return # Skip files
|
return # Skip files
|
||||||
|
|
||||||
for root, dirs, files in os.walk(path):
|
for root, dirs, files in os.walk(path):
|
||||||
for d in dirs:
|
for d in dirs:
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
__all__ = ["mainDB"]
|
__all__ = ["mainDB"]
|
||||||
|
|
|
@ -14,6 +14,7 @@ def backupDatabase(version):
|
||||||
else:
|
else:
|
||||||
logger.info("Proceeding with upgrade")
|
logger.info("Proceeding with upgrade")
|
||||||
|
|
||||||
|
|
||||||
# ======================
|
# ======================
|
||||||
# = Main DB Migrations =
|
# = Main DB Migrations =
|
||||||
# ======================
|
# ======================
|
||||||
|
@ -45,21 +46,21 @@ class InitialSchema(nzbToMediaDB.SchemaUpgrade):
|
||||||
cur_db_version) + ") is too old to migrate from what this version of nzbToMedia supports (" + \
|
cur_db_version) + ") is too old to migrate from what this version of nzbToMedia supports (" + \
|
||||||
str(MIN_DB_VERSION) + ").\n" + \
|
str(MIN_DB_VERSION) + ").\n" + \
|
||||||
"Please remove nzbtomedia.db file to begin fresh."
|
"Please remove nzbtomedia.db file to begin fresh."
|
||||||
)
|
)
|
||||||
|
|
||||||
if cur_db_version > MAX_DB_VERSION:
|
if cur_db_version > MAX_DB_VERSION:
|
||||||
logger.log_error_and_exit("Your database version (" + str(
|
logger.log_error_and_exit("Your database version (" + str(
|
||||||
cur_db_version) + ") has been incremented past what this version of nzbToMedia supports (" + \
|
cur_db_version) + ") has been incremented past what this version of nzbToMedia supports (" + \
|
||||||
str(MAX_DB_VERSION) + ").\n" + \
|
str(MAX_DB_VERSION) + ").\n" + \
|
||||||
"If you have used other forks of nzbToMedia, your database may be unusable due to their modifications."
|
"If you have used other forks of nzbToMedia, your database may be unusable due to their modifications."
|
||||||
)
|
)
|
||||||
if cur_db_version < MAX_DB_VERSION: # We need to upgrade.
|
if cur_db_version < MAX_DB_VERSION: # We need to upgrade.
|
||||||
queries = [
|
queries = [
|
||||||
"CREATE TABLE downloads2 (input_directory TEXT, input_name TEXT, input_hash TEXT, input_id TEXT, client_agent TEXT, status INTEGER, last_update NUMERIC, CONSTRAINT pk_downloadID PRIMARY KEY (input_directory, input_name));",
|
"CREATE TABLE downloads2 (input_directory TEXT, input_name TEXT, input_hash TEXT, input_id TEXT, client_agent TEXT, status INTEGER, last_update NUMERIC, CONSTRAINT pk_downloadID PRIMARY KEY (input_directory, input_name));",
|
||||||
"INSERT INTO downloads2 SELECT * FROM downloads;",
|
"INSERT INTO downloads2 SELECT * FROM downloads;",
|
||||||
"DROP TABLE IF EXISTS downloads;",
|
"DROP TABLE IF EXISTS downloads;",
|
||||||
"ALTER TABLE downloads2 RENAME TO downloads;",
|
"ALTER TABLE downloads2 RENAME TO downloads;",
|
||||||
"INSERT INTO db_version (db_version) VALUES (2);"
|
"INSERT INTO db_version (db_version) VALUES (2);"
|
||||||
]
|
]
|
||||||
for query in queries:
|
for query in queries:
|
||||||
self.connection.action(query)
|
self.connection.action(query)
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
|
|
|
@ -8,6 +8,7 @@ import core
|
||||||
from subprocess import call, Popen
|
from subprocess import call, Popen
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
def extract(filePath, outputDestination):
|
def extract(filePath, outputDestination):
|
||||||
success = 0
|
success = 0
|
||||||
# Using Windows
|
# Using Windows
|
||||||
|
@ -22,9 +23,9 @@ def extract(filePath, outputDestination):
|
||||||
# Using unix
|
# Using unix
|
||||||
else:
|
else:
|
||||||
required_cmds = ["unrar", "unzip", "tar", "unxz", "unlzma", "7zr", "bunzip2"]
|
required_cmds = ["unrar", "unzip", "tar", "unxz", "unlzma", "7zr", "bunzip2"]
|
||||||
## Possible future suport:
|
# ## Possible future suport:
|
||||||
# gunzip: gz (cmd will delete original archive)
|
# gunzip: gz (cmd will delete original archive)
|
||||||
## the following do not extract to dest dir
|
# ## the following do not extract to dest dir
|
||||||
# ".xz": ["xz", "-d --keep"],
|
# ".xz": ["xz", "-d --keep"],
|
||||||
# ".lzma": ["xz", "-d --format=lzma --keep"],
|
# ".lzma": ["xz", "-d --format=lzma --keep"],
|
||||||
# ".bz2": ["bzip2", "-d --keep"],
|
# ".bz2": ["bzip2", "-d --keep"],
|
||||||
|
@ -43,12 +44,13 @@ def extract(filePath, outputDestination):
|
||||||
if not os.getenv('TR_TORRENT_DIR'):
|
if not os.getenv('TR_TORRENT_DIR'):
|
||||||
devnull = open(os.devnull, 'w')
|
devnull = open(os.devnull, 'w')
|
||||||
for cmd in required_cmds:
|
for cmd in required_cmds:
|
||||||
if call(['which', cmd], stdout=devnull, stderr=devnull): #note, returns 0 if exists, or 1 if doesn't exist.
|
if call(['which', cmd], stdout=devnull,
|
||||||
|
stderr=devnull): # note, returns 0 if exists, or 1 if doesn't exist.
|
||||||
if cmd == "7zr" and not call(["which", "7z"]): # we do have "7z" command
|
if cmd == "7zr" and not call(["which", "7z"]): # we do have "7z" command
|
||||||
EXTRACT_COMMANDS[".7z"] = ["7z", "x"]
|
EXTRACT_COMMANDS[".7z"] = ["7z", "x"]
|
||||||
elif cmd == "7zr" and not call(["which", "7za"]): # we do have "7za" command
|
elif cmd == "7zr" and not call(["which", "7za"]): # we do have "7za" command
|
||||||
EXTRACT_COMMANDS[".7z"] = ["7za", "x"]
|
EXTRACT_COMMANDS[".7z"] = ["7za", "x"]
|
||||||
else:
|
else:
|
||||||
for k, v in EXTRACT_COMMANDS.items():
|
for k, v in EXTRACT_COMMANDS.items():
|
||||||
if cmd in v[0]:
|
if cmd in v[0]:
|
||||||
core.logger.error("EXTRACTOR: %s not found, disabling support for %s" % (cmd, k))
|
core.logger.error("EXTRACTOR: %s not found, disabling support for %s" % (cmd, k))
|
||||||
|
@ -77,7 +79,7 @@ def extract(filePath, outputDestination):
|
||||||
core.logger.debug("EXTRACTOR: Unknown file type: %s" % ext[1])
|
core.logger.debug("EXTRACTOR: Unknown file type: %s" % ext[1])
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Create outputDestination folder
|
# Create outputDestination folder
|
||||||
core.makeDir(outputDestination)
|
core.makeDir(outputDestination)
|
||||||
|
|
||||||
if core.PASSWORDSFILE != "" and os.path.isfile(os.path.normpath(core.PASSWORDSFILE)):
|
if core.PASSWORDSFILE != "" and os.path.isfile(os.path.normpath(core.PASSWORDSFILE)):
|
||||||
|
@ -99,7 +101,7 @@ def extract(filePath, outputDestination):
|
||||||
pwd = os.getcwd() # Get our Present Working Directory
|
pwd = os.getcwd() # Get our Present Working Directory
|
||||||
os.chdir(outputDestination) # Not all unpack commands accept full paths, so just extract into this directory
|
os.chdir(outputDestination) # Not all unpack commands accept full paths, so just extract into this directory
|
||||||
devnull = open(os.devnull, 'w')
|
devnull = open(os.devnull, 'w')
|
||||||
|
|
||||||
try: # now works same for nt and *nix
|
try: # now works same for nt and *nix
|
||||||
info = None
|
info = None
|
||||||
cmd.append(filePath) # add filePath to final cmd arg.
|
cmd.append(filePath) # add filePath to final cmd arg.
|
||||||
|
@ -112,7 +114,8 @@ def extract(filePath, outputDestination):
|
||||||
cmd2.append("-p-") # don't prompt for password.
|
cmd2.append("-p-") # don't prompt for password.
|
||||||
p = Popen(cmd2, stdout=devnull, stderr=devnull, startupinfo=info) # should extract files fine.
|
p = Popen(cmd2, stdout=devnull, stderr=devnull, startupinfo=info) # should extract files fine.
|
||||||
res = p.wait()
|
res = p.wait()
|
||||||
if (res >= 0 and os.name == 'nt') or res == 0: # for windows chp returns process id if successful or -1*Error code. Linux returns 0 for successful.
|
if (
|
||||||
|
res >= 0 and os.name == 'nt') or res == 0: # for windows chp returns process id if successful or -1*Error code. Linux returns 0 for successful.
|
||||||
core.logger.info("EXTRACTOR: Extraction was successful for %s to %s" % (filePath, outputDestination))
|
core.logger.info("EXTRACTOR: Extraction was successful for %s to %s" % (filePath, outputDestination))
|
||||||
success = 1
|
success = 1
|
||||||
elif len(passwords) > 0:
|
elif len(passwords) > 0:
|
||||||
|
@ -121,14 +124,14 @@ def extract(filePath, outputDestination):
|
||||||
if password == "": # if edited in windows or otherwise if blank lines.
|
if password == "": # if edited in windows or otherwise if blank lines.
|
||||||
continue
|
continue
|
||||||
cmd2 = cmd
|
cmd2 = cmd
|
||||||
#append password here.
|
# append password here.
|
||||||
passcmd = "-p" + password
|
passcmd = "-p" + password
|
||||||
cmd2.append(passcmd)
|
cmd2.append(passcmd)
|
||||||
p = Popen(cmd2, stdout=devnull, stderr=devnull, startupinfo=info) # should extract files fine.
|
p = Popen(cmd2, stdout=devnull, stderr=devnull, startupinfo=info) # should extract files fine.
|
||||||
res = p.wait()
|
res = p.wait()
|
||||||
if (res >= 0 and platform == 'Windows') or res == 0:
|
if (res >= 0 and platform == 'Windows') or res == 0:
|
||||||
core.logger.info("EXTRACTOR: Extraction was successful for %s to %s using password: %s" % (
|
core.logger.info("EXTRACTOR: Extraction was successful for %s to %s using password: %s" % (
|
||||||
filePath, outputDestination, password))
|
filePath, outputDestination, password))
|
||||||
success = 1
|
success = 1
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
@ -142,19 +145,21 @@ def extract(filePath, outputDestination):
|
||||||
os.chdir(pwd) # Go back to our Original Working Directory
|
os.chdir(pwd) # Go back to our Original Working Directory
|
||||||
if success:
|
if success:
|
||||||
# sleep to let files finish writing to disk
|
# sleep to let files finish writing to disk
|
||||||
sleep (3)
|
sleep(3)
|
||||||
perms = stat.S_IMODE(os.lstat(os.path.split(filePath)[0]).st_mode)
|
perms = stat.S_IMODE(os.lstat(os.path.split(filePath)[0]).st_mode)
|
||||||
for dir, subdirs, files in os.walk(outputDestination):
|
for dir, subdirs, files in os.walk(outputDestination):
|
||||||
for subdir in subdirs:
|
for subdir in subdirs:
|
||||||
if not os.path.join(dir, subdir) in origFiles:
|
if not os.path.join(dir, subdir) in origFiles:
|
||||||
try:
|
try:
|
||||||
os.chmod(os.path.join(dir, subdir), perms)
|
os.chmod(os.path.join(dir, subdir), perms)
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
for file in files:
|
for file in files:
|
||||||
if not os.path.join(dir, file) in origFiles:
|
if not os.path.join(dir, file) in origFiles:
|
||||||
try:
|
try:
|
||||||
shutil.copymode(filePath, os.path.join(dir, file))
|
shutil.copymode(filePath, os.path.join(dir, file))
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
core.logger.error("EXTRACTOR: Extraction failed for %s. Result was %s" % (filePath, res))
|
core.logger.error("EXTRACTOR: Extraction failed for %s. Result was %s" % (filePath, res))
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import json
|
import json
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
|
||||||
class GitHub(object):
|
class GitHub(object):
|
||||||
"""
|
"""
|
||||||
Simple api wrapper for the Github API v3.
|
Simple api wrapper for the Github API v3.
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
|
|
|
@ -30,6 +30,7 @@ if os.name == 'nt':
|
||||||
info = subprocess.STARTUPINFO()
|
info = subprocess.STARTUPINFO()
|
||||||
info.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
info.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||||
|
|
||||||
|
|
||||||
# Prevent spaces from messing with us!
|
# Prevent spaces from messing with us!
|
||||||
def _escape_param(param):
|
def _escape_param(param):
|
||||||
return '"%s"' % param
|
return '"%s"' % param
|
||||||
|
@ -45,9 +46,9 @@ def _link_windows(src, dest):
|
||||||
|
|
||||||
raise IOError(err.output.decode('utf-8'))
|
raise IOError(err.output.decode('utf-8'))
|
||||||
|
|
||||||
# TODO, find out what kind of messages Windows sends us from mklink
|
# TODO, find out what kind of messages Windows sends us from mklink
|
||||||
# print(stdout)
|
# print(stdout)
|
||||||
# assume if they ret-coded 0 we're good
|
# assume if they ret-coded 0 we're good
|
||||||
|
|
||||||
|
|
||||||
def _symlink_windows(src, dest):
|
def _symlink_windows(src, dest):
|
||||||
|
@ -58,9 +59,10 @@ def _symlink_windows(src, dest):
|
||||||
except CalledProcessError as err:
|
except CalledProcessError as err:
|
||||||
raise IOError(err.output.decode('utf-8'))
|
raise IOError(err.output.decode('utf-8'))
|
||||||
|
|
||||||
# TODO, find out what kind of messages Windows sends us from mklink
|
# TODO, find out what kind of messages Windows sends us from mklink
|
||||||
# print(stdout)
|
# print(stdout)
|
||||||
# assume if they ret-coded 0 we're good
|
# assume if they ret-coded 0 we're good
|
||||||
|
|
||||||
|
|
||||||
def _dirlink_windows(src, dest):
|
def _dirlink_windows(src, dest):
|
||||||
try:
|
try:
|
||||||
|
@ -70,9 +72,10 @@ def _dirlink_windows(src, dest):
|
||||||
except CalledProcessError as err:
|
except CalledProcessError as err:
|
||||||
raise IOError(err.output.decode('utf-8'))
|
raise IOError(err.output.decode('utf-8'))
|
||||||
|
|
||||||
# TODO, find out what kind of messages Windows sends us from mklink
|
# TODO, find out what kind of messages Windows sends us from mklink
|
||||||
# print(stdout)
|
# print(stdout)
|
||||||
# assume if they ret-coded 0 we're good
|
# assume if they ret-coded 0 we're good
|
||||||
|
|
||||||
|
|
||||||
def _junctionlink_windows(src, dest):
|
def _junctionlink_windows(src, dest):
|
||||||
try:
|
try:
|
||||||
|
@ -82,9 +85,10 @@ def _junctionlink_windows(src, dest):
|
||||||
except CalledProcessError as err:
|
except CalledProcessError as err:
|
||||||
raise IOError(err.output.decode('utf-8'))
|
raise IOError(err.output.decode('utf-8'))
|
||||||
|
|
||||||
# TODO, find out what kind of messages Windows sends us from mklink
|
# TODO, find out what kind of messages Windows sends us from mklink
|
||||||
# print(stdout)
|
# print(stdout)
|
||||||
# assume if they ret-coded 0 we're good
|
# assume if they ret-coded 0 we're good
|
||||||
|
|
||||||
|
|
||||||
# Create a hard link to src named as dest
|
# Create a hard link to src named as dest
|
||||||
# This version of link, unlike os.link, supports nt systems as well
|
# This version of link, unlike os.link, supports nt systems as well
|
||||||
|
@ -102,6 +106,7 @@ def symlink(src, dest):
|
||||||
else:
|
else:
|
||||||
os.symlink(src, dest)
|
os.symlink(src, dest)
|
||||||
|
|
||||||
|
|
||||||
# Create a symlink to src named as dest, but don't fail if you're on nt
|
# Create a symlink to src named as dest, but don't fail if you're on nt
|
||||||
def dirlink(src, dest):
|
def dirlink(src, dest):
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
|
@ -109,9 +114,10 @@ def dirlink(src, dest):
|
||||||
else:
|
else:
|
||||||
os.symlink(src, dest)
|
os.symlink(src, dest)
|
||||||
|
|
||||||
|
|
||||||
# Create a symlink to src named as dest, but don't fail if you're on nt
|
# Create a symlink to src named as dest, but don't fail if you're on nt
|
||||||
def junctionlink(src, dest):
|
def junctionlink(src, dest):
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
_junctionlink_windows(src, dest)
|
_junctionlink_windows(src, dest)
|
||||||
else:
|
else:
|
||||||
os.symlink(src, dest)
|
os.symlink(src, dest)
|
||||||
|
|
|
@ -27,6 +27,7 @@ reverseNames = {u'ERROR': ERROR,
|
||||||
u'POSTPROCESS': POSTPROCESS,
|
u'POSTPROCESS': POSTPROCESS,
|
||||||
u'DB': DB}
|
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):
|
||||||
self.num_files = num_files
|
self.num_files = num_files
|
||||||
|
@ -68,7 +69,7 @@ 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 levels
|
# Add a new logging levels
|
||||||
logging.addLevelName(21, 'POSTPROCESS')
|
logging.addLevelName(21, 'POSTPROCESS')
|
||||||
logging.addLevelName(5, 'DB')
|
logging.addLevelName(5, 'DB')
|
||||||
|
|
||||||
|
@ -85,7 +86,7 @@ class NTMRotatingLogHandler(object):
|
||||||
{'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')
|
'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
|
||||||
|
@ -122,7 +123,7 @@ class NTMRotatingLogHandler(object):
|
||||||
{'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')
|
'db': logging.Formatter('%(asctime)s %(levelname)-8s::%(message)s', '%Y-%m-%d %H:%M:%S')
|
||||||
},
|
},
|
||||||
logging.Formatter('%(message)s'), ))
|
logging.Formatter('%(message)s'), ))
|
||||||
|
|
||||||
return file_handler
|
return file_handler
|
||||||
|
@ -234,6 +235,7 @@ class NTMRotatingLogHandler(object):
|
||||||
else:
|
else:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
class DispatchingFormatter:
|
class DispatchingFormatter:
|
||||||
def __init__(self, formatters, default_formatter):
|
def __init__(self, formatters, default_formatter):
|
||||||
self._formatters = formatters
|
self._formatters = formatters
|
||||||
|
@ -243,31 +245,41 @@ class DispatchingFormatter:
|
||||||
formatter = self._formatters.get(record.name, self._default_formatter)
|
formatter = self._formatters.get(record.name, self._default_formatter)
|
||||||
return formatter.format(record)
|
return formatter.format(record)
|
||||||
|
|
||||||
|
|
||||||
ntm_log_instance = NTMRotatingLogHandler(core.LOG_FILE, NUM_LOGS, LOG_SIZE)
|
ntm_log_instance = NTMRotatingLogHandler(core.LOG_FILE, NUM_LOGS, LOG_SIZE)
|
||||||
|
|
||||||
|
|
||||||
def log(toLog, logLevel=MESSAGE, section='MAIN'):
|
def log(toLog, logLevel=MESSAGE, section='MAIN'):
|
||||||
ntm_log_instance.log(toLog, logLevel, section)
|
ntm_log_instance.log(toLog, logLevel, section)
|
||||||
|
|
||||||
|
|
||||||
def info(toLog, section='MAIN'):
|
def info(toLog, section='MAIN'):
|
||||||
log(toLog, MESSAGE, section)
|
log(toLog, MESSAGE, section)
|
||||||
|
|
||||||
|
|
||||||
def error(toLog, section='MAIN'):
|
def error(toLog, section='MAIN'):
|
||||||
log(toLog, ERROR, section)
|
log(toLog, ERROR, section)
|
||||||
|
|
||||||
|
|
||||||
def warning(toLog, section='MAIN'):
|
def warning(toLog, section='MAIN'):
|
||||||
log(toLog, WARNING, section)
|
log(toLog, WARNING, section)
|
||||||
|
|
||||||
|
|
||||||
def debug(toLog, section='MAIN'):
|
def debug(toLog, section='MAIN'):
|
||||||
log(toLog, DEBUG, section)
|
log(toLog, DEBUG, section)
|
||||||
|
|
||||||
|
|
||||||
def postprocess(toLog, section='POSTPROCESS'):
|
def postprocess(toLog, section='POSTPROCESS'):
|
||||||
log(toLog, POSTPROCESS, section)
|
log(toLog, POSTPROCESS, section)
|
||||||
|
|
||||||
|
|
||||||
def db(toLog, section='DB'):
|
def db(toLog, section='DB'):
|
||||||
log(toLog, DB, section)
|
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)
|
||||||
|
|
||||||
|
|
||||||
def close():
|
def close():
|
||||||
ntm_log_instance.close_log()
|
ntm_log_instance.close_log()
|
||||||
|
|
|
@ -4,6 +4,7 @@ import core
|
||||||
import requests
|
import requests
|
||||||
from core import logger
|
from core import logger
|
||||||
|
|
||||||
|
|
||||||
def autoFork(section, inputCategory):
|
def autoFork(section, inputCategory):
|
||||||
# auto-detect correct section
|
# auto-detect correct section
|
||||||
# config settings
|
# config settings
|
||||||
|
@ -49,13 +50,13 @@ def autoFork(section, inputCategory):
|
||||||
detected = False
|
detected = False
|
||||||
if section == "NzbDrone":
|
if section == "NzbDrone":
|
||||||
logger.info("Attempting to verify %s fork" % inputCategory)
|
logger.info("Attempting to verify %s fork" % inputCategory)
|
||||||
url = "%s%s:%s%s/api/rootfolder" % (protocol,host,port,web_root)
|
url = "%s%s:%s%s/api/rootfolder" % (protocol, host, port, web_root)
|
||||||
headers={"X-Api-Key": apikey}
|
headers = {"X-Api-Key": apikey}
|
||||||
try:
|
try:
|
||||||
r = requests.get(url, headers=headers, stream=True, verify=False)
|
r = requests.get(url, headers=headers, stream=True, verify=False)
|
||||||
except requests.ConnectionError:
|
except requests.ConnectionError:
|
||||||
logger.warning("Could not connect to %s:%s to verify fork!" % (section, inputCategory))
|
logger.warning("Could not connect to %s:%s to verify fork!" % (section, inputCategory))
|
||||||
|
|
||||||
if not r.ok:
|
if not r.ok:
|
||||||
logger.warning("Connection to %s:%s failed! Check your configuration" % (section, inputCategory))
|
logger.warning("Connection to %s:%s failed! Check your configuration" % (section, inputCategory))
|
||||||
|
|
||||||
|
@ -67,12 +68,12 @@ def autoFork(section, inputCategory):
|
||||||
logger.info("Attempting to auto-detect %s fork" % inputCategory)
|
logger.info("Attempting to auto-detect %s fork" % inputCategory)
|
||||||
# define the order to test. Default must be first since the default fork doesn't reject parameters.
|
# define the order to test. Default must be first since the default fork doesn't reject parameters.
|
||||||
# then in order of most unique parameters.
|
# then in order of most unique parameters.
|
||||||
url = "%s%s:%s%s/home/postprocess/" % (protocol,host,port,web_root)
|
url = "%s%s:%s%s/home/postprocess/" % (protocol, host, port, web_root)
|
||||||
# attempting to auto-detect fork
|
# attempting to auto-detect fork
|
||||||
try:
|
try:
|
||||||
if username and password:
|
if username and password:
|
||||||
s = requests.Session()
|
s = requests.Session()
|
||||||
login = "%s%s:%s%s/login" % (protocol,host,port,web_root)
|
login = "%s%s:%s%s/login" % (protocol, host, port, web_root)
|
||||||
login_params = {'username': username, 'password': password}
|
login_params = {'username': username, 'password': password}
|
||||||
s.post(login, data=login_params, stream=True, verify=False)
|
s.post(login, data=login_params, stream=True, verify=False)
|
||||||
r = s.get(url, auth=(username, password), verify=False)
|
r = s.get(url, auth=(username, password), verify=False)
|
||||||
|
@ -83,10 +84,10 @@ def autoFork(section, inputCategory):
|
||||||
r = []
|
r = []
|
||||||
if r and r.ok:
|
if r and r.ok:
|
||||||
for param in params:
|
for param in params:
|
||||||
if not 'name="%s"' %(param) in r.text:
|
if not 'name="%s"' % (param) in r.text:
|
||||||
rem_params.append(param)
|
rem_params.append(param)
|
||||||
for param in rem_params:
|
for param in rem_params:
|
||||||
params.pop(param)
|
params.pop(param)
|
||||||
for fork in sorted(core.FORKS.iteritems(), reverse=False):
|
for fork in sorted(core.FORKS.iteritems(), reverse=False):
|
||||||
if params == fork[1]:
|
if params == fork[1]:
|
||||||
detected = True
|
detected = True
|
||||||
|
@ -101,4 +102,4 @@ def autoFork(section, inputCategory):
|
||||||
fork = core.FORKS.items()[core.FORKS.keys().index(core.FORK_DEFAULT)]
|
fork = core.FORKS.items()[core.FORKS.keys().index(core.FORK_DEFAULT)]
|
||||||
|
|
||||||
logger.info("%s:%s fork set to %s" % (section, inputCategory, fork[0]))
|
logger.info("%s:%s fork set to %s" % (section, inputCategory, fork[0]))
|
||||||
return fork[0], fork[1]
|
return fork[0], fork[1]
|
||||||
|
|
|
@ -8,13 +8,15 @@ from core import logger
|
||||||
|
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
|
|
||||||
class Section(configobj.Section):
|
class Section(configobj.Section):
|
||||||
def isenabled(section):
|
def isenabled(section):
|
||||||
# checks if subsection enabled, returns true/false if subsection specified otherwise returns true/false in {}
|
# checks if subsection enabled, returns true/false if subsection specified otherwise returns true/false in {}
|
||||||
if not section.sections:
|
if not section.sections:
|
||||||
try:
|
try:
|
||||||
value = list(ConfigObj.find_key(section, 'enabled'))[0]
|
value = list(ConfigObj.find_key(section, 'enabled'))[0]
|
||||||
except:value = 0
|
except:
|
||||||
|
value = 0
|
||||||
if int(value) == 1:
|
if int(value) == 1:
|
||||||
return section
|
return section
|
||||||
else:
|
else:
|
||||||
|
@ -23,7 +25,8 @@ class Section(configobj.Section):
|
||||||
for subsection in subsections:
|
for subsection in subsections:
|
||||||
try:
|
try:
|
||||||
value = list(ConfigObj.find_key(subsections, 'enabled'))[0]
|
value = list(ConfigObj.find_key(subsections, 'enabled'))[0]
|
||||||
except:value = 0
|
except:
|
||||||
|
value = 0
|
||||||
|
|
||||||
if int(value) != 1:
|
if int(value) != 1:
|
||||||
del to_return[section_name][subsection]
|
del to_return[section_name][subsection]
|
||||||
|
@ -39,7 +42,8 @@ class Section(configobj.Section):
|
||||||
for subsection in to_return:
|
for subsection in to_return:
|
||||||
try:
|
try:
|
||||||
value = list(ConfigObj.find_key(to_return[subsection], key))[0]
|
value = list(ConfigObj.find_key(to_return[subsection], key))[0]
|
||||||
except:value = None
|
except:
|
||||||
|
value = None
|
||||||
|
|
||||||
if not value:
|
if not value:
|
||||||
del to_return[subsection]
|
del to_return[subsection]
|
||||||
|
@ -80,6 +84,7 @@ class Section(configobj.Section):
|
||||||
|
|
||||||
return to_return
|
return to_return
|
||||||
|
|
||||||
|
|
||||||
class ConfigObj(configobj.ConfigObj, Section):
|
class ConfigObj(configobj.ConfigObj, Section):
|
||||||
def __init__(self, *args, **kw):
|
def __init__(self, *args, **kw):
|
||||||
if len(args) == 0:
|
if len(args) == 0:
|
||||||
|
@ -190,7 +195,8 @@ class ConfigObj(configobj.ConfigObj, Section):
|
||||||
if not list(ConfigObj.find_key(CFG_NEW, option)):
|
if not list(ConfigObj.find_key(CFG_NEW, option)):
|
||||||
try:
|
try:
|
||||||
values.pop(option)
|
values.pop(option)
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
@ -221,7 +227,7 @@ class ConfigObj(configobj.ConfigObj, Section):
|
||||||
subsection = None
|
subsection = None
|
||||||
if section in list(chain.from_iterable(subsections.values())):
|
if section in list(chain.from_iterable(subsections.values())):
|
||||||
subsection = section
|
subsection = section
|
||||||
section = ''.join([k for k,v in subsections.iteritems() if subsection in v])
|
section = ''.join([k for k, v in subsections.iteritems() if subsection in v])
|
||||||
process_section(section, subsection)
|
process_section(section, subsection)
|
||||||
elif section in subsections.keys():
|
elif section in subsections.keys():
|
||||||
subsection = subsections[section]
|
subsection = subsections[section]
|
||||||
|
@ -247,7 +253,8 @@ class ConfigObj(configobj.ConfigObj, Section):
|
||||||
try:
|
try:
|
||||||
if os.environ.has_key('NZBPO_NDCATEGORY') and os.environ.has_key('NZBPO_SBCATEGORY'):
|
if os.environ.has_key('NZBPO_NDCATEGORY') and os.environ.has_key('NZBPO_SBCATEGORY'):
|
||||||
if os.environ['NZBPO_NDCATEGORY'] == os.environ['NZBPO_SBCATEGORY']:
|
if os.environ['NZBPO_NDCATEGORY'] == os.environ['NZBPO_SBCATEGORY']:
|
||||||
logger.warning("%s category is set for SickBeard and NzbDrone. Please check your config in NZBGet" % (os.environ['NZBPO_NDCATEGORY']))
|
logger.warning("%s category is set for SickBeard and NzbDrone. "
|
||||||
|
"Please check your config in NZBGet" % (os.environ['NZBPO_NDCATEGORY']))
|
||||||
|
|
||||||
section = "Nzb"
|
section = "Nzb"
|
||||||
key = 'NZBOP_DESTDIR'
|
key = 'NZBOP_DESTDIR'
|
||||||
|
@ -274,12 +281,14 @@ class ConfigObj(configobj.ConfigObj, Section):
|
||||||
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]
|
||||||
CFG_NEW[section][option] = value
|
CFG_NEW[section][option] = value
|
||||||
|
|
||||||
section = "CouchPotato"
|
section = "CouchPotato"
|
||||||
envCatKey = 'NZBPO_CPSCATEGORY'
|
envCatKey = 'NZBPO_CPSCATEGORY'
|
||||||
envKeys = ['ENABLED', 'APIKEY', 'HOST', 'PORT', 'SSL', 'WEB_ROOT', 'METHOD', 'DELETE_FAILED', 'REMOTE_PATH', 'WAIT_FOR', 'WATCH_DIR']
|
envKeys = ['ENABLED', 'APIKEY', 'HOST', 'PORT', 'SSL', 'WEB_ROOT', 'METHOD', 'DELETE_FAILED', 'REMOTE_PATH',
|
||||||
cfgKeys = ['enabled', 'apikey', 'host', 'port', 'ssl', 'web_root', 'method', 'delete_failed', 'remote_path', 'wait_for', 'watch_dir']
|
'WAIT_FOR', 'WATCH_DIR']
|
||||||
|
cfgKeys = ['enabled', 'apikey', 'host', 'port', 'ssl', 'web_root', 'method', 'delete_failed', 'remote_path',
|
||||||
|
'wait_for', 'watch_dir']
|
||||||
if os.environ.has_key(envCatKey):
|
if os.environ.has_key(envCatKey):
|
||||||
for index in range(len(envKeys)):
|
for index in range(len(envKeys)):
|
||||||
key = 'NZBPO_CPS' + envKeys[index]
|
key = 'NZBPO_CPS' + envKeys[index]
|
||||||
|
@ -293,8 +302,10 @@ class ConfigObj(configobj.ConfigObj, Section):
|
||||||
|
|
||||||
section = "SickBeard"
|
section = "SickBeard"
|
||||||
envCatKey = 'NZBPO_SBCATEGORY'
|
envCatKey = 'NZBPO_SBCATEGORY'
|
||||||
envKeys = ['ENABLED', 'HOST', 'PORT', 'USERNAME', 'PASSWORD', 'SSL', 'WEB_ROOT', 'WATCH_DIR', 'FORK', 'DELETE_FAILED', 'TORRENT_NOLINK', 'NZBEXTRACTIONBY', 'REMOTE_PATH', 'PROCESS_METHOD']
|
envKeys = ['ENABLED', 'HOST', 'PORT', 'USERNAME', 'PASSWORD', 'SSL', 'WEB_ROOT', 'WATCH_DIR', 'FORK',
|
||||||
cfgKeys = ['enabled', 'host', 'port', 'username', 'password', 'ssl', 'web_root', 'watch_dir', 'fork', 'delete_failed', 'Torrent_NoLink', 'nzbExtractionBy', 'remote_path', 'process_method']
|
'DELETE_FAILED', 'TORRENT_NOLINK', 'NZBEXTRACTIONBY', 'REMOTE_PATH', 'PROCESS_METHOD']
|
||||||
|
cfgKeys = ['enabled', 'host', 'port', 'username', 'password', 'ssl', 'web_root', 'watch_dir', 'fork',
|
||||||
|
'delete_failed', 'Torrent_NoLink', 'nzbExtractionBy', 'remote_path', 'process_method']
|
||||||
if os.environ.has_key(envCatKey):
|
if os.environ.has_key(envCatKey):
|
||||||
for index in range(len(envKeys)):
|
for index in range(len(envKeys)):
|
||||||
key = 'NZBPO_SB' + envKeys[index]
|
key = 'NZBPO_SB' + envKeys[index]
|
||||||
|
@ -325,8 +336,10 @@ class ConfigObj(configobj.ConfigObj, Section):
|
||||||
|
|
||||||
section = "Mylar"
|
section = "Mylar"
|
||||||
envCatKey = 'NZBPO_MYCATEGORY'
|
envCatKey = 'NZBPO_MYCATEGORY'
|
||||||
envKeys = ['ENABLED', 'HOST', 'PORT', 'USERNAME', 'PASSWORD', 'APIKEY', 'SSL', 'WEB_ROOT', 'WATCH_DIR', 'REMOTE_PATH']
|
envKeys = ['ENABLED', 'HOST', 'PORT', 'USERNAME', 'PASSWORD', 'APIKEY', 'SSL', 'WEB_ROOT', 'WATCH_DIR',
|
||||||
cfgKeys = ['enabled', 'host', 'port', 'username', 'password', 'apikey', 'ssl', 'web_root', 'watch_dir', 'remote_path']
|
'REMOTE_PATH']
|
||||||
|
cfgKeys = ['enabled', 'host', 'port', 'username', 'password', 'apikey', 'ssl', 'web_root', 'watch_dir',
|
||||||
|
'remote_path']
|
||||||
if os.environ.has_key(envCatKey):
|
if os.environ.has_key(envCatKey):
|
||||||
for index in range(len(envKeys)):
|
for index in range(len(envKeys)):
|
||||||
key = 'NZBPO_MY' + envKeys[index]
|
key = 'NZBPO_MY' + envKeys[index]
|
||||||
|
@ -355,8 +368,10 @@ class ConfigObj(configobj.ConfigObj, Section):
|
||||||
|
|
||||||
section = "NzbDrone"
|
section = "NzbDrone"
|
||||||
envCatKey = 'NZBPO_NDCATEGORY'
|
envCatKey = 'NZBPO_NDCATEGORY'
|
||||||
envKeys = ['ENABLED', 'HOST', 'APIKEY', 'PORT', 'SSL', 'WEB_ROOT', 'WATCH_DIR', 'FORK', 'DELETE_FAILED', 'TORRENT_NOLINK', 'NZBEXTRACTIONBY', 'WAIT_FOR', 'DELETE_FAILED', 'REMOTE_PATH']
|
envKeys = ['ENABLED', 'HOST', 'APIKEY', 'PORT', 'SSL', 'WEB_ROOT', 'WATCH_DIR', 'FORK', 'DELETE_FAILED',
|
||||||
cfgKeys = ['enabled', 'host', 'apikey', 'port', 'ssl', 'web_root', 'watch_dir', 'fork', 'delete_failed', 'Torrent_NoLink', 'nzbExtractionBy', 'wait_for', 'delete_failed', 'remote_path']
|
'TORRENT_NOLINK', 'NZBEXTRACTIONBY', 'WAIT_FOR', 'DELETE_FAILED', 'REMOTE_PATH']
|
||||||
|
cfgKeys = ['enabled', 'host', 'apikey', 'port', 'ssl', 'web_root', 'watch_dir', 'fork', 'delete_failed',
|
||||||
|
'Torrent_NoLink', 'nzbExtractionBy', 'wait_for', 'delete_failed', 'remote_path']
|
||||||
if os.environ.has_key(envCatKey):
|
if os.environ.has_key(envCatKey):
|
||||||
for index in range(len(envKeys)):
|
for index in range(len(envKeys)):
|
||||||
key = 'NZBPO_ND' + envKeys[index]
|
key = 'NZBPO_ND' + envKeys[index]
|
||||||
|
@ -391,16 +406,26 @@ class ConfigObj(configobj.ConfigObj, Section):
|
||||||
CFG_NEW[section][option] = value
|
CFG_NEW[section][option] = value
|
||||||
|
|
||||||
section = "Transcoder"
|
section = "Transcoder"
|
||||||
envKeys = ['TRANSCODE', 'DUPLICATE', 'IGNOREEXTENSIONS', 'OUTPUTFASTSTART', 'OUTPUTVIDEOPATH', 'PROCESSOUTPUT', 'AUDIOLANGUAGE', 'ALLAUDIOLANGUAGES', 'SUBLANGUAGES',
|
envKeys = ['TRANSCODE', 'DUPLICATE', 'IGNOREEXTENSIONS', 'OUTPUTFASTSTART', 'OUTPUTVIDEOPATH',
|
||||||
'ALLSUBLANGUAGES', 'EMBEDSUBS', 'BURNINSUBTITLE', 'EXTRACTSUBS', 'EXTERNALSUBDIR', 'OUTPUTDEFAULT', 'OUTPUTVIDEOEXTENSION', 'OUTPUTVIDEOCODEC', 'VIDEOCODECALLOW',
|
'PROCESSOUTPUT', 'AUDIOLANGUAGE', 'ALLAUDIOLANGUAGES', 'SUBLANGUAGES',
|
||||||
'OUTPUTVIDEOPRESET', 'OUTPUTVIDEOFRAMERATE', 'OUTPUTVIDEOBITRATE', 'OUTPUTAUDIOCODEC', 'AUDIOCODECALLOW', 'OUTPUTAUDIOBITRATE', 'OUTPUTQUALITYPERCENT', 'GETSUBS',
|
'ALLSUBLANGUAGES', 'EMBEDSUBS', 'BURNINSUBTITLE', 'EXTRACTSUBS', 'EXTERNALSUBDIR',
|
||||||
'OUTPUTAUDIOTRACK2CODEC', 'AUDIOCODEC2ALLOW', 'OUTPUTAUDIOTRACK2BITRATE', 'OUTPUTAUDIOOTHERCODEC', 'AUDIOOTHERCODECALLOW', 'OUTPUTAUDIOOTHERBITRATE',
|
'OUTPUTDEFAULT', 'OUTPUTVIDEOEXTENSION', 'OUTPUTVIDEOCODEC', 'VIDEOCODECALLOW',
|
||||||
'OUTPUTSUBTITLECODEC', 'OUTPUTAUDIOCHANNELS', 'OUTPUTAUDIOTRACK2CHANNELS', 'OUTPUTAUDIOOTHERCHANNELS']
|
'OUTPUTVIDEOPRESET', 'OUTPUTVIDEOFRAMERATE', 'OUTPUTVIDEOBITRATE', 'OUTPUTAUDIOCODEC',
|
||||||
cfgKeys = ['transcode', 'duplicate', 'ignoreExtensions', 'outputFastStart', 'outputVideoPath', 'processOutput', 'audioLanguage', 'allAudioLanguages', 'subLanguages',
|
'AUDIOCODECALLOW', 'OUTPUTAUDIOBITRATE', 'OUTPUTQUALITYPERCENT', 'GETSUBS',
|
||||||
'allSubLanguages', 'embedSubs', 'burnInSubtitle', 'extractSubs', 'externalSubDir', 'outputDefault', 'outputVideoExtension', 'outputVideoCodec', 'VideoCodecAllow',
|
'OUTPUTAUDIOTRACK2CODEC', 'AUDIOCODEC2ALLOW', 'OUTPUTAUDIOTRACK2BITRATE',
|
||||||
'outputVideoPreset', 'outputVideoFramerate', 'outputVideoBitrate', 'outputAudioCodec', 'AudioCodecAllow', 'outputAudioBitrate', 'outputQualityPercent', 'getSubs',
|
'OUTPUTAUDIOOTHERCODEC', 'AUDIOOTHERCODECALLOW', 'OUTPUTAUDIOOTHERBITRATE',
|
||||||
'outputAudioTrack2Codec', 'AudioCodec2Allow', 'outputAudioTrack2Bitrate', 'outputAudioOtherCodec', 'AudioOtherCodecAllow', 'outputAudioOtherBitrate',
|
'OUTPUTSUBTITLECODEC', 'OUTPUTAUDIOCHANNELS', 'OUTPUTAUDIOTRACK2CHANNELS',
|
||||||
'outputSubtitleCodec', 'outputAudioChannels', 'outputAudioTrack2Channels', 'outputAudioOtherChannels']
|
'OUTPUTAUDIOOTHERCHANNELS']
|
||||||
|
cfgKeys = ['transcode', 'duplicate', 'ignoreExtensions', 'outputFastStart', 'outputVideoPath',
|
||||||
|
'processOutput', 'audioLanguage', 'allAudioLanguages', 'subLanguages',
|
||||||
|
'allSubLanguages', 'embedSubs', 'burnInSubtitle', 'extractSubs', 'externalSubDir',
|
||||||
|
'outputDefault', 'outputVideoExtension', 'outputVideoCodec', 'VideoCodecAllow',
|
||||||
|
'outputVideoPreset', 'outputVideoFramerate', 'outputVideoBitrate', 'outputAudioCodec',
|
||||||
|
'AudioCodecAllow', 'outputAudioBitrate', 'outputQualityPercent', 'getSubs',
|
||||||
|
'outputAudioTrack2Codec', 'AudioCodec2Allow', 'outputAudioTrack2Bitrate',
|
||||||
|
'outputAudioOtherCodec', 'AudioOtherCodecAllow', 'outputAudioOtherBitrate',
|
||||||
|
'outputSubtitleCodec', 'outputAudioChannels', 'outputAudioTrack2Channels',
|
||||||
|
'outputAudioOtherChannels']
|
||||||
for index in range(len(envKeys)):
|
for index in range(len(envKeys)):
|
||||||
key = 'NZBPO_' + envKeys[index]
|
key = 'NZBPO_' + envKeys[index]
|
||||||
if os.environ.has_key(key):
|
if os.environ.has_key(key):
|
||||||
|
@ -420,8 +445,10 @@ class ConfigObj(configobj.ConfigObj, Section):
|
||||||
|
|
||||||
section = "UserScript"
|
section = "UserScript"
|
||||||
envCatKey = 'NZBPO_USCATEGORY'
|
envCatKey = 'NZBPO_USCATEGORY'
|
||||||
envKeys = ['USER_SCRIPT_MEDIAEXTENSIONS', 'USER_SCRIPT_PATH', 'USER_SCRIPT_PARAM', 'USER_SCRIPT_RUNONCE', 'USER_SCRIPT_SUCCESSCODES', 'USER_SCRIPT_CLEAN', 'USDELAY', 'USREMOTE_PATH']
|
envKeys = ['USER_SCRIPT_MEDIAEXTENSIONS', 'USER_SCRIPT_PATH', 'USER_SCRIPT_PARAM', 'USER_SCRIPT_RUNONCE',
|
||||||
cfgKeys = ['user_script_mediaExtensions', 'user_script_path', 'user_script_param', 'user_script_runOnce', 'user_script_successCodes', 'user_script_clean', 'delay', 'remote_path']
|
'USER_SCRIPT_SUCCESSCODES', 'USER_SCRIPT_CLEAN', 'USDELAY', 'USREMOTE_PATH']
|
||||||
|
cfgKeys = ['user_script_mediaExtensions', 'user_script_path', 'user_script_param', 'user_script_runOnce',
|
||||||
|
'user_script_successCodes', 'user_script_clean', 'delay', 'remote_path']
|
||||||
if os.environ.has_key(envCatKey):
|
if os.environ.has_key(envCatKey):
|
||||||
for index in range(len(envKeys)):
|
for index in range(len(envKeys)):
|
||||||
key = 'NZBPO_' + envKeys[index]
|
key = 'NZBPO_' + envKeys[index]
|
||||||
|
@ -441,10 +468,11 @@ class ConfigObj(configobj.ConfigObj, Section):
|
||||||
CFG_NEW.filename = core.CONFIG_FILE
|
CFG_NEW.filename = core.CONFIG_FILE
|
||||||
CFG_NEW.write()
|
CFG_NEW.write()
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
logger.debug("Error %s when writing changes to .cfg" % (e))
|
logger.debug("Error %s when writing changes to .cfg" % (e))
|
||||||
|
|
||||||
return CFG_NEW
|
return CFG_NEW
|
||||||
|
|
||||||
|
|
||||||
configobj.Section = Section
|
configobj.Section = Section
|
||||||
configobj.ConfigObj = ConfigObj
|
configobj.ConfigObj = ConfigObj
|
||||||
config = ConfigObj
|
config = ConfigObj
|
||||||
|
|
|
@ -8,6 +8,7 @@ import time
|
||||||
import core
|
import core
|
||||||
from core import logger
|
from core import logger
|
||||||
|
|
||||||
|
|
||||||
def dbFilename(filename="nzbtomedia.db", suffix=None):
|
def dbFilename(filename="nzbtomedia.db", suffix=None):
|
||||||
"""
|
"""
|
||||||
@param filename: The sqlite database filename to use. If not specified,
|
@param filename: The sqlite database filename to use. If not specified,
|
||||||
|
@ -153,7 +154,6 @@ class DBConnection:
|
||||||
|
|
||||||
return sqlResult
|
return sqlResult
|
||||||
|
|
||||||
|
|
||||||
def select(self, query, args=None):
|
def select(self, query, args=None):
|
||||||
|
|
||||||
sqlResults = self.action(query, args).fetchall()
|
sqlResults = self.action(query, args).fetchall()
|
||||||
|
@ -244,7 +244,7 @@ class SchemaUpgrade(object):
|
||||||
self.connection = connection
|
self.connection = connection
|
||||||
|
|
||||||
def hasTable(self, tableName):
|
def hasTable(self, tableName):
|
||||||
return len(self.connection.action("SELECT 1 FROM sqlite_master WHERE name = ?;", (tableName, )).fetchall()) > 0
|
return len(self.connection.action("SELECT 1 FROM sqlite_master WHERE name = ?;", (tableName,)).fetchall()) > 0
|
||||||
|
|
||||||
def hasColumn(self, tableName, column):
|
def hasColumn(self, tableName, column):
|
||||||
return column in self.connection.tableInfo(tableName)
|
return column in self.connection.tableInfo(tableName)
|
||||||
|
@ -264,4 +264,3 @@ class SchemaUpgrade(object):
|
||||||
new_version = self.checkDBVersion() + 1
|
new_version = self.checkDBVersion() + 1
|
||||||
self.connection.action("UPDATE db_version SET db_version = ?", [new_version])
|
self.connection.action("UPDATE db_version SET db_version = ?", [new_version])
|
||||||
return new_version
|
return new_version
|
||||||
|
|
||||||
|
|
|
@ -2,23 +2,28 @@
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import core
|
import core
|
||||||
import shlex
|
import shlex
|
||||||
from core import logger
|
from core import logger
|
||||||
from core.nzbToMediaUtil import listMediaFiles
|
from core.nzbToMediaUtil import listMediaFiles
|
||||||
|
|
||||||
reverse_list = [r"\.\d{2}e\d{2}s\.", r"\.[pi]0801\.", r"\.p027\.", r"\.[pi]675\.", r"\.[pi]084\.", r"\.p063\.", r"\b[45]62[xh]\.", r"\.yarulb\.", r"\.vtd[hp]\.",
|
reverse_list = [r"\.\d{2}e\d{2}s\.", r"\.[pi]0801\.", r"\.p027\.", r"\.[pi]675\.", r"\.[pi]084\.", r"\.p063\.",
|
||||||
r"\.ld[.-]?bew\.", r"\.pir.?(dov|dvd|bew|db|rb)\.", r"\brdvd\.", r"\.vts\.", r"\.reneercs\.", r"\.dcv\.", r"\b(pir|mac)dh\b", r"\.reporp\.", r"\.kcaper\.",
|
r"\b[45]62[xh]\.", r"\.yarulb\.", r"\.vtd[hp]\.",
|
||||||
|
r"\.ld[.-]?bew\.", r"\.pir.?(dov|dvd|bew|db|rb)\.", r"\brdvd\.", r"\.vts\.", r"\.reneercs\.",
|
||||||
|
r"\.dcv\.", r"\b(pir|mac)dh\b", r"\.reporp\.", r"\.kcaper\.",
|
||||||
r"\.lanretni\.", r"\b3ca\b", r"\.cstn\."]
|
r"\.lanretni\.", r"\b3ca\b", r"\.cstn\."]
|
||||||
reverse_pattern = re.compile('|'.join(reverse_list), flags=re.IGNORECASE)
|
reverse_pattern = re.compile('|'.join(reverse_list), flags=re.IGNORECASE)
|
||||||
season_pattern = re.compile(r"(.*\.\d{2}e\d{2}s\.)(.*)", flags=re.IGNORECASE)
|
season_pattern = re.compile(r"(.*\.\d{2}e\d{2}s\.)(.*)", flags=re.IGNORECASE)
|
||||||
word_pattern = re.compile(r"([^A-Z0-9]*[A-Z0-9]+)")
|
word_pattern = re.compile(r"([^A-Z0-9]*[A-Z0-9]+)")
|
||||||
media_list = [r"\.s\d{2}e\d{2}\.", r"\.1080[pi]\.", r"\.720p\.", r"\.576[pi]", r"\.480[pi]\.", r"\.360p\.", r"\.[xh]26[45]\b", r"\.bluray\.", r"\.[hp]dtv\.",
|
media_list = [r"\.s\d{2}e\d{2}\.", r"\.1080[pi]\.", r"\.720p\.", r"\.576[pi]", r"\.480[pi]\.", r"\.360p\.",
|
||||||
r"\.web[.-]?dl\.", r"\.(vod|dvd|web|bd|br).?rip\.", r"\.dvdr\b", r"\.stv\.", r"\.screener\.", r"\.vcd\.", r"\bhd(cam|rip)\b", r"\.proper\.", r"\.repack\.",
|
r"\.[xh]26[45]\b", r"\.bluray\.", r"\.[hp]dtv\.",
|
||||||
|
r"\.web[.-]?dl\.", r"\.(vod|dvd|web|bd|br).?rip\.", r"\.dvdr\b", r"\.stv\.", r"\.screener\.", r"\.vcd\.",
|
||||||
|
r"\bhd(cam|rip)\b", r"\.proper\.", r"\.repack\.",
|
||||||
r"\.internal\.", r"\bac3\b", r"\.ntsc\.", r"\.pal\.", r"\.secam\.", r"\bdivx\b", r"\bxvid\b"]
|
r"\.internal\.", r"\bac3\b", r"\.ntsc\.", r"\.pal\.", r"\.secam\.", r"\bdivx\b", r"\bxvid\b"]
|
||||||
media_pattern = re.compile('|'.join(media_list), flags=re.IGNORECASE)
|
media_pattern = re.compile('|'.join(media_list), flags=re.IGNORECASE)
|
||||||
garbage_name = re.compile(r"^[a-zA-Z0-9]*$")
|
garbage_name = re.compile(r"^[a-zA-Z0-9]*$")
|
||||||
char_replace = [[r"(\w)1\.(\w)",r"\1i\2"]
|
char_replace = [[r"(\w)1\.(\w)", r"\1i\2"]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def process_all_exceptions(name, dirname):
|
def process_all_exceptions(name, dirname):
|
||||||
rename_script(dirname)
|
rename_script(dirname)
|
||||||
|
@ -27,7 +32,7 @@ def process_all_exceptions(name, dirname):
|
||||||
parentDir = os.path.dirname(filename)
|
parentDir = os.path.dirname(filename)
|
||||||
head, fileExtension = os.path.splitext(os.path.basename(filename))
|
head, fileExtension = os.path.splitext(os.path.basename(filename))
|
||||||
if reverse_pattern.search(head) is not None:
|
if reverse_pattern.search(head) is not None:
|
||||||
exception = reverse_filename
|
exception = reverse_filename
|
||||||
elif garbage_name.search(head) is not None:
|
elif garbage_name.search(head) is not None:
|
||||||
exception = replace_filename
|
exception = replace_filename
|
||||||
else:
|
else:
|
||||||
|
@ -38,7 +43,8 @@ def process_all_exceptions(name, dirname):
|
||||||
if core.GROUPS:
|
if core.GROUPS:
|
||||||
newfilename = strip_groups(newfilename)
|
newfilename = strip_groups(newfilename)
|
||||||
if newfilename != filename:
|
if newfilename != filename:
|
||||||
rename_file(filename, newfilename)
|
rename_file(filename, newfilename)
|
||||||
|
|
||||||
|
|
||||||
def strip_groups(filename):
|
def strip_groups(filename):
|
||||||
if not core.GROUPS:
|
if not core.GROUPS:
|
||||||
|
@ -48,33 +54,36 @@ def strip_groups(filename):
|
||||||
newname = head.replace(' ', '.')
|
newname = head.replace(' ', '.')
|
||||||
for group in core.GROUPS:
|
for group in core.GROUPS:
|
||||||
newname = newname.replace(group, '')
|
newname = newname.replace(group, '')
|
||||||
newname = newname.replace('[]', '')
|
newname = newname.replace('[]', '')
|
||||||
newfile = newname + fileExtension
|
newfile = newname + fileExtension
|
||||||
newfilePath = os.path.join(dirname, newfile)
|
newfilePath = os.path.join(dirname, newfile)
|
||||||
return newfilePath
|
return newfilePath
|
||||||
|
|
||||||
|
|
||||||
def rename_file(filename, newfilePath):
|
def rename_file(filename, newfilePath):
|
||||||
logger.debug("Replacing file name %s with download name %s" % (filename, newfilePath), "EXCEPTION")
|
logger.debug("Replacing file name %s with download name %s" % (filename, newfilePath), "EXCEPTION")
|
||||||
try:
|
try:
|
||||||
os.rename(filename, newfilePath)
|
os.rename(filename, newfilePath)
|
||||||
except Exception,e:
|
except Exception, e:
|
||||||
logger.error("Unable to rename file due to: %s" % (str(e)), "EXCEPTION")
|
logger.error("Unable to rename file due to: %s" % (str(e)), "EXCEPTION")
|
||||||
|
|
||||||
|
|
||||||
def replace_filename(filename, dirname, name):
|
def replace_filename(filename, dirname, name):
|
||||||
head, fileExtension = os.path.splitext(os.path.basename(filename))
|
head, fileExtension = os.path.splitext(os.path.basename(filename))
|
||||||
if media_pattern.search(os.path.basename(dirname).replace(' ','.')) is not None:
|
if media_pattern.search(os.path.basename(dirname).replace(' ', '.')) is not None:
|
||||||
newname = os.path.basename(dirname).replace(' ', '.')
|
newname = os.path.basename(dirname).replace(' ', '.')
|
||||||
logger.debug("Replacing file name %s with directory name %s" % (head, newname), "EXCEPTION")
|
logger.debug("Replacing file name %s with directory name %s" % (head, newname), "EXCEPTION")
|
||||||
elif media_pattern.search(name.replace(' ','.').lower()) is not None:
|
elif media_pattern.search(name.replace(' ', '.').lower()) is not None:
|
||||||
newname = name.replace(' ', '.')
|
newname = name.replace(' ', '.')
|
||||||
logger.debug("Replacing file name %s with download name %s" % (head, newname), "EXCEPTION")
|
logger.debug("Replacing file name %s with download name %s" % (head, newname), "EXCEPTION")
|
||||||
else:
|
else:
|
||||||
logger.warning("No name replacement determined for %s" % (head), "EXCEPTION")
|
logger.warning("No name replacement determined for %s" % (head), "EXCEPTION")
|
||||||
newname = name
|
newname = name
|
||||||
newfile = newname + fileExtension
|
newfile = newname + fileExtension
|
||||||
newfilePath = os.path.join(dirname, newfile)
|
newfilePath = os.path.join(dirname, newfile)
|
||||||
return newfilePath
|
return newfilePath
|
||||||
|
|
||||||
|
|
||||||
def reverse_filename(filename, dirname, name):
|
def reverse_filename(filename, dirname, name):
|
||||||
head, fileExtension = os.path.splitext(os.path.basename(filename))
|
head, fileExtension = os.path.splitext(os.path.basename(filename))
|
||||||
na_parts = season_pattern.search(head)
|
na_parts = season_pattern.search(head)
|
||||||
|
@ -85,11 +94,11 @@ def reverse_filename(filename, dirname, name):
|
||||||
for wp in word_p:
|
for wp in word_p:
|
||||||
if wp[0] == ".":
|
if wp[0] == ".":
|
||||||
new_words += "."
|
new_words += "."
|
||||||
new_words += re.sub(r"\W","",wp)
|
new_words += re.sub(r"\W", "", wp)
|
||||||
else:
|
else:
|
||||||
new_words = na_parts.group(2)
|
new_words = na_parts.group(2)
|
||||||
for cr in char_replace:
|
for cr in char_replace:
|
||||||
new_words = re.sub(cr[0],cr[1],new_words)
|
new_words = re.sub(cr[0], cr[1], new_words)
|
||||||
newname = new_words[::-1] + na_parts.group(1)[::-1]
|
newname = new_words[::-1] + na_parts.group(1)[::-1]
|
||||||
else:
|
else:
|
||||||
newname = head[::-1].title()
|
newname = head[::-1].title()
|
||||||
|
@ -99,15 +108,16 @@ def reverse_filename(filename, dirname, name):
|
||||||
newfilePath = os.path.join(dirname, newfile)
|
newfilePath = os.path.join(dirname, newfile)
|
||||||
return newfilePath
|
return newfilePath
|
||||||
|
|
||||||
|
|
||||||
def rename_script(dirname):
|
def rename_script(dirname):
|
||||||
rename_file = ""
|
rename_file = ""
|
||||||
for dir, dirs, files in os.walk(dirname):
|
for dir, dirs, files in os.walk(dirname):
|
||||||
for file in files:
|
for file in files:
|
||||||
if re.search('(rename\S*\.(sh|bat)$)',file,re.IGNORECASE):
|
if re.search('(rename\S*\.(sh|bat)$)', file, re.IGNORECASE):
|
||||||
rename_file = os.path.join(dir, file)
|
rename_file = os.path.join(dir, file)
|
||||||
dirname = dir
|
dirname = dir
|
||||||
break
|
break
|
||||||
if rename_file:
|
if rename_file:
|
||||||
rename_lines = [line.strip() for line in open(rename_file)]
|
rename_lines = [line.strip() for line in open(rename_file)]
|
||||||
for line in rename_lines:
|
for line in rename_lines:
|
||||||
if re.search('^(mv|Move)', line, re.IGNORECASE):
|
if re.search('^(mv|Move)', line, re.IGNORECASE):
|
||||||
|
@ -122,10 +132,9 @@ def rename_script(dirname):
|
||||||
logger.debug("Renaming file %s to %s" % (orig, dest), "EXCEPTION")
|
logger.debug("Renaming file %s to %s" % (orig, dest), "EXCEPTION")
|
||||||
try:
|
try:
|
||||||
os.rename(orig, dest)
|
os.rename(orig, dest)
|
||||||
except Exception,e:
|
except Exception, e:
|
||||||
logger.error("Unable to rename file due to: %s" % (str(e)), "EXCEPTION")
|
logger.error("Unable to rename file due to: %s" % (str(e)), "EXCEPTION")
|
||||||
|
|
||||||
# dict for custom groups
|
# dict for custom groups
|
||||||
# we can add more to this list
|
# we can add more to this list
|
||||||
#__customgroups__ = {'Q o Q': process_qoq, '-ECI': process_eci}
|
# _customgroups = {'Q o Q': process_qoq, '-ECI': process_eci}
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,14 @@ from core.transcoder import transcoder
|
||||||
from core.nzbToMediaUtil import import_subs, listMediaFiles, rmDir
|
from core.nzbToMediaUtil import import_subs, listMediaFiles, rmDir
|
||||||
from core import logger
|
from core import logger
|
||||||
|
|
||||||
|
|
||||||
def external_script(outputDestination, torrentName, torrentLabel, settings):
|
def external_script(outputDestination, torrentName, torrentLabel, settings):
|
||||||
final_result = 0 # start at 0.
|
final_result = 0 # start at 0.
|
||||||
num_files = 0
|
num_files = 0
|
||||||
try:
|
try:
|
||||||
core.USER_SCRIPT_MEDIAEXTENSIONS = settings["user_script_mediaExtensions"]
|
core.USER_SCRIPT_MEDIAEXTENSIONS = settings["user_script_mediaExtensions"]
|
||||||
if isinstance(core.USER_SCRIPT_MEDIAEXTENSIONS, str): core.USER_SCRIPT_MEDIAEXTENSIONS = core.USER_SCRIPT_MEDIAEXTENSIONS.split(',')
|
if isinstance(core.USER_SCRIPT_MEDIAEXTENSIONS, str):
|
||||||
|
core.USER_SCRIPT_MEDIAEXTENSIONS = core.USER_SCRIPT_MEDIAEXTENSIONS.split(',')
|
||||||
except:
|
except:
|
||||||
core.USER_SCRIPT_MEDIAEXTENSIONS = []
|
core.USER_SCRIPT_MEDIAEXTENSIONS = []
|
||||||
try:
|
try:
|
||||||
|
@ -22,12 +24,14 @@ def external_script(outputDestination, torrentName, torrentLabel, settings):
|
||||||
return [0, ""]
|
return [0, ""]
|
||||||
try:
|
try:
|
||||||
core.USER_SCRIPT_PARAM = settings["user_script_param"]
|
core.USER_SCRIPT_PARAM = settings["user_script_param"]
|
||||||
if isinstance(core.USER_SCRIPT_PARAM, str): core.USER_SCRIPT_PARAM = core.USER_SCRIPT_PARAM.split(',')
|
if isinstance(core.USER_SCRIPT_PARAM, str):
|
||||||
|
core.USER_SCRIPT_PARAM = core.USER_SCRIPT_PARAM.split(',')
|
||||||
except:
|
except:
|
||||||
core.USER_SCRIPT_PARAM = []
|
core.USER_SCRIPT_PARAM = []
|
||||||
try:
|
try:
|
||||||
core.USER_SCRIPT_SUCCESSCODES = settings["user_script_successCodes"]
|
core.USER_SCRIPT_SUCCESSCODES = settings["user_script_successCodes"]
|
||||||
if isinstance(core.USER_SCRIPT_SUCCESSCODES, str): core.USER_SCRIPT_SUCCESSCODES = core.USER_SCRIPT_SUCCESSCODES.split(',')
|
if isinstance(core.USER_SCRIPT_SUCCESSCODES, str):
|
||||||
|
core.USER_SCRIPT_SUCCESSCODES = core.USER_SCRIPT_SUCCESSCODES.split(',')
|
||||||
except:
|
except:
|
||||||
core.USER_SCRIPT_SUCCESSCODES = 0
|
core.USER_SCRIPT_SUCCESSCODES = 0
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -14,7 +14,7 @@ import beets
|
||||||
import requests
|
import requests
|
||||||
import core
|
import core
|
||||||
from babelfish import Language
|
from babelfish import Language
|
||||||
import subliminal
|
import subliminal
|
||||||
|
|
||||||
from core.extractor import extractor
|
from core.extractor import extractor
|
||||||
from core.linktastic import linktastic
|
from core.linktastic import linktastic
|
||||||
|
@ -25,13 +25,14 @@ from core import logger, nzbToMediaDB
|
||||||
|
|
||||||
requests.packages.urllib3.disable_warnings()
|
requests.packages.urllib3.disable_warnings()
|
||||||
|
|
||||||
|
|
||||||
def reportNzb(failure_link, clientAgent):
|
def reportNzb(failure_link, clientAgent):
|
||||||
# Contact indexer site
|
# Contact indexer site
|
||||||
logger.info("Sending failure notification to indexer site")
|
logger.info("Sending failure notification to indexer site")
|
||||||
if clientAgent == 'nzbget':
|
if clientAgent == 'nzbget':
|
||||||
headers = {'User-Agent' : 'NZBGet / nzbToMedia.py'}
|
headers = {'User-Agent': 'NZBGet / nzbToMedia.py'}
|
||||||
elif clientAgent == 'sabnzbd':
|
elif clientAgent == 'sabnzbd':
|
||||||
headers = {'User-Agent' : 'SABnzbd / nzbToMedia.py'}
|
headers = {'User-Agent': 'SABnzbd / nzbToMedia.py'}
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
|
@ -40,8 +41,9 @@ def reportNzb(failure_link, clientAgent):
|
||||||
logger.error("Unable to open URL %s due to %s" % (failure_link, e))
|
logger.error("Unable to open URL %s due to %s" % (failure_link, e))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
def sanitizeName(name):
|
def sanitizeName(name):
|
||||||
'''
|
"""
|
||||||
>>> sanitizeName('a/b/c')
|
>>> sanitizeName('a/b/c')
|
||||||
'a-b-c'
|
'a-b-c'
|
||||||
>>> sanitizeName('abc')
|
>>> sanitizeName('abc')
|
||||||
|
@ -50,7 +52,7 @@ def sanitizeName(name):
|
||||||
'ab'
|
'ab'
|
||||||
>>> sanitizeName('.a.b..')
|
>>> sanitizeName('.a.b..')
|
||||||
'a.b'
|
'a.b'
|
||||||
'''
|
"""
|
||||||
|
|
||||||
# remove bad chars from the filename
|
# remove bad chars from the filename
|
||||||
name = re.sub(r'[\\\/*]', '-', name)
|
name = re.sub(r'[\\\/*]', '-', name)
|
||||||
|
@ -60,10 +62,12 @@ def sanitizeName(name):
|
||||||
name = name.strip(' .')
|
name = name.strip(' .')
|
||||||
try:
|
try:
|
||||||
name = name.encode(core.SYS_ENCODING)
|
name = name.encode(core.SYS_ENCODING)
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
def makeDir(path):
|
def makeDir(path):
|
||||||
if not os.path.isdir(path):
|
if not os.path.isdir(path):
|
||||||
try:
|
try:
|
||||||
|
@ -72,12 +76,13 @@ def makeDir(path):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def remoteDir(path):
|
def remoteDir(path):
|
||||||
if not core.REMOTEPATHS:
|
if not core.REMOTEPATHS:
|
||||||
return path
|
return path
|
||||||
for local,remote in core.REMOTEPATHS:
|
for local, remote in core.REMOTEPATHS:
|
||||||
if local in path:
|
if local in path:
|
||||||
base_dirs = path.replace(local,"").split(os.sep)
|
base_dirs = path.replace(local, "").split(os.sep)
|
||||||
if '/' in remote:
|
if '/' in remote:
|
||||||
remote_sep = '/'
|
remote_sep = '/'
|
||||||
else:
|
else:
|
||||||
|
@ -89,22 +94,25 @@ def remoteDir(path):
|
||||||
return new_path
|
return new_path
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
def category_search(inputDirectory, inputName, inputCategory, root, categories):
|
def category_search(inputDirectory, inputName, inputCategory, root, categories):
|
||||||
tordir = False
|
tordir = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
inputName = inputName.encode(core.SYS_ENCODING)
|
inputName = inputName.encode(core.SYS_ENCODING)
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
try:
|
try:
|
||||||
inputDirectory = inputDirectory.encode(core.SYS_ENCODING)
|
inputDirectory = inputDirectory.encode(core.SYS_ENCODING)
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
if inputDirectory is None: # =Nothing to process here.
|
if inputDirectory is None: # =Nothing to process here.
|
||||||
return inputDirectory, inputName, inputCategory, root
|
return inputDirectory, inputName, inputCategory, root
|
||||||
|
|
||||||
pathlist = os.path.normpath(inputDirectory).split(os.sep)
|
pathlist = os.path.normpath(inputDirectory).split(os.sep)
|
||||||
|
|
||||||
if inputCategory and inputCategory in pathlist:
|
if inputCategory and inputCategory in pathlist:
|
||||||
logger.debug("SEARCH: Found the Category: %s in directory structure" % (inputCategory))
|
logger.debug("SEARCH: Found the Category: %s in directory structure" % (inputCategory))
|
||||||
elif inputCategory:
|
elif inputCategory:
|
||||||
logger.debug("SEARCH: Could not find the category: %s in the directory structure" % (inputCategory))
|
logger.debug("SEARCH: Could not find the category: %s in the directory structure" % (inputCategory))
|
||||||
|
@ -116,7 +124,8 @@ def category_search(inputDirectory, inputName, inputCategory, root, categories):
|
||||||
inputCategory = ""
|
inputCategory = ""
|
||||||
logger.debug("SEARCH: Could not find a category in the directory structure")
|
logger.debug("SEARCH: Could not find a category in the directory structure")
|
||||||
if not os.path.isdir(inputDirectory) and os.path.isfile(inputDirectory): # If the input directory is a file
|
if not os.path.isdir(inputDirectory) and os.path.isfile(inputDirectory): # If the input directory is a file
|
||||||
if not inputName: inputName = os.path.split(os.path.normpath(inputDirectory))[1]
|
if not inputName:
|
||||||
|
inputName = os.path.split(os.path.normpath(inputDirectory))[1]
|
||||||
return inputDirectory, inputName, inputCategory, root
|
return inputDirectory, inputName, inputCategory, root
|
||||||
|
|
||||||
if inputCategory and os.path.isdir(os.path.join(inputDirectory, inputCategory)):
|
if inputCategory and os.path.isdir(os.path.join(inputDirectory, inputCategory)):
|
||||||
|
@ -158,7 +167,8 @@ def category_search(inputDirectory, inputName, inputCategory, root, categories):
|
||||||
if index + 1 < len(pathlist):
|
if index + 1 < len(pathlist):
|
||||||
tordir = True
|
tordir = True
|
||||||
logger.info("SEARCH: Found a unique directory %s in the category directory" % (pathlist[index + 1]))
|
logger.info("SEARCH: Found a unique directory %s in the category directory" % (pathlist[index + 1]))
|
||||||
if not inputName: inputName = pathlist[index + 1]
|
if not inputName:
|
||||||
|
inputName = pathlist[index + 1]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -177,15 +187,17 @@ def category_search(inputDirectory, inputName, inputCategory, root, categories):
|
||||||
|
|
||||||
return inputDirectory, inputName, inputCategory, root
|
return inputDirectory, inputName, inputCategory, root
|
||||||
|
|
||||||
|
|
||||||
def getDirSize(inputPath):
|
def getDirSize(inputPath):
|
||||||
from functools import partial
|
from functools import partial
|
||||||
prepend = partial(os.path.join, inputPath)
|
prepend = partial(os.path.join, inputPath)
|
||||||
return sum([(os.path.getsize(f) if os.path.isfile(f) else getDirSize(f)) for f in map(prepend, os.listdir(inputPath))])
|
return sum(
|
||||||
|
[(os.path.getsize(f) if os.path.isfile(f) else getDirSize(f)) for f in map(prepend, os.listdir(inputPath))])
|
||||||
|
|
||||||
|
|
||||||
def is_minSize(inputName, minSize):
|
def is_minSize(inputName, minSize):
|
||||||
fileName, fileExt = os.path.splitext(os.path.basename(inputName))
|
fileName, fileExt = os.path.splitext(os.path.basename(inputName))
|
||||||
|
|
||||||
|
|
||||||
# audio files we need to check directory size not file size
|
# audio files we need to check directory size not file size
|
||||||
inputSize = os.path.getsize(inputName)
|
inputSize = os.path.getsize(inputName)
|
||||||
if fileExt in (core.AUDIOCONTAINER):
|
if fileExt in (core.AUDIOCONTAINER):
|
||||||
|
@ -199,11 +211,13 @@ def is_minSize(inputName, minSize):
|
||||||
if inputSize > minSize * 1048576:
|
if inputSize > minSize * 1048576:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def is_sample(inputName):
|
def is_sample(inputName):
|
||||||
# Ignore 'sample' in files
|
# Ignore 'sample' in files
|
||||||
if re.search('(^|[\W_])sample\d*[\W_]', inputName.lower()):
|
if re.search('(^|[\W_])sample\d*[\W_]', inputName.lower()):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def copy_link(src, targetLink, useLink):
|
def copy_link(src, targetLink, useLink):
|
||||||
logger.info("MEDIAFILE: [%s]" % (os.path.basename(targetLink)), 'COPYLINK')
|
logger.info("MEDIAFILE: [%s]" % (os.path.basename(targetLink)), 'COPYLINK')
|
||||||
logger.info("SOURCE FOLDER: [%s]" % (os.path.dirname(src)), 'COPYLINK')
|
logger.info("SOURCE FOLDER: [%s]" % (os.path.dirname(src)), 'COPYLINK')
|
||||||
|
@ -254,6 +268,7 @@ def copy_link(src, targetLink, useLink):
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def replace_links(link):
|
def replace_links(link):
|
||||||
n = 0
|
n = 0
|
||||||
target = link
|
target = link
|
||||||
|
@ -277,6 +292,7 @@ def replace_links(link):
|
||||||
os.unlink(link)
|
os.unlink(link)
|
||||||
linktastic.symlink(target, link)
|
linktastic.symlink(target, link)
|
||||||
|
|
||||||
|
|
||||||
def flatten(outputDestination):
|
def flatten(outputDestination):
|
||||||
logger.info("FLATTEN: Flattening directory: %s" % (outputDestination))
|
logger.info("FLATTEN: Flattening directory: %s" % (outputDestination))
|
||||||
for outputFile in listMediaFiles(outputDestination):
|
for outputFile in listMediaFiles(outputDestination):
|
||||||
|
@ -295,29 +311,31 @@ def flatten(outputDestination):
|
||||||
|
|
||||||
removeEmptyFolders(outputDestination) # Cleanup empty directories
|
removeEmptyFolders(outputDestination) # Cleanup empty directories
|
||||||
|
|
||||||
|
|
||||||
def removeEmptyFolders(path, removeRoot=True):
|
def removeEmptyFolders(path, removeRoot=True):
|
||||||
'Function to remove empty folders'
|
"""Function to remove empty folders"""
|
||||||
if not os.path.isdir(path):
|
if not os.path.isdir(path):
|
||||||
return
|
return
|
||||||
|
|
||||||
# remove empty subfolders
|
# remove empty subfolders
|
||||||
logger.debug("Checking for empty folders in:%s" % (path))
|
logger.debug("Checking for empty folders in:%s" % (path))
|
||||||
files = os.listdir(path)
|
files = os.listdir(path)
|
||||||
if len(files):
|
if len(files):
|
||||||
for f in files:
|
for f in files:
|
||||||
fullpath = os.path.join(path, f)
|
fullpath = os.path.join(path, f)
|
||||||
if os.path.isdir(fullpath):
|
if os.path.isdir(fullpath):
|
||||||
removeEmptyFolders(fullpath)
|
removeEmptyFolders(fullpath)
|
||||||
|
|
||||||
|
# if folder empty, delete it
|
||||||
|
files = os.listdir(path)
|
||||||
|
if len(files) == 0 and removeRoot:
|
||||||
|
logger.debug("Removing empty folder:%s" % (path))
|
||||||
|
os.rmdir(path)
|
||||||
|
|
||||||
# if folder empty, delete it
|
|
||||||
files = os.listdir(path)
|
|
||||||
if len(files) == 0 and removeRoot:
|
|
||||||
logger.debug("Removing empty folder:%s" % (path))
|
|
||||||
os.rmdir(path)
|
|
||||||
|
|
||||||
def rmReadOnly(filename):
|
def rmReadOnly(filename):
|
||||||
if os.path.isfile(filename):
|
if os.path.isfile(filename):
|
||||||
#check first the read-only attribute
|
# check first the read-only attribute
|
||||||
file_attribute = os.stat(filename)[0]
|
file_attribute = os.stat(filename)[0]
|
||||||
if (not file_attribute & stat.S_IWRITE):
|
if (not file_attribute & stat.S_IWRITE):
|
||||||
# File is read-only, so make it writeable
|
# File is read-only, so make it writeable
|
||||||
|
@ -327,7 +345,8 @@ def rmReadOnly(filename):
|
||||||
except:
|
except:
|
||||||
logger.warning('Cannot change permissions of ' + filename, logger.WARNING)
|
logger.warning('Cannot change permissions of ' + filename, logger.WARNING)
|
||||||
|
|
||||||
#Wake function
|
|
||||||
|
# Wake function
|
||||||
def WakeOnLan(ethernet_address):
|
def WakeOnLan(ethernet_address):
|
||||||
addr_byte = ethernet_address.split(':')
|
addr_byte = ethernet_address.split(':')
|
||||||
hw_addr = struct.pack(b'BBBBBB', int(addr_byte[0], 16),
|
hw_addr = struct.pack(b'BBBBBB', int(addr_byte[0], 16),
|
||||||
|
@ -349,7 +368,7 @@ def WakeOnLan(ethernet_address):
|
||||||
ss.close()
|
ss.close()
|
||||||
|
|
||||||
|
|
||||||
#Test Connection function
|
# Test Connection function
|
||||||
def TestCon(host, port):
|
def TestCon(host, port):
|
||||||
try:
|
try:
|
||||||
socket.create_connection((host, port))
|
socket.create_connection((host, port))
|
||||||
|
@ -372,7 +391,7 @@ def WakeUp():
|
||||||
|
|
||||||
if TestCon(host, port) == "Down": # final check.
|
if TestCon(host, port) == "Down": # final check.
|
||||||
logger.warning("System with mac: %s has not woken after 3 attempts. Continuing with the rest of the script." % (
|
logger.warning("System with mac: %s has not woken after 3 attempts. Continuing with the rest of the script." % (
|
||||||
mac))
|
mac))
|
||||||
else:
|
else:
|
||||||
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))
|
||||||
|
|
||||||
|
@ -392,7 +411,8 @@ def CharReplace(Name):
|
||||||
# /!\ detection is done 2char by 2char for UTF-8 special character
|
# /!\ detection is done 2char by 2char for UTF-8 special character
|
||||||
if (len(Name) != 1) & (Idx < (len(Name) - 1)):
|
if (len(Name) != 1) & (Idx < (len(Name) - 1)):
|
||||||
# Detect UTF-8
|
# Detect UTF-8
|
||||||
if ((Name[Idx] == '\xC2') | (Name[Idx] == '\xC3')) & ((Name[Idx+1] >= '\xA0') & (Name[Idx+1] <= '\xFF')):
|
if ((Name[Idx] == '\xC2') | (Name[Idx] == '\xC3')) & (
|
||||||
|
(Name[Idx + 1] >= '\xA0') & (Name[Idx + 1] <= '\xFF')):
|
||||||
encoding = 'utf-8'
|
encoding = 'utf-8'
|
||||||
break
|
break
|
||||||
# Detect CP850
|
# Detect CP850
|
||||||
|
@ -433,7 +453,7 @@ def convert_to_ascii(inputName, dirName):
|
||||||
if encoded:
|
if encoded:
|
||||||
dirName = os.path.join(dir, base2)
|
dirName = os.path.join(dir, base2)
|
||||||
logger.info("Renaming directory to: %s." % (base2), 'ENCODER')
|
logger.info("Renaming directory to: %s." % (base2), 'ENCODER')
|
||||||
os.rename(os.path.join(dir,base), dirName)
|
os.rename(os.path.join(dir, base), dirName)
|
||||||
if os.environ.has_key('NZBOP_SCRIPTDIR'):
|
if os.environ.has_key('NZBOP_SCRIPTDIR'):
|
||||||
print "[NZB] DIRECTORY=%s" % (dirName) # Return the new directory to NZBGet.
|
print "[NZB] DIRECTORY=%s" % (dirName) # Return the new directory to NZBGet.
|
||||||
|
|
||||||
|
@ -576,23 +596,23 @@ def parse_args(clientAgent, args):
|
||||||
return None, None, None, None, None
|
return None, None, None, None, None
|
||||||
|
|
||||||
|
|
||||||
def getDirs(section, subsection, link = 'hard'):
|
def getDirs(section, subsection, link='hard'):
|
||||||
to_return = []
|
to_return = []
|
||||||
|
|
||||||
def processDir(path):
|
def processDir(path):
|
||||||
folders = []
|
folders = []
|
||||||
|
|
||||||
logger.info("Searching %s for mediafiles to post-process ..." % (path))
|
logger.info("Searching %s for mediafiles to post-process ..." % (path))
|
||||||
sync = [ o for o in os.listdir(path) if os.path.splitext(o)[1] in ['.!sync','.bts'] ]
|
sync = [o for o in os.listdir(path) if os.path.splitext(o)[1] in ['.!sync', '.bts']]
|
||||||
# search for single files and move them into their own folder for post-processing
|
# search for single files and move them into their own folder for post-processing
|
||||||
for mediafile in [ os.path.join(path, o) for o in os.listdir(path) if
|
for mediafile in [os.path.join(path, o) for o in os.listdir(path) if
|
||||||
os.path.isfile(os.path.join(path, o)) ]:
|
os.path.isfile(os.path.join(path, o))]:
|
||||||
if len(sync) > 0:
|
if len(sync) > 0:
|
||||||
break
|
break
|
||||||
if os.path.split(mediafile)[1] in ['Thumbs.db', 'thumbs.db']:
|
if os.path.split(mediafile)[1] in ['Thumbs.db', 'thumbs.db']:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
logger.debug("Found file %s in root directory %s." % (os.path.split(mediafile)[1], path))
|
logger.debug("Found file %s in root directory %s." % (os.path.split(mediafile)[1], path))
|
||||||
newPath = None
|
newPath = None
|
||||||
fileExt = os.path.splitext(mediafile)[1]
|
fileExt = os.path.splitext(mediafile)[1]
|
||||||
try:
|
try:
|
||||||
|
@ -627,8 +647,9 @@ def getDirs(section, subsection, link = 'hard'):
|
||||||
newPath = os.path.join(path, sanitizeName(title))
|
newPath = os.path.join(path, sanitizeName(title))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
newPath = newPath.encode(core.SYS_ENCODING)
|
newPath = newPath.encode(core.SYS_ENCODING)
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
# Just fail-safe incase we already have afile with this clean-name (was actually a bug from earlier code, but let's be safe).
|
# Just fail-safe incase we already have afile with this clean-name (was actually a bug from earlier code, but let's be safe).
|
||||||
if os.path.isfile(newPath):
|
if os.path.isfile(newPath):
|
||||||
|
@ -642,19 +663,20 @@ def getDirs(section, subsection, link = 'hard'):
|
||||||
newfile = os.path.join(newPath, sanitizeName(os.path.split(mediafile)[1]))
|
newfile = os.path.join(newPath, sanitizeName(os.path.split(mediafile)[1]))
|
||||||
try:
|
try:
|
||||||
newfile = newfile.encode(core.SYS_ENCODING)
|
newfile = newfile.encode(core.SYS_ENCODING)
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
# link file to its new path
|
# link file to its new path
|
||||||
copy_link(mediafile, newfile, link)
|
copy_link(mediafile, newfile, link)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Failed to move %s to its own directory: %s" % (os.path.split(mediafile)[1], e))
|
logger.error("Failed to move %s to its own directory: %s" % (os.path.split(mediafile)[1], e))
|
||||||
|
|
||||||
#removeEmptyFolders(path, removeRoot=False)
|
# removeEmptyFolders(path, removeRoot=False)
|
||||||
|
|
||||||
if os.listdir(path):
|
if os.listdir(path):
|
||||||
for dir in [os.path.join(path, o) for o in os.listdir(path) if
|
for dir in [os.path.join(path, o) for o in os.listdir(path) if
|
||||||
os.path.isdir(os.path.join(path, o))]:
|
os.path.isdir(os.path.join(path, o))]:
|
||||||
sync = [ o for o in os.listdir(dir) if os.path.splitext(o)[1] in ['.!sync','.bts'] ]
|
sync = [o for o in os.listdir(dir) if os.path.splitext(o)[1] in ['.!sync', '.bts']]
|
||||||
if len(sync) > 0 or len(os.listdir(dir)) == 0:
|
if len(sync) > 0 or len(os.listdir(dir)) == 0:
|
||||||
continue
|
continue
|
||||||
folders.extend([dir])
|
folders.extend([dir])
|
||||||
|
@ -667,7 +689,8 @@ def getDirs(section, subsection, link = 'hard'):
|
||||||
elif os.path.exists(core.CFG[section][subsection]["watch_dir"]):
|
elif os.path.exists(core.CFG[section][subsection]["watch_dir"]):
|
||||||
to_return.extend(processDir(core.CFG[section][subsection]["watch_dir"]))
|
to_return.extend(processDir(core.CFG[section][subsection]["watch_dir"]))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Failed to add directories from %s for post-processing: %s" % (core.CFG[section][subsection]["watch_dir"], e))
|
logger.error("Failed to add directories from %s for post-processing: %s" % (
|
||||||
|
core.CFG[section][subsection]["watch_dir"], e))
|
||||||
|
|
||||||
if core.USELINK == 'move':
|
if core.USELINK == 'move':
|
||||||
try:
|
try:
|
||||||
|
@ -678,10 +701,11 @@ def getDirs(section, subsection, link = 'hard'):
|
||||||
logger.error("Failed to add directories from %s for post-processing: %s" % (core.OUTPUTDIRECTORY, e))
|
logger.error("Failed to add directories from %s for post-processing: %s" % (core.OUTPUTDIRECTORY, e))
|
||||||
|
|
||||||
if not to_return:
|
if not to_return:
|
||||||
logger.debug("No directories identified in %s:%s for post-processing" % (section,subsection))
|
logger.debug("No directories identified in %s:%s for post-processing" % (section, subsection))
|
||||||
|
|
||||||
return list(set(to_return))
|
return list(set(to_return))
|
||||||
|
|
||||||
|
|
||||||
def onerror(func, path, exc_info):
|
def onerror(func, path, exc_info):
|
||||||
"""
|
"""
|
||||||
Error handler for ``shutil.rmtree``.
|
Error handler for ``shutil.rmtree``.
|
||||||
|
@ -700,6 +724,7 @@ def onerror(func, path, exc_info):
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
def rmDir(dirName):
|
def rmDir(dirName):
|
||||||
logger.info("Deleting %s" % (dirName))
|
logger.info("Deleting %s" % (dirName))
|
||||||
try:
|
try:
|
||||||
|
@ -707,6 +732,7 @@ def rmDir(dirName):
|
||||||
except:
|
except:
|
||||||
logger.error("Unable to delete folder %s" % (dirName))
|
logger.error("Unable to delete folder %s" % (dirName))
|
||||||
|
|
||||||
|
|
||||||
def cleanDir(path, section, subsection):
|
def cleanDir(path, section, subsection):
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
logger.info('Directory %s has been processed and removed ...' % (path), 'CLEANDIR')
|
logger.info('Directory %s has been processed and removed ...' % (path), 'CLEANDIR')
|
||||||
|
@ -717,10 +743,12 @@ def cleanDir(path, section, subsection):
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
minSize = int(core.CFG[section][subsection]['minSize'])
|
minSize = int(core.CFG[section][subsection]['minSize'])
|
||||||
except:minSize = 0
|
except:
|
||||||
|
minSize = 0
|
||||||
try:
|
try:
|
||||||
delete_ignored = int(core.CFG[section][subsection]['delete_ignored'])
|
delete_ignored = int(core.CFG[section][subsection]['delete_ignored'])
|
||||||
except:delete_ignored = 0
|
except:
|
||||||
|
delete_ignored = 0
|
||||||
try:
|
try:
|
||||||
num_files = len(listMediaFiles(path, minSize=minSize, delete_ignored=delete_ignored))
|
num_files = len(listMediaFiles(path, minSize=minSize, delete_ignored=delete_ignored))
|
||||||
except:
|
except:
|
||||||
|
@ -737,6 +765,7 @@ def cleanDir(path, section, subsection):
|
||||||
except:
|
except:
|
||||||
logger.error("Unable to delete directory %s" % (path))
|
logger.error("Unable to delete directory %s" % (path))
|
||||||
|
|
||||||
|
|
||||||
def create_torrent_class(clientAgent):
|
def create_torrent_class(clientAgent):
|
||||||
# Hardlink solution for Torrents
|
# Hardlink solution for Torrents
|
||||||
tc = None
|
tc = None
|
||||||
|
@ -753,8 +782,8 @@ def create_torrent_class(clientAgent):
|
||||||
logger.debug("Connecting to %s: http://%s:%s" % (
|
logger.debug("Connecting to %s: http://%s:%s" % (
|
||||||
clientAgent, core.TRANSMISSIONHOST, core.TRANSMISSIONPORT))
|
clientAgent, core.TRANSMISSIONHOST, core.TRANSMISSIONPORT))
|
||||||
tc = TransmissionClient(core.TRANSMISSIONHOST, core.TRANSMISSIONPORT,
|
tc = TransmissionClient(core.TRANSMISSIONHOST, core.TRANSMISSIONPORT,
|
||||||
core.TRANSMISSIONUSR,
|
core.TRANSMISSIONUSR,
|
||||||
core.TRANSMISSIONPWD)
|
core.TRANSMISSIONPWD)
|
||||||
except:
|
except:
|
||||||
logger.error("Failed to connect to Transmission")
|
logger.error("Failed to connect to Transmission")
|
||||||
|
|
||||||
|
@ -763,12 +792,13 @@ def create_torrent_class(clientAgent):
|
||||||
logger.debug("Connecting to %s: http://%s:%s" % (clientAgent, core.DELUGEHOST, core.DELUGEPORT))
|
logger.debug("Connecting to %s: http://%s:%s" % (clientAgent, core.DELUGEHOST, core.DELUGEPORT))
|
||||||
tc = DelugeClient()
|
tc = DelugeClient()
|
||||||
tc.connect(host=core.DELUGEHOST, port=core.DELUGEPORT, username=core.DELUGEUSR,
|
tc.connect(host=core.DELUGEHOST, port=core.DELUGEPORT, username=core.DELUGEUSR,
|
||||||
password=core.DELUGEPWD)
|
password=core.DELUGEPWD)
|
||||||
except:
|
except:
|
||||||
logger.error("Failed to connect to Deluge")
|
logger.error("Failed to connect to Deluge")
|
||||||
|
|
||||||
return tc
|
return tc
|
||||||
|
|
||||||
|
|
||||||
def pause_torrent(clientAgent, inputHash, inputID, inputName):
|
def pause_torrent(clientAgent, inputHash, inputID, inputName):
|
||||||
logger.debug("Stopping torrent %s in %s while processing" % (inputName, clientAgent))
|
logger.debug("Stopping torrent %s in %s while processing" % (inputName, clientAgent))
|
||||||
try:
|
try:
|
||||||
|
@ -782,6 +812,7 @@ def pause_torrent(clientAgent, inputHash, inputID, inputName):
|
||||||
except:
|
except:
|
||||||
logger.warning("Failed to stop torrent %s in %s" % (inputName, clientAgent))
|
logger.warning("Failed to stop torrent %s in %s" % (inputName, clientAgent))
|
||||||
|
|
||||||
|
|
||||||
def resume_torrent(clientAgent, inputHash, inputID, inputName):
|
def resume_torrent(clientAgent, inputHash, inputID, inputName):
|
||||||
if not core.TORRENT_RESUME == 1:
|
if not core.TORRENT_RESUME == 1:
|
||||||
return
|
return
|
||||||
|
@ -797,6 +828,7 @@ def resume_torrent(clientAgent, inputHash, inputID, inputName):
|
||||||
except:
|
except:
|
||||||
logger.warning("Failed to start torrent %s in %s" % (inputName, clientAgent))
|
logger.warning("Failed to start torrent %s in %s" % (inputName, clientAgent))
|
||||||
|
|
||||||
|
|
||||||
def remove_torrent(clientAgent, inputHash, inputID, inputName):
|
def remove_torrent(clientAgent, inputHash, inputID, inputName):
|
||||||
if core.DELETE_ORIGINAL == 1 or core.USELINK == 'move':
|
if core.DELETE_ORIGINAL == 1 or core.USELINK == 'move':
|
||||||
logger.debug("Deleting torrent %s from %s" % (inputName, clientAgent))
|
logger.debug("Deleting torrent %s from %s" % (inputName, clientAgent))
|
||||||
|
@ -811,9 +843,10 @@ def remove_torrent(clientAgent, inputHash, inputID, inputName):
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
except:
|
except:
|
||||||
logger.warning("Failed to delete torrent %s in %s" % (inputName, clientAgent))
|
logger.warning("Failed to delete torrent %s in %s" % (inputName, clientAgent))
|
||||||
else:
|
else:
|
||||||
resume_torrent(clientAgent, inputHash, inputID, inputName)
|
resume_torrent(clientAgent, inputHash, inputID, inputName)
|
||||||
|
|
||||||
|
|
||||||
def find_download(clientAgent, download_id):
|
def find_download(clientAgent, download_id):
|
||||||
logger.debug("Searching for Download on %s ..." % (clientAgent))
|
logger.debug("Searching for Download on %s ..." % (clientAgent))
|
||||||
if clientAgent == 'utorrent':
|
if clientAgent == 'utorrent':
|
||||||
|
@ -851,6 +884,7 @@ def find_download(clientAgent, download_id):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def get_nzoid(inputName):
|
def get_nzoid(inputName):
|
||||||
nzoid = None
|
nzoid = None
|
||||||
slots = []
|
slots = []
|
||||||
|
@ -923,6 +957,7 @@ def is_archive_file(filename):
|
||||||
return regext.split(filename)[0]
|
return regext.split(filename)[0]
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def isMediaFile(mediafile, media=True, audio=True, meta=True, archives=True):
|
def isMediaFile(mediafile, media=True, audio=True, meta=True, archives=True):
|
||||||
fileName, fileExt = os.path.splitext(mediafile)
|
fileName, fileExt = os.path.splitext(mediafile)
|
||||||
|
|
||||||
|
@ -933,17 +968,18 @@ def isMediaFile(mediafile, media=True, audio=True, meta=True, archives=True):
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if (media and fileExt.lower() in core.MEDIACONTAINER)\
|
if (media and fileExt.lower() in core.MEDIACONTAINER) \
|
||||||
or (audio and fileExt.lower() in core.AUDIOCONTAINER)\
|
or (audio and fileExt.lower() in core.AUDIOCONTAINER) \
|
||||||
or (meta and fileExt.lower() in core.METACONTAINER)\
|
or (meta and fileExt.lower() in core.METACONTAINER) \
|
||||||
or (archives and is_archive_file(mediafile)):
|
or (archives and is_archive_file(mediafile)):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def listMediaFiles(path, minSize=0, delete_ignored=0, media=True, audio=True, meta=True, archives=True):
|
def listMediaFiles(path, minSize=0, delete_ignored=0, media=True, audio=True, meta=True, archives=True):
|
||||||
files = []
|
files = []
|
||||||
if not os.path.isdir(path):
|
if not os.path.isdir(path):
|
||||||
if os.path.isfile(path): # Single file downloads.
|
if os.path.isfile(path): # Single file downloads.
|
||||||
curFile = os.path.split(path)[1]
|
curFile = os.path.split(path)[1]
|
||||||
if isMediaFile(curFile, media, audio, meta, archives):
|
if isMediaFile(curFile, media, audio, meta, archives):
|
||||||
|
@ -953,7 +989,8 @@ def listMediaFiles(path, minSize=0, delete_ignored=0, media=True, audio=True, me
|
||||||
try:
|
try:
|
||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
logger.debug('Ignored file %s has been removed ...' % (curFile))
|
logger.debug('Ignored file %s has been removed ...' % (curFile))
|
||||||
except:pass
|
except:
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
files.append(path)
|
files.append(path)
|
||||||
|
|
||||||
|
@ -973,12 +1010,14 @@ def listMediaFiles(path, minSize=0, delete_ignored=0, media=True, audio=True, me
|
||||||
try:
|
try:
|
||||||
os.unlink(fullCurFile)
|
os.unlink(fullCurFile)
|
||||||
logger.debug('Ignored file %s has been removed ...' % (curFile))
|
logger.debug('Ignored file %s has been removed ...' % (curFile))
|
||||||
except:pass
|
except:
|
||||||
|
pass
|
||||||
continue
|
continue
|
||||||
|
|
||||||
files.append(fullCurFile)
|
files.append(fullCurFile)
|
||||||
|
|
||||||
return sorted(files,key=len)
|
return sorted(files, key=len)
|
||||||
|
|
||||||
|
|
||||||
def find_imdbid(dirName, inputName):
|
def find_imdbid(dirName, inputName):
|
||||||
imdbid = None
|
imdbid = None
|
||||||
|
@ -987,7 +1026,7 @@ def find_imdbid(dirName, 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+inputName)
|
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)
|
||||||
|
@ -1000,14 +1039,14 @@ def find_imdbid(dirName, inputName):
|
||||||
logger.info("Found imdbID [%s] via file name" % imdbid)
|
logger.info("Found imdbID [%s] via file name" % imdbid)
|
||||||
return imdbid
|
return imdbid
|
||||||
if os.environ.has_key('NZBPR__DNZB_MOREINFO'):
|
if os.environ.has_key('NZBPR__DNZB_MOREINFO'):
|
||||||
dnzb_more_info=os.environ.get('NZBPR__DNZB_MOREINFO', '')
|
dnzb_more_info = os.environ.get('NZBPR__DNZB_MOREINFO', '')
|
||||||
if dnzb_more_info != '':
|
if dnzb_more_info != '':
|
||||||
regex = re.compile(r'^http://www.imdb.com/title/(tt[0-9]+)/$', re.IGNORECASE)
|
regex = re.compile(r'^http://www.imdb.com/title/(tt[0-9]+)/$', re.IGNORECASE)
|
||||||
m = regex.match(dnzb_more_info)
|
m = regex.match(dnzb_more_info)
|
||||||
if m:
|
if m:
|
||||||
imdbid = m.group(1)
|
imdbid = m.group(1)
|
||||||
logger.info("Found imdbID [%s] from DNZB-MoreInfo" % imdbid)
|
logger.info("Found imdbID [%s] from DNZB-MoreInfo" % imdbid)
|
||||||
return imdbid
|
return imdbid
|
||||||
logger.info('Searching IMDB for imdbID ...')
|
logger.info('Searching IMDB for imdbID ...')
|
||||||
guess = guessit.guess_movie_info(inputName)
|
guess = guessit.guess_movie_info(inputName)
|
||||||
if guess:
|
if guess:
|
||||||
|
@ -1045,7 +1084,8 @@ def find_imdbid(dirName, inputName):
|
||||||
logger.warning('Unable to find a imdbID for %s' % (inputName))
|
logger.warning('Unable to find a imdbID for %s' % (inputName))
|
||||||
return imdbid
|
return imdbid
|
||||||
|
|
||||||
def extractFiles(src, dst=None, keep_archive = None):
|
|
||||||
|
def extractFiles(src, dst=None, keep_archive=None):
|
||||||
extracted_folder = []
|
extracted_folder = []
|
||||||
extracted_archive = []
|
extracted_archive = []
|
||||||
|
|
||||||
|
@ -1081,13 +1121,14 @@ def extractFiles(src, dst=None, keep_archive = None):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Unable to remove file %s due to: %s" % (inputFile, e))
|
logger.error("Unable to remove file %s due to: %s" % (inputFile, e))
|
||||||
|
|
||||||
|
|
||||||
def import_subs(filename):
|
def import_subs(filename):
|
||||||
if not core.GETSUBS:
|
if not core.GETSUBS:
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
subliminal.cache_region.configure('dogpile.cache.memory')
|
subliminal.cache_region.configure('dogpile.cache.memory')
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
languages = set()
|
languages = set()
|
||||||
for item in core.SLANGUAGES:
|
for item in core.SLANGUAGES:
|
||||||
|
@ -1098,13 +1139,14 @@ def import_subs(filename):
|
||||||
if not languages:
|
if not languages:
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.debug("Attempting to download subtitles for %s" %(filename), 'SUBTITLES')
|
logger.debug("Attempting to download subtitles for %s" % (filename), 'SUBTITLES')
|
||||||
try:
|
try:
|
||||||
video = subliminal.scan_video(filename, subtitles=True, embedded_subtitles=True)
|
video = subliminal.scan_video(filename, subtitles=True, embedded_subtitles=True)
|
||||||
subtitles = subliminal.download_best_subtitles([video], languages, hearing_impaired=False)
|
subtitles = subliminal.download_best_subtitles([video], languages, hearing_impaired=False)
|
||||||
subliminal.save_subtitles(subtitles)
|
subliminal.save_subtitles(subtitles)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Failed to download subtitles for %s due to: %s" %(filename, e), 'SUBTITLES')
|
logger.error("Failed to download subtitles for %s due to: %s" % (filename, e), 'SUBTITLES')
|
||||||
|
|
||||||
|
|
||||||
def server_responding(baseURL):
|
def server_responding(baseURL):
|
||||||
try:
|
try:
|
||||||
|
@ -1113,6 +1155,7 @@ def server_responding(baseURL):
|
||||||
except (requests.ConnectionError, requests.exceptions.Timeout):
|
except (requests.ConnectionError, requests.exceptions.Timeout):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def plex_update(category):
|
def plex_update(category):
|
||||||
if core.FAILED:
|
if core.FAILED:
|
||||||
return
|
return
|
||||||
|
@ -1124,7 +1167,7 @@ def plex_update(category):
|
||||||
section = None
|
section = None
|
||||||
if not core.PLEXSEC:
|
if not core.PLEXSEC:
|
||||||
return
|
return
|
||||||
logger.debug("Attempting to update Plex Library for category %s." %(category), 'PLEX')
|
logger.debug("Attempting to update Plex Library for category %s." % (category), 'PLEX')
|
||||||
for item in core.PLEXSEC:
|
for item in core.PLEXSEC:
|
||||||
if item[0] == category:
|
if item[0] == category:
|
||||||
section = item[1]
|
section = item[1]
|
||||||
|
@ -1136,6 +1179,7 @@ def plex_update(category):
|
||||||
else:
|
else:
|
||||||
logger.debug("Could not identify section for plex update", 'PLEX')
|
logger.debug("Could not identify section for plex update", 'PLEX')
|
||||||
|
|
||||||
|
|
||||||
def backupVersionedFile(old_file, version):
|
def backupVersionedFile(old_file, version):
|
||||||
numTries = 0
|
numTries = 0
|
||||||
|
|
||||||
|
@ -1152,7 +1196,8 @@ def backupVersionedFile(old_file, version):
|
||||||
logger.log(u"Backup done", logger.DEBUG)
|
logger.log(u"Backup done", logger.DEBUG)
|
||||||
break
|
break
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
logger.log(u"Error while trying to back up " + old_file + " to " + new_file + " : " + str(e), logger.WARNING)
|
logger.log(u"Error while trying to back up " + old_file + " to " + new_file + " : " + str(e),
|
||||||
|
logger.WARNING)
|
||||||
numTries += 1
|
numTries += 1
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
logger.log(u"Trying again.", logger.DEBUG)
|
logger.log(u"Trying again.", logger.DEBUG)
|
||||||
|
@ -1181,6 +1226,7 @@ def get_downloadInfo(inputName, status):
|
||||||
|
|
||||||
return sqlResults
|
return sqlResults
|
||||||
|
|
||||||
|
|
||||||
class RunningProcess():
|
class RunningProcess():
|
||||||
""" Limits application to single instance """
|
""" Limits application to single instance """
|
||||||
|
|
||||||
|
@ -1193,13 +1239,13 @@ class RunningProcess():
|
||||||
def alreadyrunning(self):
|
def alreadyrunning(self):
|
||||||
return self.process.alreadyrunning()
|
return self.process.alreadyrunning()
|
||||||
|
|
||||||
#def __del__(self):
|
# def __del__(self):
|
||||||
# self.process.__del__()
|
# self.process.__del__()
|
||||||
|
|
||||||
|
|
||||||
class WindowsProcess():
|
class WindowsProcess():
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.mutexname = "nzbtomedia_" + core.PID_FILE.replace('\\','/') # {D0E858DF-985E-4907-B7FB-8D732C3FC3B9}"
|
self.mutexname = "nzbtomedia_" + core.PID_FILE.replace('\\', '/') # {D0E858DF-985E-4907-B7FB-8D732C3FC3B9}"
|
||||||
if platform.system() == 'Windows':
|
if platform.system() == 'Windows':
|
||||||
from win32event import CreateMutex
|
from win32event import CreateMutex
|
||||||
from win32api import CloseHandle, GetLastError
|
from win32api import CloseHandle, GetLastError
|
||||||
|
@ -1208,7 +1254,7 @@ class WindowsProcess():
|
||||||
self.CloseHandle = CloseHandle
|
self.CloseHandle = CloseHandle
|
||||||
self.GetLastError = GetLastError
|
self.GetLastError = GetLastError
|
||||||
self.ERROR_ALREADY_EXISTS = ERROR_ALREADY_EXISTS
|
self.ERROR_ALREADY_EXISTS = ERROR_ALREADY_EXISTS
|
||||||
|
|
||||||
def alreadyrunning(self):
|
def alreadyrunning(self):
|
||||||
self.mutex = self.CreateMutex(None, 0, self.mutexname)
|
self.mutex = self.CreateMutex(None, 0, self.mutexname)
|
||||||
self.lasterror = self.GetLastError()
|
self.lasterror = self.GetLastError()
|
||||||
|
@ -1217,14 +1263,13 @@ class WindowsProcess():
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
if self.mutex:
|
if self.mutex:
|
||||||
self.CloseHandle(self.mutex)
|
self.CloseHandle(self.mutex)
|
||||||
|
|
||||||
class PosixProcess():
|
|
||||||
|
|
||||||
|
class PosixProcess():
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.pidpath = core.PID_FILE
|
self.pidpath = core.PID_FILE
|
||||||
self.lock_socket = None
|
self.lock_socket = None
|
||||||
|
@ -1239,7 +1284,8 @@ class PosixProcess():
|
||||||
if "Address already in use" in e:
|
if "Address already in use" in e:
|
||||||
self.lasterror = True
|
self.lasterror = True
|
||||||
return self.lasterror
|
return self.lasterror
|
||||||
except AttributeError: pass
|
except AttributeError:
|
||||||
|
pass
|
||||||
if os.path.exists(self.pidpath):
|
if os.path.exists(self.pidpath):
|
||||||
# Make sure it is not a "stale" pidFile
|
# Make sure it is not a "stale" pidFile
|
||||||
try:
|
try:
|
||||||
|
@ -1256,7 +1302,7 @@ class PosixProcess():
|
||||||
else:
|
else:
|
||||||
self.lasterror = False
|
self.lasterror = False
|
||||||
else:
|
else:
|
||||||
self.lasterror = False
|
self.lasterror = False
|
||||||
|
|
||||||
if not self.lasterror:
|
if not self.lasterror:
|
||||||
# Write my pid into pidFile to keep multiple copies of program from running
|
# Write my pid into pidFile to keep multiple copies of program from running
|
||||||
|
@ -1264,7 +1310,8 @@ class PosixProcess():
|
||||||
fp = open(self.pidpath, 'w')
|
fp = open(self.pidpath, 'w')
|
||||||
fp.write(str(os.getpid()))
|
fp.write(str(os.getpid()))
|
||||||
fp.close()
|
fp.close()
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
return self.lasterror
|
return self.lasterror
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,8 @@ from exceptions import DelugeRPCError
|
||||||
from protocol import DelugeRPCRequest, DelugeRPCResponse
|
from protocol import DelugeRPCRequest, DelugeRPCResponse
|
||||||
from transfer import DelugeTransfer
|
from transfer import DelugeTransfer
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["DelugeClient"]
|
__all__ = ["DelugeClient"]
|
||||||
|
|
||||||
|
|
||||||
RPC_RESPONSE = 1
|
RPC_RESPONSE = 1
|
||||||
RPC_ERROR = 2
|
RPC_ERROR = 2
|
||||||
RPC_EVENT = 3
|
RPC_EVENT = 3
|
||||||
|
@ -31,7 +29,8 @@ class DelugeClient(object):
|
||||||
appDataPath = os.environ.get("APPDATA")
|
appDataPath = os.environ.get("APPDATA")
|
||||||
if not appDataPath:
|
if not appDataPath:
|
||||||
import _winreg
|
import _winreg
|
||||||
hkey = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders")
|
hkey = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER,
|
||||||
|
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders")
|
||||||
appDataReg = _winreg.QueryValueEx(hkey, "AppData")
|
appDataReg = _winreg.QueryValueEx(hkey, "AppData")
|
||||||
appDataPath = appDataReg[0]
|
appDataPath = appDataReg[0]
|
||||||
_winreg.CloseKey(hkey)
|
_winreg.CloseKey(hkey)
|
||||||
|
@ -44,7 +43,6 @@ class DelugeClient(object):
|
||||||
except OSError, e:
|
except OSError, e:
|
||||||
return username, password
|
return username, password
|
||||||
|
|
||||||
|
|
||||||
if os.path.exists(auth_file):
|
if os.path.exists(auth_file):
|
||||||
for line in open(auth_file):
|
for line in open(auth_file):
|
||||||
if line.startswith("#"):
|
if line.startswith("#"):
|
||||||
|
@ -108,20 +106,20 @@ class DelugeClient(object):
|
||||||
|
|
||||||
message_type = message[0]
|
message_type = message[0]
|
||||||
|
|
||||||
# if message_type == RPC_EVENT:
|
# if message_type == RPC_EVENT:
|
||||||
# event = message[1]
|
# event = message[1]
|
||||||
# values = message[2]
|
# values = message[2]
|
||||||
#
|
#
|
||||||
# if event in self._event_handlers:
|
# if event in self._event_handlers:
|
||||||
# for handler in self._event_handlers[event]:
|
# for handler in self._event_handlers[event]:
|
||||||
# gevent.spawn(handler, *values)
|
# gevent.spawn(handler, *values)
|
||||||
#
|
#
|
||||||
# elif message_type in (RPC_RESPONSE, RPC_ERROR):
|
# elif message_type in (RPC_RESPONSE, RPC_ERROR):
|
||||||
if message_type in (RPC_RESPONSE, RPC_ERROR):
|
if message_type in (RPC_RESPONSE, RPC_ERROR):
|
||||||
request_id = message[1]
|
request_id = message[1]
|
||||||
value = message[2]
|
value = message[2]
|
||||||
|
|
||||||
if request_id == self._request_counter :
|
if request_id == self._request_counter:
|
||||||
if message_type == RPC_RESPONSE:
|
if message_type == RPC_RESPONSE:
|
||||||
response.set(value)
|
response.set(value)
|
||||||
elif message_type == RPC_ERROR:
|
elif message_type == RPC_ERROR:
|
||||||
|
@ -160,4 +158,3 @@ class DelugeClient(object):
|
||||||
def disconnect(self):
|
def disconnect(self):
|
||||||
"""Disconnects from the daemon."""
|
"""Disconnects from the daemon."""
|
||||||
self.transfer.disconnect()
|
self.transfer.disconnect()
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
__all__ = ["DelugeRPCError"]
|
__all__ = ["DelugeRPCError"]
|
||||||
|
|
||||||
|
|
||||||
class DelugeRPCError(Exception):
|
class DelugeRPCError(Exception):
|
||||||
def __init__(self, name, msg, traceback):
|
def __init__(self, name, msg, traceback):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -9,4 +10,3 @@ class DelugeRPCError(Exception):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "{0}: {1}: {2}".format(self.__class__.__name__, self.name, self.msg)
|
return "{0}: {1}: {2}".format(self.__class__.__name__, self.name, self.msg)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
__all__ = ["DelugeRPCRequest", "DelugeRPCResponse"]
|
__all__ = ["DelugeRPCRequest", "DelugeRPCResponse"]
|
||||||
|
|
||||||
|
|
||||||
class DelugeRPCRequest(object):
|
class DelugeRPCRequest(object):
|
||||||
def __init__(self, request_id, method, *args, **kwargs):
|
def __init__(self, request_id, method, *args, **kwargs):
|
||||||
self.request_id = request_id
|
self.request_id = request_id
|
||||||
|
@ -11,6 +12,7 @@ class DelugeRPCRequest(object):
|
||||||
def format(self):
|
def format(self):
|
||||||
return (self.request_id, self.method, self.args, self.kwargs)
|
return (self.request_id, self.method, self.args, self.kwargs)
|
||||||
|
|
||||||
|
|
||||||
class DelugeRPCResponse(object):
|
class DelugeRPCResponse(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.value = None
|
self.value = None
|
||||||
|
@ -36,4 +38,3 @@ class DelugeRPCResponse(object):
|
||||||
return self.value
|
return self.value
|
||||||
else:
|
else:
|
||||||
raise self._exception
|
raise self._exception
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,9 @@ BitTorrent project. For complex, heterogeneous data structures with
|
||||||
many small elements, r-encodings take up significantly less space than
|
many small elements, r-encodings take up significantly less space than
|
||||||
b-encodings:
|
b-encodings:
|
||||||
|
|
||||||
>>> len(rencode.dumps({'a':0, 'b':[1,2], 'c':99}))
|
>>> len(rencode.dumps({'a': 0, 'b': [1, 2], 'c': 99}))
|
||||||
13
|
13
|
||||||
>>> len(bencode.bencode({'a':0, 'b':[1,2], 'c':99}))
|
>>> len(bencode.bencode({'a': 0, 'b': [1, 2], 'c': 99}))
|
||||||
26
|
26
|
||||||
|
|
||||||
The rencode format is not standardized, and may change with different
|
The rencode format is not standardized, and may change with different
|
||||||
|
@ -73,19 +73,19 @@ MAX_INT_LENGTH = 64
|
||||||
|
|
||||||
# The bencode 'typecodes' such as i, d, etc have been extended and
|
# The bencode 'typecodes' such as i, d, etc have been extended and
|
||||||
# relocated on the base-256 character set.
|
# relocated on the base-256 character set.
|
||||||
CHR_LIST = chr(59)
|
CHR_LIST = chr(59)
|
||||||
CHR_DICT = chr(60)
|
CHR_DICT = chr(60)
|
||||||
CHR_INT = chr(61)
|
CHR_INT = chr(61)
|
||||||
CHR_INT1 = chr(62)
|
CHR_INT1 = chr(62)
|
||||||
CHR_INT2 = chr(63)
|
CHR_INT2 = chr(63)
|
||||||
CHR_INT4 = chr(64)
|
CHR_INT4 = chr(64)
|
||||||
CHR_INT8 = chr(65)
|
CHR_INT8 = chr(65)
|
||||||
CHR_FLOAT32 = chr(66)
|
CHR_FLOAT32 = chr(66)
|
||||||
CHR_FLOAT64 = chr(44)
|
CHR_FLOAT64 = chr(44)
|
||||||
CHR_TRUE = chr(67)
|
CHR_TRUE = chr(67)
|
||||||
CHR_FALSE = chr(68)
|
CHR_FALSE = chr(68)
|
||||||
CHR_NONE = chr(69)
|
CHR_NONE = chr(69)
|
||||||
CHR_TERM = chr(127)
|
CHR_TERM = chr(127)
|
||||||
|
|
||||||
# Positive integers with value embedded in typecode.
|
# Positive integers with value embedded in typecode.
|
||||||
INT_POS_FIXED_START = 0
|
INT_POS_FIXED_START = 0
|
||||||
|
@ -104,9 +104,10 @@ STR_FIXED_START = 128
|
||||||
STR_FIXED_COUNT = 64
|
STR_FIXED_COUNT = 64
|
||||||
|
|
||||||
# Lists with length embedded in typecode.
|
# Lists with length embedded in typecode.
|
||||||
LIST_FIXED_START = STR_FIXED_START+STR_FIXED_COUNT
|
LIST_FIXED_START = STR_FIXED_START + STR_FIXED_COUNT
|
||||||
LIST_FIXED_COUNT = 64
|
LIST_FIXED_COUNT = 64
|
||||||
|
|
||||||
|
|
||||||
def decode_int(x, f):
|
def decode_int(x, f):
|
||||||
f += 1
|
f += 1
|
||||||
newf = x.index(CHR_TERM, f)
|
newf = x.index(CHR_TERM, f)
|
||||||
|
@ -119,35 +120,42 @@ def decode_int(x, f):
|
||||||
if x[f] == '-':
|
if x[f] == '-':
|
||||||
if x[f + 1] == '0':
|
if x[f + 1] == '0':
|
||||||
raise ValueError
|
raise ValueError
|
||||||
elif x[f] == '0' and newf != f+1:
|
elif x[f] == '0' and newf != f + 1:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
return (n, newf+1)
|
return (n, newf + 1)
|
||||||
|
|
||||||
|
|
||||||
def decode_intb(x, f):
|
def decode_intb(x, f):
|
||||||
f += 1
|
f += 1
|
||||||
return (struct.unpack('!b', x[f:f+1])[0], f+1)
|
return (struct.unpack('!b', x[f:f + 1])[0], f + 1)
|
||||||
|
|
||||||
|
|
||||||
def decode_inth(x, f):
|
def decode_inth(x, f):
|
||||||
f += 1
|
f += 1
|
||||||
return (struct.unpack('!h', x[f:f+2])[0], f+2)
|
return (struct.unpack('!h', x[f:f + 2])[0], f + 2)
|
||||||
|
|
||||||
|
|
||||||
def decode_intl(x, f):
|
def decode_intl(x, f):
|
||||||
f += 1
|
f += 1
|
||||||
return (struct.unpack('!l', x[f:f+4])[0], f+4)
|
return (struct.unpack('!l', x[f:f + 4])[0], f + 4)
|
||||||
|
|
||||||
|
|
||||||
def decode_intq(x, f):
|
def decode_intq(x, f):
|
||||||
f += 1
|
f += 1
|
||||||
return (struct.unpack('!q', x[f:f+8])[0], f+8)
|
return (struct.unpack('!q', x[f:f + 8])[0], f + 8)
|
||||||
|
|
||||||
|
|
||||||
def decode_float32(x, f):
|
def decode_float32(x, f):
|
||||||
f += 1
|
f += 1
|
||||||
n = struct.unpack('!f', x[f:f+4])[0]
|
n = struct.unpack('!f', x[f:f + 4])[0]
|
||||||
return (n, f+4)
|
return (n, f + 4)
|
||||||
|
|
||||||
|
|
||||||
def decode_float64(x, f):
|
def decode_float64(x, f):
|
||||||
f += 1
|
f += 1
|
||||||
n = struct.unpack('!d', x[f:f+8])[0]
|
n = struct.unpack('!d', x[f:f + 8])[0]
|
||||||
return (n, f+8)
|
return (n, f + 8)
|
||||||
|
|
||||||
|
|
||||||
def decode_string(x, f):
|
def decode_string(x, f):
|
||||||
colon = x.index(':', f)
|
colon = x.index(':', f)
|
||||||
|
@ -155,40 +163,46 @@ def decode_string(x, f):
|
||||||
n = int(x[f:colon])
|
n = int(x[f:colon])
|
||||||
except (OverflowError, ValueError):
|
except (OverflowError, ValueError):
|
||||||
n = long(x[f:colon])
|
n = long(x[f:colon])
|
||||||
if x[f] == '0' and colon != f+1:
|
if x[f] == '0' and colon != f + 1:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
colon += 1
|
colon += 1
|
||||||
s = x[colon:colon+n]
|
s = x[colon:colon + n]
|
||||||
try:
|
try:
|
||||||
t = s.decode("utf8")
|
t = s.decode("utf8")
|
||||||
if len(t) != len(s):
|
if len(t) != len(s):
|
||||||
s = t
|
s = t
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
pass
|
pass
|
||||||
return (s, colon+n)
|
return (s, colon + n)
|
||||||
|
|
||||||
|
|
||||||
def decode_list(x, f):
|
def decode_list(x, f):
|
||||||
r, f = [], f+1
|
r, f = [], f + 1
|
||||||
while x[f] != CHR_TERM:
|
while x[f] != CHR_TERM:
|
||||||
v, f = decode_func[x[f]](x, f)
|
v, f = decode_func[x[f]](x, f)
|
||||||
r.append(v)
|
r.append(v)
|
||||||
return (tuple(r), f + 1)
|
return (tuple(r), f + 1)
|
||||||
|
|
||||||
|
|
||||||
def decode_dict(x, f):
|
def decode_dict(x, f):
|
||||||
r, f = {}, f+1
|
r, f = {}, f + 1
|
||||||
while x[f] != CHR_TERM:
|
while x[f] != CHR_TERM:
|
||||||
k, f = decode_func[x[f]](x, f)
|
k, f = decode_func[x[f]](x, f)
|
||||||
r[k], f = decode_func[x[f]](x, f)
|
r[k], f = decode_func[x[f]](x, f)
|
||||||
return (r, f + 1)
|
return (r, f + 1)
|
||||||
|
|
||||||
|
|
||||||
def decode_true(x, f):
|
def decode_true(x, f):
|
||||||
return (True, f+1)
|
return (True, f + 1)
|
||||||
|
|
||||||
|
|
||||||
def decode_false(x, f):
|
def decode_false(x, f):
|
||||||
return (False, f+1)
|
return (False, f + 1)
|
||||||
|
|
||||||
|
|
||||||
def decode_none(x, f):
|
def decode_none(x, f):
|
||||||
return (None, f+1)
|
return (None, f + 1)
|
||||||
|
|
||||||
|
|
||||||
decode_func = {}
|
decode_func = {}
|
||||||
decode_func['0'] = decode_string
|
decode_func['0'] = decode_string
|
||||||
|
@ -201,77 +215,94 @@ decode_func['6'] = decode_string
|
||||||
decode_func['7'] = decode_string
|
decode_func['7'] = decode_string
|
||||||
decode_func['8'] = decode_string
|
decode_func['8'] = decode_string
|
||||||
decode_func['9'] = decode_string
|
decode_func['9'] = decode_string
|
||||||
decode_func[CHR_LIST ] = decode_list
|
decode_func[CHR_LIST] = decode_list
|
||||||
decode_func[CHR_DICT ] = decode_dict
|
decode_func[CHR_DICT] = decode_dict
|
||||||
decode_func[CHR_INT ] = decode_int
|
decode_func[CHR_INT] = decode_int
|
||||||
decode_func[CHR_INT1 ] = decode_intb
|
decode_func[CHR_INT1] = decode_intb
|
||||||
decode_func[CHR_INT2 ] = decode_inth
|
decode_func[CHR_INT2] = decode_inth
|
||||||
decode_func[CHR_INT4 ] = decode_intl
|
decode_func[CHR_INT4] = decode_intl
|
||||||
decode_func[CHR_INT8 ] = decode_intq
|
decode_func[CHR_INT8] = decode_intq
|
||||||
decode_func[CHR_FLOAT32] = decode_float32
|
decode_func[CHR_FLOAT32] = decode_float32
|
||||||
decode_func[CHR_FLOAT64] = decode_float64
|
decode_func[CHR_FLOAT64] = decode_float64
|
||||||
decode_func[CHR_TRUE ] = decode_true
|
decode_func[CHR_TRUE] = decode_true
|
||||||
decode_func[CHR_FALSE ] = decode_false
|
decode_func[CHR_FALSE] = decode_false
|
||||||
decode_func[CHR_NONE ] = decode_none
|
decode_func[CHR_NONE] = decode_none
|
||||||
|
|
||||||
|
|
||||||
def make_fixed_length_string_decoders():
|
def make_fixed_length_string_decoders():
|
||||||
def make_decoder(slen):
|
def make_decoder(slen):
|
||||||
def f(x, f):
|
def f(x, f):
|
||||||
s = x[f+1:f+1+slen]
|
s = x[f + 1:f + 1 + slen]
|
||||||
try:
|
try:
|
||||||
t = s.decode("utf8")
|
t = s.decode("utf8")
|
||||||
if len(t) != len(s):
|
if len(t) != len(s):
|
||||||
s = t
|
s = t
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
pass
|
pass
|
||||||
return (s, f+1+slen)
|
return (s, f + 1 + slen)
|
||||||
|
|
||||||
return f
|
return f
|
||||||
|
|
||||||
for i in range(STR_FIXED_COUNT):
|
for i in range(STR_FIXED_COUNT):
|
||||||
decode_func[chr(STR_FIXED_START+i)] = make_decoder(i)
|
decode_func[chr(STR_FIXED_START + i)] = make_decoder(i)
|
||||||
|
|
||||||
|
|
||||||
make_fixed_length_string_decoders()
|
make_fixed_length_string_decoders()
|
||||||
|
|
||||||
|
|
||||||
def make_fixed_length_list_decoders():
|
def make_fixed_length_list_decoders():
|
||||||
def make_decoder(slen):
|
def make_decoder(slen):
|
||||||
def f(x, f):
|
def f(x, f):
|
||||||
r, f = [], f+1
|
r, f = [], f + 1
|
||||||
for i in range(slen):
|
for i in range(slen):
|
||||||
v, f = decode_func[x[f]](x, f)
|
v, f = decode_func[x[f]](x, f)
|
||||||
r.append(v)
|
r.append(v)
|
||||||
return (tuple(r), f)
|
return (tuple(r), f)
|
||||||
|
|
||||||
return f
|
return f
|
||||||
|
|
||||||
for i in range(LIST_FIXED_COUNT):
|
for i in range(LIST_FIXED_COUNT):
|
||||||
decode_func[chr(LIST_FIXED_START+i)] = make_decoder(i)
|
decode_func[chr(LIST_FIXED_START + i)] = make_decoder(i)
|
||||||
|
|
||||||
|
|
||||||
make_fixed_length_list_decoders()
|
make_fixed_length_list_decoders()
|
||||||
|
|
||||||
|
|
||||||
def make_fixed_length_int_decoders():
|
def make_fixed_length_int_decoders():
|
||||||
def make_decoder(j):
|
def make_decoder(j):
|
||||||
def f(x, f):
|
def f(x, f):
|
||||||
return (j, f+1)
|
return (j, f + 1)
|
||||||
|
|
||||||
return f
|
return f
|
||||||
|
|
||||||
for i in range(INT_POS_FIXED_COUNT):
|
for i in range(INT_POS_FIXED_COUNT):
|
||||||
decode_func[chr(INT_POS_FIXED_START+i)] = make_decoder(i)
|
decode_func[chr(INT_POS_FIXED_START + i)] = make_decoder(i)
|
||||||
for i in range(INT_NEG_FIXED_COUNT):
|
for i in range(INT_NEG_FIXED_COUNT):
|
||||||
decode_func[chr(INT_NEG_FIXED_START+i)] = make_decoder(-1-i)
|
decode_func[chr(INT_NEG_FIXED_START + i)] = make_decoder(-1 - i)
|
||||||
|
|
||||||
|
|
||||||
make_fixed_length_int_decoders()
|
make_fixed_length_int_decoders()
|
||||||
|
|
||||||
|
|
||||||
def make_fixed_length_dict_decoders():
|
def make_fixed_length_dict_decoders():
|
||||||
def make_decoder(slen):
|
def make_decoder(slen):
|
||||||
def f(x, f):
|
def f(x, f):
|
||||||
r, f = {}, f+1
|
r, f = {}, f + 1
|
||||||
for j in range(slen):
|
for j in range(slen):
|
||||||
k, f = decode_func[x[f]](x, f)
|
k, f = decode_func[x[f]](x, f)
|
||||||
r[k], f = decode_func[x[f]](x, f)
|
r[k], f = decode_func[x[f]](x, f)
|
||||||
return (r, f)
|
return (r, f)
|
||||||
|
|
||||||
return f
|
return f
|
||||||
|
|
||||||
for i in range(DICT_FIXED_COUNT):
|
for i in range(DICT_FIXED_COUNT):
|
||||||
decode_func[chr(DICT_FIXED_START+i)] = make_decoder(i)
|
decode_func[chr(DICT_FIXED_START + i)] = make_decoder(i)
|
||||||
|
|
||||||
|
|
||||||
make_fixed_length_dict_decoders()
|
make_fixed_length_dict_decoders()
|
||||||
|
|
||||||
def encode_dict(x,r):
|
|
||||||
|
def encode_dict(x, r):
|
||||||
r.append(CHR_DICT)
|
r.append(CHR_DICT)
|
||||||
for k, v in x.items():
|
for k, v in x.items():
|
||||||
encode_func[type(k)](k, r)
|
encode_func[type(k)](k, r)
|
||||||
|
@ -288,13 +319,15 @@ def loads(x):
|
||||||
raise ValueError
|
raise ValueError
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
from types import StringType, IntType, LongType, DictType, ListType, TupleType, FloatType, NoneType, UnicodeType
|
from types import StringType, IntType, LongType, DictType, ListType, TupleType, FloatType, NoneType, UnicodeType
|
||||||
|
|
||||||
|
|
||||||
def encode_int(x, r):
|
def encode_int(x, r):
|
||||||
if 0 <= x < INT_POS_FIXED_COUNT:
|
if 0 <= x < INT_POS_FIXED_COUNT:
|
||||||
r.append(chr(INT_POS_FIXED_START+x))
|
r.append(chr(INT_POS_FIXED_START + x))
|
||||||
elif -INT_NEG_FIXED_COUNT <= x < 0:
|
elif -INT_NEG_FIXED_COUNT <= x < 0:
|
||||||
r.append(chr(INT_NEG_FIXED_START-1-x))
|
r.append(chr(INT_NEG_FIXED_START - 1 - x))
|
||||||
elif -128 <= x < 128:
|
elif -128 <= x < 128:
|
||||||
r.extend((CHR_INT1, struct.pack('!b', x)))
|
r.extend((CHR_INT1, struct.pack('!b', x)))
|
||||||
elif -32768 <= x < 32768:
|
elif -32768 <= x < 32768:
|
||||||
|
@ -309,27 +342,34 @@ def encode_int(x, r):
|
||||||
raise ValueError('overflow')
|
raise ValueError('overflow')
|
||||||
r.extend((CHR_INT, s, CHR_TERM))
|
r.extend((CHR_INT, s, CHR_TERM))
|
||||||
|
|
||||||
|
|
||||||
def encode_float32(x, r):
|
def encode_float32(x, r):
|
||||||
r.extend((CHR_FLOAT32, struct.pack('!f', x)))
|
r.extend((CHR_FLOAT32, struct.pack('!f', x)))
|
||||||
|
|
||||||
|
|
||||||
def encode_float64(x, r):
|
def encode_float64(x, r):
|
||||||
r.extend((CHR_FLOAT64, struct.pack('!d', x)))
|
r.extend((CHR_FLOAT64, struct.pack('!d', x)))
|
||||||
|
|
||||||
|
|
||||||
def encode_bool(x, r):
|
def encode_bool(x, r):
|
||||||
r.extend({False: CHR_FALSE, True: CHR_TRUE}[bool(x)])
|
r.extend({False: CHR_FALSE, True: CHR_TRUE}[bool(x)])
|
||||||
|
|
||||||
|
|
||||||
def encode_none(x, r):
|
def encode_none(x, r):
|
||||||
r.extend(CHR_NONE)
|
r.extend(CHR_NONE)
|
||||||
|
|
||||||
|
|
||||||
def encode_string(x, r):
|
def encode_string(x, r):
|
||||||
if len(x) < STR_FIXED_COUNT:
|
if len(x) < STR_FIXED_COUNT:
|
||||||
r.extend((chr(STR_FIXED_START + len(x)), x))
|
r.extend((chr(STR_FIXED_START + len(x)), x))
|
||||||
else:
|
else:
|
||||||
r.extend((str(len(x)), ':', x))
|
r.extend((str(len(x)), ':', x))
|
||||||
|
|
||||||
|
|
||||||
def encode_unicode(x, r):
|
def encode_unicode(x, r):
|
||||||
encode_string(x.encode("utf8"), r)
|
encode_string(x.encode("utf8"), r)
|
||||||
|
|
||||||
|
|
||||||
def encode_list(x, r):
|
def encode_list(x, r):
|
||||||
if len(x) < LIST_FIXED_COUNT:
|
if len(x) < LIST_FIXED_COUNT:
|
||||||
r.append(chr(LIST_FIXED_START + len(x)))
|
r.append(chr(LIST_FIXED_START + len(x)))
|
||||||
|
@ -341,7 +381,8 @@ def encode_list(x, r):
|
||||||
encode_func[type(i)](i, r)
|
encode_func[type(i)](i, r)
|
||||||
r.append(CHR_TERM)
|
r.append(CHR_TERM)
|
||||||
|
|
||||||
def encode_dict(x,r):
|
|
||||||
|
def encode_dict(x, r):
|
||||||
if len(x) < DICT_FIXED_COUNT:
|
if len(x) < DICT_FIXED_COUNT:
|
||||||
r.append(chr(DICT_FIXED_START + len(x)))
|
r.append(chr(DICT_FIXED_START + len(x)))
|
||||||
for k, v in x.items():
|
for k, v in x.items():
|
||||||
|
@ -354,6 +395,7 @@ def encode_dict(x,r):
|
||||||
encode_func[type(v)](v, r)
|
encode_func[type(v)](v, r)
|
||||||
r.append(CHR_TERM)
|
r.append(CHR_TERM)
|
||||||
|
|
||||||
|
|
||||||
encode_func = {}
|
encode_func = {}
|
||||||
encode_func[IntType] = encode_int
|
encode_func[IntType] = encode_int
|
||||||
encode_func[LongType] = encode_int
|
encode_func[LongType] = encode_int
|
||||||
|
@ -368,10 +410,12 @@ lock = Lock()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from types import BooleanType
|
from types import BooleanType
|
||||||
|
|
||||||
encode_func[BooleanType] = encode_bool
|
encode_func[BooleanType] = encode_bool
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def dumps(x, float_bits=DEFAULT_FLOAT_BITS):
|
def dumps(x, float_bits=DEFAULT_FLOAT_BITS):
|
||||||
"""
|
"""
|
||||||
Dump data structure to str.
|
Dump data structure to str.
|
||||||
|
@ -392,41 +436,46 @@ def dumps(x, float_bits=DEFAULT_FLOAT_BITS):
|
||||||
lock.release()
|
lock.release()
|
||||||
return ''.join(r)
|
return ''.join(r)
|
||||||
|
|
||||||
|
|
||||||
def test():
|
def test():
|
||||||
f1 = struct.unpack('!f', struct.pack('!f', 25.5))[0]
|
f1 = struct.unpack('!f', struct.pack('!f', 25.5))[0]
|
||||||
f2 = struct.unpack('!f', struct.pack('!f', 29.3))[0]
|
f2 = struct.unpack('!f', struct.pack('!f', 29.3))[0]
|
||||||
f3 = struct.unpack('!f', struct.pack('!f', -0.6))[0]
|
f3 = struct.unpack('!f', struct.pack('!f', -0.6))[0]
|
||||||
L = (({'a':15, 'bb':f1, 'ccc':f2, '':(f3,(),False,True,'')},('a',10**20),tuple(range(-100000,100000)),'b'*31,'b'*62,'b'*64,2**30,2**33,2**62,2**64,2**30,2**33,2**62,2**64,False,False, True, -1, 2, 0),)
|
L = (({'a': 15, 'bb': f1, 'ccc': f2, '': (f3, (), False, True, '')}, ('a', 10 ** 20), tuple(range(-100000, 100000)),
|
||||||
|
'b' * 31, 'b' * 62, 'b' * 64, 2 ** 30, 2 ** 33, 2 ** 62, 2 ** 64, 2 ** 30, 2 ** 33, 2 ** 62, 2 ** 64, False,
|
||||||
|
False, True, -1, 2, 0),)
|
||||||
assert loads(dumps(L)) == L
|
assert loads(dumps(L)) == L
|
||||||
d = dict(zip(range(-100000,100000),range(-100000,100000)))
|
d = dict(zip(range(-100000, 100000), range(-100000, 100000)))
|
||||||
d.update({'a':20, 20:40, 40:41, f1:f2, f2:f3, f3:False, False:True, True:False})
|
d.update({'a': 20, 20: 40, 40: 41, f1: f2, f2: f3, f3: False, False: True, True: False})
|
||||||
L = (d, {}, {5:6}, {7:7,True:8}, {9:10, 22:39, 49:50, 44: ''})
|
L = (d, {}, {5: 6}, {7: 7, True: 8}, {9: 10, 22: 39, 49: 50, 44: ''})
|
||||||
assert loads(dumps(L)) == L
|
assert loads(dumps(L)) == L
|
||||||
L = ('', 'a'*10, 'a'*100, 'a'*1000, 'a'*10000, 'a'*100000, 'a'*1000000, 'a'*10000000)
|
L = ('', 'a' * 10, 'a' * 100, 'a' * 1000, 'a' * 10000, 'a' * 100000, 'a' * 1000000, 'a' * 10000000)
|
||||||
assert loads(dumps(L)) == L
|
assert loads(dumps(L)) == L
|
||||||
L = tuple([dict(zip(range(n),range(n))) for n in range(100)]) + ('b',)
|
L = tuple([dict(zip(range(n), range(n))) for n in range(100)]) + ('b',)
|
||||||
assert loads(dumps(L)) == L
|
assert loads(dumps(L)) == L
|
||||||
L = tuple([dict(zip(range(n),range(-n,0))) for n in range(100)]) + ('b',)
|
L = tuple([dict(zip(range(n), range(-n, 0))) for n in range(100)]) + ('b',)
|
||||||
assert loads(dumps(L)) == L
|
assert loads(dumps(L)) == L
|
||||||
L = tuple([tuple(range(n)) for n in range(100)]) + ('b',)
|
L = tuple([tuple(range(n)) for n in range(100)]) + ('b',)
|
||||||
assert loads(dumps(L)) == L
|
assert loads(dumps(L)) == L
|
||||||
L = tuple(['a'*n for n in range(1000)]) + ('b',)
|
L = tuple(['a' * n for n in range(1000)]) + ('b',)
|
||||||
assert loads(dumps(L)) == L
|
assert loads(dumps(L)) == L
|
||||||
L = tuple(['a'*n for n in range(1000)]) + (None,True,None)
|
L = tuple(['a' * n for n in range(1000)]) + (None, True, None)
|
||||||
assert loads(dumps(L)) == L
|
assert loads(dumps(L)) == L
|
||||||
assert loads(dumps(None)) == None
|
assert loads(dumps(None)) == None
|
||||||
assert loads(dumps({None:None})) == {None:None}
|
assert loads(dumps({None: None})) == {None: None}
|
||||||
assert 1e-10<abs(loads(dumps(1.1))-1.1)<1e-6
|
assert 1e-10 < abs(loads(dumps(1.1)) - 1.1) < 1e-6
|
||||||
assert 1e-10<abs(loads(dumps(1.1,32))-1.1)<1e-6
|
assert 1e-10 < abs(loads(dumps(1.1, 32)) - 1.1) < 1e-6
|
||||||
assert abs(loads(dumps(1.1,64))-1.1)<1e-12
|
assert abs(loads(dumps(1.1, 64)) - 1.1) < 1e-12
|
||||||
assert loads(dumps(u"Hello World!!"))
|
assert loads(dumps(u"Hello World!!"))
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import psyco
|
import psyco
|
||||||
|
|
||||||
psyco.bind(dumps)
|
psyco.bind(dumps)
|
||||||
psyco.bind(loads)
|
psyco.bind(loads)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
test()
|
test()
|
||||||
|
|
|
@ -6,9 +6,9 @@ import ssl
|
||||||
|
|
||||||
from core.synchronousdeluge import rencode
|
from core.synchronousdeluge import rencode
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["DelugeTransfer"]
|
__all__ = ["DelugeTransfer"]
|
||||||
|
|
||||||
|
|
||||||
class DelugeTransfer(object):
|
class DelugeTransfer(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.sock = None
|
self.sock = None
|
||||||
|
@ -54,5 +54,3 @@ class DelugeTransfer(object):
|
||||||
buf = dobj.unused_data
|
buf = dobj.unused_data
|
||||||
|
|
||||||
yield message
|
yield message
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import re
|
||||||
from core import logger
|
from core import logger
|
||||||
from core.nzbToMediaUtil import makeDir
|
from core.nzbToMediaUtil import makeDir
|
||||||
|
|
||||||
|
|
||||||
def isVideoGood(videofile, status):
|
def isVideoGood(videofile, status):
|
||||||
fileNameExt = os.path.basename(videofile)
|
fileNameExt = os.path.basename(videofile)
|
||||||
fileName, fileExt = os.path.splitext(fileNameExt)
|
fileName, fileExt = os.path.splitext(fileNameExt)
|
||||||
|
@ -20,7 +21,7 @@ def isVideoGood(videofile, status):
|
||||||
disable = True
|
disable = True
|
||||||
else:
|
else:
|
||||||
test_details, res = getVideoDetails(core.TEST_FILE)
|
test_details, res = getVideoDetails(core.TEST_FILE)
|
||||||
if res !=0 or test_details.get("error"):
|
if res != 0 or test_details.get("error"):
|
||||||
disable = True
|
disable = True
|
||||||
logger.info("DISABLED: ffprobe failed to analyse test file. Stopping corruption check.", 'TRANSCODER')
|
logger.info("DISABLED: ffprobe failed to analyse test file. Stopping corruption check.", 'TRANSCODER')
|
||||||
if test_details.get("streams"):
|
if test_details.get("streams"):
|
||||||
|
@ -28,7 +29,8 @@ def isVideoGood(videofile, status):
|
||||||
audStreams = [item for item in test_details["streams"] if item["codec_type"] == "audio"]
|
audStreams = [item for item in test_details["streams"] if item["codec_type"] == "audio"]
|
||||||
if not (len(vidStreams) > 0 and len(audStreams) > 0):
|
if not (len(vidStreams) > 0 and len(audStreams) > 0):
|
||||||
disable = True
|
disable = True
|
||||||
logger.info("DISABLED: ffprobe failed to analyse streams from test file. Stopping corruption check.", 'TRANSCODER')
|
logger.info("DISABLED: ffprobe failed to analyse streams from test file. Stopping corruption check.",
|
||||||
|
'TRANSCODER')
|
||||||
if disable:
|
if disable:
|
||||||
if status: # if the download was "failed", assume bad. If it was successful, assume good.
|
if status: # if the download was "failed", assume bad. If it was successful, assume good.
|
||||||
return False
|
return False
|
||||||
|
@ -51,9 +53,11 @@ def isVideoGood(videofile, status):
|
||||||
logger.info("SUCCESS: [%s] has no corruption." % (fileNameExt), 'TRANSCODER')
|
logger.info("SUCCESS: [%s] has no corruption." % (fileNameExt), 'TRANSCODER')
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
logger.info("FAILED: [%s] has %s video streams and %s audio streams. Assume corruption." % (fileNameExt, str(len(videoStreams)), str(len(audioStreams))), 'TRANSCODER')
|
logger.info("FAILED: [%s] has %s video streams and %s audio streams. Assume corruption." % (
|
||||||
|
fileNameExt, str(len(videoStreams)), str(len(audioStreams))), 'TRANSCODER')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def zip_out(file, img, bitbucket):
|
def zip_out(file, img, bitbucket):
|
||||||
procin = None
|
procin = None
|
||||||
cmd = [core.SEVENZIP, '-so', 'e', img, file]
|
cmd = [core.SEVENZIP, '-so', 'e', img, file]
|
||||||
|
@ -63,6 +67,7 @@ def zip_out(file, img, bitbucket):
|
||||||
logger.error("Extracting [%s] has failed" % (file), 'TRANSCODER')
|
logger.error("Extracting [%s] has failed" % (file), 'TRANSCODER')
|
||||||
return procin
|
return procin
|
||||||
|
|
||||||
|
|
||||||
def getVideoDetails(videofile, img=None, bitbucket=None):
|
def getVideoDetails(videofile, img=None, bitbucket=None):
|
||||||
video_details = {}
|
video_details = {}
|
||||||
result = 1
|
result = 1
|
||||||
|
@ -76,7 +81,8 @@ def getVideoDetails(videofile, img=None, bitbucket=None):
|
||||||
try:
|
try:
|
||||||
if img:
|
if img:
|
||||||
videofile = '-'
|
videofile = '-'
|
||||||
command = [core.FFPROBE, '-v', 'quiet', print_format, 'json', '-show_format', '-show_streams', '-show_error', videofile]
|
command = [core.FFPROBE, '-v', 'quiet', print_format, 'json', '-show_format', '-show_streams', '-show_error',
|
||||||
|
videofile]
|
||||||
print_cmd(command)
|
print_cmd(command)
|
||||||
if img:
|
if img:
|
||||||
procin = zip_out(file, img, bitbucket)
|
procin = zip_out(file, img, bitbucket)
|
||||||
|
@ -87,7 +93,8 @@ def getVideoDetails(videofile, img=None, bitbucket=None):
|
||||||
out, err = proc.communicate()
|
out, err = proc.communicate()
|
||||||
result = proc.returncode
|
result = proc.returncode
|
||||||
video_details = json.loads(out)
|
video_details = json.loads(out)
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
if not video_details:
|
if not video_details:
|
||||||
try:
|
try:
|
||||||
command = [core.FFPROBE, '-v', 'quiet', print_format, 'json', '-show_format', '-show_streams', videofile]
|
command = [core.FFPROBE, '-v', 'quiet', print_format, 'json', '-show_format', '-show_streams', videofile]
|
||||||
|
@ -104,6 +111,7 @@ def getVideoDetails(videofile, img=None, bitbucket=None):
|
||||||
logger.error("Checking [%s] has failed" % (file), 'TRANSCODER')
|
logger.error("Checking [%s] has failed" % (file), 'TRANSCODER')
|
||||||
return video_details, result
|
return video_details, result
|
||||||
|
|
||||||
|
|
||||||
def buildCommands(file, newDir, movieName, bitbucket):
|
def buildCommands(file, newDir, movieName, bitbucket):
|
||||||
if isinstance(file, str):
|
if isinstance(file, str):
|
||||||
inputFile = file
|
inputFile = file
|
||||||
|
@ -119,8 +127,8 @@ def buildCommands(file, newDir, movieName, bitbucket):
|
||||||
name = ('%s.cd%s' % (movieName, check.groups()[0]))
|
name = ('%s.cd%s' % (movieName, check.groups()[0]))
|
||||||
elif core.CONCAT and re.match("(.+)[cC][dD][0-9]", name):
|
elif core.CONCAT and re.match("(.+)[cC][dD][0-9]", name):
|
||||||
name = re.sub("([\ \.\-\_\=\:]+[cC][dD][0-9])", "", name)
|
name = re.sub("([\ \.\-\_\=\:]+[cC][dD][0-9])", "", name)
|
||||||
if ext == core.VEXTENSION and newDir == dir: # we need to change the name to prevent overwriting itself.
|
if ext == core.VEXTENSION and newDir == dir: # we need to change the name to prevent overwriting itself.
|
||||||
core.VEXTENSION = '-transcoded' + core.VEXTENSION # adds '-transcoded.ext'
|
core.VEXTENSION = '-transcoded' + core.VEXTENSION # adds '-transcoded.ext'
|
||||||
else:
|
else:
|
||||||
img, data = file.iteritems().next()
|
img, data = file.iteritems().next()
|
||||||
name = data['name']
|
name = data['name']
|
||||||
|
@ -139,7 +147,8 @@ def buildCommands(file, newDir, movieName, bitbucket):
|
||||||
meta_cmd = []
|
meta_cmd = []
|
||||||
other_cmd = []
|
other_cmd = []
|
||||||
|
|
||||||
if not video_details or not video_details.get("streams"): # we couldn't read streams with ffprobe. Set defaults to try transcoding.
|
if not video_details or not video_details.get(
|
||||||
|
"streams"): # we couldn't read streams with ffprobe. Set defaults to try transcoding.
|
||||||
videoStreams = []
|
videoStreams = []
|
||||||
audioStreams = []
|
audioStreams = []
|
||||||
subStreams = []
|
subStreams = []
|
||||||
|
@ -166,12 +175,13 @@ def buildCommands(file, newDir, movieName, bitbucket):
|
||||||
|
|
||||||
if core.ACODEC:
|
if core.ACODEC:
|
||||||
audio_cmd.extend(['-c:a', core.ACODEC])
|
audio_cmd.extend(['-c:a', core.ACODEC])
|
||||||
if core.ACODEC in ['aac', 'dts']: # Allow users to use the experimental AAC codec that's built into recent versions of ffmpeg
|
if core.ACODEC in ['aac',
|
||||||
|
'dts']: # Allow users to use the experimental AAC codec that's built into recent versions of ffmpeg
|
||||||
audio_cmd.extend(['-strict', '-2'])
|
audio_cmd.extend(['-strict', '-2'])
|
||||||
else:
|
else:
|
||||||
audio_cmd.extend(['-c:a', 'copy'])
|
audio_cmd.extend(['-c:a', 'copy'])
|
||||||
if core.ACHANNELS:
|
if core.ACHANNELS:
|
||||||
audio_cmd.extend(['-ac', str(core.ACHANNELS)])
|
audio_cmd.extend(['-ac', str(core.ACHANNELS)])
|
||||||
if core.ABITRATE:
|
if core.ABITRATE:
|
||||||
audio_cmd.extend(['-b:a', str(core.ABITRATE)])
|
audio_cmd.extend(['-b:a', str(core.ABITRATE)])
|
||||||
if core.OUTPUTQUALITYPERCENT:
|
if core.OUTPUTQUALITYPERCENT:
|
||||||
|
@ -183,7 +193,7 @@ def buildCommands(file, newDir, movieName, bitbucket):
|
||||||
sub_cmd.extend(['-c:s', 'copy'])
|
sub_cmd.extend(['-c:s', 'copy'])
|
||||||
else: # http://en.wikibooks.org/wiki/FFMPEG_An_Intermediate_Guide/subtitle_options
|
else: # http://en.wikibooks.org/wiki/FFMPEG_An_Intermediate_Guide/subtitle_options
|
||||||
sub_cmd.extend(['-sn']) # Don't copy the subtitles over
|
sub_cmd.extend(['-sn']) # Don't copy the subtitles over
|
||||||
|
|
||||||
if core.OUTPUTFASTSTART:
|
if core.OUTPUTFASTSTART:
|
||||||
other_cmd.extend(['-movflags', '+faststart'])
|
other_cmd.extend(['-movflags', '+faststart'])
|
||||||
|
|
||||||
|
@ -192,23 +202,29 @@ def buildCommands(file, newDir, movieName, bitbucket):
|
||||||
audioStreams = [item for item in video_details["streams"] if item["codec_type"] == "audio"]
|
audioStreams = [item for item in video_details["streams"] if item["codec_type"] == "audio"]
|
||||||
subStreams = [item for item in video_details["streams"] if item["codec_type"] == "subtitle"]
|
subStreams = [item for item in video_details["streams"] if item["codec_type"] == "subtitle"]
|
||||||
if core.VEXTENSION not in ['.mkv', '.mpegts']:
|
if core.VEXTENSION not in ['.mkv', '.mpegts']:
|
||||||
subStreams = [item for item in video_details["streams"] if item["codec_type"] == "subtitle" and item["codec_name"] != "hdmv_pgs_subtitle" and item["codec_name"] != "pgssub"]
|
subStreams = [item for item in video_details["streams"] if
|
||||||
|
item["codec_type"] == "subtitle" and item["codec_name"] != "hdmv_pgs_subtitle" and item[
|
||||||
|
"codec_name"] != "pgssub"]
|
||||||
|
|
||||||
for video in videoStreams:
|
for video in videoStreams:
|
||||||
codec = video["codec_name"]
|
codec = video["codec_name"]
|
||||||
try:
|
try:
|
||||||
fr = video["avg_frame_rate"]
|
fr = video["avg_frame_rate"]
|
||||||
except: fr = 0
|
except:
|
||||||
|
fr = 0
|
||||||
try:
|
try:
|
||||||
width = video["width"]
|
width = video["width"]
|
||||||
except: width = 0
|
except:
|
||||||
|
width = 0
|
||||||
try:
|
try:
|
||||||
height = video["height"]
|
height = video["height"]
|
||||||
except: height = 0
|
except:
|
||||||
|
height = 0
|
||||||
scale = core.VRESOLUTION
|
scale = core.VRESOLUTION
|
||||||
try:
|
try:
|
||||||
framerate = float(fr.split('/')[0])/float(fr.split('/')[1])
|
framerate = float(fr.split('/')[0]) / float(fr.split('/')[1])
|
||||||
except: framerate = 0
|
except:
|
||||||
|
framerate = 0
|
||||||
if codec in core.VCODEC_ALLOW or not core.VCODEC:
|
if codec in core.VCODEC_ALLOW or not core.VCODEC:
|
||||||
video_cmd.extend(['-c:v', 'copy'])
|
video_cmd.extend(['-c:v', 'copy'])
|
||||||
else:
|
else:
|
||||||
|
@ -216,16 +232,16 @@ def buildCommands(file, newDir, movieName, bitbucket):
|
||||||
if core.VFRAMERATE and not (core.VFRAMERATE * 0.999 <= fr <= core.VFRAMERATE * 1.001):
|
if core.VFRAMERATE and not (core.VFRAMERATE * 0.999 <= fr <= core.VFRAMERATE * 1.001):
|
||||||
video_cmd.extend(['-r', str(core.VFRAMERATE)])
|
video_cmd.extend(['-r', str(core.VFRAMERATE)])
|
||||||
if scale:
|
if scale:
|
||||||
w_scale = width/float(scale.split(':')[0])
|
w_scale = width / float(scale.split(':')[0])
|
||||||
h_scale = height/float(scale.split(':')[1])
|
h_scale = height / float(scale.split(':')[1])
|
||||||
if w_scale > h_scale: # widescreen, Scale by width only.
|
if w_scale > h_scale: # widescreen, Scale by width only.
|
||||||
scale = scale.split(':')[0] + ":" + str(int((height/w_scale)/2)*2)
|
scale = scale.split(':')[0] + ":" + str(int((height / w_scale) / 2) * 2)
|
||||||
if w_scale > 1:
|
if w_scale > 1:
|
||||||
video_cmd.extend(['-vf', 'scale=' + scale])
|
video_cmd.extend(['-vf', 'scale=' + scale])
|
||||||
else: # lower or mathcing ratio, scale by height only.
|
else: # lower or mathcing ratio, scale by height only.
|
||||||
scale = str(int((width/h_scale)/2)*2) + ":" + scale.split(':')[1]
|
scale = str(int((width / h_scale) / 2) * 2) + ":" + scale.split(':')[1]
|
||||||
if h_scale > 1:
|
if h_scale > 1:
|
||||||
video_cmd.extend(['-vf', 'scale=' + scale])
|
video_cmd.extend(['-vf', 'scale=' + scale])
|
||||||
if core.VBITRATE:
|
if core.VBITRATE:
|
||||||
video_cmd.extend(['-b:v', str(core.VBITRATE)])
|
video_cmd.extend(['-b:v', str(core.VBITRATE)])
|
||||||
if core.VPRESET:
|
if core.VPRESET:
|
||||||
|
@ -238,7 +254,7 @@ def buildCommands(file, newDir, movieName, bitbucket):
|
||||||
if video_cmd[1] == 'copy' and any(i in video_cmd for i in no_copy):
|
if video_cmd[1] == 'copy' and any(i in video_cmd for i in no_copy):
|
||||||
video_cmd[1] = core.VCODEC
|
video_cmd[1] = core.VCODEC
|
||||||
if core.VCODEC == 'copy': # force copy. therefore ignore all other video transcoding.
|
if core.VCODEC == 'copy': # force copy. therefore ignore all other video transcoding.
|
||||||
video_cmd = ['-c:v', 'copy']
|
video_cmd = ['-c:v', 'copy']
|
||||||
map_cmd.extend(['-map', '0:' + str(video["index"])])
|
map_cmd.extend(['-map', '0:' + str(video["index"])])
|
||||||
break # Only one video needed
|
break # Only one video needed
|
||||||
|
|
||||||
|
@ -246,12 +262,12 @@ def buildCommands(file, newDir, movieName, bitbucket):
|
||||||
a_mapped = []
|
a_mapped = []
|
||||||
if audioStreams:
|
if audioStreams:
|
||||||
try:
|
try:
|
||||||
audio1 = [ item for item in audioStreams if item["tags"]["language"] == core.ALANGUAGE ]
|
audio1 = [item for item in audioStreams if item["tags"]["language"] == core.ALANGUAGE]
|
||||||
except: # no language tags. Assume only 1 language.
|
except: # no language tags. Assume only 1 language.
|
||||||
audio1 = audioStreams
|
audio1 = audioStreams
|
||||||
audio2 = [ item for item in audio1 if item["codec_name"] in core.ACODEC_ALLOW ]
|
audio2 = [item for item in audio1 if item["codec_name"] in core.ACODEC_ALLOW]
|
||||||
try:
|
try:
|
||||||
audio3 = [ item for item in audioStreams if item["tags"]["language"] != core.ALANGUAGE ]
|
audio3 = [item for item in audioStreams if item["tags"]["language"] != core.ALANGUAGE]
|
||||||
except:
|
except:
|
||||||
audio3 = []
|
audio3 = []
|
||||||
|
|
||||||
|
@ -259,21 +275,25 @@ def buildCommands(file, newDir, movieName, bitbucket):
|
||||||
map_cmd.extend(['-map', '0:' + str(audio2[0]["index"])])
|
map_cmd.extend(['-map', '0:' + str(audio2[0]["index"])])
|
||||||
a_mapped.extend([audio2[0]["index"]])
|
a_mapped.extend([audio2[0]["index"]])
|
||||||
try:
|
try:
|
||||||
bitrate = int(audio2[0]["bit_rate"])/1000
|
bitrate = int(audio2[0]["bit_rate"]) / 1000
|
||||||
except: bitrate = 0
|
except:
|
||||||
|
bitrate = 0
|
||||||
try:
|
try:
|
||||||
channels = int(audio2[0]["channels"])
|
channels = int(audio2[0]["channels"])
|
||||||
except: channels = 0
|
except:
|
||||||
|
channels = 0
|
||||||
audio_cmd.extend(['-c:a:' + str(used_audio), 'copy'])
|
audio_cmd.extend(['-c:a:' + str(used_audio), 'copy'])
|
||||||
elif audio1: # right language wrong codec.
|
elif audio1: # right language wrong codec.
|
||||||
map_cmd.extend(['-map', '0:' + str(audio1[0]["index"])])
|
map_cmd.extend(['-map', '0:' + str(audio1[0]["index"])])
|
||||||
a_mapped.extend([audio1[0]["index"]])
|
a_mapped.extend([audio1[0]["index"]])
|
||||||
try:
|
try:
|
||||||
bitrate = int(audio1[0]["bit_rate"])/1000
|
bitrate = int(audio1[0]["bit_rate"]) / 1000
|
||||||
except: bitrate = 0
|
except:
|
||||||
|
bitrate = 0
|
||||||
try:
|
try:
|
||||||
channels = int(audio1[0]["channels"])
|
channels = int(audio1[0]["channels"])
|
||||||
except: channels = 0
|
except:
|
||||||
|
channels = 0
|
||||||
if core.ACODEC:
|
if core.ACODEC:
|
||||||
audio_cmd.extend(['-c:a:' + str(used_audio), core.ACODEC])
|
audio_cmd.extend(['-c:a:' + str(used_audio), core.ACODEC])
|
||||||
else:
|
else:
|
||||||
|
@ -282,11 +302,13 @@ def buildCommands(file, newDir, movieName, bitbucket):
|
||||||
map_cmd.extend(['-map', '0:' + str(audio3[0]["index"])])
|
map_cmd.extend(['-map', '0:' + str(audio3[0]["index"])])
|
||||||
a_mapped.extend([audio3[0]["index"]])
|
a_mapped.extend([audio3[0]["index"]])
|
||||||
try:
|
try:
|
||||||
bitrate = int(audio3[0]["bit_rate"])/1000
|
bitrate = int(audio3[0]["bit_rate"]) / 1000
|
||||||
except: bitrate = 0
|
except:
|
||||||
|
bitrate = 0
|
||||||
try:
|
try:
|
||||||
channels = int(audio3[0]["channels"])
|
channels = int(audio3[0]["channels"])
|
||||||
except: channels = 0
|
except:
|
||||||
|
channels = 0
|
||||||
if core.ACODEC:
|
if core.ACODEC:
|
||||||
audio_cmd.extend(['-c:a:' + str(used_audio), core.ACODEC])
|
audio_cmd.extend(['-c:a:' + str(used_audio), core.ACODEC])
|
||||||
else:
|
else:
|
||||||
|
@ -309,26 +331,30 @@ def buildCommands(file, newDir, movieName, bitbucket):
|
||||||
|
|
||||||
if core.ACODEC2_ALLOW:
|
if core.ACODEC2_ALLOW:
|
||||||
used_audio += 1
|
used_audio += 1
|
||||||
audio4 = [ item for item in audio1 if item["codec_name"] in core.ACODEC2_ALLOW ]
|
audio4 = [item for item in audio1 if item["codec_name"] in core.ACODEC2_ALLOW]
|
||||||
if audio4: # right language and codec.
|
if audio4: # right language and codec.
|
||||||
map_cmd.extend(['-map', '0:' + str(audio4[0]["index"])])
|
map_cmd.extend(['-map', '0:' + str(audio4[0]["index"])])
|
||||||
a_mapped.extend([audio4[0]["index"]])
|
a_mapped.extend([audio4[0]["index"]])
|
||||||
try:
|
try:
|
||||||
bitrate = int(audio4[0]["bit_rate"])/1000
|
bitrate = int(audio4[0]["bit_rate"]) / 1000
|
||||||
except: bitrate = 0
|
except:
|
||||||
|
bitrate = 0
|
||||||
try:
|
try:
|
||||||
channels = int(audio4[0]["channels"])
|
channels = int(audio4[0]["channels"])
|
||||||
except: channels = 0
|
except:
|
||||||
|
channels = 0
|
||||||
audio_cmd2.extend(['-c:a:' + str(used_audio), 'copy'])
|
audio_cmd2.extend(['-c:a:' + str(used_audio), 'copy'])
|
||||||
elif audio1: # right language wrong codec.
|
elif audio1: # right language wrong codec.
|
||||||
map_cmd.extend(['-map', '0:' + str(audio1[0]["index"])])
|
map_cmd.extend(['-map', '0:' + str(audio1[0]["index"])])
|
||||||
a_mapped.extend([audio1[0]["index"]])
|
a_mapped.extend([audio1[0]["index"]])
|
||||||
try:
|
try:
|
||||||
bitrate = int(audio1[0]["bit_rate"])/1000
|
bitrate = int(audio1[0]["bit_rate"]) / 1000
|
||||||
except: bitrate = 0
|
except:
|
||||||
|
bitrate = 0
|
||||||
try:
|
try:
|
||||||
channels = int(audio1[0]["channels"])
|
channels = int(audio1[0]["channels"])
|
||||||
except: channels = 0
|
except:
|
||||||
|
channels = 0
|
||||||
if core.ACODEC2:
|
if core.ACODEC2:
|
||||||
audio_cmd2.extend(['-c:a:' + str(used_audio), core.ACODEC2])
|
audio_cmd2.extend(['-c:a:' + str(used_audio), core.ACODEC2])
|
||||||
else:
|
else:
|
||||||
|
@ -337,11 +363,13 @@ def buildCommands(file, newDir, movieName, bitbucket):
|
||||||
map_cmd.extend(['-map', '0:' + str(audio3[0]["index"])])
|
map_cmd.extend(['-map', '0:' + str(audio3[0]["index"])])
|
||||||
a_mapped.extend([audio3[0]["index"]])
|
a_mapped.extend([audio3[0]["index"]])
|
||||||
try:
|
try:
|
||||||
bitrate = int(audio3[0]["bit_rate"])/1000
|
bitrate = int(audio3[0]["bit_rate"]) / 1000
|
||||||
except: bitrate = 0
|
except:
|
||||||
|
bitrate = 0
|
||||||
try:
|
try:
|
||||||
channels = int(audio3[0]["channels"])
|
channels = int(audio3[0]["channels"])
|
||||||
except: channels = 0
|
except:
|
||||||
|
channels = 0
|
||||||
if core.ACODEC2:
|
if core.ACODEC2:
|
||||||
audio_cmd2.extend(['-c:a:' + str(used_audio), core.ACODEC2])
|
audio_cmd2.extend(['-c:a:' + str(used_audio), core.ACODEC2])
|
||||||
else:
|
else:
|
||||||
|
@ -371,11 +399,13 @@ def buildCommands(file, newDir, movieName, bitbucket):
|
||||||
map_cmd.extend(['-map', '0:' + str(audio["index"])])
|
map_cmd.extend(['-map', '0:' + str(audio["index"])])
|
||||||
audio_cmd3 = []
|
audio_cmd3 = []
|
||||||
try:
|
try:
|
||||||
bitrate = int(audio["bit_rate"])/1000
|
bitrate = int(audio["bit_rate"]) / 1000
|
||||||
except: bitrate = 0
|
except:
|
||||||
|
bitrate = 0
|
||||||
try:
|
try:
|
||||||
channels = int(audio["channels"])
|
channels = int(audio["channels"])
|
||||||
except: channels = 0
|
except:
|
||||||
|
channels = 0
|
||||||
if audio["codec_name"] in core.ACODEC3_ALLOW:
|
if audio["codec_name"] in core.ACODEC3_ALLOW:
|
||||||
audio_cmd3.extend(['-c:a:' + str(used_audio), 'copy'])
|
audio_cmd3.extend(['-c:a:' + str(used_audio), 'copy'])
|
||||||
else:
|
else:
|
||||||
|
@ -406,8 +436,9 @@ def buildCommands(file, newDir, movieName, bitbucket):
|
||||||
n = 0
|
n = 0
|
||||||
for lan in core.SLANGUAGES:
|
for lan in core.SLANGUAGES:
|
||||||
try:
|
try:
|
||||||
subs1 = [ item for item in subStreams if item["tags"]["language"] == lan ]
|
subs1 = [item for item in subStreams if item["tags"]["language"] == lan]
|
||||||
except: subs1 = []
|
except:
|
||||||
|
subs1 = []
|
||||||
if core.BURN and not subs1 and not burnt and os.path.isfile(file):
|
if core.BURN and not subs1 and not burnt and os.path.isfile(file):
|
||||||
for subfile in get_subs(file):
|
for subfile in get_subs(file):
|
||||||
if lan in os.path.split(subfile)[1]:
|
if lan in os.path.split(subfile)[1]:
|
||||||
|
@ -426,7 +457,7 @@ def buildCommands(file, newDir, movieName, bitbucket):
|
||||||
break
|
break
|
||||||
map_cmd.extend(['-map', '0:' + str(sub["index"])])
|
map_cmd.extend(['-map', '0:' + str(sub["index"])])
|
||||||
s_mapped.extend([sub["index"]])
|
s_mapped.extend([sub["index"]])
|
||||||
|
|
||||||
if core.SINCLUDE:
|
if core.SINCLUDE:
|
||||||
for sub in subStreams:
|
for sub in subStreams:
|
||||||
if not core.ALLOWSUBS:
|
if not core.ALLOWSUBS:
|
||||||
|
@ -434,7 +465,7 @@ def buildCommands(file, newDir, movieName, bitbucket):
|
||||||
if sub["index"] in s_mapped:
|
if sub["index"] in s_mapped:
|
||||||
continue
|
continue
|
||||||
map_cmd.extend(['-map', '0:' + str(sub["index"])])
|
map_cmd.extend(['-map', '0:' + str(sub["index"])])
|
||||||
s_mapped.extend([sub["index"]])
|
s_mapped.extend([sub["index"]])
|
||||||
|
|
||||||
if core.OUTPUTFASTSTART:
|
if core.OUTPUTFASTSTART:
|
||||||
other_cmd.extend(['-movflags', '+faststart'])
|
other_cmd.extend(['-movflags', '+faststart'])
|
||||||
|
@ -446,7 +477,7 @@ def buildCommands(file, newDir, movieName, bitbucket):
|
||||||
if core.GENERALOPTS:
|
if core.GENERALOPTS:
|
||||||
command.extend(core.GENERALOPTS)
|
command.extend(core.GENERALOPTS)
|
||||||
|
|
||||||
command.extend([ '-i', inputFile])
|
command.extend(['-i', inputFile])
|
||||||
|
|
||||||
if core.SEMBED and os.path.isfile(file):
|
if core.SEMBED and os.path.isfile(file):
|
||||||
for subfile in get_subs(file):
|
for subfile in get_subs(file):
|
||||||
|
@ -461,7 +492,7 @@ def buildCommands(file, newDir, movieName, bitbucket):
|
||||||
|
|
||||||
if not core.ALLOWSUBS or (not s_mapped and not n):
|
if not core.ALLOWSUBS or (not s_mapped and not n):
|
||||||
sub_cmd.extend(['-sn'])
|
sub_cmd.extend(['-sn'])
|
||||||
else:
|
else:
|
||||||
if core.SCODEC:
|
if core.SCODEC:
|
||||||
sub_cmd.extend(['-c:s', core.SCODEC])
|
sub_cmd.extend(['-c:s', core.SCODEC])
|
||||||
else:
|
else:
|
||||||
|
@ -478,6 +509,7 @@ def buildCommands(file, newDir, movieName, bitbucket):
|
||||||
command = core.NICENESS + command
|
command = core.NICENESS + command
|
||||||
return command
|
return command
|
||||||
|
|
||||||
|
|
||||||
def get_subs(file):
|
def get_subs(file):
|
||||||
filepaths = []
|
filepaths = []
|
||||||
subExt = ['.srt', '.sub', '.idx']
|
subExt = ['.srt', '.sub', '.idx']
|
||||||
|
@ -486,9 +518,10 @@ def get_subs(file):
|
||||||
for dirname, dirs, filenames in os.walk(dir):
|
for dirname, dirs, filenames in os.walk(dir):
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
filepaths.extend([os.path.join(dirname, filename)])
|
filepaths.extend([os.path.join(dirname, filename)])
|
||||||
subfiles = [ item for item in filepaths if os.path.splitext(item)[1] in subExt and name in item ]
|
subfiles = [item for item in filepaths if os.path.splitext(item)[1] in subExt and name in item]
|
||||||
return subfiles
|
return subfiles
|
||||||
|
|
||||||
|
|
||||||
def extract_subs(file, newfilePath, bitbucket):
|
def extract_subs(file, newfilePath, bitbucket):
|
||||||
video_details, result = getVideoDetails(file)
|
video_details, result = getVideoDetails(file)
|
||||||
if not video_details:
|
if not video_details:
|
||||||
|
@ -501,34 +534,39 @@ def extract_subs(file, newfilePath, bitbucket):
|
||||||
name = os.path.splitext(os.path.split(newfilePath)[1])[0]
|
name = os.path.splitext(os.path.split(newfilePath)[1])[0]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subStreams = [item for item in video_details["streams"] if item["codec_type"] == "subtitle" and item["tags"]["language"] in core.SLANGUAGES and item["codec_name"] != "hdmv_pgs_subtitle" and item["codec_name"] != "pgssub"]
|
subStreams = [item for item in video_details["streams"] if
|
||||||
|
item["codec_type"] == "subtitle" and item["tags"]["language"] in core.SLANGUAGES and item[
|
||||||
|
"codec_name"] != "hdmv_pgs_subtitle" and item["codec_name"] != "pgssub"]
|
||||||
except:
|
except:
|
||||||
subStreams = [item for item in video_details["streams"] if item["codec_type"] == "subtitle" and item["codec_name"] != "hdmv_pgs_subtitle" and item["codec_name"] != "pgssub"]
|
subStreams = [item for item in video_details["streams"] if
|
||||||
|
item["codec_type"] == "subtitle" and item["codec_name"] != "hdmv_pgs_subtitle" and item[
|
||||||
|
"codec_name"] != "pgssub"]
|
||||||
num = len(subStreams)
|
num = len(subStreams)
|
||||||
for n in range(num):
|
for n in range(num):
|
||||||
sub = subStreams[n]
|
sub = subStreams[n]
|
||||||
idx = sub["index"]
|
idx = sub["index"]
|
||||||
try:
|
try:
|
||||||
lan = sub["tags"]["language"]
|
lan = sub["tags"]["language"]
|
||||||
except:
|
except:
|
||||||
lan = "unk"
|
lan = "unk"
|
||||||
|
|
||||||
if num == 1:
|
if num == 1:
|
||||||
outputFile = os.path.join(subdir, "%s.srt" %(name))
|
outputFile = os.path.join(subdir, "%s.srt" % (name))
|
||||||
if os.path.isfile(outputFile):
|
if os.path.isfile(outputFile):
|
||||||
outputFile = os.path.join(subdir, "%s.%s.srt" %(name, n))
|
outputFile = os.path.join(subdir, "%s.%s.srt" % (name, n))
|
||||||
else:
|
else:
|
||||||
outputFile = os.path.join(subdir, "%s.%s.srt" %(name, lan))
|
outputFile = os.path.join(subdir, "%s.%s.srt" % (name, lan))
|
||||||
if os.path.isfile(outputFile):
|
if os.path.isfile(outputFile):
|
||||||
outputFile = os.path.join(subdir, "%s.%s.%s.srt" %(name, lan, n))
|
outputFile = os.path.join(subdir, "%s.%s.%s.srt" % (name, lan, n))
|
||||||
|
|
||||||
command = [core.FFMPEG, '-loglevel', 'warning', '-i', file, '-vn', '-an', '-codec:' + str(idx), 'srt', outputFile]
|
command = [core.FFMPEG, '-loglevel', 'warning', '-i', file, '-vn', '-an', '-codec:' + str(idx), 'srt',
|
||||||
|
outputFile]
|
||||||
if platform.system() != 'Windows':
|
if platform.system() != 'Windows':
|
||||||
command = core.NICENESS + command
|
command = core.NICENESS + command
|
||||||
|
|
||||||
logger.info("Extracting %s subtitle from: %s" % (lan, file))
|
logger.info("Extracting %s subtitle from: %s" % (lan, file))
|
||||||
print_cmd(command)
|
print_cmd(command)
|
||||||
result = 1 # set result to failed in case call fails.
|
result = 1 # set result to failed in case call fails.
|
||||||
try:
|
try:
|
||||||
proc = subprocess.Popen(command, stdout=bitbucket, stderr=bitbucket)
|
proc = subprocess.Popen(command, stdout=bitbucket, stderr=bitbucket)
|
||||||
proc.communicate()
|
proc.communicate()
|
||||||
|
@ -539,11 +577,13 @@ def extract_subs(file, newfilePath, bitbucket):
|
||||||
if result == 0:
|
if result == 0:
|
||||||
try:
|
try:
|
||||||
shutil.copymode(file, outputFile)
|
shutil.copymode(file, outputFile)
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
logger.info("Extracting %s subtitle from %s has succeeded" % (lan, file))
|
logger.info("Extracting %s subtitle from %s has succeeded" % (lan, file))
|
||||||
else:
|
else:
|
||||||
logger.error("Extracting subtitles has failed")
|
logger.error("Extracting subtitles has failed")
|
||||||
|
|
||||||
|
|
||||||
def processList(List, newDir, bitbucket):
|
def processList(List, newDir, bitbucket):
|
||||||
remList = []
|
remList = []
|
||||||
newList = []
|
newList = []
|
||||||
|
@ -562,7 +602,7 @@ def processList(List, newDir, bitbucket):
|
||||||
logger.debug("Found VIDEO_TS image file: %s" % (item), "TRANSCODER")
|
logger.debug("Found VIDEO_TS image file: %s" % (item), "TRANSCODER")
|
||||||
if not vtsPath:
|
if not vtsPath:
|
||||||
try:
|
try:
|
||||||
vtsPath = re.match("(.+VIDEO_TS)",item).groups()[0]
|
vtsPath = re.match("(.+VIDEO_TS)", item).groups()[0]
|
||||||
except:
|
except:
|
||||||
vtsPath = os.path.split(item)[0]
|
vtsPath = os.path.split(item)[0]
|
||||||
remList.append(item)
|
remList.append(item)
|
||||||
|
@ -571,7 +611,8 @@ def processList(List, newDir, bitbucket):
|
||||||
elif core.CONCAT and re.match(".+[cC][dD][0-9].", item):
|
elif core.CONCAT and re.match(".+[cC][dD][0-9].", item):
|
||||||
remList.append(item)
|
remList.append(item)
|
||||||
combine.append(item)
|
combine.append(item)
|
||||||
else: continue
|
else:
|
||||||
|
continue
|
||||||
if vtsPath:
|
if vtsPath:
|
||||||
newList.extend(combineVTS(vtsPath))
|
newList.extend(combineVTS(vtsPath))
|
||||||
if combine:
|
if combine:
|
||||||
|
@ -589,7 +630,8 @@ def processList(List, newDir, bitbucket):
|
||||||
newList = []
|
newList = []
|
||||||
remList = []
|
remList = []
|
||||||
logger.error("Failed extracting .vob files from disk image. Stopping transcoding.", "TRANSCODER")
|
logger.error("Failed extracting .vob files from disk image. Stopping transcoding.", "TRANSCODER")
|
||||||
return List, remList, newList, success
|
return List, remList, newList, success
|
||||||
|
|
||||||
|
|
||||||
def ripISO(item, newDir, bitbucket):
|
def ripISO(item, newDir, bitbucket):
|
||||||
newFiles = []
|
newFiles = []
|
||||||
|
@ -606,13 +648,14 @@ def ripISO(item, newDir, bitbucket):
|
||||||
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=bitbucket)
|
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=bitbucket)
|
||||||
out, err = proc.communicate()
|
out, err = proc.communicate()
|
||||||
result = proc.returncode
|
result = proc.returncode
|
||||||
fileList = [ re.match(".+(VIDEO_TS[\\\/]VTS_[0-9][0-9]_[0-9].[Vv][Oo][Bb])", line).groups()[0] for line in out.splitlines() if re.match(".+VIDEO_TS[\\\/]VTS_[0-9][0-9]_[0-9].[Vv][Oo][Bb]", line) ]
|
fileList = [re.match(".+(VIDEO_TS[\\\/]VTS_[0-9][0-9]_[0-9].[Vv][Oo][Bb])", line).groups()[0] for line in
|
||||||
|
out.splitlines() if re.match(".+VIDEO_TS[\\\/]VTS_[0-9][0-9]_[0-9].[Vv][Oo][Bb]", line)]
|
||||||
combined = []
|
combined = []
|
||||||
for n in range(99):
|
for n in range(99):
|
||||||
concat = []
|
concat = []
|
||||||
m = 1
|
m = 1
|
||||||
while True:
|
while True:
|
||||||
vtsName = 'VIDEO_TS%sVTS_%02d_%d.VOB' % (os.sep, n+1, m)
|
vtsName = 'VIDEO_TS%sVTS_%02d_%d.VOB' % (os.sep, n + 1, m)
|
||||||
if vtsName in fileList:
|
if vtsName in fileList:
|
||||||
concat.append(vtsName)
|
concat.append(vtsName)
|
||||||
m += 1
|
m += 1
|
||||||
|
@ -623,11 +666,11 @@ def ripISO(item, newDir, bitbucket):
|
||||||
if core.CONCAT:
|
if core.CONCAT:
|
||||||
combined.extend(concat)
|
combined.extend(concat)
|
||||||
continue
|
continue
|
||||||
name = '%s.cd%s' % (os.path.splitext(os.path.split(item)[1])[0] ,str(n+1))
|
name = '%s.cd%s' % (os.path.splitext(os.path.split(item)[1])[0], str(n + 1))
|
||||||
newFiles.append({item: {'name': name , 'files': concat}})
|
newFiles.append({item: {'name': name, 'files': concat}})
|
||||||
if core.CONCAT:
|
if core.CONCAT:
|
||||||
name = os.path.splitext(os.path.split(item)[1])[0]
|
name = os.path.splitext(os.path.split(item)[1])[0]
|
||||||
newFiles.append({item: {'name': name , 'files': combined}})
|
newFiles.append({item: {'name': name, 'files': combined}})
|
||||||
if not newFiles:
|
if not newFiles:
|
||||||
logger.error("No VIDEO_TS folder found in image file %s" % (item), "TRANSCODER")
|
logger.error("No VIDEO_TS folder found in image file %s" % (item), "TRANSCODER")
|
||||||
newFiles = [failure_dir]
|
newFiles = [failure_dir]
|
||||||
|
@ -636,6 +679,7 @@ def ripISO(item, newDir, bitbucket):
|
||||||
newFiles = [failure_dir]
|
newFiles = [failure_dir]
|
||||||
return newFiles
|
return newFiles
|
||||||
|
|
||||||
|
|
||||||
def combineVTS(vtsPath):
|
def combineVTS(vtsPath):
|
||||||
newFiles = []
|
newFiles = []
|
||||||
combined = ''
|
combined = ''
|
||||||
|
@ -643,7 +687,7 @@ def combineVTS(vtsPath):
|
||||||
concat = ''
|
concat = ''
|
||||||
m = 1
|
m = 1
|
||||||
while True:
|
while True:
|
||||||
vtsName = 'VTS_%02d_%d.VOB' % (n+1, m)
|
vtsName = 'VTS_%02d_%d.VOB' % (n + 1, m)
|
||||||
if os.path.isfile(os.path.join(vtsPath, vtsName)):
|
if os.path.isfile(os.path.join(vtsPath, vtsName)):
|
||||||
concat = concat + os.path.join(vtsPath, vtsName) + '|'
|
concat = concat + os.path.join(vtsPath, vtsName) + '|'
|
||||||
m += 1
|
m += 1
|
||||||
|
@ -659,12 +703,14 @@ def combineVTS(vtsPath):
|
||||||
newFiles.append('concat:%s' % combined[:-1])
|
newFiles.append('concat:%s' % combined[:-1])
|
||||||
return newFiles
|
return newFiles
|
||||||
|
|
||||||
|
|
||||||
def combineCD(combine):
|
def combineCD(combine):
|
||||||
newFiles = []
|
newFiles = []
|
||||||
for item in set([ re.match("(.+)[cC][dD][0-9].",item).groups()[0] for item in combine ]):
|
for item in set([re.match("(.+)[cC][dD][0-9].", item).groups()[0] for item in combine]):
|
||||||
concat = ''
|
concat = ''
|
||||||
for n in range(99):
|
for n in range(99):
|
||||||
files = [ file for file in combine if n+1 == int(re.match(".+[cC][dD]([0-9]+).",file).groups()[0]) and item in file ]
|
files = [file for file in combine if
|
||||||
|
n + 1 == int(re.match(".+[cC][dD]([0-9]+).", file).groups()[0]) and item in file]
|
||||||
if files:
|
if files:
|
||||||
concat = concat + files[0] + '|'
|
concat = concat + files[0] + '|'
|
||||||
else:
|
else:
|
||||||
|
@ -673,17 +719,19 @@ def combineCD(combine):
|
||||||
newFiles.append('concat:%s' % concat[:-1])
|
newFiles.append('concat:%s' % concat[:-1])
|
||||||
return newFiles
|
return newFiles
|
||||||
|
|
||||||
|
|
||||||
def print_cmd(command):
|
def print_cmd(command):
|
||||||
cmd = ""
|
cmd = ""
|
||||||
for item in command:
|
for item in command:
|
||||||
cmd = cmd + " " + str(item)
|
cmd = cmd + " " + str(item)
|
||||||
logger.debug("calling command:%s" % (cmd))
|
logger.debug("calling command:%s" % (cmd))
|
||||||
|
|
||||||
|
|
||||||
def Transcode_directory(dirName):
|
def Transcode_directory(dirName):
|
||||||
if not core.FFMPEG:
|
if not core.FFMPEG:
|
||||||
return 1, dirName
|
return 1, dirName
|
||||||
logger.info("Checking for files to be transcoded")
|
logger.info("Checking for files to be transcoded")
|
||||||
final_result = 0 # initialize as successful
|
final_result = 0 # initialize as successful
|
||||||
if core.OUTPUTVIDEOPATH:
|
if core.OUTPUTVIDEOPATH:
|
||||||
newDir = core.OUTPUTVIDEOPATH
|
newDir = core.OUTPUTVIDEOPATH
|
||||||
makeDir(newDir)
|
makeDir(newDir)
|
||||||
|
@ -713,17 +761,17 @@ def Transcode_directory(dirName):
|
||||||
if core.SEXTRACT and isinstance(file, str):
|
if core.SEXTRACT and isinstance(file, str):
|
||||||
extract_subs(file, newfilePath, bitbucket)
|
extract_subs(file, newfilePath, bitbucket)
|
||||||
|
|
||||||
try: # Try to remove the file that we're transcoding to just in case. (ffmpeg will return an error if it already exists for some reason)
|
try: # Try to remove the file that we're transcoding to just in case. (ffmpeg will return an error if it already exists for some reason)
|
||||||
os.remove(newfilePath)
|
os.remove(newfilePath)
|
||||||
except OSError, e:
|
except OSError, e:
|
||||||
if e.errno != errno.ENOENT: # Ignore the error if it's just telling us that the file doesn't exist
|
if e.errno != errno.ENOENT: # Ignore the error if it's just telling us that the file doesn't exist
|
||||||
logger.debug("Error when removing transcoding target: %s" % (e))
|
logger.debug("Error when removing transcoding target: %s" % (e))
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
logger.debug("Error when removing transcoding target: %s" % (e))
|
logger.debug("Error when removing transcoding target: %s" % (e))
|
||||||
|
|
||||||
logger.info("Transcoding video: %s" % (newfilePath))
|
logger.info("Transcoding video: %s" % (newfilePath))
|
||||||
print_cmd(command)
|
print_cmd(command)
|
||||||
result = 1 # set result to failed in case call fails.
|
result = 1 # set result to failed in case call fails.
|
||||||
try:
|
try:
|
||||||
if isinstance(file, str):
|
if isinstance(file, str):
|
||||||
proc = subprocess.Popen(command, stdout=bitbucket, stderr=bitbucket)
|
proc = subprocess.Popen(command, stdout=bitbucket, stderr=bitbucket)
|
||||||
|
@ -752,12 +800,14 @@ def Transcode_directory(dirName):
|
||||||
if result == 0:
|
if result == 0:
|
||||||
try:
|
try:
|
||||||
shutil.copymode(file, newfilePath)
|
shutil.copymode(file, newfilePath)
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
logger.info("Transcoding of video to %s succeeded" % (newfilePath))
|
logger.info("Transcoding of video to %s succeeded" % (newfilePath))
|
||||||
if os.path.isfile(newfilePath) and (file in newList or not core.DUPLICATE):
|
if os.path.isfile(newfilePath) and (file in newList or not core.DUPLICATE):
|
||||||
try:
|
try:
|
||||||
os.unlink(file)
|
os.unlink(file)
|
||||||
except: pass
|
except:
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
logger.error("Transcoding of video to %s failed with result %s" % (newfilePath, str(result)))
|
logger.error("Transcoding of video to %s failed with result %s" % (newfilePath, str(result)))
|
||||||
# this will be 0 (successful) it all are successful, else will return a positive integer for failure.
|
# this will be 0 (successful) it all are successful, else will return a positive integer for failure.
|
||||||
|
@ -766,8 +816,9 @@ def Transcode_directory(dirName):
|
||||||
for file in remList:
|
for file in remList:
|
||||||
try:
|
try:
|
||||||
os.unlink(file)
|
os.unlink(file)
|
||||||
except: pass
|
except:
|
||||||
if not os.listdir(newDir): #this is an empty directory and we didn't transcode into it.
|
pass
|
||||||
|
if not os.listdir(newDir): # this is an empty directory and we didn't transcode into it.
|
||||||
os.rmdir(newDir)
|
os.rmdir(newDir)
|
||||||
newDir = dirName
|
newDir = dirName
|
||||||
if not core.PROCESSOUTPUT and core.DUPLICATE: # We postprocess the original files to CP/SB
|
if not core.PROCESSOUTPUT and core.DUPLICATE: # We postprocess the original files to CP/SB
|
||||||
|
|
|
@ -10,9 +10,9 @@ from core.transmissionrpc.session import Session
|
||||||
from core.transmissionrpc.client import Client
|
from core.transmissionrpc.client import Client
|
||||||
from core.transmissionrpc.utils import add_stdout_logger, add_file_logger
|
from core.transmissionrpc.utils import add_stdout_logger, add_file_logger
|
||||||
|
|
||||||
__author__ = 'Erik Svensson <erik.public@gmail.com>'
|
__author__ = 'Erik Svensson <erik.public@gmail.com>'
|
||||||
__version_major__ = 0
|
__version_major__ = 0
|
||||||
__version_minor__ = 11
|
__version_minor__ = 11
|
||||||
__version__ = '{0}.{1}'.format(__version_major__, __version_minor__)
|
__version__ = '{0}.{1}'.format(__version_major__, __version_minor__)
|
||||||
__copyright__ = 'Copyright (c) 2008-2013 Erik Svensson'
|
__copyright__ = 'Copyright (c) 2008-2013 Erik Svensson'
|
||||||
__license__ = 'MIT'
|
__license__ = 'MIT'
|
||||||
|
|
|
@ -18,7 +18,6 @@ from core.transmissionrpc.torrent import Torrent
|
||||||
from core.transmissionrpc.session import Session
|
from core.transmissionrpc.session import Session
|
||||||
from six import PY3, integer_types, string_types, iteritems
|
from six import PY3, integer_types, string_types, iteritems
|
||||||
|
|
||||||
|
|
||||||
if PY3:
|
if PY3:
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
from urllib.request import urlopen
|
from urllib.request import urlopen
|
||||||
|
@ -26,6 +25,7 @@ else:
|
||||||
from urlparse import urlparse
|
from urlparse import urlparse
|
||||||
from urllib2 import urlopen
|
from urllib2 import urlopen
|
||||||
|
|
||||||
|
|
||||||
def debug_httperror(error):
|
def debug_httperror(error):
|
||||||
"""
|
"""
|
||||||
Log the Transmission RPC HTTP error.
|
Log the Transmission RPC HTTP error.
|
||||||
|
@ -49,6 +49,7 @@ def debug_httperror(error):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def parse_torrent_id(arg):
|
def parse_torrent_id(arg):
|
||||||
"""Parse an torrent id or torrent hashString."""
|
"""Parse an torrent id or torrent hashString."""
|
||||||
torrent_id = None
|
torrent_id = None
|
||||||
|
@ -62,7 +63,7 @@ def parse_torrent_id(arg):
|
||||||
elif isinstance(arg, string_types):
|
elif isinstance(arg, string_types):
|
||||||
try:
|
try:
|
||||||
torrent_id = int(arg)
|
torrent_id = int(arg)
|
||||||
if torrent_id >= 2**31:
|
if torrent_id >= 2 ** 31:
|
||||||
torrent_id = None
|
torrent_id = None
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
pass
|
pass
|
||||||
|
@ -75,6 +76,7 @@ def parse_torrent_id(arg):
|
||||||
pass
|
pass
|
||||||
return torrent_id
|
return torrent_id
|
||||||
|
|
||||||
|
|
||||||
def parse_torrent_ids(args):
|
def parse_torrent_ids(args):
|
||||||
"""
|
"""
|
||||||
Take things and make them valid torrent identifiers
|
Take things and make them valid torrent identifiers
|
||||||
|
@ -115,6 +117,7 @@ def parse_torrent_ids(args):
|
||||||
ids = [torrent_id]
|
ids = [torrent_id]
|
||||||
return ids
|
return ids
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Torrent ids
|
Torrent ids
|
||||||
|
|
||||||
|
@ -129,12 +132,14 @@ possible to provide a argument called ``timeout``. Timeout is only effective
|
||||||
when using Python 2.6 or later and the default timeout is 30 seconds.
|
when using Python 2.6 or later and the default timeout is 30 seconds.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Client(object):
|
class Client(object):
|
||||||
"""
|
"""
|
||||||
Client is the class handling the Transmission JSON-RPC client protocol.
|
Client is the class handling the Transmission JSON-RPC client protocol.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, address='localhost', port=DEFAULT_PORT, user=None, password=None, http_handler=None, timeout=None):
|
def __init__(self, address='localhost', port=DEFAULT_PORT, user=None, password=None, http_handler=None,
|
||||||
|
timeout=None):
|
||||||
if isinstance(timeout, (integer_types, float)):
|
if isinstance(timeout, (integer_types, float)):
|
||||||
self._query_timeout = float(timeout)
|
self._query_timeout = float(timeout)
|
||||||
else:
|
else:
|
||||||
|
@ -204,7 +209,8 @@ class Client(object):
|
||||||
if timeout is None:
|
if timeout is None:
|
||||||
timeout = self._query_timeout
|
timeout = self._query_timeout
|
||||||
while True:
|
while True:
|
||||||
LOGGER.debug(json.dumps({'url': self.url, 'headers': headers, 'query': query, 'timeout': timeout}, indent=2))
|
LOGGER.debug(
|
||||||
|
json.dumps({'url': self.url, 'headers': headers, 'query': query, 'timeout': timeout}, indent=2))
|
||||||
try:
|
try:
|
||||||
result = self.http_handler.request(self.url, query, headers, timeout)
|
result = self.http_handler.request(self.url, query, headers, timeout)
|
||||||
break
|
break
|
||||||
|
@ -244,8 +250,7 @@ class Client(object):
|
||||||
elif require_ids:
|
elif require_ids:
|
||||||
raise ValueError('request require ids')
|
raise ValueError('request require ids')
|
||||||
|
|
||||||
query = json.dumps({'tag': self._sequence, 'method': method
|
query = json.dumps({'tag': self._sequence, 'method': method, 'arguments': arguments})
|
||||||
, 'arguments': arguments})
|
|
||||||
self._sequence += 1
|
self._sequence += 1
|
||||||
start = time.time()
|
start = time.time()
|
||||||
http_data = self._http_query(query, timeout)
|
http_data = self._http_query(query, timeout)
|
||||||
|
@ -348,7 +353,7 @@ class Client(object):
|
||||||
"""
|
"""
|
||||||
if self.rpc_version < version:
|
if self.rpc_version < version:
|
||||||
LOGGER.warning('Using feature not supported by server. RPC version for server %d, feature introduced in %d.'
|
LOGGER.warning('Using feature not supported by server. RPC version for server %d, feature introduced in %d.'
|
||||||
% (self.rpc_version, version))
|
% (self.rpc_version, version))
|
||||||
|
|
||||||
def add_torrent(self, torrent, timeout=None, **kwargs):
|
def add_torrent(self, torrent, timeout=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -476,7 +481,7 @@ class Client(object):
|
||||||
"""
|
"""
|
||||||
self._rpc_version_warning(3)
|
self._rpc_version_warning(3)
|
||||||
self._request('torrent-remove',
|
self._request('torrent-remove',
|
||||||
{'delete-local-data':rpc_bool(delete_data)}, ids, True, timeout=timeout)
|
{'delete-local-data': rpc_bool(delete_data)}, ids, True, timeout=timeout)
|
||||||
|
|
||||||
def remove(self, ids, delete_data=False, timeout=None):
|
def remove(self, ids, delete_data=False, timeout=None):
|
||||||
"""
|
"""
|
||||||
|
@ -606,34 +611,34 @@ class Client(object):
|
||||||
the new methods. list returns a dictionary indexed by torrent id.
|
the new methods. list returns a dictionary indexed by torrent id.
|
||||||
"""
|
"""
|
||||||
warnings.warn('list has been deprecated, please use get_torrent or get_torrents instead.', DeprecationWarning)
|
warnings.warn('list has been deprecated, please use get_torrent or get_torrents instead.', DeprecationWarning)
|
||||||
fields = ['id', 'hashString', 'name', 'sizeWhenDone', 'leftUntilDone'
|
fields = ['id', 'hashString', 'name', 'sizeWhenDone', 'leftUntilDone',
|
||||||
, 'eta', 'status', 'rateUpload', 'rateDownload', 'uploadedEver'
|
'eta', 'status', 'rateUpload', 'rateDownload', 'uploadedEver',
|
||||||
, 'downloadedEver', 'uploadRatio', 'queuePosition']
|
'downloadedEver', 'uploadRatio', 'queuePosition']
|
||||||
return self._request('torrent-get', {'fields': fields}, timeout=timeout)
|
return self._request('torrent-get', {'fields': fields}, timeout=timeout)
|
||||||
|
|
||||||
def get_files(self, ids=None, timeout=None):
|
def get_files(self, ids=None, timeout=None):
|
||||||
"""
|
"""
|
||||||
Get list of files for provided torrent id(s). If ids is empty,
|
Get list of files for provided torrent id(s). If ids is empty,
|
||||||
information for all torrents are fetched. This function returns a dictionary
|
information for all torrents are fetched. This function returns a dictionary
|
||||||
for each requested torrent id holding the information about the files.
|
for each requested torrent id holding the information about the files.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
{
|
{
|
||||||
<torrent id>: {
|
<torrent id>: {
|
||||||
<file id>: {
|
<file id>: {
|
||||||
'name': <file name>,
|
'name': <file name>,
|
||||||
'size': <file size in bytes>,
|
'size': <file size in bytes>,
|
||||||
'completed': <bytes completed>,
|
'completed': <bytes completed>,
|
||||||
'priority': <priority ('high'|'normal'|'low')>,
|
'priority': <priority ('high'|'normal'|'low')>,
|
||||||
'selected': <selected for download (True|False)>
|
'selected': <selected for download (True|False)>
|
||||||
}
|
}
|
||||||
|
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
|
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
fields = ['id', 'name', 'hashString', 'files', 'priorities', 'wanted']
|
fields = ['id', 'name', 'hashString', 'files', 'priorities', 'wanted']
|
||||||
request_result = self._request('torrent-get', {'fields': fields}, ids, timeout=timeout)
|
request_result = self._request('torrent-get', {'fields': fields}, ids, timeout=timeout)
|
||||||
|
@ -645,22 +650,22 @@ class Client(object):
|
||||||
def set_files(self, items, timeout=None):
|
def set_files(self, items, timeout=None):
|
||||||
"""
|
"""
|
||||||
Set file properties. Takes a dictionary with similar contents as the result
|
Set file properties. Takes a dictionary with similar contents as the result
|
||||||
of `get_files`.
|
of `get_files`.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
{
|
{
|
||||||
<torrent id>: {
|
<torrent id>: {
|
||||||
<file id>: {
|
<file id>: {
|
||||||
'priority': <priority ('high'|'normal'|'low')>,
|
'priority': <priority ('high'|'normal'|'low')>,
|
||||||
'selected': <selected for download (True|False)>
|
'selected': <selected for download (True|False)>
|
||||||
}
|
}
|
||||||
|
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
|
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
if not isinstance(items, dict):
|
if not isinstance(items, dict):
|
||||||
raise ValueError('Invalid file description')
|
raise ValueError('Invalid file description')
|
||||||
|
@ -703,8 +708,8 @@ class Client(object):
|
||||||
|
|
||||||
def change_torrent(self, ids, timeout=None, **kwargs):
|
def change_torrent(self, ids, timeout=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Change torrent parameters for the torrent(s) with the supplied id's. The
|
Change torrent parameters for the torrent(s) with the supplied id's. The
|
||||||
parameters are:
|
parameters are:
|
||||||
|
|
||||||
============================ ===== =============== =======================================================================================
|
============================ ===== =============== =======================================================================================
|
||||||
Argument RPC Replaced by Description
|
Argument RPC Replaced by Description
|
||||||
|
@ -736,13 +741,13 @@ class Client(object):
|
||||||
``uploadLimited`` 5 - Enable upload speed limiter.
|
``uploadLimited`` 5 - Enable upload speed limiter.
|
||||||
============================ ===== =============== =======================================================================================
|
============================ ===== =============== =======================================================================================
|
||||||
|
|
||||||
.. NOTE::
|
.. NOTE::
|
||||||
transmissionrpc will try to automatically fix argument errors.
|
transmissionrpc will try to automatically fix argument errors.
|
||||||
"""
|
"""
|
||||||
args = {}
|
args = {}
|
||||||
for key, value in iteritems(kwargs):
|
for key, value in iteritems(kwargs):
|
||||||
argument = make_rpc_name(key)
|
argument = make_rpc_name(key)
|
||||||
(arg, val) = argument_value_convert('torrent-set' , argument, value, self.rpc_version)
|
(arg, val) = argument_value_convert('torrent-set', argument, value, self.rpc_version)
|
||||||
args[arg] = val
|
args[arg] = val
|
||||||
|
|
||||||
if len(args) > 0:
|
if len(args) > 0:
|
||||||
|
@ -814,7 +819,7 @@ class Client(object):
|
||||||
"""Move transfer to the bottom of the queue."""
|
"""Move transfer to the bottom of the queue."""
|
||||||
self._rpc_version_warning(14)
|
self._rpc_version_warning(14)
|
||||||
self._request('queue-move-bottom', ids=ids, require_ids=True, timeout=timeout)
|
self._request('queue-move-bottom', ids=ids, require_ids=True, timeout=timeout)
|
||||||
|
|
||||||
def queue_up(self, ids, timeout=None):
|
def queue_up(self, ids, timeout=None):
|
||||||
"""Move transfer up in the queue."""
|
"""Move transfer up in the queue."""
|
||||||
self._rpc_version_warning(14)
|
self._rpc_version_warning(14)
|
||||||
|
@ -888,14 +893,14 @@ class Client(object):
|
||||||
================================ ===== ================= ==========================================================================================================================
|
================================ ===== ================= ==========================================================================================================================
|
||||||
|
|
||||||
.. NOTE::
|
.. NOTE::
|
||||||
transmissionrpc will try to automatically fix argument errors.
|
transmissionrpc will try to automatically fix argument errors.
|
||||||
"""
|
"""
|
||||||
args = {}
|
args = {}
|
||||||
for key, value in iteritems(kwargs):
|
for key, value in iteritems(kwargs):
|
||||||
if key == 'encryption' and value not in ['required', 'preferred', 'tolerated']:
|
if key == 'encryption' and value not in ['required', 'preferred', 'tolerated']:
|
||||||
raise ValueError('Invalid encryption value')
|
raise ValueError('Invalid encryption value')
|
||||||
argument = make_rpc_name(key)
|
argument = make_rpc_name(key)
|
||||||
(arg, val) = argument_value_convert('session-set' , argument, value, self.rpc_version)
|
(arg, val) = argument_value_convert('session-set', argument, value, self.rpc_version)
|
||||||
args[arg] = val
|
args[arg] = val
|
||||||
if len(args) > 0:
|
if len(args) > 0:
|
||||||
self._request('session-set', args, timeout=timeout)
|
self._request('session-set', args, timeout=timeout)
|
||||||
|
|
|
@ -6,10 +6,10 @@ import logging
|
||||||
|
|
||||||
from core.transmissionrpc.six import iteritems
|
from core.transmissionrpc.six import iteritems
|
||||||
|
|
||||||
|
|
||||||
LOGGER = logging.getLogger('transmissionrpc')
|
LOGGER = logging.getLogger('transmissionrpc')
|
||||||
LOGGER.setLevel(logging.ERROR)
|
LOGGER.setLevel(logging.ERROR)
|
||||||
|
|
||||||
|
|
||||||
def mirror_dict(source):
|
def mirror_dict(source):
|
||||||
"""
|
"""
|
||||||
Creates a dictionary with all values as keys and all keys as values.
|
Creates a dictionary with all values as keys and all keys as values.
|
||||||
|
@ -17,38 +17,39 @@ def mirror_dict(source):
|
||||||
source.update(dict((value, key) for key, value in iteritems(source)))
|
source.update(dict((value, key) for key, value in iteritems(source)))
|
||||||
return source
|
return source
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_PORT = 9091
|
DEFAULT_PORT = 9091
|
||||||
|
|
||||||
DEFAULT_TIMEOUT = 30.0
|
DEFAULT_TIMEOUT = 30.0
|
||||||
|
|
||||||
TR_PRI_LOW = -1
|
TR_PRI_LOW = -1
|
||||||
TR_PRI_NORMAL = 0
|
TR_PRI_NORMAL = 0
|
||||||
TR_PRI_HIGH = 1
|
TR_PRI_HIGH = 1
|
||||||
|
|
||||||
PRIORITY = mirror_dict({
|
PRIORITY = mirror_dict({
|
||||||
'low' : TR_PRI_LOW,
|
'low': TR_PRI_LOW,
|
||||||
'normal' : TR_PRI_NORMAL,
|
'normal': TR_PRI_NORMAL,
|
||||||
'high' : TR_PRI_HIGH
|
'high': TR_PRI_HIGH
|
||||||
})
|
})
|
||||||
|
|
||||||
TR_RATIOLIMIT_GLOBAL = 0 # follow the global settings
|
TR_RATIOLIMIT_GLOBAL = 0 # follow the global settings
|
||||||
TR_RATIOLIMIT_SINGLE = 1 # override the global settings, seeding until a certain ratio
|
TR_RATIOLIMIT_SINGLE = 1 # override the global settings, seeding until a certain ratio
|
||||||
TR_RATIOLIMIT_UNLIMITED = 2 # override the global settings, seeding regardless of ratio
|
TR_RATIOLIMIT_UNLIMITED = 2 # override the global settings, seeding regardless of ratio
|
||||||
|
|
||||||
RATIO_LIMIT = mirror_dict({
|
RATIO_LIMIT = mirror_dict({
|
||||||
'global' : TR_RATIOLIMIT_GLOBAL,
|
'global': TR_RATIOLIMIT_GLOBAL,
|
||||||
'single' : TR_RATIOLIMIT_SINGLE,
|
'single': TR_RATIOLIMIT_SINGLE,
|
||||||
'unlimited' : TR_RATIOLIMIT_UNLIMITED
|
'unlimited': TR_RATIOLIMIT_UNLIMITED
|
||||||
})
|
})
|
||||||
|
|
||||||
TR_IDLELIMIT_GLOBAL = 0 # follow the global settings
|
TR_IDLELIMIT_GLOBAL = 0 # follow the global settings
|
||||||
TR_IDLELIMIT_SINGLE = 1 # override the global settings, seeding until a certain idle time
|
TR_IDLELIMIT_SINGLE = 1 # override the global settings, seeding until a certain idle time
|
||||||
TR_IDLELIMIT_UNLIMITED = 2 # override the global settings, seeding regardless of activity
|
TR_IDLELIMIT_UNLIMITED = 2 # override the global settings, seeding regardless of activity
|
||||||
|
|
||||||
IDLE_LIMIT = mirror_dict({
|
IDLE_LIMIT = mirror_dict({
|
||||||
'global' : TR_RATIOLIMIT_GLOBAL,
|
'global': TR_RATIOLIMIT_GLOBAL,
|
||||||
'single' : TR_RATIOLIMIT_SINGLE,
|
'single': TR_RATIOLIMIT_SINGLE,
|
||||||
'unlimited' : TR_RATIOLIMIT_UNLIMITED
|
'unlimited': TR_RATIOLIMIT_UNLIMITED
|
||||||
})
|
})
|
||||||
|
|
||||||
# A note on argument maps
|
# A note on argument maps
|
||||||
|
@ -62,236 +63,266 @@ IDLE_LIMIT = mirror_dict({
|
||||||
|
|
||||||
# Arguments for torrent methods
|
# Arguments for torrent methods
|
||||||
TORRENT_ARGS = {
|
TORRENT_ARGS = {
|
||||||
'get' : {
|
'get': {
|
||||||
'activityDate': ('number', 1, None, None, None, 'Last time of upload or download activity.'),
|
'activityDate': ('number', 1, None, None, None, 'Last time of upload or download activity.'),
|
||||||
'addedDate': ('number', 1, None, None, None, 'The date when this torrent was first added.'),
|
'addedDate': ('number', 1, None, None, None, 'The date when this torrent was first added.'),
|
||||||
'announceResponse': ('string', 1, 7, None, None, 'The announce message from the tracker.'),
|
'announceResponse': ('string', 1, 7, None, None, 'The announce message from the tracker.'),
|
||||||
'announceURL': ('string', 1, 7, None, None, 'Current announce URL.'),
|
'announceURL': ('string', 1, 7, None, None, 'Current announce URL.'),
|
||||||
'bandwidthPriority': ('number', 5, None, None, None, 'Bandwidth priority. Low (-1), Normal (0) or High (1).'),
|
'bandwidthPriority': ('number', 5, None, None, None, 'Bandwidth priority. Low (-1), Normal (0) or High (1).'),
|
||||||
'comment': ('string', 1, None, None, None, 'Torrent comment.'),
|
'comment': ('string', 1, None, None, None, 'Torrent comment.'),
|
||||||
'corruptEver': ('number', 1, None, None, None, 'Number of bytes of corrupt data downloaded.'),
|
'corruptEver': ('number', 1, None, None, None, 'Number of bytes of corrupt data downloaded.'),
|
||||||
'creator': ('string', 1, None, None, None, 'Torrent creator.'),
|
'creator': ('string', 1, None, None, None, 'Torrent creator.'),
|
||||||
'dateCreated': ('number', 1, None, None, None, 'Torrent creation date.'),
|
'dateCreated': ('number', 1, None, None, None, 'Torrent creation date.'),
|
||||||
'desiredAvailable': ('number', 1, None, None, None, 'Number of bytes avalable and left to be downloaded.'),
|
'desiredAvailable': ('number', 1, None, None, None, 'Number of bytes avalable and left to be downloaded.'),
|
||||||
'doneDate': ('number', 1, None, None, None, 'The date when the torrent finished downloading.'),
|
'doneDate': ('number', 1, None, None, None, 'The date when the torrent finished downloading.'),
|
||||||
'downloadDir': ('string', 4, None, None, None, 'The directory path where the torrent is downloaded to.'),
|
'downloadDir': ('string', 4, None, None, None, 'The directory path where the torrent is downloaded to.'),
|
||||||
'downloadedEver': ('number', 1, None, None, None, 'Number of bytes of good data downloaded.'),
|
'downloadedEver': ('number', 1, None, None, None, 'Number of bytes of good data downloaded.'),
|
||||||
'downloaders': ('number', 4, 7, None, None, 'Number of downloaders.'),
|
'downloaders': ('number', 4, 7, None, None, 'Number of downloaders.'),
|
||||||
'downloadLimit': ('number', 1, None, None, None, 'Download limit in Kbps.'),
|
'downloadLimit': ('number', 1, None, None, None, 'Download limit in Kbps.'),
|
||||||
'downloadLimited': ('boolean', 5, None, None, None, 'Download limit is enabled'),
|
'downloadLimited': ('boolean', 5, None, None, None, 'Download limit is enabled'),
|
||||||
'downloadLimitMode': ('number', 1, 5, None, None, 'Download limit mode. 0 means global, 1 means signle, 2 unlimited.'),
|
'downloadLimitMode': (
|
||||||
'error': ('number', 1, None, None, None, 'Kind of error. 0 means OK, 1 means tracker warning, 2 means tracker error, 3 means local error.'),
|
'number', 1, 5, None, None, 'Download limit mode. 0 means global, 1 means signle, 2 unlimited.'),
|
||||||
'errorString': ('number', 1, None, None, None, 'Error message.'),
|
'error': ('number', 1, None, None, None,
|
||||||
'eta': ('number', 1, None, None, None, 'Estimated number of seconds left when downloading or seeding. -1 means not available and -2 means unknown.'),
|
'Kind of error. 0 means OK, 1 means tracker warning, 2 means tracker error, 3 means local error.'),
|
||||||
'etaIdle': ('number', 15, None, None, None, 'Estimated number of seconds left until the idle time limit is reached. -1 means not available and -2 means unknown.'),
|
'errorString': ('number', 1, None, None, None, 'Error message.'),
|
||||||
'files': ('array', 1, None, None, None, 'Array of file object containing key, bytesCompleted, length and name.'),
|
'eta': ('number', 1, None, None, None,
|
||||||
'fileStats': ('array', 5, None, None, None, 'Aray of file statistics containing bytesCompleted, wanted and priority.'),
|
'Estimated number of seconds left when downloading or seeding. -1 means not available and -2 means unknown.'),
|
||||||
'hashString': ('string', 1, None, None, None, 'Hashstring unique for the torrent even between sessions.'),
|
'etaIdle': ('number', 15, None, None, None,
|
||||||
'haveUnchecked': ('number', 1, None, None, None, 'Number of bytes of partial pieces.'),
|
'Estimated number of seconds left until the idle time limit is reached. -1 means not available and -2 means unknown.'),
|
||||||
'haveValid': ('number', 1, None, None, None, 'Number of bytes of checksum verified data.'),
|
'files': (
|
||||||
'honorsSessionLimits': ('boolean', 5, None, None, None, 'True if session upload limits are honored'),
|
'array', 1, None, None, None, 'Array of file object containing key, bytesCompleted, length and name.'),
|
||||||
'id': ('number', 1, None, None, None, 'Session unique torrent id.'),
|
'fileStats': (
|
||||||
'isFinished': ('boolean', 9, None, None, None, 'True if the torrent is finished. Downloaded and seeded.'),
|
'array', 5, None, None, None, 'Aray of file statistics containing bytesCompleted, wanted and priority.'),
|
||||||
'isPrivate': ('boolean', 1, None, None, None, 'True if the torrent is private.'),
|
'hashString': ('string', 1, None, None, None, 'Hashstring unique for the torrent even between sessions.'),
|
||||||
'isStalled': ('boolean', 14, None, None, None, 'True if the torrent has stalled (been idle for a long time).'),
|
'haveUnchecked': ('number', 1, None, None, None, 'Number of bytes of partial pieces.'),
|
||||||
'lastAnnounceTime': ('number', 1, 7, None, None, 'The time of the last announcement.'),
|
'haveValid': ('number', 1, None, None, None, 'Number of bytes of checksum verified data.'),
|
||||||
'lastScrapeTime': ('number', 1, 7, None, None, 'The time af the last successful scrape.'),
|
'honorsSessionLimits': ('boolean', 5, None, None, None, 'True if session upload limits are honored'),
|
||||||
'leechers': ('number', 1, 7, None, None, 'Number of leechers.'),
|
'id': ('number', 1, None, None, None, 'Session unique torrent id.'),
|
||||||
'leftUntilDone': ('number', 1, None, None, None, 'Number of bytes left until the download is done.'),
|
'isFinished': ('boolean', 9, None, None, None, 'True if the torrent is finished. Downloaded and seeded.'),
|
||||||
'magnetLink': ('string', 7, None, None, None, 'The magnet link for this torrent.'),
|
'isPrivate': ('boolean', 1, None, None, None, 'True if the torrent is private.'),
|
||||||
'manualAnnounceTime': ('number', 1, None, None, None, 'The time until you manually ask for more peers.'),
|
'isStalled': ('boolean', 14, None, None, None, 'True if the torrent has stalled (been idle for a long time).'),
|
||||||
'maxConnectedPeers': ('number', 1, None, None, None, 'Maximum of connected peers.'),
|
'lastAnnounceTime': ('number', 1, 7, None, None, 'The time of the last announcement.'),
|
||||||
'metadataPercentComplete': ('number', 7, None, None, None, 'Download progress of metadata. 0.0 to 1.0.'),
|
'lastScrapeTime': ('number', 1, 7, None, None, 'The time af the last successful scrape.'),
|
||||||
'name': ('string', 1, None, None, None, 'Torrent name.'),
|
'leechers': ('number', 1, 7, None, None, 'Number of leechers.'),
|
||||||
'nextAnnounceTime': ('number', 1, 7, None, None, 'Next announce time.'),
|
'leftUntilDone': ('number', 1, None, None, None, 'Number of bytes left until the download is done.'),
|
||||||
'nextScrapeTime': ('number', 1, 7, None, None, 'Next scrape time.'),
|
'magnetLink': ('string', 7, None, None, None, 'The magnet link for this torrent.'),
|
||||||
'peer-limit': ('number', 5, None, None, None, 'Maximum number of peers.'),
|
'manualAnnounceTime': ('number', 1, None, None, None, 'The time until you manually ask for more peers.'),
|
||||||
'peers': ('array', 2, None, None, None, 'Array of peer objects.'),
|
'maxConnectedPeers': ('number', 1, None, None, None, 'Maximum of connected peers.'),
|
||||||
'peersConnected': ('number', 1, None, None, None, 'Number of peers we are connected to.'),
|
'metadataPercentComplete': ('number', 7, None, None, None, 'Download progress of metadata. 0.0 to 1.0.'),
|
||||||
'peersFrom': ('object', 1, None, None, None, 'Object containing download peers counts for different peer types.'),
|
'name': ('string', 1, None, None, None, 'Torrent name.'),
|
||||||
'peersGettingFromUs': ('number', 1, None, None, None, 'Number of peers we are sending data to.'),
|
'nextAnnounceTime': ('number', 1, 7, None, None, 'Next announce time.'),
|
||||||
'peersKnown': ('number', 1, 13, None, None, 'Number of peers that the tracker knows.'),
|
'nextScrapeTime': ('number', 1, 7, None, None, 'Next scrape time.'),
|
||||||
'peersSendingToUs': ('number', 1, None, None, None, 'Number of peers sending to us'),
|
'peer-limit': ('number', 5, None, None, None, 'Maximum number of peers.'),
|
||||||
'percentDone': ('double', 5, None, None, None, 'Download progress of selected files. 0.0 to 1.0.'),
|
'peers': ('array', 2, None, None, None, 'Array of peer objects.'),
|
||||||
'pieces': ('string', 5, None, None, None, 'String with base64 encoded bitfield indicating finished pieces.'),
|
'peersConnected': ('number', 1, None, None, None, 'Number of peers we are connected to.'),
|
||||||
'pieceCount': ('number', 1, None, None, None, 'Number of pieces.'),
|
'peersFrom': (
|
||||||
'pieceSize': ('number', 1, None, None, None, 'Number of bytes in a piece.'),
|
'object', 1, None, None, None, 'Object containing download peers counts for different peer types.'),
|
||||||
'priorities': ('array', 1, None, None, None, 'Array of file priorities.'),
|
'peersGettingFromUs': ('number', 1, None, None, None, 'Number of peers we are sending data to.'),
|
||||||
'queuePosition': ('number', 14, None, None, None, 'The queue position.'),
|
'peersKnown': ('number', 1, 13, None, None, 'Number of peers that the tracker knows.'),
|
||||||
'rateDownload': ('number', 1, None, None, None, 'Download rate in bps.'),
|
'peersSendingToUs': ('number', 1, None, None, None, 'Number of peers sending to us'),
|
||||||
'rateUpload': ('number', 1, None, None, None, 'Upload rate in bps.'),
|
'percentDone': ('double', 5, None, None, None, 'Download progress of selected files. 0.0 to 1.0.'),
|
||||||
'recheckProgress': ('double', 1, None, None, None, 'Progress of recheck. 0.0 to 1.0.'),
|
'pieces': ('string', 5, None, None, None, 'String with base64 encoded bitfield indicating finished pieces.'),
|
||||||
'secondsDownloading': ('number', 15, None, None, None, ''),
|
'pieceCount': ('number', 1, None, None, None, 'Number of pieces.'),
|
||||||
'secondsSeeding': ('number', 15, None, None, None, ''),
|
'pieceSize': ('number', 1, None, None, None, 'Number of bytes in a piece.'),
|
||||||
'scrapeResponse': ('string', 1, 7, None, None, 'Scrape response message.'),
|
'priorities': ('array', 1, None, None, None, 'Array of file priorities.'),
|
||||||
'scrapeURL': ('string', 1, 7, None, None, 'Current scrape URL'),
|
'queuePosition': ('number', 14, None, None, None, 'The queue position.'),
|
||||||
'seeders': ('number', 1, 7, None, None, 'Number of seeders reported by the tracker.'),
|
'rateDownload': ('number', 1, None, None, None, 'Download rate in bps.'),
|
||||||
'seedIdleLimit': ('number', 10, None, None, None, 'Idle limit in minutes.'),
|
'rateUpload': ('number', 1, None, None, None, 'Upload rate in bps.'),
|
||||||
'seedIdleMode': ('number', 10, None, None, None, 'Use global (0), torrent (1), or unlimited (2) limit.'),
|
'recheckProgress': ('double', 1, None, None, None, 'Progress of recheck. 0.0 to 1.0.'),
|
||||||
'seedRatioLimit': ('double', 5, None, None, None, 'Seed ratio limit.'),
|
'secondsDownloading': ('number', 15, None, None, None, ''),
|
||||||
'seedRatioMode': ('number', 5, None, None, None, 'Use global (0), torrent (1), or unlimited (2) limit.'),
|
'secondsSeeding': ('number', 15, None, None, None, ''),
|
||||||
'sizeWhenDone': ('number', 1, None, None, None, 'Size of the torrent download in bytes.'),
|
'scrapeResponse': ('string', 1, 7, None, None, 'Scrape response message.'),
|
||||||
'startDate': ('number', 1, None, None, None, 'The date when the torrent was last started.'),
|
'scrapeURL': ('string', 1, 7, None, None, 'Current scrape URL'),
|
||||||
'status': ('number', 1, None, None, None, 'Current status, see source'),
|
'seeders': ('number', 1, 7, None, None, 'Number of seeders reported by the tracker.'),
|
||||||
'swarmSpeed': ('number', 1, 7, None, None, 'Estimated speed in Kbps in the swarm.'),
|
'seedIdleLimit': ('number', 10, None, None, None, 'Idle limit in minutes.'),
|
||||||
'timesCompleted': ('number', 1, 7, None, None, 'Number of successful downloads reported by the tracker.'),
|
'seedIdleMode': ('number', 10, None, None, None, 'Use global (0), torrent (1), or unlimited (2) limit.'),
|
||||||
'trackers': ('array', 1, None, None, None, 'Array of tracker objects.'),
|
'seedRatioLimit': ('double', 5, None, None, None, 'Seed ratio limit.'),
|
||||||
'trackerStats': ('object', 7, None, None, None, 'Array of object containing tracker statistics.'),
|
'seedRatioMode': ('number', 5, None, None, None, 'Use global (0), torrent (1), or unlimited (2) limit.'),
|
||||||
'totalSize': ('number', 1, None, None, None, 'Total size of the torrent in bytes'),
|
'sizeWhenDone': ('number', 1, None, None, None, 'Size of the torrent download in bytes.'),
|
||||||
'torrentFile': ('string', 5, None, None, None, 'Path to .torrent file.'),
|
'startDate': ('number', 1, None, None, None, 'The date when the torrent was last started.'),
|
||||||
'uploadedEver': ('number', 1, None, None, None, 'Number of bytes uploaded, ever.'),
|
'status': ('number', 1, None, None, None, 'Current status, see source'),
|
||||||
'uploadLimit': ('number', 1, None, None, None, 'Upload limit in Kbps'),
|
'swarmSpeed': ('number', 1, 7, None, None, 'Estimated speed in Kbps in the swarm.'),
|
||||||
'uploadLimitMode': ('number', 1, 5, None, None, 'Upload limit mode. 0 means global, 1 means signle, 2 unlimited.'),
|
'timesCompleted': ('number', 1, 7, None, None, 'Number of successful downloads reported by the tracker.'),
|
||||||
'uploadLimited': ('boolean', 5, None, None, None, 'Upload limit enabled.'),
|
'trackers': ('array', 1, None, None, None, 'Array of tracker objects.'),
|
||||||
'uploadRatio': ('double', 1, None, None, None, 'Seed ratio.'),
|
'trackerStats': ('object', 7, None, None, None, 'Array of object containing tracker statistics.'),
|
||||||
'wanted': ('array', 1, None, None, None, 'Array of booleans indicated wanted files.'),
|
'totalSize': ('number', 1, None, None, None, 'Total size of the torrent in bytes'),
|
||||||
'webseeds': ('array', 1, None, None, None, 'Array of webseeds objects'),
|
'torrentFile': ('string', 5, None, None, None, 'Path to .torrent file.'),
|
||||||
'webseedsSendingToUs': ('number', 1, None, None, None, 'Number of webseeds seeding to us.'),
|
'uploadedEver': ('number', 1, None, None, None, 'Number of bytes uploaded, ever.'),
|
||||||
|
'uploadLimit': ('number', 1, None, None, None, 'Upload limit in Kbps'),
|
||||||
|
'uploadLimitMode': (
|
||||||
|
'number', 1, 5, None, None, 'Upload limit mode. 0 means global, 1 means signle, 2 unlimited.'),
|
||||||
|
'uploadLimited': ('boolean', 5, None, None, None, 'Upload limit enabled.'),
|
||||||
|
'uploadRatio': ('double', 1, None, None, None, 'Seed ratio.'),
|
||||||
|
'wanted': ('array', 1, None, None, None, 'Array of booleans indicated wanted files.'),
|
||||||
|
'webseeds': ('array', 1, None, None, None, 'Array of webseeds objects'),
|
||||||
|
'webseedsSendingToUs': ('number', 1, None, None, None, 'Number of webseeds seeding to us.'),
|
||||||
},
|
},
|
||||||
'set': {
|
'set': {
|
||||||
'bandwidthPriority': ('number', 5, None, None, None, 'Priority for this transfer.'),
|
'bandwidthPriority': ('number', 5, None, None, None, 'Priority for this transfer.'),
|
||||||
'downloadLimit': ('number', 5, None, 'speed-limit-down', None, 'Set the speed limit for download in Kib/s.'),
|
'downloadLimit': ('number', 5, None, 'speed-limit-down', None, 'Set the speed limit for download in Kib/s.'),
|
||||||
'downloadLimited': ('boolean', 5, None, 'speed-limit-down-enabled', None, 'Enable download speed limiter.'),
|
'downloadLimited': ('boolean', 5, None, 'speed-limit-down-enabled', None, 'Enable download speed limiter.'),
|
||||||
'files-wanted': ('array', 1, None, None, None, "A list of file id's that should be downloaded."),
|
'files-wanted': ('array', 1, None, None, None, "A list of file id's that should be downloaded."),
|
||||||
'files-unwanted': ('array', 1, None, None, None, "A list of file id's that shouldn't be downloaded."),
|
'files-unwanted': ('array', 1, None, None, None, "A list of file id's that shouldn't be downloaded."),
|
||||||
'honorsSessionLimits': ('boolean', 5, None, None, None, "Enables or disables the transfer to honour the upload limit set in the session."),
|
'honorsSessionLimits': ('boolean', 5, None, None, None,
|
||||||
'location': ('array', 1, None, None, None, 'Local download location.'),
|
"Enables or disables the transfer to honour the upload limit set in the session."),
|
||||||
'peer-limit': ('number', 1, None, None, None, 'The peer limit for the torrents.'),
|
'location': ('array', 1, None, None, None, 'Local download location.'),
|
||||||
'priority-high': ('array', 1, None, None, None, "A list of file id's that should have high priority."),
|
'peer-limit': ('number', 1, None, None, None, 'The peer limit for the torrents.'),
|
||||||
'priority-low': ('array', 1, None, None, None, "A list of file id's that should have normal priority."),
|
'priority-high': ('array', 1, None, None, None, "A list of file id's that should have high priority."),
|
||||||
'priority-normal': ('array', 1, None, None, None, "A list of file id's that should have low priority."),
|
'priority-low': ('array', 1, None, None, None, "A list of file id's that should have normal priority."),
|
||||||
'queuePosition': ('number', 14, None, None, None, 'Position of this transfer in its queue.'),
|
'priority-normal': ('array', 1, None, None, None, "A list of file id's that should have low priority."),
|
||||||
'seedIdleLimit': ('number', 10, None, None, None, 'Seed inactivity limit in minutes.'),
|
'queuePosition': ('number', 14, None, None, None, 'Position of this transfer in its queue.'),
|
||||||
'seedIdleMode': ('number', 10, None, None, None, 'Seed inactivity mode. 0 = Use session limit, 1 = Use transfer limit, 2 = Disable limit.'),
|
'seedIdleLimit': ('number', 10, None, None, None, 'Seed inactivity limit in minutes.'),
|
||||||
'seedRatioLimit': ('double', 5, None, None, None, 'Seeding ratio.'),
|
'seedIdleMode': ('number', 10, None, None, None,
|
||||||
'seedRatioMode': ('number', 5, None, None, None, 'Which ratio to use. 0 = Use session limit, 1 = Use transfer limit, 2 = Disable limit.'),
|
'Seed inactivity mode. 0 = Use session limit, 1 = Use transfer limit, 2 = Disable limit.'),
|
||||||
'speed-limit-down': ('number', 1, 5, None, 'downloadLimit', 'Set the speed limit for download in Kib/s.'),
|
'seedRatioLimit': ('double', 5, None, None, None, 'Seeding ratio.'),
|
||||||
'speed-limit-down-enabled': ('boolean', 1, 5, None, 'downloadLimited', 'Enable download speed limiter.'),
|
'seedRatioMode': ('number', 5, None, None, None,
|
||||||
'speed-limit-up': ('number', 1, 5, None, 'uploadLimit', 'Set the speed limit for upload in Kib/s.'),
|
'Which ratio to use. 0 = Use session limit, 1 = Use transfer limit, 2 = Disable limit.'),
|
||||||
'speed-limit-up-enabled': ('boolean', 1, 5, None, 'uploadLimited', 'Enable upload speed limiter.'),
|
'speed-limit-down': ('number', 1, 5, None, 'downloadLimit', 'Set the speed limit for download in Kib/s.'),
|
||||||
'trackerAdd': ('array', 10, None, None, None, 'Array of string with announce URLs to add.'),
|
'speed-limit-down-enabled': ('boolean', 1, 5, None, 'downloadLimited', 'Enable download speed limiter.'),
|
||||||
'trackerRemove': ('array', 10, None, None, None, 'Array of ids of trackers to remove.'),
|
'speed-limit-up': ('number', 1, 5, None, 'uploadLimit', 'Set the speed limit for upload in Kib/s.'),
|
||||||
'trackerReplace': ('array', 10, None, None, None, 'Array of (id, url) tuples where the announce URL should be replaced.'),
|
'speed-limit-up-enabled': ('boolean', 1, 5, None, 'uploadLimited', 'Enable upload speed limiter.'),
|
||||||
'uploadLimit': ('number', 5, None, 'speed-limit-up', None, 'Set the speed limit for upload in Kib/s.'),
|
'trackerAdd': ('array', 10, None, None, None, 'Array of string with announce URLs to add.'),
|
||||||
'uploadLimited': ('boolean', 5, None, 'speed-limit-up-enabled', None, 'Enable upload speed limiter.'),
|
'trackerRemove': ('array', 10, None, None, None, 'Array of ids of trackers to remove.'),
|
||||||
|
'trackerReplace': (
|
||||||
|
'array', 10, None, None, None, 'Array of (id, url) tuples where the announce URL should be replaced.'),
|
||||||
|
'uploadLimit': ('number', 5, None, 'speed-limit-up', None, 'Set the speed limit for upload in Kib/s.'),
|
||||||
|
'uploadLimited': ('boolean', 5, None, 'speed-limit-up-enabled', None, 'Enable upload speed limiter.'),
|
||||||
},
|
},
|
||||||
'add': {
|
'add': {
|
||||||
'bandwidthPriority': ('number', 8, None, None, None, 'Priority for this transfer.'),
|
'bandwidthPriority': ('number', 8, None, None, None, 'Priority for this transfer.'),
|
||||||
'download-dir': ('string', 1, None, None, None, 'The directory where the downloaded contents will be saved in.'),
|
'download-dir': (
|
||||||
'cookies': ('string', 13, None, None, None, 'One or more HTTP cookie(s).'),
|
'string', 1, None, None, None, 'The directory where the downloaded contents will be saved in.'),
|
||||||
'filename': ('string', 1, None, None, None, "A file path or URL to a torrent file or a magnet link."),
|
'cookies': ('string', 13, None, None, None, 'One or more HTTP cookie(s).'),
|
||||||
'files-wanted': ('array', 1, None, None, None, "A list of file id's that should be downloaded."),
|
'filename': ('string', 1, None, None, None, "A file path or URL to a torrent file or a magnet link."),
|
||||||
'files-unwanted': ('array', 1, None, None, None, "A list of file id's that shouldn't be downloaded."),
|
'files-wanted': ('array', 1, None, None, None, "A list of file id's that should be downloaded."),
|
||||||
'metainfo': ('string', 1, None, None, None, 'The content of a torrent file, base64 encoded.'),
|
'files-unwanted': ('array', 1, None, None, None, "A list of file id's that shouldn't be downloaded."),
|
||||||
'paused': ('boolean', 1, None, None, None, 'If True, does not start the transfer when added.'),
|
'metainfo': ('string', 1, None, None, None, 'The content of a torrent file, base64 encoded.'),
|
||||||
'peer-limit': ('number', 1, None, None, None, 'Maximum number of peers allowed.'),
|
'paused': ('boolean', 1, None, None, None, 'If True, does not start the transfer when added.'),
|
||||||
'priority-high': ('array', 1, None, None, None, "A list of file id's that should have high priority."),
|
'peer-limit': ('number', 1, None, None, None, 'Maximum number of peers allowed.'),
|
||||||
'priority-low': ('array', 1, None, None, None, "A list of file id's that should have low priority."),
|
'priority-high': ('array', 1, None, None, None, "A list of file id's that should have high priority."),
|
||||||
'priority-normal': ('array', 1, None, None, None, "A list of file id's that should have normal priority."),
|
'priority-low': ('array', 1, None, None, None, "A list of file id's that should have low priority."),
|
||||||
|
'priority-normal': ('array', 1, None, None, None, "A list of file id's that should have normal priority."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Arguments for session methods
|
# Arguments for session methods
|
||||||
SESSION_ARGS = {
|
SESSION_ARGS = {
|
||||||
'get': {
|
'get': {
|
||||||
"alt-speed-down": ('number', 5, None, None, None, 'Alternate session download speed limit (in Kib/s).'),
|
"alt-speed-down": ('number', 5, None, None, None, 'Alternate session download speed limit (in Kib/s).'),
|
||||||
"alt-speed-enabled": ('boolean', 5, None, None, None, 'True if alternate global download speed limiter is ebabled.'),
|
"alt-speed-enabled": (
|
||||||
"alt-speed-time-begin": ('number', 5, None, None, None, 'Time when alternate speeds should be enabled. Minutes after midnight.'),
|
'boolean', 5, None, None, None, 'True if alternate global download speed limiter is ebabled.'),
|
||||||
"alt-speed-time-enabled": ('boolean', 5, None, None, None, 'True if alternate speeds scheduling is enabled.'),
|
"alt-speed-time-begin": (
|
||||||
"alt-speed-time-end": ('number', 5, None, None, None, 'Time when alternate speeds should be disabled. Minutes after midnight.'),
|
'number', 5, None, None, None, 'Time when alternate speeds should be enabled. Minutes after midnight.'),
|
||||||
"alt-speed-time-day": ('number', 5, None, None, None, 'Days alternate speeds scheduling is enabled.'),
|
"alt-speed-time-enabled": ('boolean', 5, None, None, None, 'True if alternate speeds scheduling is enabled.'),
|
||||||
"alt-speed-up": ('number', 5, None, None, None, 'Alternate session upload speed limit (in Kib/s)'),
|
"alt-speed-time-end": (
|
||||||
"blocklist-enabled": ('boolean', 5, None, None, None, 'True when blocklist is enabled.'),
|
'number', 5, None, None, None, 'Time when alternate speeds should be disabled. Minutes after midnight.'),
|
||||||
"blocklist-size": ('number', 5, None, None, None, 'Number of rules in the blocklist'),
|
"alt-speed-time-day": ('number', 5, None, None, None, 'Days alternate speeds scheduling is enabled.'),
|
||||||
"blocklist-url": ('string', 11, None, None, None, 'Location of the block list. Updated with blocklist-update.'),
|
"alt-speed-up": ('number', 5, None, None, None, 'Alternate session upload speed limit (in Kib/s)'),
|
||||||
"cache-size-mb": ('number', 10, None, None, None, 'The maximum size of the disk cache in MB'),
|
"blocklist-enabled": ('boolean', 5, None, None, None, 'True when blocklist is enabled.'),
|
||||||
"config-dir": ('string', 8, None, None, None, 'location of transmissions configuration directory'),
|
"blocklist-size": ('number', 5, None, None, None, 'Number of rules in the blocklist'),
|
||||||
"dht-enabled": ('boolean', 6, None, None, None, 'True if DHT enabled.'),
|
"blocklist-url": ('string', 11, None, None, None, 'Location of the block list. Updated with blocklist-update.'),
|
||||||
"download-dir": ('string', 1, None, None, None, 'The download directory.'),
|
"cache-size-mb": ('number', 10, None, None, None, 'The maximum size of the disk cache in MB'),
|
||||||
"download-dir-free-space": ('number', 12, None, None, None, 'Free space in the download directory, in bytes'),
|
"config-dir": ('string', 8, None, None, None, 'location of transmissions configuration directory'),
|
||||||
"download-queue-size": ('number', 14, None, None, None, 'Number of slots in the download queue.'),
|
"dht-enabled": ('boolean', 6, None, None, None, 'True if DHT enabled.'),
|
||||||
"download-queue-enabled": ('boolean', 14, None, None, None, 'True if the download queue is enabled.'),
|
"download-dir": ('string', 1, None, None, None, 'The download directory.'),
|
||||||
"encryption": ('string', 1, None, None, None, 'Encryption mode, one of ``required``, ``preferred`` or ``tolerated``.'),
|
"download-dir-free-space": ('number', 12, None, None, None, 'Free space in the download directory, in bytes'),
|
||||||
"idle-seeding-limit": ('number', 10, None, None, None, 'Seed inactivity limit in minutes.'),
|
"download-queue-size": ('number', 14, None, None, None, 'Number of slots in the download queue.'),
|
||||||
"idle-seeding-limit-enabled": ('boolean', 10, None, None, None, 'True if the seed activity limit is enabled.'),
|
"download-queue-enabled": ('boolean', 14, None, None, None, 'True if the download queue is enabled.'),
|
||||||
"incomplete-dir": ('string', 7, None, None, None, 'The path to the directory for incomplete torrent transfer data.'),
|
"encryption": (
|
||||||
"incomplete-dir-enabled": ('boolean', 7, None, None, None, 'True if the incomplete dir is enabled.'),
|
'string', 1, None, None, None, 'Encryption mode, one of ``required``, ``preferred`` or ``tolerated``.'),
|
||||||
"lpd-enabled": ('boolean', 9, None, None, None, 'True if local peer discovery is enabled.'),
|
"idle-seeding-limit": ('number', 10, None, None, None, 'Seed inactivity limit in minutes.'),
|
||||||
"peer-limit": ('number', 1, 5, None, 'peer-limit-global', 'Maximum number of peers.'),
|
"idle-seeding-limit-enabled": ('boolean', 10, None, None, None, 'True if the seed activity limit is enabled.'),
|
||||||
"peer-limit-global": ('number', 5, None, 'peer-limit', None, 'Maximum number of peers.'),
|
"incomplete-dir": (
|
||||||
"peer-limit-per-torrent": ('number', 5, None, None, None, 'Maximum number of peers per transfer.'),
|
'string', 7, None, None, None, 'The path to the directory for incomplete torrent transfer data.'),
|
||||||
"pex-allowed": ('boolean', 1, 5, None, 'pex-enabled', 'True if PEX is allowed.'),
|
"incomplete-dir-enabled": ('boolean', 7, None, None, None, 'True if the incomplete dir is enabled.'),
|
||||||
"pex-enabled": ('boolean', 5, None, 'pex-allowed', None, 'True if PEX is enabled.'),
|
"lpd-enabled": ('boolean', 9, None, None, None, 'True if local peer discovery is enabled.'),
|
||||||
"port": ('number', 1, 5, None, 'peer-port', 'Peer port.'),
|
"peer-limit": ('number', 1, 5, None, 'peer-limit-global', 'Maximum number of peers.'),
|
||||||
"peer-port": ('number', 5, None, 'port', None, 'Peer port.'),
|
"peer-limit-global": ('number', 5, None, 'peer-limit', None, 'Maximum number of peers.'),
|
||||||
"peer-port-random-on-start": ('boolean', 5, None, None, None, 'Enables randomized peer port on start of Transmission.'),
|
"peer-limit-per-torrent": ('number', 5, None, None, None, 'Maximum number of peers per transfer.'),
|
||||||
"port-forwarding-enabled": ('boolean', 1, None, None, None, 'True if port forwarding is enabled.'),
|
"pex-allowed": ('boolean', 1, 5, None, 'pex-enabled', 'True if PEX is allowed.'),
|
||||||
"queue-stalled-minutes": ('number', 14, None, None, None, 'Number of minutes of idle that marks a transfer as stalled.'),
|
"pex-enabled": ('boolean', 5, None, 'pex-allowed', None, 'True if PEX is enabled.'),
|
||||||
"queue-stalled-enabled": ('boolean', 14, None, None, None, 'True if stalled tracking of transfers is enabled.'),
|
"port": ('number', 1, 5, None, 'peer-port', 'Peer port.'),
|
||||||
"rename-partial-files": ('boolean', 8, None, None, None, 'True if ".part" is appended to incomplete files'),
|
"peer-port": ('number', 5, None, 'port', None, 'Peer port.'),
|
||||||
"rpc-version": ('number', 4, None, None, None, 'Transmission RPC API Version.'),
|
"peer-port-random-on-start": (
|
||||||
"rpc-version-minimum": ('number', 4, None, None, None, 'Minimum accepted RPC API Version.'),
|
'boolean', 5, None, None, None, 'Enables randomized peer port on start of Transmission.'),
|
||||||
"script-torrent-done-enabled": ('boolean', 9, None, None, None, 'True if the done script is enabled.'),
|
"port-forwarding-enabled": ('boolean', 1, None, None, None, 'True if port forwarding is enabled.'),
|
||||||
"script-torrent-done-filename": ('string', 9, None, None, None, 'Filename of the script to run when the transfer is done.'),
|
"queue-stalled-minutes": (
|
||||||
"seedRatioLimit": ('double', 5, None, None, None, 'Seed ratio limit. 1.0 means 1:1 download and upload ratio.'),
|
'number', 14, None, None, None, 'Number of minutes of idle that marks a transfer as stalled.'),
|
||||||
"seedRatioLimited": ('boolean', 5, None, None, None, 'True if seed ration limit is enabled.'),
|
"queue-stalled-enabled": ('boolean', 14, None, None, None, 'True if stalled tracking of transfers is enabled.'),
|
||||||
"seed-queue-size": ('number', 14, None, None, None, 'Number of slots in the upload queue.'),
|
"rename-partial-files": ('boolean', 8, None, None, None, 'True if ".part" is appended to incomplete files'),
|
||||||
"seed-queue-enabled": ('boolean', 14, None, None, None, 'True if upload queue is enabled.'),
|
"rpc-version": ('number', 4, None, None, None, 'Transmission RPC API Version.'),
|
||||||
"speed-limit-down": ('number', 1, None, None, None, 'Download speed limit (in Kib/s).'),
|
"rpc-version-minimum": ('number', 4, None, None, None, 'Minimum accepted RPC API Version.'),
|
||||||
"speed-limit-down-enabled": ('boolean', 1, None, None, None, 'True if the download speed is limited.'),
|
"script-torrent-done-enabled": ('boolean', 9, None, None, None, 'True if the done script is enabled.'),
|
||||||
"speed-limit-up": ('number', 1, None, None, None, 'Upload speed limit (in Kib/s).'),
|
"script-torrent-done-filename": (
|
||||||
"speed-limit-up-enabled": ('boolean', 1, None, None, None, 'True if the upload speed is limited.'),
|
'string', 9, None, None, None, 'Filename of the script to run when the transfer is done.'),
|
||||||
"start-added-torrents": ('boolean', 9, None, None, None, 'When true uploaded torrents will start right away.'),
|
"seedRatioLimit": ('double', 5, None, None, None, 'Seed ratio limit. 1.0 means 1:1 download and upload ratio.'),
|
||||||
"trash-original-torrent-files": ('boolean', 9, None, None, None, 'When true added .torrent files will be deleted.'),
|
"seedRatioLimited": ('boolean', 5, None, None, None, 'True if seed ration limit is enabled.'),
|
||||||
'units': ('object', 10, None, None, None, 'An object containing units for size and speed.'),
|
"seed-queue-size": ('number', 14, None, None, None, 'Number of slots in the upload queue.'),
|
||||||
'utp-enabled': ('boolean', 13, None, None, None, 'True if Micro Transport Protocol (UTP) is enabled.'),
|
"seed-queue-enabled": ('boolean', 14, None, None, None, 'True if upload queue is enabled.'),
|
||||||
"version": ('string', 3, None, None, None, 'Transmission version.'),
|
"speed-limit-down": ('number', 1, None, None, None, 'Download speed limit (in Kib/s).'),
|
||||||
|
"speed-limit-down-enabled": ('boolean', 1, None, None, None, 'True if the download speed is limited.'),
|
||||||
|
"speed-limit-up": ('number', 1, None, None, None, 'Upload speed limit (in Kib/s).'),
|
||||||
|
"speed-limit-up-enabled": ('boolean', 1, None, None, None, 'True if the upload speed is limited.'),
|
||||||
|
"start-added-torrents": ('boolean', 9, None, None, None, 'When true uploaded torrents will start right away.'),
|
||||||
|
"trash-original-torrent-files": (
|
||||||
|
'boolean', 9, None, None, None, 'When true added .torrent files will be deleted.'),
|
||||||
|
'units': ('object', 10, None, None, None, 'An object containing units for size and speed.'),
|
||||||
|
'utp-enabled': ('boolean', 13, None, None, None, 'True if Micro Transport Protocol (UTP) is enabled.'),
|
||||||
|
"version": ('string', 3, None, None, None, 'Transmission version.'),
|
||||||
},
|
},
|
||||||
'set': {
|
'set': {
|
||||||
"alt-speed-down": ('number', 5, None, None, None, 'Alternate session download speed limit (in Kib/s).'),
|
"alt-speed-down": ('number', 5, None, None, None, 'Alternate session download speed limit (in Kib/s).'),
|
||||||
"alt-speed-enabled": ('boolean', 5, None, None, None, 'Enables alternate global download speed limiter.'),
|
"alt-speed-enabled": ('boolean', 5, None, None, None, 'Enables alternate global download speed limiter.'),
|
||||||
"alt-speed-time-begin": ('number', 5, None, None, None, 'Time when alternate speeds should be enabled. Minutes after midnight.'),
|
"alt-speed-time-begin": (
|
||||||
"alt-speed-time-enabled": ('boolean', 5, None, None, None, 'Enables alternate speeds scheduling.'),
|
'number', 5, None, None, None, 'Time when alternate speeds should be enabled. Minutes after midnight.'),
|
||||||
"alt-speed-time-end": ('number', 5, None, None, None, 'Time when alternate speeds should be disabled. Minutes after midnight.'),
|
"alt-speed-time-enabled": ('boolean', 5, None, None, None, 'Enables alternate speeds scheduling.'),
|
||||||
"alt-speed-time-day": ('number', 5, None, None, None, 'Enables alternate speeds scheduling these days.'),
|
"alt-speed-time-end": (
|
||||||
"alt-speed-up": ('number', 5, None, None, None, 'Alternate session upload speed limit (in Kib/s).'),
|
'number', 5, None, None, None, 'Time when alternate speeds should be disabled. Minutes after midnight.'),
|
||||||
"blocklist-enabled": ('boolean', 5, None, None, None, 'Enables the block list'),
|
"alt-speed-time-day": ('number', 5, None, None, None, 'Enables alternate speeds scheduling these days.'),
|
||||||
"blocklist-url": ('string', 11, None, None, None, 'Location of the block list. Updated with blocklist-update.'),
|
"alt-speed-up": ('number', 5, None, None, None, 'Alternate session upload speed limit (in Kib/s).'),
|
||||||
"cache-size-mb": ('number', 10, None, None, None, 'The maximum size of the disk cache in MB'),
|
"blocklist-enabled": ('boolean', 5, None, None, None, 'Enables the block list'),
|
||||||
"dht-enabled": ('boolean', 6, None, None, None, 'Enables DHT.'),
|
"blocklist-url": ('string', 11, None, None, None, 'Location of the block list. Updated with blocklist-update.'),
|
||||||
"download-dir": ('string', 1, None, None, None, 'Set the session download directory.'),
|
"cache-size-mb": ('number', 10, None, None, None, 'The maximum size of the disk cache in MB'),
|
||||||
"download-queue-size": ('number', 14, None, None, None, 'Number of slots in the download queue.'),
|
"dht-enabled": ('boolean', 6, None, None, None, 'Enables DHT.'),
|
||||||
"download-queue-enabled": ('boolean', 14, None, None, None, 'Enables download queue.'),
|
"download-dir": ('string', 1, None, None, None, 'Set the session download directory.'),
|
||||||
"encryption": ('string', 1, None, None, None, 'Set the session encryption mode, one of ``required``, ``preferred`` or ``tolerated``.'),
|
"download-queue-size": ('number', 14, None, None, None, 'Number of slots in the download queue.'),
|
||||||
"idle-seeding-limit": ('number', 10, None, None, None, 'The default seed inactivity limit in minutes.'),
|
"download-queue-enabled": ('boolean', 14, None, None, None, 'Enables download queue.'),
|
||||||
"idle-seeding-limit-enabled": ('boolean', 10, None, None, None, 'Enables the default seed inactivity limit'),
|
"encryption": ('string', 1, None, None, None,
|
||||||
"incomplete-dir": ('string', 7, None, None, None, 'The path to the directory of incomplete transfer data.'),
|
'Set the session encryption mode, one of ``required``, ``preferred`` or ``tolerated``.'),
|
||||||
"incomplete-dir-enabled": ('boolean', 7, None, None, None, 'Enables the incomplete transfer data directory. Otherwise data for incomplete transfers are stored in the download target.'),
|
"idle-seeding-limit": ('number', 10, None, None, None, 'The default seed inactivity limit in minutes.'),
|
||||||
"lpd-enabled": ('boolean', 9, None, None, None, 'Enables local peer discovery for public torrents.'),
|
"idle-seeding-limit-enabled": ('boolean', 10, None, None, None, 'Enables the default seed inactivity limit'),
|
||||||
"peer-limit": ('number', 1, 5, None, 'peer-limit-global', 'Maximum number of peers.'),
|
"incomplete-dir": ('string', 7, None, None, None, 'The path to the directory of incomplete transfer data.'),
|
||||||
"peer-limit-global": ('number', 5, None, 'peer-limit', None, 'Maximum number of peers.'),
|
"incomplete-dir-enabled": ('boolean', 7, None, None, None,
|
||||||
"peer-limit-per-torrent": ('number', 5, None, None, None, 'Maximum number of peers per transfer.'),
|
'Enables the incomplete transfer data directory. Otherwise data for incomplete transfers are stored in the download target.'),
|
||||||
"pex-allowed": ('boolean', 1, 5, None, 'pex-enabled', 'Allowing PEX in public torrents.'),
|
"lpd-enabled": ('boolean', 9, None, None, None, 'Enables local peer discovery for public torrents.'),
|
||||||
"pex-enabled": ('boolean', 5, None, 'pex-allowed', None, 'Allowing PEX in public torrents.'),
|
"peer-limit": ('number', 1, 5, None, 'peer-limit-global', 'Maximum number of peers.'),
|
||||||
"port": ('number', 1, 5, None, 'peer-port', 'Peer port.'),
|
"peer-limit-global": ('number', 5, None, 'peer-limit', None, 'Maximum number of peers.'),
|
||||||
"peer-port": ('number', 5, None, 'port', None, 'Peer port.'),
|
"peer-limit-per-torrent": ('number', 5, None, None, None, 'Maximum number of peers per transfer.'),
|
||||||
"peer-port-random-on-start": ('boolean', 5, None, None, None, 'Enables randomized peer port on start of Transmission.'),
|
"pex-allowed": ('boolean', 1, 5, None, 'pex-enabled', 'Allowing PEX in public torrents.'),
|
||||||
"port-forwarding-enabled": ('boolean', 1, None, None, None, 'Enables port forwarding.'),
|
"pex-enabled": ('boolean', 5, None, 'pex-allowed', None, 'Allowing PEX in public torrents.'),
|
||||||
"rename-partial-files": ('boolean', 8, None, None, None, 'Appends ".part" to incomplete files'),
|
"port": ('number', 1, 5, None, 'peer-port', 'Peer port.'),
|
||||||
"queue-stalled-minutes": ('number', 14, None, None, None, 'Number of minutes of idle that marks a transfer as stalled.'),
|
"peer-port": ('number', 5, None, 'port', None, 'Peer port.'),
|
||||||
"queue-stalled-enabled": ('boolean', 14, None, None, None, 'Enable tracking of stalled transfers.'),
|
"peer-port-random-on-start": (
|
||||||
"script-torrent-done-enabled": ('boolean', 9, None, None, None, 'Whether or not to call the "done" script.'),
|
'boolean', 5, None, None, None, 'Enables randomized peer port on start of Transmission.'),
|
||||||
"script-torrent-done-filename": ('string', 9, None, None, None, 'Filename of the script to run when the transfer is done.'),
|
"port-forwarding-enabled": ('boolean', 1, None, None, None, 'Enables port forwarding.'),
|
||||||
"seed-queue-size": ('number', 14, None, None, None, 'Number of slots in the upload queue.'),
|
"rename-partial-files": ('boolean', 8, None, None, None, 'Appends ".part" to incomplete files'),
|
||||||
"seed-queue-enabled": ('boolean', 14, None, None, None, 'Enables upload queue.'),
|
"queue-stalled-minutes": (
|
||||||
"seedRatioLimit": ('double', 5, None, None, None, 'Seed ratio limit. 1.0 means 1:1 download and upload ratio.'),
|
'number', 14, None, None, None, 'Number of minutes of idle that marks a transfer as stalled.'),
|
||||||
"seedRatioLimited": ('boolean', 5, None, None, None, 'Enables seed ration limit.'),
|
"queue-stalled-enabled": ('boolean', 14, None, None, None, 'Enable tracking of stalled transfers.'),
|
||||||
"speed-limit-down": ('number', 1, None, None, None, 'Download speed limit (in Kib/s).'),
|
"script-torrent-done-enabled": ('boolean', 9, None, None, None, 'Whether or not to call the "done" script.'),
|
||||||
"speed-limit-down-enabled": ('boolean', 1, None, None, None, 'Enables download speed limiting.'),
|
"script-torrent-done-filename": (
|
||||||
"speed-limit-up": ('number', 1, None, None, None, 'Upload speed limit (in Kib/s).'),
|
'string', 9, None, None, None, 'Filename of the script to run when the transfer is done.'),
|
||||||
"speed-limit-up-enabled": ('boolean', 1, None, None, None, 'Enables upload speed limiting.'),
|
"seed-queue-size": ('number', 14, None, None, None, 'Number of slots in the upload queue.'),
|
||||||
"start-added-torrents": ('boolean', 9, None, None, None, 'Added torrents will be started right away.'),
|
"seed-queue-enabled": ('boolean', 14, None, None, None, 'Enables upload queue.'),
|
||||||
"trash-original-torrent-files": ('boolean', 9, None, None, None, 'The .torrent file of added torrents will be deleted.'),
|
"seedRatioLimit": ('double', 5, None, None, None, 'Seed ratio limit. 1.0 means 1:1 download and upload ratio.'),
|
||||||
'utp-enabled': ('boolean', 13, None, None, None, 'Enables Micro Transport Protocol (UTP).'),
|
"seedRatioLimited": ('boolean', 5, None, None, None, 'Enables seed ration limit.'),
|
||||||
|
"speed-limit-down": ('number', 1, None, None, None, 'Download speed limit (in Kib/s).'),
|
||||||
|
"speed-limit-down-enabled": ('boolean', 1, None, None, None, 'Enables download speed limiting.'),
|
||||||
|
"speed-limit-up": ('number', 1, None, None, None, 'Upload speed limit (in Kib/s).'),
|
||||||
|
"speed-limit-up-enabled": ('boolean', 1, None, None, None, 'Enables upload speed limiting.'),
|
||||||
|
"start-added-torrents": ('boolean', 9, None, None, None, 'Added torrents will be started right away.'),
|
||||||
|
"trash-original-torrent-files": (
|
||||||
|
'boolean', 9, None, None, None, 'The .torrent file of added torrents will be deleted.'),
|
||||||
|
'utp-enabled': ('boolean', 13, None, None, None, 'Enables Micro Transport Protocol (UTP).'),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,13 @@
|
||||||
|
|
||||||
from core.transmissionrpc.six import string_types, integer_types
|
from core.transmissionrpc.six import string_types, integer_types
|
||||||
|
|
||||||
|
|
||||||
class TransmissionError(Exception):
|
class TransmissionError(Exception):
|
||||||
"""
|
"""
|
||||||
This exception is raised when there has occurred an error related to
|
This exception is raised when there has occurred an error related to
|
||||||
communication with Transmission. It is a subclass of Exception.
|
communication with Transmission. It is a subclass of Exception.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, message='', original=None):
|
def __init__(self, message='', original=None):
|
||||||
Exception.__init__(self)
|
Exception.__init__(self)
|
||||||
self.message = message
|
self.message = message
|
||||||
|
@ -21,11 +23,13 @@ class TransmissionError(Exception):
|
||||||
else:
|
else:
|
||||||
return self.message
|
return self.message
|
||||||
|
|
||||||
|
|
||||||
class HTTPHandlerError(Exception):
|
class HTTPHandlerError(Exception):
|
||||||
"""
|
"""
|
||||||
This exception is raised when there has occurred an error related to
|
This exception is raised when there has occurred an error related to
|
||||||
the HTTP handler. It is a subclass of Exception.
|
the HTTP handler. It is a subclass of Exception.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, httpurl=None, httpcode=None, httpmsg=None, httpheaders=None, httpdata=None):
|
def __init__(self, httpurl=None, httpcode=None, httpmsg=None, httpheaders=None, httpdata=None):
|
||||||
Exception.__init__(self)
|
Exception.__init__(self)
|
||||||
self.url = ''
|
self.url = ''
|
||||||
|
|
|
@ -18,10 +18,12 @@ else:
|
||||||
from urllib2 import HTTPError, URLError
|
from urllib2 import HTTPError, URLError
|
||||||
from httplib import BadStatusLine
|
from httplib import BadStatusLine
|
||||||
|
|
||||||
|
|
||||||
class HTTPHandler(object):
|
class HTTPHandler(object):
|
||||||
"""
|
"""
|
||||||
Prototype for HTTP handling.
|
Prototype for HTTP handling.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def set_authentication(self, uri, login, password):
|
def set_authentication(self, uri, login, password):
|
||||||
"""
|
"""
|
||||||
Transmission use basic authentication in earlier versions and digest
|
Transmission use basic authentication in earlier versions and digest
|
||||||
|
@ -44,10 +46,12 @@ class HTTPHandler(object):
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError("Bad HTTPHandler, failed to implement request.")
|
raise NotImplementedError("Bad HTTPHandler, failed to implement request.")
|
||||||
|
|
||||||
|
|
||||||
class DefaultHTTPHandler(HTTPHandler):
|
class DefaultHTTPHandler(HTTPHandler):
|
||||||
"""
|
"""
|
||||||
The default HTTP handler provided with transmissionrpc.
|
The default HTTP handler provided with transmissionrpc.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
HTTPHandler.__init__(self)
|
HTTPHandler.__init__(self)
|
||||||
self.http_opener = build_opener()
|
self.http_opener = build_opener()
|
||||||
|
|
|
@ -6,6 +6,7 @@ from core.transmissionrpc.utils import Field
|
||||||
|
|
||||||
from core.transmissionrpc.six import iteritems, integer_types
|
from core.transmissionrpc.six import iteritems, integer_types
|
||||||
|
|
||||||
|
|
||||||
class Session(object):
|
class Session(object):
|
||||||
"""
|
"""
|
||||||
Session is a class holding the session data for a Transmission daemon.
|
Session is a class holding the session data for a Transmission daemon.
|
||||||
|
|
|
@ -28,7 +28,6 @@ import types
|
||||||
__author__ = "Benjamin Peterson <benjamin@python.org>"
|
__author__ = "Benjamin Peterson <benjamin@python.org>"
|
||||||
__version__ = "1.4.1"
|
__version__ = "1.4.1"
|
||||||
|
|
||||||
|
|
||||||
# Useful for very coarse version differentiation.
|
# Useful for very coarse version differentiation.
|
||||||
PY2 = sys.version_info[0] == 2
|
PY2 = sys.version_info[0] == 2
|
||||||
PY3 = sys.version_info[0] == 3
|
PY3 = sys.version_info[0] == 3
|
||||||
|
@ -56,6 +55,8 @@ else:
|
||||||
class X(object):
|
class X(object):
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return 1 << 31
|
return 1 << 31
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
len(X())
|
len(X())
|
||||||
except OverflowError:
|
except OverflowError:
|
||||||
|
@ -79,7 +80,6 @@ def _import_module(name):
|
||||||
|
|
||||||
|
|
||||||
class _LazyDescr(object):
|
class _LazyDescr(object):
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
|
@ -92,7 +92,6 @@ class _LazyDescr(object):
|
||||||
|
|
||||||
|
|
||||||
class MovedModule(_LazyDescr):
|
class MovedModule(_LazyDescr):
|
||||||
|
|
||||||
def __init__(self, name, old, new=None):
|
def __init__(self, name, old, new=None):
|
||||||
super(MovedModule, self).__init__(name)
|
super(MovedModule, self).__init__(name)
|
||||||
if PY3:
|
if PY3:
|
||||||
|
@ -107,7 +106,6 @@ class MovedModule(_LazyDescr):
|
||||||
|
|
||||||
|
|
||||||
class MovedAttribute(_LazyDescr):
|
class MovedAttribute(_LazyDescr):
|
||||||
|
|
||||||
def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
|
def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
|
||||||
super(MovedAttribute, self).__init__(name)
|
super(MovedAttribute, self).__init__(name)
|
||||||
if PY3:
|
if PY3:
|
||||||
|
@ -131,7 +129,6 @@ class MovedAttribute(_LazyDescr):
|
||||||
return getattr(module, self.attr)
|
return getattr(module, self.attr)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class _MovedItems(types.ModuleType):
|
class _MovedItems(types.ModuleType):
|
||||||
"""Lazy loading of moved objects"""
|
"""Lazy loading of moved objects"""
|
||||||
|
|
||||||
|
@ -199,7 +196,6 @@ del attr
|
||||||
moves = sys.modules[__name__ + ".moves"] = _MovedItems(__name__ + ".moves")
|
moves = sys.modules[__name__ + ".moves"] = _MovedItems(__name__ + ".moves")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Module_six_moves_urllib_parse(types.ModuleType):
|
class Module_six_moves_urllib_parse(types.ModuleType):
|
||||||
"""Lazy loading of moved objects in six.moves.urllib_parse"""
|
"""Lazy loading of moved objects in six.moves.urllib_parse"""
|
||||||
|
|
||||||
|
@ -320,8 +316,10 @@ for attr in _urllib_robotparser_moved_attributes:
|
||||||
setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
|
setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
|
||||||
del attr
|
del attr
|
||||||
|
|
||||||
sys.modules[__name__ + ".moves.urllib_robotparser"] = Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib_robotparser")
|
sys.modules[__name__ + ".moves.urllib_robotparser"] = Module_six_moves_urllib_robotparser(
|
||||||
sys.modules[__name__ + ".moves.urllib.robotparser"] = Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser")
|
__name__ + ".moves.urllib_robotparser")
|
||||||
|
sys.modules[__name__ + ".moves.urllib.robotparser"] = Module_six_moves_urllib_robotparser(
|
||||||
|
__name__ + ".moves.urllib.robotparser")
|
||||||
|
|
||||||
|
|
||||||
class Module_six_moves_urllib(types.ModuleType):
|
class Module_six_moves_urllib(types.ModuleType):
|
||||||
|
@ -379,7 +377,6 @@ else:
|
||||||
_iteritems = "iteritems"
|
_iteritems = "iteritems"
|
||||||
_iterlists = "iterlists"
|
_iterlists = "iterlists"
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
advance_iterator = next
|
advance_iterator = next
|
||||||
except NameError:
|
except NameError:
|
||||||
|
@ -387,18 +384,17 @@ except NameError:
|
||||||
return it.next()
|
return it.next()
|
||||||
next = advance_iterator
|
next = advance_iterator
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
callable = callable
|
callable = callable
|
||||||
except NameError:
|
except NameError:
|
||||||
def callable(obj):
|
def callable(obj):
|
||||||
return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
|
return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
|
||||||
|
|
||||||
|
|
||||||
if PY3:
|
if PY3:
|
||||||
def get_unbound_function(unbound):
|
def get_unbound_function(unbound):
|
||||||
return unbound
|
return unbound
|
||||||
|
|
||||||
|
|
||||||
create_bound_method = types.MethodType
|
create_bound_method = types.MethodType
|
||||||
|
|
||||||
Iterator = object
|
Iterator = object
|
||||||
|
@ -406,19 +402,21 @@ else:
|
||||||
def get_unbound_function(unbound):
|
def get_unbound_function(unbound):
|
||||||
return unbound.im_func
|
return unbound.im_func
|
||||||
|
|
||||||
|
|
||||||
def create_bound_method(func, obj):
|
def create_bound_method(func, obj):
|
||||||
return types.MethodType(func, obj, obj.__class__)
|
return types.MethodType(func, obj, obj.__class__)
|
||||||
|
|
||||||
|
|
||||||
class Iterator(object):
|
class Iterator(object):
|
||||||
|
|
||||||
def next(self):
|
def next(self):
|
||||||
return type(self).__next__(self)
|
return type(self).__next__(self)
|
||||||
|
|
||||||
|
|
||||||
callable = callable
|
callable = callable
|
||||||
_add_doc(get_unbound_function,
|
_add_doc(get_unbound_function,
|
||||||
"""Get the function out of a possibly unbound function""")
|
"""Get the function out of a possibly unbound function""")
|
||||||
|
|
||||||
|
|
||||||
get_method_function = operator.attrgetter(_meth_func)
|
get_method_function = operator.attrgetter(_meth_func)
|
||||||
get_method_self = operator.attrgetter(_meth_self)
|
get_method_self = operator.attrgetter(_meth_self)
|
||||||
get_function_closure = operator.attrgetter(_func_closure)
|
get_function_closure = operator.attrgetter(_func_closure)
|
||||||
|
@ -431,14 +429,17 @@ def iterkeys(d, **kw):
|
||||||
"""Return an iterator over the keys of a dictionary."""
|
"""Return an iterator over the keys of a dictionary."""
|
||||||
return iter(getattr(d, _iterkeys)(**kw))
|
return iter(getattr(d, _iterkeys)(**kw))
|
||||||
|
|
||||||
|
|
||||||
def itervalues(d, **kw):
|
def itervalues(d, **kw):
|
||||||
"""Return an iterator over the values of a dictionary."""
|
"""Return an iterator over the values of a dictionary."""
|
||||||
return iter(getattr(d, _itervalues)(**kw))
|
return iter(getattr(d, _itervalues)(**kw))
|
||||||
|
|
||||||
|
|
||||||
def iteritems(d, **kw):
|
def iteritems(d, **kw):
|
||||||
"""Return an iterator over the (key, value) pairs of a dictionary."""
|
"""Return an iterator over the (key, value) pairs of a dictionary."""
|
||||||
return iter(getattr(d, _iteritems)(**kw))
|
return iter(getattr(d, _iteritems)(**kw))
|
||||||
|
|
||||||
|
|
||||||
def iterlists(d, **kw):
|
def iterlists(d, **kw):
|
||||||
"""Return an iterator over the (key, [values]) pairs of a dictionary."""
|
"""Return an iterator over the (key, [values]) pairs of a dictionary."""
|
||||||
return iter(getattr(d, _iterlists)(**kw))
|
return iter(getattr(d, _iterlists)(**kw))
|
||||||
|
@ -447,8 +448,12 @@ def iterlists(d, **kw):
|
||||||
if PY3:
|
if PY3:
|
||||||
def b(s):
|
def b(s):
|
||||||
return s.encode("latin-1")
|
return s.encode("latin-1")
|
||||||
|
|
||||||
|
|
||||||
def u(s):
|
def u(s):
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
unichr = chr
|
unichr = chr
|
||||||
if sys.version_info[1] <= 1:
|
if sys.version_info[1] <= 1:
|
||||||
def int2byte(i):
|
def int2byte(i):
|
||||||
|
@ -460,29 +465,43 @@ if PY3:
|
||||||
indexbytes = operator.getitem
|
indexbytes = operator.getitem
|
||||||
iterbytes = iter
|
iterbytes = iter
|
||||||
import io
|
import io
|
||||||
|
|
||||||
StringIO = io.StringIO
|
StringIO = io.StringIO
|
||||||
BytesIO = io.BytesIO
|
BytesIO = io.BytesIO
|
||||||
else:
|
else:
|
||||||
def b(s):
|
def b(s):
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
def u(s):
|
def u(s):
|
||||||
return unicode(s, "unicode_escape")
|
return unicode(s, "unicode_escape")
|
||||||
|
|
||||||
|
|
||||||
unichr = unichr
|
unichr = unichr
|
||||||
int2byte = chr
|
int2byte = chr
|
||||||
|
|
||||||
|
|
||||||
def byte2int(bs):
|
def byte2int(bs):
|
||||||
return ord(bs[0])
|
return ord(bs[0])
|
||||||
|
|
||||||
|
|
||||||
def indexbytes(buf, i):
|
def indexbytes(buf, i):
|
||||||
return ord(buf[i])
|
return ord(buf[i])
|
||||||
|
|
||||||
|
|
||||||
def iterbytes(buf):
|
def iterbytes(buf):
|
||||||
return (ord(byte) for byte in buf)
|
return (ord(byte) for byte in buf)
|
||||||
|
|
||||||
|
|
||||||
import StringIO
|
import StringIO
|
||||||
|
|
||||||
StringIO = BytesIO = StringIO.StringIO
|
StringIO = BytesIO = StringIO.StringIO
|
||||||
_add_doc(b, """Byte literal""")
|
_add_doc(b, """Byte literal""")
|
||||||
_add_doc(u, """Text literal""")
|
_add_doc(u, """Text literal""")
|
||||||
|
|
||||||
|
|
||||||
if PY3:
|
if PY3:
|
||||||
import builtins
|
import builtins
|
||||||
|
|
||||||
exec_ = getattr(builtins, "exec")
|
exec_ = getattr(builtins, "exec")
|
||||||
|
|
||||||
|
|
||||||
|
@ -506,7 +525,7 @@ else:
|
||||||
del frame
|
del frame
|
||||||
elif _locs_ is None:
|
elif _locs_ is None:
|
||||||
_locs_ = _globs_
|
_locs_ = _globs_
|
||||||
exec("""exec _code_ in _globs_, _locs_""")
|
exec ("""exec _code_ in _globs_, _locs_""")
|
||||||
|
|
||||||
|
|
||||||
exec_("""def reraise(tp, value, tb=None):
|
exec_("""def reraise(tp, value, tb=None):
|
||||||
|
@ -519,10 +538,12 @@ else:
|
||||||
fp = kwargs.pop("file", sys.stdout)
|
fp = kwargs.pop("file", sys.stdout)
|
||||||
if fp is None:
|
if fp is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
def write(data):
|
def write(data):
|
||||||
if not isinstance(data, basestring):
|
if not isinstance(data, basestring):
|
||||||
data = str(data)
|
data = str(data)
|
||||||
fp.write(data)
|
fp.write(data)
|
||||||
|
|
||||||
want_unicode = False
|
want_unicode = False
|
||||||
sep = kwargs.pop("sep", None)
|
sep = kwargs.pop("sep", None)
|
||||||
if sep is not None:
|
if sep is not None:
|
||||||
|
@ -566,8 +587,10 @@ def with_metaclass(meta, *bases):
|
||||||
"""Create a base class with a metaclass."""
|
"""Create a base class with a metaclass."""
|
||||||
return meta("NewBase", bases, {})
|
return meta("NewBase", bases, {})
|
||||||
|
|
||||||
|
|
||||||
def add_metaclass(metaclass):
|
def add_metaclass(metaclass):
|
||||||
"""Class decorator for creating a class with a metaclass."""
|
"""Class decorator for creating a class with a metaclass."""
|
||||||
|
|
||||||
def wrapper(cls):
|
def wrapper(cls):
|
||||||
orig_vars = cls.__dict__.copy()
|
orig_vars = cls.__dict__.copy()
|
||||||
orig_vars.pop('__dict__', None)
|
orig_vars.pop('__dict__', None)
|
||||||
|
@ -575,4 +598,5 @@ def add_metaclass(metaclass):
|
||||||
for slots_var in orig_vars.get('__slots__', ()):
|
for slots_var in orig_vars.get('__slots__', ()):
|
||||||
orig_vars.pop(slots_var)
|
orig_vars.pop(slots_var)
|
||||||
return metaclass(cls.__name__, cls.__bases__, orig_vars)
|
return metaclass(cls.__name__, cls.__bases__, orig_vars)
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
|
@ -13,14 +13,15 @@ from six import integer_types, string_types, text_type, iteritems
|
||||||
def get_status_old(code):
|
def get_status_old(code):
|
||||||
"""Get the torrent status using old status codes"""
|
"""Get the torrent status using old status codes"""
|
||||||
mapping = {
|
mapping = {
|
||||||
(1<<0): 'check pending',
|
(1 << 0): 'check pending',
|
||||||
(1<<1): 'checking',
|
(1 << 1): 'checking',
|
||||||
(1<<2): 'downloading',
|
(1 << 2): 'downloading',
|
||||||
(1<<3): 'seeding',
|
(1 << 3): 'seeding',
|
||||||
(1<<4): 'stopped',
|
(1 << 4): 'stopped',
|
||||||
}
|
}
|
||||||
return mapping[code]
|
return mapping[code]
|
||||||
|
|
||||||
|
|
||||||
def get_status_new(code):
|
def get_status_new(code):
|
||||||
"""Get the torrent status using new status codes"""
|
"""Get the torrent status using new status codes"""
|
||||||
mapping = {
|
mapping = {
|
||||||
|
@ -34,6 +35,7 @@ def get_status_new(code):
|
||||||
}
|
}
|
||||||
return mapping[code]
|
return mapping[code]
|
||||||
|
|
||||||
|
|
||||||
class Torrent(object):
|
class Torrent(object):
|
||||||
"""
|
"""
|
||||||
Torrent is a class holding the data received from Transmission regarding a bittorrent transfer.
|
Torrent is a class holding the data received from Transmission regarding a bittorrent transfer.
|
||||||
|
@ -99,8 +101,9 @@ class Torrent(object):
|
||||||
|
|
||||||
def _dirty_fields(self):
|
def _dirty_fields(self):
|
||||||
"""Enumerate changed fields"""
|
"""Enumerate changed fields"""
|
||||||
outgoing_keys = ['bandwidthPriority', 'downloadLimit', 'downloadLimited', 'peer_limit', 'queuePosition'
|
outgoing_keys = ['bandwidthPriority', 'downloadLimit', 'downloadLimited', 'peer_limit', 'queuePosition',
|
||||||
, 'seedIdleLimit', 'seedIdleMode', 'seedRatioLimit', 'seedRatioMode', 'uploadLimit', 'uploadLimited']
|
'seedIdleLimit', 'seedIdleMode', 'seedRatioLimit', 'seedRatioMode', 'uploadLimit',
|
||||||
|
'uploadLimited']
|
||||||
fields = []
|
fields = []
|
||||||
for key in outgoing_keys:
|
for key in outgoing_keys:
|
||||||
if key in self._fields and self._fields[key].dirty:
|
if key in self._fields and self._fields[key].dirty:
|
||||||
|
@ -131,7 +134,7 @@ class Torrent(object):
|
||||||
else:
|
else:
|
||||||
raise ValueError('Cannot update with supplied data')
|
raise ValueError('Cannot update with supplied data')
|
||||||
self._incoming_pending = False
|
self._incoming_pending = False
|
||||||
|
|
||||||
def _status(self):
|
def _status(self):
|
||||||
"""Get the torrent status"""
|
"""Get the torrent status"""
|
||||||
code = self._fields['status'].value
|
code = self._fields['status'].value
|
||||||
|
@ -270,7 +273,8 @@ class Torrent(object):
|
||||||
else:
|
else:
|
||||||
raise ValueError("Not a valid limit")
|
raise ValueError("Not a valid limit")
|
||||||
|
|
||||||
download_limit = property(_get_download_limit, _set_download_limit, None, "Download limit in Kbps or None. This is a mutator.")
|
download_limit = property(_get_download_limit, _set_download_limit, None,
|
||||||
|
"Download limit in Kbps or None. This is a mutator.")
|
||||||
|
|
||||||
def _get_peer_limit(self):
|
def _get_peer_limit(self):
|
||||||
"""
|
"""
|
||||||
|
@ -307,7 +311,7 @@ class Torrent(object):
|
||||||
self._push()
|
self._push()
|
||||||
|
|
||||||
priority = property(_get_priority, _set_priority, None
|
priority = property(_get_priority, _set_priority, None
|
||||||
, "Bandwidth priority as string. Can be one of 'low', 'normal', 'high'. This is a mutator.")
|
, "Bandwidth priority as string. Can be one of 'low', 'normal', 'high'. This is a mutator.")
|
||||||
|
|
||||||
def _get_seed_idle_limit(self):
|
def _get_seed_idle_limit(self):
|
||||||
"""
|
"""
|
||||||
|
@ -326,7 +330,7 @@ class Torrent(object):
|
||||||
raise ValueError("Not a valid limit")
|
raise ValueError("Not a valid limit")
|
||||||
|
|
||||||
seed_idle_limit = property(_get_seed_idle_limit, _set_seed_idle_limit, None
|
seed_idle_limit = property(_get_seed_idle_limit, _set_seed_idle_limit, None
|
||||||
, "Torrent seed idle limit in minutes. Also see seed_idle_mode. This is a mutator.")
|
, "Torrent seed idle limit in minutes. Also see seed_idle_mode. This is a mutator.")
|
||||||
|
|
||||||
def _get_seed_idle_mode(self):
|
def _get_seed_idle_mode(self):
|
||||||
"""
|
"""
|
||||||
|
@ -345,7 +349,7 @@ class Torrent(object):
|
||||||
raise ValueError("Not a valid limit")
|
raise ValueError("Not a valid limit")
|
||||||
|
|
||||||
seed_idle_mode = property(_get_seed_idle_mode, _set_seed_idle_mode, None,
|
seed_idle_mode = property(_get_seed_idle_mode, _set_seed_idle_mode, None,
|
||||||
"""
|
"""
|
||||||
Seed idle mode as string. Can be one of 'global', 'single' or 'unlimited'.
|
Seed idle mode as string. Can be one of 'global', 'single' or 'unlimited'.
|
||||||
|
|
||||||
* global, use session seed idle limit.
|
* global, use session seed idle limit.
|
||||||
|
@ -354,7 +358,7 @@ class Torrent(object):
|
||||||
|
|
||||||
This is a mutator.
|
This is a mutator.
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
def _get_seed_ratio_limit(self):
|
def _get_seed_ratio_limit(self):
|
||||||
"""
|
"""
|
||||||
|
@ -373,7 +377,7 @@ class Torrent(object):
|
||||||
raise ValueError("Not a valid limit")
|
raise ValueError("Not a valid limit")
|
||||||
|
|
||||||
seed_ratio_limit = property(_get_seed_ratio_limit, _set_seed_ratio_limit, None
|
seed_ratio_limit = property(_get_seed_ratio_limit, _set_seed_ratio_limit, None
|
||||||
, "Torrent seed ratio limit as float. Also see seed_ratio_mode. This is a mutator.")
|
, "Torrent seed ratio limit as float. Also see seed_ratio_mode. This is a mutator.")
|
||||||
|
|
||||||
def _get_seed_ratio_mode(self):
|
def _get_seed_ratio_mode(self):
|
||||||
"""
|
"""
|
||||||
|
@ -392,7 +396,7 @@ class Torrent(object):
|
||||||
raise ValueError("Not a valid limit")
|
raise ValueError("Not a valid limit")
|
||||||
|
|
||||||
seed_ratio_mode = property(_get_seed_ratio_mode, _set_seed_ratio_mode, None,
|
seed_ratio_mode = property(_get_seed_ratio_mode, _set_seed_ratio_mode, None,
|
||||||
"""
|
"""
|
||||||
Seed ratio mode as string. Can be one of 'global', 'single' or 'unlimited'.
|
Seed ratio mode as string. Can be one of 'global', 'single' or 'unlimited'.
|
||||||
|
|
||||||
* global, use session seed ratio limit.
|
* global, use session seed ratio limit.
|
||||||
|
@ -401,7 +405,7 @@ class Torrent(object):
|
||||||
|
|
||||||
This is a mutator.
|
This is a mutator.
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
def _get_upload_limit(self):
|
def _get_upload_limit(self):
|
||||||
"""
|
"""
|
||||||
|
@ -428,7 +432,8 @@ class Torrent(object):
|
||||||
else:
|
else:
|
||||||
raise ValueError("Not a valid limit")
|
raise ValueError("Not a valid limit")
|
||||||
|
|
||||||
upload_limit = property(_get_upload_limit, _set_upload_limit, None, "Upload limit in Kbps or None. This is a mutator.")
|
upload_limit = property(_get_upload_limit, _set_upload_limit, None,
|
||||||
|
"Upload limit in Kbps or None. This is a mutator.")
|
||||||
|
|
||||||
def _get_queue_position(self):
|
def _get_queue_position(self):
|
||||||
"""Get the queue position for this torrent."""
|
"""Get the queue position for this torrent."""
|
||||||
|
|
|
@ -10,6 +10,7 @@ from six import string_types, iteritems
|
||||||
|
|
||||||
UNITS = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB']
|
UNITS = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB']
|
||||||
|
|
||||||
|
|
||||||
def format_size(size):
|
def format_size(size):
|
||||||
"""
|
"""
|
||||||
Format byte size into IEC prefixes, B, KiB, MiB ...
|
Format byte size into IEC prefixes, B, KiB, MiB ...
|
||||||
|
@ -21,6 +22,7 @@ def format_size(size):
|
||||||
size /= 1024.0
|
size /= 1024.0
|
||||||
return (size, UNITS[i])
|
return (size, UNITS[i])
|
||||||
|
|
||||||
|
|
||||||
def format_speed(size):
|
def format_speed(size):
|
||||||
"""
|
"""
|
||||||
Format bytes per second speed into IEC prefixes, B/s, KiB/s, MiB/s ...
|
Format bytes per second speed into IEC prefixes, B/s, KiB/s, MiB/s ...
|
||||||
|
@ -28,6 +30,7 @@ def format_speed(size):
|
||||||
(size, unit) = format_size(size)
|
(size, unit) = format_size(size)
|
||||||
return (size, unit + '/s')
|
return (size, unit + '/s')
|
||||||
|
|
||||||
|
|
||||||
def format_timedelta(delta):
|
def format_timedelta(delta):
|
||||||
"""
|
"""
|
||||||
Format datetime.timedelta into <days> <hours>:<minutes>:<seconds>.
|
Format datetime.timedelta into <days> <hours>:<minutes>:<seconds>.
|
||||||
|
@ -36,6 +39,7 @@ def format_timedelta(delta):
|
||||||
hours, minutes = divmod(minutes, 60)
|
hours, minutes = divmod(minutes, 60)
|
||||||
return '%d %02d:%02d:%02d' % (delta.days, hours, minutes, seconds)
|
return '%d %02d:%02d:%02d' % (delta.days, hours, minutes, seconds)
|
||||||
|
|
||||||
|
|
||||||
def format_timestamp(timestamp, utc=False):
|
def format_timestamp(timestamp, utc=False):
|
||||||
"""
|
"""
|
||||||
Format unix timestamp into ISO date format.
|
Format unix timestamp into ISO date format.
|
||||||
|
@ -49,12 +53,14 @@ def format_timestamp(timestamp, utc=False):
|
||||||
else:
|
else:
|
||||||
return '-'
|
return '-'
|
||||||
|
|
||||||
|
|
||||||
class INetAddressError(Exception):
|
class INetAddressError(Exception):
|
||||||
"""
|
"""
|
||||||
Error parsing / generating a internet address.
|
Error parsing / generating a internet address.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def inet_address(address, default_port, default_address='localhost'):
|
def inet_address(address, default_port, default_address='localhost'):
|
||||||
"""
|
"""
|
||||||
Parse internet address.
|
Parse internet address.
|
||||||
|
@ -84,6 +90,7 @@ def inet_address(address, default_port, default_address='localhost'):
|
||||||
raise INetAddressError('Cannot look up address "%s".' % address)
|
raise INetAddressError('Cannot look up address "%s".' % address)
|
||||||
return (addr, port)
|
return (addr, port)
|
||||||
|
|
||||||
|
|
||||||
def rpc_bool(arg):
|
def rpc_bool(arg):
|
||||||
"""
|
"""
|
||||||
Convert between Python boolean and Transmission RPC boolean.
|
Convert between Python boolean and Transmission RPC boolean.
|
||||||
|
@ -95,27 +102,31 @@ def rpc_bool(arg):
|
||||||
arg = arg.lower() in ['true', 'yes']
|
arg = arg.lower() in ['true', 'yes']
|
||||||
return 1 if bool(arg) else 0
|
return 1 if bool(arg) else 0
|
||||||
|
|
||||||
|
|
||||||
TR_TYPE_MAP = {
|
TR_TYPE_MAP = {
|
||||||
'number' : int,
|
'number': int,
|
||||||
'string' : str,
|
'string': str,
|
||||||
'double': float,
|
'double': float,
|
||||||
'boolean' : rpc_bool,
|
'boolean': rpc_bool,
|
||||||
'array': list,
|
'array': list,
|
||||||
'object': dict
|
'object': dict
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def make_python_name(name):
|
def make_python_name(name):
|
||||||
"""
|
"""
|
||||||
Convert Transmission RPC name to python compatible name.
|
Convert Transmission RPC name to python compatible name.
|
||||||
"""
|
"""
|
||||||
return name.replace('-', '_')
|
return name.replace('-', '_')
|
||||||
|
|
||||||
|
|
||||||
def make_rpc_name(name):
|
def make_rpc_name(name):
|
||||||
"""
|
"""
|
||||||
Convert python compatible name to Transmission RPC name.
|
Convert python compatible name to Transmission RPC name.
|
||||||
"""
|
"""
|
||||||
return name.replace('_', '-')
|
return name.replace('_', '-')
|
||||||
|
|
||||||
|
|
||||||
def argument_value_convert(method, argument, value, rpc_version):
|
def argument_value_convert(method, argument, value, rpc_version):
|
||||||
"""
|
"""
|
||||||
Check and fix Transmission RPC issues with regards to methods, arguments and values.
|
Check and fix Transmission RPC issues with regards to methods, arguments and values.
|
||||||
|
@ -154,6 +165,7 @@ def argument_value_convert(method, argument, value, rpc_version):
|
||||||
raise ValueError('Argument "%s" does not exists for method "%s".',
|
raise ValueError('Argument "%s" does not exists for method "%s".',
|
||||||
(argument, method))
|
(argument, method))
|
||||||
|
|
||||||
|
|
||||||
def get_arguments(method, rpc_version):
|
def get_arguments(method, rpc_version):
|
||||||
"""
|
"""
|
||||||
Get arguments for method in specified Transmission RPC version.
|
Get arguments for method in specified Transmission RPC version.
|
||||||
|
@ -175,6 +187,7 @@ def get_arguments(method, rpc_version):
|
||||||
accessible.append(argument)
|
accessible.append(argument)
|
||||||
return accessible
|
return accessible
|
||||||
|
|
||||||
|
|
||||||
def add_stdout_logger(level='debug'):
|
def add_stdout_logger(level='debug'):
|
||||||
"""
|
"""
|
||||||
Add a stdout target for the transmissionrpc logging.
|
Add a stdout target for the transmissionrpc logging.
|
||||||
|
@ -189,6 +202,7 @@ def add_stdout_logger(level='debug'):
|
||||||
loghandler.setLevel(loglevel)
|
loghandler.setLevel(loglevel)
|
||||||
trpc_logger.addHandler(loghandler)
|
trpc_logger.addHandler(loghandler)
|
||||||
|
|
||||||
|
|
||||||
def add_file_logger(filepath, level='debug'):
|
def add_file_logger(filepath, level='debug'):
|
||||||
"""
|
"""
|
||||||
Add a stdout target for the transmissionrpc logging.
|
Add a stdout target for the transmissionrpc logging.
|
||||||
|
@ -203,4 +217,5 @@ def add_file_logger(filepath, level='debug'):
|
||||||
loghandler.setLevel(loglevel)
|
loghandler.setLevel(loglevel)
|
||||||
trpc_logger.addHandler(loghandler)
|
trpc_logger.addHandler(loghandler)
|
||||||
|
|
||||||
|
|
||||||
Field = namedtuple('Field', ['value', 'dirty'])
|
Field = namedtuple('Field', ['value', 'dirty'])
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
|
|
|
@ -1,29 +1,31 @@
|
||||||
#coding=utf8
|
# coding=utf8
|
||||||
|
|
||||||
import urllib
|
import urllib
|
||||||
import urllib2
|
import urllib2
|
||||||
import urlparse
|
import urlparse
|
||||||
import cookielib
|
import cookielib
|
||||||
import re
|
import re
|
||||||
import StringIO
|
import StringIO
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import json
|
import json
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import simplejson as json
|
import simplejson as json
|
||||||
|
|
||||||
from core.utorrent.upload import MultiPartForm
|
from core.utorrent.upload import MultiPartForm
|
||||||
|
|
||||||
class UTorrentClient(object):
|
|
||||||
|
|
||||||
|
class UTorrentClient(object):
|
||||||
def __init__(self, base_url, username, password):
|
def __init__(self, base_url, username, password):
|
||||||
self.base_url = base_url
|
self.base_url = base_url
|
||||||
self.username = username
|
self.username = username
|
||||||
self.password = password
|
self.password = password
|
||||||
self.opener = self._make_opener('uTorrent', base_url, username, password)
|
self.opener = self._make_opener('uTorrent', base_url, username, password)
|
||||||
self.token = self._get_token()
|
self.token = self._get_token()
|
||||||
#TODO refresh token, when necessary
|
# TODO refresh token, when necessary
|
||||||
|
|
||||||
def _make_opener(self, realm, base_url, username, password):
|
def _make_opener(self, realm, base_url, username, password):
|
||||||
'''uTorrent API need HTTP Basic Auth and cookie support for token verify.'''
|
"""uTorrent API need HTTP Basic Auth and cookie support for token verify."""
|
||||||
|
|
||||||
auth_handler = urllib2.HTTPBasicAuthHandler()
|
auth_handler = urllib2.HTTPBasicAuthHandler()
|
||||||
auth_handler.add_password(realm=realm,
|
auth_handler.add_password(realm=realm,
|
||||||
|
@ -31,7 +33,7 @@ class UTorrentClient(object):
|
||||||
user=username,
|
user=username,
|
||||||
passwd=password)
|
passwd=password)
|
||||||
opener = urllib2.build_opener(auth_handler)
|
opener = urllib2.build_opener(auth_handler)
|
||||||
urllib2.install_opener(opener)
|
urllib2.install_opener(opener)
|
||||||
|
|
||||||
cookie_jar = cookielib.CookieJar()
|
cookie_jar = cookielib.CookieJar()
|
||||||
cookie_handler = urllib2.HTTPCookieProcessor(cookie_jar)
|
cookie_handler = urllib2.HTTPCookieProcessor(cookie_jar)
|
||||||
|
@ -47,69 +49,68 @@ class UTorrentClient(object):
|
||||||
match = re.search(token_re, response.read())
|
match = re.search(token_re, response.read())
|
||||||
return match.group(1)
|
return match.group(1)
|
||||||
|
|
||||||
|
|
||||||
def list(self, **kwargs):
|
def list(self, **kwargs):
|
||||||
params = [('list', '1')]
|
params = [('list', '1')]
|
||||||
params += kwargs.items()
|
params += kwargs.items()
|
||||||
return self._action(params)
|
return self._action(params)
|
||||||
|
|
||||||
def start(self, *hashes):
|
def start(self, *hashes):
|
||||||
params = [('action', 'start'),]
|
params = [('action', 'start'), ]
|
||||||
for hash in hashes:
|
for hash in hashes:
|
||||||
params.append(('hash', hash))
|
params.append(('hash', hash))
|
||||||
return self._action(params)
|
return self._action(params)
|
||||||
|
|
||||||
def stop(self, *hashes):
|
def stop(self, *hashes):
|
||||||
params = [('action', 'stop'),]
|
params = [('action', 'stop'), ]
|
||||||
for hash in hashes:
|
for hash in hashes:
|
||||||
params.append(('hash', hash))
|
params.append(('hash', hash))
|
||||||
return self._action(params)
|
return self._action(params)
|
||||||
|
|
||||||
def pause(self, *hashes):
|
def pause(self, *hashes):
|
||||||
params = [('action', 'pause'),]
|
params = [('action', 'pause'), ]
|
||||||
for hash in hashes:
|
for hash in hashes:
|
||||||
params.append(('hash', hash))
|
params.append(('hash', hash))
|
||||||
return self._action(params)
|
return self._action(params)
|
||||||
|
|
||||||
def forcestart(self, *hashes):
|
def forcestart(self, *hashes):
|
||||||
params = [('action', 'forcestart'),]
|
params = [('action', 'forcestart'), ]
|
||||||
for hash in hashes:
|
for hash in hashes:
|
||||||
params.append(('hash', hash))
|
params.append(('hash', hash))
|
||||||
return self._action(params)
|
return self._action(params)
|
||||||
|
|
||||||
def remove(self, *hashes):
|
def remove(self, *hashes):
|
||||||
params = [('action', 'remove'),]
|
params = [('action', 'remove'), ]
|
||||||
for hash in hashes:
|
for hash in hashes:
|
||||||
params.append(('hash', hash))
|
params.append(('hash', hash))
|
||||||
return self._action(params)
|
return self._action(params)
|
||||||
|
|
||||||
def removedata(self, *hashes):
|
def removedata(self, *hashes):
|
||||||
params = [('action', 'removedata'),]
|
params = [('action', 'removedata'), ]
|
||||||
for hash in hashes:
|
for hash in hashes:
|
||||||
params.append(('hash', hash))
|
params.append(('hash', hash))
|
||||||
return self._action(params)
|
return self._action(params)
|
||||||
|
|
||||||
def recheck(self, *hashes):
|
def recheck(self, *hashes):
|
||||||
params = [('action', 'recheck'),]
|
params = [('action', 'recheck'), ]
|
||||||
for hash in hashes:
|
for hash in hashes:
|
||||||
params.append(('hash', hash))
|
params.append(('hash', hash))
|
||||||
return self._action(params)
|
return self._action(params)
|
||||||
|
|
||||||
def getfiles(self, hash):
|
def getfiles(self, hash):
|
||||||
params = [('action', 'getfiles'), ('hash', hash)]
|
params = [('action', 'getfiles'), ('hash', hash)]
|
||||||
return self._action(params)
|
return self._action(params)
|
||||||
|
|
||||||
def getprops(self, hash):
|
def getprops(self, hash):
|
||||||
params = [('action', 'getprops'), ('hash', hash)]
|
params = [('action', 'getprops'), ('hash', hash)]
|
||||||
return self._action(params)
|
return self._action(params)
|
||||||
|
|
||||||
def setprio(self, hash, priority, *files):
|
def setprio(self, hash, priority, *files):
|
||||||
params = [('action', 'setprio'), ('hash', hash), ('p', str(priority))]
|
params = [('action', 'setprio'), ('hash', hash), ('p', str(priority))]
|
||||||
for file_index in files:
|
for file_index in files:
|
||||||
params.append(('f', str(file_index)))
|
params.append(('f', str(file_index)))
|
||||||
|
|
||||||
return self._action(params)
|
return self._action(params)
|
||||||
|
|
||||||
def addfile(self, filename, filepath=None, bytes=None):
|
def addfile(self, filename, filepath=None, bytes=None):
|
||||||
params = [('action', 'add-file')]
|
params = [('action', 'add-file')]
|
||||||
|
|
||||||
|
@ -118,13 +119,13 @@ class UTorrentClient(object):
|
||||||
file_handler = open(filepath)
|
file_handler = open(filepath)
|
||||||
else:
|
else:
|
||||||
file_handler = StringIO.StringIO(bytes)
|
file_handler = StringIO.StringIO(bytes)
|
||||||
|
|
||||||
form.add_file('torrent_file', filename.encode('utf-8'), file_handler)
|
form.add_file('torrent_file', filename.encode('utf-8'), file_handler)
|
||||||
|
|
||||||
return self._action(params, str(form), form.get_content_type())
|
return self._action(params, str(form), form.get_content_type())
|
||||||
|
|
||||||
def _action(self, params, body=None, content_type=None):
|
def _action(self, params, body=None, content_type=None):
|
||||||
#about token, see https://github.com/bittorrent/webui/wiki/TokenSystem
|
# about token, see https://github.com/bittorrent/webui/wiki/TokenSystem
|
||||||
url = self.base_url + '?token=' + self.token + '&' + urllib.urlencode(params)
|
url = self.base_url + '?token=' + self.token + '&' + urllib.urlencode(params)
|
||||||
request = urllib2.Request(url)
|
request = urllib2.Request(url)
|
||||||
|
|
||||||
|
@ -137,6 +138,5 @@ class UTorrentClient(object):
|
||||||
try:
|
try:
|
||||||
response = self.opener.open(request)
|
response = self.opener.open(request)
|
||||||
return response.code, json.loads(response.read())
|
return response.code, json.loads(response.read())
|
||||||
except urllib2.HTTPError,e:
|
except urllib2.HTTPError, e:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
#code copied from http://www.doughellmann.com/PyMOTW/urllib2/
|
# code copied from http://www.doughellmann.com/PyMOTW/urllib2/
|
||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
import mimetools
|
import mimetools
|
||||||
|
@ -14,7 +14,7 @@ class MultiPartForm(object):
|
||||||
self.files = []
|
self.files = []
|
||||||
self.boundary = mimetools.choose_boundary()
|
self.boundary = mimetools.choose_boundary()
|
||||||
return
|
return
|
||||||
|
|
||||||
def get_content_type(self):
|
def get_content_type(self):
|
||||||
return 'multipart/form-data; boundary=%s' % self.boundary
|
return 'multipart/form-data; boundary=%s' % self.boundary
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ class MultiPartForm(object):
|
||||||
mimetype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
|
mimetype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
|
||||||
self.files.append((fieldname, filename, mimetype, body))
|
self.files.append((fieldname, filename, mimetype, body))
|
||||||
return
|
return
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""Return a string representing the form data, including attached files."""
|
"""Return a string representing the form data, including attached files."""
|
||||||
# Build a list of lists, each containing "lines" of the
|
# Build a list of lists, each containing "lines" of the
|
||||||
|
@ -39,29 +39,28 @@ class MultiPartForm(object):
|
||||||
# line is separated by '\r\n'.
|
# line is separated by '\r\n'.
|
||||||
parts = []
|
parts = []
|
||||||
part_boundary = '--' + self.boundary
|
part_boundary = '--' + self.boundary
|
||||||
|
|
||||||
# Add the form fields
|
# Add the form fields
|
||||||
parts.extend(
|
parts.extend(
|
||||||
[ part_boundary,
|
[part_boundary,
|
||||||
'Content-Disposition: form-data; name="%s"' % name,
|
'Content-Disposition: form-data; name="%s"' % name,
|
||||||
'',
|
'',
|
||||||
value,
|
value,
|
||||||
]
|
]
|
||||||
for name, value in self.form_fields
|
for name, value in self.form_fields
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add the files to upload
|
# Add the files to upload
|
||||||
parts.extend(
|
parts.extend(
|
||||||
[ part_boundary,
|
[part_boundary,
|
||||||
'Content-Disposition: file; name="%s"; filename="%s"' % \
|
'Content-Disposition: file; name="%s"; filename="%s"' % (field_name, filename),
|
||||||
(field_name, filename),
|
'Content-Type: %s' % content_type,
|
||||||
'Content-Type: %s' % content_type,
|
'',
|
||||||
'',
|
body,
|
||||||
body,
|
]
|
||||||
]
|
|
||||||
for field_name, filename, content_type, body in self.files
|
for field_name, filename, content_type, body in self.files
|
||||||
)
|
)
|
||||||
|
|
||||||
# Flatten the list and add closing boundary marker,
|
# Flatten the list and add closing boundary marker,
|
||||||
# then return CR+LF separated data
|
# then return CR+LF separated data
|
||||||
flattened = list(itertools.chain(*parts))
|
flattened = list(itertools.chain(*parts))
|
||||||
|
|
|
@ -16,6 +16,7 @@ import gh_api as github
|
||||||
import core
|
import core
|
||||||
from core import logger
|
from core import logger
|
||||||
|
|
||||||
|
|
||||||
class CheckVersion():
|
class CheckVersion():
|
||||||
"""
|
"""
|
||||||
Version check class meant to run as a thread object with the SB scheduler.
|
Version check class meant to run as a thread object with the SB scheduler.
|
||||||
|
@ -80,6 +81,7 @@ class CheckVersion():
|
||||||
if self.updater.need_update():
|
if self.updater.need_update():
|
||||||
return self.updater.update()
|
return self.updater.update()
|
||||||
|
|
||||||
|
|
||||||
class UpdateManager():
|
class UpdateManager():
|
||||||
def get_github_repo_user(self):
|
def get_github_repo_user(self):
|
||||||
return core.GIT_USER
|
return core.GIT_USER
|
||||||
|
@ -90,6 +92,7 @@ class UpdateManager():
|
||||||
def get_github_branch(self):
|
def get_github_branch(self):
|
||||||
return core.GIT_BRANCH
|
return core.GIT_BRANCH
|
||||||
|
|
||||||
|
|
||||||
class GitUpdateManager(UpdateManager):
|
class GitUpdateManager(UpdateManager):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._git_path = self._find_working_git()
|
self._git_path = self._find_working_git()
|
||||||
|
@ -103,7 +106,8 @@ class GitUpdateManager(UpdateManager):
|
||||||
self._num_commits_ahead = 0
|
self._num_commits_ahead = 0
|
||||||
|
|
||||||
def _git_error(self):
|
def _git_error(self):
|
||||||
logger.debug('Unable to find your git executable - Set git_path in your autoProcessMedia.cfg OR delete your .git folder and run from source to enable updates.')
|
logger.debug(
|
||||||
|
'Unable to find your git executable - Set git_path in your autoProcessMedia.cfg OR delete your .git folder and run from source to enable updates.')
|
||||||
|
|
||||||
def _find_working_git(self):
|
def _find_working_git(self):
|
||||||
test_cmd = 'version'
|
test_cmd = 'version'
|
||||||
|
@ -148,7 +152,8 @@ class GitUpdateManager(UpdateManager):
|
||||||
logger.log(u"Not using: " + cur_git, logger.DEBUG)
|
logger.log(u"Not using: " + cur_git, logger.DEBUG)
|
||||||
|
|
||||||
# Still haven't found a working git
|
# Still haven't found a working git
|
||||||
logger.debug('Unable to find your git executable - Set git_path in your autoProcessMedia.cfg OR delete your .git folder and run from source to enable updates.')
|
logger.debug(
|
||||||
|
'Unable to find your git executable - Set git_path in your autoProcessMedia.cfg OR delete your .git folder and run from source to enable updates.')
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -279,9 +284,10 @@ class GitUpdateManager(UpdateManager):
|
||||||
logger.log(u"git didn't return numbers for behind and ahead, not using it", logger.DEBUG)
|
logger.log(u"git didn't return numbers for behind and ahead, not using it", logger.DEBUG)
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.log(u"cur_commit = " + str(self._cur_commit_hash) + u" % (newest_commit)= " + str(self._newest_commit_hash)
|
logger.log(
|
||||||
+ u", num_commits_behind = " + str(self._num_commits_behind) + u", num_commits_ahead = " + str(
|
u"cur_commit = " + str(self._cur_commit_hash) + u" % (newest_commit)= " + str(self._newest_commit_hash) +
|
||||||
self._num_commits_ahead), logger.DEBUG)
|
u", num_commits_behind = " + str(self._num_commits_behind) + u", num_commits_ahead = " +
|
||||||
|
str(self._num_commits_ahead), logger.DEBUG)
|
||||||
|
|
||||||
def set_newest_text(self):
|
def set_newest_text(self):
|
||||||
if self._num_commits_ahead:
|
if self._num_commits_ahead:
|
||||||
|
@ -411,8 +417,9 @@ class SourceUpdateManager(UpdateManager):
|
||||||
# when _cur_commit_hash doesn't match anything _num_commits_behind == 100
|
# when _cur_commit_hash doesn't match anything _num_commits_behind == 100
|
||||||
self._num_commits_behind += 1
|
self._num_commits_behind += 1
|
||||||
|
|
||||||
logger.log(u"cur_commit = " + str(self._cur_commit_hash) + u" % (newest_commit)= " + str(self._newest_commit_hash)
|
logger.log(
|
||||||
+ u", num_commits_behind = " + str(self._num_commits_behind), logger.DEBUG)
|
u"cur_commit = " + str(self._cur_commit_hash) + u" % (newest_commit)= " + str(self._newest_commit_hash) +
|
||||||
|
u", num_commits_behind = " + str(self._num_commits_behind), logger.DEBUG)
|
||||||
|
|
||||||
def set_newest_text(self):
|
def set_newest_text(self):
|
||||||
|
|
||||||
|
@ -489,9 +496,9 @@ class SourceUpdateManager(UpdateManager):
|
||||||
old_path = os.path.join(content_dir, dirname, curfile)
|
old_path = os.path.join(content_dir, dirname, curfile)
|
||||||
new_path = os.path.join(core.PROGRAM_DIR, dirname, curfile)
|
new_path = os.path.join(core.PROGRAM_DIR, dirname, curfile)
|
||||||
|
|
||||||
#Avoid DLL access problem on WIN32/64
|
# Avoid DLL access problem on WIN32/64
|
||||||
#These files needing to be updated manually
|
# These files needing to be updated manually
|
||||||
#or find a way to kill the access from memory
|
# or find a way to kill the access from memory
|
||||||
if curfile in ('unrar.dll', 'unrar64.dll'):
|
if curfile in ('unrar.dll', 'unrar64.dll'):
|
||||||
try:
|
try:
|
||||||
os.chmod(new_path, stat.S_IWRITE)
|
os.chmod(new_path, stat.S_IWRITE)
|
||||||
|
@ -519,4 +526,4 @@ class SourceUpdateManager(UpdateManager):
|
||||||
logger.log(u"Traceback: " + traceback.format_exc(), logger.DEBUG)
|
logger.log(u"Traceback: " + traceback.format_exc(), logger.DEBUG)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue