=== modified file 'src/unity-lens-video' --- src/unity-lens-video 2012-07-04 16:13:49 +0000 +++ src/unity-lens-video 2012-07-31 09:24:36 +0000 @@ -25,12 +25,14 @@ from zeitgeist.client import ZeitgeistClient from zeitgeist import client, datamodel import time +import multiprocessing #pylint: disable=E0611 from gi.repository import ( GLib, GObject, Gio, + GnomeDesktop, Unity, Dee, ) @@ -57,13 +59,22 @@ HOME_FOLDER = GLib.get_home_dir() CACHE = "%s/unity-lens-video" % GLib.get_user_cache_dir() DB = "videos.db" -Q = [] -Q_MAX = 3 try: ZG = ZeitgeistClient() except: raise SystemExit(1) + +def thumb_multi(uri, mtime): + fac = GnomeDesktop.DesktopThumbnailFactory() + # Any video/* will do here, just to get the factory to use totem. + pixbuf = fac.generate_thumbnail(uri, 'video/mpeg') + if pixbuf: + fac.save_thumbnail(pixbuf, uri, mtime) + else: + fac.create_failed_thumbnail(uri, mtime) + + # pylint: disable=R0903 class Daemon: @@ -111,6 +122,10 @@ self._lens.add_local_scope(self._scope) self._lens.export() + self.pool = None + self.pool_set = {} + self.watching_pool = False + def on_filtering_changed(self, *_): """Run another search when a filter change is notified.""" self._scope.queue_search_changed(Unity.SearchType.DEFAULT) @@ -203,7 +218,10 @@ item.append(icon) result_list.append(item) result_list = self.sort_alpha(result_list) - + + if not self.watching_pool: + self.watching_pool = True + GLib.timeout_add_seconds(1, self.cleanup_processes) GLib.idle_add(self.add_results, search_status, model, cat, cancellable, result_list, search) @@ -304,19 +322,15 @@ """This method checks several locations for a video thumbnail. 1) .jpg file in the same folder (not activated for now) - 1) Nautilus thumbnails - 2) Cached thumbnails generated by the scope + 2) Nautilus thumbnails If nothing has been found, it tries to generate a thumbnail with Totem, - stores and uses it. + stores and uses it. (Using GnomeDesktop) If the generation fails or is slow, it fallbacks to the standard video icon. """ icon_path = None g_file = Gio.file_new_for_path(uri) - video_path = g_file.get_path() - thumb_name = video_path.replace(FOLDER, '').replace('/', '_') - icon_check = '%s/thumb_%s.png' % (CACHE, thumb_name) # if not icon_path: # print 'Check for local cover' # local_cover = uri.replace('.%s' % uri.split('.')[-1], '.jpg') @@ -325,7 +339,8 @@ if not icon_path: icon = g_file.query_info( ','.join((Gio.FILE_ATTRIBUTE_THUMBNAIL_PATH, - Gio.FILE_ATTRIBUTE_THUMBNAILING_FAILED)), + Gio.FILE_ATTRIBUTE_THUMBNAILING_FAILED, + Gio.FILE_ATTRIBUTE_TIME_MODIFIED)), Gio.FileQueryInfoFlags.NONE, None) if icon.get_attribute_boolean(Gio.FILE_ATTRIBUTE_THUMBNAILING_FAILED): @@ -333,28 +348,34 @@ else: icon_path = icon.get_attribute_as_string( Gio.FILE_ATTRIBUTE_THUMBNAIL_PATH) - if not icon_path: - if self.is_file(icon_check): - icon_path = icon_check - if not icon_path: - # Check if processes can be removed from the thumbnailing queue - for process in Q: - if not Gio.file_new_for_path( - "/proc/"+str(process)).query_exists(None): - Q.remove(process) - if len(Q) < Q_MAX: - try: - p = GLib.spawn_async(['/usr/bin/totem-video-thumbnailer', - video_path, icon_check]) - Q.append(p[0]) - if self.is_file(icon_check): - icon_path = icon_check - except: - print "Warning : the file may have been removed." - if not icon_path: + + if icon_path is None: + self.thumb(g_file, icon) icon_path = 'video' return icon_path + def thumb(self, g_file, icon): + # Don't rethumbnail if the file is already in the queue to be processed + if g_file.get_uri() not in self.pool_set: + if self.pool is None: + self.pool = multiprocessing.Pool() + mtime = icon.get_modification_time().tv_sec + res = self.pool.apply_async(thumb_multi, (g_file.get_uri(), mtime)) + self.pool_set[g_file.get_uri()] = res + + def cleanup_processes(self): + if self.pool is None: + return False + for _, res in self.pool_set.iteritems(): + if not res.ready(): + return True + self.pool_set.clear() + self.pool.close() + self.pool.join() # Should not block since all processes have finished. + self.pool = None + self.watching_pool = False + return False + def zg_call (self): active = self._scope.props.sources.get_option("local").props.active filtering = self._scope.props.sources.props.filtering