Boxcar2, OS X Notifications

- OS X Notifications require an application bundle, allow user to
create a dummy app in a specified location and then use bundle when
notifying
- Boxcar2
- Moved snatched notify logic to searcher so we can pass extra info
incl provider
This commit is contained in:
Ade
2014-04-16 21:41:22 +12:00
parent d6cd9cd7e4
commit 3b2350cd8f
11 changed files with 505 additions and 142 deletions
+87 -5
View File
@@ -690,8 +690,7 @@
</div> </div>
</div> </div>
</fieldset> </fieldset>
</td>
<td>
<fieldset> <fieldset>
<h3>Pushalot</h3> <h3>Pushalot</h3>
<div class="checkbox row"> <div class="checkbox row">
@@ -708,6 +707,10 @@
</div> </div>
</div> </div>
</fieldset> </fieldset>
</td>
<td>
<fieldset> <fieldset>
<h3>Synology NAS</h3> <h3>Synology NAS</h3>
<div class="checkbox row"> <div class="checkbox row">
@@ -776,7 +779,40 @@
</div> </div>
</div> </div>
</fieldset> </fieldset>
</td>
<fieldset>
<h3>OS X</h3>
<div class="row checkbox">
<input type="checkbox" name="osx_notify_enabled" id="osx_notify" value="1" ${config['osx_notify_enabled']} /><label>Enable OS X Notifications</label>
</div>
<div id="osx_notify_options">
<div class="row">
<small>Enter the path/application name to be registered with the Notification Center, default is /Applications/Headphones</small>
<input type="text" id="osx_notify_reg" name="osx_notify_app" value="${config['osx_notify_app']}" size="50"><label>Register Notify App</label>
<input type="button" value="Register" id="osxnotifyregister">
</div>
<div class="row checkbox">
<input type="checkbox" name="osx_notify_onsnatch" value="1" ${config['osx_notify_onsnatch']} /><label>Notify on snatch?</label>
</div>
</div>
</fieldset>
<fieldset>
<h3>Boxcar2</h3>
<div class="row checkbox">
<input type="checkbox" name="boxcar_enabled" id="boxcar" value="1" ${config['boxcar_enabled']} /><label>Enable Boxcar2 Notifications</label>
</div>
<div id="boxcar_options">
<div class="row">
<label>Access Token</label><input type="text" name="boxcar_token" value="${config['boxcar_token']}" size="35">
</div>
<div class="row checkbox">
<input type="checkbox" name="boxcar_onsnatch" value="1" ${config['boxcar_onsnatch']} /><label>Notify on snatch?</label>
</div>
</div>
</fieldset>
</td>
</tr> </tr>
</table> </table>
<input type="button" class="configsubmit" value="Save Changes" onclick="doAjaxCall('configUpdate',$(this),'tabs',true);return false;" data-success="Changes saved successfully"> <input type="button" class="configsubmit" value="Save Changes" onclick="doAjaxCall('configUpdate',$(this),'tabs',true);return false;" data-success="Changes saved successfully">
@@ -1457,8 +1493,48 @@
$("#twitteroptions").slideUp(); $("#twitteroptions").slideUp();
} }
}); });
if ($("#songkick").is(":checked")) if ($("#osx_notify").is(":checked"))
{
$("#osx_notify_options").show();
}
else
{
$("#osx_notify_options").hide();
}
$("#osx_notify").click(function(){
if ($("#osx_notify").is(":checked"))
{
$("#osx_notify_options").slideDown();
}
else
{
$("#osx_notify_options").slideUp();
}
});
if ($("#boxcar").is(":checked"))
{
$("#boxcar_options").show();
}
else
{
$("#boxcar_options").hide();
}
$("#boxcar").click(function(){
if ($("#boxcar").is(":checked"))
{
$("#boxcar_options").slideDown();
}
else
{
$("#boxcar_options").slideUp();
}
});
if ($("#songkick").is(":checked"))
{ {
$("#songkickoptions").show(); $("#songkickoptions").show();
} }
@@ -1671,6 +1747,12 @@
function (data) { $('#ajaxMsg').html("<div class='msg'><span class='ui-icon ui-icon-check'></span>"+data+"</div>"); }); function (data) { $('#ajaxMsg').html("<div class='msg'><span class='ui-icon ui-icon-check'></span>"+data+"</div>"); });
$('#ajaxMsg').addClass('success').fadeIn().delay(3000).fadeOut(); $('#ajaxMsg').addClass('success').fadeIn().delay(3000).fadeOut();
}); });
$('#osxnotifyregister').click(function () {
var osx_notify_app = $("#osx_notify_reg").val();
$.get("/osxnotifyregister", {'app': osx_notify_app}, function (data) { $('#ajaxMsg').html("<div class='msg'><span class='ui-icon ui-icon-check'></span>"+data+"</div>"); });
$('#ajaxMsg').addClass('success').fadeIn().delay(3000).fadeOut()
})
} }
$(document).ready(function() { $(document).ready(function() {
+28 -2
View File
@@ -270,6 +270,12 @@ TWITTER_ONSNATCH = False
TWITTER_USERNAME = None TWITTER_USERNAME = None
TWITTER_PASSWORD = None TWITTER_PASSWORD = None
TWITTER_PREFIX = None TWITTER_PREFIX = None
OSX_NOTIFY_ENABLED = False
OSX_NOTIFY_ONSNATCH = False
OSX_NOTIFY_APP = None
BOXCAR_ENABLED = False
BOXCAR_ONSNATCH = False
BOXCAR_TOKEN = None
MIRRORLIST = ["musicbrainz.org","headphones","custom"] MIRRORLIST = ["musicbrainz.org","headphones","custom"]
MIRROR = None MIRROR = None
CUSTOMHOST = None CUSTOMHOST = None
@@ -349,7 +355,7 @@ def initialize():
INTERFACE, FOLDER_PERMISSIONS, FILE_PERMISSIONS, ENCODERFOLDER, ENCODER_PATH, ENCODER, XLDPROFILE, BITRATE, SAMPLINGFREQUENCY, \ INTERFACE, FOLDER_PERMISSIONS, FILE_PERMISSIONS, ENCODERFOLDER, ENCODER_PATH, ENCODER, XLDPROFILE, BITRATE, SAMPLINGFREQUENCY, \
MUSIC_ENCODER, ADVANCEDENCODER, ENCODEROUTPUTFORMAT, ENCODERQUALITY, ENCODERVBRCBR, ENCODERLOSSLESS, ENCODER_MULTICORE, ENCODER_MULTICORE_COUNT, DELETE_LOSSLESS_FILES, \ MUSIC_ENCODER, ADVANCEDENCODER, ENCODEROUTPUTFORMAT, ENCODERQUALITY, ENCODERVBRCBR, ENCODERLOSSLESS, ENCODER_MULTICORE, ENCODER_MULTICORE_COUNT, DELETE_LOSSLESS_FILES, \
GROWL_ENABLED, GROWL_HOST, GROWL_PASSWORD, GROWL_ONSNATCH, PROWL_ENABLED, PROWL_PRIORITY, PROWL_KEYS, PROWL_ONSNATCH, PUSHOVER_ENABLED, PUSHOVER_PRIORITY, PUSHOVER_KEYS, PUSHOVER_ONSNATCH, PUSHOVER_APITOKEN, MIRRORLIST, \ GROWL_ENABLED, GROWL_HOST, GROWL_PASSWORD, GROWL_ONSNATCH, PROWL_ENABLED, PROWL_PRIORITY, PROWL_KEYS, PROWL_ONSNATCH, PUSHOVER_ENABLED, PUSHOVER_PRIORITY, PUSHOVER_KEYS, PUSHOVER_ONSNATCH, PUSHOVER_APITOKEN, MIRRORLIST, \
TWITTER_ENABLED, TWITTER_ONSNATCH, TWITTER_USERNAME, TWITTER_PASSWORD, TWITTER_PREFIX, \ TWITTER_ENABLED, TWITTER_ONSNATCH, TWITTER_USERNAME, TWITTER_PASSWORD, TWITTER_PREFIX, OSX_NOTIFY_ENABLED, OSX_NOTIFY_ONSNATCH, OSX_NOTIFY_APP, BOXCAR_ENABLED, BOXCAR_ONSNATCH, BOXCAR_TOKEN, \
PUSHBULLET_ENABLED, PUSHBULLET_APIKEY, PUSHBULLET_DEVICEID, PUSHBULLET_ONSNATCH, \ PUSHBULLET_ENABLED, PUSHBULLET_APIKEY, PUSHBULLET_DEVICEID, PUSHBULLET_ONSNATCH, \
MIRROR, CUSTOMHOST, CUSTOMPORT, CUSTOMSLEEP, HPUSER, HPPASS, XBMC_ENABLED, XBMC_HOST, XBMC_USERNAME, XBMC_PASSWORD, XBMC_UPDATE, \ MIRROR, CUSTOMHOST, CUSTOMPORT, CUSTOMSLEEP, HPUSER, HPPASS, XBMC_ENABLED, XBMC_HOST, XBMC_USERNAME, XBMC_PASSWORD, XBMC_UPDATE, \
XBMC_NOTIFY, LMS_ENABLED, LMS_HOST, NMA_ENABLED, NMA_APIKEY, NMA_PRIORITY, NMA_ONSNATCH, SYNOINDEX_ENABLED, ALBUM_COMPLETION_PCT, PREFERRED_BITRATE_HIGH_BUFFER, \ XBMC_NOTIFY, LMS_ENABLED, LMS_HOST, NMA_ENABLED, NMA_APIKEY, NMA_PRIORITY, NMA_ONSNATCH, SYNOINDEX_ENABLED, ALBUM_COMPLETION_PCT, PREFERRED_BITRATE_HIGH_BUFFER, \
@@ -386,6 +392,8 @@ def initialize():
CheckSection('Pushalot') CheckSection('Pushalot')
CheckSection('Synoindex') CheckSection('Synoindex')
CheckSection('Twitter') CheckSection('Twitter')
CheckSection('OSX_Notify')
CheckSection('Boxcar')
CheckSection('Songkick') CheckSection('Songkick')
CheckSection('Advanced') CheckSection('Advanced')
@@ -609,7 +617,15 @@ def initialize():
TWITTER_USERNAME = check_setting_str(CFG, 'Twitter', 'twitter_username', '') TWITTER_USERNAME = check_setting_str(CFG, 'Twitter', 'twitter_username', '')
TWITTER_PASSWORD = check_setting_str(CFG, 'Twitter', 'twitter_password', '') TWITTER_PASSWORD = check_setting_str(CFG, 'Twitter', 'twitter_password', '')
TWITTER_PREFIX = check_setting_str(CFG, 'Twitter', 'twitter_prefix', 'Headphones') TWITTER_PREFIX = check_setting_str(CFG, 'Twitter', 'twitter_prefix', 'Headphones')
OSX_NOTIFY_ENABLED = bool(check_setting_int(CFG, 'OSX_Notify', 'osx_notify_enabled', 0))
OSX_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'OSX_Notify', 'osx_notify_onsnatch', 0))
OSX_NOTIFY_APP = check_setting_str(CFG, 'OSX_Notify', 'osx_notify_app', '/Applications/Headphones')
BOXCAR_ENABLED = bool(check_setting_int(CFG, 'Boxcar', 'boxcar_enabled', 0))
BOXCAR_ONSNATCH = bool(check_setting_int(CFG, 'Boxcar', 'boxcar_onsnatch', 0))
BOXCAR_TOKEN = check_setting_str(CFG, 'Boxcar', 'boxcar_token', '')
SONGKICK_ENABLED = bool(check_setting_int(CFG, 'Songkick', 'songkick_enabled', 1)) SONGKICK_ENABLED = bool(check_setting_int(CFG, 'Songkick', 'songkick_enabled', 1))
SONGKICK_APIKEY = check_setting_str(CFG, 'Songkick', 'songkick_apikey', 'nd1We7dFW2RqxPw8') SONGKICK_APIKEY = check_setting_str(CFG, 'Songkick', 'songkick_apikey', 'nd1We7dFW2RqxPw8')
SONGKICK_LOCATION = check_setting_str(CFG, 'Songkick', 'songkick_location', '') SONGKICK_LOCATION = check_setting_str(CFG, 'Songkick', 'songkick_location', '')
@@ -1025,6 +1041,16 @@ def config_write():
new_config['Twitter']['twitter_password'] = TWITTER_PASSWORD new_config['Twitter']['twitter_password'] = TWITTER_PASSWORD
new_config['Twitter']['twitter_prefix'] = TWITTER_PREFIX new_config['Twitter']['twitter_prefix'] = TWITTER_PREFIX
new_config['OSX_Notify'] = {}
new_config['OSX_Notify']['osx_notify_enabled'] = int(OSX_NOTIFY_ENABLED)
new_config['OSX_Notify']['osx_notify_onsnatch'] = int(OSX_NOTIFY_ONSNATCH)
new_config['OSX_Notify']['osx_notify_app'] = OSX_NOTIFY_APP
new_config['Boxcar'] = {}
new_config['Boxcar']['boxcar_enabled'] = int(BOXCAR_ENABLED)
new_config['Boxcar']['boxcar_onsnatch'] = int(BOXCAR_ONSNATCH)
new_config['Boxcar']['boxcar_token'] = BOXCAR_TOKEN
new_config['Songkick'] = {} new_config['Songkick'] = {}
new_config['Songkick']['songkick_enabled'] = int(SONGKICK_ENABLED) new_config['Songkick']['songkick_enabled'] = int(SONGKICK_ENABLED)
new_config['Songkick']['songkick_apikey'] = SONGKICK_APIKEY new_config['Songkick']['songkick_apikey'] = SONGKICK_APIKEY
+153 -70
View File
@@ -132,7 +132,7 @@ class PROWL:
return return
http_handler = HTTPSConnection("api.prowlapp.com") http_handler = HTTPSConnection("api.prowlapp.com")
data = {'apikey': headphones.PROWL_KEYS, data = {'apikey': headphones.PROWL_KEYS,
'application': 'Headphones', 'application': 'Headphones',
'event': event, 'event': event,
@@ -167,7 +167,7 @@ class PROWL:
self.priority = priority self.priority = priority
self.notify('ZOMG Lazors Pewpewpew!', 'Test Message') self.notify('ZOMG Lazors Pewpewpew!', 'Test Message')
class XBMC: class XBMC:
def __init__(self): def __init__(self):
@@ -207,10 +207,10 @@ class XBMC:
for host in hosts: for host in hosts:
logger.info('Sending library update command to XBMC @ '+host) logger.info('Sending library update command to XBMC @ '+host)
request = self._sendjson(host, 'AudioLibrary.Scan') request = self._sendjson(host, 'AudioLibrary.Scan')
if not request: if not request:
logger.warn('Error sending update request to XBMC') logger.warn('Error sending update request to XBMC')
def notify(self, artist, album, albumartpath): def notify(self, artist, album, albumartpath):
hosts = [x.strip() for x in self.hosts.split(',')] hosts = [x.strip() for x in self.hosts.split(',')]
@@ -238,14 +238,15 @@ class XBMC:
except: except:
logger.warn('Error sending notification request to XBMC') logger.warn('Error sending notification request to XBMC')
class LMS: class LMS:
#Class for updating a Logitech Media Server #Class for updating a Logitech Media Server
def __init__(self): def __init__(self):
self.hosts = headphones.LMS_HOST self.hosts = headphones.LMS_HOST
def _sendjson(self, host): def _sendjson(self, host):
data = {'id': 1, 'method': 'slim.request', 'params': ["",["rescan"]]} #Had a lot of trouble with simplejson, but this works. data = {'id': 1, 'method': 'slim.request', 'params': ["",["rescan"]]} #Had a lot of trouble with simplejson, but this works.
data = simplejson.JSONEncoder().encode(data) data = simplejson.JSONEncoder().encode(data)
@@ -262,7 +263,7 @@ class LMS:
response = simplejson.JSONDecoder().decode(handle.read()) response = simplejson.JSONDecoder().decode(handle.read())
server_result = simplejson.dumps(response) server_result = simplejson.dumps(response)
try: try:
return response[0]['result'] return response[0]['result']
except: except:
@@ -270,24 +271,24 @@ class LMS:
return return
def update(self): def update(self):
#Send the ["rescan"] command to an LMS server. #Send the ["rescan"] command to an LMS server.
#Note that the command must be prefixed with the 'player' that the command is aimed at, #Note that the command must be prefixed with the 'player' that the command is aimed at,
#But with this being a request for the server to update its library, the player is blank, so "" #But with this being a request for the server to update its library, the player is blank, so ""
hosts = [x.strip() for x in self.hosts.split(',')] hosts = [x.strip() for x in self.hosts.split(',')]
for host in hosts: for host in hosts:
logger.info('Sending library rescan command to LMS @ '+host) logger.info('Sending library rescan command to LMS @ '+host)
request = self._sendjson(host) request = self._sendjson(host)
if not request: if not request:
logger.warn('Error sending rescan request to LMS') logger.warn('Error sending rescan request to LMS')
class Plex: class Plex:
def __init__(self): def __init__(self):
self.server_hosts = headphones.PLEX_SERVER_HOST self.server_hosts = headphones.PLEX_SERVER_HOST
self.client_hosts = headphones.PLEX_CLIENT_HOST self.client_hosts = headphones.PLEX_CLIENT_HOST
self.username = headphones.PLEX_USERNAME self.username = headphones.PLEX_USERNAME
@@ -297,31 +298,31 @@ class Plex:
username = self.username username = self.username
password = self.password password = self.password
url_command = urllib.urlencode(command) url_command = urllib.urlencode(command)
url = host + '/xbmcCmds/xbmcHttp/?' + url_command url = host + '/xbmcCmds/xbmcHttp/?' + url_command
req = urllib2.Request(url) req = urllib2.Request(url)
if password: if password:
base64string = base64.encodestring('%s:%s' % (username, password)).replace('\n', '') base64string = base64.encodestring('%s:%s' % (username, password)).replace('\n', '')
req.add_header("Authorization", "Basic %s" % base64string) req.add_header("Authorization", "Basic %s" % base64string)
logger.info('Plex url: %s' % url) logger.info('Plex url: %s' % url)
try: try:
handle = urllib2.urlopen(req) handle = urllib2.urlopen(req)
except Exception, e: except Exception, e:
logger.warn('Error opening Plex url: %s' % e) logger.warn('Error opening Plex url: %s' % e)
return return
response = handle.read().decode(headphones.SYS_ENCODING) response = handle.read().decode(headphones.SYS_ENCODING)
return response return response
def update(self): def update(self):
# From what I read you can't update the music library on a per directory or per path basis # From what I read you can't update the music library on a per directory or per path basis
# so need to update the whole thing # so need to update the whole thing
@@ -349,7 +350,7 @@ class Plex:
except Exception, e: except Exception, e:
logger.warn("Error updating library section for Plex Media Server: %s" % e) logger.warn("Error updating library section for Plex Media Server: %s" % e)
return False return False
def notify(self, artist, album, albumartpath): def notify(self, artist, album, albumartpath):
hosts = [x.strip() for x in self.client_hosts.split(',')] hosts = [x.strip() for x in self.client_hosts.split(',')]
@@ -361,12 +362,12 @@ class Plex:
for host in hosts: for host in hosts:
logger.info('Sending notification command to Plex Media Server @ '+host) logger.info('Sending notification command to Plex Media Server @ '+host)
try: try:
notification = header + "," + message + "," + time + "," + albumartpath notification = header + "," + message + "," + time + "," + albumartpath
notifycommand = {'command': 'ExecBuiltIn', 'parameter': 'Notification('+notification+')'} notifycommand = {'command': 'ExecBuiltIn', 'parameter': 'Notification('+notification+')'}
request = self._sendhttp(host, notifycommand) request = self._sendhttp(host, notifycommand)
if not request: if not request:
raise Exception raise Exception
except: except:
logger.warn('Error sending notification request to Plex Media Server') logger.warn('Error sending notification request to Plex Media Server')
@@ -374,30 +375,30 @@ class Plex:
class NMA: class NMA:
def __init__(self): def __init__(self):
self.apikey = headphones.NMA_APIKEY self.apikey = headphones.NMA_APIKEY
self.priority = headphones.NMA_PRIORITY self.priority = headphones.NMA_PRIORITY
def _send(self, data): def _send(self, data):
return request.request_content('https://www.notifymyandroid.com/publicapi/notify', data=data) return request.request_content('https://www.notifymyandroid.com/publicapi/notify', data=data)
def notify(self, artist=None, album=None, snatched_nzb=None): def notify(self, artist=None, album=None, snatched_nzb=None):
apikey = self.apikey apikey = self.apikey
priority = self.priority priority = self.priority
if snatched_nzb: if snatched_nzb:
event = snatched_nzb + " snatched!" event = snatched_nzb + " snatched!"
description = "Headphones has snatched: " + snatched_nzb + " and has sent it to SABnzbd+" description = "Headphones has snatched: " + snatched_nzb + " and has sent it to SABnzbd+"
else: else:
event = artist + ' - ' + album + ' complete!' event = artist + ' - ' + album + ' complete!'
description = "Headphones has downloaded and postprocessed: " + artist + ' [' + album + ']' description = "Headphones has downloaded and postprocessed: " + artist + ' [' + album + ']'
data = { 'apikey': apikey, 'application':'Headphones', 'event': event, 'description': description, 'priority': priority} data = { 'apikey': apikey, 'application':'Headphones', 'event': event, 'description': description, 'priority': priority}
logger.info('Sending notification request to NotifyMyAndroid') logger.info('Sending notification request to NotifyMyAndroid')
request = self._send(data) request = self._send(data)
if not request: if not request:
logger.warn('Error sending notification request to NotifyMyAndroid') logger.warn('Error sending notification request to NotifyMyAndroid')
@@ -415,7 +416,7 @@ class PUSHBULLET:
return return
http_handler = HTTPSConnection("api.pushbullet.com") http_handler = HTTPSConnection("api.pushbullet.com")
data = {'device_iden': headphones.PUSHBULLET_DEVICEID, data = {'device_iden': headphones.PUSHBULLET_DEVICEID,
'type': "note", 'type': "note",
'title': "Headphones", 'title': "Headphones",
@@ -454,21 +455,20 @@ class PUSHBULLET:
self.notify('Main Screen Activate', 'Test Message') self.notify('Main Screen Activate', 'Test Message')
class PUSHALOT: class PUSHALOT:
def notify(self, message, event): def notify(self, message, event):
if not headphones.PUSHALOT_ENABLED: if not headphones.PUSHALOT_ENABLED:
return return
pushalot_authorizationtoken = headphones.PUSHALOT_APIKEY pushalot_authorizationtoken = headphones.PUSHALOT_APIKEY
logger.debug(u"Pushalot event: " + event) logger.debug(u"Pushalot event: " + event)
logger.debug(u"Pushalot message: " + message) logger.debug(u"Pushalot message: " + message)
logger.debug(u"Pushalot api: " + pushalot_authorizationtoken) logger.debug(u"Pushalot api: " + pushalot_authorizationtoken)
http_handler = HTTPSConnection("pushalot.com") http_handler = HTTPSConnection("pushalot.com")
data = {'AuthorizationToken': pushalot_authorizationtoken, data = {'AuthorizationToken': pushalot_authorizationtoken,
'Title': event.encode('utf-8'), 'Title': event.encode('utf-8'),
'Body': message.encode("utf-8") } 'Body': message.encode("utf-8") }
@@ -529,6 +529,7 @@ class Synoindex:
if isinstance(path_list, list): if isinstance(path_list, list):
for path in path_list: for path in path_list:
self.notify(path) self.notify(path)
class PUSHOVER: class PUSHOVER:
application_token = "LdPCoy0dqC21ktsbEyAVCcwvQiVlsz" application_token = "LdPCoy0dqC21ktsbEyAVCcwvQiVlsz"
@@ -551,7 +552,7 @@ class PUSHOVER:
return return
http_handler = HTTPSConnection("api.pushover.net") http_handler = HTTPSConnection("api.pushover.net")
data = {'token': self.application_token, data = {'token': self.application_token,
'user': headphones.PUSHOVER_KEYS, 'user': headphones.PUSHOVER_KEYS,
'title': event, 'title': event,
@@ -589,17 +590,17 @@ class PUSHOVER:
self.priority = priority self.priority = priority
self.notify('Main Screen Activate', 'Test Message') self.notify('Main Screen Activate', 'Test Message')
class TwitterNotifier: class TwitterNotifier:
consumer_key = "oYKnp2ddX5gbARjqX8ZAAg" consumer_key = "oYKnp2ddX5gbARjqX8ZAAg"
consumer_secret = "A4Xkw9i5SjHbTk7XT8zzOPqivhj9MmRDR9Qn95YA9sk" consumer_secret = "A4Xkw9i5SjHbTk7XT8zzOPqivhj9MmRDR9Qn95YA9sk"
REQUEST_TOKEN_URL = 'https://api.twitter.com/oauth/request_token' REQUEST_TOKEN_URL = 'https://api.twitter.com/oauth/request_token'
ACCESS_TOKEN_URL = 'https://api.twitter.com/oauth/access_token' ACCESS_TOKEN_URL = 'https://api.twitter.com/oauth/access_token'
AUTHORIZATION_URL = 'https://api.twitter.com/oauth/authorize' AUTHORIZATION_URL = 'https://api.twitter.com/oauth/authorize'
SIGNIN_URL = 'https://api.twitter.com/oauth/authenticate' SIGNIN_URL = 'https://api.twitter.com/oauth/authenticate'
def notify_snatch(self, title): def notify_snatch(self, title):
if headphones.TWITTER_ONSNATCH: if headphones.TWITTER_ONSNATCH:
self._notifyTwitter(common.notifyStrings[common.NOTIFY_SNATCH]+': '+title+' at '+helpers.now()) self._notifyTwitter(common.notifyStrings[common.NOTIFY_SNATCH]+': '+title+' at '+helpers.now())
@@ -612,37 +613,37 @@ class TwitterNotifier:
return self._notifyTwitter("This is a test notification from Headphones at "+helpers.now(), force=True) return self._notifyTwitter("This is a test notification from Headphones at "+helpers.now(), force=True)
def _get_authorization(self): def _get_authorization(self):
signature_method_hmac_sha1 = oauth.SignatureMethod_HMAC_SHA1() #@UnusedVariable signature_method_hmac_sha1 = oauth.SignatureMethod_HMAC_SHA1() #@UnusedVariable
oauth_consumer = oauth.Consumer(key=self.consumer_key, secret=self.consumer_secret) oauth_consumer = oauth.Consumer(key=self.consumer_key, secret=self.consumer_secret)
oauth_client = oauth.Client(oauth_consumer) oauth_client = oauth.Client(oauth_consumer)
logger.info('Requesting temp token from Twitter') logger.info('Requesting temp token from Twitter')
resp, content = oauth_client.request(self.REQUEST_TOKEN_URL, 'GET') resp, content = oauth_client.request(self.REQUEST_TOKEN_URL, 'GET')
if resp['status'] != '200': if resp['status'] != '200':
logger.info('Invalid respond from Twitter requesting temp token: %s' % resp['status']) logger.info('Invalid respond from Twitter requesting temp token: %s' % resp['status'])
else: else:
request_token = dict(parse_qsl(content)) request_token = dict(parse_qsl(content))
headphones.TWITTER_USERNAME = request_token['oauth_token'] headphones.TWITTER_USERNAME = request_token['oauth_token']
headphones.TWITTER_PASSWORD = request_token['oauth_token_secret'] headphones.TWITTER_PASSWORD = request_token['oauth_token_secret']
return self.AUTHORIZATION_URL+"?oauth_token="+ request_token['oauth_token'] return self.AUTHORIZATION_URL+"?oauth_token="+ request_token['oauth_token']
def _get_credentials(self, key): def _get_credentials(self, key):
request_token = {} request_token = {}
request_token['oauth_token'] = headphones.TWITTER_USERNAME request_token['oauth_token'] = headphones.TWITTER_USERNAME
request_token['oauth_token_secret'] = headphones.TWITTER_PASSWORD request_token['oauth_token_secret'] = headphones.TWITTER_PASSWORD
request_token['oauth_callback_confirmed'] = 'true' request_token['oauth_callback_confirmed'] = 'true'
token = oauth.Token(request_token['oauth_token'], request_token['oauth_token_secret']) token = oauth.Token(request_token['oauth_token'], request_token['oauth_token_secret'])
token.set_verifier(key) token.set_verifier(key)
logger.info('Generating and signing request for an access token using key '+key) logger.info('Generating and signing request for an access token using key '+key)
signature_method_hmac_sha1 = oauth.SignatureMethod_HMAC_SHA1() #@UnusedVariable signature_method_hmac_sha1 = oauth.SignatureMethod_HMAC_SHA1() #@UnusedVariable
oauth_consumer = oauth.Consumer(key=self.consumer_key, secret=self.consumer_secret) oauth_consumer = oauth.Consumer(key=self.consumer_key, secret=self.consumer_secret)
logger.info('oauth_consumer: '+str(oauth_consumer)) logger.info('oauth_consumer: '+str(oauth_consumer))
@@ -650,10 +651,10 @@ class TwitterNotifier:
logger.info('oauth_client: '+str(oauth_client)) logger.info('oauth_client: '+str(oauth_client))
resp, content = oauth_client.request(self.ACCESS_TOKEN_URL, method='POST', body='oauth_verifier=%s' % key) resp, content = oauth_client.request(self.ACCESS_TOKEN_URL, method='POST', body='oauth_verifier=%s' % key)
logger.info('resp, content: '+str(resp)+','+str(content)) logger.info('resp, content: '+str(resp)+','+str(content))
access_token = dict(parse_qsl(content)) access_token = dict(parse_qsl(content))
logger.info('access_token: '+str(access_token)) logger.info('access_token: '+str(access_token))
logger.info('resp[status] = '+str(resp['status'])) logger.info('resp[status] = '+str(resp['status']))
if resp['status'] != '200': if resp['status'] != '200':
logger.info('The request for a token with did not succeed: '+str(resp['status']), logger.ERROR) logger.info('The request for a token with did not succeed: '+str(resp['status']), logger.ERROR)
@@ -664,33 +665,115 @@ class TwitterNotifier:
headphones.TWITTER_USERNAME = access_token['oauth_token'] headphones.TWITTER_USERNAME = access_token['oauth_token']
headphones.TWITTER_PASSWORD = access_token['oauth_token_secret'] headphones.TWITTER_PASSWORD = access_token['oauth_token_secret']
return True return True
def _send_tweet(self, message=None): def _send_tweet(self, message=None):
username=self.consumer_key username=self.consumer_key
password=self.consumer_secret password=self.consumer_secret
access_token_key=headphones.TWITTER_USERNAME access_token_key=headphones.TWITTER_USERNAME
access_token_secret=headphones.TWITTER_PASSWORD access_token_secret=headphones.TWITTER_PASSWORD
logger.info(u"Sending tweet: "+message) logger.info(u"Sending tweet: "+message)
api = twitter.Api(username, password, access_token_key, access_token_secret) api = twitter.Api(username, password, access_token_key, access_token_secret)
try: try:
api.PostUpdate(message) api.PostUpdate(message)
except Exception, e: except Exception, e:
logger.info(u"Error Sending Tweet: %s" % e) logger.info(u"Error Sending Tweet: %s" % e)
return False return False
return True return True
def _notifyTwitter(self, message='', force=False): def _notifyTwitter(self, message='', force=False):
prefix = headphones.TWITTER_PREFIX prefix = headphones.TWITTER_PREFIX
if not headphones.TWITTER_ENABLED and not force: if not headphones.TWITTER_ENABLED and not force:
return False return False
return self._send_tweet(prefix+": "+message) return self._send_tweet(prefix+": "+message)
notifier = TwitterNotifier notifier = TwitterNotifier
class OSX_NOTIFY:
objc = None
def __init__(self):
try:
self.objc = __import__("objc")
except:
return False
def swizzle(self, cls, SEL, func):
old_IMP = cls.instanceMethodForSelector_(SEL)
def wrapper(self, *args, **kwargs):
return func(self, old_IMP, *args, **kwargs)
new_IMP = self.objc.selector(wrapper, selector=old_IMP.selector,
signature=old_IMP.signature)
self.objc.classAddMethod(cls, SEL, new_IMP)
def notify(self, title, subtitle=None, text=None, sound=True):
try:
self.swizzle(self.objc.lookUpClass('NSBundle'),
b'bundleIdentifier',
self.swizzled_bundleIdentifier)
NSUserNotification = self.objc.lookUpClass('NSUserNotification')
NSUserNotificationCenter = self.objc.lookUpClass('NSUserNotificationCenter')
NSAutoreleasePool = self.objc.lookUpClass('NSAutoreleasePool')
if not NSUserNotification or not NSUserNotificationCenter:
return False
pool = NSAutoreleasePool.alloc().init()
notification = NSUserNotification.alloc().init()
notification.setTitle_(title)
if subtitle:
notification.setSubtitle_(subtitle)
if text:
notification.setInformativeText_(text)
if sound:
notification.setSoundName_("NSUserNotificationDefaultSoundName")
notification.setHasActionButton_(False)
notification_center = NSUserNotificationCenter.defaultUserNotificationCenter()
notification_center.deliverNotification_(notification)
del pool
return True
except Exception, e:
logger.warn('Error sending OS X Notification: %s' % e)
return False
def swizzled_bundleIdentifier(self, original, swizzled):
return 'ade.headphones.osxnotify'
class BOXCAR:
def __init__(self):
self.url = 'https://new.boxcar.io/api/notifications'
def notify(self, title, message):
try:
data = urllib.urlencode({
'user_credentials': headphones.BOXCAR_TOKEN,
'notification[title]': title.encode('utf-8'),
'notification[long_message]': message.encode('utf-8'),
'notification[sound]': "done"
})
req = urllib2.Request(self.url)
handle = urllib2.urlopen(req, data)
handle.close()
return True
except urllib2.URLError, e:
logger.warn('Error sending Boxcar2 Notification: %s' % e)
return False
+12 -1
View File
@@ -473,7 +473,18 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list,
logger.info(u"Sending Twitter notification") logger.info(u"Sending Twitter notification")
twitter = notifiers.TwitterNotifier() twitter = notifiers.TwitterNotifier()
twitter.notify_download(pushmessage) twitter.notify_download(pushmessage)
if headphones.OSX_NOTIFY_ENABLED:
logger.info(u"Sending OS X notification")
osx_notify = notifiers.OSX_NOTIFY()
osx_notify.notify(release['ArtistName'], release['AlbumTitle'], "Download and Postprocessing completed")
if headphones.BOXCAR_ENABLED:
pushmessage = release['ArtistName'] + ' - ' + release['AlbumTitle']
logger.info(u"Sending Boxcar2 notification")
boxcar = notifiers.BOXCAR()
boxcar.notify('Headphones processed: ' + pushmessage, "Download and Postprocessing completed")
def embedAlbumArt(artwork, downloaded_track_list): def embedAlbumArt(artwork, downloaded_track_list):
logger.info('Embedding album art') logger.info('Embedding album art')
-29
View File
@@ -119,35 +119,6 @@ def sendNZB(nzb):
if sabText == "ok": if sabText == "ok":
logger.info(u"NZB sent to SAB successfully") logger.info(u"NZB sent to SAB successfully")
if headphones.GROWL_ENABLED and headphones.GROWL_ONSNATCH:
logger.info(u"Sending Growl notification")
growl = notifiers.GROWL()
growl.notify(nzb.name,"Download started")
if headphones.PROWL_ENABLED and headphones.PROWL_ONSNATCH:
logger.info(u"Sending Prowl notification")
prowl = notifiers.PROWL()
prowl.notify(nzb.name,"Download started")
if headphones.PUSHOVER_ENABLED and headphones.PUSHOVER_ONSNATCH:
logger.info(u"Sending Pushover notification")
prowl = notifiers.PUSHOVER()
prowl.notify(nzb.name,"Download started")
if headphones.PUSHBULLET_ENABLED and headphones.PUSHBULLET_ONSNATCH:
logger.info(u"Sending PushBullet notification")
pushbullet = notifiers.PUSHBULLET()
pushbullet.notify(nzb.name + " has been snatched!", "Download started")
if headphones.TWITTER_ENABLED and headphones.TWITTER_ONSNATCH:
logger.info(u"Sending Twitter notification")
twitter = notifiers.TwitterNotifier()
twitter.notify_snatch(nzb.name)
if headphones.NMA_ENABLED and headphones.NMA_ONSNATCH:
logger.debug(u"Sending NMA notification")
nma = notifiers.NMA()
nma.notify(snatched_nzb=nzb.name)
if headphones.PUSHALOT_ENABLED and headphones.PUSHALOT_ONSNATCH:
logger.info(u"Sending Pushalot notification")
pushalot = notifiers.PUSHALOT()
pushalot.notify(nzb.name,"Download started")
return True return True
elif sabText == "Missing authentication": elif sabText == "Missing authentication":
logger.info(u"Incorrect username/password sent to SAB, NZB not sent") logger.info(u"Incorrect username/password sent to SAB, NZB not sent")
+57 -6
View File
@@ -31,7 +31,7 @@ import subprocess
import headphones import headphones
from headphones.common import USER_AGENT from headphones.common import USER_AGENT
from headphones import logger, db, helpers, classes, sab, nzbget, request from headphones import logger, db, helpers, classes, sab, nzbget, request
from headphones import transmission from headphones import transmission, notifiers
import lib.bencode as bencode import lib.bencode as bencode
@@ -359,6 +359,9 @@ def searchNZB(album, new=False, losslessOnly=False):
logger.info("Album type is audiobook/spokenword. Using audiobook category") logger.info("Album type is audiobook/spokenword. Using audiobook category")
for newznab_host in newznab_hosts: for newznab_host in newznab_hosts:
provider = newznab_host[0]
# Add a little mod for kere.ws # Add a little mod for kere.ws
if newznab_host[0] == "http://kere.ws": if newznab_host[0] == "http://kere.ws":
if categories == "3040": if categories == "3040":
@@ -573,14 +576,16 @@ def send_to_downloader(data, bestqual, album):
nzb = classes.NZBDataSearchResult() nzb = classes.NZBDataSearchResult()
nzb.extraInfo.append(data) nzb.extraInfo.append(data)
nzb.name = folder_name nzb.name = folder_name
nzbget.sendNZB(nzb) if not nzbget.sendNZB(nzb):
return
elif headphones.NZB_DOWNLOADER == 0: elif headphones.NZB_DOWNLOADER == 0:
nzb = classes.NZBDataSearchResult() nzb = classes.NZBDataSearchResult()
nzb.extraInfo.append(data) nzb.extraInfo.append(data)
nzb.name = folder_name nzb.name = folder_name
sab.sendNZB(nzb) if not sab.sendNZB(nzb):
return
# If we sent the file to sab, we can check how it was renamed and insert that into the snatched table # If we sent the file to sab, we can check how it was renamed and insert that into the snatched table
(replace_spaces, replace_dots) = sab.checkConfig() (replace_spaces, replace_dots) = sab.checkConfig()
@@ -695,6 +700,52 @@ def send_to_downloader(data, bestqual, album):
myDB.action('UPDATE albums SET status = "Snatched" WHERE AlbumID=?', [album['AlbumID']]) myDB.action('UPDATE albums SET status = "Snatched" WHERE AlbumID=?', [album['AlbumID']])
myDB.action('INSERT INTO snatched VALUES( ?, ?, ?, ?, DATETIME("NOW", "localtime"), ?, ?, ?)', [album['AlbumID'], bestqual[0], bestqual[1], bestqual[2], "Snatched", folder_name, kind]) myDB.action('INSERT INTO snatched VALUES( ?, ?, ?, ?, DATETIME("NOW", "localtime"), ?, ?, ?)', [album['AlbumID'], bestqual[0], bestqual[1], bestqual[2], "Snatched", folder_name, kind])
# notify
artist = album[1]
album = album[2]
title = artist + ' - ' + album
provider = bestqual[3]
if provider.startswith(("http://", "https://")):
provider = provider.split("//")[1]
name = folder_name if folder_name else None
if headphones.GROWL_ENABLED and headphones.GROWL_ONSNATCH:
logger.info(u"Sending Growl notification")
growl = notifiers.GROWL()
growl.notify(name,"Download started")
if headphones.PROWL_ENABLED and headphones.PROWL_ONSNATCH:
logger.info(u"Sending Prowl notification")
prowl = notifiers.PROWL()
prowl.notify(name,"Download started")
if headphones.PUSHOVER_ENABLED and headphones.PUSHOVER_ONSNATCH:
logger.info(u"Sending Pushover notification")
prowl = notifiers.PUSHOVER()
prowl.notify(name,"Download started")
if headphones.PUSHBULLET_ENABLED and headphones.PUSHBULLET_ONSNATCH:
logger.info(u"Sending PushBullet notification")
pushbullet = notifiers.PUSHBULLET()
pushbullet.notify(name + " has been snatched!", "Download started")
if headphones.TWITTER_ENABLED and headphones.TWITTER_ONSNATCH:
logger.info(u"Sending Twitter notification")
twitter = notifiers.TwitterNotifier()
twitter.notify_snatch(name)
if headphones.NMA_ENABLED and headphones.NMA_ONSNATCH:
logger.info(u"Sending NMA notification")
nma = notifiers.NMA()
nma.notify(snatched_nzb=name)
if headphones.PUSHALOT_ENABLED and headphones.PUSHALOT_ONSNATCH:
logger.info(u"Sending Pushalot notification")
pushalot = notifiers.PUSHALOT()
pushalot.notify(name,"Download started")
if headphones.OSX_NOTIFY_ENABLED and headphones.OSX_NOTIFY_ONSNATCH:
logger.info(u"Sending OS X notification")
osx_notify = notifiers.OSX_NOTIFY()
osx_notify.notify(artist, album, 'Snatched: ' + provider + '. ' + name)
if headphones.BOXCAR_ENABLED and headphones.BOXCAR_ONSNATCH:
logger.info(u"Sending Boxcar2 notification")
boxcar = notifiers.BOXCAR()
boxcar.notify('Headphones snatched: ' + title, 'From ' + provider + '. ' + name)
def verifyresult(title, artistterm, term, lossless): def verifyresult(title, artistterm, term, lossless):
title = re.sub('[\.\-\/\_]', ' ', title) title = re.sub('[\.\-\/\_]', ' ', title)
@@ -862,7 +913,7 @@ def searchTorrent(album, new=False, losslessOnly=False):
minimumseeders = int(headphones.NUMBEROFSEEDERS) - 1 minimumseeders = int(headphones.NUMBEROFSEEDERS) - 1
if headphones.KAT: if headphones.KAT:
provider = "Kick Ass Torrent" provider = "Kick Ass Torrents"
providerurl = url_fix("http://kickass.to/usearch/" + term) providerurl = url_fix("http://kickass.to/usearch/" + term)
if headphones.PREFERRED_QUALITY == 3 or losslessOnly: if headphones.PREFERRED_QUALITY == 3 or losslessOnly:
categories = "7" #music categories = "7" #music
@@ -1324,7 +1375,7 @@ def preprocess(resultlist):
#Get out of here if we're using Transmission or uTorrent #Get out of here if we're using Transmission or uTorrent
if headphones.TORRENT_DOWNLOADER != 0: if headphones.TORRENT_DOWNLOADER != 0:
return True, result return True, result
# get outta here if rutracker or piratebay # get outta here if rutracker
if result[3] == 'rutracker.org': if result[3] == 'rutracker.org':
return True, result return True, result
# Get out of here if it's a magnet link # Get out of here if it's a magnet link
@@ -1334,7 +1385,7 @@ def preprocess(resultlist):
# Download the torrent file # Download the torrent file
headers = {} headers = {}
if result[3] == 'Kick Ass Torrent': if result[3] == 'Kick Ass Torrents':
headers['Referer'] = 'http://kat.ph/' headers['Referer'] = 'http://kat.ph/'
elif result[3] == 'What.cd': elif result[3] == 'What.cd':
headers['User-Agent'] = 'Headphones' headers['User-Agent'] = 'Headphones'
-25
View File
@@ -49,31 +49,6 @@ def addTorrent(link):
retid = False retid = False
logger.info(u"Torrent sent to Transmission successfully") logger.info(u"Torrent sent to Transmission successfully")
if headphones.GROWL_ENABLED and headphones.GROWL_ONSNATCH:
logger.info(u"Sending Growl notification")
growl = notifiers.GROWL()
growl.notify(name,"Download started")
if headphones.PROWL_ENABLED and headphones.PROWL_ONSNATCH:
logger.info(u"Sending Prowl notification")
prowl = notifiers.PROWL()
prowl.notify(name,"Download started")
if headphones.PUSHOVER_ENABLED and headphones.PUSHOVER_ONSNATCH:
logger.info(u"Sending Pushover notification")
pushover = notifiers.PUSHOVER()
pushover.notify(name,"Download started")
if headphones.TWITTER_ENABLED and headphones.TWITTER_ONSNATCH:
logger.info(u"Sending Twitter notification")
twitter = notifiers.TwitterNotifier()
twitter.notify_snatch(nzb.name)
if headphones.NMA_ENABLED and headphones.NMA_ONSNATCH:
logger.info(u"Sending NMA notification")
nma = notifiers.NMA()
nma.notify(snatched_nzb=name)
if headphones.PUSHALOT_ENABLED and headphones.PUSHALOT_ONSNATCH:
logger.info(u"Sending Pushalot notification")
pushalot = notifiers.PUSHALOT()
pushalot.notify(name,"Download started")
return retid return retid
else: else:
+33 -4
View File
@@ -1059,6 +1059,12 @@ class WebInterface(object):
"pushbullet_deviceid": headphones.PUSHBULLET_DEVICEID, "pushbullet_deviceid": headphones.PUSHBULLET_DEVICEID,
"twitter_enabled": checked(headphones.TWITTER_ENABLED), "twitter_enabled": checked(headphones.TWITTER_ENABLED),
"twitter_onsnatch": checked(headphones.TWITTER_ONSNATCH), "twitter_onsnatch": checked(headphones.TWITTER_ONSNATCH),
"osx_notify_enabled": checked(headphones.OSX_NOTIFY_ENABLED),
"osx_notify_onsnatch": checked(headphones.OSX_NOTIFY_ONSNATCH),
"osx_notify_app": headphones.OSX_NOTIFY_APP,
"boxcar_enabled": checked(headphones.BOXCAR_ENABLED),
"boxcar_onsnatch": checked(headphones.BOXCAR_ONSNATCH),
"boxcar_token": headphones.BOXCAR_TOKEN,
"mirror_list": headphones.MIRRORLIST, "mirror_list": headphones.MIRRORLIST,
"mirror": headphones.MIRROR, "mirror": headphones.MIRROR,
"customhost": headphones.CUSTOMHOST, "customhost": headphones.CUSTOMHOST,
@@ -1105,10 +1111,11 @@ class WebInterface(object):
bitrate=None, samplingfrequency=None, encoderfolder=None, advancedencoder=None, encoderoutputformat=None, encodervbrcbr=None, encoderquality=None, encoderlossless=0, bitrate=None, samplingfrequency=None, encoderfolder=None, advancedencoder=None, encoderoutputformat=None, encodervbrcbr=None, encoderquality=None, encoderlossless=0,
delete_lossless_files=0, growl_enabled=0, growl_onsnatch=0, growl_host=None, growl_password=None, prowl_enabled=0, prowl_onsnatch=0, prowl_keys=None, prowl_priority=0, xbmc_enabled=0, xbmc_host=None, xbmc_username=None, xbmc_password=None, delete_lossless_files=0, growl_enabled=0, growl_onsnatch=0, growl_host=None, growl_password=None, prowl_enabled=0, prowl_onsnatch=0, prowl_keys=None, prowl_priority=0, xbmc_enabled=0, xbmc_host=None, xbmc_username=None, xbmc_password=None,
xbmc_update=0, xbmc_notify=0, nma_enabled=False, nma_apikey=None, nma_priority=0, nma_onsnatch=0, pushalot_enabled=False, pushalot_apikey=None, pushalot_onsnatch=0, synoindex_enabled=False, lms_enabled=0, lms_host=None, xbmc_update=0, xbmc_notify=0, nma_enabled=False, nma_apikey=None, nma_priority=0, nma_onsnatch=0, pushalot_enabled=False, pushalot_apikey=None, pushalot_onsnatch=0, synoindex_enabled=False, lms_enabled=0, lms_host=None,
pushover_enabled=0, pushover_onsnatch=0, pushover_keys=None, pushover_priority=0, pushover_apitoken=None, pushbullet_enabled=0, pushbullet_onsnatch=0, pushbullet_apikey=None, pushbullet_deviceid=None, twitter_enabled=0, twitter_onsnatch=0, mirror=None, customhost=None, customport=None, pushover_enabled=0, pushover_onsnatch=0, pushover_keys=None, pushover_priority=0, pushover_apitoken=None, pushbullet_enabled=0, pushbullet_onsnatch=0, pushbullet_apikey=None, pushbullet_deviceid=None, twitter_enabled=0, twitter_onsnatch=0,
customsleep=None, hpuser=None, hppass=None, preferred_bitrate_high_buffer=None, preferred_bitrate_low_buffer=None, preferred_bitrate_allow_lossless=0, cache_sizemb=None, osx_notify_enabled=0, osx_notify_onsnatch=0, osx_notify_app=None, boxcar_enabled=0, boxcar_onsnatch=0, boxcar_token=None, mirror=None, customhost=None, customport=None, customsleep=None, hpuser=None, hppass=None,
enable_https=0, https_cert=None, https_key=None, file_permissions=None, folder_permissions=None, plex_enabled=0, plex_server_host=None, plex_client_host=None, plex_username=None, preferred_bitrate_high_buffer=None, preferred_bitrate_low_buffer=None, preferred_bitrate_allow_lossless=0, cache_sizemb=None, enable_https=0, https_cert=None, https_key=None, file_permissions=None, folder_permissions=None,
plex_password=None, plex_update=0, plex_notify=0, songkick_enabled=0, songkick_apikey=None, songkick_location=None, songkick_filter_enabled=0, encoder_multicore=False, encoder_multicore_count=0, **kwargs): plex_enabled=0, plex_server_host=None, plex_client_host=None, plex_username=None, plex_password=None, plex_update=0, plex_notify=0,
songkick_enabled=0, songkick_apikey=None, songkick_location=None, songkick_filter_enabled=0, encoder_multicore=False, encoder_multicore_count=0, **kwargs):
headphones.HTTP_HOST = http_host headphones.HTTP_HOST = http_host
headphones.HTTP_PORT = http_port headphones.HTTP_PORT = http_port
@@ -1268,6 +1275,15 @@ class WebInterface(object):
headphones.SONGKICK_FILTER_ENABLED = songkick_filter_enabled headphones.SONGKICK_FILTER_ENABLED = songkick_filter_enabled
headphones.TWITTER_ENABLED = twitter_enabled headphones.TWITTER_ENABLED = twitter_enabled
headphones.TWITTER_ONSNATCH = twitter_onsnatch headphones.TWITTER_ONSNATCH = twitter_onsnatch
headphones.OSX_NOTIFY_ENABLED = osx_notify_enabled
headphones.OSX_NOTIFY_ONSNATCH = osx_notify_onsnatch
headphones.OSX_NOTIFY_APP = osx_notify_app
headphones.BOXCAR_ENABLED = boxcar_enabled
headphones.BOXCAR_ONSNATCH = boxcar_onsnatch
headphones.BOXCAR_TOKEN = boxcar_token
headphones.MIRROR = mirror headphones.MIRROR = mirror
headphones.CUSTOMHOST = customhost headphones.CUSTOMHOST = customhost
headphones.CUSTOMPORT = customport headphones.CUSTOMPORT = customport
@@ -1435,6 +1451,19 @@ class WebInterface(object):
return "Error sending tweet" return "Error sending tweet"
testTwitter.exposed = True testTwitter.exposed = True
def osxnotifyregister(self, app):
cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
from lib.osxnotify import registerapp as osxnotify
result, msg = osxnotify.registerapp(app)
if result:
osx_notify = notifiers.OSX_NOTIFY()
osx_notify.notify('Registered', result, 'Success :-)')
logger.info('Registered %s, to re-register a different app, delete this app first' % result)
else:
logger.warn(msg)
return msg
osxnotifyregister.exposed = True
class Artwork(object): class Artwork(object):
def index(self): def index(self):
return "Artwork" return "Artwork"
View File
BIN
View File
Binary file not shown.
+135
View File
@@ -0,0 +1,135 @@
#!/usr/bin/python
import shutil
import os
import stat
import platform
import subprocess
def registerapp(app):
# don't do any of this unless >= 10.8
v, _, _ = platform.mac_ver()
v = float('.'.join(v.split('.')[:2]))
if v < 10.8:
return None, 'Registering requires OS X version >= 10.8'
app_path = None
# check app bundle doesn't already exist
app_path = subprocess.check_output(['/usr/bin/mdfind', 'kMDItemCFBundleIdentifier == "ade.headphones.osxnotify"']).strip()
if app_path:
return app_path, 'App previously registered'
# check app doesn't already exist
app = app.strip()
if not app:
return None, 'Path/Application not entered'
if os.path.splitext(app)[1] == ".app":
app_path = app
else:
app_path = app + '.app'
if os.path.exists(app_path):
return None, 'App %s already exists, choose a different name' % app_path
# generate app
try:
os.mkdir(app_path)
os.mkdir(app_path + "/Contents")
os.mkdir(app_path + "/Contents/MacOS")
os.mkdir(app_path + "/Contents/Resources")
shutil.copy(os.path.join(os.path.dirname(__file__), "appIcon.icns"), app_path + "/Contents/Resources/")
version = "1.0.0"
bundleName = "OSXNotify"
bundleIdentifier = "ade.headphones.osxnotify"
f = open(app_path + "/Contents/Info.plist", "w")
f.write("""<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>main.py</string>
<key>CFBundleGetInfoString</key>
<string>%s</string>
<key>CFBundleIconFile</key>
<string>appIcon.icns</string>
<key>CFBundleIdentifier</key>
<string>%s</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>%s</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>%s</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>%s</string>
<key>NSAppleScriptEnabled</key>
<string>YES</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>
""" % (bundleName + " " + version, bundleIdentifier, bundleName, bundleName + " " + version, version))
f.close()
f = open(app_path + "/Contents/PkgInfo", "w")
f.write("APPL????")
f.close()
f = open(app_path + "/Contents/MacOS/main.py", "w")
f.write("""#!/usr/bin/python
objc = None
def swizzle(cls, SEL, func):
old_IMP = cls.instanceMethodForSelector_(SEL)
def wrapper(self, *args, **kwargs):
return func(self, old_IMP, *args, **kwargs)
new_IMP = objc.selector(wrapper, selector=old_IMP.selector,
signature=old_IMP.signature)
objc.classAddMethod(cls, SEL, new_IMP)
def notify(title, subtitle=None, text=None, sound=True):
global objc
objc = __import__("objc")
swizzle(objc.lookUpClass('NSBundle'),
b'bundleIdentifier',
swizzled_bundleIdentifier)
NSUserNotification = objc.lookUpClass('NSUserNotification')
NSUserNotificationCenter = objc.lookUpClass('NSUserNotificationCenter')
NSAutoreleasePool = objc.lookUpClass('NSAutoreleasePool')
pool = NSAutoreleasePool.alloc().init()
notification = NSUserNotification.alloc().init()
notification.setTitle_(title)
notification.setSubtitle_(subtitle)
notification.setInformativeText_(text)
notification.setSoundName_("NSUserNotificationDefaultSoundName")
notification_center = NSUserNotificationCenter.defaultUserNotificationCenter()
notification_center.deliverNotification_(notification)
del pool
def swizzled_bundleIdentifier(self, original):
return 'ade.headphones.osxnotify'
if __name__ == '__main__':
notify('Half Man Half Biscuit', 'Back in the DHSS', '99% Of Gargoyles Look Like Bob Todd')
""")
f.close()
oldmode = os.stat(app_path + "/Contents/MacOS/main.py").st_mode
os.chmod(app_path + "/Contents/MacOS/main.py", oldmode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
return app_path, 'App registered'
except Exception, e:
return None, 'Error creating App %s. %s' % (app_path, e)