Skip to content

Commit

Permalink
Merge branch 'release-2012.08.31'
Browse files Browse the repository at this point in the history
  • Loading branch information
simon-weber committed Aug 31, 2012
2 parents fb49e4c + 0ce3a98 commit e4797d4
Show file tree
Hide file tree
Showing 11 changed files with 49 additions and 20 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Expand Up @@ -12,3 +12,8 @@ Thumbs.db
docs/build
__pycache__
gmusicapi.log

bin
lib
include
share
4 changes: 1 addition & 3 deletions README.md
Expand Up @@ -12,11 +12,9 @@ For bugs reports, feature requests, and contributions, go ahead and [open an iss

##Features

**New in version 2012.05.04**
**New in version 2012.08.31**

* various fixes and compatibility updates

There were also numerous breaking changes needed to improve the Api interface. See the changelog and documentation for details.

**Feature Overview:**

Expand Down
4 changes: 4 additions & 0 deletions changelog
@@ -1,3 +1,7 @@
2012.08.31:
metadata compatibility updates (storeId, lastPlayed)
fix uploading of unicode filenames without tags

2012.05.04:
update allowed rating values to 1-5 (David Dooling)
update metajamId to matchedId (David Dooling)
Expand Down
2 changes: 1 addition & 1 deletion docs/source/conf.py
Expand Up @@ -43,7 +43,7 @@
# built documents.
#
# The short X.Y version.
version = '2012.05.04'
version = '2012.08.31'
# The full version, including alpha/beta/rc tags.
release = version

Expand Down
6 changes: 4 additions & 2 deletions docs/source/index.rst
Expand Up @@ -79,8 +79,10 @@ These dictionaries have up to 30 keys. Here is an example::
'artistNorm': 'the cat empire',
'subjectToCuration': False,
'matchedId': '',
(optional entry; exists if there is album art)
'albumArtUrl': '//lh6.googleusercontent.com/<long identifier>'

#optional entries:
'albumArtUrl': '//lh6.googleusercontent.com/<long identifier>',
'storeID': '<27 char base64 string>'
}


Expand Down
10 changes: 3 additions & 7 deletions gmusicapi/api.py
Expand Up @@ -287,17 +287,15 @@ def get_playlist_songs(self, playlist_id):

return self._wc_call("loadplaylist", playlist_id)["playlist"]

