Created
August 3, 2012 04:53
-
-
Save jkschoen/3244485 to your computer and use it in GitHub Desktop.
Script that will work with Script Launcher module in Maraschino to delete watched tv shows in xbmc.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import datetime, getopt, sys, urllib, urllib2 | |
import base64, httplib, json, os, socket | |
HOSTS = [ | |
{'host': '192.168.1.2', 'port': 8080, 'username': 'xbmc', 'password': ''}, | |
{'host': '192.168.1.3', 'port': 8080, 'username': 'xbmc', 'password': ''} | |
] | |
#XBMC access media through the network, an handles the various networking | |
#protocols. I do not. The script assumes that the media is accessible | |
#through the normal file system. So the first value is the part of | |
#the filepath that XBMC uses to access the media, that we need to | |
#replace to access the file from the system running the script. The | |
#second part is the part that it will be replaced with. If you have multiple | |
#items that may need to be replaced, just seperate them with a comma. | |
PATH_SUBS = { | |
'nfs://192.168.1.15/shares/':'/shares/drive1/' | |
} | |
#This is for when there is a show you want to hang on to and not delete, | |
#or even just a certain season of a show. You can put the path to the | |
#directory where the show or season is, and the script will skip thos | |
#items found there. | |
IGNORE_FOLDERS = [ | |
'/drive1/TV Shows/Burn Notice' | |
] | |
def main(argv): | |
try: | |
opts, args = getopt.getopt(argv, "ipsw:", ["ip=", "port=", "script_id=", "webroot="]) | |
except getopt.GetoptError: | |
sys.exit(2) | |
ip = None | |
port = None | |
script_id = None | |
webroot = None | |
for opt, arg in opts: | |
if opt in ("-i", "--ip"): | |
ip = arg | |
elif opt in ("-p", "--port"): | |
port = arg | |
elif opt in ("-s", "--script_id"): | |
script_id = arg | |
elif opt in ("-w", "--webroot"): | |
webroot = arg | |
#loop through our hosts to see if we have on that is on right now | |
for host in HOSTS: | |
try: | |
conn = JSONConnection(host['host'], host['port'], host['username'], host['password']) | |
data = conn.VideoLibrary.GetEpisodes(properties=["playcount","file"]) | |
update_status("Getting watched episodes", ip, port, webroot, script_id) | |
watched = findWatchedEpisodes(data) | |
update_status("%s Watched shows" % len(watched), ip, port, webroot, script_id) | |
deleteWatchedEpisodes(watched, ip, port, webroot, script_id) | |
update_status("Starting Clean of Library", ip, port, webroot, script_id) | |
data = conn.VideoLibrary.Clean() | |
finished(ip, port, webroot, script_id) | |
break | |
except socket.error: | |
update_status("Failed: Make sure XBMC is running", ip, port, webroot, script_id) | |
def findWatchedEpisodes(data): | |
watched = [] | |
for episode in data["episodes"]: | |
if (episode["playcount"] > 0 ): | |
watched.append(episode) | |
return watched | |
def deleteWatchedEpisodes(watched, ip=None, port=None, webroot=None, script_id=None): | |
count = len(watched) | |
for episode in watched: | |
filePath = episode["file"] | |
#first replace prefix with mounted location | |
modPath = filePath | |
for key, value in PATH_SUBS.iteritems(): | |
if filePath.startswith(key): | |
modPath = filePath.replace(key, value, 1) | |
break | |
ignoreThisOne = False; | |
for ignore in IGNORE_FOLDERS: | |
if modPath.startswith(ignore): | |
ignoreThisOne = True; | |
continue | |
if (ignoreThisOne == False): | |
#then get filename without extension | |
filename = os.path.basename(modPath) | |
minusExt = os.path.splitext(filename)[0] | |
#then delete all files found in the same directory that start with filename regardless of extension | |
folderPath = os.path.dirname(modPath) | |
for subdir, dirs, files in os.walk(folderPath): | |
for file in files: | |
if (os.path.splitext(file)[0] == minusExt): | |
fullfilePath = os.path.join(folderPath, file) | |
try: | |
os.unlink(fullfilePath) | |
except: | |
pass | |
count = count - 1 | |
update_status("%s Watched shows" % count, ip, port, webroot, script_id) | |
def finished(ip=None, port=None, webroot=None, script_id=None): | |
now = datetime.datetime.now() | |
update_status("Last Ran: %s" % now.strftime("%m-%d-%Y %H:%M"), ip, port, webroot, script_id) | |
def update_status(status, ip=None, port=None, webroot=None, script_id=None): | |
if script_id == None or ip == None or port == None: | |
return | |
path = None | |
if webroot: | |
path='http://%s:%s/%s/xhr/script_launcher/script_status/%s' % (ip, port, webroot, script_id) | |
else: | |
path='http://%s:%s/xhr/script_launcher/script_status/%s' % (ip, port, script_id) | |
data = [('status', status)] | |
data=urllib.urlencode(data) | |
req=urllib2.Request(path, data) | |
req.add_header("Content-type", "application/x-www-form-urlencoded") | |
open=urllib2.urlopen(req) | |
page = open.read() | |
# I found this code on the XBMC Forums somewhere if I remember right, but I con not | |
# find it again. If you know where it is please let me know, so I can add a link to this. | |
__all__ = ['JSONConnection', 'JSONRPCError', 'JSONConnectionError', 'JSONUserPassError'] | |
class JSONError(Exception): | |
pass | |
class JSONRPCError(JSONError): | |
def __init__(self, code, message): | |
JSONError.__init__(self, message) | |
self.code = code | |
self.message = message | |
class JSONConnectionError(JSONError): | |
def __init__(self, code, message): | |
JSONError.__init__(self, message) | |
self.code = code | |
self.message = message | |
class JSONUserPassError(JSONError): | |
pass | |
class JSONConnection(object): | |
# def __init__(self, host='192.168.1.2', port=8080, username='xbmc', password=''): | |
def __init__(self, host='', port=0, username='', password=''): | |
self.host = host | |
self.port = port | |
self.username = username | |
self.password = password | |
self._namespace_cache = {} | |
def __getattr__(self, namespace): | |
if namespace in self._namespace_cache: | |
return self._namespace_cache[namespace] | |
nsobj = self.Namespace(namespace, self) | |
self._namespace_cache[namespace] = nsobj | |
return nsobj | |
class Namespace(object): | |
def __init__(self, name, connection): | |
self.name = name | |
self.connection = connection | |
self._id = 1 | |
self._handler_cache = {} | |
def __getattr__(self, method): | |
if method in self._handler_cache: | |
return self._handler_cache[method] | |
def handler(**kwargs): | |
data = {'jsonrpc': '2.0', | |
'id': self._id, | |
'method': '%s.%s' % (self.name, method) | |
} | |
if kwargs: | |
data['params'] = {} | |
for k in kwargs: | |
data['params'][k] = kwargs[k] | |
postdata = json.dumps(data, ensure_ascii=False) | |
postdata = postdata.encode('utf-8') | |
headers = {'Content-Type': 'application/json-rpc; charset=utf-8'} | |
if self.connection.password != '': | |
userpass = base64.encodestring('%s:%s' % \ | |
(self.connection.username, self.connection.password))[:-1] | |
headers['Authorization'] = 'Basic %s' % userpass | |
conn = httplib.HTTPConnection(self.connection.host, | |
self.connection.port) | |
conn.request('POST', '/jsonrpc', postdata, headers) | |
data = None | |
response = conn.getresponse() | |
if response.status == httplib.OK: | |
data = response.read() | |
conn.close() | |
if response.status != httplib.OK: | |
if response.status == httplib.UNAUTHORIZED: | |
raise JSONUserPassError() | |
else: | |
raise JSONConnectionError(response.status, | |
'Connection Error: %s' % httplib.responses[response.status]) | |
conn.close() | |
self._id += 1 | |
if data is not None: | |
response = json.loads(data) | |
if 'error' in response: | |
raise JSONRPCError(json['error']['code'], | |
json['error']['message']) | |
return response['result'] | |
else: | |
return None | |
handler.method = method | |
self._handler_cache[method] = handler | |
return handler | |
if __name__ == '__main__': | |
main(sys.argv[1:]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment