XBMC Library Updater

Come up with a useful post-processing script? Share it here!
Ashex
Newbie
Newbie
Posts: 18
Joined: February 28th, 2009, 12:20 am

Re: XBMC Library Updater

Post by Ashex »

Please do! It's great to share our work O0
scotia
Newbie
Newbie
Posts: 2
Joined: April 10th, 2013, 7:36 am

Re: XBMC Library Updater

Post by scotia »

Here's a Perl script I use to perform some automated functions on my XBMC HTPC.

A few notes:
  • It probably has bugs.
    It should use GetOpt but doesn't.
    It could be coded better but isn't (please don't tell me how it could be).
    It should be commented in-line but isn't.
    You need to have the XBMC web server enabled (I use port 8080).
    You'll need RPC::Client installed
    Most called to XBMC are asynchronous, so they return immediately. (ie, you should wait after calling a 'clean' before calling an 'update').
    Some calls only work on Frodo (RPC 6 I think), like 'GUI.ShowNotification'.
    I have a three XBMC HTPCs (one on OSX, two on ATVs) so there is code there to let me choose which one the code runs against. Delete this and hardcode '$host' if you like.
    The 'ping' code there is because my HTPC sleeps when not being used and needs to be woken sometimes. The ping call expects to run on FreeBSD. YMMV. Remove it if you have trouble.

Code: Select all

use strict;
use warnings;

use JSON::RPC::Client;
use Data::Dumper;

my $client = new JSON::RPC::Client;

my $method; my $title; my $message; my $verbose = 0; my $silent = 0; my $host = ''; my $addon_id;
my $timeout = 5; my $notify_time = 1500;

sub bail () { die "Error: please specify a method (scan/update music|video, clean music|video, library, notify, quit, ping, version, sleep, wake, fullscreen, artwork, addon 'addon')"; exit 0; };

if (defined $ARGV[0] and $ARGV[0] eq '-v') { $verbose = 1; shift @ARGV; }
if (defined $ARGV[0] and $ARGV[0] eq '-s') { $silent = 1; shift @ARGV; }

if (defined $ARGV[0] and $ARGV[0] eq 'atv-bedroom') { $host = 'atv-bedroom'; shift @ARGV; }
elsif (defined $ARGV[0] and $ARGV[0] eq 'atv-main') { $host = 'atv-main'; shift @ARGV; }
elsif (defined $ARGV[0] and $ARGV[0] eq 'htpc-main') { $host = 'htpc-main'; shift @ARGV; }
else { $host = 'htpc-main'; }
$client->ua->credentials("$host:8080",'XBMC','xbmc'=>'xbmc',);
my $pingcheck = "/sbin/ping -n -c 2 -i 1 -W 500 -o $host 2>&1 >/dev/null";

if (!defined $ARGV[0]) { bail; }

if ($ARGV[0] =~ /^(scan|update)$/i) { $method = 'Library.Scan'; $ARGV[1] or $ARGV[1] = 'video'; if ($ARGV[1] =~ m/music/i) { $method = "Audio$method" } else { $method = "Video$method" } }
elsif ($ARGV[0] =~ /^clean$/i) { $method = 'Library.Clean'; $ARGV[1] or $ARGV[1] = 'video'; if ($ARGV[1] =~ m/music/i) { $method = "Audio$method" } else { $method = "Video$method" } }
elsif ($ARGV[0] =~ /^library$/i) { $method = 'VideoLibrary.'; $timeout = 60; if ($ARGV[1] =~ m/tv/i) { $method .= 'GetTVShows' } else { $method .= 'GetMovies' } }
elsif ($ARGV[0] =~ /^notify$/i) { $method = 'GUI.ShowNotification'; $title = $ARGV[1] || 'empty'; $message = $ARGV[2] || "empty"; $notify_time = int $ARGV[3] || 1500 }
elsif ($ARGV[0] =~ /^(ping|wake)$/i) { $method = 'JSONRPC.Ping' }
elsif ($ARGV[0] =~ /^version$/i) { $method = 'JSONRPC.Version' }
elsif ($ARGV[0] =~ /^quit$/i) { $method = 'Application.Quit' }
elsif ($ARGV[0] =~ /^sleep$/i) { $method = 'System.Suspend' }
elsif ($ARGV[0] =~ /^fullscreen$/i) { $method = 'GUI.SetFullscreen' }
elsif ($ARGV[0] =~ /^addon$/i) { $method = 'Addons.ExecuteAddon'; $addon_id = $ARGV[1] or die }
elsif ($ARGV[0] =~ /^artwork$/i) { $method = 'Addons.ExecuteAddon'; $addon_id = 'script.artwork.downloader'; }
else { bail }

