totem r5783 - in branches/gnome-2-24: . src/plugins/youtube
- From: pwithnall svn gnome org
- To: svn-commits-list gnome org
- Subject: totem r5783 - in branches/gnome-2-24: . src/plugins/youtube
- Date: Sat, 25 Oct 2008 14:02:09 +0000 (UTC)
Author: pwithnall
Date: Sat Oct 25 14:02:09 2008
New Revision: 5783
URL: http://svn.gnome.org/viewvc/totem?rev=5783&view=rev
Log:
2008-10-25 Philip Withnall <philip tecnocode co uk>
* src/plugins/youtube/youtube.py: Fix YouTube plugin behaviour
when
attempting to perform multiple searches simultaneously, and fix
some
threading problems where GTK+ functions were being called from a
spawned thread. (Closes: #557470)
Modified:
branches/gnome-2-24/ChangeLog
branches/gnome-2-24/src/plugins/youtube/youtube.py
Modified: branches/gnome-2-24/src/plugins/youtube/youtube.py
==============================================================================
--- branches/gnome-2-24/src/plugins/youtube/youtube.py (original)
+++ branches/gnome-2-24/src/plugins/youtube/youtube.py Sat Oct 25 14:02:09 2008
@@ -9,6 +9,7 @@
import time
import re
import os
+import random
class DownloadThread (threading.Thread):
def __init__ (self, youtube, url, treeview_name):
@@ -16,7 +17,7 @@
self.url = url
self.treeview_name = treeview_name
self._done = False
- self._lock = threading.Lock()
+ self._lock = threading.Lock ()
threading.Thread.__init__ (self)
def run (self):
@@ -26,7 +27,7 @@
"""Probably a 503 service unavailable. Unfortunately we can't give an error message, as we're not in the GUI thread"""
"""Just let the lock go and return"""
res = None
- gobject.idle_add(self.publish_results, res)
+ gobject.idle_add (self.publish_results, res)
def publish_results(self, res):
self._lock.acquire (True)
@@ -36,24 +37,24 @@
return False
@property
- def done(self):
+ def done (self):
""" Thread-safe property to know whether the query is done or not """
- self._lock.acquire(True)
+ self._lock.acquire (True)
res = self._done
- self._lock.release()
+ self._lock.release ()
return res
-class CallbackThread(threading.Thread):
- def __init__(self, callback, *args, **kwargs):
+class CallbackThread (threading.Thread):
+ def __init__ (self, callback, *args, **kwargs):
self.callback = callback
self.args = args
self.kwargs = kwargs
- threading.Thread.__init__(self)
+ threading.Thread.__init__ (self)
def run (self):
- res = self.callback(*self.args, **self.kwargs)
+ res = self.callback (*self.args, **self.kwargs)
while res == True:
- res = self.callback(*self.args, **self.kwargs)
+ res = self.callback (*self.args, **self.kwargs)
class YouTube (totem.Plugin):
def __init__ (self):
@@ -61,6 +62,10 @@
self.debug = False
self.gstreamer_plugins_present = True
+ """Search counters (per search type)"""
+ self.in_search = {}
+ self.search_token = {} # Used as an ID for a search thread
+
self.max_results = 20
self.button_down = False
@@ -77,6 +82,7 @@
self.vadjust = {}
self.liststore = {}
self.treeview = {}
+
def activate (self, totem_object):
"""Check for the availability of the flvdemux and souphttpsrc GStreamer plugins"""
bvw_name = totem_object.get_video_widget_backend_name ()
@@ -122,12 +128,15 @@
"""Set up the service"""
self.service = gdata.service.GDataService (account_type = "HOSTED_OR_GOOGLE", server = "gdata.youtube.com")
+
def deactivate (self, totem):
totem.remove_sidebar_page ("youtube")
+
def setup_treeview (self, treeview_name):
self.start_index[treeview_name] = 1
self.results[treeview_name] = 0
self.entry[treeview_name] = None
+ self.in_search[treeview_name] = False
"""This is done here rather than in the UI file, because UI files parsed in C and GObjects created in Python apparently don't mix."""
renderer = totem.CellRendererVideo (use_placeholder = True)
@@ -163,10 +172,14 @@
self.liststore[treeview_name] = self.builder.get_object ("yt_liststore_" + treeview_name)
self.treeview[treeview_name] = treeview
treeview.set_model (self.liststore[treeview_name])
+
def on_notebook_page_changed (self, notebook, notebook_page, page_num):
self.current_treeview_name = self.notebook_pages[page_num]
+
def on_row_activated (self, treeview, path, column):
- print "Activating row"
+ if self.debug:
+ print "Activating row"
+
model, rows = treeview.get_selection ().get_selected_rows ()
iter = model.get_iter (rows[0])
youtube_id = model.get_value (iter, 3)
@@ -175,9 +188,12 @@
self.youtube_id = youtube_id
self.start_index["related"] = 1
self.results["related"] = 0
- self.progress_bar.set_text (_("Fetching related videos..."))
+ if self.in_search == False or self.current_treeview_name == "related":
+ self.progress_bar.set_text (_("Fetching related videos..."))
self.get_results ("/feeds/api/videos/" + urllib.quote (youtube_id) + "/related?max-results=" + str (self.max_results), "related")
- print "Done Activating row"
+
+ if self.debug:
+ print "Done activating row"
def get_fmt_string (self):
if self.gconf_client.get_int ("/apps/totem/connection_speed") >= 10:
@@ -220,6 +236,7 @@
return False
return True
+
def on_open_in_web_browser_activated (self, action):
model, rows = self.treeview[self.current_treeview_name].get_selection ().get_selected_rows ()
iter = model.get_iter (rows[0])
@@ -227,46 +244,59 @@
"""Open the video in the browser"""
os.spawnlp (os.P_NOWAIT, "xdg-open", "xdg-open", "http://www.youtube.com/watch?v=" + urllib.quote (youtube_id) + self.get_fmt_string ())
+
def on_button_press_event (self, widget, event):
self.button_down = True
+
def on_button_release_event (self, widget, event):
self.button_down = False
self.on_value_changed (self.vadjust[self.current_treeview_name])
+
def on_value_changed (self, adjustment):
"""Load more results when we get near the bottom of the treeview"""
if not self.button_down and (adjustment.get_value () + adjustment.page_size) / adjustment.upper > 0.8 and self.results[self.current_treeview_name] >= self.max_results:
- self.results[self.current_treeview_name] = 0
- self.progress_bar.set_text (_("Fetching more videos..."))
if self.current_treeview_name == "search":
- self.get_results ("/feeds/api/videos?vq=" + urllib.quote_plus (self.search_terms) + "&max-results=" + str (self.max_results) + "&orderby=relevance&start-index=" + str (self.start_index["search"]), "search", False)
if self.debug:
print "Getting more results for search \"" + self.search_terms + "\" from offset " + str (self.start_index["search"])
+ self.get_results ("/feeds/api/videos?vq=" + urllib.quote_plus (self.search_terms) + "&max-results=" + str (self.max_results) + "&orderby=relevance&start-index=" + str (self.start_index["search"]), "search", False)
elif self.current_treeview_name == "related":
- self.get_results ("/feeds/api/videos/" + urllib.quote_plus (self.youtube_id) + "/related?max-results=" + str (self.max_results) + "&start-index=" + str (self.start_index["related"]), "related", False)
if self.debug:
print "Getting more related videos for video \"" + self.youtube_id + "\" from offset " + str (self.start_index["related"])
+ self.get_results ("/feeds/api/videos/" + urllib.quote_plus (self.youtube_id) + "/related?max-results=" + str (self.max_results) + "&start-index=" + str (self.start_index["related"]), "related", False)
+
def convert_url_to_id (self, url):
"""Find the last clause in the URL; after the last /"""
return url.split ("/").pop ()
- def populate_list_from_results (self, treeview_name, thread):
+ def populate_list_from_results (self, search_token, treeview_name, thread):
+ """Check to see if this search has been cancelled"""
+ if search_token != self.search_token[treeview_name]:
+ return False
+
"""Check and acquire the lock"""
if not thread.done:
- self.progress_bar.pulse ()
+ if self.current_treeview_name == treeview_name:
+ self.progress_bar.pulse ()
return True
- CallbackThread(self.process_next_thumbnail, treeview_name).start()
+ CallbackThread (self.process_next_thumbnail, search_token, treeview_name).start ()
+ return False
+
+ def process_next_thumbnail (self, search_token, treeview_name):
+ """Note that all the calls to gobject.idle_add are so that the respective
+ UI function calls are made in the main thread, since process_next_thumbnail
+ is run in the CallbackThread thread."""
+
+ """Check to see if this search has been cancelled"""
+ if search_token != self.search_token[treeview_name]:
+ return False
- def process_next_thumbnail(self, treeview_name):
"""Return if there are no results (or we've finished)"""
if self.entry[treeview_name] == None or len (self.entry[treeview_name]) == 0:
- """Revert the cursor"""
- window = self.vbox.window
- window.set_cursor (None)
- self.progress_bar.set_fraction (0.0)
- self.progress_bar.set_text ("")
+ gobject.idle_add (self._clear_ui, treeview_name)
self.entry[treeview_name] = None
+ self.in_search[treeview_name] = False
return False
@@ -276,9 +306,6 @@
self.start_index[treeview_name] += 1
youtube_id = self.convert_url_to_id (entry.id.text)
- """Update the progress bar"""
- self.progress_bar.set_fraction (float (self.results[treeview_name]) / float (self.max_results))
-
"""Find the content tag"""
for _element in entry.extension_elements:
if _element.tag =="group":
@@ -311,14 +338,31 @@
if t_param != "":
mrl = "http://www.youtube.com/get_video?video_id=" + urllib.quote (youtube_id) + "&t=" + urllib.quote (t_param) + self.get_fmt_string ()
- gobject.idle_add (self._append_to_liststore, treeview_name,
- pixbuf, entry.title.text, mrl, youtube_id)
+ gobject.idle_add (self._append_to_liststore, treeview_name, pixbuf, entry.title.text, mrl, youtube_id, search_token)
return True
- def _append_to_liststore(self, treeview_name, pixbuf, title, mrl, id):
- self.liststore[treeview_name].append([pixbuf, title, mrl, id])
+ def _clear_ui (self, treeview_name):
+ """Revert the cursor"""
+ window = self.vbox.window
+ window.set_cursor (None)
+ if self.in_search == True or self.current_treeview_name == treeview_name:
+ """Only blank the progress bar if we're the only search taking place"""
+ self.progress_bar.set_fraction (0.0)
+ self.progress_bar.set_text ("")
+
+ return False
+
+ def _append_to_liststore (self, treeview_name, pixbuf, title, mrl, id, search_token):
+ """Check to see if this search has been cancelled"""
+ if search_token != self.search_token[treeview_name]:
+ return False
+
+ if self.in_search == True or self.current_treeview_name == treeview_name:
+ self.progress_bar.set_fraction (float (self.results[treeview_name]) / float (self.max_results))
+ self.liststore[treeview_name].append ([pixbuf, title, mrl, id])
+ return False
def on_search_button_clicked (self, button):
search_terms = self.search_entry.get_text ()
@@ -334,23 +378,40 @@
self.results["search"] = 0
self.progress_bar.set_text (_("Fetching search results..."))
self.get_results ("/feeds/api/videos?vq=" + urllib.quote_plus (search_terms) + "&orderby=relevance&max-results=" + str (self.max_results), "search")
+
def on_search_entry_activated (self, entry):
self.search_button.clicked ()
def get_results (self, url, treeview_name, clear = True):
+ if self.in_search[treeview_name] == True and clear == False:
+ """If we're trying to fetch more results while another search is happening, just cancel"""
+ if self.debug:
+ print "Cancelling getting more results due to existing incomplete search."
+ self.in_search[treeview_name] = False
+ return
+ elif clear == False:
+ self.results[self.current_treeview_name] = 0
+ self.progress_bar.set_text (_("Fetching more videos..."))
+
+ """If we're trying to do another full search while one's already happening, just continue as
+ normal. The populate_list_from_results function will notice, and cancel the old search."""
+
if clear:
self.liststore[treeview_name].clear ()
if self.debug:
print "Getting results from URL \"" + url + "\""
+ self.in_search[treeview_name] = True
+ self.search_token[treeview_name] = random.random ()
+
"""Give us a nice waiting cursor"""
window = self.vbox.window
window.set_cursor (gtk.gdk.Cursor (gtk.gdk.WATCH))
- self.progress_bar.pulse ()
+ if self.current_treeview_name == treeview_name:
+ self.progress_bar.pulse ()
- self.results_downloaded = False
thread = DownloadThread (self, url, treeview_name)
- gobject.timeout_add (350, self.populate_list_from_results, treeview_name, thread)
+ gobject.timeout_add (350, self.populate_list_from_results, self.search_token[treeview_name], treeview_name, thread)
thread.start()
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]