Media Player with GStreamer and PyGI

When trying to implement a media plugin for Liferea I learned at lot from Laszlo Pandy's session slides from the Ubuntu Opportunistic Developer Week (PDF, source). The only problem was that it is for PyGtk, the GTK binding for Python, which is now more or less deprecated for PyGI, the GTK+ 3.0 GObject introspection based binding.

While it is easy to convert all pieces manually following the Novacut/GStreamer1.0 documentation I still want to share a complete music player source example that everyone interested can copy from. I hope this saves the one or the other some time in guessing how to write something like "gst.FORMAT_TIME" in PyGI (actually it is "Gst.Format.TIME").

So here is the example code (download file):

from gi.repository import GObject
from gi.repository import GLib
from gi.repository import Gtk
from gi.repository import Gst

class PlaybackInterface:

    def __init__(self):
	self.playing = False

	# A free example sound track
	self.uri = ""

	# GTK window and widgets
	self.window = Gtk.Window()

	vbox = Gtk.Box(Gtk.Orientation.HORIZONTAL, 0)

	self.playButtonImage = Gtk.Image()
	self.playButtonImage.set_from_stock("gtk-media-play", Gtk.IconSize.BUTTON)
	self.playButton =
	self.playButton.connect("clicked", self.playToggled)
	Gtk.Box.pack_start(vbox, self.playButton, False, False, 0)

	self.slider = Gtk.HScale()
	self.slider.set_range(0, 100)
	self.slider.set_increments(1, 10)

	Gtk.Box.pack_start(vbox, self.slider, True, True, 0)

	self.label = Gtk.Label(label='0:00')
	Gtk.Box.pack_start(vbox, self.label, False, False, 0)


        # GStreamer Setup
        self.IS_GST010 = Gst.version()[0] == 0
	self.player = Gst.ElementFactory.make("playbin2", "player")
	fakesink = Gst.ElementFactory.make("fakesink", "fakesink")
	self.player.set_property("video-sink", fakesink)
	bus = self.player.get_bus()
	bus.connect("message", self.on_message)
	self.player.connect("about-to-finish",  self.on_finished)

    def on_message(self, bus, message):
	t = message.type
	if t == Gst.Message.EOS:
		self.playing = False
	elif t == Gst.Message.ERROR:
		err, debug = message.parse_error()
		print "Error: %s" % err, debug
		self.playing = False


    def on_finished(self, player):
	self.playing = False

    def play(self):
	self.player.set_property("uri", self.uri)
	GObject.timeout_add(1000, self.updateSlider)

    def stop(self):
    def playToggled(self, w):

	if(self.playing == False):


    def updateSlider(self):
	if(self.playing == False):
	   return False # cancel timeout

	   if self.IS_GST010:
	      nanosecs = self.player.query_position(Gst.Format.TIME)[2]
	      duration_nanosecs = self.player.query_duration(Gst.Format.TIME)[2]
	      nanosecs = self.player.query_position(Gst.Format.TIME)[1]
	      duration_nanosecs = self.player.query_duration(Gst.Format.TIME)[1]

	   # block seek handler so we don't seek when we set_value()
	   # self.slider.handler_block_by_func(self.on_slider_change)

           duration = float(duration_nanosecs) / Gst.SECOND
	   position = float(nanosecs) / Gst.SECOND
	   self.slider.set_range(0, duration)
           self.label.set_text ("%d" % (position / 60) + ":%02d" % (position % 60))

	except Exception as e:
		# pipeline must not be ready and does not know position
		print e

	return True

    def updateButtons(self):
        if(self.playing == False):
           self.playButtonImage.set_from_stock("gtk-media-play", Gtk.IconSize.BUTTON)
           self.playButtonImage.set_from_stock("gtk-media-stop", Gtk.IconSize.BUTTON)

if __name__ == "__main__":

and this is how it should look like if everything goes well:

Example Player Screenshot

Please post comments below if you have improvement suggestions!

Further Improving the Liferea Music Player Plugin

Yesterday I found time to improve the music player plugin. It now provides a slider showing the playing progress and the time position in the track.

The plugin is written in Python and uses GStreamer. So far I tested with GStreamer 0.10. The plugin is currently missing seeking and GStreamer 0.11 testing.

I'm wondering if some reader would like to apply his Python skills and improve the player further? Check out the so far simple script:

How to Run VACUUM on SQLite


On any SQLite based database (e.g. Firefox or Liferea) you can run "VACUUM" to reduce the database file size. To do this you need the SQLite command line client which can be run using

sqlite3 <database file>

When called like this you get a query prompt. To directly run "VACUUM" just call

