[PP-Script] NotifyPlex - Library Update and GUI Notification

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.
mannibis
Posts: 58
Joined: 29 Jul 2014, 15:10

[PP-Script] NotifyPlex - Library Update and GUI Notification

Post by mannibis » 17 Aug 2014, 20:37

The PP-Script will call a targeted Plex refresh/update upon successful download and send a GUI Notification to Plex Home Theater.
If snatched via a NewzNab+ indexer, the script can use Direct NZB headers to display the ProperName and EpisodeName in the notification. This is now a new option called "dHeaders". If dHeaders is set to "No" or the indexer does not support DNZB Headers, the full NZB name will be used in the notification.

Library Refresh Mode has now been implemented

Users no longer have to enter in section numbers or copy the script to refresh their TV shows and Movies separately. Library Refresh Mode is a new option that allows the user to choose how they want to script to refresh their library. 'Auto' mode will automatically refresh TV Shows and Movies sections. The script will detect the NZBGet category and also auto-detect the Plex sections by polling the Plex Server's API to figure out what sections are for TV Shows and what sections are for Movies. The plexSections setting has been changed to customPlexSections, if the user would like to scan a section other than TV Shows or Movies. The Custom sections will only be scanned if the Library Refresh Mode is set to Custom or Both.

New settings are movieCat and tvCat. You will have to specify which NZBGet category(s) that you created are intended for movies, and which ones are intended for TV Shows

Sending a GUI Notification to Plex Home Theater client is supported by specifying the IP(s) where PHT is installed. Sending GUI Notifications to multiple clients is now supported.

The script has also been updated to support Plex Home. It will now authenticate with Plex.tv first using HTTP Basic Auth, which is now required for all API commands/methods if Plex Home is enabled. Note that you will need to add your username and password to the NotifyPlex settings (required).

Any feedback or feature requests are welcome.

Requirements: NZBGet v13+ and Python 2.x with requests module

Installation Instructions
  • Download NotifyPlex.zip and extract NotifyPlex folder into your NZBGet Post-Processing Scripts Directory
  • If using VideoSort or other Sort/Rename Scripts, run NotifyPlex after those scripts have sorted/renamed your files.
  • Configure Options in WebUI, Save Changes, Reload NZBGet
  • Any duplicate scripts (from previous versions) can be deleted. Only one NotifyPlex.py is needed for Movie/TV show/Custom section(s)
  • If requests module is missing, do "pip install requests" from your command line to install
  • If linux permissions issues arise, do "chmod +x NotifyPlex.py from your command line

Code: Select all

#!/usr/bin/env python2
#
##############################################################################
### NZBGET POST-PROCESSING SCRIPT                                          ###

# Post-Processing Script to Update Plex Library and Notify PHT.
#
# This script triggers a targeted library update to your Plex Media Server and sends a GUI Notification to Plex Home Theater.
# Auto-Detection of NZBGet category and Plex sections is now supported. This script also works with Plex Home enabled.
#
# Copyright (C) 2019 mannibis
# Version 2.5.0
#
#
# NOTE: This script requires Python 2.x and the "requests" module to be installed on your system.

##############################################################################
### OPTIONS                                                                ###

## General

# Refresh Plex Library (yes,no).
#
# Activate if you want NotifyPlex to refresh your Plex library
#refreshLibrary=yes

# Send GUI Notification to Plex Home Theater (yes,no).
#
# Activate if you want NotifyPlex to Send a GUI notification to Plex Home Theater
#guiShow=yes

# Use Direct NZB ProperName for notification (yes,no).
#
# Activate if you want to use the DNZB Header ProperName for the title of the media if available
#dHeaders=yes

## Plex Media Server

# Plex Media Server Settings.
#
# Host IP of your Plex Media Server including port (only 1 server is supported)
#plexIP=192.168.1.XXX:32400

# Plex.tv Username [Required]
#plexUser=
# Plex.tv Password [Required]
#plexPass=

# Library Refresh Mode (Auto,Custom,Both).
#
# Select Refresh Mode: Auto will automatically detect your NZBGet category and refresh the appropriate sections, Custom will only refresh the sections you input into the Custom Sections setting below, Both will auto-detect and refresh the Custom Sections
#refreshMode=Auto