def get_all_playlist_ids(self, auto=True, instant=True, user=True, always_id_lists=False):
def get_all_playlist_ids(self, auto=True, user=True, always_id_lists=False):
"""Returns a dictionary mapping playlist types to dictionaries of ``{"<playlist name>": "<playlist id>"}`` pairs.
Available playlist types are:
* "`auto`" - auto playlists
* "`instant`" - instant mixes
* "`user`" - user-defined playlists
* "`user`" - user-defined playlists (including instant mixes)
:param auto: make an "`auto`" entry in the result.
:param instant: make an "`instant`" entry in the result.
:param user: make a "`user`" entry in the result.
:param always_id_lists: when False, map name -> id when there is a single playlist for that name. When True, always map to a list (which may only have a single id in it).
Expand All @@ -312,8 +310,6 @@ def get_all_playlist_ids(self, auto=True, instant=True, user=True, always_id_lis

if auto:
playlists['auto'] = self._get_auto_playlists()
if instant:
playlists['instant'] = self._playlist_list_to_dict(res['magicPlaylists'])
if user:
playlists['user'] = self._playlist_list_to_dict(res['playlists'])

Expand All @@ -339,7 +335,7 @@ def _get_auto_playlists(self):

#Auto playlist ids are hardcoded in the wc javascript.
#If Google releases Music internationally, this will probably be broken.
#TODO: how to test for this? if loaded, will the calls just fail?
#When testing, an incorrect name here will be caught.
return {"Thumbs up": "auto-playlist-thumbs-up",
"Last added": "auto-playlist-recent",
"Free and purchased": "auto-playlist-promo"}
Expand Down
15 changes: 13 additions & 2 deletions gmusicapi/protocol.py
Expand Up @@ -266,6 +266,11 @@ class subjectToCuration(_Metadata_Expectation):
val_type = "boolean"
class matchedId(_Metadata_Expectation):
mutable = False

#Seems to be a matching track in the store.
class storeId(_Metadata_Expectation):
mutable = False
optional = True


#Dependent metadata:
Expand Down Expand Up @@ -310,6 +315,7 @@ class lastPlayed(_Metadata_Expectation):
mutable = False
volatile = True
val_type = "integer"
optional = True #only appears if song has been played


class WC_Protocol(object):
Expand Down Expand Up @@ -553,7 +559,6 @@ def build_transaction(playlist_id):
req = {}
res = {"type":"object",
"properties":{
"magicPlaylists": WC_Protocol.pl_array,
"playlists": WC_Protocol.pl_array,
},
"additionalProperties":False
Expand Down Expand Up @@ -787,7 +792,13 @@ def make_metadata_request(self, filenames):
track.duration = int(audio.info.length * 1000)

#GM requires at least a title.
track.title = audio["title"][0] if "title" in audio else filename.split(r'/')[-1]
if "title" in audio:
track.title = audio["title"][0]
else:
#attempt to handle unicode filenames.
enc = utils.guess_str_encoding(filename)[0]
track.title = filename.decode(enc).split(r'/')[-1]


if "album" in audio: track.album = audio["album"][0]
if "artist" in audio: track.artist = audio["artist"][0]
Expand Down
7 changes: 4 additions & 3 deletions gmusicapi/test/regression_test_api.py
Expand Up @@ -39,8 +39,9 @@
from ..protocol import UnknownExpectation

#Expected to be in this directory.
no_tags_filename = "no_tags.mp3"
has_tags_filename = "test.mp3"
has_tags_filename = 'test.mp3'
#Also tests unicode compatibility.
no_tags_filename = '한글.mp3'

#Lots of things to be pulled out of the other api call test class here.

Expand All @@ -57,13 +58,13 @@ def setUpClass(cls):
cls.no_tags_filename = path[:string.rfind(path, os.sep)] + os.sep + no_tags_filename
cls.has_tags_filename = path[:string.rfind(path, os.sep)] + os.sep + has_tags_filename


#---
# Monolithic tests:
# (messy, but less likely to destructively modify the library)
# Modified from http://stackoverflow.com/questions/5387299/python-unittest-testcase-execution-order
#---


def notags_1_upload_notags(self):
"""Upload the file without tags."""
result = self.api.upload(self.no_tags_filename)
Expand Down
Binary file added gmusicapi/test/한글.mp3
Binary file not shown.
11 changes: 11 additions & 0 deletions gmusicapi/utils/utils.py
Expand Up @@ -34,10 +34,21 @@

import mutagen
from decorator import decorator
import chardet

from apilogging import LogController
log = LogController.get_logger("utils")

def guess_str_encoding(s):
"""Return a tuple (guessed encoding, confidence)."""

res = chardet.detect(s)
return (res['encoding'], res['confidence'])

def guess_file_encoding(filename):
with open(filename) as f:
return guess_str_encoding(f.read())

def copy_md_tags(from_fname, to_fname):
"""Copy all metadata from *from_fname* to *to_fname* and write.
Expand Down
5 changes: 3 additions & 2 deletions setup.py
Expand Up @@ -28,7 +28,7 @@

setup(
name='gmusicapi',
version='2012.05.04',
version='2012.08.31',
author='Simon Weber',
author_email='simon@simonmweber.com',
url='http://pypi.python.org/pypi/gmusicapi/',
Expand All @@ -45,7 +45,8 @@
"validictory >= 0.8.3",
"decorator >= 3.3.2",
"mutagen >= 1.2.0",
"protobuf >= 2.4.0"
"protobuf >= 2.4.0",
"chardet",
],
classifiers = [
"Programming Language :: Python",
Expand Down

0 comments on commit e4797d4

Please sign in to comment.