A lot of the unity-scopes do not work in ubuntu (for example the unity-scope-gmusicbrowser), thus I've tried to "correct" them in order to get results in the dash.

Ok, I succeeded in doing this (for the file: /usr/share/unity-scopes/gmusicbrowser/unity_gmusicbrowser_deamon.py: the "corrected" code: https://gist.github.com/wa4557/d6cc4ec5354bbb95042b (is it ok to post it here, even if the main part is not from me?)). This works flawlessly, and results from gmusicbrowser are now visible in the music-dash, like I want them to show up.

But I have still one small problem: How can i implement the available filters in the scopes? I think the relevant lines in the code are (line 372 ff):

def do_get_filters(self):
    Adds filters
    fs = Unity.FilterSet.new()
    #if FILTERS:
    return fs

Unfortunately however there's everything commented out, and there is no serious documentation or something similar

Filters in the scope would let me to filter Music, so for example to select only Rock music etc.; I think the screenshot explains what I mean (it's german).

enter image description here

As you can see, there are no results even though I have a lot of music from the 00s in my collection...

EDIT: I have found a scope with a similar loader (unity-gdrive-scope) : https://gist.github.com/wa4557/e3a9cdef5806dc3c13c9, where the filters are added. Frankly I don't understand how this works. But there is definitely something in the do_get_filters function...


I'm trying to do something similar for the clementine scope and I think I made some progress. I modified the do_get_filters in the following way

    def do_get_filters(self):
        Adds filters
        fs = Unity.FilterSet.new()
        if FILTERS:
            fil = Unity.MultiRangeFilter.new(FILTERS[0]['id'], FILTERS[0]['name'],

            fil = Unity.RadioOptionFilter.new(FILTERS[1]['id'], FILTERS[1]['name'],
        return fs

after defining FILTERS as

f1 = {'id': 'decade',
      'name': _('Decade'),
      'icon': '',
      'collapsed': True}

f2 = {'id': 'genre',
      'name': _('Genre'),
      'icon': '',
      'collapsed': True}

FILTERS = [f1, f2]

At this point, you can do something like this in the do_run method of the MySearch class

    def do_run(self):
        Adds results to the model
            decade, genre = self.search_context.filter_state.get_filters()

            if decade.get_first_active():
                start_year = int( decade.get_first_active().get_property('id') )
                start_year = 0
            if decade.get_last_active():
                if decade.get_last_active().get_property('id') == '0':
                    end_year = 1950 + 9
                    end_year = int( decade.get_last_active().get_property('id') ) + 9
                end_year = 3000

and after that

            result_set = self.search_context.result_set
            for i in search(self.search_context.search_query,
                if not (start_year < i['year'].get_int32() < end_year) :
                if not 'uri' in i or not i['uri'] or i['uri'] == '':
                if not 'icon' in i or not i['icon'] or i['icon'] == '':
                    i['icon'] = DEFAULT_RESULT_ICON
                if not 'mimetype' in i or not i['mimetype'] or i['mimetype'] == '':
                    i['mimetype'] = DEFAULT_RESULT_MIMETYPE
                if not 'result_type' in i or not i['result_type'] or i['result_type'] == '':
                    i['result_type'] = DEFAULT_RESULT_TYPE
                if not 'category' in i or not i['category'] or i['category'] == '':
                    i['category'] = 0
                if not 'title' in i or not i['title']:
                    i['title'] = ''
                if not 'comment' in i or not i['comment']:
                    i['comment'] = ''
                if not 'dnd_uri' in i or not i['dnd_uri'] or i['dnd_uri'] == '':
                    i['dnd_uri'] = i['uri']
                i['provider_credits'] = GLib.Variant('s', PROVIDER_CREDITS)
        except Exception as error:
The genre part should be similar, I have to implement it yet though.

  • yeah, finally! cool thanks. I will try to implement this and report progress. Unfortunately the scopes are quite buggy :( – wa4557 Jan 27 '14 at 20:58
  • 1
    I'm glad I helped you (and myself :-) ). I also realized that the filtering on the results that I suggested to implement in "do_run" can be implemented in the "search" method since its "filters" argument is nothing but the "self.search_context.filter_state" found in "do_run". On a different subject, in the clementine scope I fixed the album art extraction, it called the mutagen module which does not exist for python3. Did/Do you have the same problem? – Antonio Jan 27 '14 at 22:24
  • Yeah, exactly. This should should speed up things significantly, since this means, that not the whole collection is scanned. Mutagen sounds interesting. Gmusicbrowser is handling albumart differently though... – wa4557 Jan 28 '14 at 8:06
  • I don't know if you actually get a speed up, I think it's just a matter of doing the filtering at one point or another. Maybe an actual speed up can be obtained by "embedding" the filters in the SQL query string, dunno if it's doable – Antonio Jan 28 '14 at 8:38
  • Ok so it seems, that here's the difference between our scopes. Gmusicbrowser saves the database as plain Text, thus there's most definitely a speedup in my case... – wa4557 Jan 28 '14 at 9:43

Even if we're getting off topic respect to the original question, in the clementine scope there's a query string made like

SEARCH_SQL = '''SELECT title, filename, artist, album, albumartist, art_automatic, year, genre, art_manual, track, length
            FROM songs
            WHERE album LIKE '%%%s%%' OR artist LIKE '%%%s%%' OR title LIKE '%%%s%%'
            ORDER BY disc, track'''

(I added the disc ordering respect to the original string) and this is called with something like

tracks = get_music_from_clementine(search, search, search))

where search is the search term. Now I defined a new search string

MY_SEARCH_SQL = '''SELECT title, filename, artist, album, albumartist, art_automatic, year, genre, art_manual, track, length
               FROM songs
               WHERE (year  >= %s AND year <= %s AND (album LIKE '%%%s%%' OR artist LIKE '%%%s%%' OR title LIKE '%%%s%%') )
               ORDER BY disc, track'''

which I call with

    tracks = get_music_from_clementine(MY_SEARCH_SQL % (str(start_year), str(end_year), search, search, search))

With this search string I only get the results I'm interested in directly from the database query and the scope is definitely more snappy, dunno if something similar can be done for the genre case since in that case the number of filters is variable (you can select more than one genre at the time and not just a range like for the "decade" filter)

  • well you could do something like: """WHERE (year >= %s AND year <= %s AND """ +s*number_of_genres + """ AND (album LIKE '%%%s%%' OR artist LIKE '%%%s%%' OR title LIKE '%%%s%%') )"""with s=if number_of_genre == 1: 'genre=%s' else: 'genre=%s OR '*number_of_genres[:-3]. Synthax is definitely not correct, but the idea behind it should work. A few % are missing as well. When you're finished, you should propose merging on launchpad – wa4557 Jan 28 '14 at 12:13
  • I found a way to do it, I create the part relative to the genre filter and then pass it as a single string to be replaced in MY_SEARCH_SQL. – Antonio Jan 28 '14 at 23:20
  • Can you show make your loader available anywhere? I'm just curious what you did... – wa4557 Jan 31 '14 at 9:37
  • You can find it here code.launchpad.net/~antoniomariano/unity-scope-clementine/… but I've pushed only a part of it. See lines 170-177 in src/unity_clementine_daemon.py for the genre string creation, this string then is passed as the the 3rd argument to a database query such as MY_SEARCH_SQL = '''SELECT title, filename, artist, album, albumartist, art_automatic, year, genre, art_manual, track, length FROM songs WHERE (year >= %s AND year <= %s AND %s (album LIKE '%%%s%%' OR artist LIKE '%%%s%%' OR title LIKE '%%%s%%') ) ORDER BY disc, track''' – Antonio Jan 31 '14 at 10:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.