Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorManuel Kaufmann <humitos@gmail.com>2013-01-14 15:42:35 (GMT)
committer Gonzalo Odiard <godiard@gmail.com>2013-01-25 12:38:40 (GMT)
commit0bf46d8bf132609bab93a93360e78e09f01d7b88 (patch)
tree45cf4f0892622a61b3a06caf8365920dadc7f0b1
parentf4ac3ea1126a571bd0b8e60bcced451066853b9c (diff)
Modularization of classes
- Put each class in a different .py file - Rename jukeboxactivity.py to activity.py to make this more standard Signed-off-by: Manuel Kaufmann <humitos@gmail.com> Reviewed-by: Gonzalo Odiard <gonzalo@laptop.org>
-rw-r--r--activity.py (renamed from jukeboxactivity.py)187
-rw-r--r--activity/activity.info2
-rw-r--r--controls.py (renamed from ControlToolbar.py)73
-rw-r--r--player.py182
-rw-r--r--playlist.py (renamed from widgets.py)2
-rw-r--r--viewtoolbar.py63
6 files changed, 278 insertions, 231 deletions
diff --git a/jukeboxactivity.py b/activity.py
index 1bc4d4a..14fcbe9 100644
--- a/jukeboxactivity.py
+++ b/activity.py
@@ -1,11 +1,3 @@
-"""
- jukeboxactivity.py
- Activity that plays media.
- Copyright (C) 2007 Andy Wingo <wingo@pobox.com>
- Copyright (C) 2007 Red Hat, Inc.
- Copyright (C) 2008-2010 Kushal Das <kushal@fedoraproject.org>
-"""
-
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
@@ -21,6 +13,12 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
+# Activity that plays media.
+# Copyright (C) 2007 Andy Wingo <wingo@pobox.com>
+# Copyright (C) 2007 Red Hat, Inc.
+# Copyright (C) 2008-2010 Kushal Das <kushal@fedoraproject.org>
+# Copyright (C) 2013 Manuel Kaufmann <humitos@gmail.com>
+
import sys
import logging
import tempfile
@@ -49,23 +47,12 @@ from gi.repository import Gtk
from gi.repository import Gst
from gi.repository import Gio
-# Needed for window.get_xid(), xvimagesink.set_window_handle(),
-# respectively:
-from gi.repository import GdkX11, GstVideo
-
-# Avoid "Fatal Python error: GC object already tracked"
-# http://stackoverflow.com/questions/7496629/gstreamer-appsrc-causes-random-crashes
-GObject.threads_init()
-
-# Initialize GStreamer
-Gst.init(None)
-
import urllib
-from ControlToolbar import Control, ViewToolbar
-from ConfigParser import ConfigParser
-cf = ConfigParser()
+from viewtoolbar import ViewToolbar
+from controls import Controls
+from player import GstPlayer
-from widgets import PlayListWidget
+from playlist import PlayList
PLAYLIST_WIDTH_PROP = 1.0 / 3
@@ -106,7 +93,7 @@ class JukeboxActivity(activity.Activity):
toolbar_box.toolbar.insert(view_toolbar_button, -1)
view_toolbar_button.show()
- self.control = Control(toolbar_box.toolbar, self)
+ self.control = Controls(toolbar_box.toolbar, self)
toolbar_box.toolbar.insert(StopButton(self), -1)
@@ -155,7 +142,7 @@ class JukeboxActivity(activity.Activity):
self.canvas = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
self._alert = None
- self.playlist_widget = PlayListWidget(self.play)
+ self.playlist_widget = PlayList(self.play)
self.playlist_widget.update(self.playlist)
self.playlist_widget.show()
self.canvas.pack_start(self.playlist_widget, False, True, 0)
@@ -657,156 +644,6 @@ class JukeboxActivity(activity.Activity):
self.canvas.queue_draw()
-class GstPlayer(GObject.GObject):
-
- __gsignals__ = {
- 'error': (GObject.SignalFlags.RUN_FIRST, None, [str, str]),
- 'eos': (GObject.SignalFlags.RUN_FIRST, None, []),
- }
-
- def __init__(self, videowidget):
- GObject.GObject.__init__(self)
-
- self.playing = False
- self.error = False
-
- # Create GStreamer pipeline
- self.pipeline = Gst.Pipeline()
- # Create bus to get events from GStreamer pipeline
- self.bus = self.pipeline.get_bus()
- self.bus.add_signal_watch()
-
- self.bus.connect('message::eos', self.__on_eos_message)
- self.bus.connect('message::error', self.__on_error_message)
-
- # This is needed to make the video output in our DrawingArea
- self.bus.enable_sync_message_emission()
- self.bus.connect('sync-message::element', self.__on_sync_message)
-
- # Create GStreamer elements
- self.player = Gst.ElementFactory.make('playbin', None)
- self.pipeline.add(self.player)
-
- # Set the proper flags to render the vis-plugin
- GST_PLAY_FLAG_VIS = 1 << 3
- GST_PLAY_FLAG_TEXT = 1 << 2
- self.player.props.flags |= GST_PLAY_FLAG_VIS
- self.player.props.flags |= GST_PLAY_FLAG_TEXT
-
- r = Gst.Registry.get()
- l = [x for x in r.get_feature_list(Gst.ElementFactory)
- if (x.get_metadata('klass') == "Visualization")]
- if len(l):
- e = l.pop() # take latest plugin in the list
- vis_plug = Gst.ElementFactory.make(e.get_name(), e.get_name())
- self.player.set_property('vis-plugin', vis_plug)
-
- self.overlay = None
- videowidget.realize()
- self.videowidget = videowidget
- self.videowidget_xid = videowidget.get_window().get_xid()
- self._init_video_sink()
-
- def __on_error_message(self, bus, msg):
- self.stop()
- self.playing = False
- self.error = True
- err, debug = msg.parse_error()
- self.emit('error', err, debug)
-
- def __on_eos_message(self, bus, msg):
- logging.debug('SIGNAL: eos')
- self.playing = False
- self.emit('eos')
-
- def __on_sync_message(self, bus, msg):
- if msg.get_structure().get_name() == 'prepare-window-handle':
- msg.src.set_window_handle(self.videowidget_xid)
-
- def set_uri(self, uri):
- self.pipeline.set_state(Gst.State.READY)
- logging.debug('### Setting URI: %s', uri)
- self.player.set_property('uri', uri)
-
- def _init_video_sink(self):
- self.bin = Gst.Bin()
- videoscale = Gst.ElementFactory.make('videoscale', 'videoscale')
- self.bin.add(videoscale)
- pad = videoscale.get_static_pad("sink")
- ghostpad = Gst.GhostPad.new("sink", pad)
- self.bin.add_pad(ghostpad)
- videoscale.set_property("method", 0)
-
- textoverlay = Gst.ElementFactory.make('textoverlay', 'textoverlay')
- self.overlay = textoverlay
- self.bin.add(textoverlay)
- conv = Gst.ElementFactory.make("videoconvert", "conv")
- self.bin.add(conv)
- videosink = Gst.ElementFactory.make('autovideosink', 'autovideosink')
- self.bin.add(videosink)
-
- videoscale.link(textoverlay)
- textoverlay.link(conv)
- conv.link(videosink)
-
- self.player.set_property("video-sink", self.bin)
-
- def set_overlay(self, title, artist, album):
- text = "%s\n%s" % (title, artist)
- if album and len(album):
- text += "\n%s" % album
- self.overlay.set_property("text", text)
- self.overlay.set_property("font-desc", "sans bold 14")
- self.overlay.set_property("halignment", "right")
- self.overlay.set_property("valignment", "bottom")
- try:
- # Only in OLPC versions of gstreamer-plugins-base for now
- self.overlay.set_property("line-align", "left")
- except:
- pass
-
- def query_position(self):
- "Returns a (position, duration) tuple"
-
- p_success, position = self.player.query_position(Gst.Format.TIME)
- d_success, duration = self.player.query_duration(Gst.Format.TIME)
-
- return (p_success and d_success, position, duration)
-
- def seek(self, location):
- """
- @param location: time to seek to, in nanoseconds
- """
-
- logging.debug('Seek: %s ns', location)
-
- self.pipeline.seek_simple(Gst.Format.TIME,
- Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT,
- location)
-
- def pause(self):
- logging.debug("pausing player")
- self.pipeline.set_state(Gst.State.PAUSED)
- self.playing = False
-
- def play(self):
- logging.debug("playing player")
- self.pipeline.set_state(Gst.State.PLAYING)
- self.playing = True
- self.error = False
-
- def stop(self):
- self.playing = False
- self.pipeline.set_state(Gst.State.NULL)
- logging.debug("stopped player")
-
- def get_state(self, timeout=1):
- return self.player.get_state(timeout=timeout)
-
- def is_playing(self):
- return self.playing
-
-
class VideoWidget(Gtk.DrawingArea):
def __init__(self):
GObject.GObject.__init__(self)
diff --git a/activity/activity.info b/activity/activity.info
index 76cfd1b..f66c422 100644
--- a/activity/activity.info
+++ b/activity/activity.info
@@ -3,7 +3,7 @@ name = Jukebox
bundle_id = org.laptop.sugar.Jukebox
license = GPLv2+
icon = activity-jukebox
-exec = sugar-activity jukeboxactivity.JukeboxActivity
+exec = sugar-activity activity.JukeboxActivity
show_launcher = yes
activity_version = 29
mime_types = video/x-theora;audio/x-vorbis;audio/x-flac;audio/x-speex;application/x-ogm-video;application/x-ogm-audio;video/x-mng;audio/x-aiff;audio/x-wav;audio/x-m4a;video/mpeg4;video/mpeg-stream;video/mpeg;application/ogg;video/mpegts;video/mpeg2;video/mpeg1;audio/mpeg;audio/x-ac3;video/x-cdxa;audio/x-au;audio/mpegurl;audio/x-mpegurl;audio/x-vorbis+ogg;audio/x-scpls;audio/ogg;video/ogg;audio/x-flac+ogg;audio/x-speex+ogg;video/x-theora+ogg;video/x-ogm+ogg;video/x-flv;video/mp4;video/x-matroska;video/x-msvideo;video/quicktime, video/x-quicktime, image/mov, audio/aiff, audio/x-midi, video/avi
diff --git a/ControlToolbar.py b/controls.py
index 2205bde..b84a55a 100644
--- a/ControlToolbar.py
+++ b/controls.py
@@ -1,68 +1,33 @@
-# Copyright (C) 2007 Andy Wingo <wingo@pobox.com>
-# Copyright (C) 2007 Red Hat, Inc.
-# Copyright (C) 2008 Kushal Das <kushal@fedoraproject.org>
-# 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 2 of the License, or
-# (at your option) any later version.
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
#
-# This program is distributed in the hope that it will be useful,
+# This library 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.
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
-import logging
+# Copyright (C) 2013 Manuel Kaufmann <humitos@gmail.com>
-from gettext import gettext as _
+import logging
-from gi.repository import GObject
from gi.repository import Gtk
+from gi.repository import GObject
-from sugar3.graphics.toolbutton import ToolButton
-from sugar3.graphics.toggletoolbutton import ToggleToolButton
-
-
-class ViewToolbar(Gtk.Toolbar):
- __gtype_name__ = 'ViewToolbar'
-
- __gsignals__ = {
- 'go-fullscreen': (GObject.SignalFlags.RUN_FIRST,
- None,
- ([])),
- 'toggle-playlist': (GObject.SignalFlags.RUN_FIRST,
- None,
- ([]))
- }
-
- def __init__(self):
- GObject.GObject.__init__(self)
-
- self._show_playlist = ToggleToolButton('view-list')
- self._show_playlist.set_active(True)
- self._show_playlist.set_tooltip(_('Show Playlist'))
- self._show_playlist.connect('toggled', self._playlist_toggled_cb)
- self.insert(self._show_playlist, -1)
- self._show_playlist.show()
-
- self._fullscreen = ToolButton('view-fullscreen')
- self._fullscreen.set_tooltip(_('Fullscreen'))
- self._fullscreen.connect('clicked', self._fullscreen_cb)
- self.insert(self._fullscreen, -1)
- self._fullscreen.show()
-
- def _fullscreen_cb(self, button):
- self.emit('go-fullscreen')
+from gettext import gettext as _
- def _playlist_toggled_cb(self, button):
- self.emit('toggle-playlist')
+from sugar3.graphics.toolbutton import ToolButton
-class Control(GObject.GObject):
- """Class to create the Control (play) toolbar"""
+class Controls(GObject.GObject):
+ """Class to create the Control (play, back, forward,
+ add, remove, etc) toolbar"""
def __init__(self, toolbar, jukebox):
GObject.GObject.__init__(self)
diff --git a/player.py b/player.py
new file mode 100644
index 0000000..b5b03da
--- /dev/null
+++ b/player.py
@@ -0,0 +1,182 @@
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+# Copyright (C) 2013 Manuel Kaufmann <humitos@gmail.com>
+
+import logging
+
+from gi.repository import Gst
+from gi.repository import GObject
+
+# Needed for window.get_xid(), xvimagesink.set_window_handle(),
+# respectively:
+from gi.repository import GdkX11, GstVideo
+
+# Avoid "Fatal Python error: GC object already tracked"
+# http://stackoverflow.com/questions/7496629/gstreamer-appsrc-causes-random-crashes
+# GObject.threads_init()
+
+# Initialize GStreamer
+Gst.init(None)
+
+
+class GstPlayer(GObject.GObject):
+
+ __gsignals__ = {
+ 'error': (GObject.SignalFlags.RUN_FIRST, None, [str, str]),
+ 'eos': (GObject.SignalFlags.RUN_FIRST, None, []),
+ }
+
+ def __init__(self, videowidget):
+ GObject.GObject.__init__(self)
+
+ self.playing = False
+ self.error = False
+
+ # Create GStreamer pipeline
+ self.pipeline = Gst.Pipeline()
+ # Create bus to get events from GStreamer pipeline
+ self.bus = self.pipeline.get_bus()
+ self.bus.add_signal_watch()
+
+ self.bus.connect('message::eos', self.__on_eos_message)
+ self.bus.connect('message::error', self.__on_error_message)
+
+ # This is needed to make the video output in our DrawingArea
+ self.bus.enable_sync_message_emission()
+ self.bus.connect('sync-message::element', self.__on_sync_message)
+
+ # Create GStreamer elements
+ self.player = Gst.ElementFactory.make('playbin', None)
+ self.pipeline.add(self.player)
+
+ # Set the proper flags to render the vis-plugin
+ GST_PLAY_FLAG_VIS = 1 << 3
+ GST_PLAY_FLAG_TEXT = 1 << 2
+ self.player.props.flags |= GST_PLAY_FLAG_VIS
+ self.player.props.flags |= GST_PLAY_FLAG_TEXT
+
+ r = Gst.Registry.get()
+ l = [x for x in r.get_feature_list(Gst.ElementFactory)
+ if (x.get_metadata('klass') == "Visualization")]
+ if len(l):
+ e = l.pop() # take latest plugin in the list
+ vis_plug = Gst.ElementFactory.make(e.get_name(), e.get_name())
+ self.player.set_property('vis-plugin', vis_plug)
+
+ self.overlay = None
+ videowidget.realize()
+ self.videowidget = videowidget
+ self.videowidget_xid = videowidget.get_window().get_xid()
+ self._init_video_sink()
+
+ def __on_error_message(self, bus, msg):
+ self.stop()
+ self.playing = False
+ self.error = True
+ err, debug = msg.parse_error()
+ self.emit('error', err, debug)
+
+ def __on_eos_message(self, bus, msg):
+ logging.debug('SIGNAL: eos')
+ self.playing = False
+ self.emit('eos')
+
+ def __on_sync_message(self, bus, msg):
+ if msg.get_structure().get_name() == 'prepare-window-handle':
+ msg.src.set_window_handle(self.videowidget_xid)
+
+ def set_uri(self, uri):
+ self.pipeline.set_state(Gst.State.READY)
+ logging.debug('### Setting URI: %s', uri)
+ self.player.set_property('uri', uri)
+
+ def _init_video_sink(self):
+ self.bin = Gst.Bin()
+ videoscale = Gst.ElementFactory.make('videoscale', 'videoscale')
+ self.bin.add(videoscale)
+ pad = videoscale.get_static_pad("sink")
+ ghostpad = Gst.GhostPad.new("sink", pad)
+ self.bin.add_pad(ghostpad)
+ videoscale.set_property("method", 0)
+
+ textoverlay = Gst.ElementFactory.make('textoverlay', 'textoverlay')
+ self.overlay = textoverlay
+ self.bin.add(textoverlay)
+ conv = Gst.ElementFactory.make("videoconvert", "conv")
+ self.bin.add(conv)
+ videosink = Gst.ElementFactory.make('autovideosink', 'autovideosink')
+ self.bin.add(videosink)
+
+ videoscale.link(textoverlay)
+ textoverlay.link(conv)
+ conv.link(videosink)
+
+ self.player.set_property("video-sink", self.bin)
+
+ def set_overlay(self, title, artist, album):
+ text = "%s\n%s" % (title, artist)
+ if album and len(album):
+ text += "\n%s" % album
+ self.overlay.set_property("text", text)
+ self.overlay.set_property("font-desc", "sans bold 14")
+ self.overlay.set_property("halignment", "right")
+ self.overlay.set_property("valignment", "bottom")
+ try:
+ # Only in OLPC versions of gstreamer-plugins-base for now
+ self.overlay.set_property("line-align", "left")
+ except:
+ pass
+
+ def query_position(self):
+ "Returns a (position, duration) tuple"
+
+ p_success, position = self.player.query_position(Gst.Format.TIME)
+ d_success, duration = self.player.query_duration(Gst.Format.TIME)
+
+ return (p_success and d_success, position, duration)
+
+ def seek(self, location):
+ """
+ @param location: time to seek to, in nanoseconds
+ """
+
+ logging.debug('Seek: %s ns', location)
+
+ self.pipeline.seek_simple(Gst.Format.TIME,
+ Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT,
+ location)
+
+ def pause(self):
+ logging.debug("pausing player")
+ self.pipeline.set_state(Gst.State.PAUSED)
+ self.playing = False
+
+ def play(self):
+ logging.debug("playing player")
+ self.pipeline.set_state(Gst.State.PLAYING)
+ self.playing = True
+ self.error = False
+
+ def stop(self):
+ self.playing = False
+ self.pipeline.set_state(Gst.State.NULL)
+ logging.debug("stopped player")
+
+ def get_state(self, timeout=1):
+ return self.player.get_state(timeout=timeout)
+
+ def is_playing(self):
+ return self.playing
diff --git a/widgets.py b/playlist.py
index fbbd9c5..4563d47 100644
--- a/widgets.py
+++ b/playlist.py
@@ -31,7 +31,7 @@ COLUMNS_NAME = ('index', 'media', 'available')
COLUMNS = dict((name, i) for i, name in enumerate(COLUMNS_NAME))
-class PlayListWidget(Gtk.ScrolledWindow):
+class PlayList(Gtk.ScrolledWindow):
def __init__(self, play_callback):
self._playlist = None
self._play_callback = play_callback
diff --git a/viewtoolbar.py b/viewtoolbar.py
new file mode 100644
index 0000000..2f15bbe
--- /dev/null
+++ b/viewtoolbar.py
@@ -0,0 +1,63 @@
+# 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 2 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+# Copyright (C) 2007 Andy Wingo <wingo@pobox.com>
+# Copyright (C) 2007 Red Hat, Inc.
+# Copyright (C) 2008 Kushal Das <kushal@fedoraproject.org>
+# Copyright (C) 2013 Manuel Kaufmann <humitos@gmail.com>
+
+import logging
+
+from gettext import gettext as _
+
+from gi.repository import GObject
+from gi.repository import Gtk
+
+from sugar3.graphics.toolbutton import ToolButton
+from sugar3.graphics.toggletoolbutton import ToggleToolButton
+
+
+class ViewToolbar(Gtk.Toolbar):
+ __gtype_name__ = 'ViewToolbar'
+
+ __gsignals__ = {
+ 'go-fullscreen': (GObject.SignalFlags.RUN_FIRST,
+ None,
+ ([])),
+ 'toggle-playlist': (GObject.SignalFlags.RUN_FIRST,
+ None,
+ ([]))
+ }
+
+ def __init__(self):
+ GObject.GObject.__init__(self)
+
+ self._show_playlist = ToggleToolButton('view-list')
+ self._show_playlist.set_active(True)
+ self._show_playlist.set_tooltip(_('Show Playlist'))
+ self._show_playlist.connect('toggled', self._playlist_toggled_cb)
+ self.insert(self._show_playlist, -1)
+ self._show_playlist.show()
+
+ self._fullscreen = ToolButton('view-fullscreen')
+ self._fullscreen.set_tooltip(_('Fullscreen'))
+ self._fullscreen.connect('clicked', self._fullscreen_cb)
+ self.insert(self._fullscreen, -1)
+ self._fullscreen.show()
+
+ def _fullscreen_cb(self, button):
+ self.emit('go-fullscreen')
+
+ def _playlist_toggled_cb(self, button):
+ self.emit('toggle-playlist')