Merge branch 'nightly' into bluray-1

This commit is contained in:
clinton-hall 2019-05-31 12:43:39 +12:00
commit b02d6ef419
71 changed files with 957 additions and 311 deletions

View file

@ -1,16 +1,22 @@
#!/usr/bin/env python #!/usr/bin/env python
# coding=utf-8 # coding=utf-8
import eol from __future__ import (
eol.check() absolute_import,
division,
import cleanup print_function,
cleanup.clean(cleanup.FOLDER_STRUCTURE) unicode_literals,
)
import datetime import datetime
import os import os
import sys import sys
import eol
import cleanup
eol.check()
cleanup.clean(cleanup.FOLDER_STRUCTURE)
import core import core
from core import logger, main_db from core import logger, main_db
from core.auto_process import comics, games, movies, music, tv, books from core.auto_process import comics, games, movies, music, tv, books
@ -18,7 +24,11 @@ from core.auto_process.common import ProcessResult
from core.plugins.plex import plex_update from core.plugins.plex import plex_update
from core.user_scripts import external_script from core.user_scripts import external_script
from core.utils import char_replace, convert_to_ascii, replace_links from core.utils import char_replace, convert_to_ascii, replace_links
from six import text_type
try:
text_type = unicode
except NameError:
text_type = str
def process_torrent(input_directory, input_name, input_category, input_hash, input_id, client_agent): def process_torrent(input_directory, input_name, input_category, input_hash, input_id, client_agent):
@ -60,13 +70,13 @@ def process_torrent(input_directory, input_name, input_category, input_hash, inp
input_category = 'UNCAT' input_category = 'UNCAT'
usercat = input_category usercat = input_category
#try: # try:
# input_name = input_name.encode(core.SYS_ENCODING) # input_name = input_name.encode(core.SYS_ENCODING)
#except UnicodeError: # except UnicodeError:
# pass # pass
#try: # try:
# input_directory = input_directory.encode(core.SYS_ENCODING) # input_directory = input_directory.encode(core.SYS_ENCODING)
#except UnicodeError: # except UnicodeError:
# pass # pass
logger.debug('Determined Directory: {0} | Name: {1} | Category: {2}'.format logger.debug('Determined Directory: {0} | Name: {1} | Category: {2}'.format
@ -125,9 +135,9 @@ def process_torrent(input_directory, input_name, input_category, input_hash, inp
else: else:
output_destination = os.path.normpath( output_destination = os.path.normpath(
core.os.path.join(core.OUTPUT_DIRECTORY, input_category)) core.os.path.join(core.OUTPUT_DIRECTORY, input_category))
#try: # try:
# output_destination = output_destination.encode(core.SYS_ENCODING) # output_destination = output_destination.encode(core.SYS_ENCODING)
#except UnicodeError: # except UnicodeError:
# pass # pass
if output_destination in input_directory: if output_destination in input_directory:
@ -170,9 +180,9 @@ def process_torrent(input_directory, input_name, input_category, input_hash, inp
core.os.path.join(output_destination, os.path.basename(file_path)), full_file_name) core.os.path.join(output_destination, os.path.basename(file_path)), full_file_name)
logger.debug('Setting outputDestination to {0} to preserve folder structure'.format logger.debug('Setting outputDestination to {0} to preserve folder structure'.format
(os.path.dirname(target_file))) (os.path.dirname(target_file)))
#try: # try:
# target_file = target_file.encode(core.SYS_ENCODING) # target_file = target_file.encode(core.SYS_ENCODING)
#except UnicodeError: # except UnicodeError:
# pass # pass
if root == 1: if root == 1:
if not found_file: if not found_file:
@ -259,7 +269,6 @@ def process_torrent(input_directory, input_name, input_category, input_hash, inp
elif section_name == 'LazyLibrarian': elif section_name == 'LazyLibrarian':
result = books.process(section_name, output_destination, input_name, status, client_agent, input_category) result = books.process(section_name, output_destination, input_name, status, client_agent, input_category)
plex_update(input_category) plex_update(input_category)
if result.status_code != 0: if result.status_code != 0:
@ -279,7 +288,7 @@ def process_torrent(input_directory, input_name, input_category, input_hash, inp
# remove torrent # remove torrent
if core.USE_LINK == 'move-sym' and not core.DELETE_ORIGINAL == 1: if core.USE_LINK == 'move-sym' and not core.DELETE_ORIGINAL == 1:
logger.debug('Checking for sym-links to re-direct in: {0}'.format(input_directory)) logger.debug('Checking for sym-links to re-direct in: {0}'.format(input_directory))
for dirpath, dirs, files in os.walk(input_directory): for dirpath, _, files in os.walk(input_directory):
for file in files: for file in files:
logger.debug('Checking symlink: {0}'.format(os.path.join(dirpath, file))) logger.debug('Checking symlink: {0}'.format(os.path.join(dirpath, file)))
replace_links(os.path.join(dirpath, file)) replace_links(os.path.join(dirpath, file))
@ -353,14 +362,14 @@ def main(args):
if client_agent.lower() not in core.TORRENT_CLIENTS: if client_agent.lower() not in core.TORRENT_CLIENTS:
continue continue
#try: # try:
# dir_name = dir_name.encode(core.SYS_ENCODING) # dir_name = dir_name.encode(core.SYS_ENCODING)
#except UnicodeError: # except UnicodeError:
# pass # pass
input_name = os.path.basename(dir_name) input_name = os.path.basename(dir_name)
#try: # try:
# input_name = input_name.encode(core.SYS_ENCODING) # input_name = input_name.encode(core.SYS_ENCODING)
#except UnicodeError: # except UnicodeError:
# pass # pass
results = process_torrent(dir_name, input_name, subsection, input_hash or None, input_id or None, results = process_torrent(dir_name, input_name, subsection, input_hash or None, input_id or None,

View file

@ -1,6 +1,11 @@
#!/usr/bin/env python #!/usr/bin/env python
from __future__ import print_function from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import os import os
import subprocess import subprocess
@ -25,6 +30,7 @@ FOLDER_STRUCTURE = {
class WorkingDirectory(object): class WorkingDirectory(object):
"""Context manager for changing current working directory.""" """Context manager for changing current working directory."""
def __init__(self, new, original=None): def __init__(self, new, original=None):
self.working_directory = new self.working_directory = new
self.original_directory = os.getcwd() if original is None else original self.original_directory = os.getcwd() if original is None else original
@ -43,7 +49,7 @@ class WorkingDirectory(object):
original_directory=self.original_directory, original_directory=self.original_directory,
error=error, error=error,
working_directory=self.working_directory, working_directory=self.working_directory,
) ),
) )

View file

@ -1,6 +1,11 @@
# coding=utf-8 # coding=utf-8
from __future__ import print_function from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import itertools import itertools
import locale import locale
@ -108,9 +113,9 @@ FORKS = {
FORK_SICKCHILL: {'proc_dir': None, 'failed': None, 'process_method': None, 'force': None, 'delete_on': None, 'force_next': None}, FORK_SICKCHILL: {'proc_dir': None, 'failed': None, 'process_method': None, 'force': None, 'delete_on': None, 'force_next': None},
FORK_SICKBEARD_API: {'path': None, 'failed': None, 'process_method': None, 'force_replace': None, 'return_data': None, 'type': None, 'delete': None, 'force_next': None}, FORK_SICKBEARD_API: {'path': None, 'failed': None, 'process_method': None, 'force_replace': None, 'return_data': None, 'type': None, 'delete': None, 'force_next': None},
FORK_MEDUSA: {'proc_dir': None, 'failed': None, 'process_method': None, 'force': None, 'delete_on': None, 'ignore_subs': None}, 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}, FORK_MEDUSA_API: {'path': None, 'failed': None, 'process_method': None, 'force_replace': None, 'return_data': None, 'type': None, 'delete_files': None, 'is_priority': None},
FORK_SICKGEAR: {'dir': None, 'failed': None, 'process_method': None, 'force': None}, FORK_SICKGEAR: {'dir': None, 'failed': None, 'process_method': None, 'force': None},
FORK_STHENO: {"proc_dir": None, "failed": None, "process_method": None, "force": None, "delete_on": None, "ignore_subs": None} FORK_STHENO: {'proc_dir': None, 'failed': None, 'process_method': None, 'force': None, 'delete_on': None, 'ignore_subs': None},
} }
ALL_FORKS = {k: None for k in set(list(itertools.chain.from_iterable([FORKS[x].keys() for x in FORKS.keys()])))} ALL_FORKS = {k: None for k in set(list(itertools.chain.from_iterable([FORKS[x].keys() for x in FORKS.keys()])))}
@ -656,7 +661,7 @@ def configure_transcoder():
codec_alias = { codec_alias = {
'libx264': ['libx264', 'h264', 'h.264', 'AVC', 'MPEG-4'], 'libx264': ['libx264', 'h264', 'h.264', 'AVC', 'MPEG-4'],
'libmp3lame': ['libmp3lame', 'mp3'], 'libmp3lame': ['libmp3lame', 'mp3'],
'libfaac': ['libfaac', 'aac', 'faac'] 'libfaac': ['libfaac', 'aac', 'faac'],
} }
transcode_defaults = { transcode_defaults = {
'iPad': { 'iPad': {
@ -665,7 +670,7 @@ def configure_transcoder():
'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': None, 'ACHANNELS': 2, 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': None, 'ACHANNELS': 2,
'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6, 'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6,
'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None,
'SCODEC': 'mov_text' 'SCODEC': 'mov_text',
}, },
'iPad-1080p': { 'iPad-1080p': {
'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None, 'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None,
@ -673,7 +678,7 @@ def configure_transcoder():
'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': None, 'ACHANNELS': 2, 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': None, 'ACHANNELS': 2,
'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6, 'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6,
'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None,
'SCODEC': 'mov_text' 'SCODEC': 'mov_text',
}, },
'iPad-720p': { 'iPad-720p': {
'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None, 'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None,
@ -681,7 +686,7 @@ def configure_transcoder():
'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': None, 'ACHANNELS': 2, 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': None, 'ACHANNELS': 2,
'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6, 'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6,
'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None,
'SCODEC': 'mov_text' 'SCODEC': 'mov_text',
}, },
'Apple-TV': { 'Apple-TV': {
'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None, 'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None,
@ -689,7 +694,7 @@ def configure_transcoder():
'ACODEC': 'ac3', 'ACODEC_ALLOW': ['ac3'], 'ABITRATE': None, 'ACHANNELS': 6, 'ACODEC': 'ac3', 'ACODEC_ALLOW': ['ac3'], 'ABITRATE': None, 'ACHANNELS': 6,
'ACODEC2': 'aac', 'ACODEC2_ALLOW': ['libfaac'], 'ABITRATE2': None, 'ACHANNELS2': 2, 'ACODEC2': 'aac', 'ACODEC2_ALLOW': ['libfaac'], 'ABITRATE2': None, 'ACHANNELS2': 2,
'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None,
'SCODEC': 'mov_text' 'SCODEC': 'mov_text',
}, },
'iPod': { 'iPod': {
'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None, 'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None,
@ -697,7 +702,7 @@ def configure_transcoder():
'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2, 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2,
'ACODEC2': None, 'ACODEC2_ALLOW': [], 'ABITRATE2': None, 'ACHANNELS2': None, 'ACODEC2': None, 'ACODEC2_ALLOW': [], 'ABITRATE2': None, 'ACHANNELS2': None,
'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None,
'SCODEC': 'mov_text' 'SCODEC': 'mov_text',
}, },
'iPhone': { 'iPhone': {
'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None, 'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None,
@ -705,7 +710,7 @@ def configure_transcoder():
'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2, 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2,
'ACODEC2': None, 'ACODEC2_ALLOW': [], 'ABITRATE2': None, 'ACHANNELS2': None, 'ACODEC2': None, 'ACODEC2_ALLOW': [], 'ABITRATE2': None, 'ACHANNELS2': None,
'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None,
'SCODEC': 'mov_text' 'SCODEC': 'mov_text',
}, },
'PS3': { 'PS3': {
'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None, 'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None,
@ -713,7 +718,7 @@ def configure_transcoder():
'ACODEC': 'ac3', 'ACODEC_ALLOW': ['ac3'], 'ABITRATE': None, 'ACHANNELS': 6, 'ACODEC': 'ac3', 'ACODEC_ALLOW': ['ac3'], 'ABITRATE': None, 'ACHANNELS': 6,
'ACODEC2': 'aac', 'ACODEC2_ALLOW': ['libfaac'], 'ABITRATE2': None, 'ACHANNELS2': 2, 'ACODEC2': 'aac', 'ACODEC2_ALLOW': ['libfaac'], 'ABITRATE2': None, 'ACHANNELS2': 2,
'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None,
'SCODEC': 'mov_text' 'SCODEC': 'mov_text',
}, },
'xbox': { 'xbox': {
'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None, 'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None,
@ -721,7 +726,7 @@ def configure_transcoder():
'ACODEC': 'ac3', 'ACODEC_ALLOW': ['ac3'], 'ABITRATE': None, 'ACHANNELS': 6, 'ACODEC': 'ac3', 'ACODEC_ALLOW': ['ac3'], 'ABITRATE': None, 'ACHANNELS': 6,
'ACODEC2': None, 'ACODEC2_ALLOW': [], 'ABITRATE2': None, 'ACHANNELS2': None, 'ACODEC2': None, 'ACODEC2_ALLOW': [], 'ABITRATE2': None, 'ACHANNELS2': None,
'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None,
'SCODEC': 'mov_text' 'SCODEC': 'mov_text',
}, },
'Roku-480p': { 'Roku-480p': {
'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None, 'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None,
@ -729,7 +734,7 @@ def configure_transcoder():
'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2, 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2,
'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6, 'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6,
'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None,
'SCODEC': 'mov_text' 'SCODEC': 'mov_text',
}, },
'Roku-720p': { 'Roku-720p': {
'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None, 'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None,
@ -737,7 +742,7 @@ def configure_transcoder():
'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2, 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2,
'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6, 'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6,
'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None,
'SCODEC': 'mov_text' 'SCODEC': 'mov_text',
}, },
'Roku-1080p': { 'Roku-1080p': {
'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None, 'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None,
@ -745,7 +750,7 @@ def configure_transcoder():
'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 160000, 'ACHANNELS': 2, 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 160000, 'ACHANNELS': 2,
'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6, 'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6,
'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None,
'SCODEC': 'mov_text' 'SCODEC': 'mov_text',
}, },
'mkv': { 'mkv': {
'VEXTENSION': '.mkv', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None, 'VEXTENSION': '.mkv', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': None, 'VLEVEL': None,
@ -761,7 +766,7 @@ def configure_transcoder():
'ACODEC': 'dts', 'ACODEC_ALLOW': ['libfaac', 'dts', 'ac3', 'mp2', 'mp3'], 'ABITRATE': None, 'ACHANNELS': 8, 'ACODEC': 'dts', 'ACODEC_ALLOW': ['libfaac', 'dts', 'ac3', 'mp2', 'mp3'], 'ABITRATE': None, 'ACHANNELS': 8,
'ACODEC2': None, 'ACODEC2_ALLOW': [], 'ABITRATE2': None, 'ACHANNELS2': None, 'ACODEC2': None, 'ACODEC2_ALLOW': [], 'ABITRATE2': None, 'ACHANNELS2': None,
'ACODEC3': 'ac3', 'ACODEC3_ALLOW': ['libfaac', 'dts', 'ac3', 'mp2', 'mp3'], 'ABITRATE3': None, 'ACHANNELS3': 8, 'ACODEC3': 'ac3', 'ACODEC3_ALLOW': ['libfaac', 'dts', 'ac3', 'mp2', 'mp3'], 'ABITRATE3': None, 'ACHANNELS3': 8,
'SCODEC': 'mov_text' 'SCODEC': 'mov_text',
}, },
'mp4-scene-release': { 'mp4-scene-release': {
'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': 19, 'VLEVEL': '3.1', 'VEXTENSION': '.mp4', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': None, 'VCRF': 19, 'VLEVEL': '3.1',
@ -769,7 +774,7 @@ def configure_transcoder():
'ACODEC': 'dts', 'ACODEC_ALLOW': ['libfaac', 'dts', 'ac3', 'mp2', 'mp3'], 'ABITRATE': None, 'ACHANNELS': 8, 'ACODEC': 'dts', 'ACODEC_ALLOW': ['libfaac', 'dts', 'ac3', 'mp2', 'mp3'], 'ABITRATE': None, 'ACHANNELS': 8,
'ACODEC2': None, 'ACODEC2_ALLOW': [], 'ABITRATE2': None, 'ACHANNELS2': None, 'ACODEC2': None, 'ACODEC2_ALLOW': [], 'ABITRATE2': None, 'ACHANNELS2': None,
'ACODEC3': 'ac3', 'ACODEC3_ALLOW': ['libfaac', 'dts', 'ac3', 'mp2', 'mp3'], 'ABITRATE3': None, 'ACHANNELS3': 8, 'ACODEC3': 'ac3', 'ACODEC3_ALLOW': ['libfaac', 'dts', 'ac3', 'mp2', 'mp3'], 'ABITRATE3': None, 'ACHANNELS3': 8,
'SCODEC': 'mov_text' 'SCODEC': 'mov_text',
}, },
'MKV-SD': { 'MKV-SD': {
'VEXTENSION': '.mkv', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': '1200k', 'VCRF': None, 'VLEVEL': None, 'VEXTENSION': '.mkv', 'VCODEC': 'libx264', 'VPRESET': None, 'VFRAMERATE': None, 'VBITRATE': '1200k', 'VCRF': None, 'VLEVEL': None,
@ -777,8 +782,8 @@ def configure_transcoder():
'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2, 'ACODEC': 'aac', 'ACODEC_ALLOW': ['libfaac'], 'ABITRATE': 128000, 'ACHANNELS': 2,
'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6, 'ACODEC2': 'ac3', 'ACODEC2_ALLOW': ['ac3'], 'ABITRATE2': None, 'ACHANNELS2': 6,
'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None, 'ACODEC3': None, 'ACODEC3_ALLOW': [], 'ABITRATE3': None, 'ACHANNELS3': None,
'SCODEC': 'mov_text' 'SCODEC': 'mov_text',
} },
} }
if DEFAULTS and DEFAULTS in transcode_defaults: if DEFAULTS and DEFAULTS in transcode_defaults:
VEXTENSION = transcode_defaults[DEFAULTS]['VEXTENSION'] VEXTENSION = transcode_defaults[DEFAULTS]['VEXTENSION']
@ -881,7 +886,7 @@ def configure_utility_locations():
else: else:
if SYS_PATH: if SYS_PATH:
os.environ['PATH'] += ':'+SYS_PATH os.environ['PATH'] += ':' + SYS_PATH
try: try:
SEVENZIP = subprocess.Popen(['which', '7z'], stdout=subprocess.PIPE).communicate()[0].strip().decode() SEVENZIP = subprocess.Popen(['which', '7z'], stdout=subprocess.PIPE).communicate()[0].strip().decode()
except Exception: except Exception:
@ -968,7 +973,7 @@ def check_python():
major=sys.version_info[0], major=sys.version_info[0],
minor=sys.version_info[1], minor=sys.version_info[1],
x=days_left, x=days_left,
) ),
) )
if days_left <= grace_period: if days_left <= grace_period:
logger.warning('Please upgrade to a more recent Python version.') logger.warning('Please upgrade to a more recent Python version.')

