mirror of
https://github.com/clinton-hall/nzbToMedia.git
synced 2025-03-12 12:35:28 -07:00
Merge branch 'nightly' into dev
This commit is contained in:
commit
cbd0c25c88
TorrentToMedia.py_config.ymlautoProcessMedia.cfg.specazure-pipelines.ymlcleanup.py
core
eol.pylibs/common/qbittorrent
nzbToMedia.pynzbToWatcher3.pytests
@ -79,7 +79,7 @@ def process_torrent(input_directory, input_name, input_category, input_hash, inp
|
||||
if section is None: #Check for user_scripts for 'ALL' and 'UNCAT'
|
||||
if usercat in core.CATEGORIES:
|
||||
section = core.CFG.findsection('ALL').isenabled()
|
||||
usercat = 'ALL'
|
||||
usercat = 'ALL'
|
||||
else:
|
||||
section = core.CFG.findsection('UNCAT').isenabled()
|
||||
usercat = 'UNCAT'
|
||||
@ -213,7 +213,7 @@ def process_torrent(input_directory, input_name, input_category, input_hash, inp
|
||||
core.flatten(output_destination)
|
||||
|
||||
# Now check if video files exist in destination:
|
||||
if section_name in ['SickBeard', 'NzbDrone', 'Sonarr', 'CouchPotato', 'Radarr']:
|
||||
if section_name in ['SickBeard', 'NzbDrone', 'Sonarr', 'CouchPotato', 'Radarr', 'Watcher3']:
|
||||
num_videos = len(
|
||||
core.list_media_files(output_destination, media=True, audio=False, meta=False, archives=False))
|
||||
if num_videos > 0:
|
||||
@ -227,7 +227,7 @@ def process_torrent(input_directory, input_name, input_category, input_hash, inp
|
||||
|
||||
# Only these sections can handling failed downloads
|
||||
# so make sure everything else gets through without the check for failed
|
||||
if section_name not in ['CouchPotato', 'Radarr', 'SickBeard', 'NzbDrone', 'Sonarr']:
|
||||
if section_name not in ['CouchPotato', 'Radarr', 'SickBeard', 'NzbDrone', 'Sonarr', 'Watcher3']:
|
||||
status = 0
|
||||
|
||||
logger.info('Calling {0}:{1} to post-process:{2}'.format(section_name, usercat, input_name))
|
||||
@ -241,7 +241,7 @@ def process_torrent(input_directory, input_name, input_category, input_hash, inp
|
||||
)
|
||||
if section_name == 'UserScript':
|
||||
result = external_script(output_destination, input_name, input_category, section)
|
||||
elif section_name in ['CouchPotato', 'Radarr']:
|
||||
elif section_name in ['CouchPotato', 'Radarr', 'Watcher3']:
|
||||
result = movies.process(section_name, output_destination, input_name, status, client_agent, input_hash, input_category)
|
||||
elif section_name in ['SickBeard', 'NzbDrone', 'Sonarr']:
|
||||
if input_hash:
|
||||
|
1
_config.yml
Normal file
1
_config.yml
Normal file
@ -0,0 +1 @@
|
||||
theme: jekyll-theme-cayman
|
@ -12,7 +12,7 @@
|
||||
git_user =
|
||||
# GitHUB branch for repo
|
||||
git_branch =
|
||||
# Enable/Disable forceful cleaning of leftover files following postprocess
|
||||
# Enable/Disable forceful cleaning of leftover files following postprocess
|
||||
force_clean = 0
|
||||
# Enable/Disable logging debug messages to nzbtomedia.log
|
||||
log_debug = 0
|
||||
@ -36,7 +36,7 @@
|
||||
[Posix]
|
||||
### Process priority setting for External commands (Extractor and Transcoder) on Posix (Unix/Linux/OSX) systems.
|
||||
# Set the Niceness value for the nice command. These range from -20 (most favorable to the process) to 19 (least favorable to the process).
|
||||
# If entering an integer e.g 'niceness = 4', this is added to the nice command and passed as 'nice -n4' (Default).
|
||||
# If entering an integer e.g 'niceness = 4', this is added to the nice command and passed as 'nice -n4' (Default).
|
||||
# If entering a comma separated list e.g. 'niceness = nice,4' this will be passed as 'nice 4' (Safer).
|
||||
niceness = nice,-n0
|
||||
# Set the ionice scheduling class. 0 for none, 1 for real time, 2 for best-effort, 3 for idle.
|
||||
@ -111,6 +111,36 @@
|
||||
##### Set to define import behavior Move or Copy
|
||||
importMode = Copy
|
||||
|
||||
[Watcher3]
|
||||
#### autoProcessing for Movies
|
||||
#### movie - category that gets called for post-processing with CPS
|
||||
[[movie]]
|
||||
enabled = 0
|
||||
apikey =
|
||||
host = localhost
|
||||
port = 9090
|
||||
###### ADVANCED USE - ONLY EDIT IF YOU KNOW WHAT YOU'RE DOING ######
|
||||
ssl = 0
|
||||
web_root =
|
||||
# api key for www.omdbapi.com (used as alternative to imdb)
|
||||
omdbapikey =
|
||||
# Enable/Disable linking for Torrents
|
||||
Torrent_NoLink = 0
|
||||
keep_archive = 1
|
||||
delete_failed = 0
|
||||
wait_for = 0
|
||||
extract = 1
|
||||
# Set this to minimum required size to consider a media file valid (in MB)
|
||||
minSize = 0
|
||||
# Enable/Disable deleting ignored files (samples and invalid media files)
|
||||
delete_ignored = 0
|
||||
##### Enable if Watcher3 is on a remote server for this category
|
||||
remote_path = 0
|
||||
##### Set to path where download client places completed downloads locally for this category
|
||||
watch_dir =
|
||||
##### Set the recursive directory permissions to the following (0 to disable)
|
||||
chmodDirectory = 0
|
||||
|
||||
[SickBeard]
|
||||
#### autoProcessing for TV Series
|
||||
#### tv - category that gets called for post-processing with SB
|
||||
@ -266,7 +296,7 @@
|
||||
apikey =
|
||||
host = localhost
|
||||
port = 8085
|
||||
######
|
||||
######
|
||||
library = Set to path where you want the processed games to be moved to.
|
||||
###### ADVANCED USE - ONLY EDIT IF YOU KNOW WHAT YOU'RE DOING ######
|
||||
ssl = 0
|
||||
@ -312,7 +342,7 @@
|
||||
[Network]
|
||||
# Enter Mount points as LocalPath,RemotePath and separate each pair with '|'
|
||||
# e.g. MountPoints = /volume1/Public/,E:\|/volume2/share/,\\NAS\
|
||||
mount_points =
|
||||
mount_points =
|
||||
|
||||
[Nzb]
|
||||
###### clientAgent - Supported clients: sabnzbd, nzbget
|
||||
@ -331,7 +361,7 @@
|
||||
useLink = hard
|
||||
###### outputDirectory - Default output directory (categories will be appended as sub directory to outputDirectory)
|
||||
outputDirectory = /abs/path/to/complete/
|
||||
###### Enter the default path to your default download directory (non-category downloads). this directory is protected by safe_mode.
|
||||
###### Enter the default path to your default download directory (non-category downloads). this directory is protected by safe_mode.
|
||||
default_downloadDirectory =
|
||||
###### Other categories/labels defined for your downloader. Does not include CouchPotato, SickBeard, HeadPhones, Mylar categories.
|
||||
categories = music_videos,pictures,software,manual
|
||||
@ -374,15 +404,15 @@
|
||||
plex_host = localhost
|
||||
plex_port = 32400
|
||||
plex_token =
|
||||
plex_ssl = 0
|
||||
plex_ssl = 0
|
||||
# Enter Plex category to section mapping as Category,section and separate each pair with '|'
|
||||
# e.g. plex_sections = movie,3|tv,4
|
||||
plex_sections =
|
||||
plex_sections =
|
||||
|
||||
[Transcoder]
|
||||
# getsubs. enable to download subtitles.
|
||||
getSubs = 0
|
||||
# subLanguages. create a list of languages in the order you want them in your subtitles.
|
||||
# subLanguages. create a list of languages in the order you want them in your subtitles.
|
||||
subLanguages = eng,spa,fra
|
||||
# transcode. enable to use transcoder
|
||||
transcode = 0
|
||||
@ -397,7 +427,7 @@
|
||||
# outputQualityPercent. used as -q:a value. 0 will disable this from being used.
|
||||
outputQualityPercent = 0
|
||||
# outputVideoPath. Set path you want transcoded videos moved to. Leave blank to disable.
|
||||
outputVideoPath =
|
||||
outputVideoPath =
|
||||
# processOutput. 1 will send the outputVideoPath to SickBeard/CouchPotato. 0 will send original files.
|
||||
processOutput = 0
|
||||
# audioLanguage. set the 3 letter language code you want as your primary audio track.
|
||||
@ -427,7 +457,7 @@
|
||||
#### Define custom settings below.
|
||||
outputVideoExtension = .mp4
|
||||
outputVideoCodec = libx264
|
||||
VideoCodecAllow =
|
||||
VideoCodecAllow =
|
||||
outputVideoPreset = medium
|
||||
outputVideoResolution = 1920:1080
|
||||
outputVideoFramerate = 24
|
||||
@ -435,15 +465,15 @@
|
||||
outputVideoCRF = 19
|
||||
outputVideoLevel = 3.1
|
||||
outputAudioCodec = ac3
|
||||
AudioCodecAllow =
|
||||
AudioCodecAllow =
|
||||
outputAudioChannels = 6
|
||||
outputAudioBitrate = 640k
|
||||
outputAudioTrack2Codec = libfaac
|
||||
AudioCodec2Allow =
|
||||
outputAudioTrack2Channels = 2
|
||||
AudioCodec2Allow =
|
||||
outputAudioTrack2Channels = 2
|
||||
outputAudioTrack2Bitrate = 128000
|
||||
outputAudioOtherCodec = libmp3lame
|
||||
AudioOtherCodecAllow =
|
||||
AudioOtherCodecAllow =
|
||||
outputAudioOtherChannels =
|
||||
outputAudioOtherBitrate = 128000
|
||||
outputSubtitleCodec =
|
||||
@ -500,4 +530,4 @@
|
||||
# enter a list (comma separated) of Group Tags you want removed from filenames to help with subtitle matching.
|
||||
# e.g remove_group = [rarbag],-NZBgeek
|
||||
# be careful if your "group" is a common "real" word. Please report if you have any group replacements that would fall in this category.
|
||||
remove_group =
|
||||
remove_group =
|
||||
|
@ -21,45 +21,14 @@ jobs:
|
||||
python.version: '3.6'
|
||||
Python37:
|
||||
python.version: '3.7'
|
||||
maxParallel: 4
|
||||
Python38:
|
||||
python.version: '3.8'
|
||||
maxParallel: 5
|
||||
|
||||
steps:
|
||||
#- script: |
|
||||
# Make sure all packages are pulled from latest
|
||||
#sudo apt-get update
|
||||
|
||||
# Fail out if any setups fail
|
||||
#set -e
|
||||
|
||||
# Delete old Pythons
|
||||
#rm -rf $AGENT_TOOLSDIRECTORY/Python/2.7.16
|
||||
#rm -rf $AGENT_TOOLSDIRECTORY/Python/3.5.7
|
||||
#rm -rf $AGENT_TOOLSDIRECTORY/Python/3.7.3
|
||||
|
||||
# Download new Pythons
|
||||
#azcopy --recursive \
|
||||
#--source https://vstsagenttools.blob.core.windows.net/tools/hostedtoolcache/linux/Python/2.7.15 \
|
||||
#--destination $AGENT_TOOLSDIRECTORY/Python/2.7.15
|
||||
|
||||
#azcopy --recursive \
|
||||
#--source https://vstsagenttools.blob.core.windows.net/tools/hostedtoolcache/linux/Python/3.5.5 \
|
||||
#--destination $AGENT_TOOLSDIRECTORY/Python/3.5.5
|
||||
|
||||
#azcopy --recursive \
|
||||
#--source https://vstsagenttools.blob.core.windows.net/tools/hostedtoolcache/linux/Python/3.7.2 \
|
||||
#--destination $AGENT_TOOLSDIRECTORY/Python/3.7.2
|
||||
|
||||
# Install new Pythons
|
||||
#original_directory=$PWD
|
||||
#setups=$(find $AGENT_TOOLSDIRECTORY/Python -name setup.sh)
|
||||
#for setup in $setups; do
|
||||
#chmod +x $setup;
|
||||
#cd $(dirname $setup);
|
||||
#./$(basename $setup);
|
||||
#cd $original_directory;
|
||||
#done;
|
||||
#displayName: 'Workaround: update apt and roll back Python versions'
|
||||
|
||||
- script: sudo apt-get install ffmpeg
|
||||
displayName: 'Install ffmpeg'
|
||||
|
||||
- task: UsePythonVersion@0
|
||||
inputs:
|
||||
versionSpec: '$(python.version)'
|
||||
@ -68,9 +37,6 @@ jobs:
|
||||
- script: python -m pip install --upgrade pip
|
||||
displayName: 'Install dependencies'
|
||||
|
||||
- script: sudo apt-get install ffmpeg
|
||||
displayName: 'Install ffmpeg'
|
||||
|
||||
- script: |
|
||||
pip install pytest
|
||||
pytest tests --doctest-modules --junitxml=junit/test-results.xml
|
||||
|
@ -12,6 +12,8 @@ import subprocess
|
||||
import sys
|
||||
import shutil
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
FOLDER_STRUCTURE = {
|
||||
'libs': [
|
||||
'common',
|
||||
|
@ -72,6 +72,8 @@ def process(section, dir_name, input_name=None, status=0, client_agent='manual',
|
||||
base_url = '{0}{1}:{2}{3}/api/command'.format(protocol, host, port, web_root)
|
||||
url2 = '{0}{1}:{2}{3}/api/config/downloadClient'.format(protocol, host, port, web_root)
|
||||
headers = {'X-Api-Key': apikey}
|
||||
if section == 'Watcher3':
|
||||
base_url = '{0}{1}:{2}{3}/postprocessing'.format(protocol, host, port, web_root)
|
||||
if not apikey:
|
||||
logger.info('No CouchPotato or Radarr apikey entered. Performing transcoder functions only')
|
||||
release = None
|
||||
@ -178,7 +180,7 @@ def process(section, dir_name, input_name=None, status=0, client_agent='manual',
|
||||
os.rename(video, video2)
|
||||
|
||||
if not apikey: # If only using Transcoder functions, exit here.
|
||||
logger.info('No CouchPotato or Radarr apikey entered. Processing completed.')
|
||||
logger.info('No CouchPotato or Radarr or Watcher3 apikey entered. Processing completed.')
|
||||
return ProcessResult(
|
||||
message='{0}: Successfully post-processed {1}'.format(section, input_name),
|
||||
status_code=0,
|
||||
@ -210,9 +212,20 @@ def process(section, dir_name, input_name=None, status=0, client_agent='manual',
|
||||
logger.debug('Opening URL: {0} with PARAMS: {1}'.format(base_url, payload), section)
|
||||
logger.postprocess('Starting DownloadedMoviesScan scan for {0}'.format(input_name), section)
|
||||
|
||||
if section == 'Watcher3':
|
||||
if input_name and os.path.isfile(os.path.join(dir_name, input_name)):
|
||||
params['media_folder'] = os.path.join(params['media_folder'], input_name)
|
||||
payload = {'apikey': apikey, 'path': params['media_folder'], 'guid': download_id, 'mode': 'complete'}
|
||||
if not download_id:
|
||||
payload.pop('guid')
|
||||
logger.debug('Opening URL: {0} with PARAMS: {1}'.format(base_url, payload), section)
|
||||
logger.postprocess('Starting postprocessing scan for {0}'.format(input_name), section)
|
||||
|
||||
try:
|
||||
if section == 'CouchPotato':
|
||||
r = requests.get(url, params=params, verify=False, timeout=(30, 1800))
|
||||
elif section == 'Watcher3':
|
||||
r = requests.post(base_url, data=payload, verify=False, timeout=(30, 1800))
|
||||
else:
|
||||
r = requests.post(base_url, data=json.dumps(payload), headers=headers, stream=True, verify=False, timeout=(30, 1800))
|
||||
except requests.ConnectionError:
|
||||
@ -239,12 +252,23 @@ def process(section, dir_name, input_name=None, status=0, client_agent='manual',
|
||||
elif section == 'Radarr':
|
||||
logger.postprocess('Radarr response: {0}'.format(result['state']))
|
||||
try:
|
||||
res = json.loads(r.content)
|
||||
scan_id = int(res['id'])
|
||||
scan_id = int(result['id'])
|
||||
logger.debug('Scan started with id: {0}'.format(scan_id), section)
|
||||
except Exception as e:
|
||||
logger.warning('No scan id was returned due to: {0}'.format(e), section)
|
||||
scan_id = None
|
||||
elif section == 'Watcher3' and result['status'] == 'finished':
|
||||
logger.postprocess('Watcher3 updated status to {0}'.format(result['tasks']['update_movie_status']))
|
||||
if result['tasks']['update_movie_status'] == 'Finished':
|
||||
return ProcessResult(
|
||||
message='{0}: Successfully post-processed {1}'.format(section, input_name),
|
||||
status_code=status,
|
||||
)
|
||||
else:
|
||||
return ProcessResult(
|
||||
message='{0}: Failed to post-process - changed status to {1}'.format(section, result['tasks']['update_movie_status']),
|
||||
status_code=1,
|
||||
)
|
||||
else:
|
||||
logger.error('FAILED: {0} scan was unable to finish for folder {1}. exiting!'.format(method, dir_name),
|
||||
section)
|
||||
@ -264,6 +288,20 @@ def process(section, dir_name, input_name=None, status=0, client_agent='manual',
|
||||
message='{0}: Sending failed download back to {0}'.format(section),
|
||||
status_code=1, # Return as failed to flag this in the downloader.
|
||||
) # Return failed flag, but log the event as successful.
|
||||
elif section == 'Watcher3':
|
||||
logger.postprocess('Sending failed download to {0} for CDH processing'.format(section), section)
|
||||
path = remote_dir(dir_name) if remote_path else dir_name
|
||||
if input_name and os.path.isfile(os.path.join(dir_name, input_name)):
|
||||
path = os.path.join(path, input_name)
|
||||
payload = {'apikey': apikey, 'path': path, 'guid': download_id, 'mode': 'failed'}
|
||||
r = requests.post(base_url, data=payload, verify=False, timeout=(30, 1800))
|
||||
result = r.json()
|
||||
logger.postprocess('Watcher3 response: {0}'.format(result))
|
||||
if result['status'] == 'finished':
|
||||
return ProcessResult(
|
||||
message='{0}: Sending failed download back to {0}'.format(section),
|
||||
status_code=1, # Return as failed to flag this in the downloader.
|
||||
) # Return failed flag, but log the event as successful.
|
||||
|
||||
if delete_failed and os.path.isdir(dir_name) and not os.path.dirname(dir_name) == dir_name:
|
||||
logger.postprocess('Deleting failed files and folder {0}'.format(dir_name), section)
|
||||
|
@ -150,7 +150,7 @@ class ConfigObj(configobj.ConfigObj, Section):
|
||||
if CFG_OLD[section].sections:
|
||||
subsections.update({section: CFG_OLD[section].sections})
|
||||
for option, value in CFG_OLD[section].items():
|
||||
if option in ['category', 'cpsCategory', 'sbCategory', 'hpCategory', 'mlCategory', 'gzCategory', 'raCategory', 'ndCategory']:
|
||||
if option in ['category', 'cpsCategory', 'sbCategory', 'hpCategory', 'mlCategory', 'gzCategory', 'raCategory', 'ndCategory', 'W3Category']:
|
||||
if not isinstance(value, list):
|
||||
value = [value]
|
||||
|
||||
@ -271,6 +271,16 @@ class ConfigObj(configobj.ConfigObj, Section):
|
||||
logger.warning('{x} category is set for CouchPotato and Radarr. '
|
||||
'Please check your config in NZBGet'.format
|
||||
(x=os.environ['NZBPO_RACATEGORY']))
|
||||
if 'NZBPO_RACATEGORY' in os.environ and 'NZBPO_W3CATEGORY' in os.environ:
|
||||
if os.environ['NZBPO_RACATEGORY'] == os.environ['NZBPO_W3CATEGORY']:
|
||||
logger.warning('{x} category is set for Watcher3 and Radarr. '
|
||||
'Please check your config in NZBGet'.format
|
||||
(x=os.environ['NZBPO_RACATEGORY']))
|
||||
if 'NZBPO_W3CATEGORY' in os.environ and 'NZBPO_CPSCATEGORY' in os.environ:
|
||||
if os.environ['NZBPO_W3CATEGORY'] == os.environ['NZBPO_CPSCATEGORY']:
|
||||
logger.warning('{x} category is set for CouchPotato and Watcher3. '
|
||||
'Please check your config in NZBGet'.format
|
||||
(x=os.environ['NZBPO_W3CATEGORY']))
|
||||
if 'NZBPO_LICATEGORY' in os.environ and 'NZBPO_HPCATEGORY' in os.environ:
|
||||
if os.environ['NZBPO_LICATEGORY'] == os.environ['NZBPO_HPCATEGORY']:
|
||||
logger.warning('{x} category is set for HeadPhones and Lidarr. '
|
||||
@ -321,6 +331,29 @@ class ConfigObj(configobj.ConfigObj, Section):
|
||||
cfg_new[section][os.environ[env_cat_key]]['enabled'] = 1
|
||||
if os.environ[env_cat_key] in cfg_new['Radarr'].sections:
|
||||
cfg_new['Radarr'][env_cat_key]['enabled'] = 0
|
||||
if os.environ[env_cat_key] in cfg_new['Watcher3'].sections:
|
||||
cfg_new['Watcher3'][env_cat_key]['enabled'] = 0
|
||||
|
||||
section = 'Watcher3'
|
||||
env_cat_key = 'NZBPO_W3CATEGORY'
|
||||
env_keys = ['ENABLED', 'APIKEY', 'HOST', 'PORT', 'SSL', 'WEB_ROOT', 'METHOD', 'DELETE_FAILED', 'REMOTE_PATH',
|
||||
'WAIT_FOR', 'WATCH_DIR', 'OMDBAPIKEY']
|
||||
cfg_keys = ['enabled', 'apikey', 'host', 'port', 'ssl', 'web_root', 'method', 'delete_failed', 'remote_path',
|
||||
'wait_for', 'watch_dir', 'omdbapikey']
|
||||
if env_cat_key in os.environ:
|
||||
for index in range(len(env_keys)):
|
||||
key = 'NZBPO_W3{index}'.format(index=env_keys[index])
|
||||
if key in os.environ:
|
||||
option = cfg_keys[index]
|
||||
value = os.environ[key]
|
||||
if os.environ[env_cat_key] not in cfg_new[section].sections:
|
||||
cfg_new[section][os.environ[env_cat_key]] = {}
|
||||
cfg_new[section][os.environ[env_cat_key]][option] = value
|
||||
cfg_new[section][os.environ[env_cat_key]]['enabled'] = 1
|
||||
if os.environ[env_cat_key] in cfg_new['Radarr'].sections:
|
||||
cfg_new['Radarr'][env_cat_key]['enabled'] = 0
|
||||
if os.environ[env_cat_key] in cfg_new['CouchPotato'].sections:
|
||||
cfg_new['CouchPotato'][env_cat_key]['enabled'] = 0
|
||||
|
||||
section = 'SickBeard'
|
||||
env_cat_key = 'NZBPO_SBCATEGORY'
|
||||
@ -444,6 +477,8 @@ class ConfigObj(configobj.ConfigObj, Section):
|
||||
cfg_new[section][os.environ[env_cat_key]]['enabled'] = 1
|
||||
if os.environ[env_cat_key] in cfg_new['CouchPotato'].sections:
|
||||
cfg_new['CouchPotato'][env_cat_key]['enabled'] = 0
|
||||
if os.environ[env_cat_key] in cfg_new['Wacther3'].sections:
|
||||
cfg_new['Watcher3'][env_cat_key]['enabled'] = 0
|
||||
|
||||
section = 'Lidarr'
|
||||
env_cat_key = 'NZBPO_LICATEGORY'
|
||||
|
@ -168,7 +168,8 @@ def auto_fork(section, input_category):
|
||||
else:
|
||||
logger.info('{section}:{category} fork auto-detection failed'.format
|
||||
(section=section, category=input_category))
|
||||
fork = core.FORKS.items()[core.FORKS.keys().index(core.FORK_DEFAULT)]
|
||||
fork = list(core.FORKS.items())[list(core.FORKS.keys()).index(core.FORK_DEFAULT)]
|
||||
|
||||
|
||||
logger.info('{section}:{category} fork set to {fork}'.format
|
||||
(section=section, category=input_category, fork=fork[0]))
|
||||
|
@ -11,6 +11,10 @@ import subliminal
|
||||
import core
|
||||
from core import logger
|
||||
|
||||
for provider in subliminal.provider_manager.internal_extensions:
|
||||
if provider not in [str(x) for x in subliminal.provider_manager.list_entry_points()]:
|
||||
subliminal.provider_manager.register(str(provider))
|
||||
|
||||
|
||||
def import_subs(filename):
|
||||
if not core.GETSUBS:
|
||||
|
@ -53,10 +53,11 @@ def move_file(mediafile, path, link):
|
||||
title = os.path.splitext(os.path.basename(mediafile))[0]
|
||||
new_path = os.path.join(path, sanitize_name(title))
|
||||
|
||||
try:
|
||||
new_path = new_path.encode(core.SYS_ENCODING)
|
||||
except Exception:
|
||||
pass
|
||||
# Removed as encoding of directory no-longer required
|
||||
#try:
|
||||
# new_path = new_path.encode(core.SYS_ENCODING)
|
||||
#except Exception:
|
||||
# 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).
|
||||
if os.path.isfile(new_path):
|
||||
|
@ -53,7 +53,7 @@ class CheckVersion(object):
|
||||
'source': running from source without git
|
||||
"""
|
||||
# check if we're a windows build
|
||||
if os.path.isdir(os.path.join(core.APP_ROOT, u'.git')):
|
||||
if os.path.exists(os.path.join(core.APP_ROOT, u'.git')):
|
||||
install_type = 'git'
|
||||
else:
|
||||
install_type = 'source'
|
||||
|
1
eol.py
1
eol.py
@ -28,6 +28,7 @@ def date(string, fmt='%Y-%m-%d'):
|
||||
# https://devguide.python.org/
|
||||
# https://devguide.python.org/devcycle/#devcycle
|
||||
PYTHON_EOL = {
|
||||
(3, 8): date('2024-10-14'),
|
||||
(3, 7): date('2023-06-27'),
|
||||
(3, 6): date('2021-12-23'),
|
||||
(3, 5): date('2020-09-13'),
|
||||
|
@ -1,7 +1,6 @@
|
||||
import requests
|
||||
import json
|
||||
|
||||
|
||||
class LoginRequired(Exception):
|
||||
def __str__(self):
|
||||
return 'Please login first.'
|
||||
@ -15,7 +14,7 @@ class Client(object):
|
||||
self.url = url
|
||||
|
||||
session = requests.Session()
|
||||
check_prefs = session.get(url+'query/preferences')
|
||||
check_prefs = session.get(url+'api/v2/app/preferences')
|
||||
|
||||
if check_prefs.status_code == 200:
|
||||
self._is_authenticated = True
|
||||
@ -24,9 +23,9 @@ class Client(object):
|
||||
elif check_prefs.status_code == 404:
|
||||
self._is_authenticated = False
|
||||
raise RuntimeError("""
|
||||
This wrapper only supports qBittorrent applications
|
||||
with version higher than 3.1.x.
|
||||
Please use the latest qBittorrent release.
|
||||
This wrapper only supports qBittorrent applications with
|
||||
version higher than 4.1.0 (which implemented Web API v2.0).
|
||||
Please use the latest qBittorrent release.
|
||||
""")
|
||||
|
||||
else:
|
||||
@ -35,10 +34,8 @@ class Client(object):
|
||||
def _get(self, endpoint, **kwargs):
|
||||
"""
|
||||
Method to perform GET request on the API.
|
||||
|
||||
:param endpoint: Endpoint of the API.
|
||||
:param kwargs: Other keyword arguments for requests.
|
||||
|
||||
:return: Response of the GET request.
|
||||
"""
|
||||
return self._request(endpoint, 'get', **kwargs)
|
||||
@ -46,11 +43,9 @@ class Client(object):
|
||||
def _post(self, endpoint, data, **kwargs):
|
||||
"""
|
||||
Method to perform POST request on the API.
|
||||
|
||||
:param endpoint: Endpoint of the API.
|
||||
:param data: POST DATA for the request.
|
||||
:param kwargs: Other keyword arguments for requests.
|
||||
|
||||
:return: Response of the POST request.
|
||||
"""
|
||||
return self._request(endpoint, 'post', data, **kwargs)
|
||||
@ -58,12 +53,10 @@ class Client(object):
|
||||
def _request(self, endpoint, method, data=None, **kwargs):
|
||||
"""
|
||||
Method to hanle both GET and POST requests.
|
||||
|
||||
:param endpoint: Endpoint of the API.
|
||||
:param method: Method of HTTP request.
|
||||
:param data: POST DATA for the request.
|
||||
:param kwargs: Other keyword arguments.
|
||||
|
||||
:return: Response for the request.
|
||||
"""
|
||||
final_url = self.url + endpoint
|
||||
@ -93,18 +86,15 @@ class Client(object):
|
||||
def login(self, username='admin', password='admin'):
|
||||
"""
|
||||
Method to authenticate the qBittorrent Client.
|
||||
|
||||
Declares a class attribute named ``session`` which
|
||||
stores the authenticated session if the login is correct.
|
||||
Else, shows the login error.
|
||||
|
||||
:param username: Username.
|
||||
:param password: Password.
|
||||
|
||||
:return: Response to login request to the API.
|
||||
"""
|
||||
self.session = requests.Session()
|
||||
login = self.session.post(self.url+'login',
|
||||
login = self.session.post(self.url+'api/v2/auth/login',
|
||||
data={'username': username,
|
||||
'password': password})
|
||||
if login.text == 'Ok.':
|
||||
@ -116,7 +106,7 @@ class Client(object):
|
||||
"""
|
||||
Logout the current session.
|
||||
"""
|
||||
response = self._get('logout')
|
||||
response = self._get('api/v2/auth/logout')
|
||||
self._is_authenticated = False
|
||||
return response
|
||||
|
||||
@ -125,39 +115,31 @@ class Client(object):
|
||||
"""
|
||||
Get qBittorrent version.
|
||||
"""
|
||||
return self._get('version/qbittorrent')
|
||||
return self._get('api/v2/app/version')
|
||||
|
||||
@property
|
||||
def api_version(self):
|
||||
"""
|
||||
Get WEB API version.
|
||||
"""
|
||||
return self._get('version/api')
|
||||
|
||||
@property
|
||||
def api_min_version(self):
|
||||
"""
|
||||
Get minimum WEB API version.
|
||||
"""
|
||||
return self._get('version/api_min')
|
||||
return self._get('api/v2/app/webapiVersion')
|
||||
|
||||
def shutdown(self):
|
||||
"""
|
||||
Shutdown qBittorrent.
|
||||
"""
|
||||
return self._get('command/shutdown')
|
||||
return self._get('api/v2/app/shutdown')
|
||||
|
||||
def torrents(self, **filters):
|
||||
"""
|
||||
Returns a list of torrents matching the supplied filters.
|
||||
|
||||
:param filter: Current status of the torrents.
|
||||
:param category: Fetch all torrents with the supplied label.
|
||||
:param sort: Sort torrents by.
|
||||
:param reverse: Enable reverse sorting.
|
||||
:param limit: Limit the number of torrents returned.
|
||||
:param offset: Set offset (if less than 0, offset from end).
|
||||
|
||||
:param hashes: Filter by hashes. Can contain multiple hashes separated by |.
|
||||
:return: list() of torrent with matching filter.
|
||||
"""
|
||||
params = {}
|
||||
@ -166,46 +148,42 @@ class Client(object):
|
||||
name = 'filter' if name == 'status' else name
|
||||
params[name] = value
|
||||
|
||||
return self._get('query/torrents', params=params)
|
||||
return self._get('api/v2/torrents/info', params=params)
|
||||
|
||||
def get_torrent(self, infohash):
|
||||
"""
|
||||
Get details of the torrent.
|
||||
|
||||
:param infohash: INFO HASH of the torrent.
|
||||
"""
|
||||
return self._get('query/propertiesGeneral/' + infohash.lower())
|
||||
return self._get('api/v2/torrents/properties', params={'hash': infohash.lower()})
|
||||
|
||||
def get_torrent_trackers(self, infohash):
|
||||
"""
|
||||
Get trackers for the torrent.
|
||||
|
||||
:param infohash: INFO HASH of the torrent.
|
||||
"""
|
||||
return self._get('query/propertiesTrackers/' + infohash.lower())
|
||||
return self._get('api/v2/torrents/trackers', params={'hash': infohash.lower()})
|
||||
|
||||
def get_torrent_webseeds(self, infohash):
|
||||
"""
|
||||
Get webseeds for the torrent.
|
||||
|
||||
:param infohash: INFO HASH of the torrent.
|
||||
"""
|
||||
return self._get('query/propertiesWebSeeds/' + infohash.lower())
|
||||
return self._get('api/v2/torrents/webseeds', params={'hash': infohash.lower()})
|
||||
|
||||
def get_torrent_files(self, infohash):
|
||||
"""
|
||||
Get list of files for the torrent.
|
||||
|
||||
:param infohash: INFO HASH of the torrent.
|
||||
"""
|
||||
return self._get('query/propertiesFiles/' + infohash.lower())
|
||||
return self._get('api/v2/torrents/files', params={'hash': infohash.lower()})
|
||||
|
||||
@property
|
||||
def global_transfer_info(self):
|
||||
"""
|
||||
Get JSON data of the global transfer info of qBittorrent.
|
||||
"""
|
||||
return self._get('query/transferInfo')
|
||||
return self._get('api/v2/transfer/info')
|
||||
|
||||
@property
|
||||
def preferences(self):
|
||||
@ -214,39 +192,27 @@ class Client(object):
|
||||
Can also be used to assign individual preferences.
|
||||
For setting multiple preferences at once,
|
||||
see ``set_preferences`` method.
|
||||
|
||||
Note: Even if this is a ``property``,
|
||||
to fetch the current preferences dict, you are required
|
||||
to call it like a bound method.
|
||||
|
||||
Wrong::
|
||||
|
||||
qb.preferences
|
||||
|
||||
Right::
|
||||
|
||||
qb.preferences()
|
||||
|
||||
"""
|
||||
prefs = self._get('query/preferences')
|
||||
prefs = self._get('api/v2/app/preferences')
|
||||
|
||||
class Proxy(Client):
|
||||
"""
|
||||
Proxy class to to allow assignment of individual preferences.
|
||||
this class overrides some methods to ease things.
|
||||
|
||||
Because of this, settings can be assigned like::
|
||||
|
||||
In [5]: prefs = qb.preferences()
|
||||
|
||||
In [6]: prefs['autorun_enabled']
|
||||
Out[6]: True
|
||||
|
||||
In [7]: prefs['autorun_enabled'] = False
|
||||
|
||||
In [8]: prefs['autorun_enabled']
|
||||
Out[8]: False
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, url, prefs, auth, session):
|
||||
@ -270,78 +236,74 @@ class Client(object):
|
||||
def sync(self, rid=0):
|
||||
"""
|
||||
Sync the torrents by supplied LAST RESPONSE ID.
|
||||
Read more @ http://git.io/vEgXr
|
||||
|
||||
Read more @ https://git.io/fxgB8
|
||||
:param rid: Response ID of last request.
|
||||
"""
|
||||
return self._get('sync/maindata', params={'rid': rid})
|
||||
return self._get('api/v2/sync/maindata', params={'rid': rid})
|
||||
|
||||
def download_from_link(self, link, **kwargs):
|
||||
"""
|
||||
Download torrent using a link.
|
||||
|
||||
:param link: URL Link or list of.
|
||||
:param savepath: Path to download the torrent.
|
||||
:param category: Label or Category of the torrent(s).
|
||||
|
||||
:return: Empty JSON data.
|
||||
"""
|
||||
# old:new format
|
||||
old_arg_map = {'save_path': 'savepath'} # , 'label': 'category'}
|
||||
|
||||
# convert old option names to new option names
|
||||
options = kwargs.copy()
|
||||
for old_arg, new_arg in old_arg_map.items():
|
||||
if options.get(old_arg) and not options.get(new_arg):
|
||||
options[new_arg] = options[old_arg]
|
||||
|
||||
options['urls'] = link
|
||||
|
||||
# workaround to send multipart/formdata request
|
||||
# http://stackoverflow.com/a/23131823/4726598
|
||||
dummy_file = {'_dummy': (None, '_dummy')}
|
||||
|
||||
return self._post('command/download', data=options, files=dummy_file)
|
||||
# qBittorrent requires adds to be done with multipath/form-data
|
||||
# POST requests for both URLs and .torrent files. Info on this
|
||||
# can be found here, and here:
|
||||
# http://docs.python-requests.org/en/master/user/quickstart/#post-a-multipart-encoded-file
|
||||
# http://docs.python-requests.org/en/master/user/advanced/#post-multiple-multipart-encoded-files
|
||||
if isinstance(link, list):
|
||||
links = '\n'.join(link)
|
||||
else:
|
||||
links = link
|
||||
torrent_data = {}
|
||||
torrent_data['urls'] = (None, links)
|
||||
for k, v in kwargs.iteritems():
|
||||
torrent_data[k] = (None, v)
|
||||
return self._post('api/v2/torrents/add', data=None, files=torrent_data)
|
||||
|
||||
def download_from_file(self, file_buffer, **kwargs):
|
||||
"""
|
||||
Download torrent using a file.
|
||||
|
||||
:param file_buffer: Single file() buffer or list of.
|
||||
:param save_path: Path to download the torrent.
|
||||
:param label: Label of the torrent(s).
|
||||
|
||||
:return: Empty JSON data.
|
||||
"""
|
||||
# qBittorrent requires adds to be done with multipath/form-data
|
||||
# POST requests for both URLs and .torrent files. Info on this
|
||||
# can be found here, and here:
|
||||
# http://docs.python-requests.org/en/master/user/quickstart/#post-a-multipart-encoded-file
|
||||
# http://docs.python-requests.org/en/master/user/advanced/#post-multiple-multipart-encoded-files
|
||||
if isinstance(file_buffer, list):
|
||||
torrent_files = {}
|
||||
for i, f in enumerate(file_buffer):
|
||||
torrent_files.update({'torrents%s' % i: f})
|
||||
torrent_data = []
|
||||
for f in file_buffer:
|
||||
fname = f.name
|
||||
torrent_data.append(('torrents', (fname, f)))
|
||||
else:
|
||||
torrent_files = {'torrents': file_buffer}
|
||||
fname = file_buffer.name
|
||||
torrent_data = [('torrents', (fname, file_buffer))]
|
||||
for k, v in kwargs.iteritems():
|
||||
torrent_data.append((k, (None, v)))
|
||||
|
||||
data = kwargs.copy()
|
||||
|
||||
if data.get('save_path'):
|
||||
data.update({'savepath': data['save_path']})
|
||||
return self._post('command/upload', data=data, files=torrent_files)
|
||||
return self._post('api/v2/torrents/add', data=None, files=torrent_data)
|
||||
|
||||
def add_trackers(self, infohash, trackers):
|
||||
"""
|
||||
Add trackers to a torrent.
|
||||
|
||||
:param infohash: INFO HASH of torrent.
|
||||
:param trackers: Trackers.
|
||||
"""
|
||||
data = {'hash': infohash.lower(),
|
||||
'urls': trackers}
|
||||
return self._post('command/addTrackers', data=data)
|
||||
return self._post('api/v2/torrents/addTrackers', data=data)
|
||||
|
||||
@staticmethod
|
||||
def _process_infohash_list(infohash_list):
|
||||
"""
|
||||
Method to convert the infohash_list to qBittorrent API friendly values.
|
||||
|
||||
:param infohash_list: List of infohash.
|
||||
"""
|
||||
if isinstance(infohash_list, list):
|
||||
@ -353,142 +315,122 @@ class Client(object):
|
||||
def pause(self, infohash):
|
||||
"""
|
||||
Pause a torrent.
|
||||
|
||||
:param infohash: INFO HASH of torrent.
|
||||
"""
|
||||
return self._post('command/pause', data={'hash': infohash.lower()})
|
||||
return self._post('api/v2/torrents/pause', data={'hashes': infohash.lower()})
|
||||
|
||||
def pause_all(self):
|
||||
"""
|
||||
Pause all torrents.
|
||||
"""
|
||||
return self._get('command/pauseAll')
|
||||
return self._post('api/v2/torrents/pause', data={'hashes': 'all'})
|
||||
|
||||
def pause_multiple(self, infohash_list):
|
||||
"""
|
||||
Pause multiple torrents.
|
||||
|
||||
:param infohash_list: Single or list() of infohashes.
|
||||
"""
|
||||
data = self._process_infohash_list(infohash_list)
|
||||
return self._post('command/pauseAll', data=data)
|
||||
|
||||
def set_label(self, infohash_list, label):
|
||||
"""
|
||||
Set the label on multiple torrents.
|
||||
IMPORTANT: OLD API method, kept as it is to avoid breaking stuffs.
|
||||
|
||||
:param infohash_list: Single or list() of infohashes.
|
||||
"""
|
||||
data = self._process_infohash_list(infohash_list)
|
||||
data['label'] = label
|
||||
return self._post('command/setLabel', data=data)
|
||||
return self._post('api/v2/torrents/pause', data=data)
|
||||
|
||||
def set_category(self, infohash_list, category):
|
||||
"""
|
||||
Set the category on multiple torrents.
|
||||
|
||||
:param infohash_list: Single or list() of infohashes.
|
||||
"""
|
||||
data = self._process_infohash_list(infohash_list)
|
||||
data['category'] = category
|
||||
return self._post('command/setCategory', data=data)
|
||||
return self._post('api/v2/torrents/setCategory', data=data)
|
||||
|
||||
def resume(self, infohash):
|
||||
"""
|
||||
Resume a paused torrent.
|
||||
|
||||
:param infohash: INFO HASH of torrent.
|
||||
"""
|
||||
return self._post('command/resume', data={'hash': infohash.lower()})
|
||||
return self._post('api/v2/torrents/resume', data={'hashes': infohash.lower()})
|
||||
|
||||
def resume_all(self):
|
||||
"""
|
||||
Resume all torrents.
|
||||
"""
|
||||
return self._get('command/resumeAll')
|
||||
return self._get('api/v2/torrents/resume', data={'hashes': 'all'})
|
||||
|
||||
def resume_multiple(self, infohash_list):
|
||||
"""
|
||||
Resume multiple paused torrents.
|
||||
|
||||
:param infohash_list: Single or list() of infohashes.
|
||||
"""
|
||||
data = self._process_infohash_list(infohash_list)
|
||||
return self._post('command/resumeAll', data=data)
|
||||
return self._post('api/v2/torrents/resume', data=data)
|
||||
|
||||
def delete(self, infohash_list):
|
||||
"""
|
||||
Delete torrents.
|
||||
|
||||
:param infohash_list: Single or list() of infohashes.
|
||||
"""
|
||||
data = self._process_infohash_list(infohash_list)
|
||||
return self._post('command/delete', data=data)
|
||||
data['deleteFiles'] = 'false'
|
||||
return self._post('api/v2/torrents/delete', data=data)
|
||||
|
||||
def delete_permanently(self, infohash_list):
|
||||
"""
|
||||
Permanently delete torrents.
|
||||
|
||||
*** WARNING : This will instruct qBittorrent to delete files
|
||||
*** from your hard disk. Use with caution.
|
||||
:param infohash_list: Single or list() of infohashes.
|
||||
"""
|
||||
data = self._process_infohash_list(infohash_list)
|
||||
return self._post('command/deletePerm', data=data)
|
||||
data['deleteFiles'] = 'true'
|
||||
return self._post('api/v2/torrents/delete', data=data)
|
||||
|
||||
def recheck(self, infohash_list):
|
||||
"""
|
||||
Recheck torrents.
|
||||
|
||||
:param infohash_list: Single or list() of infohashes.
|
||||
"""
|
||||
data = self._process_infohash_list(infohash_list)
|
||||
return self._post('command/recheck', data=data)
|
||||
return self._post('api/v2/torrents/recheck', data=data)
|
||||
|
||||
def increase_priority(self, infohash_list):
|
||||
"""
|
||||
Increase priority of torrents.
|
||||
|
||||
:param infohash_list: Single or list() of infohashes.
|
||||
"""
|
||||
data = self._process_infohash_list(infohash_list)
|
||||
return self._post('command/increasePrio', data=data)
|
||||
return self._post('api/v2/torrents/increasePrio', data=data)
|
||||
|
||||
def decrease_priority(self, infohash_list):
|
||||
"""
|
||||
Decrease priority of torrents.
|
||||
|
||||
:param infohash_list: Single or list() of infohashes.
|
||||
"""
|
||||
data = self._process_infohash_list(infohash_list)
|
||||
return self._post('command/decreasePrio', data=data)
|
||||
return self._post('api/v2/torrents/decreasePrio', data=data)
|
||||
|
||||
def set_max_priority(self, infohash_list):
|
||||
"""
|
||||
Set torrents to maximum priority level.
|
||||
|
||||
:param infohash_list: Single or list() of infohashes.
|
||||
"""
|
||||
data = self._process_infohash_list(infohash_list)
|
||||
return self._post('command/topPrio', data=data)
|
||||
return self._post('api/v2/torrents/topPrio', data=data)
|
||||
|
||||
def set_min_priority(self, infohash_list):
|
||||
"""
|
||||
Set torrents to minimum priority level.
|
||||
|
||||
:param infohash_list: Single or list() of infohashes.
|
||||
"""
|
||||
data = self._process_infohash_list(infohash_list)
|
||||
return self._post('command/bottomPrio', data=data)
|
||||
return self._post('api/v2/torrents/bottomPrio', data=data)
|
||||
|
||||
def set_file_priority(self, infohash, file_id, priority):
|
||||
"""
|
||||
Set file of a torrent to a supplied priority level.
|
||||
|
||||
:param infohash: INFO HASH of torrent.
|
||||
:param file_id: ID of the file to set priority.
|
||||
:param priority: Priority level of the file.
|
||||
"""
|
||||
if priority not in [0, 1, 2, 7]:
|
||||
if priority not in [0, 1, 6, 7]:
|
||||
raise ValueError("Invalid priority, refer WEB-UI docs for info.")
|
||||
elif not isinstance(file_id, int):
|
||||
raise TypeError("File ID must be an int")
|
||||
@ -497,7 +439,7 @@ class Client(object):
|
||||
'id': file_id,
|
||||
'priority': priority}
|
||||
|
||||
return self._post('command/setFilePrio', data=data)
|
||||
return self._post('api/v2/torrents/filePrio', data=data)
|
||||
|
||||
# Get-set global download and upload speed limits.
|
||||
|
||||
@ -505,15 +447,14 @@ class Client(object):
|
||||
"""
|
||||
Get global download speed limit.
|
||||
"""
|
||||
return self._get('command/getGlobalDlLimit')
|
||||
return self._get('api/v2/transfer/downloadLimit')
|
||||
|
||||
def set_global_download_limit(self, limit):
|
||||
"""
|
||||
Set global download speed limit.
|
||||
|
||||
:param limit: Speed limit in bytes.
|
||||
"""
|
||||
return self._post('command/setGlobalDlLimit', data={'limit': limit})
|
||||
return self._post('api/v2/transfer/setDownloadLimit', data={'limit': limit})
|
||||
|
||||
global_download_limit = property(get_global_download_limit,
|
||||
set_global_download_limit)
|
||||
@ -522,15 +463,14 @@ class Client(object):
|
||||
"""
|
||||
Get global upload speed limit.
|
||||
"""
|
||||
return self._get('command/getGlobalUpLimit')
|
||||
return self._get('api/v2/transfer/uploadLimit')
|
||||
|
||||
def set_global_upload_limit(self, limit):
|
||||
"""
|
||||
Set global upload speed limit.
|
||||
|
||||
:param limit: Speed limit in bytes.
|
||||
"""
|
||||
return self._post('command/setGlobalUpLimit', data={'limit': limit})
|
||||
return self._post('api/v2/transfer/setUploadLimit', data={'limit': limit})
|
||||
|
||||
global_upload_limit = property(get_global_upload_limit,
|
||||
set_global_upload_limit)
|
||||
@ -539,61 +479,56 @@ class Client(object):
|
||||
def get_torrent_download_limit(self, infohash_list):
|
||||
"""
|
||||
Get download speed limit of the supplied torrents.
|
||||
|
||||
:param infohash_list: Single or list() of infohashes.
|
||||
"""
|
||||
data = self._process_infohash_list(infohash_list)
|
||||
return self._post('command/getTorrentsDlLimit', data=data)
|
||||
return self._post('api/v2/torrents/downloadLimit', data=data)
|
||||
|
||||
def set_torrent_download_limit(self, infohash_list, limit):
|
||||
"""
|
||||
Set download speed limit of the supplied torrents.
|
||||
|
||||
:param infohash_list: Single or list() of infohashes.
|
||||
:param limit: Speed limit in bytes.
|
||||
"""
|
||||
data = self._process_infohash_list(infohash_list)
|
||||
data.update({'limit': limit})
|
||||
return self._post('command/setTorrentsDlLimit', data=data)
|
||||
return self._post('api/v2/torrents/setDownloadLimit', data=data)
|
||||
|
||||
def get_torrent_upload_limit(self, infohash_list):
|
||||
"""
|
||||
Get upoload speed limit of the supplied torrents.
|
||||
|
||||
:param infohash_list: Single or list() of infohashes.
|
||||
"""
|
||||
data = self._process_infohash_list(infohash_list)
|
||||
return self._post('command/getTorrentsUpLimit', data=data)
|
||||
return self._post('api/v2/torrents/uploadLimit', data=data)
|
||||
|
||||
def set_torrent_upload_limit(self, infohash_list, limit):
|
||||
"""
|
||||
Set upload speed limit of the supplied torrents.
|
||||
|
||||
:param infohash_list: Single or list() of infohashes.
|
||||
:param limit: Speed limit in bytes.
|
||||
"""
|
||||
data = self._process_infohash_list(infohash_list)
|
||||
data.update({'limit': limit})
|
||||
return self._post('command/setTorrentsUpLimit', data=data)
|
||||
return self._post('api/v2/torrents/setUploadLimit', data=data)
|
||||
|
||||
# setting preferences
|
||||
def set_preferences(self, **kwargs):
|
||||
"""
|
||||
Set preferences of qBittorrent.
|
||||
Read all possible preferences @ http://git.io/vEgDQ
|
||||
|
||||
Read all possible preferences @ https://git.io/fx2Y9
|
||||
:param kwargs: set preferences in kwargs form.
|
||||
"""
|
||||
json_data = "json={}".format(json.dumps(kwargs))
|
||||
headers = {'content-type': 'application/x-www-form-urlencoded'}
|
||||
return self._post('command/setPreferences', data=json_data,
|
||||
return self._post('api/v2/app/setPreferences', data=json_data,
|
||||
headers=headers)
|
||||
|
||||
def get_alternative_speed_status(self):
|
||||
"""
|
||||
Get Alternative speed limits. (1/0)
|
||||
"""
|
||||
return self._get('command/alternativeSpeedLimitsEnabled')
|
||||
return self._get('api/v2/transfer/speedLimitsMode')
|
||||
|
||||
alternative_speed_status = property(get_alternative_speed_status)
|
||||
|
||||
@ -601,33 +536,30 @@ class Client(object):
|
||||
"""
|
||||
Toggle alternative speed limits.
|
||||
"""
|
||||
return self._get('command/toggleAlternativeSpeedLimits')
|
||||
return self._get('api/v2/transfer/toggleSpeedLimitsMode')
|
||||
|
||||
def toggle_sequential_download(self, infohash_list):
|
||||
"""
|
||||
Toggle sequential download in supplied torrents.
|
||||
|
||||
:param infohash_list: Single or list() of infohashes.
|
||||
"""
|
||||
data = self._process_infohash_list(infohash_list)
|
||||
return self._post('command/toggleSequentialDownload', data=data)
|
||||
return self._post('api/v2/torrents/toggleSequentialDownload', data=data)
|
||||
|
||||
def toggle_first_last_piece_priority(self, infohash_list):
|
||||
"""
|
||||
Toggle first/last piece priority of supplied torrents.
|
||||
|
||||
:param infohash_list: Single or list() of infohashes.
|
||||
"""
|
||||
data = self._process_infohash_list(infohash_list)
|
||||
return self._post('command/toggleFirstLastPiecePrio', data=data)
|
||||
return self._post('api/v2/torrents/toggleFirstLastPiecePrio', data=data)
|
||||
|
||||
def force_start(self, infohash_list, value=True):
|
||||
"""
|
||||
Force start selected torrents.
|
||||
|
||||
:param infohash_list: Single or list() of infohashes.
|
||||
:param value: Force start value (bool)
|
||||
"""
|
||||
data = self._process_infohash_list(infohash_list)
|
||||
data.update({'value': json.dumps(value)})
|
||||
return self._post('command/setForceStart', data=data)
|
||||
return self._post('api/v2/torrents/setForceStart', data=data)
|
||||
|
@ -5,7 +5,7 @@
|
||||
### NZBGET POST-PROCESSING SCRIPT ###
|
||||
|
||||
# Post-Process to CouchPotato, SickBeard, Sonarr, Mylar, Gamez, HeadPhones,
|
||||
# LazyLibrarian, Radarr, Lidarr
|
||||
# LazyLibrarian, Radarr, Lidarr, Watcher3
|
||||
#
|
||||
# This script sends the download to your automated media management servers.
|
||||
#
|
||||
@ -142,6 +142,54 @@
|
||||
# Enable to replace local path with the path as per the mountPoints below.
|
||||
#raremote_path=0
|
||||
|
||||
## Watcher3
|
||||
|
||||
# Wather3 script category.
|
||||
#
|
||||
# category that gets called for post-processing with Watcher3.
|
||||
#W3Category=movie
|
||||
|
||||
# Watcher3 api key.
|
||||
#W3apikey=
|
||||
|
||||
# Watcher3 host.
|
||||
#
|
||||
# The ipaddress for your Watcher3 server. e.g For the Same system use localhost or 127.0.0.1
|
||||
#W3host=localhost
|
||||
|
||||
# Watcher3 port.
|
||||
#W3port=9090
|
||||
|
||||
# Watcher3 uses ssl (0, 1).
|
||||
#
|
||||
# Set to 1 if using ssl, else set to 0.
|
||||
#W3ssl=0
|
||||
|
||||
# Watcher3 URL_Base
|
||||
#
|
||||
# set this if using a reverse proxy.
|
||||
#W3web_root=
|
||||
|
||||
# OMDB API Key.
|
||||
#
|
||||
# api key for www.omdbapi.com (used as alternative to imdb to assist with movie identification).
|
||||
#W3omdbapikey=
|
||||
|
||||
# Wacther3 Delete Failed Downloads (0, 1).
|
||||
#
|
||||
# set to 1 to delete failed, or 0 to leave files in place.
|
||||
#W3delete_failed=0
|
||||
|
||||
# Wacther3 wait_for
|
||||
#
|
||||
# Set the number of minutes to wait after calling the renamer, to check the movie has changed status.
|
||||
#W3wait_for=2
|
||||
|
||||
# Watcher3 and NZBGet are a different system (0, 1).
|
||||
#
|
||||
# Enable to replace local path with the path as per the mountPoints below.
|
||||
#W3remote_path=0
|
||||
|
||||
## SickBeard
|
||||
|
||||
# SickBeard script category.
|
||||
@ -799,7 +847,7 @@ def process(input_directory, input_name=None, status=0, client_agent='manual', d
|
||||
|
||||
logger.info('Calling {0}:{1} to post-process:{2}'.format(section_name, input_category, input_name))
|
||||
|
||||
if section_name in ['CouchPotato', 'Radarr']:
|
||||
if section_name in ['CouchPotato', 'Radarr', 'Watcher3']:
|
||||
result = movies.process(section_name, input_directory, input_name, status, client_agent, download_id, input_category, failure_link)
|
||||
elif section_name in ['SickBeard', 'NzbDrone', 'Sonarr']:
|
||||
result = tv.process(section_name, input_directory, input_name, status, client_agent, download_id, input_category, failure_link)
|
||||
|
268
nzbToWatcher3.py
Normal file
268
nzbToWatcher3.py
Normal file
@ -0,0 +1,268 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
#
|
||||
##############################################################################
|
||||
### NZBGET POST-PROCESSING SCRIPT ###
|
||||
|
||||
# Post-Process to Watcher3
|
||||
#
|
||||
# This script sends the download to your automated media management servers.
|
||||
#
|
||||
# NOTE: This script requires Python to be installed on your system.
|
||||
|
||||
##############################################################################
|
||||
### OPTIONS ###
|
||||
|
||||
## General
|
||||
|
||||
# Auto Update nzbToMedia (0, 1).
|
||||
#
|
||||
# Set to 1 if you want nzbToMedia to automatically check for and update to the latest version
|
||||
#auto_update=0
|
||||
|
||||
# Check Media for corruption (0, 1).
|
||||
#
|
||||
# Enable/Disable media file checking using ffprobe.
|
||||
#check_media=1
|
||||
|
||||
# Safe Mode protection of DestDir (0, 1).
|
||||
#
|
||||
# Enable/Disable a safety check to ensure we don't process all downloads in the default_downloadDirectory by mistake.
|
||||
#safe_mode=1
|
||||
|
||||
# Disable additional extraction checks for failed (0, 1).
|
||||
#
|
||||
# Turn this on to disable additional extraction attempts for failed downloads. Default = 0 this will attempt to extract and verify if media is present.
|
||||
#no_extract_failed = 0
|
||||
|
||||
## Watcher3
|
||||
|
||||
# Watcher3 script category.
|
||||
#
|
||||
# category that gets called for post-processing with Watcher3.
|
||||
#W3Category=movie
|
||||
|
||||
# Watcher3 api key.
|
||||
#W3apikey=
|
||||
|
||||
# Watcher3 host.
|
||||
#
|
||||
# The ipaddress for your Watcher3 server. e.g For the Same system use localhost or 127.0.0.1
|
||||
#W3host=localhost
|
||||
|
||||
# Watcher3 port.
|
||||
#W3port=5050
|
||||
|
||||
# Watcher3 uses ssl (0, 1).
|
||||
#
|
||||
# Set to 1 if using ssl, else set to 0.
|
||||
#W3ssl=0
|
||||
|
||||
# Watcher3 URL_Base
|
||||
#
|
||||
# set this if using a reverse proxy.
|
||||
#W3web_root=
|
||||
|
||||
# Watcher3 watch directory.
|
||||
#
|
||||
# set this to where your Watcher3 completed downloads are.
|
||||
#W3watch_dir=
|
||||
|
||||
# OMDB API Key.
|
||||
#
|
||||
# api key for www.omdbapi.com (used as alternative to imdb to assist with movie identification).
|
||||
#W3omdbapikey=
|
||||
|
||||
# Watcher3 Delete Failed Downloads (0, 1).
|
||||
#
|
||||
# set to 1 to delete failed, or 0 to leave files in place.
|
||||
#W3delete_failed=0
|
||||
|
||||
# Watcher3 wait_for
|
||||
#
|
||||
# Set the number of minutes to wait after calling the renamer, to check the movie has changed status.
|
||||
#W3wait_for=2
|
||||
|
||||
# Watcher3 and NZBGet are a different system (0, 1).
|
||||
#
|
||||
# Enable to replace local path with the path as per the mountPoints below.
|
||||
#W3remote_path=0
|
||||
|
||||
## Network
|
||||
|
||||
# Network Mount Points (Needed for remote path above)
|
||||
#
|
||||
# Enter Mount points as LocalPath,RemotePath and separate each pair with '|'
|
||||
# e.g. mountPoints=/volume1/Public/,E:\|/volume2/share/,\\NAS\
|
||||
#mountPoints=
|
||||
|
||||
## Extensions
|
||||
|
||||
# Media Extensions
|
||||
#
|
||||
# This is a list of media extensions that are used to verify that the download does contain valid media.
|
||||
#mediaExtensions=.mkv,.avi,.divx,.xvid,.mov,.wmv,.mp4,.mpg,.mpeg,.vob,.iso,.ts
|
||||
|
||||
## Posix
|
||||
|
||||
# Niceness for external tasks Extractor and Transcoder.
|
||||
#
|
||||
# Set the Niceness value for the nice command. These range from -20 (most favorable to the process) to 19 (least favorable to the process).
|
||||
# If entering an integer e.g 'niceness=4', this is added to the nice command and passed as 'nice -n4' (Default).
|
||||
# If entering a comma separated list e.g. 'niceness=nice,4' this will be passed as 'nice 4' (Safer).
|
||||
#niceness=nice,-n0
|
||||
|
||||
# ionice scheduling class (0, 1, 2, 3).
|
||||
#
|
||||
# Set the ionice scheduling class. 0 for none, 1 for real time, 2 for best-effort, 3 for idle.
|
||||
#ionice_class=2
|
||||
|
||||
# ionice scheduling class data.
|
||||
#
|
||||
# Set the ionice scheduling class data. This defines the class data, if the class accepts an argument. For real time and best-effort, 0-7 is valid data.
|
||||
#ionice_classdata=4
|
||||
|
||||
## Transcoder
|
||||
|
||||
# getSubs (0, 1).
|
||||
#
|
||||
# set to 1 to download subtitles.
|
||||
#getSubs=0
|
||||
|
||||
# subLanguages.
|
||||
#
|
||||
# subLanguages. create a list of languages in the order you want them in your subtitles.
|
||||
#subLanguages=eng,spa,fra
|
||||
|
||||
# Transcode (0, 1).
|
||||
#
|
||||
# set to 1 to transcode, otherwise set to 0.
|
||||
#transcode=0
|
||||
|
||||
# create a duplicate, or replace the original (0, 1).
|
||||
#
|
||||
# set to 1 to cretae a new file or 0 to replace the original
|
||||
#duplicate=1
|
||||
|
||||
# ignore extensions.
|
||||
#
|
||||
# list of extensions that won't be transcoded.
|
||||
#ignoreExtensions=.avi,.mkv
|
||||
|
||||
# outputFastStart (0,1).
|
||||
#
|
||||
# outputFastStart. 1 will use -movflags + faststart. 0 will disable this from being used.
|
||||
#outputFastStart=0
|
||||
|
||||
# outputVideoPath.
|
||||
#
|
||||
# outputVideoPath. Set path you want transcoded videos moved to. Leave blank to disable.
|
||||
#outputVideoPath=
|
||||
|
||||
# processOutput (0,1).
|
||||
#
|
||||
# processOutput. 1 will send the outputVideoPath to SickBeard/CouchPotato. 0 will send original files.
|
||||
#processOutput=0
|
||||
|
||||
# audioLanguage.
|
||||
#
|
||||
# audioLanguage. set the 3 letter language code you want as your primary audio track.
|
||||
#audioLanguage=eng
|
||||
|
||||
# allAudioLanguages (0,1).
|
||||
#
|
||||
# allAudioLanguages. 1 will keep all audio tracks (uses AudioCodec3) where available.
|
||||
#allAudioLanguages=0
|
||||
|
||||
# allSubLanguages (0,1).
|
||||
#
|
||||
# allSubLanguages. 1 will keep all exisiting sub languages. 0 will discare those not in your list above.
|
||||
#allSubLanguages=0
|
||||
|
||||
# embedSubs (0,1).
|
||||
#
|
||||
# embedSubs. 1 will embded external sub/srt subs into your video if this is supported.
|
||||
#embedSubs=1
|
||||
|
||||
# burnInSubtitle (0,1).
|
||||
#
|
||||
# burnInSubtitle. burns the default sub language into your video (needed for players that don't support subs)
|
||||
#burnInSubtitle=0
|
||||
|
||||
# extractSubs (0,1).
|
||||
#
|
||||
# extractSubs. 1 will extract subs from the video file and save these as external srt files.
|
||||
#extractSubs=0
|
||||
|
||||
# externalSubDir.
|
||||
#
|
||||
# externalSubDir. set the directory where subs should be saved (if not the same directory as the video)
|
||||
#externalSubDir=
|
||||
|
||||
# outputDefault (None, iPad, iPad-1080p, iPad-720p, Apple-TV2, iPod, iPhone, PS3, xbox, Roku-1080p, Roku-720p, Roku-480p, mkv, mkv-bluray, mp4-scene-release, MKV-SD).
|
||||
#
|
||||
# outputDefault. Loads default configs for the selected device. The remaining options below are ignored.
|
||||
# If you want to use your own profile, set None and set the remaining options below.
|
||||
#outputDefault=None
|
||||
|
||||
# hwAccel (0,1).
|
||||
#
|
||||
# hwAccel. 1 will set ffmpeg to enable hardware acceleration (this requires a recent ffmpeg).
|
||||
#hwAccel=0
|
||||
|
||||
# ffmpeg output settings.
|
||||
#outputVideoExtension=.mp4
|
||||
#outputVideoCodec=libx264
|
||||
#VideoCodecAllow=
|
||||
#outputVideoResolution=720:-1
|
||||
#outputVideoPreset=medium
|
||||
#outputVideoFramerate=24
|
||||
#outputVideoBitrate=800k
|
||||
#outputAudioCodec=ac3
|
||||
#AudioCodecAllow=
|
||||
#outputAudioChannels=6
|
||||
#outputAudioBitrate=640k
|
||||
#outputQualityPercent=
|
||||
#outputAudioTrack2Codec=libfaac
|
||||
#AudioCodec2Allow=
|
||||
#outputAudioTrack2Channels=2
|
||||
#outputAudioTrack2Bitrate=160k
|
||||
#outputAudioOtherCodec=libmp3lame
|
||||
#AudioOtherCodecAllow=
|
||||
#outputAudioOtherChannels=2
|
||||
#outputAudioOtherBitrate=128k
|
||||
#outputSubtitleCodec=
|
||||
|
||||
## WakeOnLan
|
||||
|
||||
# use WOL (0, 1).
|
||||
#
|
||||
# set to 1 to send WOL broadcast to the mac and test the server (e.g. xbmc) on the host and port specified.
|
||||
#wolwake=0
|
||||
|
||||
# WOL MAC
|
||||
#
|
||||
# enter the mac address of the system to be woken.
|
||||
#wolmac=00:01:2e:2D:64:e1
|
||||
|
||||
# Set the Host and Port of a server to verify system has woken.
|
||||
#wolhost=192.168.1.37
|
||||
#wolport=80
|
||||
|
||||
### NZBGET POST-PROCESSING SCRIPT ###
|
||||
##############################################################################
|
||||
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
import sys
|
||||
|
||||
import nzbToMedia
|
||||
|
||||
section = 'Watcher3'
|
||||
result = nzbToMedia.main(sys.argv, section)
|
||||
sys.exit(result)
|
@ -11,4 +11,4 @@ from core import transcoder
|
||||
|
||||
|
||||
def test_transcoder_check():
|
||||
assert transcoder.is_video_good(core.TEST_FILE, 0) is True
|
||||
assert transcoder.is_video_good(core.TEST_FILE, 1) is True
|
||||
|
Loading…
x
Reference in New Issue
Block a user