Collections Python

Erik Massop edited this page Nov 4, 2017 · 1 revision
In the process of porting the collections api to python it becomes apparent that a decision has to be made about how to represent the collections structure.

There are a number of options, some of them are:

All python examples use Sync API for simplicity

Alternative 1

create a collections class which represents the various collections semantics as properties and attributes (with the occasional method if necessary).

A few examples:

Get an id list from a collection and set it to the id list of a new collection.

coll_foo = xc.Collection('Foo') ids = coll_foo.ids idl_coll = xc.Collection(XMMS_COLL_TYPE_IDLIST) idl_coll.ids = ids

create a union of collection "Foo" and a custom collection (all media by Sonic Youth), and filter the resulting collection by year (only media from 2006).

coll = xc.Collection(XMMS_COLL_TYPE_AND); # Reference to collection 'Foo' operand = xc.Connection(XMMS_COLL_TYPE_REFERENCE); operand['reference'] = ('Foo', 'Collections') coll.operand.append(operand) # All media by Sonic Youth operand = xc.Collection(XMMS_COLL_TYPE_MATCH); allmedia = xc.Collection(XMMS_COLL_TYPE_UNIVERSE) operand.operand.append(allmedia) operand['field'] = 'artist' operand['value'] = 'Sonic Youth' coll.operand.append(operand) # Filter media on year 2006 last = xc.Collection(XMMS_COLL_TYPE_MATCH); last['year'] = '2006' last.operand.append(coll)

Alternative 2

Create a collections class with the same get/set methods as in the c bindings.

Alternative 3

Lets cut to the examples directly:

from xmmsclient import collections as c coll = c.And(c.Reference('Collections:Foo'),              c.Match(c.Universe(), {'field': 'artist', 'value': 'Sonic Youth'}),              c.Match(c.Universe(), {'field': 'year', 'value': '2006'}))

How to use and modify them!

print coll.operands # [] del coll.operands[0] coll.operands.append(c.Reference('Collections:Party music')) print coll.operands # [] # make it persistant on server! xc.coll_save(coll, 'Collections', 'My collection')   m = c.Match(c.Universe(), {'field': 'artist', 'value': 'Thievery Corporation'}) # get a list of ids matching collection matchingids = xc.coll_query_id(m) # I changed my mind.. m['value'] = 'Air' songsbyair = xc.coll_query_info(m, ['title', 'duration', 'rating']) for song in songsbyair:   print "%(album)s - %(title)s (%(duration)s)" % song

Alternative 3.1

Let's start with a pseudo-class-def

class SomeOp (Collection):   def __init__(self, *based_on, **arguments)   def __getitem__(self, name)   def __setitem__(self, name, val)   def get_data(self)   def set_data(self, data)   def get_bases(self)            # these three might need better names   def set_bases(self, based_on)   def add_bases(self, *based_on)

and examples:

base = c.Reference(name="SomeColl")  # **arguments for name, *based_on nothing jovibon = c.Match(base, field="artist", value="Jovi, Bun") # *based_on = [base] bonjovi = c.Match()       # we can add stuff later bonjovi.set_bases([base]) # like... here or in the next line. bonjovi.set_data({'field': 'artist', 'value': 'Bon Jovi'}) alljovi = c.Or(jovibon, bonjovi) cool = c.And(alljovi)     # This time, set half the data later. cool.add_bases(c.Not(c.Contains(base, field="title", value="Life"))) jovibon['value'] = 'Jovi, Bon'  # fix the typo, "SomeCoolCol") # XMMS2 is needed for saving to XMMS2.

Alternative 4

This one is based somewhat on alternative 3, with a few mods that I think make it more pythonic, and certainly requires less manual building (more automagic).

from xmmsclient import collections as c # Get a ref to Collections:Foo foo = c.Collection('Foo')  # Create a filter that extracts all Bon Jovi 2k6 tracks from the whole medialib. # Note that this filter filters on several elements, so several match items chained # one behind the other will be created under the hood. fil = c.Match(c.Collection(), artist='Bon Jovi', year='2006') # Intersect the two. intersect = c.op.And(foo, fil) # Append another anti-filter on a playlist intersect.append(c.op.Not(c.Playlist('Bar'))) # All track by Machinae Supremacy. masu = c.Match(c.Collection()), artist="Machinae Supremacy") # Combine these two into the final collection coll = c.op.Or(intersect, masu)

Alternative 5

This is supposed to take the good parts of 4 and merge into 3.

from xmmsclient import collections as c coll = c.And(c.CollectionRef('Foo'),              c.Match(c.Universe(), field='artist', value='Sonic Youth'}),              c.Match(c.Universe(), field='year', value='2006'})) print coll.operands # [] del coll.operands[0] coll.operands.append(c.Reference('Collections:Party music')) print coll.operands # [] # make it persistant on server! xc.coll_save(coll, 'Collections', 'My collection')   m = c.Match(c.Universe(), field='artist', value='Thievery Corporation'}) print m.attributes # {'field': 'artist', 'value': 'Thievery Corporation'} print m.operands # []   # get a list of ids matching collection matchingids = xc.coll_query_id(m) # I changed my mind.. m.attributes['value'] = 'Air' songsbyair = xc.coll_query_info(m, ['title', 'duration', 'rating']) for song in songsbyair:   print "%(album)s - %(title)s (%(duration)s)" % song

This should make clear separation between "operands" and "attributes". Instead of the c[attrib]=aval of alt3 and the c.append(operand) of alt4.

I find the automatic chaining of alt4 scary as it removes the 1:1 mapping to nodes in the dag, and when you later list that collection it will not be the same as you created. It would be better to just make a (trivial) higher level function that does it: (which makes it clear what happens, and doesn't do any magic behind the scenes -- Explicit is better than implicit)

def make_chained_matches(coll, **matches):     for f,v in matches.iteritems:         coll = c.Match(coll, field=f, value=v);     return coll

Also it is important that it is easy to change the attributes. A GUI would probably do something like:

def setup_match_gui(..):     ...     self.match_field = SomeInputWidget()     def on_field_change(val):         self.collection.attributes['field'] = val     self.match_field.on_change = on_field_change     self.match_value = SomeInputWidget()     def on_value_change(val):         self.collection.attributes['value'] = val     self.match_field.on_change = on_value_change

Alternative 6

this is at least implemented in the git version(works for me)

import xmmsclient from xmmsclient import collections import os import sys   xmms = xmmsclient.XMMS("Sasdlkfjselect")   try:    xmms.connect(os.getenv("XMMS_PATH")) except IOError, detail:    print "Connection failed:", detail    sys.exit(1)     artist = collections.Match( field="artist", value="Pink" ) album = collections.Has(artist, "album") title = collections.Match(field="title", value="%pink%")    result = xmms.coll_query_infos( artist, ["artist", "title" ]) result.wait() print result.value() print "---" result = xmms.coll_query_infos( album, ["artist", "title" ]) result.wait() print result.value()   print "---" result = xmms.coll_query_infos( title, ["artist", "title" ]) result.wait() print result.value()   print "---" result = xmms.coll_query_infos( title | album, ["artist", "title" ]) result.wait() print result.value()

Other ideas welcome!

Clone this wiki locally
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.