View file

@ -1,14 +1,23 @@
# coding=utf-8 # coding=utf-8
import os from __future__ import (
import shutil absolute_import,
division,
print_function,
unicode_literals,
)
import requests import requests
import core import core
from core import logger from core import logger
from core.auto_process.common import ProcessResult from core.auto_process.common import ProcessResult
from core.utils import convert_to_ascii, server_responding from core.utils import (
convert_to_ascii,
remote_dir,
server_responding,
)
requests.packages.urllib3.disable_warnings() requests.packages.urllib3.disable_warnings()
@ -24,6 +33,7 @@ def process(section, dir_name, input_name=None, status=0, client_agent='manual',
ssl = int(cfg.get('ssl', 0)) ssl = int(cfg.get('ssl', 0))
web_root = cfg.get('web_root', '') web_root = cfg.get('web_root', '')
protocol = 'https://' if ssl else 'http://' protocol = 'https://' if ssl else 'http://'
remote_path = int(cfg.get('remote_path', 0))
url = '{0}{1}:{2}{3}/api'.format(protocol, host, port, web_root) url = '{0}{1}:{2}{3}/api'.format(protocol, host, port, web_root)
if not server_responding(url): if not server_responding(url):
@ -38,7 +48,7 @@ def process(section, dir_name, input_name=None, status=0, client_agent='manual',
params = { params = {
'apikey': apikey, 'apikey': apikey,
'cmd': 'forceProcess', 'cmd': 'forceProcess',
'dir': dir_name, 'dir': remote_dir(dir_name) if remote_path else dir_name,
} }
logger.debug('Opening URL: {0} with params: {1}'.format(url, params), section) logger.debug('Opening URL: {0} with params: {1}'.format(url, params), section)
@ -52,7 +62,7 @@ def process(section, dir_name, input_name=None, status=0, client_agent='manual',
) )
logger.postprocess('{0}'.format(r.text), section) logger.postprocess('{0}'.format(r.text), section)
if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]:
logger.error('Server returned status {0}'.format(r.status_code), section) logger.error('Server returned status {0}'.format(r.status_code), section)
return ProcessResult( return ProcessResult(

View file

@ -1,5 +1,12 @@
# coding=utf-8 # coding=utf-8
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import os import os
import requests import requests
@ -60,7 +67,7 @@ def process(section, dir_name, input_name=None, status=0, client_agent='manual',
logger.error('Unable to open URL', section) logger.error('Unable to open URL', section)
return ProcessResult( return ProcessResult(
message='{0}: Failed to post-process - Unable to connect to {0}'.format(section), message='{0}: Failed to post-process - Unable to connect to {0}'.format(section),
status_code=1 status_code=1,
) )
if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]: if r.status_code not in [requests.codes.ok, requests.codes.created, requests.codes.accepted]:
logger.error('Server returned status {0}'.format(r.status_code), section) logger.error('Server returned status {0}'.format(r.status_code), section)

View file

@ -1,3 +1,10 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import requests import requests
from core import logger from core import logger
@ -17,7 +24,7 @@ class ProcessResult(object):
def __str__(self): def __str__(self):
return 'Processing {0}: {1}'.format( return 'Processing {0}: {1}'.format(
'succeeded' if bool(self) else 'failed', 'succeeded' if bool(self) else 'failed',
self.message self.message,
) )
def __repr__(self): def __repr__(self):

View file

@ -1,5 +1,12 @@
# coding=utf-8 # coding=utf-8
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import os import os
import shutil import shutil
@ -46,7 +53,7 @@ def process(section, dir_name, input_name=None, status=0, client_agent='manual',
'api_key': apikey, 'api_key': apikey,
'mode': 'UPDATEREQUESTEDSTATUS', 'mode': 'UPDATEREQUESTEDSTATUS',
'db_id': gamez_id, 'db_id': gamez_id,
'status': download_status 'status': download_status,
} }
logger.debug('Opening URL: {0}'.format(url), section) logger.debug('Opening URL: {0}'.format(url), section)

View file

@ -1,5 +1,12 @@
# coding=utf-8 # coding=utf-8
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import json import json
import os import os
import time import time
@ -256,7 +263,7 @@ def process(section, dir_name, input_name=None, status=0, client_agent='manual',
return ProcessResult( return ProcessResult(
message='{0}: Sending failed download back to {0}'.format(section), message='{0}: Sending failed download back to {0}'.format(section),
status_code=1, # Return as failed to flag this in the downloader. status_code=1, # Return as failed to flag this in the downloader.
) # Return failed flag, but log the event as successful. ) # Return failed flag, but log the event as successful.
if delete_failed and os.path.isdir(dir_name) and not os.path.dirname(dir_name) == dir_name: if delete_failed and os.path.isdir(dir_name) and not os.path.dirname(dir_name) == dir_name:
logger.postprocess('Deleting failed files and folder {0}'.format(dir_name), section) logger.postprocess('Deleting failed files and folder {0}'.format(dir_name), section)
@ -373,22 +380,22 @@ def process(section, dir_name, input_name=None, status=0, client_agent='manual',
except Exception: except Exception:
pass pass
elif scan_id: elif scan_id:
url = '{0}/{1}'.format(base_url, scan_id) url = '{0}/{1}'.format(base_url, scan_id)
command_status = command_complete(url, params, headers, section) command_status = command_complete(url, params, headers, section)
if command_status: if command_status:
logger.debug('The Scan command return status: {0}'.format(command_status), section) logger.debug('The Scan command return status: {0}'.format(command_status), section)
if command_status in ['completed']: if command_status in ['completed']:
logger.debug('The Scan command has completed successfully. Renaming was successful.', section) logger.debug('The Scan command has completed successfully. Renaming was successful.', section)
return ProcessResult( return ProcessResult(
message='{0}: Successfully post-processed {1}'.format(section, input_name), message='{0}: Successfully post-processed {1}'.format(section, input_name),
status_code=0, status_code=0,
) )
elif command_status in ['failed']: elif command_status in ['failed']:
logger.debug('The Scan command has failed. Renaming was not successful.', section) logger.debug('The Scan command has failed. Renaming was not successful.', section)
# return ProcessResult( # return ProcessResult(
# message='{0}: Failed to post-process {1}'.format(section, input_name), # message='{0}: Failed to post-process {1}'.format(section, input_name),
# status_code=1, # status_code=1,
# ) # )
if not os.path.isdir(dir_name): if not os.path.isdir(dir_name):
logger.postprocess('SUCCESS: Input Directory [{0}] has been processed and removed'.format( logger.postprocess('SUCCESS: Input Directory [{0}] has been processed and removed'.format(
@ -504,7 +511,7 @@ def get_release(base_url, imdb_id=None, download_id=None, release_id=None):
# Narrow results by removing old releases by comparing their last_edit field # Narrow results by removing old releases by comparing their last_edit field
if len(results) > 1: if len(results) > 1:
for id1, x1 in results.items(): for id1, x1 in results.items():
for id2, x2 in results.items(): for x2 in results.values():
try: try:
if x2['last_edit'] > x1['last_edit']: if x2['last_edit'] > x1['last_edit']:
results.pop(id1) results.pop(id1)

View file

@ -1,5 +1,12 @@
# coding=utf-8 # coding=utf-8
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import json import json
import os import os
import time import time
@ -73,7 +80,7 @@ def process(section, dir_name, input_name=None, status=0, client_agent='manual',
params = { params = {
'apikey': apikey, 'apikey': apikey,
'cmd': 'forceProcess', 'cmd': 'forceProcess',
'dir': remote_dir(dir_name) if remote_path else dir_name 'dir': remote_dir(dir_name) if remote_path else dir_name,
} }
res = force_process(params, url, apikey, input_name, dir_name, section, wait_for) res = force_process(params, url, apikey, input_name, dir_name, section, wait_for)
@ -83,7 +90,7 @@ def process(section, dir_name, input_name=None, status=0, client_agent='manual',
params = { params = {
'apikey': apikey, 'apikey': apikey,
'cmd': 'forceProcess', 'cmd': 'forceProcess',
'dir': os.path.split(remote_dir(dir_name))[0] if remote_path else os.path.split(dir_name)[0] 'dir': os.path.split(remote_dir(dir_name))[0] if remote_path else os.path.split(dir_name)[0],
} }
res = force_process(params, url, apikey, input_name, dir_name, section, wait_for) res = force_process(params, url, apikey, input_name, dir_name, section, wait_for)
@ -187,7 +194,7 @@ def get_status(url, apikey, dir_name):
params = { params = {
'apikey': apikey, 'apikey': apikey,
'cmd': 'getHistory' 'cmd': 'getHistory',
} }
logger.debug('Opening URL: {0} with PARAMS: {1}'.format(url, params)) logger.debug('Opening URL: {0} with PARAMS: {1}'.format(url, params))

View file

@ -1,5 +1,12 @@
# coding=utf-8 # coding=utf-8
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import copy import copy
import errno import errno
import json import json
@ -266,7 +273,7 @@ def process(section, dir_name, input_name=None, failed=False, client_agent='manu
if apikey: if apikey:
url = '{0}{1}:{2}{3}/api/{4}/?cmd=postprocess'.format(protocol, host, port, web_root, apikey) url = '{0}{1}:{2}{3}/api/{4}/?cmd=postprocess'.format(protocol, host, port, web_root, apikey)
elif fork == 'Stheno': elif fork == 'Stheno':
url = "{0}{1}:{2}{3}/home/postprocess/process_episode".format(protocol, host, port, web_root) url = '{0}{1}:{2}{3}/home/postprocess/process_episode'.format(protocol, host, port, web_root)
else: else:
url = '{0}{1}:{2}{3}/home/postprocess/processEpisode'.format(protocol, host, port, web_root) url = '{0}{1}:{2}{3}/home/postprocess/processEpisode'.format(protocol, host, port, web_root)
elif section == 'NzbDrone': elif section == 'NzbDrone':

View file

@ -1,5 +1,12 @@
# coding=utf-8 # coding=utf-8
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import copy import copy
import os import os
import shutil import shutil
@ -13,17 +20,17 @@ from core import logger
class Section(configobj.Section, object): class Section(configobj.Section, object):
def isenabled(section): def isenabled(self):
# checks if subsection enabled, returns true/false if subsection specified otherwise returns true/false in {} # checks if subsection enabled, returns true/false if subsection specified otherwise returns true/false in {}
if not section.sections: if not self.sections:
try: try:
value = list(ConfigObj.find_key(section, 'enabled'))[0] value = list(ConfigObj.find_key(self, 'enabled'))[0]
except Exception: except Exception:
value = 0 value = 0
if int(value) == 1: if int(value) == 1:
return section return self
else: else:
to_return = copy.deepcopy(section) to_return = copy.deepcopy(self)
for section_name, subsections in to_return.items(): for section_name, subsections in to_return.items():
for subsection in subsections: for subsection in subsections:
try: try:
@ -40,8 +47,8 @@ class Section(configobj.Section, object):
return to_return return to_return
def findsection(section, key): def findsection(self, key):
to_return = copy.deepcopy(section) to_return = copy.deepcopy(self)
for subsection in to_return: for subsection in to_return:
try: try:
value = list(ConfigObj.find_key(to_return[subsection], key))[0] value = list(ConfigObj.find_key(to_return[subsection], key))[0]
@ -136,10 +143,10 @@ class ConfigObj(configobj.ConfigObj, Section):
subsections = {} subsections = {}
# gather all new-style and old-style sub-sections # gather all new-style and old-style sub-sections
for newsection, newitems in CFG_NEW.items(): for newsection in CFG_NEW:
if CFG_NEW[newsection].sections: if CFG_NEW[newsection].sections:
subsections.update({newsection: CFG_NEW[newsection].sections}) subsections.update({newsection: CFG_NEW[newsection].sections})
for section, items in CFG_OLD.items(): for section in CFG_OLD:
if CFG_OLD[section].sections: if CFG_OLD[section].sections:
subsections.update({section: CFG_OLD[section].sections}) subsections.update({section: CFG_OLD[section].sections})
for option, value in CFG_OLD[section].items(): for option, value in CFG_OLD[section].items():

View file

@ -1,5 +1,12 @@
# coding=utf-8 # coding=utf-8
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
from core import logger, main_db from core import logger, main_db
from core.utils import backup_versioned_file from core.utils import backup_versioned_file
@ -33,7 +40,7 @@ class InitialSchema(main_db.SchemaUpgrade):
queries = [ queries = [
'CREATE TABLE db_version (db_version INTEGER);', '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));', '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);' 'INSERT INTO db_version (db_version) VALUES (2);',
] ]
for query in queries: for query in queries:
self.connection.action(query) self.connection.action(query)
@ -59,7 +66,7 @@ class InitialSchema(main_db.SchemaUpgrade):
'INSERT INTO downloads2 SELECT * FROM downloads;', 'INSERT INTO downloads2 SELECT * FROM downloads;',
'DROP TABLE IF EXISTS downloads;', 'DROP TABLE IF EXISTS downloads;',
'ALTER TABLE downloads2 RENAME TO downloads;', 'ALTER TABLE downloads2 RENAME TO downloads;',
'INSERT INTO db_version (db_version) VALUES (2);' 'INSERT INTO db_version (db_version) VALUES (2);',
] ]
for query in queries: for query in queries:
self.connection.action(query) self.connection.action(query)

View file

@ -1,5 +1,12 @@
# coding=utf-8 # coding=utf-8
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import os import os
import platform import platform
import shutil import shutil

View file

@ -1,5 +1,12 @@
# coding=utf-8 # coding=utf-8
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import requests import requests
from six import iteritems from six import iteritems
@ -42,7 +49,8 @@ def auto_fork(section, input_category):
logger.info('Attempting to verify {category} fork'.format logger.info('Attempting to verify {category} fork'.format
(category=input_category)) (category=input_category))
url = '{protocol}{host}:{port}{root}/api/rootfolder'.format( url = '{protocol}{host}:{port}{root}/api/rootfolder'.format(
protocol=protocol, host=host, port=port, root=web_root) protocol=protocol, host=host, port=port, root=web_root,
)
headers = {'X-Api-Key': apikey} headers = {'X-Api-Key': apikey}
try: try:
r = requests.get(url, headers=headers, stream=True, verify=False) r = requests.get(url, headers=headers, stream=True, verify=False)
@ -65,10 +73,12 @@ def auto_fork(section, input_category):
if apikey: if apikey:
url = '{protocol}{host}:{port}{root}/api/{apikey}/?cmd=help&subject=postprocess'.format( url = '{protocol}{host}:{port}{root}/api/{apikey}/?cmd=help&subject=postprocess'.format(
protocol=protocol, host=host, port=port, root=web_root, apikey=apikey) protocol=protocol, host=host, port=port, root=web_root, apikey=apikey,
)
else: else:
url = '{protocol}{host}:{port}{root}/home/postprocess/'.format( url = '{protocol}{host}:{port}{root}/home/postprocess/'.format(
protocol=protocol, host=host, port=port, root=web_root) protocol=protocol, host=host, port=port, root=web_root,
)
# attempting to auto-detect fork # attempting to auto-detect fork
try: try:

View file

@ -1,12 +1,17 @@
# coding=utf-8 # coding=utf-8
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import requests import requests
class GitHub(object): class GitHub(object):
""" """Simple api wrapper for the Github API v3."""
Simple api wrapper for the Github API v3.
"""
def __init__(self, github_repo_user, github_repo, branch='master'): def __init__(self, github_repo_user, github_repo, branch='master'):
@ -15,16 +20,14 @@ class GitHub(object):
self.branch = branch self.branch = branch
def _access_api(self, path, params=None): def _access_api(self, path, params=None):
""" """Access API at given an API path and optional parameters."""
Access the API at the path given and with the optional params given.
"""
url = 'https://api.github.com/{path}'.format(path='/'.join(path)) url = 'https://api.github.com/{path}'.format(path='/'.join(path))
data = requests.get(url, params=params, verify=False) data = requests.get(url, params=params, verify=False)
return data.json() if data.ok else [] return data.json() if data.ok else []
def commits(self): def commits(self):
""" """
Uses the API to get a list of the 100 most recent commits from the specified user/repo/branch, starting from HEAD. 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 user: The github username of the person whose repo you're querying
repo: The repo name to query repo: The repo name to query
@ -39,7 +42,7 @@ class GitHub(object):
def compare(self, base, head, per_page=1): def compare(self, base, head, per_page=1):
""" """
Uses the API to get a list of compares between base and head. Get compares between base and head.
user: The github username of the person whose repo you're querying user: The github username of the person whose repo you're querying
repo: The repo name to query repo: The repo name to query

View file

@ -1,11 +1,19 @@
# coding=utf-8 # coding=utf-8
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import logging import logging
import os import os
import sys import sys
import threading import threading
import core import core
import functools
# number of log files to keep # number of log files to keep
NUM_LOGS = 3 NUM_LOGS = 3
@ -85,9 +93,9 @@ class NTMRotatingLogHandler(object):
console.setFormatter(DispatchingFormatter( console.setFormatter(DispatchingFormatter(
{'nzbtomedia': logging.Formatter('[%(asctime)s] [%(levelname)s]::%(message)s', '%H:%M:%S'), {'nzbtomedia': logging.Formatter('[%(asctime)s] [%(levelname)s]::%(message)s', '%H:%M:%S'),
'postprocess': logging.Formatter('[%(asctime)s] [%(levelname)s]::%(message)s', '%H:%M:%S'), 'postprocess': logging.Formatter('[%(asctime)s] [%(levelname)s]::%(message)s', '%H:%M:%S'),
'db': logging.Formatter('[%(asctime)s] [%(levelname)s]::%(message)s', '%H:%M:%S') 'db': logging.Formatter('[%(asctime)s] [%(levelname)s]::%(message)s', '%H:%M:%S'),
}, },
logging.Formatter('%(message)s'), )) logging.Formatter('%(message)s')))
# add the handler to the root logger # add the handler to the root logger
logging.getLogger('nzbtomedia').addHandler(console) logging.getLogger('nzbtomedia').addHandler(console)
@ -111,10 +119,7 @@ class NTMRotatingLogHandler(object):
self.close_log(old_handler) self.close_log(old_handler)
def _config_handler(self): def _config_handler(self):
""" """Configure a file handler to log at file_name and return it."""
Configure a file handler to log at file_name and return it.
"""
file_handler = logging.FileHandler(self.log_file_path, encoding='utf-8') file_handler = logging.FileHandler(self.log_file_path, encoding='utf-8')
file_handler.setLevel(DB) file_handler.setLevel(DB)
@ -122,29 +127,29 @@ class NTMRotatingLogHandler(object):
file_handler.setFormatter(DispatchingFormatter( file_handler.setFormatter(DispatchingFormatter(
{'nzbtomedia': logging.Formatter('%(asctime)s %(levelname)-8s::%(message)s', '%Y-%m-%d %H:%M:%S'), {'nzbtomedia': logging.Formatter('%(asctime)s %(levelname)-8s::%(message)s', '%Y-%m-%d %H:%M:%S'),
'postprocess': logging.Formatter('%(asctime)s %(levelname)-8s::%(message)s', '%Y-%m-%d %H:%M:%S'), 'postprocess': logging.Formatter('%(asctime)s %(levelname)-8s::%(message)s', '%Y-%m-%d %H:%M:%S'),
'db': logging.Formatter('%(asctime)s %(levelname)-8s::%(message)s', '%Y-%m-%d %H:%M:%S') 'db': logging.Formatter('%(asctime)s %(levelname)-8s::%(message)s', '%Y-%m-%d %H:%M:%S'),
}, },
logging.Formatter('%(message)s'), )) logging.Formatter('%(message)s')))
return file_handler return file_handler
def _log_file_name(self, i): def _log_file_name(self, i):
""" """
Returns a numbered log file name depending on i. If i==0 it just uses logName, if not it appends Return a numbered log file name depending on i.
it to the extension (blah.log.3 for i == 3)
If i==0 it just uses logName, if not it appends it to the extension
e.g. (blah.log.3 for i == 3)
i: Log number to ues i: Log number to ues
""" """
return self.log_file_path + ('.{0}'.format(i) if i else '') return self.log_file_path + ('.{0}'.format(i) if i else '')
def _num_logs(self): def _num_logs(self):
""" """
Scans the log folder and figures out how many log files there are already on disk Scan the log folder and figure out how many log files there are already on disk.
Returns: The number of the last used file (eg. mylog.log.3 would return 3). If there are no logs it returns -1 Returns: The number of the last used file (eg. mylog.log.3 would return 3). If there are no logs it returns -1
""" """
cur_log = 0 cur_log = 0
while os.path.isfile(self._log_file_name(cur_log)): while os.path.isfile(self._log_file_name(cur_log)):
cur_log += 1 cur_log += 1
@ -202,9 +207,8 @@ class NTMRotatingLogHandler(object):
ntm_logger = logging.getLogger('nzbtomedia') ntm_logger = logging.getLogger('nzbtomedia')
pp_logger = logging.getLogger('postprocess') pp_logger = logging.getLogger('postprocess')
db_logger = logging.getLogger('db') db_logger = logging.getLogger('db')
setattr(pp_logger, 'postprocess', lambda *args: pp_logger.log(POSTPROCESS, *args)) pp_logger.postprocess = functools.partial(pp_logger.log, POSTPROCESS)
setattr(db_logger, 'db', lambda *args: db_logger.log(DB, *args)) db_logger.db = functools.partial(db_logger.log, DB)
try: try:
if log_level == DEBUG: if log_level == DEBUG:
if core.LOG_DEBUG == 1: if core.LOG_DEBUG == 1:

View file

@ -1,19 +1,52 @@
# coding=utf-8 # coding=utf-8
from __future__ import print_function from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import re import re
import sqlite3 import sqlite3
import time import time
from six import text_type from six import text_type, PY2
import core import core
from core import logger from core import logger
if PY2:
class Row(sqlite3.Row, object):
"""
Row factory that uses Byte Strings for keys.
The sqlite3.Row in Python 2 does not support unicode keys.
This overrides __getitem__ to attempt to encode the key to bytes first.
"""
def __getitem__(self, item):
"""
Get an item from the row by index or key.
:param item: Index or Key of item to return.
:return: An item from the sqlite3.Row.
"""
try:
# sqlite3.Row column names should be Bytes in Python 2
item = item.encode()
except AttributeError:
pass # assume item is a numeric index
return super(Row, self).__getitem__(item)
else:
from sqlite3 import Row
def db_filename(filename='nzbtomedia.db', suffix=None): def db_filename(filename='nzbtomedia.db', suffix=None):
""" """
Return the correct location of the database file.
@param filename: The sqlite database filename to use. If not specified, @param filename: The sqlite database filename to use. If not specified,
will be made to be nzbtomedia.db will be made to be nzbtomedia.db
@param suffix: The suffix to append to the filename. A '.' will be added @param suffix: The suffix to append to the filename. A '.' will be added
@ -30,10 +63,7 @@ class DBConnection(object):
self.filename = filename self.filename = filename
self.connection = sqlite3.connect(db_filename(filename), 20) self.connection = sqlite3.connect(db_filename(filename), 20)
if row_type == 'dict': self.connection.row_factory = Row
self.connection.row_factory = self._dict_factory
else:
self.connection.row_factory = sqlite3.Row
def check_db_version(self): def check_db_version(self):
result = None result = None
@ -183,9 +213,9 @@ class DBConnection(object):
'WHERE {conditions}'.format( 'WHERE {conditions}'.format(
table=table_name, table=table_name,
params=', '.join(gen_params(value_dict)), params=', '.join(gen_params(value_dict)),
conditions=' AND '.join(gen_params(key_dict)) conditions=' AND '.join(gen_params(key_dict)),
), ),
items items,
) )
if self.connection.total_changes == changes_before: if self.connection.total_changes == changes_before:
@ -194,9 +224,9 @@ class DBConnection(object):
'VALUES ({values})'.format( 'VALUES ({values})'.format(
table=table_name, table=table_name,
columns=', '.join(map(text_type, value_dict.keys())), columns=', '.join(map(text_type, value_dict.keys())),
values=', '.join(['?'] * len(value_dict.values())) values=', '.join(['?'] * len(value_dict.values())),
), ),
list(value_dict.values()) list(value_dict.values()),
) )
def table_info(self, table_name): def table_info(self, table_name):
@ -207,13 +237,6 @@ class DBConnection(object):
for column in cursor for column in cursor
} }
# http://stackoverflow.com/questions/3300464/how-can-i-get-dict-from-sqlite-query
def _dict_factory(self, cursor, row):
return {
col[0]: row[idx]
for idx, col in enumerate(cursor.description)
}
def sanity_check_database(connection, sanity_check): def sanity_check_database(connection, sanity_check):
sanity_check(connection).check() sanity_check(connection).check()

View file

@ -1,3 +1,10 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import core import core

View file

@ -1,3 +1,10 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import os import os
import requests import requests

View file

@ -1,3 +1,10 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import core import core
from core.plugins.downloaders.torrent.utils import create_torrent_class from core.plugins.downloaders.torrent.utils import create_torrent_class

View file

@ -1,3 +1,10 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
from synchronousdeluge.client import DelugeClient from synchronousdeluge.client import DelugeClient
import core import core

View file

@ -1,4 +1,10 @@
from __future__ import absolute_import from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
from qbittorrent import Client as qBittorrentClient from qbittorrent import Client as qBittorrentClient
@ -14,7 +20,7 @@ def configure_client():
password = core.QBITTORRENT_PASSWORD password = core.QBITTORRENT_PASSWORD
logger.debug( logger.debug(
'Connecting to {0}: http://{1}:{2}'.format(agent, host, port) 'Connecting to {0}: http://{1}:{2}'.format(agent, host, port),
) )
client = qBittorrentClient('http://{0}:{1}/'.format(host, port)) client = qBittorrentClient('http://{0}:{1}/'.format(host, port))
try: try:

View file

@ -1,3 +1,10 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
from transmissionrpc.client import Client as TransmissionClient from transmissionrpc.client import Client as TransmissionClient
import core import core

View file

@ -1,3 +1,10 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import time import time
import core import core

View file

@ -1,4 +1,9 @@
from __future__ import absolute_import from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
from utorrent.client import UTorrentClient from utorrent.client import UTorrentClient

View file

@ -1,3 +1,10 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import requests import requests
import core import core

View file

@ -1,3 +1,10 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
from babelfish import Language from babelfish import Language
import subliminal import subliminal

View file

@ -1,5 +1,12 @@
# coding=utf-8 # coding=utf-8
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import os import os
import platform import platform
import re import re
@ -25,7 +32,7 @@ media_list = [r'\.s\d{2}e\d{2}\.', r'\.1080[pi]\.', r'\.720p\.', r'\.576[pi]', r
r'\.internal\.', r'\bac3\b', r'\.ntsc\.', r'\.pal\.', r'\.secam\.', r'\bdivx\b', r'\bxvid\b'] r'\.internal\.', r'\bac3\b', r'\.ntsc\.', r'\.pal\.', r'\.secam\.', r'\bdivx\b', r'\bxvid\b']
media_pattern = re.compile('|'.join(media_list), flags=re.IGNORECASE) media_pattern = re.compile('|'.join(media_list), flags=re.IGNORECASE)
garbage_name = re.compile(r'^[a-zA-Z0-9]*$') garbage_name = re.compile(r'^[a-zA-Z0-9]*$')
char_replace = [[r'(\w)1\.(\w)', r'\1i\2'] char_replace = [[r'(\w)1\.(\w)', r'\1i\2'],
] ]
@ -121,7 +128,7 @@ def reverse_filename(filename, dirname, name):
def rename_script(dirname): def rename_script(dirname):
rename_file = '' rename_file = ''
for directory, directories, files in os.walk(dirname): for directory, _, files in os.walk(dirname):
for file in files: for file in files:
if re.search(r'(rename\S*\.(sh|bat)$)', file, re.IGNORECASE): if re.search(r'(rename\S*\.(sh|bat)$)', file, re.IGNORECASE):
rename_file = os.path.join(directory, file) rename_file = os.path.join(directory, file)
@ -171,7 +178,7 @@ def par2(dirname):
cmd = '' cmd = ''
for item in command: for item in command:
cmd = '{cmd} {item}'.format(cmd=cmd, item=item) cmd = '{cmd} {item}'.format(cmd=cmd, item=item)
logger.debug('calling command:{0}'.format(cmd), 'PAR2') logger.debug('calling command:{0}'.format(cmd), 'PAR2')
try: try:
proc = subprocess.Popen(command, stdout=bitbucket, stderr=bitbucket) proc = subprocess.Popen(command, stdout=bitbucket, stderr=bitbucket)
proc.communicate() proc.communicate()

View file

@ -1,5 +1,12 @@
# coding=utf-8 # coding=utf-8
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import errno import errno
import json import json
import sys import sys
@ -548,7 +555,7 @@ def get_subs(file):
sub_ext = ['.srt', '.sub', '.idx'] sub_ext = ['.srt', '.sub', '.idx']
name = os.path.splitext(os.path.split(file)[1])[0] name = os.path.splitext(os.path.split(file)[1])[0]
path = os.path.split(file)[0] path = os.path.split(file)[0]
for directory, directories, filenames in os.walk(path): for directory, _, filenames in os.walk(path):
for filename in filenames: for filename in filenames:
filepaths.extend([os.path.join(directory, filename)]) filepaths.extend([os.path.join(directory, filename)])
subfiles = [item for item in filepaths if os.path.splitext(item)[1] in sub_ext and name in item] subfiles = [item for item in filepaths if os.path.splitext(item)[1] in sub_ext and name in item]
@ -854,7 +861,7 @@ def combine_mts(mts_path):
def combine_cd(combine): def combine_cd(combine):
new_files = [] new_files = []
for item in set([re.match('(.+)[cC][dD][0-9].', item).groups()[0] for item in combine]): for item in {re.match('(.+)[cC][dD][0-9].', item).groups()[0] for item in combine}:
concat = '' concat = ''
for n in range(99): for n in range(99):
files = [file for file in combine if files = [file for file in combine if
@ -920,7 +927,7 @@ def transcode_directory(dir_name):
logger.info('Transcoding video: {0}'.format(newfile_path)) logger.info('Transcoding video: {0}'.format(newfile_path))
print_cmd(command) print_cmd(command)
result = 1 # set result to failed in case call fails. result = 1 # set result to failed in case call fails.
if True: try:
if isinstance(file, string_types): if isinstance(file, string_types):
proc = subprocess.Popen(command, stdout=bitbucket, stderr=bitbucket) proc = subprocess.Popen(command, stdout=bitbucket, stderr=bitbucket)
else: else:
@ -934,8 +941,8 @@ def transcode_directory(dir_name):
procin.stdout.close() procin.stdout.close()
proc.communicate() proc.communicate()
result = proc.returncode result = proc.returncode
#except Exception: except Exception:
# logger.error('Transcoding of video {0} has failed'.format(newfile_path)) logger.error('Transcoding of video {0} has failed'.format(newfile_path))
if core.SUBSDIR and result == 0 and isinstance(file, string_types): if core.SUBSDIR and result == 0 and isinstance(file, string_types):
for sub in get_subs(file): for sub in get_subs(file):
@ -979,7 +986,7 @@ def transcode_directory(dir_name):
if not os.listdir(text_type(new_dir)): # this is an empty directory and we didn't transcode into it. if not os.listdir(text_type(new_dir)): # this is an empty directory and we didn't transcode into it.
os.rmdir(new_dir) os.rmdir(new_dir)
new_dir = dir_name new_dir = dir_name
if not core.PROCESSOUTPUT and core.DUPLICATE: # We postprocess the original files to CP/SB if not core.PROCESSOUTPUT and core.DUPLICATE: # We postprocess the original files to CP/SB
new_dir = dir_name new_dir = dir_name
bitbucket.close() bitbucket.close()
return final_result, new_dir return final_result, new_dir

View file

@ -1,5 +1,12 @@
# coding=utf-8 # coding=utf-8
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import os import os
from subprocess import Popen from subprocess import Popen
@ -47,7 +54,7 @@ def external_script(output_destination, torrent_name, torrent_label, settings):
logger.info('Corrupt video file found {0}. Deleting.'.format(video), 'USERSCRIPT') logger.info('Corrupt video file found {0}. Deleting.'.format(video), 'USERSCRIPT')
os.unlink(video) os.unlink(video)
for dirpath, dirnames, filenames in os.walk(output_destination): for dirpath, _, filenames in os.walk(output_destination):
for file in filenames: for file in filenames:
file_path = core.os.path.join(dirpath, file) file_path = core.os.path.join(dirpath, file)
@ -102,7 +109,7 @@ def external_script(output_destination, torrent_name, torrent_label, settings):
final_result += result final_result += result
num_files_new = 0 num_files_new = 0
for dirpath, dirnames, filenames in os.walk(output_destination): for _, _, filenames in os.walk(output_destination):
for file in filenames: for file in filenames:
file_name, file_extension = os.path.splitext(file) file_name, file_extension = os.path.splitext(file)

View file

@ -1,5 +1,12 @@
# coding=utf-8 # coding=utf-8
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import requests import requests
from core.utils import shutil_custom from core.utils import shutil_custom

View file

@ -1,3 +1,9 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import os.path import os.path

View file

@ -1,3 +1,10 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import datetime import datetime
from six import text_type from six import text_type

View file

@ -1,3 +1,10 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import os import os
from six import text_type from six import text_type
@ -68,14 +75,14 @@ def convert_to_ascii(input_name, dir_name):
if 'NZBOP_SCRIPTDIR' in os.environ: if 'NZBOP_SCRIPTDIR' in os.environ:
print('[NZB] DIRECTORY={0}'.format(dir_name)) print('[NZB] DIRECTORY={0}'.format(dir_name))
for dirname, dirnames, filenames in os.walk(dir_name, topdown=False): for dirname, dirnames, _ in os.walk(dir_name, topdown=False):
for subdirname in dirnames: for subdirname in dirnames:
encoded, subdirname2 = char_replace(subdirname) encoded, subdirname2 = char_replace(subdirname)
if encoded: if encoded:
logger.info('Renaming directory to: {0}.'.format(subdirname2), 'ENCODER') logger.info('Renaming directory to: {0}.'.format(subdirname2), 'ENCODER')
os.rename(os.path.join(dirname, subdirname), os.path.join(dirname, subdirname2)) os.rename(os.path.join(dirname, subdirname), os.path.join(dirname, subdirname2))
for dirname, dirnames, filenames in os.walk(dir_name): for dirname, _, filenames in os.walk(dir_name):
for filename in filenames: for filename in filenames:
encoded, filename2 = char_replace(filename) encoded, filename2 = char_replace(filename)
if encoded: if encoded:

View file

@ -1,3 +1,10 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import os import os
import re import re
import shutil import shutil
@ -88,7 +95,7 @@ def is_min_size(input_name, min_size):
def is_archive_file(filename): def is_archive_file(filename):
"""Check if the filename is allowed for the Archive""" """Check if the filename is allowed for the Archive."""
for regext in core.COMPRESSED_CONTAINER: for regext in core.COMPRESSED_CONTAINER:
if regext.search(filename): if regext.search(filename):
return regext.split(filename)[0] return regext.split(filename)[0]

View file

@ -1,3 +1,10 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import os import os
import re import re
@ -5,7 +12,6 @@ import guessit
import requests import requests
from six import text_type from six import text_type
import core
from core import logger from core import logger
from core.utils.naming import sanitize_name from core.utils.naming import sanitize_name
@ -90,13 +96,13 @@ def find_imdbid(dir_name, input_name, omdb_api_key):
def category_search(input_directory, input_name, input_category, root, categories): def category_search(input_directory, input_name, input_category, root, categories):
tordir = False tordir = False
#try: # try:
# input_name = input_name.encode(core.SYS_ENCODING) # input_name = input_name.encode(core.SYS_ENCODING)
#except Exception: # except Exception:
# pass # pass
#try: # try:
# input_directory = input_directory.encode(core.SYS_ENCODING) # input_directory = input_directory.encode(core.SYS_ENCODING)
#except Exception: # except Exception:
# pass # pass
if input_directory is None: # =Nothing to process here. if input_directory is None: # =Nothing to process here.

View file

@ -1,3 +1,10 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import os import os
import shutil import shutil

View file

@ -1,9 +1,17 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import re import re
import core
def sanitize_name(name): def sanitize_name(name):
""" """
Remove bad chars from the filename.
>>> sanitize_name('a/b/c') >>> sanitize_name('a/b/c')
'a-b-c' 'a-b-c'
>>> sanitize_name('abc') >>> sanitize_name('abc')
@ -13,29 +21,26 @@ def sanitize_name(name):
>>> sanitize_name('.a.b..') >>> sanitize_name('.a.b..')
'a.b' 'a.b'
""" """
# remove bad chars from the filename
name = re.sub(r'[\\/*]', '-', name) name = re.sub(r'[\\/*]', '-', name)
name = re.sub(r'[:\'<>|?]', '', name) name = re.sub(r'[:\'<>|?]', '', name)
# remove leading/trailing periods and spaces # remove leading/trailing periods and spaces
name = name.strip(' .') name = name.strip(' .')
#try: # try:
# name = name.encode(core.SYS_ENCODING) # name = name.encode(core.SYS_ENCODING)
#except Exception: # except Exception:
# pass # pass
return name return name
def clean_file_name(filename): def clean_file_name(filename):
"""Cleans up nzb name by removing any . and _ """
characters, along with any trailing hyphens. Clean up nzb name by removing any . and _ characters and trailing hyphens.
Is basically equivalent to replacing all _ and . with a Is basically equivalent to replacing all _ and . with a
space, but handles decimal numbers in string, for example: space, but handles decimal numbers in string, for example:
""" """
filename = re.sub(r'(\D)\.(?!\s)(\D)', r'\1 \2', filename) filename = re.sub(r'(\D)\.(?!\s)(\D)', r'\1 \2', filename)
filename = re.sub(r'(\d)\.(\d{4})', r'\1 \2', filename) # if it ends in a year then don't keep the dot filename = re.sub(r'(\d)\.(\d{4})', r'\1 \2', filename) # if it ends in a year then don't keep the dot
filename = re.sub(r'(\D)\.(?!\s)', r'\1 ', filename) filename = re.sub(r'(\D)\.(?!\s)', r'\1 ', filename)

View file

@ -1,3 +1,10 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import socket import socket
import struct import struct
import time import time

View file

@ -1,3 +1,10 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import os import os
import core import core

View file

@ -1,3 +1,9 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
from functools import partial from functools import partial
import os import os
@ -67,14 +73,14 @@ def remote_dir(path):
def get_dir_size(input_path): def get_dir_size(input_path):
prepend = partial(os.path.join, input_path) prepend = partial(os.path.join, input_path)
return sum([ return sum(
(os.path.getsize(f) if os.path.isfile(f) else get_dir_size(f)) (os.path.getsize(f) if os.path.isfile(f) else get_dir_size(f))
for f in map(prepend, os.listdir(text_type(input_path))) for f in map(prepend, os.listdir(text_type(input_path)))
]) )
def remove_empty_folders(path, remove_root=True): def remove_empty_folders(path, remove_root=True):
"""Function to remove empty folders""" """Remove empty folders."""
if not os.path.isdir(path): if not os.path.isdir(path):
return return

View file

@ -1,3 +1,10 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import os import os
import socket import socket
import subprocess import subprocess

View file

@ -1,3 +1,10 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
from functools import partial from functools import partial
import shutil import shutil
from six import PY2 from six import PY2

View file

@ -2,6 +2,13 @@
# Author: Nic Wolfe <nic@wolfeden.ca> # Author: Nic Wolfe <nic@wolfeden.ca>
# Modified by: echel0n # Modified by: echel0n
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import os import os
import platform import platform
import re import re
@ -19,9 +26,7 @@ from core import github_api as github, logger
class CheckVersion(object): class CheckVersion(object):
""" """Version checker that runs in a thread with the SB scheduler."""
Version check class meant to run as a thread object with the SB scheduler.
"""
def __init__(self): def __init__(self):
self.install_type = self.find_install_type() self.install_type = self.find_install_type()
@ -40,14 +45,13 @@ class CheckVersion(object):
def find_install_type(self): def find_install_type(self):
""" """
Determines how this copy of SB was installed. Determine how this copy of SB was installed.
returns: type of installation. Possible values are: returns: type of installation. Possible values are:
'win': any compiled windows build 'win': any compiled windows build
'git': running from source using git 'git': running from source using git
'source': running from source without git 'source': running from source without git
""" """
# check if we're a windows build # check if we're a windows build
if os.path.isdir(os.path.join(core.APP_ROOT, u'.git')): if os.path.isdir(os.path.join(core.APP_ROOT, u'.git')):
install_type = 'git' install_type = 'git'
@ -58,13 +62,12 @@ class CheckVersion(object):
def check_for_new_version(self, force=False): def check_for_new_version(self, force=False):
""" """
Checks the internet for a newer version. Check the internet for a newer version.
returns: bool, True for new version or False for no new 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 force: if true the VERSION_NOTIFY setting will be ignored and a check will be forced
""" """
if not core.VERSION_NOTIFY and not force: if not core.VERSION_NOTIFY and not force:
logger.log(u'Version checking is disabled, not checking for the newest version') logger.log(u'Version checking is disabled, not checking for the newest version')
return False return False
@ -199,8 +202,8 @@ class GitUpdateManager(UpdateManager):
logger.log(u'{cmd} : returned successful'.format(cmd=cmd), logger.DEBUG) logger.log(u'{cmd} : returned successful'.format(cmd=cmd), logger.DEBUG)
exit_status = 0 exit_status = 0
elif core.LOG_GIT and exit_status in (1, 128): elif core.LOG_GIT and exit_status in (1, 128):
logger.log(u'{cmd} returned : {output}'.format logger.log(u'{cmd} returned : {output}'.format
(cmd=cmd, output=output), logger.DEBUG) (cmd=cmd, output=output), logger.DEBUG)
else: else:
if core.LOG_GIT: if core.LOG_GIT:
logger.log(u'{cmd} returned : {output}, treat as error for now'.format logger.log(u'{cmd} returned : {output}, treat as error for now'.format
@ -211,13 +214,12 @@ class GitUpdateManager(UpdateManager):
def _find_installed_version(self): def _find_installed_version(self):
""" """
Attempts to find the currently installed version of Sick Beard. Attempt to find the currently installed version of Sick Beard.
Uses git show to get commit version. Uses git show to get commit version.
Returns: True for success or False for failure Returns: True for success or False for failure
""" """
output, err, exit_status = self._run_git(self._git_path, 'rev-parse HEAD') # @UnusedVariable output, err, exit_status = self._run_git(self._git_path, 'rev-parse HEAD') # @UnusedVariable
if exit_status == 0 and output: if exit_status == 0 and output:
@ -244,10 +246,12 @@ class GitUpdateManager(UpdateManager):
def _check_github_for_update(self): def _check_github_for_update(self):
""" """
Uses git commands to check if there is a newer version that the provided Check Github for a new version.
commit hash. If there is a newer version it sets _num_commits_behind.
"""
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._newest_commit_hash = None
self._num_commits_behind = 0 self._num_commits_behind = 0
self._num_commits_ahead = 0 self._num_commits_ahead = 0
@ -324,10 +328,11 @@ class GitUpdateManager(UpdateManager):
def update(self): def update(self):
""" """
Calls git pull origin <branch> in order to update Sick Beard. Returns a bool depending Check git for a new version.
on the call's success.
"""
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, 'pull origin {branch}'.format(branch=self.branch)) # @UnusedVariable output, err, exit_status = self._run_git(self._git_path, 'pull origin {branch}'.format(branch=self.branch)) # @UnusedVariable
if exit_status == 0: if exit_status == 0:
@ -382,12 +387,14 @@ class SourceUpdateManager(UpdateManager):
def _check_github_for_update(self): def _check_github_for_update(self):
""" """
Uses pygithub to ask github if there is a newer version that the provided Check Github for a new version.
commit hash. If there is a newer version it sets Sick Beard's version text.
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 commit_hash: hash that we're checking against
""" """
self._num_commits_behind = 0 self._num_commits_behind = 0
self._newest_commit_hash = None self._newest_commit_hash = None
@ -435,9 +442,7 @@ class SourceUpdateManager(UpdateManager):
return return
def update(self): def update(self):
""" """Download and install latest source tarball from github."""
Downloads the latest source tarball from github and installs it over the existing version.
"""
tar_download_url = 'https://github.com/{org}/{repo}/tarball/{branch}'.format( tar_download_url = 'https://github.com/{org}/{repo}/tarball/{branch}'.format(
org=self.github_repo_user, repo=self.github_repo, branch=self.branch) org=self.github_repo_user, repo=self.github_repo, branch=self.branch)
version_path = os.path.join(core.APP_ROOT, u'version.txt') version_path = os.path.join(core.APP_ROOT, u'version.txt')
@ -489,7 +494,7 @@ class SourceUpdateManager(UpdateManager):
# walk temp folder and move files to main folder # walk temp folder and move files to main folder
logger.log(u'Moving files from {source} to {destination}'.format logger.log(u'Moving files from {source} to {destination}'.format
(source=content_dir, destination=core.APP_ROOT)) (source=content_dir, destination=core.APP_ROOT))
for dirname, dirnames, filenames in os.walk(content_dir): # @UnusedVariable for dirname, _, filenames in os.walk(content_dir): # @UnusedVariable
dirname = dirname[len(content_dir) + 1:] dirname = dirname[len(content_dir) + 1:]
for curfile in filenames: for curfile in filenames:
old_path = os.path.join(content_dir, dirname, curfile) old_path = os.path.join(content_dir, dirname, curfile)

11
eol.py
View file

@ -1,5 +1,12 @@
#!/usr/bin/env python #!/usr/bin/env python
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import datetime import datetime
import sys import sys
import warnings import warnings
@ -157,7 +164,7 @@ def print_statuses(show_expired=False):
major=python_version[0], major=python_version[0],
minor=python_version[1], minor=python_version[1],
remaining=days_left, remaining=days_left,
) ),
) )
if not show_expired: if not show_expired:
return return
@ -171,7 +178,7 @@ def print_statuses(show_expired=False):
major=python_version[0], major=python_version[0],
minor=python_version[1], minor=python_version[1],
remaining=-days_left, remaining=-days_left,
) ),
) )