$client->ua->timeout($timeout);

my $url      = "http://$host:8080/jsonrpc";

my $pingobj  = { jsonrpc=>'2.0', id=>'1', method=>'JSONRPC.Ping', };
my $sleepobj = { jsonrpc=>'2.0', id=>'1', method=>'System.Suspend', };

my $json_true = JSON::true;
my $json_false = JSON::false;

my $callobj  = {
    jsonrpc  => '2.0',
    id       => '1',
    method   => $method,
};

if ($method eq 'GUI.ShowNotification') {
    $callobj = {
        %$callobj,
        params  => {
            title       => $title,
            message     => $message,
            displaytime => $notify_time,
        },
    };
}

if ($method eq 'GUI.SetFullscreen') {
    $callobj = {
        %$callobj,
        params  => {
            fullscreen => 'toggle',
        },
    };
}

if ($method eq 'Addons.ExecuteAddon') {
    $callobj = {
        %$callobj,
        params  => {
            addonid => $addon_id,
        },
    };
}

print Dumper $callobj if $verbose;

my $result = '';
my $att = 0; my $was_awake ='0';
do {
    sleep 3 if $att;
    system($pingcheck);
    if ($? and !$att and $verbose) { print "Waking...\n" }
    if (!$? and !$att) { $was_awake = 1; }
    my $call_result = $client->call($url, $pingobj);
    $result = $$call_result{content}{result};
    $att++;
} until ($result and $result eq 'pong' or $att > 5 or $was_awake);

if (!$was_awake and $result and $result ne 'pong') {
    die "Could not wake $host for operaton.";
}

if (!$was_awake and $verbose) { print "Awake now ($att).\n"; }

my $call_result = $client->call($url, $callobj);

print Dumper $call_result if $verbose;

if ($call_result) {
    if ($call_result->is_error) {
        print "Error : ", $call_result->error_message;
    } else {
        if ($method =~ m/^VideoLibrary\.Get(.*)$/) {
            (my $type = $1) =~ tr/A-Z/a-z/;
            foreach (@{$$call_result{content}{result}{$type}}) {
                print $_->{label} . "\n";
            }
        } elsif ($method =~ m/^JSONRPC\.Ping$/) {
            $result = $$call_result{content}{result};
            print "$result\n" if !$silent;
        } elsif ($method =~ m/^JSONRPC\.Version$/) {
            $result = $$call_result{content}{result}{version};
            print "$result\n";
        }
    }
#    if (!$was_awake) { print "Sleeping...\n" if $verbose; my $call_result = $client->call($url, $sleepobj) }
    exit 0;
} else {
    print $client->status_line . "\n";
    exit 1;
}
Enjoy!
Scott
drawde
Newbie
Newbie
Posts: 11
Joined: January 17th, 2012, 6:34 pm

Re: XBMC Library Updater

Post by drawde »

ericab wrote:the final command is:

Code: Select all

echo "curl -s -H \"Content-Type: application/json\" -u xbmc:xbmc -X POST -d '{\"jsonrpc\": \"2.0\", \"method\": \"VideoLibrary.Scan\", \"params\":{\"directory\":\"nfs://192.168.2.104/export/_TV/show/\"}, \"id\": \"scan\"}' http://192.168.2.33:8090/jsonrpc" | sed 's_show_'"$show"'_g' | sh
just what i needed! modified it a little bit because i only needed /movieABC/ scanned but with a few edits it working perfectly! also now i get a success output which i didn't get before.
unRAID 4.7 Pro: ASUS M4A88T-M | AMD Sempron 145 Sargas @ 2.8GHz | 1GB DDR3 SDRAM 1066 (PC3 8500) RAM | Antec NEO ECO 620C 620W PSU | NETGEAR gigE
ericab
Newbie
Newbie
Posts: 12
Joined: May 26th, 2009, 5:03 pm

Re: XBMC Library Updater

Post by ericab »

just a little update incase anyone was interested;

ive got a repo up of my library update post proc script.

https://github.com/wavrunrx/SAB-XBMC_Lib-Update

any questions, let me know !
enjoy.

** Note
it may require some modifications to work for your specific setup
magiin83
Newbie
Newbie
Posts: 4
Joined: December 16th, 2014, 1:14 pm

Re: XBMC Library Updater

Post by magiin83 »

Hi all,

new to the forum, but using SAB for years. Currently in the middle of setting up my unRAID NAS, which is all setup aside from some tweaks with SAB, which is causing me a huge headache with this library updater script.

Currently endless frustration with creating a post-processing script to go along with SABnzbd, so I thought if I opened it up to the forum, hopefully someone with some python scripting skills can help.
Here's where I'm at.
I currently have nzbgeek-report script running and workign fine, and within, on the second-script area of the script, I have the xbmc library updater script (Originally from JohnSCS on page 4 of this thread, adapted with my information, etc) that I'm trying to tailor to myself.

The NZBgeekreport goes through fine, and then the xbmc script attempts to go, but fails immediately. This is the report from SABnzbd:

[INFO] NZB OK (SABNZBD STATUS=0)
[INFO] Status OK sent to NZBgeek
[INFO] Running: /mnt/cache/apps/SABnzbd/config/post-proc/TESTING_XBMC_Notify.py
[INFO] Success : /mnt/cache/apps/SABnzbd/config/post-proc/TESTING_XBMC_Notify.py
/mnt/cache/apps/SABnzbd/config/post-proc/TESTING_XBMC_Notify.py: line 2: import: command not found
/mnt/cache/apps/SABnzbd/config/post-proc/TESTING_XBMC_Notify.py: line 3: from: command not found
/mnt/cache/apps/SABnzbd/config/post-proc/TESTING_XBMC_Notify.py: line 4:: command not found
/mnt/cache/apps/SABnzbd/config/post-proc/TESTING_XBMC_Notify.py: line 5:: command not found
/mnt/cache/apps/SABnzbd/config/post-proc/TESTING_XBMC_Notify.py: line 7: full_path: command not found
/mnt/cache/apps/SABnzbd/config/post-proc/TESTING_XBMC_Notify.py: line 9: syntax error near unexpected token `('
/mnt/cache/apps/SABnzbd/config/post-proc/TESTING_XBMC_Notify.py: line 9: `full_path=full_path.split("\\")

and so forth and so on.. Essentially, the whole script not functioning.
What could be causing this? I'm a complete newb to scripting, especially in python, but follow directions well and there's plenty of information to lead me in the right direction in here, so any help would be greatly appreciated.
I'm essentially using this script to just have SAB notify XBMC to only update the folder thats currently downloaded, NOT a full library scan.
User avatar
shypike
Administrator
Administrator
Posts: 19774
Joined: January 18th, 2008, 12:49 pm

Re: XBMC Library Updater

Post by shypike »

What does the first line of the script say?
Can you run it from an ssh session window?
magiin83
Newbie
Newbie
Posts: 4
Joined: December 16th, 2014, 1:14 pm

Re: XBMC Library Updater

Post by magiin83 »

The very first line?

#!/usr/bin/python

