~ubuntu-branches/ubuntu/oneiric/rhythmbox/oneiric

« back to all changes in this revision

Viewing changes to plugins/artdisplay/artdisplay/LocalCoverArtSearch.py

  • Committer: Bazaar Package Importer
  • Author(s): Rico Tzschichholz
  • Date: 2011-07-29 16:41:38 UTC
  • mto: This revision was merged to the branch mainline in revision 191.
  • Revision ID: james.westby@ubuntu.com-20110729164138-wwicy8nqalm18ck7
Tags: upstream-2.90.1~20110802
ImportĀ upstreamĀ versionĀ 2.90.1~20110802

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- Mode: python; coding: utf-8; tab-width: 8; indent-tabs-mode: t; -*- 
2
 
#
3
 
# Copyright (C) 2006 - Ed Catmur <ed@catmur.co.uk>
4
 
# Copyright (C) 2009 - Jonathan Matthew <jonathan@d14n.org>
5
 
#
6
 
# This program is free software; you can redistribute it and/or modify
7
 
# it under the terms of the GNU General Public License as published by
8
 
# the Free Software Foundation; either version 2, or (at your option)
9
 
# any later version.
10
 
#
11
 
# The Rhythmbox authors hereby grant permission for non-GPL compatible
12
 
# GStreamer plugins to be used and distributed together with GStreamer
13
 
# and Rhythmbox. This permission is above and beyond the permissions granted
14
 
# by the GPL license by which Rhythmbox is covered. If you modify this code
15
 
# you may extend this exception to your version of the code, but you are not
16
 
# obligated to do so. If you do not wish to do so, delete this exception
17
 
# statement from your version.
18
 
19
 
# This program is distributed in the hope that it will be useful,
20
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 
# GNU General Public License for more details.
23
 
#
24
 
# You should have received a copy of the GNU General Public License
25
 
# along with this program; if not, write to the Free Software
26
 
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
27
 
 
28
 
import os
29
 
import gobject
30
 
import glib
31
 
import gi
32
 
 
33
 
from gi.repository import RB
34
 
from gi.repository import Gio
35
 
 
36
 
IMAGE_NAMES = ["cover", "album", "albumart", ".folder", "folder"]
37
 
ITEMS_PER_NOTIFICATION = 10
38
 
ART_SAVE_NAME = 'Cover.jpg'
39
 
ART_SAVE_FORMAT = 'jpeg'
40
 
ART_SAVE_SETTINGS = {"quality": "100"}
41
 
 
42
 
IGNORED_SCHEMES = ('http', 'cdda', 'daap', 'mms')
43
 
 
44
 
def file_root (f_name):
45
 
        return os.path.splitext (f_name)[0].lower ()
46
 
 
47
 
def shared_prefix_length (a, b):
48
 
        l = 0
49
 
        while a[l] == b[l]:
50
 
                l = l+1
51
 
        return l
52
 
 
53
 
def get_search_props(db, entry):
54
 
        artist = entry.get_string(RB.RhythmDBPropType.ALBUM_ARTIST)
55
 
        if artist == "":
56
 
                artist = entry.get_string(RB.RhythmDBPropType.ARTIST)
57
 
        album = entry.get_string(RB.RhythmDBPropType.ALBUM)
58
 
        return (artist, album)
59
 
 
60
 
 
61
 
class LocalCoverArtSearch:
62
 
        def __init__ (self):
63
 
                pass
64
 
 
65
 
        def _enum_dir_cb(self, fileenum, result, (results, on_search_completed_cb, entry, args)):
66
 
                try:
67
 
                        files = fileenum.next_files_finish(result)
68
 
                        if files is None or len(files) == 0:
69
 
                                print "okay, done; got %d files" % len(results)
70
 
                                on_search_completed_cb(self, entry, results, *args)
71
 
                                return
72
 
 
73
 
                        for f in files:
74
 
                                ct = f.get_attribute_string("standard::content-type")
75
 
                                # assume readable unless told otherwise
76
 
                                readable = True
77
 
                                if f.has_attribute("access::can-read"):
78
 
                                        readable = f.get_attribute_boolean("access::can-read")
79
 
                                if ct is not None and ct.startswith("image/") and readable:
80
 
                                        results.append(f.get_name())
81
 
 
82
 
                        fileenum.next_files_async(ITEMS_PER_NOTIFICATION, glib.PRIORITY_DEFAULT, None, self._enum_dir_cb, (results, on_search_completed_cb, entry, args))
83
 
                except Exception, e:
84
 
                        print "okay, probably done: %s" % e
85
 
                        import sys
86
 
                        sys.excepthook(*sys.exc_info())
87
 
                        on_search_completed_cb(self, entry, results, *args)
88
 
 
89
 
 
90
 
        def _enum_children_cb(self, parent, result, (on_search_completed_cb, entry, args)):
91
 
                try:
92
 
                        enumfiles = parent.enumerate_children_finish(result)
93
 
                        enumfiles.next_files_async(ITEMS_PER_NOTIFICATION, glib.PRIORITY_DEFAULT, None, self._enum_dir_cb, ([], on_search_completed_cb, entry, args))
94
 
                except Exception, e:
95
 
                        print "okay, probably done: %s" % e
96
 
                        import sys
97
 
                        sys.excepthook(*sys.exc_info())
98
 
                        on_search_completed_cb(self, entry, [], *args)
99
 
 
100
 
 
101
 
        def search (self, db, entry, is_playing, on_search_completed_cb, *args):
102
 
 
103
 
                self.file = Gio.file_new_for_uri(entry.get_playback_uri())
104
 
                if self.file.get_uri_scheme() in IGNORED_SCHEMES:
105
 
                        print 'not searching for local art for %s' % (self.file.get_uri())
106
 
                        on_search_completed_cb (self, entry, [], *args)
107
 
                        return
108
 
 
109
 
                self.artist, self.album = get_search_props(db, entry)
110
 
 
111
 
                print 'searching for local art for %s' % (self.file.get_uri())
112
 
                parent = self.file.get_parent()
113
 
                enumfiles = parent.enumerate_children_async("standard::content-type,access::can-read,standard::name", 0, 0, None, self._enum_children_cb, (on_search_completed_cb, entry, args))
114
 
 
115
 
        def search_next (self):
116
 
                return False
117
 
 
118
 
        def get_result_meta (self, results):
119
 
                return (None, None)
120
 
 
121
 
        def get_result_pixbuf (self, results):
122
 
                return None
123
 
 
124
 
        def get_best_match_urls (self, results):
125
 
                parent = self.file.get_parent()
126
 
 
127
 
                # Compare lower case, without file extension
128
 
                for name in [file_root (self.file.get_basename())] + IMAGE_NAMES:
129
 
                        for f_name in results:
130
 
                                if file_root (f_name) == name:
131
 
                                        yield parent.resolve_relative_path(f_name).get_uri()
132
 
 
133
 
                # look for file names containing the artist and album (case-insensitive)
134
 
                # (mostly for jamendo downloads)
135
 
                artist = self.artist.lower()
136
 
                album = self.album.lower()
137
 
                for f_name in results:
138
 
                        f_root = file_root (f_name).lower()
139
 
                        if f_root.find (artist) != -1 and f_root.find (album) != -1:
140
 
                                yield parent.resolve_relative_path(f_name).get_uri()
141
 
 
142
 
                # if that didn't work, look for the longest shared prefix
143
 
                # only accept matches longer than 2 to avoid weird false positives
144
 
                match = (2, None)
145
 
                for f_name in results:
146
 
                        pl = shared_prefix_length(f_name, self.file.get_basename())
147
 
                        if pl > match[0]:
148
 
                                match = (pl, f_name)
149
 
 
150
 
                if match[1] is not None:
151
 
                        yield parent.resolve_relative_path(match[1]).get_uri()
152
 
 
153
 
 
154
 
 
155
 
        def _pixbuf_save (self, pixbuf, file):
156
 
                def pixbuf_cb(buf, stream):
157
 
                        # can't be bothered doing this asynchronously..
158
 
                        stream.write(buf)
159
 
 
160
 
                def replace_cb(file, result, pixbuf):
161
 
                        try:
162
 
                                stream = file.replace_finish(result)
163
 
                                pixbuf.save_to_callback(pixbuf_cb, ART_SAVE_FORMAT, ART_SAVE_SETTINGS, user_data=stream)
164
 
                                stream.close()
165
 
                        except Exception,e :
166
 
                                print "error creating %s: %s" % (file.get_uri(), e)
167
 
 
168
 
                file.replace_async(replace_cb, user_data=pixbuf())
169
 
 
170
 
        def _save_dir_cb (self, enum, result, (db, entry, parent, pixbuf)):
171
 
                artist, album = get_search_props(db, entry)
172
 
 
173
 
                try:
174
 
                        files = enum.next_files_finish(result)
175
 
                        if len(files) == 0:
176
 
                                art_file = dir.resolve_relative_path(ART_SAVE_NAME)
177
 
                                print "saving local art to \"%s\"" % art_file.get_uri()
178
 
                                self._pixbuf_save (pixbuf, art_file)
179
 
                                enum.close()
180
 
                                return
181
 
 
182
 
                        for f in files:
183
 
                                ct = f.get_attribute_string("standard::fast-content-type")
184
 
                                if ct.startswith("image/") or ct.startswith("x-directory/"):
185
 
                                        continue
186
 
 
187
 
                                uri = parent.resolve_relative_path(f.get_name()).get_uri()
188
 
                                u_entry = db.entry_lookup_by_location (uri)
189
 
                                if u_entry:
190
 
                                        u_artist, u_album = get_search_props(db, u_entry)
191
 
                                        if album != u_album:
192
 
                                                print "Not saving local art; encountered media with different album (%s, %s, %s)" % (uri, u_artist, u_album)
193
 
                                                enum.close()
194
 
                                                return
195
 
                                        continue
196
 
                                print "Not saving local art; encountered unknown file (%s)" % uri
197
 
                                enum.close()
198
 
                                return
199
 
 
200
 
                        enum.next_files_async(ITEMS_PER_NOTIFICATION, glib.PRIORITY_DEFAULT, None, self._save_dir_cb, (db, entry, parent, pixbuf))
201
 
                except Exception, e:
202
 
                        print "Error reading \"%s\": %s" % (dir, e)
203
 
 
204
 
        def _save_enum_children_cb (self, parent, result, (db, entry, pixbuf)):
205
 
                try:
206
 
                        enum = parent.enumerate_children_finish(result)
207
 
                        enum.next_files_async(ITEMS_PER_NOTIFICATION, glib.PRIORITY_DEFAULT, None, self._save_dir_cb, (db, entry, parent, pixbuf))
208
 
                except Exception, e:
209
 
                        return
210
 
 
211
 
        def save_pixbuf (self, db, entry, pixbuf):
212
 
                uri = entry.get_playback_uri()
213
 
                if uri is None or uri == '':
214
 
                        return
215
 
 
216
 
                f = Gio.file_new_for_uri(uri)
217
 
                if f.get_uri_scheme() in IGNORED_SCHEMES:
218
 
                        print "not saving local art for %s" % uri
219
 
                        return
220
 
 
221
 
                print 'checking whether to save local art for %s' % uri
222
 
                parent = f.get_parent()
223
 
                try:
224
 
                        parent.enumerate_children_async("standard::content-type,access::can-read,standard::name", Gio.FileQueryInfoFlags.NONE, glib.PRIORITY_DEFAULT, None, self._save_enum_children_cb, (db, entry, pixbuf))
225
 
                except Exception, e:
226
 
                        print "unable to scan directory %s: %s" % (parent.get_uri(), e)