# NZBGet Movies Category/Categories [Required for Auto Mode].
#
# List the name(s) of your NZBGet categories (CategoryX.Name) that correspond to Movies (Comma Separated)
#moviesCat=movies

# NZBGet TV Category/Categories [Required for Auto Mode].
#
# List the name(s) of your NZBGet categories (CategoryX.Name) that correspond to TV Shows (Comma Separated)
#tvCat=tv

# Custom Plex Section(s) you would like to update [Optional].
#
# Section Number(s) corresponding to your Plex library (comma seperated). These sections will only refreshed if Library Refesh Mode is set to Custom or Both
#customPlexSection=

## Plex Home Theater

# Plex Home Theater Settings [Optional].
#
# Host IP(s) of your Plex Home Theater client(s) (comma separated)
#clientsIP=192.168.1.XXX

# Use Silent Failure Mode (yes,no).
#
# Activate if you want NZBGet to report a SUCCESS status regardless of errors, in cases where PMS is offline.
#silentFailure=no

### NZBGET POST-PROCESSING SCRIPT                                          ###
##############################################################################

import os
import sys
import requests
import json
import xml.etree.ElementTree as ET

POSTPROCESS_SUCCESS = 93
POSTPROCESS_ERROR = 94
POSTPROCESS_NONE = 95

if 'NZBPP_STATUS' not in os.environ:
	print('*** NZBGet post-processing script ***')
	print('This script is supposed to be called from NZBGet v13.0 or later.')
	sys.exit(POSTPROCESS_ERROR)

required_options = ('NZBPO_SILENTFAILURE', 'NZBPO_MOVIESCAT', 'NZBPO_TVCAT', 'NZBPO_REFRESHMODE', 'NZBPO_REFRESHLIBRARY', 'NZBPO_DHEADERS', 'NZBPO_GUISHOW', 'NZBPO_PLEXUSER', 'NZBPO_PLEXPASS')
for optname in required_options:
	if optname not in os.environ:
		print('[ERROR] NOTIFYPLEX: OPTION %s IS MISSING IN CONFIGURATION FILE. PLEASE CHECK SCRIPT SETTINGS' % optname[6:])
		sys.exit(POSTPROCESS_ERROR)

# Check to see if download was successful
pp_status = os.environ['NZBPP_STATUS'].startswith('SUCCESS/')

dnzboptions = ('NZBPR__DNZB_PROPERNAME', 'NZBPR__DNZB_EPISODENAME', 'NZBPR__DNZB_MOVIEYEAR')
if dnzboptions[0] in os.environ:
	proper_name = os.environ[dnzboptions[0]]
else:
	proper_name = ''
if dnzboptions[1] in os.environ:
	proper_ep = os.environ[dnzboptions[1]]
else:
	proper_ep = ''
if dnzboptions[2] in os.environ:
	proper_year = os.environ[dnzboptions[2]]
else:
	proper_year = ''

nzb_name = os.environ['NZBPP_NZBNAME']
nzb_cat = os.environ['NZBPP_CATEGORY']
gui_show = os.environ['NZBPO_GUISHOW'] == 'yes'
plex_username = os.environ['NZBPO_PLEXUSER']
plex_password = os.environ['NZBPO_PLEXPASS']
refresh_library = os.environ['NZBPO_REFRESHLIBRARY'] == 'yes'
refresh_mode = os.environ['NZBPO_REFRESHMODE']
silent_mode = os.environ['NZBPO_SILENTFAILURE'] == 'yes'