The nzbgeek reporting script is run, and then within:
# SECOND SCRIPT TO RUN
# It will only run if PAR and UNPACK FINISHES OK
# For windows use .cmd file. For linux use .py file (Don't need to add 'python' before the script. Only filename with fullpath.
# Dont forget to check permission
SCRIPT='/mnt/cache/apps/SABnzbd/config/post-proc/TESTING_XBMC_Notify.py'

that script is the file I'm currently running and getting that error from (which is right now a pick up of the script JohnSCS posted on page 5. Seen here, just NAS ip's and paths put in)
https://sabnzbd.org/viewtopic.php?f=9&t ... =60#p54410
User avatar
shypike
Administrator
Administrator
Posts: 19774
Joined: January 18th, 2008, 12:49 pm

Re: XBMC Library Updater

Post by shypike »

Make sure the script is in Unix format (using only LF characters for end-of-line).
MSDOS/Windows files (using CR+LF) will fail as scripts.
magiin83
Newbie
Newbie
Posts: 4
Joined: December 16th, 2014, 1:14 pm

Re: XBMC Library Updater

Post by magiin83 »

I believe that they already are, I don't see any discrepancies, but then again my eyes aren't using to looking over code. I'm really not trying to be a nuisance here, I honestly have no idea what could be wrong with this or why I'm getting the errors. The code itself looks right and is formatted right in notepad++

Code: Select all

# Python 2.6.4 | http://www.python.org/download/releases/2.6.4/
	import socket,sys,urllib2,os
	from urllib2 import Request, URLError, urlopen


# Get final folder name
full_path = sys.argv[1]
# Split into drive and folder names
full_path=full_path.split("\\")
# Shorten folder_name by removing Season folder (eg S:\TV Series\Showname\Season 1\ to S:\TV Series\Showname\)
# This helped fix some issues with episodes not being added to library
folder_name=full_path[0] + "\\" + full_path[1] + "\\" + full_path[2] + "\\" + full_path[3] + "\\"
	print folder_name
# As SAB runs on my NAS, use the following line to change from a drive letter to a SMB share
folder_name=folder_name.replace('S:', 'SMB://NAS-IP/SHARE')
# Make folder_name URL friendly
folder_name=folder_name.replace(' ', '%20')
	print folder_name

# Rename the movie file to match the folder (job) name -  ** Thanks to rudyb **
# Get the Newzbin category
cat = sys.argv[4]
# Get the folder name again
ugly_folder = sys.argv[1]
# Get the job name
ugly_jobname = sys.argv[3]
# Set movie (and related) file extensions. These are filetypes we care about. Add more if you want those renamed, too
movie_extensions = ['avi', 'mkv', 'wmv', 'ts', 'img', 'iso', 'sub', 'idx', 'srt']
print cat

def ext_count(file_list):
    # Create the extensions dictionary
    extensions = {}
    # Loop through the file list to make a list of extensions and how many there are of each
    for filename in file_list:
        # Split filename by period
        ext = filename.split('.')
        # Get the file extension
        ext = ext[-1]
        # Check if the extension exists in the array list yet
        if extensions.has_key(ext):
            extensions[ext] = extensions[ext] + 1
            # If so, add one to the existing entry
        else:
            # Otherwise, create the list entry
            extensions[ext] = 1
    return extensions


