mirror of
https://github.com/clinton-hall/nzbToMedia.git
synced 2025-08-21 05:43:16 -07:00
Lots of refactoring.
Remove version checks and update logic Remove extraneous constants: SOURCE_ROOT, SYS_ARGV, APP_FILENAME, CONFIG_MOVIE_FILE, MY_APP, CONFIG_TV_FILE, GIT_* Remove nzb2media.utils.processes Update requirements Flatten project structure Keep settings close to code Refactor NZBget, torrent configs, torrents, transcoder, tools, constants and forks Refactor `nzbToMedia.main` to `nzb2media.app.main` Fix flake/lint issues
This commit is contained in:
parent
90f1751c8e
commit
bfb45c180a
65 changed files with 1518 additions and 2016 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -16,3 +16,5 @@
|
|||
*.egg-info
|
||||
/.vscode
|
||||
/htmlcov/
|
||||
/.tox/
|
||||
/.mypy_cache/
|
||||
|
|
|
@ -15,35 +15,6 @@ repos:
|
|||
- id: fix-byte-order-marker
|
||||
- id: name-tests-test
|
||||
- id: requirements-txt-fixer
|
||||
- repo: https://github.com/pycqa/flake8
|
||||
rev: '6.0.0'
|
||||
hooks:
|
||||
- id: flake8
|
||||
name: Flake8 primary tests
|
||||
additional_dependencies:
|
||||
- flake8-bugbear
|
||||
- flake8-commas
|
||||
- flake8-comprehensions
|
||||
- flake8-docstrings
|
||||
- flake8-future-import
|
||||
- id: flake8
|
||||
name: Flake8 selective tests
|
||||
args: [
|
||||
# ** SELECTIVE TESTS **
|
||||
# Run flake8 tests (with plugins) for specific optional codes defined below
|
||||
# -- flake8 --
|
||||
# E123 closing bracket does not match indentation of opening bracket’s line
|
||||
# E226 missing whitespace around arithmetic operator
|
||||
# E241 multiple spaces after ‘,’
|
||||
# E242 tab after ‘,’
|
||||
# E704 multiple statements on one line
|
||||
# W504 line break after binary operator
|
||||
# W505 doc line too long
|
||||
# -- flake8-bugbear --
|
||||
# B902 Invalid first argument used for instance method.
|
||||
# B903 Data class should be immutable or use __slots__ to save memory.
|
||||
'--select=B902,B903,E123,E226,E241,E242,E704,W504,W505'
|
||||
]
|
||||
- repo: https://github.com/asottile/add-trailing-comma
|
||||
rev: v2.4.0
|
||||
hooks:
|
||||
|
@ -54,10 +25,21 @@ repos:
|
|||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py37-plus]
|
||||
#- repo: https://github.com/pre-commit/mirrors-autopep8
|
||||
# rev: v2.0.0
|
||||
# hooks:
|
||||
# - id: autopep8
|
||||
- repo: https://github.com/pre-commit/mirrors-autopep8
|
||||
rev: v2.0.1
|
||||
hooks:
|
||||
- id: autopep8
|
||||
- repo: https://github.com/pycqa/flake8
|
||||
rev: '6.0.0'
|
||||
hooks:
|
||||
- id: flake8
|
||||
name: flake8
|
||||
additional_dependencies:
|
||||
- flake8-bugbear
|
||||
- flake8-commas
|
||||
- flake8-comprehensions
|
||||
- flake8-docstrings
|
||||
- flake8-future-import
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: pylint
|
||||
|
|
|
@ -74,7 +74,6 @@ disable=
|
|||
C0415, # import-outside-toplevel
|
||||
|
||||
R0204, # redifined-variable-type
|
||||
R0401, # cyclic-import
|
||||
R0801, # duplicate-code
|
||||
R0903, # too-few-public-methods
|
||||
R0902, # too-many-instance-attributes
|
||||
|
|
|
@ -4,10 +4,11 @@ import os
|
|||
import sys
|
||||
|
||||
import nzb2media
|
||||
import nzb2media.torrent
|
||||
from nzb2media import main_db
|
||||
from nzb2media.auto_process import comics, games, movies, music, tv, books
|
||||
from nzb2media.auto_process.common import ProcessResult
|
||||
from nzb2media.plugins.plex import plex_update
|
||||
from nzb2media.plex import plex_update
|
||||
from nzb2media.user_scripts import external_script
|
||||
from nzb2media.utils.encoding import char_replace, convert_to_ascii
|
||||
from nzb2media.utils.links import replace_links
|
||||
|
@ -116,7 +117,7 @@ def process_torrent(input_directory, input_name, input_category, input_hash, inp
|
|||
|
||||
log.info(f'Output directory set to: {output_destination}')
|
||||
|
||||
if nzb2media.SAFE_MODE and output_destination == nzb2media.TORRENT_DEFAULT_DIRECTORY:
|
||||
if nzb2media.SAFE_MODE and output_destination == nzb2media.torrent.DEFAULT_DIRECTORY:
|
||||
log.error(f'The output directory:[{input_directory}] is the Download Directory. Edit outputDirectory in autoProcessMedia.cfg. Exiting')
|
||||
return [-1, '']
|
||||
|
||||
|
@ -124,7 +125,7 @@ def process_torrent(input_directory, input_name, input_category, input_hash, inp
|
|||
|
||||
if section_name in {'HeadPhones', 'Lidarr'}:
|
||||
# Make sure we preserve folder structure for HeadPhones.
|
||||
nzb2media.NOFLATTEN.extend(input_category)
|
||||
nzb2media.torrent.NO_FLATTEN.extend(input_category)
|
||||
|
||||
now = datetime.datetime.now()
|
||||
|
||||
|
@ -143,7 +144,7 @@ def process_torrent(input_directory, input_name, input_category, input_hash, inp
|
|||
full_file_name = os.path.basename(input_file)
|
||||
|
||||
target_file = nzb2media.os.path.join(output_destination, full_file_name)
|
||||
if input_category in nzb2media.NOFLATTEN:
|
||||
if input_category in nzb2media.torrent.NO_FLATTEN:
|
||||
if not os.path.basename(file_path) in output_destination:
|
||||
target_file = nzb2media.os.path.join(
|
||||
nzb2media.os.path.join(output_destination, os.path.basename(file_path)), full_file_name,
|
||||
|
@ -186,7 +187,7 @@ def process_torrent(input_directory, input_name, input_category, input_hash, inp
|
|||
log.debug(f'Checking for archives to extract in directory: {input_directory}')
|
||||
nzb2media.extract_files(input_directory, output_destination, keep_archive)
|
||||
|
||||
if input_category not in nzb2media.NOFLATTEN:
|
||||
if input_category not in nzb2media.torrent.NO_FLATTEN:
|
||||
# don't flatten hp in case multi cd albums, and we need to copy this back later.
|
||||
nzb2media.flatten(output_destination)
|
||||
|
||||
|
@ -211,8 +212,8 @@ def process_torrent(input_directory, input_name, input_category, input_hash, inp
|
|||
|
||||
log.info(f'Calling {section_name}:{usercat} to post-process:{input_name}')
|
||||
|
||||
if nzb2media.TORRENT_CHMOD_DIRECTORY:
|
||||
nzb2media.rchmod(output_destination, nzb2media.TORRENT_CHMOD_DIRECTORY)
|
||||
if nzb2media.torrent.CHMOD_DIRECTORY:
|
||||
nzb2media.rchmod(output_destination, nzb2media.torrent.CHMOD_DIRECTORY)
|
||||
|
||||
if section_name == 'UserScript':
|
||||
result = external_script(output_destination, input_name, input_category, section)
|
||||
|
@ -247,7 +248,7 @@ def process_torrent(input_directory, input_name, input_category, input_hash, inp
|
|||
plex_update(input_category)
|
||||
|
||||
if result.status_code:
|
||||
if not nzb2media.TORRENT_RESUME_ON_FAILURE:
|
||||
if not nzb2media.torrent.RESUME_ON_FAILURE:
|
||||
log.error(
|
||||
'A problem was reported in the autoProcess* script. '
|
||||
'Torrent won\'t resume seeding (settings)',
|
||||
|
@ -265,7 +266,7 @@ def process_torrent(input_directory, input_name, input_category, input_hash, inp
|
|||
nzb2media.update_download_info_status(input_name, 1)
|
||||
|
||||
# remove torrent
|
||||
if nzb2media.USE_LINK == 'move-sym' and not nzb2media.DELETE_ORIGINAL == 1:
|
||||
if nzb2media.USE_LINK == 'move-sym' and nzb2media.DELETE_ORIGINAL != 1:
|
||||
log.debug(f'Checking for sym-links to re-direct in: {input_directory}')
|
||||
for dirpath, _, files in os.walk(input_directory):
|
||||
for file in files:
|
||||
|
@ -286,7 +287,7 @@ def main(args):
|
|||
nzb2media.initialize()
|
||||
|
||||
# clientAgent for Torrents
|
||||
client_agent = nzb2media.TORRENT_CLIENT_AGENT
|
||||
client_agent = nzb2media.torrent.CLIENT_AGENT
|
||||
|
||||
log.info('#########################################################')
|
||||
log.info(f'## ..::[{os.path.basename(__file__)}]::.. ##')
|
||||
|
@ -335,7 +336,7 @@ def main(args):
|
|||
input_hash = ''
|
||||
input_id = ''
|
||||
|
||||
if client_agent.lower() not in nzb2media.TORRENT_CLIENTS:
|
||||
if client_agent.lower() not in nzb2media.torrent.CLIENTS:
|
||||
continue
|
||||
|
||||
input_name = os.path.basename(dir_name)
|
||||
|
@ -352,7 +353,6 @@ def main(args):
|
|||
log.info(f'The {args[0]} script completed successfully.')
|
||||
else:
|
||||
log.error(f'A problem was reported in the {args[0]} script.')
|
||||
del nzb2media.MYAPP
|
||||
return result.status_code
|
||||
|
||||
|
||||
|
|
|
@ -1,35 +1,32 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import itertools
|
||||
import locale
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import typing
|
||||
from subprocess import DEVNULL
|
||||
from typing import Any
|
||||
|
||||
import setuptools_scm
|
||||
|
||||
import nzb2media.fork.medusa
|
||||
import nzb2media.fork.sickbeard
|
||||
import nzb2media.fork.sickchill
|
||||
import nzb2media.fork.sickgear
|
||||
import nzb2media.tool
|
||||
from nzb2media import databases
|
||||
from nzb2media import main_db
|
||||
from nzb2media import tool
|
||||
from nzb2media import version_check
|
||||
from nzb2media.configuration import Config
|
||||
from nzb2media.nzb.configuration import configure_nzbs
|
||||
from nzb2media.plugins.plex import configure_plex
|
||||
from nzb2media.torrent.configuration import configure_torrent_class
|
||||
from nzb2media.torrent.configuration import configure_torrents
|
||||
from nzb2media.utils.files import make_dir
|
||||
from nzb2media.transcoder import configure_transcoder
|
||||
from nzb2media.utils.network import wake_up
|
||||
from nzb2media.utils.processes import RunningProcess
|
||||
from nzb2media.utils.processes import restart
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
||||
__version__ = setuptools_scm.get_version()
|
||||
|
||||
|
||||
def module_path(module=__file__):
|
||||
try:
|
||||
|
@ -39,107 +36,35 @@ def module_path(module=__file__):
|
|||
return path.parent.absolute()
|
||||
|
||||
|
||||
SOURCE_ROOT = module_path()
|
||||
APP_ROOT = SOURCE_ROOT.parent
|
||||
APP_ROOT = module_path().parent
|
||||
# init preliminaries
|
||||
SYS_ARGV = sys.argv[1:]
|
||||
APP_FILENAME = pathlib.Path(sys.argv[0])
|
||||
APP_NAME: str = APP_FILENAME.name
|
||||
APP_NAME: str = pathlib.Path(sys.argv[0]).name
|
||||
LOG_DIR: pathlib.Path = APP_ROOT / 'logs'
|
||||
LOG_FILE: pathlib.Path = LOG_DIR / 'nzbtomedia.log'
|
||||
PID_FILE = LOG_DIR / 'nzbtomedia.pid'
|
||||
CONFIG_FILE = APP_ROOT / 'autoProcessMedia.cfg'
|
||||
CONFIG_SPEC_FILE = APP_ROOT / 'autoProcessMedia.cfg.spec'
|
||||
CONFIG_MOVIE_FILE = APP_ROOT / 'autoProcessMovie.cfg'
|
||||
CONFIG_TV_FILE = APP_ROOT / 'autoProcessTv.cfg'
|
||||
TEST_FILE = APP_ROOT / 'tests' / 'test.mp4'
|
||||
MYAPP = None
|
||||
__version__ = '12.1.11'
|
||||
# Client Agents
|
||||
NZB_CLIENTS = ['sabnzbd', 'nzbget', 'manual']
|
||||
TORRENT_CLIENTS = ['transmission', 'deluge', 'utorrent', 'rtorrent', 'qbittorrent', 'other', 'manual']
|
||||
# sickbeard fork/branch constants
|
||||
FORK_DEFAULT = 'default'
|
||||
FORK_FAILED = 'failed'
|
||||
FORK_FAILED_TORRENT = 'failed-torrent'
|
||||
FORK_SICKCHILL = 'SickChill'
|
||||
FORK_SICKCHILL_API = 'SickChill-api'
|
||||
FORK_SICKBEARD_API = 'SickBeard-api'
|
||||
FORK_MEDUSA = 'Medusa'
|
||||
FORK_MEDUSA_API = 'Medusa-api'
|
||||
FORK_MEDUSA_APIV2 = 'Medusa-apiv2'
|
||||
FORK_SICKGEAR = 'SickGear'
|
||||
FORK_SICKGEAR_API = 'SickGear-api'
|
||||
FORK_STHENO = 'Stheno'
|
||||
FORKS: typing.Mapping[str, typing.Mapping] = {FORK_DEFAULT: {'dir': None}, FORK_FAILED: {'dirName': None, 'failed': None}, FORK_FAILED_TORRENT: {'dir': None, 'failed': None, 'process_method': None}, FORK_SICKCHILL: {'proc_dir': None, 'failed': None, 'process_method': None, 'force': None, 'delete_on': None, 'force_next': None}, FORK_SICKCHILL_API: {'path': None, 'proc_dir': None, 'failed': None, 'process_method': None, 'force': None, 'force_replace': None, 'return_data': None, 'type': None, 'delete': None, 'force_next': None, 'is_priority': None, 'cmd': 'postprocess'}, FORK_SICKBEARD_API: {'path': None, 'failed': None, 'process_method': None, 'force_replace': None, 'return_data': None, 'type': None, 'delete': None, 'force_next': None, 'cmd': 'postprocess'}, FORK_MEDUSA: {'proc_dir': None, 'failed': None, 'process_method': None, 'force': None, 'delete_on': None, 'ignore_subs': None}, FORK_MEDUSA_API: {'path': None, 'failed': None, 'process_method': None, 'force_replace': None, 'return_data': None, 'type': None, 'delete_files': None, 'is_priority': None, 'cmd': 'postprocess'}, FORK_MEDUSA_APIV2: {'proc_dir': None, 'resource': None, 'failed': None, 'process_method': None, 'force': None, 'type': None, 'delete_on': None, 'is_priority': None}, FORK_SICKGEAR: {'dir': None, 'failed': None, 'process_method': None, 'force': None}, FORK_SICKGEAR_API: {'path': None, 'process_method': None, 'force_replace': None, 'return_data': None, 'type': None, 'is_priority': None, 'failed': None, 'cmd': 'sg.postprocess'}, FORK_STHENO: {'proc_dir': None, 'failed': None, 'process_method': None, 'force': None, 'delete_on': None, 'ignore_subs': None}}
|
||||
|
||||
FORKS: typing.Mapping[str, typing.Mapping[str, Any]] = {
|
||||
'default': {'dir': None},
|
||||
'failed': {'dirName': None, 'failed': None},
|
||||
'failed-torrent': {'dir': None, 'failed': None, 'process_method': None},
|
||||
**nzb2media.fork.sickbeard.CONFIG,
|
||||
**nzb2media.fork.sickchill.CONFIG,
|
||||
**nzb2media.fork.sickgear.CONFIG,
|
||||
**nzb2media.fork.medusa.CONFIG,
|
||||
}
|
||||
ALL_FORKS = {k: None for k in set(itertools.chain.from_iterable([FORKS[x].keys() for x in FORKS.keys()]))}
|
||||
# SiCKRAGE OAuth2
|
||||
SICKRAGE_OAUTH_CLIENT_ID = 'nzbtomedia'
|
||||
SICKRAGE_OAUTH_TOKEN_URL = 'https://auth.sickrage.ca/realms/sickrage/protocol/openid-connect/token'
|
||||
# NZBGet Exit Codes
|
||||
NZBGET_POSTPROCESS_PAR_CHECK = 92
|
||||
NZBGET_POSTPROCESS_SUCCESS = 93
|
||||
NZBGET_POSTPROCESS_ERROR = 94
|
||||
NZBGET_POSTPROCESS_NONE = 95
|
||||
CFG = None
|
||||
LOG_DEBUG = None
|
||||
LOG_DB = None
|
||||
LOG_ENV = None
|
||||
LOG_GIT = None
|
||||
SYS_ENCODING = None
|
||||
FAILED = False
|
||||
AUTO_UPDATE = None
|
||||
NZBTOMEDIA_VERSION = __version__
|
||||
NEWEST_VERSION = None
|
||||
NEWEST_VERSION_STRING = None
|
||||
VERSION_NOTIFY = None
|
||||
GIT_PATH = None
|
||||
GIT_USER = None
|
||||
GIT_BRANCH = None
|
||||
GIT_REPO = None
|
||||
FORCE_CLEAN = None
|
||||
SAFE_MODE = None
|
||||
NOEXTRACTFAILED = None
|
||||
NZB_CLIENT_AGENT = None
|
||||
SABNZBD_HOST = ''
|
||||
SABNZBD_PORT = None
|
||||
SABNZBD_APIKEY = None
|
||||
NZB_DEFAULT_DIRECTORY = None
|
||||
TORRENT_CLIENT_AGENT = None
|
||||
TORRENT_CLASS = None
|
||||
USE_LINK = None
|
||||
OUTPUT_DIRECTORY = None
|
||||
NOFLATTEN: list[str] = []
|
||||
DELETE_ORIGINAL = None
|
||||
TORRENT_CHMOD_DIRECTORY = None
|
||||
TORRENT_DEFAULT_DIRECTORY = None
|
||||
TORRENT_RESUME = None
|
||||
TORRENT_RESUME_ON_FAILURE = None
|
||||
REMOTE_PATHS = []
|
||||
UTORRENT_WEB_UI = None
|
||||
UTORRENT_USER = None
|
||||
UTORRENT_PASSWORD = None
|
||||
TRANSMISSION_HOST = None
|
||||
TRANSMISSION_PORT = None
|
||||
TRANSMISSION_USER = None
|
||||
TRANSMISSION_PASSWORD = None
|
||||
SYNO_HOST = None
|
||||
SYNO_PORT = None
|
||||
SYNO_USER = None
|
||||
SYNO_PASSWORD = None
|
||||
DELUGE_HOST = None
|
||||
DELUGE_PORT = None
|
||||
DELUGE_USER = None
|
||||
DELUGE_PASSWORD = None
|
||||
QBITTORRENT_HOST = None
|
||||
QBITTORRENT_PORT = None
|
||||
QBITTORRENT_USER = None
|
||||
QBITTORRENT_PASSWORD = None
|
||||
PLEX_SSL = None
|
||||
PLEX_HOST = None
|
||||
PLEX_PORT = None
|
||||
PLEX_TOKEN = None
|
||||
PLEX_SECTION: list[str] = []
|
||||
EXT_CONTAINER: list[str] = []
|
||||
COMPRESSED_CONTAINER = []
|
||||
MEDIA_CONTAINER = []
|
||||
|
@ -148,104 +73,15 @@ META_CONTAINER = []
|
|||
SECTIONS: list[str] = []
|
||||
CATEGORIES: list[str] = []
|
||||
FORK_SET: list[str] = []
|
||||
MOUNTED = None
|
||||
GETSUBS = False
|
||||
TRANSCODE = None
|
||||
CONCAT = None
|
||||
FFMPEG_PATH: pathlib.Path | None = None
|
||||
SYS_PATH = None
|
||||
DUPLICATE = None
|
||||
IGNOREEXTENSIONS = []
|
||||
VEXTENSION = None
|
||||
OUTPUTVIDEOPATH = None
|
||||
PROCESSOUTPUT = False
|
||||
GENERALOPTS = []
|
||||
OTHEROPTS = []
|
||||
ALANGUAGE = None
|
||||
AINCLUDE = False
|
||||
SLANGUAGES = []
|
||||
SINCLUDE = False
|
||||
SUBSDIR = None
|
||||
ALLOWSUBS = False
|
||||
SEXTRACT = False
|
||||
SEMBED = False
|
||||
BURN = False
|
||||
DEFAULTS = None
|
||||
VCODEC = None
|
||||
VCODEC_ALLOW = []
|
||||
VPRESET = None
|
||||
VFRAMERATE = None
|
||||
VBITRATE = None
|
||||
VLEVEL = None
|
||||
VCRF = None
|
||||
VRESOLUTION = None
|
||||
ACODEC = None
|
||||
ACODEC_ALLOW = []
|
||||
ACHANNELS = None
|
||||
ABITRATE = None
|
||||
ACODEC2 = None
|
||||
ACODEC2_ALLOW = []
|
||||
ACHANNELS2 = None
|
||||
ABITRATE2 = None
|
||||
ACODEC3 = None
|
||||
ACODEC3_ALLOW = []
|
||||
ACHANNELS3 = None
|
||||
ABITRATE3 = None
|
||||
SCODEC = None
|
||||
OUTPUTFASTSTART = None
|
||||
OUTPUTQUALITYPERCENT = None
|
||||
FFMPEG: pathlib.Path | None = None
|
||||
SEVENZIP: pathlib.Path | None = None
|
||||
SHOWEXTRACT = 0
|
||||
PAR2CMD: pathlib.Path | None = None
|
||||
FFPROBE: pathlib.Path | None = None
|
||||
CHECK_MEDIA = None
|
||||
REQUIRE_LAN = None
|
||||
NICENESS = []
|
||||
HWACCEL = False
|
||||
PASSWORDS_FILE = None
|
||||
DOWNLOAD_INFO = None
|
||||
GROUPS = None
|
||||
USER_SCRIPT_MEDIAEXTENSIONS = None
|
||||
USER_SCRIPT = None
|
||||
USER_SCRIPT_PARAM = None
|
||||
USER_SCRIPT_SUCCESSCODES = None
|
||||
USER_SCRIPT_CLEAN = None
|
||||
USER_DELAY = None
|
||||
USER_SCRIPT_RUNONCE = None
|
||||
__INITIALIZED__ = False
|
||||
|
||||
|
||||
def configure_logging():
|
||||
global LOG_FILE
|
||||
global LOG_DIR
|
||||
if 'NTM_LOGFILE' in os.environ:
|
||||
LOG_FILE = os.environ['NTM_LOGFILE']
|
||||
LOG_DIR = os.path.split(LOG_FILE)[0]
|
||||
if not make_dir(LOG_DIR):
|
||||
print('No log folder, logging to screen only')
|
||||
|
||||
|
||||
def configure_process():
|
||||
global MYAPP
|
||||
MYAPP = RunningProcess()
|
||||
while MYAPP.alreadyrunning():
|
||||
print('Waiting for existing session to end')
|
||||
time.sleep(30)
|
||||
|
||||
|
||||
def configure_locale():
|
||||
global SYS_ENCODING
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
SYS_ENCODING = locale.getpreferredencoding()
|
||||
except (locale.Error, OSError):
|
||||
pass
|
||||
# For OSes that are poorly configured I'll just randomly force UTF-8
|
||||
if not SYS_ENCODING or SYS_ENCODING in {'ANSI_X3.4-1968', 'US-ASCII', 'ASCII'}:
|
||||
SYS_ENCODING = 'UTF-8'
|
||||
|
||||
|
||||
def configure_migration():
|
||||
global CONFIG_FILE
|
||||
global CFG
|
||||
|
@ -264,42 +100,15 @@ def configure_migration():
|
|||
CFG = Config(None)
|
||||
|
||||
|
||||
def configure_logging_part_2():
|
||||
global LOG_DB
|
||||
global LOG_DEBUG
|
||||
global LOG_ENV
|
||||
global LOG_GIT
|
||||
# Enable/Disable DEBUG Logging
|
||||
LOG_DB = int(CFG['General']['log_db'])
|
||||
LOG_DEBUG = int(CFG['General']['log_debug'])
|
||||
LOG_ENV = int(CFG['General']['log_env'])
|
||||
LOG_GIT = int(CFG['General']['log_git'])
|
||||
if LOG_ENV:
|
||||
for item in os.environ:
|
||||
log.info(f'{item}: {os.environ[item]}')
|
||||
|
||||
|
||||
def configure_general():
|
||||
global VERSION_NOTIFY
|
||||
global GIT_REPO
|
||||
global GIT_PATH
|
||||
global GIT_USER
|
||||
global GIT_BRANCH
|
||||
global FORCE_CLEAN
|
||||
global FFMPEG_PATH
|
||||
global SYS_PATH
|
||||
global CHECK_MEDIA
|
||||
global REQUIRE_LAN
|
||||
global SAFE_MODE
|
||||
global NOEXTRACTFAILED
|
||||
# Set Version and GIT variables
|
||||
VERSION_NOTIFY = int(CFG['General']['version_notify'])
|
||||
GIT_REPO = 'nzbToMedia'
|
||||
GIT_PATH = CFG['General']['git_path']
|
||||
GIT_USER = CFG['General']['git_user'] or 'clinton-hall'
|
||||
GIT_BRANCH = CFG['General']['git_branch'] or 'master'
|
||||
FORCE_CLEAN = int(CFG['General']['force_clean'])
|
||||
FFMPEG_PATH = pathlib.Path(CFG['General']['ffmpeg_path'])
|
||||
nzb2media.tool.FFMPEG_PATH = pathlib.Path(CFG['General']['ffmpeg_path'])
|
||||
SYS_PATH = CFG['General']['sys_path']
|
||||
CHECK_MEDIA = int(CFG['General']['check_media'])
|
||||
REQUIRE_LAN = None if not CFG['General']['require_lan'] else CFG['General']['require_lan'].split(',')
|
||||
|
@ -307,27 +116,6 @@ def configure_general():
|
|||
NOEXTRACTFAILED = int(CFG['General']['no_extract_failed'])
|
||||
|
||||
|
||||
def configure_updates():
|
||||
global AUTO_UPDATE
|
||||
global MYAPP
|
||||
AUTO_UPDATE = int(CFG['General']['auto_update'])
|
||||
version_checker = version_check.CheckVersion()
|
||||
# Check for updates via GitHUB
|
||||
if version_checker.check_for_new_version() and AUTO_UPDATE:
|
||||
log.info('Auto-Updating nzbToMedia, Please wait ...')
|
||||
if version_checker.update():
|
||||
# restart nzbToMedia
|
||||
try:
|
||||
del MYAPP
|
||||
except Exception:
|
||||
pass
|
||||
restart()
|
||||
else:
|
||||
log.error('Update failed, not restarting. Check your log for more information.')
|
||||
# Set Current Version
|
||||
log.info(f'nzbToMedia Version:{NZBTOMEDIA_VERSION} Branch:{GIT_BRANCH} ({platform.system()} {platform.release()})')
|
||||
|
||||
|
||||
def configure_wake_on_lan():
|
||||
if int(CFG['WakeOnLan']['wake']):
|
||||
wake_up()
|
||||
|
@ -355,38 +143,6 @@ def configure_remote_paths():
|
|||
REMOTE_PATHS = [(local.strip(), remote.strip()) for local, remote in REMOTE_PATHS]
|
||||
|
||||
|
||||
def configure_niceness():
|
||||
global NICENESS
|
||||
try:
|
||||
with subprocess.Popen(['nice'], stdout=DEVNULL, stderr=DEVNULL) as proc:
|
||||
proc.communicate()
|
||||
niceness = CFG['Posix']['niceness']
|
||||
if len(niceness.split(',')) > 1: # Allow passing of absolute command, not just value.
|
||||
NICENESS.extend(niceness.split(','))
|
||||
else:
|
||||
NICENESS.extend(['nice', f'-n{int(niceness)}'])
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
with subprocess.Popen(['ionice'], stdout=DEVNULL, stderr=DEVNULL) as proc:
|
||||
proc.communicate()
|
||||
try:
|
||||
ionice = CFG['Posix']['ionice_class']
|
||||
NICENESS.extend(['ionice', f'-c{int(ionice)}'])
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
if 'ionice' in NICENESS:
|
||||
ionice = CFG['Posix']['ionice_classdata']
|
||||
NICENESS.extend([f'-n{int(ionice)}'])
|
||||
else:
|
||||
NICENESS.extend(['ionice', f'-n{int(ionice)}'])
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def configure_containers():
|
||||
global COMPRESSED_CONTAINER
|
||||
global MEDIA_CONTAINER
|
||||
|
@ -407,234 +163,6 @@ def configure_containers():
|
|||
META_CONTAINER = META_CONTAINER.split(',')
|
||||
|
||||
|
||||
def configure_transcoder():
|
||||
global MOUNTED
|
||||
global GETSUBS
|
||||
global TRANSCODE
|
||||
global DUPLICATE
|
||||
global CONCAT
|
||||
global IGNOREEXTENSIONS
|
||||
global OUTPUTFASTSTART
|
||||
global GENERALOPTS
|
||||
global OTHEROPTS
|
||||
global OUTPUTQUALITYPERCENT
|
||||
global OUTPUTVIDEOPATH
|
||||
global PROCESSOUTPUT
|
||||
global ALANGUAGE
|
||||
global AINCLUDE
|
||||
global SLANGUAGES
|
||||
global SINCLUDE
|
||||
global SEXTRACT
|
||||
global SEMBED
|
||||
global SUBSDIR
|
||||
global VEXTENSION
|
||||
global VCODEC
|
||||
global VPRESET
|
||||
global VFRAMERATE
|
||||
global VBITRATE
|
||||
global VRESOLUTION
|
||||
global VCRF
|
||||
global VLEVEL
|
||||
global VCODEC_ALLOW
|
||||
global ACODEC
|
||||
global ACODEC_ALLOW
|
||||
global ACHANNELS
|
||||
global ABITRATE
|
||||
global ACODEC2
|
||||
global ACODEC2_ALLOW
|
||||
global ACHANNELS2
|
||||
global ABITRATE2
|
||||
global ACODEC3
|
||||
global ACODEC3_ALLOW
|
||||
global ACHANNELS3
|
||||
global ABITRATE3
|
||||
global SCODEC
|
||||
global BURN
|
||||
global HWACCEL
|
||||
global ALLOWSUBS
|
||||
global DEFAULTS
|
||||
MOUNTED = None
|
||||
GETSUBS = int(CFG['Transcoder']['getSubs'])
|
||||
TRANSCODE = int(CFG['Transcoder']['transcode'])
|
||||
DUPLICATE = int(CFG['Transcoder']['duplicate'])
|
||||
CONCAT = int(CFG['Transcoder']['concat'])
|
||||
IGNOREEXTENSIONS = CFG['Transcoder']['ignoreExtensions']
|
||||
if isinstance(IGNOREEXTENSIONS, str):
|
||||
IGNOREEXTENSIONS = IGNOREEXTENSIONS.split(',')
|
||||
OUTPUTFASTSTART = int(CFG['Transcoder']['outputFastStart'])
|
||||
GENERALOPTS = CFG['Transcoder']['generalOptions']
|
||||
if isinstance(GENERALOPTS, str):
|
||||
GENERALOPTS = GENERALOPTS.split(',')
|
||||
if GENERALOPTS == ['']:
|
||||
GENERALOPTS = []
|
||||
if '-fflags' not in GENERALOPTS:
|
||||
GENERALOPTS.append('-fflags')
|
||||
if '+genpts' not in GENERALOPTS:
|
||||
GENERALOPTS.append('+genpts')
|
||||
OTHEROPTS = CFG['Transcoder']['otherOptions']
|
||||
if isinstance(OTHEROPTS, str):
|
||||
OTHEROPTS = OTHEROPTS.split(',')
|
||||
if OTHEROPTS == ['']:
|
||||
OTHEROPTS = []
|
||||
try:
|
||||
OUTPUTQUALITYPERCENT = int(CFG['Transcoder']['outputQualityPercent'])
|
||||
except Exception:
|
||||
pass
|
||||
OUTPUTVIDEOPATH = CFG['Transcoder']['outputVideoPath']
|
||||
PROCESSOUTPUT = int(CFG['Transcoder']['processOutput'])
|
||||
ALANGUAGE = CFG['Transcoder']['audioLanguage']
|
||||
AINCLUDE = int(CFG['Transcoder']['allAudioLanguages'])
|
||||
SLANGUAGES = CFG['Transcoder']['subLanguages']
|
||||
if isinstance(SLANGUAGES, str):
|
||||
SLANGUAGES = SLANGUAGES.split(',')
|
||||
if SLANGUAGES == ['']:
|
||||
SLANGUAGES = []
|
||||
SINCLUDE = int(CFG['Transcoder']['allSubLanguages'])
|
||||
SEXTRACT = int(CFG['Transcoder']['extractSubs'])
|
||||
SEMBED = int(CFG['Transcoder']['embedSubs'])
|
||||
SUBSDIR = CFG['Transcoder']['externalSubDir']
|
||||
VEXTENSION = CFG['Transcoder']['outputVideoExtension'].strip()
|
||||
VCODEC = CFG['Transcoder']['outputVideoCodec'].strip()
|
||||
VCODEC_ALLOW = CFG['Transcoder']['VideoCodecAllow'].strip()
|
||||
if isinstance(VCODEC_ALLOW, str):
|
||||
VCODEC_ALLOW = VCODEC_ALLOW.split(',')
|
||||
if VCODEC_ALLOW == ['']:
|
||||
VCODEC_ALLOW = []
|
||||
VPRESET = CFG['Transcoder']['outputVideoPreset'].strip()
|
||||
try:
|
||||
VFRAMERATE = float(CFG['Transcoder']['outputVideoFramerate'].strip())
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
VCRF = int(CFG['Transcoder']['outputVideoCRF'].strip())
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
VLEVEL = CFG['Transcoder']['outputVideoLevel'].strip()
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
VBITRATE = int((CFG['Transcoder']['outputVideoBitrate'].strip()).replace('k', '000'))
|
||||
except Exception:
|
||||
pass
|
||||
VRESOLUTION = CFG['Transcoder']['outputVideoResolution']
|
||||
ACODEC = CFG['Transcoder']['outputAudioCodec'].strip()
|
||||
ACODEC_ALLOW = CFG['Transcoder']['AudioCodecAllow'].strip()
|
||||
if isinstance(ACODEC_ALLOW, str):
|
||||
ACODEC_ALLOW = ACODEC_ALLOW.split(',')
|
||||
if ACODEC_ALLOW == ['']:
|
||||
ACODEC_ALLOW = []
|
||||
try:
|
||||
ACHANNELS = int(CFG['Transcoder']['outputAudioChannels'].strip())
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
ABITRATE = int((CFG['Transcoder']['outputAudioBitrate'].strip()).replace('k', '000'))
|
||||
except Exception:
|
||||
pass
|
||||
ACODEC2 = CFG['Transcoder']['outputAudioTrack2Codec'].strip()
|
||||
ACODEC2_ALLOW = CFG['Transcoder']['AudioCodec2Allow'].strip()
|
||||
if isinstance(ACODEC2_ALLOW, str):
|
||||
ACODEC2_ALLOW = ACODEC2_ALLOW.split(',')
|
||||
if ACODEC2_ALLOW == ['']:
|
||||
ACODEC2_ALLOW = []
|
||||
try:
|
||||
ACHANNELS2 = int(CFG['Transcoder']['outputAudioTrack2Channels'].strip())
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
ABITRATE2 = int((CFG['Transcoder']['outputAudioTrack2Bitrate'].strip()).replace('k', '000'))
|
||||
except Exception:
|
||||
pass
|
||||
ACODEC3 = CFG['Transcoder']['outputAudioOtherCodec'].strip()
|
||||
ACODEC3_ALLOW = CFG['Transcoder']['AudioOtherCodecAllow'].strip()
|
||||
if isinstance(ACODEC3_ALLOW, str):
|
||||
ACODEC3_ALLOW = ACODEC3_ALLOW.split(',')
|
||||
if ACODEC3_ALLOW == ['']:
|
||||
ACODEC3_ALLOW = []
|
||||
try:
|
||||
ACHANNELS3 = int(CFG['Transcoder']['outputAudioOtherChannels'].strip())
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
ABITRATE3 = int((CFG['Transcoder']['outputAudioOtherBitrate'].strip()).replace('k', '000'))
|
||||
except Exception:
|
||||
pass
|
||||
SCODEC = CFG['Transcoder']['outputSubtitleCodec'].strip()
|
||||
BURN = int(CFG['Transcoder']['burnInSubtitle'].strip())
|
||||
DEFAULTS = CFG['Transcoder']['outputDefault'].strip()
|
||||
HWACCEL = int(CFG['Transcoder']['hwAccel'])
|
||||
allow_subs = ['.mkv', '.mp4', '.m4v', 'asf', 'wma', 'wmv']
|
||||
codec_alias = {'libx264': ['libx264', 'h264', 'h.264', 'AVC', 'MPEG-4'], 'libmp3lame': ['libmp3lame', 'mp3'], 'libfaac': ['libfaac', 'aac', 'faac']}
|
||||
transcode_defaults = {
|
||||
'iPad': {'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'], 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': None, 'ACHANNELS': 2, 'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'SCODEC': 'mov_text'},
|
||||
'iPad-1080p': {'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'], 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': None, 'ACHANNELS': 2, 'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'SCODEC': 'mov_text'},
|
||||
'iPad-720p': {'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None, 'VRESOLUTION': '1280:720', 'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'], 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': None, 'ACHANNELS': 2, 'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'SCODEC': 'mov_text'},
|
||||
'Apple-TV': {'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None, 'VRESOLUTION': '1280:720', 'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'], 'ACODEC': 'ac3', 'ACODEC_ALLOW': ['ac3'], 'ABITRATE': None, 'ACHANNELS': 6, 'ACODEC2': 'aac', 'ACODEC2_ALLOW': ['libfaac'], 'ABITRATE2': None, 'ACHANNELS2': 2, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'SCODEC': 'mov_text'},
|
||||
'iPod': {'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None, 'VRESOLUTION': '1280:720', 'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'], 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2, 'ACODEC2': None, 'ACODEC2_ALLOW': [], 'ABITRATE2': None, 'ACHANNELS2': None, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'SCODEC': 'mov_text'},
|
||||
'iPhone': {'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None, 'VRESOLUTION': '460:320', 'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'], 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2, 'ACODEC2': None, 'ACODEC2_ALLOW': [], 'ABITRATE2': None, 'ACHANNELS2': None, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'SCODEC': 'mov_text'},
|
||||
'PS3': {'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'], 'ACODEC': 'ac3', 'ACODEC_ALLOW': ['ac3'], 'ABITRATE': None, 'ACHANNELS': 6, 'ACODEC2': 'aac', 'ACODEC2_ALLOW': ['libfaac'], 'ABITRATE2': None, 'ACHANNELS2': 2, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'SCODEC': 'mov_text'},
|
||||
'xbox': {'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'], 'ACODEC': 'ac3', 'ACODEC_ALLOW': ['ac3'], 'ABITRATE': None, 'ACHANNELS': 6, 'ACODEC2': None, 'ACODEC2_ALLOW': [], 'ABITRATE2': None, 'ACHANNELS2': None, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'SCODEC': 'mov_text'},
|
||||
'Roku-480p': {'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'], 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2, 'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'SCODEC': 'mov_text'},
|
||||
'Roku-720p': {'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'], 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2, 'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'SCODEC': 'mov_text'},
|
||||
'Roku-1080p': {'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'], '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'},
|
||||
'mkv-bluray': {'VEXTENSION': '.mkv', 'VCODEC': 'libx265', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None, 'VRESOLUTION': None, 'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'hevc', 'h265', 'libx265', 'h.265', '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'},
|
||||
'MKV-SD': {'VEXTENSION': '.mkv', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': '1200k', 'VCRF': None, 'VLEVEL': None, 'VRESOLUTION': '720: -1', 'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'], 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2, 'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'SCODEC': 'mov_text'},
|
||||
}
|
||||
if DEFAULTS and DEFAULTS in transcode_defaults:
|
||||
VEXTENSION = transcode_defaults[DEFAULTS]['VEXTENSION']
|
||||
VCODEC = transcode_defaults[DEFAULTS]['VCODEC']
|
||||
VPRESET = transcode_defaults[DEFAULTS]['VPRESET']
|
||||
VFRAMERATE = transcode_defaults[DEFAULTS]['VFRAMERATE']
|
||||
VBITRATE = transcode_defaults[DEFAULTS]['VBITRATE']
|
||||
VRESOLUTION = transcode_defaults[DEFAULTS]['VRESOLUTION']
|
||||
VCRF = transcode_defaults[DEFAULTS]['VCRF']
|
||||
VLEVEL = transcode_defaults[DEFAULTS]['VLEVEL']
|
||||
VCODEC_ALLOW = transcode_defaults[DEFAULTS]['VCODEC_ALLOW']
|
||||
ACODEC = transcode_defaults[DEFAULTS]['ACODEC']
|
||||
ACODEC_ALLOW = transcode_defaults[DEFAULTS]['ACODEC_ALLOW']
|
||||
ACHANNELS = transcode_defaults[DEFAULTS]['ACHANNELS']
|
||||
ABITRATE = transcode_defaults[DEFAULTS]['ABITRATE']
|
||||
ACODEC2 = transcode_defaults[DEFAULTS]['ACODEC2']
|
||||
ACODEC2_ALLOW = transcode_defaults[DEFAULTS]['ACODEC2_ALLOW']
|
||||
ACHANNELS2 = transcode_defaults[DEFAULTS]['ACHANNELS2']
|
||||
ABITRATE2 = transcode_defaults[DEFAULTS]['ABITRATE2']
|
||||
ACODEC3 = transcode_defaults[DEFAULTS]['ACODEC3']
|
||||
ACODEC3_ALLOW = transcode_defaults[DEFAULTS]['ACODEC3_ALLOW']
|
||||
ACHANNELS3 = transcode_defaults[DEFAULTS]['ACHANNELS3']
|
||||
ABITRATE3 = transcode_defaults[DEFAULTS]['ABITRATE3']
|
||||
SCODEC = transcode_defaults[DEFAULTS]['SCODEC']
|
||||
del transcode_defaults
|
||||
if VEXTENSION in allow_subs:
|
||||
ALLOWSUBS = 1
|
||||
if not VCODEC_ALLOW and VCODEC:
|
||||
VCODEC_ALLOW.extend([VCODEC])
|
||||
for codec in VCODEC_ALLOW:
|
||||
if codec in codec_alias:
|
||||
extra = [item for item in codec_alias[codec] if item not in VCODEC_ALLOW]
|
||||
VCODEC_ALLOW.extend(extra)
|
||||
if not ACODEC_ALLOW and ACODEC:
|
||||
ACODEC_ALLOW.extend([ACODEC])
|
||||
for codec in ACODEC_ALLOW:
|
||||
if codec in codec_alias:
|
||||
extra = [item for item in codec_alias[codec] if item not in ACODEC_ALLOW]
|
||||
ACODEC_ALLOW.extend(extra)
|
||||
if not ACODEC2_ALLOW and ACODEC2:
|
||||
ACODEC2_ALLOW.extend([ACODEC2])
|
||||
for codec in ACODEC2_ALLOW:
|
||||
if codec in codec_alias:
|
||||
extra = [item for item in codec_alias[codec] if item not in ACODEC2_ALLOW]
|
||||
ACODEC2_ALLOW.extend(extra)
|
||||
if not ACODEC3_ALLOW and ACODEC3:
|
||||
ACODEC3_ALLOW.extend([ACODEC3])
|
||||
for codec in ACODEC3_ALLOW:
|
||||
if codec in codec_alias:
|
||||
extra = [item for item in codec_alias[codec] if item not in ACODEC3_ALLOW]
|
||||
ACODEC3_ALLOW.extend(extra)
|
||||
|
||||
|
||||
def configure_passwords_file():
|
||||
global PASSWORDS_FILE
|
||||
PASSWORDS_FILE = CFG['passwords']['PassWordFile']
|
||||
|
@ -650,49 +178,22 @@ def configure_sections(section):
|
|||
CATEGORIES = list(set(CATEGORIES))
|
||||
|
||||
|
||||
def configure_utility_locations():
|
||||
global SHOWEXTRACT
|
||||
global SEVENZIP
|
||||
global FFMPEG
|
||||
global FFPROBE
|
||||
global PAR2CMD
|
||||
|
||||
# Setup FFMPEG, FFPROBE and SEVENZIP locations
|
||||
FFMPEG = tool.find_transcoder(FFMPEG_PATH)
|
||||
FFPROBE = tool.find_video_corruption_detector(FFMPEG_PATH)
|
||||
PAR2CMD = tool.find_archive_repairer()
|
||||
if platform.system() == 'Windows':
|
||||
path = APP_ROOT / f'nzb2media/extractor/bin/{platform.machine()}'
|
||||
else:
|
||||
path = None
|
||||
SEVENZIP = tool.find_unzip(path)
|
||||
|
||||
|
||||
def initialize(section=None):
|
||||
global __INITIALIZED__
|
||||
if __INITIALIZED__:
|
||||
return False
|
||||
configure_logging()
|
||||
configure_process()
|
||||
configure_locale()
|
||||
configure_migration()
|
||||
configure_logging_part_2()
|
||||
# initialize the main SB database
|
||||
main_db.upgrade_database(main_db.DBConnection(), databases.InitialSchema)
|
||||
configure_general()
|
||||
configure_updates()
|
||||
configure_wake_on_lan()
|
||||
configure_nzbs(CFG)
|
||||
configure_torrents(CFG)
|
||||
configure_remote_paths()
|
||||
configure_plex(CFG)
|
||||
configure_niceness()
|
||||
nzb2media.tool.configure_niceness()
|
||||
configure_containers()
|
||||
configure_transcoder()
|
||||
configure_passwords_file()
|
||||
configure_utility_locations()
|
||||
nzb2media.tool.configure_utility_locations()
|
||||
configure_sections(section)
|
||||
configure_torrent_class()
|
||||
__INITIALIZED__ = True
|
||||
# finished initializing
|
||||
return __INITIALIZED__
|
||||
|
|
78
nzb2media/app.py
Normal file
78
nzb2media/app.py
Normal file
|
@ -0,0 +1,78 @@
|
|||
import argparse
|
||||
import logging
|
||||
import os
|
||||
|
||||
import nzb2media
|
||||
import nzb2media.nzb
|
||||
from nzb2media.auto_process.common import ProcessResult
|
||||
from nzb2media.processor import nzbget, sab, manual
|
||||
from nzb2media.processor.nzb import process
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('args', nargs='*')
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None, section=None):
|
||||
# Initialize the config
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG,
|
||||
style='{',
|
||||
format='{asctime} | {levelname:<8} | {message}',
|
||||
datefmt='%Y-%m-%d %H:%M:%S',
|
||||
)
|
||||
parsed = parser.parse_args(argv)
|
||||
nzb2media.initialize(section)
|
||||
|
||||
log.info('#########################################################')
|
||||
log.info('## ..::[ nzbToMedia ]::.. ##')
|
||||
log.info('#########################################################')
|
||||
|
||||
# debug command line options
|
||||
log.debug(f'Options passed into nzbToMedia: {parsed}')
|
||||
args = parsed.args
|
||||
|
||||
# Post-Processing Result
|
||||
result = ProcessResult(
|
||||
message='',
|
||||
status_code=0,
|
||||
)
|
||||
|
||||
# NZBGet
|
||||
if 'NZBOP_SCRIPTDIR' in os.environ:
|
||||
result = nzbget.process()
|
||||
# SABnzbd
|
||||
elif 'SAB_SCRIPT' in os.environ:
|
||||
result = sab.process_script()
|
||||
# SABnzbd Pre 0.7.17
|
||||
elif len(args) >= sab.MINIMUM_ARGUMENTS:
|
||||
result = sab.process(args)
|
||||
# Generic program
|
||||
elif len(args) > 5 and args[5] == 'generic':
|
||||
log.info('Script triggered from generic program')
|
||||
result = process(
|
||||
input_directory=args[1],
|
||||
input_name=args[2],
|
||||
input_category=args[3],
|
||||
download_id=args[4],
|
||||
)
|
||||
elif nzb2media.nzb.NO_MANUAL:
|
||||
log.warning('Invalid number of arguments received from client, and no_manual set')
|
||||
else:
|
||||
manual.process()
|
||||
|
||||
if not result.status_code:
|
||||
log.info(f'The {section or "nzbToMedia"} script completed successfully.')
|
||||
if result.message:
|
||||
print(result.message + '!')
|
||||
if 'NZBOP_SCRIPTDIR' in os.environ: # return code for nzbget v11
|
||||
return nzbget.ExitCode.SUCCESS
|
||||
else:
|
||||
log.error(f'A problem was reported in the {args[0]} script.')
|
||||
if result.message:
|
||||
print(result.message + '!')
|
||||
if 'NZBOP_SCRIPTDIR' in os.environ: # return code for nzbget v11
|
||||
return nzbget.ExitCode.FAILURE
|
||||
return result.status_code
|
|
@ -8,21 +8,22 @@ import time
|
|||
import requests
|
||||
|
||||
import nzb2media
|
||||
import nzb2media.torrent
|
||||
import nzb2media.utils.common
|
||||
from nzb2media import transcoder
|
||||
from nzb2media.auto_process.common import ProcessResult
|
||||
from nzb2media.auto_process.common import command_complete
|
||||
from nzb2media.auto_process.common import completed_download_handling
|
||||
from nzb2media.plugins.subtitles import import_subs
|
||||
from nzb2media.plugins.subtitles import rename_subs
|
||||
from nzb2media.nzb import report_nzb
|
||||
from nzb2media.scene_exceptions import process_all_exceptions
|
||||
from nzb2media.subtitles import import_subs
|
||||
from nzb2media.subtitles import rename_subs
|
||||
from nzb2media.utils.encoding import convert_to_ascii
|
||||
from nzb2media.utils.files import extract_files
|
||||
from nzb2media.utils.files import list_media_files
|
||||
from nzb2media.utils.identification import find_imdbid
|
||||
from nzb2media.utils.network import find_download
|
||||
from nzb2media.utils.network import server_responding
|
||||
from nzb2media.utils.nzb import report_nzb
|
||||
from nzb2media.utils.paths import rchmod
|
||||
from nzb2media.utils.paths import remote_dir
|
||||
from nzb2media.utils.paths import remove_dir
|
||||
|
@ -152,7 +153,7 @@ def process(*, section: str, dir_name: str, input_name: str = '', status: int =
|
|||
if 'NZBOP_VERSION' in os.environ and os.environ['NZBOP_VERSION'][0:5] >= '14.0':
|
||||
print('[NZB] MARK=BAD')
|
||||
if not status:
|
||||
if nzb2media.TRANSCODE == 1:
|
||||
if transcoder.TRANSCODE == 1:
|
||||
result, new_dir_name = transcoder.transcode_directory(dir_name)
|
||||
if not result:
|
||||
log.debug(f'Transcoding succeeded for files in {dir_name}')
|
||||
|
@ -168,7 +169,7 @@ def process(*, section: str, dir_name: str, input_name: str = '', status: int =
|
|||
if not release and '.cp(tt' not in video and imdbid:
|
||||
video_name, video_ext = os.path.splitext(video)
|
||||
video2 = f'{video_name}.cp({imdbid}){video_ext}'
|
||||
if not (client_agent in [nzb2media.TORRENT_CLIENT_AGENT, 'manual'] and nzb2media.USE_LINK == 'move-sym'):
|
||||
if not (client_agent in [nzb2media.torrent.CLIENT_AGENT, 'manual'] and nzb2media.USE_LINK == 'move-sym'):
|
||||
log.debug(f'Renaming: {video} to: {video2}')
|
||||
os.rename(video, video2)
|
||||
if not apikey: # If only using Transcoder functions, exit here.
|
||||
|
|
|
@ -12,21 +12,23 @@ from oauthlib.oauth2 import LegacyApplicationClient
|
|||
from requests_oauthlib import OAuth2Session
|
||||
|
||||
import nzb2media
|
||||
import nzb2media.fork.sickrage
|
||||
import nzb2media.torrent
|
||||
import nzb2media.utils.common
|
||||
from nzb2media import transcoder
|
||||
from nzb2media.auto_process.common import ProcessResult
|
||||
from nzb2media.auto_process.common import command_complete
|
||||
from nzb2media.auto_process.common import completed_download_handling
|
||||
from nzb2media.managers.sickbeard import InitSickBeard
|
||||
from nzb2media.plugins.subtitles import import_subs
|
||||
from nzb2media.plugins.subtitles import rename_subs
|
||||
from nzb2media.nzb import report_nzb
|
||||
from nzb2media.scene_exceptions import process_all_exceptions
|
||||
from nzb2media.subtitles import import_subs
|
||||
from nzb2media.subtitles import rename_subs
|
||||
from nzb2media.utils.common import flatten
|
||||
from nzb2media.utils.encoding import convert_to_ascii
|
||||
from nzb2media.utils.files import extract_files
|
||||
from nzb2media.utils.files import list_media_files
|
||||
from nzb2media.utils.network import server_responding
|
||||
from nzb2media.utils.nzb import report_nzb
|
||||
from nzb2media.utils.paths import rchmod
|
||||
from nzb2media.utils.paths import remote_dir
|
||||
from nzb2media.utils.paths import remove_dir
|
||||
|
@ -85,7 +87,7 @@ def process(*, section: str, dir_name: str, input_name: str = '', status: int =
|
|||
else:
|
||||
log.error('Server did not respond. Exiting')
|
||||
return ProcessResult.failure(f'{section}: Failed to post-process - {section} did not respond.')
|
||||
if client_agent == nzb2media.TORRENT_CLIENT_AGENT and nzb2media.USE_LINK == 'move-sym':
|
||||
if client_agent == nzb2media.torrent.CLIENT_AGENT and nzb2media.USE_LINK == 'move-sym':
|
||||
process_method = 'symlink'
|
||||
if not os.path.isdir(dir_name) and os.path.isfile(dir_name): # If the input directory is a file, assume single file download and split dir/name.
|
||||
dir_name = os.path.split(os.path.normpath(dir_name))[0]
|
||||
|
@ -160,7 +162,7 @@ def process(*, section: str, dir_name: str, input_name: str = '', status: int =
|
|||
status = 1
|
||||
if 'NZBOP_VERSION' in os.environ and os.environ['NZBOP_VERSION'][0:5] >= '14.0':
|
||||
print('[NZB] MARK=BAD')
|
||||
if not status and nzb2media.TRANSCODE == 1:
|
||||
if not status and transcoder.TRANSCODE == 1:
|
||||
# only transcode successful downloads
|
||||
result, new_dir_name = transcoder.transcode_directory(dir_name)
|
||||
if not result:
|
||||
|
@ -300,8 +302,8 @@ def process(*, section: str, dir_name: str, input_name: str = '', status: int =
|
|||
elif section == 'SiCKRAGE':
|
||||
session = requests.Session()
|
||||
if api_version >= 2 and sso_username and sso_password:
|
||||
oauth = OAuth2Session(client=LegacyApplicationClient(client_id=nzb2media.SICKRAGE_OAUTH_CLIENT_ID))
|
||||
oauth_token = oauth.fetch_token(client_id=nzb2media.SICKRAGE_OAUTH_CLIENT_ID, token_url=nzb2media.SICKRAGE_OAUTH_TOKEN_URL, username=sso_username, password=sso_password)
|
||||
oauth = OAuth2Session(client=LegacyApplicationClient(client_id=nzb2media.fork.sickrage.SICKRAGE_OAUTH_CLIENT_ID))
|
||||
oauth_token = oauth.fetch_token(client_id=nzb2media.fork.sickrage.SICKRAGE_OAUTH_CLIENT_ID, token_url=nzb2media.fork.sickrage.SICKRAGE_OAUTH_TOKEN_URL, username=sso_username, password=sso_password)
|
||||
session.headers.update({'Authorization': 'Bearer ' + oauth_token['access_token']})
|
||||
params = {'path': fork_params['path'], 'failed': str(bool(fork_params['failed'])).lower(), 'processMethod': 'move', 'forceReplace': str(bool(fork_params['force_replace'])).lower(), 'returnData': str(bool(fork_params['return_data'])).lower(), 'delete': str(bool(fork_params['delete'])).lower(), 'forceNext': str(bool(fork_params['force_next'])).lower(), 'nzbName': fork_params['nzbName']}
|
||||
else:
|
||||
|
|
|
@ -35,7 +35,23 @@ class InitialSchema(main_db.SchemaUpgrade):
|
|||
|
||||
def execute(self):
|
||||
if not self.has_table('downloads') and not self.has_table('db_version'):
|
||||
queries = ['CREATE TABLE db_version (db_version INTEGER);', 'CREATE TABLE downloads (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 db_version (db_version) VALUES (2);']
|
||||
queries = [
|
||||
'CREATE TABLE db_version (db_version INTEGER);',
|
||||
"""
|
||||
CREATE TABLE downloads (
|
||||
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 db_version (db_version) VALUES (2);',
|
||||
]
|
||||
for query in queries:
|
||||
self.connection.action(query)
|
||||
else:
|
||||
|
@ -47,6 +63,24 @@ class InitialSchema(main_db.SchemaUpgrade):
|
|||
log.critical(f'Your database version ({cur_db_version}) has been incremented past what this version of nzbToMedia supports ({MAX_DB_VERSION}).\nIf you have used other forks of nzbToMedia, your database may be unusable due to their modifications.')
|
||||
sys.exit(1)
|
||||
if cur_db_version < MAX_DB_VERSION: # We need to upgrade.
|
||||
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));', 'INSERT INTO downloads2 SELECT * FROM downloads;', 'DROP TABLE IF EXISTS downloads;', 'ALTER TABLE downloads2 RENAME TO downloads;', 'INSERT INTO db_version (db_version) VALUES (2);']
|
||||
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)
|
||||
);
|
||||
""",
|
||||
'INSERT INTO downloads2 SELECT * FROM downloads;',
|
||||
'DROP TABLE IF EXISTS downloads;',
|
||||
'ALTER TABLE downloads2 RENAME TO downloads;',
|
||||
'INSERT INTO db_version (db_version) VALUES (2);',
|
||||
]
|
||||
for query in queries:
|
||||
self.connection.action(query)
|
||||
|
|
41
nzb2media/deluge.py
Normal file
41
nzb2media/deluge.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from deluge_client import DelugeRPCClient
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
||||
HOST = None
|
||||
PORT = None
|
||||
USERNAME = None
|
||||
PASSWORD = None
|
||||
|
||||
|
||||
def configure_deluge(config):
|
||||
global HOST
|
||||
global PORT
|
||||
global USERNAME
|
||||
global PASSWORD
|
||||
|
||||
HOST = config['DelugeHost'] # localhost
|
||||
PORT = int(config['DelugePort']) # 8084
|
||||
USERNAME = config['DelugeUSR'] # mysecretusr
|
||||
PASSWORD = config['DelugePWD'] # mysecretpwr
|
||||
|
||||
|
||||
def configure_client():
|
||||
agent = 'deluge'
|
||||
host = HOST
|
||||
port = PORT
|
||||
user = USERNAME
|
||||
password = PASSWORD
|
||||
log.debug(f'Connecting to {agent}: http://{host}:{port}')
|
||||
client = DelugeRPCClient(host, port, user, password)
|
||||
try:
|
||||
client.connect()
|
||||
except Exception:
|
||||
log.error('Failed to connect to Deluge')
|
||||
else:
|
||||
return client
|
|
@ -1,158 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import stat
|
||||
import subprocess
|
||||
from subprocess import DEVNULL
|
||||
from subprocess import Popen
|
||||
from subprocess import call
|
||||
from time import sleep
|
||||
|
||||
import nzb2media
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
def extract(file_path, output_destination):
|
||||
success = 0
|
||||
# Using Windows
|
||||
if platform.system() == 'Windows':
|
||||
if not os.path.exists(nzb2media.SEVENZIP):
|
||||
log.error('EXTRACTOR: Could not find 7-zip, Exiting')
|
||||
return False
|
||||
wscriptlocation = os.path.join(os.environ['WINDIR'], 'system32', 'wscript.exe')
|
||||
invislocation = os.path.join(nzb2media.APP_ROOT, 'nzb2media', 'extractor', 'bin', 'invisible.vbs')
|
||||
cmd_7zip = [wscriptlocation, invislocation, str(nzb2media.SHOWEXTRACT), nzb2media.SEVENZIP, 'x', '-y']
|
||||
ext_7zip = ['.rar', '.zip', '.tar.gz', 'tgz', '.tar.bz2', '.tbz', '.tar.lzma', '.tlz', '.7z', '.xz', '.gz']
|
||||
extract_commands = dict.fromkeys(ext_7zip, cmd_7zip)
|
||||
# Using unix
|
||||
else:
|
||||
required_cmds = ['unrar', 'unzip', 'tar', 'unxz', 'unlzma', '7zr', 'bunzip2', 'gunzip']
|
||||
# ## Possible future suport:
|
||||
# gunzip: gz (cmd will delete original archive)
|
||||
# ## the following do not extract to destination dir
|
||||
# '.xz': ['xz', '-d --keep'],
|
||||
# '.lzma': ['xz', '-d --format=lzma --keep'],
|
||||
# '.bz2': ['bzip2', '-d --keep']
|
||||
extract_commands = {'.rar': ['unrar', 'x', '-o+', '-y'], '.tar': ['tar', '-xf'], '.zip': ['unzip'], '.tar.gz': ['tar', '-xzf'], '.tgz': ['tar', '-xzf'], '.tar.bz2': ['tar', '-xjf'], '.tbz': ['tar', '-xjf'], '.tar.lzma': ['tar', '--lzma', '-xf'], '.tlz': ['tar', '--lzma', '-xf'], '.tar.xz': ['tar', '--xz', '-xf'], '.txz': ['tar', '--xz', '-xf'], '.7z': ['7zr', 'x'], '.gz': ['gunzip']}
|
||||
# Test command exists and if not, remove
|
||||
if not os.getenv('TR_TORRENT_DIR'):
|
||||
for cmd in required_cmds:
|
||||
if call(['which', cmd], stdout=DEVNULL, stderr=DEVNULL):
|
||||
# note, returns 0 if exists, or 1 if doesn't exist.
|
||||
for key, val in extract_commands.items():
|
||||
if cmd in val[0]:
|
||||
if not call(['which', '7zr'], stdout=DEVNULL, stderr=DEVNULL):
|
||||
# we do have '7zr'
|
||||
extract_commands[key] = ['7zr', 'x', '-y']
|
||||
elif not call(['which', '7z'], stdout=DEVNULL, stderr=DEVNULL):
|
||||
# we do have '7z'
|
||||
extract_commands[key] = ['7z', 'x', '-y']
|
||||
elif not call(['which', '7za'], stdout=DEVNULL, stderr=DEVNULL):
|
||||
# we do have '7za'
|
||||
extract_commands[key] = ['7za', 'x', '-y']
|
||||
else:
|
||||
log.error(f'EXTRACTOR: {cmd} not found, disabling support for {key}')
|
||||
del extract_commands[key]
|
||||
else:
|
||||
log.warning('EXTRACTOR: Cannot determine which tool to use when called from Transmission')
|
||||
if not extract_commands:
|
||||
log.warning('EXTRACTOR: No archive extracting programs found, plugin will be disabled')
|
||||
ext = os.path.splitext(file_path)
|
||||
cmd = []
|
||||
if ext[1] in {'.gz', '.bz2', '.lzma'}:
|
||||
# Check if this is a tar
|
||||
if os.path.splitext(ext[0])[1] == '.tar':
|
||||
cmd = extract_commands[f'.tar{ext[1]}']
|
||||
else: # Try gunzip
|
||||
cmd = extract_commands[ext[1]]
|
||||
elif ext[1] in {'.1', '.01', '.001'} and os.path.splitext(ext[0])[1] in {'.rar', '.zip', '.7z'}:
|
||||
cmd = extract_commands[os.path.splitext(ext[0])[1]]
|
||||
elif ext[1] in {'.cb7', '.cba', '.cbr', '.cbt', '.cbz'}:
|
||||
# don't extract these comic book archives.
|
||||
return False
|
||||
else:
|
||||
if ext[1] in extract_commands:
|
||||
cmd = extract_commands[ext[1]]
|
||||
else:
|
||||
log.debug(f'EXTRACTOR: Unknown file type: {ext[1]}')
|
||||
return False
|
||||
# Create outputDestination folder
|
||||
nzb2media.make_dir(output_destination)
|
||||
if nzb2media.PASSWORDS_FILE and os.path.isfile(os.path.normpath(nzb2media.PASSWORDS_FILE)):
|
||||
with open(os.path.normpath(nzb2media.PASSWORDS_FILE), encoding='utf-8') as fin:
|
||||
passwords = [line.strip() for line in fin]
|
||||
else:
|
||||
passwords = []
|
||||
log.info(f'Extracting {file_path} to {output_destination}')
|
||||
log.debug(f'Extracting {cmd} {file_path} {output_destination}')
|
||||
orig_files = []
|
||||
orig_dirs = []
|
||||
for directory, subdirs, files in os.walk(output_destination):
|
||||
for subdir in subdirs:
|
||||
orig_dirs.append(os.path.join(directory, subdir))
|
||||
for file in files:
|
||||
orig_files.append(os.path.join(directory, file))
|
||||
pwd = os.getcwd() # Get our Present Working Directory
|
||||
# Not all unpack commands accept full paths, so just extract into this directory
|
||||
os.chdir(output_destination)
|
||||
try: # now works same for nt and *nix
|
||||
info = None
|
||||
cmd.append(file_path) # add filePath to final cmd arg.
|
||||
if platform.system() == 'Windows':
|
||||
info = subprocess.STARTUPINFO()
|
||||
info.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
else:
|
||||
cmd = nzb2media.NICENESS + cmd
|
||||
cmd2 = cmd
|
||||
if 'gunzip' not in cmd: # gunzip doesn't support password
|
||||
cmd2.append('-p-') # don't prompt for password.
|
||||
with Popen(cmd2, stdout=DEVNULL, stderr=DEVNULL, startupinfo=info) as proc:
|
||||
res = proc.wait() # should extract files fine.
|
||||
if not res: # Both Linux and Windows return 0 for successful.
|
||||
log.info(f'EXTRACTOR: Extraction was successful for {file_path} to {output_destination}')
|
||||
success = 1
|
||||
elif len(passwords) > 0 and 'gunzip' not in cmd:
|
||||
log.info('EXTRACTOR: Attempting to extract with passwords')
|
||||
for password in passwords:
|
||||
if not password:
|
||||
continue # if edited in windows or otherwise if blank lines.
|
||||
cmd2 = cmd
|
||||
# append password here.
|
||||
passcmd = f'-p{password}'
|
||||
cmd2.append(passcmd)
|
||||
with Popen(cmd2, stdout=DEVNULL, stderr=DEVNULL, startupinfo=info) as proc:
|
||||
res = proc.wait() # should extract files fine.
|
||||
if not res or (res >= 0 and platform == 'Windows'):
|
||||
log.info(f'EXTRACTOR: Extraction was successful for {file_path} to {output_destination} using password: {password}')
|
||||
success = 1
|
||||
break
|
||||
except Exception:
|
||||
log.error(f'EXTRACTOR: Extraction failed for {file_path}. Could not call command {cmd}')
|
||||
os.chdir(pwd)
|
||||
return False
|
||||
os.chdir(pwd) # Go back to our Original Working Directory
|
||||
if success:
|
||||
# sleep to let files finish writing to disk
|
||||
sleep(3)
|
||||
perms = stat.S_IMODE(os.lstat(os.path.split(file_path)[0]).st_mode)
|
||||
for directory, subdirs, files in os.walk(output_destination):
|
||||
for subdir in subdirs:
|
||||
if not os.path.join(directory, subdir) in orig_files:
|
||||
try:
|
||||
os.chmod(os.path.join(directory, subdir), perms)
|
||||
except Exception:
|
||||
pass
|
||||
for file in files:
|
||||
if not os.path.join(directory, file) in orig_files:
|
||||
try:
|
||||
shutil.copymode(file_path, os.path.join(directory, file))
|
||||
except Exception:
|
||||
pass
|
||||
return True
|
||||
log.error(f'EXTRACTOR: Extraction failed for {file_path}. Result was {res}')
|
||||
return False
|
16
nzb2media/fork/medusa.py
Normal file
16
nzb2media/fork/medusa.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
MEDUSA_KEYS = ('proc_dir', 'failed', 'process_method', 'force', 'delete_on', 'ignore_subs')
|
||||
MEDUSA_API_KEYS = ('path', 'failed', 'process_method', 'force_replace', 'return_data', 'type', 'delete_files', 'is_priority')
|
||||
MEDUSA_API_V2_KEYS = ('proc_dir', 'resource', 'failed', 'process_method', 'force', 'type', 'delete_on', 'is_priority')
|
||||
|
||||
CONFIG: dict[str, dict[str, Any]] = {
|
||||
'Medusa': {key: None for key in MEDUSA_KEYS},
|
||||
'Medusa-api': {
|
||||
**{key: None for key in MEDUSA_API_KEYS},
|
||||
'cmd': 'postprocess',
|
||||
},
|
||||
'Medusa-apiv2': {key: None for key in MEDUSA_API_V2_KEYS},
|
||||
}
|
12
nzb2media/fork/sickbeard.py
Normal file
12
nzb2media/fork/sickbeard.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
SICKBEARD_API_KEYS = ('path', 'failed', 'process_method', 'force_replace', 'return_data', 'type', 'delete', 'force_next')
|
||||
|
||||
CONFIG: dict[str, dict[str, Any]] = {
|
||||
'SickBeard-api': {
|
||||
**{key: None for key in SICKBEARD_API_KEYS},
|
||||
'cmd': 'postprocess',
|
||||
},
|
||||
}
|
14
nzb2media/fork/sickchill.py
Normal file
14
nzb2media/fork/sickchill.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
SICKCHILL_KEYS = ('proc_dir', 'failed', 'process_method', 'force', 'delete_on', 'force_next')
|
||||
SICKCHILL_API_KEYS = ('path', 'proc_dir', 'failed', 'process_method', 'force', 'force_replace', 'return_data', 'type', 'delete', 'force_next', 'is_priority')
|
||||
|
||||
CONFIG: dict[str, dict[str, Any]] = {
|
||||
'SickChill': {key: None for key in SICKCHILL_KEYS},
|
||||
'SickChill-api': {
|
||||
**{key: None for key in SICKCHILL_API_KEYS},
|
||||
'cmd': 'postprocess',
|
||||
},
|
||||
}
|
14
nzb2media/fork/sickgear.py
Normal file
14
nzb2media/fork/sickgear.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
SICKGEAR_KEYS = ('dir', 'failed', 'process_method', 'force')
|
||||
SICKGEAR_API_KEYS = ('path', 'process_method', 'force_replace', 'return_data', 'type', 'is_priority', 'failed')
|
||||
|
||||
CONFIG: dict[str, dict[str, Any]] = {
|
||||
'SickGear': {key: None for key in SICKGEAR_KEYS},
|
||||
'SickGear-api': {
|
||||
'cmd': 'sg.postprocess',
|
||||
**{key: None for key in SICKGEAR_API_KEYS},
|
||||
},
|
||||
}
|
4
nzb2media/fork/sickrage.py
Normal file
4
nzb2media/fork/sickrage.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
from __future__ import annotations
|
||||
|
||||
SICKRAGE_OAUTH_CLIENT_ID = 'nzbtomedia'
|
||||
SICKRAGE_OAUTH_TOKEN_URL = 'https://auth.sickrage.ca/realms/sickrage/protocol/openid-connect/token'
|
|
@ -1,42 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
class GitHub:
|
||||
"""Simple api wrapper for the Github API v3."""
|
||||
|
||||
def __init__(self, github_repo_user, github_repo, branch='master'):
|
||||
self.github_repo_user = github_repo_user
|
||||
self.github_repo = github_repo
|
||||
self.branch = branch
|
||||
|
||||
@staticmethod
|
||||
def _access_api(path, params=None):
|
||||
"""Access API at given an API path and optional parameters."""
|
||||
route = '/'.join(path)
|
||||
url = f'https://api.github.com/{route}'
|
||||
data = requests.get(url, params=params, verify=False)
|
||||
return data.json() if data.ok else []
|
||||
|
||||
def commits(self):
|
||||
"""Get the 100 most recent commits from the specified user/repo/branch, starting from HEAD.
|
||||
|
||||
user: The github username of the person whose repo you're querying
|
||||
repo: The repo name to query
|
||||
branch: Optional, the branch name to show commits from
|
||||
Returns a deserialized json object containing the commit info. See http://developer.github.com/v3/repos/commits/
|
||||
"""
|
||||
return self._access_api(['repos', self.github_repo_user, self.github_repo, 'commits'], params={'per_page': 100, 'sha': self.branch})
|
||||
|
||||
def compare(self, base, head, per_page=1):
|
||||
"""Get compares between base and head.
|
||||
|
||||
user: The github username of the person whose repo you're querying
|
||||
repo: The repo name to query
|
||||
base: Start compare from branch
|
||||
head: Current commit sha or branch name to compare
|
||||
per_page: number of items per page
|
||||
Returns a deserialized json object containing the compare info. See http://developer.github.com/v3/repos/commits/
|
||||
"""
|
||||
return self._access_api(['repos', self.github_repo_user, self.github_repo, 'compare', f'{base}...{head}'], params={'per_page': per_page})
|
|
@ -8,6 +8,8 @@ from oauthlib.oauth2 import LegacyApplicationClient
|
|||
from requests_oauthlib import OAuth2Session
|
||||
|
||||
import nzb2media
|
||||
import nzb2media.fork.sickrage
|
||||
import nzb2media.torrent
|
||||
from nzb2media.auto_process.common import ProcessResult
|
||||
from nzb2media.utils.paths import remote_dir
|
||||
|
||||
|
@ -87,8 +89,8 @@ class InitSickBeard:
|
|||
api_params = {'cmd': 'postprocess', 'help': '1'}
|
||||
try:
|
||||
if self.api_version >= 2 and self.sso_username and self.sso_password:
|
||||
oauth = OAuth2Session(client=LegacyApplicationClient(client_id=nzb2media.SICKRAGE_OAUTH_CLIENT_ID))
|
||||
oauth_token = oauth.fetch_token(client_id=nzb2media.SICKRAGE_OAUTH_CLIENT_ID, token_url=nzb2media.SICKRAGE_OAUTH_TOKEN_URL, username=self.sso_username, password=self.sso_password)
|
||||
oauth = OAuth2Session(client=LegacyApplicationClient(client_id=nzb2media.fork.sickrage.SICKRAGE_OAUTH_CLIENT_ID))
|
||||
oauth_token = oauth.fetch_token(client_id=nzb2media.fork.sickrage.SICKRAGE_OAUTH_CLIENT_ID, token_url=nzb2media.fork.sickrage.SICKRAGE_OAUTH_TOKEN_URL, username=self.sso_username, password=self.sso_password)
|
||||
token = oauth_token['access_token']
|
||||
response = requests.get(url, headers={'Authorization': f'Bearer {token}'}, stream=True, verify=False)
|
||||
else:
|
||||
|
@ -258,7 +260,7 @@ class SickBeard:
|
|||
self.extract = 0
|
||||
else:
|
||||
self.extract = int(self.sb_init.config.get('extract', 0))
|
||||
if client_agent == nzb2media.TORRENT_CLIENT_AGENT and nzb2media.USE_LINK == 'move-sym':
|
||||
if client_agent == nzb2media.torrent.CLIENT_AGENT and nzb2media.USE_LINK == 'move-sym':
|
||||
self.process_method = 'symlink'
|
||||
|
||||
@property
|
||||
|
|
|
@ -5,22 +5,52 @@ import os
|
|||
|
||||
import requests
|
||||
|
||||
import nzb2media
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
||||
CLIENTS = ['sabnzbd', 'nzbget', 'manual']
|
||||
CLIENT_AGENT = None
|
||||
DEFAULT_DIRECTORY = None
|
||||
NO_MANUAL = None
|
||||
|
||||
SABNZBD_HOST = ''
|
||||
SABNZBD_PORT = None
|
||||
SABNZBD_APIKEY = None
|
||||
|
||||
|
||||
def configure_nzbs(config):
|
||||
global CLIENT_AGENT
|
||||
global DEFAULT_DIRECTORY
|
||||
global NO_MANUAL
|
||||
|
||||
nzb_config = config['Nzb']
|
||||
CLIENT_AGENT = nzb_config['clientAgent'] # sabnzbd
|
||||
DEFAULT_DIRECTORY = nzb_config['default_downloadDirectory']
|
||||
NO_MANUAL = int(nzb_config['no_manual'], 0)
|
||||
configure_sabnzbd(nzb_config)
|
||||
|
||||
|
||||
def configure_sabnzbd(config):
|
||||
global SABNZBD_HOST
|
||||
global SABNZBD_PORT
|
||||
global SABNZBD_APIKEY
|
||||
|
||||
SABNZBD_HOST = config['sabnzbd_host']
|
||||
# defaults to accommodate NzbGet
|
||||
SABNZBD_PORT = int(config['sabnzbd_port'] or 8080)
|
||||
SABNZBD_APIKEY = config['sabnzbd_apikey']
|
||||
|
||||
|
||||
def get_nzoid(input_name):
|
||||
nzoid = None
|
||||
slots = []
|
||||
log.debug('Searching for nzoid from SAbnzbd ...')
|
||||
if 'http' in nzb2media.SABNZBD_HOST:
|
||||
base_url = f'{nzb2media.SABNZBD_HOST}:{nzb2media.SABNZBD_PORT}/api'
|
||||
if 'http' in SABNZBD_HOST:
|
||||
base_url = f'{SABNZBD_HOST}:{SABNZBD_PORT}/api'
|
||||
else:
|
||||
base_url = f'http://{nzb2media.SABNZBD_HOST}:{nzb2media.SABNZBD_PORT}/api'
|
||||
base_url = f'http://{SABNZBD_HOST}:{SABNZBD_PORT}/api'
|
||||
url = base_url
|
||||
params = {'apikey': nzb2media.SABNZBD_APIKEY, 'mode': 'queue', 'output': 'json'}
|
||||
params = {'apikey': SABNZBD_APIKEY, 'mode': 'queue', 'output': 'json'}
|
||||
try:
|
||||
response = requests.get(url, params=params, verify=False, timeout=(30, 120))
|
||||
except requests.ConnectionError:
|
|
@ -1,18 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import nzb2media
|
||||
|
||||
|
||||
def configure_nzbs(config):
|
||||
nzb_config = config['Nzb']
|
||||
nzb2media.NZB_CLIENT_AGENT = nzb_config['clientAgent'] # sabnzbd
|
||||
nzb2media.NZB_DEFAULT_DIRECTORY = nzb_config['default_downloadDirectory']
|
||||
nzb2media.NZB_NO_MANUAL = int(nzb_config['no_manual'], 0)
|
||||
configure_sabnzbd(nzb_config)
|
||||
|
||||
|
||||
def configure_sabnzbd(config):
|
||||
nzb2media.SABNZBD_HOST = config['sabnzbd_host']
|
||||
# defaults to accommodate NzbGet
|
||||
nzb2media.SABNZBD_PORT = int(config['sabnzbd_port'] or 8080)
|
||||
nzb2media.SABNZBD_APIKEY = config['sabnzbd_apikey']
|
|
@ -9,34 +9,46 @@ import nzb2media
|
|||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
||||
SSL = None
|
||||
HOST = None
|
||||
PORT = None
|
||||
TOKEN = None
|
||||
SECTION: list[tuple[str, str]] = []
|
||||
|
||||
|
||||
def configure_plex(config):
|
||||
nzb2media.PLEX_SSL = int(config['Plex']['plex_ssl'])
|
||||
nzb2media.PLEX_HOST = config['Plex']['plex_host']
|
||||
nzb2media.PLEX_PORT = config['Plex']['plex_port']
|
||||
nzb2media.PLEX_TOKEN = config['Plex']['plex_token']
|
||||
global SSL
|
||||
global HOST
|
||||
global PORT
|
||||
global TOKEN
|
||||
global SECTION
|
||||
|
||||
SSL = int(config['Plex']['plex_ssl'])
|
||||
HOST = config['Plex']['plex_host']
|
||||
PORT = config['Plex']['plex_port']
|
||||
TOKEN = config['Plex']['plex_token']
|
||||
plex_section = config['Plex']['plex_sections'] or []
|
||||
if plex_section:
|
||||
if isinstance(plex_section, list):
|
||||
plex_section = ','.join(plex_section) # fix in case this imported as list.
|
||||
plex_section = [tuple(item.split(',')) for item in plex_section.split('|')]
|
||||
nzb2media.PLEX_SECTION = plex_section
|
||||
SECTION = plex_section
|
||||
|
||||
|
||||
def plex_update(category):
|
||||
if nzb2media.FAILED:
|
||||
return
|
||||
scheme = 'https' if nzb2media.PLEX_SSL else 'http'
|
||||
url = f'{scheme}://{nzb2media.PLEX_HOST}:{nzb2media.PLEX_PORT}/library/sections/'
|
||||
scheme = 'https' if SSL else 'http'
|
||||
url = f'{scheme}://{HOST}:{PORT}/library/sections/'
|
||||
section = None
|
||||
if not nzb2media.PLEX_SECTION:
|
||||
if not SECTION:
|
||||
return
|
||||
log.debug(f'Attempting to update Plex Library for category {category}.')
|
||||
for item in nzb2media.PLEX_SECTION:
|
||||
for item in SECTION:
|
||||
if item[0] == category:
|
||||
section = item[1]
|
||||
if section:
|
||||
url = f'{url}{section}/refresh?X-Plex-Token={nzb2media.PLEX_TOKEN}'
|
||||
url = f'{url}{section}/refresh?X-Plex-Token={TOKEN}'
|
||||
requests.get(url, timeout=(60, 120), verify=False)
|
||||
log.debug('Plex Library has been refreshed.')
|
||||
else:
|
|
@ -4,6 +4,7 @@ import logging
|
|||
import os
|
||||
|
||||
import nzb2media
|
||||
import nzb2media.nzb
|
||||
from nzb2media.auto_process.common import ProcessResult
|
||||
from nzb2media.processor import nzb
|
||||
from nzb2media.utils.common import get_dirs
|
||||
|
@ -20,25 +21,35 @@ def process():
|
|||
result = ProcessResult(message='', status_code=0)
|
||||
for section, subsections in nzb2media.SECTIONS.items():
|
||||
for subsection in subsections:
|
||||
if not nzb2media.CFG[section][subsection].isenabled():
|
||||
continue
|
||||
for dir_name in get_dirs(section, subsection, link='move'):
|
||||
log.info(f'Starting manual run for {section}:{subsection} - Folder: {dir_name}')
|
||||
log.info(f'Checking database for download info for {os.path.basename(dir_name)} ...')
|
||||
nzb2media.DOWNLOAD_INFO = get_download_info(os.path.basename(dir_name), 0)
|
||||
if nzb2media.DOWNLOAD_INFO:
|
||||
log.info(f'Found download info for {os.path.basename(dir_name)}, setting variables now ...')
|
||||
client_agent = nzb2media.DOWNLOAD_INFO[0]['client_agent'] or 'manual'
|
||||
download_id = nzb2media.DOWNLOAD_INFO[0]['input_id'] or ''
|
||||
else:
|
||||
log.info(f'Unable to locate download info for {os.path.basename(dir_name)}, continuing to try and process this release ...')
|
||||
client_agent = 'manual'
|
||||
download_id = ''
|
||||
if client_agent and client_agent.lower() not in nzb2media.NZB_CLIENTS:
|
||||
continue
|
||||
input_name = os.path.basename(dir_name)
|
||||
results = nzb.process(dir_name, input_name, 0, client_agent=client_agent, download_id=download_id or None, input_category=subsection)
|
||||
if results.status_code:
|
||||
log.error(f'A problem was reported when trying to perform a manual run for {section}:{subsection}.')
|
||||
result = results
|
||||
if nzb2media.CFG[section][subsection].isenabled():
|
||||
result = _process(section, subsection)
|
||||
return result
|
||||
|
||||
|
||||
def _process(section, subsection):
|
||||
for dir_name in get_dirs(section, subsection, link='move'):
|
||||
log.info(f'Starting manual run for {section}:{subsection} - Folder: {dir_name}')
|
||||
log.info(f'Checking database for download info for {os.path.basename(dir_name)} ...')
|
||||
download_info = get_download_info(os.path.basename(dir_name), 0)
|
||||
nzb2media.DOWNLOAD_INFO = download_info
|
||||
if download_info:
|
||||
log.info(f'Found download info for {os.path.basename(dir_name)}, setting variables now ...')
|
||||
else:
|
||||
log.info(f'Unable to locate download info for {os.path.basename(dir_name)}, continuing to try and process this release ...')
|
||||
client_agent, download_id = _process_download_info(nzb2media.DOWNLOAD_INFO)
|
||||
if client_agent != 'manual' and client_agent.lower() not in nzb2media.nzb.CLIENTS:
|
||||
continue
|
||||
input_name = os.path.basename(dir_name)
|
||||
result = nzb.process(input_directory=dir_name, input_name=input_name, client_agent=client_agent, download_id=download_id or None, input_category=subsection)
|
||||
if result.status_code:
|
||||
log.error(f'A problem was reported when trying to perform a manual run for {section}:{subsection}.')
|
||||
return result
|
||||
|
||||
|
||||
def _process_download_info(download_info):
|
||||
agent = None
|
||||
download_id = None
|
||||
if not download_info:
|
||||
agent = download_info[0]['client_agent']
|
||||
download_id = download_info[0]['input_id']
|
||||
return agent or 'manual', download_id or ''
|
||||
|
|
|
@ -4,6 +4,7 @@ import datetime
|
|||
import logging
|
||||
|
||||
import nzb2media
|
||||
import nzb2media.nzb
|
||||
from nzb2media import main_db
|
||||
from nzb2media.auto_process import books
|
||||
from nzb2media.auto_process import comics
|
||||
|
@ -12,21 +13,21 @@ from nzb2media.auto_process import movies
|
|||
from nzb2media.auto_process import music
|
||||
from nzb2media.auto_process import tv
|
||||
from nzb2media.auto_process.common import ProcessResult
|
||||
from nzb2media.plugins.plex import plex_update
|
||||
from nzb2media.nzb import get_nzoid
|
||||
from nzb2media.plex import plex_update
|
||||
from nzb2media.user_scripts import external_script
|
||||
from nzb2media.utils.common import clean_dir
|
||||
from nzb2media.utils.download_info import update_download_info_status
|
||||
from nzb2media.utils.encoding import char_replace
|
||||
from nzb2media.utils.encoding import convert_to_ascii
|
||||
from nzb2media.utils.files import extract_files
|
||||
from nzb2media.utils.nzb import get_nzoid
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
def process(input_directory, input_name=None, status=0, client_agent='manual', download_id=None, input_category=None, failure_link=None):
|
||||
if nzb2media.SAFE_MODE and input_directory == nzb2media.NZB_DEFAULT_DIRECTORY:
|
||||
def process(*, input_directory, input_name=None, status=0, client_agent='manual', download_id=None, input_category=None, failure_link=None):
|
||||
if nzb2media.SAFE_MODE and input_directory == nzb2media.nzb.DEFAULT_DIRECTORY:
|
||||
log.error(f'The input directory:[{input_directory}] is the Default Download Directory. Please configure category directories to prevent processing of other media.')
|
||||
return ProcessResult(message='', status_code=-1)
|
||||
if not download_id and client_agent == 'sabnzbd':
|
||||
|
|
|
@ -1,12 +1,22 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import enum
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
import nzb2media
|
||||
from nzb2media.processor import nzb
|
||||
|
||||
|
||||
class ExitCode(enum.IntEnum):
|
||||
"""Exit codes for post-processing with NZBget."""
|
||||
|
||||
REPAIR = 92
|
||||
SUCCESS = 93
|
||||
FAILURE = 94
|
||||
SKIPPED = 95
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
||||
|
@ -86,7 +96,7 @@ def check_version():
|
|||
# Check if the script is called from nzbget 11.0 or later
|
||||
if version[0:5] < '11.0':
|
||||
log.error(f'NZBGet Version {version} is not supported. Please update NZBGet.')
|
||||
sys.exit(nzb2media.NZBGET_POSTPROCESS_ERROR)
|
||||
sys.exit(ExitCode.FAILURE)
|
||||
log.info(f'Script triggered from NZBGet Version {version}.')
|
||||
|
||||
|
||||
|
@ -95,4 +105,12 @@ def process():
|
|||
status = parse_status()
|
||||
download_id = parse_download_id()
|
||||
failure_link = parse_failure_link()
|
||||
return nzb.process(input_directory=os.environ['NZBPP_DIRECTORY'], input_name=os.environ['NZBPP_NZBNAME'], status=status, client_agent='nzbget', download_id=download_id, input_category=os.environ['NZBPP_CATEGORY'], failure_link=failure_link)
|
||||
return nzb.process(
|
||||
input_directory=os.environ['NZBPP_DIRECTORY'],
|
||||
input_name=os.environ['NZBPP_NZBNAME'],
|
||||
status=status,
|
||||
client_agent='nzbget',
|
||||
download_id=download_id,
|
||||
input_category=os.environ['NZBPP_CATEGORY'],
|
||||
failure_link=failure_link,
|
||||
)
|
||||
|
|
|
@ -13,7 +13,15 @@ MINIMUM_ARGUMENTS = 8
|
|||
def process_script():
|
||||
version = os.environ['SAB_VERSION']
|
||||
log.info(f'Script triggered from SABnzbd {version}.')
|
||||
return nzb.process(input_directory=os.environ['SAB_COMPLETE_DIR'], input_name=os.environ['SAB_FINAL_NAME'], status=int(os.environ['SAB_PP_STATUS']), client_agent='sabnzbd', download_id=os.environ['SAB_NZO_ID'], input_category=os.environ['SAB_CAT'], failure_link=os.environ['SAB_FAILURE_URL'])
|
||||
return nzb.process(
|
||||
input_directory=os.environ['SAB_COMPLETE_DIR'],
|
||||
input_name=os.environ['SAB_FINAL_NAME'],
|
||||
status=int(os.environ['SAB_PP_STATUS']),
|
||||
client_agent='sabnzbd',
|
||||
download_id=os.environ['SAB_NZO_ID'],
|
||||
input_category=os.environ['SAB_CAT'],
|
||||
failure_link=os.environ['SAB_FAILURE_URL'],
|
||||
)
|
||||
|
||||
|
||||
def process(args):
|
||||
|
@ -35,4 +43,12 @@ def process(args):
|
|||
"""
|
||||
version = '0.7.17+' if len(args) > MINIMUM_ARGUMENTS else ''
|
||||
log.info(f'Script triggered from SABnzbd {version}')
|
||||
return nzb.process(input_directory=args[1], input_name=args[2], status=int(args[7]), input_category=args[5], client_agent='sabnzbd', download_id='', failure_link=''.join(args[8:]))
|
||||
return nzb.process(
|
||||
input_directory=args[1],
|
||||
input_name=args[2],
|
||||
status=int(args[7]),
|
||||
input_category=args[5],
|
||||
client_agent='sabnzbd',
|
||||
download_id='',
|
||||
failure_link=''.join(args[8:]),
|
||||
)
|
||||
|
|
|
@ -4,18 +4,33 @@ import logging
|
|||
|
||||
from qbittorrent import Client as qBittorrentClient
|
||||
|
||||
import nzb2media
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
||||
HOST = None
|
||||
PORT = None
|
||||
USERNAME = None
|
||||
PASSWORD = None
|
||||
|
||||
|
||||
def configure_qbittorrent(config):
|
||||
global HOST
|
||||
global PORT
|
||||
global USERNAME
|
||||
global PASSWORD
|
||||
|
||||
HOST = config['qBittorrentHost'] # localhost
|
||||
PORT = int(config['qBittorrentPort']) # 8080
|
||||
USERNAME = config['qBittorrentUSR'] # mysecretusr
|
||||
PASSWORD = config['qBittorrentPWD'] # mysecretpwr
|
||||
|
||||
|
||||
def configure_client():
|
||||
agent = 'qbittorrent'
|
||||
host = nzb2media.QBITTORRENT_HOST
|
||||
port = nzb2media.QBITTORRENT_PORT
|
||||
user = nzb2media.QBITTORRENT_USER
|
||||
password = nzb2media.QBITTORRENT_PASSWORD
|
||||
host = HOST
|
||||
port = PORT
|
||||
user = USERNAME
|
||||
password = PASSWORD
|
||||
log.debug(f'Connecting to {agent}: http://{host}:{port}')
|
||||
client = qBittorrentClient(f'http://{host}:{port}/')
|
||||
try:
|
|
@ -8,6 +8,7 @@ import subprocess
|
|||
from subprocess import DEVNULL
|
||||
|
||||
import nzb2media
|
||||
import nzb2media.tool
|
||||
from nzb2media.utils.files import list_media_files
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -149,11 +150,11 @@ def par2(dirname):
|
|||
if size > sofar:
|
||||
sofar = size
|
||||
parfile = item
|
||||
if nzb2media.PAR2CMD and parfile:
|
||||
if nzb2media.tool.PAR2CMD and parfile:
|
||||
pwd = os.getcwd() # Get our Present Working Directory
|
||||
os.chdir(dirname) # set directory to run par on.
|
||||
log.info(f'Running par2 on file {parfile}.')
|
||||
command = [nzb2media.PAR2CMD, 'r', parfile, '*']
|
||||
command = [nzb2media.tool.PAR2CMD, 'r', parfile, '*']
|
||||
cmd = ''
|
||||
for item in command:
|
||||
cmd = f'{cmd} {item}'
|
||||
|
@ -168,7 +169,6 @@ def par2(dirname):
|
|||
log.info('par2 file processing succeeded')
|
||||
os.chdir(pwd)
|
||||
|
||||
|
||||
# dict for custom groups
|
||||
# we can add more to this list
|
||||
# _customgroups = {'Q o Q': process_qoq, '-ECI': process_eci}
|
||||
|
|
|
@ -7,8 +7,7 @@ import re
|
|||
import subliminal
|
||||
from babelfish import Language
|
||||
|
||||
from nzb2media import GETSUBS
|
||||
from nzb2media import SLANGUAGES
|
||||
from nzb2media.transcoder import GETSUBS, SLANGUAGES
|
||||
from nzb2media.utils.files import list_media_files
|
||||
|
||||
log = logging.getLogger(__name__)
|
40
nzb2media/synology.py
Normal file
40
nzb2media/synology.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from syno.downloadstation import DownloadStation
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
||||
HOST = None
|
||||
PORT = None
|
||||
USERNAME = None
|
||||
PASSWORD = None
|
||||
|
||||
|
||||
def configure_syno(config):
|
||||
global HOST
|
||||
global PORT
|
||||
global USERNAME
|
||||
global PASSWORD
|
||||
|
||||
HOST = config['synoHost'] # localhost
|
||||
PORT = int(config['synoPort'])
|
||||
USERNAME = config['synoUSR'] # mysecretusr
|
||||
PASSWORD = config['synoPWD'] # mysecretpwr
|
||||
|
||||
|
||||
def configure_client():
|
||||
agent = 'synology'
|
||||
host = HOST
|
||||
port = PORT
|
||||
user = USERNAME
|
||||
password = PASSWORD
|
||||
log.debug(f'Connecting to {agent}: http://{host}:{port}')
|
||||
try:
|
||||
client = DownloadStation(host, port, user, password)
|
||||
except Exception:
|
||||
log.error('Failed to connect to synology')
|
||||
else:
|
||||
return client
|
|
@ -4,12 +4,25 @@ import itertools
|
|||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import platform
|
||||
import shutil
|
||||
import stat
|
||||
import subprocess
|
||||
import typing
|
||||
from subprocess import call, DEVNULL, Popen
|
||||
from time import sleep
|
||||
|
||||
import nzb2media
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
||||
FFMPEG: pathlib.Path | None = None
|
||||
FFPROBE: pathlib.Path | None = None
|
||||
PAR2CMD: pathlib.Path | None = None
|
||||
SEVENZIP: pathlib.Path | None = None
|
||||
SHOWEXTRACT = 0
|
||||
|
||||
|
||||
def in_path(name: str) -> pathlib.Path | None:
|
||||
"""Find tool if its on the system loc."""
|
||||
|
@ -112,3 +125,196 @@ def find_unzip(root: pathlib.Path | None = None) -> pathlib.Path | None:
|
|||
log.debug(f'Failed to locate any of the following: {names}')
|
||||
log.warning('Transcoding of disk images and extraction zip files will not be possible!')
|
||||
return found
|
||||
|
||||
|
||||
def configure_utility_locations():
|
||||
# Setup FFMPEG, FFPROBE and SEVENZIP locations
|
||||
global FFMPEG
|
||||
global FFPROBE
|
||||
global PAR2CMD
|
||||
global SEVENZIP
|
||||
FFMPEG = find_transcoder(FFMPEG_PATH)
|
||||
FFPROBE = find_video_corruption_detector(FFMPEG_PATH)
|
||||
PAR2CMD = find_archive_repairer()
|
||||
if platform.system() == 'Windows':
|
||||
path = nzb2media.APP_ROOT / f'nzb2media/extractor/bin/{platform.machine()}'
|
||||
else:
|
||||
path = None
|
||||
SEVENZIP = find_unzip(path)
|
||||
|
||||
|
||||
def extract(file_path, output_destination):
|
||||
success = 0
|
||||
# Using Windows
|
||||
if platform.system() == 'Windows':
|
||||
if not os.path.exists(nzb2media.tool.SEVENZIP):
|
||||
log.error('EXTRACTOR: Could not find 7-zip, Exiting')
|
||||
return False
|
||||
wscriptlocation = os.path.join(os.environ['WINDIR'], 'system32', 'wscript.exe')
|
||||
invislocation = os.path.join(nzb2media.APP_ROOT, 'nzb2media', 'extractor', 'bin', 'invisible.vbs')
|
||||
cmd_7zip = [wscriptlocation, invislocation, str(nzb2media.tool.SHOWEXTRACT), nzb2media.tool.SEVENZIP, 'x', '-y']
|
||||
ext_7zip = ['.rar', '.zip', '.tar.gz', 'tgz', '.tar.bz2', '.tbz', '.tar.lzma', '.tlz', '.7z', '.xz', '.gz']
|
||||
extract_commands = dict.fromkeys(ext_7zip, cmd_7zip)
|
||||
# Using unix
|
||||
else:
|
||||
required_cmds = ['unrar', 'unzip', 'tar', 'unxz', 'unlzma', '7zr', 'bunzip2', 'gunzip']
|
||||
# ## Possible future suport:
|
||||
# gunzip: gz (cmd will delete original archive)
|
||||
# ## the following do not extract to destination dir
|
||||
# '.xz': ['xz', '-d --keep'],
|
||||
# '.lzma': ['xz', '-d --format=lzma --keep'],
|
||||
# '.bz2': ['bzip2', '-d --keep']
|
||||
extract_commands = {'.rar': ['unrar', 'x', '-o+', '-y'], '.tar': ['tar', '-xf'], '.zip': ['unzip'], '.tar.gz': ['tar', '-xzf'], '.tgz': ['tar', '-xzf'], '.tar.bz2': ['tar', '-xjf'], '.tbz': ['tar', '-xjf'], '.tar.lzma': ['tar', '--lzma', '-xf'], '.tlz': ['tar', '--lzma', '-xf'], '.tar.xz': ['tar', '--xz', '-xf'], '.txz': ['tar', '--xz', '-xf'], '.7z': ['7zr', 'x'], '.gz': ['gunzip']}
|
||||
# Test command exists and if not, remove
|
||||
if not os.getenv('TR_TORRENT_DIR'):
|
||||
for cmd in required_cmds:
|
||||
if call(['which', cmd], stdout=DEVNULL, stderr=DEVNULL):
|
||||
# note, returns 0 if exists, or 1 if doesn't exist.
|
||||
for key, val in extract_commands.items():
|
||||
if cmd in val[0]:
|
||||
if not call(['which', '7zr'], stdout=DEVNULL, stderr=DEVNULL):
|
||||
# we do have '7zr'
|
||||
extract_commands[key] = ['7zr', 'x', '-y']
|
||||
elif not call(['which', '7z'], stdout=DEVNULL, stderr=DEVNULL):
|
||||
# we do have '7z'
|
||||
extract_commands[key] = ['7z', 'x', '-y']
|
||||
elif not call(['which', '7za'], stdout=DEVNULL, stderr=DEVNULL):
|
||||
# we do have '7za'
|
||||
extract_commands[key] = ['7za', 'x', '-y']
|
||||
else:
|
||||
log.error(f'EXTRACTOR: {cmd} not found, disabling support for {key}')
|
||||
del extract_commands[key]
|
||||
else:
|
||||
log.warning('EXTRACTOR: Cannot determine which tool to use when called from Transmission')
|
||||
if not extract_commands:
|
||||
log.warning('EXTRACTOR: No archive extracting programs found, plugin will be disabled')
|
||||
ext = os.path.splitext(file_path)
|
||||
cmd = []
|
||||
if ext[1] in {'.gz', '.bz2', '.lzma'}:
|
||||
# Check if this is a tar
|
||||
if os.path.splitext(ext[0])[1] == '.tar':
|
||||
cmd = extract_commands[f'.tar{ext[1]}']
|
||||
else: # Try gunzip
|
||||
cmd = extract_commands[ext[1]]
|
||||
elif ext[1] in {'.1', '.01', '.001'} and os.path.splitext(ext[0])[1] in {'.rar', '.zip', '.7z'}:
|
||||
cmd = extract_commands[os.path.splitext(ext[0])[1]]
|
||||
elif ext[1] in {'.cb7', '.cba', '.cbr', '.cbt', '.cbz'}:
|
||||
# don't extract these comic book archives.
|
||||
return False
|
||||
else:
|
||||
if ext[1] in extract_commands:
|
||||
cmd = extract_commands[ext[1]]
|
||||
else:
|
||||
log.debug(f'EXTRACTOR: Unknown file type: {ext[1]}')
|
||||
return False
|
||||
# Create outputDestination folder
|
||||
nzb2media.make_dir(output_destination)
|
||||
if nzb2media.PASSWORDS_FILE and os.path.isfile(os.path.normpath(nzb2media.PASSWORDS_FILE)):
|
||||
with open(os.path.normpath(nzb2media.PASSWORDS_FILE), encoding='utf-8') as fin:
|
||||
passwords = [line.strip() for line in fin]
|
||||
else:
|
||||
passwords = []
|
||||
log.info(f'Extracting {file_path} to {output_destination}')
|
||||
log.debug(f'Extracting {cmd} {file_path} {output_destination}')
|
||||
orig_files = []
|
||||
orig_dirs = []
|
||||
for directory, subdirs, files in os.walk(output_destination):
|
||||
for subdir in subdirs:
|
||||
orig_dirs.append(os.path.join(directory, subdir))
|
||||
for file in files:
|
||||
orig_files.append(os.path.join(directory, file))
|
||||
pwd = os.getcwd() # Get our Present Working Directory
|
||||
# Not all unpack commands accept full paths, so just extract into this directory
|
||||
os.chdir(output_destination)
|
||||
try: # now works same for nt and *nix
|
||||
info = None
|
||||
cmd.append(file_path) # add filePath to final cmd arg.
|
||||
if platform.system() == 'Windows':
|
||||
info = subprocess.STARTUPINFO()
|
||||
info.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
else:
|
||||
cmd = NICENESS + cmd
|
||||
cmd2 = cmd
|
||||
if 'gunzip' not in cmd: # gunzip doesn't support password
|
||||
cmd2.append('-p-') # don't prompt for password.
|
||||
with Popen(cmd2, stdout=DEVNULL, stderr=DEVNULL, startupinfo=info) as proc:
|
||||
res = proc.wait() # should extract files fine.
|
||||
if not res: # Both Linux and Windows return 0 for successful.
|
||||
log.info(f'EXTRACTOR: Extraction was successful for {file_path} to {output_destination}')
|
||||
success = 1
|
||||
elif len(passwords) > 0 and 'gunzip' not in cmd:
|
||||
log.info('EXTRACTOR: Attempting to extract with passwords')
|
||||
for password in passwords:
|
||||
if not password:
|
||||
continue # if edited in windows or otherwise if blank lines.
|
||||
cmd2 = cmd
|
||||
# append password here.
|
||||
passcmd = f'-p{password}'
|
||||
cmd2.append(passcmd)
|
||||
with Popen(cmd2, stdout=DEVNULL, stderr=DEVNULL, startupinfo=info) as proc:
|
||||
res = proc.wait() # should extract files fine.
|
||||
if not res or (res >= 0 and platform == 'Windows'):
|
||||
log.info(f'EXTRACTOR: Extraction was successful for {file_path} to {output_destination} using password: {password}')
|
||||
success = 1
|
||||
break
|
||||
except Exception:
|
||||
log.error(f'EXTRACTOR: Extraction failed for {file_path}. Could not call command {cmd}')
|
||||
os.chdir(pwd)
|
||||
return False
|
||||
os.chdir(pwd) # Go back to our Original Working Directory
|
||||
if success:
|
||||
# sleep to let files finish writing to disk
|
||||
sleep(3)
|
||||
perms = stat.S_IMODE(os.lstat(os.path.split(file_path)[0]).st_mode)
|
||||
for directory, subdirs, files in os.walk(output_destination):
|
||||
for subdir in subdirs:
|
||||
if not os.path.join(directory, subdir) in orig_files:
|
||||
try:
|
||||
os.chmod(os.path.join(directory, subdir), perms)
|
||||
except Exception:
|
||||
pass
|
||||
for file in files:
|
||||
if not os.path.join(directory, file) in orig_files:
|
||||
try:
|
||||
shutil.copymode(file_path, os.path.join(directory, file))
|
||||
except Exception:
|
||||
pass
|
||||
return True
|
||||
log.error(f'EXTRACTOR: Extraction failed for {file_path}. Result was {res}')
|
||||
return False
|
||||
|
||||
|
||||
def configure_niceness():
|
||||
global NICENESS
|
||||
try:
|
||||
with subprocess.Popen(['nice'], stdout=DEVNULL, stderr=DEVNULL) as proc:
|
||||
proc.communicate()
|
||||
niceness = nzb2media.CFG['Posix']['niceness']
|
||||
if len(niceness.split(',')) > 1: # Allow passing of absolute command, not just value.
|
||||
NICENESS.extend(niceness.split(','))
|
||||
else:
|
||||
NICENESS.extend(['nice', f'-n{int(niceness)}'])
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
with subprocess.Popen(['ionice'], stdout=DEVNULL, stderr=DEVNULL) as proc:
|
||||
proc.communicate()
|
||||
try:
|
||||
ionice = nzb2media.CFG['Posix']['ionice_class']
|
||||
NICENESS.extend(['ionice', f'-c{int(ionice)}'])
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
if 'ionice' in NICENESS:
|
||||
ionice = nzb2media.CFG['Posix']['ionice_classdata']
|
||||
NICENESS.extend([f'-n{int(ionice)}'])
|
||||
else:
|
||||
NICENESS.extend(['ionice', f'-n{int(ionice)}'])
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
NICENESS: list[str] = []
|
||||
FFMPEG_PATH: pathlib.Path | None = None
|
||||
|
|
171
nzb2media/torrent.py
Normal file
171
nzb2media/torrent.py
Normal file
|
@ -0,0 +1,171 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import time
|
||||
|
||||
import nzb2media
|
||||
import nzb2media.deluge
|
||||
import nzb2media.qbittorrent
|
||||
import nzb2media.synology
|
||||
import nzb2media.transmission
|
||||
import nzb2media.utorrent
|
||||
from nzb2media.deluge import configure_deluge
|
||||
from nzb2media.qbittorrent import configure_qbittorrent
|
||||
from nzb2media.synology import configure_syno
|
||||
from nzb2media.transmission import configure_transmission
|
||||
from nzb2media.utorrent import configure_utorrent
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
||||
CLIENTS = ['transmission', 'deluge', 'utorrent', 'rtorrent', 'qbittorrent', 'other', 'manual']
|
||||
CLIENT_AGENT = None
|
||||
CLASS = None
|
||||
CHMOD_DIRECTORY = None
|
||||
DEFAULT_DIRECTORY = None
|
||||
RESUME = None
|
||||
RESUME_ON_FAILURE = None
|
||||
|
||||
torrent_clients = {
|
||||
'deluge': nzb2media.deluge,
|
||||
'qbittorrent': nzb2media.qbittorrent,
|
||||
'transmission': nzb2media.transmission,
|
||||
'utorrent': nzb2media.utorrent,
|
||||
'synods': nzb2media.synology,
|
||||
}
|
||||
|
||||
|
||||
def configure_torrents(config):
|
||||
global CLIENT_AGENT
|
||||
global DEFAULT_DIRECTORY
|
||||
|
||||
torrent_config = config['Torrent']
|
||||
CLIENT_AGENT = torrent_config['clientAgent'] # utorrent | deluge | transmission | rtorrent | vuze | qbittorrent | synods | other
|
||||
nzb2media.OUTPUT_DIRECTORY = torrent_config['outputDirectory'] # /abs/path/to/complete/
|
||||
DEFAULT_DIRECTORY = torrent_config['default_downloadDirectory']
|
||||
nzb2media.TORRENT_NO_MANUAL = int(torrent_config['no_manual'], 0)
|
||||
configure_torrent_linking(torrent_config)
|
||||
configure_flattening(torrent_config)
|
||||
configure_torrent_deletion(torrent_config)
|
||||
configure_torrent_categories(torrent_config)
|
||||
configure_torrent_permissions(torrent_config)
|
||||
configure_torrent_resuming(torrent_config)
|
||||
configure_utorrent(torrent_config)
|
||||
configure_transmission(torrent_config)
|
||||
configure_deluge(torrent_config)
|
||||
configure_qbittorrent(torrent_config)
|
||||
configure_syno(torrent_config)
|
||||
|
||||
|
||||
def configure_torrent_linking(config):
|
||||
nzb2media.USE_LINK = config['useLink'] # no | hard | sym
|
||||
|
||||
|
||||
def configure_flattening(config):
|
||||
global NO_FLATTEN
|
||||
NO_FLATTEN = config['noFlatten']
|
||||
if isinstance(NO_FLATTEN, str):
|
||||
NO_FLATTEN = NO_FLATTEN.split(',')
|
||||
|
||||
|
||||
def configure_torrent_categories(config):
|
||||
nzb2media.CATEGORIES = config['categories'] # music,music_videos,pictures,software
|
||||
if isinstance(nzb2media.CATEGORIES, str):
|
||||
nzb2media.CATEGORIES = nzb2media.CATEGORIES.split(',')
|
||||
|
||||
|
||||
def configure_torrent_resuming(config):
|
||||
global RESUME_ON_FAILURE
|
||||
global RESUME
|
||||
RESUME_ON_FAILURE = int(config['resumeOnFailure'])
|
||||
RESUME = int(config['resume'])
|
||||
|
||||
|
||||
def configure_torrent_permissions(config):
|
||||
global CHMOD_DIRECTORY
|
||||
CHMOD_DIRECTORY = int(str(config['chmodDirectory']), 8)
|
||||
|
||||
|
||||
def configure_torrent_deletion(config):
|
||||
nzb2media.DELETE_ORIGINAL = int(config['deleteOriginal'])
|
||||
|
||||
|
||||
def configure_torrent_class():
|
||||
# create torrent class
|
||||
global CLASS
|
||||
CLASS = create_torrent_class(CLIENT_AGENT)
|
||||
|
||||
|
||||
def create_torrent_class(client_agent) -> object | None:
|
||||
if nzb2media.APP_NAME != 'TorrentToMedia.py':
|
||||
return None # Skip loading Torrent for NZBs.
|
||||
try:
|
||||
agent = torrent_clients[client_agent]
|
||||
except KeyError:
|
||||
return None
|
||||
else:
|
||||
nzb2media.deluge.configure_client()
|
||||
return agent.configure_client()
|
||||
|
||||
|
||||
def pause_torrent(client_agent, input_hash, input_id, input_name):
|
||||
log.debug(f'Stopping torrent {input_name} in {client_agent} while processing')
|
||||
try:
|
||||
if client_agent == 'utorrent' and CLASS:
|
||||
CLASS.stop(input_hash)
|
||||
if client_agent == 'transmission' and CLASS:
|
||||
CLASS.stop_torrent(input_id)
|
||||
if client_agent == 'synods' and CLASS:
|
||||
CLASS.pause_task(input_id)
|
||||
if client_agent == 'deluge' and CLASS:
|
||||
CLASS.core.pause_torrent([input_id])
|
||||
if client_agent == 'qbittorrent' and CLASS:
|
||||
CLASS.pause(input_hash)
|
||||
time.sleep(5)
|
||||
except Exception:
|
||||
log.warning(f'Failed to stop torrent {input_name} in {client_agent}')
|
||||
|
||||
|
||||
def resume_torrent(client_agent, input_hash, input_id, input_name):
|
||||
if RESUME != 1:
|
||||
return
|
||||
log.debug(f'Starting torrent {input_name} in {client_agent}')
|
||||
try:
|
||||
if client_agent == 'utorrent' and CLASS:
|
||||
CLASS.start(input_hash)
|
||||
if client_agent == 'transmission' and CLASS:
|
||||
CLASS.start_torrent(input_id)
|
||||
if client_agent == 'synods' and CLASS:
|
||||
CLASS.resume_task(input_id)
|
||||
if client_agent == 'deluge' and CLASS:
|
||||
CLASS.core.resume_torrent([input_id])
|
||||
if client_agent == 'qbittorrent' and CLASS:
|
||||
CLASS.resume(input_hash)
|
||||
time.sleep(5)
|
||||
except Exception:
|
||||
log.warning(f'Failed to start torrent {input_name} in {client_agent}')
|
||||
|
||||
|
||||
def remove_torrent(client_agent, input_hash, input_id, input_name):
|
||||
if nzb2media.DELETE_ORIGINAL == 1 or nzb2media.USE_LINK == 'move':
|
||||
log.debug(f'Deleting torrent {input_name} from {client_agent}')
|
||||
try:
|
||||
if client_agent == 'utorrent' and CLASS:
|
||||
CLASS.removedata(input_hash)
|
||||
CLASS.remove(input_hash)
|
||||
if client_agent == 'transmission' and CLASS:
|
||||
CLASS.remove_torrent(input_id, True)
|
||||
if client_agent == 'synods' and CLASS:
|
||||
CLASS.delete_task(input_id)
|
||||
if client_agent == 'deluge' and CLASS:
|
||||
CLASS.core.remove_torrent(input_id, True)
|
||||
if client_agent == 'qbittorrent' and CLASS:
|
||||
CLASS.delete_permanently(input_hash)
|
||||
time.sleep(5)
|
||||
except Exception:
|
||||
log.warning(f'Failed to delete torrent {input_name} in {client_agent}')
|
||||
else:
|
||||
resume_torrent(client_agent, input_hash, input_id, input_name)
|
||||
|
||||
|
||||
NO_FLATTEN: list[str] = []
|
|
@ -1,91 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import nzb2media
|
||||
from nzb2media.utils.torrent import create_torrent_class
|
||||
|
||||
|
||||
def configure_torrents(config):
|
||||
torrent_config = config['Torrent']
|
||||
nzb2media.TORRENT_CLIENT_AGENT = torrent_config['clientAgent'] # utorrent | deluge | transmission | rtorrent | vuze | qbittorrent | synods | other
|
||||
nzb2media.OUTPUT_DIRECTORY = torrent_config['outputDirectory'] # /abs/path/to/complete/
|
||||
nzb2media.TORRENT_DEFAULT_DIRECTORY = torrent_config['default_downloadDirectory']
|
||||
nzb2media.TORRENT_NO_MANUAL = int(torrent_config['no_manual'], 0)
|
||||
configure_torrent_linking(torrent_config)
|
||||
configure_flattening(torrent_config)
|
||||
configure_torrent_deletion(torrent_config)
|
||||
configure_torrent_categories(torrent_config)
|
||||
configure_torrent_permissions(torrent_config)
|
||||
configure_torrent_resuming(torrent_config)
|
||||
configure_utorrent(torrent_config)
|
||||
configure_transmission(torrent_config)
|
||||
configure_deluge(torrent_config)
|
||||
configure_qbittorrent(torrent_config)
|
||||
configure_syno(torrent_config)
|
||||
|
||||
|
||||
def configure_torrent_linking(config):
|
||||
nzb2media.USE_LINK = config['useLink'] # no | hard | sym
|
||||
|
||||
|
||||
def configure_flattening(config):
|
||||
nzb2media.NOFLATTEN = config['noFlatten']
|
||||
if isinstance(nzb2media.NOFLATTEN, str):
|
||||
nzb2media.NOFLATTEN = nzb2media.NOFLATTEN.split(',')
|
||||
|
||||
|
||||
def configure_torrent_categories(config):
|
||||
nzb2media.CATEGORIES = config['categories'] # music,music_videos,pictures,software
|
||||
if isinstance(nzb2media.CATEGORIES, str):
|
||||
nzb2media.CATEGORIES = nzb2media.CATEGORIES.split(',')
|
||||
|
||||
|
||||
def configure_torrent_resuming(config):
|
||||
nzb2media.TORRENT_RESUME_ON_FAILURE = int(config['resumeOnFailure'])
|
||||
nzb2media.TORRENT_RESUME = int(config['resume'])
|
||||
|
||||
|
||||
def configure_torrent_permissions(config):
|
||||
nzb2media.TORRENT_CHMOD_DIRECTORY = int(str(config['chmodDirectory']), 8)
|
||||
|
||||
|
||||
def configure_torrent_deletion(config):
|
||||
nzb2media.DELETE_ORIGINAL = int(config['deleteOriginal'])
|
||||
|
||||
|
||||
def configure_utorrent(config):
|
||||
nzb2media.UTORRENT_WEB_UI = config['uTorrentWEBui'] # http://localhost:8090/gui/
|
||||
nzb2media.UTORRENT_USER = config['uTorrentUSR'] # mysecretusr
|
||||
nzb2media.UTORRENT_PASSWORD = config['uTorrentPWD'] # mysecretpwr
|
||||
|
||||
|
||||
def configure_transmission(config):
|
||||
nzb2media.TRANSMISSION_HOST = config['TransmissionHost'] # localhost
|
||||
nzb2media.TRANSMISSION_PORT = int(config['TransmissionPort'])
|
||||
nzb2media.TRANSMISSION_USER = config['TransmissionUSR'] # mysecretusr
|
||||
nzb2media.TRANSMISSION_PASSWORD = config['TransmissionPWD'] # mysecretpwr
|
||||
|
||||
|
||||
def configure_syno(config):
|
||||
nzb2media.SYNO_HOST = config['synoHost'] # localhost
|
||||
nzb2media.SYNO_PORT = int(config['synoPort'])
|
||||
nzb2media.SYNO_USER = config['synoUSR'] # mysecretusr
|
||||
nzb2media.SYNO_PASSWORD = config['synoPWD'] # mysecretpwr
|
||||
|
||||
|
||||
def configure_deluge(config):
|
||||
nzb2media.DELUGE_HOST = config['DelugeHost'] # localhost
|
||||
nzb2media.DELUGE_PORT = int(config['DelugePort']) # 8084
|
||||
nzb2media.DELUGE_USER = config['DelugeUSR'] # mysecretusr
|
||||
nzb2media.DELUGE_PASSWORD = config['DelugePWD'] # mysecretpwr
|
||||
|
||||
|
||||
def configure_qbittorrent(config):
|
||||
nzb2media.QBITTORRENT_HOST = config['qBittorrentHost'] # localhost
|
||||
nzb2media.QBITTORRENT_PORT = int(config['qBittorrentPort']) # 8080
|
||||
nzb2media.QBITTORRENT_USER = config['qBittorrentUSR'] # mysecretusr
|
||||
nzb2media.QBITTORRENT_PASSWORD = config['qBittorrentPWD'] # mysecretpwr
|
||||
|
||||
|
||||
def configure_torrent_class():
|
||||
# create torrent class
|
||||
nzb2media.TORRENT_CLASS = create_torrent_class(nzb2media.TORRENT_CLIENT_AGENT)
|
|
@ -1,26 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from deluge_client import DelugeRPCClient
|
||||
|
||||
import nzb2media
|
||||
|
||||
log = logging.getLogger()
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
def configure_client():
|
||||
agent = 'deluge'
|
||||
host = nzb2media.DELUGE_HOST
|
||||
port = nzb2media.DELUGE_PORT
|
||||
user = nzb2media.DELUGE_USER
|
||||
password = nzb2media.DELUGE_PASSWORD
|
||||
log.debug(f'Connecting to {agent}: http://{host}:{port}')
|
||||
client = DelugeRPCClient(host, port, user, password)
|
||||
try:
|
||||
client.connect()
|
||||
except Exception:
|
||||
log.error('Failed to connect to Deluge')
|
||||
else:
|
||||
return client
|
|
@ -1,24 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from syno.downloadstation import DownloadStation
|
||||
|
||||
import nzb2media
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def configure_client():
|
||||
agent = 'synology'
|
||||
host = nzb2media.SYNO_HOST
|
||||
port = nzb2media.SYNO_PORT
|
||||
user = nzb2media.SYNO_USER
|
||||
password = nzb2media.SYNO_PASSWORD
|
||||
log.debug(f'Connecting to {agent}: http://{host}:{port}')
|
||||
try:
|
||||
client = DownloadStation(host, port, user, password)
|
||||
except Exception:
|
||||
log.error('Failed to connect to synology')
|
||||
else:
|
||||
return client
|
|
@ -1,25 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from transmissionrpc.client import Client as TransmissionClient
|
||||
|
||||
import nzb2media
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
def configure_client():
|
||||
agent = 'transmission'
|
||||
host = nzb2media.TRANSMISSION_HOST
|
||||
port = nzb2media.TRANSMISSION_PORT
|
||||
user = nzb2media.TRANSMISSION_USER
|
||||
password = nzb2media.TRANSMISSION_PASSWORD
|
||||
log.debug(f'Connecting to {agent}: http://{host}:{port}')
|
||||
try:
|
||||
client = TransmissionClient(host, port, user, password)
|
||||
except Exception:
|
||||
log.error('Failed to connect to Transmission')
|
||||
else:
|
||||
return client
|
|
@ -1,3 +1,4 @@
|
|||
# pylint: disable=too-many-lines
|
||||
from __future__ import annotations
|
||||
|
||||
import errno
|
||||
|
@ -17,17 +18,64 @@ from subprocess import PIPE
|
|||
from babelfish import Language
|
||||
|
||||
import nzb2media
|
||||
import nzb2media.tool
|
||||
from nzb2media.utils.files import list_media_files
|
||||
from nzb2media.utils.paths import make_dir
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
__author__ = 'Justin'
|
||||
|
||||
MOUNTED = None
|
||||
GETSUBS = False
|
||||
TRANSCODE = None
|
||||
CONCAT = None
|
||||
DUPLICATE = None
|
||||
IGNOREEXTENSIONS = []
|
||||
VEXTENSION = None
|
||||
OUTPUTVIDEOPATH = None
|
||||
PROCESSOUTPUT = False
|
||||
GENERALOPTS: list[str] = []
|
||||
OTHEROPTS: list[str] = []
|
||||
ALANGUAGE = None
|
||||
AINCLUDE = False
|
||||
SLANGUAGES: list[str] = []
|
||||
SINCLUDE = False
|
||||
SUBSDIR = None
|
||||
ALLOWSUBS = False
|
||||
SEXTRACT = False
|
||||
SEMBED = False
|
||||
BURN = False
|
||||
DEFAULTS = None
|
||||
VCODEC = None
|
||||
VCODEC_ALLOW = []
|
||||
VPRESET = None
|
||||
VFRAMERATE = None
|
||||
VBITRATE = None
|
||||
VLEVEL = None
|
||||
VCRF = None
|
||||
VRESOLUTION = None
|
||||
ACODEC = None
|
||||
ACODEC_ALLOW = []
|
||||
ACHANNELS = None
|
||||
ABITRATE = None
|
||||
ACODEC2 = None
|
||||
ACODEC2_ALLOW: list[str] = []
|
||||
ACHANNELS2 = None
|
||||
ABITRATE2 = None
|
||||
ACODEC3 = None
|
||||
ACODEC3_ALLOW = []
|
||||
ACHANNELS3 = None
|
||||
ABITRATE3 = None
|
||||
SCODEC = None
|
||||
OUTPUTFASTSTART = None
|
||||
OUTPUTQUALITYPERCENT = None
|
||||
HWACCEL = False
|
||||
|
||||
|
||||
def is_video_good(video: pathlib.Path, status, require_lan=None):
|
||||
file_ext = video.suffix
|
||||
disable = False
|
||||
if file_ext not in nzb2media.MEDIA_CONTAINER or not nzb2media.FFPROBE or not nzb2media.CHECK_MEDIA or file_ext in {'.iso'} or (status > 0 and nzb2media.NOEXTRACTFAILED):
|
||||
if file_ext not in nzb2media.MEDIA_CONTAINER or not nzb2media.tool.FFPROBE or not nzb2media.CHECK_MEDIA or file_ext in {'.iso'} or (status > 0 and nzb2media.NOEXTRACTFAILED):
|
||||
disable = True
|
||||
else:
|
||||
test_details, res = get_video_details(nzb2media.TEST_FILE)
|
||||
|
@ -73,7 +121,7 @@ def zip_out(file, img):
|
|||
if os.path.isfile(file):
|
||||
cmd = ['cat', file]
|
||||
else:
|
||||
cmd = [os.fspath(nzb2media.SEVENZIP), '-so', 'e', img, file]
|
||||
cmd = [os.fspath(nzb2media.tool.SEVENZIP), '-so', 'e', img, file]
|
||||
try:
|
||||
with subprocess.Popen(cmd, stdout=PIPE, stderr=DEVNULL) as proc:
|
||||
return proc
|
||||
|
@ -86,13 +134,13 @@ def get_video_details(videofile, img=None):
|
|||
video_details = {}
|
||||
result = 1
|
||||
file = videofile
|
||||
if not nzb2media.FFPROBE:
|
||||
if not nzb2media.tool.FFPROBE:
|
||||
return video_details, result
|
||||
print_format = '-of' if 'avprobe' in nzb2media.FFPROBE.name else '-print_format'
|
||||
print_format = '-of' if 'avprobe' in nzb2media.tool.FFPROBE.name else '-print_format'
|
||||
try:
|
||||
if img:
|
||||
videofile = '-'
|
||||
command = [os.fspath(nzb2media.FFPROBE), '-v', 'quiet', print_format, 'json', '-show_format', '-show_streams', '-show_error', videofile]
|
||||
command = [os.fspath(nzb2media.tool.FFPROBE), '-v', 'quiet', print_format, 'json', '-show_format', '-show_streams', '-show_error', videofile]
|
||||
print_cmd(command)
|
||||
if img:
|
||||
procin = zip_out(file, img)
|
||||
|
@ -107,7 +155,7 @@ def get_video_details(videofile, img=None):
|
|||
video_details = json.loads(proc_out.decode())
|
||||
except Exception:
|
||||
try: # try this again without -show error in case of ffmpeg limitation
|
||||
command = [os.fspath(nzb2media.FFPROBE), '-v', 'quiet', print_format, 'json', '-show_format', '-show_streams', videofile]
|
||||
command = [os.fspath(nzb2media.tool.FFPROBE), '-v', 'quiet', print_format, 'json', '-show_format', '-show_streams', videofile]
|
||||
print_cmd(command)
|
||||
if img:
|
||||
procin = zip_out(file, img)
|
||||
|
@ -140,6 +188,7 @@ def check_vid_file(video_details, result):
|
|||
|
||||
|
||||
def build_commands(file, new_dir, movie_name):
|
||||
global VEXTENSION
|
||||
if isinstance(file, str):
|
||||
input_file = file
|
||||
if 'concat:' in file:
|
||||
|
@ -148,14 +197,14 @@ def build_commands(file, new_dir, movie_name):
|
|||
directory, name = os.path.split(file)
|
||||
name, ext = os.path.splitext(name)
|
||||
check = re.match('VTS_([0-9][0-9])_[0-9]+', name)
|
||||
if check and nzb2media.CONCAT:
|
||||
if check and CONCAT:
|
||||
name = movie_name
|
||||
elif check:
|
||||
name = f'{movie_name}.cd{check.groups()[0]}'
|
||||
elif nzb2media.CONCAT and re.match('(.+)[cC][dD][0-9]', name):
|
||||
elif CONCAT and re.match('(.+)[cC][dD][0-9]', name):
|
||||
name = re.sub('([ ._=:-]+[cC][dD][0-9])', '', name)
|
||||
if ext == nzb2media.VEXTENSION and new_dir == directory: # we need to change the name to prevent overwriting itself.
|
||||
nzb2media.VEXTENSION = f'-transcoded{nzb2media.VEXTENSION}' # adds '-transcoded.ext'
|
||||
if ext == VEXTENSION and new_dir == directory: # we need to change the name to prevent overwriting itself.
|
||||
VEXTENSION = f'-transcoded{VEXTENSION}' # adds '-transcoded.ext'
|
||||
new_file = file
|
||||
else:
|
||||
img, data = next(file.items())
|
||||
|
@ -172,7 +221,7 @@ def build_commands(file, new_dir, movie_name):
|
|||
video_details, result = get_video_details(data['files'][0], img)
|
||||
input_file = '-'
|
||||
file = '-'
|
||||
newfile_path = os.path.normpath(os.path.join(new_dir, name) + nzb2media.VEXTENSION)
|
||||
newfile_path = os.path.normpath(os.path.join(new_dir, name) + VEXTENSION)
|
||||
map_cmd = []
|
||||
video_cmd = []
|
||||
audio_cmd = []
|
||||
|
@ -186,63 +235,63 @@ def build_commands(file, new_dir, movie_name):
|
|||
audio_streams = []
|
||||
sub_streams = []
|
||||
map_cmd.extend(['-map', '0'])
|
||||
if nzb2media.VCODEC:
|
||||
video_cmd.extend(['-c:v', nzb2media.VCODEC])
|
||||
if nzb2media.VCODEC == 'libx264' and nzb2media.VPRESET:
|
||||
video_cmd.extend(['-pre', nzb2media.VPRESET])
|
||||
if VCODEC:
|
||||
video_cmd.extend(['-c:v', VCODEC])
|
||||
if VCODEC == 'libx264' and VPRESET:
|
||||
video_cmd.extend(['-pre', VPRESET])
|
||||
else:
|
||||
video_cmd.extend(['-c:v', 'copy'])
|
||||
if nzb2media.VFRAMERATE:
|
||||
video_cmd.extend(['-r', str(nzb2media.VFRAMERATE)])
|
||||
if nzb2media.VBITRATE:
|
||||
video_cmd.extend(['-b:v', str(nzb2media.VBITRATE)])
|
||||
if nzb2media.VRESOLUTION:
|
||||
video_cmd.extend(['-vf', f'scale={nzb2media.VRESOLUTION}'])
|
||||
if nzb2media.VPRESET:
|
||||
video_cmd.extend(['-preset', nzb2media.VPRESET])
|
||||
if nzb2media.VCRF:
|
||||
video_cmd.extend(['-crf', str(nzb2media.VCRF)])
|
||||
if nzb2media.VLEVEL:
|
||||
video_cmd.extend(['-level', str(nzb2media.VLEVEL)])
|
||||
if nzb2media.ACODEC:
|
||||
audio_cmd.extend(['-c:a', nzb2media.ACODEC])
|
||||
if nzb2media.ACODEC in {'aac', 'dts'}:
|
||||
if VFRAMERATE:
|
||||
video_cmd.extend(['-r', str(VFRAMERATE)])
|
||||
if VBITRATE:
|
||||
video_cmd.extend(['-b:v', str(VBITRATE)])
|
||||
if VRESOLUTION:
|
||||
video_cmd.extend(['-vf', f'scale={VRESOLUTION}'])
|
||||
if VPRESET:
|
||||
video_cmd.extend(['-preset', VPRESET])
|
||||
if VCRF:
|
||||
video_cmd.extend(['-crf', str(VCRF)])
|
||||
if VLEVEL:
|
||||
video_cmd.extend(['-level', str(VLEVEL)])
|
||||
if ACODEC:
|
||||
audio_cmd.extend(['-c:a', ACODEC])
|
||||
if 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'])
|
||||
else:
|
||||
audio_cmd.extend(['-c:a', 'copy'])
|
||||
if nzb2media.ACHANNELS:
|
||||
audio_cmd.extend(['-ac', str(nzb2media.ACHANNELS)])
|
||||
if nzb2media.ABITRATE:
|
||||
audio_cmd.extend(['-b:a', str(nzb2media.ABITRATE)])
|
||||
if nzb2media.OUTPUTQUALITYPERCENT:
|
||||
audio_cmd.extend(['-q:a', str(nzb2media.OUTPUTQUALITYPERCENT)])
|
||||
if nzb2media.SCODEC and nzb2media.ALLOWSUBS:
|
||||
sub_cmd.extend(['-c:s', nzb2media.SCODEC])
|
||||
elif nzb2media.ALLOWSUBS: # Not every subtitle codec can be used for every video container format!
|
||||
if ACHANNELS:
|
||||
audio_cmd.extend(['-ac', str(ACHANNELS)])
|
||||
if ABITRATE:
|
||||
audio_cmd.extend(['-b:a', str(ABITRATE)])
|
||||
if OUTPUTQUALITYPERCENT:
|
||||
audio_cmd.extend(['-q:a', str(OUTPUTQUALITYPERCENT)])
|
||||
if SCODEC and ALLOWSUBS:
|
||||
sub_cmd.extend(['-c:s', SCODEC])
|
||||
elif ALLOWSUBS: # Not every subtitle codec can be used for every video container format!
|
||||
sub_cmd.extend(['-c:s', 'copy'])
|
||||
else: # http://en.wikibooks.org/wiki/FFMPEG_An_Intermediate_Guide/subtitle_options
|
||||
sub_cmd.extend(['-sn']) # Don't copy the subtitles over
|
||||
if nzb2media.OUTPUTFASTSTART:
|
||||
if OUTPUTFASTSTART:
|
||||
other_cmd.extend(['-movflags', '+faststart'])
|
||||
else:
|
||||
video_streams = [item for item in video_details['streams'] if item['codec_type'] == 'video']
|
||||
audio_streams = [item for item in video_details['streams'] if item['codec_type'] == 'audio']
|
||||
sub_streams = [item for item in video_details['streams'] if item['codec_type'] == 'subtitle']
|
||||
if nzb2media.VEXTENSION not in ['.mkv', '.mpegts']:
|
||||
if VEXTENSION not in ['.mkv', '.mpegts']:
|
||||
sub_streams = [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 video_streams:
|
||||
codec = video['codec_name']
|
||||
frame_rate = video.get('avg_frame_rate', 0)
|
||||
width = video.get('width', 0)
|
||||
height = video.get('height', 0)
|
||||
scale = nzb2media.VRESOLUTION
|
||||
if codec in nzb2media.VCODEC_ALLOW or not nzb2media.VCODEC:
|
||||
scale = VRESOLUTION
|
||||
if codec in VCODEC_ALLOW or not VCODEC:
|
||||
video_cmd.extend(['-c:v', 'copy'])
|
||||
else:
|
||||
video_cmd.extend(['-c:v', nzb2media.VCODEC])
|
||||
if nzb2media.VFRAMERATE and not nzb2media.VFRAMERATE * 0.999 <= frame_rate <= nzb2media.VFRAMERATE * 1.001:
|
||||
video_cmd.extend(['-r', str(nzb2media.VFRAMERATE)])
|
||||
video_cmd.extend(['-c:v', VCODEC])
|
||||
if VFRAMERATE and not VFRAMERATE * 0.999 <= frame_rate <= VFRAMERATE * 1.001:
|
||||
video_cmd.extend(['-r', str(VFRAMERATE)])
|
||||
if scale:
|
||||
w_scale = width / float(scale.split(':')[0])
|
||||
h_scale = height / float(scale.split(':')[1])
|
||||
|
@ -258,18 +307,18 @@ def build_commands(file, new_dir, movie_name):
|
|||
scale = f'{_width}:{_height}'
|
||||
if h_scale > 1:
|
||||
video_cmd.extend(['-vf', f'scale={scale}'])
|
||||
if nzb2media.VBITRATE:
|
||||
video_cmd.extend(['-b:v', str(nzb2media.VBITRATE)])
|
||||
if nzb2media.VPRESET:
|
||||
video_cmd.extend(['-preset', nzb2media.VPRESET])
|
||||
if nzb2media.VCRF:
|
||||
video_cmd.extend(['-crf', str(nzb2media.VCRF)])
|
||||
if nzb2media.VLEVEL:
|
||||
video_cmd.extend(['-level', str(nzb2media.VLEVEL)])
|
||||
if VBITRATE:
|
||||
video_cmd.extend(['-b:v', str(VBITRATE)])
|
||||
if VPRESET:
|
||||
video_cmd.extend(['-preset', VPRESET])
|
||||
if VCRF:
|
||||
video_cmd.extend(['-crf', str(VCRF)])
|
||||
if VLEVEL:
|
||||
video_cmd.extend(['-level', str(VLEVEL)])
|
||||
no_copy = ['-vf', '-r', '-crf', '-level', '-preset', '-b:v']
|
||||
if video_cmd[1] == 'copy' and any(i in video_cmd for i in no_copy):
|
||||
video_cmd[1] = nzb2media.VCODEC
|
||||
if nzb2media.VCODEC == 'copy': # force copy. therefore ignore all other video transcoding.
|
||||
video_cmd[1] = VCODEC
|
||||
if VCODEC == 'copy': # force copy. therefore ignore all other video transcoding.
|
||||
video_cmd = ['-c:v', 'copy']
|
||||
_index = video['index']
|
||||
map_cmd.extend(['-map', f'0:{_index}'])
|
||||
|
@ -287,19 +336,19 @@ def build_commands(file, new_dir, movie_name):
|
|||
except Exception:
|
||||
continue
|
||||
try:
|
||||
audio1 = [item for item in audio_streams if item['tags']['language'] == nzb2media.ALANGUAGE]
|
||||
audio1 = [item for item in audio_streams if item['tags']['language'] == ALANGUAGE]
|
||||
except Exception: # no language tags. Assume only 1 language.
|
||||
audio1 = audio_streams
|
||||
try:
|
||||
audio2 = [item for item in audio1 if item['codec_name'] in nzb2media.ACODEC_ALLOW]
|
||||
audio2 = [item for item in audio1 if item['codec_name'] in ACODEC_ALLOW]
|
||||
except Exception:
|
||||
audio2 = []
|
||||
try:
|
||||
audio3 = [item for item in audio_streams if item['tags']['language'] != nzb2media.ALANGUAGE]
|
||||
audio3 = [item for item in audio_streams if item['tags']['language'] != ALANGUAGE]
|
||||
except Exception:
|
||||
audio3 = []
|
||||
try:
|
||||
audio4 = [item for item in audio3 if item['codec_name'] in nzb2media.ACODEC_ALLOW]
|
||||
audio4 = [item for item in audio3 if item['codec_name'] in ACODEC_ALLOW]
|
||||
except Exception:
|
||||
audio4 = []
|
||||
if audio2: # right (or only) language and codec...
|
||||
|
@ -315,7 +364,7 @@ def build_commands(file, new_dir, movie_name):
|
|||
a_mapped.extend([audio1[0]['index']])
|
||||
bitrate = int(float(audio1[0].get('bit_rate', 0))) / 1000
|
||||
channels = int(float(audio1[0].get('channels', 0)))
|
||||
audio_cmd.extend([f'-c:a:{used_audio}', nzb2media.ACODEC if nzb2media.ACODEC else 'copy'])
|
||||
audio_cmd.extend([f'-c:a:{used_audio}', ACODEC if ACODEC else 'copy'])
|
||||
elif audio4:
|
||||
# wrong language, right codec.
|
||||
_index = audio4[0]['index']
|
||||
|
@ -331,29 +380,29 @@ def build_commands(file, new_dir, movie_name):
|
|||
a_mapped.extend([audio3[0]['index']])
|
||||
bitrate = int(float(audio3[0].get('bit_rate', 0))) / 1000
|
||||
channels = int(float(audio3[0].get('channels', 0)))
|
||||
audio_cmd.extend([f'-c:a:{used_audio}', nzb2media.ACODEC if nzb2media.ACODEC else 'copy'])
|
||||
if nzb2media.ACHANNELS and channels and channels > nzb2media.ACHANNELS:
|
||||
audio_cmd.extend([f'-ac:a:{used_audio}', str(nzb2media.ACHANNELS)])
|
||||
audio_cmd.extend([f'-c:a:{used_audio}', ACODEC if ACODEC else 'copy'])
|
||||
if ACHANNELS and channels and channels > ACHANNELS:
|
||||
audio_cmd.extend([f'-ac:a:{used_audio}', str(ACHANNELS)])
|
||||
if audio_cmd[1] == 'copy':
|
||||
audio_cmd[1] = nzb2media.ACODEC
|
||||
if nzb2media.ABITRATE and not nzb2media.ABITRATE * 0.9 < bitrate < nzb2media.ABITRATE * 1.1:
|
||||
audio_cmd.extend([f'-b:a:{used_audio}', str(nzb2media.ABITRATE)])
|
||||
audio_cmd[1] = ACODEC
|
||||
if ABITRATE and not ABITRATE * 0.9 < bitrate < ABITRATE * 1.1:
|
||||
audio_cmd.extend([f'-b:a:{used_audio}', str(ABITRATE)])
|
||||
if audio_cmd[1] == 'copy':
|
||||
audio_cmd[1] = nzb2media.ACODEC
|
||||
if nzb2media.OUTPUTQUALITYPERCENT:
|
||||
audio_cmd.extend([f'-q:a:{used_audio}', str(nzb2media.OUTPUTQUALITYPERCENT)])
|
||||
audio_cmd[1] = ACODEC
|
||||
if OUTPUTQUALITYPERCENT:
|
||||
audio_cmd.extend([f'-q:a:{used_audio}', str(OUTPUTQUALITYPERCENT)])
|
||||
if audio_cmd[1] == 'copy':
|
||||
audio_cmd[1] = nzb2media.ACODEC
|
||||
audio_cmd[1] = ACODEC
|
||||
if audio_cmd[1] in {'aac', 'dts'}:
|
||||
audio_cmd[2:2] = ['-strict', '-2']
|
||||
if nzb2media.ACODEC2_ALLOW:
|
||||
if ACODEC2_ALLOW:
|
||||
used_audio += 1
|
||||
try:
|
||||
audio5 = [item for item in audio1 if item['codec_name'] in nzb2media.ACODEC2_ALLOW]
|
||||
audio5 = [item for item in audio1 if item['codec_name'] in ACODEC2_ALLOW]
|
||||
except Exception:
|
||||
audio5 = []
|
||||
try:
|
||||
audio6 = [item for item in audio3 if item['codec_name'] in nzb2media.ACODEC2_ALLOW]
|
||||
audio6 = [item for item in audio3 if item['codec_name'] in ACODEC2_ALLOW]
|
||||
except Exception:
|
||||
audio6 = []
|
||||
if audio5: # right language and codec.
|
||||
|
@ -369,8 +418,8 @@ def build_commands(file, new_dir, movie_name):
|
|||
a_mapped.extend([audio1[0]['index']])
|
||||
bitrate = int(float(audio1[0].get('bit_rate', 0))) / 1000
|
||||
channels = int(float(audio1[0].get('channels', 0)))
|
||||
if nzb2media.ACODEC2:
|
||||
audio_cmd2.extend([f'-c:a:{used_audio}', nzb2media.ACODEC2])
|
||||
if ACODEC2:
|
||||
audio_cmd2.extend([f'-c:a:{used_audio}', ACODEC2])
|
||||
else:
|
||||
audio_cmd2.extend([f'-c:a:{used_audio}', 'copy'])
|
||||
elif audio6: # wrong language, right codec
|
||||
|
@ -387,22 +436,22 @@ def build_commands(file, new_dir, movie_name):
|
|||
a_mapped.extend([audio3[0]['index']])
|
||||
bitrate = int(float(audio3[0].get('bit_rate', 0))) / 1000
|
||||
channels = int(float(audio3[0].get('channels', 0)))
|
||||
if nzb2media.ACODEC2:
|
||||
audio_cmd2.extend([f'-c:a:{used_audio}', nzb2media.ACODEC2])
|
||||
if ACODEC2:
|
||||
audio_cmd2.extend([f'-c:a:{used_audio}', ACODEC2])
|
||||
else:
|
||||
audio_cmd2.extend([f'-c:a:{used_audio}', 'copy'])
|
||||
if nzb2media.ACHANNELS2 and channels and channels > nzb2media.ACHANNELS2:
|
||||
audio_cmd2.extend([f'-ac:a:{used_audio}', str(nzb2media.ACHANNELS2)])
|
||||
if ACHANNELS2 and channels and channels > ACHANNELS2:
|
||||
audio_cmd2.extend([f'-ac:a:{used_audio}', str(ACHANNELS2)])
|
||||
if audio_cmd2[1] == 'copy':
|
||||
audio_cmd2[1] = nzb2media.ACODEC2
|
||||
if nzb2media.ABITRATE2 and not nzb2media.ABITRATE2 * 0.9 < bitrate < nzb2media.ABITRATE2 * 1.1:
|
||||
audio_cmd2.extend([f'-b:a:{used_audio}', str(nzb2media.ABITRATE2)])
|
||||
audio_cmd2[1] = ACODEC2
|
||||
if ABITRATE2 and not ABITRATE2 * 0.9 < bitrate < ABITRATE2 * 1.1:
|
||||
audio_cmd2.extend([f'-b:a:{used_audio}', str(ABITRATE2)])
|
||||
if audio_cmd2[1] == 'copy':
|
||||
audio_cmd2[1] = nzb2media.ACODEC2
|
||||
if nzb2media.OUTPUTQUALITYPERCENT:
|
||||
audio_cmd2.extend([f'-q:a:{used_audio}', str(nzb2media.OUTPUTQUALITYPERCENT)])
|
||||
audio_cmd2[1] = ACODEC2
|
||||
if OUTPUTQUALITYPERCENT:
|
||||
audio_cmd2.extend([f'-q:a:{used_audio}', str(OUTPUTQUALITYPERCENT)])
|
||||
if audio_cmd2[1] == 'copy':
|
||||
audio_cmd2[1] = nzb2media.ACODEC2
|
||||
audio_cmd2[1] = ACODEC2
|
||||
if audio_cmd2[1] in {'aac', 'dts'}:
|
||||
audio_cmd2[2:2] = ['-strict', '-2']
|
||||
if a_mapped[1] == a_mapped[0] and audio_cmd2[1:] == audio_cmd[1:]:
|
||||
|
@ -410,7 +459,7 @@ def build_commands(file, new_dir, movie_name):
|
|||
del map_cmd[-2:]
|
||||
else:
|
||||
audio_cmd.extend(audio_cmd2)
|
||||
if nzb2media.AINCLUDE and nzb2media.ACODEC3:
|
||||
if AINCLUDE and ACODEC3:
|
||||
# add commentary tracks back here.
|
||||
audio_streams.extend(commentary)
|
||||
for audio in audio_streams:
|
||||
|
@ -422,42 +471,42 @@ def build_commands(file, new_dir, movie_name):
|
|||
audio_cmd3 = []
|
||||
bitrate = int(float(audio.get('bit_rate', 0))) / 1000
|
||||
channels = int(float(audio.get('channels', 0)))
|
||||
if audio['codec_name'] in nzb2media.ACODEC3_ALLOW:
|
||||
if audio['codec_name'] in ACODEC3_ALLOW:
|
||||
audio_cmd3.extend([f'-c:a:{used_audio}', 'copy'])
|
||||
elif nzb2media.ACODEC3:
|
||||
audio_cmd3.extend([f'-c:a:{used_audio}', nzb2media.ACODEC3])
|
||||
elif ACODEC3:
|
||||
audio_cmd3.extend([f'-c:a:{used_audio}', ACODEC3])
|
||||
else:
|
||||
audio_cmd3.extend([f'-c:a:{used_audio}', 'copy'])
|
||||
if nzb2media.ACHANNELS3 and channels and channels > nzb2media.ACHANNELS3:
|
||||
audio_cmd3.extend([f'-ac:a:{used_audio}', str(nzb2media.ACHANNELS3)])
|
||||
if ACHANNELS3 and channels and channels > ACHANNELS3:
|
||||
audio_cmd3.extend([f'-ac:a:{used_audio}', str(ACHANNELS3)])
|
||||
if audio_cmd3[1] == 'copy':
|
||||
audio_cmd3[1] = nzb2media.ACODEC3
|
||||
if nzb2media.ABITRATE3 and not nzb2media.ABITRATE3 * 0.9 < bitrate < nzb2media.ABITRATE3 * 1.1:
|
||||
audio_cmd3.extend([f'-b:a:{used_audio}', str(nzb2media.ABITRATE3)])
|
||||
audio_cmd3[1] = ACODEC3
|
||||
if ABITRATE3 and not ABITRATE3 * 0.9 < bitrate < ABITRATE3 * 1.1:
|
||||
audio_cmd3.extend([f'-b:a:{used_audio}', str(ABITRATE3)])
|
||||
if audio_cmd3[1] == 'copy':
|
||||
audio_cmd3[1] = nzb2media.ACODEC3
|
||||
if nzb2media.OUTPUTQUALITYPERCENT > 0:
|
||||
audio_cmd3.extend([f'-q:a:{used_audio}', str(nzb2media.OUTPUTQUALITYPERCENT)])
|
||||
audio_cmd3[1] = ACODEC3
|
||||
if OUTPUTQUALITYPERCENT > 0:
|
||||
audio_cmd3.extend([f'-q:a:{used_audio}', str(OUTPUTQUALITYPERCENT)])
|
||||
if audio_cmd3[1] == 'copy':
|
||||
audio_cmd3[1] = nzb2media.ACODEC3
|
||||
audio_cmd3[1] = ACODEC3
|
||||
if audio_cmd3[1] in {'aac', 'dts'}:
|
||||
audio_cmd3[2:2] = ['-strict', '-2']
|
||||
audio_cmd.extend(audio_cmd3)
|
||||
s_mapped = []
|
||||
burnt = 0
|
||||
num = 0
|
||||
for lan in nzb2media.SLANGUAGES:
|
||||
for lan in SLANGUAGES:
|
||||
try:
|
||||
subs1 = [item for item in sub_streams if item['tags']['language'] == lan]
|
||||
except Exception:
|
||||
subs1 = []
|
||||
if nzb2media.BURN and not subs1 and not burnt and os.path.isfile(file):
|
||||
if BURN and not subs1 and not burnt and os.path.isfile(file):
|
||||
for subfile in get_subs(file):
|
||||
if lan in os.path.split(subfile)[1]:
|
||||
video_cmd.extend(['-vf', f'subtitles={subfile}'])
|
||||
burnt = 1
|
||||
for sub in subs1:
|
||||
if nzb2media.BURN and not burnt and os.path.isfile(input_file):
|
||||
if BURN and not burnt and os.path.isfile(input_file):
|
||||
subloc = 0
|
||||
for index, sub_stream in enumerate(sub_streams):
|
||||
if sub_stream['index'] == sub['index']:
|
||||
|
@ -465,40 +514,40 @@ def build_commands(file, new_dir, movie_name):
|
|||
break
|
||||
video_cmd.extend(['-vf', f'subtitles={input_file}:si={subloc}'])
|
||||
burnt = 1
|
||||
if not nzb2media.ALLOWSUBS:
|
||||
if not ALLOWSUBS:
|
||||
break
|
||||
if sub['codec_name'] in {'dvd_subtitle', 'VobSub'} and nzb2media.SCODEC == 'mov_text':
|
||||
if sub['codec_name'] in {'dvd_subtitle', 'VobSub'} and SCODEC == 'mov_text':
|
||||
continue # We can't convert these.
|
||||
_index = sub['index']
|
||||
map_cmd.extend(['-map', f'0:{_index}'])
|
||||
s_mapped.extend([sub['index']])
|
||||
if nzb2media.SINCLUDE:
|
||||
if SINCLUDE:
|
||||
for sub in sub_streams:
|
||||
if not nzb2media.ALLOWSUBS:
|
||||
if not ALLOWSUBS:
|
||||
break
|
||||
if sub['index'] in s_mapped:
|
||||
continue
|
||||
if sub['codec_name'] in {'dvd_subtitle', 'VobSub'} and nzb2media.SCODEC == 'mov_text': # We can't convert these.
|
||||
if sub['codec_name'] in {'dvd_subtitle', 'VobSub'} and SCODEC == 'mov_text': # We can't convert these.
|
||||
continue
|
||||
_index = sub['index']
|
||||
map_cmd.extend(['-map', f'0:{_index}'])
|
||||
s_mapped.extend([sub['index']])
|
||||
if nzb2media.OUTPUTFASTSTART:
|
||||
if OUTPUTFASTSTART:
|
||||
other_cmd.extend(['-movflags', '+faststart'])
|
||||
if nzb2media.OTHEROPTS:
|
||||
other_cmd.extend(nzb2media.OTHEROPTS)
|
||||
command = [nzb2media.FFMPEG, '-loglevel', 'warning']
|
||||
if nzb2media.HWACCEL:
|
||||
if OTHEROPTS:
|
||||
other_cmd.extend(OTHEROPTS)
|
||||
command = [nzb2media.tool.FFMPEG, '-loglevel', 'warning']
|
||||
if HWACCEL:
|
||||
command.extend(['-hwaccel', 'auto'])
|
||||
if nzb2media.GENERALOPTS:
|
||||
command.extend(nzb2media.GENERALOPTS)
|
||||
if GENERALOPTS:
|
||||
command.extend(GENERALOPTS)
|
||||
command.extend(['-i', input_file])
|
||||
if nzb2media.SEMBED and os.path.isfile(file):
|
||||
if SEMBED and os.path.isfile(file):
|
||||
for subfile in get_subs(file):
|
||||
sub_details, result = get_video_details(subfile)
|
||||
if not sub_details or not sub_details.get('streams'):
|
||||
continue
|
||||
if nzb2media.SCODEC == 'mov_text':
|
||||
if SCODEC == 'mov_text':
|
||||
subcode = [stream['codec_name'] for stream in sub_details['streams']]
|
||||
if set(subcode).intersection(['dvd_subtitle', 'VobSub']):
|
||||
# We can't convert these.
|
||||
|
@ -517,10 +566,10 @@ def build_commands(file, new_dir, movie_name):
|
|||
meta_cmd.extend([f'-metadata:s:s:{len(s_mapped) + num}', f'language={metlan.alpha3}'])
|
||||
num += 1
|
||||
map_cmd.extend(['-map', f'{num}:0'])
|
||||
if not nzb2media.ALLOWSUBS or (not s_mapped and not num):
|
||||
if not ALLOWSUBS or (not s_mapped and not num):
|
||||
sub_cmd.extend(['-sn'])
|
||||
elif nzb2media.SCODEC:
|
||||
sub_cmd.extend(['-c:s', nzb2media.SCODEC])
|
||||
elif SCODEC:
|
||||
sub_cmd.extend(['-c:s', SCODEC])
|
||||
else:
|
||||
sub_cmd.extend(['-c:s', 'copy'])
|
||||
command.extend(map_cmd)
|
||||
|
@ -531,7 +580,7 @@ def build_commands(file, new_dir, movie_name):
|
|||
command.extend(other_cmd)
|
||||
command.append(newfile_path)
|
||||
if platform.system() != 'Windows':
|
||||
command = nzb2media.NICENESS + command
|
||||
command = nzb2media.tool.NICENESS + command
|
||||
return command, new_file
|
||||
|
||||
|
||||
|
@ -551,13 +600,13 @@ def extract_subs(file, newfile_path):
|
|||
video_details, result = get_video_details(file)
|
||||
if not video_details:
|
||||
return
|
||||
if nzb2media.SUBSDIR:
|
||||
subdir = nzb2media.SUBSDIR
|
||||
if SUBSDIR:
|
||||
subdir = SUBSDIR
|
||||
else:
|
||||
subdir = os.path.split(newfile_path)[0]
|
||||
name = os.path.splitext(os.path.split(newfile_path)[1])[0]
|
||||
try:
|
||||
sub_streams = [item for item in video_details['streams'] if item['codec_type'] == 'subtitle' and item['tags']['language'] in nzb2media.SLANGUAGES and item['codec_name'] != 'hdmv_pgs_subtitle' and item['codec_name'] != 'pgssub']
|
||||
sub_streams = [item for item in video_details['streams'] if item['codec_type'] == 'subtitle' and item['tags']['language'] in SLANGUAGES and item['codec_name'] != 'hdmv_pgs_subtitle' and item['codec_name'] != 'pgssub']
|
||||
except Exception:
|
||||
sub_streams = [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(sub_streams)
|
||||
|
@ -573,9 +622,9 @@ def extract_subs(file, newfile_path):
|
|||
output_file = os.path.join(subdir, f'{name}.{lan}.srt')
|
||||
if os.path.isfile(output_file):
|
||||
output_file = os.path.join(subdir, f'{name}.{lan}.{ea_num}.srt')
|
||||
command = [nzb2media.FFMPEG, '-loglevel', 'warning', '-i', file, '-vn', '-an', f'-codec:{idx}', 'srt', output_file]
|
||||
command = [nzb2media.tool.FFMPEG, '-loglevel', 'warning', '-i', file, '-vn', '-an', f'-codec:{idx}', 'srt', output_file]
|
||||
if platform.system() != 'Windows':
|
||||
command = nzb2media.NICENESS + command
|
||||
command = nzb2media.tool.NICENESS + command
|
||||
log.info(f'Extracting {lan} subtitle from: {file}')
|
||||
print_cmd(command)
|
||||
result = 1 # set result to failed in case call fails.
|
||||
|
@ -604,11 +653,11 @@ def process_list(iterable):
|
|||
success = True
|
||||
for item in iterable:
|
||||
ext = os.path.splitext(item)[1].lower()
|
||||
if ext in {'.iso', '.bin', '.img'} and ext not in nzb2media.IGNOREEXTENSIONS:
|
||||
if ext in {'.iso', '.bin', '.img'} and ext not in IGNOREEXTENSIONS:
|
||||
log.debug(f'Attempting to rip disk image: {item}')
|
||||
new_list.extend(rip_iso(item))
|
||||
rem_list.append(item)
|
||||
elif re.match('.+VTS_[0-9][0-9]_[0-9].[Vv][Oo][Bb]', item) and '.vob' not in nzb2media.IGNOREEXTENSIONS:
|
||||
elif re.match('.+VTS_[0-9][0-9]_[0-9].[Vv][Oo][Bb]', item) and '.vob' not in IGNOREEXTENSIONS:
|
||||
log.debug(f'Found VIDEO_TS image file: {item}')
|
||||
if not vts_path:
|
||||
try:
|
||||
|
@ -616,7 +665,7 @@ def process_list(iterable):
|
|||
except Exception:
|
||||
vts_path = os.path.split(item)[0]
|
||||
rem_list.append(item)
|
||||
elif re.match('.+BDMV[/\\]SOURCE[/\\][0-9]+[0-9].[Mm][Tt][Ss]', item) and '.mts' not in nzb2media.IGNOREEXTENSIONS:
|
||||
elif re.match('.+BDMV[/\\]SOURCE[/\\][0-9]+[0-9].[Mm][Tt][Ss]', item) and '.mts' not in IGNOREEXTENSIONS:
|
||||
log.debug(f'Found MTS image file: {item}')
|
||||
if not mts_path:
|
||||
try:
|
||||
|
@ -626,7 +675,7 @@ def process_list(iterable):
|
|||
rem_list.append(item)
|
||||
elif re.match('.+VIDEO_TS.', item) or re.match('.+VTS_[0-9][0-9]_[0-9].', item):
|
||||
rem_list.append(item)
|
||||
elif nzb2media.CONCAT and re.match('.+[cC][dD][0-9].', item):
|
||||
elif CONCAT and re.match('.+[cC][dD][0-9].', item):
|
||||
rem_list.append(item)
|
||||
combine.append(item)
|
||||
else:
|
||||
|
@ -654,6 +703,7 @@ def process_list(iterable):
|
|||
|
||||
|
||||
def mount_iso(item): # Currently only supports Linux Mount when permissions allow.
|
||||
global MOUNTED
|
||||
if platform.system() == 'Windows':
|
||||
log.error(f'No mounting options available under Windows for image file {item}')
|
||||
return []
|
||||
|
@ -663,18 +713,18 @@ def mount_iso(item): # Currently only supports Linux Mount when permissions all
|
|||
print_cmd(cmd)
|
||||
with subprocess.Popen(cmd, stdout=PIPE, stderr=DEVNULL) as proc:
|
||||
proc_out, proc_err = proc.communicate()
|
||||
nzb2media.MOUNTED = mount_point # Allows us to verify this has been done and then cleanup.
|
||||
MOUNTED = mount_point # Allows us to verify this has been done and then cleanup.
|
||||
for root, _dirs, files in os.walk(mount_point):
|
||||
for file in files:
|
||||
full_path = os.path.join(root, file)
|
||||
if re.match('.+VTS_[0-9][0-9]_[0-9].[Vv][Oo][Bb]', full_path) and '.vob' not in nzb2media.IGNOREEXTENSIONS:
|
||||
if re.match('.+VTS_[0-9][0-9]_[0-9].[Vv][Oo][Bb]', full_path) and '.vob' not in IGNOREEXTENSIONS:
|
||||
log.debug(f'Found VIDEO_TS image file: {full_path}')
|
||||
try:
|
||||
vts_path = re.match('(.+VIDEO_TS)', full_path).groups()[0]
|
||||
except Exception:
|
||||
vts_path = os.path.split(full_path)[0]
|
||||
return combine_vts(vts_path)
|
||||
if re.match('.+BDMV[/\\]STREAM[/\\][0-9]+[0-9].[Mm]', full_path) and '.mts' not in nzb2media.IGNOREEXTENSIONS:
|
||||
if re.match('.+BDMV[/\\]STREAM[/\\][0-9]+[0-9].[Mm]', full_path) and '.mts' not in IGNOREEXTENSIONS:
|
||||
log.debug(f'Found MTS image file: {full_path}')
|
||||
try:
|
||||
mts_path = re.match('(.+BDMV[/\\]STREAM)', full_path).groups()[0]
|
||||
|
@ -689,7 +739,7 @@ def rip_iso(item):
|
|||
new_files = []
|
||||
failure_dir = 'failure'
|
||||
# Mount the ISO in your OS and call combineVTS.
|
||||
if not nzb2media.SEVENZIP:
|
||||
if not nzb2media.tool.SEVENZIP:
|
||||
log.debug(f'No 7zip installed. Attempting to mount image file {item}')
|
||||
try:
|
||||
# Currently only works for Linux.
|
||||
|
@ -698,7 +748,7 @@ def rip_iso(item):
|
|||
log.error(f'Failed to mount and extract from image file {item}')
|
||||
new_files = [failure_dir]
|
||||
return new_files
|
||||
cmd = [nzb2media.SEVENZIP, 'l', item]
|
||||
cmd = [nzb2media.tool.SEVENZIP, 'l', item]
|
||||
try:
|
||||
log.debug(f'Attempting to extract .vob or .mts from image file {item}')
|
||||
print_cmd(cmd)
|
||||
|
@ -720,7 +770,7 @@ def rip_iso(item):
|
|||
break
|
||||
if not concat:
|
||||
break
|
||||
if nzb2media.CONCAT:
|
||||
if CONCAT:
|
||||
combined.extend(concat)
|
||||
continue
|
||||
name = f'{os.path.splitext(os.path.split(item)[1])[0]}.cd{title_set + 1}'
|
||||
|
@ -735,12 +785,12 @@ def rip_iso(item):
|
|||
concat = []
|
||||
title_set += 1
|
||||
concat.append(mts_name)
|
||||
if nzb2media.CONCAT:
|
||||
if CONCAT:
|
||||
combined.extend(concat)
|
||||
continue
|
||||
name = f'{os.path.splitext(os.path.split(item)[1])[0]}.cd{title_set}'
|
||||
new_files.append({item: {'name': name, 'files': concat}})
|
||||
if nzb2media.CONCAT and combined:
|
||||
if CONCAT and combined:
|
||||
name = os.path.splitext(os.path.split(item)[1])[0]
|
||||
new_files.append({item: {'name': name, 'files': combined}})
|
||||
if not new_files:
|
||||
|
@ -772,12 +822,12 @@ def combine_vts(vts_path):
|
|||
break
|
||||
if not concat:
|
||||
break
|
||||
if nzb2media.CONCAT:
|
||||
if CONCAT:
|
||||
combined.extend(concat)
|
||||
continue
|
||||
name = f'{name}.cd{title_set + 1}'
|
||||
new_files.append({vts_path: {'name': name, 'files': concat}})
|
||||
if nzb2media.CONCAT:
|
||||
if CONCAT:
|
||||
new_files.append({vts_path: {'name': name, 'files': combined}})
|
||||
return new_files
|
||||
|
||||
|
@ -799,13 +849,13 @@ def combine_mts(mts_path):
|
|||
for mts_name in mts_list: # need to sort all files [1 - 998].mts in order
|
||||
concat = []
|
||||
concat.append(os.path.join(mts_path, mts_name))
|
||||
if nzb2media.CONCAT:
|
||||
if CONCAT:
|
||||
combined.extend(concat)
|
||||
continue
|
||||
name = f'{name}.cd{num + 1}'
|
||||
new_files.append({mts_path: {'name': name, 'files': concat}})
|
||||
num += 1
|
||||
if nzb2media.CONCAT:
|
||||
if CONCAT:
|
||||
new_files.append({mts_path: {'name': name, 'files': combined}})
|
||||
return new_files
|
||||
|
||||
|
@ -833,12 +883,13 @@ def print_cmd(command):
|
|||
|
||||
|
||||
def transcode_directory(dir_name):
|
||||
if not nzb2media.FFMPEG:
|
||||
global MOUNTED
|
||||
if not nzb2media.tool.FFMPEG:
|
||||
return 1, dir_name
|
||||
log.info('Checking for files to be transcoded')
|
||||
final_result = 0 # initialize as successful
|
||||
if nzb2media.OUTPUTVIDEOPATH:
|
||||
new_dir = nzb2media.OUTPUTVIDEOPATH
|
||||
if OUTPUTVIDEOPATH:
|
||||
new_dir = OUTPUTVIDEOPATH
|
||||
make_dir(new_dir)
|
||||
name = os.path.splitext(os.path.split(dir_name)[1])[0]
|
||||
new_dir = os.path.join(new_dir, name)
|
||||
|
@ -846,17 +897,17 @@ def transcode_directory(dir_name):
|
|||
else:
|
||||
new_dir = dir_name
|
||||
movie_name = os.path.splitext(os.path.split(dir_name)[1])[0]
|
||||
file_list = nzb2media.list_media_files(dir_name, media=True, audio=False, meta=False, archives=False)
|
||||
file_list = list_media_files(dir_name, media=True, audio=False, meta=False, archives=False)
|
||||
file_list, rem_list, new_list, success = process_list(file_list)
|
||||
if not success:
|
||||
return 1, dir_name
|
||||
for file in file_list:
|
||||
if isinstance(file, str) and os.path.splitext(file)[1] in nzb2media.IGNOREEXTENSIONS:
|
||||
if isinstance(file, str) and os.path.splitext(file)[1] in IGNOREEXTENSIONS:
|
||||
continue
|
||||
command, file = build_commands(file, new_dir, movie_name)
|
||||
newfile_path = command[-1]
|
||||
# transcoding files may remove the original file, so make sure to extract subtitles first
|
||||
if nzb2media.SEXTRACT and isinstance(file, str):
|
||||
if SEXTRACT and isinstance(file, str):
|
||||
extract_subs(file, newfile_path)
|
||||
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(newfile_path)
|
||||
|
@ -887,12 +938,12 @@ def transcode_directory(dir_name):
|
|||
result = proc.returncode
|
||||
except Exception:
|
||||
log.error(f'Transcoding of video {newfile_path} has failed')
|
||||
if nzb2media.SUBSDIR and not result and isinstance(file, str):
|
||||
if SUBSDIR and not result and isinstance(file, str):
|
||||
for sub in get_subs(file):
|
||||
name = os.path.splitext(os.path.split(file)[1])[0]
|
||||
subname = os.path.split(sub)[1]
|
||||
newname = os.path.splitext(os.path.split(newfile_path)[1])[0]
|
||||
newpath = os.path.join(nzb2media.SUBSDIR, subname.replace(name, newname))
|
||||
newpath = os.path.join(SUBSDIR, subname.replace(name, newname))
|
||||
if not os.path.isfile(newpath):
|
||||
os.rename(sub, newpath)
|
||||
if not result:
|
||||
|
@ -901,7 +952,7 @@ def transcode_directory(dir_name):
|
|||
except Exception:
|
||||
pass
|
||||
log.info(f'Transcoding of video to {newfile_path} succeeded')
|
||||
if os.path.isfile(newfile_path) and (file in new_list or not nzb2media.DUPLICATE):
|
||||
if os.path.isfile(newfile_path) and (file in new_list or not DUPLICATE):
|
||||
try:
|
||||
os.unlink(file)
|
||||
except Exception:
|
||||
|
@ -910,16 +961,16 @@ def transcode_directory(dir_name):
|
|||
log.error(f'Transcoding of video to {newfile_path} failed with result {result}')
|
||||
# this will be 0 (successful) it all are successful, else will return a positive integer for failure.
|
||||
final_result = final_result + result
|
||||
if nzb2media.MOUNTED: # In case we mounted an .iso file, unmount here.
|
||||
if MOUNTED: # In case we mounted an .iso file, unmount here.
|
||||
time.sleep(5) # play it safe and avoid failing to unmount.
|
||||
cmd = ['umount', '-l', nzb2media.MOUNTED]
|
||||
cmd = ['umount', '-l', MOUNTED]
|
||||
print_cmd(cmd)
|
||||
with subprocess.Popen(cmd, stdout=PIPE, stderr=DEVNULL) as proc:
|
||||
proc_out, proc_err = proc.communicate()
|
||||
time.sleep(5)
|
||||
os.rmdir(nzb2media.MOUNTED)
|
||||
nzb2media.MOUNTED = None
|
||||
if not final_result and not nzb2media.DUPLICATE:
|
||||
os.rmdir(MOUNTED)
|
||||
MOUNTED = None
|
||||
if not final_result and not DUPLICATE:
|
||||
for file in rem_list:
|
||||
try:
|
||||
os.unlink(file)
|
||||
|
@ -929,7 +980,235 @@ def transcode_directory(dir_name):
|
|||
# this is an empty directory and we didn't transcode into it.
|
||||
os.rmdir(new_dir)
|
||||
new_dir = dir_name
|
||||
if not nzb2media.PROCESSOUTPUT and nzb2media.DUPLICATE:
|
||||
if not PROCESSOUTPUT and DUPLICATE:
|
||||
# We postprocess the original files to CP/SB
|
||||
new_dir = dir_name
|
||||
return final_result, new_dir
|
||||
|
||||
|
||||
def configure_transcoder():
|
||||
global MOUNTED
|
||||
global GETSUBS
|
||||
global TRANSCODE
|
||||
global DUPLICATE
|
||||
global CONCAT
|
||||
global IGNOREEXTENSIONS
|
||||
global OUTPUTFASTSTART
|
||||
global GENERALOPTS
|
||||
global OTHEROPTS
|
||||
global OUTPUTQUALITYPERCENT
|
||||
global OUTPUTVIDEOPATH
|
||||
global PROCESSOUTPUT
|
||||
global ALANGUAGE
|
||||
global AINCLUDE
|
||||
global SLANGUAGES
|
||||
global SINCLUDE
|
||||
global SEXTRACT
|
||||
global SEMBED
|
||||
global SUBSDIR
|
||||
global VEXTENSION
|
||||
global VCODEC
|
||||
global VPRESET
|
||||
global VFRAMERATE
|
||||
global VBITRATE
|
||||
global VRESOLUTION
|
||||
global VCRF
|
||||
global VLEVEL
|
||||
global VCODEC_ALLOW
|
||||
global ACODEC
|
||||
global ACODEC_ALLOW
|
||||
global ACHANNELS
|
||||
global ABITRATE
|
||||
global ACODEC2
|
||||
global ACODEC2_ALLOW
|
||||
global ACHANNELS2
|
||||
global ABITRATE2
|
||||
global ACODEC3
|
||||
global ACODEC3_ALLOW
|
||||
global ACHANNELS3
|
||||
global ABITRATE3
|
||||
global SCODEC
|
||||
global BURN
|
||||
global HWACCEL
|
||||
global ALLOWSUBS
|
||||
global DEFAULTS
|
||||
MOUNTED = None
|
||||
GETSUBS = int(nzb2media.CFG['Transcoder']['getSubs'])
|
||||
TRANSCODE = int(nzb2media.CFG['Transcoder']['transcode'])
|
||||
DUPLICATE = int(nzb2media.CFG['Transcoder']['duplicate'])
|
||||
CONCAT = int(nzb2media.CFG['Transcoder']['concat'])
|
||||
IGNOREEXTENSIONS = nzb2media.CFG['Transcoder']['ignoreExtensions']
|
||||
if isinstance(IGNOREEXTENSIONS, str):
|
||||
IGNOREEXTENSIONS = IGNOREEXTENSIONS.split(',')
|
||||
OUTPUTFASTSTART = int(nzb2media.CFG['Transcoder']['outputFastStart'])
|
||||
GENERALOPTS = nzb2media.CFG['Transcoder']['generalOptions']
|
||||
if isinstance(GENERALOPTS, str):
|
||||
GENERALOPTS = GENERALOPTS.split(',')
|
||||
if GENERALOPTS == ['']:
|
||||
GENERALOPTS = []
|
||||
if '-fflags' not in GENERALOPTS:
|
||||
GENERALOPTS.append('-fflags')
|
||||
if '+genpts' not in GENERALOPTS:
|
||||
GENERALOPTS.append('+genpts')
|
||||
OTHEROPTS = nzb2media.CFG['Transcoder']['otherOptions']
|
||||
if isinstance(OTHEROPTS, str):
|
||||
OTHEROPTS = OTHEROPTS.split(',')
|
||||
if OTHEROPTS == ['']:
|
||||
OTHEROPTS = []
|
||||
try:
|
||||
OUTPUTQUALITYPERCENT = int(nzb2media.CFG['Transcoder']['outputQualityPercent'])
|
||||
except Exception:
|
||||
pass
|
||||
OUTPUTVIDEOPATH = nzb2media.CFG['Transcoder']['outputVideoPath']
|
||||
PROCESSOUTPUT = int(nzb2media.CFG['Transcoder']['processOutput'])
|
||||
ALANGUAGE = nzb2media.CFG['Transcoder']['audioLanguage']
|
||||
AINCLUDE = int(nzb2media.CFG['Transcoder']['allAudioLanguages'])
|
||||
SLANGUAGES = nzb2media.CFG['Transcoder']['subLanguages']
|
||||
if isinstance(SLANGUAGES, str):
|
||||
SLANGUAGES = SLANGUAGES.split(',')
|
||||
if SLANGUAGES == ['']:
|
||||
SLANGUAGES = []
|
||||
SINCLUDE = int(nzb2media.CFG['Transcoder']['allSubLanguages'])
|
||||
SEXTRACT = int(nzb2media.CFG['Transcoder']['extractSubs'])
|
||||
SEMBED = int(nzb2media.CFG['Transcoder']['embedSubs'])
|
||||
SUBSDIR = nzb2media.CFG['Transcoder']['externalSubDir']
|
||||
VEXTENSION = nzb2media.CFG['Transcoder']['outputVideoExtension'].strip()
|
||||
VCODEC = nzb2media.CFG['Transcoder']['outputVideoCodec'].strip()
|
||||
VCODEC_ALLOW = nzb2media.CFG['Transcoder']['VideoCodecAllow'].strip()
|
||||
if isinstance(VCODEC_ALLOW, str):
|
||||
VCODEC_ALLOW = VCODEC_ALLOW.split(',')
|
||||
if VCODEC_ALLOW == ['']:
|
||||
VCODEC_ALLOW = []
|
||||
VPRESET = nzb2media.CFG['Transcoder']['outputVideoPreset'].strip()
|
||||
try:
|
||||
VFRAMERATE = float(nzb2media.CFG['Transcoder']['outputVideoFramerate'].strip())
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
VCRF = int(nzb2media.CFG['Transcoder']['outputVideoCRF'].strip())
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
VLEVEL = nzb2media.CFG['Transcoder']['outputVideoLevel'].strip()
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
VBITRATE = int((nzb2media.CFG['Transcoder']['outputVideoBitrate'].strip()).replace('k', '000'))
|
||||
except Exception:
|
||||
pass
|
||||
VRESOLUTION = nzb2media.CFG['Transcoder']['outputVideoResolution']
|
||||
ACODEC = nzb2media.CFG['Transcoder']['outputAudioCodec'].strip()
|
||||
ACODEC_ALLOW = nzb2media.CFG['Transcoder']['AudioCodecAllow'].strip()
|
||||
if isinstance(ACODEC_ALLOW, str):
|
||||
ACODEC_ALLOW = ACODEC_ALLOW.split(',')
|
||||
if ACODEC_ALLOW == ['']:
|
||||
ACODEC_ALLOW = []
|
||||
try:
|
||||
ACHANNELS = int(nzb2media.CFG['Transcoder']['outputAudioChannels'].strip())
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
ABITRATE = int((nzb2media.CFG['Transcoder']['outputAudioBitrate'].strip()).replace('k', '000'))
|
||||
except Exception:
|
||||
pass
|
||||
ACODEC2 = nzb2media.CFG['Transcoder']['outputAudioTrack2Codec'].strip()
|
||||
ACODEC2_ALLOW = nzb2media.CFG['Transcoder']['AudioCodec2Allow'].strip()
|
||||
if isinstance(ACODEC2_ALLOW, str):
|
||||
ACODEC2_ALLOW = ACODEC2_ALLOW.split(',')
|
||||
if ACODEC2_ALLOW == ['']:
|
||||
ACODEC2_ALLOW = []
|
||||
try:
|
||||
ACHANNELS2 = int(nzb2media.CFG['Transcoder']['outputAudioTrack2Channels'].strip())
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
ABITRATE2 = int((nzb2media.CFG['Transcoder']['outputAudioTrack2Bitrate'].strip()).replace('k', '000'))
|
||||
except Exception:
|
||||
pass
|
||||
ACODEC3 = nzb2media.CFG['Transcoder']['outputAudioOtherCodec'].strip()
|
||||
ACODEC3_ALLOW = nzb2media.CFG['Transcoder']['AudioOtherCodecAllow'].strip()
|
||||
if isinstance(ACODEC3_ALLOW, str):
|
||||
ACODEC3_ALLOW = ACODEC3_ALLOW.split(',')
|
||||
if ACODEC3_ALLOW == ['']:
|
||||
ACODEC3_ALLOW = []
|
||||
try:
|
||||
ACHANNELS3 = int(nzb2media.CFG['Transcoder']['outputAudioOtherChannels'].strip())
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
ABITRATE3 = int((nzb2media.CFG['Transcoder']['outputAudioOtherBitrate'].strip()).replace('k', '000'))
|
||||
except Exception:
|
||||
pass
|
||||
SCODEC = nzb2media.CFG['Transcoder']['outputSubtitleCodec'].strip()
|
||||
BURN = int(nzb2media.CFG['Transcoder']['burnInSubtitle'].strip())
|
||||
DEFAULTS = nzb2media.CFG['Transcoder']['outputDefault'].strip()
|
||||
HWACCEL = int(nzb2media.CFG['Transcoder']['hwAccel'])
|
||||
allow_subs = ['.mkv', '.mp4', '.m4v', 'asf', 'wma', 'wmv']
|
||||
codec_alias = {'libx264': ['libx264', 'h264', 'h.264', 'AVC', 'MPEG-4'], 'libmp3lame': ['libmp3lame', 'mp3'], 'libfaac': ['libfaac', 'aac', 'faac']}
|
||||
transcode_defaults = {
|
||||
'iPad': {'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'], 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': None, 'ACHANNELS': 2, 'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'SCODEC': 'mov_text'},
|
||||
'iPad-1080p': {'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'], 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': None, 'ACHANNELS': 2, 'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'SCODEC': 'mov_text'},
|
||||
'iPad-720p': {'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None, 'VRESOLUTION': '1280:720', 'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'], 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': None, 'ACHANNELS': 2, 'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'SCODEC': 'mov_text'},
|
||||
'Apple-TV': {'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None, 'VRESOLUTION': '1280:720', 'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'], 'ACODEC': 'ac3', 'ACODEC_ALLOW': ['ac3'], 'ABITRATE': None, 'ACHANNELS': 6, 'ACODEC2': 'aac', 'ACODEC2_ALLOW': ['libfaac'], 'ABITRATE2': None, 'ACHANNELS2': 2, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'SCODEC': 'mov_text'},
|
||||
'iPod': {'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None, 'VRESOLUTION': '1280:720', 'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'], 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2, 'ACODEC2': None, 'ACODEC2_ALLOW': [], 'ABITRATE2': None, 'ACHANNELS2': None, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'SCODEC': 'mov_text'},
|
||||
'iPhone': {'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None, 'VRESOLUTION': '460:320', 'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'], 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2, 'ACODEC2': None, 'ACODEC2_ALLOW': [], 'ABITRATE2': None, 'ACHANNELS2': None, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'SCODEC': 'mov_text'},
|
||||
'PS3': {'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'], 'ACODEC': 'ac3', 'ACODEC_ALLOW': ['ac3'], 'ABITRATE': None, 'ACHANNELS': 6, 'ACODEC2': 'aac', 'ACODEC2_ALLOW': ['libfaac'], 'ABITRATE2': None, 'ACHANNELS2': 2, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'SCODEC': 'mov_text'},
|
||||
'xbox': {'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'], 'ACODEC': 'ac3', 'ACODEC_ALLOW': ['ac3'], 'ABITRATE': None, 'ACHANNELS': 6, 'ACODEC2': None, 'ACODEC2_ALLOW': [], 'ABITRATE2': None, 'ACHANNELS2': None, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'SCODEC': 'mov_text'},
|
||||
'Roku-480p': {'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'], 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2, 'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'SCODEC': 'mov_text'},
|
||||
'Roku-720p': {'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'], 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2, 'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'SCODEC': 'mov_text'},
|
||||
'Roku-1080p': {'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'], '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'},
|
||||
'mkv-bluray': {'VEXTENSION': '.mkv', 'VCODEC': 'libx265', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None, 'VRESOLUTION': None, 'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'hevc', 'h265', 'libx265', 'h.265', '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'},
|
||||
'MKV-SD': {'VEXTENSION': '.mkv', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': '1200k', 'VCRF': None, 'VLEVEL': None, 'VRESOLUTION': '720: -1', 'VCODEC_ALLOW': ['libx264', 'h264', 'h.264', 'AVC', 'avc', 'mpeg4', 'msmpeg4', 'MPEG-4'], 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2, 'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'SCODEC': 'mov_text'},
|
||||
}
|
||||
if DEFAULTS and DEFAULTS in transcode_defaults:
|
||||
VEXTENSION = transcode_defaults[DEFAULTS]['VEXTENSION']
|
||||
VCODEC = transcode_defaults[DEFAULTS]['VCODEC']
|
||||
VPRESET = transcode_defaults[DEFAULTS]['VPRESET']
|
||||
VFRAMERATE = transcode_defaults[DEFAULTS]['VFRAMERATE']
|
||||
VBITRATE = transcode_defaults[DEFAULTS]['VBITRATE']
|
||||
VRESOLUTION = transcode_defaults[DEFAULTS]['VRESOLUTION']
|
||||
VCRF = transcode_defaults[DEFAULTS]['VCRF']
|
||||
VLEVEL = transcode_defaults[DEFAULTS]['VLEVEL']
|
||||
VCODEC_ALLOW = transcode_defaults[DEFAULTS]['VCODEC_ALLOW']
|
||||
ACODEC = transcode_defaults[DEFAULTS]['ACODEC']
|
||||
ACODEC_ALLOW = transcode_defaults[DEFAULTS]['ACODEC_ALLOW']
|
||||
ACHANNELS = transcode_defaults[DEFAULTS]['ACHANNELS']
|
||||
ABITRATE = transcode_defaults[DEFAULTS]['ABITRATE']
|
||||
ACODEC2 = transcode_defaults[DEFAULTS]['ACODEC2']
|
||||
ACODEC2_ALLOW = transcode_defaults[DEFAULTS]['ACODEC2_ALLOW']
|
||||
ACHANNELS2 = transcode_defaults[DEFAULTS]['ACHANNELS2']
|
||||
ABITRATE2 = transcode_defaults[DEFAULTS]['ABITRATE2']
|
||||
ACODEC3 = transcode_defaults[DEFAULTS]['ACODEC3']
|
||||
ACODEC3_ALLOW = transcode_defaults[DEFAULTS]['ACODEC3_ALLOW']
|
||||
ACHANNELS3 = transcode_defaults[DEFAULTS]['ACHANNELS3']
|
||||
ABITRATE3 = transcode_defaults[DEFAULTS]['ABITRATE3']
|
||||
SCODEC = transcode_defaults[DEFAULTS]['SCODEC']
|
||||
del transcode_defaults
|
||||
if VEXTENSION in allow_subs:
|
||||
ALLOWSUBS = 1
|
||||
if not VCODEC_ALLOW and VCODEC:
|
||||
VCODEC_ALLOW.extend([VCODEC])
|
||||
for codec in VCODEC_ALLOW:
|
||||
if codec in codec_alias:
|
||||
extra = [item for item in codec_alias[codec] if item not in VCODEC_ALLOW]
|
||||
VCODEC_ALLOW.extend(extra)
|
||||
if not ACODEC_ALLOW and ACODEC:
|
||||
ACODEC_ALLOW.extend([ACODEC])
|
||||
for codec in ACODEC_ALLOW:
|
||||
if codec in codec_alias:
|
||||
extra = [item for item in codec_alias[codec] if item not in ACODEC_ALLOW]
|
||||
ACODEC_ALLOW.extend(extra)
|
||||
if not ACODEC2_ALLOW and ACODEC2:
|
||||
ACODEC2_ALLOW.extend([ACODEC2])
|
||||
for codec in ACODEC2_ALLOW:
|
||||
if codec in codec_alias:
|
||||
extra = [item for item in codec_alias[codec] if item not in ACODEC2_ALLOW]
|
||||
ACODEC2_ALLOW.extend(extra)
|
||||
if not ACODEC3_ALLOW and ACODEC3:
|
||||
ACODEC3_ALLOW.extend([ACODEC3])
|
||||
for codec in ACODEC3_ALLOW:
|
||||
if codec in codec_alias:
|
||||
extra = [item for item in codec_alias[codec] if item not in ACODEC3_ALLOW]
|
||||
ACODEC3_ALLOW.extend(extra)
|
||||
|
|
45
nzb2media/transmission.py
Normal file
45
nzb2media/transmission.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from transmission_rpc.client import Client as TransmissionClient
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
||||
HOST = None
|
||||
PORT = None
|
||||
USERNAME = None
|
||||
PASSWORD = None
|
||||
|
||||
|
||||
def configure_transmission(config):
|
||||
global HOST
|
||||
global PORT
|
||||
global USERNAME
|
||||
global PASSWORD
|
||||
|
||||
HOST = config['TransmissionHost'] # localhost
|
||||
PORT = int(config['TransmissionPort'])
|
||||
USERNAME = config['TransmissionUSR'] # mysecretusr
|
||||
PASSWORD = config['TransmissionPWD'] # mysecretpwr
|
||||
|
||||
|
||||
def configure_client():
|
||||
agent = 'transmission'
|
||||
host = HOST
|
||||
port = PORT
|
||||
user = USERNAME
|
||||
password = PASSWORD
|
||||
log.debug(f'Connecting to {agent}: http://{host}:{port}')
|
||||
try:
|
||||
client = TransmissionClient(
|
||||
host=host or '127.0.0.1',
|
||||
port=port or 9091,
|
||||
username=user,
|
||||
password=password,
|
||||
)
|
||||
except Exception:
|
||||
log.error('Failed to connect to Transmission')
|
||||
else:
|
||||
return client
|
|
@ -7,46 +7,62 @@ from subprocess import Popen
|
|||
import nzb2media
|
||||
from nzb2media import transcoder
|
||||
from nzb2media.auto_process.common import ProcessResult
|
||||
from nzb2media.plugins.subtitles import import_subs
|
||||
from nzb2media.subtitles import import_subs
|
||||
from nzb2media.utils.files import list_media_files
|
||||
from nzb2media.utils.paths import remove_dir
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
||||
MEDIA_EXTENSIONS = None
|
||||
SCRIPT = None
|
||||
PARAMETERS = None
|
||||
SUCCESS_CODES = None
|
||||
CLEAN = None
|
||||
DELAY = None
|
||||
RUN_ONCE = None
|
||||
|
||||
|
||||
def external_script(output_destination, torrent_name, torrent_label, settings):
|
||||
global MEDIA_EXTENSIONS
|
||||
global SCRIPT
|
||||
global PARAMETERS
|
||||
global SUCCESS_CODES
|
||||
global CLEAN
|
||||
global RUN_ONCE
|
||||
global DELAY
|
||||
|
||||
final_result = 0 # start at 0.
|
||||
num_files = 0
|
||||
nzb2media.USER_SCRIPT_MEDIAEXTENSIONS = settings.get('user_script_mediaExtensions', '')
|
||||
MEDIA_EXTENSIONS = settings.get('user_script_mediaExtensions', '')
|
||||
try:
|
||||
if isinstance(nzb2media.USER_SCRIPT_MEDIAEXTENSIONS, str):
|
||||
nzb2media.USER_SCRIPT_MEDIAEXTENSIONS = nzb2media.USER_SCRIPT_MEDIAEXTENSIONS.lower().split(',')
|
||||
if isinstance(MEDIA_EXTENSIONS, str):
|
||||
MEDIA_EXTENSIONS = MEDIA_EXTENSIONS.lower().split(',')
|
||||
except Exception:
|
||||
log.error('user_script_mediaExtensions could not be set')
|
||||
nzb2media.USER_SCRIPT_MEDIAEXTENSIONS = []
|
||||
nzb2media.USER_SCRIPT = settings.get('user_script_path', '')
|
||||
if not nzb2media.USER_SCRIPT or nzb2media.USER_SCRIPT == 'None':
|
||||
MEDIA_EXTENSIONS = []
|
||||
SCRIPT = settings.get('user_script_path', '')
|
||||
if not SCRIPT or SCRIPT == 'None':
|
||||
# do nothing and return success. This allows the user an option to Link files only and not run a script.
|
||||
return ProcessResult(status_code=0, message='No user script defined')
|
||||
nzb2media.USER_SCRIPT_PARAM = settings.get('user_script_param', '')
|
||||
PARAMETERS = settings.get('user_script_param', '')
|
||||
try:
|
||||
if isinstance(nzb2media.USER_SCRIPT_PARAM, str):
|
||||
nzb2media.USER_SCRIPT_PARAM = nzb2media.USER_SCRIPT_PARAM.split(',')
|
||||
if isinstance(PARAMETERS, str):
|
||||
PARAMETERS = PARAMETERS.split(',')
|
||||
except Exception:
|
||||
log.error('user_script_params could not be set')
|
||||
nzb2media.USER_SCRIPT_PARAM = []
|
||||
nzb2media.USER_SCRIPT_SUCCESSCODES = settings.get('user_script_successCodes', 0)
|
||||
PARAMETERS = []
|
||||
SUCCESS_CODES = settings.get('user_script_successCodes', 0)
|
||||
try:
|
||||
if isinstance(nzb2media.USER_SCRIPT_SUCCESSCODES, str):
|
||||
nzb2media.USER_SCRIPT_SUCCESSCODES = nzb2media.USER_SCRIPT_SUCCESSCODES.split(',')
|
||||
if isinstance(SUCCESS_CODES, str):
|
||||
SUCCESS_CODES = SUCCESS_CODES.split(',')
|
||||
except Exception:
|
||||
log.error('user_script_successCodes could not be set')
|
||||
nzb2media.USER_SCRIPT_SUCCESSCODES = 0
|
||||
nzb2media.USER_SCRIPT_CLEAN = int(settings.get('user_script_clean', 1))
|
||||
nzb2media.USER_SCRIPT_RUNONCE = int(settings.get('user_script_runOnce', 1))
|
||||
SUCCESS_CODES = 0
|
||||
CLEAN = int(settings.get('user_script_clean', 1))
|
||||
RUN_ONCE = int(settings.get('user_script_runOnce', 1))
|
||||
if nzb2media.CHECK_MEDIA:
|
||||
for video in list_media_files(output_destination, media=True, audio=False, meta=False, archives=False):
|
||||
for video in list_media_files(output_destination, audio=False, meta=False, archives=False):
|
||||
if transcoder.is_video_good(video, 0):
|
||||
import_subs(video)
|
||||
else:
|
||||
|
@ -57,12 +73,12 @@ def external_script(output_destination, torrent_name, torrent_label, settings):
|
|||
file_path = nzb2media.os.path.join(dirpath, file)
|
||||
file_name, file_extension = os.path.splitext(file)
|
||||
log.debug(f'Checking file {file} to see if this should be processed.')
|
||||
if file_extension in nzb2media.USER_SCRIPT_MEDIAEXTENSIONS or 'all' in nzb2media.USER_SCRIPT_MEDIAEXTENSIONS:
|
||||
if file_extension in MEDIA_EXTENSIONS or 'all' in MEDIA_EXTENSIONS:
|
||||
num_files += 1
|
||||
if nzb2media.USER_SCRIPT_RUNONCE == 1 and num_files > 1: # we have already run once, so just continue to get number of files.
|
||||
if RUN_ONCE == 1 and num_files > 1: # we have already run once, so just continue to get number of files.
|
||||
continue
|
||||
command = [nzb2media.USER_SCRIPT]
|
||||
for param in nzb2media.USER_SCRIPT_PARAM:
|
||||
command = [SCRIPT]
|
||||
for param in PARAMETERS:
|
||||
if param == 'FN':
|
||||
command.append(f'{file}')
|
||||
continue
|
||||
|
@ -76,7 +92,7 @@ def external_script(output_destination, torrent_name, torrent_label, settings):
|
|||
command.append(f'{torrent_label}')
|
||||
continue
|
||||
if param == 'DN':
|
||||
if nzb2media.USER_SCRIPT_RUNONCE == 1:
|
||||
if RUN_ONCE == 1:
|
||||
command.append(f'{output_destination}')
|
||||
else:
|
||||
command.append(f'{dirpath}')
|
||||
|
@ -93,7 +109,7 @@ def external_script(output_destination, torrent_name, torrent_label, settings):
|
|||
log.error(f'UserScript {command[0]} has failed')
|
||||
result = 1
|
||||
else:
|
||||
if str(res) in nzb2media.USER_SCRIPT_SUCCESSCODES:
|
||||
if str(res) in SUCCESS_CODES:
|
||||
# Linux returns 0 for successful.
|
||||
log.info(f'UserScript {command[0]} was successfull')
|
||||
result = 0
|
||||
|
@ -106,11 +122,11 @@ def external_script(output_destination, torrent_name, torrent_label, settings):
|
|||
for _, _, filenames in os.walk(output_destination):
|
||||
for file in filenames:
|
||||
file_name, file_extension = os.path.splitext(file)
|
||||
if file_extension in nzb2media.USER_SCRIPT_MEDIAEXTENSIONS or nzb2media.USER_SCRIPT_MEDIAEXTENSIONS == 'ALL':
|
||||
if file_extension in MEDIA_EXTENSIONS or MEDIA_EXTENSIONS == 'ALL':
|
||||
num_files_new += 1
|
||||
if nzb2media.USER_SCRIPT_CLEAN == 1 and not num_files_new and not final_result:
|
||||
if CLEAN == 1 and not num_files_new and not final_result:
|
||||
log.info(f'All files have been processed. Cleaning outputDirectory {output_destination}')
|
||||
remove_dir(output_destination)
|
||||
elif nzb2media.USER_SCRIPT_CLEAN == 1 and num_files_new:
|
||||
elif CLEAN == 1 and num_files_new:
|
||||
log.info(f'{num_files} files were processed, but {num_files_new} still remain. outputDirectory will not be cleaned.')
|
||||
return ProcessResult(status_code=final_result, message='User Script Completed')
|
||||
|
|
|
@ -11,7 +11,7 @@ import guessit
|
|||
import mediafile
|
||||
|
||||
import nzb2media
|
||||
from nzb2media import extractor
|
||||
import nzb2media.tool
|
||||
from nzb2media.utils.links import copy_link
|
||||
from nzb2media.utils.naming import is_sample
|
||||
from nzb2media.utils.naming import sanitize_name
|
||||
|
@ -153,7 +153,7 @@ def extract_files(src, dst=None, keep_archive=None):
|
|||
if dir_path in extracted_folder and archive_name in extracted_archive:
|
||||
continue # no need to extract this, but keep going to look for other archives and sub directories.
|
||||
try:
|
||||
if extractor.extract(input_file, dst or dir_path):
|
||||
if nzb2media.tool.extract(input_file, dst or dir_path):
|
||||
extracted_folder.append(dir_path)
|
||||
extracted_archive.append(archive_name)
|
||||
except Exception:
|
||||
|
|
|
@ -8,6 +8,8 @@ import time
|
|||
import requests
|
||||
|
||||
import nzb2media
|
||||
import nzb2media.nzb
|
||||
import nzb2media.torrent
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
@ -80,12 +82,12 @@ def server_responding(base_url):
|
|||
def find_download(client_agent, download_id):
|
||||
log.debug(f'Searching for Download on {client_agent} ...')
|
||||
if client_agent == 'utorrent':
|
||||
torrents = nzb2media.TORRENT_CLASS.list()[1]['torrents']
|
||||
torrents = nzb2media.torrent.CLASS.list()[1]['torrents']
|
||||
for torrent in torrents:
|
||||
if download_id in torrent:
|
||||
return True
|
||||
if client_agent == 'transmission':
|
||||
torrents = nzb2media.TORRENT_CLASS.get_torrents()
|
||||
torrents = nzb2media.torrent.CLASS.get_torrents()
|
||||
for torrent in torrents:
|
||||
torrent_hash = torrent.hashString
|
||||
if torrent_hash == download_id:
|
||||
|
@ -93,17 +95,17 @@ def find_download(client_agent, download_id):
|
|||
if client_agent == 'deluge':
|
||||
return False
|
||||
if client_agent == 'qbittorrent':
|
||||
torrents = nzb2media.TORRENT_CLASS.torrents()
|
||||
torrents = nzb2media.torrent.CLASS.torrents()
|
||||
for torrent in torrents:
|
||||
if torrent['hash'] == download_id:
|
||||
return True
|
||||
if client_agent == 'sabnzbd':
|
||||
if 'http' in nzb2media.SABNZBD_HOST:
|
||||
base_url = f'{nzb2media.SABNZBD_HOST}:{nzb2media.SABNZBD_PORT}/api'
|
||||
if 'http' in nzb2media.nzb.SABNZBD_HOST:
|
||||
base_url = f'{nzb2media.nzb.SABNZBD_HOST}:{nzb2media.nzb.SABNZBD_PORT}/api'
|
||||
else:
|
||||
base_url = f'http://{nzb2media.SABNZBD_HOST}:{nzb2media.SABNZBD_PORT}/api'
|
||||
base_url = f'http://{nzb2media.nzb.SABNZBD_HOST}:{nzb2media.nzb.SABNZBD_PORT}/api'
|
||||
url = base_url
|
||||
params = {'apikey': nzb2media.SABNZBD_APIKEY, 'mode': 'get_files', 'output': 'json', 'value': download_id}
|
||||
params = {'apikey': nzb2media.nzb.SABNZBD_APIKEY, 'mode': 'get_files', 'output': 'json', 'value': download_id}
|
||||
try:
|
||||
response = requests.get(url, params=params, verify=False, timeout=(30, 120))
|
||||
except requests.ConnectionError:
|
||||
|
|
|
@ -4,6 +4,7 @@ import logging
|
|||
import os
|
||||
|
||||
import nzb2media
|
||||
import nzb2media.torrent
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
@ -61,7 +62,7 @@ def parse_deluge(args):
|
|||
input_hash = args[1]
|
||||
input_id = args[1]
|
||||
try:
|
||||
input_category = nzb2media.TORRENT_CLASS.core.get_torrent_status(input_id, ['label']).get(b'label').decode()
|
||||
input_category = nzb2media.torrent.CLASS.core.get_torrent_status(input_id, ['label']).get(b'label').decode()
|
||||
except Exception:
|
||||
input_category = ''
|
||||
return input_directory, input_name, input_category, input_hash, input_id
|
||||
|
@ -89,7 +90,7 @@ def parse_synods():
|
|||
torrent_id = os.getenv('TR_TORRENT_ID')
|
||||
input_id = f'dbid_{torrent_id}'
|
||||
# res = nzb2media.TORRENT_CLASS.tasks_list(additional_param='detail')
|
||||
res = nzb2media.TORRENT_CLASS.tasks_info(input_id, additional_param='detail')
|
||||
res = nzb2media.torrent.CLASS.tasks_info(input_id, additional_param='detail')
|
||||
log.debug(f'result from syno {res}')
|
||||
if res['success']:
|
||||
try:
|
||||
|
@ -176,7 +177,16 @@ def parse_qbittorrent(args):
|
|||
|
||||
|
||||
def parse_args(client_agent, args):
|
||||
clients = {'other': parse_other, 'rtorrent': parse_rtorrent, 'utorrent': parse_utorrent, 'deluge': parse_deluge, 'transmission': parse_transmission, 'qbittorrent': parse_qbittorrent, 'vuze': parse_vuze, 'synods': parse_synods}
|
||||
clients = {
|
||||
'other': parse_other,
|
||||
'rtorrent': parse_rtorrent,
|
||||
'utorrent': parse_utorrent,
|
||||
'deluge': parse_deluge,
|
||||
'transmission': parse_transmission,
|
||||
'qbittorrent': parse_qbittorrent,
|
||||
'vuze': parse_vuze,
|
||||
'synods': parse_synods,
|
||||
}
|
||||
try:
|
||||
return clients[client_agent](args)
|
||||
except Exception:
|
||||
|
|
|
@ -1,118 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import typing
|
||||
|
||||
import nzb2media
|
||||
|
||||
if os.name == 'nt':
|
||||
# Silence errors on linux
|
||||
# pylint: disable=import-error
|
||||
# pylint: disable=no-name-in-module
|
||||
from win32api import CloseHandle
|
||||
from win32api import GetLastError
|
||||
from win32event import CreateMutex
|
||||
from winerror import ERROR_ALREADY_EXISTS
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
class WindowsProcess:
|
||||
def __init__(self):
|
||||
self.mutex = None
|
||||
# {D0E858DF-985E-4907-B7FB-8D732C3FC3B9}
|
||||
_path_str = os.fspath(nzb2media.PID_FILE).replace('\\', '/')
|
||||
self.mutexname = f'nzbtomedia_{_path_str}'
|
||||
self.create_mutex = CreateMutex
|
||||
self.close_handle = CloseHandle
|
||||
self.get_last_error = GetLastError
|
||||
self.error_already_exists = ERROR_ALREADY_EXISTS
|
||||
|
||||
def alreadyrunning(self):
|
||||
self.mutex = self.create_mutex(None, 0, self.mutexname)
|
||||
self.lasterror = self.get_last_error()
|
||||
if self.lasterror == self.error_already_exists:
|
||||
self.close_handle(self.mutex)
|
||||
return True
|
||||
return False
|
||||
|
||||
def __del__(self):
|
||||
if self.mutex:
|
||||
self.close_handle(self.mutex)
|
||||
|
||||
|
||||
class PosixProcess:
|
||||
def __init__(self):
|
||||
self.pidpath = nzb2media.PID_FILE
|
||||
self.lock_socket = None
|
||||
|
||||
def alreadyrunning(self):
|
||||
try:
|
||||
self.lock_socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
|
||||
self.lock_socket.bind(f'\0{self.pidpath}')
|
||||
self.lasterror = False
|
||||
return self.lasterror
|
||||
except OSError as error:
|
||||
if 'Address already in use' in str(error):
|
||||
self.lasterror = True
|
||||
return self.lasterror
|
||||
except AttributeError:
|
||||
pass
|
||||
if self.pidpath.exists():
|
||||
# Make sure it is not a 'stale' pidFile
|
||||
try:
|
||||
pid = int(self.pidpath.read_text().strip())
|
||||
except Exception:
|
||||
pid = None
|
||||
# Check list of running pids, if not running it is stale so overwrite
|
||||
if isinstance(pid, int):
|
||||
try:
|
||||
os.kill(pid, 0)
|
||||
self.lasterror = True
|
||||
except OSError:
|
||||
self.lasterror = False
|
||||
else:
|
||||
self.lasterror = False
|
||||
else:
|
||||
self.lasterror = False
|
||||
if not self.lasterror:
|
||||
# Write my pid into pidFile to keep multiple copies of program
|
||||
# from running
|
||||
self.pidpath.write_text(os.getpid())
|
||||
return self.lasterror
|
||||
|
||||
def __del__(self):
|
||||
if not self.lasterror:
|
||||
if self.lock_socket:
|
||||
self.lock_socket.close()
|
||||
if self.pidpath.is_file():
|
||||
self.pidpath.unlink()
|
||||
|
||||
|
||||
# Alternative union syntax using | fails on Python < 3.10
|
||||
# pylint: disable-next=consider-alternative-union-syntax
|
||||
ProcessType = typing.Type[typing.Union[PosixProcess, WindowsProcess]]
|
||||
if os.name == 'nt':
|
||||
RunningProcess: ProcessType = WindowsProcess
|
||||
else:
|
||||
RunningProcess = PosixProcess
|
||||
|
||||
|
||||
def restart():
|
||||
install_type = nzb2media.version_check.CheckVersion().install_type
|
||||
status = 0
|
||||
popen_list = []
|
||||
if install_type in {'git', 'source'}:
|
||||
popen_list = [sys.executable, nzb2media.APP_FILENAME]
|
||||
if popen_list:
|
||||
popen_list += nzb2media.SYS_ARGV
|
||||
log.info(f'Restarting nzbToMedia with {popen_list}')
|
||||
with subprocess.Popen(popen_list, cwd=os.getcwd()) as proc:
|
||||
proc.wait()
|
||||
status = proc.returncode
|
||||
os._exit(status)
|
|
@ -1,87 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import time
|
||||
|
||||
import nzb2media
|
||||
from nzb2media.torrent import deluge
|
||||
from nzb2media.torrent import qbittorrent
|
||||
from nzb2media.torrent import synology
|
||||
from nzb2media.torrent import transmission
|
||||
from nzb2media.torrent import utorrent
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
torrent_clients = {'deluge': deluge, 'qbittorrent': qbittorrent, 'transmission': transmission, 'utorrent': utorrent, 'synods': synology}
|
||||
|
||||
|
||||
def create_torrent_class(client_agent) -> object | None:
|
||||
if nzb2media.APP_NAME != 'TorrentToMedia.py':
|
||||
return None # Skip loading Torrent for NZBs.
|
||||
try:
|
||||
agent = torrent_clients[client_agent]
|
||||
except KeyError:
|
||||
return None
|
||||
else:
|
||||
deluge.configure_client()
|
||||
return agent.configure_client()
|
||||
|
||||
|
||||
def pause_torrent(client_agent, input_hash, input_id, input_name):
|
||||
log.debug(f'Stopping torrent {input_name} in {client_agent} while processing')
|
||||
try:
|
||||
if client_agent == 'utorrent' and nzb2media.TORRENT_CLASS:
|
||||
nzb2media.TORRENT_CLASS.stop(input_hash)
|
||||
if client_agent == 'transmission' and nzb2media.TORRENT_CLASS:
|
||||
nzb2media.TORRENT_CLASS.stop_torrent(input_id)
|
||||
if client_agent == 'synods' and nzb2media.TORRENT_CLASS:
|
||||
nzb2media.TORRENT_CLASS.pause_task(input_id)
|
||||
if client_agent == 'deluge' and nzb2media.TORRENT_CLASS:
|
||||
nzb2media.TORRENT_CLASS.core.pause_torrent([input_id])
|
||||
if client_agent == 'qbittorrent' and nzb2media.TORRENT_CLASS:
|
||||
nzb2media.TORRENT_CLASS.pause(input_hash)
|
||||
time.sleep(5)
|
||||
except Exception:
|
||||
log.warning(f'Failed to stop torrent {input_name} in {client_agent}')
|
||||
|
||||
|
||||
def resume_torrent(client_agent, input_hash, input_id, input_name):
|
||||
if nzb2media.TORRENT_RESUME != 1:
|
||||
return
|
||||
log.debug(f'Starting torrent {input_name} in {client_agent}')
|
||||
try:
|
||||
if client_agent == 'utorrent' and nzb2media.TORRENT_CLASS:
|
||||
nzb2media.TORRENT_CLASS.start(input_hash)
|
||||
if client_agent == 'transmission' and nzb2media.TORRENT_CLASS:
|
||||
nzb2media.TORRENT_CLASS.start_torrent(input_id)
|
||||
if client_agent == 'synods' and nzb2media.TORRENT_CLASS:
|
||||
nzb2media.TORRENT_CLASS.resume_task(input_id)
|
||||
if client_agent == 'deluge' and nzb2media.TORRENT_CLASS:
|
||||
nzb2media.TORRENT_CLASS.core.resume_torrent([input_id])
|
||||
if client_agent == 'qbittorrent' and nzb2media.TORRENT_CLASS:
|
||||
nzb2media.TORRENT_CLASS.resume(input_hash)
|
||||
time.sleep(5)
|
||||
except Exception:
|
||||
log.warning(f'Failed to start torrent {input_name} in {client_agent}')
|
||||
|
||||
|
||||
def remove_torrent(client_agent, input_hash, input_id, input_name):
|
||||
if nzb2media.DELETE_ORIGINAL == 1 or nzb2media.USE_LINK == 'move':
|
||||
log.debug(f'Deleting torrent {input_name} from {client_agent}')
|
||||
try:
|
||||
if client_agent == 'utorrent' and nzb2media.TORRENT_CLASS:
|
||||
nzb2media.TORRENT_CLASS.removedata(input_hash)
|
||||
nzb2media.TORRENT_CLASS.remove(input_hash)
|
||||
if client_agent == 'transmission' and nzb2media.TORRENT_CLASS:
|
||||
nzb2media.TORRENT_CLASS.remove_torrent(input_id, True)
|
||||
if client_agent == 'synods' and nzb2media.TORRENT_CLASS:
|
||||
nzb2media.TORRENT_CLASS.delete_task(input_id)
|
||||
if client_agent == 'deluge' and nzb2media.TORRENT_CLASS:
|
||||
nzb2media.TORRENT_CLASS.core.remove_torrent(input_id, True)
|
||||
if client_agent == 'qbittorrent' and nzb2media.TORRENT_CLASS:
|
||||
nzb2media.TORRENT_CLASS.delete_permanently(input_hash)
|
||||
time.sleep(5)
|
||||
except Exception:
|
||||
log.warning(f'Failed to delete torrent {input_name} in {client_agent}')
|
||||
else:
|
||||
resume_torrent(client_agent, input_hash, input_id, input_name)
|
|
@ -4,17 +4,29 @@ import logging
|
|||
|
||||
from utorrent.client import UTorrentClient
|
||||
|
||||
import nzb2media
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
||||
HOST = None
|
||||
USERNAME = None
|
||||
PASSWORD = None
|
||||
|
||||
|
||||
def configure_utorrent(config):
|
||||
global HOST
|
||||
global USERNAME
|
||||
global PASSWORD
|
||||
|
||||
HOST = config['uTorrentWEBui'] # http://localhost:8090/gui/
|
||||
USERNAME = config['uTorrentUSR'] # mysecretusr
|
||||
PASSWORD = config['uTorrentPWD'] # mysecretpwr
|
||||
|
||||
|
||||
def configure_client():
|
||||
agent = 'utorrent'
|
||||
web_ui = nzb2media.UTORRENT_WEB_UI
|
||||
user = nzb2media.UTORRENT_USER
|
||||
password = nzb2media.UTORRENT_PASSWORD
|
||||
web_ui = HOST
|
||||
user = USERNAME
|
||||
password = PASSWORD
|
||||
log.debug(f'Connecting to {agent}: {web_ui}')
|
||||
try:
|
||||
client = UTorrentClient(web_ui, user, password)
|
|
@ -1,423 +0,0 @@
|
|||
# Author: Nic Wolfe <nic@wolfeden.ca>
|
||||
# Modified by: echel0n
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
import stat
|
||||
import subprocess
|
||||
import tarfile
|
||||
import traceback
|
||||
from subprocess import PIPE
|
||||
from subprocess import STDOUT
|
||||
from urllib.request import urlretrieve
|
||||
|
||||
import nzb2media
|
||||
from nzb2media import github_api as github
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
class CheckVersion:
|
||||
"""Version checker that runs in a thread with the SB scheduler."""
|
||||
|
||||
def __init__(self):
|
||||
self.install_type = self.find_install_type()
|
||||
self.installed_version = None
|
||||
self.installed_branch = None
|
||||
if self.install_type == 'git':
|
||||
self.updater = GitUpdateManager()
|
||||
elif self.install_type == 'source':
|
||||
self.updater = SourceUpdateManager()
|
||||
else:
|
||||
self.updater = None
|
||||
|
||||
def run(self):
|
||||
self.check_for_new_version()
|
||||
|
||||
@staticmethod
|
||||
def find_install_type():
|
||||
"""Determine how this copy of SB was installed.
|
||||
|
||||
returns: type of installation. Possible values are:
|
||||
'win': any compiled windows build
|
||||
'git': running from source using git
|
||||
'source': running from source without git
|
||||
"""
|
||||
# check if we're a windows build
|
||||
if os.path.exists(os.path.join(nzb2media.APP_ROOT, '.git')):
|
||||
install_type = 'git'
|
||||
else:
|
||||
install_type = 'source'
|
||||
return install_type
|
||||
|
||||
def check_for_new_version(self, force=False):
|
||||
"""Check the internet for a newer version.
|
||||
|
||||
returns: bool, True for new version or False for no new version.
|
||||
force: if true the VERSION_NOTIFY setting will be ignored and a check will be forced
|
||||
"""
|
||||
if not nzb2media.VERSION_NOTIFY and not force:
|
||||
log.info('Version checking is disabled, not checking for the newest version')
|
||||
return False
|
||||
log.info(f'Checking if {self.install_type} needs an update')
|
||||
if not self.updater.need_update():
|
||||
nzb2media.NEWEST_VERSION_STRING = None
|
||||
log.info('No update needed')
|
||||
return False
|
||||
self.updater.set_newest_text()
|
||||
return True
|
||||
|
||||
def update(self):
|
||||
if self.updater.need_update():
|
||||
result = self.updater.update()
|
||||
return result
|
||||
|
||||
|
||||
class UpdateManager:
|
||||
@staticmethod
|
||||
def get_github_repo_user():
|
||||
return nzb2media.GIT_USER
|
||||
|
||||
@staticmethod
|
||||
def get_github_repo():
|
||||
return nzb2media.GIT_REPO
|
||||
|
||||
@staticmethod
|
||||
def get_github_branch():
|
||||
return nzb2media.GIT_BRANCH
|
||||
|
||||
|
||||
class GitUpdateManager(UpdateManager):
|
||||
def __init__(self):
|
||||
self._git_path = self._find_working_git()
|
||||
self.github_repo_user = self.get_github_repo_user()
|
||||
self.github_repo = self.get_github_repo()
|
||||
self.branch = self._find_git_branch()
|
||||
self._cur_commit_hash = None
|
||||
self._newest_commit_hash = None
|
||||
self._num_commits_behind = 0
|
||||
self._num_commits_ahead = 0
|
||||
|
||||
@staticmethod
|
||||
def _git_error():
|
||||
log.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):
|
||||
test_cmd = 'version'
|
||||
if nzb2media.GIT_PATH:
|
||||
main_git = f'"{nzb2media.GIT_PATH}"'
|
||||
else:
|
||||
main_git = 'git'
|
||||
log.debug(f'Checking if we can use git commands: {main_git} {test_cmd}')
|
||||
output, err, exit_status = self._run_git(main_git, test_cmd)
|
||||
if not exit_status:
|
||||
log.debug(f'Using: {main_git}')
|
||||
return main_git
|
||||
log.debug(f'Not using: {main_git}')
|
||||
# trying alternatives
|
||||
alternative_git = []
|
||||
# osx people who start SB from launchd have a broken path, so try a hail-mary attempt for them
|
||||
if platform.system().lower() == 'darwin':
|
||||
alternative_git.append('/usr/local/git/bin/git')
|
||||
if platform.system().lower() == 'windows':
|
||||
if main_git != main_git.lower():
|
||||
alternative_git.append(main_git.lower())
|
||||
if alternative_git:
|
||||
log.debug('Trying known alternative git locations')
|
||||
for cur_git in alternative_git:
|
||||
log.debug(f'Checking if we can use git commands: {cur_git} {test_cmd}')
|
||||
output, err, exit_status = self._run_git(cur_git, test_cmd)
|
||||
if not exit_status:
|
||||
log.debug(f'Using: {cur_git}')
|
||||
return cur_git
|
||||
log.debug(f'Not using: {cur_git}')
|
||||
# Still haven't found a working git
|
||||
log.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
|
||||
|
||||
@staticmethod
|
||||
def _run_git(git_path, args):
|
||||
result = ''
|
||||
proc_err = ''
|
||||
if not git_path:
|
||||
log.debug('No git specified, can\'t use git commands')
|
||||
proc_status = 1
|
||||
return result, proc_err, proc_status
|
||||
cmd = f'{git_path} {args}'
|
||||
try:
|
||||
log.debug(f'Executing {cmd} with your shell in {nzb2media.APP_ROOT}')
|
||||
with subprocess.Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT, shell=True, cwd=nzb2media.APP_ROOT) as proc:
|
||||
proc_out, proc_err = proc.communicate()
|
||||
proc_status = proc.returncode
|
||||
if nzb2media.LOG_GIT:
|
||||
msg = proc_out.decode('utf-8').strip()
|
||||
log.debug(f'git output: {msg}')
|
||||
except OSError:
|
||||
log.error(f'Command {cmd} didn\'t work')
|
||||
proc_status = 1
|
||||
proc_status = 128 if ('fatal:' in result) or proc_err else proc_status
|
||||
if not proc_status:
|
||||
log.debug(f'{cmd} : returned successful')
|
||||
proc_status = 0
|
||||
elif nzb2media.LOG_GIT and proc_status in {1, 128}:
|
||||
log.debug(f'{cmd} returned : {result}')
|
||||
else:
|
||||
if nzb2media.LOG_GIT:
|
||||
log.debug(f'{cmd} returned : {result}, treat as error for now')
|
||||
proc_status = 1
|
||||
return result, proc_err, proc_status
|
||||
|
||||
def _find_installed_version(self):
|
||||
"""Attempt to find the currently installed version of Sick Beard.
|
||||
|
||||
Uses git show to get commit version.
|
||||
Returns: True for success or False for failure
|
||||
"""
|
||||
output, err, exit_status = self._run_git(self._git_path, 'rev-parse HEAD')
|
||||
if not exit_status and output:
|
||||
cur_commit_hash = output.strip()
|
||||
if not re.match('^[a-z0-9]+$', cur_commit_hash):
|
||||
log.error('Output doesn\'t look like a hash, not using it')
|
||||
return False
|
||||
self._cur_commit_hash = cur_commit_hash
|
||||
if self._cur_commit_hash:
|
||||
nzb2media.NZBTOMEDIA_VERSION = self._cur_commit_hash
|
||||
return True
|
||||
return False
|
||||
|
||||
def _find_git_branch(self):
|
||||
nzb2media.NZBTOMEDIA_BRANCH = self.get_github_branch()
|
||||
branch_info, err, exit_status = self._run_git(self._git_path, 'symbolic-ref -q HEAD')
|
||||
if not exit_status and branch_info:
|
||||
branch = branch_info.strip().replace('refs/heads/', '', 1)
|
||||
if branch:
|
||||
nzb2media.NZBTOMEDIA_BRANCH = branch
|
||||
nzb2media.GIT_BRANCH = branch
|
||||
return nzb2media.GIT_BRANCH
|
||||
|
||||
def _check_github_for_update(self):
|
||||
"""Check Github for a new version.
|
||||
|
||||
Uses git commands to check if there is a newer version than
|
||||
the provided commit hash. If there is a newer version it
|
||||
sets _num_commits_behind.
|
||||
"""
|
||||
self._newest_commit_hash = None
|
||||
self._num_commits_behind = 0
|
||||
self._num_commits_ahead = 0
|
||||
# get all new info from github
|
||||
output, err, exit_status = self._run_git(self._git_path, 'fetch origin')
|
||||
if exit_status:
|
||||
log.error('Unable to contact github, can\'t check for update')
|
||||
return
|
||||
# get latest commit_hash from remote
|
||||
output, err, exit_status = self._run_git(self._git_path, 'rev-parse --verify --quiet \'@{upstream}\'')
|
||||
if not exit_status and output:
|
||||
cur_commit_hash = output.strip()
|
||||
if not re.match('^[a-z0-9]+$', cur_commit_hash):
|
||||
log.debug('Output doesn\'t look like a hash, not using it')
|
||||
return
|
||||
self._newest_commit_hash = cur_commit_hash
|
||||
else:
|
||||
log.debug('git didn\'t return newest commit hash')
|
||||
return
|
||||
# get number of commits behind and ahead (option --count not supported git < 1.7.2)
|
||||
output, err, exit_status = self._run_git(self._git_path, 'rev-list --left-right \'@{upstream}\'...HEAD')
|
||||
if not exit_status and output:
|
||||
try:
|
||||
self._num_commits_behind = int(output.count('<'))
|
||||
self._num_commits_ahead = int(output.count('>'))
|
||||
except Exception:
|
||||
log.debug('git didn\'t return numbers for behind and ahead, not using it')
|
||||
return
|
||||
log.debug(f'cur_commit = {self._cur_commit_hash} % (newest_commit)= {self._newest_commit_hash}, num_commits_behind = {self._num_commits_behind}, num_commits_ahead = {self._num_commits_ahead}')
|
||||
|
||||
def set_newest_text(self):
|
||||
if self._num_commits_ahead:
|
||||
log.error(f'Local branch is ahead of {self.branch}. Automatic update not possible.')
|
||||
elif self._num_commits_behind:
|
||||
_plural = 's' if self._num_commits_behind > 1 else ''
|
||||
log.info(f'There is a newer version available (you\'re {self._num_commits_behind} commit{_plural} behind)')
|
||||
else:
|
||||
return
|
||||
|
||||
def need_update(self):
|
||||
if not self._find_installed_version():
|
||||
log.error('Unable to determine installed version via git, please check your logs!')
|
||||
return False
|
||||
if not self._cur_commit_hash:
|
||||
return True
|
||||
try:
|
||||
self._check_github_for_update()
|
||||
except Exception as error:
|
||||
log.error(f'Unable to contact github, can\'t check for update: {error!r}')
|
||||
return False
|
||||
if self._num_commits_behind > 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def update(self):
|
||||
"""Check git for a new version.
|
||||
|
||||
Calls git pull origin <branch> in order to update Sick Beard.
|
||||
Returns a bool depending on the call's success.
|
||||
"""
|
||||
output, err, exit_status = self._run_git(self._git_path, f'pull origin {self.branch}')
|
||||
if not exit_status:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class SourceUpdateManager(UpdateManager):
|
||||
def __init__(self):
|
||||
self.github_repo_user = self.get_github_repo_user()
|
||||
self.github_repo = self.get_github_repo()
|
||||
self.branch = self.get_github_branch()
|
||||
self._cur_commit_hash = None
|
||||
self._newest_commit_hash = None
|
||||
self._num_commits_behind = 0
|
||||
|
||||
def _find_installed_version(self):
|
||||
version_file = os.path.join(nzb2media.APP_ROOT, 'version.txt')
|
||||
if not os.path.isfile(version_file):
|
||||
self._cur_commit_hash = None
|
||||
return
|
||||
try:
|
||||
with open(version_file, encoding='utf-8') as fin:
|
||||
self._cur_commit_hash = fin.read().strip(' \n\r')
|
||||
except OSError as error:
|
||||
log.debug(f'Unable to open \'version.txt\': {error}')
|
||||
if not self._cur_commit_hash:
|
||||
self._cur_commit_hash = None
|
||||
else:
|
||||
nzb2media.NZBTOMEDIA_VERSION = self._cur_commit_hash
|
||||
|
||||
def need_update(self):
|
||||
self._find_installed_version()
|
||||
try:
|
||||
self._check_github_for_update()
|
||||
except Exception as error:
|
||||
log.error(f'Unable to contact github, can\'t check for update: {error!r}')
|
||||
return False
|
||||
if not self._cur_commit_hash or self._num_commits_behind > 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _check_github_for_update(self):
|
||||
"""Check Github for a new version.
|
||||
|
||||
Uses pygithub to ask github if there is a newer version than
|
||||
the provided commit hash. If there is a newer version it sets
|
||||
Sick Beard's version text.
|
||||
commit_hash: hash that we're checking against
|
||||
"""
|
||||
self._num_commits_behind = 0
|
||||
self._newest_commit_hash = None
|
||||
repository = github.GitHub(self.github_repo_user, self.github_repo, self.branch)
|
||||
# try to get newest commit hash and commits behind directly by
|
||||
# comparing branch and current commit
|
||||
if self._cur_commit_hash:
|
||||
branch_compared = repository.compare(base=self.branch, head=self._cur_commit_hash)
|
||||
if 'base_commit' in branch_compared:
|
||||
self._newest_commit_hash = branch_compared['base_commit']['sha']
|
||||
if 'behind_by' in branch_compared:
|
||||
self._num_commits_behind = int(branch_compared['behind_by'])
|
||||
# fall back and iterate over last 100 (items per page in gh_api) commits
|
||||
if not self._newest_commit_hash:
|
||||
for cur_commit in repository.commits():
|
||||
if not self._newest_commit_hash:
|
||||
self._newest_commit_hash = cur_commit['sha']
|
||||
if not self._cur_commit_hash:
|
||||
break
|
||||
if cur_commit['sha'] == self._cur_commit_hash:
|
||||
break
|
||||
# when _cur_commit_hash doesn't match anything _num_commits_behind == 100
|
||||
self._num_commits_behind += 1
|
||||
log.debug(f'cur_commit = {self._cur_commit_hash} % (newest_commit)= {self._newest_commit_hash}, num_commits_behind = {self._num_commits_behind}')
|
||||
|
||||
def set_newest_text(self):
|
||||
# if we're up to date then don't set this
|
||||
nzb2media.NEWEST_VERSION_STRING = None
|
||||
if not self._cur_commit_hash:
|
||||
log.error('Unknown current version number, don\'t know if we should update or not')
|
||||
elif self._num_commits_behind > 0:
|
||||
_plural = 's' if self._num_commits_behind > 1 else ''
|
||||
log.info(f'There is a newer version available (you\'re {self._num_commits_behind} commit{_plural} behind)')
|
||||
else:
|
||||
return
|
||||
|
||||
def update(self):
|
||||
"""Download and install latest source tarball from github."""
|
||||
tar_download_url = f'https://github.com/{self.github_repo_user}/{self.github_repo}/tarball/{self.branch}'
|
||||
version_path = os.path.join(nzb2media.APP_ROOT, 'version.txt')
|
||||
try:
|
||||
# prepare the update dir
|
||||
sb_update_dir = os.path.join(nzb2media.APP_ROOT, 'sb-update')
|
||||
if os.path.isdir(sb_update_dir):
|
||||
log.info(f'Clearing out update folder {sb_update_dir} before extracting')
|
||||
shutil.rmtree(sb_update_dir)
|
||||
log.info(f'Creating update folder {sb_update_dir} before extracting')
|
||||
os.makedirs(sb_update_dir)
|
||||
# retrieve file
|
||||
log.info(f'Downloading update from {tar_download_url!r}')
|
||||
tar_download_path = os.path.join(sb_update_dir, 'nzbtomedia-update.tar')
|
||||
urlretrieve(tar_download_url, tar_download_path)
|
||||
if not os.path.isfile(tar_download_path):
|
||||
log.error(f'Unable to retrieve new version from {tar_download_url}, can\'t update')
|
||||
return False
|
||||
if not tarfile.is_tarfile(tar_download_path):
|
||||
log.error(f'Retrieved version from {tar_download_url} is corrupt, can\'t update')
|
||||
return False
|
||||
# extract to sb-update dir
|
||||
log.info(f'Extracting file {tar_download_path}')
|
||||
with tarfile.open(tar_download_path) as tar:
|
||||
tar.extractall(sb_update_dir)
|
||||
# delete .tar.gz
|
||||
log.info(f'Deleting file {tar_download_path}')
|
||||
os.remove(tar_download_path)
|
||||
# find update dir name
|
||||
update_dir_contents = [x for x in os.listdir(sb_update_dir) if os.path.isdir(os.path.join(sb_update_dir, x))]
|
||||
if len(update_dir_contents) != 1:
|
||||
log.error(f'Invalid update data, update failed: {update_dir_contents}')
|
||||
return False
|
||||
content_dir = os.path.join(sb_update_dir, update_dir_contents[0])
|
||||
# walk temp folder and move files to main folder
|
||||
log.info(f'Moving files from {content_dir} to {nzb2media.APP_ROOT}')
|
||||
for dirname, _, filenames in os.walk(content_dir):
|
||||
dirname = dirname[len(content_dir) + 1:]
|
||||
for curfile in filenames:
|
||||
old_path = os.path.join(content_dir, dirname, curfile)
|
||||
new_path = os.path.join(nzb2media.APP_ROOT, dirname, curfile)
|
||||
# Avoid DLL access problem on WIN32/64
|
||||
# These files needing to be updated manually
|
||||
# or find a way to kill the access from memory
|
||||
if curfile in {'unrar.dll', 'unrar64.dll'}:
|
||||
try:
|
||||
os.chmod(new_path, stat.S_IWRITE)
|
||||
os.remove(new_path)
|
||||
os.renames(old_path, new_path)
|
||||
except Exception as error:
|
||||
log.debug(f'Unable to update {new_path}: {error}')
|
||||
# Trash the updated file without moving in new path
|
||||
os.remove(old_path)
|
||||
continue
|
||||
if os.path.isfile(new_path):
|
||||
os.remove(new_path)
|
||||
os.renames(old_path, new_path)
|
||||
# update version.txt with commit hash
|
||||
try:
|
||||
with open(version_path, 'w', encoding='utf-8') as ver_file:
|
||||
ver_file.write(self._newest_commit_hash)
|
||||
except OSError as error:
|
||||
log.error(f'Unable to write version file, update not complete: {error}')
|
||||
return False
|
||||
except Exception as error:
|
||||
log.error(f'Error while trying to update: {error}')
|
||||
log.debug(f'Traceback: {traceback.format_exc()}')
|
||||
return False
|
||||
return True
|
|
@ -1,7 +1,7 @@
|
|||
import sys
|
||||
|
||||
import nzbToMedia
|
||||
from nzb2media.app import main
|
||||
|
||||
SECTION = 'CouchPotato'
|
||||
result = nzbToMedia.main(sys.argv, SECTION)
|
||||
sys.exit(result)
|
||||
if __name__ == '__main__':
|
||||
section = 'CouchPotato'
|
||||
sys.exit(main(section=section))
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import sys
|
||||
|
||||
import nzbToMedia
|
||||
from nzb2media.app import main
|
||||
|
||||
SECTION = 'Gamez'
|
||||
result = nzbToMedia.main(sys.argv, SECTION)
|
||||
sys.exit(result)
|
||||
if __name__ == '__main__':
|
||||
section = 'Gamez'
|
||||
sys.exit(main(section=section))
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import sys
|
||||
|
||||
import nzbToMedia
|
||||
from nzb2media.app import main
|
||||
|
||||
SECTION = 'HeadPhones'
|
||||
result = nzbToMedia.main(sys.argv, SECTION)
|
||||
sys.exit(result)
|
||||
if __name__ == '__main__':
|
||||
section = 'HeadPhones'
|
||||
sys.exit(main(section=section))
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import sys
|
||||
|
||||
import nzbToMedia
|
||||
from nzb2media.app import main
|
||||
|
||||
SECTION = 'LazyLibrarian'
|
||||
result = nzbToMedia.main(sys.argv, SECTION)
|
||||
sys.exit(result)
|
||||
if __name__ == '__main__':
|
||||
section = 'LazyLibrarian'
|
||||
sys.exit(main(section=section))
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import sys
|
||||
|
||||
import nzbToMedia
|
||||
from nzb2media.app import main
|
||||
|
||||
SECTION = 'Lidarr'
|
||||
result = nzbToMedia.main(sys.argv, SECTION)
|
||||
sys.exit(result)
|
||||
if __name__ == '__main__':
|
||||
section = 'Lidarr'
|
||||
sys.exit(main(section=section))
|
||||
|
|
|
@ -1,68 +1,7 @@
|
|||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
import nzb2media
|
||||
from nzb2media.processor import nzbget, sab, manual
|
||||
from nzb2media.processor.nzb import process
|
||||
from nzb2media.auto_process.common import ProcessResult
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
def main(args, section=None):
|
||||
# Initialize the config
|
||||
nzb2media.initialize(section)
|
||||
|
||||
log.info('#########################################################')
|
||||
log.info(f'## ..::[{os.path.basename(__file__)}]::.. ##')
|
||||
log.info('#########################################################')
|
||||
|
||||
# debug command line options
|
||||
log.debug(f'Options passed into nzbToMedia: {args}')
|
||||
|
||||
# Post-Processing Result
|
||||
result = ProcessResult(
|
||||
message='',
|
||||
status_code=0,
|
||||
)
|
||||
|
||||
# NZBGet
|
||||
if 'NZBOP_SCRIPTDIR' in os.environ:
|
||||
result = nzbget.process()
|
||||
# SABnzbd
|
||||
elif 'SAB_SCRIPT' in os.environ:
|
||||
result = sab.process_script()
|
||||
# SABnzbd Pre 0.7.17
|
||||
elif len(args) >= sab.MINIMUM_ARGUMENTS:
|
||||
result = sab.process(args)
|
||||
# Generic program
|
||||
elif len(args) > 5 and args[5] == 'generic':
|
||||
log.info('Script triggered from generic program')
|
||||
result = process(args[1], input_name=args[2], input_category=args[3], download_id=args[4])
|
||||
elif nzb2media.NZB_NO_MANUAL:
|
||||
log.warning('Invalid number of arguments received from client, and no_manual set')
|
||||
else:
|
||||
manual.process()
|
||||
|
||||
if not result.status_code:
|
||||
log.info(f'The {args[0]} script completed successfully.')
|
||||
if result.message:
|
||||
print(result.message + '!')
|
||||
if 'NZBOP_SCRIPTDIR' in os.environ: # return code for nzbget v11
|
||||
del nzb2media.MYAPP
|
||||
return nzb2media.NZBGET_POSTPROCESS_SUCCESS
|
||||
else:
|
||||
log.error(f'A problem was reported in the {args[0]} script.')
|
||||
if result.message:
|
||||
print(result.message + '!')
|
||||
if 'NZBOP_SCRIPTDIR' in os.environ: # return code for nzbget v11
|
||||
del nzb2media.MYAPP
|
||||
return nzb2media.NZBGET_POSTPROCESS_ERROR
|
||||
del nzb2media.MYAPP
|
||||
return result.status_code
|
||||
|
||||
from nzb2media.app import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv))
|
||||
section = ''
|
||||
sys.exit(main(section=section))
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import sys
|
||||
|
||||
import nzbToMedia
|
||||
from nzb2media.app import main
|
||||
|
||||
SECTION = 'Mylar'
|
||||
result = nzbToMedia.main(sys.argv, SECTION)
|
||||
sys.exit(result)
|
||||
if __name__ == '__main__':
|
||||
section = 'Mylar'
|
||||
sys.exit(main(section=section))
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import sys
|
||||
|
||||
import nzbToMedia
|
||||
from nzb2media.app import main
|
||||
|
||||
SECTION = 'NzbDrone'
|
||||
result = nzbToMedia.main(sys.argv, SECTION)
|
||||
sys.exit(result)
|
||||
if __name__ == '__main__':
|
||||
section = 'NzbDrone'
|
||||
sys.exit(main(section=section))
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import sys
|
||||
|
||||
import nzbToMedia
|
||||
from nzb2media.app import main
|
||||
|
||||
SECTION = 'Radarr'
|
||||
result = nzbToMedia.main(sys.argv, SECTION)
|
||||
sys.exit(result)
|
||||
if __name__ == '__main__':
|
||||
section = 'Radarr'
|
||||
sys.exit(main(section=section))
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import sys
|
||||
|
||||
import nzbToMedia
|
||||
from nzb2media.app import main
|
||||
|
||||
SECTION = 'SiCKRAGE'
|
||||
result = nzbToMedia.main(sys.argv, SECTION)
|
||||
sys.exit(result)
|
||||
if __name__ == '__main__':
|
||||
section = 'SiCKRAGE'
|
||||
sys.exit(main(section=section))
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import sys
|
||||
|
||||
import nzbToMedia
|
||||
from nzb2media.app import main
|
||||
|
||||
SECTION = 'SickBeard'
|
||||
result = nzbToMedia.main(sys.argv, SECTION)
|
||||
sys.exit(result)
|
||||
if __name__ == '__main__':
|
||||
section = 'SickBeard'
|
||||
sys.exit(main(section=section))
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import sys
|
||||
|
||||
import nzbToMedia
|
||||
from nzb2media.app import main
|
||||
|
||||
SECTION = 'Watcher3'
|
||||
result = nzbToMedia.main(sys.argv, SECTION)
|
||||
sys.exit(result)
|
||||
if __name__ == '__main__':
|
||||
section = 'Watcher3'
|
||||
sys.exit(main(section=section))
|
||||
|
|
|
@ -14,9 +14,28 @@ authors = [
|
|||
{name = "Clinton Hall", email="fock_wulf@hotmail.com"}
|
||||
]
|
||||
dependencies =[
|
||||
'pywin32;platform_system=="Windows"'
|
||||
'babelfish',
|
||||
'beets',
|
||||
'configobj',
|
||||
'deluge-client@git+https://github.com/labrys/deluge.git@master',
|
||||
'guessit',
|
||||
'jaraco-windows ; sys.platform == "win32"',
|
||||
'linktastic',
|
||||
'mediafile',
|
||||
'python-qbittorrent',
|
||||
'pywin32 ; sys.platform == "win32"',
|
||||
'pyxdg',
|
||||
'rencode',
|
||||
'requests',
|
||||
'requests_oauthlib',
|
||||
'setuptools',
|
||||
'setuptools-scm',
|
||||
'subliminal != 2.1.0',
|
||||
'syno@git+https://github.com/labrys/syno.git@master',
|
||||
'transmission-rpc@git+https://github.com/labrys/transmission-rpc@master',
|
||||
'utorrent@git+https://github.com/labrys/utorrent.git@master',
|
||||
]
|
||||
requires-python = ">=3.7"
|
||||
requires-python = ">=3.8"
|
||||
classifiers =[
|
||||
# complete classifier list: http://pypi.python.org/pypi?%3Aaction=list_classifiers
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
|
@ -30,7 +49,6 @@ classifiers =[
|
|||
"Operating System :: Unix",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
|
|
|
@ -13,8 +13,7 @@ rencode
|
|||
requests
|
||||
requests_oauthlib
|
||||
setuptools
|
||||
six
|
||||
subliminal != 2.1.0
|
||||
syno@git+https://github.com/labrys/syno.git@master
|
||||
transmissionrpc
|
||||
transmission-rpc
|
||||
utorrent@git+https://github.com/labrys/utorrent.git@master
|
||||
|
|
|
@ -44,19 +44,13 @@ def test_import_nzb():
|
|||
import nzb2media.nzb
|
||||
assert nzb2media.nzb
|
||||
|
||||
import nzb2media.nzb.configuration
|
||||
assert nzb2media.nzb.configuration
|
||||
|
||||
|
||||
def test_import_plugins():
|
||||
import nzb2media.plugins
|
||||
assert nzb2media.plugins
|
||||
import nzb2media.plex
|
||||
assert nzb2media.plex
|
||||
|
||||
import nzb2media.plugins.plex
|
||||
assert nzb2media.plugins.plex
|
||||
|
||||
import nzb2media.plugins.subtitles
|
||||
assert nzb2media.plugins.subtitles
|
||||
import nzb2media.subtitles
|
||||
assert nzb2media.subtitles
|
||||
|
||||
|
||||
def test_import_processor():
|
||||
|
@ -80,23 +74,20 @@ def test_import_torrent():
|
|||
import nzb2media.torrent
|
||||
assert nzb2media.torrent
|
||||
|
||||
import nzb2media.torrent.configuration
|
||||
assert nzb2media.torrent.configuration
|
||||
import nzb2media.deluge
|
||||
assert nzb2media.deluge
|
||||
|
||||
import nzb2media.torrent.deluge
|
||||
assert nzb2media.torrent.deluge
|
||||
import nzb2media.qbittorrent
|
||||
assert nzb2media.qbittorrent
|
||||
|
||||
import nzb2media.torrent.qbittorrent
|
||||
assert nzb2media.torrent.qbittorrent
|
||||
import nzb2media.synology
|
||||
assert nzb2media.synology
|
||||
|
||||
import nzb2media.torrent.synology
|
||||
assert nzb2media.torrent.synology
|
||||
import nzb2media.transmission
|
||||
assert nzb2media.transmission
|
||||
|
||||
import nzb2media.torrent.transmission
|
||||
assert nzb2media.torrent.transmission
|
||||
|
||||
import nzb2media.torrent.utorrent
|
||||
assert nzb2media.torrent.utorrent
|
||||
import nzb2media.utorrent
|
||||
assert nzb2media.utorrent
|
||||
|
||||
|
||||
def test_import_utils():
|
||||
|
@ -127,21 +118,12 @@ def test_import_utils():
|
|||
import nzb2media.utils.network
|
||||
assert nzb2media.utils.network
|
||||
|
||||
import nzb2media.utils.nzb
|
||||
assert nzb2media.utils.nzb
|
||||
|
||||
import nzb2media.utils.parsers
|
||||
assert nzb2media.utils.parsers
|
||||
|
||||
import nzb2media.utils.paths
|
||||
assert nzb2media.utils.paths
|
||||
|
||||
import nzb2media.utils.processes
|
||||
assert nzb2media.utils.processes
|
||||
|
||||
import nzb2media.utils.torrent
|
||||
assert nzb2media.utils.torrent
|
||||
|
||||
|
||||
def test_import_nzb2media():
|
||||
import nzb2media
|
||||
|
@ -153,9 +135,6 @@ def test_import_nzb2media():
|
|||
import nzb2media.databases
|
||||
assert nzb2media.databases
|
||||
|
||||
import nzb2media.github_api
|
||||
assert nzb2media.github_api
|
||||
|
||||
import nzb2media.main_db
|
||||
assert nzb2media.main_db
|
||||
|
||||
|
@ -167,6 +146,3 @@ def test_import_nzb2media():
|
|||
|
||||
import nzb2media.user_scripts
|
||||
assert nzb2media.user_scripts
|
||||
|
||||
import nzb2media.version_check
|
||||
assert nzb2media.version_check
|
||||
|
|
|
@ -6,7 +6,6 @@ import nzb2media
|
|||
|
||||
def test_initial():
|
||||
nzb2media.initialize()
|
||||
del nzb2media.MYAPP
|
||||
|
||||
|
||||
def test_core_parameters():
|
||||
|
|
8
tox.ini
8
tox.ini
|
@ -4,12 +4,11 @@
|
|||
envlist =
|
||||
clean,
|
||||
check,
|
||||
{py37, py38, py39, py310, py311},
|
||||
{py38, py39, py310, py311},
|
||||
report
|
||||
|
||||
[testenv]
|
||||
basepython =
|
||||
py37: {env:TOXPYTHON:python3.7}
|
||||
py38: {env:TOXPYTHON:python3.8}
|
||||
py39: {env:TOXPYTHON:python3.9}
|
||||
py310: {env:TOXPYTHON:python3.10}
|
||||
|
@ -20,22 +19,21 @@ setenv =
|
|||
PYTHONUNBUFFERED=yes
|
||||
passenv =
|
||||
*
|
||||
usedevelop = false
|
||||
skip_install = true
|
||||
deps =
|
||||
pytest
|
||||
pytest-cov
|
||||
-rrequirements.txt
|
||||
commands =
|
||||
{posargs:pytest -vvv -rA --cov --cov-report=term-missing --cov-branch tests}
|
||||
|
||||
[testenv:check]
|
||||
deps =
|
||||
pre-commit
|
||||
mypy
|
||||
skip_install = true
|
||||
commands =
|
||||
pre-commit autoupdate
|
||||
pre-commit run --all-files
|
||||
mypy --install-types --non-interactive --ignore-missing-imports nzb2media
|
||||
|
||||
[coverage:run]
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue