home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / lib / rhythmbox / plugins / upnp_coherence / MediaStore.py < prev    next >
Encoding:
Python Source  |  2009-04-07  |  14.7 KB  |  443 lines

  1. # Licensed under the MIT license
  2. # http://opensource.org/licenses/mit-license.php
  3. #
  4. # Copyright 2007, James Livingston  <doclivingston@gmail.com>
  5. # Copyright 2007, Frank Scholz <coherence@beebits.net>
  6.  
  7. import rhythmdb
  8. import coherence.extern.louie as louie
  9. import urllib
  10. from coherence.upnp.core import DIDLLite
  11.  
  12. from coherence.backend import BackendItem, BackendStore
  13.  
  14. ROOT_CONTAINER_ID = 0
  15. AUDIO_CONTAINER = 100
  16. AUDIO_ALL_CONTAINER_ID = 101
  17. AUDIO_ARTIST_CONTAINER_ID = 102
  18. AUDIO_ALBUM_CONTAINER_ID = 103
  19.  
  20. CONTAINER_COUNT = 10000
  21.  
  22. TRACK_COUNT = 1000000
  23.  
  24. # most of this class is from Coherence, originally under the MIT licence
  25.  
  26. class Container(BackendItem):
  27.  
  28.     logCategory = 'rb_media_store'
  29.  
  30.     def __init__(self, id, parent_id, name, children_callback=None):
  31.         self.id = id
  32.         self.parent_id = parent_id
  33.         self.name = name
  34.         self.mimetype = 'directory'
  35.         self.item = DIDLLite.Container(id, parent_id,self.name)
  36.         self.update_id = 0
  37.         self.item.childCount = 0
  38.         if children_callback != None:
  39.             self.children = children_callback
  40.         else:
  41.             self.children = []
  42.  
  43.     def add_child(self, child):
  44.         self.children.append(child)
  45.         self.item.childCount += 1
  46.  
  47.     def get_children(self,start=0,request_count=0):
  48.         if callable(self.children):
  49.             children = self.children(self.id)
  50.         else:
  51.             children = self.children
  52.  
  53.         self.info("Container get_children %r (%r,%r)", children, start, request_count)
  54.         if request_count == 0:
  55.             return children[start:]
  56.         else:
  57.             return children[start:request_count]
  58.  
  59.     def get_child_count(self):
  60.         return len(self.get_children())
  61.  
  62.     def get_item(self, parent_id=None):
  63.         self.item.childCount = self.get_child_count()
  64.         return self.item
  65.  
  66.     def get_name(self):
  67.         return self.name
  68.  
  69.     def get_id(self):
  70.         return self.id
  71.  
  72.  
  73. class Album(BackendItem):
  74.  
  75.     logCategory = 'rb_media_store'
  76.  
  77.     def __init__(self, store, title, id, parent_id):
  78.         self.id = id
  79.         self.title = title
  80.         self.store = store
  81.  
  82.         query = self.store.db.query_new()
  83.         self.store.db.query_append(query,[rhythmdb.QUERY_PROP_EQUALS, rhythmdb.PROP_TYPE, self.store.db.entry_type_get_by_name('song')],
  84.                                       [rhythmdb.QUERY_PROP_EQUALS, rhythmdb.PROP_ALBUM, self.title])
  85.         self.tracks_per_album_query = self.store.db.query_model_new(query)
  86.         #self.tracks_per_album_query.set_sort_order(rhythmdb.rhythmdb_query_model_track_sort_func)
  87.         self.store.db.do_full_query_async_parsed(self.tracks_per_album_query, query)
  88.  
  89.     def get_children(self,start=0,request_count=0):
  90.         children = []
  91.  
  92.         def track_sort(x,y):
  93.             entry = self.store.db.entry_lookup_by_id (x.id)
  94.             x_track = self.store.db.entry_get (entry, rhythmdb.PROP_TRACK_NUMBER)
  95.             entry = self.store.db.entry_lookup_by_id (y.id)
  96.             y_track = self.store.db.entry_get (entry, rhythmdb.PROP_TRACK_NUMBER)
  97.             return cmp(x_track,y_track)
  98.  
  99.         def collate (model, path, iter):
  100.             self.info("Album get_children %r %r %r" %(model, path, iter))
  101.             id = model.get(iter, 0)[0]
  102.             children.append(Track(self.store,id,self.id))
  103.  
  104.         self.tracks_per_album_query.foreach(collate)
  105.  
  106.         children.sort(cmp=track_sort)
  107.  
  108.         if request_count == 0:
  109.             return children[start:]
  110.         else:
  111.             return children[start:request_count]
  112.  
  113.     def get_child_count(self):
  114.         return len(self.get_children())
  115.  
  116.     def get_item(self, parent_id = AUDIO_ALBUM_CONTAINER_ID):
  117.         item = DIDLLite.MusicAlbum(self.id, parent_id, self.title)
  118.         return item
  119.  
  120.     def get_id(self):
  121.         return self.id
  122.  
  123.     def get_name(self):
  124.         return self.title
  125.  
  126.     def get_cover(self):
  127.         return self.cover
  128.  
  129.  
  130. class Artist(BackendItem):
  131.  
  132.     logCategory = 'rb_media_store'
  133.  
  134.     def __init__(self, store, name, id, parent_id):
  135.         self.id = id
  136.         self.name = name
  137.         self.store = store
  138.  
  139.         query = self.store.db.query_new()
  140.         self.store.db.query_append(query,[rhythmdb.QUERY_PROP_EQUALS, rhythmdb.PROP_TYPE, self.store.db.entry_type_get_by_name('song')],
  141.                                       [rhythmdb.QUERY_PROP_EQUALS, rhythmdb.PROP_ARTIST, self.name])
  142.         qm = self.store.db.query_model_new(query)
  143.         self.store.db.do_full_query_async_parsed(qm, query)
  144.  
  145.         self.albums_per_artist_query = self.store.db.property_model_new(rhythmdb.PROP_ALBUM)
  146.         self.albums_per_artist_query.props.query_model = qm
  147.  
  148.     def get_children(self,start=0,request_count=0):
  149.         children = []
  150.  
  151.         def collate (model, path, iter):
  152.             name = model.get(iter, 0)[0]
  153.             priority = model.get(iter, 1)[0]
  154.             self.info("get_children collate %r %r", name, priority)
  155.             if priority is False:
  156.                 try:
  157.                     album = self.store.albums[name]
  158.                     children.append(album)
  159.                 except:
  160.                     self.warning("hmm, a new album %r, that shouldn't happen", name)
  161.  
  162.         self.albums_per_artist_query.foreach(collate)
  163.  
  164.         if request_count == 0:
  165.             return children[start:]
  166.         else:
  167.             return children[start:request_count]
  168.  
  169.     def get_child_count(self):
  170.         return len(self.get_children())
  171.  
  172.     def get_item(self, parent_id = AUDIO_ARTIST_CONTAINER_ID):
  173.         item = DIDLLite.MusicArtist(self.id, parent_id, self.name)
  174.         return item
  175.  
  176.     def get_id(self):
  177.         return self.id
  178.  
  179.     def get_name(self):
  180.         return self.name
  181.  
  182.  
  183. class Track(BackendItem):
  184.  
  185.     logCategory = 'rb_media_store'
  186.  
  187.     def __init__(self, store, id, parent_id):
  188.         self.store = store
  189.         if type(id) == int:
  190.             self.id = id
  191.         else:
  192.             self.id = self.store.db.entry_get (id, rhythmdb.PROP_ENTRY_ID)
  193.         self.parent_id = parent_id
  194.  
  195.     def get_children(self, start=0, request_count=0):
  196.         return []
  197.  
  198.     def get_child_count(self):
  199.         return 0
  200.  
  201.     def get_item(self, parent_id=None):
  202.  
  203.         self.info("Track get_item %r @ %r" %(self.id,self.parent_id))
  204.  
  205.         host = ""
  206.  
  207.         # load common values
  208.         entry = self.store.db.entry_lookup_by_id(self.id)
  209.         # Bitrate is in bytes/second, not kilobits/second
  210.         bitrate = self.store.db.entry_get(entry, rhythmdb.PROP_BITRATE) * 1024 / 8
  211.         # Duration is in HH:MM:SS format
  212.         seconds = self.store.db.entry_get(entry, rhythmdb.PROP_DURATION)
  213.         hours = seconds / 3600
  214.         seconds = seconds - hours * 3600
  215.         minutes = seconds / 60
  216.         seconds = seconds - minutes * 60
  217.         duration = ("%02d:%02d:%02d") % (hours, minutes, seconds)
  218.  
  219.         location = self.get_path(entry)
  220.         mimetype = self.store.db.entry_get(entry, rhythmdb.PROP_MIMETYPE)
  221.         # This isn't a real mime-type
  222.         if mimetype == "application/x-id3":
  223.             mimetype = "audio/mpeg"
  224.         size = self.store.db.entry_get(entry, rhythmdb.PROP_FILE_SIZE)
  225.  
  226.         album = self.store.db.entry_get(entry, rhythmdb.PROP_ALBUM)
  227.         if self.parent_id == None:
  228.             try:
  229.                 self.parent_id = self.store.albums[album].id
  230.             except:
  231.                 pass
  232.  
  233.         # create item
  234.         item = DIDLLite.MusicTrack(self.id + TRACK_COUNT,self.parent_id)
  235.         item.album = album
  236.  
  237.         item.artist = self.store.db.entry_get(entry, rhythmdb.PROP_ARTIST)
  238.         #item.date =
  239.         item.genre = self.store.db.entry_get(entry, rhythmdb.PROP_GENRE)
  240.         item.originalTrackNumber = str(self.store.db.entry_get (entry, rhythmdb.PROP_TRACK_NUMBER))
  241.         item.title = self.store.db.entry_get(entry, rhythmdb.PROP_TITLE) # much nicer if it was entry.title
  242.  
  243.         #cover = self.store.db.entry_request_extra_metadata(entry, "rb:coverArt")
  244.         #self.warning("cover for %r is %r", item.title, cover)
  245.         #item.albumArtURI = ## can we somehow store art in the upnp share??
  246.  
  247.         # add http resource
  248.         res = DIDLLite.Resource(self.get_url(), 'http-get:*:%s:*' % mimetype)
  249.         if size > 0:
  250.             res.size = size
  251.         if duration > 0:
  252.             res.duration = str(duration)
  253.         if bitrate > 0:
  254.             res.bitrate = str(bitrate)
  255.         item.res.append(res)
  256.  
  257.         # add internal resource
  258.         res = DIDLLite.Resource('track-%d' % self.id, 'rhythmbox:%s:%s:*' % (self.store.server.coherence.hostname, mimetype))
  259.         if size > 0:
  260.             res.size = size
  261.         if duration > 0:
  262.             res.duration = str(duration)
  263.         if bitrate > 0:
  264.             res.bitrate = str(bitrate)
  265.         item.res.append(res)
  266.  
  267.         return item
  268.  
  269.     def get_id(self):
  270.         return self.id
  271.  
  272.     def get_name(self):
  273.         entry = self.store.db.entry_lookup_by_id (self.id)
  274.         return self.store.db.entry_get(entry, rhythmdb.PROP_TITLE)
  275.  
  276.     def get_url(self):
  277.         return self.store.urlbase + str(self.id + TRACK_COUNT)
  278.  
  279.     def get_path(self, entry = None):
  280.         if entry is None:
  281.             entry = self.store.db.entry_lookup_by_id (self.id)
  282.         uri = self.store.db.entry_get(entry, rhythmdb.PROP_LOCATION)
  283.         self.warning("Track get_path uri = %r", uri)
  284.         location = None
  285.         if uri.startswith("file://"):
  286.             location = unicode(urllib.unquote(uri[len("file://"):]))
  287.             self.warning("Track get_path location = %r", location)
  288.  
  289.         return location
  290.  
  291. class MediaStore(BackendStore):
  292.  
  293.     logCategory = 'rb_media_store'
  294.     implements = ['MediaServer']
  295.  
  296.     def __init__(self, server, **kwargs):
  297.         self.warning("__init__ MediaStore %r", kwargs)
  298.         self.server = server
  299.         self.db = kwargs['db']
  300.         self.plugin = kwargs['plugin']
  301.  
  302.         self.wmc_mapping.update({'4': lambda : self.get_by_id(AUDIO_ALL_CONTAINER_ID),    # all tracks
  303.                                  '7': lambda : self.get_by_id(AUDIO_ALBUM_CONTAINER_ID),    # all albums
  304.                                  '6': lambda : self.get_by_id(AUDIO_ARTIST_CONTAINER_ID),    # all artists
  305.                                 })
  306.  
  307.         self.update_id = 0
  308.  
  309.         self.next_id = CONTAINER_COUNT
  310.         self.albums = None
  311.         self.artists = None
  312.         self.tracks = None
  313.  
  314.         self.urlbase = kwargs.get('urlbase','')
  315.         if( len(self.urlbase) > 0 and self.urlbase[len(self.urlbase)-1] != '/'):
  316.             self.urlbase += '/'
  317.  
  318.         self.name = "Rhythmbox on %s" % self.server.coherence.hostname
  319.  
  320.         query = self.db.query_new()
  321.         self.info(query)
  322.         self.db.query_append(query, [rhythmdb.QUERY_PROP_EQUALS, rhythmdb.PROP_TYPE, self.db.entry_type_get_by_name('song')])
  323.         qm = self.db.query_model_new(query)
  324.         self.db.do_full_query_async_parsed(qm, query)
  325.  
  326.         self.album_query = self.db.property_model_new(rhythmdb.PROP_ALBUM)
  327.         self.album_query.props.query_model = qm
  328.  
  329.         self.artist_query = self.db.property_model_new(rhythmdb.PROP_ARTIST)
  330.         self.artist_query.props.query_model = qm
  331.  
  332.         self.containers = {}
  333.         self.containers[ROOT_CONTAINER_ID] = \
  334.                 Container( ROOT_CONTAINER_ID,-1, "Rhythmbox on %s" % self.server.coherence.hostname)
  335.  
  336.         self.containers[AUDIO_ALL_CONTAINER_ID] = \
  337.                 Container( AUDIO_ALL_CONTAINER_ID,ROOT_CONTAINER_ID, 'All tracks',
  338.                           children_callback=self.children_tracks)
  339.         self.containers[ROOT_CONTAINER_ID].add_child(self.containers[AUDIO_ALL_CONTAINER_ID])
  340.  
  341.         self.containers[AUDIO_ALBUM_CONTAINER_ID] = \
  342.                 Container( AUDIO_ALBUM_CONTAINER_ID,ROOT_CONTAINER_ID, 'Albums',
  343.                           children_callback=self.children_albums)
  344.         self.containers[ROOT_CONTAINER_ID].add_child(self.containers[AUDIO_ALBUM_CONTAINER_ID])
  345.  
  346.         self.containers[AUDIO_ARTIST_CONTAINER_ID] = \
  347.                 Container( AUDIO_ARTIST_CONTAINER_ID,ROOT_CONTAINER_ID, 'Artists',
  348.                           children_callback=self.children_artists)
  349.         self.containers[ROOT_CONTAINER_ID].add_child(self.containers[AUDIO_ARTIST_CONTAINER_ID])
  350.  
  351.         louie.send('Coherence.UPnP.Backend.init_completed', None, backend=self)
  352.  
  353.     def get_by_id(self,id):
  354.  
  355.         self.info("looking for id %r", id)
  356.         id = id.split('@',1)
  357.         item_id = id[0]
  358.         item_id = int(item_id)
  359.         if item_id < TRACK_COUNT:
  360.             try:
  361.                 item = self.containers[item_id]
  362.             except KeyError:
  363.                 item = None
  364.         else:
  365.             item = Track(self, (item_id - TRACK_COUNT),None)
  366.  
  367.         return item
  368.  
  369.     def get_next_container_id(self):
  370.         ret = self.next_id
  371.         self.next_id += 1
  372.         return ret
  373.  
  374.     def upnp_init(self):
  375.         if self.server:
  376.             self.server.connection_manager_server.set_variable(0, 'SourceProtocolInfo', [
  377.                 'rhythmbox:%s:*:*' % self.server.coherence.hostname,
  378.                 'http-get:*:audio/mpeg:*',
  379.             ])
  380.         self.warning("__init__ MediaStore initialized")
  381.  
  382.  
  383.     def children_tracks(self, parent_id):
  384.         tracks = []
  385.  
  386.         def track_cb (entry):
  387.             if self.db.entry_get (entry, rhythmdb.PROP_HIDDEN):
  388.                 return
  389.             id = self.db.entry_get (entry, rhythmdb.PROP_ENTRY_ID)
  390.             track = Track(self, id, parent_id)
  391.             tracks.append(track)
  392.  
  393.         self.db.entry_foreach_by_type (self.db.entry_type_get_by_name('song'), track_cb)
  394.         return tracks
  395.  
  396.     def children_albums(self,parent_id):
  397.         albums =  {}
  398.  
  399.         self.info('children_albums')
  400.  
  401.         def album_sort(x,y):
  402.             r = cmp(x.title,y.title)
  403.             self.info("sort %r - %r = %r", x.title, y.title, r)
  404.             return r
  405.  
  406.         def collate (model, path, iter):
  407.             name = model.get(iter, 0)[0]
  408.             priority = model.get(iter, 1)[0]
  409.             self.info("children_albums collate %r %r", name, priority)
  410.             if priority is False:
  411.                 id = self.get_next_container_id()
  412.                 album = Album(self, name, id,parent_id)
  413.                 self.containers[id] = album
  414.                 albums[name] = album
  415.  
  416.         if self.albums is None:
  417.             self.album_query.foreach(collate)
  418.             self.albums = albums
  419.  
  420.         albums = self.albums.values() #.sort(cmp=album_sort)
  421.         albums.sort(cmp=album_sort)
  422.         return albums
  423.  
  424.     def children_artists(self,parent_id):
  425.         artists = []
  426.  
  427.         self.info('children_artists')
  428.  
  429.         def collate (model, path, iter):
  430.             name = model.get(iter, 0)[0]
  431.             priority = model.get(iter, 1)[0]
  432.             if priority is False:
  433.                 id = self.get_next_container_id()
  434.                 artist = Artist(self,name, id,parent_id)
  435.                 self.containers[id] = artist
  436.                 artists.append(artist)
  437.  
  438.         if self.artists is None:
  439.             self.artist_query.foreach(collate)
  440.             self.artists = artists
  441.  
  442.         return self.artists
  443.