Updated to match changes on the server side. Upstream commits in trunk: r5310, r5325, r5349, r5431, r5501, r5778, r5780 Index: totem-2.22.2/src/plugins/youtube/youtube.py =================================================================== --- totem-2.22.2.orig/src/plugins/youtube/youtube.py 2008-10-25 13:42:12.403333174 +0200 +++ totem-2.22.2/src/plugins/youtube/youtube.py 2008-10-25 14:22:37.659830788 +0200 @@ -1,5 +1,5 @@ import totem -import gobject, gtk +import gobject, gtk, gconf import gdata.service import urllib import httplib @@ -16,12 +16,16 @@ class DownloadThread (threading.Thread): threading.Thread.__init__ (self) def run (self): self.youtube.entry_lock.acquire (True) - self.youtube.entry[self.treeview_name] = self.youtube.service.Get (self.url).entry + try: + self.youtube.entry[self.treeview_name] = self.youtube.service.Get (self.url).entry + except gdata.service.RequestError: + """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""" self.youtube.entry_lock.release () class YouTube (totem.Plugin): def __init__ (self): - totem.Plugin.__init__(self) + totem.Plugin.__init__ (self) self.debug = False self.gstreamer_plugins_present = True @@ -45,6 +49,10 @@ class YouTube (totem.Plugin): """Check for the availability of the flvdemux and soup GStreamer plugins""" bvw_name = totem_object.get_video_widget_backend_name () + """If the user's selected 1.5Mbps or greater as their connection speed, grab higher-quality videos + and drop the requirement for the flvdemux plugin.""" + self.gconf_client = gconf.client_get_default () + if bvw_name.find ("GStreamer") != -1: try: import pygst @@ -52,13 +60,13 @@ class YouTube (totem.Plugin): import gst registry = gst.registry_get_default () - if registry.find_plugin ("flvdemux") == None or registry.find_plugin ("soup") == None: + if (self.get_fmt_string () == "" and registry.find_plugin ("flvdemux") == None) or registry.find_plugin ("soup") == None: """This means an error will be displayed when they try to play anything""" self.gstreamer_plugins_present = False except ImportError: """Do nothing; either it's using xine or python-gstreamer isn't installed""" - """Continue loading the plugin as before""" + """Continue loading the plugin as before""" self.builder = self.load_interface ("youtube.ui", True, totem_object.get_main_window (), self) self.totem = totem_object @@ -80,7 +88,7 @@ class YouTube (totem.Plugin): totem_object.add_sidebar_page ("youtube", _("YouTube"), self.vbox) """Set up the service""" - self.service = gdata.service.GDataService (None, None, "HOSTED_OR_GOOGLE", None, None, "gdata.youtube.com") + 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): @@ -115,7 +123,37 @@ class YouTube (totem.Plugin): self.youtube_id = youtube_id self.start_index["related"] = 1 self.results["related"] = 0 - self.get_results ("/feeds/videos/" + urllib.quote (youtube_id) + "/related?max-results=" + str (self.max_results), "related") + self.get_results ("/feeds/api/videos/" + urllib.quote (youtube_id) + "/related?max-results=" + str (self.max_results), "related") + def get_fmt_string (self): + if self.gconf_client.get_int ("/apps/totem/connection_speed") >= 10: + return "&fmt=18" + else: + return "" + + def resolve_t_param (self, youtube_id): + """We have to get the t parameter from the actual video page, since Google changed how their URLs work""" + stream = urllib.urlopen ("http://youtube.com/watch?v=" + urllib.quote (youtube_id)) + regexp1 = re.compile ("swfArgs.*\"t\": \"([^\"]+)\"") + regexp2 = re.compile ("") + + line = stream.readline () + while (line != ""): + """Check for the t parameter, which is now in a JavaScript array on the video page""" + matches = regexp1.search (line) + if (matches != None): + stream.close () + return matches.group (1) + + """Check to see if we've come to the end of the tag; in which case, we should give up""" + if (regexp2.search (line) != None): + stream.close () + return "" + + line = stream.readline () + + stream.close () + return "" + def on_starting_video (self, treeview, path, user_data): """Display an error if the required GStreamer plugins aren't installed""" if self.gstreamer_plugins_present == False: @@ -126,28 +164,6 @@ class YouTube (totem.Plugin): self.totem.get_main_window ()) return False - model, rows = treeview.get_selection ().get_selected_rows () - iter = model.get_iter (rows[0]) - youtube_id = model.get_value (iter, 3) - - """Get the video stream MRL""" - try: - conn = httplib.HTTPConnection ("www.youtube.com") - conn.request ("GET", "/v/" + urllib.quote (youtube_id)) - response = conn.getresponse () - except: - print "Could not resolve stream MRL for YouTube video \"" + youtube_id + "\"." - return False - - if response.status == 303: - location = response.getheader("location") - mrl = "http://www.youtube.com/get_video?video_id=" + urllib.quote (youtube_id) + "&t=" + urllib.quote (re.match (".*[?&]t=([^&]+)", location).groups ()[0]) - else: - mrl = "http://www.youtube.com/v/" + urllib.quote (youtube_id) - conn.close () - - model.set_value (iter, 2, mrl) - return True def on_button_press_event (self, widget, event): self.button_down = True @@ -159,11 +175,11 @@ class YouTube (totem.Plugin): 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 if self.current_treeview_name == "search": - self.get_results ("/feeds/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) + 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"]) elif self.current_treeview_name == "related": - self.get_results ("/feeds/videos/" + urllib.quote_plus (self.youtube_id) + "/related?max-results=" + str (self.max_results) + "&start-index=" + str (self.start_index["related"]), "related", False) + 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"]) def convert_url_to_id (self, url): @@ -190,14 +206,18 @@ class YouTube (totem.Plugin): self.results[treeview_name] += 1 self.start_index[treeview_name] += 1 youtube_id = self.convert_url_to_id (entry.id.text) - mrl = "http://www.youtube.com/v/" + urllib.quote (youtube_id) self.entry_lock.release () - """Find the thumbnail tag""" + """Find the content tag""" for _element in entry.extension_elements: - if _element.tag == "group": - break + if _element.tag =="group": + break; + + content_elements = _element.FindChildren ("content") + if len (content_elements) == 0: + return True; + mrl = content_elements[0].attributes['url'] """Download the thumbnail and store it in a temporary location so we can get a pixbuf from it""" thumbnail_url = _element.FindChildren ("thumbnail")[0].attributes['url'] @@ -216,6 +236,12 @@ class YouTube (totem.Plugin): """Don't leak the temporary file""" unlink (filename) + """Get the video stream MRL""" + t_param = self.resolve_t_param (youtube_id) + + 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 () + self.liststore[treeview_name].append ([pixbuf, entry.title.text, mrl, youtube_id]) return True @@ -231,7 +257,7 @@ class YouTube (totem.Plugin): self.search_terms = search_terms self.start_index["search"] = 1 self.results["search"] = 0 - self.get_results ("/feeds/videos?vq=" + urllib.quote_plus (search_terms) + "&orderby=relevance&max-results=" + str (self.max_results), "search") + 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):