# Apply this to movies only
if (cat == "movies"):
    # Make an empty dictionary for counting how many we've renamed of each extension
    ext_tally = {}
    # Make a list (downloaded_files) containing all of the downloaded files
    downloaded_files = sorted(os.listdir(ugly_folder))
    # Create a dictionary of extensions (the key) and the number of each (the value)
    extensions = ext_count(downloaded_files)
    # Loop through the list of downloaded files
    for filename in downloaded_files:
        # We don't know if this file is relevant to our interests
        is_video = 0
        # See if the ext matches one we care about. Loop through movie_extensions
        for mov_ext in movie_extensions:
            # See if the filename ends with a relevant extension
            if filename.endswith('.' + mov_ext):
                # Flag the file as relevant
                is_video = 1
                # Stop checking (theoretically, it shouldn't have more than one extension)
                break
        # If we determined that the file was relevant...
        if is_video == 1:
            # Start building the new filename
            new_filename = ugly_jobname
            # Get the extension of the file
            file_extension = filename.split('.')[-1]
            # Check to see if there was more than one of that extension
            if extensions[file_extension] > 1:
                # If so, add " - CD" to the end
                new_filename = new_filename + ' - CD'
                # Check to see if we've already renamed one file with this extension
                if ext_tally.has_key(file_extension):
                    # If so, add one to the count
                    ext_tally[file_extension] = ext_tally[file_extension] + 1
                else:
                    # If not, create a counter and set it to 1
                    ext_tally[file_extension] = 1
                # Then append that number to the end of the filename
                new_filename = new_filename + str(ext_tally[file_extension])
            # Finally, add the extension
            new_filename = new_filename + '.' + file_extension
            # See if the new filename and the old filename match
            if filename == new_filename:
                # If so, end this iteration without renaming, and say so:
                print "Filenames are the same. Not renaming"
                continue
            # Uncomment this line to print the original filename and new filename
            print 'Renaming ' + filename + ' to ' + new_filename
            # Last, but not least, rename the file
            os.rename(ugly_folder + '\\' + filename, ugly_folder + '\\' + new_filename)






# Get current date/time and strip spaces
	from time import gmtime, strftime
	event_time = strftime("%d/%m/%y %H:%M")
	event_time=event_time.replace(' ', '%20')

# Strip illegal/unwanted chars from NZB name
job_name=job_name.replace(' ', '%20')
job_name=job_name.replace('_', '%20')
job_name=job_name.replace('.', '%20')

# Lounge Room XBMC Live PC. Note the different path for the JPG image when using AppleTV XBMC.
xbmc3=urllib2.Request("http://user:pass@ip:port/xbmcCmds/xbmcHttp?command=ExecBuiltIn(Notification(" + job_title + "," + job_name + ",20000,//NAS-IP/boot/config/plugins/sabnzbd/sabnzbd.png))")
	try: urllib2.urlopen(xbmc3)
	except URLError, e:
        print 'Lounge Room XBMC Failed'
	else:
   urllib2.urlopen("http://user:[email protected]:8080/xbmcCmds/xbmcHttp?command=ExecBuiltIn&parameter=XBMC.updatelibrary(video," + folder_name + ")")
       
Last edited by magiin83 on December 22nd, 2014, 9:41 am, edited 1 time in total.
User avatar
shypike
Administrator
Administrator
Posts: 19774
Joined: January 18th, 2008, 12:49 pm

Re: XBMC Library Updater

Post by shypike »

A script should start with something like this:
#!/usr/bin/python
Otherwise the operating system doesn't know how to run it.
Check that the path to Python is actually /usr/bin/python
Richard63
Newbie
Newbie
Posts: 17
Joined: January 18th, 2015, 3:58 pm

Re: XBMC Library Updater

Post by Richard63 »

This part is OLD for notify, you have to use JSON commands now !!

like this

wget -q -O --header="Accept: application/json" --header="Content-type: application/json" --post-data="{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"GUI.ShowNotification\",\"params\":{\"title\":\"Movie Mode\",\"message\":\"Movie Mode is ingeschakeld\",\"image\":\"http://2.bp.blogspot.com/-uERK9tX0Fs4/T ... cation.png\"}}" "http://192.168.2.4:8088/jsonrpc
rm jsonrpc"

# Lounge Room XBMC Live PC. Note the different path for the JPG image when using AppleTV XBMC.
xbmc3=urllib2.Request("http://user:pass@ip:port/xbmcCmds/xbmcHttp?command=ExecBuiltIn(Notification(" + job_title + "," + job_name + ",20000,//NAS-IP/boot/config/plugins/sabnzbd/sabnzbd.png))")
try: urllib2.urlopen(xbmc3)
except URLError, e:
print 'Lounge Room XBMC Failed'
else:
urllib2.urlopen("http://user:[email protected]:8080/xbm ... rary(video," + folder_name + ")")
Post Reply