View file

@ -1,4 +1,11 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import os import os
import site import site
import sys import sys

View file

@ -1,4 +1,11 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import shutil import shutil
import os import os
import time import time

View file

@ -1,4 +1,11 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import libs import libs
__all__ = ['completed'] __all__ = ['completed']

View file

@ -1,7 +1,8 @@
# coding=utf-8 # coding=utf-8
"""A synchronous implementation of the Deluge RPC protocol """
based on gevent-deluge by Christopher Rosell. A synchronous implementation of the Deluge RPC protocol.
Based on gevent-deluge by Christopher Rosell:
https://github.com/chrippa/gevent-deluge https://github.com/chrippa/gevent-deluge
Example usage: Example usage:
@ -15,9 +16,16 @@ Example usage:
download_location = client.core.get_config_value("download_location").get() download_location = client.core.get_config_value("download_location").get()
""" """
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
from .exceptions import DelugeRPCError from .exceptions import DelugeRPCError
__title__ = "synchronous-deluge" __title__ = 'synchronous-deluge'
__version__ = "0.1" __version__ = '0.1'
__author__ = "Christian Dale" __author__ = 'Christian Dale'

View file

@ -1,4 +1,12 @@
# coding=utf-8 # coding=utf-8
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import os import os
import platform import platform
from collections import defaultdict from collections import defaultdict
@ -9,7 +17,7 @@ from .exceptions import DelugeRPCError
from .protocol import DelugeRPCRequest, DelugeRPCResponse from .protocol import DelugeRPCRequest, DelugeRPCResponse
from .transfer import DelugeTransfer from .transfer import DelugeTransfer
__all__ = ["DelugeClient"] __all__ = ['DelugeClient']
RPC_RESPONSE = 1 RPC_RESPONSE = 1
RPC_ERROR = 2 RPC_ERROR = 2
@ -18,41 +26,41 @@ RPC_EVENT = 3
class DelugeClient(object): class DelugeClient(object):
def __init__(self): def __init__(self):
"""A deluge client session.""" """Create a deluge client session."""
self.transfer = DelugeTransfer() self.transfer = DelugeTransfer()
self.modules = [] self.modules = []
self._request_counter = 0 self._request_counter = 0
def _get_local_auth(self): def _get_local_auth(self):
username = password = "" username = password = ''
if platform.system() in ('Windows', 'Microsoft'): if platform.system() in ('Windows', 'Microsoft'):
app_data_path = os.environ.get("APPDATA") app_data_path = os.environ.get('APPDATA')
if not app_data_path: if not app_data_path:
from six.moves import winreg from six.moves import winreg
hkey = winreg.OpenKey( hkey = winreg.OpenKey(
winreg.HKEY_CURRENT_USER, winreg.HKEY_CURRENT_USER,
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders',
) )
app_data_reg = winreg.QueryValueEx(hkey, "AppData") app_data_reg = winreg.QueryValueEx(hkey, 'AppData')
app_data_path = app_data_reg[0] app_data_path = app_data_reg[0]
winreg.CloseKey(hkey) winreg.CloseKey(hkey)
auth_file = os.path.join(app_data_path, "deluge", "auth") auth_file = os.path.join(app_data_path, 'deluge', 'auth')
else: else:
from xdg.BaseDirectory import save_config_path from xdg.BaseDirectory import save_config_path
try: try:
auth_file = os.path.join(save_config_path("deluge"), "auth") auth_file = os.path.join(save_config_path('deluge'), 'auth')
except OSError: except OSError:
return username, password return username, password
if os.path.exists(auth_file): if os.path.exists(auth_file):
for line in open(auth_file): for line in open(auth_file):
if line.startswith("#"): if line.startswith('#'):
# This is a comment line # This is a comment line
continue continue
line = line.strip() line = line.strip()
try: try:
lsplit = line.split(":") lsplit = line.split(':')
except Exception: except Exception:
continue continue
@ -63,13 +71,13 @@ class DelugeClient(object):
else: else:
continue continue
if username == "localclient": if username == 'localclient':
return username, password return username, password
return "", "" return '', ''
def _create_module_method(self, module, method): def _create_module_method(self, module, method):
fullname = "{0}.{1}".format(module, method) fullname = '{0}.{1}'.format(module, method)
def func(obj, *args, **kwargs): def func(obj, *args, **kwargs):
return self.remote_call(fullname, *args, **kwargs) return self.remote_call(fullname, *args, **kwargs)
@ -80,18 +88,18 @@ class DelugeClient(object):
def _introspect(self): def _introspect(self):
def splitter(value): def splitter(value):
return value.split(".") return value.split('.')
self.modules = [] self.modules = []
methods = self.remote_call("daemon.get_method_list").get() methods = self.remote_call('daemon.get_method_list').get()
methodmap = defaultdict(dict) methodmap = defaultdict(dict)
for module, method in imap(splitter, methods): for module, method in imap(splitter, methods):
methodmap[module][method] = self._create_module_method(module, method) methodmap[module][method] = self._create_module_method(module, method)
for module, methods in methodmap.items(): for module, methods in methodmap.items():
clsname = "DelugeModule{0}".format(module.capitalize()) clsname = 'DelugeModule{0}'.format(module.capitalize())
cls = type(clsname, (), methods) cls = type(clsname, (), methods)
setattr(self, module, cls()) setattr(self, module, cls())
self.modules.append(module) self.modules.append(module)
@ -133,24 +141,23 @@ class DelugeClient(object):
self._request_counter += 1 self._request_counter += 1
return response return response
def connect(self, host="127.0.0.1", port=58846, username="", password=""): def connect(self, host='127.0.0.1', port=58846, username='', password=''):
"""Connects to a daemon process. """Connect to a daemon process.
:param host: str, the hostname of the daemon :param host: str, the hostname of the daemon
:param port: int, the port of the daemon :param port: int, the port of the daemon
:param username: str, the username to login with :param username: str, the username to login with
:param password: str, the password to login with :param password: str, the password to login with
""" """
# Connect transport # Connect transport
self.transfer.connect((host, port)) self.transfer.connect((host, port))
# Attempt to fetch local auth info if needed # Attempt to fetch local auth info if needed
if not username and host in ("127.0.0.1", "localhost"): if not username and host in ('127.0.0.1', 'localhost'):
username, password = self._get_local_auth() username, password = self._get_local_auth()
# Authenticate # Authenticate
self.remote_call("daemon.login", username, password).get() self.remote_call('daemon.login', username, password).get()
# Introspect available methods # Introspect available methods
self._introspect() self._introspect()

View file

@ -1,5 +1,12 @@
# coding=utf-8 # coding=utf-8
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
class DelugeRPCError(Exception): class DelugeRPCError(Exception):
def __init__(self, name, msg, traceback): def __init__(self, name, msg, traceback):
@ -8,4 +15,4 @@ class DelugeRPCError(Exception):
self.traceback = traceback self.traceback = traceback
def __str__(self): def __str__(self):
return "{0}: {1}: {2}".format(self.__class__.__name__, self.name, self.msg) return '{0}: {1}: {2}'.format(self.__class__.__name__, self.name, self.msg)

View file

@ -1,5 +1,12 @@
# coding=utf-8 # coding=utf-8
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
class DelugeRPCRequest(object): class DelugeRPCRequest(object):
def __init__(self, request_id, method, *args, **kwargs): def __init__(self, request_id, method, *args, **kwargs):

View file

@ -1,4 +1,12 @@
# coding=utf-8 # coding=utf-8
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import socket import socket
import ssl import ssl
import struct import struct
@ -6,7 +14,7 @@ import zlib
import rencode import rencode
__all__ = ["DelugeTransfer"] __all__ = ['DelugeTransfer']
class DelugeTransfer(object): class DelugeTransfer(object):
@ -33,7 +41,7 @@ class DelugeTransfer(object):
payload = zlib.compress(rencode.dumps(data)) payload = zlib.compress(rencode.dumps(data))
self.conn.sendall(payload) self.conn.sendall(payload)
buf = b"" buf = b''
while True: while True:
data = self.conn.recv(1024) data = self.conn.recv(1024)

