Hello VALAds, 

i now have my first "useful" tool/code "done", well at least to the
extend that it "works for me" :) (eeepc701, ubuntu 9.04, valac7.2)
(bug reports, suggestions, rant, etc. highly welcome!)

SccPlayer
"Player for shoutcast.com playlist streams"

Give it a file:// or http:// uri of the playlist file.

Yeah, you could call this "re-inventing the wheel", but i did it as an
exercise (and itch:) to learn Vala and GTK, and programming in general.

And yes, i'm one of those hobby hackers who "can't do quality software"
but there's hope -> http://see.stanford.edu/ ! ;P

seen that, been there, next please.
Andre "Osku" Schmidt

ps. no, i don't have anything to do with shoutcast.com, it just happens
to be the "internet radio uri database" i used. and the name _is_, as
the code too, just a "working-title"...

// -*- tab-width: 4 -*-
/*
valac --pkg gtk+-2.0 --pkg gio-2.0 --pkg gstreamer-0.10 --pkg unique-1.0 sccplayer.vala
sccplayer http://yp.shoutcast.com/sbin/tunein-station.pls?id=6687
*/

using Gtk;
using Gst;
using Unique;

public class SccPlayer : Gtk.Object {

enum Command {
  COMMAND_0,
  COMMAND_PLAYURI
}

private Gtk.Window main_window;
private Gtk.Label channel_label;
private Gtk.Label track_label;
private Gtk.Statusbar status_bar;
public dynamic Gst.Element play_src;

private const string uistring = 
"""
<?xml version="1.0"?>
<interface>
  <requires lib="gtk+" version="2.16"/>
  <!-- interface-naming-policy project-wide -->
  <object class="GtkWindow" id="main_window">
    <property name="width_request">320</property>
    <property name="title" translatable="yes">SccPlayer</property>
    <property name="icon_name">media-playback-start</property>
    <signal name="destroy" handler="scc_player_on_main_window_destroy"/>
    <child>
      <object class="GtkVBox" id="vbox1">
        <property name="visible">True</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkVBox" id="vbox2">
            <property name="visible">True</property>
            <property name="border_width">6</property>
            <property name="orientation">vertical</property>
            <child>
              <object class="GtkLabel" id="channel_label">
                <property name="visible">True</property>
                <property name="xalign">0</property>
                <property name="label" translatable="yes">label</property>
                <property name="use_markup">True</property>
                <property name="selectable">True</property>
                <property name="ellipsize">end</property>
              </object>
              <packing>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkLabel" id="track_label">
                <property name="visible">True</property>
                <property name="xalign">0</property>
                <property name="label" translatable="yes">label</property>
                <property name="use_markup">True</property>
                <property name="selectable">True</property>
                <property name="ellipsize">end</property>
              </object>
              <packing>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkStatusbar" id="status_bar">
            <property name="visible">True</property>
            <property name="spacing">2</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>
""";



[CCode (instance_pos = -1)]
public void
/******************************************************************************/
on_main_window_destroy () {
	play_src.set_state (State.PAUSED);
	play_src.set_state (State.NULL);
	Gtk.main_quit();
}



static bool 
/******************************************************************************/
command_line_ok (string[] args) {

	if (args.length <= 1) {
		stdout.printf ("Usage: %s <playlist uri>\n", args[0]);
		return false;
	}

	return true;

}



static void 
/******************************************************************************/
handle_fail_or_user_cancel () {
	stdout.printf ("TODO: handle libunique cancel/fail...\n");
}



public bool 
/******************************************************************************/
do_command_playuri (Unique.MessageData message) {
	status_bar.push (1, "Waiting...");
	this.play (message.get_text());
	return true;
}



public Unique.Response 
/******************************************************************************/
message_received_cb (
	int command, Unique.MessageData message, uint time){
	Unique.Response res;
	
	switch (command) {
	
		case Unique.Command.ACTIVATE:
			res = Unique.Response.OK;
			break;
	
		case Command.COMMAND_PLAYURI:
			if (do_command_playuri (message))
				res = Unique.Response.OK;
			else
				res = Unique.Response.FAIL;
			break;
	
		default:
			res = Unique.Response.OK;
			break;
	}
	return res;
}



public void 
/******************************************************************************/
foreach_tag (Gst.TagList list, string tag) {
	string tag_string;

	switch (tag) {

		case "title":
			list.get_string (tag, out tag_string);

			main_window.title = tag_string.strip();
			tag_string = tag_string.replace ("&", "&amp;");
			track_label.label = "<b>" + tag_string.strip() + "</b>";

			while (events_pending()) main_iteration();
			break;

		default:
			break;
	}
}



public bool 
/******************************************************************************/
bus_callback (Gst.Bus bus, Gst.Message message) {
	GLib.Error err;
	string debug;

	Gst.State oldstate;
	Gst.State newstate;
	Gst.State pendstate;
	Gst.TagList tag_list;

	switch (message.type) {

		case Gst.MessageType.ERROR:
			play_src.set_state (State.NULL);
			message.parse_error (out err, out debug);
			stdout.printf ("Error: %s\n", err.message);
			break;

		case Gst.MessageType.EOS:
			stdout.printf ("eos\n");
			break;

		case Gst.MessageType.STATE_CHANGED:
			string blah;
			message.parse_state_changed 
					(out oldstate, out newstate, out pendstate);

			if (newstate == Gst.State.PLAYING) {
				blah = "Playing...";

			} else if (oldstate == Gst.State.READY
					&& newstate == Gst.State.NULL 
					&& pendstate == Gst.State.VOID_PENDING) {
				blah = "Buffering...";

			} else if (newstate == Gst.State.READY) {
				blah = "Waiting...";

			} else if (newstate == Gst.State.PAUSED) {
				blah = "Paused...";

			} else {
				blah = "%s > %s | %s".printf (
									oldstate.to_string(), newstate.to_string()
									,pendstate.to_string());
			}

			status_bar.push (1, blah);

			break;

		case Gst.MessageType.TAG:
			message.parse_tag (out tag_list);
			tag_list.foreach (foreach_tag);
			break;

		default:
			break;
	}
	return true;
}



public string
/******************************************************************************/
get_channel (string playlist_uri) {
//TODO: should we do "balancing" or at least check if stream full ?

	var file = File.new_for_commandline_arg (playlist_uri);
	string outval = "";

	try {
		var in_stream = new DataInputStream (file.read (null));
		string line;
		string[] keyval;

		while ((line = in_stream.read_line (null, null)) != null) {
			keyval = line.split ("=", 2);

			if (keyval[0] == "Title1") {
				string[] tt = keyval[1].split (")", 2);
				tt[1] = tt[1].replace ("&", "&amp;");
				channel_label.label = tt[1].strip();
				while (events_pending()) main_iteration();
			}

			if (keyval[0] == "File1") {
				outval = keyval[1];
			}
		}
	} catch (IOError e) {
		error ("%s", e.message);
	}

	return outval;
}



public void 
/******************************************************************************/
play (string playlist_uri) {
	main_window.set_title ("::Waiting for Title::");
	channel_label.set_markup ("<i>::Fetching Channel Name::</i>");
	track_label.set_markup ("<b><i>::Waiting for Title::</i></b>");

	play_src.set_state (State.PAUSED);
	play_src.set_state (State.NULL);

	while (events_pending()) main_iteration();

	string foo = get_channel (playlist_uri);
	play_src.uri = foo;

	play_src.set_state (State.PLAYING);
}



public 
/******************************************************************************/
SccPlayer () {

	try {

        var builder = new Builder ();
//		builder.add_from_file ("sccplayer.ui");
		builder.add_from_string (uistring, uistring.len());
		builder.connect_signals (null);

        main_window = builder.get_object ("main_window") as Gtk.Window;
		channel_label = builder.get_object ("channel_label") as Gtk.Label;
		track_label = builder.get_object ("track_label") as Gtk.Label;
		status_bar = builder.get_object ("status_bar") as Gtk.Statusbar;

		main_window.show_all ();

    } catch (Error e) {

        stderr.printf ("Could not load UI: %s\n", e.message);

    }

	Bus bus;

	play_src = ElementFactory.make ("playbin", "play_src");
	bus = play_src.get_bus ();

	bus.add_watch (bus_callback);
	play_src.set_state (State.NULL);
}



public static int 
/******************************************************************************/
main (string[] args) {

	if (!command_line_ok (args)) 
		return 1;

	Unique.App app;
	Gtk.init (ref args);
	Gst.init (ref args);

	app = new Unique.App.with_commands (
									"de.osku.sccplayer", null,
									"playuri", Command.COMMAND_PLAYURI, null);

	if (app.is_running) {
		Unique.Command command;
		Unique.Response response;
		Unique.MessageData message;
		message = new MessageData ();

		command = (Unique.Command) Command.COMMAND_PLAYURI;
		message.set_text (args[1], args[1].len());
		response = app.send_message (command, message);
	
		if (response == Unique.Response.OK) return 0;
		else handle_fail_or_user_cancel ();
	
	} else {
		var player = new SccPlayer ();
		player.play (args[1]);

		app.watch_window (player.main_window);
		app.message_received += player.message_received_cb;

		Gtk.main ();
	}

	return 0;
}

} // class

// vim:tabstop=4:

_______________________________________________
Vala-list mailing list
[email protected]
http://mail.gnome.org/mailman/listinfo/vala-list

Reply via email to