Skip to content
This repository

Ampache resolver #33

Open
wants to merge 6 commits into from

1 participant

Remo Giermann
Remo Giermann
modul commented

Hey,
I wrote another ampache resolver which uses its web API and can resolve to multiple ampaches at once.
It works for me, you might want to take a look.

Best regards,
mo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
3  contrib/ampache-api/README
... ... @@ -0,0 +1,3 @@
  1 + This is an Ampache resolver which uses Ampache's XML Web-API.
  2 + To use it, one only needs a user account on any ampache installation.
  3 + It's possible to use multiple Ampache servers/accounts.
4 contrib/ampache-api/ampache-resolver.cfg
... ... @@ -0,0 +1,4 @@
  1 +[localhost] # section name is used to form source name
  2 +url = http://localhost/ampache
  3 +user = John
  4 +password = Doe
92 contrib/ampache-api/ampache-resolver.py
... ... @@ -0,0 +1,92 @@
  1 +#!/usr/bin/env python
  2 +# -*- coding: utf8 -*-
  3 +#
  4 +# Ampache resolver for playdar
  5 +# using Ampache's Web API
  6 +#
  7 +# author: Remo Giermann <mo@liberejo.de>
  8 +# created: 2011/04/03
  9 +#
  10 +
  11 +import os, sys
  12 +from ConfigParser import ConfigParser
  13 +
  14 +import ampache
  15 +import playdar_resolver
  16 +from playdar_resolver import soundex
  17 +
  18 +configuration = os.path.join(os.path.dirname(sys.argv[0]), "ampache-resolver.cfg")
  19 +
  20 +
  21 +class AmpacheResolver(playdar_resolver.PlaydarResolver):
  22 +
  23 + def __init__(self):
  24 +
  25 + self.ampaches = []
  26 +
  27 + config = ConfigParser()
  28 + if not config.read(configuration):
  29 + return
  30 + else:
  31 + for section in config.sections():
  32 + if config.options(section) == ['url', 'password', 'user']:
  33 + url = config.get(section, 'url')
  34 + un = config.get(section, 'user')
  35 + pw = config.get(section, 'password')
  36 +
  37 + amp = ampache.Ampache(url)
  38 + if amp.handshake(un, pw) is True:
  39 + self.ampaches.append((amp, "Ampache (%s)" % section))
  40 +
  41 +
  42 + def resolver_settings(self):
  43 + """My settings"""
  44 + return {'name':"Ampache (API) Resolver", 'targettime':5000, 'weight':60}
  45 +
  46 +
  47 + def results(self, query):
  48 + results = []
  49 +
  50 + for amp, name in self.ampaches:
  51 +
  52 + songs = amp.search_songs(u'{artist} {track}'.format(**query))
  53 +
  54 + # first item in search results is best match, last item is worst,
  55 + # so scoring is simple:
  56 + score = 1.0
  57 + for s in songs:
  58 +
  59 + if s.artist['name'].lower() == query['artist'].lower() \
  60 + and score > 0.5:
  61 +
  62 + results.append(
  63 + {'artist': s.artist['name'],
  64 + 'track': s.title,
  65 + 'album': s.album['name'],
  66 + 'duration': int(s.time),
  67 + 'url': s.url,
  68 + 'score': score,
  69 + 'extension': s.url[-3:],
  70 + 'art': s.art,
  71 + #'size': int(s.size),
  72 + 'source': name
  73 + })
  74 + # decrement score for next match
  75 + score -= .1
  76 +
  77 + else:
  78 + break
  79 +
  80 +
  81 + return results
  82 +
  83 +
  84 +
  85 +if __name__ == "__main__":
  86 + #q = {'qid':'test', 'artist': 'iron maiden', 'track': 'aces high'}
  87 + #r = AmpacheResolver()
  88 + #playdar_resolver.print_result(r.resolve(q))
  89 + try:
  90 + AmpacheResolver.start_static()
  91 + except:
  92 + traceback.print_exc(file=sys.stderr)
180 contrib/ampache-api/ampache.py
... ... @@ -0,0 +1,180 @@
  1 +# -*- coding: utf8 -*-
  2 +# This class speaks to the Ampache XML API
  3 +#
  4 +# author: Remo Giermann <mo@liberejo.de>
  5 +# created: 2011/03/31
  6 +#
  7 +
  8 +import time, urllib
  9 +from hashlib import sha256
  10 +from xml.etree.ElementTree import ElementTree
  11 +
  12 +def passphrase(password):
  13 + """
  14 + Generates the PASSPRHASE needed for a handshake.
  15 + """
  16 + now = int(time.time())
  17 + key = sha256(password).hexdigest()
  18 + phrase = sha256(str(now)+key).hexdigest()
  19 +
  20 + return (now, phrase)
  21 +
  22 +
  23 +class AmpacheArtist(object):
  24 + """
  25 + Simple container class corresponding to
  26 + Ampache's Artist XML.
  27 + """
  28 + def __init__(self, element):
  29 + self.id = element.attrib['id']
  30 + self.tags = element.findall('tag')
  31 + self.name = element.find('name').text
  32 +
  33 + self.albums = element.find('albums').text
  34 + self.songs = element.find('songs').text
  35 +
  36 + self.preciserating = element.find('preciserating').text
  37 + self.rating = element.find('rating').text
  38 +
  39 +class AmpacheAlbum(object):
  40 + """
  41 + Simple container class corresponding to
  42 + Ampache's Album XML.
  43 + """
  44 + def __init__(self, element):
  45 + self.id = element.attrib['id']
  46 + self.tags = element.findall('tag')
  47 + self.name = element.find('name').text
  48 + artist = element.find('artist')
  49 + self.artist = {'id': artist.attrib['id'], 'name': artist.text}
  50 +
  51 + self.year = element.find('year').text
  52 + self.tracks = element.find('tracks').text
  53 + self.disk = element.find('disk').text
  54 + self.art = element.find('art').text
  55 +
  56 + self.preciserating = element.find('preciserating').text
  57 + self.rating = element.find('rating').text
  58 +
  59 +class AmpacheSong(object):
  60 + """
  61 + Simple container class corresponding to
  62 + Ampache's Song XML.
  63 + """
  64 + def __init__(self, element):
  65 + self.id = element.attrib['id']
  66 + self.tags = element.findall('tag')
  67 + self.title = element.find('title').text
  68 + artist = element.find('artist')
  69 + self.artist = {'id': artist.attrib['id'], 'name': artist.text}
  70 + album = element.find('album')
  71 + self.album = {'id': album.attrib['id'], 'name': album.text}
  72 +
  73 + self.track = element.find('track').text
  74 + self.time = element.find('time').text
  75 + self.url = element.find('url').text
  76 + self.size = element.find('size').text
  77 + self.art = element.find('art').text
  78 +
  79 + self.preciserating = element.find('preciserating').text
  80 + self.rating = element.find('rating').text
  81 +
  82 +
  83 +class Ampache(object):
  84 + """
  85 + This class speaks to an Ampache server through its
  86 + XML API. Only a few methods are implemented, but
  87 + every method of the API could be added quickly.
  88 +
  89 + See http://ampache.org/wiki/dev:xmlapi for info.
  90 + """
  91 +
  92 + def __init__(self, url):
  93 + self.auth = ''
  94 + self.serverurl = url + u"/server/xml.server.php?"
  95 +
  96 +
  97 + def __query(self, **kwargs):
  98 + for k,v in kwargs.items():
  99 + if type(v) is unicode:
  100 + kwargs[k] = v.encode('utf8')
  101 + query = urllib.urlencode(kwargs)
  102 +
  103 + try:
  104 + fd = urllib.urlopen(self.serverurl+query)
  105 + except IOError:
  106 + self.auth = ''
  107 + return None
  108 + else:
  109 + tree = ElementTree()
  110 + tree.parse(fd)
  111 + return tree
  112 +
  113 +
  114 + def __action(self, action, **kwargs):
  115 + if not self.auth:
  116 + return False
  117 +
  118 + return self.__query(action=action, auth=self.auth, **kwargs)
  119 +
  120 +
  121 + def handshake(self, username, password):
  122 +
  123 + timestamp, phrase = passphrase(password)
  124 + tree = self.__query(action='handshake', auth=phrase, timestamp=timestamp, version=350001, user=username)
  125 +
  126 + if tree is None:
  127 + auth = None
  128 + else:
  129 + auth = tree.find('auth')
  130 +
  131 +
  132 + if auth is None:
  133 + self.auth = ''
  134 + return False
  135 + else:
  136 + self.auth = auth.text
  137 + return True
  138 +
  139 +
  140 + # API method 'artist'
  141 + def artists(self, filter, **kwargs):
  142 + tree = self.__action('artists', filter=filter, **kwargs)
  143 + artists = tree.findall('artist')
  144 + return [AmpacheArtist(a) for a in artists]
  145 +
  146 + # API method 'artist_songs'
  147 + def artist_songs(self, artist_uid):
  148 + tree = self.__action('artist_songs', filter=artist_uid)
  149 + songs = tree.findall('song')
  150 + return [AmpacheSong(a) for a in songs]
  151 +
  152 + # API method 'artist_albums'
  153 + def artist_albums(self, artist_uid):
  154 + tree = self.__action('artist_albums', filter=artist_uid)
  155 + albums = tree.findall('album')
  156 + return [AmpacheAlbum(a) for a in albums]
  157 +
  158 + # API method 'albums'
  159 + def albums(self, filter, **kwargs):
  160 + tree = self.__action('albums', filter=filter, **kwargs)
  161 + albums = tree.findall('albums')
  162 + return [AmpacheAlbums(a) for a in albums]
  163 +
  164 + # API method 'album_songs'
  165 + def album_songs(self, album_uid):
  166 + tree = self.__action('album_songs', filter=album_uid)
  167 + songs = tree.findall('song')
  168 + return [AmpacheSong(a) for a in songs]
  169 +
  170 + # API method 'songs'
  171 + def songs(self, filter, **kwargs):
  172 + tree = self.__action('songs', filter=filter, **kwargs)
  173 + songs = tree.findall('song')
  174 + return [AmpacheSong(a) for a in songs]
  175 +
  176 + # API method 'search_songs'
  177 + def search_songs(self, filter, **kwargs):
  178 + tree = self.__action('search_songs', filter=filter, **kwargs)
  179 + songs = tree.findall('song')
  180 + return [AmpacheSong(a) for a in songs]
1  contrib/ampache-api/playdar_resolver.py

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.