modified: data/interfaces/default/config.html

modified:   headphones/__init__.py
	modified:   headphones/postprocessor.py
	new file:   headphones/prowl.py
	modified:   headphones/sab.py
	modified:   headphones/webserve.py
	new file:   lib/httplib2/__init__.py
	new file:   lib/httplib2/iri2uri.py
This commit is contained in:
patrick
2012-01-15 13:59:05 +01:00
parent 0a13c92a20
commit c35259ea94
8 changed files with 1442 additions and 6 deletions

View File

@@ -321,6 +321,14 @@
<br><br>
<h3>Log Directory:</h3><input type="text" name="log_dir" value="${config['log_dir']}" size="50">
</td>
<td>
<h2>Prowl Notification:</h2>
<br>
<h3><input type="checkbox" name="prowl_enabled" value="1" ${config['prowl_enabled']} />Enabled?</h3><br>
<h3>API key:</h3><input type="text" name="prowl_keys" value="${config['prowl_keys']}" size="50"><br><br>
<h3><input type="checkbox" name="prowl_onsnatch" value="1" ${config['prowl_onsnatch']} />Notify on snatch?</h3><br><br>
<h3>Priority (-2,-1,0,1 or 2):</h3><input type="text" name="prowl_priority" value="${config['prowl_priority']}" size="5">
</td>
</tr>
<tr>
<td>
@@ -454,4 +462,4 @@
});
});
</script>
</%def>
</%def>

View File

@@ -130,6 +130,10 @@ ENCODEROUTPUTFORMAT = None
ENCODERQUALITY = None
ENCODERVBRCBR = None
ENCODERLOSSLESS = False
PROWL_ENABLED = True
PROWL_PRIORITY = 1
PROWL_KEYS = None
PROWL_ONSNATCH = True
def CheckSection(sec):
""" Check if INI section exists, if not create it """
@@ -191,7 +195,7 @@ def initialize():
NZBMATRIX, NZBMATRIX_USERNAME, NZBMATRIX_APIKEY, NEWZNAB, NEWZNAB_HOST, NEWZNAB_APIKEY, \
NZBSORG, NZBSORG_UID, NZBSORG_HASH, NEWZBIN, NEWZBIN_UID, NEWZBIN_PASSWORD, LASTFM_USERNAME, INTERFACE, FOLDER_PERMISSIONS, \
ENCODERFOLDER, ENCODER, BITRATE, SAMPLINGFREQUENCY, ENCODE, ADVANCEDENCODER, ENCODEROUTPUTFORMAT, ENCODERQUALITY, ENCODERVBRCBR, \
ENCODERLOSSLESS
ENCODERLOSSLESS, PROWL_ENABLED, PROWL_PRIORITY, PROWL_KEYS, PROWL_ONSNATCH
if __INITIALIZED__:
return False
@@ -203,6 +207,7 @@ def initialize():
CheckSection('Newznab')
CheckSection('NZBsorg')
CheckSection('Newzbin')
CheckSection('Prowl')
# Set global variables based on config file or use defaults
try:
@@ -290,6 +295,11 @@ def initialize():
ENCODERQUALITY = check_setting_int(CFG, 'General', 'encoderquality', 2)
ENCODERVBRCBR = check_setting_str(CFG, 'General', 'encodervbrcbr', 'cbr')
ENCODERLOSSLESS = bool(check_setting_int(CFG, 'General', 'encoderlossless', 1))
PROWL_ENABLED = bool(check_setting_int(CFG, 'Prowl', 'prowl_enabled', 0))
PROWL_KEYS = check_setting_str(CFG, 'Prowl', 'prowl_keys', '')
PROWL_ONSNATCH = bool(check_setting_int(CFG, 'Prowl', 'prowl_onsnatch', 0))
PROWL_PRIORITY = check_setting_int(CFG, 'Prowl', 'prowl_priority', 0)
if not LOG_DIR:
LOG_DIR = os.path.join(DATA_DIR, 'logs')
@@ -471,6 +481,12 @@ def config_write():
new_config['Newzbin']['newzbin_uid'] = NEWZBIN_UID
new_config['Newzbin']['newzbin_password'] = NEWZBIN_PASSWORD
new_config['Prowl'] = {}
new_config['Prowl']['prowl_enabled'] = PROWL_ENABLED
new_config['Prowl']['prowl_keys'] = PROWL_KEYS
new_config['Prowl']['prowl_onsnatch'] = PROWL_ONSNATCH
new_config['Prowl']['prowl_priority'] = PROWL_PRIORITY
new_config['General']['lastfm_username'] = LASTFM_USERNAME
new_config['General']['interface'] = INTERFACE
new_config['General']['folder_permissions'] = FOLDER_PERMISSIONS
@@ -625,4 +641,4 @@ def shutdown(restart=False, update=False):
logger.info('Restarting Headphones with ' + str(popen_list))
subprocess.Popen(popen_list, cwd=os.getcwd())
os._exit(0)
os._exit(0)

View File

