Collections Python - nesciens/xmms2-wiki GitHub Wiki

**SOMETHING PRETTY SIMILAR TO ALTERNATIVE 5 IS IMPLEMENTED - IT NEEDS TO BE DOCUMENTED HERE**
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 cool.save(xmmsc, "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!

⚠️ **GitHub.com Fallback** ⚠️