Mercurial > hg > pmhd
changeset 7:8c29444cb5fd
Just did some work
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/7digital-python/.gitignore Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,2 @@ +*.pyc +*.pkl
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/7digital-python/LICENSE Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,19 @@ +Copyright (C) 2011 by 7digital + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/7digital-python/README Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,92 @@ +7 digital API access via python | http://developer.7digital.net/ + +======= +Download python code: http://github.com/ocelma/7-digital/downloads + +Using the 7digital developer platform you can: + * Build digital music applications for the web, desktop and mobile devices + * Generate revenue with your own branded music download store + * Access our catalogue of over 10,000,000 MP3 music tracks + * Create your own streaming radio service + * Include official sleeve artwork and audio samples + * Have access to major labels licensed content in 16 major territories included US, Canada, UK, Germany, France, Spain, Italy and many more + +See some usage examples here: http://github.com/ocelma/7-digital/tree/v1.0 + +DOWNLOAD LATEST VERSION: http://github.com/ocelma/7-digital/downloads + +- Examples: + +# If you want to use the cache, you have to manually create that dir (e.g. $ mkdir ./cache) +import py7digital + +#Search artist +results = py7digital.search_artist('stones') +print results.get_total_result_count() +for artist in results.get_next_page(): + print artist.get_name() #, artist.get_image(), artist.get_url(), artist.get_tags() + print '\tTop tracks:' + for top_track in artist.get_top_tracks(): + print '\t\t', top_track.get_title(), top_track.get_isrc(), top_track.get_duration(), top_track.get_position(), top_track.get_explicit(), top_track.get_version() + print '\tRec. Albums:' + for rec_album in artist.get_recommended_albums(): + print '\t\t', rec_album, rec_album.get_year() #, album.get_barcode(), album.get_type(), album.get_artist(), album.get_tags(), album.get_label() + for album in artist.get_albums(5): + print '\t', album, album.get_year(), album.get_barcode(), album.get_type(), album.get_artist(), album.get_tags(), album.get_label(), album.get_release_date(), album.get_added_date() + for sim_album in album.get_similar(): + print '\t\tSimilar:', sim_album, sim_album.get_year(), sim_album.get_artist() + for track in album.get_tracks(): + print '\t\t', track, track.get_isrc() #, track.get_url(), track.get_audio() + +#Browse artists starting with 'J' +results = py7digital.browse_artists('j') +print results.get_total_result_count() +for artist in results.get_next_page(): + print artist.get_name() #, artist.get_image(), artist.get_url(), artist.get_tags() + for album in artist.get_albums(2): + print '\t', album, album.get_year() #album.get_barcode(), album.get_type(), album.get_artist(), album.get_tags(), album.get_label() + for track in album.get_tracks(): + print '\t\t', track.get_title(), track.get_isrc() #, track.get_url(), track.get_audio() + +#Search albums +searcher = py7digital.search_album('u2') +print searcher.get_total_result_count() +while searcher.has_results(): + for album in searcher.get_next_page(): + print album, album.get_similar() + +#Search tracks +searcher = py7digital.search_track('u2 one') +print searcher.get_total_result_count() +while searcher.has_results(): + for track in searcher.get_next_page(): + print track + +# New releases in a given period of time +results = py7digital.album_releases('20100901', '20100924') +for album in results.get_next_page(): + print album, album.get_year(), album.get_barcode(), album.get_type(), album.get_artist(), album.get_tags(), album.get_label(), album.get_release_date(), album.get_added_date() + for sim_album in album.get_similar(): + print '\tSimilar:', sim_album, sim_album.get_year(), sim_album.get_artist() + for track in album.get_tracks(): + print '\t', track, track.get_isrc() #, track.get_url(), track.get_audio() + +# Album charts in a given period of time +results = py7digital.album_charts('month', '20100901') +for album in results.get_next_page(): + print album, album.get_year(), album.get_barcode(), album.get_type(), album.get_artist(), album.get_tags(), album.get_label(), album.get_release_date(), album.get_added_date() + for sim_album in album.get_similar(): + print '\tSimilar:', sim_album, sim_album.get_year(), sim_album.get_artist() + for track in album.get_tracks(): + print '\t', track, track.get_isrc() #, track.get_url(), track.get_audio() + + +-- OAuth Usage + +auth = Oauth7digital(CONSUMER_KEY, CONSUMER_SECRET) +token = auth.request_token() +authorized = auth.authorize_request_token(token) +access_token = auth.request_access_token(token) + +sevendigital = Oauth7digital(CONSUMER_KEY, CONSUMER_SECRET, access_token) +results = sevendigital.get_locker()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/7digital-python/TODO Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,44 @@ +- Catalogue API + + + means DONE + - means TODO + + o Artist methods + + artist/browse + - artist/chart + + artist/details + + artist/releases + + artist/search + + artist/toptracks + o Release methods + + release/bydate + + release/chart + + release/details + + release/recommend + + release/search + + release/tracks + o Track methods + - track/chart + + track/details + + track/preview + + track/search + o Tag methods + + tag + + artist/tags + - artist/bytag/top + + release/tags + - release/bytag/new + - release/bytag/top + +- RELEASE attributes to add: + +price + price object pricing information + +formats + list of format objects list of formats the tracks appearing on this release are available in + +- TRACK attributes to add: + +price + price object pricing information
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/7digital-python/access_token.pkl Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,21 @@ +ccopy_reg +_reconstructor +p0 +(clib.oauth +OAuthToken +p1 +c__builtin__ +object +p2 +Ntp3 +Rp4 +(dp5 +S'secret' +p6 +S'H9Sd66Uyv+92e1WH0giB+g==' +p7 +sS'key' +p8 +S'qtKibfZanGxQR3B9QVHCsg==' +p9 +sb. \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/7digital-python/app.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,48 @@ +import pickle +from lib.oauth7digital import Oauth7digital + + +TOKEN_FILE = './access_token.pkl' + +def test_sevendigital(): + try: + pkl_file = open(TOKEN_FILE, 'rb') + access_token = pickle.load(pkl_file) + pkl_file.close() + except: + access_token = None + if access_token: + print 'You have an access token: %s' % str(access_token.key) + else: + auth = Oauth7digital(CONSUMER_KEY, CONSUMER_SECRET) + + token = auth.request_token() + authorized = auth.authorize_request_token(token) + access_token = auth.request_access_token(token) + + pkl_file=open(TOKEN_FILE, 'wb') + pickle.dump(access_token, pkl_file) + pkl_file.close() + + return access_token + +def test_locker(): + access_token = test_sevendigital() + + sevendigital = Oauth7digital(CONSUMER_KEY, CONSUMER_SECRET, access_token) + results = sevendigital.get_locker() + for i in results: + print "-----------------------------" + print i.release.title + print i.release.artist.name + for a in i.tracks: + print a.track.title + return results + +# app entry point +if __name__ == '__main__': + test_locker() + print 'Done.' + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/7digital-python/lib/oauth.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,657 @@ +""" +The MIT License + +Copyright (c) 2007 Leah Culver + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +import cgi +import urllib +import time +import random +import urlparse +import hmac +import binascii +import re + + +VERSION = '1.0' # Hi Blaine! +HTTP_METHOD = 'GET' +SIGNATURE_METHOD = 'PLAINTEXT' + + +class OAuthError(RuntimeError): + """Generic exception class.""" + def __init__(self, message='OAuth error occured.'): + self.message = message + +def build_authenticate_header(realm=''): + """Optional WWW-Authenticate header (401 error)""" + return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} + +def escape(s): + """Escape a URL including any /.""" + return urllib.quote(s, safe='~') + +def _utf8_str(s): + """Convert unicode to utf-8.""" + if isinstance(s, unicode): + return s.encode("utf-8") + else: + return str(s) + +def generate_timestamp(): + """Get seconds since epoch (UTC).""" + return int(time.time()) + +def generate_nonce(length=8): + """Generate pseudorandom number.""" + return ''.join([str(random.randint(0, 9)) for i in range(length)]) + +def generate_verifier(length=8): + """Generate pseudorandom number.""" + return ''.join([str(random.randint(0, 9)) for i in range(length)]) + + +class OAuthConsumer(object): + """Consumer of OAuth authentication. + + OAuthConsumer is a data type that represents the identity of the Consumer + via its shared secret with the Service Provider. + + """ + key = None + secret = None + + def __init__(self, key, secret): + self.key = key + self.secret = secret + + +class OAuthToken(object): + """OAuthToken is a data type that represents an End User via either an access + or request token. + + key -- the token + secret -- the token secret + + """ + key = None + secret = None + callback = None + callback_confirmed = None + verifier = None + + def __init__(self, key, secret): + self.key = key + self.secret = secret + + def set_callback(self, callback): + self.callback = callback + self.callback_confirmed = 'true' + + def set_verifier(self, verifier=None): + if verifier is not None: + self.verifier = verifier + else: + self.verifier = generate_verifier() + + def get_callback_url(self): + if self.callback and self.verifier: + # Append the oauth_verifier. + parts = urlparse.urlparse(self.callback) + scheme, netloc, path, params, query, fragment = parts[:6] + if query: + query = '%s&oauth_verifier=%s' % (query, self.verifier) + else: + query = 'oauth_verifier=%s' % self.verifier + return urlparse.urlunparse((scheme, netloc, path, params, + query, fragment)) + return self.callback + + def to_string(self): + data = { + 'oauth_token': self.key, + 'oauth_token_secret': self.secret, + } + if self.callback_confirmed is not None: + data['oauth_callback_confirmed'] = self.callback_confirmed + return urllib.urlencode(data) + + def from_string(s): + """ Returns a token from something like: + oauth_token_secret=xxx&oauth_token=xxx + """ + print "******* %s" % s.__class__ + #params = urlparse.parse_qs(s, keep_blank_values=False) + + key = re.search("<oauth_token>(\w.+)</oauth_token>", s).groups()[0] + print "@@@@@@ key: %s" %key + secret = re.search("<oauth_token_secret>(\w.+)</oauth_token_secret>", s).groups()[0] + print "@@@@@@ secret: %s" % secret + token = OAuthToken(key, secret) + + return token + from_string = staticmethod(from_string) + + def __str__(self): + return self.to_string() + + +class OAuthRequest(object): + """OAuthRequest represents the request and can be serialized. + + OAuth parameters: + - oauth_consumer_key + - oauth_token + - oauth_signature_method + - oauth_signature + - oauth_timestamp + - oauth_nonce + - oauth_version + - oauth_verifier + ... any additional parameters, as defined by the Service Provider. + """ + parameters = None # OAuth parameters. + http_method = HTTP_METHOD + http_url = None + version = VERSION + + def __init__(self, http_method=HTTP_METHOD, http_url=None, parameters=None): + self.http_method = http_method + self.http_url = http_url + self.parameters = parameters or {} + + def set_parameter(self, parameter, value): + self.parameters[parameter] = value + + def get_parameter(self, parameter): + try: + return self.parameters[parameter] + except: + raise OAuthError('Parameter not found: %s' % parameter) + + def _get_timestamp_nonce(self): + return self.get_parameter('oauth_timestamp'), self.get_parameter( + 'oauth_nonce') + + def get_nonoauth_parameters(self): + """Get any non-OAuth parameters.""" + parameters = {} + for k, v in self.parameters.iteritems(): + # Ignore oauth parameters. + if k.find('oauth_') < 0: + parameters[k] = v + return parameters + + def to_header(self, realm=''): + """Serialize as a header for an HTTPAuth request.""" + auth_header = 'OAuth realm="%s"' % realm + # Add the oauth parameters. + if self.parameters: + for k, v in self.parameters.iteritems(): + if k[:6] == 'oauth_': + auth_header += ', %s="%s"' % (k, escape(str(v))) + return {'Authorization': auth_header} + + def to_postdata(self): + """Serialize as post data for a POST request.""" + return '&'.join(['%s=%s' % (escape(str(k)), escape(str(v))) \ + for k, v in self.parameters.iteritems()]) + + def to_url(self): + """Serialize as a URL for a GET request.""" + return '%s?%s' % (self.get_normalized_http_url(), self.to_postdata()) + + def get_normalized_parameters(self): + """Return a string that contains the parameters that must be signed.""" + params = self.parameters + try: + # Exclude the signature if it exists. + del params['oauth_signature'] + except: + pass + # Escape key values before sorting. + key_values = [(escape(_utf8_str(k)), escape(_utf8_str(v))) \ + for k,v in params.items()] + # Sort lexicographically, first after key, then after value. + key_values.sort() + # Combine key value pairs into a string. + return '&'.join(['%s=%s' % (k, v) for k, v in key_values]) + + def get_normalized_http_method(self): + """Uppercases the http method.""" + return self.http_method.upper() + + def get_normalized_http_url(self): + """Parses the URL and rebuilds it to be scheme://host/path.""" + parts = urlparse.urlparse(self.http_url) + scheme, netloc, path = parts[:3] + # Exclude default port numbers. + if scheme == 'http' and netloc[-3:] == ':80': + netloc = netloc[:-3] + elif scheme == 'https' and netloc[-4:] == ':443': + netloc = netloc[:-4] + return '%s://%s%s' % (scheme, netloc, path) + + def sign_request(self, signature_method, consumer, token): + """Set the signature parameter to the result of build_signature.""" + # Set the signature method. + self.set_parameter('oauth_signature_method', + signature_method.get_name()) + # Set the signature. + self.set_parameter('oauth_signature', + self.build_signature(signature_method, consumer, token)) + + def build_signature(self, signature_method, consumer, token): + """Calls the build signature method within the signature method.""" + return signature_method.build_signature(self, consumer, token) + + def from_request(http_method, http_url, headers=None, parameters=None, + query_string=None): + """Combines multiple parameter sources.""" + if parameters is None: + parameters = {} + + # Headers + if headers and 'Authorization' in headers: + auth_header = headers['Authorization'] + # Check that the authorization header is OAuth. + if auth_header[:6] == 'OAuth ': + auth_header = auth_header[6:] + try: + # Get the parameters from the header. + header_params = OAuthRequest._split_header(auth_header) + parameters.update(header_params) + except: + raise OAuthError('Unable to parse OAuth parameters from ' + 'Authorization header.') + + # GET or POST query string. + if query_string: + query_params = OAuthRequest._split_url_string(query_string) + parameters.update(query_params) + + # URL parameters. + param_str = urlparse.urlparse(http_url)[4] # query + url_params = OAuthRequest._split_url_string(param_str) + parameters.update(url_params) + + if parameters: + return OAuthRequest(http_method, http_url, parameters) + + return None + from_request = staticmethod(from_request) + + def from_consumer_and_token(oauth_consumer, token=None, + callback=None, verifier=None, http_method=HTTP_METHOD, + http_url=None, parameters=None): + if not parameters: + parameters = {} + + defaults = { + 'oauth_consumer_key': oauth_consumer.key, + 'oauth_timestamp': generate_timestamp(), + 'oauth_nonce': generate_nonce(), + 'oauth_version': OAuthRequest.version, + } + + defaults.update(parameters) + parameters = defaults + + if token: + parameters['oauth_token'] = token.key + if token.callback: + parameters['oauth_callback'] = token.callback + # 1.0a support for verifier. + if verifier: + parameters['oauth_verifier'] = verifier + elif callback: + # 1.0a support for callback in the request token request. + parameters['oauth_callback'] = callback + + return OAuthRequest(http_method, http_url, parameters) + from_consumer_and_token = staticmethod(from_consumer_and_token) + + def from_token_and_callback(token, callback=None, http_method=HTTP_METHOD, + http_url=None, parameters=None): + if not parameters: + parameters = {} + + parameters['oauth_token'] = token.key + + if callback: + parameters['oauth_callback'] = callback + + return OAuthRequest(http_method, http_url, parameters) + from_token_and_callback = staticmethod(from_token_and_callback) + + def _split_header(header): + """Turn Authorization: header into parameters.""" + params = {} + parts = header.split(',') + for param in parts: + # Ignore realm parameter. + if param.find('realm') > -1: + continue + # Remove whitespace. + param = param.strip() + # Split key-value. + param_parts = param.split('=', 1) + # Remove quotes and unescape the value. + params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"')) + return params + _split_header = staticmethod(_split_header) + + def _split_url_string(param_str): + """Turn URL string into parameters.""" + parameters = cgi.parse_qs(param_str, keep_blank_values=False) + for k, v in parameters.iteritems(): + parameters[k] = urllib.unquote(v[0]) + return parameters + _split_url_string = staticmethod(_split_url_string) + +class OAuthServer(object): + """A worker to check the validity of a request against a data store.""" + timestamp_threshold = 300 # In seconds, five minutes. + version = VERSION + signature_methods = None + data_store = None + + def __init__(self, data_store=None, signature_methods=None): + self.data_store = data_store + self.signature_methods = signature_methods or {} + + def set_data_store(self, data_store): + self.data_store = data_store + + def get_data_store(self): + return self.data_store + + def add_signature_method(self, signature_method): + self.signature_methods[signature_method.get_name()] = signature_method + return self.signature_methods + + def fetch_request_token(self, oauth_request): + """Processes a request_token request and returns the + request token on success. + """ + try: + # Get the request token for authorization. + token = self._get_token(oauth_request, 'request') + except OAuthError: + # No token required for the initial token request. + version = self._get_version(oauth_request) + consumer = self._get_consumer(oauth_request) + try: + callback = self.get_callback(oauth_request) + except OAuthError: + callback = None # 1.0, no callback specified. + self._check_signature(oauth_request, consumer, None) + # Fetch a new token. + token = self.data_store.fetch_request_token(consumer, callback) + return token + + def fetch_access_token(self, oauth_request): + """Processes an access_token request and returns the + access token on success. + """ + version = self._get_version(oauth_request) + consumer = self._get_consumer(oauth_request) + try: + verifier = self._get_verifier(oauth_request) + except OAuthError: + verifier = None + # Get the request token. + token = self._get_token(oauth_request, 'request') + self._check_signature(oauth_request, consumer, token) + new_token = self.data_store.fetch_access_token(consumer, token, verifier) + return new_token + + def verify_request(self, oauth_request): + """Verifies an api call and checks all the parameters.""" + # -> consumer and token + version = self._get_version(oauth_request) + consumer = self._get_consumer(oauth_request) + # Get the access token. + token = self._get_token(oauth_request, 'access') + self._check_signature(oauth_request, consumer, token) + parameters = oauth_request.get_nonoauth_parameters() + return consumer, token, parameters + + def authorize_token(self, token, user): + """Authorize a request token.""" + return self.data_store.authorize_request_token(token, user) + + def get_callback(self, oauth_request): + """Get the callback URL.""" + return oauth_request.get_parameter('oauth_callback') + + def build_authenticate_header(self, realm=''): + """Optional support for the authenticate header.""" + return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} + + def _get_version(self, oauth_request): + """Verify the correct version request for this server.""" + try: + version = oauth_request.get_parameter('oauth_version') + except: + version = VERSION + if version and version != self.version: + raise OAuthError('OAuth version %s not supported.' % str(version)) + return version + + def _get_signature_method(self, oauth_request): + """Figure out the signature with some defaults.""" + try: + signature_method = oauth_request.get_parameter( + 'oauth_signature_method') + except: + signature_method = SIGNATURE_METHOD + try: + # Get the signature method object. + signature_method = self.signature_methods[signature_method] + except: + signature_method_names = ', '.join(self.signature_methods.keys()) + raise OAuthError('Signature method %s not supported try one of the ' + 'following: %s' % (signature_method, signature_method_names)) + + return signature_method + + def _get_consumer(self, oauth_request): + consumer_key = oauth_request.get_parameter('oauth_consumer_key') + consumer = self.data_store.lookup_consumer(consumer_key) + if not consumer: + raise OAuthError('Invalid consumer.') + return consumer + + def _get_token(self, oauth_request, token_type='access'): + """Try to find the token for the provided request token key.""" + token_field = oauth_request.get_parameter('oauth_token') + token = self.data_store.lookup_token(token_type, token_field) + if not token: + raise OAuthError('Invalid %s token: %s' % (token_type, token_field)) + return token + + def _get_verifier(self, oauth_request): + return oauth_request.get_parameter('oauth_verifier') + + def _check_signature(self, oauth_request, consumer, token): + timestamp, nonce = oauth_request._get_timestamp_nonce() + self._check_timestamp(timestamp) + self._check_nonce(consumer, token, nonce) + signature_method = self._get_signature_method(oauth_request) + try: + signature = oauth_request.get_parameter('oauth_signature') + except: + raise OAuthError('Missing signature.') + # Validate the signature. + valid_sig = signature_method.check_signature(oauth_request, consumer, + token, signature) + if not valid_sig: + key, base = signature_method.build_signature_base_string( + oauth_request, consumer, token) + raise OAuthError('Invalid signature. Expected signature base ' + 'string: %s' % base) + built = signature_method.build_signature(oauth_request, consumer, token) + + def _check_timestamp(self, timestamp): + """Verify that timestamp is recentish.""" + timestamp = int(timestamp) + now = int(time.time()) + lapsed = abs(now - timestamp) + if lapsed > self.timestamp_threshold: + raise OAuthError('Expired timestamp: given %d and now %s has a ' + 'greater difference than threshold %d' % + (timestamp, now, self.timestamp_threshold)) + + def _check_nonce(self, consumer, token, nonce): + """Verify that the nonce is uniqueish.""" + nonce = self.data_store.lookup_nonce(consumer, token, nonce) + if nonce: + raise OAuthError('Nonce already used: %s' % str(nonce)) + + +class OAuthClient(object): + """OAuthClient is a worker to attempt to execute a request.""" + consumer = None + token = None + + def __init__(self, oauth_consumer, oauth_token): + self.consumer = oauth_consumer + self.token = oauth_token + + def get_consumer(self): + return self.consumer + + def get_token(self): + return self.token + + def fetch_request_token(self, oauth_request): + """-> OAuthToken.""" + raise NotImplementedError + + def fetch_access_token(self, oauth_request): + """-> OAuthToken.""" + raise NotImplementedError + + def access_resource(self, oauth_request): + """-> Some protected resource.""" + raise NotImplementedError + + +class OAuthDataStore(object): + """A database abstraction used to lookup consumers and tokens.""" + + def lookup_consumer(self, key): + """-> OAuthConsumer.""" + raise NotImplementedError + + def lookup_token(self, oauth_consumer, token_type, token_token): + """-> OAuthToken.""" + raise NotImplementedError + + def lookup_nonce(self, oauth_consumer, oauth_token, nonce): + """-> OAuthToken.""" + raise NotImplementedError + + def fetch_request_token(self, oauth_consumer, oauth_callback): + """-> OAuthToken.""" + raise NotImplementedError + + def fetch_access_token(self, oauth_consumer, oauth_token, oauth_verifier): + """-> OAuthToken.""" + raise NotImplementedError + + def authorize_request_token(self, oauth_token, user): + """-> OAuthToken.""" + raise NotImplementedError + + +class OAuthSignatureMethod(object): + """A strategy class that implements a signature method.""" + def get_name(self): + """-> str.""" + raise NotImplementedError + + def build_signature_base_string(self, oauth_request, oauth_consumer, oauth_token): + """-> str key, str raw.""" + raise NotImplementedError + + def build_signature(self, oauth_request, oauth_consumer, oauth_token): + """-> str.""" + raise NotImplementedError + + def check_signature(self, oauth_request, consumer, token, signature): + built = self.build_signature(oauth_request, consumer, token) + return built == signature + + +class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod): + + def get_name(self): + return 'HMAC-SHA1' + + def build_signature_base_string(self, oauth_request, consumer, token): + sig = ( + escape(oauth_request.get_normalized_http_method()), + escape(oauth_request.get_normalized_http_url()), + escape(oauth_request.get_normalized_parameters()), + ) + + key = '%s&' % escape(consumer.secret) + if token: + key += escape(token.secret) + raw = '&'.join(sig) + return key, raw + + def build_signature(self, oauth_request, consumer, token): + """Builds the base signature string.""" + key, raw = self.build_signature_base_string(oauth_request, consumer, + token) + + # HMAC object. + try: + import hashlib # 2.5 + hashed = hmac.new(key, raw, hashlib.sha1) + except: + import sha # Deprecated + hashed = hmac.new(key, raw, sha) + + # Calculate the digest base 64. + return binascii.b2a_base64(hashed.digest())[:-1] + + +class OAuthSignatureMethod_PLAINTEXT(OAuthSignatureMethod): + + def get_name(self): + return 'PLAINTEXT' + + def build_signature_base_string(self, oauth_request, consumer, token): + """Concatenates the consumer key and secret.""" + sig = '%s&' % escape(consumer.secret) + if token: + sig = sig + escape(token.secret) + return sig, sig + + def build_signature(self, oauth_request, consumer, token): + key, raw = self.build_signature_base_string(oauth_request, consumer, + token) + return key
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/7digital-python/lib/oauth7digital.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,96 @@ +import httplib +import oauth +from lockerEndpoint import Locker + +class Oauth7digital(object): + key = None + + SERVER = 'api.7digital.com' + REQUEST_TOKEN_URL = 'https://%s/1.2/oauth/requesttoken' % SERVER + ACCESS_TOKEN_URL = 'https://%s/1.2/oauth/accesstoken' % SERVER + LOCKER_ENDPOINT_URL = 'http://%s/1.2/user/locker' %SERVER + + def __init__(self, key, secret, access_token = None): + self.key = key + self.secret = secret + self.access_token = access_token + + def request_token(self): + print '\nOAUTH STEP 1' + oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.__consumer(), http_url = self.REQUEST_TOKEN_URL, parameters={}) + print '\nMESSAGE:: %s' %oauth_request + oauth_request.sign_request(self.__signature_method(), self.__consumer(), None) + resp = self.__fetch_response(oauth_request, self.__secure_connection()) + + token = oauth.OAuthToken.from_string(resp) + return token + + def authorize_request_token(self, token): + AUTHORIZATION_URL = 'https://account.7digital.com/%s/oauth/authorise' % self.key + print '\nOAUTH STEP 2' + auth_url="%s?oauth_token=%s" % (AUTHORIZATION_URL, token.key) + + # auth url to go to + print 'Authorization URL:\n%s' % auth_url + oauth_verifier = raw_input('Please go to the above URL and authorize the app. Hit return when you have been authorized: ') + return True + + def request_access_token(self, token): + print '\nOAUTH STEP 3' + oauth_request = self.__sign_oauth_request(token, self.ACCESS_TOKEN_URL) + resp = self.__fetch_response(oauth_request, self.__secure_connection()) + + token = oauth.OAuthToken.from_string(resp) + return token + + def get_user_locker(self): + resp = self.__get_locker() + return Locker(resp).get_content() + + def get_artist_from_user_locker(self): + resp = self.__get_locker() + return Locker(resp).get_artists() + + def get_releases_from_user_locker(self): + resp = self.__get_locker() + return Locker(resp).get_releases() + + def get_tracks_from_user_locker(self): + resp = self.__get_locker() + return Locker(resp).get_tracks() + + def get_locker(self): + resp = self.__get_locker() + return Locker(resp).get_contents() + + def __get_locker(self): + oauth_request = self.__sign_oauth_request(self.access_token, self.LOCKER_ENDPOINT_URL) + resp = self.__fetch_response(oauth_request, self.__connection()) + return resp + + def __sign_oauth_request(self, token, url_end_point): + oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.__consumer(), token=token, http_url = url_end_point, parameters={}) + + oauth_request.sign_request(self.__signature_method(), self.__consumer(), token) + return oauth_request + + def __consumer(self): + return oauth.OAuthConsumer(self.key, self.secret) + + def __signature_method(self): + return oauth.OAuthSignatureMethod_HMAC_SHA1() + + def __secure_connection(self): + return httplib.HTTPSConnection(self.SERVER) + + def __connection(self): + return httplib.HTTPConnection(self.SERVER) + + def __fetch_response(self, oauth_request, connection): + url = oauth_request.to_url() + connection.request(oauth_request.http_method, url) + response = connection.getresponse() + result = response.read() + + return result +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/7digital-python/lib/py7digital.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,925 @@ +#!/usr/bin/env python +"""A python interface to 7Digital web service (non-premium services)""" +import os +import urllib2, urllib +import re +import urlparse +from xml.dom import minidom +try: + from hashlib import md5 +except ImportError: + from md5 import md5 + +__name__ = 'py7digital' +__doc__ = 'A python interface to 7Digital web service' +__author__ = 'Oscar Celma, Pau Capella' +__version__ = '0.0.1' +__license__ = 'GPL' +__maintainer__ = 'Oscar Celma' +__email__ = 'ocelma@bmat.com' +__status__ = 'Beta' + +API_VERSION = '1.2' +HOST_NAME = 'api.7digital.com/' + API_VERSION +OAUTHKEY = '' #TODO Put your oauth key here +COUNTRY = '' # ISO Country + +__cache_dir = './cache' # Set cache directory +__cache_enabled = False # Enable cache? if set to True, make sure that __cache_dir exists! (e.g. $ mkdir ./cache) + +class ServiceException(Exception): + """Exception related to the web service.""" + + def __init__(self, type, message): + self._type = type + self._message = message + + def __str__(self): + return self._type + ': ' + self._message + + def get_message(self): + return self._message + + def get_type(self): + return self._type + +class _Request(object): + """Representing an abstract web service operation.""" + + def __init__(self, method_name, params): + self.params = params + self.method = method_name + + def _download_response(self): + """Returns a response""" + data = [] + for name in self.params.keys(): + data.append('='.join((name, urllib.quote_plus(self.params[name].replace('&', '&').encode('utf8'))))) + data = '&'.join(data) + + url = HOST_NAME + parsed_url = urlparse.urlparse(url) + if not parsed_url.scheme: + url = "http://" + url + url += self.method + '?oauth_consumer_key=' + OAUTHKEY + '&' + if COUNTRY: + url += 'country=' + COUNTRY + '&' + url += data + #print url + + request = urllib2.Request(url) + response = urllib2.urlopen(request) + return response.read() + + def execute(self, cacheable=False): + try: + if is_caching_enabled() and cacheable: + response = self._get_cached_response() + else: + response = self._download_response() + return minidom.parseString(response) + except urllib2.HTTPError, e: + raise self._get_error(e.fp.read()) + + def _get_cache_key(self): + """Cache key""" + keys = self.params.keys()[:] + keys.sort() + string = self.method + for name in keys: + string += name + string += self.params[name] + return get_md5(string) + + def _is_cached(self): + """Returns True if the request is available in the cache.""" + return os.path.exists(os.path.join(_get_cache_dir(), self._get_cache_key())) + + def _get_cached_response(self): + """Returns a file object of the cached response.""" + if not self._is_cached(): + response = self._download_response() + response_file = open(os.path.join(_get_cache_dir(), self._get_cache_key()), "w") + response_file.write(response) + response_file.close() + return open(os.path.join(_get_cache_dir(), self._get_cache_key()), "r").read() + + def _get_error(self, text): + return ServiceException('Error', text) + raise + + +class _BaseObject(object): + """An abstract webservices object.""" + + def __init__(self, method): + self._method = method + self._xml = None + + def _request(self, method_name , cacheable = False, params = None): + if not params: + params = self._get_params() + return _Request(method_name, params).execute(cacheable) + + def _get_params(self): + return dict() + + +class Artist(_BaseObject): + """ A 7digital artist """ + + def __init__(self, id): + _BaseObject.__init__(self, '/artist/') + + self.name = None + self.id = id + self._url = None + self._image = None + self._albums = None + self._top_tracks = None + self._tags = None + self._recommended_albums = None + + def __repr__(self): + return self.get_name().encode('utf8') + + def __eq__(self, other): + return self.get_id() == other.get_id() + + def __ne__(self, other): + return self.get_id() != other.get_id() + + def get_id(self): + """ Returns the 7digital artist id """ + return self.id + + def get_name(self): + """ Returns the name of the artist """ + if self.name is None: + self.name = '' + try: + if not self._xml : self._xml = self._request(self._method, + 'details', True, {'artistid': self.id}) + self.name = _extract(self._xml, 'artist', 1) or '' + except: + return self.name + return self.name + + def set_name(self, name) : + self.name = name + + def get_image(self): + """ Returns the image url of an artist """ + if self._image is None : + if not self._xml : self._xml = self._request(self._method + 'details', True, {'artistid': self.id}) + self._image = _extract(self._xml, 'image') + return self._image + + def set_image(self, image) : + self._image = image + + def get_url(self): + """ Returns the url of an artist """ + if self._url is None : + if not self._xml : self._xml = self._request(self._method + 'details', True, {'artistid': self.id}) + self._url = _extract(self._xml, 'url') + return self._url + + def set_url(self, url) : + self._url = url + + def get_tags(self, pageSize=10): + """ Returns the tags of an artist """ + if self._tags is None: + self._tags = [] + xml = self._request(self._method + 'tags', True, {'artistid': self.id}) + for node in xml.getElementsByTagName('tag') : + self._tags.append(_get_tag(node)) + if self._tags: + self._tags.sort() + return self._tags[:pageSize] + + def get_albums(self, pageSize=10): + """ Returns the albums of an artist """ + if self._albums is not None: return self._albums + + results = [] + xml = self._request(self._method + 'releases', True, {'artistid': self.id, 'pageSize': str(pageSize)}) + for node in xml.getElementsByTagName('release'): + album = _get_album(node, self) + results.append(album) + self._albums = results + return self._albums + + def get_recommended_albums(self, pageSize=10): + """ Returns a list of recommended albums based on the seed artist """ + if self._recommended_albums is not None: return self._recommended_albums + + results = [] + xml = self._request('/release/recommend', True, {'artistid': self.id, 'pageSize': str(pageSize)}) # TODO if country is set gives different results + for node in xml.getElementsByTagName('release'): + results.append(_get_album(node, _get_artist(node.getElementsByTagName('artist')[0]))) + self._recommended_albums = results + return self._recommended_albums + + def get_top_tracks(self, pageSize=10): + """ Returns the top tracks of an artist """ + if self._top_tracks is not None: return self._top_tracks + + results = [] + xml = self._request(self._method + 'toptracks', True, {'artistid': self.id, 'pageSize': str(pageSize)}) + for node in xml.getElementsByTagName('track'): + results.append(_get_track(node, None, self)) + self._top_tracks = results + return self._top_tracks + +class Album(_BaseObject): + """ A 7digital album """ + def __init__(self, id=HOST_NAME): + _BaseObject.__init__(self, '/release/') + self.id = id + self.artist = None + self.title = None + + self._url = None + self._type = None + self._barcode = None + self._year = None + self._image = None + self._label = None + self._tags = None + self._tracks = None + self._similar = None + self._release_date = None + self._added_date = None + + def __repr__(self): + if self.get_artist(): + return self.get_artist().get_name().encode('utf8') + ' - ' + self.get_title().encode('utf8') + return self.get_title().encode('utf8') + + def __eq__(self, other): + return self.get_id() == other.get_id() + + def __ne__(self, other): + return self.get_id() != other.get_id() + + def get_id(self): + """ Returns the 7digital album id """ + return self.id + + def get_title(self): + """ Returns the name of the album """ + if self.title is None: + if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) + self.title = _extract(self._xml, 'release', 1) + return self.title + + def set_title(self, title): + if title is None: + title = '' + self.title = title + + def get_type(self): + """ Returns the type (CD, DVD, etc.) of the album """ + if self._type is None: + if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) + self._type = _extract(self._xml, 'type') + return self._type + + def set_type(self, type): + self._type = type + + def get_year(self): + """ Returns the year of the album """ + if self._year is None: + if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) + self._year = _extract(self._xml, 'year') + return self._year + + def set_year(self, year): + self._year = year + + def get_barcode(self): + """ Returns the barcode of the album """ + if self._barcode is None: + if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) + self.barcode = _extract(self._xml, 'barcode') + return self._barcode + + def set_barcode(self, barcode): + self._barcode = barcode + + def get_url(self): + """ Returns the url of the album """ + if self._url is None : + if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) + self._url = _extract(self._xml, 'url') + return self._url + + def set_url(self, url) : + self._url = url + + def get_label(self): + """ Returns the label of the album """ + if self._label is None: + if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) + self.set_label(_get_label(self._xml.getElementsByTagName('label'))) + return self._label + + def set_label(self, label): + self._label = label + + def get_release_date(self): + """ Returns the release date of the album """ + if self._release_date is None : + if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) + self._release_date = _extract(self._xml, 'releaseDate') + return self._release_date + + def set_release_date(self, release_date): + self._release_date = release_date + + def get_added_date(self): + """ Returns the added date of the album """ + if self._added_date is None : + if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) + self._added_date = _extract(self._xml, 'addedDate') + return self._added_date + + def set_added_date(self, added_date): + self._added_date = added_date + + def get_artist(self): + """ Returns the Artist of the album """ + if not self.artist: + self.set_artist(_get_artist(self._xml.getElementsByTagName('artist'))) + return self.artist + + def set_artist(self, artist): + """ Sets the Artist object of the track """ + self.artist = artist + + def get_image(self): + """ Returns album image url """ + if self._image is None: + if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) + self._image = _extract(self._xml, 'release_small_image') + return self._image + + def set_image(self, image): + if image is None: image = '' + self._image = image + + def get_tags(self, pageSize=10): + """ Returns the tags of the album """ + if self._tags is None: + self._tags = [] + xml = self._request(self._method + 'tags', True, {'releaseid': self.id}) + for node in xml.getElementsByTagName('tag') : + self._tags.append(_get_tag(node)) + if self._tags: + self._tags.sort() + return self._tags[:pageSize] + + def get_tracks(self, pageSize=10): + """ Returns the tracks of the album """ + if self._tracks is not None: return self._tracks + + results = [] + xml = self._request(self._method + 'tracks', True, {'releaseid': self.id, 'pageSize': str(pageSize)}) + for node in xml.getElementsByTagName('track'): + if self.artist is None: + self.set_artist(_get_artist(node.getElementsByTagName('artist'))) + track = _get_track(node, self, self.get_artist()) + results.append(track) + self._tracks = results + return self._tracks + + def get_similar(self, pageSize=10): + """ Returns a list similar albums """ + if self._similar is not None: return self._similar + + results = [] + xml = self._request(self._method + 'recommend', True, {'releaseid': self.id, 'pageSize': str(pageSize)}) + for node in xml.getElementsByTagName('release'): + album = _get_album(node, _get_artist(node.getElementsByTagName('artist')[0])) + if self == album: + continue #Same album! + results.append(album) + self._similar = results + return self._similar + + +class Track(_BaseObject): + """ A Bmat track. """ + def __init__(self, id, artist=None): + _BaseObject.__init__(self, '/track/') + + if isinstance(artist, Artist): + self.artist = artist + else: + self.artist = None + self.id = id + self.title = None + self.artist = None + self.album = None + + self._isrc = None + self._url = None + self._preview = 'http://api.7digital.com/1.2/track/preview?trackid=' + self.id + self._image = None + self._tags = None + self._duration = None + self._version = None + self._explicit = None + self._position = None + + def __repr__(self): + return self.get_artist().get_name().encode('utf8') + ' - ' + self.get_title().encode('utf8') + + def __eq__(self, other): + return self.get_id() == other.get_id() + + def __ne__(self, other): + return self.get_id() != other.get_id() + + def get_id(self): + """ Returns the track id """ + return self.id + + def get_title(self): + """ Returns the track title """ + if self.title is None: + if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id}) + self.title = _extract(self._xml, 'track', 1) + return self.title + + def set_title(self, title): + self.title = title + + def get_isrc(self): + """ Returns the ISRC of the track """ + if self._isrc is None: + if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id}) + self._isrc = _extract(self._xml, 'isrc') + return self._isrc + + def set_isrc(self, isrc): + self._isrc = isrc + + def get_url(self): + """ Returns the url of the track """ + if self._url is None : + if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id}) + self._url = _extract(self._xml, 'url') + return self._url + + def set_url(self, url): + self._url = url + + def get_audio(self): + return self.get_preview() + + def get_preview(self): + """ Returns the url of the track """ + if self._preview is None : + if not self._xml : self._xml = self._request(self._method + 'preview', True, {'trackid': self.id}) + self._preview = _extract(self._xml, 'url') + return self._preview + + def get_duration(self): + """ Returns the duration of the track """ + if self._duration is None : + if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id}) + self._duration = _extract(self._xml, 'duration') + return self._duration + + def set_duration(self, duration): + self._duration = duration + + def get_position(self): + """ Returns the track number in the release """ + if self._position is None : + if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id}) + self._position = _extract(self._xml, 'trackNumber') + return self._position + + def set_position(self, track_number): + self._position = track_number + + def get_explicit(self): + """ Returns whether the track contains explicit content """ + if self._explicit is None : + if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id}) + self._explicit = _extract(self._xml, 'explicitContent') + return self._explicit + + def set_explicit(self, explicit): + self._explicit = explicit + + def get_version(self): + """ Returns the version of the track """ + if self._version is None : + if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id}) + self._version = _extract(self._xml, 'version') + return self._version + + def set_version(self, version): + self._version = version + + def get_artist(self): + """ Returns the Artist of the track """ + if not self.artist: + self.set_artist(_get_artist(self._xml.getElementsByTagName('artist'))) + return self.artist + + def set_artist(self, artist): + """ Sets the Artist object of the track """ + self.artist = artist + + def get_album(self): + """ Returns the associated Album object """ + if not self.album: + self.set_album(_get_album(self._xml.getElementsByTagName('release'))) + return self.album + + def set_album(self, album): + """ Sets the Album object of the track """ + self.album = album + + +class Tag(_BaseObject): + """ A Tag """ + def __init__(self, id): + _BaseObject.__init__(self, '/tag/') + + self.id = id + self.name = None + self._url = None + + def __repr__(self): + return self.get_name().encode('utf8') + + def __eq__(self, other): + return self.get_id() == other.get_id() + + def __ne__(self, other): + return self.get_id() != other.get_id() + + def get_id(self): + """ Returns the tag id """ + return self.id + + def get_name(self): + """ Returns the tag name """ + if self.name is None: + if not self._xml : self._xml = self._request(self._method + 'details', True, {'tagid': self.id}) + self.name = _extract(self._xml, 'name') + return self.name + + def set_name(self, name): + self.name = name + + def get_url(self): + """ Returns the url of the tag""" + if self._url is None: + if not self._xml : self._xml = self._request(self._method + 'details', True, {'tagid': self.id}) + self._url = _extract(self._xml, 'url') + return self._url + + def set_url(self, url): + self._url = url + +class Label(_BaseObject): + """ A Label """ + def __init__(self, id): + _BaseObject.__init__(self, '') + + self.id = id + self.name = None + + def __repr__(self): + return self.get_name().encode('utf8') + + def __eq__(self, other): + return self.get_id() == other.get_id() + + def __ne__(self, other): + return self.get_id() != other.get_id() + + def get_id(self): + """ Returns the label id """ + return self.id + + def get_name(self): + """ Returns the label name """ + return self.name + + def set_name(self, name): + self.name = name + + +class _Search(_BaseObject): + """ An abstract class for search """ + + def __init__(self, method, search_terms, xml_tag): + _BaseObject.__init__(self, method) + + self._search_terms = search_terms + self._xml_tag = xml_tag + + self._results_per_page = 10 + if self._search_terms.has_key('pageSize'): + self._results_per_page = str(self._search_terms['pageSize']) + else: + self._search_terms['pageSize'] = str(self._results_per_page) + self._last_page_index = 0 + self._hits = None + + def get_total_result_count(self): + if self._hits is not None: + return self._hits + params = self._get_params() + params['pageSize'] = '1' + xml = self._request(self._method, True, params) + hits = int(_extract(xml, 'totalItems')) + self._hits = hits + return self._hits + + def _get_params(self): + params = {} + for key in self._search_terms.keys(): + params[key] = self._search_terms[key] + return params + + def _retrieve_page(self, page_index): + """ Returns the xml nodes to process """ + params = self._get_params() + + if page_index != 0: + #offset = self._results_per_page * page_index + params["page"] = str(page_index) + + doc = self._request(self._method, True, params) + return doc.getElementsByTagName(self._xml_tag)[0] + + def _retrieve_next_page(self): + self._last_page_index += 1 + return self._retrieve_page(self._last_page_index) + + def has_results(self): + return self.get_total_result_count() > (self._results_per_page * self._last_page_index) + + def get_next_page(self): + master_node = self._retrieve_next_page() + return self._get_results(master_node) + + def get_page(self, page=0): + if page < 0: page = 0 + if page > 0: page = page-1 + + master_node = self._retrieve_page(page) + return self._get_results(master_node) + + +class ArtistSearch(_Search): + """ Search artists """ + + def __init__(self, query): + _Search.__init__(self, '/artist/search', {'q': query}, 'searchResults') + + def _get_results(self, master_node): + results = [] + for node in master_node.getElementsByTagName('artist'): + artist = _get_artist(node) + results.append(artist) + return results + + +class ArtistBrowse(_Search): + """ Browse artists """ + + def __init__(self, letter): + _Search.__init__(self, '/artist/browse', {'letter': letter}, 'artists') + + def _get_results(self, master_node): + results = [] + for node in master_node.getElementsByTagName('artist'): + artist = _get_artist(node) + results.append(artist) + return results + +class ArtistDetail(_Search): + def __init__(self, artist_id): + _Search.__init__(self, '/artist/details', {'artistid': artist_id}, 'artist') + + def _get_results(self, master_node): + results = [] + node = master_node + + artist = _get_artist(node) + + results.append(artist) + return results + +class AlbumSearch(_Search): + """ Search albums """ + + def __init__(self, query): + _Search.__init__(self, '/release/search', {'q': query}, 'searchResults') + + def _get_results(self, master_node): + results = [] + for node in master_node.getElementsByTagName('release'): + artist = _get_artist(node.getElementsByTagName('artist')[0]) + album = _get_album(node, artist) + results.append(album) + return results + +class AlbumCharts(_Search): + """ Chart albums """ + + def __init__(self, period, todate): + _Search.__init__(self, '/release/chart', {'period': period, 'todate': todate}, 'chart') + + def _get_results(self, master_node): + results = [] + for node in master_node.getElementsByTagName('release'): + artist = _get_artist(node.getElementsByTagName('artist')[0]) + album = _get_album(node, artist) + results.append(album) + return results + +class AlbumReleases(_Search): + """ Release albums by date """ + + def __init__(self, fromdate, todate): + _Search.__init__(self, '/release/bydate', {'fromDate': fromdate, 'toDate': todate}, 'releases') + + def _get_results(self, master_node): + results = [] + for node in master_node.getElementsByTagName('release'): + artist = _get_artist(node.getElementsByTagName('artist')[0]) + album = _get_album(node, artist) + results.append(album) + return results + +class TrackSearch(_Search): + """ Search for tracks """ + + def __init__(self, query): + _Search.__init__(self, '/track/search', {'q': query}, 'searchResults') + + def _get_results(self, master_node): + results = [] + for node in master_node.getElementsByTagName('track'): + artist = _get_artist(node.getElementsByTagName('artist')[0]) + album = _get_album(node.getElementsByTagName('release')[0], artist) + track = _get_track(node, album, artist) + results.append(track) + return results + +def get_artist_detail(artistId): + return ArtistDetail(artistId) + +def search_artist(query): + """Search artists by query. Returns an ArtistSearch object. + Use get_next_page() to retrieve sequences of results.""" + return ArtistSearch(query) + +def browse_artists(letter): + """Browse artists by letter [a..z]. Returns an ArtistBrowse object. + Use get_next_page() to retrieve sequences of results.""" + return ArtistBrowse(letter) + +def search_album(query): + """Search albums by query. Returns the albumSearch object. + Use get_next_page() to retrieve sequences of results.""" + return AlbumSearch(query) + +def album_charts(period, todate): + """Get chart albums in a given period of time """ + return AlbumCharts(period, todate) + +def album_releases(fromdate, todate): + """Get releases in a given period of time""" + return AlbumReleases(fromdate, todate) + +def search_track(query): + """Search tracks by query. Returns a TrackSearch object. + Use get_next_page() to retrieve sequences of results.""" + return TrackSearch(query) + + +# XML +def _extract(node, name, index = 0): + """Extracts a value from the xml string""" + try: + nodes = node.getElementsByTagName(name) + + if len(nodes): + if nodes[index].firstChild: + return nodes[index].firstChild.data.strip() + else: + return None + except: + return None + +def _extract_all(node, name, pageSize_count = None): + """Extracts all the values from the xml string. It returns a list.""" + results = [] + for i in range(0, len(node.getElementsByTagName(name))): + if len(results) == pageSize_count: + break + results.append(_extract(node, name, i)) + return results + +def _get_artist(xml): + artist_id = xml.getAttribute('id') + artist = Artist(artist_id) + artist.set_name(_extract(xml, 'name')) + return artist + +def _get_album(xml, artist): + album_id = xml.getAttribute('id') + album = Album(album_id) + album.set_artist(artist) + album.set_title(_extract(xml, 'title')) + album.set_type(_extract(xml, 'type')) + album.set_image(_extract(xml, 'image')) + album.set_year(_extract(xml, 'year')) + album.set_barcode(_extract(xml, 'barcode')) + album.set_url(_extract(xml, 'url')) + album.set_release_date(_extract(xml, 'releaseDate')) + album.set_added_date(_extract(xml, 'addedDate')) + #TODO price, formats, artist appears_as + try : + album.set_label(_get_label(xml.getElementsByTagName('label')[0])) #In some cases that are albums with no label (record company) attached! :-( + except: + pass + return album + +def _get_track(xml, album, artist): + track_id = xml.getAttribute('id') + track = Track(track_id) + track.set_title(_extract(xml, 'title')) + track.set_url(_extract(xml, 'url')) + track.set_isrc(_extract(xml, 'isrc')) + track.set_duration(_extract(xml, 'duration')) + track.set_position(_extract(xml, 'trackNumber')) + track.set_explicit(_extract(xml, 'explicitContent')) + track.set_version(_extract(xml, 'version')) + track.set_album(album) + track.set_artist(artist) + #TODO price, formats + return track + +def _get_tag(xml): + tag = Tag(_extract(xml, 'text')) + tag.set_name(tag.get_id()) + return tag + +def _get_label(xml): + label = '' + try: + label = Label(xml.getAttribute('id')) + label.set_name(_extract(xml, 'name')) + except: + pass + return label + +# CACHE +def enable_caching(cache_dir = None): + global __cache_dir + global __cache_enabled + + if cache_dir == None: + import tempfile + __cache_dir = tempfile.mkdtemp() + else: + if not os.path.exists(cache_dir): + os.mkdir(cache_dir) + __cache_dir = cache_dir + __cache_enabled = True + +def disable_caching(): + global __cache_enabled + __cache_enabled = False + +def is_caching_enabled(): + """Returns True if caching is enabled.""" + global __cache_enabled + return __cache_enabled + +def _get_cache_dir(): + """Returns the directory in which cache files are saved.""" + global __cache_dir + global __cache_enabled + return __cache_dir + +def get_md5(text): + """Returns the md5 hash of a string.""" + hash = md5() + hash.update(text.encode('utf8')) + return hash.hexdigest() +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/7digital-python/lockerEndpoint.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,118 @@ +#!/usr/bin/env python +"""A python interface to 7digital's locker endpoint""" +import os +from xml.dom.minidom import parseString + + +class Locker(object): + def __init__(self, xml_response): + self.xml_response = parseString(xml_response) + + def get_contents(self): + results = [] + locker_items = self.xml_response.getElementsByTagName('lockerRelease') + + for item in locker_items: + results.append(LockerItem(item)) + + return results + + def get_artists(self): + results = [] + artist_nodes = self.xml_response.getElementsByTagName('artist') + + for artist in artist_nodes: + results.append(LockerArtist(artist)) + + return results + + def get_releases(self): + results = [] + release_nodes = self.xml_response.getElementsByTagName('release') + for release in release_nodes: + results.append(LockerRelease(release)) + + return results + + def get_tracks(self): + results = [] + track_nodes = self.xml_response.getElementsByTagName('lockerTrack') + for track in track_nodes: + results.append(LockerTrack(track)) + + return results + +class _LockerBase(object): + def extract(self, node, name, index = 0): + """Extracts a value from the xml string""" + try: + nodes = node.getElementsByTagName(name) + + if len(nodes): + if nodes[index].firstChild: + return nodes[index].firstChild.data.strip() + else: + return None + except: + return None + +class LockerItem(_LockerBase): + def __init__(self, xml): + self.release = LockerRelease(xml.getElementsByTagName('release')[0]) + self.tracks = self.__get_release_tracks(xml.getElementsByTagName('lockerTrack')) + + def __get_release_tracks(self, tracks): + result = [] + for track in tracks: + result.append(LockerTrack(track)) + return result + +class LockerTrack(_LockerBase): + def __init__(self, xml): + self.track = Track(xml.getElementsByTagName('track')[0]) + self.remaining_downloads = self.extract(xml, 'remainingDownloads') + self.purchaseDate = self.extract(xml, 'purchaseDate') + self.download_urls = self.__get_download_urls(xml.getElementsByTagName('downloadUrls')) + + def __get_download_urls(self, urls): + result = [] + for url in urls: + result.append(DownloadUrls(url)) + return result + +class DownloadUrls(_LockerBase): + def __init__(self, xml): + self.url = self.extract(xml, 'url') + self.format = Format(xml.getElementsByTagName('format')[0]) + +class Format(_LockerBase): + def __init__(self, xml): + self.id = xml.getAttribute('id') + self.file_format = self.extract(xml, 'fileFormat') + self.bit_rate = self.extract(xml, 'bitRate') + +class Track(_LockerBase): + def __init__(self, xml): + self.id = xml.getAttribute('id') + self.title = self.extract(xml, 'title') + self.version = self.extract(xml, 'version') + self.artist = LockerArtist(xml.getElementsByTagName('artist')[0]) + self.url = self.extract(xml, 'url') + +class LockerRelease(_LockerBase): + def __init__(self, xml): + self.id = xml.getAttribute('id') + self.title = self.extract(xml, 'title') + self.type = self.extract(xml, 'type') + self.artist = LockerArtist(xml.getElementsByTagName('artist')[0]) + self.url = self.extract(xml, 'url') + self.image = self.extract(xml, 'image') + self.release_date = self.extract(xml, 'releaseDate') + +class LockerArtist(_LockerBase): + def __init__(self, xml): + self.id = xml.getAttribute('id') + self.name = self.extract(xml, 'name') + self.url = self.extract(xml, 'url') + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/7digital-python/tests/artist_tests.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,11 @@ +import py7digital + +def test_get_artist_detail_returns_keane(): + artists = py7digital.get_artist_detail('1') + for artist in artists.get_next_page(): + assert artist.get_name().lower() == "keane" + +def test_artist_search_returns_names_containing_stone(): + artists = py7digital.search_artist("stones") + for i in artists.get_next_page(): + assert i.get_name().lower().find("stones") > -1 \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/7digital-python/tests/search_acceptance_tests.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,62 @@ +import py7digital + +#Search artist +results = py7digital.search_artist('stones') +print results.get_total_result_count() +for artist in results.get_next_page(): + print artist.get_name() #, artist.get_image(), artist.get_url(), artist.get_tags() + print '\tTop tracks:' + for top_track in artist.get_top_tracks(): + print '\t\t', top_track.get_title(), top_track.get_isrc(), top_track.get_duration(), top_track.get_position(), top_track.get_explicit(), top_track.get_version() + print '\tRec. Albums:' + for rec_album in artist.get_recommended_albums(): + print '\t\t', rec_album, rec_album.get_year() #, album.get_barcode(), album.get_type(), album.get_artist(), album.get_tags(), album.get_label() + for album in artist.get_albums(5): + print '\t', album, album.get_year(), album.get_barcode(), album.get_type(), album.get_artist(), album.get_tags(), album.get_label(), album.get_release_date(), album.get_added_date() + for sim_album in album.get_similar(): + print '\t\tSimilar:', sim_album, sim_album.get_year(), sim_album.get_artist() + for track in album.get_tracks(): + print '\t\t', track, track.get_isrc() #, track.get_url(), track.get_audio() + +#Browse artists starting with 'J' +results = py7digital.browse_artists('j') +print results.get_total_result_count() +for artist in results.get_next_page(): + print artist.get_name() #, artist.get_image(), artist.get_url(), artist.get_tags() + for album in artist.get_albums(2): + print '\t', album, album.get_year() #album.get_barcode(), album.get_type(), album.get_artist(), album.get_tags(), album.get_label() + for track in album.get_tracks(): + print '\t\t', track.get_title(), track.get_isrc() #, track.get_url(), track.get_audio() + +#Search albums +searcher = py7digital.search_album('u2') +print searcher.get_total_result_count() +while searcher.has_results(): + for album in searcher.get_next_page(): + print album, album.get_similar() + +#Search tracks +searcher = py7digital.search_track('u2 one') +print searcher.get_total_result_count() +while searcher.has_results(): + for track in searcher.get_next_page(): + print track + +# New releases in a given period of time +results = py7digital.album_releases('20100901', '20100924') +for album in results.get_next_page(): + print album, album.get_year(), album.get_barcode(), album.get_type(), album.get_artist(), album.get_tags(), album.get_label(), album.get_release_date(), album.get_added_date() + for sim_album in album.get_similar(): + print '\tSimilar:', sim_album, sim_album.get_year(), sim_album.get_artist() + for track in album.get_tracks(): + print '\t', track, track.get_isrc() #, track.get_url(), track.get_audio() + +# Album charts in a given period of time +results = py7digital.album_charts('month', '20100901') +for album in results.get_next_page(): + print album, album.get_year(), album.get_barcode(), album.get_type(), album.get_artist(), album.get_tags(), album.get_label(), album.get_release_date(), album.get_added_date() + for sim_album in album.get_similar(): + print '\tSimilar:', sim_album, sim_album.get_year(), sim_album.get_artist() + for track in album.get_tracks(): + print '\t', track, track.get_isrc() #, track.get_url(), track.get_audio() +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/lastfm_tags.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,96 @@ + +import os +import sys +import pickle + +from xml.dom import minidom +import urllib2 #call url function +import time +import sqlite3 +import cPickle + +import pyechonest # import echonest API +import pylast # import last.fm API +from pyechonest import artist, catalog, config, playlist # How it works instead of pyechonest + + +#change the path to 7-digital python library +sys.path.append("./7digital-python/lib/") +import py7digital + +""" API Key from echonest """ +#Your API Key: SFXNKMTRAZ3ULHK6U +#Your Consumer Key: 54a06c9bd235d47f787d8cf614577a94 +#Your Shared Secret: aiPUaTToTpixW4Ttaf4O9A + +"""API key from 7 digital""" +# oauth_consumer_key=7dbpa63h3y3d +# oauth_consumer_secret=zz48d4epsqmrsuvp + +"""API key from last.fm""" +# Your API Key is bd1cb09de31188b43aa46f39b8e40614 +# Your secret is d2537b0ce8bc859a6068833c5e2a72a7 + +""""*********************************************************************************""" + +# Echo Nest API key +config.ECHO_NEST_API_KEY="SFXNKMTRAZ3ULHK6U " + +# 7 digital API key +DIGITAL7_API_KEY = '7dbpa63h3y3d' + + +"""The use of last.fm API""" +API_KEY = "bd1cb09de31188b43aa46f39b8e40614" +API_SECRET = "d2537b0ce8bc859a6068833c5e2a72a7" + +"""Authentication, username and password""" +username = "QMYading" +password_hash = pylast.md5("123456") +network = pylast.LastFMNetwork(api_key = API_KEY, api_secret = + API_SECRET, username = username, password_hash = password_hash) + +# get artist object +# artist_object = network.get_artist("System of a Down") + +# get the mbid for the tracks +# print(artist_object.get_mbid()) + +# print(os.getcwd()) #print current working directory +# artist_object.shout("Artist object created successful!") + +# get track object + +track_object = network.get_track("Adele","Someone like you") + +print track_object.get_tag() + + +""" +# track_object.add_tags(("awesome", "favorite")) # add tags to the tracks + +#get get_album object +#album_object = network.get_album("Adele","21") + +# get tags object +#os.chdir('./Resuls/') +tag_classification = ["Happy"] + + +# search for similar tag according to the seeds in tag_classification +for tags_name in tag_classification: + + tags = network.search_for_tag(tags_name) + print tags + tags_info = tags.get_next_page() + + #print tags_info + #print tags_search + #tag_file_name = tags_name + "_tags_results.txt" + + #tag_data = open(tag_file_name, 'wb') # open and write the file for top tags + + #os.makedirs(str(tags_name)) + #os.chdir('./' + str(tags_name) + '/') + +"""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/.gitignore Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,6 @@ +*.pyc +*.pyo +*.egg-info +build +*.DS_Store +dist
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/CHANGES.rst Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,18 @@ +Changes +======= + +0.9 + * Added support for XML response messages. + * Deprecated **musixmatch_apiversion** environment variable in favour of + **musixmatch_wslocation**. This will let developers to setup different api + location and a testing environment. (musicae-ipsum under construction) + * **apikey** and **format** in api.Method are now lookedup as follow: + + 1. Positional arguments. + 2. Package wide variables. + 3. Operating system environment. + + Keyword arguments are not considered any more. +0.8 + * API interface with JSON response support. + * Low level objects library built around JSON response messages.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/README.rst Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,74 @@ +Quick start +=========== + +1. First thing first, read the documentation at http://developer.musixmatch.com . +2. Get an api key by signing up at http://developer.musixmatch.com/mmplans . +3. Install the musixmatch package +4. Run the python prompt + +>>> import musixmatch +>>> apikey = '<your-apikey>' +>>> try: +... chart = musixmatch.ws.track.chart.get(country='it', apikey=apikey) +... except musixmatch.api.Error, e: +... pass + +It's that simple. Last, you can brows this documentation and have fun with the other modules. + +Building / Installing +===================== + +You can just use setup.py to build and install python-musixmatch:: + + prompt $ python setup.py bdist_egg + +Once built, you can use easy_install on the python egg. + +Documentation +============= +You can read documentation online_, or generate your own local copy using +`Sphinx`_ trough the setup.py:: + + prompt $ python setup.py build_sphinx + +.. _Sphinx: http://sphinx.pocoo.org +.. _online: http://projects.monkeython.com/musixmatch/python-musixmatch/html/index.html + +Unit testing +============ +python-musixmatch comes with some essential unit testing. If you set up +**musixmatch_apikey** environment variable, and have internet connection, you +can also run some tests on API calls:: + + prompt $ python setup.py test + +Caching support +=============== + +Applications using python-musixmatch may take advantage of standard +urllib support for **http_proxy**, so they can just set up the proper +environment variable: + +http_proxy + the complete HTTP proxy URL to use in queries. + +Considering all the available HTTP proxy solutions, I'm reluctant to implement +a further caching support. Though i can consider serialization support. + +Environment variables +===================== + +python-musixmatch takes advantage of operating system environment to get +**apikey**, **format** and api **version** values to use in API calls: + +musixmatch_apikey + the apikey value to use in query strings +musixmatch_format + the response message format. For example: json +musixmatch_wslocation + the webservice base url. For example: http://api.musixmatch.com/ws/1.1 +musixmatch_apiversion + the api version to use in queryes. For example: 1.1. Use of + **musixmatch_apiversion** was deprecated in favour of + **musixmatch_wslocation**. +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/build/lib/musixmatch/__init__.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,44 @@ +import os +__author__ = "Luca De Vitis <luca@monkeython.com>" +__version__ = '0.9' +__copyright__ = "2011, %s " % __author__ +__license__ = """ + Copyright (C) %s + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +""" % __copyright__ +__doc__ = """ +:abstract: Python interface to Musixmatch API +:version: %s +:author: %s +:organization: Monkeython +:contact: http://www.monkeython.com +:copyright: %s +""" % (__version__, __author__, __license__) +__docformat__ = 'restructuredtext en' +__classifiers__ = [ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: GNU General Public License (GPL)', + 'Operating System :: OS Independent', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Software Development :: Libraries', +] +__all__ = [ + 'ws', 'api', 'base', + 'artist', 'track', 'lyrics', 'subtitle', 'album' +] + +apikey = os.environ.get('musixmatch_apikey', None) +format = os.environ.get('musixmatch_format', 'json')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/build/lib/musixmatch/album.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,62 @@ +""" +This module contains higher level classes to query Musixmatch API and build +simple dictionary-like objects representing an Album or an AlbumsCollection. + +>>> from musixmatch.album import Album, AlbumsCollection +>>> import musixmatch.api +>>> +>>> try: +... album = Album(album_id=292) +... collection = AlbumsCollection.fromArtist(country='it', page=1) +... except musixmatch.api.Error, e: +... pass +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch.base import Item, ItemsCollection +from musixmatch.ws import album, artist + +class Album(Item): + """ + This class build a :py:class:`dict` like object representing an album. It + can get album information through the :py:class:`musixmatch.api.Method` + **album.get** or from an already well-formed :py:class:`dict`. Create an + Album object based on a given keyword argument: + + :param album_id: musiXmatch album ID + :param album_data: an already well-formed :py:class:`dict` of album data. + + Once information are collected, the following keys are available: + + :keyword album_id: musiXmatch album ID + :keyword album_name: album name + :keyword album_release_date: album release date + :keyword album_release_type: type of the album + :keyword album_coverart_100x100: coverart URL + :keyword artist_id: album artist musiXmatch ID + :keyword artist_name: album artist name + """ + __api_method__ = album.get + +class AlbumsCollection(ItemsCollection): + """ + This class build a :py:class:`list` like object representing an albums + collection. It accepts :py:class:`dict` or :py:class:`Album` objects. + """ + __allowedin__ = Album + + @classmethod + def fromArtist(cls, **keywords): + """ + This classmethod builds an :py:class:`AlbumsCollection` from a + **artist.albums.get** :py:class:`musixmatch.api.Method` call. + + :param artist_id: album artist musiXmatch ID + :param g_album_name: group albums by name + :param s_release_date: sort albums by release date + :rtype: :py:class:`AlbumsCollection` + """ + return cls.fromResponseMessage(artist.albums.get(**keywords)) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/build/lib/musixmatch/api.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,389 @@ +""" This module define the base API classes. +""" +import musixmatch +from urllib import urlencode, urlopen +from contextlib import contextmanager +import os +try: + import json +except ImportError: + import simplejson as json +try: + from lxml import etree +except ImportError: + try: + import xml.etree.cElementTree as etree + except ImportError: + try: + import xml.etree.ElementTree as etree + except ImportError: + try: + import cElementTree as etree + except ImportError: + import elementtree.ElementTree as etree + +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +class Error(Exception): + """Base musiXmatch API error. + + >>> import musixmatch + >>> raise musixmatch.api.Error('Error message') + Traceback (most recent call last): + ... + Error: Error message + """ + def __str__(self): + return ': '.join(map(str, self.args)) + + def __repr__(self): + name = self.__class__.__name__ + return '%s%r' % (name, self.args) + +class ResponseMessageError(Error): + """Represents errors occurred while parsing the response messages.""" + +class ResponseStatusCode(int): + """ + Represents response message status code. Casting a + :py:class:`ResponseStatusCode` to :py:class:`str` returns the message + associated with the status code: + + >>> from musixmatch.api import ResponseStatusCode + >>> str(ResponseStatusCode(200)) + 'The request was successful.' + >>> str(ResponseStatusCode(401)) + 'Authentication failed, probably because of a bad API key.' + + The status code to description mapping is: + + +------+-----------------------------------------------------------+ + | Code | Description | + +======+===========================================================+ + | 200 | The request was successful. | + +------+-----------------------------------------------------------+ + | 400 | The request had bad syntax or was inherently | + | | impossible to be satisfied. | + +------+-----------------------------------------------------------+ + | 401 | Authentication failed, probably because of a bad API key. | + +------+-----------------------------------------------------------+ + | 402 | A limit was reached, either you exceeded per hour | + | | requests limits or your balance is insufficient. | + +------+-----------------------------------------------------------+ + | 403 | You are not authorized to perform this operation or | + | | the api version you're trying to use has been shut down. | + +------+-----------------------------------------------------------+ + | 404 | Requested resource was not found. | + +------+-----------------------------------------------------------+ + | 405 | Requested method was not found. | + +------+-----------------------------------------------------------+ + + Any other status code will produce a default message: + + >>> from musixmatch.api import ResponseStatusCode + >>> str(ResponseStatusCode(666)) + 'Unknown status code 666!' + + Casting a :py:class:`ResponseStatusCode` to :py:class:`bool` returns True if + status code is 200, False otherwise: + + >>> from musixmatch.api import ResponseStatusCode + >>> bool(ResponseStatusCode(200)) + True + >>> bool(ResponseStatusCode(400)) + False + >>> bool(ResponseStatusCode(666)) + False + + """ + __status__ = { + 200: "The request was successful.", + 400: "The request had bad syntax or was inherently " + \ + "impossible to be satisfied.", + 401: "Authentication failed, probably because of a bad API key.", + 402: "A limit was reached, either you exceeded per hour " + \ + "requests limits or your balance is insufficient.", + 403: "You are not authorized to perform this operation or " + \ + "the api version you're trying to use has been shut down.", + 404: "Requested resource was not found.", + 405: "Requested method was not found.", + } + + def __str__(self): + return self.__status__.get(self, 'Unknown status code %i!' % self) + + def __repr__(self): + return 'ResponseStatusCode(%i)' % self + + def __nonzero__(self): + return self == 200 + +class ResponseMessage(dict): + """ + Abstract class which provides a base class for formatted response. + """ + def __init__(self, response): + raise NotImplementedError + + @property + def status_code(self): + """ + Is the :py:class:`ResponseStatusCode` object representing the + message status code. + + :raises: :py:exc:`ValueError` if not set. + """ + raise NotImplementedError + + def __repr__(self): + return "%s('...')" % type(self).__name__ + +class JsonResponseMessage(ResponseMessage, dict): + """ + A :py:class:`ResponseMessage` subclass which behaves like a + :py:class:`dict` to expose the Json structure contained in the response + message. Parses the Json response message and build a proper python + :py:class:`dict` containing all the information. Also, setup a + :py:class:`ResponseStatusCode` by querying the :py:class:`dict` for the + *['header']['status_code']* item. + """ + def __init__(self, response): + try: + parsed = json.load(response) + except Exception, e: + raise ResponseMessageError(u'Invalid Json response message', e) + self.update(parsed['message']) + + def __str__(self): + s = json.dumps({ 'message': self }, sort_keys=True, indent=4) + return '\n'.join([l.rstrip() for l in s.splitlines()]) + + @property + def status_code(self): + """Overload :py:meth:`ResponseMessage.status_code`""" + return ResponseStatusCode(self['header']['status_code']) + +class XMLResponseMessage(ResponseMessage, etree.ElementTree): + """ + A :py:class:`ResponseMessage` subclass which exposes + :py:class:`ElementTree` methods to handle XML response + messages. Parses the XML response message and build a + :py:class:`ElementTree` instance. Also setup the a + :py:class:`ResponseStatusCode` by querying the *status_code* tag content. + + Casting a :py:class:`XMLResponseMessage` returns (actually re-builds) a + pretty printed string representing the XML API response message. + """ + + def __init__(self, response): + etree.ElementTree.__init__(self, None, response) + + def __str__(self): + s = StringIO() + self.wite(s) + return s.getvalue() + + @property + def status_code(self): + """Overload :py:meth:`ResponseMessage.status_code`""" + return ResponseStatusCode(self.findtext('header/status_code')) + +class QueryString(dict): + """ + A class representing the keyword arguments to be used in HTTP requests as + query string. Takes a :py:class:`dict` of keywords, and encode values + using utf-8. Also, the query string is sorted by keyword name, so that its + string representation is always the same, thus can be used in hashes. + + Casting a :py:class:`QueryString` to :py:class:`str` returns the urlencoded + query string: + + >>> from musixmatch.api import QueryString + >>> str(QueryString({ 'country': 'it', 'page': 1, 'page_size': 3 })) + 'country=it&page=1&page_size=3' + + Using :py:func:`repr` on :py:class:`QueryString` returns an evaluable + representation of the current instance, excluding apikey value: + + >>> from musixmatch.api import QueryString + >>> repr(QueryString({ 'country': 'it', 'page': 1, 'apikey': 'whatever'})) + "QueryString({'country': 'it', 'page': '1'})" + """ + def __init__(self, items=(), **keywords): + dict.__init__(self, items, **keywords) + for k in self: + self[k] = str(self[k]).encode('utf-8') + + def __str__(self): + return urlencode(self) + + def __repr__(self): + query = self.copy() + if 'apikey' in query: + del query['apikey'] + return 'QueryString(%r)' % query + + def __iter__(self): + """ + Returns an iterator method which will yield keys sorted by name. + Sorting allow the query strings to be used (reasonably) as caching key. + """ + keys = dict.keys(self) + keys.sort() + for key in keys: + yield key + + def values(self): + """Overloads :py:meth:`dict.values` using :py:meth:`__iter__`.""" + return tuple(self[k] for k in self) + + def keys(self): + """Overloads :py:meth:`dict.keys` using :py:meth:`__iter__`.""" + return tuple(k for k in self) + + def items(self): + """Overloads :py:meth:`dict.item` using :py:meth:`__iter__`.""" + return tuple((k, self[k]) for k in self) + + def __hash__(self): + return hash(str(self)) + + def __cmp__(self, other): + return cmp(hash(self), hash(other)) + +class Method(str): + """ + Utility class to build API methods name and call them as functions. + + :py:class:`Method` has custom attribute access to build method names like + those specified in the API. Each attribute access builds a new Method with + a new name. + + Calling a :py:class:`Method` as a function with keyword arguments, + builds a :py:class:`Request`, runs it and returns the result. If **apikey** + is undefined, environment variable **musixmatch_apikey** will be used. If + **format** is undefined, environment variable **musixmatch_format** will be + used. If **musixmatch_format** is undefined, jason format will be used. + + >>> import musixmatch + >>> artist = musixmatch.api.Method('artist') + >>> + >>> try: + ... chart = artist.chart.get(country='it', page=1, page_size=3) + ... except musixmatch.api.Error, e: + ... pass + """ + __separator__ = '.' + + def __getattribute__(self, name): + if name.startswith('_'): + return super(Method, self).__getattribute__(name) + else: + return Method(self.__separator__.join([self, name])) + + def __call__ (self, apikey=None, format=None, **query): + query['apikey'] = apikey or musixmatch.apikey + query['format'] = format or musixmatch.format + return Request(self, query).response + + def __repr__(self): + return "Method('%s')" % self + +class Request(object): + """ + This is the main API class. Given a :py:class:`Method` or a method name, a + :py:class:`QueryString` or a :py:class:`dict`, it can build the API query + URL, run the request and return the response either as a string or as a + :py:class:`ResponseMessage` subclass. Assuming the default web services + location, this class try to build a proper request: + + >>> from musixmatch.api import Request, Method, QueryString + >>> method_name = 'artist.chart.get' + >>> method = Method(method_name) + >>> keywords = { 'country': 'it', 'page': 1, 'page_size': 3 } + >>> query_string = QueryString(keywords) + >>> + >>> r1 = Request(method_name, keywords) + >>> r2 = Request(method_name, **keywords) + >>> r3 = Request(method_name, query_string) + >>> r4 = Request(method, keywords) + >>> r5 = Request(method, **keywords) + >>> r6 = Request(method, query_string) + + If **method** is string, try to cast it into a :py:class:`Method`. If + **query_string** is a :py:class:`dict`, try to cast it into a + :py:class:`QueryString`. If **query_string** is not specified, try to + use **keywords** arguments as a :py:class:`dict` and cast it into a + :py:class:`QueryString`. + + Turning the :py:class:`Request` into a :py:class:`str` returns the URL + representing the API request: + + >>> str(Request('artist.chart.get', { 'country': 'it', 'page': 1 })) + 'http://api.musixmatch.com/ws/1.1/artist.chart.get?country=it&page=1' + """ + def __init__ (self, api_method, query=(), **keywords): + self.__api_method = isinstance(api_method, Method) and \ + api_method or Method(api_method) + self.__query_string = isinstance(query, QueryString) and \ + query or QueryString(query) + self.__query_string.update(keywords) + self.__response = None + + @property + def api_method(self): + """The :py:class:`Method` instance.""" + return self.__api_method + + @property + def query_string(self): + """The :py:class:`QueryString` instance.""" + return self.__query_string + + @contextmanager + def _received(self): + """A context manager to handle url opening""" + try: + response = urlopen(str(self)) + yield response + finally: + response.close() + + @property + def response(self): + """ + The :py:class:`ResponseMessage` based on the **format** key in the + :py:class:`QueryString`. + """ + if self.__response is None: + + format = self.query_string.get('format') + ResponseMessageClass = { + 'json': JsonResponseMessage, + 'xml': XMLResponseMessage, + }.get(format, None) + + if not ResponseMessageClass: + raise ResponseMessageError("Unsupported format `%s'" % format) + + with self._received() as response: + self.__response = ResponseMessageClass(response) + + return self.__response + + def __repr__(self): + return 'Request(%r, %r)' % (self.api_method, self.query_string) + + def __str__(self): + return '%(ws_location)s/%(api_method)s?%(query_string)s' % { + 'ws_location': musixmatch.ws.location, + 'api_method': self.api_method, + 'query_string': self.query_string + } + + def __hash__(self): + return hash(str(self)) + + def __cmp__(self, other): + return cmp(hash(self), hash(other))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/build/lib/musixmatch/artist.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,80 @@ +""" +This module contains higher level classes to query Musixmatch API and build +simple dictionary-like objects representing an Artist or an ArtistsCollection. + +>>> from musixmatch.artist import Artist, ArtistsCollection +>>> import musixmatch.api +>>> +>>> try: +... artist = Artist(artist_id=292) +... collection = ArtistsCollection.fromChart(country='it', page=1) +... except musixmatch.api.Error, e: +... pass +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch.base import Item, ItemsCollection +from musixmatch.ws import artist + +class Artist(Item): + """ + This class build a :py:class:`dict` like object representing an artist. It + can get artist information through the :py:class:`musixmatch.api.Method` + **artist.get** or from an already well-formed :py:class:`dict`. Create an + Artist object based on a given keyword argument: + + :param artist_id: musiXmatch artist ID + :param artist_mbid: Musicbrainz artist ID + :param artist_data: an already well-formed :py:class:`dict` of artist data. + + Once information are collected, the following keys are available: + + :keyword artist_id: musiXmatch artist ID + :keyword artist_mbid: Musicbrainz artist ID + :keyword artist_name: Artist name + """ + __api_method__ = artist.get + +class ArtistsCollection(ItemsCollection): + """ + This class build a :py:class:`list` like object representing an artists + collection. It accepts :py:class:`dict` or :py:class:`Artist` objects. + """ + __allowedin__ = Artist + + @classmethod + def fromSearch(cls, **keywords): + """ + This classmethod builds an :py:class:`ArtistsCollection` from a + **artist.search** :py:class:`musixmatch.api.Method` call. + + :param q: a string that will be searched in every data field + (q_track, q_artist, q_lyrics) + :param q_track: words to be searched among track titles + :param q_artist: words to be searched among artist names + :param q_lyrics: words to be searched into the lyrics + :param page: requested page of results + :param page_size: desired number of items per result page + :param f_has_lyrics: exclude tracks without an available lyrics + (automatic if q_lyrics is set) + :param f_artist_id: filter the results by the artist_id + :param f_artist_mbid: filter the results by the artist_mbid + :rtype: :py:class:`ArtistsCollection` + """ + return cls.fromResponseMessage(artist.search(**keywords)) + + @classmethod + def fromChart(cls, **keywords): + """ + This classmethod builds an :py:class:`ArtistsCollection` from a + **artist.chart.get** :py:class:`musixmatch.api.Method` call. + + :param page: requested page of results + :param page_size: desired number of items per result page + :param country: the country code of the desired country chart + :rtype: :py:class:`ArtistsCollection` + """ + return cls.fromResponseMessage(artist.chart.get(**keywords)) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/build/lib/musixmatch/base.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,240 @@ +""" +This module contains tha base classes for the Musixmatch API generated content: + +* :py:class:`musixmatch.artist.Artist` +* :py:class:`musixmatch.artist.Album` +* :py:class:`musixmatch.track.Track` +* :py:class:`musixmatch.lyrics.Lyrics` +* :py:class:`musixmatch.subtitle.Subtitle` +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch import api +import pprint + +class Base(object): + """ + The very base (abstract) class of the musixmatch package. I want all + classes to implement :py:meth:`__str__` and :py:meth:`__repr__`. + + Casting an :py:class:`Base` into a :py:class:`str` returns a pretty printed + (maybe using :py:mod:`pprint`) string representing the object. + + Using :py:func:`repr` on an :py:class:`Base` returns an evaluable string + representing the instance, whenever it is reasonable. + + :py:class:`Base` instances are hashable. + """ + + @classmethod + def label(cls): + """ + Returns the label that should be used as keyword in the + :py:class:`musixmatch.api.JsonResponseMessage` body. + """ + return getattr(cls, '__label__', cls.__name__.lower()) + + @classmethod + def apiMethod(cls): + """ + Returns the :py:class:`musixmatch.api.Method` that should be used to + build the object. Defaults to *label.get* where *label* is the result + from :py:meth:`label` + """ + api_method = getattr(cls, '__api_method__', '%s.%s' % (cls.label(),'get')) + if not isinstance(api_method, api.Method): + api_method = api.Method(str(api_method)) + return api_method + + def __str__(self): + raise NotImplementedError + + def __repr__(self): + raise NotImplementedError + +class Item(Base, dict): + """ + This is the base class for any entity in musixmatch package. Even if + response messages may have XML format, the JSON representation will be the + main data format, so the :py:class:`dict` sounds like the best base class. + It fetches the item data by guessing the :py:class:`musixmatch.api.Method` + and building the query based on a given keyword argument. Positional + argument is meant to be used by collection classes. Use only keyword + arguments. + """ + __api_method__ = None + + def __init__(self, dictionary=None, **keywords): + if dictionary: + dict.update(self, dictionary) + elif keywords: + message = self.apiMethod()(**keywords) + dict.update(self, self.fromResponseMessage(message)) + + def __str__(self): + return pprint.pformat(dict(self),4,1) + + def __repr__(self): + return '%s(%r)' % (type(self).__name__, dict(self)) + + def __hash__(self): + return int(self['%s_id' % self.label()]) + + @classmethod + def fromResponseMessage(cls, message): + """ + Returns an object instance, built from a + :py:class:`musixmatch.api.ResponseMessage` + """ + if not message.status_code: + raise api.Error(str(message.status_code)) + return cls.fromDictionary(message['body'][cls.label()]) + + @classmethod + def fromDictionary(cls, dictionary, **keywords): + """ + Returns an object instance, built from a :py:class:`dict` + """ + item = cls() + dict.update(item, dictionary, **keywords) + return item + +class ItemsCollection(Base, list): + """ + This is the base class for collections of items, like search results, or + charts. It behaves like :py:class:`list`, but enforce new items to be + instance of appropriate class checking against :py:meth:`allowedin`. + """ + + __allowedin__ = Item + + def __init__(self, *items): + self.extend(items) + + def __repr__(self): + items = [ repr(i) for i in self ] + return '%s(%s)' % (type(self).__name__, ', '.join(items)) + + def __str__(self): + return list.__str__(self) + + def __add__(self, iterable): + collection = self.copy() + collection.extend(iterable) + return collection + + def __iadd__(self, iterable): + raise NotImplementedError + + def __mul__(self, by): + raise NotImplementedError + + def __imul__(self, by): + raise NotImplementedError + + def __setitem__(self, key, item): + raise NotImplementedError + + def __setslice__(self, *indices): + raise NotImplementedError + + def __getslice__(self, i=0, j=-1): + return self.__getitem__(slice(i,j)) + + def __getitem__(self, i): + if type(i) is int: + return list.__getitem__(self, i) + elif type(i) is slice: + collection = type(self)() + list.extend(collection, list.__getitem__(self, i)) + return collection + else: + raise TypeError, i + + def append(self, item): + self.insert(len(self), item) + + def extend(self, iterable): + for item in iterable: + self.append(item) + + def count(self, item): + return int(item in self) + + def copy(self): + """Returns a shallow copy of the collection.""" + collection = type(self)() + list.extend(collection, self) + return collection + + def index(self, item, *indices): + return list.index(self, item, *indices[:2]) + + def insert(self, key, item): + allowed = self.allowedin() + if not isinstance(item, allowed): + item = allowed.fromDictionary(item) + if not item in self: + list.insert(self, key, item) + + def paged(self, page_size=3): + """ + Returns self, paged by **page_size**. That is, a list of + sub-collections which contain "at most" **page_size** items. + """ + return [ self.page(i,page_size) + for i in range(self.pages(page_size)) ] + + def page(self, page_index, page_size=3): + """ + Returns a specific page, considering pages that contain "at most" + **page_size** items. + """ + page = type(self)() + i = page_index * page_size + list.extend(page, self[i:i+page_size]) + return page + + def pager(self, page_size=3): + """ + A generator of pages, considering pages that contain "at most" + **page_size** items. + """ + for i in xrange(self.pages(page_size)): + yield self.page(i, page_size) + + def pages(self,page_size=3): + """ + Returns the number of pages, considering pages that contain "at most" + **page_size** items. + """ + pages, more = divmod(len(self), page_size) + return more and pages + 1 or pages + + @classmethod + def fromResponseMessage(cls, message): + """ + Returns an object instance, built on a + :py:class:`musixmatch.api.ResponseMessage` + """ + if not message.status_code: + raise api.Error(str(message.status_code)) + list_label = cls.label() + item_label = cls.allowedin().label() + items = [ i[item_label] for i in message['body'][list_label] ] + return cls(*items) + + @classmethod + def allowedin(cls): + """ + Returns the allowed content class. Defaults to :py:class:`Item` + """ + return cls.__allowedin__ + + @classmethod + def label(cls): + item_name = cls.allowedin().label() + return '%s_list' % item_name +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/build/lib/musixmatch/lyrics.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,43 @@ +""" +This module contains higher level classes to query Musixmatch API and build +simple dictionary-like objects representing a track lyrics. + +>>> from musixmatch.lyrics import Lyrics +>>> import musixmatch.api +>>> +>>> try: +... lyrics = Lyrics(lyrics_id=292) +... except musixmatch.api.Error, e: +... pass +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch.base import Item +from musixmatch.ws import track + +class Lyrics(Item): + """ + This class builds a :py:class:`dict` object representing a the lyrics of a + track. It can get lyrics through the :py:class:`musixmatch.api.Method` + **track.lyrics.get** or from an already well-formed :py:class:`dict`. + Create a Track object based on a given keyword argument: + + :param track_id: musiXmatch track ID + :param musicbrainz_id: Musicbrainz track ID + :param track_echonest_id: Echonest track ID + :param lyrics_data: an already well-formed :py:class:`dict` of track data + :raises: :py:class:`musixmatch.api.Error` if :py:class:`musixmatch.api.ResponseStatusCode` is not 200 + + Once information are collected, the following keys are available: + + :keyword lyrics_body: the lyrics text + :keyword lyrics_id: the Musixmatch lyrics id + :keyword lyrics_language: the lyrics language + :keyword lyrics_copyright: the lyrics copyright statement + :keyword pixel_tracking_url: the pixel tracking url + :keyword script_tracking_url: the script tracking url + """ + __api_method__ = track.lyrics.get +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/build/lib/musixmatch/subtitle.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,40 @@ +""" +This module contains higher level classes to query Musixmatch API and build +simple dictionary-like objects representing a track subtitle. + +>>> from musixmatch.subtitle import Subtitle +>>> import musixmatch.api +>>> +>>> try: +... subtitle = Subtitle(subtitle_id=292) +... except musixmatch.api.Error, e: +... pass +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch.base import Item +from musixmatch.ws import track + +class Subtitle(Item): + """ + This class builds a :py:class:`dict` object representing a subtitle of a + track. It can get subtitle through the :py:class:`musixmatch.api.Method` + **track.subtitle.get** or from an already well-formed :py:class:`dict`. + Create a Track object based on a given keyword argument: + + :param track_id: musiXmatch track ID + :param musicbrainz_id: Musicbrainz track ID + :param track_echonest_id: Echonest track ID + :param subtitle_data: an already well-formed :py:class:`dict` of track data + :raises: :py:class:`musixmatch.api.Error` if :py:class:`musixmatch.api.StatusCode` is not 200 + + Once information are collected, the following keys are available: + + :keyword subtitle_body: the subtitle text + :keyword subtitle_id: the Musixmatch subtitle id + :keyword subtitle_language: the subtitle language + """ + __api_method__ = track.subtitle.get +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/build/lib/musixmatch/track.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,171 @@ +""" +This module contains higher level classes to query Musixmatch API and build +simple dictionary-like objects representing a Track or a TracksCollection. + +>>> from musixmatch.track import Track, TracksCollection +>>> import musixmatch.api +>>> +>>> try: +... track = Track(track_mbid=8976) +... collection = TracksCollection.fromChart(country='us', page=1) +... except musixmatch.api.Error, e: +... pass +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch import api, lyrics, subtitle +from musixmatch.base import Item, ItemsCollection +from musixmatch.ws import track, matcher, album + +_marker=object() + +class Track(Item): + """ + This class builds a :py:class:`dict` like object representing a track. It + can get track information through the :py:class:`musixmatch.api.Method` + **track.get** or from an already well-formed :py:class:`dict`. Create a + Track object based on a given keyword argument: + + :param track_id: musiXmatch track ID + :param musicbrainz_id: Musicbrainz track ID + :param track_echonest_id: Echonest track ID + :param track_data: an already well-formed :py:class:`dict` of track data + :raises: :py:exc:`musixmatch.api.Error` if :py:class:`musixmatch.api.ResponseStatusCode` is not 200 + + Once information are collected, the following keys are available: + + :keyword track_id: musiXmatch track ID + :keyword track_mbid: Musicbrainz track ID + :keyword lyrics_id: musiXmatch lyrics ID + :keyword instrumental: wether the track is instrumental or not + :keyword subtitle_id: musiXmatch subtitle ID + :keyword track_name: track name + :keyword album_coverart_100x100: album cover URL + :keyword artist_id: musiXmatch artist ID + :keyword artist_mbid: Musicbrainz artist ID + :keyword artist_name: artist name + + Keyword access have been overloaded thanks to the :py:meth:`get` method + which will eventually fetch the matching lyrics or subtitle. + """ + __api_method__ = track.get + + @classmethod + def fromMatcher(cls, **keywords): + """ + Returns a :py:class:`Track` based on the result of the + :py:class:`musiXmatch.api.Method` **matcher.track.get**. Accepts the + following keywords: + + :param q_track: words to be searched among track titles + :param q_artist: words to be searched among artist names + """ + return cls.fromResponseMessage(matcher.track.get(**keywords)) + + def get(self, key, default=_marker): + """ + If key is *lyrics* or *subtitle* try to query api for proper value, + and build an :py:class:`musixmatch.lyrics.Lyrics` or + :py:class:`musixmatch.subtitle.Subtitle`. Access to the above mentioned + keys may fail with :py:exc:`musixmatch.api.Error`. Once fetched, the + result is saved. + """ + special = { + 'lyrics': lyrics.Lyrics, + 'subtitle': subtitle.Subtitle, + } + if key in special and not key in self: + self[key] = special[key](track_id=self['track_id']) + value = dict.get(self, key, default) + if value == _marker: + raise KeyError, key + return value + + def __getitem__(self, key): + return self.get(key) + + def postFeedback(self, feedback): + """ + Post feedback about lyrics for this track. **feedback** can be one of: + + :keyword wrong_attribution: the lyrics shown are not by the artist that + I selected. + :keyword bad_characters: there are strange characters and/or words + that are partially scrambled. + :keyword lines_too_long: the text for each verse is too long! + :keyword wrong_verses: there are some verses missing from the + beginning or at the end. + :keyword wrong_formatting: the text looks horrible, please fix it! + """ + accepted = [ + 'wrong_attribution', 'bad_characters', 'lines_too_long', + 'wrong_verses', 'wrong_formatting' ] + if feedback in accepted: + message = track.lyrics.feedback.post( + track_id=self['track_id'], + lyrics_id=self['track_id']['lyrics']['lyrics_id'], + feedback=feedback + ) + if not message.status_code: + raise api.Error(str(message.status_code)) + else: + raise TypeError, '%r not in %r' % (feedback, accepted) + +class TracksCollection(ItemsCollection): + """ + This class build a :py:class:`list` like object representing a tracks + collection. It accepts :py:class:`dict` or :py:class:`Track` objects. + """ + __allowedin__ = Track + + @classmethod + def fromAlbum(cls, **keywords): + """ + This classmethod builds an :py:class:`TracksCollection` from a + **album.tracks.get** :py:class:`musixmatch.api.Method` call. + + :param album_id: musiXmatch album ID + """ + return cls.fromResponseMessage(album.tracks.get(**keywords)) + + @classmethod + def fromSearch(cls, **keywords): + """ + This classmethod builds an :py:class:`TracksCollection` from a + **track.search** :py:class:`musixmatch.api.Method` call. + + :param q: a string that will be searched in every data field + (q_track, q_artist, q_lyrics) + :param q_track: words to be searched among track titles + :param q_artist: words to be searched among artist names + :param q_track_artist: words to be searched among track titles or + artist names + :param q_lyrics: words to be searched into the lyrics + :param page: requested page of results + :param page_size: desired number of items per result page + :param f_has_lyrics: exclude tracks without an available lyrics + (automatic if q_lyrics is set) + :param f_artist_id: filter the results by the artist_id + :param f_artist_mbid: filter the results by the artist_mbid + :param quorum_factor: only works together with q and q_track_artist + parameter. Possible values goes from 0.1 to + 0.9. A value of 0.9 means: 'match at least 90 + percent of the words'. + """ + return cls.fromResponseMessage(track.search(**keywords)) + + @classmethod + def fromChart(cls, **keywords): + """ + This classmethod builds an :py:class:`TracksCollection` from a + **track.chart.get** :py:class:`musixmatch.api.Method` call. + + :param page: requested page of results + :param page_size: desired number of items per result page + :param country: the country code of the desired country chart + :param f_has_lyrics: exclude tracks without an available lyrics + (automatic if q_lyrics is set) + """ + return cls.fromResponseMessage(track.chart.get(**keywords))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/build/lib/musixmatch/ws.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,30 @@ +""" +This is an utility module that provides a row musiXmatch web API interface. +Ideally it should be used like this: + +>>> import musixmatch +>>> +>>> try: +... chart = musixmatch.ws.track.chart.get(country='it', f_has_lyrics=1) +... except musixmatch.api.Error, e: +... pass +""" +from warnings import warn +import os +import musixmatch.api +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +_version = os.environ.get('musixmatch_apiversion', None) +if not _version: + _version = '1.1' +else: + warn("Use of `musixmatch_apiversion' was deprecated in favour of `musixmatch_wslocation'", DeprecationWarning) + +location = os.environ.get('musixmatch_wslocation', 'http://api.musixmatch.com/ws/%s' % _version) + +artist = musixmatch.api.Method('artist') +album = musixmatch.api.Method('album') +track = musixmatch.api.Method('track') +tracking = musixmatch.api.Method('tracking') +matcher = musixmatch.api.Method('matcher')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/docs/album.rst Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,12 @@ +============ +album module +============ + +.. automodule:: musixmatch.album + + .. autoclass:: Album + :show-inheritance: + + .. autoclass:: AlbumsCollection + :show-inheritance: + :members: fromArtist
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/docs/api.rst Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,31 @@ +========== +api module +========== + +.. automodule:: musixmatch.api + :undoc-members: + + .. autoexception:: Error + :show-inheritance: + + .. autoexception:: ResponseMessageError + :show-inheritance: + + .. autoclass:: ResponseStatusCode + :show-inheritance: + + .. autoclass:: ResponseMessage + :members: status_code + + .. autoclass:: JsonResponseMessage + + .. autoclass:: QueryString + :members: items + + .. autoclass:: Method + :undoc-members: + + .. autoclass:: Request + :undoc-members: + :members: query_string, api_method, response +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/docs/artist.rst Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,12 @@ +============= +artist module +============= + +.. automodule:: musixmatch.artist + + .. autoclass:: Artist + :show-inheritance: + + .. autoclass:: ArtistsCollection + :show-inheritance: + :members: fromSearch, fromChart
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/docs/base.rst Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,16 @@ +=========== +base module +=========== + +.. automodule:: musixmatch.base + + .. autoclass:: Base + :show-inheritance: + :members: label, apiMethod + + .. autoclass:: Item + :show-inheritance: + + .. autoclass:: ItemsCollection + :show-inheritance: + :members: copy, page, pages, pager, paged, fromResponseMessage, allowedin
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/docs/conf.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,224 @@ +# -*- coding: utf-8 -*- +# +# Python musiXmatch documentation build configuration file, created by +# sphinx-quickstart on Mon Mar 28 22:28:39 2011. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath(os.path.dirname(__file__))) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +# extensions = [ +# 'sphinx.ext.autodoc', +# 'sphinx.ext.doctest', +# 'sphinx.ext.todo', +# 'sphinx.ext.coverage'] +extensions = [ + 'sphinx.ext.doctest', + 'sphinx.ext.coverage', + 'sphinx.ext.autodoc'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Python musiXmatch' +copyright = u'2011, Luca De Vitis' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.6' +# The full version, including alpha/beta/rc tags. +release = '0.6.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +add_module_names = False + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +show_authors = True + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# "<project> v<release> documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +html_domain_indices = True + +# If false, no index is generated. +html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a <link> tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'PythonmusiXmatchdoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'PythonmusiXmatch.tex', u'Python musiXmatch Documentation', + u'Luca De Vitis', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'pythonmusixmatch', u'Python musiXmatch Documentation', + [u'Luca De Vitis'], 1) +]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/docs/index.rst Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,36 @@ +.. Python musiXmatch documentation master file, created by + sphinx-quickstart on Mon Mar 28 22:28:39 2011. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Package overview +================ + +.. automodule:: musixmatch.__init__ + +.. include:: ../README.rst + +.. include:: ../CHANGES.rst + +Package API +=========== + +.. toctree:: + :numbered: + + api + ws + base + artist + track + album + lyrics + subtitle + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/docs/lyrics.rst Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,8 @@ +============= +lyrics module +============= + +.. automodule:: musixmatch.lyrics + + .. autoclass:: Lyrics + :show-inheritance:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/docs/subtitle.rst Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,8 @@ +=============== +subtitle module +=============== + +.. automodule:: musixmatch.subtitle + + .. autoclass:: Subtitle + :show-inheritance:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/docs/track.rst Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,13 @@ +============= +track module +============= + +.. automodule:: musixmatch.track + + .. autoclass:: Track + :show-inheritance: + :members: fromMatcher, get, postFeedback + + .. autoclass:: TracksCollection + :show-inheritance: + :members: fromAlbum, fromSearch, fromChart
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/docs/ws.rst Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,5 @@ +========= +WS module +========= + +.. automodule:: musixmatch.ws
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/musixmatch.egg-info/PKG-INFO Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,91 @@ +Metadata-Version: 1.0 +Name: musixmatch +Version: 0.9 +Summary: Package to interface with the Musixmatch API +Home-page: ('http://projects.monkeython.com/musixmatch',) +Author: Luca De Vitis +Author-email: luca@monkeython.com +License: UNKNOWN +Download-URL: http://projects.monkeython.com/musixmatch/dists +Description: Quick start + =========== + + 1. First thing first, read the documentation at http://developer.musixmatch.com . + 2. Get an api key by signing up at http://developer.musixmatch.com/mmplans . + 3. Install the musixmatch package + 4. Run the python prompt + + >>> import musixmatch + >>> apikey = '<your-apikey>' + >>> try: + ... chart = musixmatch.ws.track.chart.get(country='it', apikey=apikey) + ... except musixmatch.api.Error, e: + ... pass + + It's that simple. Last, you can brows this documentation and have fun with the other modules. + + Building / Installing + ===================== + + You can just use setup.py to build and install python-musixmatch:: + + prompt $ python setup.py bdist_egg + + Once built, you can use easy_install on the python egg. + + Documentation + ============= + You can read documentation online_, or generate your own local copy using + `Sphinx`_ trough the setup.py:: + + prompt $ python setup.py build_sphinx + + .. _Sphinx: http://sphinx.pocoo.org + .. _online: http://projects.monkeython.com/musixmatch/python-musixmatch/html/index.html + + Unit testing + ============ + python-musixmatch comes with some essential unit testing. If you set up + **musixmatch_apikey** environment variable, and have internet connection, you + can also run some tests on API calls:: + + prompt $ python setup.py test + + Caching support + =============== + + Applications using python-musixmatch may take advantage of standard + urllib support for **http_proxy**, so they can just set up the proper + environment variable: + + http_proxy + the complete HTTP proxy URL to use in queries. + + Considering all the available HTTP proxy solutions, I'm reluctant to implement + a further caching support. Though i can consider serialization support. + + Environment variables + ===================== + + python-musixmatch takes advantage of operating system environment to get + **apikey**, **format** and api **version** values to use in API calls: + + musixmatch_apikey + the apikey value to use in query strings + musixmatch_format + the response message format. For example: json + musixmatch_wslocation + the webservice base url. For example: http://api.musixmatch.com/ws/1.1 + musixmatch_apiversion + the api version to use in queryes. For example: 1.1. Use of + **musixmatch_apiversion** was deprecated in favour of + **musixmatch_wslocation**. + + +Platform: UNKNOWN +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: GNU General Public License (GPL) +Classifier: Operating System :: OS Independent +Classifier: Topic :: Internet :: WWW/HTTP +Classifier: Topic :: Software Development :: Libraries
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/musixmatch.egg-info/SOURCES.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,15 @@ +setup.cfg +setup.py +musixmatch/__init__.py +musixmatch/album.py +musixmatch/api.py +musixmatch/artist.py +musixmatch/base.py +musixmatch/lyrics.py +musixmatch/subtitle.py +musixmatch/track.py +musixmatch/ws.py +musixmatch.egg-info/PKG-INFO +musixmatch.egg-info/SOURCES.txt +musixmatch.egg-info/dependency_links.txt +musixmatch.egg-info/top_level.txt \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/musixmatch.egg-info/dependency_links.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,1 @@ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/musixmatch.egg-info/top_level.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,1 @@ +musixmatch
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/musixmatch/__init__.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,44 @@ +import os +__author__ = "Luca De Vitis <luca@monkeython.com>" +__version__ = '0.9' +__copyright__ = "2011, %s " % __author__ +__license__ = """ + Copyright (C) %s + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +""" % __copyright__ +__doc__ = """ +:abstract: Python interface to Musixmatch API +:version: %s +:author: %s +:organization: Monkeython +:contact: http://www.monkeython.com +:copyright: %s +""" % (__version__, __author__, __license__) +__docformat__ = 'restructuredtext en' +__classifiers__ = [ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: GNU General Public License (GPL)', + 'Operating System :: OS Independent', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Software Development :: Libraries', +] +__all__ = [ + 'ws', 'api', 'base', + 'artist', 'track', 'lyrics', 'subtitle', 'album' +] + +apikey = os.environ.get('musixmatch_apikey', None) +format = os.environ.get('musixmatch_format', 'json')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/musixmatch/album.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,62 @@ +""" +This module contains higher level classes to query Musixmatch API and build +simple dictionary-like objects representing an Album or an AlbumsCollection. + +>>> from musixmatch.album import Album, AlbumsCollection +>>> import musixmatch.api +>>> +>>> try: +... album = Album(album_id=292) +... collection = AlbumsCollection.fromArtist(country='it', page=1) +... except musixmatch.api.Error, e: +... pass +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch.base import Item, ItemsCollection +from musixmatch.ws import album, artist + +class Album(Item): + """ + This class build a :py:class:`dict` like object representing an album. It + can get album information through the :py:class:`musixmatch.api.Method` + **album.get** or from an already well-formed :py:class:`dict`. Create an + Album object based on a given keyword argument: + + :param album_id: musiXmatch album ID + :param album_data: an already well-formed :py:class:`dict` of album data. + + Once information are collected, the following keys are available: + + :keyword album_id: musiXmatch album ID + :keyword album_name: album name + :keyword album_release_date: album release date + :keyword album_release_type: type of the album + :keyword album_coverart_100x100: coverart URL + :keyword artist_id: album artist musiXmatch ID + :keyword artist_name: album artist name + """ + __api_method__ = album.get + +class AlbumsCollection(ItemsCollection): + """ + This class build a :py:class:`list` like object representing an albums + collection. It accepts :py:class:`dict` or :py:class:`Album` objects. + """ + __allowedin__ = Album + + @classmethod + def fromArtist(cls, **keywords): + """ + This classmethod builds an :py:class:`AlbumsCollection` from a + **artist.albums.get** :py:class:`musixmatch.api.Method` call. + + :param artist_id: album artist musiXmatch ID + :param g_album_name: group albums by name + :param s_release_date: sort albums by release date + :rtype: :py:class:`AlbumsCollection` + """ + return cls.fromResponseMessage(artist.albums.get(**keywords)) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/musixmatch/api.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,389 @@ +""" This module define the base API classes. +""" +import musixmatch +from urllib import urlencode, urlopen +from contextlib import contextmanager +import os +try: + import json +except ImportError: + import simplejson as json +try: + from lxml import etree +except ImportError: + try: + import xml.etree.cElementTree as etree + except ImportError: + try: + import xml.etree.ElementTree as etree + except ImportError: + try: + import cElementTree as etree + except ImportError: + import elementtree.ElementTree as etree + +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +class Error(Exception): + """Base musiXmatch API error. + + >>> import musixmatch + >>> raise musixmatch.api.Error('Error message') + Traceback (most recent call last): + ... + Error: Error message + """ + def __str__(self): + return ': '.join(map(str, self.args)) + + def __repr__(self): + name = self.__class__.__name__ + return '%s%r' % (name, self.args) + +class ResponseMessageError(Error): + """Represents errors occurred while parsing the response messages.""" + +class ResponseStatusCode(int): + """ + Represents response message status code. Casting a + :py:class:`ResponseStatusCode` to :py:class:`str` returns the message + associated with the status code: + + >>> from musixmatch.api import ResponseStatusCode + >>> str(ResponseStatusCode(200)) + 'The request was successful.' + >>> str(ResponseStatusCode(401)) + 'Authentication failed, probably because of a bad API key.' + + The status code to description mapping is: + + +------+-----------------------------------------------------------+ + | Code | Description | + +======+===========================================================+ + | 200 | The request was successful. | + +------+-----------------------------------------------------------+ + | 400 | The request had bad syntax or was inherently | + | | impossible to be satisfied. | + +------+-----------------------------------------------------------+ + | 401 | Authentication failed, probably because of a bad API key. | + +------+-----------------------------------------------------------+ + | 402 | A limit was reached, either you exceeded per hour | + | | requests limits or your balance is insufficient. | + +------+-----------------------------------------------------------+ + | 403 | You are not authorized to perform this operation or | + | | the api version you're trying to use has been shut down. | + +------+-----------------------------------------------------------+ + | 404 | Requested resource was not found. | + +------+-----------------------------------------------------------+ + | 405 | Requested method was not found. | + +------+-----------------------------------------------------------+ + + Any other status code will produce a default message: + + >>> from musixmatch.api import ResponseStatusCode + >>> str(ResponseStatusCode(666)) + 'Unknown status code 666!' + + Casting a :py:class:`ResponseStatusCode` to :py:class:`bool` returns True if + status code is 200, False otherwise: + + >>> from musixmatch.api import ResponseStatusCode + >>> bool(ResponseStatusCode(200)) + True + >>> bool(ResponseStatusCode(400)) + False + >>> bool(ResponseStatusCode(666)) + False + + """ + __status__ = { + 200: "The request was successful.", + 400: "The request had bad syntax or was inherently " + \ + "impossible to be satisfied.", + 401: "Authentication failed, probably because of a bad API key.", + 402: "A limit was reached, either you exceeded per hour " + \ + "requests limits or your balance is insufficient.", + 403: "You are not authorized to perform this operation or " + \ + "the api version you're trying to use has been shut down.", + 404: "Requested resource was not found.", + 405: "Requested method was not found.", + } + + def __str__(self): + return self.__status__.get(self, 'Unknown status code %i!' % self) + + def __repr__(self): + return 'ResponseStatusCode(%i)' % self + + def __nonzero__(self): + return self == 200 + +class ResponseMessage(dict): + """ + Abstract class which provides a base class for formatted response. + """ + def __init__(self, response): + raise NotImplementedError + + @property + def status_code(self): + """ + Is the :py:class:`ResponseStatusCode` object representing the + message status code. + + :raises: :py:exc:`ValueError` if not set. + """ + raise NotImplementedError + + def __repr__(self): + return "%s('...')" % type(self).__name__ + +class JsonResponseMessage(ResponseMessage, dict): + """ + A :py:class:`ResponseMessage` subclass which behaves like a + :py:class:`dict` to expose the Json structure contained in the response + message. Parses the Json response message and build a proper python + :py:class:`dict` containing all the information. Also, setup a + :py:class:`ResponseStatusCode` by querying the :py:class:`dict` for the + *['header']['status_code']* item. + """ + def __init__(self, response): + try: + parsed = json.load(response) + except Exception, e: + raise ResponseMessageError(u'Invalid Json response message', e) + self.update(parsed['message']) + + def __str__(self): + s = json.dumps({ 'message': self }, sort_keys=True, indent=4) + return '\n'.join([l.rstrip() for l in s.splitlines()]) + + @property + def status_code(self): + """Overload :py:meth:`ResponseMessage.status_code`""" + return ResponseStatusCode(self['header']['status_code']) + +class XMLResponseMessage(ResponseMessage, etree.ElementTree): + """ + A :py:class:`ResponseMessage` subclass which exposes + :py:class:`ElementTree` methods to handle XML response + messages. Parses the XML response message and build a + :py:class:`ElementTree` instance. Also setup the a + :py:class:`ResponseStatusCode` by querying the *status_code* tag content. + + Casting a :py:class:`XMLResponseMessage` returns (actually re-builds) a + pretty printed string representing the XML API response message. + """ + + def __init__(self, response): + etree.ElementTree.__init__(self, None, response) + + def __str__(self): + s = StringIO() + self.wite(s) + return s.getvalue() + + @property + def status_code(self): + """Overload :py:meth:`ResponseMessage.status_code`""" + return ResponseStatusCode(self.findtext('header/status_code')) + +class QueryString(dict): + """ + A class representing the keyword arguments to be used in HTTP requests as + query string. Takes a :py:class:`dict` of keywords, and encode values + using utf-8. Also, the query string is sorted by keyword name, so that its + string representation is always the same, thus can be used in hashes. + + Casting a :py:class:`QueryString` to :py:class:`str` returns the urlencoded + query string: + + >>> from musixmatch.api import QueryString + >>> str(QueryString({ 'country': 'it', 'page': 1, 'page_size': 3 })) + 'country=it&page=1&page_size=3' + + Using :py:func:`repr` on :py:class:`QueryString` returns an evaluable + representation of the current instance, excluding apikey value: + + >>> from musixmatch.api import QueryString + >>> repr(QueryString({ 'country': 'it', 'page': 1, 'apikey': 'whatever'})) + "QueryString({'country': 'it', 'page': '1'})" + """ + def __init__(self, items=(), **keywords): + dict.__init__(self, items, **keywords) + for k in self: + self[k] = str(self[k]).encode('utf-8') + + def __str__(self): + return urlencode(self) + + def __repr__(self): + query = self.copy() + if 'apikey' in query: + del query['apikey'] + return 'QueryString(%r)' % query + + def __iter__(self): + """ + Returns an iterator method which will yield keys sorted by name. + Sorting allow the query strings to be used (reasonably) as caching key. + """ + keys = dict.keys(self) + keys.sort() + for key in keys: + yield key + + def values(self): + """Overloads :py:meth:`dict.values` using :py:meth:`__iter__`.""" + return tuple(self[k] for k in self) + + def keys(self): + """Overloads :py:meth:`dict.keys` using :py:meth:`__iter__`.""" + return tuple(k for k in self) + + def items(self): + """Overloads :py:meth:`dict.item` using :py:meth:`__iter__`.""" + return tuple((k, self[k]) for k in self) + + def __hash__(self): + return hash(str(self)) + + def __cmp__(self, other): + return cmp(hash(self), hash(other)) + +class Method(str): + """ + Utility class to build API methods name and call them as functions. + + :py:class:`Method` has custom attribute access to build method names like + those specified in the API. Each attribute access builds a new Method with + a new name. + + Calling a :py:class:`Method` as a function with keyword arguments, + builds a :py:class:`Request`, runs it and returns the result. If **apikey** + is undefined, environment variable **musixmatch_apikey** will be used. If + **format** is undefined, environment variable **musixmatch_format** will be + used. If **musixmatch_format** is undefined, jason format will be used. + + >>> import musixmatch + >>> artist = musixmatch.api.Method('artist') + >>> + >>> try: + ... chart = artist.chart.get(country='it', page=1, page_size=3) + ... except musixmatch.api.Error, e: + ... pass + """ + __separator__ = '.' + + def __getattribute__(self, name): + if name.startswith('_'): + return super(Method, self).__getattribute__(name) + else: + return Method(self.__separator__.join([self, name])) + + def __call__ (self, apikey=None, format=None, **query): + query['apikey'] = apikey or musixmatch.apikey + query['format'] = format or musixmatch.format + return Request(self, query).response + + def __repr__(self): + return "Method('%s')" % self + +class Request(object): + """ + This is the main API class. Given a :py:class:`Method` or a method name, a + :py:class:`QueryString` or a :py:class:`dict`, it can build the API query + URL, run the request and return the response either as a string or as a + :py:class:`ResponseMessage` subclass. Assuming the default web services + location, this class try to build a proper request: + + >>> from musixmatch.api import Request, Method, QueryString + >>> method_name = 'artist.chart.get' + >>> method = Method(method_name) + >>> keywords = { 'country': 'it', 'page': 1, 'page_size': 3 } + >>> query_string = QueryString(keywords) + >>> + >>> r1 = Request(method_name, keywords) + >>> r2 = Request(method_name, **keywords) + >>> r3 = Request(method_name, query_string) + >>> r4 = Request(method, keywords) + >>> r5 = Request(method, **keywords) + >>> r6 = Request(method, query_string) + + If **method** is string, try to cast it into a :py:class:`Method`. If + **query_string** is a :py:class:`dict`, try to cast it into a + :py:class:`QueryString`. If **query_string** is not specified, try to + use **keywords** arguments as a :py:class:`dict` and cast it into a + :py:class:`QueryString`. + + Turning the :py:class:`Request` into a :py:class:`str` returns the URL + representing the API request: + + >>> str(Request('artist.chart.get', { 'country': 'it', 'page': 1 })) + 'http://api.musixmatch.com/ws/1.1/artist.chart.get?country=it&page=1' + """ + def __init__ (self, api_method, query=(), **keywords): + self.__api_method = isinstance(api_method, Method) and \ + api_method or Method(api_method) + self.__query_string = isinstance(query, QueryString) and \ + query or QueryString(query) + self.__query_string.update(keywords) + self.__response = None + + @property + def api_method(self): + """The :py:class:`Method` instance.""" + return self.__api_method + + @property + def query_string(self): + """The :py:class:`QueryString` instance.""" + return self.__query_string + + @contextmanager + def _received(self): + """A context manager to handle url opening""" + try: + response = urlopen(str(self)) + yield response + finally: + response.close() + + @property + def response(self): + """ + The :py:class:`ResponseMessage` based on the **format** key in the + :py:class:`QueryString`. + """ + if self.__response is None: + + format = self.query_string.get('format') + ResponseMessageClass = { + 'json': JsonResponseMessage, + 'xml': XMLResponseMessage, + }.get(format, None) + + if not ResponseMessageClass: + raise ResponseMessageError("Unsupported format `%s'" % format) + + with self._received() as response: + self.__response = ResponseMessageClass(response) + + return self.__response + + def __repr__(self): + return 'Request(%r, %r)' % (self.api_method, self.query_string) + + def __str__(self): + return '%(ws_location)s/%(api_method)s?%(query_string)s' % { + 'ws_location': musixmatch.ws.location, + 'api_method': self.api_method, + 'query_string': self.query_string + } + + def __hash__(self): + return hash(str(self)) + + def __cmp__(self, other): + return cmp(hash(self), hash(other))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/musixmatch/artist.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,80 @@ +""" +This module contains higher level classes to query Musixmatch API and build +simple dictionary-like objects representing an Artist or an ArtistsCollection. + +>>> from musixmatch.artist import Artist, ArtistsCollection +>>> import musixmatch.api +>>> +>>> try: +... artist = Artist(artist_id=292) +... collection = ArtistsCollection.fromChart(country='it', page=1) +... except musixmatch.api.Error, e: +... pass +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch.base import Item, ItemsCollection +from musixmatch.ws import artist + +class Artist(Item): + """ + This class build a :py:class:`dict` like object representing an artist. It + can get artist information through the :py:class:`musixmatch.api.Method` + **artist.get** or from an already well-formed :py:class:`dict`. Create an + Artist object based on a given keyword argument: + + :param artist_id: musiXmatch artist ID + :param artist_mbid: Musicbrainz artist ID + :param artist_data: an already well-formed :py:class:`dict` of artist data. + + Once information are collected, the following keys are available: + + :keyword artist_id: musiXmatch artist ID + :keyword artist_mbid: Musicbrainz artist ID + :keyword artist_name: Artist name + """ + __api_method__ = artist.get + +class ArtistsCollection(ItemsCollection): + """ + This class build a :py:class:`list` like object representing an artists + collection. It accepts :py:class:`dict` or :py:class:`Artist` objects. + """ + __allowedin__ = Artist + + @classmethod + def fromSearch(cls, **keywords): + """ + This classmethod builds an :py:class:`ArtistsCollection` from a + **artist.search** :py:class:`musixmatch.api.Method` call. + + :param q: a string that will be searched in every data field + (q_track, q_artist, q_lyrics) + :param q_track: words to be searched among track titles + :param q_artist: words to be searched among artist names + :param q_lyrics: words to be searched into the lyrics + :param page: requested page of results + :param page_size: desired number of items per result page + :param f_has_lyrics: exclude tracks without an available lyrics + (automatic if q_lyrics is set) + :param f_artist_id: filter the results by the artist_id + :param f_artist_mbid: filter the results by the artist_mbid + :rtype: :py:class:`ArtistsCollection` + """ + return cls.fromResponseMessage(artist.search(**keywords)) + + @classmethod + def fromChart(cls, **keywords): + """ + This classmethod builds an :py:class:`ArtistsCollection` from a + **artist.chart.get** :py:class:`musixmatch.api.Method` call. + + :param page: requested page of results + :param page_size: desired number of items per result page + :param country: the country code of the desired country chart + :rtype: :py:class:`ArtistsCollection` + """ + return cls.fromResponseMessage(artist.chart.get(**keywords)) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/musixmatch/base.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,240 @@ +""" +This module contains tha base classes for the Musixmatch API generated content: + +* :py:class:`musixmatch.artist.Artist` +* :py:class:`musixmatch.artist.Album` +* :py:class:`musixmatch.track.Track` +* :py:class:`musixmatch.lyrics.Lyrics` +* :py:class:`musixmatch.subtitle.Subtitle` +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch import api +import pprint + +class Base(object): + """ + The very base (abstract) class of the musixmatch package. I want all + classes to implement :py:meth:`__str__` and :py:meth:`__repr__`. + + Casting an :py:class:`Base` into a :py:class:`str` returns a pretty printed + (maybe using :py:mod:`pprint`) string representing the object. + + Using :py:func:`repr` on an :py:class:`Base` returns an evaluable string + representing the instance, whenever it is reasonable. + + :py:class:`Base` instances are hashable. + """ + + @classmethod + def label(cls): + """ + Returns the label that should be used as keyword in the + :py:class:`musixmatch.api.JsonResponseMessage` body. + """ + return getattr(cls, '__label__', cls.__name__.lower()) + + @classmethod + def apiMethod(cls): + """ + Returns the :py:class:`musixmatch.api.Method` that should be used to + build the object. Defaults to *label.get* where *label* is the result + from :py:meth:`label` + """ + api_method = getattr(cls, '__api_method__', '%s.%s' % (cls.label(),'get')) + if not isinstance(api_method, api.Method): + api_method = api.Method(str(api_method)) + return api_method + + def __str__(self): + raise NotImplementedError + + def __repr__(self): + raise NotImplementedError + +class Item(Base, dict): + """ + This is the base class for any entity in musixmatch package. Even if + response messages may have XML format, the JSON representation will be the + main data format, so the :py:class:`dict` sounds like the best base class. + It fetches the item data by guessing the :py:class:`musixmatch.api.Method` + and building the query based on a given keyword argument. Positional + argument is meant to be used by collection classes. Use only keyword + arguments. + """ + __api_method__ = None + + def __init__(self, dictionary=None, **keywords): + if dictionary: + dict.update(self, dictionary) + elif keywords: + message = self.apiMethod()(**keywords) + dict.update(self, self.fromResponseMessage(message)) + + def __str__(self): + return pprint.pformat(dict(self),4,1) + + def __repr__(self): + return '%s(%r)' % (type(self).__name__, dict(self)) + + def __hash__(self): + return int(self['%s_id' % self.label()]) + + @classmethod + def fromResponseMessage(cls, message): + """ + Returns an object instance, built from a + :py:class:`musixmatch.api.ResponseMessage` + """ + if not message.status_code: + raise api.Error(str(message.status_code)) + return cls.fromDictionary(message['body'][cls.label()]) + + @classmethod + def fromDictionary(cls, dictionary, **keywords): + """ + Returns an object instance, built from a :py:class:`dict` + """ + item = cls() + dict.update(item, dictionary, **keywords) + return item + +class ItemsCollection(Base, list): + """ + This is the base class for collections of items, like search results, or + charts. It behaves like :py:class:`list`, but enforce new items to be + instance of appropriate class checking against :py:meth:`allowedin`. + """ + + __allowedin__ = Item + + def __init__(self, *items): + self.extend(items) + + def __repr__(self): + items = [ repr(i) for i in self ] + return '%s(%s)' % (type(self).__name__, ', '.join(items)) + + def __str__(self): + return list.__str__(self) + + def __add__(self, iterable): + collection = self.copy() + collection.extend(iterable) + return collection + + def __iadd__(self, iterable): + raise NotImplementedError + + def __mul__(self, by): + raise NotImplementedError + + def __imul__(self, by): + raise NotImplementedError + + def __setitem__(self, key, item): + raise NotImplementedError + + def __setslice__(self, *indices): + raise NotImplementedError + + def __getslice__(self, i=0, j=-1): + return self.__getitem__(slice(i,j)) + + def __getitem__(self, i): + if type(i) is int: + return list.__getitem__(self, i) + elif type(i) is slice: + collection = type(self)() + list.extend(collection, list.__getitem__(self, i)) + return collection + else: + raise TypeError, i + + def append(self, item): + self.insert(len(self), item) + + def extend(self, iterable): + for item in iterable: + self.append(item) + + def count(self, item): + return int(item in self) + + def copy(self): + """Returns a shallow copy of the collection.""" + collection = type(self)() + list.extend(collection, self) + return collection + + def index(self, item, *indices): + return list.index(self, item, *indices[:2]) + + def insert(self, key, item): + allowed = self.allowedin() + if not isinstance(item, allowed): + item = allowed.fromDictionary(item) + if not item in self: + list.insert(self, key, item) + + def paged(self, page_size=3): + """ + Returns self, paged by **page_size**. That is, a list of + sub-collections which contain "at most" **page_size** items. + """ + return [ self.page(i,page_size) + for i in range(self.pages(page_size)) ] + + def page(self, page_index, page_size=3): + """ + Returns a specific page, considering pages that contain "at most" + **page_size** items. + """ + page = type(self)() + i = page_index * page_size + list.extend(page, self[i:i+page_size]) + return page + + def pager(self, page_size=3): + """ + A generator of pages, considering pages that contain "at most" + **page_size** items. + """ + for i in xrange(self.pages(page_size)): + yield self.page(i, page_size) + + def pages(self,page_size=3): + """ + Returns the number of pages, considering pages that contain "at most" + **page_size** items. + """ + pages, more = divmod(len(self), page_size) + return more and pages + 1 or pages + + @classmethod + def fromResponseMessage(cls, message): + """ + Returns an object instance, built on a + :py:class:`musixmatch.api.ResponseMessage` + """ + if not message.status_code: + raise api.Error(str(message.status_code)) + list_label = cls.label() + item_label = cls.allowedin().label() + items = [ i[item_label] for i in message['body'][list_label] ] + return cls(*items) + + @classmethod + def allowedin(cls): + """ + Returns the allowed content class. Defaults to :py:class:`Item` + """ + return cls.__allowedin__ + + @classmethod + def label(cls): + item_name = cls.allowedin().label() + return '%s_list' % item_name +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/musixmatch/lyrics.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,43 @@ +""" +This module contains higher level classes to query Musixmatch API and build +simple dictionary-like objects representing a track lyrics. + +>>> from musixmatch.lyrics import Lyrics +>>> import musixmatch.api +>>> +>>> try: +... lyrics = Lyrics(lyrics_id=292) +... except musixmatch.api.Error, e: +... pass +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch.base import Item +from musixmatch.ws import track + +class Lyrics(Item): + """ + This class builds a :py:class:`dict` object representing a the lyrics of a + track. It can get lyrics through the :py:class:`musixmatch.api.Method` + **track.lyrics.get** or from an already well-formed :py:class:`dict`. + Create a Track object based on a given keyword argument: + + :param track_id: musiXmatch track ID + :param musicbrainz_id: Musicbrainz track ID + :param track_echonest_id: Echonest track ID + :param lyrics_data: an already well-formed :py:class:`dict` of track data + :raises: :py:class:`musixmatch.api.Error` if :py:class:`musixmatch.api.ResponseStatusCode` is not 200 + + Once information are collected, the following keys are available: + + :keyword lyrics_body: the lyrics text + :keyword lyrics_id: the Musixmatch lyrics id + :keyword lyrics_language: the lyrics language + :keyword lyrics_copyright: the lyrics copyright statement + :keyword pixel_tracking_url: the pixel tracking url + :keyword script_tracking_url: the script tracking url + """ + __api_method__ = track.lyrics.get +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/musixmatch/subtitle.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,40 @@ +""" +This module contains higher level classes to query Musixmatch API and build +simple dictionary-like objects representing a track subtitle. + +>>> from musixmatch.subtitle import Subtitle +>>> import musixmatch.api +>>> +>>> try: +... subtitle = Subtitle(subtitle_id=292) +... except musixmatch.api.Error, e: +... pass +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch.base import Item +from musixmatch.ws import track + +class Subtitle(Item): + """ + This class builds a :py:class:`dict` object representing a subtitle of a + track. It can get subtitle through the :py:class:`musixmatch.api.Method` + **track.subtitle.get** or from an already well-formed :py:class:`dict`. + Create a Track object based on a given keyword argument: + + :param track_id: musiXmatch track ID + :param musicbrainz_id: Musicbrainz track ID + :param track_echonest_id: Echonest track ID + :param subtitle_data: an already well-formed :py:class:`dict` of track data + :raises: :py:class:`musixmatch.api.Error` if :py:class:`musixmatch.api.StatusCode` is not 200 + + Once information are collected, the following keys are available: + + :keyword subtitle_body: the subtitle text + :keyword subtitle_id: the Musixmatch subtitle id + :keyword subtitle_language: the subtitle language + """ + __api_method__ = track.subtitle.get +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/musixmatch/track.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,171 @@ +""" +This module contains higher level classes to query Musixmatch API and build +simple dictionary-like objects representing a Track or a TracksCollection. + +>>> from musixmatch.track import Track, TracksCollection +>>> import musixmatch.api +>>> +>>> try: +... track = Track(track_mbid=8976) +... collection = TracksCollection.fromChart(country='us', page=1) +... except musixmatch.api.Error, e: +... pass +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch import api, lyrics, subtitle +from musixmatch.base import Item, ItemsCollection +from musixmatch.ws import track, matcher, album + +_marker=object() + +class Track(Item): + """ + This class builds a :py:class:`dict` like object representing a track. It + can get track information through the :py:class:`musixmatch.api.Method` + **track.get** or from an already well-formed :py:class:`dict`. Create a + Track object based on a given keyword argument: + + :param track_id: musiXmatch track ID + :param musicbrainz_id: Musicbrainz track ID + :param track_echonest_id: Echonest track ID + :param track_data: an already well-formed :py:class:`dict` of track data + :raises: :py:exc:`musixmatch.api.Error` if :py:class:`musixmatch.api.ResponseStatusCode` is not 200 + + Once information are collected, the following keys are available: + + :keyword track_id: musiXmatch track ID + :keyword track_mbid: Musicbrainz track ID + :keyword lyrics_id: musiXmatch lyrics ID + :keyword instrumental: wether the track is instrumental or not + :keyword subtitle_id: musiXmatch subtitle ID + :keyword track_name: track name + :keyword album_coverart_100x100: album cover URL + :keyword artist_id: musiXmatch artist ID + :keyword artist_mbid: Musicbrainz artist ID + :keyword artist_name: artist name + + Keyword access have been overloaded thanks to the :py:meth:`get` method + which will eventually fetch the matching lyrics or subtitle. + """ + __api_method__ = track.get + + @classmethod + def fromMatcher(cls, **keywords): + """ + Returns a :py:class:`Track` based on the result of the + :py:class:`musiXmatch.api.Method` **matcher.track.get**. Accepts the + following keywords: + + :param q_track: words to be searched among track titles + :param q_artist: words to be searched among artist names + """ + return cls.fromResponseMessage(matcher.track.get(**keywords)) + + def get(self, key, default=_marker): + """ + If key is *lyrics* or *subtitle* try to query api for proper value, + and build an :py:class:`musixmatch.lyrics.Lyrics` or + :py:class:`musixmatch.subtitle.Subtitle`. Access to the above mentioned + keys may fail with :py:exc:`musixmatch.api.Error`. Once fetched, the + result is saved. + """ + special = { + 'lyrics': lyrics.Lyrics, + 'subtitle': subtitle.Subtitle, + } + if key in special and not key in self: + self[key] = special[key](track_id=self['track_id']) + value = dict.get(self, key, default) + if value == _marker: + raise KeyError, key + return value + + def __getitem__(self, key): + return self.get(key) + + def postFeedback(self, feedback): + """ + Post feedback about lyrics for this track. **feedback** can be one of: + + :keyword wrong_attribution: the lyrics shown are not by the artist that + I selected. + :keyword bad_characters: there are strange characters and/or words + that are partially scrambled. + :keyword lines_too_long: the text for each verse is too long! + :keyword wrong_verses: there are some verses missing from the + beginning or at the end. + :keyword wrong_formatting: the text looks horrible, please fix it! + """ + accepted = [ + 'wrong_attribution', 'bad_characters', 'lines_too_long', + 'wrong_verses', 'wrong_formatting' ] + if feedback in accepted: + message = track.lyrics.feedback.post( + track_id=self['track_id'], + lyrics_id=self['track_id']['lyrics']['lyrics_id'], + feedback=feedback + ) + if not message.status_code: + raise api.Error(str(message.status_code)) + else: + raise TypeError, '%r not in %r' % (feedback, accepted) + +class TracksCollection(ItemsCollection): + """ + This class build a :py:class:`list` like object representing a tracks + collection. It accepts :py:class:`dict` or :py:class:`Track` objects. + """ + __allowedin__ = Track + + @classmethod + def fromAlbum(cls, **keywords): + """ + This classmethod builds an :py:class:`TracksCollection` from a + **album.tracks.get** :py:class:`musixmatch.api.Method` call. + + :param album_id: musiXmatch album ID + """ + return cls.fromResponseMessage(album.tracks.get(**keywords)) + + @classmethod + def fromSearch(cls, **keywords): + """ + This classmethod builds an :py:class:`TracksCollection` from a + **track.search** :py:class:`musixmatch.api.Method` call. + + :param q: a string that will be searched in every data field + (q_track, q_artist, q_lyrics) + :param q_track: words to be searched among track titles + :param q_artist: words to be searched among artist names + :param q_track_artist: words to be searched among track titles or + artist names + :param q_lyrics: words to be searched into the lyrics + :param page: requested page of results + :param page_size: desired number of items per result page + :param f_has_lyrics: exclude tracks without an available lyrics + (automatic if q_lyrics is set) + :param f_artist_id: filter the results by the artist_id + :param f_artist_mbid: filter the results by the artist_mbid + :param quorum_factor: only works together with q and q_track_artist + parameter. Possible values goes from 0.1 to + 0.9. A value of 0.9 means: 'match at least 90 + percent of the words'. + """ + return cls.fromResponseMessage(track.search(**keywords)) + + @classmethod + def fromChart(cls, **keywords): + """ + This classmethod builds an :py:class:`TracksCollection` from a + **track.chart.get** :py:class:`musixmatch.api.Method` call. + + :param page: requested page of results + :param page_size: desired number of items per result page + :param country: the country code of the desired country chart + :param f_has_lyrics: exclude tracks without an available lyrics + (automatic if q_lyrics is set) + """ + return cls.fromResponseMessage(track.chart.get(**keywords))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/musixmatch/ws.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,30 @@ +""" +This is an utility module that provides a row musiXmatch web API interface. +Ideally it should be used like this: + +>>> import musixmatch +>>> +>>> try: +... chart = musixmatch.ws.track.chart.get(country='it', f_has_lyrics=1) +... except musixmatch.api.Error, e: +... pass +""" +from warnings import warn +import os +import musixmatch.api +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +_version = os.environ.get('musixmatch_apiversion', None) +if not _version: + _version = '1.1' +else: + warn("Use of `musixmatch_apiversion' was deprecated in favour of `musixmatch_wslocation'", DeprecationWarning) + +location = os.environ.get('musixmatch_wslocation', 'http://api.musixmatch.com/ws/%s' % _version) + +artist = musixmatch.api.Method('artist') +album = musixmatch.api.Method('album') +track = musixmatch.api.Method('track') +tracking = musixmatch.api.Method('tracking') +matcher = musixmatch.api.Method('matcher')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/setup.cfg Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,11 @@ +[build_sphinx] +source-dir = docs +build-dir = build/docs +all_files = 1 + +[sdist] +formats = gztar,zip + +[upload_sphinx] +upload-dir = build/docs/html +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/setup.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,33 @@ +from setuptools import setup +import sys +import os + +wd = os.path.dirname(os.path.abspath(__file__)) +os.chdir(wd) +sys.path.insert(1, wd) + +name = 'musixmatch' +pkg = __import__(name) +author, email = pkg.__author__.rsplit(' ', 1) + +with open(os.path.join(wd, 'README.rst'),'r') as readme: + long_description = readme.read() + +url = 'http://projects.monkeython.com/musixmatch', +egg = { + 'name': name, + 'version': pkg.__version__, + 'author': author, + 'author_email': email.strip('<>'), + 'url': url, + 'description': "Package to interface with the Musixmatch API", + 'long_description': long_description, + 'download_url': '%s/dists' % url, + 'classifiers': pkg.__classifiers__, + 'packages': [name], + 'include_package_data': True, + 'exclude_package_data': {name: ["*.rst", "docs", "tests"]}, + 'test_suite': 'tests.suite'} + +if __name__ == '__main__': + setup(**egg)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/tests/__init__.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,39 @@ +import os +from unittest import defaultTestLoader, TestSuite +import api +import apikey +import artist +import base +import lyrics +import subtitle +import track +import album + +suite = TestSuite() +suite.addTest(defaultTestLoader.loadTestsFromModule(api)) +suite.addTest(defaultTestLoader.loadTestsFromModule(artist)) +suite.addTest(defaultTestLoader.loadTestsFromModule(base)) +suite.addTest(defaultTestLoader.loadTestsFromModule(lyrics)) +suite.addTest(defaultTestLoader.loadTestsFromModule(subtitle)) +suite.addTest(defaultTestLoader.loadTestsFromModule(track)) +suite.addTest(defaultTestLoader.loadTestsFromModule(album)) +# if os.environ.get('musixmatch_apikey', None): +# suite.addTest(defaultTestLoader.loadTestsFromModule(apikey)) + +# suite.addTest(api.TestError()) +# suite.addTest(api.TestResponseStatusCode()) +# suite.addTest(api.TestResponseMessage()) +# suite.addTest(api.TestXMLResponseMessage()) +# suite.addTest(api.TestJsonResponseMessage()) +# suite.addTest(api.TestQueryString()) +# suite.addTest(api.TestRequest()) +# suite.addTest(api.TestMethod()) +# suite.addTest(base.TestBase()) +# suite.addTest(base.TestItem()) +# suite.addTest(base.TestCollection()) +# suite.addTest(artist.TestArtist()) +# suite.addTest(artist.TestArtistsCollection()) +# suite.addTest(track.TestTrack()) +# suite.addTest(track.TestTracksCollection()) +# suite.addTest(lyrics.TestLyrics()) +# suite.addTest(subtitle.TestSubtitle())
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/tests/album.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,48 @@ +from musixmatch import * +from tests import base + +class TestAlbum(base.TestItem): + Class = album.Album + item = { + "album_id": "292", + "album_name": "292", + } + item_str = "{ 'album_id': '292',\n 'album_name': '292'}" + item_repr = "Album({'album_name': '292', 'album_id': '292'})" + item_hash = 292 + +class TestAlbumsCollection(base.TestCollection): + CollectionClass = album.AlbumsCollection + AllowedContentClass = album.AlbumsCollection.allowedin() + item_list = 'album_list' + item_id = 'album_id' + item = 'album' + message = { + "body": { + "album_list": [ + { + "album": { + "album_id": "292", + "album_name": "292", + } + }, + { + "album": { + "album_id": "8976", + "album_name": "8976", + } + }, + { + "album": { + "album_id": "9673", + "album_name": "9673", + } + } + ] + }, + "header": { + "execute_time": 0.14144802093506001, + "status_code": 200 + } + } +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/tests/api.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,91 @@ +import unittest +from musixmatch import * +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + +class TestError(unittest.TestCase): + + def test__str__(self): + error = api.Error('test error', Exception('test exception')) + self.assertEqual(str(error), 'test error: test exception') + +class TestResponseStatusCode(unittest.TestCase): + + def test__init__(self): + self.assertRaises(ValueError, api.ResponseStatusCode, 'fail') + + self.assertRaises(TypeError, api.ResponseStatusCode, [1,2,3]) + + def test__int__(self): + self.assertEqual(int(api.ResponseStatusCode('1')), 1) + + def test__str__(self): + self.assertEqual(str(api.ResponseStatusCode('200')), + 'The request was successful.') + + self.assertEqual(str(api.ResponseStatusCode('-1')), + 'Unknown status code -1!') + + def test__nonzero__(self): + self.assertEqual(bool(api.ResponseStatusCode('200')), True) + self.assertEqual(bool(api.ResponseStatusCode('404')), False) + +class TestResponseMessage(unittest.TestCase): + + def test__init__(self): + self.assertRaises(NotImplementedError, api.ResponseMessage, '') + +class TestXMLResponseMessage(unittest.TestCase): + + message = """<message> + <header> + <status_code>200</status_code> + </header> + <body> + </body> +</message>""" + + def test_status_code(self): + message = api.XMLResponseMessage(StringIO(self.message)) + self.assertEqual(isinstance(message.status_code, api.ResponseStatusCode), True) + +class TestJsonResponseMessage(unittest.TestCase): + message = """{"message":{ + "header":{ + "status_code":200}, + "body":{ +}}}""" + + def test_status_code(self): + message = api.JsonResponseMessage(StringIO(self.message)) + self.assertEqual(isinstance(message.status_code, api.ResponseStatusCode), True) + +class TestQueryString(unittest.TestCase): + def test__str__(self): + keywords = { 'country': 'it', 'page': 1, 'page_size': 3 } + query_string = api.QueryString(keywords) + self.assertEqual(str(query_string), 'country=it&page=1&page_size=3') + def test__repr__(self): + keywords = { 'apikey': 'it', 'id': 12345, 'format': 'json' } + query_string = api.QueryString(keywords) + self.assertEqual(repr(query_string).count('apikey'), 0) + +class TestRequest(unittest.TestCase): + def test__str__(self): + url = 'http://api.musixmatch.com/ws/1.1/test?apikey=apikey&format=format'.encode('utf-8') + method = api.Method('test') + query_string = api.QueryString({'apikey':'apikey','format':'format'}) + request = api.Request(method, query_string) + self.assertEqual(str(request), url) + +class TestMethod(unittest.TestCase): + + def test__getattribute__(self): + method = api.Method('test') + self.assertEqual(hasattr(method, 'subtest'), True) + self.assertEqual(hasattr(method, '__nothing__'), False) + +if __name__ == '__main__': + unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/tests/apikey.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,67 @@ +import unittest +from musixmatch import * + +class TestArtist(unittest.TestCase): + def test_Artist(self): + a1 = artist.Artist(artist_id=378462) + a2 = artist.Artist(artist_mbid='650e7db6-b795-4eb5-a702-5ea2fc46c848') + self.assertEqual(a1,a2) + +class TestArtistsCollection(unittest.TestCase): + def test_fromChart(self): + c = artist.ArtistsCollection.fromChart(page=1, page_size=10) + self.assertEqual(len(c), 10) + for i in c: + self.assertEqual(type(i), artist.Artist) + + def test_fromSearch(self): + c = artist.ArtistsCollection.fromSearch( + q='madonna', page=1, page_size=10) + self.assertEqual(len(c), 10) + for i in c: + self.assertEqual(type(i), artist.Artist) + +class TestTrack(unittest.TestCase): + def test_Track(self): + t1 = track.Track(track_id=7176425) + t2 = track.Track(track_mbid='a5424f77-42d9-428c-9c6f-3f06ff19d756') + self.assertEqual(t1,t2) + + def test_fromMatcher(self): + t = track.Track.fromMatcher( + q_track='lose yourself (album version)', q_artist='eminem') + self.assertEqual(bool(t), True) + + def test_get(self): + t = track.Track(track_id=6593495) + l = t['lyrics'] + self.assertEqual(bool(l), True) + self.assertEqual(type(l), lyrics.Lyrics) + s = t.get('subtitle') + self.assertEqual(bool(s), True) + self.assertEqual(type(s), subtitle.Subtitle) + + +class TestTracksCollection(unittest.TestCase): + def test_fromChart(self): + c = track.TracksCollection.fromChart(page=1, page_size=10) + self.assertEqual(len(c), 10) + for i in c: + self.assertEqual(type(i), track.Track) + + def test_fromSearch(self): + c = track.TracksCollection.fromSearch(q_track='Cotton eye Joe') + self.assertEqual(len(c), 10) + for i in c: + self.assertEqual(type(i), track.Track) + +class TestLyrics(unittest.TestCase): + def test_Lyrics(self): + l = lyrics.Lyrics(track_id='4559887') + self.assertEqual(bool(l), True) + +class TestSubtitle(unittest.TestCase): + def test_Subtitle(self): + s = subtitle.Subtitle(track_id='6593495') + self.assertEqual(bool(s), True) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/tests/artist.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,48 @@ +from musixmatch import * +from tests import base + +class TestArtist(base.TestItem): + Class = artist.Artist + item = { + "artist_id": "292", + "artist_mbid": "292", + } + item_str = "{ 'artist_id': '292',\n 'artist_mbid': '292'}" + item_repr = "Artist({'artist_mbid': '292', 'artist_id': '292'})" + item_hash = 292 + +class TestArtistsCollection(base.TestCollection): + CollectionClass = artist.ArtistsCollection + AllowedContentClass = artist.ArtistsCollection.allowedin() + item_list = 'artist_list' + item_id = 'artist_id' + item = 'artist' + message = { + "body": { + "artist_list": [ + { + "artist": { + "artist_id": "292", + "artist_mbid": "292", + } + }, + { + "artist": { + "artist_id": "8976", + "artist_mbid": "8976", + } + }, + { + "artist": { + "artist_id": "9673", + "artist_mbid": "9673", + } + } + ] + }, + "header": { + "execute_time": 0.14144802093506001, + "status_code": 200 + } + } +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/tests/base.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,214 @@ +import unittest +from musixmatch import * + +class TestBase(unittest.TestCase): + + Class = base.Base + def test_label(self): + self.assertEqual(self.Class.label(), self.Class.__name__.lower()) + +class TestItem(TestBase): + Class = base.Item + item = { "item_id": "9673" } + item_str = "{ 'item_id': '9673'}" + item_repr = "Item({'item_id': '9673'})" + item_hash = 9673 + item_id = 'item_id' + + # def test_fromResponseMessage(self): + # self.assertRaises(api.Error, + # self.Class.fromResponseMessage, self.fail) + # success = self.Class.fromResponseMessage(self.success) + # self.assertEqual(success[item_id], + # self.success['body'][self.label()][self.item_id]) + def test_fromDictionary(self): + item = self.Class.fromDictionary(self.item) + # Data integrity + for k in self.item.keys(): + self.assertEqual(item[k], self.item[k]) + + def test__str__(self): + item = self.Class.fromDictionary(self.item) + self.assertEqual(str(item),self.item_str) + + def test__repr__(self): + item = self.Class.fromDictionary(self.item) + self.assertEqual(repr(item),self.item_repr) + + def test__hash__(self): + item = self.Class.fromDictionary(self.item) + self.assertEqual(hash(item), self.item_hash) + +class TestCollection(unittest.TestCase): + + Class = base.ItemsCollection + AllowedContent = base.ItemsCollection.allowedin() + item_list = 'item_list' + item_id = 'item_id' + item = 'item' + message = { + "body": { + "item_list": [ + { + "item": { + "item_id": "292", + "item_name": "item_292" + } + }, + { + "item": { + "item_id": "8976", + "item_name": "item_8976" + } + }, + { + "item": { + "item_id": "9673", + "item_name": "item_9673" + } + } + ] + }, + "header": { + "execute_time": 0.14144802093506001, + "status_code": 200 + } + } + + def test_insert(self): + collection = self.Class() + saved = self.message['body'][self.item_list][0][self.item] + item = self.AllowedContent(saved) + collection.insert(0, item) + # Item correctly inserted + self.assertEqual(collection[0], item) + + saved = self.message['body'][self.item_list][1][self.item] + collection.insert(0, saved) + # Item corectly casted to self.AllowedContent + self.assertEqual(type(collection[0]), self.AllowedContent) + # Item content integrity + self.assertEqual(collection[0][self.item_id], saved[self.item_id]) + # Previously inserted item has shifted position + self.assertEqual(collection[1], item) + + def test_append(self): + collection = self.Class() + saved = self.message['body'][self.item_list][1][self.item] + item = self.AllowedContent.fromDictionary(saved) + collection.append(item) + # Item correctly appended + self.assertEqual(collection[0], item) + + saved = self.message['body'][self.item_list][2][self.item] + collection.append(saved) + # Item correctly appended + self.assertEqual(collection[1][self.item_id], saved[self.item_id]) + + saved = self.message['body'][self.item_list][0][self.item] + collection.append(saved) + # Item corectly casted to self.AllowedContent + self.assertEqual(type(collection[2]), self.AllowedContent) + # Item content integrity + self.assertEqual(collection[2][self.item_id], saved[self.item_id]) + + def test_extend(self): + items = [ i[self.item] for i in self.message['body'][self.item_list] ] + collection = self.Class(self.AllowedContent(items[0])) + self.assertEqual(type(collection[0]), self.AllowedContent) + typed = [ self.AllowedContent(i) for i in items[1:] ] + for i in typed: + self.assertEqual(type(i), self.AllowedContent) + collection.extend(typed) + # Collection correctly extended + self.assertEqual(collection[1], typed[0]) + self.assertEqual(collection[2], typed[1]) + + row = items[:2] + items[1:3] + collection.extend(row) + # Items content integrity: no duplicate + self.assertEqual(len(collection), 3) + + collection = self.Class() + collection.extend(row) + self.assertEqual(len(row), 4) + self.assertEqual(len(collection), 3) + # Items corectly casted to self.AllowedContent + for i in range(3): + self.assertEqual(type(collection[i]), self.AllowedContent) + + # def test__setitem__(self): + # collection = self.Class(self.AllowedContent( + # self.message['body'][self.item_list][1][self.item])) + # saved = self.message['body'][self.item_list][2][self.item] + # item = self.AllowedContent(saved) + # collection[0] = item + # # Index of ItemsCollection correctly set + # self.assertEqual(collection[0], item) + + # saved = self.message['body'][self.item_list][0][self.item] + # collection[0] = saved + # # Item corectly casted to self.AllowedContent + # self.assertEqual(type(collection[0]),self.AllowedContent) + # # Item content integrity + # self.assertEqual(collection[0][self.item_id], saved[self.item_id]) + # # Wrong setting + # self.assertRaises(IndexError, collection.__setitem__, 9, saved) + # self.assertRaises(TypeError, collection.__setitem__, 'test', saved) + + def test_page(self): + items = [ i[self.item] for i in self.message['body'][self.item_list] ] + collection = self.Class(*items) + self.assertEqual(len(collection), 3) + for i in range(3): + self.assertEqual(type(collection[i]), self.AllowedContent) + page = collection.page(1,2) + self.assertEqual(len(page), 1) + self.assertEqual(type(page), self.Class) + self.assertEqual(type(page[0]), self.AllowedContent) + + def test_pages(self): + items = [ i[self.item] for i in self.message['body'][self.item_list] ] + collection = self.Class(*items) + self.assertEqual(len(collection), 3) + pages = collection.pages(2) + self.assertEqual(pages, 2) + + def test_paged(self): + items = [ i[self.item] for i in self.message['body'][self.item_list] ] + collection = self.Class(*items) + self.assertEqual(len(collection), 3) + for i in range(3): + self.assertEqual(type(collection[i]), self.AllowedContent) + paged = collection.paged(2) + self.assertEqual(len(paged), 2) + for i,l in zip(range(2), (2,1)): + self.assertEqual(len(paged[i]), l) + self.assertEqual(type(paged[i]), self.Class) + for p,i in [(0,0),(0,1),(1,0)]: + self.assertEqual(id(paged[p][i]), id(collection[(2*p)+i])) + + def test_pager(self): + items = [ i[self.item] for i in self.message['body'][self.item_list] ] + collection = self.Class(*items) + self.assertEqual(len(collection), 3) + for i in range(3): + self.assertEqual(type(collection[i]), self.AllowedContent) + pager = [] + for page in collection.pager(2): + self.assertEqual(type(page), self.Class) + self.assertEqual(type(page[0]), self.AllowedContent) + pager.append(page) + self.assertEqual(len(pager), 2) + self.assertEqual(len(pager[0]), 2) + self.assertEqual(len(pager[1]), 1) + + def test__add__(self): + items = [ i[self.item] for i in self.message['body'][self.item_list] ] + collection1 = self.Class(*items[:2]) + collection2 = self.Class(*items[1:3]) + collection3 = collection1 + collection2 + # Collection correctly created + self.assertEqual(type(collection3), self.Class) + self.assertEqual(len(collection3), 3) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/tests/lyrics.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,12 @@ +from musixmatch import * +from tests import base + +class TestLyrics(base.TestItem): + Class = lyrics.Lyrics + item = { + "lyrics_id": "292", + "lyrics_mbid": "292", + } + item_str = "{ 'lyrics_id': '292',\n 'lyrics_mbid': '292'}" + item_repr = "Lyrics({'lyrics_id': '292', 'lyrics_mbid': '292'})" + item_hash = 292
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/tests/subtitle.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,12 @@ +from musixmatch import * +from tests import base + +class TestSubtitle(base.TestItem): + Class = subtitle.Subtitle + item = { + "subtitle_id": "292", + "subtitle_mbid": "292", + } + item_str = "{ 'subtitle_id': '292',\n 'subtitle_mbid': '292'}" + item_repr = "Subtitle({'subtitle_mbid': '292', 'subtitle_id': '292'})" + item_hash = 292
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/musixmatch-master/tests/track.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,48 @@ +from musixmatch import * +from tests import base + +class TestTrack(base.TestItem): + Class = track.Track + item = { + "track_id": "292", + "track_mbid": "292", + } + item_str = "{ 'track_id': '292',\n 'track_mbid': '292'}" + item_repr = "Track({'track_mbid': '292', 'track_id': '292'})" + item_hash = 292 + +class TestTracksCollection(base.TestCollection): + CollectionClass = track.TracksCollection + AllowedContentClass = track.TracksCollection.allowedin() + item_list = 'track_list' + item_id = 'track_id' + item = 'track' + message = { + "body": { + "track_list": [ + { + "track": { + "track_id": "292", + "track_mbid": "292", + } + }, + { + "track": { + "track_id": "8976", + "track_mbid": "8976", + } + }, + { + "track": { + "track_id": "9673", + "track_mbid": "9673", + } + } + ] + }, + "header": { + "execute_time": 0.14144802093506001, + "status_code": 200 + } + } +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/new_track_data.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,266 @@ + + +import os +import sys +import pickle + +from xml.dom import minidom +import urllib2 #call url function +import time +import sqlite3 +import cPickle + +import pyechonest # import echonest API +import pylast # import last.fm API +from pyechonest import artist, catalog, config, playlist # How it works instead of pyechonest + + +#change the path to 7-digital python library +sys.path.append("7digital-python/lib/") +import py7digital + +""" API Key from echonest """ +#Your API Key: SFXNKMTRAZ3ULHK6U +#Your Consumer Key: 54a06c9bd235d47f787d8cf614577a94 +#Your Shared Secret: aiPUaTToTpixW4Ttaf4O9A + +"""API key from 7 digital""" +# oauth_consumer_key=7dbpa63h3y3d +# oauth_consumer_secret=zz48d4epsqmrsuvp + +"""API key from last.fm""" +# Your API Key is bd1cb09de31188b43aa46f39b8e40614 +# Your secret is d2537b0ce8bc859a6068833c5e2a72a7 + +""""*********************************************************************************""" + +# Echo Nest API key +config.ECHO_NEST_API_KEY="SFXNKMTRAZ3ULHK6U " + +# 7 digital API key +DIGITAL7_API_KEY = '7dbpa63h3y3d' + +"""The use of last.fm API""" +API_KEY = "bd1cb09de31188b43aa46f39b8e40614" +API_SECRET = "d2537b0ce8bc859a6068833c5e2a72a7" + +"""Authentication, username and password""" +username = "QMYading" +password_hash = pylast.md5("123456") +network = pylast.LastFMNetwork(api_key = API_KEY, api_secret = + API_SECRET, username = username, password_hash = password_hash) + +def url_call(url): + """ + ***This method is from get_preview_url.py by Thierry Bertin-Mahieux*** + Do a simple request to the 7digital API + We assume we don't do intense querying, this function is not robust + Return the answer as na xml document + """ + stream = urllib2.urlopen(url) + xmldoc = minidom.parse(stream).documentElement + stream.close() + return xmldoc + +def get_trackid_from_text_search(title,artistname=''): + """ + ***This method is from get_preview_url.py by Thierry Bertin-Mahieux*** + Search for an artist + title using 7digital search API + Return None if there is a problem, or tuple (title,trackid) + """ + url = 'http://api.7digital.com/1.2/track/search?' + url += 'oauth_consumer_key='+DIGITAL7_API_KEY + query = title + if artistname != '': + query = artistname + ' ' + query + query = urllib2.quote(query) + url += '&q='+query + xmldoc = url_call(url) + status = xmldoc.getAttribute('status') + if status != 'ok': + return None + resultelem = xmldoc.getElementsByTagName('searchResult') + if len(resultelem) == 0: + return None + track = resultelem[0].getElementsByTagName('track')[0] + tracktitle = track.getElementsByTagName('title')[0].firstChild.data + trackid = int(track.getAttribute('id')) + return (tracktitle,trackid) + +def get_preview_from_trackid(trackid): + """ + ***This method is from get_preview_url.py by Thierry Bertin-Mahieux*** + Ask for the preview to a particular track, get the XML answer + After calling the API with a given track id, + we get an XML response that looks like: + + <response status="ok" version="1.2" xsi:noNamespaceSchemaLocation="http://api.7digital.com/1.2/static/7digitalAPI.xsd"> + <url> + http://previews.7digital.com/clips/34/6804688.clip.mp3 + </url> + </response> + + We parse it for the URL that we return, or '' if a problem + """ + url = 'http://api.7digital.com/1.2/track/preview?redirect=false' + url += '&trackid='+str(trackid) + url += '&oauth_consumer_key='+DIGITAL7_API_KEY + xmldoc = url_call(url) + status = xmldoc.getAttribute('status') + if status != 'ok': + return '' + urlelem = xmldoc.getElementsByTagName('url')[0] + preview = urlelem.firstChild.nodeValue + return preview + + #function that downloads a file +def download_file(file_url, file_name): + # open the url + mp3file = urllib2.urlopen(file_url) + + # open the local file for writing + local_file = open(file_name, "wb") + # write to file + local_file.write(mp3file.read()) + local_file.close() + + + + +# get artist object +# artist_object = network.get_artist("System of a Down") + +# get the mbid for the tracks +# print(artist_object.get_mbid()) + +# print(os.getcwd()) #print current working directory +# artist_object.shout("Artist object created successful!") + +# get track object +# track_object = network.get_track("Iron Maiden", "The Nomad") +# track_object.add_tags(("awesome", "favorite")) # add tags to the tracks + +#get get_album object +#album_object = network.get_album("Adele","21") + +# get tags object +#os.chdir('./NEW_Tagsets/') # change current working directory +#tag_classification = ["Happy", "Sad", "Angry", "Relax","Sport", "Study","Entertainment","Travelling"] +#tag_classification = ["Happy", "Sad", "Angry", "Relax","Sport", "Study","Entertainment","Travel"] +#tag_classification = ["Sad", "Angry", "Relax","Sport", "Study","Entertainment","Travel"] +#tag_classification = ["Travel"] + +tag_classification = ["wake me up"] + +#tag_classification = ["Passionate","Rollicking","Literate","Humorous","Aggressive"] +#tag_classification = ["Joy","Fun","Cheerful","Pleasing","Pleasant"] +#tag_classification = ["Romantic","Love"] + +#tag_classification = ["silly", "campy", "quirky", "whimsical", "witty", "wry"] +#tag_classification = ["fiery","tense","anxious","intense", "volatile","visceral"] +#tag_classification = ["poignant","wistful", "bittersweet", "autumnal", "brooding"] +#tag_classification = ["rousing", "confident","boisterous", "rowdy"] +#tag_classification = ["sweet", "amiable","good natured"] + + +track = network.track + +# search for similar tag according to the seeds in tag_classification +for tags_name in tag_classification: + tags = network.search_for_tag(tags_name) + # filtering the results + tags_search = tags.get_next_page() + tag_file_name = tags_name + "_tags_results.txt" + + # print the tag name, i.e happy_tag_results.txt + print(tag_file_name) + + print("successfully written in the file") + + tag_data = open(tag_file_name, 'wb') # open and write the file for top tags + + os.makedirs(str(tags_name)) + os.chdir('./' + str(tags_name) + '/') + + # write the similar with a topic + for each_letter in tags_search: + # store the each lexical into the file. i.e 'happy' 'very happy' + tag_data.write("%s\n" % each_letter) + # create a directory with tag name + os.makedirs(str(each_letter)) + os.chdir('./' + str(each_letter) + '/') + + # store each lexical tag as a tag_object + tag_object = network.get_tag(each_letter) + tracks_results = tag_object.get_top_tracks() + + track_file_name = str(each_letter) + "_results.txt" + tag_track_file = open(track_file_name,'wb') # open and write the file for tracks with tag + + print(track_file_name) + + # write the tracks result with special tags + for each_line in tracks_results: + tag_track_file.write("%s%s\n" % each_line) + + tag_track_file.close() + + artist_file_name = str(each_letter) + "_artist.txt" + title_file_name = str(each_letter) + "_title.txt" + #id_file_name = str(each_letter) + "_id.txt" + + # open the track info file + newdata = open(track_file_name) + artist_data = open(artist_file_name,'wb') + title_data = open(title_file_name,'wb') + #id_file = open(id_file_name,"wb") + + + for each_row in newdata: + + (artist,title) = each_row.split('-',1) + title = title.strip() #delete the extra space + title = title.rstrip('0') #get rid of the 0 at the end + + artist_data.write("%s\n" % artist) + title_data.write("%s\n" % title) + + print artist + print title + + try: + (tracktitle,trackid) = get_trackid_from_text_search(title,artist) + + if trackid is not None: + # fetch the url + audio_url = get_preview_from_trackid(trackid) + print(audio_url) + # fetch the audio and save in the correct folder + + file_name = tracktitle + u'.wav' + file_name = file_name.replace(u'/', u' ') + #file_name = t.title + u'.mp3' + print file_name + print("downloading") + + # download_path = os.path.join(file_path, file_name) + path_name = './' + file_name + mp3 = download_file(audio_url, path_name) + except: + pass + + + + # id_file_name.close() + artist_data.close() + title_data.close() + newdata.close() + parent_path = '../' + os.chdir(parent_path) + + os.chdir(parent_path) + +tag_data.close() + +# check the directory, write to the current directory, +# if it doens't exist, create a new one
Binary file Yading/wake me up/cheers me up/Beach Boys Medley: Good Vibrations California Girls Fun Fun Fun I Get Around Surfin' USA.wav has changed
Binary file Yading/wake me up/cheers me up/First Week Last Week.... Carefree (2005 Remastered Album Version ).wav has changed
Binary file Yading/wake me up/cheers me up/I Zimbra (2005 Remastered Album Version)).wav has changed
Binary file Yading/wake me up/cheers me up/Mortal Kombat: Utah Saints Techno-Syndrome 7' Mix.wav has changed
Binary file Yading/wake me up/cheers me up/Star Rider Originally Performed By Kat-tun.wav has changed
Binary file Yading/wake me up/cheers me up/The Big Bang Theory Theme (Vocal Version) Tv Edit.wav has changed
Binary file Yading/wake me up/cheers me up/The Great Curve (Remastered & Extended Version ).wav has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/cheers me up/cheers me up_artist.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Yes +Yes +The Kinks +Arctic Monkeys +Dead Boys +Noel Gallagher +Superchunk +Queen +G +L'Arc~en~Ciel +Jane's Addiction +Talking Heads +Big Bang +Marvin Gaye +Marvin Gaye +The Evens +Big Bang +KAT +Superchunk +Noel Gallagher +Talking Heads +SHINee +The Kinks +John Frusciante +Lemon Demon +Talking Heads +Lô Borges +Donots +The Kinks +Why? +Eluphant +Neil Young +The Beach Boys +Screeching Weasel +태양 +Hall & Oates +Love +The Kinks +[spunge] +Blackbird Blackbird +Paul Weller +David Bowie +SHINee +Paul McCartney +林俊傑 +Paul McCartney +The Beach Boys +Todd Rundgren +The Kinks +The Immortals
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/cheers me up/cheers me up_results.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Yes - Looking Around0 +Yes - Harold Land0 +The Kinks - The Hard Way0 +Arctic Monkeys - Piledriver Waltz0 +Dead Boys - All This And More0 +Noel Gallagher - The Death of You and Me0 +Superchunk - Smarter Hearts0 +Queen - Keep Yourself Alive (Live)0 +G-Dragon - Hello (feat. 다라)0 +L'Arc~en~Ciel - Link -KISS Mix-0 +Jane's Addiction - My Time0 +Talking Heads - First Week Last Week... Carefree0 +Big Bang - 声をきかせて -Acoustic Version-0 +Marvin Gaye - God Is Love (B-Side Version)0 +Marvin Gaye - Sad Tomorrows (B-Side Version)0 +The Evens - Cache Is Empty0 +Big Bang - Wonderful0 +KAT-TUN - 喜びの歌0 +Superchunk - Why Do You Have to Put a Date on Everything0 +Noel Gallagher - AKA... What a Life!0 +Talking Heads - I Zimbra [Live]0 +SHINee - 사랑의 길 (Love's Way)0 +The Kinks - Lincoln County0 +John Frusciante - Ah Yom0 +Lemon Demon - Kitten Is Angry0 +Talking Heads - The Great Curve [Live]0 +Lô Borges - O Caçador0 +Donots - Superhero0 +The Kinks - Get Up0 +Why? - Women Eye, "NO."0 +Eluphant - Bye Bye Bike0 +Neil Young - Lotta Love0 +The Beach Boys - When Girls Get Together0 +Screeching Weasel - Get Off My Back0 +태양 - 니가 잠든 후에0 +Hall & Oates - Abandoned Luncheonette0 +Love - Laughing Stock [Single B-Side]0 +The Kinks - Berkeley Mews (stereo)0 +[spunge] - Live Another Day0 +Blackbird Blackbird - Hawaii (Niva remix)0 +Paul Weller - Starlite0 +David Bowie - What In The World (live)0 +SHINee - Stand By Me0 +Paul McCartney - Blue Jean Bop0 +林俊傑 - Baby Baby0 +Paul McCartney - Honey Hush0 +The Beach Boys - Here She Comes0 +Todd Rundgren - Long Flowing Robe0 +The Kinks - Lavender Hill0 +The Immortals - Techno-Syndrome 7'' Mix0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/cheers me up/cheers me up_title.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Looking Around +Harold Land +The Hard Way +Piledriver Waltz +All This And More +The Death of You and Me +Smarter Hearts +Keep Yourself Alive (Live) +Dragon - Hello (feat. 다라) +Link -KISS Mix- +My Time +First Week Last Week... Carefree +声をきかせて -Acoustic Version- +God Is Love (B-Side Version) +Sad Tomorrows (B-Side Version) +Cache Is Empty +Wonderful +TUN - 喜びの歌 +Why Do You Have to Put a Date on Everything +AKA... What a Life! +I Zimbra [Live] +사랑의 길 (Love's Way) +Lincoln County +Ah Yom +Kitten Is Angry +The Great Curve [Live] +O Caçador +Superhero +Get Up +Women Eye, "NO." +Bye Bye Bike +Lotta Love +When Girls Get Together +Get Off My Back +니가 잠든 후에 +Abandoned Luncheonette +Laughing Stock [Single B-Side] +Berkeley Mews (stereo) +Live Another Day +Hawaii (Niva remix) +Starlite +What In The World (live) +Stand By Me +Blue Jean Bop +Baby Baby +Honey Hush +Here She Comes +Long Flowing Robe +Lavender Hill +Techno-Syndrome 7'' Mix
Binary file Yading/wake me up/dont wake me up/(David Bowie I Love You) Since I Was Six.wav has changed
Binary file Yading/wake me up/dont wake me up/Ladies And Gentlemen We Are Floating In Space.wav has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/dont wake me up/dont wake me up_artist.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,36 @@ +Black Rebel Motorcycle Club +Mark Lanegan +Black Rebel Motorcycle Club +Black Rebel Motorcycle Club +Black Rebel Motorcycle Club +Isobel Campbell & Mark Lanegan +Bright Eyes +Sparklehorse +Black Rebel Motorcycle Club +Sparklehorse +Black Rebel Motorcycle Club +Black Rebel Motorcycle Club +The Raveonettes +Eels +The Brian Jonestown Massacre +Sparklehorse +Iron & Wine +M83 +Sparklehorse +Sparklehorse +Four Tet +Radiohead +Spiritualized +Tortoise & Bonnie 'Prince' Billy +The Battle of Land and Sea +Soulsavers +Thurston Moore +Chris Brown +Chris Brown +Chris Brown feat. Big Sean & Wiz Khalifa +Chris Brown +Chris Brown +Chris Brown +Chris Brown +Chris Brown +Chris Brown
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/dont wake me up/dont wake me up_results.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,36 @@ +Black Rebel Motorcycle Club - Pretend0 +Mark Lanegan - Can't catch the Train0 +Black Rebel Motorcycle Club - Am I Only0 +Black Rebel Motorcycle Club - Mercy0 +Black Rebel Motorcycle Club - Gospel Song0 +Isobel Campbell & Mark Lanegan - It's Hard to Kill a Bad Thing0 +Bright Eyes - Lua0 +Sparklehorse - Some Sweet Day0 +Black Rebel Motorcycle Club - All You Do Is Talk0 +Sparklehorse - Apple Bed0 +Black Rebel Motorcycle Club - Salvation0 +Black Rebel Motorcycle Club - Open Invitation0 +The Raveonettes - The Heavens0 +Eels - Bride of Theme From Blinking Lights0 +The Brian Jonestown Massacre - (David Bowie I Love You) Since I Was Six0 +Sparklehorse - Morning Hollow0 +Iron & Wine - Such Great Heights0 +M83 - I'm Happy, She Said0 +Sparklehorse - It's a Wonderful Life0 +Sparklehorse - Gold Day0 +Four Tet - Hilarious Movie of the 90's0 +Radiohead - Nice Dream0 +Spiritualized - Ladies And Gentlemen We Are Floating in Space0 +Tortoise & Bonnie 'Prince' Billy - Pancho0 +The Battle of Land and Sea - you are a sailor0 +Soulsavers - Spiritual0 +Thurston Moore - Honest James0 +Chris Brown - Sweet Love0 +Chris Brown - Don't Wake Me Up0 +Chris Brown feat. Big Sean & Wiz Khalifa - Till I Die (Main Version)0 +Chris Brown - Don't Wake Me Up (Free School/William Orbit Mix)0 +Chris Brown - Don't Wake Me Up (dBerrie Remix)0 +Chris Brown - Don't Wake Me Up (TheFatRat Remix)0 +Chris Brown - Don't Wake Me Up (DJ White Shadow Remix)0 +Chris Brown - Don't Wake Me Up (Clinton Sparks Remix)0 +Chris Brown - Don't Wake Me Up (Panic City Remix Radio Edit)0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/dont wake me up/dont wake me up_title.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,36 @@ +Pretend +Can't catch the Train +Am I Only +Mercy +Gospel Song +It's Hard to Kill a Bad Thing +Lua +Some Sweet Day +All You Do Is Talk +Apple Bed +Salvation +Open Invitation +The Heavens +Bride of Theme From Blinking Lights +(David Bowie I Love You) Since I Was Six +Morning Hollow +Such Great Heights +I'm Happy, She Said +It's a Wonderful Life +Gold Day +Hilarious Movie of the 90's +Nice Dream +Ladies And Gentlemen We Are Floating in Space +Pancho +you are a sailor +Spiritual +Honest James +Sweet Love +Don't Wake Me Up +Till I Die (Main Version) +Don't Wake Me Up (Free School/William Orbit Mix) +Don't Wake Me Up (dBerrie Remix) +Don't Wake Me Up (TheFatRat Remix) +Don't Wake Me Up (DJ White Shadow Remix) +Don't Wake Me Up (Clinton Sparks Remix) +Don't Wake Me Up (Panic City Remix Radio Edit)
Binary file Yading/wake me up/lift me up/Days of the Ring [Featuring Annie Lennox Performing "Into The West"] (Album Version).wav has changed
Binary file Yading/wake me up/lift me up/Everything's Not Lost (Includes Hidden Track 'Life Is For Living').wav has changed
Binary file Yading/wake me up/lift me up/Get Up (I Feel Like Being A) Sex Machine (Live).wav has changed
Binary file Yading/wake me up/lift me up/I Am (Recollection Album Version) (2002 Digital Remaster).wav has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/lift me up/lift me up_artist.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Thriving Ivory +Little Comets +Kate Voegele +中鶴純一 +中鶴純一 +Moby +Lemon Jelly +Tikal +The Cab +New Mjøndalen Disco Swingers +Нина Николина +Howard Shore +Red 5 +Mika +Celtic Bagpipes +SMILETRON +Solaris BC +Ayahuasca +Michael Jackson +Jimmy Eat World +Christina Aguilera +Moby +Tramaine Hawkins +Ellis Island Sound +Kim Hiorthøy +James Brown +Jars Of Clay +Conjure One +Conjure One +Matmos +Torcuato Mariano +Nichole Nordeman +Christina Aguilera +Iszoloscope +Eurovision 2009 +Bruce Springsteen +Interpol +Coldplay +Alphabeat +VNV Nation +Howard Shore +Mugison +Jars Of Clay +TeeBee +Jimmy Cliff +Howard Shore +Air +Puscifer +Muse +Jimmy Cliff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/lift me up/lift me up_results.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Thriving Ivory - For Heaven's Sake0 +Little Comets - Joanna0 +Kate Voegele - Lift Me Up0 +中鶴純一 - Fearless Eyes0 +中鶴純一 - The Blade Seeker0 +Moby - Lift Me Up0 +Lemon Jelly - '95 (Make Things Right)0 +Tikal - Crystal Wind0 +The Cab - Intoxicated0 +New Mjøndalen Disco Swingers - Eurodans0 +Нина Николина - Ти0 +Howard Shore - The Breaking of the Fellowship & End Titles (Film version)0 +Red 5 - Lift Me Up (radio version)0 +Mika - Any Other World0 +Celtic Bagpipes - Hymn to the Sea0 +SMILETRON - This Is Who We Are (Monster! Monster! Remix)0 +Solaris BC - 5.10 +Ayahuasca - Mics0 +Michael Jackson - Smooth Criminal0 +Jimmy Eat World - The Middle0 +Christina Aguilera - Lift Me Up0 +Moby - AudioTrack 040 +Tramaine Hawkins - Lift Me Up (Tramaine Live Album Version)0 +Ellis Island Sound - Cuckoo Hill0 +Kim Hiorthøy - Going Down [Ostete Jantemix]0 +James Brown - Get Up (I Feel Like Being a) Sex Machine0 +Jars Of Clay - Flood0 +Conjure One - Center of the Sun (Solarstone's Chilled Out remix)0 +Conjure One - Center Of The Sun0 +Matmos - YTTE (Yield to Total Elation)0 +Torcuato Mariano - Another Day in Luanda0 +Nichole Nordeman - I Am (Recollection Album Version) (2002 Digital Remaster)0 +Christina Aguilera - Lift Me Up (Live)0 +Iszoloscope - This Monstrosity Is Part of My Fibric0 +Eurovision 2009 - Portugal - Flor-de-Lis - Todas as ruas do amor0 +Bruce Springsteen - Paradise0 +Interpol - Hands Away0 +Coldplay - Everything's Not Lost0 +Alphabeat - The Spell0 +VNV Nation - Perpetual0 +Howard Shore - The Return of the King / Finale0 +Mugison - Scrap Yard0 +Jars Of Clay - Flood (Live From The Eleventh Hour Tour)0 +TeeBee - Quiet Moment0 +Jimmy Cliff - Wild World0 +Howard Shore - Into the West0 +Air - Ce Matin-là0 +Puscifer - Trekka (Sean Beaven Mix)0 +Muse - Con-Science0 +Jimmy Cliff - I Can See Clearly Now0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/lift me up/lift me up_title.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +For Heaven's Sake +Joanna +Lift Me Up +Fearless Eyes +The Blade Seeker +Lift Me Up +'95 (Make Things Right) +Crystal Wind +Intoxicated +Eurodans +Ти +The Breaking of the Fellowship & End Titles (Film version) +Lift Me Up (radio version) +Any Other World +Hymn to the Sea +This Is Who We Are (Monster! Monster! Remix) +5.1 +Mics +Smooth Criminal +The Middle +Lift Me Up +AudioTrack 04 +Lift Me Up (Tramaine Live Album Version) +Cuckoo Hill +Going Down [Ostete Jantemix] +Get Up (I Feel Like Being a) Sex Machine +Flood +Center of the Sun (Solarstone's Chilled Out remix) +Center Of The Sun +YTTE (Yield to Total Elation) +Another Day in Luanda +I Am (Recollection Album Version) (2002 Digital Remaster) +Lift Me Up (Live) +This Monstrosity Is Part of My Fibric +Portugal - Flor-de-Lis - Todas as ruas do amor +Paradise +Hands Away +Everything's Not Lost +The Spell +Perpetual +The Return of the King / Finale +Scrap Yard +Flood (Live From The Eleventh Hour Tour) +Quiet Moment +Wild World +Into the West +Ce Matin-là +Trekka (Sean Beaven Mix) +Con-Science +I Can See Clearly Now
Binary file Yading/wake me up/pick me up/Beautiful Queensland Goodbye Melbourne Town Have You Ever Been to See Kings Cross Patsy Fagan.wav has changed
Binary file Yading/wake me up/pick me up/Effervescing Elephant (2010 Digital Remaster).wav has changed
Binary file Yading/wake me up/pick me up/Freddy Feel Good's Funky Little Four-Piece Band.wav has changed
Binary file Yading/wake me up/pick me up/I Beg Your Pardon (I Never Promised You A Rose Garden) (LP Version).wav has changed
Binary file Yading/wake me up/pick me up/Rock Me Amadeus (Ihn liebten alle Frauen...) - ( Live Version ).wav has changed
Binary file Yading/wake me up/pick me up/Say Hello, Wave Goodbye (feat. Marc Almond).wav has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/pick me up/pick me up_artist.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +NEEDTOBREATHE +The Holloways +Falco +Alphabeat +John Frusciante +Jennifer Hudson +O +Stacy Clark +Sara Groves +Marc Almond +Jon Brion +Blessid Union Of Souls +Elliott Smith +Mouthwash +Emilia de Poret +A Perfect Kiss +The Common Ground Band +Mariah Carey +Chinatown +Sash! +The Holloways +Korn +Mark Knopfler +Philip Glass +Miami Horror +Brian Simpson +Vangelis +Kon Kan +The Pointer Sisters +Pop Levi +Marc Almond +Sara Groves +Yann Tiersen +Jennifer Lopez +Sébastien Tellier +a +Erasure +Boys Night Out +Syd Barrett +Paul McCartney +Émilie Simon +Book Of Love +Mott the Hoople +Shadowy Men On A Shadowy Planet +The Four Tops +Lily Allen +Jerry Lee Lewis +Van Morrison +Information Society +Interpol
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/pick me up/pick me up_results.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +NEEDTOBREATHE - White Fences0 +The Holloways - Under a Cloud0 +Falco - Rock Me Amadeus (Ihn liebten alle Frauen...)0 +Alphabeat - The Spell0 +John Frusciante - Prostution song0 +Jennifer Hudson - I Remember Me0 +O-Town - Been Around The World0 +Stacy Clark - White Lies0 +Sara Groves - What I Thought I Wanted0 +Marc Almond - Waifs And Strays0 +Jon Brion - Ruin My Day0 +Blessid Union Of Souls - End Of The World0 +Elliott Smith - Our Thing (Acoustic)0 +Mouthwash - We Evolve0 +Emilia de Poret - Pick Me Up0 +A Perfect Kiss - Equals0 +The Common Ground Band - Refuge0 +Mariah Carey - Make It Happen0 +Chinatown - Tour Assassine0 +Sash! - Just Around The Hill0 +The Holloways - Generator0 +Korn - Alone I Break0 +Mark Knopfler - What It Is0 +Philip Glass - The Poet Acts0 +Miami Horror - I Look to You0 +Brian Simpson - I Will Wait For You0 +Vangelis - End Titles From "Bladerunner"0 +Kon Kan - I Beg Your Pardon (I Never Promised You A Rose Garden) (Top Kat Dub)0 +The Pointer Sisters - Baby Come And Get It0 +Pop Levi - Sugar Assault Me0 +Marc Almond - Say Hello, Wave Goodbye0 +Sara Groves - Like a Skin0 +Yann Tiersen - La valse des monstres0 +Jennifer Lopez - Play0 +Sébastien Tellier - Divine0 +a-ha - Take On Me0 +Erasure - Love to Hate You0 +Boys Night Out - Relapsing0 +Syd Barrett - Effervescing Elephant0 +Paul McCartney - Maybe I'm Amazed0 +Émilie Simon - Rose Hybride De Thé0 +Book Of Love - Sunny Day (Single Remix)0 +Mott the Hoople - Roll Away The Stone0 +Shadowy Men On A Shadowy Planet - Having an Average Weekend0 +The Four Tops - Loco in Acapulco0 +Lily Allen - The Fear0 +Jerry Lee Lewis - What'd I Say0 +Van Morrison - Moondance0 +Information Society - Tomorrow0 +Interpol - Evil0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/pick me up/pick me up_title.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +White Fences +Under a Cloud +Rock Me Amadeus (Ihn liebten alle Frauen...) +The Spell +Prostution song +I Remember Me +Town - Been Around The World +White Lies +What I Thought I Wanted +Waifs And Strays +Ruin My Day +End Of The World +Our Thing (Acoustic) +We Evolve +Pick Me Up +Equals +Refuge +Make It Happen +Tour Assassine +Just Around The Hill +Generator +Alone I Break +What It Is +The Poet Acts +I Look to You +I Will Wait For You +End Titles From "Bladerunner" +I Beg Your Pardon (I Never Promised You A Rose Garden) (Top Kat Dub) +Baby Come And Get It +Sugar Assault Me +Say Hello, Wave Goodbye +Like a Skin +La valse des monstres +Play +Divine +ha - Take On Me +Love to Hate You +Relapsing +Effervescing Elephant +Maybe I'm Amazed +Rose Hybride De Thé +Sunny Day (Single Remix) +Roll Away The Stone +Having an Average Weekend +Loco in Acapulco +The Fear +What'd I Say +Moondance +Tomorrow +Evil
Binary file Yading/wake me up/songs to wake up to/Bust It Baby Part 2 [Feat. Ne-Yo] (Explicit Album Version).wav has changed
Binary file Yading/wake me up/songs to wake up to/Carbon Monoxide (Album Version) [Alt Mix].wav has changed
Binary file Yading/wake me up/songs to wake up to/Cemetry Gates (2008 Remastered Version).wav has changed
Binary file Yading/wake me up/songs to wake up to/Gallery Piece (Minitel Rose Remix).wav has changed
Binary file Yading/wake me up/songs to wake up to/Karneval der Tiere - Aquarium Aquarium.wav has changed
Binary file Yading/wake me up/songs to wake up to/Open the door softly (1968 Digital Remaster).wav has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/songs to wake up to/songs to wake up to_artist.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +The Raveonettes +Stereolab +Annuals +Mediæval Bæbes +of Montreal +Pygmy Lush +Eagles of Death Metal +mewithoutYou +Plies +Bayside +Mark Ronson +Lil' Wayne +JJ +Interpol +Guardian Bird +Regina Spektor +GZA Feat. Santi White +Underworld +New Order +Harvey Danger +Ingrid Michaelson +Gin Blossoms +Einstürzende Neubauten +Christian Death +ih +植松伸夫 +Eric Himan +The Good Life +Half +Cat Stevens +Tokyo Police Club +50 Cent +Best Coast +Oasis +Animal Collective +The B +Tilly and the Wall +The Smiths +Koushik +Camille Saint +Forever The Sickest Kids +U2 +Feist +Ne +The Prodigy +Grizzly Bear +Belle and Sebastian +Hayden Panettiere +Animal Collective +The Monkees
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/songs to wake up to/songs to wake up to_results.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +The Raveonettes - Young And Beautiful0 +Stereolab - Nothing to do With Me0 +Annuals - Complete or Completing0 +Mediæval Bæbes - Musa Venit Carmine0 +of Montreal - Gallery Piece (Minitel Rose Remix)0 +Pygmy Lush - Asphalt0 +Eagles of Death Metal - Chase the Devil0 +mewithoutYou - My Exit, Unfair0 +Plies - Bust It Baby Part 20 +Bayside - Paternal Reversal0 +Mark Ronson - Golden Skans (ft. Daniel Merriweather)0 +Lil' Wayne - 1000 Degrees0 +JJ - Are You Still In Vallda?0 +Interpol - 01 Untitled0 +Guardian Bird - Renaissance0 +Regina Spektor - Carbon Monoxide0 +GZA Feat. Santi White - Stay In Line0 +Underworld - Born Slippery (Nuxx)0 +New Order - Age of Consent0 +Harvey Danger - Happiness Writes White0 +Ingrid Michaelson - Die Alone0 +Gin Blossoms - Hey Jealousy0 +Einstürzende Neubauten - Youme & Meyou0 +Christian Death - Infans Vexatio0 +ih - Between Sheets (Imogen Heap)0 +植松伸夫 - Final Fantasy IV - Main Theme of Final Fantasy IV0 +Eric Himan - Open The Door0 +The Good Life - You're Not You0 +Half-Handed Cloud - Disaster Will Come Upon You and You Will Not Know How to Conjure It Away0 +Cat Stevens - Portobello Road0 +Tokyo Police Club - Citizens Of Tomorrow0 +50 Cent - 21 Questions0 +Best Coast - When I'm With You0 +Oasis - The Hindu Times0 +Animal Collective - Summertime Clothes0 +The B-52's - Pump0 +Tilly and the Wall - Patience, Babe0 +The Smiths - The Smiths - Cemetry Gates0 +Koushik - ifoundu0 +Camille Saint-Saëns - Aquarium0 +Forever The Sickest Kids - My Worst Nightmare0 +U2 - Beautiful Day0 +Feist - 1 2 3 40 +Ne-Yo - Closer0 +The Prodigy - Warrior's Dance0 +Grizzly Bear - Two Weeks0 +Belle and Sebastian - The Boy With the Arab Strap0 +Hayden Panettiere - Wake Up Call0 +Animal Collective - Grass0 +The Monkees - Daydream Believer0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/songs to wake up to/songs to wake up to_title.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Young And Beautiful +Nothing to do With Me +Complete or Completing +Musa Venit Carmine +Gallery Piece (Minitel Rose Remix) +Asphalt +Chase the Devil +My Exit, Unfair +Bust It Baby Part 2 +Paternal Reversal +Golden Skans (ft. Daniel Merriweather) +1000 Degrees +Are You Still In Vallda? +01 Untitled +Renaissance +Carbon Monoxide +Stay In Line +Born Slippery (Nuxx) +Age of Consent +Happiness Writes White +Die Alone +Hey Jealousy +Youme & Meyou +Infans Vexatio +Between Sheets (Imogen Heap) +Final Fantasy IV - Main Theme of Final Fantasy IV +Open The Door +You're Not You +Handed Cloud - Disaster Will Come Upon You and You Will Not Know How to Conjure It Away +Portobello Road +Citizens Of Tomorrow +21 Questions +When I'm With You +The Hindu Times +Summertime Clothes +52's - Pump +Patience, Babe +The Smiths - Cemetry Gates +ifoundu +Saëns - Aquarium +My Worst Nightmare +Beautiful Day +1 2 3 4 +Yo - Closer +Warrior's Dance +Two Weeks +The Boy With the Arab Strap +Wake Up Call +Grass +Daydream Believer
Binary file Yading/wake me up/wake me up before you go-go/Wake Me Up Before You Go-Go.wav has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake me up before you go-go/wake me up before you go-go_artist.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,2 @@ +Wham! +Alex Megane
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake me up before you go-go/wake me up before you go-go_results.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,2 @@ +Wham! - Wake Me Up Before You Go-Go0 +Alex Megane - Listen Feel Enjoy (Alex M. Radio Mix)0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake me up before you go-go/wake me up before you go-go_title.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,2 @@ +Wake Me Up Before You Go-Go +Listen Feel Enjoy (Alex M. Radio Mix)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake me up inside/wake me up inside_artist.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,2 @@ +Evanescence +Evanescence
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake me up inside/wake me up inside_results.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,2 @@ +Evanescence - Wake me up inside (GOOD COPY) w/ linkin park0 +Evanescence - Bring Me to Life0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake me up inside/wake me up inside_title.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,2 @@ +Wake me up inside (GOOD COPY) w/ linkin park +Bring Me to Life
Binary file Yading/wake me up/wake me up now/Cute Without the 'E' (Cut From the Team).wav has changed
Binary file Yading/wake me up/wake me up now/I Write Sins Not Tragedies (MTV VMA Performance 2006).wav has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake me up now/wake me up now_artist.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,30 @@ +Train +Beth Hart +Beth Hart +Beth Hart +Beth Hart +The Who +Detroit Women +Acda en de Munnik +James Morrison +Acda en de Munnik +The Tragically Hip +Acda en de Munnik +Taking Back Sunday +Train +Hanson +Blondie +Gavin DeGraw +Acda en de Munnik +Rascal Flatts +Papa Roach +Unwritten Law +Kelly Clarkson +GACKT +Teddy Geiger +Panic! At the Disco +Melissa Etheridge +Taylor Hicks +Train +Teddy Geiger +Jars Of Clay
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake me up now/wake me up now_results.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,30 @@ +Train - Get Out0 +Beth Hart - Immortal0 +Beth Hart - One Eyed Chicken0 +Beth Hart - Sick0 +Beth Hart - Crazy Kind Of Day0 +The Who - Baba O Reilly0 +Detroit Women - Leap of Faith0 +Acda en de Munnik - Niemand sterft0 +James Morrison - Under the Influence0 +Acda en de Munnik - Van de regels en het spel0 +The Tragically Hip - Born In The Water0 +Acda en de Munnik - De liefde voortaan0 +Taking Back Sunday - Cute Without the "E" (Cut From the Team)0 +Train - She's On Fire0 +Hanson - If Only0 +Blondie - Divine0 +Gavin DeGraw - I Don't Want To Be0 +Acda en de Munnik - Het regent zonnestralen0 +Rascal Flatts - Feels Like Today0 +Papa Roach - Scars0 +Unwritten Law - Save Me0 +Kelly Clarkson - Behind These Hazel Eyes0 +GACKT - Metamorphoze0 +Teddy Geiger - For You I Will (Confidence)0 +Panic! At the Disco - I Write Sins Not Tragedies0 +Melissa Etheridge - I Run For Life0 +Taylor Hicks - Soul Thing0 +Train - If I Can't Change Your Mind0 +Teddy Geiger - These Walls0 +Jars Of Clay - Work0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake me up now/wake me up now_title.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,30 @@ +Get Out +Immortal +One Eyed Chicken +Sick +Crazy Kind Of Day +Baba O Reilly +Leap of Faith +Niemand sterft +Under the Influence +Van de regels en het spel +Born In The Water +De liefde voortaan +Cute Without the "E" (Cut From the Team) +She's On Fire +If Only +Divine +I Don't Want To Be +Het regent zonnestralen +Feels Like Today +Scars +Save Me +Behind These Hazel Eyes +Metamorphoze +For You I Will (Confidence) +I Write Sins Not Tragedies +I Run For Life +Soul Thing +If I Can't Change Your Mind +These Walls +Work
Binary file Yading/wake me up/wake me up slowly/Elizabeth, You Were Born To Play That Part.wav has changed
Binary file Yading/wake me up/wake me up slowly/Healing Hands (RemasteredLP Version).wav has changed
Binary file Yading/wake me up/wake me up slowly/Hold Me Through The Night (LP Version).wav has changed
Binary file Yading/wake me up/wake me up slowly/How Can I Keep From Singing? (Remastered 2009).wav has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake me up slowly/wake me up slowly_artist.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Kenny Rogers +Beth Hart +De Poema's +Train +George Winston +Joshua Kadison +Five for Fighting +Veldhuis & Kemper +Rich Wyman +JW Roy +Keb' Mo' +Beth Hart +JW Roy +JW Roy +Joshua Kadison +Kenny Rogers +Craig Armstrong +George Winston +Sarah Bettens +Acda en de Munnik +Faith Hill +Eric Bibb +Marc Cohn +David Gray +Tori Amos +Acda en de Munnik +Keb' Mo' +Gavin DeGraw +Sarah McLachlan +Shakira +Marc Cohn +Acda en de Munnik +Alexi Murdoch +Vienna Teng +Charlotte Martin +Charlotte Martin +Charlotte Martin +Rascal Flatts +Bløf +Enya +Eva Cassidy +Ryan Adams +Evanescence +Vienna Teng +Ryan Adams +Trisha Yearwood +Racoon +Tyrone Wells +Lucie Silvas +k.d. lang
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake me up slowly/wake me up slowly_results.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Kenny Rogers - I Can't Unlove You0 +Beth Hart - Hold Me Through the Night0 +De Poema's - Zitten Voor De Blues0 +Train - Landmine0 +George Winston - Montana Glide0 +Joshua Kadison - The Gospel According To My Ol' Man0 +Five for Fighting - Superman (Live Acoustic)0 +Veldhuis & Kemper - Oud Geboren0 +Rich Wyman - This Liason0 +JW Roy - Kitchen Table Blues II0 +Keb' Mo' - Follow Me Up0 +Beth Hart - Rise again0 +JW Roy - Better Days0 +JW Roy - First Train Home0 +Joshua Kadison - Do You Know How Beautiful You Are0 +Kenny Rogers - Someone Somewhere Tonight0 +Craig Armstrong - Glasgow Love Theme0 +George Winston - Black Stallion0 +Sarah Bettens - Grey (Bonus Track)0 +Acda en de Munnik - Geen liedje0 +Faith Hill - If You Ask0 +Eric Bibb - Tall Cotton0 +Marc Cohn - Ghost Train0 +David Gray - Hold On To Nothing0 +Tori Amos - Have Yourself a Merry Little Christmas0 +Acda en de Munnik - Lopen tot de zon komt0 +Keb' Mo' - Every Morning0 +Gavin DeGraw - Belief0 +Sarah McLachlan - Stupid0 +Shakira - Underneath Your Clothes0 +Marc Cohn - Healing Hands0 +Acda en de Munnik - Morgen is ze weg0 +Alexi Murdoch - Orange Sky0 +Vienna Teng - Homecoming (Walter's Song)0 +Charlotte Martin - Your Armor0 +Charlotte Martin - Beautiful Life0 +Charlotte Martin - Sweet Chariot0 +Rascal Flatts - Skin0 +Bløf - Harder Dan Ik Hebben Kan0 +Enya - How Can I Keep From Singing?0 +Eva Cassidy - Time After Time0 +Ryan Adams - Desire0 +Evanescence - Lithium0 +Vienna Teng - Nothing Without You0 +Ryan Adams - Elizabeth, You Were Born To Play That Part0 +Trisha Yearwood - Georgia Rain0 +Racoon - Love You More0 +Tyrone Wells - All I Can Do0 +Lucie Silvas - Last Man Standing0 +k.d. lang - Hallelujah0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake me up slowly/wake me up slowly_title.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +I Can't Unlove You +Hold Me Through the Night +Zitten Voor De Blues +Landmine +Montana Glide +The Gospel According To My Ol' Man +Superman (Live Acoustic) +Oud Geboren +This Liason +Kitchen Table Blues II +Follow Me Up +Rise again +Better Days +First Train Home +Do You Know How Beautiful You Are +Someone Somewhere Tonight +Glasgow Love Theme +Black Stallion +Grey (Bonus Track) +Geen liedje +If You Ask +Tall Cotton +Ghost Train +Hold On To Nothing +Have Yourself a Merry Little Christmas +Lopen tot de zon komt +Every Morning +Belief +Stupid +Underneath Your Clothes +Healing Hands +Morgen is ze weg +Orange Sky +Homecoming (Walter's Song) +Your Armor +Beautiful Life +Sweet Chariot +Skin +Harder Dan Ik Hebben Kan +How Can I Keep From Singing? +Time After Time +Desire +Lithium +Nothing Without You +Elizabeth, You Were Born To Play That Part +Georgia Rain +Love You More +All I Can Do +Last Man Standing +Hallelujah
Binary file Yading/wake me up/wake me up when september ends/Are We The Waiting St. Jimmy (Album Version).wav has changed
Binary file Yading/wake me up/wake me up when september ends/Best Thing In Town (Album version).wav has changed
Binary file Yading/wake me up/wake me up when september ends/Blue Moon of Kentucky (Live).wav has changed
Binary file Yading/wake me up/wake me up when september ends/Bright Lights Bigger City (feat. Wiz Khalifa).wav has changed
Binary file Yading/wake me up/wake me up when september ends/Coming Clean (Album Version).wav has changed
Binary file Yading/wake me up/wake me up when september ends/Dry Ice (Album version).wav has changed
Binary file Yading/wake me up/wake me up when september ends/Extraordinary Girl Letterbomb (Album Version).wav has changed
Binary file Yading/wake me up/wake me up when september ends/Going To Pasalacqua (Live).wav has changed
Binary file Yading/wake me up/wake me up when september ends/Good Riddance (Time Of Your Life) [Live].wav has changed
Binary file Yading/wake me up/wake me up when september ends/Good Riddance [Time Of Your Life] (Album Version).wav has changed
Binary file Yading/wake me up/wake me up when september ends/Holiday (Feat. John Gallagher Jr., Stark Sands, Theo Stockman, Company) [Album Version].wav has changed
Binary file Yading/wake me up/wake me up when september ends/Holiday Boulevard Of Broken Dreams (Album Version).wav has changed
Binary file Yading/wake me up/wake me up when september ends/In The End (Album Version).wav has changed
Binary file Yading/wake me up/wake me up when september ends/Jesus Of Suburbia (I. Jesus Of Suburbia II. City Of The Damned III. I Don't Care IV. Dearly Beloved V. Tales Of Another Broken Home) [Feat. John Gallagher Jr., Michael Esper, Stark Sands, Mary Faber, Company] {Album Version}.wav has changed
Binary file Yading/wake me up/wake me up when september ends/Macy's Day Parade (Album Version).wav has changed
Binary file Yading/wake me up/wake me up when september ends/Poprocks & Coke (Album Version).wav has changed
Binary file Yading/wake me up/wake me up when september ends/Rocking Around The Christmas Tree Medley: Rocking Around The Christmas Tree Santa Claus Is Coming .wav has changed
Binary file Yading/wake me up/wake me up when september ends/Wake Me Up When September Ends (Feat. John Gallagher Jr., Michael Esper, Stark Sands, Company) [Album Version].wav has changed
Binary file Yading/wake me up/wake me up when september ends/Wake Me Up When September Ends (Live at Foxboro, MA 9 3 05).wav has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake me up when september ends/wake me up when september ends_artist.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Green Day +Cube Experience +Green Day +Green Day +Kevin Cryderman +Green Day +Screeching Weasel +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day +Green Day
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake me up when september ends/wake me up when september ends_results.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Green Day - Wake Me Up When September Ends0 +Cube Experience - Wake Me Up When September Ends0 +Green Day - American Idiot0 +Green Day - Give Me Novacaine/She's A Rebel0 +Kevin Cryderman - Parallel World0 +Green Day - Wake Me Up When September Ends (Live At Foxboro, MA 9/3/05)0 +Screeching Weasel - Kamala's Too Nice0 +Green Day - Whatsername0 +Green Day - Homecoming0 +Green Day - Are We The Waiting/St. Jimmy0 +Green Day - Jesus of Suburbia0 +Green Day - Extraordinary Girl/Letterbomb0 +Green Day - Holiday/Boulevard Of Broken Dreams0 +Green Day - Greenday - Holiday0 +Green Day - Green Day-St. Jimmy0 +Green Day - Poprock and Coke0 +Green Day - Give Me Nocacaine0 +Green Day - life during wartime0 +Green Day - Extradordinary Girl0 +Green Day - Hauskinka0 +Green Day - Green Day - Greenday Basketcase0 +Green Day - Good RiddanceTime of your life0 +Green Day - Bascketcase0 +Green Day - Chump20 +Green Day - Good Riddance (Time Of Your Life, Live)0 +Green Day - Going to Pasalaqua [Live]0 +Green Day - Walking A Lone0 +Green Day - Green Day - American Idiot - track 110 +Green Day - rockyoulike0 +Green Day - Green Day - American Idiot - track 130 +Green Day - Time Of Your Life (Good Riddan0 +Green Day - Basketcase (Leeds Festival 2004)0 +Green Day - Blue Moon of Kentucky (Live)0 +Green Day - Rocking' Around the Christmas Tree0 +Green Day - Geek Stink Beath0 +Green Day - Blitzkrieg bop (cover ramones)0 +Green Day - Green Day / Wake Me Up When September Ends0 +Green Day - 09 - Green Day - Android0 +Green Day - 21 - Macy's Day Parade0 +Green Day - City of Lights0 +Green Day - Comin Clean0 +Green Day - C Yo Yus - Sometimes I Don't Mind - (Fifteen Cover) (Live - WFMU)0 +Green Day - Green Day - 14 Best Thing In Town0 +Green Day - Are we the waiting [Bullet In A Bible]0 +Green Day - Greenday - In the End0 +Green Day - Longview [Bullet In A Bible]0 +Green Day - Dry Ice (live)0 +Green Day - Brain stew [Bullet In A Bible]0 +Green Day - Jinx0 +Green Day - King for a day/Shout [Bullet In A Bible]0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake me up when september ends/wake me up when september ends_title.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Wake Me Up When September Ends +Wake Me Up When September Ends +American Idiot +Give Me Novacaine/She's A Rebel +Parallel World +Wake Me Up When September Ends (Live At Foxboro, MA 9/3/05) +Kamala's Too Nice +Whatsername +Homecoming +Are We The Waiting/St. Jimmy +Jesus of Suburbia +Extraordinary Girl/Letterbomb +Holiday/Boulevard Of Broken Dreams +Greenday - Holiday +Green Day-St. Jimmy +Poprock and Coke +Give Me Nocacaine +life during wartime +Extradordinary Girl +Hauskinka +Green Day - Greenday Basketcase +Good RiddanceTime of your life +Bascketcase +Chump2 +Good Riddance (Time Of Your Life, Live) +Going to Pasalaqua [Live] +Walking A Lone +Green Day - American Idiot - track 11 +rockyoulike +Green Day - American Idiot - track 13 +Time Of Your Life (Good Riddan +Basketcase (Leeds Festival 2004) +Blue Moon of Kentucky (Live) +Rocking' Around the Christmas Tree +Geek Stink Beath +Blitzkrieg bop (cover ramones) +Green Day / Wake Me Up When September Ends +09 - Green Day - Android +21 - Macy's Day Parade +City of Lights +Comin Clean +C Yo Yus - Sometimes I Don't Mind - (Fifteen Cover) (Live - WFMU) +Green Day - 14 Best Thing In Town +Are we the waiting [Bullet In A Bible] +Greenday - In the End +Longview [Bullet In A Bible] +Dry Ice (live) +Brain stew [Bullet In A Bible] +Jinx +King for a day/Shout [Bullet In A Bible]
Binary file Yading/wake me up/wake me up/Wake Me Up When September Ends (Feat. John Gallagher Jr., Michael Esper, Stark Sands, Company) [Album Version].wav has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake me up/wake me up_artist.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Latterman +Evanescence +Zebrahead +Green Day +Atlantic At Pacific +Matt & Kim +Max Sedgley +Evanescence +M.I.A. +Zebrahead +Aloha From Hell +The Clash +The Chemical Brothers +Lisandro Aristimuño +The Chemical Brothers +Ash +ESG +Ben Lee +Zongamin +Radiohead +Andrew Bird +M.I.A. +We Are Scientists +M.I.A. +The Raconteurs +Missy Higgins +Wolfmother +Wolfmother +Radiohead +The Beatles +Queen +The Books +Ed Sheeran +Ed Sheeran +Ed Sheeran +Ed Sheeran +Ed Sheeran +Ed Sheeran +Ed Sheeran +Ed Sheeran +Ed Sheeran +Ed Sheeran +Ed Sheeran +Ed Sheeran +Ed Sheeran +Ed Sheeran +Ed Sheeran +Ed Sheeran +Myoon +Myoon
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake me up/wake me up_results.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Latterman - We Work the Night Shift0 +Evanescence - Wake Me Up Inside (remix)0 +Zebrahead - Walk Away0 +Green Day - Wake Me Up When September Ends0 +Atlantic At Pacific - Proud of you0 +Matt & Kim - Daylight0 +Max Sedgley - Happy (Make You Happy)0 +Evanescence - Bring Me to Life0 +M.I.A. - Bucky Done Gun0 +Zebrahead - Anthem0 +Aloha From Hell - Wake Me Up0 +The Clash - Police on My Back0 +The Chemical Brothers - Block Rockin' Beats0 +Lisandro Aristimuño - Es todo lo que tengo y es todo lo que hay0 +The Chemical Brothers - Out of Control0 +Ash - Burn Baby Burn0 +ESG - My Street0 +Ben Lee - Catch My Disease (That's The Way I Like It)0 +Zongamin - Bongo Song0 +Radiohead - Separator0 +Andrew Bird - Fitz and the Dizzyspells0 +M.I.A. - Galang0 +We Are Scientists - Nobody Move, Nobody Get Hurt0 +M.I.A. - Amazon0 +The Raconteurs - Broken Boy Soldier0 +Missy Higgins - Warm Whispers0 +Wolfmother - Woman0 +Wolfmother - Where Eagles Have Been0 +Radiohead - The Bends0 +The Beatles - I'm Only Sleeping0 +Queen - Don't Stop Me Now0 +The Books - Getting the Done Job0 +Ed Sheeran - Give Me Love / The Parting Glass0 +Ed Sheeran - Gold Rush0 +Ed Sheeran - Kiss Me0 +Ed Sheeran - Drunk0 +Ed Sheeran - Small Bump0 +Ed Sheeran - U.N.I.0 +Ed Sheeran - Grade 80 +Ed Sheeran - Wake Me Up0 +Ed Sheeran - Lego House0 +Ed Sheeran - Little Bird0 +Ed Sheeran - The A Team0 +Ed Sheeran - Sunburn0 +Ed Sheeran - The City0 +Ed Sheeran - This0 +Ed Sheeran - Autumn Leaves0 +Ed Sheeran - You Need Me, I Don't Need You0 +Myoon - Wake Me Up0 +Myoon - So Fast0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake me up/wake me up_title.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +We Work the Night Shift +Wake Me Up Inside (remix) +Walk Away +Wake Me Up When September Ends +Proud of you +Daylight +Happy (Make You Happy) +Bring Me to Life +Bucky Done Gun +Anthem +Wake Me Up +Police on My Back +Block Rockin' Beats +Es todo lo que tengo y es todo lo que hay +Out of Control +Burn Baby Burn +My Street +Catch My Disease (That's The Way I Like It) +Bongo Song +Separator +Fitz and the Dizzyspells +Galang +Nobody Move, Nobody Get Hurt +Amazon +Broken Boy Soldier +Warm Whispers +Woman +Where Eagles Have Been +The Bends +I'm Only Sleeping +Don't Stop Me Now +Getting the Done Job +Give Me Love / The Parting Glass +Gold Rush +Kiss Me +Drunk +Small Bump +U.N.I. +Grade 8 +Wake Me Up +Lego House +Little Bird +The A Team +Sunburn +The City +This +Autumn Leaves +You Need Me, I Don't Need You +Wake Me Up +So Fast
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake up call/wake up call_artist.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Hawthorne Heights +Maroon 5 +Fonzie +AK4711 +Maroon 5 +Hawthorne Heights +Hayden Panettiere +A Silver Mt. Zion +Open System +FISH +Relient K +Miles Davis +Operation Ivy +Grinderman +Chet Baker +Weezer +Three Days Grace +Angélique Kidjo +Florence + the Machine +Backstreet Boys +Minor Threat +Blue Knights +Blue Knights +Blue Knights +Blue Knights +Blue Knights +Blue Knights +Blue Knights +Blue Knights +Blue Knights +Blue Knights +Blue Knights +Blue Knights +Blue Knights +John Mayall +John Mayall +John Mayall +John Mayall +John Mayall +John Mayall +John Mayall +John Mayall +John Mayall +John Mayall +Spit Ya Teeth +Spit Ya Teeth +Spit Ya Teeth +Spit Ya Teeth +Spit Ya Teeth +Spit Ya Teeth
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake up call/wake up call_results.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Hawthorne Heights - Wake Up Call0 +Maroon 5 - Wake Up Call feat. Mary J. Blige (Mark Ronson)0 +Fonzie - Gotta Get Away0 +AK4711 - Rock0 +Maroon 5 - Wake Up Call0 +Hawthorne Heights - Wake Up Call (Instrumental Version)0 +Hayden Panettiere - Wake Up Call0 +A Silver Mt. Zion - The Triumph of Our Tired Eyes0 +Open System - Rise and Shine0 +FISH - Plague of Ghosts: (6) Wake Up Call (Make It Happen)0 +Relient K - Wake up Call ()0 +Miles Davis - Freddie Freeloader0 +Operation Ivy - Sleep Long0 +Grinderman - Get It On0 +Chet Baker - I Fall in Love Too Easily0 +Weezer - Troublemaker0 +Three Days Grace - Animal I Have Become0 +Angélique Kidjo - Les Enfants Perdus (Single Version)0 +Florence + the Machine - Dog Days Are Over0 +Backstreet Boys - The Call0 +Minor Threat - Filler0 +Blue Knights - Wake Up Call0 +Blue Knights - California Drive0 +Blue Knights - Key West0 +Blue Knights - Traffic Lights0 +Blue Knights - Tropical Night0 +Blue Knights - Highway Of Passion0 +Blue Knights - Oriental Sea0 +Blue Knights - Vicious Love0 +Blue Knights - Streetwalk0 +Blue Knights - Boogie Slide0 +Blue Knights - Yelow Night0 +Blue Knights - Nightfall0 +Blue Knights - Missing You0 +John Mayall - Light The Fuse0 +John Mayall - Ke Up Call (Featuring Mavis Staples)0 +John Mayall - Ain't That Lovin' You Baby0 +John Mayall - Maydell0 +John Mayall - Loaded Dice0 +John Mayall - Not At Home (Featuring Mick Taylor)0 +John Mayall - Ture's-Disappearing John-Mayall0 +John Mayall - Undercover Agent For The Blues0 +John Mayall - I Could Cry0 +John Mayall - Mail Order Mystics0 +Spit Ya Teeth - Intro0 +Spit Ya Teeth - Wont Give In0 +Spit Ya Teeth - Price to Pay0 +Spit Ya Teeth - Born to Lose0 +Spit Ya Teeth - Bullet For My Enemy0 +Spit Ya Teeth - Lost For My Words0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake up call/wake up call_title.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Wake Up Call +Wake Up Call feat. Mary J. Blige (Mark Ronson) +Gotta Get Away +Rock +Wake Up Call +Wake Up Call (Instrumental Version) +Wake Up Call +The Triumph of Our Tired Eyes +Rise and Shine +Plague of Ghosts: (6) Wake Up Call (Make It Happen) +Wake up Call () +Freddie Freeloader +Sleep Long +Get It On +I Fall in Love Too Easily +Troublemaker +Animal I Have Become +Les Enfants Perdus (Single Version) +Dog Days Are Over +The Call +Filler +Wake Up Call +California Drive +Key West +Traffic Lights +Tropical Night +Highway Of Passion +Oriental Sea +Vicious Love +Streetwalk +Boogie Slide +Yelow Night +Nightfall +Missing You +Light The Fuse +Ke Up Call (Featuring Mavis Staples) +Ain't That Lovin' You Baby +Maydell +Loaded Dice +Not At Home (Featuring Mick Taylor) +Ture's-Disappearing John-Mayall +Undercover Agent For The Blues +I Could Cry +Mail Order Mystics +Intro +Wont Give In +Price to Pay +Born to Lose +Bullet For My Enemy +Lost For My Words
Binary file Yading/wake me up/wake up music/Crystal Frontier (Buscemi Instrumental Remix) (Bonus Track).wav has changed
Binary file Yading/wake me up/wake up music/Las cosas que no me espero (duet with Carlos Baute).wav has changed
Binary file Yading/wake me up/wake up music/Medley : Prendo te - She (uguale a lei) - Cinque giorni - Strani amori (live).wav has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake up music/wake up music_artist.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +The Suicide Machines +Kari Rueslåtten +Eximinds +Laura Pausini +Sister Hazel +Laura Pausini +Against All Authority +Laura Pausini +Screeching Weasel +Sahara Hotnights +Guster + +Kingsley +Wohnout +Wohnout +OutKast +Laura Pausini +Laura Pausini +Laura Pausini +Laura Pausini +Laura Pausini +The Weather Girls +Oingo Boingo +Laura Pausini +Laura Pausini +Laura Pausini +Laura Pausini +Laura Pausini +The Deadfly Ensemble +Tuxedomoon +Laura Pausini +R.E.M. +Laura Pausini +Laura Pausini +Laura Pausini +Laura Pausini +Laura Pausini +Wohnout +Shooglenifty +Laura Pausini +Laura Pausini +Laura Pausini +Laura Pausini +Jamiroquai +Great Big Sea +Club X Project +Łzy +Calexico +ZAKRęT +Vanessa St. James & Lou Reed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake up music/wake up music_results.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +The Suicide Machines - Face Values0 +Kari Rueslåtten - Vintersol0 +Eximinds - Sunrise 6AM0 +Laura Pausini - Emergencia De Amor0 +Sister Hazel - Life Got in the Way0 +Laura Pausini - ¿tú con qué sueñas?0 +Against All Authority - We Don't Need You0 +Laura Pausini - Un'emergenza d'amore0 +Screeching Weasel - Gotta Girlfriend0 +Sahara Hotnights - Who Do You Dance For?0 +Guster - The New Underground0 +-M- - Le complexe du corn flakes0 +Kingsley - Popcorn0 +Wohnout - Rayda0 +Wohnout - Festivalová0 +OutKast - Call The Law (Main Version - Explicit)0 +Laura Pausini - Así Celeste0 +Laura Pausini - Looking For An Angel0 +Laura Pausini - Mi Respuesta0 +Laura Pausini - Prendo Te0 +Laura Pausini - Gente (Ordinary People)0 +The Weather Girls - It's Raining Man0 +Oingo Boingo - On The Outside0 +Laura Pausini - Entre tú y Mil Mares0 +Laura Pausini - Jamás Abandoné0 +Laura Pausini - La felicità0 +Laura Pausini - Quédate Esta Noche0 +Laura Pausini - Benvenuto0 +The Deadfly Ensemble - Horse On the Moor0 +Tuxedomoon - Baron Brown0 +Laura Pausini - La mia risposta0 +R.E.M. - Shiny Happy People0 +Laura Pausini - Felicidad0 +Laura Pausini - Las Cosas Que No Me Espero0 +Laura Pausini - Una Historia Seria0 +Laura Pausini - ángeles en el cielo0 +Laura Pausini - Me Quedo0 +Wohnout - Kulibrci0 +Shooglenifty - The Pipe Tunes0 +Laura Pausini - Che bene mi fai0 +Laura Pausini - Surrender0 +Laura Pausini - I Need Love0 +Laura Pausini - Hace Tiempo0 +Jamiroquai - So Good to Feel Real (hidden track)0 +Great Big Sea - Sea of No Cares (Live)0 +Club X Project - Amore0 +Łzy - Przebój (kaprys amoll)0 +Calexico - Crystal Frontier (Buscemi Instrumental mix)0 +ZAKRęT - Szał0 +Vanessa St. James & Lou Reed - Sunday Morning Casino (Radio Edit)0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake up music/wake up music_title.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Face Values +Vintersol +Sunrise 6AM +Emergencia De Amor +Life Got in the Way +¿tú con qué sueñas? +We Don't Need You +Un'emergenza d'amore +Gotta Girlfriend +Who Do You Dance For? +The New Underground +M- - Le complexe du corn flakes +Popcorn +Rayda +Festivalová +Call The Law (Main Version - Explicit) +Así Celeste +Looking For An Angel +Mi Respuesta +Prendo Te +Gente (Ordinary People) +It's Raining Man +On The Outside +Entre tú y Mil Mares +Jamás Abandoné +La felicità +Quédate Esta Noche +Benvenuto +Horse On the Moor +Baron Brown +La mia risposta +Shiny Happy People +Felicidad +Las Cosas Que No Me Espero +Una Historia Seria +ángeles en el cielo +Me Quedo +Kulibrci +The Pipe Tunes +Che bene mi fai +Surrender +I Need Love +Hace Tiempo +So Good to Feel Real (hidden track) +Sea of No Cares (Live) +Amore +Przebój (kaprys amoll) +Crystal Frontier (Buscemi Instrumental mix) +Szał +Sunday Morning Casino (Radio Edit)
Binary file Yading/wake me up/wake up song/Living Well Is The Best Revenge (Album Version).wav has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake up song/wake up song_artist.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Manal +The pAper chAse +Shy Child +Jonas Brothers +The Mars Volta +Demons & Wizards +MUTEMATH +Jason Mraz +Sean Kingston +Booka Shade +Gwen Stefani +Philip Glass +Muse +...And You Will Know Us by the Trail of Dead +Britney Spears +cutzero +The Ting Tings +Black Eyed Peas +Belle and Sebastian +The Beatles +Spice Girls +Billy Idol +Natalie Imbruglia +Shakira +Aerosmith +Beastie Boys +Red Hot Chili Peppers +Kaiser Chiefs +Justice +Madvillain +Corinne Bailey Rae +Pearl Jam +R.E.M. +The Dresden Dolls +The Pretenders +The Rolling Stones +Eels +Edguy +The Chemical Brothers +Natasha Bedingfield +Los Campesinos! +Katy Perry +The Chemical Brothers +All Time Low +Weezer +The Young Punx +The Young Punx +The Young Punx +The Young Punx +The Young Punx
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake up song/wake up song_results.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Manal - Avenida Rivadavia0 +The pAper chAse - Now, We Just Slowly Circle the Draining Fish Bowl0 +Shy Child - Dawn to Dust0 +Jonas Brothers - Live To Party0 +The Mars Volta - A Plague Upon Your Hissing0 +Demons & Wizards - Crimson King0 +MUTEMATH - Spotlight (Twilight Mix)0 +Jason Mraz - I'm Yours0 +Sean Kingston - Fire Burning0 +Booka Shade - the birds and the beats at the window0 +Gwen Stefani - Hollaback Girl0 +Philip Glass - Morning Passages0 +Muse - Supermassive Black Hole0 +...And You Will Know Us by the Trail of Dead - Summer 910 +Britney Spears - Circus0 +cutzero - good morning mandy(4mygirl)0 +The Ting Tings - Great DJ0 +Black Eyed Peas - Pump It0 +Belle and Sebastian - Mayfly0 +The Beatles - Lady Madonna0 +Spice Girls - Wannabe0 +Billy Idol - Mony Mony0 +Natalie Imbruglia - Torn0 +Shakira - Un Poco de Amor0 +Aerosmith - Jaded0 +Beastie Boys - Intergalactic0 +Red Hot Chili Peppers - Around the World0 +Kaiser Chiefs - Na Na Na Na Naa0 +Justice - D.A.N.C.E.0 +Madvillain - Great Day0 +Corinne Bailey Rae - Put Your Records On0 +Pearl Jam - Gonna See My Friend0 +R.E.M. - Living Well Is the Best Revenge0 +The Dresden Dolls - Girl Anachronism0 +The Pretenders - Middle Of The Road0 +The Rolling Stones - Ride On, Baby0 +Eels - Saturday Morning0 +Edguy - When a Hero Cries0 +The Chemical Brothers - Surface to Air0 +Natasha Bedingfield - Piece of Your Heart0 +Los Campesinos! - Death to Los Campesinos!0 +Katy Perry - Hot n Cold0 +The Chemical Brothers - Galvanize0 +All Time Low - Jasey Rae0 +Weezer - You Wont Get With Me Tonight0 +The Young Punx - Interplanetary (Openair Mix)0 +The Young Punx - You' Ve Got To0 +The Young Punx - You've Got to0 +The Young Punx - You've Got To [Norman Cook Mix]0 +The Young Punx - Rockall (Ben Braund Remix)0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake up song/wake up song_title.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Avenida Rivadavia +Now, We Just Slowly Circle the Draining Fish Bowl +Dawn to Dust +Live To Party +A Plague Upon Your Hissing +Crimson King +Spotlight (Twilight Mix) +I'm Yours +Fire Burning +the birds and the beats at the window +Hollaback Girl +Morning Passages +Supermassive Black Hole +Summer 91 +Circus +good morning mandy(4mygirl) +Great DJ +Pump It +Mayfly +Lady Madonna +Wannabe +Mony Mony +Torn +Un Poco de Amor +Jaded +Intergalactic +Around the World +Na Na Na Na Naa +D.A.N.C.E. +Great Day +Put Your Records On +Gonna See My Friend +Living Well Is the Best Revenge +Girl Anachronism +Middle Of The Road +Ride On, Baby +Saturday Morning +When a Hero Cries +Surface to Air +Piece of Your Heart +Death to Los Campesinos! +Hot n Cold +Galvanize +Jasey Rae +You Wont Get With Me Tonight +Interplanetary (Openair Mix) +You' Ve Got To +You've Got to +You've Got To [Norman Cook Mix] +Rockall (Ben Braund Remix)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake up wake up/wake up wake up_artist.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,1 @@ +Suicide Silence
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake up wake up/wake up wake up_results.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,1 @@ +Suicide Silence - Wake Up0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake up wake up/wake up wake up_title.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,1 @@ +Wake Up
Binary file Yading/wake me up/wake up/A Star Is Born [Jay-Z + J. Cole] (Explicit Album Version).wav has changed
Binary file Yading/wake me up/wake up/Earl of Errol - 6 Steps @ 78 BPM [feat. Michael Grey] (Tune: The Earl of Errol).wav has changed
Binary file Yading/wake me up/wake up/Every Single Day (feat. Nate Dogg & Snoop Dogg & Jewell).wav has changed
Binary file Yading/wake me up/wake up/Street Spirit (Fade Out) (BBC Radio 1 Evening Session).wav has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake up/wake up_artist.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Cocteau Twins +The Boo Radleys +Hilary Duff +Albert Hammond, Jr. +Cheap Trick +David Byrne +Richie Havens +Toto +Port O'Brien +Radiohead +Public Image Ltd. +Snoop Dogg +John Tesh +Fukkk Offf +Everything But the Girl +Damageplan +Melanie Horsnell +The Ditty Bops +Fiona Joy Hawkins +The Courteeners +Alpha Blondy +Alpha Blondy +Earl Grey +JS (Joerg Sommermeyer) +David Holmes +Herbie Hancock +The Mountain Goats +Pink Floyd +The Secret Handshake +Three Days Grace +Malcos +Autre Ne Veut +Snoop Dogg +Bond +J. Cole +Division Of Laura Lee +Herbie Hancock +Tha Eastsidaz +Snoop Dogg +Score +Youssou N'Dour +Daniel Bautista +Justin Timberlake +The Noisy Freaks +Akron/Family +ClariS +Damien Jurado +Pariisin Kevät +Fruit Bats +Thy Art Is Murder
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake up/wake up_results.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Cocteau Twins - Eggs and Their Shells0 +The Boo Radleys - Wake Up Boo!0 +Hilary Duff - Wake Up0 +Albert Hammond, Jr. - Spooky Couch0 +Cheap Trick - How Are You?0 +David Byrne - Lazy0 +Richie Havens - Going Back to My Roots0 +Toto - Mr. Friendly0 +Port O'Brien - I Woke Up Today0 +Radiohead - Street Spirit (Fade Out) (BBC Session 4/14/94)0 +Public Image Ltd. - Ease0 +Snoop Dogg - See Ya When I Get There0 +John Tesh - Roundball Rock0 +Fukkk Offf - Worldwide0 +Everything But the Girl - Each And Everyone0 +Damageplan - Wake Up0 +Melanie Horsnell - I Just Want Some Love0 +The Ditty Bops - Wake Up0 +Fiona Joy Hawkins - Iced Rain0 +The Courteeners - Scratch Your Name Upon My Lips0 +Alpha Blondy - Les Chiens0 +Alpha Blondy - Peace in Liberia0 +Earl Grey - The Lick0 +JS (Joerg Sommermeyer) - Comediantendans (A. Valerius, arr. Sommermeyer)0 +David Holmes - Jailbreak0 +Herbie Hancock - Essence (Bukem's DJ mix)0 +The Mountain Goats - Going to Port Washington0 +Pink Floyd - Love Scene Version 40 +The Secret Handshake - Wanted You0 +Three Days Grace - Wake Up0 +Malcos - Prepare Yourself (Here Comes the New Challenger)0 +Autre Ne Veut - Wake Up0 +Snoop Dogg - Doggy Dogg World (feat. Tha Dogg Pound & The Dramatics)0 +Bond - I'll Fly Away0 +J. Cole - Like A Star0 +Division Of Laura Lee - Black City0 +Herbie Hancock - Chan's Song (Never Said)0 +Tha Eastsidaz - Life Goes On0 +Snoop Dogg - The One And Only0 +Score - Shiny Stockings0 +Youssou N'Dour - Old Man (Gorgui)0 +Daniel Bautista - Flight Of The Bumblebee0 +Justin Timberlake - Signs (ft Snoop Dogg)0 +The Noisy Freaks - D.R.E.A.M0 +Akron/Family - Crickets0 +ClariS - Wake Up0 +Damien Jurado - A Jealous Heart Is a Heavy Heart0 +Pariisin Kevät - Joulujoulumaa0 +Fruit Bats - Everyday That We Wake Up It's a Beautiful Day0 +Thy Art Is Murder - This Hole Is Not Deep Enough0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake up/wake up_title.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Eggs and Their Shells +Wake Up Boo! +Wake Up +Spooky Couch +How Are You? +Lazy +Going Back to My Roots +Mr. Friendly +I Woke Up Today +Street Spirit (Fade Out) (BBC Session 4/14/94) +Ease +See Ya When I Get There +Roundball Rock +Worldwide +Each And Everyone +Wake Up +I Just Want Some Love +Wake Up +Iced Rain +Scratch Your Name Upon My Lips +Les Chiens +Peace in Liberia +The Lick +Comediantendans (A. Valerius, arr. Sommermeyer) +Jailbreak +Essence (Bukem's DJ mix) +Going to Port Washington +Love Scene Version 4 +Wanted You +Wake Up +Prepare Yourself (Here Comes the New Challenger) +Wake Up +Doggy Dogg World (feat. Tha Dogg Pound & The Dramatics) +I'll Fly Away +Like A Star +Black City +Chan's Song (Never Said) +Life Goes On +The One And Only +Shiny Stockings +Old Man (Gorgui) +Flight Of The Bumblebee +Signs (ft Snoop Dogg) +D.R.E.A.M +Crickets +Wake Up +A Jealous Heart Is a Heavy Heart +Joulujoulumaa +Everyday That We Wake Up It's a Beautiful Day +This Hole Is Not Deep Enough
Binary file Yading/wake me up/wake-up music/Jump (Feat. Joel Madden And Benji Madden).wav has changed
Binary file Yading/wake me up/wake-up music/The Bash-The Way To Mull River Wissahickon Drive Joan Beaton's Reel Trip To Windsor.wav has changed
Binary file Yading/wake me up/wake-up music/The Royal Military Police Horse and Motor Cycle Display Team - The Redcaps: 1. Fanfare: Redcaps, II. A Walk in the Black Forest, III. Post Horn Galop, IV: Koeniggratzer March St Patricks's Day, V: Ap Shenkins, VI: The Windsor.wav has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake-up music/wake-up music_results.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +The All-American Rejects - Gives You Hell0 +Sirens Sister - Hold On0 +Benji Madden - Jump0 +Yeah Yeah Yeahs - Bang0 +Sherwood - Worn0 +Windsor Drive - Shine0 +Hot Hot Heat - No, Not Now0 +Eminem - Like Toy Soldiers (Produced By Eminem)0 +Franz Ferdinand - The Dark of the Matinee0 +Franz Ferdinand - You Could Have It So Much Better0 +Beck - Where It's At0 +Muchy - Galanteria0 +Shout Out Louds - The Comeback0 +The Hush Sound - Lighthouse0 +Jupiter Sunrise - Cherry Wine0 +Sirens Sister - Echoes0 +Modest Mouse - the ocean breeze is salty0 +Windsor Drive - The Forest0 +Nailbomb - Vai Toma No Cú0 +Tapes 'n Tapes - Insistor0 +Coldplay - Yellow0 +Ne-Yo - So Sick0 +The Fratellis - Whistle for the Choir0 +Joy Division - They Walked in Line0 +The Fratellis - Henrietta0 +Le Tigre - Le Tigre-My My Metrocard0 +Coldplay - Til Kingdom Come [Hidden Track]0 +The Killers - Somebody Told Me0 +James Blunt - You're Beautiful0 +The Libertines - Vertigo0 +Shout Out Louds - Very Loud0 +Chris Brown - Yo0 +Beastie Boys - Sabotage0 +Sunrise Avenue - Hollywood Hills0 +N*E*R*D - She Wants to Move0 +The Hush Sound - Sweet Tangerine0 +The Go! Team - Friendship Update0 +Beck - Today Has Been a Fucked Up Day0 +Anouk - Girl0 +Shout Out Louds - Shut Your Eyes0 +Franz Ferdinand - Darts of Pleasure0 +The Cloud Room - Hey Now Now0 +will.i.am - Heartbreaker0 +The Unicorns - I Was Born (A Unicorn)0 +Jace Everett - Bad Things0 +Shout Out Louds - Hard Rain0 +N*E*R*D - Backseat Love0 +N*E*R*D - Wonderful Place0 +Modest Mouse - Float On0 +Shout Out Louds - very loud (architecture in helsinki remix)0
Binary file Yading/wake me up/wake-up song/Andy Grammer - Keep Your Head Up (Vocal Version) .wav has changed
Binary file Yading/wake me up/wake-up song/I Wanna Be the Only One (Radio Edit) [feat. Bebe Winans].wav has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake-up song/wake-up song_artist.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Andy Grammer +Maroon 5 feat. Christina Aguilera +Ultimate Kaos +Luther Vandross & Janet Jackson +De Su +eternal feat. bebe winans +Kasia Kowalska +The Beloved +Reni Jusis +Brooke Fraser +Natalia Lesz +Craig David +Sofa +The KDMS +The Sol +The Braxtons +Soultans +The S.O.U.L. S.Y.S.T.E.M. +Londonbeat +Stakka Bo +Helena Paparizou +2 +Mika feat. Pharrell Williams +Dance Nation vs. Shaun Baker +Danzel +Beverley Knight +Taio Cruz Feat. Kylie Minogue +Kayah +Sixteen +Freemasons feat. Siedah Garrett +Feel +Kate Ryan +Kto To i Kasia Wilk +Miami Horror Feat. Kimbra +Andrzej Piaseczny +Tv Rock +Michelle Gayle +Bruno Mars +Madonna Feat. Pharrell Williams +Laura Pausini +Laura Pausini +Madonna +Voodoo & Serano +Janet Jackson +Right Said Fred +Candy Girl +Ruby Amanfu +Natalie Imbruglia +Kylie Minogue +Yes
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake-up song/wake-up song_results.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Andy Grammer - Keep Your Head Up0 +Maroon 5 feat. Christina Aguilera - Moves Like Jagger0 +Ultimate Kaos - Casanova0 +Luther Vandross & Janet Jackson - The Best Things In Life Are Free0 +De Su - życie cudem jest0 +eternal feat. bebe winans - i wanna be the only one0 +Kasia Kowalska - Coś optymistycznego0 +The Beloved - Satellite0 +Reni Jusis - It's Not Enough0 +Brooke Fraser - Something In The Water0 +Natalia Lesz - That Girl0 +Craig David - One More Lie (Standing In The Shadows)0 +Sofa - Hardkor I Disko0 +The KDMS - Circles0 +The Sol - Say Hello0 +The Braxtons - The Boss0 +Soultans - Can't Take My Hands Off You0 +The S.O.U.L. S.Y.S.T.E.M. - It's Gonna Be a Lovely Day0 +Londonbeat - You Bring On The Sun0 +Stakka Bo - Here We Go0 +Helena Paparizou - Mambo!0 +2-4 Grooves - Writing On The Wall (St. Elmo's Fire)0 +Mika feat. Pharrell Williams - Celebrate0 +Dance Nation vs. Shaun Baker - Sunshine 20090 +Danzel - You Spin Me Round (Like A Record)0 +Beverley Knight - Greatest Day0 +Taio Cruz Feat. Kylie Minogue - Higher0 +Kayah - Za późno0 +Sixteen - Twoja Lawa0 +Freemasons feat. Siedah Garrett - Rain Down Love0 +Feel - No Pokaż na co Cię stać0 +Kate Ryan - I Surrender (Single Version)0 +Kto To i Kasia Wilk - Zero Do Stracenia0 +Miami Horror Feat. Kimbra - I Look To You0 +Andrzej Piaseczny - Rysowane Tobie0 +Tv Rock - In the Air0 +Michelle Gayle - Sweetness0 +Bruno Mars - The Lazy Song (Single Version)0 +Madonna Feat. Pharrell Williams - Give it 2 me0 +Laura Pausini - ¿tú con qué sueñas?0 +Laura Pausini - Entre tú y Mil Mares0 +Madonna - Holiday0 +Voodoo & Serano - Transatlantic Blow0 +Janet Jackson - Whoops Now0 +Right Said Fred - Don't Talk Just Kiss0 +Candy Girl - Wszystko czego dziś chcę0 +Ruby Amanfu - Sugah0 +Natalie Imbruglia - Lukas0 +Kylie Minogue - Better the Devil You Know0 +Yes - Owner of a Lonely Heart0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake-up song/wake-up song_title.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Keep Your Head Up +Moves Like Jagger +Casanova +The Best Things In Life Are Free +życie cudem jest +i wanna be the only one +Coś optymistycznego +Satellite +It's Not Enough +Something In The Water +That Girl +One More Lie (Standing In The Shadows) +Hardkor I Disko +Circles +Say Hello +The Boss +Can't Take My Hands Off You +It's Gonna Be a Lovely Day +You Bring On The Sun +Here We Go +Mambo! +4 Grooves - Writing On The Wall (St. Elmo's Fire) +Celebrate +Sunshine 2009 +You Spin Me Round (Like A Record) +Greatest Day +Higher +Za późno +Twoja Lawa +Rain Down Love +No Pokaż na co Cię stać +I Surrender (Single Version) +Zero Do Stracenia +I Look To You +Rysowane Tobie +In the Air +Sweetness +The Lazy Song (Single Version) +Give it 2 me +¿tú con qué sueñas? +Entre tú y Mil Mares +Holiday +Transatlantic Blow +Whoops Now +Don't Talk Just Kiss +Wszystko czego dziś chcę +Sugah +Lukas +Better the Devil You Know +Owner of a Lonely Heart
Binary file Yading/wake me up/wake-up/César Franck: Sonata In A Major For Flute And Piano 1. Allegretto Ben Moderato.wav has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake-up/wake-up_artist.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Clubfeet +Rihanna & Kardinal Offishall +Kelis +Seeed +V6 +Blue +Ali Akbar Khan +Burn The Lies +North Atlantic Oscillation +Rick James +Wham! +Chevelle +Modest Mouse +Fenech +The Boo Radleys +Fatboy Slim +Dire Straits +Limp Bizkit +Foo Fighters +Lynyrd Skynyrd +New Order +P.O.D. +Glenn Miller +Get Well Soon +Spoon +Minitel Rose +R. Carlos Nakai +Tom Waits +Katrina and the Waves +César Franck +Ascii.Disko +Bill Conti +Delphic +James Morrison +Girls +Franz Ferdinand +Moullinex +Maurice Ravel +Porcupine Tree +Mama Cass +Marbert Rocel +Tim Curry +Tim Curry +Tim Curry +Tim Curry +Tim Curry +Tim Curry +Tim Curry +Tim Curry +Tim Curry
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake-up/wake-up_results.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Clubfeet - Last Words0 +Rihanna & Kardinal Offishall - Rush0 +Kelis - Millionare0 +Seeed - Aufstehn ! Featuring Cee-Lo Green0 +V6 - Sagarina Heartbeat0 +Blue - Get Ready0 +Ali Akbar Khan - Tarana in Mian Ki Malhar [Bidar Style]0 +Burn The Lies - In Your Face MotherFucker (Wake-Up)0 +North Atlantic Oscillation - Cell Count0 +Rick James - Super Freak0 +Wham! - Wake Me Up Before You Go-Go0 +Chevelle - Tug-O-War0 +Modest Mouse - Invisible0 +Fenech-Soler - I Need Love0 +The Boo Radleys - Wake Up Boo!0 +Fatboy Slim - Break In0 +Dire Straits - Money for Nothing0 +Limp Bizkit - Take a Look Around0 +Foo Fighters - Learn to Fly0 +Lynyrd Skynyrd - Free Bird0 +New Order - Age of Consent0 +P.O.D. - Boom0 +Glenn Miller - In the Mood0 +Get Well Soon - Prelude0 +Spoon - The Way We Get By0 +Minitel Rose - When I Was Punk0 +R. Carlos Nakai - Rocks and Rills0 +Tom Waits - Ice Cream Man0 +Katrina and the Waves - Walking on Sunshine0 +César Franck - Sonata For Cello And Piano0 +Ascii.Disko - Aldimarkt0 +Bill Conti - Gonna Fly Now (Theme From "Rocky")0 +Delphic - Halcyon0 +James Morrison - The Only Night0 +Girls - Lust For Life0 +Franz Ferdinand - You're the Reason I'm Leaving0 +Moullinex - Leisure Suit0 +Maurice Ravel - Bolero0 +Porcupine Tree - Even Less0 +Mama Cass - Make Your Own Kind Of Music0 +Marbert Rocel - tttictictac0 +Tim Curry - Rocky Horror Picture Show - Sw0 +Tim Curry - Rose Tint My World: Floor Show/Fanfare/Don't Dream It/Wild and Untamed0 +Tim Curry - Rocky Horror Picture Show, The - I Put A Spell On You0 +Tim Curry - Planet, Shmanet, Janet0 +Tim Curry - 03 - I Do The Rock0 +Tim Curry - Timewarp0 +Tim Curry - 06 I Can Make You a Man0 +Tim Curry - Biting My Nails (Tim Curry)0 +Tim Curry - I Can Make You A Man(reprise)0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/wake-up/wake-up_title.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Last Words +Rush +Millionare +Aufstehn ! Featuring Cee-Lo Green +Sagarina Heartbeat +Get Ready +Tarana in Mian Ki Malhar [Bidar Style] +In Your Face MotherFucker (Wake-Up) +Cell Count +Super Freak +Wake Me Up Before You Go-Go +Tug-O-War +Invisible +Soler - I Need Love +Wake Up Boo! +Break In +Money for Nothing +Take a Look Around +Learn to Fly +Free Bird +Age of Consent +Boom +In the Mood +Prelude +The Way We Get By +When I Was Punk +Rocks and Rills +Ice Cream Man +Walking on Sunshine +Sonata For Cello And Piano +Aldimarkt +Gonna Fly Now (Theme From "Rocky") +Halcyon +The Only Night +Lust For Life +You're the Reason I'm Leaving +Leisure Suit +Bolero +Even Less +Make Your Own Kind Of Music +tttictictac +Rocky Horror Picture Show - Sw +Rose Tint My World: Floor Show/Fanfare/Don't Dream It/Wild and Untamed +Rocky Horror Picture Show, The - I Put A Spell On You +Planet, Shmanet, Janet +03 - I Do The Rock +Timewarp +06 I Can Make You a Man +Biting My Nails (Tim Curry) +I Can Make You A Man(reprise)
Binary file Yading/wake me up/you raise me up/Ancora Non Sai (with violinist Andre Rieu).wav has changed
Binary file Yading/wake me up/you raise me up/Inori - You Raise Me Up (Originally Performed by Lena Park) [Romeo X Juliet Original Soundtrack].wav has changed
Binary file Yading/wake me up/you raise me up/Ultimate Tracks - You Raise Me Up - as made popular by Selah (POS).wav has changed
Binary file Yading/wake me up/you raise me up/You Are Loved [Don't Give Up] (Album Version).wav has changed
Binary file Yading/wake me up/you raise me up/You Raise Me Up (from 'Once in a Red Moon').wav has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/you raise me up/you raise me up_artist.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Westlife +Celtic Woman +Secret Garden +Josh Groban +Josh Groban +Louise Morrissey +Sissel Kyrkjebø +Celtic Thunder +Sheila Walsh +David Garrett +Lena Park +Becky Taylor +Russell Watson +Selah +Michael +Westlife +Michael Hirte +Brian Kennedy +Josh Groban +Josh Groban +Josh Groban +André Rieu +André Rieu +André Rieu +André Rieu +André Rieu +André Rieu +André Rieu +André Rieu +André Rieu +André Rieu +André Rieu +André Rieu +André Rieu +André Rieu +André Rieu +André Rieu +Michael Hirte +Michael Hirte +Michael Hirte +Michael Hirte +Michael Hirte +Michael Hirte +Michael Hirte +Michael Hirte +Michael Hirte +Michael Hirte +Michael Hirte +Lena Park +Lena Park
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/you raise me up/you raise me up_results.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +Westlife - You Raise Me Up0 +Celtic Woman - You Raise Me Up0 +Secret Garden - You Raise Me Up0 +Josh Groban - You Raise Me Up (Album Version)0 +Josh Groban - You Raise Me Up0 +Louise Morrissey - You Raise Me Up0 +Sissel Kyrkjebø - You Raise Me Up0 +Celtic Thunder - You Raise Me Up0 +Sheila Walsh - You Raise Me Up0 +David Garrett - You Raise Me Up0 +Lena Park - You Raise me Up0 +Becky Taylor - You Raise Me Up /Becky Taylor0 +Russell Watson - You Raise Me Up0 +Selah - You Raise Me Up0 +Michael - Yours & Mine0 +Westlife - The Rose0 +Michael Hirte - You Raise Me Up0 +Brian Kennedy - You Raise Me Up0 +Josh Groban - You Are Loved (Don't Give Up)0 +Josh Groban - You Raise Me Up (Edit)0 +Josh Groban - You Raise Me Up (Radio Edit)0 +André Rieu - Feed The Birds0 +André Rieu - Il Silenzio0 +André Rieu - Love Live Forever0 +André Rieu - You Raise Me Up0 +André Rieu - Send In The Clowns, ARV_100 +André Rieu - Chianti Song, ARV_100 +André Rieu - Carnival Of Venice, ARV_100 +André Rieu - The Gypsy Princess, ARV_100 +André Rieu - The Rose, ARV_100 +André Rieu - Roses From The South0 +André Rieu - Autumn Leaves0 +André Rieu - Mama0 +André Rieu - There Is A Song In Me0 +André Rieu - Dark Roses0 +André Rieu - Supercalifragilisticexpialidocious0 +André Rieu - I Dreamed A Dream0 +Michael Hirte - Morning Has Broken0 +Michael Hirte - Amazing Grace0 +Michael Hirte - Stille Nacht0 +Michael Hirte - Aber Heidschi Bumbeidschi0 +Michael Hirte - Bridge Over Troubled Water0 +Michael Hirte - We Have A Dream0 +Michael Hirte - Bright Eyes0 +Michael Hirte - Mull Of Kintyre0 +Michael Hirte - Guten Abend, Gute Nacht0 +Michael Hirte - Time To Say Goodbye0 +Michael Hirte - Ave Maria0 +Lena Park - Mulan - Eternal Memory0 +Lena Park - Ai Qing Neng Lai Lin Ma0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Yading/wake me up/you raise me up/you raise me up_title.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,50 @@ +You Raise Me Up +You Raise Me Up +You Raise Me Up +You Raise Me Up (Album Version) +You Raise Me Up +You Raise Me Up +You Raise Me Up +You Raise Me Up +You Raise Me Up +You Raise Me Up +You Raise me Up +You Raise Me Up /Becky Taylor +You Raise Me Up +You Raise Me Up +Yours & Mine +The Rose +You Raise Me Up +You Raise Me Up +You Are Loved (Don't Give Up) +You Raise Me Up (Edit) +You Raise Me Up (Radio Edit) +Feed The Birds +Il Silenzio +Love Live Forever +You Raise Me Up +Send In The Clowns, ARV_1 +Chianti Song, ARV_1 +Carnival Of Venice, ARV_1 +The Gypsy Princess, ARV_1 +The Rose, ARV_1 +Roses From The South +Autumn Leaves +Mama +There Is A Song In Me +Dark Roses +Supercalifragilisticexpialidocious +I Dreamed A Dream +Morning Has Broken +Amazing Grace +Stille Nacht +Aber Heidschi Bumbeidschi +Bridge Over Troubled Water +We Have A Dream +Bright Eyes +Mull Of Kintyre +Guten Abend, Gute Nacht +Time To Say Goodbye +Ave Maria +Mulan - Eternal Memory +Ai Qing Neng Lai Lin Ma
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/.gitignore Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,6 @@ +*.pyc +*.pyo +*.egg-info +build +*.DS_Store +dist
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/7digital-python/.gitignore Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,2 @@ +*.pyc +*.pkl
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/7digital-python/LICENSE Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,19 @@ +Copyright (C) 2011 by 7digital + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/7digital-python/README Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,92 @@ +7 digital API access via python | http://developer.7digital.net/ + +======= +Download python code: http://github.com/ocelma/7-digital/downloads + +Using the 7digital developer platform you can: + * Build digital music applications for the web, desktop and mobile devices + * Generate revenue with your own branded music download store + * Access our catalogue of over 10,000,000 MP3 music tracks + * Create your own streaming radio service + * Include official sleeve artwork and audio samples + * Have access to major labels licensed content in 16 major territories included US, Canada, UK, Germany, France, Spain, Italy and many more + +See some usage examples here: http://github.com/ocelma/7-digital/tree/v1.0 + +DOWNLOAD LATEST VERSION: http://github.com/ocelma/7-digital/downloads + +- Examples: + +# If you want to use the cache, you have to manually create that dir (e.g. $ mkdir ./cache) +import py7digital + +#Search artist +results = py7digital.search_artist('stones') +print results.get_total_result_count() +for artist in results.get_next_page(): + print artist.get_name() #, artist.get_image(), artist.get_url(), artist.get_tags() + print '\tTop tracks:' + for top_track in artist.get_top_tracks(): + print '\t\t', top_track.get_title(), top_track.get_isrc(), top_track.get_duration(), top_track.get_position(), top_track.get_explicit(), top_track.get_version() + print '\tRec. Albums:' + for rec_album in artist.get_recommended_albums(): + print '\t\t', rec_album, rec_album.get_year() #, album.get_barcode(), album.get_type(), album.get_artist(), album.get_tags(), album.get_label() + for album in artist.get_albums(5): + print '\t', album, album.get_year(), album.get_barcode(), album.get_type(), album.get_artist(), album.get_tags(), album.get_label(), album.get_release_date(), album.get_added_date() + for sim_album in album.get_similar(): + print '\t\tSimilar:', sim_album, sim_album.get_year(), sim_album.get_artist() + for track in album.get_tracks(): + print '\t\t', track, track.get_isrc() #, track.get_url(), track.get_audio() + +#Browse artists starting with 'J' +results = py7digital.browse_artists('j') +print results.get_total_result_count() +for artist in results.get_next_page(): + print artist.get_name() #, artist.get_image(), artist.get_url(), artist.get_tags() + for album in artist.get_albums(2): + print '\t', album, album.get_year() #album.get_barcode(), album.get_type(), album.get_artist(), album.get_tags(), album.get_label() + for track in album.get_tracks(): + print '\t\t', track.get_title(), track.get_isrc() #, track.get_url(), track.get_audio() + +#Search albums +searcher = py7digital.search_album('u2') +print searcher.get_total_result_count() +while searcher.has_results(): + for album in searcher.get_next_page(): + print album, album.get_similar() + +#Search tracks +searcher = py7digital.search_track('u2 one') +print searcher.get_total_result_count() +while searcher.has_results(): + for track in searcher.get_next_page(): + print track + +# New releases in a given period of time +results = py7digital.album_releases('20100901', '20100924') +for album in results.get_next_page(): + print album, album.get_year(), album.get_barcode(), album.get_type(), album.get_artist(), album.get_tags(), album.get_label(), album.get_release_date(), album.get_added_date() + for sim_album in album.get_similar(): + print '\tSimilar:', sim_album, sim_album.get_year(), sim_album.get_artist() + for track in album.get_tracks(): + print '\t', track, track.get_isrc() #, track.get_url(), track.get_audio() + +# Album charts in a given period of time +results = py7digital.album_charts('month', '20100901') +for album in results.get_next_page(): + print album, album.get_year(), album.get_barcode(), album.get_type(), album.get_artist(), album.get_tags(), album.get_label(), album.get_release_date(), album.get_added_date() + for sim_album in album.get_similar(): + print '\tSimilar:', sim_album, sim_album.get_year(), sim_album.get_artist() + for track in album.get_tracks(): + print '\t', track, track.get_isrc() #, track.get_url(), track.get_audio() + + +-- OAuth Usage + +auth = Oauth7digital(CONSUMER_KEY, CONSUMER_SECRET) +token = auth.request_token() +authorized = auth.authorize_request_token(token) +access_token = auth.request_access_token(token) + +sevendigital = Oauth7digital(CONSUMER_KEY, CONSUMER_SECRET, access_token) +results = sevendigital.get_locker()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/7digital-python/TODO Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,44 @@ +- Catalogue API + + + means DONE + - means TODO + + o Artist methods + + artist/browse + - artist/chart + + artist/details + + artist/releases + + artist/search + + artist/toptracks + o Release methods + + release/bydate + + release/chart + + release/details + + release/recommend + + release/search + + release/tracks + o Track methods + - track/chart + + track/details + + track/preview + + track/search + o Tag methods + + tag + + artist/tags + - artist/bytag/top + + release/tags + - release/bytag/new + - release/bytag/top + +- RELEASE attributes to add: + +price + price object pricing information + +formats + list of format objects list of formats the tracks appearing on this release are available in + +- TRACK attributes to add: + +price + price object pricing information
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/7digital-python/access_token.pkl Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,21 @@ +ccopy_reg +_reconstructor +p0 +(clib.oauth +OAuthToken +p1 +c__builtin__ +object +p2 +Ntp3 +Rp4 +(dp5 +S'secret' +p6 +S'H9Sd66Uyv+92e1WH0giB+g==' +p7 +sS'key' +p8 +S'qtKibfZanGxQR3B9QVHCsg==' +p9 +sb. \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/7digital-python/app.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,48 @@ +import pickle +from lib.oauth7digital import Oauth7digital + + +TOKEN_FILE = './access_token.pkl' + +def test_sevendigital(): + try: + pkl_file = open(TOKEN_FILE, 'rb') + access_token = pickle.load(pkl_file) + pkl_file.close() + except: + access_token = None + if access_token: + print 'You have an access token: %s' % str(access_token.key) + else: + auth = Oauth7digital(CONSUMER_KEY, CONSUMER_SECRET) + + token = auth.request_token() + authorized = auth.authorize_request_token(token) + access_token = auth.request_access_token(token) + + pkl_file=open(TOKEN_FILE, 'wb') + pickle.dump(access_token, pkl_file) + pkl_file.close() + + return access_token + +def test_locker(): + access_token = test_sevendigital() + + sevendigital = Oauth7digital(CONSUMER_KEY, CONSUMER_SECRET, access_token) + results = sevendigital.get_locker() + for i in results: + print "-----------------------------" + print i.release.title + print i.release.artist.name + for a in i.tracks: + print a.track.title + return results + +# app entry point +if __name__ == '__main__': + test_locker() + print 'Done.' + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/7digital-python/lib/oauth.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,657 @@ +""" +The MIT License + +Copyright (c) 2007 Leah Culver + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +import cgi +import urllib +import time +import random +import urlparse +import hmac +import binascii +import re + + +VERSION = '1.0' # Hi Blaine! +HTTP_METHOD = 'GET' +SIGNATURE_METHOD = 'PLAINTEXT' + + +class OAuthError(RuntimeError): + """Generic exception class.""" + def __init__(self, message='OAuth error occured.'): + self.message = message + +def build_authenticate_header(realm=''): + """Optional WWW-Authenticate header (401 error)""" + return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} + +def escape(s): + """Escape a URL including any /.""" + return urllib.quote(s, safe='~') + +def _utf8_str(s): + """Convert unicode to utf-8.""" + if isinstance(s, unicode): + return s.encode("utf-8") + else: + return str(s) + +def generate_timestamp(): + """Get seconds since epoch (UTC).""" + return int(time.time()) + +def generate_nonce(length=8): + """Generate pseudorandom number.""" + return ''.join([str(random.randint(0, 9)) for i in range(length)]) + +def generate_verifier(length=8): + """Generate pseudorandom number.""" + return ''.join([str(random.randint(0, 9)) for i in range(length)]) + + +class OAuthConsumer(object): + """Consumer of OAuth authentication. + + OAuthConsumer is a data type that represents the identity of the Consumer + via its shared secret with the Service Provider. + + """ + key = None + secret = None + + def __init__(self, key, secret): + self.key = key + self.secret = secret + + +class OAuthToken(object): + """OAuthToken is a data type that represents an End User via either an access + or request token. + + key -- the token + secret -- the token secret + + """ + key = None + secret = None + callback = None + callback_confirmed = None + verifier = None + + def __init__(self, key, secret): + self.key = key + self.secret = secret + + def set_callback(self, callback): + self.callback = callback + self.callback_confirmed = 'true' + + def set_verifier(self, verifier=None): + if verifier is not None: + self.verifier = verifier + else: + self.verifier = generate_verifier() + + def get_callback_url(self): + if self.callback and self.verifier: + # Append the oauth_verifier. + parts = urlparse.urlparse(self.callback) + scheme, netloc, path, params, query, fragment = parts[:6] + if query: + query = '%s&oauth_verifier=%s' % (query, self.verifier) + else: + query = 'oauth_verifier=%s' % self.verifier + return urlparse.urlunparse((scheme, netloc, path, params, + query, fragment)) + return self.callback + + def to_string(self): + data = { + 'oauth_token': self.key, + 'oauth_token_secret': self.secret, + } + if self.callback_confirmed is not None: + data['oauth_callback_confirmed'] = self.callback_confirmed + return urllib.urlencode(data) + + def from_string(s): + """ Returns a token from something like: + oauth_token_secret=xxx&oauth_token=xxx + """ + print "******* %s" % s.__class__ + #params = urlparse.parse_qs(s, keep_blank_values=False) + + key = re.search("<oauth_token>(\w.+)</oauth_token>", s).groups()[0] + print "@@@@@@ key: %s" %key + secret = re.search("<oauth_token_secret>(\w.+)</oauth_token_secret>", s).groups()[0] + print "@@@@@@ secret: %s" % secret + token = OAuthToken(key, secret) + + return token + from_string = staticmethod(from_string) + + def __str__(self): + return self.to_string() + + +class OAuthRequest(object): + """OAuthRequest represents the request and can be serialized. + + OAuth parameters: + - oauth_consumer_key + - oauth_token + - oauth_signature_method + - oauth_signature + - oauth_timestamp + - oauth_nonce + - oauth_version + - oauth_verifier + ... any additional parameters, as defined by the Service Provider. + """ + parameters = None # OAuth parameters. + http_method = HTTP_METHOD + http_url = None + version = VERSION + + def __init__(self, http_method=HTTP_METHOD, http_url=None, parameters=None): + self.http_method = http_method + self.http_url = http_url + self.parameters = parameters or {} + + def set_parameter(self, parameter, value): + self.parameters[parameter] = value + + def get_parameter(self, parameter): + try: + return self.parameters[parameter] + except: + raise OAuthError('Parameter not found: %s' % parameter) + + def _get_timestamp_nonce(self): + return self.get_parameter('oauth_timestamp'), self.get_parameter( + 'oauth_nonce') + + def get_nonoauth_parameters(self): + """Get any non-OAuth parameters.""" + parameters = {} + for k, v in self.parameters.iteritems(): + # Ignore oauth parameters. + if k.find('oauth_') < 0: + parameters[k] = v + return parameters + + def to_header(self, realm=''): + """Serialize as a header for an HTTPAuth request.""" + auth_header = 'OAuth realm="%s"' % realm + # Add the oauth parameters. + if self.parameters: + for k, v in self.parameters.iteritems(): + if k[:6] == 'oauth_': + auth_header += ', %s="%s"' % (k, escape(str(v))) + return {'Authorization': auth_header} + + def to_postdata(self): + """Serialize as post data for a POST request.""" + return '&'.join(['%s=%s' % (escape(str(k)), escape(str(v))) \ + for k, v in self.parameters.iteritems()]) + + def to_url(self): + """Serialize as a URL for a GET request.""" + return '%s?%s' % (self.get_normalized_http_url(), self.to_postdata()) + + def get_normalized_parameters(self): + """Return a string that contains the parameters that must be signed.""" + params = self.parameters + try: + # Exclude the signature if it exists. + del params['oauth_signature'] + except: + pass + # Escape key values before sorting. + key_values = [(escape(_utf8_str(k)), escape(_utf8_str(v))) \ + for k,v in params.items()] + # Sort lexicographically, first after key, then after value. + key_values.sort() + # Combine key value pairs into a string. + return '&'.join(['%s=%s' % (k, v) for k, v in key_values]) + + def get_normalized_http_method(self): + """Uppercases the http method.""" + return self.http_method.upper() + + def get_normalized_http_url(self): + """Parses the URL and rebuilds it to be scheme://host/path.""" + parts = urlparse.urlparse(self.http_url) + scheme, netloc, path = parts[:3] + # Exclude default port numbers. + if scheme == 'http' and netloc[-3:] == ':80': + netloc = netloc[:-3] + elif scheme == 'https' and netloc[-4:] == ':443': + netloc = netloc[:-4] + return '%s://%s%s' % (scheme, netloc, path) + + def sign_request(self, signature_method, consumer, token): + """Set the signature parameter to the result of build_signature.""" + # Set the signature method. + self.set_parameter('oauth_signature_method', + signature_method.get_name()) + # Set the signature. + self.set_parameter('oauth_signature', + self.build_signature(signature_method, consumer, token)) + + def build_signature(self, signature_method, consumer, token): + """Calls the build signature method within the signature method.""" + return signature_method.build_signature(self, consumer, token) + + def from_request(http_method, http_url, headers=None, parameters=None, + query_string=None): + """Combines multiple parameter sources.""" + if parameters is None: + parameters = {} + + # Headers + if headers and 'Authorization' in headers: + auth_header = headers['Authorization'] + # Check that the authorization header is OAuth. + if auth_header[:6] == 'OAuth ': + auth_header = auth_header[6:] + try: + # Get the parameters from the header. + header_params = OAuthRequest._split_header(auth_header) + parameters.update(header_params) + except: + raise OAuthError('Unable to parse OAuth parameters from ' + 'Authorization header.') + + # GET or POST query string. + if query_string: + query_params = OAuthRequest._split_url_string(query_string) + parameters.update(query_params) + + # URL parameters. + param_str = urlparse.urlparse(http_url)[4] # query + url_params = OAuthRequest._split_url_string(param_str) + parameters.update(url_params) + + if parameters: + return OAuthRequest(http_method, http_url, parameters) + + return None + from_request = staticmethod(from_request) + + def from_consumer_and_token(oauth_consumer, token=None, + callback=None, verifier=None, http_method=HTTP_METHOD, + http_url=None, parameters=None): + if not parameters: + parameters = {} + + defaults = { + 'oauth_consumer_key': oauth_consumer.key, + 'oauth_timestamp': generate_timestamp(), + 'oauth_nonce': generate_nonce(), + 'oauth_version': OAuthRequest.version, + } + + defaults.update(parameters) + parameters = defaults + + if token: + parameters['oauth_token'] = token.key + if token.callback: + parameters['oauth_callback'] = token.callback + # 1.0a support for verifier. + if verifier: + parameters['oauth_verifier'] = verifier + elif callback: + # 1.0a support for callback in the request token request. + parameters['oauth_callback'] = callback + + return OAuthRequest(http_method, http_url, parameters) + from_consumer_and_token = staticmethod(from_consumer_and_token) + + def from_token_and_callback(token, callback=None, http_method=HTTP_METHOD, + http_url=None, parameters=None): + if not parameters: + parameters = {} + + parameters['oauth_token'] = token.key + + if callback: + parameters['oauth_callback'] = callback + + return OAuthRequest(http_method, http_url, parameters) + from_token_and_callback = staticmethod(from_token_and_callback) + + def _split_header(header): + """Turn Authorization: header into parameters.""" + params = {} + parts = header.split(',') + for param in parts: + # Ignore realm parameter. + if param.find('realm') > -1: + continue + # Remove whitespace. + param = param.strip() + # Split key-value. + param_parts = param.split('=', 1) + # Remove quotes and unescape the value. + params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"')) + return params + _split_header = staticmethod(_split_header) + + def _split_url_string(param_str): + """Turn URL string into parameters.""" + parameters = cgi.parse_qs(param_str, keep_blank_values=False) + for k, v in parameters.iteritems(): + parameters[k] = urllib.unquote(v[0]) + return parameters + _split_url_string = staticmethod(_split_url_string) + +class OAuthServer(object): + """A worker to check the validity of a request against a data store.""" + timestamp_threshold = 300 # In seconds, five minutes. + version = VERSION + signature_methods = None + data_store = None + + def __init__(self, data_store=None, signature_methods=None): + self.data_store = data_store + self.signature_methods = signature_methods or {} + + def set_data_store(self, data_store): + self.data_store = data_store + + def get_data_store(self): + return self.data_store + + def add_signature_method(self, signature_method): + self.signature_methods[signature_method.get_name()] = signature_method + return self.signature_methods + + def fetch_request_token(self, oauth_request): + """Processes a request_token request and returns the + request token on success. + """ + try: + # Get the request token for authorization. + token = self._get_token(oauth_request, 'request') + except OAuthError: + # No token required for the initial token request. + version = self._get_version(oauth_request) + consumer = self._get_consumer(oauth_request) + try: + callback = self.get_callback(oauth_request) + except OAuthError: + callback = None # 1.0, no callback specified. + self._check_signature(oauth_request, consumer, None) + # Fetch a new token. + token = self.data_store.fetch_request_token(consumer, callback) + return token + + def fetch_access_token(self, oauth_request): + """Processes an access_token request and returns the + access token on success. + """ + version = self._get_version(oauth_request) + consumer = self._get_consumer(oauth_request) + try: + verifier = self._get_verifier(oauth_request) + except OAuthError: + verifier = None + # Get the request token. + token = self._get_token(oauth_request, 'request') + self._check_signature(oauth_request, consumer, token) + new_token = self.data_store.fetch_access_token(consumer, token, verifier) + return new_token + + def verify_request(self, oauth_request): + """Verifies an api call and checks all the parameters.""" + # -> consumer and token + version = self._get_version(oauth_request) + consumer = self._get_consumer(oauth_request) + # Get the access token. + token = self._get_token(oauth_request, 'access') + self._check_signature(oauth_request, consumer, token) + parameters = oauth_request.get_nonoauth_parameters() + return consumer, token, parameters + + def authorize_token(self, token, user): + """Authorize a request token.""" + return self.data_store.authorize_request_token(token, user) + + def get_callback(self, oauth_request): + """Get the callback URL.""" + return oauth_request.get_parameter('oauth_callback') + + def build_authenticate_header(self, realm=''): + """Optional support for the authenticate header.""" + return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} + + def _get_version(self, oauth_request): + """Verify the correct version request for this server.""" + try: + version = oauth_request.get_parameter('oauth_version') + except: + version = VERSION + if version and version != self.version: + raise OAuthError('OAuth version %s not supported.' % str(version)) + return version + + def _get_signature_method(self, oauth_request): + """Figure out the signature with some defaults.""" + try: + signature_method = oauth_request.get_parameter( + 'oauth_signature_method') + except: + signature_method = SIGNATURE_METHOD + try: + # Get the signature method object. + signature_method = self.signature_methods[signature_method] + except: + signature_method_names = ', '.join(self.signature_methods.keys()) + raise OAuthError('Signature method %s not supported try one of the ' + 'following: %s' % (signature_method, signature_method_names)) + + return signature_method + + def _get_consumer(self, oauth_request): + consumer_key = oauth_request.get_parameter('oauth_consumer_key') + consumer = self.data_store.lookup_consumer(consumer_key) + if not consumer: + raise OAuthError('Invalid consumer.') + return consumer + + def _get_token(self, oauth_request, token_type='access'): + """Try to find the token for the provided request token key.""" + token_field = oauth_request.get_parameter('oauth_token') + token = self.data_store.lookup_token(token_type, token_field) + if not token: + raise OAuthError('Invalid %s token: %s' % (token_type, token_field)) + return token + + def _get_verifier(self, oauth_request): + return oauth_request.get_parameter('oauth_verifier') + + def _check_signature(self, oauth_request, consumer, token): + timestamp, nonce = oauth_request._get_timestamp_nonce() + self._check_timestamp(timestamp) + self._check_nonce(consumer, token, nonce) + signature_method = self._get_signature_method(oauth_request) + try: + signature = oauth_request.get_parameter('oauth_signature') + except: + raise OAuthError('Missing signature.') + # Validate the signature. + valid_sig = signature_method.check_signature(oauth_request, consumer, + token, signature) + if not valid_sig: + key, base = signature_method.build_signature_base_string( + oauth_request, consumer, token) + raise OAuthError('Invalid signature. Expected signature base ' + 'string: %s' % base) + built = signature_method.build_signature(oauth_request, consumer, token) + + def _check_timestamp(self, timestamp): + """Verify that timestamp is recentish.""" + timestamp = int(timestamp) + now = int(time.time()) + lapsed = abs(now - timestamp) + if lapsed > self.timestamp_threshold: + raise OAuthError('Expired timestamp: given %d and now %s has a ' + 'greater difference than threshold %d' % + (timestamp, now, self.timestamp_threshold)) + + def _check_nonce(self, consumer, token, nonce): + """Verify that the nonce is uniqueish.""" + nonce = self.data_store.lookup_nonce(consumer, token, nonce) + if nonce: + raise OAuthError('Nonce already used: %s' % str(nonce)) + + +class OAuthClient(object): + """OAuthClient is a worker to attempt to execute a request.""" + consumer = None + token = None + + def __init__(self, oauth_consumer, oauth_token): + self.consumer = oauth_consumer + self.token = oauth_token + + def get_consumer(self): + return self.consumer + + def get_token(self): + return self.token + + def fetch_request_token(self, oauth_request): + """-> OAuthToken.""" + raise NotImplementedError + + def fetch_access_token(self, oauth_request): + """-> OAuthToken.""" + raise NotImplementedError + + def access_resource(self, oauth_request): + """-> Some protected resource.""" + raise NotImplementedError + + +class OAuthDataStore(object): + """A database abstraction used to lookup consumers and tokens.""" + + def lookup_consumer(self, key): + """-> OAuthConsumer.""" + raise NotImplementedError + + def lookup_token(self, oauth_consumer, token_type, token_token): + """-> OAuthToken.""" + raise NotImplementedError + + def lookup_nonce(self, oauth_consumer, oauth_token, nonce): + """-> OAuthToken.""" + raise NotImplementedError + + def fetch_request_token(self, oauth_consumer, oauth_callback): + """-> OAuthToken.""" + raise NotImplementedError + + def fetch_access_token(self, oauth_consumer, oauth_token, oauth_verifier): + """-> OAuthToken.""" + raise NotImplementedError + + def authorize_request_token(self, oauth_token, user): + """-> OAuthToken.""" + raise NotImplementedError + + +class OAuthSignatureMethod(object): + """A strategy class that implements a signature method.""" + def get_name(self): + """-> str.""" + raise NotImplementedError + + def build_signature_base_string(self, oauth_request, oauth_consumer, oauth_token): + """-> str key, str raw.""" + raise NotImplementedError + + def build_signature(self, oauth_request, oauth_consumer, oauth_token): + """-> str.""" + raise NotImplementedError + + def check_signature(self, oauth_request, consumer, token, signature): + built = self.build_signature(oauth_request, consumer, token) + return built == signature + + +class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod): + + def get_name(self): + return 'HMAC-SHA1' + + def build_signature_base_string(self, oauth_request, consumer, token): + sig = ( + escape(oauth_request.get_normalized_http_method()), + escape(oauth_request.get_normalized_http_url()), + escape(oauth_request.get_normalized_parameters()), + ) + + key = '%s&' % escape(consumer.secret) + if token: + key += escape(token.secret) + raw = '&'.join(sig) + return key, raw + + def build_signature(self, oauth_request, consumer, token): + """Builds the base signature string.""" + key, raw = self.build_signature_base_string(oauth_request, consumer, + token) + + # HMAC object. + try: + import hashlib # 2.5 + hashed = hmac.new(key, raw, hashlib.sha1) + except: + import sha # Deprecated + hashed = hmac.new(key, raw, sha) + + # Calculate the digest base 64. + return binascii.b2a_base64(hashed.digest())[:-1] + + +class OAuthSignatureMethod_PLAINTEXT(OAuthSignatureMethod): + + def get_name(self): + return 'PLAINTEXT' + + def build_signature_base_string(self, oauth_request, consumer, token): + """Concatenates the consumer key and secret.""" + sig = '%s&' % escape(consumer.secret) + if token: + sig = sig + escape(token.secret) + return sig, sig + + def build_signature(self, oauth_request, consumer, token): + key, raw = self.build_signature_base_string(oauth_request, consumer, + token) + return key
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/7digital-python/lib/oauth7digital.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,96 @@ +import httplib +import oauth +from lockerEndpoint import Locker + +class Oauth7digital(object): + key = None + + SERVER = 'api.7digital.com' + REQUEST_TOKEN_URL = 'https://%s/1.2/oauth/requesttoken' % SERVER + ACCESS_TOKEN_URL = 'https://%s/1.2/oauth/accesstoken' % SERVER + LOCKER_ENDPOINT_URL = 'http://%s/1.2/user/locker' %SERVER + + def __init__(self, key, secret, access_token = None): + self.key = key + self.secret = secret + self.access_token = access_token + + def request_token(self): + print '\nOAUTH STEP 1' + oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.__consumer(), http_url = self.REQUEST_TOKEN_URL, parameters={}) + print '\nMESSAGE:: %s' %oauth_request + oauth_request.sign_request(self.__signature_method(), self.__consumer(), None) + resp = self.__fetch_response(oauth_request, self.__secure_connection()) + + token = oauth.OAuthToken.from_string(resp) + return token + + def authorize_request_token(self, token): + AUTHORIZATION_URL = 'https://account.7digital.com/%s/oauth/authorise' % self.key + print '\nOAUTH STEP 2' + auth_url="%s?oauth_token=%s" % (AUTHORIZATION_URL, token.key) + + # auth url to go to + print 'Authorization URL:\n%s' % auth_url + oauth_verifier = raw_input('Please go to the above URL and authorize the app. Hit return when you have been authorized: ') + return True + + def request_access_token(self, token): + print '\nOAUTH STEP 3' + oauth_request = self.__sign_oauth_request(token, self.ACCESS_TOKEN_URL) + resp = self.__fetch_response(oauth_request, self.__secure_connection()) + + token = oauth.OAuthToken.from_string(resp) + return token + + def get_user_locker(self): + resp = self.__get_locker() + return Locker(resp).get_content() + + def get_artist_from_user_locker(self): + resp = self.__get_locker() + return Locker(resp).get_artists() + + def get_releases_from_user_locker(self): + resp = self.__get_locker() + return Locker(resp).get_releases() + + def get_tracks_from_user_locker(self): + resp = self.__get_locker() + return Locker(resp).get_tracks() + + def get_locker(self): + resp = self.__get_locker() + return Locker(resp).get_contents() + + def __get_locker(self): + oauth_request = self.__sign_oauth_request(self.access_token, self.LOCKER_ENDPOINT_URL) + resp = self.__fetch_response(oauth_request, self.__connection()) + return resp + + def __sign_oauth_request(self, token, url_end_point): + oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.__consumer(), token=token, http_url = url_end_point, parameters={}) + + oauth_request.sign_request(self.__signature_method(), self.__consumer(), token) + return oauth_request + + def __consumer(self): + return oauth.OAuthConsumer(self.key, self.secret) + + def __signature_method(self): + return oauth.OAuthSignatureMethod_HMAC_SHA1() + + def __secure_connection(self): + return httplib.HTTPSConnection(self.SERVER) + + def __connection(self): + return httplib.HTTPConnection(self.SERVER) + + def __fetch_response(self, oauth_request, connection): + url = oauth_request.to_url() + connection.request(oauth_request.http_method, url) + response = connection.getresponse() + result = response.read() + + return result +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/7digital-python/lib/py7digital.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,925 @@ +#!/usr/bin/env python +"""A python interface to 7Digital web service (non-premium services)""" +import os +import urllib2, urllib +import re +import urlparse +from xml.dom import minidom +try: + from hashlib import md5 +except ImportError: + from md5 import md5 + +__name__ = 'py7digital' +__doc__ = 'A python interface to 7Digital web service' +__author__ = 'Oscar Celma, Pau Capella' +__version__ = '0.0.1' +__license__ = 'GPL' +__maintainer__ = 'Oscar Celma' +__email__ = 'ocelma@bmat.com' +__status__ = 'Beta' + +API_VERSION = '1.2' +HOST_NAME = 'api.7digital.com/' + API_VERSION +OAUTHKEY = '' #TODO Put your oauth key here +COUNTRY = '' # ISO Country + +__cache_dir = './cache' # Set cache directory +__cache_enabled = False # Enable cache? if set to True, make sure that __cache_dir exists! (e.g. $ mkdir ./cache) + +class ServiceException(Exception): + """Exception related to the web service.""" + + def __init__(self, type, message): + self._type = type + self._message = message + + def __str__(self): + return self._type + ': ' + self._message + + def get_message(self): + return self._message + + def get_type(self): + return self._type + +class _Request(object): + """Representing an abstract web service operation.""" + + def __init__(self, method_name, params): + self.params = params + self.method = method_name + + def _download_response(self): + """Returns a response""" + data = [] + for name in self.params.keys(): + data.append('='.join((name, urllib.quote_plus(self.params[name].replace('&', '&').encode('utf8'))))) + data = '&'.join(data) + + url = HOST_NAME + parsed_url = urlparse.urlparse(url) + if not parsed_url.scheme: + url = "http://" + url + url += self.method + '?oauth_consumer_key=' + OAUTHKEY + '&' + if COUNTRY: + url += 'country=' + COUNTRY + '&' + url += data + #print url + + request = urllib2.Request(url) + response = urllib2.urlopen(request) + return response.read() + + def execute(self, cacheable=False): + try: + if is_caching_enabled() and cacheable: + response = self._get_cached_response() + else: + response = self._download_response() + return minidom.parseString(response) + except urllib2.HTTPError, e: + raise self._get_error(e.fp.read()) + + def _get_cache_key(self): + """Cache key""" + keys = self.params.keys()[:] + keys.sort() + string = self.method + for name in keys: + string += name + string += self.params[name] + return get_md5(string) + + def _is_cached(self): + """Returns True if the request is available in the cache.""" + return os.path.exists(os.path.join(_get_cache_dir(), self._get_cache_key())) + + def _get_cached_response(self): + """Returns a file object of the cached response.""" + if not self._is_cached(): + response = self._download_response() + response_file = open(os.path.join(_get_cache_dir(), self._get_cache_key()), "w") + response_file.write(response) + response_file.close() + return open(os.path.join(_get_cache_dir(), self._get_cache_key()), "r").read() + + def _get_error(self, text): + return ServiceException('Error', text) + raise + + +class _BaseObject(object): + """An abstract webservices object.""" + + def __init__(self, method): + self._method = method + self._xml = None + + def _request(self, method_name , cacheable = False, params = None): + if not params: + params = self._get_params() + return _Request(method_name, params).execute(cacheable) + + def _get_params(self): + return dict() + + +class Artist(_BaseObject): + """ A 7digital artist """ + + def __init__(self, id): + _BaseObject.__init__(self, '/artist/') + + self.name = None + self.id = id + self._url = None + self._image = None + self._albums = None + self._top_tracks = None + self._tags = None + self._recommended_albums = None + + def __repr__(self): + return self.get_name().encode('utf8') + + def __eq__(self, other): + return self.get_id() == other.get_id() + + def __ne__(self, other): + return self.get_id() != other.get_id() + + def get_id(self): + """ Returns the 7digital artist id """ + return self.id + + def get_name(self): + """ Returns the name of the artist """ + if self.name is None: + self.name = '' + try: + if not self._xml : self._xml = self._request(self._method, + 'details', True, {'artistid': self.id}) + self.name = _extract(self._xml, 'artist', 1) or '' + except: + return self.name + return self.name + + def set_name(self, name) : + self.name = name + + def get_image(self): + """ Returns the image url of an artist """ + if self._image is None : + if not self._xml : self._xml = self._request(self._method + 'details', True, {'artistid': self.id}) + self._image = _extract(self._xml, 'image') + return self._image + + def set_image(self, image) : + self._image = image + + def get_url(self): + """ Returns the url of an artist """ + if self._url is None : + if not self._xml : self._xml = self._request(self._method + 'details', True, {'artistid': self.id}) + self._url = _extract(self._xml, 'url') + return self._url + + def set_url(self, url) : + self._url = url + + def get_tags(self, pageSize=10): + """ Returns the tags of an artist """ + if self._tags is None: + self._tags = [] + xml = self._request(self._method + 'tags', True, {'artistid': self.id}) + for node in xml.getElementsByTagName('tag') : + self._tags.append(_get_tag(node)) + if self._tags: + self._tags.sort() + return self._tags[:pageSize] + + def get_albums(self, pageSize=10): + """ Returns the albums of an artist """ + if self._albums is not None: return self._albums + + results = [] + xml = self._request(self._method + 'releases', True, {'artistid': self.id, 'pageSize': str(pageSize)}) + for node in xml.getElementsByTagName('release'): + album = _get_album(node, self) + results.append(album) + self._albums = results + return self._albums + + def get_recommended_albums(self, pageSize=10): + """ Returns a list of recommended albums based on the seed artist """ + if self._recommended_albums is not None: return self._recommended_albums + + results = [] + xml = self._request('/release/recommend', True, {'artistid': self.id, 'pageSize': str(pageSize)}) # TODO if country is set gives different results + for node in xml.getElementsByTagName('release'): + results.append(_get_album(node, _get_artist(node.getElementsByTagName('artist')[0]))) + self._recommended_albums = results + return self._recommended_albums + + def get_top_tracks(self, pageSize=10): + """ Returns the top tracks of an artist """ + if self._top_tracks is not None: return self._top_tracks + + results = [] + xml = self._request(self._method + 'toptracks', True, {'artistid': self.id, 'pageSize': str(pageSize)}) + for node in xml.getElementsByTagName('track'): + results.append(_get_track(node, None, self)) + self._top_tracks = results + return self._top_tracks + +class Album(_BaseObject): + """ A 7digital album """ + def __init__(self, id=HOST_NAME): + _BaseObject.__init__(self, '/release/') + self.id = id + self.artist = None + self.title = None + + self._url = None + self._type = None + self._barcode = None + self._year = None + self._image = None + self._label = None + self._tags = None + self._tracks = None + self._similar = None + self._release_date = None + self._added_date = None + + def __repr__(self): + if self.get_artist(): + return self.get_artist().get_name().encode('utf8') + ' - ' + self.get_title().encode('utf8') + return self.get_title().encode('utf8') + + def __eq__(self, other): + return self.get_id() == other.get_id() + + def __ne__(self, other): + return self.get_id() != other.get_id() + + def get_id(self): + """ Returns the 7digital album id """ + return self.id + + def get_title(self): + """ Returns the name of the album """ + if self.title is None: + if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) + self.title = _extract(self._xml, 'release', 1) + return self.title + + def set_title(self, title): + if title is None: + title = '' + self.title = title + + def get_type(self): + """ Returns the type (CD, DVD, etc.) of the album """ + if self._type is None: + if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) + self._type = _extract(self._xml, 'type') + return self._type + + def set_type(self, type): + self._type = type + + def get_year(self): + """ Returns the year of the album """ + if self._year is None: + if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) + self._year = _extract(self._xml, 'year') + return self._year + + def set_year(self, year): + self._year = year + + def get_barcode(self): + """ Returns the barcode of the album """ + if self._barcode is None: + if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) + self.barcode = _extract(self._xml, 'barcode') + return self._barcode + + def set_barcode(self, barcode): + self._barcode = barcode + + def get_url(self): + """ Returns the url of the album """ + if self._url is None : + if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) + self._url = _extract(self._xml, 'url') + return self._url + + def set_url(self, url) : + self._url = url + + def get_label(self): + """ Returns the label of the album """ + if self._label is None: + if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) + self.set_label(_get_label(self._xml.getElementsByTagName('label'))) + return self._label + + def set_label(self, label): + self._label = label + + def get_release_date(self): + """ Returns the release date of the album """ + if self._release_date is None : + if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) + self._release_date = _extract(self._xml, 'releaseDate') + return self._release_date + + def set_release_date(self, release_date): + self._release_date = release_date + + def get_added_date(self): + """ Returns the added date of the album """ + if self._added_date is None : + if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) + self._added_date = _extract(self._xml, 'addedDate') + return self._added_date + + def set_added_date(self, added_date): + self._added_date = added_date + + def get_artist(self): + """ Returns the Artist of the album """ + if not self.artist: + self.set_artist(_get_artist(self._xml.getElementsByTagName('artist'))) + return self.artist + + def set_artist(self, artist): + """ Sets the Artist object of the track """ + self.artist = artist + + def get_image(self): + """ Returns album image url """ + if self._image is None: + if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) + self._image = _extract(self._xml, 'release_small_image') + return self._image + + def set_image(self, image): + if image is None: image = '' + self._image = image + + def get_tags(self, pageSize=10): + """ Returns the tags of the album """ + if self._tags is None: + self._tags = [] + xml = self._request(self._method + 'tags', True, {'releaseid': self.id}) + for node in xml.getElementsByTagName('tag') : + self._tags.append(_get_tag(node)) + if self._tags: + self._tags.sort() + return self._tags[:pageSize] + + def get_tracks(self, pageSize=10): + """ Returns the tracks of the album """ + if self._tracks is not None: return self._tracks + + results = [] + xml = self._request(self._method + 'tracks', True, {'releaseid': self.id, 'pageSize': str(pageSize)}) + for node in xml.getElementsByTagName('track'): + if self.artist is None: + self.set_artist(_get_artist(node.getElementsByTagName('artist'))) + track = _get_track(node, self, self.get_artist()) + results.append(track) + self._tracks = results + return self._tracks + + def get_similar(self, pageSize=10): + """ Returns a list similar albums """ + if self._similar is not None: return self._similar + + results = [] + xml = self._request(self._method + 'recommend', True, {'releaseid': self.id, 'pageSize': str(pageSize)}) + for node in xml.getElementsByTagName('release'): + album = _get_album(node, _get_artist(node.getElementsByTagName('artist')[0])) + if self == album: + continue #Same album! + results.append(album) + self._similar = results + return self._similar + + +class Track(_BaseObject): + """ A Bmat track. """ + def __init__(self, id, artist=None): + _BaseObject.__init__(self, '/track/') + + if isinstance(artist, Artist): + self.artist = artist + else: + self.artist = None + self.id = id + self.title = None + self.artist = None + self.album = None + + self._isrc = None + self._url = None + self._preview = 'http://api.7digital.com/1.2/track/preview?trackid=' + self.id + self._image = None + self._tags = None + self._duration = None + self._version = None + self._explicit = None + self._position = None + + def __repr__(self): + return self.get_artist().get_name().encode('utf8') + ' - ' + self.get_title().encode('utf8') + + def __eq__(self, other): + return self.get_id() == other.get_id() + + def __ne__(self, other): + return self.get_id() != other.get_id() + + def get_id(self): + """ Returns the track id """ + return self.id + + def get_title(self): + """ Returns the track title """ + if self.title is None: + if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id}) + self.title = _extract(self._xml, 'track', 1) + return self.title + + def set_title(self, title): + self.title = title + + def get_isrc(self): + """ Returns the ISRC of the track """ + if self._isrc is None: + if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id}) + self._isrc = _extract(self._xml, 'isrc') + return self._isrc + + def set_isrc(self, isrc): + self._isrc = isrc + + def get_url(self): + """ Returns the url of the track """ + if self._url is None : + if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id}) + self._url = _extract(self._xml, 'url') + return self._url + + def set_url(self, url): + self._url = url + + def get_audio(self): + return self.get_preview() + + def get_preview(self): + """ Returns the url of the track """ + if self._preview is None : + if not self._xml : self._xml = self._request(self._method + 'preview', True, {'trackid': self.id}) + self._preview = _extract(self._xml, 'url') + return self._preview + + def get_duration(self): + """ Returns the duration of the track """ + if self._duration is None : + if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id}) + self._duration = _extract(self._xml, 'duration') + return self._duration + + def set_duration(self, duration): + self._duration = duration + + def get_position(self): + """ Returns the track number in the release """ + if self._position is None : + if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id}) + self._position = _extract(self._xml, 'trackNumber') + return self._position + + def set_position(self, track_number): + self._position = track_number + + def get_explicit(self): + """ Returns whether the track contains explicit content """ + if self._explicit is None : + if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id}) + self._explicit = _extract(self._xml, 'explicitContent') + return self._explicit + + def set_explicit(self, explicit): + self._explicit = explicit + + def get_version(self): + """ Returns the version of the track """ + if self._version is None : + if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id}) + self._version = _extract(self._xml, 'version') + return self._version + + def set_version(self, version): + self._version = version + + def get_artist(self): + """ Returns the Artist of the track """ + if not self.artist: + self.set_artist(_get_artist(self._xml.getElementsByTagName('artist'))) + return self.artist + + def set_artist(self, artist): + """ Sets the Artist object of the track """ + self.artist = artist + + def get_album(self): + """ Returns the associated Album object """ + if not self.album: + self.set_album(_get_album(self._xml.getElementsByTagName('release'))) + return self.album + + def set_album(self, album): + """ Sets the Album object of the track """ + self.album = album + + +class Tag(_BaseObject): + """ A Tag """ + def __init__(self, id): + _BaseObject.__init__(self, '/tag/') + + self.id = id + self.name = None + self._url = None + + def __repr__(self): + return self.get_name().encode('utf8') + + def __eq__(self, other): + return self.get_id() == other.get_id() + + def __ne__(self, other): + return self.get_id() != other.get_id() + + def get_id(self): + """ Returns the tag id """ + return self.id + + def get_name(self): + """ Returns the tag name """ + if self.name is None: + if not self._xml : self._xml = self._request(self._method + 'details', True, {'tagid': self.id}) + self.name = _extract(self._xml, 'name') + return self.name + + def set_name(self, name): + self.name = name + + def get_url(self): + """ Returns the url of the tag""" + if self._url is None: + if not self._xml : self._xml = self._request(self._method + 'details', True, {'tagid': self.id}) + self._url = _extract(self._xml, 'url') + return self._url + + def set_url(self, url): + self._url = url + +class Label(_BaseObject): + """ A Label """ + def __init__(self, id): + _BaseObject.__init__(self, '') + + self.id = id + self.name = None + + def __repr__(self): + return self.get_name().encode('utf8') + + def __eq__(self, other): + return self.get_id() == other.get_id() + + def __ne__(self, other): + return self.get_id() != other.get_id() + + def get_id(self): + """ Returns the label id """ + return self.id + + def get_name(self): + """ Returns the label name """ + return self.name + + def set_name(self, name): + self.name = name + + +class _Search(_BaseObject): + """ An abstract class for search """ + + def __init__(self, method, search_terms, xml_tag): + _BaseObject.__init__(self, method) + + self._search_terms = search_terms + self._xml_tag = xml_tag + + self._results_per_page = 10 + if self._search_terms.has_key('pageSize'): + self._results_per_page = str(self._search_terms['pageSize']) + else: + self._search_terms['pageSize'] = str(self._results_per_page) + self._last_page_index = 0 + self._hits = None + + def get_total_result_count(self): + if self._hits is not None: + return self._hits + params = self._get_params() + params['pageSize'] = '1' + xml = self._request(self._method, True, params) + hits = int(_extract(xml, 'totalItems')) + self._hits = hits + return self._hits + + def _get_params(self): + params = {} + for key in self._search_terms.keys(): + params[key] = self._search_terms[key] + return params + + def _retrieve_page(self, page_index): + """ Returns the xml nodes to process """ + params = self._get_params() + + if page_index != 0: + #offset = self._results_per_page * page_index + params["page"] = str(page_index) + + doc = self._request(self._method, True, params) + return doc.getElementsByTagName(self._xml_tag)[0] + + def _retrieve_next_page(self): + self._last_page_index += 1 + return self._retrieve_page(self._last_page_index) + + def has_results(self): + return self.get_total_result_count() > (self._results_per_page * self._last_page_index) + + def get_next_page(self): + master_node = self._retrieve_next_page() + return self._get_results(master_node) + + def get_page(self, page=0): + if page < 0: page = 0 + if page > 0: page = page-1 + + master_node = self._retrieve_page(page) + return self._get_results(master_node) + + +class ArtistSearch(_Search): + """ Search artists """ + + def __init__(self, query): + _Search.__init__(self, '/artist/search', {'q': query}, 'searchResults') + + def _get_results(self, master_node): + results = [] + for node in master_node.getElementsByTagName('artist'): + artist = _get_artist(node) + results.append(artist) + return results + + +class ArtistBrowse(_Search): + """ Browse artists """ + + def __init__(self, letter): + _Search.__init__(self, '/artist/browse', {'letter': letter}, 'artists') + + def _get_results(self, master_node): + results = [] + for node in master_node.getElementsByTagName('artist'): + artist = _get_artist(node) + results.append(artist) + return results + +class ArtistDetail(_Search): + def __init__(self, artist_id): + _Search.__init__(self, '/artist/details', {'artistid': artist_id}, 'artist') + + def _get_results(self, master_node): + results = [] + node = master_node + + artist = _get_artist(node) + + results.append(artist) + return results + +class AlbumSearch(_Search): + """ Search albums """ + + def __init__(self, query): + _Search.__init__(self, '/release/search', {'q': query}, 'searchResults') + + def _get_results(self, master_node): + results = [] + for node in master_node.getElementsByTagName('release'): + artist = _get_artist(node.getElementsByTagName('artist')[0]) + album = _get_album(node, artist) + results.append(album) + return results + +class AlbumCharts(_Search): + """ Chart albums """ + + def __init__(self, period, todate): + _Search.__init__(self, '/release/chart', {'period': period, 'todate': todate}, 'chart') + + def _get_results(self, master_node): + results = [] + for node in master_node.getElementsByTagName('release'): + artist = _get_artist(node.getElementsByTagName('artist')[0]) + album = _get_album(node, artist) + results.append(album) + return results + +class AlbumReleases(_Search): + """ Release albums by date """ + + def __init__(self, fromdate, todate): + _Search.__init__(self, '/release/bydate', {'fromDate': fromdate, 'toDate': todate}, 'releases') + + def _get_results(self, master_node): + results = [] + for node in master_node.getElementsByTagName('release'): + artist = _get_artist(node.getElementsByTagName('artist')[0]) + album = _get_album(node, artist) + results.append(album) + return results + +class TrackSearch(_Search): + """ Search for tracks """ + + def __init__(self, query): + _Search.__init__(self, '/track/search', {'q': query}, 'searchResults') + + def _get_results(self, master_node): + results = [] + for node in master_node.getElementsByTagName('track'): + artist = _get_artist(node.getElementsByTagName('artist')[0]) + album = _get_album(node.getElementsByTagName('release')[0], artist) + track = _get_track(node, album, artist) + results.append(track) + return results + +def get_artist_detail(artistId): + return ArtistDetail(artistId) + +def search_artist(query): + """Search artists by query. Returns an ArtistSearch object. + Use get_next_page() to retrieve sequences of results.""" + return ArtistSearch(query) + +def browse_artists(letter): + """Browse artists by letter [a..z]. Returns an ArtistBrowse object. + Use get_next_page() to retrieve sequences of results.""" + return ArtistBrowse(letter) + +def search_album(query): + """Search albums by query. Returns the albumSearch object. + Use get_next_page() to retrieve sequences of results.""" + return AlbumSearch(query) + +def album_charts(period, todate): + """Get chart albums in a given period of time """ + return AlbumCharts(period, todate) + +def album_releases(fromdate, todate): + """Get releases in a given period of time""" + return AlbumReleases(fromdate, todate) + +def search_track(query): + """Search tracks by query. Returns a TrackSearch object. + Use get_next_page() to retrieve sequences of results.""" + return TrackSearch(query) + + +# XML +def _extract(node, name, index = 0): + """Extracts a value from the xml string""" + try: + nodes = node.getElementsByTagName(name) + + if len(nodes): + if nodes[index].firstChild: + return nodes[index].firstChild.data.strip() + else: + return None + except: + return None + +def _extract_all(node, name, pageSize_count = None): + """Extracts all the values from the xml string. It returns a list.""" + results = [] + for i in range(0, len(node.getElementsByTagName(name))): + if len(results) == pageSize_count: + break + results.append(_extract(node, name, i)) + return results + +def _get_artist(xml): + artist_id = xml.getAttribute('id') + artist = Artist(artist_id) + artist.set_name(_extract(xml, 'name')) + return artist + +def _get_album(xml, artist): + album_id = xml.getAttribute('id') + album = Album(album_id) + album.set_artist(artist) + album.set_title(_extract(xml, 'title')) + album.set_type(_extract(xml, 'type')) + album.set_image(_extract(xml, 'image')) + album.set_year(_extract(xml, 'year')) + album.set_barcode(_extract(xml, 'barcode')) + album.set_url(_extract(xml, 'url')) + album.set_release_date(_extract(xml, 'releaseDate')) + album.set_added_date(_extract(xml, 'addedDate')) + #TODO price, formats, artist appears_as + try : + album.set_label(_get_label(xml.getElementsByTagName('label')[0])) #In some cases that are albums with no label (record company) attached! :-( + except: + pass + return album + +def _get_track(xml, album, artist): + track_id = xml.getAttribute('id') + track = Track(track_id) + track.set_title(_extract(xml, 'title')) + track.set_url(_extract(xml, 'url')) + track.set_isrc(_extract(xml, 'isrc')) + track.set_duration(_extract(xml, 'duration')) + track.set_position(_extract(xml, 'trackNumber')) + track.set_explicit(_extract(xml, 'explicitContent')) + track.set_version(_extract(xml, 'version')) + track.set_album(album) + track.set_artist(artist) + #TODO price, formats + return track + +def _get_tag(xml): + tag = Tag(_extract(xml, 'text')) + tag.set_name(tag.get_id()) + return tag + +def _get_label(xml): + label = '' + try: + label = Label(xml.getAttribute('id')) + label.set_name(_extract(xml, 'name')) + except: + pass + return label + +# CACHE +def enable_caching(cache_dir = None): + global __cache_dir + global __cache_enabled + + if cache_dir == None: + import tempfile + __cache_dir = tempfile.mkdtemp() + else: + if not os.path.exists(cache_dir): + os.mkdir(cache_dir) + __cache_dir = cache_dir + __cache_enabled = True + +def disable_caching(): + global __cache_enabled + __cache_enabled = False + +def is_caching_enabled(): + """Returns True if caching is enabled.""" + global __cache_enabled + return __cache_enabled + +def _get_cache_dir(): + """Returns the directory in which cache files are saved.""" + global __cache_dir + global __cache_enabled + return __cache_dir + +def get_md5(text): + """Returns the md5 hash of a string.""" + hash = md5() + hash.update(text.encode('utf8')) + return hash.hexdigest() +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/7digital-python/lockerEndpoint.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,118 @@ +#!/usr/bin/env python +"""A python interface to 7digital's locker endpoint""" +import os +from xml.dom.minidom import parseString + + +class Locker(object): + def __init__(self, xml_response): + self.xml_response = parseString(xml_response) + + def get_contents(self): + results = [] + locker_items = self.xml_response.getElementsByTagName('lockerRelease') + + for item in locker_items: + results.append(LockerItem(item)) + + return results + + def get_artists(self): + results = [] + artist_nodes = self.xml_response.getElementsByTagName('artist') + + for artist in artist_nodes: + results.append(LockerArtist(artist)) + + return results + + def get_releases(self): + results = [] + release_nodes = self.xml_response.getElementsByTagName('release') + for release in release_nodes: + results.append(LockerRelease(release)) + + return results + + def get_tracks(self): + results = [] + track_nodes = self.xml_response.getElementsByTagName('lockerTrack') + for track in track_nodes: + results.append(LockerTrack(track)) + + return results + +class _LockerBase(object): + def extract(self, node, name, index = 0): + """Extracts a value from the xml string""" + try: + nodes = node.getElementsByTagName(name) + + if len(nodes): + if nodes[index].firstChild: + return nodes[index].firstChild.data.strip() + else: + return None + except: + return None + +class LockerItem(_LockerBase): + def __init__(self, xml): + self.release = LockerRelease(xml.getElementsByTagName('release')[0]) + self.tracks = self.__get_release_tracks(xml.getElementsByTagName('lockerTrack')) + + def __get_release_tracks(self, tracks): + result = [] + for track in tracks: + result.append(LockerTrack(track)) + return result + +class LockerTrack(_LockerBase): + def __init__(self, xml): + self.track = Track(xml.getElementsByTagName('track')[0]) + self.remaining_downloads = self.extract(xml, 'remainingDownloads') + self.purchaseDate = self.extract(xml, 'purchaseDate') + self.download_urls = self.__get_download_urls(xml.getElementsByTagName('downloadUrls')) + + def __get_download_urls(self, urls): + result = [] + for url in urls: + result.append(DownloadUrls(url)) + return result + +class DownloadUrls(_LockerBase): + def __init__(self, xml): + self.url = self.extract(xml, 'url') + self.format = Format(xml.getElementsByTagName('format')[0]) + +class Format(_LockerBase): + def __init__(self, xml): + self.id = xml.getAttribute('id') + self.file_format = self.extract(xml, 'fileFormat') + self.bit_rate = self.extract(xml, 'bitRate') + +class Track(_LockerBase): + def __init__(self, xml): + self.id = xml.getAttribute('id') + self.title = self.extract(xml, 'title') + self.version = self.extract(xml, 'version') + self.artist = LockerArtist(xml.getElementsByTagName('artist')[0]) + self.url = self.extract(xml, 'url') + +class LockerRelease(_LockerBase): + def __init__(self, xml): + self.id = xml.getAttribute('id') + self.title = self.extract(xml, 'title') + self.type = self.extract(xml, 'type') + self.artist = LockerArtist(xml.getElementsByTagName('artist')[0]) + self.url = self.extract(xml, 'url') + self.image = self.extract(xml, 'image') + self.release_date = self.extract(xml, 'releaseDate') + +class LockerArtist(_LockerBase): + def __init__(self, xml): + self.id = xml.getAttribute('id') + self.name = self.extract(xml, 'name') + self.url = self.extract(xml, 'url') + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/7digital-python/tests/artist_tests.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,11 @@ +import py7digital + +def test_get_artist_detail_returns_keane(): + artists = py7digital.get_artist_detail('1') + for artist in artists.get_next_page(): + assert artist.get_name().lower() == "keane" + +def test_artist_search_returns_names_containing_stone(): + artists = py7digital.search_artist("stones") + for i in artists.get_next_page(): + assert i.get_name().lower().find("stones") > -1 \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/7digital-python/tests/search_acceptance_tests.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,62 @@ +import py7digital + +#Search artist +results = py7digital.search_artist('stones') +print results.get_total_result_count() +for artist in results.get_next_page(): + print artist.get_name() #, artist.get_image(), artist.get_url(), artist.get_tags() + print '\tTop tracks:' + for top_track in artist.get_top_tracks(): + print '\t\t', top_track.get_title(), top_track.get_isrc(), top_track.get_duration(), top_track.get_position(), top_track.get_explicit(), top_track.get_version() + print '\tRec. Albums:' + for rec_album in artist.get_recommended_albums(): + print '\t\t', rec_album, rec_album.get_year() #, album.get_barcode(), album.get_type(), album.get_artist(), album.get_tags(), album.get_label() + for album in artist.get_albums(5): + print '\t', album, album.get_year(), album.get_barcode(), album.get_type(), album.get_artist(), album.get_tags(), album.get_label(), album.get_release_date(), album.get_added_date() + for sim_album in album.get_similar(): + print '\t\tSimilar:', sim_album, sim_album.get_year(), sim_album.get_artist() + for track in album.get_tracks(): + print '\t\t', track, track.get_isrc() #, track.get_url(), track.get_audio() + +#Browse artists starting with 'J' +results = py7digital.browse_artists('j') +print results.get_total_result_count() +for artist in results.get_next_page(): + print artist.get_name() #, artist.get_image(), artist.get_url(), artist.get_tags() + for album in artist.get_albums(2): + print '\t', album, album.get_year() #album.get_barcode(), album.get_type(), album.get_artist(), album.get_tags(), album.get_label() + for track in album.get_tracks(): + print '\t\t', track.get_title(), track.get_isrc() #, track.get_url(), track.get_audio() + +#Search albums +searcher = py7digital.search_album('u2') +print searcher.get_total_result_count() +while searcher.has_results(): + for album in searcher.get_next_page(): + print album, album.get_similar() + +#Search tracks +searcher = py7digital.search_track('u2 one') +print searcher.get_total_result_count() +while searcher.has_results(): + for track in searcher.get_next_page(): + print track + +# New releases in a given period of time +results = py7digital.album_releases('20100901', '20100924') +for album in results.get_next_page(): + print album, album.get_year(), album.get_barcode(), album.get_type(), album.get_artist(), album.get_tags(), album.get_label(), album.get_release_date(), album.get_added_date() + for sim_album in album.get_similar(): + print '\tSimilar:', sim_album, sim_album.get_year(), sim_album.get_artist() + for track in album.get_tracks(): + print '\t', track, track.get_isrc() #, track.get_url(), track.get_audio() + +# Album charts in a given period of time +results = py7digital.album_charts('month', '20100901') +for album in results.get_next_page(): + print album, album.get_year(), album.get_barcode(), album.get_type(), album.get_artist(), album.get_tags(), album.get_label(), album.get_release_date(), album.get_added_date() + for sim_album in album.get_similar(): + print '\tSimilar:', sim_album, sim_album.get_year(), sim_album.get_artist() + for track in album.get_tracks(): + print '\t', track, track.get_isrc() #, track.get_url(), track.get_audio() +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/CHANGES.rst Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,18 @@ +Changes +======= + +0.9 + * Added support for XML response messages. + * Deprecated **musixmatch_apiversion** environment variable in favour of + **musixmatch_wslocation**. This will let developers to setup different api + location and a testing environment. (musicae-ipsum under construction) + * **apikey** and **format** in api.Method are now lookedup as follow: + + 1. Positional arguments. + 2. Package wide variables. + 3. Operating system environment. + + Keyword arguments are not considered any more. +0.8 + * API interface with JSON response support. + * Low level objects library built around JSON response messages.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/README.rst Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,74 @@ +Quick start +=========== + +1. First thing first, read the documentation at http://developer.musixmatch.com . +2. Get an api key by signing up at http://developer.musixmatch.com/mmplans . +3. Install the musixmatch package +4. Run the python prompt + +>>> import musixmatch +>>> apikey = '<your-apikey>' +>>> try: +... chart = musixmatch.ws.track.chart.get(country='it', apikey=apikey) +... except musixmatch.api.Error, e: +... pass + +It's that simple. Last, you can brows this documentation and have fun with the other modules. + +Building / Installing +===================== + +You can just use setup.py to build and install python-musixmatch:: + + prompt $ python setup.py bdist_egg + +Once built, you can use easy_install on the python egg. + +Documentation +============= +You can read documentation online_, or generate your own local copy using +`Sphinx`_ trough the setup.py:: + + prompt $ python setup.py build_sphinx + +.. _Sphinx: http://sphinx.pocoo.org +.. _online: http://projects.monkeython.com/musixmatch/python-musixmatch/html/index.html + +Unit testing +============ +python-musixmatch comes with some essential unit testing. If you set up +**musixmatch_apikey** environment variable, and have internet connection, you +can also run some tests on API calls:: + + prompt $ python setup.py test + +Caching support +=============== + +Applications using python-musixmatch may take advantage of standard +urllib support for **http_proxy**, so they can just set up the proper +environment variable: + +http_proxy + the complete HTTP proxy URL to use in queries. + +Considering all the available HTTP proxy solutions, I'm reluctant to implement +a further caching support. Though i can consider serialization support. + +Environment variables +===================== + +python-musixmatch takes advantage of operating system environment to get +**apikey**, **format** and api **version** values to use in API calls: + +musixmatch_apikey + the apikey value to use in query strings +musixmatch_format + the response message format. For example: json +musixmatch_wslocation + the webservice base url. For example: http://api.musixmatch.com/ws/1.1 +musixmatch_apiversion + the api version to use in queryes. For example: 1.1. Use of + **musixmatch_apiversion** was deprecated in favour of + **musixmatch_wslocation**. +
Binary file musixmatch-master/Wake Me Up When September Ends (Feat. John Gallagher Jr., Michael Esper, Stark Sands, Company) [Album Version].mp3 has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/build/lib/musixmatch/__init__.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,44 @@ +import os +__author__ = "Luca De Vitis <luca@monkeython.com>" +__version__ = '0.9' +__copyright__ = "2011, %s " % __author__ +__license__ = """ + Copyright (C) %s + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +""" % __copyright__ +__doc__ = """ +:abstract: Python interface to Musixmatch API +:version: %s +:author: %s +:organization: Monkeython +:contact: http://www.monkeython.com +:copyright: %s +""" % (__version__, __author__, __license__) +__docformat__ = 'restructuredtext en' +__classifiers__ = [ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: GNU General Public License (GPL)', + 'Operating System :: OS Independent', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Software Development :: Libraries', +] +__all__ = [ + 'ws', 'api', 'base', + 'artist', 'track', 'lyrics', 'subtitle', 'album' +] + +apikey = os.environ.get('musixmatch_apikey', None) +format = os.environ.get('musixmatch_format', 'json')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/build/lib/musixmatch/album.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,62 @@ +""" +This module contains higher level classes to query Musixmatch API and build +simple dictionary-like objects representing an Album or an AlbumsCollection. + +>>> from musixmatch.album import Album, AlbumsCollection +>>> import musixmatch.api +>>> +>>> try: +... album = Album(album_id=292) +... collection = AlbumsCollection.fromArtist(country='it', page=1) +... except musixmatch.api.Error, e: +... pass +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch.base import Item, ItemsCollection +from musixmatch.ws import album, artist + +class Album(Item): + """ + This class build a :py:class:`dict` like object representing an album. It + can get album information through the :py:class:`musixmatch.api.Method` + **album.get** or from an already well-formed :py:class:`dict`. Create an + Album object based on a given keyword argument: + + :param album_id: musiXmatch album ID + :param album_data: an already well-formed :py:class:`dict` of album data. + + Once information are collected, the following keys are available: + + :keyword album_id: musiXmatch album ID + :keyword album_name: album name + :keyword album_release_date: album release date + :keyword album_release_type: type of the album + :keyword album_coverart_100x100: coverart URL + :keyword artist_id: album artist musiXmatch ID + :keyword artist_name: album artist name + """ + __api_method__ = album.get + +class AlbumsCollection(ItemsCollection): + """ + This class build a :py:class:`list` like object representing an albums + collection. It accepts :py:class:`dict` or :py:class:`Album` objects. + """ + __allowedin__ = Album + + @classmethod + def fromArtist(cls, **keywords): + """ + This classmethod builds an :py:class:`AlbumsCollection` from a + **artist.albums.get** :py:class:`musixmatch.api.Method` call. + + :param artist_id: album artist musiXmatch ID + :param g_album_name: group albums by name + :param s_release_date: sort albums by release date + :rtype: :py:class:`AlbumsCollection` + """ + return cls.fromResponseMessage(artist.albums.get(**keywords)) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/build/lib/musixmatch/api.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,389 @@ +""" This module define the base API classes. +""" +import musixmatch +from urllib import urlencode, urlopen +from contextlib import contextmanager +import os +try: + import json +except ImportError: + import simplejson as json +try: + from lxml import etree +except ImportError: + try: + import xml.etree.cElementTree as etree + except ImportError: + try: + import xml.etree.ElementTree as etree + except ImportError: + try: + import cElementTree as etree + except ImportError: + import elementtree.ElementTree as etree + +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +class Error(Exception): + """Base musiXmatch API error. + + >>> import musixmatch + >>> raise musixmatch.api.Error('Error message') + Traceback (most recent call last): + ... + Error: Error message + """ + def __str__(self): + return ': '.join(map(str, self.args)) + + def __repr__(self): + name = self.__class__.__name__ + return '%s%r' % (name, self.args) + +class ResponseMessageError(Error): + """Represents errors occurred while parsing the response messages.""" + +class ResponseStatusCode(int): + """ + Represents response message status code. Casting a + :py:class:`ResponseStatusCode` to :py:class:`str` returns the message + associated with the status code: + + >>> from musixmatch.api import ResponseStatusCode + >>> str(ResponseStatusCode(200)) + 'The request was successful.' + >>> str(ResponseStatusCode(401)) + 'Authentication failed, probably because of a bad API key.' + + The status code to description mapping is: + + +------+-----------------------------------------------------------+ + | Code | Description | + +======+===========================================================+ + | 200 | The request was successful. | + +------+-----------------------------------------------------------+ + | 400 | The request had bad syntax or was inherently | + | | impossible to be satisfied. | + +------+-----------------------------------------------------------+ + | 401 | Authentication failed, probably because of a bad API key. | + +------+-----------------------------------------------------------+ + | 402 | A limit was reached, either you exceeded per hour | + | | requests limits or your balance is insufficient. | + +------+-----------------------------------------------------------+ + | 403 | You are not authorized to perform this operation or | + | | the api version you're trying to use has been shut down. | + +------+-----------------------------------------------------------+ + | 404 | Requested resource was not found. | + +------+-----------------------------------------------------------+ + | 405 | Requested method was not found. | + +------+-----------------------------------------------------------+ + + Any other status code will produce a default message: + + >>> from musixmatch.api import ResponseStatusCode + >>> str(ResponseStatusCode(666)) + 'Unknown status code 666!' + + Casting a :py:class:`ResponseStatusCode` to :py:class:`bool` returns True if + status code is 200, False otherwise: + + >>> from musixmatch.api import ResponseStatusCode + >>> bool(ResponseStatusCode(200)) + True + >>> bool(ResponseStatusCode(400)) + False + >>> bool(ResponseStatusCode(666)) + False + + """ + __status__ = { + 200: "The request was successful.", + 400: "The request had bad syntax or was inherently " + \ + "impossible to be satisfied.", + 401: "Authentication failed, probably because of a bad API key.", + 402: "A limit was reached, either you exceeded per hour " + \ + "requests limits or your balance is insufficient.", + 403: "You are not authorized to perform this operation or " + \ + "the api version you're trying to use has been shut down.", + 404: "Requested resource was not found.", + 405: "Requested method was not found.", + } + + def __str__(self): + return self.__status__.get(self, 'Unknown status code %i!' % self) + + def __repr__(self): + return 'ResponseStatusCode(%i)' % self + + def __nonzero__(self): + return self == 200 + +class ResponseMessage(dict): + """ + Abstract class which provides a base class for formatted response. + """ + def __init__(self, response): + raise NotImplementedError + + @property + def status_code(self): + """ + Is the :py:class:`ResponseStatusCode` object representing the + message status code. + + :raises: :py:exc:`ValueError` if not set. + """ + raise NotImplementedError + + def __repr__(self): + return "%s('...')" % type(self).__name__ + +class JsonResponseMessage(ResponseMessage, dict): + """ + A :py:class:`ResponseMessage` subclass which behaves like a + :py:class:`dict` to expose the Json structure contained in the response + message. Parses the Json response message and build a proper python + :py:class:`dict` containing all the information. Also, setup a + :py:class:`ResponseStatusCode` by querying the :py:class:`dict` for the + *['header']['status_code']* item. + """ + def __init__(self, response): + try: + parsed = json.load(response) + except Exception, e: + raise ResponseMessageError(u'Invalid Json response message', e) + self.update(parsed['message']) + + def __str__(self): + s = json.dumps({ 'message': self }, sort_keys=True, indent=4) + return '\n'.join([l.rstrip() for l in s.splitlines()]) + + @property + def status_code(self): + """Overload :py:meth:`ResponseMessage.status_code`""" + return ResponseStatusCode(self['header']['status_code']) + +class XMLResponseMessage(ResponseMessage, etree.ElementTree): + """ + A :py:class:`ResponseMessage` subclass which exposes + :py:class:`ElementTree` methods to handle XML response + messages. Parses the XML response message and build a + :py:class:`ElementTree` instance. Also setup the a + :py:class:`ResponseStatusCode` by querying the *status_code* tag content. + + Casting a :py:class:`XMLResponseMessage` returns (actually re-builds) a + pretty printed string representing the XML API response message. + """ + + def __init__(self, response): + etree.ElementTree.__init__(self, None, response) + + def __str__(self): + s = StringIO() + self.wite(s) + return s.getvalue() + + @property + def status_code(self): + """Overload :py:meth:`ResponseMessage.status_code`""" + return ResponseStatusCode(self.findtext('header/status_code')) + +class QueryString(dict): + """ + A class representing the keyword arguments to be used in HTTP requests as + query string. Takes a :py:class:`dict` of keywords, and encode values + using utf-8. Also, the query string is sorted by keyword name, so that its + string representation is always the same, thus can be used in hashes. + + Casting a :py:class:`QueryString` to :py:class:`str` returns the urlencoded + query string: + + >>> from musixmatch.api import QueryString + >>> str(QueryString({ 'country': 'it', 'page': 1, 'page_size': 3 })) + 'country=it&page=1&page_size=3' + + Using :py:func:`repr` on :py:class:`QueryString` returns an evaluable + representation of the current instance, excluding apikey value: + + >>> from musixmatch.api import QueryString + >>> repr(QueryString({ 'country': 'it', 'page': 1, 'apikey': 'whatever'})) + "QueryString({'country': 'it', 'page': '1'})" + """ + def __init__(self, items=(), **keywords): + dict.__init__(self, items, **keywords) + for k in self: + self[k] = str(self[k]).encode('utf-8') + + def __str__(self): + return urlencode(self) + + def __repr__(self): + query = self.copy() + if 'apikey' in query: + del query['apikey'] + return 'QueryString(%r)' % query + + def __iter__(self): + """ + Returns an iterator method which will yield keys sorted by name. + Sorting allow the query strings to be used (reasonably) as caching key. + """ + keys = dict.keys(self) + keys.sort() + for key in keys: + yield key + + def values(self): + """Overloads :py:meth:`dict.values` using :py:meth:`__iter__`.""" + return tuple(self[k] for k in self) + + def keys(self): + """Overloads :py:meth:`dict.keys` using :py:meth:`__iter__`.""" + return tuple(k for k in self) + + def items(self): + """Overloads :py:meth:`dict.item` using :py:meth:`__iter__`.""" + return tuple((k, self[k]) for k in self) + + def __hash__(self): + return hash(str(self)) + + def __cmp__(self, other): + return cmp(hash(self), hash(other)) + +class Method(str): + """ + Utility class to build API methods name and call them as functions. + + :py:class:`Method` has custom attribute access to build method names like + those specified in the API. Each attribute access builds a new Method with + a new name. + + Calling a :py:class:`Method` as a function with keyword arguments, + builds a :py:class:`Request`, runs it and returns the result. If **apikey** + is undefined, environment variable **musixmatch_apikey** will be used. If + **format** is undefined, environment variable **musixmatch_format** will be + used. If **musixmatch_format** is undefined, jason format will be used. + + >>> import musixmatch + >>> artist = musixmatch.api.Method('artist') + >>> + >>> try: + ... chart = artist.chart.get(country='it', page=1, page_size=3) + ... except musixmatch.api.Error, e: + ... pass + """ + __separator__ = '.' + + def __getattribute__(self, name): + if name.startswith('_'): + return super(Method, self).__getattribute__(name) + else: + return Method(self.__separator__.join([self, name])) + + def __call__ (self, apikey=None, format=None, **query): + query['apikey'] = apikey or musixmatch.apikey + query['format'] = format or musixmatch.format + return Request(self, query).response + + def __repr__(self): + return "Method('%s')" % self + +class Request(object): + """ + This is the main API class. Given a :py:class:`Method` or a method name, a + :py:class:`QueryString` or a :py:class:`dict`, it can build the API query + URL, run the request and return the response either as a string or as a + :py:class:`ResponseMessage` subclass. Assuming the default web services + location, this class try to build a proper request: + + >>> from musixmatch.api import Request, Method, QueryString + >>> method_name = 'artist.chart.get' + >>> method = Method(method_name) + >>> keywords = { 'country': 'it', 'page': 1, 'page_size': 3 } + >>> query_string = QueryString(keywords) + >>> + >>> r1 = Request(method_name, keywords) + >>> r2 = Request(method_name, **keywords) + >>> r3 = Request(method_name, query_string) + >>> r4 = Request(method, keywords) + >>> r5 = Request(method, **keywords) + >>> r6 = Request(method, query_string) + + If **method** is string, try to cast it into a :py:class:`Method`. If + **query_string** is a :py:class:`dict`, try to cast it into a + :py:class:`QueryString`. If **query_string** is not specified, try to + use **keywords** arguments as a :py:class:`dict` and cast it into a + :py:class:`QueryString`. + + Turning the :py:class:`Request` into a :py:class:`str` returns the URL + representing the API request: + + >>> str(Request('artist.chart.get', { 'country': 'it', 'page': 1 })) + 'http://api.musixmatch.com/ws/1.1/artist.chart.get?country=it&page=1' + """ + def __init__ (self, api_method, query=(), **keywords): + self.__api_method = isinstance(api_method, Method) and \ + api_method or Method(api_method) + self.__query_string = isinstance(query, QueryString) and \ + query or QueryString(query) + self.__query_string.update(keywords) + self.__response = None + + @property + def api_method(self): + """The :py:class:`Method` instance.""" + return self.__api_method + + @property + def query_string(self): + """The :py:class:`QueryString` instance.""" + return self.__query_string + + @contextmanager + def _received(self): + """A context manager to handle url opening""" + try: + response = urlopen(str(self)) + yield response + finally: + response.close() + + @property + def response(self): + """ + The :py:class:`ResponseMessage` based on the **format** key in the + :py:class:`QueryString`. + """ + if self.__response is None: + + format = self.query_string.get('format') + ResponseMessageClass = { + 'json': JsonResponseMessage, + 'xml': XMLResponseMessage, + }.get(format, None) + + if not ResponseMessageClass: + raise ResponseMessageError("Unsupported format `%s'" % format) + + with self._received() as response: + self.__response = ResponseMessageClass(response) + + return self.__response + + def __repr__(self): + return 'Request(%r, %r)' % (self.api_method, self.query_string) + + def __str__(self): + return '%(ws_location)s/%(api_method)s?%(query_string)s' % { + 'ws_location': musixmatch.ws.location, + 'api_method': self.api_method, + 'query_string': self.query_string + } + + def __hash__(self): + return hash(str(self)) + + def __cmp__(self, other): + return cmp(hash(self), hash(other))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/build/lib/musixmatch/artist.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,80 @@ +""" +This module contains higher level classes to query Musixmatch API and build +simple dictionary-like objects representing an Artist or an ArtistsCollection. + +>>> from musixmatch.artist import Artist, ArtistsCollection +>>> import musixmatch.api +>>> +>>> try: +... artist = Artist(artist_id=292) +... collection = ArtistsCollection.fromChart(country='it', page=1) +... except musixmatch.api.Error, e: +... pass +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch.base import Item, ItemsCollection +from musixmatch.ws import artist + +class Artist(Item): + """ + This class build a :py:class:`dict` like object representing an artist. It + can get artist information through the :py:class:`musixmatch.api.Method` + **artist.get** or from an already well-formed :py:class:`dict`. Create an + Artist object based on a given keyword argument: + + :param artist_id: musiXmatch artist ID + :param artist_mbid: Musicbrainz artist ID + :param artist_data: an already well-formed :py:class:`dict` of artist data. + + Once information are collected, the following keys are available: + + :keyword artist_id: musiXmatch artist ID + :keyword artist_mbid: Musicbrainz artist ID + :keyword artist_name: Artist name + """ + __api_method__ = artist.get + +class ArtistsCollection(ItemsCollection): + """ + This class build a :py:class:`list` like object representing an artists + collection. It accepts :py:class:`dict` or :py:class:`Artist` objects. + """ + __allowedin__ = Artist + + @classmethod + def fromSearch(cls, **keywords): + """ + This classmethod builds an :py:class:`ArtistsCollection` from a + **artist.search** :py:class:`musixmatch.api.Method` call. + + :param q: a string that will be searched in every data field + (q_track, q_artist, q_lyrics) + :param q_track: words to be searched among track titles + :param q_artist: words to be searched among artist names + :param q_lyrics: words to be searched into the lyrics + :param page: requested page of results + :param page_size: desired number of items per result page + :param f_has_lyrics: exclude tracks without an available lyrics + (automatic if q_lyrics is set) + :param f_artist_id: filter the results by the artist_id + :param f_artist_mbid: filter the results by the artist_mbid + :rtype: :py:class:`ArtistsCollection` + """ + return cls.fromResponseMessage(artist.search(**keywords)) + + @classmethod + def fromChart(cls, **keywords): + """ + This classmethod builds an :py:class:`ArtistsCollection` from a + **artist.chart.get** :py:class:`musixmatch.api.Method` call. + + :param page: requested page of results + :param page_size: desired number of items per result page + :param country: the country code of the desired country chart + :rtype: :py:class:`ArtistsCollection` + """ + return cls.fromResponseMessage(artist.chart.get(**keywords)) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/build/lib/musixmatch/base.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,240 @@ +""" +This module contains tha base classes for the Musixmatch API generated content: + +* :py:class:`musixmatch.artist.Artist` +* :py:class:`musixmatch.artist.Album` +* :py:class:`musixmatch.track.Track` +* :py:class:`musixmatch.lyrics.Lyrics` +* :py:class:`musixmatch.subtitle.Subtitle` +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch import api +import pprint + +class Base(object): + """ + The very base (abstract) class of the musixmatch package. I want all + classes to implement :py:meth:`__str__` and :py:meth:`__repr__`. + + Casting an :py:class:`Base` into a :py:class:`str` returns a pretty printed + (maybe using :py:mod:`pprint`) string representing the object. + + Using :py:func:`repr` on an :py:class:`Base` returns an evaluable string + representing the instance, whenever it is reasonable. + + :py:class:`Base` instances are hashable. + """ + + @classmethod + def label(cls): + """ + Returns the label that should be used as keyword in the + :py:class:`musixmatch.api.JsonResponseMessage` body. + """ + return getattr(cls, '__label__', cls.__name__.lower()) + + @classmethod + def apiMethod(cls): + """ + Returns the :py:class:`musixmatch.api.Method` that should be used to + build the object. Defaults to *label.get* where *label* is the result + from :py:meth:`label` + """ + api_method = getattr(cls, '__api_method__', '%s.%s' % (cls.label(),'get')) + if not isinstance(api_method, api.Method): + api_method = api.Method(str(api_method)) + return api_method + + def __str__(self): + raise NotImplementedError + + def __repr__(self): + raise NotImplementedError + +class Item(Base, dict): + """ + This is the base class for any entity in musixmatch package. Even if + response messages may have XML format, the JSON representation will be the + main data format, so the :py:class:`dict` sounds like the best base class. + It fetches the item data by guessing the :py:class:`musixmatch.api.Method` + and building the query based on a given keyword argument. Positional + argument is meant to be used by collection classes. Use only keyword + arguments. + """ + __api_method__ = None + + def __init__(self, dictionary=None, **keywords): + if dictionary: + dict.update(self, dictionary) + elif keywords: + message = self.apiMethod()(**keywords) + dict.update(self, self.fromResponseMessage(message)) + + def __str__(self): + return pprint.pformat(dict(self),4,1) + + def __repr__(self): + return '%s(%r)' % (type(self).__name__, dict(self)) + + def __hash__(self): + return int(self['%s_id' % self.label()]) + + @classmethod + def fromResponseMessage(cls, message): + """ + Returns an object instance, built from a + :py:class:`musixmatch.api.ResponseMessage` + """ + if not message.status_code: + raise api.Error(str(message.status_code)) + return cls.fromDictionary(message['body'][cls.label()]) + + @classmethod + def fromDictionary(cls, dictionary, **keywords): + """ + Returns an object instance, built from a :py:class:`dict` + """ + item = cls() + dict.update(item, dictionary, **keywords) + return item + +class ItemsCollection(Base, list): + """ + This is the base class for collections of items, like search results, or + charts. It behaves like :py:class:`list`, but enforce new items to be + instance of appropriate class checking against :py:meth:`allowedin`. + """ + + __allowedin__ = Item + + def __init__(self, *items): + self.extend(items) + + def __repr__(self): + items = [ repr(i) for i in self ] + return '%s(%s)' % (type(self).__name__, ', '.join(items)) + + def __str__(self): + return list.__str__(self) + + def __add__(self, iterable): + collection = self.copy() + collection.extend(iterable) + return collection + + def __iadd__(self, iterable): + raise NotImplementedError + + def __mul__(self, by): + raise NotImplementedError + + def __imul__(self, by): + raise NotImplementedError + + def __setitem__(self, key, item): + raise NotImplementedError + + def __setslice__(self, *indices): + raise NotImplementedError + + def __getslice__(self, i=0, j=-1): + return self.__getitem__(slice(i,j)) + + def __getitem__(self, i): + if type(i) is int: + return list.__getitem__(self, i) + elif type(i) is slice: + collection = type(self)() + list.extend(collection, list.__getitem__(self, i)) + return collection + else: + raise TypeError, i + + def append(self, item): + self.insert(len(self), item) + + def extend(self, iterable): + for item in iterable: + self.append(item) + + def count(self, item): + return int(item in self) + + def copy(self): + """Returns a shallow copy of the collection.""" + collection = type(self)() + list.extend(collection, self) + return collection + + def index(self, item, *indices): + return list.index(self, item, *indices[:2]) + + def insert(self, key, item): + allowed = self.allowedin() + if not isinstance(item, allowed): + item = allowed.fromDictionary(item) + if not item in self: + list.insert(self, key, item) + + def paged(self, page_size=3): + """ + Returns self, paged by **page_size**. That is, a list of + sub-collections which contain "at most" **page_size** items. + """ + return [ self.page(i,page_size) + for i in range(self.pages(page_size)) ] + + def page(self, page_index, page_size=3): + """ + Returns a specific page, considering pages that contain "at most" + **page_size** items. + """ + page = type(self)() + i = page_index * page_size + list.extend(page, self[i:i+page_size]) + return page + + def pager(self, page_size=3): + """ + A generator of pages, considering pages that contain "at most" + **page_size** items. + """ + for i in xrange(self.pages(page_size)): + yield self.page(i, page_size) + + def pages(self,page_size=3): + """ + Returns the number of pages, considering pages that contain "at most" + **page_size** items. + """ + pages, more = divmod(len(self), page_size) + return more and pages + 1 or pages + + @classmethod + def fromResponseMessage(cls, message): + """ + Returns an object instance, built on a + :py:class:`musixmatch.api.ResponseMessage` + """ + if not message.status_code: + raise api.Error(str(message.status_code)) + list_label = cls.label() + item_label = cls.allowedin().label() + items = [ i[item_label] for i in message['body'][list_label] ] + return cls(*items) + + @classmethod + def allowedin(cls): + """ + Returns the allowed content class. Defaults to :py:class:`Item` + """ + return cls.__allowedin__ + + @classmethod + def label(cls): + item_name = cls.allowedin().label() + return '%s_list' % item_name +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/build/lib/musixmatch/lyrics.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,43 @@ +""" +This module contains higher level classes to query Musixmatch API and build +simple dictionary-like objects representing a track lyrics. + +>>> from musixmatch.lyrics import Lyrics +>>> import musixmatch.api +>>> +>>> try: +... lyrics = Lyrics(lyrics_id=292) +... except musixmatch.api.Error, e: +... pass +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch.base import Item +from musixmatch.ws import track + +class Lyrics(Item): + """ + This class builds a :py:class:`dict` object representing a the lyrics of a + track. It can get lyrics through the :py:class:`musixmatch.api.Method` + **track.lyrics.get** or from an already well-formed :py:class:`dict`. + Create a Track object based on a given keyword argument: + + :param track_id: musiXmatch track ID + :param musicbrainz_id: Musicbrainz track ID + :param track_echonest_id: Echonest track ID + :param lyrics_data: an already well-formed :py:class:`dict` of track data + :raises: :py:class:`musixmatch.api.Error` if :py:class:`musixmatch.api.ResponseStatusCode` is not 200 + + Once information are collected, the following keys are available: + + :keyword lyrics_body: the lyrics text + :keyword lyrics_id: the Musixmatch lyrics id + :keyword lyrics_language: the lyrics language + :keyword lyrics_copyright: the lyrics copyright statement + :keyword pixel_tracking_url: the pixel tracking url + :keyword script_tracking_url: the script tracking url + """ + __api_method__ = track.lyrics.get +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/build/lib/musixmatch/subtitle.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,40 @@ +""" +This module contains higher level classes to query Musixmatch API and build +simple dictionary-like objects representing a track subtitle. + +>>> from musixmatch.subtitle import Subtitle +>>> import musixmatch.api +>>> +>>> try: +... subtitle = Subtitle(subtitle_id=292) +... except musixmatch.api.Error, e: +... pass +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch.base import Item +from musixmatch.ws import track + +class Subtitle(Item): + """ + This class builds a :py:class:`dict` object representing a subtitle of a + track. It can get subtitle through the :py:class:`musixmatch.api.Method` + **track.subtitle.get** or from an already well-formed :py:class:`dict`. + Create a Track object based on a given keyword argument: + + :param track_id: musiXmatch track ID + :param musicbrainz_id: Musicbrainz track ID + :param track_echonest_id: Echonest track ID + :param subtitle_data: an already well-formed :py:class:`dict` of track data + :raises: :py:class:`musixmatch.api.Error` if :py:class:`musixmatch.api.StatusCode` is not 200 + + Once information are collected, the following keys are available: + + :keyword subtitle_body: the subtitle text + :keyword subtitle_id: the Musixmatch subtitle id + :keyword subtitle_language: the subtitle language + """ + __api_method__ = track.subtitle.get +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/build/lib/musixmatch/track.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,171 @@ +""" +This module contains higher level classes to query Musixmatch API and build +simple dictionary-like objects representing a Track or a TracksCollection. + +>>> from musixmatch.track import Track, TracksCollection +>>> import musixmatch.api +>>> +>>> try: +... track = Track(track_mbid=8976) +... collection = TracksCollection.fromChart(country='us', page=1) +... except musixmatch.api.Error, e: +... pass +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch import api, lyrics, subtitle +from musixmatch.base import Item, ItemsCollection +from musixmatch.ws import track, matcher, album + +_marker=object() + +class Track(Item): + """ + This class builds a :py:class:`dict` like object representing a track. It + can get track information through the :py:class:`musixmatch.api.Method` + **track.get** or from an already well-formed :py:class:`dict`. Create a + Track object based on a given keyword argument: + + :param track_id: musiXmatch track ID + :param musicbrainz_id: Musicbrainz track ID + :param track_echonest_id: Echonest track ID + :param track_data: an already well-formed :py:class:`dict` of track data + :raises: :py:exc:`musixmatch.api.Error` if :py:class:`musixmatch.api.ResponseStatusCode` is not 200 + + Once information are collected, the following keys are available: + + :keyword track_id: musiXmatch track ID + :keyword track_mbid: Musicbrainz track ID + :keyword lyrics_id: musiXmatch lyrics ID + :keyword instrumental: wether the track is instrumental or not + :keyword subtitle_id: musiXmatch subtitle ID + :keyword track_name: track name + :keyword album_coverart_100x100: album cover URL + :keyword artist_id: musiXmatch artist ID + :keyword artist_mbid: Musicbrainz artist ID + :keyword artist_name: artist name + + Keyword access have been overloaded thanks to the :py:meth:`get` method + which will eventually fetch the matching lyrics or subtitle. + """ + __api_method__ = track.get + + @classmethod + def fromMatcher(cls, **keywords): + """ + Returns a :py:class:`Track` based on the result of the + :py:class:`musiXmatch.api.Method` **matcher.track.get**. Accepts the + following keywords: + + :param q_track: words to be searched among track titles + :param q_artist: words to be searched among artist names + """ + return cls.fromResponseMessage(matcher.track.get(**keywords)) + + def get(self, key, default=_marker): + """ + If key is *lyrics* or *subtitle* try to query api for proper value, + and build an :py:class:`musixmatch.lyrics.Lyrics` or + :py:class:`musixmatch.subtitle.Subtitle`. Access to the above mentioned + keys may fail with :py:exc:`musixmatch.api.Error`. Once fetched, the + result is saved. + """ + special = { + 'lyrics': lyrics.Lyrics, + 'subtitle': subtitle.Subtitle, + } + if key in special and not key in self: + self[key] = special[key](track_id=self['track_id']) + value = dict.get(self, key, default) + if value == _marker: + raise KeyError, key + return value + + def __getitem__(self, key): + return self.get(key) + + def postFeedback(self, feedback): + """ + Post feedback about lyrics for this track. **feedback** can be one of: + + :keyword wrong_attribution: the lyrics shown are not by the artist that + I selected. + :keyword bad_characters: there are strange characters and/or words + that are partially scrambled. + :keyword lines_too_long: the text for each verse is too long! + :keyword wrong_verses: there are some verses missing from the + beginning or at the end. + :keyword wrong_formatting: the text looks horrible, please fix it! + """ + accepted = [ + 'wrong_attribution', 'bad_characters', 'lines_too_long', + 'wrong_verses', 'wrong_formatting' ] + if feedback in accepted: + message = track.lyrics.feedback.post( + track_id=self['track_id'], + lyrics_id=self['track_id']['lyrics']['lyrics_id'], + feedback=feedback + ) + if not message.status_code: + raise api.Error(str(message.status_code)) + else: + raise TypeError, '%r not in %r' % (feedback, accepted) + +class TracksCollection(ItemsCollection): + """ + This class build a :py:class:`list` like object representing a tracks + collection. It accepts :py:class:`dict` or :py:class:`Track` objects. + """ + __allowedin__ = Track + + @classmethod + def fromAlbum(cls, **keywords): + """ + This classmethod builds an :py:class:`TracksCollection` from a + **album.tracks.get** :py:class:`musixmatch.api.Method` call. + + :param album_id: musiXmatch album ID + """ + return cls.fromResponseMessage(album.tracks.get(**keywords)) + + @classmethod + def fromSearch(cls, **keywords): + """ + This classmethod builds an :py:class:`TracksCollection` from a + **track.search** :py:class:`musixmatch.api.Method` call. + + :param q: a string that will be searched in every data field + (q_track, q_artist, q_lyrics) + :param q_track: words to be searched among track titles + :param q_artist: words to be searched among artist names + :param q_track_artist: words to be searched among track titles or + artist names + :param q_lyrics: words to be searched into the lyrics + :param page: requested page of results + :param page_size: desired number of items per result page + :param f_has_lyrics: exclude tracks without an available lyrics + (automatic if q_lyrics is set) + :param f_artist_id: filter the results by the artist_id + :param f_artist_mbid: filter the results by the artist_mbid + :param quorum_factor: only works together with q and q_track_artist + parameter. Possible values goes from 0.1 to + 0.9. A value of 0.9 means: 'match at least 90 + percent of the words'. + """ + return cls.fromResponseMessage(track.search(**keywords)) + + @classmethod + def fromChart(cls, **keywords): + """ + This classmethod builds an :py:class:`TracksCollection` from a + **track.chart.get** :py:class:`musixmatch.api.Method` call. + + :param page: requested page of results + :param page_size: desired number of items per result page + :param country: the country code of the desired country chart + :param f_has_lyrics: exclude tracks without an available lyrics + (automatic if q_lyrics is set) + """ + return cls.fromResponseMessage(track.chart.get(**keywords))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/build/lib/musixmatch/ws.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,30 @@ +""" +This is an utility module that provides a row musiXmatch web API interface. +Ideally it should be used like this: + +>>> import musixmatch +>>> +>>> try: +... chart = musixmatch.ws.track.chart.get(country='it', f_has_lyrics=1) +... except musixmatch.api.Error, e: +... pass +""" +from warnings import warn +import os +import musixmatch.api +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +_version = os.environ.get('musixmatch_apiversion', None) +if not _version: + _version = '1.1' +else: + warn("Use of `musixmatch_apiversion' was deprecated in favour of `musixmatch_wslocation'", DeprecationWarning) + +location = os.environ.get('musixmatch_wslocation', 'http://api.musixmatch.com/ws/%s' % _version) + +artist = musixmatch.api.Method('artist') +album = musixmatch.api.Method('album') +track = musixmatch.api.Method('track') +tracking = musixmatch.api.Method('tracking') +matcher = musixmatch.api.Method('matcher')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/docs/album.rst Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,12 @@ +============ +album module +============ + +.. automodule:: musixmatch.album + + .. autoclass:: Album + :show-inheritance: + + .. autoclass:: AlbumsCollection + :show-inheritance: + :members: fromArtist
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/docs/api.rst Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,31 @@ +========== +api module +========== + +.. automodule:: musixmatch.api + :undoc-members: + + .. autoexception:: Error + :show-inheritance: + + .. autoexception:: ResponseMessageError + :show-inheritance: + + .. autoclass:: ResponseStatusCode + :show-inheritance: + + .. autoclass:: ResponseMessage + :members: status_code + + .. autoclass:: JsonResponseMessage + + .. autoclass:: QueryString + :members: items + + .. autoclass:: Method + :undoc-members: + + .. autoclass:: Request + :undoc-members: + :members: query_string, api_method, response +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/docs/artist.rst Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,12 @@ +============= +artist module +============= + +.. automodule:: musixmatch.artist + + .. autoclass:: Artist + :show-inheritance: + + .. autoclass:: ArtistsCollection + :show-inheritance: + :members: fromSearch, fromChart
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/docs/base.rst Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,16 @@ +=========== +base module +=========== + +.. automodule:: musixmatch.base + + .. autoclass:: Base + :show-inheritance: + :members: label, apiMethod + + .. autoclass:: Item + :show-inheritance: + + .. autoclass:: ItemsCollection + :show-inheritance: + :members: copy, page, pages, pager, paged, fromResponseMessage, allowedin
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/docs/conf.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,224 @@ +# -*- coding: utf-8 -*- +# +# Python musiXmatch documentation build configuration file, created by +# sphinx-quickstart on Mon Mar 28 22:28:39 2011. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath(os.path.dirname(__file__))) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +# extensions = [ +# 'sphinx.ext.autodoc', +# 'sphinx.ext.doctest', +# 'sphinx.ext.todo', +# 'sphinx.ext.coverage'] +extensions = [ + 'sphinx.ext.doctest', + 'sphinx.ext.coverage', + 'sphinx.ext.autodoc'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Python musiXmatch' +copyright = u'2011, Luca De Vitis' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.6' +# The full version, including alpha/beta/rc tags. +release = '0.6.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +add_module_names = False + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +show_authors = True + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# "<project> v<release> documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +html_domain_indices = True + +# If false, no index is generated. +html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a <link> tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'PythonmusiXmatchdoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'PythonmusiXmatch.tex', u'Python musiXmatch Documentation', + u'Luca De Vitis', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'pythonmusixmatch', u'Python musiXmatch Documentation', + [u'Luca De Vitis'], 1) +]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/docs/index.rst Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,36 @@ +.. Python musiXmatch documentation master file, created by + sphinx-quickstart on Mon Mar 28 22:28:39 2011. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Package overview +================ + +.. automodule:: musixmatch.__init__ + +.. include:: ../README.rst + +.. include:: ../CHANGES.rst + +Package API +=========== + +.. toctree:: + :numbered: + + api + ws + base + artist + track + album + lyrics + subtitle + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/docs/lyrics.rst Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,8 @@ +============= +lyrics module +============= + +.. automodule:: musixmatch.lyrics + + .. autoclass:: Lyrics + :show-inheritance:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/docs/subtitle.rst Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,8 @@ +=============== +subtitle module +=============== + +.. automodule:: musixmatch.subtitle + + .. autoclass:: Subtitle + :show-inheritance:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/docs/track.rst Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,13 @@ +============= +track module +============= + +.. automodule:: musixmatch.track + + .. autoclass:: Track + :show-inheritance: + :members: fromMatcher, get, postFeedback + + .. autoclass:: TracksCollection + :show-inheritance: + :members: fromAlbum, fromSearch, fromChart
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/docs/ws.rst Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,5 @@ +========= +WS module +========= + +.. automodule:: musixmatch.ws
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/musixmatch.egg-info/PKG-INFO Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,91 @@ +Metadata-Version: 1.0 +Name: musixmatch +Version: 0.9 +Summary: Package to interface with the Musixmatch API +Home-page: ('http://projects.monkeython.com/musixmatch',) +Author: Luca De Vitis +Author-email: luca@monkeython.com +License: UNKNOWN +Download-URL: http://projects.monkeython.com/musixmatch/dists +Description: Quick start + =========== + + 1. First thing first, read the documentation at http://developer.musixmatch.com . + 2. Get an api key by signing up at http://developer.musixmatch.com/mmplans . + 3. Install the musixmatch package + 4. Run the python prompt + + >>> import musixmatch + >>> apikey = '<your-apikey>' + >>> try: + ... chart = musixmatch.ws.track.chart.get(country='it', apikey=apikey) + ... except musixmatch.api.Error, e: + ... pass + + It's that simple. Last, you can brows this documentation and have fun with the other modules. + + Building / Installing + ===================== + + You can just use setup.py to build and install python-musixmatch:: + + prompt $ python setup.py bdist_egg + + Once built, you can use easy_install on the python egg. + + Documentation + ============= + You can read documentation online_, or generate your own local copy using + `Sphinx`_ trough the setup.py:: + + prompt $ python setup.py build_sphinx + + .. _Sphinx: http://sphinx.pocoo.org + .. _online: http://projects.monkeython.com/musixmatch/python-musixmatch/html/index.html + + Unit testing + ============ + python-musixmatch comes with some essential unit testing. If you set up + **musixmatch_apikey** environment variable, and have internet connection, you + can also run some tests on API calls:: + + prompt $ python setup.py test + + Caching support + =============== + + Applications using python-musixmatch may take advantage of standard + urllib support for **http_proxy**, so they can just set up the proper + environment variable: + + http_proxy + the complete HTTP proxy URL to use in queries. + + Considering all the available HTTP proxy solutions, I'm reluctant to implement + a further caching support. Though i can consider serialization support. + + Environment variables + ===================== + + python-musixmatch takes advantage of operating system environment to get + **apikey**, **format** and api **version** values to use in API calls: + + musixmatch_apikey + the apikey value to use in query strings + musixmatch_format + the response message format. For example: json + musixmatch_wslocation + the webservice base url. For example: http://api.musixmatch.com/ws/1.1 + musixmatch_apiversion + the api version to use in queryes. For example: 1.1. Use of + **musixmatch_apiversion** was deprecated in favour of + **musixmatch_wslocation**. + + +Platform: UNKNOWN +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: GNU General Public License (GPL) +Classifier: Operating System :: OS Independent +Classifier: Topic :: Internet :: WWW/HTTP +Classifier: Topic :: Software Development :: Libraries
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/musixmatch.egg-info/SOURCES.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,16 @@ +README.rst +setup.cfg +setup.py +musixmatch/__init__.py +musixmatch/album.py +musixmatch/api.py +musixmatch/artist.py +musixmatch/base.py +musixmatch/lyrics.py +musixmatch/subtitle.py +musixmatch/track.py +musixmatch/ws.py +musixmatch.egg-info/PKG-INFO +musixmatch.egg-info/SOURCES.txt +musixmatch.egg-info/dependency_links.txt +musixmatch.egg-info/top_level.txt \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/musixmatch.egg-info/dependency_links.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,1 @@ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/musixmatch.egg-info/top_level.txt Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,1 @@ +musixmatch
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/musixmatch.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,135 @@ +import os +import sys + +import musixmatch +import musixmatch.ws +import json +sys.path.append("7digital-python/lib/") +import py7digital + +import urllib2 #call url function +from xml.dom import minidom + + +#apikey = '8496dd1c715c69a74ef9fcde1716cf4a' +newapikey = '82be3ea3f79ea404d45f47607c103eff' + +#chart = musixmatch.ws.track.chart.get(country='it', apikey=apikey) + +#lyrics = musixmatch.ws.matcher.lyrics.get(q_track='Someone like you', q_artist='Adele',format ='json',apikey=newapikey) + +song_wakeMeUp = musixmatch.ws.track.search(q_lyrics = 'wake me up',f_lyrics_language='en',s_track_rating='desc',format ='json',apikey=newapikey) + +#print lyrics + +#print lyrics['body'] + +#print pprint(song_wakeMeUp) + + +#for each_line in lyrics['body']: +# print each_line + +#print 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' + +#for each_item in chart['body']['track_list']: +# print each_item + +# 7 digital API key +DIGITAL7_API_KEY = '7dbpa63h3y3d' + +def url_call(url): + """ + ***This method is from get_preview_url.py by Thierry Bertin-Mahieux*** + Do a simple request to the 7digital API + We assume we don't do intense querying, this function is not robust + Return the answer as na xml document + """ + stream = urllib2.urlopen(url) + xmldoc = minidom.parse(stream).documentElement + stream.close() + return xmldoc + + +def download_file(file_url, file_name): + # open the url + mp3file = urllib2.urlopen(file_url) + + # open the local file for writing + local_file = open(file_name, "wb") + # write to file + local_file.write(mp3file.read()) + local_file.close() + +""""*********************************************************************************""" + +def get_trackid_from_text_search(title,artistname=''): + """ + ***This method is from get_preview_url.py by Thierry Bertin-Mahieux*** + Search for an artist + title using 7digital search API + Return None if there is a problem, or tuple (title,trackid) + """ + url = 'http://api.7digital.com/1.2/track/search?' + url += 'oauth_consumer_key='+DIGITAL7_API_KEY + query = title + if artistname != '': + query = artistname + ' ' + query + query = urllib2.quote(query) + url += '&q='+query + xmldoc = url_call(url) + status = xmldoc.getAttribute('status') + if status != 'ok': + return None + resultelem = xmldoc.getElementsByTagName('searchResult') + if len(resultelem) == 0: + return None + track = resultelem[0].getElementsByTagName('track')[0] + tracktitle = track.getElementsByTagName('title')[0].firstChild.data + trackid = int(track.getAttribute('id')) + return (tracktitle,trackid) + + +def get_preview_from_trackid(trackid): + """ + ***This method is from get_preview_url.py by Thierry Bertin-Mahieux*** + Ask for the preview to a particular track, get the XML answer + After calling the API with a given track id, + we get an XML response that looks like: + + <response status="ok" version="1.2" xsi:noNamespaceSchemaLocation="http://api.7digital.com/1.2/static/7digitalAPI.xsd"> + <url> + http://previews.7digital.com/clips/34/6804688.clip.mp3 + </url> + </response> + + We parse it for the URL that we return, or '' if a problem + """ + url = 'http://api.7digital.com/1.2/track/preview?redirect=false' + url += '&trackid='+str(trackid) + url += '&oauth_consumer_key='+DIGITAL7_API_KEY + xmldoc = url_call(url) + status = xmldoc.getAttribute('status') + if status != 'ok': + return '' + urlelem = xmldoc.getElementsByTagName('url')[0] + preview = urlelem.firstChild.nodeValue + return preview + + +for each_song in song_wakeMeUp['body']['track_list']: + print each_song['track']['track_name'] + '\t' + each_song['track']['artist_name'] + + (tracktitle, trackid) = get_trackid_from_text_search(each_song['track']['track_name'],each_song['track']['artist_name']) + + audio_url = get_preview_from_trackid(trackid) + + file_name = tracktitle + u'.mp3' + file_name = file_name.replace(u'/', u' ') + + #file_name = t.title + u'.mp3' + print file_name + print("downloading") + # download_path = os.path.join(file_path, file_name) + path_name = './' + file_name + mp3 = download_file(audio_url, path_name) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/musixmatch/__init__.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,44 @@ +import os +__author__ = "Luca De Vitis <luca@monkeython.com>" +__version__ = '0.9' +__copyright__ = "2011, %s " % __author__ +__license__ = """ + Copyright (C) %s + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +""" % __copyright__ +__doc__ = """ +:abstract: Python interface to Musixmatch API +:version: %s +:author: %s +:organization: Monkeython +:contact: http://www.monkeython.com +:copyright: %s +""" % (__version__, __author__, __license__) +__docformat__ = 'restructuredtext en' +__classifiers__ = [ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: GNU General Public License (GPL)', + 'Operating System :: OS Independent', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Software Development :: Libraries', +] +__all__ = [ + 'ws', 'api', 'base', + 'artist', 'track', 'lyrics', 'subtitle', 'album' +] + +apikey = os.environ.get('musixmatch_apikey', None) +format = os.environ.get('musixmatch_format', 'json')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/musixmatch/album.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,62 @@ +""" +This module contains higher level classes to query Musixmatch API and build +simple dictionary-like objects representing an Album or an AlbumsCollection. + +>>> from musixmatch.album import Album, AlbumsCollection +>>> import musixmatch.api +>>> +>>> try: +... album = Album(album_id=292) +... collection = AlbumsCollection.fromArtist(country='it', page=1) +... except musixmatch.api.Error, e: +... pass +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch.base import Item, ItemsCollection +from musixmatch.ws import album, artist + +class Album(Item): + """ + This class build a :py:class:`dict` like object representing an album. It + can get album information through the :py:class:`musixmatch.api.Method` + **album.get** or from an already well-formed :py:class:`dict`. Create an + Album object based on a given keyword argument: + + :param album_id: musiXmatch album ID + :param album_data: an already well-formed :py:class:`dict` of album data. + + Once information are collected, the following keys are available: + + :keyword album_id: musiXmatch album ID + :keyword album_name: album name + :keyword album_release_date: album release date + :keyword album_release_type: type of the album + :keyword album_coverart_100x100: coverart URL + :keyword artist_id: album artist musiXmatch ID + :keyword artist_name: album artist name + """ + __api_method__ = album.get + +class AlbumsCollection(ItemsCollection): + """ + This class build a :py:class:`list` like object representing an albums + collection. It accepts :py:class:`dict` or :py:class:`Album` objects. + """ + __allowedin__ = Album + + @classmethod + def fromArtist(cls, **keywords): + """ + This classmethod builds an :py:class:`AlbumsCollection` from a + **artist.albums.get** :py:class:`musixmatch.api.Method` call. + + :param artist_id: album artist musiXmatch ID + :param g_album_name: group albums by name + :param s_release_date: sort albums by release date + :rtype: :py:class:`AlbumsCollection` + """ + return cls.fromResponseMessage(artist.albums.get(**keywords)) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/musixmatch/api.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,389 @@ +""" This module define the base API classes. +""" +import musixmatch +from urllib import urlencode, urlopen +from contextlib import contextmanager +import os +try: + import json +except ImportError: + import simplejson as json +try: + from lxml import etree +except ImportError: + try: + import xml.etree.cElementTree as etree + except ImportError: + try: + import xml.etree.ElementTree as etree + except ImportError: + try: + import cElementTree as etree + except ImportError: + import elementtree.ElementTree as etree + +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +class Error(Exception): + """Base musiXmatch API error. + + >>> import musixmatch + >>> raise musixmatch.api.Error('Error message') + Traceback (most recent call last): + ... + Error: Error message + """ + def __str__(self): + return ': '.join(map(str, self.args)) + + def __repr__(self): + name = self.__class__.__name__ + return '%s%r' % (name, self.args) + +class ResponseMessageError(Error): + """Represents errors occurred while parsing the response messages.""" + +class ResponseStatusCode(int): + """ + Represents response message status code. Casting a + :py:class:`ResponseStatusCode` to :py:class:`str` returns the message + associated with the status code: + + >>> from musixmatch.api import ResponseStatusCode + >>> str(ResponseStatusCode(200)) + 'The request was successful.' + >>> str(ResponseStatusCode(401)) + 'Authentication failed, probably because of a bad API key.' + + The status code to description mapping is: + + +------+-----------------------------------------------------------+ + | Code | Description | + +======+===========================================================+ + | 200 | The request was successful. | + +------+-----------------------------------------------------------+ + | 400 | The request had bad syntax or was inherently | + | | impossible to be satisfied. | + +------+-----------------------------------------------------------+ + | 401 | Authentication failed, probably because of a bad API key. | + +------+-----------------------------------------------------------+ + | 402 | A limit was reached, either you exceeded per hour | + | | requests limits or your balance is insufficient. | + +------+-----------------------------------------------------------+ + | 403 | You are not authorized to perform this operation or | + | | the api version you're trying to use has been shut down. | + +------+-----------------------------------------------------------+ + | 404 | Requested resource was not found. | + +------+-----------------------------------------------------------+ + | 405 | Requested method was not found. | + +------+-----------------------------------------------------------+ + + Any other status code will produce a default message: + + >>> from musixmatch.api import ResponseStatusCode + >>> str(ResponseStatusCode(666)) + 'Unknown status code 666!' + + Casting a :py:class:`ResponseStatusCode` to :py:class:`bool` returns True if + status code is 200, False otherwise: + + >>> from musixmatch.api import ResponseStatusCode + >>> bool(ResponseStatusCode(200)) + True + >>> bool(ResponseStatusCode(400)) + False + >>> bool(ResponseStatusCode(666)) + False + + """ + __status__ = { + 200: "The request was successful.", + 400: "The request had bad syntax or was inherently " + \ + "impossible to be satisfied.", + 401: "Authentication failed, probably because of a bad API key.", + 402: "A limit was reached, either you exceeded per hour " + \ + "requests limits or your balance is insufficient.", + 403: "You are not authorized to perform this operation or " + \ + "the api version you're trying to use has been shut down.", + 404: "Requested resource was not found.", + 405: "Requested method was not found.", + } + + def __str__(self): + return self.__status__.get(self, 'Unknown status code %i!' % self) + + def __repr__(self): + return 'ResponseStatusCode(%i)' % self + + def __nonzero__(self): + return self == 200 + +class ResponseMessage(dict): + """ + Abstract class which provides a base class for formatted response. + """ + def __init__(self, response): + raise NotImplementedError + + @property + def status_code(self): + """ + Is the :py:class:`ResponseStatusCode` object representing the + message status code. + + :raises: :py:exc:`ValueError` if not set. + """ + raise NotImplementedError + + def __repr__(self): + return "%s('...')" % type(self).__name__ + +class JsonResponseMessage(ResponseMessage, dict): + """ + A :py:class:`ResponseMessage` subclass which behaves like a + :py:class:`dict` to expose the Json structure contained in the response + message. Parses the Json response message and build a proper python + :py:class:`dict` containing all the information. Also, setup a + :py:class:`ResponseStatusCode` by querying the :py:class:`dict` for the + *['header']['status_code']* item. + """ + def __init__(self, response): + try: + parsed = json.load(response) + except Exception, e: + raise ResponseMessageError(u'Invalid Json response message', e) + self.update(parsed['message']) + + def __str__(self): + s = json.dumps({ 'message': self }, sort_keys=True, indent=4) + return '\n'.join([l.rstrip() for l in s.splitlines()]) + + @property + def status_code(self): + """Overload :py:meth:`ResponseMessage.status_code`""" + return ResponseStatusCode(self['header']['status_code']) + +class XMLResponseMessage(ResponseMessage, etree.ElementTree): + """ + A :py:class:`ResponseMessage` subclass which exposes + :py:class:`ElementTree` methods to handle XML response + messages. Parses the XML response message and build a + :py:class:`ElementTree` instance. Also setup the a + :py:class:`ResponseStatusCode` by querying the *status_code* tag content. + + Casting a :py:class:`XMLResponseMessage` returns (actually re-builds) a + pretty printed string representing the XML API response message. + """ + + def __init__(self, response): + etree.ElementTree.__init__(self, None, response) + + def __str__(self): + s = StringIO() + self.wite(s) + return s.getvalue() + + @property + def status_code(self): + """Overload :py:meth:`ResponseMessage.status_code`""" + return ResponseStatusCode(self.findtext('header/status_code')) + +class QueryString(dict): + """ + A class representing the keyword arguments to be used in HTTP requests as + query string. Takes a :py:class:`dict` of keywords, and encode values + using utf-8. Also, the query string is sorted by keyword name, so that its + string representation is always the same, thus can be used in hashes. + + Casting a :py:class:`QueryString` to :py:class:`str` returns the urlencoded + query string: + + >>> from musixmatch.api import QueryString + >>> str(QueryString({ 'country': 'it', 'page': 1, 'page_size': 3 })) + 'country=it&page=1&page_size=3' + + Using :py:func:`repr` on :py:class:`QueryString` returns an evaluable + representation of the current instance, excluding apikey value: + + >>> from musixmatch.api import QueryString + >>> repr(QueryString({ 'country': 'it', 'page': 1, 'apikey': 'whatever'})) + "QueryString({'country': 'it', 'page': '1'})" + """ + def __init__(self, items=(), **keywords): + dict.__init__(self, items, **keywords) + for k in self: + self[k] = str(self[k]).encode('utf-8') + + def __str__(self): + return urlencode(self) + + def __repr__(self): + query = self.copy() + if 'apikey' in query: + del query['apikey'] + return 'QueryString(%r)' % query + + def __iter__(self): + """ + Returns an iterator method which will yield keys sorted by name. + Sorting allow the query strings to be used (reasonably) as caching key. + """ + keys = dict.keys(self) + keys.sort() + for key in keys: + yield key + + def values(self): + """Overloads :py:meth:`dict.values` using :py:meth:`__iter__`.""" + return tuple(self[k] for k in self) + + def keys(self): + """Overloads :py:meth:`dict.keys` using :py:meth:`__iter__`.""" + return tuple(k for k in self) + + def items(self): + """Overloads :py:meth:`dict.item` using :py:meth:`__iter__`.""" + return tuple((k, self[k]) for k in self) + + def __hash__(self): + return hash(str(self)) + + def __cmp__(self, other): + return cmp(hash(self), hash(other)) + +class Method(str): + """ + Utility class to build API methods name and call them as functions. + + :py:class:`Method` has custom attribute access to build method names like + those specified in the API. Each attribute access builds a new Method with + a new name. + + Calling a :py:class:`Method` as a function with keyword arguments, + builds a :py:class:`Request`, runs it and returns the result. If **apikey** + is undefined, environment variable **musixmatch_apikey** will be used. If + **format** is undefined, environment variable **musixmatch_format** will be + used. If **musixmatch_format** is undefined, jason format will be used. + + >>> import musixmatch + >>> artist = musixmatch.api.Method('artist') + >>> + >>> try: + ... chart = artist.chart.get(country='it', page=1, page_size=3) + ... except musixmatch.api.Error, e: + ... pass + """ + __separator__ = '.' + + def __getattribute__(self, name): + if name.startswith('_'): + return super(Method, self).__getattribute__(name) + else: + return Method(self.__separator__.join([self, name])) + + def __call__ (self, apikey=None, format=None, **query): + query['apikey'] = apikey or musixmatch.apikey + query['format'] = format or musixmatch.format + return Request(self, query).response + + def __repr__(self): + return "Method('%s')" % self + +class Request(object): + """ + This is the main API class. Given a :py:class:`Method` or a method name, a + :py:class:`QueryString` or a :py:class:`dict`, it can build the API query + URL, run the request and return the response either as a string or as a + :py:class:`ResponseMessage` subclass. Assuming the default web services + location, this class try to build a proper request: + + >>> from musixmatch.api import Request, Method, QueryString + >>> method_name = 'artist.chart.get' + >>> method = Method(method_name) + >>> keywords = { 'country': 'it', 'page': 1, 'page_size': 3 } + >>> query_string = QueryString(keywords) + >>> + >>> r1 = Request(method_name, keywords) + >>> r2 = Request(method_name, **keywords) + >>> r3 = Request(method_name, query_string) + >>> r4 = Request(method, keywords) + >>> r5 = Request(method, **keywords) + >>> r6 = Request(method, query_string) + + If **method** is string, try to cast it into a :py:class:`Method`. If + **query_string** is a :py:class:`dict`, try to cast it into a + :py:class:`QueryString`. If **query_string** is not specified, try to + use **keywords** arguments as a :py:class:`dict` and cast it into a + :py:class:`QueryString`. + + Turning the :py:class:`Request` into a :py:class:`str` returns the URL + representing the API request: + + >>> str(Request('artist.chart.get', { 'country': 'it', 'page': 1 })) + 'http://api.musixmatch.com/ws/1.1/artist.chart.get?country=it&page=1' + """ + def __init__ (self, api_method, query=(), **keywords): + self.__api_method = isinstance(api_method, Method) and \ + api_method or Method(api_method) + self.__query_string = isinstance(query, QueryString) and \ + query or QueryString(query) + self.__query_string.update(keywords) + self.__response = None + + @property + def api_method(self): + """The :py:class:`Method` instance.""" + return self.__api_method + + @property + def query_string(self): + """The :py:class:`QueryString` instance.""" + return self.__query_string + + @contextmanager + def _received(self): + """A context manager to handle url opening""" + try: + response = urlopen(str(self)) + yield response + finally: + response.close() + + @property + def response(self): + """ + The :py:class:`ResponseMessage` based on the **format** key in the + :py:class:`QueryString`. + """ + if self.__response is None: + + format = self.query_string.get('format') + ResponseMessageClass = { + 'json': JsonResponseMessage, + 'xml': XMLResponseMessage, + }.get(format, None) + + if not ResponseMessageClass: + raise ResponseMessageError("Unsupported format `%s'" % format) + + with self._received() as response: + self.__response = ResponseMessageClass(response) + + return self.__response + + def __repr__(self): + return 'Request(%r, %r)' % (self.api_method, self.query_string) + + def __str__(self): + return '%(ws_location)s/%(api_method)s?%(query_string)s' % { + 'ws_location': musixmatch.ws.location, + 'api_method': self.api_method, + 'query_string': self.query_string + } + + def __hash__(self): + return hash(str(self)) + + def __cmp__(self, other): + return cmp(hash(self), hash(other))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/musixmatch/artist.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,80 @@ +""" +This module contains higher level classes to query Musixmatch API and build +simple dictionary-like objects representing an Artist or an ArtistsCollection. + +>>> from musixmatch.artist import Artist, ArtistsCollection +>>> import musixmatch.api +>>> +>>> try: +... artist = Artist(artist_id=292) +... collection = ArtistsCollection.fromChart(country='it', page=1) +... except musixmatch.api.Error, e: +... pass +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch.base import Item, ItemsCollection +from musixmatch.ws import artist + +class Artist(Item): + """ + This class build a :py:class:`dict` like object representing an artist. It + can get artist information through the :py:class:`musixmatch.api.Method` + **artist.get** or from an already well-formed :py:class:`dict`. Create an + Artist object based on a given keyword argument: + + :param artist_id: musiXmatch artist ID + :param artist_mbid: Musicbrainz artist ID + :param artist_data: an already well-formed :py:class:`dict` of artist data. + + Once information are collected, the following keys are available: + + :keyword artist_id: musiXmatch artist ID + :keyword artist_mbid: Musicbrainz artist ID + :keyword artist_name: Artist name + """ + __api_method__ = artist.get + +class ArtistsCollection(ItemsCollection): + """ + This class build a :py:class:`list` like object representing an artists + collection. It accepts :py:class:`dict` or :py:class:`Artist` objects. + """ + __allowedin__ = Artist + + @classmethod + def fromSearch(cls, **keywords): + """ + This classmethod builds an :py:class:`ArtistsCollection` from a + **artist.search** :py:class:`musixmatch.api.Method` call. + + :param q: a string that will be searched in every data field + (q_track, q_artist, q_lyrics) + :param q_track: words to be searched among track titles + :param q_artist: words to be searched among artist names + :param q_lyrics: words to be searched into the lyrics + :param page: requested page of results + :param page_size: desired number of items per result page + :param f_has_lyrics: exclude tracks without an available lyrics + (automatic if q_lyrics is set) + :param f_artist_id: filter the results by the artist_id + :param f_artist_mbid: filter the results by the artist_mbid + :rtype: :py:class:`ArtistsCollection` + """ + return cls.fromResponseMessage(artist.search(**keywords)) + + @classmethod + def fromChart(cls, **keywords): + """ + This classmethod builds an :py:class:`ArtistsCollection` from a + **artist.chart.get** :py:class:`musixmatch.api.Method` call. + + :param page: requested page of results + :param page_size: desired number of items per result page + :param country: the country code of the desired country chart + :rtype: :py:class:`ArtistsCollection` + """ + return cls.fromResponseMessage(artist.chart.get(**keywords)) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/musixmatch/base.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,240 @@ +""" +This module contains tha base classes for the Musixmatch API generated content: + +* :py:class:`musixmatch.artist.Artist` +* :py:class:`musixmatch.artist.Album` +* :py:class:`musixmatch.track.Track` +* :py:class:`musixmatch.lyrics.Lyrics` +* :py:class:`musixmatch.subtitle.Subtitle` +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch import api +import pprint + +class Base(object): + """ + The very base (abstract) class of the musixmatch package. I want all + classes to implement :py:meth:`__str__` and :py:meth:`__repr__`. + + Casting an :py:class:`Base` into a :py:class:`str` returns a pretty printed + (maybe using :py:mod:`pprint`) string representing the object. + + Using :py:func:`repr` on an :py:class:`Base` returns an evaluable string + representing the instance, whenever it is reasonable. + + :py:class:`Base` instances are hashable. + """ + + @classmethod + def label(cls): + """ + Returns the label that should be used as keyword in the + :py:class:`musixmatch.api.JsonResponseMessage` body. + """ + return getattr(cls, '__label__', cls.__name__.lower()) + + @classmethod + def apiMethod(cls): + """ + Returns the :py:class:`musixmatch.api.Method` that should be used to + build the object. Defaults to *label.get* where *label* is the result + from :py:meth:`label` + """ + api_method = getattr(cls, '__api_method__', '%s.%s' % (cls.label(),'get')) + if not isinstance(api_method, api.Method): + api_method = api.Method(str(api_method)) + return api_method + + def __str__(self): + raise NotImplementedError + + def __repr__(self): + raise NotImplementedError + +class Item(Base, dict): + """ + This is the base class for any entity in musixmatch package. Even if + response messages may have XML format, the JSON representation will be the + main data format, so the :py:class:`dict` sounds like the best base class. + It fetches the item data by guessing the :py:class:`musixmatch.api.Method` + and building the query based on a given keyword argument. Positional + argument is meant to be used by collection classes. Use only keyword + arguments. + """ + __api_method__ = None + + def __init__(self, dictionary=None, **keywords): + if dictionary: + dict.update(self, dictionary) + elif keywords: + message = self.apiMethod()(**keywords) + dict.update(self, self.fromResponseMessage(message)) + + def __str__(self): + return pprint.pformat(dict(self),4,1) + + def __repr__(self): + return '%s(%r)' % (type(self).__name__, dict(self)) + + def __hash__(self): + return int(self['%s_id' % self.label()]) + + @classmethod + def fromResponseMessage(cls, message): + """ + Returns an object instance, built from a + :py:class:`musixmatch.api.ResponseMessage` + """ + if not message.status_code: + raise api.Error(str(message.status_code)) + return cls.fromDictionary(message['body'][cls.label()]) + + @classmethod + def fromDictionary(cls, dictionary, **keywords): + """ + Returns an object instance, built from a :py:class:`dict` + """ + item = cls() + dict.update(item, dictionary, **keywords) + return item + +class ItemsCollection(Base, list): + """ + This is the base class for collections of items, like search results, or + charts. It behaves like :py:class:`list`, but enforce new items to be + instance of appropriate class checking against :py:meth:`allowedin`. + """ + + __allowedin__ = Item + + def __init__(self, *items): + self.extend(items) + + def __repr__(self): + items = [ repr(i) for i in self ] + return '%s(%s)' % (type(self).__name__, ', '.join(items)) + + def __str__(self): + return list.__str__(self) + + def __add__(self, iterable): + collection = self.copy() + collection.extend(iterable) + return collection + + def __iadd__(self, iterable): + raise NotImplementedError + + def __mul__(self, by): + raise NotImplementedError + + def __imul__(self, by): + raise NotImplementedError + + def __setitem__(self, key, item): + raise NotImplementedError + + def __setslice__(self, *indices): + raise NotImplementedError + + def __getslice__(self, i=0, j=-1): + return self.__getitem__(slice(i,j)) + + def __getitem__(self, i): + if type(i) is int: + return list.__getitem__(self, i) + elif type(i) is slice: + collection = type(self)() + list.extend(collection, list.__getitem__(self, i)) + return collection + else: + raise TypeError, i + + def append(self, item): + self.insert(len(self), item) + + def extend(self, iterable): + for item in iterable: + self.append(item) + + def count(self, item): + return int(item in self) + + def copy(self): + """Returns a shallow copy of the collection.""" + collection = type(self)() + list.extend(collection, self) + return collection + + def index(self, item, *indices): + return list.index(self, item, *indices[:2]) + + def insert(self, key, item): + allowed = self.allowedin() + if not isinstance(item, allowed): + item = allowed.fromDictionary(item) + if not item in self: + list.insert(self, key, item) + + def paged(self, page_size=3): + """ + Returns self, paged by **page_size**. That is, a list of + sub-collections which contain "at most" **page_size** items. + """ + return [ self.page(i,page_size) + for i in range(self.pages(page_size)) ] + + def page(self, page_index, page_size=3): + """ + Returns a specific page, considering pages that contain "at most" + **page_size** items. + """ + page = type(self)() + i = page_index * page_size + list.extend(page, self[i:i+page_size]) + return page + + def pager(self, page_size=3): + """ + A generator of pages, considering pages that contain "at most" + **page_size** items. + """ + for i in xrange(self.pages(page_size)): + yield self.page(i, page_size) + + def pages(self,page_size=3): + """ + Returns the number of pages, considering pages that contain "at most" + **page_size** items. + """ + pages, more = divmod(len(self), page_size) + return more and pages + 1 or pages + + @classmethod + def fromResponseMessage(cls, message): + """ + Returns an object instance, built on a + :py:class:`musixmatch.api.ResponseMessage` + """ + if not message.status_code: + raise api.Error(str(message.status_code)) + list_label = cls.label() + item_label = cls.allowedin().label() + items = [ i[item_label] for i in message['body'][list_label] ] + return cls(*items) + + @classmethod + def allowedin(cls): + """ + Returns the allowed content class. Defaults to :py:class:`Item` + """ + return cls.__allowedin__ + + @classmethod + def label(cls): + item_name = cls.allowedin().label() + return '%s_list' % item_name +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/musixmatch/lyrics.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,43 @@ +""" +This module contains higher level classes to query Musixmatch API and build +simple dictionary-like objects representing a track lyrics. + +>>> from musixmatch.lyrics import Lyrics +>>> import musixmatch.api +>>> +>>> try: +... lyrics = Lyrics(lyrics_id=292) +... except musixmatch.api.Error, e: +... pass +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch.base import Item +from musixmatch.ws import track + +class Lyrics(Item): + """ + This class builds a :py:class:`dict` object representing a the lyrics of a + track. It can get lyrics through the :py:class:`musixmatch.api.Method` + **track.lyrics.get** or from an already well-formed :py:class:`dict`. + Create a Track object based on a given keyword argument: + + :param track_id: musiXmatch track ID + :param musicbrainz_id: Musicbrainz track ID + :param track_echonest_id: Echonest track ID + :param lyrics_data: an already well-formed :py:class:`dict` of track data + :raises: :py:class:`musixmatch.api.Error` if :py:class:`musixmatch.api.ResponseStatusCode` is not 200 + + Once information are collected, the following keys are available: + + :keyword lyrics_body: the lyrics text + :keyword lyrics_id: the Musixmatch lyrics id + :keyword lyrics_language: the lyrics language + :keyword lyrics_copyright: the lyrics copyright statement + :keyword pixel_tracking_url: the pixel tracking url + :keyword script_tracking_url: the script tracking url + """ + __api_method__ = track.lyrics.get +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/musixmatch/subtitle.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,40 @@ +""" +This module contains higher level classes to query Musixmatch API and build +simple dictionary-like objects representing a track subtitle. + +>>> from musixmatch.subtitle import Subtitle +>>> import musixmatch.api +>>> +>>> try: +... subtitle = Subtitle(subtitle_id=292) +... except musixmatch.api.Error, e: +... pass +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch.base import Item +from musixmatch.ws import track + +class Subtitle(Item): + """ + This class builds a :py:class:`dict` object representing a subtitle of a + track. It can get subtitle through the :py:class:`musixmatch.api.Method` + **track.subtitle.get** or from an already well-formed :py:class:`dict`. + Create a Track object based on a given keyword argument: + + :param track_id: musiXmatch track ID + :param musicbrainz_id: Musicbrainz track ID + :param track_echonest_id: Echonest track ID + :param subtitle_data: an already well-formed :py:class:`dict` of track data + :raises: :py:class:`musixmatch.api.Error` if :py:class:`musixmatch.api.StatusCode` is not 200 + + Once information are collected, the following keys are available: + + :keyword subtitle_body: the subtitle text + :keyword subtitle_id: the Musixmatch subtitle id + :keyword subtitle_language: the subtitle language + """ + __api_method__ = track.subtitle.get +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/musixmatch/track.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,171 @@ +""" +This module contains higher level classes to query Musixmatch API and build +simple dictionary-like objects representing a Track or a TracksCollection. + +>>> from musixmatch.track import Track, TracksCollection +>>> import musixmatch.api +>>> +>>> try: +... track = Track(track_mbid=8976) +... collection = TracksCollection.fromChart(country='us', page=1) +... except musixmatch.api.Error, e: +... pass +""" +import musixmatch +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +from musixmatch import api, lyrics, subtitle +from musixmatch.base import Item, ItemsCollection +from musixmatch.ws import track, matcher, album + +_marker=object() + +class Track(Item): + """ + This class builds a :py:class:`dict` like object representing a track. It + can get track information through the :py:class:`musixmatch.api.Method` + **track.get** or from an already well-formed :py:class:`dict`. Create a + Track object based on a given keyword argument: + + :param track_id: musiXmatch track ID + :param musicbrainz_id: Musicbrainz track ID + :param track_echonest_id: Echonest track ID + :param track_data: an already well-formed :py:class:`dict` of track data + :raises: :py:exc:`musixmatch.api.Error` if :py:class:`musixmatch.api.ResponseStatusCode` is not 200 + + Once information are collected, the following keys are available: + + :keyword track_id: musiXmatch track ID + :keyword track_mbid: Musicbrainz track ID + :keyword lyrics_id: musiXmatch lyrics ID + :keyword instrumental: wether the track is instrumental or not + :keyword subtitle_id: musiXmatch subtitle ID + :keyword track_name: track name + :keyword album_coverart_100x100: album cover URL + :keyword artist_id: musiXmatch artist ID + :keyword artist_mbid: Musicbrainz artist ID + :keyword artist_name: artist name + + Keyword access have been overloaded thanks to the :py:meth:`get` method + which will eventually fetch the matching lyrics or subtitle. + """ + __api_method__ = track.get + + @classmethod + def fromMatcher(cls, **keywords): + """ + Returns a :py:class:`Track` based on the result of the + :py:class:`musiXmatch.api.Method` **matcher.track.get**. Accepts the + following keywords: + + :param q_track: words to be searched among track titles + :param q_artist: words to be searched among artist names + """ + return cls.fromResponseMessage(matcher.track.get(**keywords)) + + def get(self, key, default=_marker): + """ + If key is *lyrics* or *subtitle* try to query api for proper value, + and build an :py:class:`musixmatch.lyrics.Lyrics` or + :py:class:`musixmatch.subtitle.Subtitle`. Access to the above mentioned + keys may fail with :py:exc:`musixmatch.api.Error`. Once fetched, the + result is saved. + """ + special = { + 'lyrics': lyrics.Lyrics, + 'subtitle': subtitle.Subtitle, + } + if key in special and not key in self: + self[key] = special[key](track_id=self['track_id']) + value = dict.get(self, key, default) + if value == _marker: + raise KeyError, key + return value + + def __getitem__(self, key): + return self.get(key) + + def postFeedback(self, feedback): + """ + Post feedback about lyrics for this track. **feedback** can be one of: + + :keyword wrong_attribution: the lyrics shown are not by the artist that + I selected. + :keyword bad_characters: there are strange characters and/or words + that are partially scrambled. + :keyword lines_too_long: the text for each verse is too long! + :keyword wrong_verses: there are some verses missing from the + beginning or at the end. + :keyword wrong_formatting: the text looks horrible, please fix it! + """ + accepted = [ + 'wrong_attribution', 'bad_characters', 'lines_too_long', + 'wrong_verses', 'wrong_formatting' ] + if feedback in accepted: + message = track.lyrics.feedback.post( + track_id=self['track_id'], + lyrics_id=self['track_id']['lyrics']['lyrics_id'], + feedback=feedback + ) + if not message.status_code: + raise api.Error(str(message.status_code)) + else: + raise TypeError, '%r not in %r' % (feedback, accepted) + +class TracksCollection(ItemsCollection): + """ + This class build a :py:class:`list` like object representing a tracks + collection. It accepts :py:class:`dict` or :py:class:`Track` objects. + """ + __allowedin__ = Track + + @classmethod + def fromAlbum(cls, **keywords): + """ + This classmethod builds an :py:class:`TracksCollection` from a + **album.tracks.get** :py:class:`musixmatch.api.Method` call. + + :param album_id: musiXmatch album ID + """ + return cls.fromResponseMessage(album.tracks.get(**keywords)) + + @classmethod + def fromSearch(cls, **keywords): + """ + This classmethod builds an :py:class:`TracksCollection` from a + **track.search** :py:class:`musixmatch.api.Method` call. + + :param q: a string that will be searched in every data field + (q_track, q_artist, q_lyrics) + :param q_track: words to be searched among track titles + :param q_artist: words to be searched among artist names + :param q_track_artist: words to be searched among track titles or + artist names + :param q_lyrics: words to be searched into the lyrics + :param page: requested page of results + :param page_size: desired number of items per result page + :param f_has_lyrics: exclude tracks without an available lyrics + (automatic if q_lyrics is set) + :param f_artist_id: filter the results by the artist_id + :param f_artist_mbid: filter the results by the artist_mbid + :param quorum_factor: only works together with q and q_track_artist + parameter. Possible values goes from 0.1 to + 0.9. A value of 0.9 means: 'match at least 90 + percent of the words'. + """ + return cls.fromResponseMessage(track.search(**keywords)) + + @classmethod + def fromChart(cls, **keywords): + """ + This classmethod builds an :py:class:`TracksCollection` from a + **track.chart.get** :py:class:`musixmatch.api.Method` call. + + :param page: requested page of results + :param page_size: desired number of items per result page + :param country: the country code of the desired country chart + :param f_has_lyrics: exclude tracks without an available lyrics + (automatic if q_lyrics is set) + """ + return cls.fromResponseMessage(track.chart.get(**keywords))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/musixmatch/ws.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,30 @@ +""" +This is an utility module that provides a row musiXmatch web API interface. +Ideally it should be used like this: + +>>> import musixmatch +>>> +>>> try: +... chart = musixmatch.ws.track.chart.get(country='it', f_has_lyrics=1) +... except musixmatch.api.Error, e: +... pass +""" +from warnings import warn +import os +import musixmatch.api +__license__ = musixmatch.__license__ +__author__ = musixmatch.__author__ + +_version = os.environ.get('musixmatch_apiversion', None) +if not _version: + _version = '1.1' +else: + warn("Use of `musixmatch_apiversion' was deprecated in favour of `musixmatch_wslocation'", DeprecationWarning) + +location = os.environ.get('musixmatch_wslocation', 'http://api.musixmatch.com/ws/%s' % _version) + +artist = musixmatch.api.Method('artist') +album = musixmatch.api.Method('album') +track = musixmatch.api.Method('track') +tracking = musixmatch.api.Method('tracking') +matcher = musixmatch.api.Method('matcher')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/setup.cfg Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,11 @@ +[build_sphinx] +source-dir = docs +build-dir = build/docs +all_files = 1 + +[sdist] +formats = gztar,zip + +[upload_sphinx] +upload-dir = build/docs/html +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/setup.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,33 @@ +from setuptools import setup +import sys +import os + +wd = os.path.dirname(os.path.abspath(__file__)) +os.chdir(wd) +sys.path.insert(1, wd) + +name = 'musixmatch' +pkg = __import__(name) +author, email = pkg.__author__.rsplit(' ', 1) + +with open(os.path.join(wd, 'README.rst'),'r') as readme: + long_description = readme.read() + +url = 'http://projects.monkeython.com/musixmatch', +egg = { + 'name': name, + 'version': pkg.__version__, + 'author': author, + 'author_email': email.strip('<>'), + 'url': url, + 'description': "Package to interface with the Musixmatch API", + 'long_description': long_description, + 'download_url': '%s/dists' % url, + 'classifiers': pkg.__classifiers__, + 'packages': [name], + 'include_package_data': True, + 'exclude_package_data': {name: ["*.rst", "docs", "tests"]}, + 'test_suite': 'tests.suite'} + +if __name__ == '__main__': + setup(**egg)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/tests/__init__.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,39 @@ +import os +from unittest import defaultTestLoader, TestSuite +import api +import apikey +import artist +import base +import lyrics +import subtitle +import track +import album + +suite = TestSuite() +suite.addTest(defaultTestLoader.loadTestsFromModule(api)) +suite.addTest(defaultTestLoader.loadTestsFromModule(artist)) +suite.addTest(defaultTestLoader.loadTestsFromModule(base)) +suite.addTest(defaultTestLoader.loadTestsFromModule(lyrics)) +suite.addTest(defaultTestLoader.loadTestsFromModule(subtitle)) +suite.addTest(defaultTestLoader.loadTestsFromModule(track)) +suite.addTest(defaultTestLoader.loadTestsFromModule(album)) +# if os.environ.get('musixmatch_apikey', None): +# suite.addTest(defaultTestLoader.loadTestsFromModule(apikey)) + +# suite.addTest(api.TestError()) +# suite.addTest(api.TestResponseStatusCode()) +# suite.addTest(api.TestResponseMessage()) +# suite.addTest(api.TestXMLResponseMessage()) +# suite.addTest(api.TestJsonResponseMessage()) +# suite.addTest(api.TestQueryString()) +# suite.addTest(api.TestRequest()) +# suite.addTest(api.TestMethod()) +# suite.addTest(base.TestBase()) +# suite.addTest(base.TestItem()) +# suite.addTest(base.TestCollection()) +# suite.addTest(artist.TestArtist()) +# suite.addTest(artist.TestArtistsCollection()) +# suite.addTest(track.TestTrack()) +# suite.addTest(track.TestTracksCollection()) +# suite.addTest(lyrics.TestLyrics()) +# suite.addTest(subtitle.TestSubtitle())
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/tests/album.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,48 @@ +from musixmatch import * +from tests import base + +class TestAlbum(base.TestItem): + Class = album.Album + item = { + "album_id": "292", + "album_name": "292", + } + item_str = "{ 'album_id': '292',\n 'album_name': '292'}" + item_repr = "Album({'album_name': '292', 'album_id': '292'})" + item_hash = 292 + +class TestAlbumsCollection(base.TestCollection): + CollectionClass = album.AlbumsCollection + AllowedContentClass = album.AlbumsCollection.allowedin() + item_list = 'album_list' + item_id = 'album_id' + item = 'album' + message = { + "body": { + "album_list": [ + { + "album": { + "album_id": "292", + "album_name": "292", + } + }, + { + "album": { + "album_id": "8976", + "album_name": "8976", + } + }, + { + "album": { + "album_id": "9673", + "album_name": "9673", + } + } + ] + }, + "header": { + "execute_time": 0.14144802093506001, + "status_code": 200 + } + } +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/tests/api.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,91 @@ +import unittest +from musixmatch import * +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + +class TestError(unittest.TestCase): + + def test__str__(self): + error = api.Error('test error', Exception('test exception')) + self.assertEqual(str(error), 'test error: test exception') + +class TestResponseStatusCode(unittest.TestCase): + + def test__init__(self): + self.assertRaises(ValueError, api.ResponseStatusCode, 'fail') + + self.assertRaises(TypeError, api.ResponseStatusCode, [1,2,3]) + + def test__int__(self): + self.assertEqual(int(api.ResponseStatusCode('1')), 1) + + def test__str__(self): + self.assertEqual(str(api.ResponseStatusCode('200')), + 'The request was successful.') + + self.assertEqual(str(api.ResponseStatusCode('-1')), + 'Unknown status code -1!') + + def test__nonzero__(self): + self.assertEqual(bool(api.ResponseStatusCode('200')), True) + self.assertEqual(bool(api.ResponseStatusCode('404')), False) + +class TestResponseMessage(unittest.TestCase): + + def test__init__(self): + self.assertRaises(NotImplementedError, api.ResponseMessage, '') + +class TestXMLResponseMessage(unittest.TestCase): + + message = """<message> + <header> + <status_code>200</status_code> + </header> + <body> + </body> +</message>""" + + def test_status_code(self): + message = api.XMLResponseMessage(StringIO(self.message)) + self.assertEqual(isinstance(message.status_code, api.ResponseStatusCode), True) + +class TestJsonResponseMessage(unittest.TestCase): + message = """{"message":{ + "header":{ + "status_code":200}, + "body":{ +}}}""" + + def test_status_code(self): + message = api.JsonResponseMessage(StringIO(self.message)) + self.assertEqual(isinstance(message.status_code, api.ResponseStatusCode), True) + +class TestQueryString(unittest.TestCase): + def test__str__(self): + keywords = { 'country': 'it', 'page': 1, 'page_size': 3 } + query_string = api.QueryString(keywords) + self.assertEqual(str(query_string), 'country=it&page=1&page_size=3') + def test__repr__(self): + keywords = { 'apikey': 'it', 'id': 12345, 'format': 'json' } + query_string = api.QueryString(keywords) + self.assertEqual(repr(query_string).count('apikey'), 0) + +class TestRequest(unittest.TestCase): + def test__str__(self): + url = 'http://api.musixmatch.com/ws/1.1/test?apikey=apikey&format=format'.encode('utf-8') + method = api.Method('test') + query_string = api.QueryString({'apikey':'apikey','format':'format'}) + request = api.Request(method, query_string) + self.assertEqual(str(request), url) + +class TestMethod(unittest.TestCase): + + def test__getattribute__(self): + method = api.Method('test') + self.assertEqual(hasattr(method, 'subtest'), True) + self.assertEqual(hasattr(method, '__nothing__'), False) + +if __name__ == '__main__': + unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/tests/apikey.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,67 @@ +import unittest +from musixmatch import * + +class TestArtist(unittest.TestCase): + def test_Artist(self): + a1 = artist.Artist(artist_id=378462) + a2 = artist.Artist(artist_mbid='650e7db6-b795-4eb5-a702-5ea2fc46c848') + self.assertEqual(a1,a2) + +class TestArtistsCollection(unittest.TestCase): + def test_fromChart(self): + c = artist.ArtistsCollection.fromChart(page=1, page_size=10) + self.assertEqual(len(c), 10) + for i in c: + self.assertEqual(type(i), artist.Artist) + + def test_fromSearch(self): + c = artist.ArtistsCollection.fromSearch( + q='madonna', page=1, page_size=10) + self.assertEqual(len(c), 10) + for i in c: + self.assertEqual(type(i), artist.Artist) + +class TestTrack(unittest.TestCase): + def test_Track(self): + t1 = track.Track(track_id=7176425) + t2 = track.Track(track_mbid='a5424f77-42d9-428c-9c6f-3f06ff19d756') + self.assertEqual(t1,t2) + + def test_fromMatcher(self): + t = track.Track.fromMatcher( + q_track='lose yourself (album version)', q_artist='eminem') + self.assertEqual(bool(t), True) + + def test_get(self): + t = track.Track(track_id=6593495) + l = t['lyrics'] + self.assertEqual(bool(l), True) + self.assertEqual(type(l), lyrics.Lyrics) + s = t.get('subtitle') + self.assertEqual(bool(s), True) + self.assertEqual(type(s), subtitle.Subtitle) + + +class TestTracksCollection(unittest.TestCase): + def test_fromChart(self): + c = track.TracksCollection.fromChart(page=1, page_size=10) + self.assertEqual(len(c), 10) + for i in c: + self.assertEqual(type(i), track.Track) + + def test_fromSearch(self): + c = track.TracksCollection.fromSearch(q_track='Cotton eye Joe') + self.assertEqual(len(c), 10) + for i in c: + self.assertEqual(type(i), track.Track) + +class TestLyrics(unittest.TestCase): + def test_Lyrics(self): + l = lyrics.Lyrics(track_id='4559887') + self.assertEqual(bool(l), True) + +class TestSubtitle(unittest.TestCase): + def test_Subtitle(self): + s = subtitle.Subtitle(track_id='6593495') + self.assertEqual(bool(s), True) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/tests/artist.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,48 @@ +from musixmatch import * +from tests import base + +class TestArtist(base.TestItem): + Class = artist.Artist + item = { + "artist_id": "292", + "artist_mbid": "292", + } + item_str = "{ 'artist_id': '292',\n 'artist_mbid': '292'}" + item_repr = "Artist({'artist_mbid': '292', 'artist_id': '292'})" + item_hash = 292 + +class TestArtistsCollection(base.TestCollection): + CollectionClass = artist.ArtistsCollection + AllowedContentClass = artist.ArtistsCollection.allowedin() + item_list = 'artist_list' + item_id = 'artist_id' + item = 'artist' + message = { + "body": { + "artist_list": [ + { + "artist": { + "artist_id": "292", + "artist_mbid": "292", + } + }, + { + "artist": { + "artist_id": "8976", + "artist_mbid": "8976", + } + }, + { + "artist": { + "artist_id": "9673", + "artist_mbid": "9673", + } + } + ] + }, + "header": { + "execute_time": 0.14144802093506001, + "status_code": 200 + } + } +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/tests/base.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,214 @@ +import unittest +from musixmatch import * + +class TestBase(unittest.TestCase): + + Class = base.Base + def test_label(self): + self.assertEqual(self.Class.label(), self.Class.__name__.lower()) + +class TestItem(TestBase): + Class = base.Item + item = { "item_id": "9673" } + item_str = "{ 'item_id': '9673'}" + item_repr = "Item({'item_id': '9673'})" + item_hash = 9673 + item_id = 'item_id' + + # def test_fromResponseMessage(self): + # self.assertRaises(api.Error, + # self.Class.fromResponseMessage, self.fail) + # success = self.Class.fromResponseMessage(self.success) + # self.assertEqual(success[item_id], + # self.success['body'][self.label()][self.item_id]) + def test_fromDictionary(self): + item = self.Class.fromDictionary(self.item) + # Data integrity + for k in self.item.keys(): + self.assertEqual(item[k], self.item[k]) + + def test__str__(self): + item = self.Class.fromDictionary(self.item) + self.assertEqual(str(item),self.item_str) + + def test__repr__(self): + item = self.Class.fromDictionary(self.item) + self.assertEqual(repr(item),self.item_repr) + + def test__hash__(self): + item = self.Class.fromDictionary(self.item) + self.assertEqual(hash(item), self.item_hash) + +class TestCollection(unittest.TestCase): + + Class = base.ItemsCollection + AllowedContent = base.ItemsCollection.allowedin() + item_list = 'item_list' + item_id = 'item_id' + item = 'item' + message = { + "body": { + "item_list": [ + { + "item": { + "item_id": "292", + "item_name": "item_292" + } + }, + { + "item": { + "item_id": "8976", + "item_name": "item_8976" + } + }, + { + "item": { + "item_id": "9673", + "item_name": "item_9673" + } + } + ] + }, + "header": { + "execute_time": 0.14144802093506001, + "status_code": 200 + } + } + + def test_insert(self): + collection = self.Class() + saved = self.message['body'][self.item_list][0][self.item] + item = self.AllowedContent(saved) + collection.insert(0, item) + # Item correctly inserted + self.assertEqual(collection[0], item) + + saved = self.message['body'][self.item_list][1][self.item] + collection.insert(0, saved) + # Item corectly casted to self.AllowedContent + self.assertEqual(type(collection[0]), self.AllowedContent) + # Item content integrity + self.assertEqual(collection[0][self.item_id], saved[self.item_id]) + # Previously inserted item has shifted position + self.assertEqual(collection[1], item) + + def test_append(self): + collection = self.Class() + saved = self.message['body'][self.item_list][1][self.item] + item = self.AllowedContent.fromDictionary(saved) + collection.append(item) + # Item correctly appended + self.assertEqual(collection[0], item) + + saved = self.message['body'][self.item_list][2][self.item] + collection.append(saved) + # Item correctly appended + self.assertEqual(collection[1][self.item_id], saved[self.item_id]) + + saved = self.message['body'][self.item_list][0][self.item] + collection.append(saved) + # Item corectly casted to self.AllowedContent + self.assertEqual(type(collection[2]), self.AllowedContent) + # Item content integrity + self.assertEqual(collection[2][self.item_id], saved[self.item_id]) + + def test_extend(self): + items = [ i[self.item] for i in self.message['body'][self.item_list] ] + collection = self.Class(self.AllowedContent(items[0])) + self.assertEqual(type(collection[0]), self.AllowedContent) + typed = [ self.AllowedContent(i) for i in items[1:] ] + for i in typed: + self.assertEqual(type(i), self.AllowedContent) + collection.extend(typed) + # Collection correctly extended + self.assertEqual(collection[1], typed[0]) + self.assertEqual(collection[2], typed[1]) + + row = items[:2] + items[1:3] + collection.extend(row) + # Items content integrity: no duplicate + self.assertEqual(len(collection), 3) + + collection = self.Class() + collection.extend(row) + self.assertEqual(len(row), 4) + self.assertEqual(len(collection), 3) + # Items corectly casted to self.AllowedContent + for i in range(3): + self.assertEqual(type(collection[i]), self.AllowedContent) + + # def test__setitem__(self): + # collection = self.Class(self.AllowedContent( + # self.message['body'][self.item_list][1][self.item])) + # saved = self.message['body'][self.item_list][2][self.item] + # item = self.AllowedContent(saved) + # collection[0] = item + # # Index of ItemsCollection correctly set + # self.assertEqual(collection[0], item) + + # saved = self.message['body'][self.item_list][0][self.item] + # collection[0] = saved + # # Item corectly casted to self.AllowedContent + # self.assertEqual(type(collection[0]),self.AllowedContent) + # # Item content integrity + # self.assertEqual(collection[0][self.item_id], saved[self.item_id]) + # # Wrong setting + # self.assertRaises(IndexError, collection.__setitem__, 9, saved) + # self.assertRaises(TypeError, collection.__setitem__, 'test', saved) + + def test_page(self): + items = [ i[self.item] for i in self.message['body'][self.item_list] ] + collection = self.Class(*items) + self.assertEqual(len(collection), 3) + for i in range(3): + self.assertEqual(type(collection[i]), self.AllowedContent) + page = collection.page(1,2) + self.assertEqual(len(page), 1) + self.assertEqual(type(page), self.Class) + self.assertEqual(type(page[0]), self.AllowedContent) + + def test_pages(self): + items = [ i[self.item] for i in self.message['body'][self.item_list] ] + collection = self.Class(*items) + self.assertEqual(len(collection), 3) + pages = collection.pages(2) + self.assertEqual(pages, 2) + + def test_paged(self): + items = [ i[self.item] for i in self.message['body'][self.item_list] ] + collection = self.Class(*items) + self.assertEqual(len(collection), 3) + for i in range(3): + self.assertEqual(type(collection[i]), self.AllowedContent) + paged = collection.paged(2) + self.assertEqual(len(paged), 2) + for i,l in zip(range(2), (2,1)): + self.assertEqual(len(paged[i]), l) + self.assertEqual(type(paged[i]), self.Class) + for p,i in [(0,0),(0,1),(1,0)]: + self.assertEqual(id(paged[p][i]), id(collection[(2*p)+i])) + + def test_pager(self): + items = [ i[self.item] for i in self.message['body'][self.item_list] ] + collection = self.Class(*items) + self.assertEqual(len(collection), 3) + for i in range(3): + self.assertEqual(type(collection[i]), self.AllowedContent) + pager = [] + for page in collection.pager(2): + self.assertEqual(type(page), self.Class) + self.assertEqual(type(page[0]), self.AllowedContent) + pager.append(page) + self.assertEqual(len(pager), 2) + self.assertEqual(len(pager[0]), 2) + self.assertEqual(len(pager[1]), 1) + + def test__add__(self): + items = [ i[self.item] for i in self.message['body'][self.item_list] ] + collection1 = self.Class(*items[:2]) + collection2 = self.Class(*items[1:3]) + collection3 = collection1 + collection2 + # Collection correctly created + self.assertEqual(type(collection3), self.Class) + self.assertEqual(len(collection3), 3) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/tests/lyrics.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,12 @@ +from musixmatch import * +from tests import base + +class TestLyrics(base.TestItem): + Class = lyrics.Lyrics + item = { + "lyrics_id": "292", + "lyrics_mbid": "292", + } + item_str = "{ 'lyrics_id': '292',\n 'lyrics_mbid': '292'}" + item_repr = "Lyrics({'lyrics_id': '292', 'lyrics_mbid': '292'})" + item_hash = 292
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/tests/subtitle.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,12 @@ +from musixmatch import * +from tests import base + +class TestSubtitle(base.TestItem): + Class = subtitle.Subtitle + item = { + "subtitle_id": "292", + "subtitle_mbid": "292", + } + item_str = "{ 'subtitle_id': '292',\n 'subtitle_mbid': '292'}" + item_repr = "Subtitle({'subtitle_mbid': '292', 'subtitle_id': '292'})" + item_hash = 292
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musixmatch-master/tests/track.py Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,48 @@ +from musixmatch import * +from tests import base + +class TestTrack(base.TestItem): + Class = track.Track + item = { + "track_id": "292", + "track_mbid": "292", + } + item_str = "{ 'track_id': '292',\n 'track_mbid': '292'}" + item_repr = "Track({'track_mbid': '292', 'track_id': '292'})" + item_hash = 292 + +class TestTracksCollection(base.TestCollection): + CollectionClass = track.TracksCollection + AllowedContentClass = track.TracksCollection.allowedin() + item_list = 'track_list' + item_id = 'track_id' + item = 'track' + message = { + "body": { + "track_list": [ + { + "track": { + "track_id": "292", + "track_mbid": "292", + } + }, + { + "track": { + "track_id": "8976", + "track_mbid": "8976", + } + }, + { + "track": { + "track_id": "9673", + "track_mbid": "9673", + } + } + ] + }, + "header": { + "execute_time": 0.14144802093506001, + "status_code": 200 + } + } +
--- a/musixmatch.py Sat Apr 20 14:01:32 2013 +0200 +++ b/musixmatch.py Sat Apr 20 19:01:57 2013 +0200 @@ -1,6 +1,21 @@ import musixmatch +import musixmatch.ws +import json -try: - chart = musixmatch.ws.track.chart.get(country='it', apikey=apikey) -except musixmatch.api.Error, e: - pass \ No newline at end of file +apikey = '8496dd1c715c69a74ef9fcde1716cf4a' + + +chart = musixmatch.ws.track.chart.get(country='it', apikey=apikey) + + +lyrics = musixmatch.ws.track.lyrics.get('Sexy and I know it',apikey=apikey) + +print '\n' + 'XXXXXXXXX' + +for each_item in chart['body']['track_list']: + print each_item + + + +for each_line in chart['body']['lyrics']: + print each_line
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/script.rb Sat Apr 20 19:01:57 2013 +0200 @@ -0,0 +1,8 @@ +require 'open-uri' + +kittens = open('http://placekitten.com/') +response_status = kittens.status +response_body = kittens.read[559, 441] + +puts response_status +puts response_body \ No newline at end of file