Skip to content

Commit

Permalink
Initial support for collections
Browse files Browse the repository at this point in the history
  • Loading branch information
fuzeman committed May 30, 2014
1 parent 78e63ea commit 71a9033
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 5 deletions.
34 changes: 34 additions & 0 deletions examples/collection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from spotify.client import Spotify

import logging
import os

log = logging.getLogger(__name__)


class App(object):
def __init__(self):
self.sp = Spotify()

def run(self):
@self.sp.login(os.environ['USERNAME'], os.environ['PASSWORD'])
def on_login():
self.sp.user.collection('albumscoverlist', callback=self.on_collection)

def on_collection(self, albums):
for album in albums:
print album.name
print album.uri
print album.artists[0].name
print album.artists[0].uri
print album.covers[0].file_url


if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)

app = App()
app.run()

while True:
raw_input()
3 changes: 3 additions & 0 deletions spotify/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ def playlist(self, uri, start=0, count=100, callback=None):
def playlists(self, username, start=0, count=100, callback=None):
return self.components.metadata.playlists(username, start, count, callback)

def collection(self, username, source, params=None, callback=None):
return self.components.metadata.collection(username, source, params, callback)

# Search
def search(self, query, query_type='all', start=0, count=50, callback=None):
return self.components.search.search(query, query_type, start, count, callback)
17 changes: 15 additions & 2 deletions spotify/components/metadata.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import logging

from spotify.components.base import Component
from spotify.hermes.request import HermesRequest
from spotify.core.uri import Uri
from spotify.objects import Album, Track, Artist, Playlist

import logging
import urllib

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -71,3 +71,16 @@ def playlists(self, username, start=0, count=100, callback=None):
})

return self.request_wrapper(request, callback)

def collection(self, username, source, params=None, callback=None):
query = urllib.urlencode(params or {})

request = HermesRequest(self.sp, {
'method': 'GET',
'uri': 'hm:https://collection-web/v1/%s/%s%s' % (username, source, query)
}, {
'album': Album,
'artist': Artist
})

return self.request_wrapper(request, callback)
32 changes: 29 additions & 3 deletions spotify/mercury/request.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from spotify.core.request import Request
from spotify.core.uri import Uri
from spotify.objects import NAME_MAP
from spotify.proto import mercury_pb2

from collections import OrderedDict
import base64
import httplib
import json
import logging

log = logging.getLogger(__name__)
Expand All @@ -30,6 +32,7 @@ def __init__(self, sp, name, requests, schema, header=None, defaults=None):

self.request = None
self.request_payload = None
self.multi = None

self.response = OrderedDict()

Expand Down Expand Up @@ -95,18 +98,36 @@ def process(self, data):
self.process_reply(header, base64.b64decode(result[1]))

def process_reply(self, header, data):
content_type = header.content_type.split(';')

if header.content_type == 'vnd.spotify/mercury-mget-reply':
self.multi = True

response = mercury_pb2.MercuryMultiGetReply()
response.ParseFromString(data)

# Parse items
items = [
(item.content_type, self.parse_protobuf(
item.body, item.content_type
))
if item.status_code == 200 else None
for item in response.reply
]
elif content_type and content_type[0].endswith('+json'):
self.multi = True

data = json.loads(data)

# Parse items
items = [
(Uri.from_uri(item.get('uri')).type, item)
for item in data
]
else:
self.multi = False

# Parse items
items = [(header.content_type, self.parse_protobuf(
data, header.content_type
))]
Expand Down Expand Up @@ -147,11 +168,16 @@ def respond(self):
# Get item descriptor
cls = self.find_descriptor(content_type)

# Build item from protobuf
result.append(cls.from_protobuf(self.sp, internal, NAME_MAP, self.defaults))
# Build object from data
if type(internal) is dict:
item = cls.from_dict(self.sp, internal, NAME_MAP)
else:
item = cls.from_protobuf(self.sp, internal, NAME_MAP, self.defaults)

result.append(item)

# Emit success event
if len(self.requests) == 1:
if len(self.requests) == 1 and not self.multi:
self.emit('success', result[0])
else:
self.emit('success', result)
Expand Down
24 changes: 24 additions & 0 deletions spotify/objects/album.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,30 @@ def from_node_dict(cls, sp, data, types):
'external_id': data.get('external-ids')
}, types)

@classmethod
def from_dict(cls, sp, data, types):
uri = Uri.from_uri(data.get('uri'))

image_uri = data.get('imageUri')
file_id = image_uri[image_uri.rfind('/') + 1:]

return cls(sp, {
'name': data.get('name'),
'gid': uri.to_gid(),
'artist': [
{
'uri': data.get('artistUri'),
'name': data.get('artistName')
}
],
'cover': [
{
'file_id': file_id,
'size': 0
}
]
}, types)

@classmethod
def get_type(cls, value):
if value == 'album':
Expand Down
9 changes: 9 additions & 0 deletions spotify/objects/artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ def from_node_dict(cls, sp, data, types):
'restriction': data.get('restrictions')
}, types)

@classmethod
def from_dict(cls, sp, data, types):
uri = Uri.from_uri(data.get('uri'))

return cls(sp, {
'name': data.get('name'),
'gid': uri.to_gid()
}, types)

@classmethod
def get_portraits(cls, data):
portrait = data.get('portrait', None)
Expand Down
7 changes: 7 additions & 0 deletions spotify/objects/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,10 @@ def playlists(self, start=0, count=100, callback=None):
start, count,
callback
)

def collection(self, source, params=None, callback=None):
return self.sp.collection(
self.username,
source, params,
callback
)

0 comments on commit 71a9033

Please sign in to comment.