View file

@ -1,4 +1,12 @@
# coding=utf8 # coding=utf8
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import re import re
from six import StringIO, iteritems from six import StringIO, iteritems
@ -31,8 +39,7 @@ class UTorrentClient(object):
# TODO refresh token, when necessary # TODO refresh token, when necessary
def _make_opener(self, realm, base_url, username, password): def _make_opener(self, realm, base_url, username, password):
"""uTorrent API need HTTP Basic Auth and cookie support for token verify.""" """HTTP Basic Auth and cookie support for token verification."""
auth_handler = HTTPBasicAuthHandler() auth_handler = HTTPBasicAuthHandler()
auth_handler.add_password(realm=realm, auth_handler.add_password(realm=realm,
uri=base_url, uri=base_url,
@ -61,25 +68,25 @@ class UTorrentClient(object):
return self._action(params) return self._action(params)
def start(self, *hashes): def start(self, *hashes):
params = [('action', 'start'), ] params = [('action', 'start')]
for cur_hash in hashes: for cur_hash in hashes:
params.append(('hash', cur_hash)) params.append(('hash', cur_hash))
return self._action(params) return self._action(params)
def stop(self, *hashes): def stop(self, *hashes):
params = [('action', 'stop'), ] params = [('action', 'stop')]
for cur_hash in hashes: for cur_hash in hashes:
params.append(('hash', cur_hash)) params.append(('hash', cur_hash))
return self._action(params) return self._action(params)
def pause(self, *hashes): def pause(self, *hashes):
params = [('action', 'pause'), ] params = [('action', 'pause')]
for cur_hash in hashes: for cur_hash in hashes:
params.append(('hash', cur_hash)) params.append(('hash', cur_hash))
return self._action(params) return self._action(params)
def forcestart(self, *hashes): def forcestart(self, *hashes):
params = [('action', 'forcestart'), ] params = [('action', 'forcestart')]
for cur_hash in hashes: for cur_hash in hashes:
params.append(('hash', cur_hash)) params.append(('hash', cur_hash))
return self._action(params) return self._action(params)
@ -95,8 +102,8 @@ class UTorrentClient(object):
def setprops(self, cur_hash, **kvpairs): def setprops(self, cur_hash, **kvpairs):
params = [('action', 'setprops'), ('hash', cur_hash)] params = [('action', 'setprops'), ('hash', cur_hash)]
for k, v in iteritems(kvpairs): for k, v in iteritems(kvpairs):
params.append(("s", k)) params.append(('s', k))
params.append(("v", v)) params.append(('v', v))
return self._action(params) return self._action(params)
@ -125,13 +132,13 @@ class UTorrentClient(object):
self._action(params) self._action(params)
def remove(self, *hashes): def remove(self, *hashes):
params = [('action', 'remove'), ] params = [('action', 'remove')]
for cur_hash in hashes: for cur_hash in hashes:
params.append(('hash', cur_hash)) params.append(('hash', cur_hash))
return self._action(params) return self._action(params)
def removedata(self, *hashes): def removedata(self, *hashes):
params = [('action', 'removedata'), ] params = [('action', 'removedata')]
for cur_hash in hashes: for cur_hash in hashes:
params.append(('hash', cur_hash)) params.append(('hash', cur_hash))
return self._action(params) return self._action(params)

View file

@ -1,6 +1,13 @@
# coding=utf-8 # coding=utf-8
# code copied from http://www.doughellmann.com/PyMOTW/urllib2/ # code copied from http://www.doughellmann.com/PyMOTW/urllib2/
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import itertools import itertools
import mimetypes import mimetypes
from email.generator import _make_boundary as choose_boundary from email.generator import _make_boundary as choose_boundary
@ -36,7 +43,7 @@ class MultiPartForm(object):
# Build a list of lists, each containing "lines" of the # Build a list of lists, each containing "lines" of the
# request. Each part is separated by a boundary string. # request. Each part is separated by a boundary string.
# Once the list is built, return a string where each # Once the list is built, return a string where each
# line is separated by '\r\n'. # line is separated by '\r\n'.
parts = [] parts = []
part_boundary = '--' + self.boundary part_boundary = '--' + self.boundary

View file

@ -1,4 +1,11 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import subprocess import subprocess
import sys import sys
import os import os

View file

@ -99,7 +99,7 @@
# #
# Enter Mount points as LocalPath,RemotePath and separate each pair with '|' # Enter Mount points as LocalPath,RemotePath and separate each pair with '|'
# e.g. mountPoints=/volume1/Public/,E:\|/volume2/share/,\\NAS\ # e.g. mountPoints=/volume1/Public/,E:\|/volume2/share/,\\NAS\
#mountPoints= #mountPoints=
## Extensions ## Extensions
@ -134,7 +134,7 @@
# subLanguages. # subLanguages.
# #
# subLanguages. create a list of languages in the order you want them in your subtitles. # subLanguages. create a list of languages in the order you want them in your subtitles.
#subLanguages=eng,spa,fra #subLanguages=eng,spa,fra
# Transcode (0, 1). # Transcode (0, 1).
@ -216,7 +216,7 @@
# ffmpeg output settings. # ffmpeg output settings.
#outputVideoExtension=.mp4 #outputVideoExtension=.mp4
#outputVideoCodec=libx264 #outputVideoCodec=libx264
#VideoCodecAllow= #VideoCodecAllow=
#outputVideoResolution=720:-1 #outputVideoResolution=720:-1
#outputVideoPreset=medium #outputVideoPreset=medium
#outputVideoFramerate=24 #outputVideoFramerate=24
@ -227,7 +227,7 @@
#outputAudioBitrate=640k #outputAudioBitrate=640k
#outputQualityPercent= #outputQualityPercent=
#outputAudioTrack2Codec=libfaac #outputAudioTrack2Codec=libfaac
#AudioCodec2Allow= #AudioCodec2Allow=
#outputAudioTrack2Channels=2 #outputAudioTrack2Channels=2
#outputAudioTrack2Bitrate=160k #outputAudioTrack2Bitrate=160k
#outputAudioOtherCodec=libmp3lame #outputAudioOtherCodec=libmp3lame
@ -255,6 +255,13 @@
### NZBGET POST-PROCESSING SCRIPT ### ### NZBGET POST-PROCESSING SCRIPT ###
############################################################################## ##############################################################################
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import sys import sys
import nzbToMedia import nzbToMedia

View file

@ -100,6 +100,13 @@
### NZBGET POST-PROCESSING SCRIPT ### ### NZBGET POST-PROCESSING SCRIPT ###
############################################################################## ##############################################################################
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import sys import sys
import nzbToMedia import nzbToMedia

View file

@ -101,7 +101,7 @@
# #
# Enter Mount points as LocalPath,RemotePath and separate each pair with '|' # Enter Mount points as LocalPath,RemotePath and separate each pair with '|'
# e.g. mountPoints=/volume1/Public/,E:\|/volume2/share/,\\NAS\ # e.g. mountPoints=/volume1/Public/,E:\|/volume2/share/,\\NAS\
#mountPoints= #mountPoints=
## WakeOnLan ## WakeOnLan
@ -122,6 +122,13 @@
### NZBGET POST-PROCESSING SCRIPT ### ### NZBGET POST-PROCESSING SCRIPT ###
############################################################################## ##############################################################################
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import sys import sys
import nzbToMedia import nzbToMedia

View file

@ -59,6 +59,19 @@
# set this to where your LazyLibrarian completed downloads are. # set this to where your LazyLibrarian completed downloads are.
#llwatch_dir= #llwatch_dir=
# LazyLibrarian and NZBGet are a different system (0, 1).
#
# Enable to replace local path with the path as per the mountPoints below.
#llremote_path=0
## Network
# Network Mount Points (Needed for remote path above)
#
# Enter Mount points as LocalPath,RemotePath and separate each pair with '|'
# e.g. mountPoints=/volume1/Public/,E:\|/volume2/share/,\\NAS\
#mountPoints=
## Posix ## Posix
# Niceness for external tasks Extractor and Transcoder. # Niceness for external tasks Extractor and Transcoder.
@ -95,6 +108,13 @@
### NZBGET POST-PROCESSING SCRIPT ### ### NZBGET POST-PROCESSING SCRIPT ###
############################################################################## ##############################################################################
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import sys import sys
import nzbToMedia import nzbToMedia

View file

@ -84,7 +84,7 @@
# #
# Enter Mount points as LocalPath,RemotePath and separate each pair with '|' # Enter Mount points as LocalPath,RemotePath and separate each pair with '|'
# e.g. mountPoints=/volume1/Public/,E:\|/volume2/share/,\\NAS\ # e.g. mountPoints=/volume1/Public/,E:\|/volume2/share/,\\NAS\
#mountPoints= #mountPoints=
## Extensions ## Extensions
@ -119,7 +119,7 @@
# subLanguages. # subLanguages.
# #
# subLanguages. create a list of languages in the order you want them in your subtitles. # subLanguages. create a list of languages in the order you want them in your subtitles.
#subLanguages = eng,spa,fra #subLanguages = eng,spa,fra
# Transcode (0, 1). # Transcode (0, 1).
@ -145,7 +145,7 @@
# outputVideoPath. # outputVideoPath.
# #
# outputVideoPath. Set path you want transcoded videos moved to. Leave blank to disable. # outputVideoPath. Set path you want transcoded videos moved to. Leave blank to disable.
#outputVideoPath = #outputVideoPath =
# processOutput (0,1). # processOutput (0,1).
# #
@ -201,20 +201,20 @@
# ffmpeg output settings. # ffmpeg output settings.
#outputVideoExtension=.mp4 #outputVideoExtension=.mp4
#outputVideoCodec=libx264 #outputVideoCodec=libx264
#VideoCodecAllow = #VideoCodecAllow =
#outputVideoResolution=720:-1 #outputVideoResolution=720:-1
#outputVideoPreset=medium #outputVideoPreset=medium
#outputVideoFramerate=24 #outputVideoFramerate=24
#outputVideoBitrate=800k #outputVideoBitrate=800k
#outputAudioCodec=libmp3lame #outputAudioCodec=libmp3lame
#AudioCodecAllow = #AudioCodecAllow =
#outputAudioBitrate=128k #outputAudioBitrate=128k
#outputQualityPercent = 0 #outputQualityPercent = 0
#outputAudioTrack2Codec = libfaac #outputAudioTrack2Codec = libfaac
#AudioCodec2Allow = #AudioCodec2Allow =
#outputAudioTrack2Bitrate = 128k #outputAudioTrack2Bitrate = 128k
#outputAudioOtherCodec = libmp3lame #outputAudioOtherCodec = libmp3lame
#AudioOtherCodecAllow = #AudioOtherCodecAllow =
#outputAudioOtherBitrate = 128k #outputAudioOtherBitrate = 128k
#outputSubtitleCodec = #outputSubtitleCodec =
@ -237,6 +237,13 @@
### NZBGET POST-PROCESSING SCRIPT ### ### NZBGET POST-PROCESSING SCRIPT ###
############################################################################## ##############################################################################
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import sys import sys
import nzbToMedia import nzbToMedia

View file

@ -444,13 +444,18 @@
# set this to where your LazyLibrarian completed downloads are. # set this to where your LazyLibrarian completed downloads are.
#llwatch_dir= #llwatch_dir=
# LazyLibrarian and NZBGet are a different system (0, 1).
#
# Enable to replace local path with the path as per the mountPoints below.
#llremote_path=0
## Network ## Network
# Network Mount Points (Needed for remote path above) # Network Mount Points (Needed for remote path above)
# #
# Enter Mount points as LocalPath,RemotePath and separate each pair with '|' # Enter Mount points as LocalPath,RemotePath and separate each pair with '|'
# e.g. mountPoints=/volume1/Public/,E:\|/volume2/share/,\\NAS\ # e.g. mountPoints=/volume1/Public/,E:\|/volume2/share/,\\NAS\
#mountPoints= #mountPoints=
## Extensions ## Extensions
@ -485,7 +490,7 @@
# subLanguages. # subLanguages.
# #
# subLanguages. create a list of languages in the order you want them in your subtitles. # subLanguages. create a list of languages in the order you want them in your subtitles.
#subLanguages=eng,spa,fra #subLanguages=eng,spa,fra
# Transcode (0, 1). # Transcode (0, 1).
@ -567,7 +572,7 @@
# ffmpeg output settings. # ffmpeg output settings.
#outputVideoExtension=.mp4 #outputVideoExtension=.mp4
#outputVideoCodec=libx264 #outputVideoCodec=libx264
#VideoCodecAllow= #VideoCodecAllow=
#outputVideoResolution=720:-1 #outputVideoResolution=720:-1
#outputVideoPreset=medium #outputVideoPreset=medium
#outputVideoFramerate=24 #outputVideoFramerate=24
@ -578,7 +583,7 @@
#outputAudioBitrate=640k #outputAudioBitrate=640k
#outputQualityPercent= #outputQualityPercent=
#outputAudioTrack2Codec=libfaac #outputAudioTrack2Codec=libfaac
#AudioCodec2Allow= #AudioCodec2Allow=
#outputAudioTrack2Channels=2 #outputAudioTrack2Channels=2
#outputAudioTrack2Bitrate=160k #outputAudioTrack2Bitrate=160k
#outputAudioOtherCodec=libmp3lame #outputAudioOtherCodec=libmp3lame
@ -655,18 +660,23 @@
### NZBGET POST-PROCESSING SCRIPT ### ### NZBGET POST-PROCESSING SCRIPT ###
############################################################################## ##############################################################################
from __future__ import print_function from __future__ import (
absolute_import,
import eol division,
eol.check() print_function,
unicode_literals,
import cleanup )
cleanup.clean(cleanup.FOLDER_STRUCTURE)
import datetime import datetime
import os import os
import sys import sys
import eol
import cleanup
eol.check()
cleanup.clean(cleanup.FOLDER_STRUCTURE)
import core import core
from core import logger, main_db from core import logger, main_db
from core.auto_process import comics, games, movies, music, tv, books from core.auto_process import comics, games, movies, music, tv, books
@ -777,11 +787,11 @@ def process(input_directory, input_name=None, status=0, client_agent='manual', d
) )
except Exception: except Exception:
logger.error('Remote Path {0} is not valid for {1}:{2} Please set this to either 0 to disable or 1 to enable!'.format( logger.error('Remote Path {0} is not valid for {1}:{2} Please set this to either 0 to disable or 1 to enable!'.format(
core.get('remote_path'), section_name, input_category)) cfg.get('remote_path'), section_name, input_category))
input_name, input_directory = convert_to_ascii(input_name, input_directory) input_name, input_directory = convert_to_ascii(input_name, input_directory)
if extract == 1: if extract == 1 and not (status > 0 and core.NOEXTRACTFAILED):
logger.debug('Checking for archives to extract in directory: {0}'.format(input_directory)) logger.debug('Checking for archives to extract in directory: {0}'.format(input_directory))
extract_files(input_directory) extract_files(input_directory)

View file

@ -92,7 +92,7 @@
# #
# Enter Mount points as LocalPath,RemotePath and separate each pair with '|' # Enter Mount points as LocalPath,RemotePath and separate each pair with '|'
# e.g. mountPoints=/volume1/Public/,E:\|/volume2/share/,\\NAS\ # e.g. mountPoints=/volume1/Public/,E:\|/volume2/share/,\\NAS\
#mountPoints= #mountPoints=
## WakeOnLan ## WakeOnLan
@ -113,6 +113,13 @@
### NZBGET POST-PROCESSING SCRIPT ### ### NZBGET POST-PROCESSING SCRIPT ###
############################################################################## ##############################################################################
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import sys import sys
import nzbToMedia import nzbToMedia

View file

@ -89,7 +89,7 @@
# #
# Enter Mount points as LocalPath,RemotePath and separate each pair with '|' # Enter Mount points as LocalPath,RemotePath and separate each pair with '|'
# e.g. mountPoints=/volume1/Public/,E:\|/volume2/share/,\\NAS\ # e.g. mountPoints=/volume1/Public/,E:\|/volume2/share/,\\NAS\
#mountPoints= #mountPoints=
## Extensions ## Extensions
@ -124,7 +124,7 @@
# subLanguages. # subLanguages.
# #
# subLanguages. create a list of languages in the order you want them in your subtitles. # subLanguages. create a list of languages in the order you want them in your subtitles.
#subLanguages = eng,spa,fra #subLanguages = eng,spa,fra
# Transcode (0, 1). # Transcode (0, 1).
@ -150,7 +150,7 @@
# outputVideoPath. # outputVideoPath.
# #
# outputVideoPath. Set path you want transcoded videos moved to. Leave blank to disable. # outputVideoPath. Set path you want transcoded videos moved to. Leave blank to disable.
#outputVideoPath = #outputVideoPath =
# processOutput (0,1). # processOutput (0,1).
# #
@ -206,20 +206,20 @@
# ffmpeg output settings. # ffmpeg output settings.
#outputVideoExtension=.mp4 #outputVideoExtension=.mp4
#outputVideoCodec=libx264 #outputVideoCodec=libx264
#VideoCodecAllow = #VideoCodecAllow =
#outputVideoResolution=720:-1 #outputVideoResolution=720:-1
#outputVideoPreset=medium #outputVideoPreset=medium
#outputVideoFramerate=24 #outputVideoFramerate=24
#outputVideoBitrate=800k #outputVideoBitrate=800k
#outputAudioCodec=libmp3lame #outputAudioCodec=libmp3lame
#AudioCodecAllow = #AudioCodecAllow =
#outputAudioBitrate=128k #outputAudioBitrate=128k
#outputQualityPercent = 0 #outputQualityPercent = 0
#outputAudioTrack2Codec = libfaac #outputAudioTrack2Codec = libfaac
#AudioCodec2Allow = #AudioCodec2Allow =
#outputAudioTrack2Bitrate = 128k #outputAudioTrack2Bitrate = 128k
#outputAudioOtherCodec = libmp3lame #outputAudioOtherCodec = libmp3lame
#AudioOtherCodecAllow = #AudioOtherCodecAllow =
#outputAudioOtherBitrate = 128k #outputAudioOtherBitrate = 128k
#outputSubtitleCodec = #outputSubtitleCodec =
@ -242,6 +242,13 @@
### NZBGET POST-PROCESSING SCRIPT ### ### NZBGET POST-PROCESSING SCRIPT ###
############################################################################## ##############################################################################
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import sys import sys
import nzbToMedia import nzbToMedia

View file

@ -94,7 +94,7 @@
# #
# Enter Mount points as LocalPath,RemotePath and separate each pair with '|' # Enter Mount points as LocalPath,RemotePath and separate each pair with '|'
# e.g. mountPoints=/volume1/Public/,E:\|/volume2/share/,\\NAS\ # e.g. mountPoints=/volume1/Public/,E:\|/volume2/share/,\\NAS\
#mountPoints= #mountPoints=
## Extensions ## Extensions
@ -129,7 +129,7 @@
# subLanguages. # subLanguages.
# #
# subLanguages. create a list of languages in the order you want them in your subtitles. # subLanguages. create a list of languages in the order you want them in your subtitles.
#subLanguages = eng,spa,fra #subLanguages = eng,spa,fra
# Transcode (0, 1). # Transcode (0, 1).
@ -155,7 +155,7 @@
# outputVideoPath. # outputVideoPath.
# #
# outputVideoPath. Set path you want transcoded videos moved to. Leave blank to disable. # outputVideoPath. Set path you want transcoded videos moved to. Leave blank to disable.
#outputVideoPath = #outputVideoPath =
# processOutput (0,1). # processOutput (0,1).
# #
@ -211,20 +211,20 @@
# ffmpeg output settings. # ffmpeg output settings.
#outputVideoExtension=.mp4 #outputVideoExtension=.mp4
#outputVideoCodec=libx264 #outputVideoCodec=libx264
#VideoCodecAllow = #VideoCodecAllow =
#outputVideoResolution=720:-1 #outputVideoResolution=720:-1
#outputVideoPreset=medium #outputVideoPreset=medium
#outputVideoFramerate=24 #outputVideoFramerate=24
#outputVideoBitrate=800k #outputVideoBitrate=800k
#outputAudioCodec=libmp3lame #outputAudioCodec=libmp3lame
#AudioCodecAllow = #AudioCodecAllow =
#outputAudioBitrate=128k #outputAudioBitrate=128k
#outputQualityPercent = 0 #outputQualityPercent = 0
#outputAudioTrack2Codec = libfaac #outputAudioTrack2Codec = libfaac
#AudioCodec2Allow = #AudioCodec2Allow =
#outputAudioTrack2Bitrate = 128k #outputAudioTrack2Bitrate = 128k
#outputAudioOtherCodec = libmp3lame #outputAudioOtherCodec = libmp3lame
#AudioOtherCodecAllow = #AudioOtherCodecAllow =
#outputAudioOtherBitrate = 128k #outputAudioOtherBitrate = 128k
#outputSubtitleCodec = #outputSubtitleCodec =
@ -247,6 +247,13 @@
### NZBGET POST-PROCESSING SCRIPT ### ### NZBGET POST-PROCESSING SCRIPT ###
############################################################################## ##############################################################################
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import sys import sys
import nzbToMedia import nzbToMedia

View file

@ -100,7 +100,7 @@
# #
# Enter Mount points as LocalPath,RemotePath and separate each pair with '|' # Enter Mount points as LocalPath,RemotePath and separate each pair with '|'
# e.g. mountPoints=/volume1/Public/,E:\|/volume2/share/,\\NAS\ # e.g. mountPoints=/volume1/Public/,E:\|/volume2/share/,\\NAS\
#mountPoints= #mountPoints=
## Extensions ## Extensions
@ -135,7 +135,7 @@
# subLanguages. # subLanguages.
# #
# subLanguages. create a list of languages in the order you want them in your subtitles. # subLanguages. create a list of languages in the order you want them in your subtitles.
#subLanguages=eng,spa,fra #subLanguages=eng,spa,fra
# Transcode (0, 1). # Transcode (0, 1).
@ -217,7 +217,7 @@
# ffmpeg output settings. # ffmpeg output settings.
#outputVideoExtension=.mp4 #outputVideoExtension=.mp4
#outputVideoCodec=libx264 #outputVideoCodec=libx264
#VideoCodecAllow= #VideoCodecAllow=
#outputVideoResolution=720:-1 #outputVideoResolution=720:-1
#outputVideoPreset=medium #outputVideoPreset=medium
#outputVideoFramerate=24 #outputVideoFramerate=24
@ -228,7 +228,7 @@
#outputAudioBitrate=640k #outputAudioBitrate=640k
#outputQualityPercent= #outputQualityPercent=
#outputAudioTrack2Codec=libfaac #outputAudioTrack2Codec=libfaac
#AudioCodec2Allow= #AudioCodec2Allow=
#outputAudioTrack2Channels=2 #outputAudioTrack2Channels=2
#outputAudioTrack2Bitrate=160k #outputAudioTrack2Bitrate=160k
#outputAudioOtherCodec=libmp3lame #outputAudioOtherCodec=libmp3lame
@ -256,6 +256,13 @@
### NZBGET POST-PROCESSING SCRIPT ### ### NZBGET POST-PROCESSING SCRIPT ###
############################################################################## ##############################################################################
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import sys import sys
import nzbToMedia import nzbToMedia

View file

@ -1,6 +1,11 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
from __future__ import absolute_import, print_function from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
import io import io
import os.path import os.path
@ -11,7 +16,7 @@ from setuptools import setup
def read(*names, **kwargs): def read(*names, **kwargs):
with io.open( with io.open(
os.path.join(os.path.dirname(__file__), *names), os.path.join(os.path.dirname(__file__), *names),
encoding=kwargs.get('encoding', 'utf8') encoding=kwargs.get('encoding', 'utf8'),
) as fh: ) as fh:
return fh.read() return fh.read()
@ -21,21 +26,21 @@ setup(
version='12.0.10', version='12.0.10',
license='GPLv3', license='GPLv3',
description='Efficient on demand post processing', description='Efficient on demand post processing',
long_description=""" long_description="""
nzbToMedia nzbToMedia
========== ==========
Efficient on demand post processing Efficient on demand post processing
----------------------------------- -----------------------------------
A PVR app needs to know when a download is ready for post-processing. There are two methods: A PVR app needs to know when a download is ready for post-processing. There are two methods:
1. On-demand post-processing script (e.g. sabToSickBeard.py or nzbToMedia.py): A script in the downloader runs once at the end of the download job and notifies the PVR app that the download is complete. 1. On-demand post-processing script (e.g. sabToSickBeard.py or nzbToMedia.py): A script in the downloader runs once at the end of the download job and notifies the PVR app that the download is complete.
2. Continuous folder scanning: The PVR app frequently polls download folder(s) for completed downloads. 2. Continuous folder scanning: The PVR app frequently polls download folder(s) for completed downloads.
On-demand is superior, for several reasons: On-demand is superior, for several reasons:
1. The PVR app is notified only once, exactly when the download is ready for post-processing 1. The PVR app is notified only once, exactly when the download is ready for post-processing
2. The PVR app does not have to wait for the next poll interval before it starts processing 2. The PVR app does not have to wait for the next poll interval before it starts processing
3. Continuous polling is not as efficient and is more stressful on low performance hardware 3. Continuous polling is not as efficient and is more stressful on low performance hardware
@ -46,7 +51,7 @@ setup(
8. On-demand scripts can be tweaked to allow for delays with slow hardware 8. On-demand scripts can be tweaked to allow for delays with slow hardware
nzbToMedia is an on-demand post-processing script and was created out of a demand for more efficient post-processing on low-performance hardware. Many features have been added so higher performance hardware can benefit too. nzbToMedia is an on-demand post-processing script and was created out of a demand for more efficient post-processing on low-performance hardware. Many features have been added so higher performance hardware can benefit too.
Many issues that users have with folder scanning can be fixed by switching to on-demand. A whole class of support issues can be eliminated by using nzbToMedia. Many issues that users have with folder scanning can be fixed by switching to on-demand. A whole class of support issues can be eliminated by using nzbToMedia.
""", """,
author='Clinton Hall', author='Clinton Hall',

View file

@ -1 +1,8 @@
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)
__author__ = 'Justin' __author__ = 'Justin'

View file

@ -1,48 +1,52 @@
#! /usr/bin/env python #! /usr/bin/env python
from __future__ import print_function from __future__ import (
import datetime absolute_import,
import os division,
import sys print_function,
unicode_literals,
)
import core
def test_eol(): def test_eol():
import eol import eol
eol.check() eol.check()
def test_cleanup(): def test_cleanup():
import cleanup import cleanup
cleanup.clean(cleanup.FOLDER_STRUCTURE) cleanup.clean(cleanup.FOLDER_STRUCTURE)
def test_import_core(): def test_import_core():
import core pass
from core import logger, main_db
def test_import_core_auto_process(): def test_import_core_auto_process():
from core.auto_process import comics, games, movies, music, tv pass
from core.auto_process.common import ProcessResult
def test_import_core_plugins(): def test_import_core_plugins():
from core.plugins.downloaders.nzb.utils import get_nzoid pass
from core.plugins.plex import plex_update
def test_import_core_user_scripts(): def test_import_core_user_scripts():
from core.user_scripts import external_script pass
def test_import_six(): def test_import_six():
from six import text_type pass
def test_import_core_utils(): def test_import_core_utils():
from core.utils import ( pass
char_replace, clean_dir, convert_to_ascii,
extract_files, get_dirs, get_download_info,
update_download_info_status, replace_links,
)
import core
from core import logger, main_db
def test_initial(): def test_initial():
core.initialize() core.initialize()
del core.MYAPP del core.MYAPP
def test_core_parameters(): def test_core_parameters():
assert core.CHECK_MEDIA == 1 assert core.CHECK_MEDIA == 1

View file

@ -1,14 +1,14 @@
#! /usr/bin/env python #! /usr/bin/env python
from __future__ import print_function from __future__ import (
import datetime absolute_import,
import os division,
import sys print_function,
import json unicode_literals,
import time )
import requests
import core import core
from core import logger, transcoder from core import transcoder
def test_transcoder_check(): def test_transcoder_check():
assert transcoder.is_video_good(core.TEST_FILE, 0) == True assert transcoder.is_video_good(core.TEST_FILE, 0) is True

146
tox.ini Normal file
View file

@ -0,0 +1,146 @@
; a generative tox configuration, see: https://tox.readthedocs.io/en/latest/config.html#generative-envlist
[tox]
envlist =
clean,
check,
{py27, py35, py36, py37},
report
[testenv]
basepython =
py27: {env:TOXPYTHON:python2.7}
py35: {env:TOXPYTHON:python3.5}
py36: {env:TOXPYTHON:python3.6}
py37: {env:TOXPYTHON:python3.7}
{clean,check,report,codecov}: {env:TOXPYTHON:python3}
setenv =
PYTHONPATH={toxinidir}/tests
PYTHONUNBUFFERED=yes
passenv =
*
usedevelop = false
skip_install = true
deps =
pytest
pytest-travis-fold
pytest-cov
pywin32 ; sys.platform == 'win32'
commands =
{posargs:pytest --cov --cov-report=term-missing tests}
[flake8]
max-line-length = 79
max-doc-length = 79
verbose = 2
statistics = True
min-version = 2.7
require-code = True
exclude =
.github/
.tox/
.pytest_cache/
htmlcov/
logs/
libs/common
libs/win
libs/py2
ignore =
; -- flake8 --
; E501 line too long
; W505 doc line too long
E501, W505
; -- flake8-docstrings --
; D100 Missing docstring in public module
; D101 Missing docstring in public class
; D102 Missing docstring in public method
; D103 Missing docstring in public function
; D104 Missing docstring in public package
; D105 Missing docstring in magic method
; D107 Missing docstring in __init__
; D200 One-line docstring should fit on one line with quotes
; D202 No blank lines allowed after function docstring
; D205 1 blank line required between summary line and description
; D400 First line should end with a period
; D401 First line should be in imperative mood
; D402 First line should not be the function's "signature"
D100, D101, D102, D103, D104, D105, D107
; -- flake8-future-import --
; x = 1 for missing, 5 for present
; FIx6 nested_scopes 2.2
; FIx7 generators 2.3
; FIx2 with_statement 2.6
; FIx1 absolute_import 3.0
; FIx0 division 3.0
; FIx3 print_function 3.0
; FIx4 unicode_literals 3.0
; FIx5 generator_stop 3.7
; ???? annotations 4.0
; FI90 __future__ import does not exist
FI50, FI51, FI53, FI54
per-file-ignores =
; F401 imported but unused
; E402 module level import not at top of file
nzbTo*.py: E265, E266, E402
TorrentToMedia.py: E402
core/__init__.py: E402, F401
core/utils/__init__.py: F401
core/plugins/downloaders/configuration.py: F401
core/plugins/downloaders/utils.py: F401
libs/custom/synchronousdeluge/__init__.py: F401
[testenv:check]
deps =
flake8
flake8-bugbear
flake8-commas
flake8-comprehensions
flake8-docstrings
flake8-future-import
flake8-quotes
skip_install = true
commands =
; ** PRIMARY TESTS **
; Run flake8 tests (with plugins) using default test selections
flake8
; ** SELECTIVE TESTS **
; Run flake8 tests (with plugins) for specific optional codes defined below
; -- flake8 --
; E123 closing bracket does not match indentation of opening brackets 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.
flake8 --select=B902,B903,E123,E226,E241,E242,E704,W504,W505
[coverage:run]
omit =
libs/*
[testenv:codecov]
deps =
codecov
skip_install = true
commands =
coverage xml --ignore-errors
codecov []
[testenv:report]
deps = coverage
skip_install = true
commands =
coverage report
coverage html
[testenv:clean]
commands = coverage erase
skip_install = true
deps = coverage