This is a framework that makes NZBGet Script development easier to do. It simplifies tons of commands that we all do on a regular basis (such as scan for files, parse NZB-Files, pass information back and forth to NZBGet, etc. The list goes on and on.
As of v0.6.4 the framework takes a step into fully supporting SABnzbd users too. The bridging of the communities allows us to write 1 script to support both platforms out of the box.
pynzbget Details
Author: Chris Caron <lead2gold@gmail.com>
Release Date: Sept 20th, 2019
License: GPLv3
Source: GitHub / Wiki
PyPi: Pypi Link; you can install it from pypi using pip:
Code: Select all
pip install pynzbget
- Sept 20th, 2019 Update (v0.6.4):
- Python v3.x compatibility fixes
- Jun 4th, 2019 Update (v0.6.3):
- Python v3.x compatible (in addition to Python v2.7)
- Oct 29th, 2017 Update (v0.6.1):
- Full SABnzbd support: Write a single script, have it work in both NZBGet and SABnzbd!
- Added support to parse and compile regular expressions from fetched information read from the scripts configuration.
- Jul 9th, 2017 Update (v0.5.1):
Added the ability to easily adapt the inline configuration checks into the script; see docs below. - Jan 22nd, 2017 Update (v0.3.1):
A minor bugfix handling an issue introduced in v0.3.0 - Nov 22nd, 2016 Update (v0.3.0):
Lots of enhancements since the scripts original creation back in Aug 2014:- Added PID-File Management; this feature will be even better once NZBGet v18 comes out with this enhancement.
- Parse user= and pass= out of the URL string.
- Improved Error Handling for RPC
- NZBGet Feed Script support
- Support for Microsoft Directory and Network Paths
- Added test suite (lots and lots of unit tests)
- It has a built in NZB-File parser that automatically fetches all of the meta content right away.
- It has a built in SQLite Database to allow you to set global variables in one script that can be retrieved later from another.
- Logging is built right into it and works out of the box; this includes a built in debug mode for developing and troubleshooting others.
- It greatly simplifies all Environment variables into an easy to reference set() and get() call. No need to reference NZBPO_, NZBSP_, NZBOP_, NZBPP_, NZBFP_ prefixes in the environment variables. The same function supports SABnzbd by controlling the SAB_ environment variables!
- push() functions which let you easily pass content back to the calling NZBGet Server.
- It supports multi-script operations: write a common script for both Scanning, Post Processing, Scheduling Queue, and Feed Scripting. There is no restriction to the combinations either; you can mix and match the different script types you wish to support in one single script. It fully supports SABnzbd Post Processing scripts as well. Multi-scripts can work seamlessly in the SABnzbd environment just as they do in NZBGet.
- the return codes are translated for you. Just return:
- True (EXIT_CODE.SUCCESS - 93 or SABnzbd - 0)
- False (EXIT_CODE.FAILURE - 94 or SABnzbd - 1)
- None (EXIT_CODE.NONE - 95 or SABnzbd - 0)
- It easily incorporates integration to a command Line Interface for you (CLI) making it easy to debug and test your code without having to use NZBGet or SABnzbd to do so.
- The Server API has been built into this framework too making it easy to query for Log Files and more.
- The entire framework is on GitHub and contains a test suite for each of the backend functions; not to mention they are all heavily documented.
- Plus a whole lot more....
A PostProcess Example:
Code: Select all
# A Post-Process Example
from nzbget import PostProcessScript
# Now define your class while inheriting the rest
class MyPostProcessScript(PostProcessScript):
def main(self, *args, **kwargs):
if not self.validate():
# No need to document a failure, validate will do that
# on the reason it failed anyway
return False
# need an environment varable?, don't bother trying
# to memorize NZBPO_, NZBOP_, NZBPP_ , etc.
# Just get it right away as it's identified in the system
directory = self.get('DIRECTORY') #NZBPP_DIRECTORY
# yes, defaults are supported
user_option = self.get('UserOption', 'yes')
# Take advantage of NZBGet's content passing
# automates sending [NZB] entries to server
self.push('Key', 'Value')
# Or use the built in SQLite database that can be
# shared across scripts call set() here and
# call get() later in another script
self.set('PersistentKey', 'AValueToGoWithIt')
# log to the screen
self.logger.detail('detail message')
self.logger.info('normal message')
self.logger.error('error message')
self.logger.warning('warning message')
# debugging, just use this:
self.logger.debug('debug message')
# return a dictionary of all files inside your directory your processing
# supports regex filtering, prefix filtering and suffix filtering (and a combination of all)
# has a flag entitle extrastats where if set to true, you'll get the filesizes and modification
# times of the files. It handles use processing of filters (hence, you don't need to clean
# up the filters before you pass in what was once an option, it'll figure it out for you)
files = self.get_files()
# just want the files (not the dictionary)?
files = self.get_files().keys()
# does the NZB-File you used have meta information? Well then it's already available
# to you. keys are not case-senstive, so `PROPERNAME` will fetch you the same thing
# `propername` does.
name = self.nzb_get('name')
propername = self.nzb_get('propername')
# Don't memorize return codes, (the framework handles it for you):
# - just return True if all is good, this translates to an NZBGet.SUCCESS (93)
# - just return False if you had a hard failure, this translates to an NZBGet.FAILURE (94)
# - just return None if nothing changed, this translates to an NZBGet.NONE (95)
# that said, go ahead and return the exit code if you want.. they still work too.
return True
# Call your script as follows:
if __name__ == "__main__":
from sys import exit
# Create an instance of your Script
script = MyPostProcessScript()
# call run() and exit() using it's returned value
exit(script.run())
You can mix and match as many of the Script classes as you want) and only the portion that needs to be called at the given time is utilized.
Code: Select all
# A MultiScript Example
from nzbget import PostProcessScript
from nzbget import SchedulerScript
# Now define your class while inheriting the rest
class MyMultiScript(SchedulerScript, PostProcessScript):
def postprocess_main(self, *args, **kwargs):
if not self.validate():
# No need to document a failure, validate will do that
# on the reason it failed anyway
return False
# Write your code here using all the variables you need
directory = self.get('DIRECTORY') #NZBPP_DIRECTORY
# Write your code
return True
def scheduler_main(self, *args, **kwargs):
if not self.validate():
# No need to document a failure, validate will do that
# on the reason it failed anyway
return False
# Write your code here using all the variables you need
directory = self.get('DESTDIR') #NZBOP_DESTDIR
# It's not case sensitive either, the above command could be also
# written like so:
directory = self.get('destdir') #NZBOP_DESTDIR
# Write all your code here or call a shared general object
# in your class so that other script types can share it's common
# functionality!
return True
# Call your script as follows:
if __name__ == "__main__":
from sys import exit
# Create an instance of your Script
script = MyMultiScript()
# call run() and exit() using it's returned value
exit(script.run())
From within your options you can now utilize the at symbol (@) to identify execution lines you wish to call.
With this framework, life is really easy... consider the following in your custom script options:
Code: Select all
############################################################################
### OPTIONS ###
#
# To check connection parameters click the button.
# TestSettings@Test My Setting
#
#
# ...
class MyScript(PostProcessScript):
"""Inheriting PostProcessScript grants you access to of the API defined
throughout this wiki
"""
def action_TestSettings(self, *args, **kwargs):
""" This function gets called whenever someone presses the TestSettings button
"""
# It's that simple...
# All of your script options will be made available to you here to do any testing
if not self.validate(keys=(
'Debug',
'MyOptionA',
'MyOptionB')):
# Fail if your options aren't found
return False
# Write any tests you want here
# When you're done, just return
return True
Code: Select all
#!/usr/bin/env python
# -*- coding: utf-8 -*-
###########################################################################
### NZBGET POST-PROCESSING SCRIPT
# Place all of your NZBGet header information here, SABnzbd just ignores it
# ...
### NZBGET POST-PROCESSING SCRIPT
###########################################################################
from nzbget import PostProcessScript
from nzbget import SABPostProcessScript
class MyMultiScript(PostProcessScript, SABPostProcessScript):
"""Inherits both the PostProcessScript and the SABPostProcessScript
This grants you all of the methods of the pynzbget framework
in addition to all the functions just used by each inherited
script.
"""
def postprocess_main(self, *args, **kwargs):
"""Write everything you'd have otherwise written for your post-process
script here.
"""
# If you used the configuration identified above, then you can do this:
myVar = self.get('MyPostProcessingVar') # returns 'Great'
# Want a specific variable from the NZBPO_.*, NZBPP_.* environment?
final_dir = self.get('FINAL_DIR') # fetches NZBPP_FINAL_DIR
temp_dir = self.get('TEMPDIR') # fetches NZBPO_TEMPDIR
# Keys are not case sensitive, so consider the 'Debug' option defined
# above. You can fetch it like this:
debug = self.get('Debug') # or self.get('DEBUG') works too
# Return True if you were successful
# Return False if you experienced a failure
# Return None if you just have nothing bad or good to report
return True
def sabnzbd_postprocess_main(self, *args, **kwargs):
"""Write everything you'd have otherwise written for your SABnzbd
script here.
"""
# Want a specific variable from the SAB_.* environment?
taskid = self.get('nzo_id') # fetches SAB_NZO_ID
final_dir = self.get('complete_dir') # fetches SAB_COMPLETE_DIR
# Write your custom code here
# Return True if you were successful
# Return False if you experienced a failure
# Return None if you just have nothing bad or good to report
return True
# Call your script as follows:
if __name__ == "__main__":
from sys import exit
# Create an instance of your Script
script = MyMultiScript()
# call run() and exit() using it's returned value
# The code will automatically return the proper value for the
# appropriate system. For example SABnzbd expects zero (0) on success
# and a non-zero on a failure meanwhile NZBGet uses values like 93, 94, etc
# to determine the status. This is automatically handled for you
exit(script.run())
Additional Notes:
The following plugins are all built upon pynzbget:
- NZBGet Subliminal Plugin: Subtitle Fetching
- NZBGet Notifications: Email, PushBullet, etc all in one
- NZBGet DirWatch Plugin: Scan multiple directories for NZB-Files for NZBGet to fetch for you!
- NZBGet TidyIt Plugin: A slick media library tidy tool.