@@ -2,6 +2,8 @@ import os
import time
import encode
import urllib, shutil, re
from headphones import prowl
from headphones.prowl import PROWL
import lib.beets as beets
from lib.beets import autotag
from lib.beets.mediafile import MediaFile
@@ -251,6 +253,13 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list)
updateHave(albumpath)
logger.info('Post-processing for %s - %s complete' % (release['ArtistName'], release['AlbumTitle']))
if headphones.PROWL_ONSNATCH:
pushmessage = release['ArtistName'] + ' - ' + release['AlbumTitle']
logger.info(u"Prowl request")
prowl = PROWL()
prowl.notify(pushmessage,"Download and Postprocessing completed")
def embedAlbumArt(artwork, downloaded_track_list):
logger.info('Embedding album art')

63
headphones/prowl.py Normal file
View File

@@ -0,0 +1,63 @@
from headphones import logger
import base64
import cherrypy
import urllib
import urllib2
import headphones
from httplib import HTTPSConnection
from urllib import urlencode
class PROWL:
keys = []
priority = []
def __init__(self):
self.enabled = headphones.PROWL_ENABLED
self.keys = headphones.PROWL_KEYS
self.priority = headphones.PROWL_PRIORITY
pass
def conf(self, options):
return cherrypy.config['config'].get('Prowl', options)
def notify(self, message, event):
if not headphones.PROWL_ENABLED:
return
http_handler = HTTPSConnection("api.prowlapp.com")
data = {'apikey': headphones.PROWL_KEYS,
'application': 'Headphones',
'event': event,
'description': message.encode("utf-8"),
'priority': headphones.PROWL_PRIORITY }
http_handler.request("POST",
"/publicapi/add",
headers = {'Content-type': "application/x-www-form-urlencoded"},
body = urlencode(data))
response = http_handler.getresponse()
request_status = response.status
if request_status == 200:
logger.info(u"Prowl notifications sent.")
return True
elif request_status == 401:
logger.info(u"Prowl auth failed: %s" % response.reason)
return False
else:
logger.info(u"Prowl notification failed.")
return False
def updateLibrary(self):
#For uniformity reasons not removed
return
def test(self, keys, priority):
self.enabled = True
self.keys = keys
self.priority = priority
self.notify('ZOMG Lazors Pewpewpew!', 'Test Message')

View File

@@ -25,7 +25,8 @@ import urllib2, cookielib
from headphones.common import USER_AGENT
from headphones import logger
from headphones import prowl
from headphones.prowl import PROWL
def sendNZB(nzb):
@@ -116,6 +117,11 @@ def sendNZB(nzb):
if sabText == "ok":
logger.info(u"NZB sent to SAB successfully")
if headphones.PROWL_ONSNATCH:
logger.info(u"Prowl request")
prowl = PROWL()
prowl.notify(nzb.name,"Download started")
return True
elif sabText == "Missing authentication":
logger.info(u"Incorrect username/password sent to SAB, NZB not sent")

View File

@@ -378,7 +378,11 @@ class WebInterface(object):
"samplingfrequency": headphones.SAMPLINGFREQUENCY,
"encodervbrcbr": headphones.ENCODERVBRCBR,
"encoderquality": headphones.ENCODERQUALITY,
"encoderlossless": checked(headphones.ENCODERLOSSLESS)
"encoderlossless": checked(headphones.ENCODERLOSSLESS),
"prowl_enabled": checked(headphones.PROWL_ENABLED),
"prowl_onsnatch": checked(headphones.PROWL_ONSNATCH),
"prowl_keys": headphones.PROWL_KEYS,
"prowl_priority": headphones.PROWL_PRIORITY
}
return serve_template(templatename="config.html", title="Settings", config=config)
config.exposed = True
@@ -390,7 +394,8 @@ class WebInterface(object):
nzbsorg=0, nzbsorg_uid=None, nzbsorg_hash=None, newzbin=0, newzbin_uid=None, newzbin_password=None, preferred_quality=0, preferred_bitrate=None, detect_bitrate=0, move_files=0,
torrentblackhole_dir=None, download_torrent_dir=None, numberofseeders=10, use_isohunt=0, use_kat=0, use_mininova=0,
rename_files=0, correct_metadata=0, cleanup_files=0, add_album_art=0, embed_album_art=0, embed_lyrics=0, destination_dir=None, folder_format=None, file_format=None, include_extras=0, interface=None, log_dir=None,
encode=0, encoder=None, bitrate=None, samplingfrequency=None, encoderfolder=None, advancedencoder=None, encoderoutputformat=None, encodervbrcbr=None, encoderquality=None, encoderlossless=0):
encode=0, encoder=None, bitrate=None, samplingfrequency=None, encoderfolder=None, advancedencoder=None, encoderoutputformat=None, encodervbrcbr=None, encoderquality=None, encoderlossless=0,
prowl_enabled=0, prowl_onsnatch=0, prowl_keys=None, prowl_priority=0):
headphones.HTTP_HOST = http_host
headphones.HTTP_PORT = http_port
@@ -450,6 +455,10 @@ class WebInterface(object):
headphones.ENCODERVBRCBR = encodervbrcbr
headphones.ENCODERQUALITY = int(encoderquality)
headphones.ENCODERLOSSLESS = encoderlossless
headphones.PROWL_ENABLED = prowl_enabled
headphones.PROWL_ONSNATCH = prowl_onsnatch
headphones.PROWL_KEYS = prowl_keys
headphones.PROWL_PRIORITY = prowl_priority
headphones.config_write()

1215
lib/httplib2/__init__.py Executable file

File diff suppressed because it is too large Load Diff

110
lib/httplib2/iri2uri.py Executable file
View File

@@ -0,0 +1,110 @@
"""
iri2uri
Converts an IRI to a URI.
"""
__author__ = "Joe Gregorio (joe@bitworking.org)"
__copyright__ = "Copyright 2006, Joe Gregorio"
__contributors__ = []
__version__ = "1.0.0"
__license__ = "MIT"
__history__ = """
"""
import urlparse
# Convert an IRI to a URI following the rules in RFC 3987
#
# The characters we need to enocde and escape are defined in the spec:
#
# iprivate = %xE000-F8FF / %xF0000-FFFFD / %x100000-10FFFD
# ucschar = %xA0-D7FF / %xF900-FDCF / %xFDF0-FFEF
# / %x10000-1FFFD / %x20000-2FFFD / %x30000-3FFFD
# / %x40000-4FFFD / %x50000-5FFFD / %x60000-6FFFD
# / %x70000-7FFFD / %x80000-8FFFD / %x90000-9FFFD
# / %xA0000-AFFFD / %xB0000-BFFFD / %xC0000-CFFFD
# / %xD0000-DFFFD / %xE1000-EFFFD
escape_range = [
(0xA0, 0xD7FF ),
(0xE000, 0xF8FF ),
(0xF900, 0xFDCF ),
(0xFDF0, 0xFFEF),
(0x10000, 0x1FFFD ),
(0x20000, 0x2FFFD ),
(0x30000, 0x3FFFD),
(0x40000, 0x4FFFD ),
(0x50000, 0x5FFFD ),
(0x60000, 0x6FFFD),
(0x70000, 0x7FFFD ),
(0x80000, 0x8FFFD ),
(0x90000, 0x9FFFD),
(0xA0000, 0xAFFFD ),
(0xB0000, 0xBFFFD ),
(0xC0000, 0xCFFFD),
(0xD0000, 0xDFFFD ),
(0xE1000, 0xEFFFD),
(0xF0000, 0xFFFFD ),
(0x100000, 0x10FFFD)
]
def encode(c):
retval = c
i = ord(c)
for low, high in escape_range:
if i < low:
break
if i >= low and i <= high:
retval = "".join(["%%%2X" % ord(o) for o in c.encode('utf-8')])
break
return retval
def iri2uri(uri):
"""Convert an IRI to a URI. Note that IRIs must be
passed in a unicode strings. That is, do not utf-8 encode
the IRI before passing it into the function."""
if isinstance(uri ,unicode):
(scheme, authority, path, query, fragment) = urlparse.urlsplit(uri)
authority = authority.encode('idna')
# For each character in 'ucschar' or 'iprivate'
# 1. encode as utf-8
# 2. then %-encode each octet of that utf-8
uri = urlparse.urlunsplit((scheme, authority, path, query, fragment))
uri = "".join([encode(c) for c in uri])
return uri
if __name__ == "__main__":
import unittest
class Test(unittest.TestCase):
def test_uris(self):
"""Test that URIs are invariant under the transformation."""
invariant = [
u"ftp://ftp.is.co.za/rfc/rfc1808.txt",
u"http://www.ietf.org/rfc/rfc2396.txt",
u"ldap://[2001:db8::7]/c=GB?objectClass?one",
u"mailto:John.Doe@example.com",
u"news:comp.infosystems.www.servers.unix",
u"tel:+1-816-555-1212",
u"telnet://192.0.2.16:80/",
u"urn:oasis:names:specification:docbook:dtd:xml:4.1.2" ]
for uri in invariant:
self.assertEqual(uri, iri2uri(uri))
def test_iri(self):
""" Test that the right type of escaping is done for each part of the URI."""
self.assertEqual("http://xn--o3h.com/%E2%98%84", iri2uri(u"http://\N{COMET}.com/\N{COMET}"))
self.assertEqual("http://bitworking.org/?fred=%E2%98%84", iri2uri(u"http://bitworking.org/?fred=\N{COMET}"))
self.assertEqual("http://bitworking.org/#%E2%98%84", iri2uri(u"http://bitworking.org/#\N{COMET}"))
self.assertEqual("#%E2%98%84", iri2uri(u"#\N{COMET}"))
self.assertEqual("/fred?bar=%E2%98%9A#%E2%98%84", iri2uri(u"/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}"))
self.assertEqual("/fred?bar=%E2%98%9A#%E2%98%84", iri2uri(iri2uri(u"/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}")))
self.assertNotEqual("/fred?bar=%E2%98%9A#%E2%98%84", iri2uri(u"/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}".encode('utf-8')))
unittest.main()