# Copyright (C) 2011 Canonical # # Authors: # Michael Vogt # # 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; version 3. # # 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, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import logging import subprocess from gettext import gettext as _ from gi.repository import Gdk # FIXME: remove this try/except and add a dependency on gir1.2-gstreamer-0.10 # if we (ever) start using VideoPlayerGtk3 try: from gi.repository import Gst except ImportError: pass from gi.repository import Gtk from gi.repository import WebKit LOG = logging.getLogger(__name__) class VideoPlayer(Gtk.VBox): def __init__(self): super(VideoPlayer, self).__init__() self.set_size_request(400, 255) self.webkit = WebKit.WebView() settings = self.webkit.get_settings() # this disables the flash and other plugins so that we force html5 # video on the system. This is works currently (11/2011) fine with # dailymotion and vimeo but youtube is opt-in only so we need # to monitor the situation settings.set_property("enable-plugins", False) # on navigation/new window etc, just use the proper browser self.webkit.connect( "new-window-policy-decision-requested", self._on_new_window) self.webkit.connect("create-web-view", self._on_create_web_view) self.pack_start(self.webkit, True, True, 0) self._uri = "" # helper required to follow ToS about the "back" link (flash version) def _on_new_window(self, view, frame, request, action, policy): subprocess.Popen(['xdg-open', request.get_uri()]) return True # helper for the embedded html5 viewer def _on_create_web_view(self, view, frame): # mvo: this is not ideal, the trouble is that we do not get the # url that the new view points to until after the view was # created. But we don't want to be a full blow internal # webbrowser so we simply go back to the youtube url here # and the user needs to click "youtube" there again :/ uri = frame.get_uri() subprocess.Popen(['xdg-open', uri]) # uri property def _set_uri(self, v): self._uri = v or "" if self._uri: # only load the uri if it's defined, otherwise we may get: # Program received signal SIGSEGV, Segmentation fault. # webkit_web_frame_load_uri () from /usr/lib/libwebkitgtk-3.0.so.0 self.webkit.load_uri(self._uri) def _get_uri(self): return self._uri uri = property(_get_uri, _set_uri, None, "uri property") def load_html_string(self, html): """ Instead of a video URI use a html embedded video like e.g. youtube or vimeo. Note that on a default install not all video codecs will play (no flash!), so be careful! """ # FIXME: add something more useful here base_uri = "http://www.ubuntu.com" self.webkit.load_html_string(html, base_uri) def stop(self): self.webkit.load_uri('') # AKA the-segfault-edition-with-no-documentation class VideoPlayerGtk3(Gtk.VBox): def __init__(self): super(VideoPlayerGtk3, self).__init__() self.uri = "" # gtk ui self.movie_window = Gtk.DrawingArea() self.pack_start(self.movie_window, True, True, 0) self.button = Gtk.Button(_("Play")) self.pack_start(self.button, False, True, 0) self.button.connect("clicked", self.on_play_clicked) # player self.player = Gst.ElementFactory.make("playbin2", "player") # bus stuff bus = self.player.get_bus() bus.add_signal_watch() bus.enable_sync_message_emission() bus.connect("message", self.on_message) # FIXME: no sync messages currently so no playing in the widget :/ # the former appears to be not working anymore with GIR, the # later is not exported (introspectable=0 in the GIR) bus.connect("sync-message", self.on_sync_message) #bus.set_sync_handler(self.on_sync_message) def on_play_clicked(self, button): if self.button.get_label() == _("Play"): self.button.set_label("Stop") print(self.uri) self.player.set_property("uri", self.uri) self.player.set_state(Gst.State.PLAYING) else: self.player.set_state(Gst.State.NULL) self.button.set_label(_("Play")) def on_message(self, bus, message): print("message: %s" % bus, message) if message is None: return t = message.type print(t) if t == Gst.MessageType.EOS: self.player.set_state(Gst.State.NULL) self.button.set_label(_("Play")) elif t == Gst.MessageType.ERROR: self.player.set_state(Gst.State.NULL) err, debug = message.parse_error() LOG.error("Error playing video: %s (%s)" % (err, debug)) self.button.set_label(_("Play")) def on_sync_message(self, bus, message): print("sync: %s" % bus, message) if message is None or message.structure is None: return message_name = message.structure.get_name() if message_name == "prepare-xwindow-id": imagesink = message.src imagesink.set_property("force-aspect-ratio", True) Gdk.threads_enter() # FIXME: this is the way to do it, *but* get_xid() is not # exported in the GIR xid = self.player.movie_window.get_window().get_xid() imagesink.set_xwindow_id(xid) Gdk.threads_leave()