1 /* Gnome Music Player Client Shoutcast Browser plugin (gmpc-shoutcast-browser)
2 * Copyright (C) 2010 Anton Romanov <theli.ua@gmail.com>
3 * Project homepage: http://gmpc.wikia.com/
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 public class ShoutBrowser
: Gmpc
.Plugin
.Base
, Gmpc
.Plugin
.BrowserIface
/*, Gmpc.Plugin.PreferencesIface*/ {
25 public const int[] pversion
= {0,0,1};
27 public override void set_enabled(bool enabled
) {
28 //var old = this.get_enabled();
29 if(this
.get_name() != null)
30 Gmpc
.config
.set_int(this
.get_name(), "enabled", (int)enabled
);
33 public override unowned
string get_name() {
34 return "Shoutcast.com browser";
37 public override unowned
int[] get_version() {
41 /* Set the plugin as an internal one and of type pl_browser */
42 this
.plugin_type
= 2; //Browser
43 if(this
.get_enabled()) {
47 if(this
.get_enabled()) {
52 * Browser Interface bindings
54 public void browser_add (Gtk
.Widget category_tree
)
57 Gtk
.TreeView tree
= (Gtk
.TreeView
)category_tree
;
58 Gtk
.ListStore store
= (Gtk
.ListStore
)tree
.get_model();
59 Gmpc
.Browser
.insert(out iter
, config
.get_int_with_default(this
.get_name(), "position", 100));
60 store
.set(iter
, 0, this
.id
, 1, this
.get_name(), 3, "gmpc-metabrowser");
61 /* Create a row reference */
63 Gtk.ListStore model = tree.get_model();
64 this.rref = new Gtk.TreeRowReference(model, model.get_path(iter));
67 public void browser_selected (Gtk
.Container container
)
72 container
.add(this
.paned
);
74 /* update if non selected */
75 genre
= browser_get_selected_genre();
82 private string?
browser_get_selected_genre()
85 var sel
= this
.tree_genre
.get_selection();
86 Gtk
.TreeModel model
= null;//this.model_genre;
87 if(sel
.get_selected(out model
, out iter
))
90 model
.get(iter
, 0,out genre
, -1);
96 private string?
browser_get_selected_id()
99 var sel
= this
.tree_station
.get_selection();
100 Gtk
.TreeModel model
= null;//this.model_genre;
101 if(sel
.get_selected(out model
, out iter
))
104 model
.get(iter
, 6,out id
, -1);
109 private bool downloading
= false;
110 private void genre_box_clear()
112 this
.model_genre
.clear ();
114 private void genre_callback(Gmpc
.AsyncDownload
.Handle handle
, Gmpc
.AsyncDownload
.Status status
){
115 if (status
!= Gmpc
.AsyncDownload
.Status
.DONE
)
118 Xml
.Doc
* doc
= Xml
.Parser
.parse_doc((string)handle
.get_data());
122 // Get the root node. notice the dereferencing operator -> instead of .
123 Xml
.Node
* node
= doc
->get_root_element ();
125 // Free the document manually before returning
130 // Loop over the node's children
131 for (Xml
.Node
* xmliter
= node
->children
; xmliter
!= null; xmliter
= xmliter
->next
) {
132 // Spaces between tags are also nodes, discard them
133 if (xmliter
->type
!= ElementType
.ELEMENT_NODE
) {
136 string genre_name
= "";
137 for (Xml
.Attr
* prop
= xmliter
->properties
; prop
!= null; prop
= prop
->next
) {
138 if (prop
->name
== "name")
139 genre_name
= prop
->children
->content
;
141 if (genre_name
!= "")
143 this
.model_genre
.append (out iter
);
144 this
.model_genre
.set (iter
, 0, genre_name
);
151 private bool tree_right_menu(Gdk
.EventButton event
)
153 if(event
.button
== 3)
155 var menu
= new Gtk
.Menu();
156 var item
= new Gtk
.ImageMenuItem
.from_stock("gtk-media-play",null);
157 item
.activate
+= station_browser_play_clicked
;
160 item
= new Gtk
.ImageMenuItem
.from_stock("gtk-add",null);
161 item
.activate
+= station_browser_add_clicked
;
164 //item = new Gtk.ImageMenuItem.with_mnemonic("_Replace");
165 //item.set_image(new Gtk.Image.from_stock("gtk-redo", Gtk.IconSize.MENU));
166 //item.activate += replace_clicked;
169 menu
.popup(null, null, null, event
.button
, event
.time
);
175 private void station_browser_play_clicked(Gtk
.ImageMenuItem item
)
177 MPD
.PlayQueue
.clear(server
);
178 station_browser_add_clicked(item
);
179 MPD
.Player
.play(server
);
181 private void station_browser_add_clicked(Gtk
.ImageMenuItem item
)
183 var id
= browser_get_selected_id();
185 Gmpc
.AsyncDownload
.download("http://shoutcast.com/sbin/tunein-station.pls?id=" + id
,playlist_callback
);
187 private void playlist_callback(Gmpc
.AsyncDownload
.Handle handle
, Gmpc
.AsyncDownload
.Status status
){
188 //MPD.PlayQueue.add_song(server,"http://91.121.201.88:8000");
190 if (status
!= Gmpc
.AsyncDownload
.Status
.DONE
)return;
191 var re
= new
Regex ("File[0-9]+=(.*)", 0,0);
193 if (re
.match((string)handle
.get_data(),0,out mi
))
194 do MPD
.PlayQueue
.add_song(server
,mi
.fetch (1)); while (mi
.next());
196 private void genre_box_update()
201 Gmpc
.AsyncDownload
.download("http://www.shoutcast.com/sbin/newxml.phtml",genre_callback
);
205 private void station_callback(Gmpc
.AsyncDownload
.Handle handle
, Gmpc
.AsyncDownload
.Status status
){
206 if (status
!= Gmpc
.AsyncDownload
.Status
.DONE
)
208 /* Disconnecting/reconnecting speeds things up */
209 this
.tree_station
.model
= null;
211 Xml
.Doc
* doc
= Xml
.Parser
.parse_doc((string)handle
.get_data());
215 // Get the root node. notice the dereferencing operator -> instead of .
216 Xml
.Node
* node
= doc
->get_root_element ();
218 // Free the document manually before returning
223 // Loop over the node's children
224 for (Xml
.Node
* xmliter
= node
->children
; xmliter
!= null; xmliter
= xmliter
->next
) {
225 // Spaces between tags are also nodes, discard them
226 if (xmliter
->type
!= ElementType
.ELEMENT_NODE
) {
229 string station_name
= "";
230 string genre_name
= "";
236 for (Xml
.Attr
* prop
= xmliter
->properties
; prop
!= null; prop
= prop
->next
) {
237 if (prop
->name
== "name")
238 station_name
= prop
->children
->content
.replace(" - [SHOUTcast.com]","");
239 else if(prop
->name
== "genre")
240 genre_name
= prop
->children
->content
;
241 else if(prop
->name
== "br")
242 br
= prop
->children
->content
;
243 else if(prop
->name
== "mt")
244 mt
= prop
->children
->content
;
245 else if(prop
->name
== "ct")
246 ct
= prop
->children
->content
;
247 else if(prop
->name
== "id")
248 id
= prop
->children
->content
;
249 else if(prop
->name
== "lc")
250 lc
= prop
->children
->content
;
253 if (station_name
!= "")
255 this
.model_station
.append (out iter
);
256 this
.model_station
.set (iter
, 0, station_name
, 1, ct
,2,lc
,3,genre_name
, 4, br
, 5, mt
,6,id
);
259 /* Disconnecting/reconnecting speeds things up */
260 this
.tree_station
.set_model(this
.model_station
);
261 tree_station
.sensitive
= true;
262 tree_genre
.sensitive
= true;
268 private void browser_genre_changed(Gtk
.TreeSelection sel
)
272 string genre
= browser_get_selected_genre();
276 tree_station
.sensitive
= false;
277 tree_genre
.sensitive
= false;
278 this
.model_station
.clear ();
279 Gmpc
.AsyncDownload
.download("http://www.shoutcast.com/sbin/newxml.phtml?genre=" + genre
,station_callback
);
291 private Gtk
.TreeView tree_genre
= null;
292 private Gtk
.ListStore model_genre
= null;
293 private Gtk
.TreeModelFilter model_filter_genre
= null;
294 private Gtk
.Entry genre_filter_entry
= null;
297 private Gtk
.TreeView tree_station
= null;
298 private Gtk
.ListStore model_station
= null;
299 private Gtk
.TreeModelFilter model_filter_station
= null;
300 private Gtk
.Entry station_filter_entry
= null;
303 private Gtk
.Paned paned
= null;
304 private void browser_init()
306 if(this
.paned
== null)
308 this
.paned
= new Gtk
.HPaned();
309 paned_size_group
.add_paned(this
.paned
);
313 this
.genre_filter_entry
= new Gtk
.Entry();
314 this
.genre_filter_entry
.set_no_show_all(true);
315 //this.genre_filter_entry.changed += browser_genre_entry_changed;
316 this
.model_genre
= new Gtk
.ListStore(1,typeof (string));
317 this
.model_filter_genre
= new Gtk
.TreeModelFilter(this
.model_genre
, null);
318 //this.model_filter_genre.set_visible_func(visible_func_genre);
319 this
.tree_genre
= new Gtk
.TreeView
.with_model(this
.model_filter_genre
);
320 this
.tree_genre
.rules_hint
= true;
321 var box
= new Gtk
.VBox(false, 2);
322 box
.pack_start(this
.genre_filter_entry
, false, false, 0);
323 var sw
= new Gtk
.ScrolledWindow(null, null);
324 sw
.set_policy(Gtk
.PolicyType
.AUTOMATIC
, Gtk
.PolicyType
.AUTOMATIC
);
325 sw
.set_shadow_type(Gtk
.ShadowType
.ETCHED_IN
);
326 box
.pack_start(sw
, true, true, 0);
327 this
.tree_genre
.set_enable_search(false);
328 //this.tree_artist.button_press_event+=browser_button_press_event;
329 //this.tree_artist.button_release_event+=artist_browser_button_release_event;
330 //this.tree_artist.key_press_event += browser_artist_key_press_event;
332 this
.tree_genre
.insert_column_with_attributes (-1, "Genre", new Gtk
.CellRendererText (), "text", 0);
333 this
.tree_genre
.get_selection().changed
+= browser_genre_changed
;
334 this
.paned
.add1(box
);
337 this
.station_filter_entry
= new Gtk
.Entry();
338 this
.station_filter_entry
.set_no_show_all(true);
339 //this.station_filter_entry.changed += browser_station_entry_changed;
340 this
.model_station
= new Gtk
.ListStore(7,typeof (string),typeof (string),
341 typeof (string),typeof (string),typeof (string),typeof (string),typeof (string));
342 this
.model_filter_station
= new Gtk
.TreeModelFilter(this
.model_station
, null);
343 //this.model_filter_station.set_visible_func(visible_func_station);
344 this
.tree_station
= new Gtk
.TreeView
.with_model(this
.model_filter_station
);
345 this
.tree_station
.rules_hint
= true;
346 box
= new Gtk
.VBox(false, 2);
347 box
.pack_start(this
.station_filter_entry
, false, false, 0);
348 sw
= new Gtk
.ScrolledWindow(null, null);
349 sw
.set_policy(Gtk
.PolicyType
.AUTOMATIC
, Gtk
.PolicyType
.AUTOMATIC
);
350 sw
.set_shadow_type(Gtk
.ShadowType
.ETCHED_IN
);
351 box
.pack_start(sw
, true, true, 0);
352 this
.tree_station
.set_enable_search(false);
353 //this.tree_artist.button_press_event+=browser_button_press_event;
354 //this.tree_artist.button_release_event+=artist_browser_button_release_event;
355 //this.tree_artist.key_press_event += browser_artist_key_press_event;
356 sw
.add(tree_station
);
357 this
.tree_station
.insert_column_with_attributes (-1, "Title", new Gtk
.CellRendererText (), "text", 0);
358 this
.tree_station
.insert_column_with_attributes (-1, "Current Track", new Gtk
.CellRendererText (), "text", 1);
359 this
.tree_station
.insert_column_with_attributes (-1, "Listeners", new Gtk
.CellRendererText (), "text", 2);
360 this
.tree_station
.insert_column_with_attributes (-1, "Genre", new Gtk
.CellRendererText (), "text", 3);
361 this
.tree_station
.insert_column_with_attributes (-1, "Bitrate", new Gtk
.CellRendererText (), "text", 4);
362 this
.tree_station
.insert_column_with_attributes (-1, "Mimetype", new Gtk
.CellRendererText (), "text", 5);
363 this
.tree_station
.insert_column_with_attributes (-1, "ID", new Gtk
.CellRendererText (), "text", 6);
365 //this.tree_station.get_selection().changed += browser_station_changed;
366 this
.tree_station
.button_release_event
+= tree_right_menu
;
368 this
.paned
.add2(box
);
373 this
.paned
.show_all();
377 private bool selected
= false;
378 public void browser_unselected(Gtk
.Container container
)
380 this
.selected
= false;
381 container
.remove(this
.paned
);
385 public Type
plugin_get_type () {
386 return typeof (ShoutBrowser
);