def get_auth_token(plex_user, plex_pass):
	auth_url = 'https://my.plexapp.com/users/sign_in.xml'
	auth_params = {'user[login]': plex_user, 'user[password]': plex_pass}
	headers = {
			'X-Plex-Platform': 'NZBGet',
			'X-Plex-Platform-Version': '21.0',
			'X-Plex-Provides': 'controller',
			'X-Plex-Product': 'NotifyPlex',
			'X-Plex-Version': "2.5",
			'X-Plex-Device': 'NZBGet',
			'X-Plex-Client-Identifier': '12286'
	}
	try:
		auth_request = requests.post(auth_url, headers=headers, data=auth_params)
		auth_response = auth_request.content
		root = ET.fromstring(auth_response)
		print ('[INFO] NOTIFYPLEX: plex.tv AUTHENTICATION SUCCESSFUL')
		return root.attrib['authToken']
	except requests.Timeout or requests.ConnectionError or requests.HTTPError:
		if silent_mode:
			print ('[WARNING] NOTIFYPLEX: THERE WAS AN ERROR AUTHENTICATING WITH plex.tv. SILENT FAILURE MODE ACTIVATED')
			sys.exit(POSTPROCESS_SUCCESS)
		else:
			print ('[ERROR] NOTIFYPLEX: ERROR AUTHENTICATING WITH plex.tv')
			sys.exit(POSTPROCESS_ERROR)


def refresh_auto(movie_cats, tv_cats, plex_ip):

	movie_cats = movie_cats.replace(' ', '')
	movie_cats_split = movie_cats.split(',')
	tv_cats = tv_cats.replace(' ', '')
	tv_cats_split = tv_cats.split(',')

	params = {
		'X-Plex-Token': get_auth_token(plex_username, plex_password)
	}

	url = 'http://%s/library/sections' % plex_ip
	try:
		section_request = requests.get(url, params=params, timeout=10)
		section_response = section_request.content
	except requests.Timeout or requests.ConnectionError or requests.HTTPError:
		if silent_mode:
			print ('[WARNING] NOTIFYPLEX: ERROR AUTO-DETECTING PLEX SECTIONS. SILENT FAILURE MODE ACTIVATED')
			sys.exit(POSTPROCESS_SUCCESS)
		else:
			print ('[ERROR] NOTIFYPLEX: ERROR AUTO-DETECTING PLEX SECTIONS. CHECK NETWORK CONNECTION AND PLEX SERVER IP:PORT')
			sys.exit(POSTPROCESS_ERROR)

	root = ET.fromstring(section_response)
	movie_sections = []
	tv_sections = []

	for directory in root.findall('Directory'):
		video_type = directory.get('type')
		if video_type == 'show':
			tv_sections.append(directory.get('key'))
		elif video_type == 'movie':
			movie_sections.append(directory.get('key'))

	for tv_cat in tv_cats_split:
		if nzb_cat == tv_cat:
			for tv_section in tv_sections:
				refresh_url = 'http://%s/library/sections/%s/refresh' % (plex_ip, tv_section)
				try:
					requests.get(refresh_url, params=params, timeout=10)
				except requests.Timeout or requests.ConnectionError or requests.HTTPError:
					if silent_mode:
						print ('[WARNING] NOTIFYPLEX: ERROR UPDATING SECTION %s. SILENT FAILURE MODE ACTIVATED' % tv_section)
						sys.exit(POSTPROCESS_SUCCESS)
					else:
						print ('[ERROR] NOTIFYPLEX: ERROR OPENING URL. CHECK NETWORK CONNECTION, PLEX SERVER IP:PORT, AND SECTION NUMBERS')
						sys.exit(POSTPROCESS_ERROR)
				print ('[INFO] NOTIFYPLEX: TARGETED PLEX UPDATE FOR SECTION %s COMPLETE' % tv_section)

	for movie_cat in movie_cats_split:
		if nzb_cat == movie_cat:
			for movie_section in movie_sections:
				section_url = 'http://%s/library/sections/%s/refresh' % (plex_ip, movie_section)
				try:
					requests.get(section_url, params=params, timeout=10)
				except requests.Timeout or requests.ConnectionError or requests.HTTPError:
					if silent_mode:
						print ('[WARNING] NOTIFYPLEX: ERROR UPDATING SECTION %s. SILENT FAILURE MODE ACTIVATED' % movie_section)
						sys.exit(POSTPROCESS_SUCCESS)
					else:
						print ('[ERROR] NOTIFYPLEX: ERROR OPENING URL. CHECK NETWORK CONNECTION, PLEX SERVER IP:PORT, AND SECTION NUMBERS')
						sys.exit(POSTPROCESS_ERROR)
				print ('[INFO] NOTIFYPLEX: TARGETED PLEX UPDATE FOR SECTION %s COMPLETE' % movie_section)


