-
Notifications
You must be signed in to change notification settings - Fork 231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Searching for music library tracks, finding all artists' albums and artist's album's tracks. #246
Conversation
1 similar comment
Hi @dajobe , Observations / thoughts:
2a) Observation: UPNP returns items found and total matches. The later lets you know if you need to loop and call again. I don't see total matches returned here, but I may have missed it. Would be useful, otherwise have to keep looping until null return
Hope this help, |
@@ -133,3 +133,9 @@ def decorated(*args, **kwargs): | |||
decorated.__doc__ = '' | |||
decorated.__doc__ += docs | |||
return decorated | |||
|
|||
|
|||
def url_escape_path(path): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd recommend to add a doctest here, to illustrate what the method does.
Hallo @dajobe This looks really great and great work on the escaping. I tried with some Danish vowels 'æøå' and it worked great. About the whole unicode encoding problem, it is something I don't believe we have discussed before. I think a lot of the problems we have with methods that take text as an argument, is that per default, if you fire up python <3 writing Regarding the actual PR, I think it looks great. One thing that I had been thinking about when we previously talked about searching, is that it would be great if it was possible to do the fuzzy searching that @DPH talks about in all the music library functions. I also had the idea, that maybe those specific searches you do, could be done easier by creating a dummy music library item and levering the browse method. Based on you work I have made an alternative implementation in #249 as a basis for discussion. I think it makes some of the things a bit simpler. (although not everything in implemented 1to1 with your functionality). I also specifically does not handle the unicode stuff mentioned above, but that's really just to add the really_unicode on all text input before anything else is done with it. Let me know what you think. Regards Kenneth |
@KennethNielsen thanks for the feedback. On Unicode, I realise it's a mess but I was happy it seemed to just work. Looks like it's the big py2/py3 issue. On the APIs themselves I'm not sure whether to return a SearchResult (that allows paging or partial results) or a list of actual values. I've got different APIs here. Maybe if a raw search, you want paging but for the 'get albums of artist' mode, you want everything, no paging. I'd like to add the fuzzy / wildcard searching, as @DPH suggested too. I'll take a look at your new PR #249 Thanks |
Hi @dajobe Regarding unicode, I guess it is not as much a py2/py3 issue, since if we were porting it there would be no issue, the problem is creating a py2/py3 compatible library that takes text input. That being said, lets try to de-mystify it a little. In the following, I have just tested with one version <3 and one version >=3, so there might be subtle differences in there, that would mess with the conclusions, but I don't know of any. In python 3 all string are created per default as unicode objects, so the text input we get and the strings created are all unicode. In core.py and in utils.py we have the Python 2.7.6 (default, Mar 22 2014, 22:59:56)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from __future__ import unicode_literals
>>> 'Hallo World!'
u'Hallo World!'
>>> a = 'bøf' # Danish for steak
>>> a
u'b\xf8f'
>>> b = ' med løg' # With onions, in Danish
>>> b
u' med l\xf8g'
>>> a + b
u'b\xf8f med l\xf8g'
>>> a += b
>>> a
u'b\xf8f med l\xf8g'
>>> '{} med {}'.format('bøf', 'løg')
u'b\xf8f med l\xf8g' Of course, if we happen to have gotten a bytestring and try to combine, we get errors, if the bytestring has characters outside of ASCII. So better to avoid combining. This means, that there are only two cases where things can go wrong:
With regards to 1. The only 3rd party library function in use for this, is the quote_url function (which is imported from urllib via the compat module) used in url_escape_path. We see that in python2, it wants byte-arrays and returns byte arrays, but because we have imported unicode literals and because a replace makes a new string, this is turned into a unicode object after the replace: Python 2.7.6 (default, Mar 22 2014, 22:59:56)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from __future__ import unicode_literals
>>> from soco.compat import quote_url
>>> quote_url('bøf') # Is unicode due to the import
/usr/lib/python2.7/urllib.py:1288: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
return ''.join(map(quoter, s))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/urllib.py", line 1288, in quote
return ''.join(map(quoter, s))
KeyError: u'\xf8'
>>> quote_url('bøf'.encode('utf-8'))
'b%C3%B8f'
>>> quote_url('bøf'.encode('utf-8')).replace('/', '%2F')
u'b%C3%B8f' In python3, things are simpler, qoute_url takes unicode objects and returns unicode objects. Python 3.4.0 (default, Apr 11 2014, 13:05:11)
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from __future__ import unicode_literals
>>> from soco.compat import quote_url
>>> quote_url('bøf')
'b%C3%B8f'
>>> # That is unicode, in python 3 unicode is the standard
>>> quote_url('bøf').replace('', '')
'b%C3%B8f' This means, that to produce a consistent result, the only thing we need, is to ensure that the input to url_escape_path is unicode. With regards to 2. Unless we want to make consistent input the users responsibility, there is really nothing we can do about this, we might get both unicode objects and byte arrays when asking for text. On the other hand, it is simple to fix, if we are aware of it. The only thing that we need to do, to make sure that we use unicode everywhere internally (which is also what we expected in 1.) is to call really_unicode whenever we use a user input text string (or to convert it as the very first thing in the methods, but in these methods, they are not used more than once which makes that a bit much) In conclusion (and I might be wrong here, character encodings and strings are not my strong suit) I would argue, that if you just call really_unicode on any text input to the methods, that will take care of both 1. and 2. and make it just work (see #249). On a side note, there are of course other solutions one might consider:
|
@dajobe With regards to paging I agree with you, that these specialized searches could (and should) return the complete result. But I would still use a data structure for it (surprise surprise), just create a new new one say CompleteSearchResult, that as the metadata has only the number of items. But that will require a bit of mangling with the inheritance, so we can do that later. But as a I said, I agree of returning a complete result (no paging). |
@KennethNielsen I updated this branch with your code from PR #249 so lets focus here. I had to disable some of the tests since the asserts weren't working - see commit ada4e0d |
1 similar comment
I'm pretty happy now with the state of this PR. It still works (firstly) and the code is a lot neater, there are a few more tests that cover the Browse() responses. |
I will look it over as soon as I find a little time. |
get_music_library_items('artists', start=0, max_items=100) | ||
get_music_library_items('artists', start=100, max_items=100) | ||
|
||
will get the first and next 100 items, respectively. It is also |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's missing the "possible" here, "It is also POSSIBLE to ask .."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
Travis CI seems broken now, so don't believe the CI build failure. |
b11b37b
to
21f4bfe
Compare
New methods: - search_track - get_albums_for_artist - get_tracks_for_album with supporting utility function url_escape_path and compatibility function quote_url
Overwrite soco/core.py with Kenneth Nielsen's approach.
Removed reference to max_items arg.
Add missing '+possible'
21f4bfe
to
d9d093b
Compare
Hey @KennethNielsen @stefankoegl @lawrenceakka I rebased this patch again (which is getting rather tedious) and we're lucky that Travis CI is working today. Is there any chance we can get this patch merged since it's been cooking for a while? |
I'm cannot test it right now, but provided you have done some quick testing on it, to confirm that nothing was broken in the rebasing, then +1 from me. Since we already have a lot of positive feedback, I'll merge by tomorrow unless anyone objects. |
@KennethNielsen Quick testing works with my Unicode album and tracks names which says it's good for me! |
Searching for music library tracks, finding all artists' albums and artist's album's tracks.
Merged, thanks for the great work :) |
Can you please add a short summary for the release notes (#261). |
Thanks a lot, added the release notes. |
@dajobe Great work. Worry, I forgot about merging this, but luckily @stefankoegl toke care of it. |
Adds searching for music library tracks, finding all artists' albums and artist's album's tracks.
This adds a set of search methods for the music library (not music services) by the means of updating
get_music_library_information
to support fuzzy searches, category searches and getting all matches by handling paging through results.The new methods (that all call
get_music_library_information
with the new args) are:search_tracks
: to find everything for an artist, artist+album, artist+album+track combinationsget_albums_for_artist
to get all albums for an artistget_tracks_for_album
to get all tracks on an artist's albumThe code works well and I've used this to search for a bunch of music library tracks automatically. It took a while to get the Unicode and URL encoding working especially from py2.6 to 3.x. I also added a new compatibility call quote_path from urllib or urllib.parse (py3) that is used by new utility function
url_escape_path
.