Sunday, April 26, 2009

A little tracking for the people waiting for Fremantle

I haven't been blogging much about Fremantle yet. But, yesterday there was a question on the maemo developers mailinglist about what files are indexed to the trackers metadata database, so I though to clear out that issue and also to tell a bit about how your app can use tracker.

So, to answer that question first: Tracker tracks the user home and any mounted media that is attached to the device. For the memory cards, it retains a stack of 3 cards in it's database, so, you can change card A to card B, and back to card A and tracker won't need to reindex the content of the cards. And yes, you can be then swithing to B, to A, to B and you won't lose any data. The amount of cards to support is a configuration option, but by default it's set to 3. So, internal card=1, external card1=2 and then you have the one more as exteral card2. For a third external card, the device will need to flush the oldest seen card away from it's indexes.

There was some concern also as to whether applications can put sound effects and pixmaps into the cards and to make sure they won't be indexed. Well, to this, we have two solutions:
1. put the files to a folder that is hidden (so, it has a "." in the beginning of the folder name) - tracker won't index any hidden folders by default
2. Add the folders to trackers blacklist file

I recommend the solution 1 for multiple reasons.
1. it's simple.
2. user doesn't have any reason to see application data anyway, so this way it'll be also hidden in the file manger. Just make sure your app will flush the data on uninstall of your app.

Ok, then a bit on how you can use Tracker.
As you probably have read, I've been on paternity leave, from which, I've taken a bit of time to integrate ukmp to the new Fremantle stack.

So, first thing I did was, I replaced my own indexing code with code to load all music metadata from tracker database. Loading of this data on startup takes almost no time and tracker also does sorting of the data really easily for me. Not that sorting would actually be any issue in python, nice anyway.

On startup, I hear you saying? Why not on demand? Sure, that would be an option, just happens that how ukmp was built, it's easier for me to get all the content on startup and not on demand. Both are fine. I could write a small comment on how to do stuff on demand as well, but let's start with this.

We'll need to use two interfaces: search and metadata

Corresponding dbus introspection files are: search and metadata

You can find the whole dbus introspection from here


Let's start with defining the needed proxy objects:

import dbus
bus = dbus.SessionBus()
searchproxy = bus.get_object('org.freedesktop.Tracker','/org/freedesktop/Tracker/Search')
metadataproxy=bus.get_object('org.freedesktop.Tracker','/org/freedesktop/Tracker/Metadata')

#Ok, let's then get all music files and for those, the artist, album, title and track# sorted by artist

metadata=searchproxy.Query(-1, "Music", ["Audio:Artist","Audio:Album","Audio:Title","Audio:TrackNo"],"", dbus.Array([], signature='s') ,"",False,["Audio:Artist"], False,0, 40000)

#Now that we have the data, we'll just add it to the internal structures

for songItem in metadata:
fileUrl=songItem[0]
artist=songItem[2]
album=songItem[3]
song=songItem[4]
track=songItem[5]
self.appendSong(track, album, artist, song, fileUrl)



Nice and simple. Now we have the data. This saved me about 300 lines of code, plus multiple library dependencies and tons of headache.

Of course, with my approach of loading everything on startup, I need to update the data when the data changes, but for this, tracker provides a really nice signal that looks like this:


signal sender=:1.15 -> dest=(null destination) serial=403 path=/org/freedesktop/Tracker; interface=org.freedesktop.Tracker; member=ServiceStatisticsUpdated
array [
array [
string "Files"
string "2320"
]
array [
string "Music"
string "543"
]
]

I won't show the implementation on how to keep the data update on this blog post, I'll save it for a future blog post. I'll instead now tell how to keep Tracker up-to-date on usage of the files. All media players on Fremantle should either use MAFW or do the following so that we would all be happy campers no matter which media player user uses.

When you are playing a music file, please notify tracker of the play event. I do so at the end of a track, but your heuristic may vary. Firts we get the current playcount, then we add 1 to it, then we set the new playcount and the curren playtime. We set the time in GMT in UTC format, which is rather easy to get in python.

import time
currentCount=metadataproxy.Get("Music",currentPlayFile, ["Audio:PlayCount"])
newcount=1
if len(currentCount[0])>0: newcount=int(currentCount[0])+1
currentTimeUTC=time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
metadataproxy.Set("Music",currentPlayFile, ["Audio:PlayCount","Audio:LastPlay"],[str(newcount),currentTimeUTC])


Ah, now we have the data in tracker, we are able to update the playcounts and playtimes so that all music players can benefit from the data. In my next blog post, I'll tell how the album art can be handled in common way across the platform. I'll tell you how you should do it and I'll tell you how I do it now (which might not be the case I will do when the device has been out for a while).

Then I'll make a blog about how to make dynamic lists, e.g. to list most popular tracks, most recently added and the most recently played tracks.

Then, to top this, I'll let you know how the signaling can be used to keep your internal data structures up-to-date, in case you are not using on-demand loading of the data.

edit: fixed typo as noticed by timeless

2 comments:

  1. Depending on how deeply trackerd is integrated with fremantle and how rarely application media files should be exposed to the user, wouldn't it make sense to *whitelist* ? This would also make the transition easier as nothing would have to be modified in already existing applications.

    For example, by default trackerd could be hooked up with the application manager, or apt, or simply check on indexing if the given media file is part of a .deb and if yes, it would be blacklisted. If any app DOES think the media files should be exposed, it can whitelist/remove itself from the blacklist in a postinst or similar.

    ReplyDelete
  2. There is a whitelist as well as a blacklist. The whitelist just happens to include the mounted cards by default. This is the only way for the random people to have the metadata extraction to 'just work'. Anyway, it's not like we have the root in whitelist. Just the media cards. Anyway, if app decides to put data to the media cards, they really can also add the blacklist entry, as they are already doing platform dependent stuff, or they can (and should), just name the data folder with ".foldername", as user really should not be poking to the application data anyway.

    ReplyDelete