def refresh_custom_sections(raw_plex_sections, plex_ip):

	plex_sections = raw_plex_sections.replace(' ', '')
	plex_sections_split = plex_sections.split(',')

	params = {
		'X-Plex-Token': get_auth_token(plex_username, plex_password)
	}

	for plex_section in plex_sections_split:
		section_url = 'http://%s/library/sections/%s/refresh' % (plex_ip, plex_section)
		try:
			requests.get(section_url, params=params, timeout=10)
		except requests.Timeout or requests.ConnectionError or requests.HTTPError:
			if silent_mode:
				print ('[WARNING] NOTIFYPLEX: ERROR UPDATING SECTION %s. SILENT FAILURE MODE ACTIVATED' % plex_section)
				sys.exit(POSTPROCESS_SUCCESS)
			else:
				print ('[ERROR] NOTIFYPLEX: ERROR OPENING URL. CHECK NETWORK CONNECTION, PLEX SERVER IP:PORT, AND SECTION NUMBERS')
				sys.exit(POSTPROCESS_ERROR)
		print ('[INFO] NOTIFYPLEX: TARGETED PLEX UPDATE FOR SECTION %s COMPLETE' % plex_section)


def show_gui_notification(raw_pht_ips):

	d_headers = os.environ['NZBPO_DHEADERS'] == 'yes'
	pht_url = raw_pht_ips.replace(' ', '')
	pht_url_split = pht_url.split(',')
	for pht_url in pht_url_split:
		if d_headers:
			if (proper_name != '') and (proper_ep != ''):
				gui_text = '%s - %s' % (proper_name, proper_ep)
			elif (proper_name != '') and (proper_year != ''):
				gui_text = '%s (%s)' % (proper_name, proper_year)
			elif (proper_name == '') and (proper_ep == ''):
				gui_text = nzb_name
			else:
				gui_text = proper_name
		else:
			gui_text = nzb_name

		pht_rpc_url = 'http://%s:3005/jsonrpc' % pht_url
		headers = {'content-type': 'application/json'}
		payload= {'id': 1, 'jsonrpc': '2.0', 'method': 'GUI.ShowNotification', 'params': {'title': 'Downloaded', 'message': gui_text}}
		try:
			requests.post(pht_rpc_url, data=json.dumps(payload), headers=headers, timeout=10)
			print ('[INFO] NOTIFYPLEX: GUI NOTIFICATION TO PHT INSTANCE SUCCESSFUL')
		except requests.exceptions.ConnectionError or requests.Timeout or requests.HTTPError:
			print ('[WARNING] NOTIFYPLEX: PHT GUI NOTIFICATION FAILED')


if pp_status:

	if gui_show:
		pht_urls = os.environ['NZBPO_CLIENTSIP']
		show_gui_notification(pht_urls)

	if refresh_library:
		plex_ip = os.environ['NZBPO_PLEXIP']
		raw_plex_section = os.environ['NZBPO_CUSTOMPLEXSECTION']
		movie_cats = os.environ['NZBPO_MOVIESCAT']
		tv_cats = os.environ['NZBPO_TVCAT']

		if refresh_mode == 'Custom':
			refresh_custom_sections(raw_plex_section, plex_ip)
		elif refresh_mode == 'Auto':
			refresh_auto(movie_cats, tv_cats, plex_ip)
		else:
			refresh_custom_sections(raw_plex_section, plex_ip)
			refresh_auto(movie_cats, tv_cats, plex_ip)

	sys.exit(POSTPROCESS_SUCCESS)

else:
	print ('[ERROR] NOTIFYPLEX: SKIPPING PLEX UPDATE BECAUSE DOWNLOAD FAILED.')
	sys.exit(POSTPROCESS_NONE)
Revision 1: Formatting Changes

Revision 2: Added ability to refresh multiple Plex sections at once. Separate multiple sections with a comma (etc. plexSection=1,2)

Revision 3: Added GUI Notification for Plex Home Theater upon successful download. URL requests are now handled by "requests" library which is included in the zip.

