NzbGet Scripts¶
Clean¶
Clean NZB name
- Title:
Clean.py
- Author(s): ???
Removes the following suffixes from NZB name: NZBgeek / Obfuscated / BUYMORE / Scrambled /etc... Cleans the NZB name by removing the retagged stuff (-Obfuscated, -postbox, etc).
Script
#!/usr/bin/env python3
#
##############################################################################
### NZBGET SCAN SCRIPT ###
# Clean NZB name.
#
# Removes the following suffixes from NZB name:
# NZBgeek / Obfuscated / BUYMORE / Scrambled.
#
# NOTE: This script requires Python to be installed on your system.
### NZBGET SCAN SCRIPT ###
##############################################################################
from __future__ import print_function
import os, re, sys
# Exit codes used by NZBGet
POSTPROCESS_SUCCESS=93
POSTPROCESS_ERROR=94
POSTPROCESS_SKIP=95
# Check if the script is called from NZBGet 13.0 or later
if not 'NZBOP_SCRIPTDIR' in os.environ:
print('*** NZBGet post-processing script ***')
print('This script is supposed to be called from NZBGet (13.0 or later).')
sys.exit(POSTPROCESS_ERROR)
if not 'NZBNP_NZBNAME' in os.environ:
print('[WARN] Filename not found in environment')
sys.exit(POSTPROCESS_ERROR)
fwp = os.environ['NZBNP_NZBNAME']
fwp = re.sub('(?i)-4P\.nzb$', '.nzb', fwp)
fwp = re.sub('(?i)-4Planet\.nzb$', '.nzb', fwp)
fwp = re.sub('(?i)-AsRequested\.nzb$', '.nzb', fwp)
fwp = re.sub('(?i)-AsRequested-xpost\.nzb$', '.nzb', fwp)
fwp = re.sub('(?i)-BUYMORE\.nzb$', '.nzb', fwp)
fwp = re.sub('(?i)-Chamele0n\.nzb$', '.nzb', fwp)
fwp = re.sub('(?i)-GEROV\.nzb$', '.nzb', fwp)
fwp = re.sub('(?i)-iNC0GNiTO\.nzb$', '.nzb', fwp)
fwp = re.sub('(?i)-NZBGeek\.nzb$', '.nzb', fwp)
fwp = re.sub('(?i)-Obfuscated\.nzb$', '.nzb', fwp)
fwp = re.sub('(?i)-postbot\.nzb$', '.nzb', fwp)
fwp = re.sub('(?i)-Rakuv\.nzb$', '.nzb', fwp)
fwp = re.sub('(?i)-Scrambled\.nzb$', '.nzb', fwp)
fwp = re.sub('(?i)-WhiteRev\.nzb$', '.nzb', fwp)
fwp = re.sub('(?i)-xpost\.nzb$', '.nzb', fwp)
fwp = re.sub('(?i)\[eztv\]\.nzb$', '.nzb', fwp)
fwp = re.sub('(?i)\[TGx\]\.nzb$', '.nzb', fwp)
fwp = re.sub('(?i)\[TGx\]-xpost\.nzb$', '.nzb', fwp)
fwp = re.sub('(?i)\[ettv\]\.nzb$', '.nzb', fwp)
fwp = re.sub('(?i)-WRTEAM\.nzb$', '.nzb', fwp)
fwp = re.sub('(?i)-CAPTCHA\.nzb$', '.nzb', fwp)
fwp = re.sub(r'(\-[^-.\n]*)(\-.{4})?\.nzb$', r'\1.nzb', fwp)
if fwp:
print('[NZB] NZBNAME=', fwp, sep='')
sys.exit(POSTPROCESS_SUCCESS)
HashRenamer¶
Renames hashed media files to match the source NZB
- Title:
HashRenamer.py
- Author(s): l3uddz
- URL: github.com/cloudbox/cloudbox
Part of the Cloudbox project: https://cloudbox.works Built on top of the NZBGet scripts template created by Clinton Hall. Released under GNU General Public License v2.0
Script
#!/usr/bin/env python3
#
##############################################################################
# Title: HashRenamer.py #
# Author(s): l3uddz, desimaniac #
# URL: https://github.com/l3uddz/nzbgetScripts #
# Description: Renames hashed media files to match the source NZB. #
# -- #
# Part of the Cloudbox project: https://cloudbox.works #
##############################################################################
##############################################################################
# Built on top of the NZBGet scripts template created by Clinton Hall #
# (https://github.com/clinton-hall). #
# Released under GNU General Public License v2.0 #
##############################################################################
##############################################################################
### NZBGET POST-PROCESSING SCRIPT ###
# Rename files with hashes for file name
#
# NOTE: This script requires Python to be installed on your system.
#
##############################################################################
### NZBGET POST-PROCESSING SCRIPT ###
##############################################################################
import os
import re
import shutil
import sys
# NZBGet Exit Codes
NZBGET_POSTPROCESS_PARCHECK = 92
NZBGET_POSTPROCESS_SUCCESS = 93
NZBGET_POSTPROCESS_ERROR = 94
NZBGET_POSTPROCESS_NONE = 95
############################################################
# EXTENSION STUFF
############################################################
def do_check():
if 'NZBOP_SCRIPTDIR' not in os.environ:
print("This script can only be called from NZBGet (11.0 or later).")
sys.exit(0)
if os.environ['NZBOP_VERSION'][0:5] < '11.0':
print("[ERROR] NZBGet Version %s is not supported. Please update NZBGet." % (str(os.environ['NZBOP_VERSION'])))
sys.exit(0)
print("Script triggered from NZBGet Version %s." % (str(os.environ['NZBOP_VERSION'])))
status = 0
if 'NZBPP_TOTALSTATUS' in os.environ:
if not os.environ['NZBPP_TOTALSTATUS'] == 'SUCCESS':
print("[ERROR] Download failed with status %s." % (os.environ['NZBPP_STATUS']))
status = 1
else:
# Check par status
if os.environ['NZBPP_PARSTATUS'] == '1' or os.environ['NZBPP_PARSTATUS'] == '4':
print("[ERROR] Par-repair failed, setting status \"failed\".")
status = 1
# Check unpack status
if os.environ['NZBPP_UNPACKSTATUS'] == '1':
print("[ERROR] Unpack failed, setting status \"failed\".")
status = 1
if os.environ['NZBPP_UNPACKSTATUS'] == '0' and os.environ['NZBPP_PARSTATUS'] == '0':
# Unpack was skipped due to nzb-file properties or due to errors during par-check
if os.environ['NZBPP_HEALTH'] < 1000:
print("[ERROR] Download health is compromised and Par-check/repair disabled or no .par2 files found. " \
"Setting status \"failed\".")
print("[ERROR] Please check your Par-check/repair settings for future downloads.")
status = 1
else:
print("[ERROR] Par-check/repair disabled or no .par2 files found, and Unpack not required. Health is " \
"ok so handle as though download successful.")
print("[WARNING] Please check your Par-check/repair settings for future downloads.")
# Check if destination directory exists (important for reprocessing of history items)
if not os.path.isdir(os.environ['NZBPP_DIRECTORY']):
print("[ERROR] Nothing to post-process: destination directory", os.environ[
'NZBPP_DIRECTORY'], "doesn't exist. Setting status \"failed\".")
status = 1
# All checks done, now launching the script.
if status == 1:
sys.exit(NZBGET_POSTPROCESS_NONE)
def get_file_name(path):
try:
file_name = os.path.basename(path)
extensions = re.findall(r'\.([^.]+)', file_name)
ext = '.'.join(extensions)
name = file_name.replace(".%s" % ext, '')
return name, ext
except Exception:
pass
return None
def is_file_hash(file_name):
hash_regexp = [
r'^[a-fA-F0-9]{40}$',
r'^[a-fA-F0-9]{32}$',
r'^[a-f0-9]{128}$',
r'^[a-zA-Z0-9]{42}$'
]
for hash in hash_regexp:
if re.match(hash, file_name):
return True
return False
def find_files(folder, extension=None, depth=None):
file_list = []
start_count = folder.count(os.sep)
for path, subdirs, files in os.walk(folder, topdown=True):
for name in files:
if depth and path.count(os.sep) - start_count >= depth:
del subdirs[:]
continue
file = os.path.join(path, name)
if not extension:
file_list.append(file)
else:
if file.lower().endswith(extension.lower()):
file_list.append(file)
return sorted(file_list, key=lambda x: x.count(os.path.sep), reverse=True)
############################################################
# MAIN
############################################################
# do checks
do_check()
# retrieve required variables
directory = os.path.normpath(os.environ['NZBPP_DIRECTORY'])
nzb_name = os.environ['NZBPP_NZBFILENAME']
if nzb_name is None:
print("[ERROR] Unable to retrieve NZBPP_NZBFILENAME")
sys.exit(NZBGET_POSTPROCESS_ERROR)
nzb_name = nzb_name.replace('.nzb', '')
print(("[INFO] Using \"%s\" for hashed filenames" % nzb_name))
print(("[INFO] Scanning \"%s\" for hashed filenames" % directory))
# scan for files
found_files = find_files(directory)
if not found_files:
print(("[INFO] No files were found in \"%s\"" % directory))
sys.exit(NZBGET_POSTPROCESS_NONE)
else:
print(("[INFO] Found %d files to check for hashed filenames" % len(found_files)))
# loop files checking for file hash
moved_files = 0
for found_file_path in found_files:
# set variable
dir_name = os.path.dirname(found_file_path)
file_name, file_ext = get_file_name(found_file_path)
# is this a file hash
if is_file_hash(file_name):
new_file_path = os.path.join(dir_name, "%s.%s" % (nzb_name, file_ext))
print(("[INFO] Moving \"%s\" to \"%s\"" % (found_file_path, new_file_path)))
try:
shutil.move(found_file_path, new_file_path)
moved_files += 1
except Exception:
print(("[ERROR] Failed moving \"%s\" to \"%s\"" % (found_file_path, new_file_path)))
print(("[INFO] Finished processing \"%s\", moved %d files" % (directory, moved_files)))
sys.exit(NZBGET_POSTPROCESS_SUCCESS)
WtFnZb-Renamer¶
Renames hashed media files to match the source NZB
- Title:
WtFnZb-Renamer.py
- Author(s): WtFnZb
- URL: ??
NZBGET SCAN SCRIPT
Extract filenames from subjects containing [PRiVATE]-[WtFnZb]
This extensions extracts obfuscated filenames from .nzb files
Script
#!/usr/bin/env python3
### NZBGET SCAN SCRIPT
# Extract filenames from subjects containing [PRiVATE]-[WtFnZb]
#
# This extensions extracts obfuscated filenames from .nzb files
# created by WtFnZb.
#
# Supported subject formats:
#
# - [PRiVATE]-[WtFnZb]-[filename]-[1/5] - "" yEnc 0 (1/1)"
#
# - [PRiVATE]-[WtFnZb]-[5]-[1/filename] - "" yEnc
#
#
# NOTE: Requires Python and lxml (sudo apt install python3-lxml python-lxml)
#
### NZBGET SCAN SCRIPT
import sys
import os
import re
# Exit codes used by NZBGet
POSTPROCESS_SUCCESS = 93
POSTPROCESS_NONE = 95
POSTPROCESS_ERROR = 94
try:
from lxml import etree
except ImportError:
print(u'[ERROR] Python lxml required. Please install with "sudo apt install python-lxml" or "pip install lxml".')
sys.exit(POSTPROCESS_ERROR)
patterns = (
re.compile(r'^(?P<prefix>.*\[PRiVATE\]-\[WtFnZb\]-)'
r'\[(?P<total>\d+)\]-\[(?P<segment>\d+)\/(?P<filename>.{3,}?)\]'
r'\s+-\s+""\s+yEnc\s+',
re.MULTILINE | re.UNICODE),
re.compile(r'^(?P<prefix>.*\[PRiVATE\]-\[WtFnZb\]-)'
r'\[(?P<filename>.{3,}?)\]-\[(?P<segment>\d+)/(?P<total>\d+)\]'
r'\s+-\s+""\s+yEnc\s+',
re.MULTILINE | re.UNICODE))
nzb_dir = os.getenv('NZBNP_DIRECTORY')
nzb_filename = os.getenv('NZBNP_FILENAME')
nzb_name = os.getenv('NZBNP_NZBNAME')
nzb_file_naming = os.getenv('NZBOP_FILENAMING')
if nzb_dir is None or nzb_filename is None or nzb_name is None:
print('Please run as NZBGet plugin')
sys.exit(POSTPROCESS_ERROR)
if nzb_file_naming is not None and nzb_file_naming.lower() != 'nzb':
print(u'[ERROR] NZBGet setting FileNaming (under Download Queue) '
u'must be set to "Nzb" for this extension to work correctly, exiting.')
sys.exit(POSTPROCESS_ERROR)
if not os.path.exists(nzb_dir):
print('[ERROR] NZB directory doesn\'t exist, exiting')
sys.exit(POSTPROCESS_ERROR)
if not nzb_filename.lower().endswith('.nzb'):
print(u'[ERROR] {} is not a .nzb file.'.format(nzb_filename))
sys.exit(POSTPROCESS_ERROR)
nzb = os.path.join(nzb_dir, nzb_filename)
if not os.path.exists(nzb):
print('[ERROR] {nzb} doesn\'t exist, exiting'.format(nzb=nzb))
sys.exit(POSTPROCESS_ERROR)
with open(nzb, mode='rb') as infile:
tree = etree.parse(infile)
changed = False
file_count = 0
totals = set()
filenames = set()
for f in tree.getiterator('{http://www.newzbin.com/DTD/2003/nzb}file'):
subject = f.get('subject')
if subject is None:
print(u'[DETAIL] No subject in <file>, skipping')
continue
file_count += 1
result = [re.match(pattern, subject) for pattern in patterns]
matched = [m for m in result if m is not None]
if len(matched) == 0:
print(u'[INFO] No pattern matching subject, exiting.')
sys.exit(POSTPROCESS_NONE)
elif len(matched) > 1:
print(u'[ERROR] Multiple patterns matched, exiting.')
sys.exit(POSTPROCESS_ERROR)
else:
match = matched[0].groupdict()
if match['filename'].lower().endswith('.par2'):
print(u'[INFO] par2 exists, exiting')
sys.exit(POSTPROCESS_NONE)
if int(match['segment']) > int(match['total']):
print(u'[DETAIL] Segment index is greater then total, skipping')
continue
# NZBGet subject parsing changes when duplicate filenames are present
# prefix duplicates to avoid that
if match['filename'] in filenames:
match['filename'] = u'{}.{}'.format(file_count, match['filename'])
filenames.add(match['filename'])
s = u'WtFnZb "{filename}" yEnc ({segment}/{total})'.format(
filename = match['filename'],
segment = match['segment'],
total = match['total'])
print(u'[INFO] New subject {subject}'.format(subject=s.encode('ascii', 'ignore')))
f.set('subject', s)
changed = True
totals.add(int(match['total']))
if not changed:
print(u'[WARNING] No subject changed, exiting.')
sys.exit(POSTPROCESS_NONE)
if len(totals) != 1:
print(u'[WARNING] Mixed values for number of total segments, exiting.')
sys.exit(POSTPROCESS_NONE)
if totals.pop() != file_count:
print(u'[WARNING] Listed segment count does not match <file> count, exiting.')
sys.exit(POSTPROCESS_NONE)
org = u'{}.wtfnzb.original.processed'.format(nzb)
exists_counter = 0
while os.path.exists(org):
exists_counter += 1
org = u'{}.{}.wtfnzb.original.processed'.format(nzb, exists_counter)
print(u'[INFO] Preserving original nzb as {}'.format(org))
os.rename(nzb, org)
print(u'[INFO] Writing {}'.format(nzb))
with open(nzb, mode='wb') as outfile:
outfile.write(etree.tostring(tree,
xml_declaration=True,
encoding=tree.docinfo.encoding,
doctype=tree.docinfo.doctype))
sys.exit(POSTPROCESS_SUCCESS)
Tip
This script doesn't always work and is often needed if you use a certain indexer.
It might be better to use the following Sonarr Regex in your release profile
Last update:
May 31, 2021 19:50:32