From 29e534174ddd5292228dbdccab7bf9e159634245 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Sim=C3=B3n=20Pena?= Date: Sun, 20 Jun 2010 14:22:09 +0200 Subject: [PATCH] ui: Added support for fetching movie images Initial support for fetching images is added. * AsyncWorker and ImageDownloader have been taken from SeriesFinale. They allow asynchronously fetching the images from the Internet using the url provided from DBus * The UI displays a placeholder image while the right one is being fetched * Images downloaded aren't cached, they are download in /tmp/ * Asynchronous threads aren't canceled if we leave the movie view before the image is fetched. --- ui/maeviesui/maeviesui/gui.py | 35 ++++++++++++++-- ui/maeviesui/util/asyncworker.py | 82 +++++++++++++++++++++++++++++++++++++ ui/maeviesui/util/constants.py | 2 +- ui/maeviesui/util/moviemanager.py | 27 ++++++++++++ ui/maeviesui/util/util.py | 31 ++++++++++++++ 5 files changed, 172 insertions(+), 5 deletions(-) create mode 100644 ui/maeviesui/util/asyncworker.py create mode 100644 ui/maeviesui/util/util.py diff --git a/ui/maeviesui/maeviesui/gui.py b/ui/maeviesui/maeviesui/gui.py index beb191f..ae1393e 100644 --- a/ui/maeviesui/maeviesui/gui.py +++ b/ui/maeviesui/maeviesui/gui.py @@ -19,6 +19,7 @@ ########################################################################### import pygtk +import os pygtk.require('2.0') import gtk import hildon @@ -26,6 +27,8 @@ import pango import gobject from maeviesui.util import constants +from maeviesui.util.asyncworker import AsyncWorker, AsyncItem +from maeviesui.util.util import image_downloader from maeviesui.util.moviemanager import MovieManager class Maevies(hildon.StackableWindow): @@ -222,7 +225,8 @@ class MoviesView(gtk.TreeView): def add_movies(self, movie_list): model = self.get_model() - model.add(movie_list) + if model: + model.add(movie_list) def get_movie_from_path(self, path): model = self.get_model() @@ -259,6 +263,30 @@ class AboutDialog(gtk.Dialog): class MovieWindow(hildon.StackableWindow): + def _create_movie_image(self, movie): + image = gtk.Image() + image.set_from_pixbuf(gtk.IconTheme().load_icon('general_video', + 256, 0)) + banner = hildon.hildon_banner_show_information_with_markup(self, + 'ignored', + 'Fetching movie cover') + banner.set_timeout(constants.TIMEOUT_TIME_MILLIS) + hildon.hildon_gtk_window_set_progress_indicator(self, True) + + async_item = AsyncItem(image_downloader, (movie.get_images()[0].get_url(), '/tmp/' + movie.get_title()), + self._set_downloaded_image, (image,)) + self.async_worker.queue.put(async_item) + self.async_worker.start() + + return image + + def _set_downloaded_image(self, image, target, error): + image_file = os.path.abspath(target) + image.set_from_pixbuf(gtk.gdk.pixbuf_new_from_file_at_size(image_file, + 256, + 256)) + hildon.hildon_gtk_window_set_progress_indicator(self, False) + def _create_contents(self, movie): main_area = hildon.PannableArea() @@ -267,9 +295,7 @@ class MovieWindow(hildon.StackableWindow): upper_content = gtk.HBox(False, 40) upper_content.set_border_width(20) - image = gtk.Image() - image.set_from_pixbuf(gtk.IconTheme().load_icon('mediaplayer_default_album', - 256, 0)) + image = self._create_movie_image(movie) side_content = gtk.VBox(False, 30) @@ -306,6 +332,7 @@ class MovieWindow(hildon.StackableWindow): def __init__(self, movie): super(MovieWindow, self).__init__() + self.async_worker = AsyncWorker() self.set_title('Movie info') self.add(self._create_contents(movie)) self.show_all() diff --git a/ui/maeviesui/util/asyncworker.py b/ui/maeviesui/util/asyncworker.py new file mode 100644 index 0000000..4deb97c --- /dev/null +++ b/ui/maeviesui/util/asyncworker.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- + +########################################################################### +# SeriesFinale +# Copyright (C) 2009 Joaquim Rocha +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +########################################################################### + +from threading import Thread +import Queue +import gobject +import logging +logging.basicConfig(level=logging.DEBUG) + +class AsyncItem(object): + + def __init__(self, target_method, target_method_args, finish_callback=None, finish_callback_args=()): + self.target_method = target_method + self.target_method_args = target_method_args + self.finish_callback = finish_callback + self.finish_callback_args = finish_callback_args + self.canceled = False + + def run(self): + if self.canceled: + return + results = error = None + try: + results = self.target_method(*self.target_method_args) + except Exception, exception: + logging.debug(str(exception)) + error = exception + if self.canceled or not self.finish_callback: + return + self.finish_callback_args += (results,) + self.finish_callback_args += (error,) + gobject.idle_add(self.finish_callback, *self.finish_callback_args) + + def cancel(self): + self.canceled = True + +class AsyncWorker(Thread): + + def __init__(self): + Thread.__init__(self) + self.queue = Queue.Queue(0) + self.stopped = False + self.async_item = None + self.item_number = -1 + + def run(self): + while not self.stopped: + if self.queue.empty(): + self.stop() + break + try: + self.async_item = self.queue.get() + self.item_number += 1 + self.async_item.run() + self.queue.task_done() + self.async_item = None + except Exception, exception: + logging.debug(str(exception)) + self.stop() + + def stop(self): + self.stopped = True + if self.async_item: + self.async_item.cancel() + diff --git a/ui/maeviesui/util/constants.py b/ui/maeviesui/util/constants.py index 988c1c0..d08f6e1 100644 --- a/ui/maeviesui/util/constants.py +++ b/ui/maeviesui/util/constants.py @@ -25,7 +25,7 @@ LOCAL_DATA_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, 'data')) -TIMEOUT_TIME_MILLIS = 500 +TIMEOUT_TIME_MILLIS = 1000 LEFT_ALIGNMENT = 0 CENTER_ALIGNMENT = 0.5 LOREM_IPSUM = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ diff --git a/ui/maeviesui/util/moviemanager.py b/ui/maeviesui/util/moviemanager.py index d5148c9..8cfc617 100644 --- a/ui/maeviesui/util/moviemanager.py +++ b/ui/maeviesui/util/moviemanager.py @@ -71,12 +71,29 @@ class MovieManager: if self.response_received_cb: self.response_received_cb(movies) +class MovieImage: + def __init__(self, image_struct): + self._type, self._url, self._size, self._id = image_struct + + def get_url(self): + return self._url + + def get_id(self): + return self._id + + def get_size(self): + return self._size + + def get_type(self): + return self._type + class MovieProxy: def __init__(self, bus, object_path): self._bus = bus self.interface = self._create_movie_interface(object_path) self.fields = ['Title', 'Release date', 'Rating', 'Popularity'] + self._images = self._retrieve_images() def _create_movie_interface(self, object_path): proxy = self._bus.get_object(TMDB_MOVIE_BUS_NAME, @@ -85,6 +102,13 @@ class MovieProxy: dbus_interface=TMDB_MOVIE_INTERFACE) return interface + def _retrieve_images(self): + images = [] + dbus_images = self.interface.GetImages() + for image in dbus_images: + images.append(MovieImage(image)) + return images + def get_value(self, field): if field == 'Title': return self.get_title() @@ -106,6 +130,9 @@ class MovieProxy: def get_rating(self): return self.interface.GetRating() + def get_images(self): + return self._images + def get_released(self): return self.interface.GetReleased() diff --git a/ui/maeviesui/util/util.py b/ui/maeviesui/util/util.py new file mode 100644 index 0000000..a1cc206 --- /dev/null +++ b/ui/maeviesui/util/util.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- + +########################################################################### +# Maevies +# Copyright (C) 2010 Simón Pena +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +########################################################################### + +import urllib +import os + +def image_downloader(url, save_name): + image = urllib.URLopener() + path, format = os.path.splitext(url) + target = save_name + format + temp_target = target + '.tmp' + image.retrieve(url, temp_target) + os.rename(temp_target, target) + return target -- 1.7.9.5