Revision 4: Added Authentication with Plex.tv to allow for library refreshing. This was introduced with Plex Media Server version 0.9.11.4 which includes the Plex Home feature. Also added Silent Failure mode, in case you want NZBGet to report SUCCESS if contacting/authenticating with the Plex server fails. There will still be a warning message in the logs.

Revision 5: Added Auto Mode, which will automatically detect the NZBGet category and Plex sections. Added option to Disable Refresh (for users who only want GUI notification). Added ability to send GUI notifications to multiple clients. Changed plexSections to customPlexSections for Custom mode.

Revision 6: Fixed DNZB Header for Movie Year. GUI notification now displays year of movie, i.e. "The Guest (2014)"

Revision 7: Fixed XML Parsing for Plex Sections. Script now properly detects Movie and TV sections by "type" instead of "title"

Revision 8: Did a code overhaul and changed from urllib2 to requests module. Reworked XML parsing to be faster and more efficient and also made some minor code formatting changes. Configuration options/variables have not changed. Simply replace old NotifyPlex.py with new .py and that is all.

Download Link: https://www.dropbox.com/s/ar5nf9kko66xx ... ex.py?dl=0
Last edited by mannibis on 25 Oct 2019, 18:43, edited 53 times in total.

hugbug
Developer & Admin
Posts: 7435
Joined: 09 Sep 2008, 11:58
Location: Germany

Re: [PP-Script] NotifyPlex

Post by hugbug » 19 Aug 2014, 13:54

I don't use Plex but since you've asked here is my feedback based purely on the script code and is not related to how well the script works :)
  • I would replace (1, 0) with (yes, no)

    Code: Select all

    # Auto Update Plex Library (yes, no).
    #
    # Activate if you want NotifyPlex to Automatically Update/Refresh your Plex Library
    #PlexUpdate=no
    Then where it is checked:

    Code: Select all

    	if plexUpdate=="yes":
  • Actually it looks like the script doesn't do anything if the option "PlexUpdate" is disabled. If that's true the option is superfluous because the user can just disable the script completely.
  • The description of the options must follow certain (not documented) format:

    Code: Select all

    # Plex Section you would like to Update (1 for Movies, 2 for TV, etc.)
    #plexSection=1
    • The first line of option description must end with dot. This is how NZBGet distinguish the short description from the rest. In this example there is no long description, so it's not a problem;
    • The text in braces in the first line is interpreted in a special way - it lists possible option values to choose from a combobox element. Since this is not what you wanted here, you should move that text to the long option description. The combobox wasn't shown here because you forget the dot at the end, one bug has compensated for another. :)
    • That's how it should be:

      Code: Select all

      # Plex Section you would like to Update.
      #
      #  Section numbers: 1 for Movies, 2 for TV, etc.
      #plexSection=1
  • You recommend to make a copy of the script if more than one section have to be updated. Instead you could allow for multiple sections be entered in the option plexSection (renamed to PlexSections):

    Code: Select all

    # Plex Sections you would like to Update.
    #  1 for Movies, 2 for TV, etc.
    # Multiple sections can be separated with commas.
    #PlexSections=1
    

    Code: Select all

    # This code is not tested:
    plex_sections=os.environ['NZBPO_PLEXSECTIONS'].split(',')
    for plexSection in plex_sections:
    	if plexUpdate=="1":
    		url = 'http://%s/library/sections/%s/refresh' % (plexIP, plexSection)
    
  • Small thing - there is a #-character at the line end in the description. The character is also visible in web-interface and needs to be deleted.

    Code: Select all

    # Post-Process to Update Plex Library Based on Section #
  • Also add missing trailing dots in the script description. The description (About NotifyPlex) will look better in web-interface (it detects paragraphs better).

    Code: Select all

    # Post-Process to Update Plex Library Based on Section.
    #
    # This script sends a Targeted Library Update URL to your Plex Media Server.
    #
    # NOTE: This script requires Python to be installed on your system.

mannibis
Posts: 58
Joined: 29 Jul 2014, 15:10

Re: [PP-Script] NotifyPlex

Post by mannibis » 19 Aug 2014, 14:02

Thanks for your feedback, will revise the script.

As for the copying of the script, this was to be able to set the PP-Script to certain categories. For someone with a Movies category, they would only want the Movies section to be updated, and the same for TV. This is the only way I can think of right now that would be able to do a refresh for different sections based on the category they are using the script with.

