Script Development & Framework - pynzbget

Share your scripts or request scripts with specific features.
Forum rules
Please keep the forum clean - one topic per script. Questions not related to a specific script should be posted in Support forum.
Post Reply
l2g
Posts: 228
Joined: 27 Jun 2014, 22:13
Contact:

Script Development & Framework - pynzbget

Post by l2g » 01 Aug 2014, 19:57

What is it?
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
Updates:
  • 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)
So What Can it Do?
  • 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....
It's better to check out the GitHub page and the wiki for a full breakdown.

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())
A MultiScript Example
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())
Inline Configuration Testing:
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
A SABnzbd/NZBGet PostProcess Example:

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:
Last edited by l2g on 22 Sep 2019, 14:56, edited 7 times in total.

l2g
Posts: 228
Joined: 27 Jun 2014, 22:13
Contact:

Re: Script Development & Framework - pynzbget

Post by l2g » 23 Nov 2016, 03:59

Many new enhancements and unit tests to back them up. Incremented the version to v0.3.0 including on Pypi.

For those who are interested, here is the direct download link to v0.3.0, but it's much easier to just use PIP:

Code: Select all

pip install pynzbget
This is after all a framework; so it by itself won't do anything. But it can certainly make scripting for NZBGet (in python) easy when trying to tackle more complex situations!

l2g
Posts: 228
Joined: 27 Jun 2014, 22:13
Contact:

Re: Script Development & Framework - pynzbget

Post by l2g » 10 Jul 2017, 02:03

I realize this framework doesn't get a lot of use, but I just updated it again because my Notification Script (about to post about it next) to support the Inline Configuration Actions available to us developers in NZBGet. The inline configuration actions allow a user to call any NZBGet script (that takes advantage of the feature) and have it execute at any time.

You can tie any amount of actions to anything you want. More information can be found here.

Incremented the version to v0.5.1 including on Pypi.

For those who are interested, here is the direct download link to v0.5.1, but it's much easier to just use PIP:

Code: Select all

pip install pynzbget
This is after all a framework; so it by itself won't do anything. But it can certainly make scripting for NZBGet (in python) easy when trying to tackle more complex situations!

l2g
Posts: 228
Joined: 27 Jun 2014, 22:13
Contact:

Re: Script Development & Framework - pynzbget

Post by l2g » 29 Oct 2017, 19:02

Incremented the version to v0.6.1 including on Pypi.

The biggest change to the framework is that now it crosses over to the dark side (with respect to this forum) ;)
The framework fully supports SABnzbd now

Why would you do this?
The biggest reason for this is now i can continue to maintain the scripts I already look after, but now i can additionally cater to both active communities.

At this time SABnzbd only supports Post Processing Scripts; thus that is all this framework extends to at this time (in addition to all of the ones NZBGet already supports).

At this time the only script I added SABnzbd support to was my subtitle (nzb-subliminal) fetching script. With the framework enhancement, it only required me to add 1 function called sabnzbd_postprocess_main()

Here is how simple it is:

Code: Select all

# Subliminal.py Snip-it
# ...
# I import the new script type
from nzbget import SABPostProcessScript

# Then i appended into the SubliminalScript class i already had:
class SubliminalScript(SABPostProcessScript, PostProcessScript, SchedulerScript):
    """A wrapper to Subliminal written for NZBGet and now SABnzbd
    """
    
#  ... all of the class code resides here

   def postprocess_main(self, *args, **kwargs):
      """
      NZBGet PostProcessing Support
      """
      # This code already existed, it goes out and gets all of the subtitles for people
      # This function only gets executed during a NZBGet PostProcessing execution


   # **NEW**
   # This is the new function i added and only gets called if you're in SABnzbd and Post-Processing:
   def sabnzbd_postprocess_main(self, *args, **kwargs):
      """
      SABNZBd PostProcessing Support
      """
      # I just call the NZBGet function i already wrote above...
      return self.postprocess_main(*args, **kwargs)
      
 # Boom! Subtitle support for both Communities
For those who are interested, here is the direct download link to v0.6.1, but it's much easier to just use PIP:

Code: Select all

pip install pynzbget
This is after all a framework; so it by itself won't do anything. But it can certainly make scripting for NZBGet and now SABnzbd (in python) easy when trying to tackle more complex situations!

kloaknet
Posts: 337
Joined: 23 Jul 2014, 08:52

Re: Script Development & Framework - pynzbget

Post by kloaknet » 01 Nov 2017, 15:41

hmmm, I was afraid I had to convert the completion script towards sab too now, but it only supports post-processing : ;)

l2g
Posts: 228
Joined: 27 Jun 2014, 22:13
Contact:

Re: Script Development & Framework - pynzbget

Post by l2g » 01 Nov 2017, 21:52

lucky you! You got off easy I think! ;)

l2g
Posts: 228
Joined: 27 Jun 2014, 22:13
Contact:

Re: Script Development & Framework - pynzbget

Post by l2g » 19 Jul 2019, 22:12

Incremented the version to v0.6.3 including on Pypi.

This package is completely compatible with Python v3 and Python v2.7 now.

For those who are interested, here is the direct download link to v0.6.3, but it's much easier to just use PIP:

Code: Select all

pip install pynzbget

Post Reply

Who is online

Users browsing this forum: No registered users and 38 guests