sqlite3 <database file> "VACUUM;"

Ensure that the program using the database file is not running!

Alternatives to Manual VACUUM

If you are unsure how to do it manually you can also use a helper tool like BleachBit which along many other cleanup jobs also performs SQLite database compaction.

Liferea 1.8.10 released

This release fixes a search folder counting and rebuilding, it fixes crash when removing nodes from Google Reader subscriptions and it prevents sorting feeds and adding unsupported node types to Google Reader subscriptions.

Please upgrade!

Grab the newest release from the homepage!

Liferea 1.9.7 released

This release introduces a new preference to set the default viewing mode (email, wide or 2pane), adds Google Chrome as supported browser preference and brings an overhaul of the search folder implementation to fix counting and rebuilding issues.

Grab the newest release from the homepage!

chef-server "Failed to authenticate."

If your chef GUI suddenly stops working and you see something like the following exception in both server.log and server-webui.log:

merb : chef-server (api) : worker (port 4000) ~ Failed to authenticate. Ensure that your client key is valid. - (Merb::ControllerExceptions::Unauthorized)
/usr/share/chef-server-api/app/controllers/application.rb:56:in `authenticate_every'
/usr/lib/ruby/vendor_ruby/merb-core/controller/abstract_controller.rb:352:in `send'
/usr/lib/ruby/vendor_ruby/merb-core/controller/abstract_controller.rb:352:in `_call_filters'
/usr/lib/ruby/vendor_ruby/merb-core/controller/abstract_controller.rb:344:in `each'
/usr/lib/ruby/vendor_ruby/merb-core/controller/abstract_controller.rb:344:in `_call_filters'
/usr/lib/ruby/vendor_ruby/merb-core/controller/abstract_controller.rb:286:in `_dispatch'
/usr/lib/ruby/vendor_ruby/merb-core/controller/abstract_controller.rb:284:in `catch'
/usr/lib/ruby/vendor_ruby/merb-core/controller/abstract_controller.rb:284:in `_dispatch'

Then try stopping all chef processes, remove


and start everything again. It will regenerate the keys.

The downside is that you have to

knife configure -i

all you knife setup locations again.

Chef: Which nodes have role X / recipe Y

Why is it so hard to find out which nodes have a given role or recipe in chef?

The only way seems to loop yourself:

for node in $(knife node list); do
   if knife node show -r $node | grep 'role\[base\]' >/dev/null; then       
     echo $node;

Did I miss some other obvious way? I'd like to have some "knife run_list filter ..." command!

Added Google Chrome to Browser Preferences

Chrome Logo

After reading this recent review of Liferea over at TechNewsWorld I found that we somehow missed adding Google Chrome to the browser preferences!

Thanks to review author Jack M. Germain this is now added in 1.9 git branch to be released with 1.9.7.

As a workaround you can still use Chrome if you select "Default Browser" if your system preferences default to Chrome or you can specify the following "Manual Command" in the browser settings tab:

google-chrome "%s"

Have fun!

PHP preg_match() Examples

This post gives some simple examples for using regular expressions with preg_match() in PHP scripts.

1. Syntax of preg_match

While full syntax is

int preg_match ( string $pattern , string $subject 
     [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] )

you propably will use preg_match() mostly with two parameters for simply matching checks or with three to extract matches.

You probably won't use the 4th and 5th parameter which can be used to return match offsets and limit matching to a given offset in the string.

2. Simple String Checks with preg_match()

Here are some syntax examples that check strings for certain content:

Basic matching

preg_match("/PHP/", "PHP")       # Match for an unbound literal
preg_match("/^PHP/", "PHP")      # Match literal at start of string
preg_match("/PHP$/", "PHP")      # Match literal at end of string
preg_match("/^PHP$/", "PHP")     # Match for exact string content
preg_match("/^$/", "")           # Match empty string

Using different regex delimiters

preg_match("/PHP/", "PHP")                # / as commonly used delimiter
preg_match("@PHP@", "PHP")                # @ as delimiter
preg_match("!PHP!", "PHP")                # ! as delimiter

Changing the delimiter becomes useful in some cases

preg_match("/http:\/\//", "http://");     # match http:// protocol prefix with / delimiter
preg_match("#http://#",   "http://")      # match http:// protocol prefix with # delimiter

Case sensitivity

preg_match("/PHP/", "PHP")                # case sensitive string matching
preg_match("/php/i", "PHP")               # case in-sensitive string matching

Matching with wildcards