EDIT: Updated Attachment in main post to reflect formatting changes

mannibis
Posts: 58
Joined: 29 Jul 2014, 15:10

Re: [PP-Script] NotifyPlex

Post by mannibis » 22 Aug 2014, 00:40

Added ability to scan multiple Plex sections at once by separating section numbers with a comma.
Main post and script has been revised and updated.

mannibis
Posts: 58
Joined: 29 Jul 2014, 15:10

Re: [PP-Script] NotifyPlex - Library Update and GUI Notifica

Post by mannibis » 07 Sep 2014, 04:33

Added GUI Notification for Plex Home Theater

Main Post is updated with new code and URL for download.
The script now uses the requests library.

mannibis
Posts: 58
Joined: 29 Jul 2014, 15:10

Re: [PP-Script] NotifyPlex - Library Update and GUI Notifica

Post by mannibis » 22 Nov 2014, 23:00

The script has been updated to supported Plex Media Server 0.9.11.4, which at the time of writing this post is a Plex-Pass Only release. I noticed that the API call to refresh libraries wasn't working anymore and learned that you need to now authenticate with Plex.tv and get an authentication token before you can perform any API commands/methods. This should not affect users with older versions of Plex, as it only adds an authentication request, but note you will need to add your username and password to the NotifyPlex settings. You will also notice that NotifyPlex is now visible in the Devices section of Plex/Web ;)

I also added (at a user's request) Silent Failure mode, in case you want NZBGet to report SUCCESS if contacting/authenticating with the Plex server fails. There will still be a warning message in the logs. It is an option in the NotifyPlex settings called "silentFailure" and is off by default.

Main post is updated and the same link will bring you to the new updated version.

binreader
Posts: 20
Joined: 07 Oct 2014, 03:00

Re: [PP-Script] NotifyPlex - Library Update and GUI Notifica

Post by binreader » 23 Nov 2014, 02:55

thanks for the updates you workin hard!

mannibis
Posts: 58
Joined: 29 Jul 2014, 15:10

Re: [PP-Script] NotifyPlex - Library Update and GUI Notifica

Post by mannibis » 29 Nov 2014, 23:15

Big changes in this Update...

Library Refresh Mode has now been implemented

Users no longer have to enter in section numbers or copy the script to refresh their TV shows and Movies separately. Library Refresh Mode is a new option that allows the user to choose how they want to script to refresh their library. 'Auto' mode will automatically refresh TV Shows and Movies sections. The script has been completely re-designed to auto-detect the NZBGet category and also auto-detect the Plex sections by polling the Plex Server's API to figure out what sections are for TV Shows and what sections are for Movies. The plexSections setting has been changed to customPlexSections, if the user would like to scan a section other than TV Shows or Movies. The Custom sections will only be scanned if the Library Refresh Mode is set to Custom or Both.

Added option to Disable Refresh (for users who only want GUI notifications).

Added ability to send GUI notifications to multiple clients.

rubylaser
Posts: 33
Joined: 06 Feb 2014, 14:47

Re: [PP-Script] NotifyPlex - Library Update and GUI Notifica

Post by rubylaser » 12 Dec 2014, 01:05

I can't get this to work. Attached is an image showing my settings. Plexmediaserver is running on the same machine as NZBGet. Everytime the postprocessing runs, I get an error of NotifyPlex: Failure. Any ideas to get this working? I'm running plexmediaserver 0.9.11.5 with Plex Home enabled.
Attachments
notify-plex-settings.png

mannibis
Posts: 58
Joined: 29 Jul 2014, 15:10

Re: [PP-Script] NotifyPlex - Library Update and GUI Notifica

Post by mannibis » 12 Dec 2014, 01:16

Are there any logs in the messages tab you can post? I need to see what type of error you are getting. Does it say that Plex.tv authentication is successful? Check the messages tab when you run it again (Actions -> Post Process Again) and paste the output if you can.

Also, double check the names of your movies category. Usually people have one category for movies in their NZBGet settings. Is "Movies > HD" the name of a category in Settings > Categories?

Post Reply

Who is online

Users browsing this forum: No registered users and 4 guests