preg_match("/P.P/",     "PHP")            # match a single character
preg_match("/P.*P/",    "PHP")            # match multipe characters
preg_match("/P[A-Z]P/", "PHP")            # match from character range A-Z
preg_match("/[PH]*/",   "PHP")            # match from character set P and H
preg_match("/P\wP/",    "PHP")            # match one word character
preg_match("/\bPHP\b/", "regex in PHP")   # match the word "PHP", but not "PHP" as larger string

Using quantifiers

preg_match("/[PH]{3}/",   "PHP")          # match exactly 3 characters from set [PH]
preg_match("/[PH]{3,3}/", "PHP")          # match exactly 3 characters from set [PH]
preg_match("/[PH]{,3}/",  "PHP")          # match at most 3 characters from set [PH]
preg_match("/[PH]{3,}/",  "PHP")          # match at least 3 characters from set [PH]

Note: all of the examples above should work (please comment if you find an error)!

3. Extracting Data with preg_match()

To extract data using regular expression we have to use capture/grouping syntax.

Some basic examples

# Extract everything after the literal "START"
preg_match("/START(.*)/", $string, $results)   

# Extract the number from a date string 
preg_match("/(\d{4})-(\d{2})-(\d{2})/", "2012-10-20", $results)

# Nesting of capture groups, extract full name, and both parts...
preg_match("/name is ((\w+), (\w+))/", "name is Doe, John", $results)

So you basically just enclose the sub patterns you want to extract with braces and fetch the results by passing a third parameter which preg_match() will fill as an array.

Named Capture Groups

# Extract the number from a date string 
preg_match("/(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})/", "2012-10-20", $results)

Now the $result array will additionally to the position matches 1, 2 and 3 contain the keys "year", "month" and "day". The advantage is never having to think of the capture positions anymore when you modify the expression!

4. Check for preg_match() Processing Errors!

While it might often be unimportant be aware that applying a regular expression might fail due to PCRE constraints. This usually happens when matching overly long strings or strings with faulty encoding.

The only way to notice that preg_match() was not able to check the string is by calling


Only if it returns PREG_NO_ERROR you got a safe result! Consider this when using preg_match() for security purposes.

How to Vacuum SQLite

This post is a summary on how to effectively VACUUM SQLite databases. Actually open source project like Firefox and Liferea were significantly hurt by not efficiently VACUUMing their SQLite databases. For Firefox this was caused by the Places database containing bookmarks and the history. In case of Liferea it was the feed cache database. Both projects suffered from fragmentation caused by frequent insertion and deletion while not vacuuming the database. This of course caused much frustration with end users and workarounds to vacuum manually.

In the end both projects started to automatically vacuum their sqlite databases on demand based on free list threshold thereby solving the performance issues. Read on to learn how to perform vacuum and why not to use auto-vacuum in those cases!

1. Manual VACUUM

First for the basics: with SQLite 3 you simply vacuum by running:

sqlite3 my.db "VACUUM;"

Depending on the database size and the last vacuum run it might take a while for sqlite3 to finish with it. Using this you can perform manual VACUUM runs (e.g. nightly) or on demand runs (for example on application startup).

2. Using Auto-VACCUM

Note: SQLite Auto-VACUUM does not do the same as VACUUM! It only moves free pages to the end of the database thereby reducing the database size. By doing so it can significantly fragment the database while VACUUM ensures defragmentation. So Auto-VACUUM just keeps the database small!

You can enable/disable SQLite auto-vacuuming by the following pragmas:

PRAGMA auto_vacuum = NONE;
PRAGMA auto_vacuum = FULL;

So effectively you have two modes: full and incremental. In full mode free pages are removed from the database upon each transaction. When in incremental mode no pages are free'd automatically, but only metadata is kept to help freeing them. At any time you can call

PRAGMA incremental_vacuum(n);

to free up to n pages and resize the database by this amount of pages.

To check the auto-vacuum setting in a sqlite database run

sqlite3 my.db "PRAGMA auto_vacuum;"

which should return a number from 0 to 2 meaning: 0=None, 1=Incremental, 2=Full.

3. On Demand VACUUM

Another possibility is to VACUUM on demand based on the fragmentation level of your sqlite database. Compared to peridioc or auto-vaccum this is propably the best solution as (depending on your application) it might only rarely be necessary. You could for example decide to perform on demand VACUUM upon startup when the empty page ratio reaches a certain threshold which you can determine by running

PRAGMA page_count;
PRAGMA freelist_count;

Both PRAGMA statements return a number of pages which together give you a rough guess at the fragmentation ratio. As far as I know there is currently no real measurement for the exact table fragmentation so we have to go with the free list ratio.

Syndicate content Syndicate content