1
/* * -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3
* Copyright (C) 2007 Openismus GmbH
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
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
* The Totem project hereby grant permission for non-gpl compatible GStreamer
20
* plugins to be used and distributed together with GStreamer and Totem. This
21
* permission are above and beyond the permissions granted by the GPL license
22
* Totem is covered by.
24
* See license_change file for details.
33
#include <glib-object.h>
34
#include <glib/gi18n-lib.h>
37
#include <libepc/consumer.h>
38
#include <libepc/enums.h>
39
#include <libepc/publisher.h>
40
#include <libepc/service-monitor.h>
41
#include <libepc-ui/progress-window.h>
43
#include <libpeas/peas-extension-base.h>
44
#include <libpeas/peas-object-module.h>
45
#include <libpeas/peas-activatable.h>
46
#include <libpeas-gtk/peas-gtk-configurable.h>
48
#include "totem-plugin.h"
49
#include "totem-private.h"
50
#include "totem-dirs.h"
53
#define TOTEM_TYPE_PUBLISH_PLUGIN (totem_publish_plugin_get_type ())
54
#define TOTEM_PUBLISH_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), TOTEM_TYPE_PUBLISH_PLUGIN, TotemPublishPlugin))
55
#define TOTEM_PUBLISH_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), TOTEM_TYPE_PUBLISH_PLUGIN, TotemPublishPluginClass))
56
#define TOTEM_IS_PUBLISH_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), TOTEM_TYPE_PUBLISH_PLUGIN))
57
#define TOTEM_IS_PUBLISH_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), TOTEM_TYPE_PUBLISH_PLUGIN))
58
#define TOTEM_PUBLISH_PLUGIN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TOTEM_TYPE_PUBLISH_PLUGIN, TotemPublishPluginClass))
60
#define TOTEM_PUBLISH_SCHEMA TOTEM_GSETTINGS_SCHEMA ".plugins.publish"
76
EpcPublisher *publisher;
77
EpcServiceMonitor *monitor;
78
GtkListStore *neighbours;
84
gulong item_removed_id;
85
} TotemPublishPluginPrivate;
87
TOTEM_PLUGIN_REGISTER_CONFIGURABLE (TOTEM_TYPE_PUBLISH_PLUGIN, TotemPublishPlugin, totem_publish_plugin);
89
void totem_publish_plugin_service_name_entry_changed_cb (GtkEntry *entry,
90
TotemPublishPlugin *self);
91
void totem_publish_plugin_encryption_button_toggled_cb (GtkToggleButton *button,
92
TotemPublishPlugin *self);
93
void totem_publish_plugin_dialog_response_cb (GtkDialog *dialog,
96
void totem_publish_plugin_neighbours_list_row_activated_cb (GtkTreeView *view,
98
GtkTreeViewColumn *column,
101
G_LOCK_DEFINE_STATIC(totem_publish_plugin_lock);
104
totem_publish_plugin_name_changed_cb (GSettings *settings, const gchar *key, TotemPublishPlugin *self)
106
gchar *pattern, *name;
108
pattern = g_settings_get_string (settings, "name-format");
109
name = epc_publisher_expand_name (pattern, NULL);
112
epc_publisher_set_service_name (self->priv->publisher, name);
118
totem_publish_plugin_service_name_entry_changed_cb (GtkEntry *entry, TotemPublishPlugin *self)
120
g_settings_set_string (self->priv->gsettings, "name-format", gtk_entry_get_text (entry));
124
totem_publish_plugin_encryption_button_toggled_cb (GtkToggleButton *button, TotemPublishPlugin *self)
126
g_settings_set_string (self->priv->gsettings, "protocol", gtk_toggle_button_get_active (button) ? "https" : "http");
130
totem_publish_plugin_protocol_changed_cb (GSettings *settings, const gchar *key, TotemPublishPlugin *self)
132
gchar *protocol_name;
133
EpcProtocol protocol;
134
GError *error = NULL;
136
protocol_name = g_settings_get_string (settings, "protocol");
137
protocol = epc_protocol_from_name (protocol_name, EPC_PROTOCOL_HTTPS);
138
g_free (protocol_name);
140
epc_publisher_quit (self->priv->publisher);
141
epc_publisher_set_protocol (self->priv->publisher, protocol);
142
epc_publisher_run_async (self->priv->publisher, &error);
145
g_warning ("%s: %s", G_STRFUNC, error->message);
146
g_error_free (error);
151
totem_publish_plugin_build_key (const gchar *filename)
153
return g_strconcat ("media/", filename, NULL);
157
totem_publish_plugin_playlist_cb (EpcPublisher *publisher,
161
TotemPublishPlugin *self = TOTEM_PUBLISH_PLUGIN (data);
162
GString *buffer = g_string_new (NULL);
163
EpcContents *contents = NULL;
167
G_LOCK (totem_publish_plugin_lock);
169
g_string_append_printf (buffer,
170
"[playlist]\nNumberOfEntries=%d\n",
171
g_slist_length (self->priv->playlist));
173
for (iter = self->priv->playlist, i = 1; iter; iter = iter->next, ++i) {
174
gchar *file_key = iter->data;
177
uri = epc_publisher_get_uri (publisher, file_key, NULL);
179
g_string_append_printf (buffer,
180
"File%d=%s\nTitle%d=%s\n",
181
i, uri, i, file_key + 6);
186
G_UNLOCK (totem_publish_plugin_lock);
188
contents = epc_contents_new ("audio/x-scpls",
189
buffer->str, buffer->len,
192
g_string_free (buffer, FALSE);
198
totem_publish_plugin_stream_cb (EpcContents *contents,
203
GInputStream *stream = data;
206
g_return_val_if_fail (NULL != contents, FALSE);
207
g_return_val_if_fail (NULL != length, FALSE);
209
if (NULL == data || *length < (gsize)size) {
210
*length = MAX (*length, (gsize)size);
214
size = g_input_stream_read (stream, buffer, size, NULL, NULL);
216
g_input_stream_close (stream, NULL, NULL);
226
totem_publish_plugin_media_cb (EpcPublisher *publisher,
230
GFileInputStream *stream;
231
const gchar *url = data;
234
file = g_file_new_for_uri (url);
235
stream = g_file_read (file, NULL, NULL);
236
g_object_unref (file);
239
EpcContents *output = epc_contents_stream_new (
240
NULL, totem_publish_plugin_stream_cb,
241
stream, g_object_unref);
250
totem_publish_plugin_rebuild_playlist_cb (TotemPlaylist *playlist,
251
const gchar *filename,
255
TotemPublishPlugin *self = TOTEM_PUBLISH_PLUGIN (data);
256
gchar *key = totem_publish_plugin_build_key (filename);
257
self->priv->playlist = g_slist_prepend (self->priv->playlist, key);
261
totem_publish_plugin_playlist_changed_cb (TotemPlaylist *playlist,
264
TotemPublishPlugin *self = TOTEM_PUBLISH_PLUGIN (data);
266
G_LOCK (totem_publish_plugin_lock);
268
g_slist_foreach (self->priv->playlist, (GFunc) g_free, NULL);
269
g_slist_free (self->priv->playlist);
270
self->priv->playlist = NULL;
272
totem_playlist_foreach (playlist,
273
totem_publish_plugin_rebuild_playlist_cb,
276
self->priv->playlist = g_slist_reverse (self->priv->playlist);
278
G_UNLOCK (totem_publish_plugin_lock);
282
totem_publish_plugin_playlist_item_added_cb (TotemPlaylist *playlist,
283
const gchar *filename,
287
TotemPublishPlugin *self = TOTEM_PUBLISH_PLUGIN (data);
288
gchar *key = totem_publish_plugin_build_key (filename);
290
epc_publisher_add_handler (self->priv->publisher, key,
291
totem_publish_plugin_media_cb,
292
g_strdup (url), g_free);
299
totem_publish_plugin_playlist_item_removed_cb (TotemPlaylist *playlist,
300
const gchar *filename,
304
TotemPublishPlugin *self = TOTEM_PUBLISH_PLUGIN (data);
305
gchar *key = totem_publish_plugin_build_key (filename);
306
epc_publisher_remove (self->priv->publisher, key);
311
totem_publish_plugin_service_found_cb (TotemPublishPlugin *self,
313
EpcServiceInfo *info)
317
gtk_list_store_append (self->priv->neighbours, &iter);
318
gtk_list_store_set (self->priv->neighbours, &iter, NAME_COLUMN, name,
324
totem_publish_plugin_service_removed_cb (TotemPublishPlugin *self,
328
GtkTreeModel *model = GTK_TREE_MODEL (self->priv->neighbours);
331
if (gtk_tree_model_get_iter_first (model, &iter)) {
332
GSList *path_list = NULL, *path_iter;
337
gtk_tree_model_get (model, &iter, NAME_COLUMN, &stored, -1);
339
if (g_str_equal (stored, name)) {
340
path = gtk_tree_model_get_path (model, &iter);
341
path_list = g_slist_prepend (path_list, path);
343
} while (gtk_tree_model_iter_next (model, &iter));
345
for (path_iter = path_list; path_iter; path_iter = path_iter->next) {
346
path = path_iter->data;
348
if (gtk_tree_model_get_iter (model, &iter, path))
349
gtk_list_store_remove (self->priv->neighbours, &iter);
351
gtk_tree_path_free (path);
354
g_slist_free (path_list);
359
totem_publish_plugin_scanning_done_cb (TotemPublishPlugin *self,
362
if (self->priv->scanning_id) {
363
g_source_remove (self->priv->scanning_id);
364
self->priv->scanning_id = 0;
367
if (self->priv->scanning)
368
gtk_widget_hide (self->priv->scanning);
372
totem_publish_plugin_load_playlist (TotemPublishPlugin *self,
373
const EpcServiceInfo *info)
375
EpcConsumer *consumer = epc_consumer_new (info);
376
GKeyFile *keyfile = g_key_file_new ();
377
gchar *contents = NULL;
378
GError *error = NULL;
381
contents = epc_consumer_lookup (consumer, "playlist.pls", &length, &error);
383
if (contents && g_key_file_load_from_data (keyfile, contents, length, G_KEY_FILE_NONE, &error)) {
385
GList *mrl_list = NULL;
387
/* returns zero in case of errors */
388
n_entries = g_key_file_get_integer (keyfile, "playlist", "NumberOfEntries", &error);
394
ev_sidebar_set_current_page (EV_SIDEBAR (self->priv->totem->sidebar), "playlist"); */
395
totem_playlist_clear (self->priv->totem->playlist);
397
for (i = 0; i < n_entries; ++i) {
398
gchar *key, *mrl, *title;
400
key = g_strdup_printf ("File%d", i + 1);
401
mrl = g_key_file_get_string (keyfile, "playlist", key, NULL);
404
key = g_strdup_printf ("Title%d", i + 1);
405
title = g_key_file_get_string (keyfile, "playlist", key, NULL);
409
mrl_list = g_list_prepend (mrl_list, totem_playlist_mrl_data_new (mrl, title));
415
/* Add the MRLs to the playlist asynchronously and in order */
416
if (mrl_list != NULL)
417
totem_playlist_add_mrls (self->priv->totem->playlist, g_list_reverse (mrl_list), FALSE, NULL, NULL, NULL);
422
g_warning ("Cannot load playlist: %s", error->message);
423
g_error_free (error);
426
g_key_file_free (keyfile);
429
g_object_unref (consumer);
433
totem_publish_plugin_neighbours_list_row_activated_cb (GtkTreeView *view,
435
GtkTreeViewColumn *column,
438
TotemPublishPlugin *self = TOTEM_PUBLISH_PLUGIN (data);
439
EpcServiceInfo *info = NULL;
442
if (gtk_tree_model_get_iter (GTK_TREE_MODEL (self->priv->neighbours), &iter, path)) {
443
gtk_tree_model_get (GTK_TREE_MODEL (self->priv->neighbours),
444
&iter, INFO_COLUMN, &info, -1);
445
totem_publish_plugin_load_playlist (self, info);
446
epc_service_info_unref (info);
451
totem_publish_plugin_scanning_cb (gpointer data)
453
gtk_progress_bar_pulse (GTK_PROGRESS_BAR (data));
458
totem_publish_plugin_create_neigbours_page (TotemPublishPlugin *self, GtkBuilder *builder)
460
GtkWidget *page, *list;
462
page = GTK_WIDGET (gtk_builder_get_object (builder, "neighbours-page"));
463
list = GTK_WIDGET (gtk_builder_get_object (builder, "neighbours-list"));
465
self->priv->scanning = GTK_WIDGET (gtk_builder_get_object (builder, "neighbours-scanning"));
466
self->priv->scanning_id = g_timeout_add (100, totem_publish_plugin_scanning_cb, self->priv->scanning);
468
g_signal_connect_swapped (self->priv->monitor, "service-found",
469
G_CALLBACK (totem_publish_plugin_service_found_cb),
471
g_signal_connect_swapped (self->priv->monitor, "service-removed",
472
G_CALLBACK (totem_publish_plugin_service_removed_cb),
474
g_signal_connect_swapped (self->priv->monitor, "scanning-done",
475
G_CALLBACK (totem_publish_plugin_scanning_done_cb),
478
self->priv->neighbours = gtk_list_store_new (LAST_COLUMN, G_TYPE_STRING, EPC_TYPE_SERVICE_INFO);
480
gtk_tree_view_set_model (GTK_TREE_VIEW (list),
481
GTK_TREE_MODEL (self->priv->neighbours));
483
gtk_tree_view_append_column (GTK_TREE_VIEW (list),
484
gtk_tree_view_column_new_with_attributes (
485
NULL, gtk_cell_renderer_text_new (),
486
"text", NAME_COLUMN, NULL));
489
gtk_widget_unparent (page);
495
impl_activate (PeasActivatable *plugin)
497
TotemPublishPlugin *self = TOTEM_PUBLISH_PLUGIN (plugin);
498
TotemPublishPluginPrivate *priv = self->priv;
499
EpcProtocol protocol = EPC_PROTOCOL_HTTPS;
502
GError *internal_error = NULL;
504
gchar *protocol_name;
506
gchar *service_pattern;
509
g_return_if_fail (NULL == priv->publisher);
510
g_return_if_fail (NULL == priv->totem);
512
G_LOCK (totem_publish_plugin_lock);
514
priv->totem = g_object_ref (g_object_get_data (G_OBJECT (plugin), "object"));
516
window = totem_get_main_window (priv->totem);
517
builder = totem_plugin_load_interface ("publish", "publish-plugin.ui", TRUE, window, self);
518
epc_progress_window_install (window);
519
g_object_unref (window);
521
priv->gsettings = g_settings_new (TOTEM_PUBLISH_SCHEMA);
523
protocol_name = g_settings_get_string (priv->gsettings, "protocol");
524
service_pattern = g_settings_get_string (priv->gsettings, "name-format");
526
if (*protocol_name == '\0') {
527
protocol_name = g_strdup ("http");
528
g_settings_set_string (priv->gsettings, "protocol", protocol_name);
531
if (*service_pattern == '\0') {
532
service_pattern = g_strdup ("%a of %u on %h");
533
g_settings_set_string (priv->gsettings, "name-format", service_pattern);
536
g_signal_connect (priv->gsettings, "changed::name", (GCallback) totem_publish_plugin_name_changed_cb, self);
537
g_signal_connect (priv->gsettings, "changed::protocol", (GCallback) totem_publish_plugin_protocol_changed_cb, self);
539
protocol = epc_protocol_from_name (protocol_name, EPC_PROTOCOL_HTTPS);
540
service_name = epc_publisher_expand_name (service_pattern, &internal_error);
541
g_free (service_pattern);
543
if (internal_error) {
544
g_warning ("%s: %s", G_STRFUNC, internal_error->message);
545
g_clear_error (&internal_error);
548
priv->monitor = epc_service_monitor_new ("totem", NULL, EPC_PROTOCOL_UNKNOWN);
549
epc_service_monitor_set_skip_our_own (priv->monitor, TRUE);
551
/* Translators: computers on the local network which are publishing their playlists over the network */
552
totem_add_sidebar_page (priv->totem, "neighbours", _("Neighbors"),
553
totem_publish_plugin_create_neigbours_page (self, builder));
554
g_object_unref (builder);
556
priv->publisher = epc_publisher_new (service_name, "totem", NULL);
557
epc_publisher_set_protocol (priv->publisher, protocol);
559
g_free (protocol_name);
560
g_free (service_name);
562
epc_publisher_add_handler (priv->publisher, "playlist.pls",
563
totem_publish_plugin_playlist_cb,
565
epc_publisher_add_bookmark (priv->publisher, "playlist.pls", NULL);
567
priv->item_added_id = g_signal_connect (priv->totem->playlist, "changed",
568
G_CALLBACK (totem_publish_plugin_playlist_changed_cb), self);
569
priv->item_added_id = g_signal_connect (priv->totem->playlist, "item-added",
570
G_CALLBACK (totem_publish_plugin_playlist_item_added_cb), self);
571
priv->item_removed_id = g_signal_connect (priv->totem->playlist, "item-removed",
572
G_CALLBACK (totem_publish_plugin_playlist_item_removed_cb), self);
574
G_UNLOCK (totem_publish_plugin_lock);
576
totem_playlist_foreach (priv->totem->playlist,
577
totem_publish_plugin_playlist_item_added_cb, self);
579
totem_publish_plugin_playlist_changed_cb (priv->totem->playlist, self);
581
epc_publisher_run_async (priv->publisher, NULL);
587
impl_deactivate (PeasActivatable *plugin)
589
TotemPublishPlugin *self = TOTEM_PUBLISH_PLUGIN (plugin);
590
TotemPublishPluginPrivate *priv = self->priv;
591
TotemPlaylist *playlist = NULL;
593
G_LOCK (totem_publish_plugin_lock);
596
playlist = priv->totem->playlist;
598
if (priv->scanning_id) {
599
g_source_remove (priv->scanning_id);
600
priv->scanning_id = 0;
603
if (playlist && priv->item_added_id) {
604
g_signal_handler_disconnect (playlist, priv->item_added_id);
605
priv->item_added_id = 0;
608
if (playlist && priv->item_removed_id) {
609
g_signal_handler_disconnect (playlist, priv->item_removed_id);
610
priv->item_removed_id = 0;
614
g_object_unref (priv->monitor);
615
priv->monitor = NULL;
618
if (priv->publisher) {
619
epc_publisher_quit (priv->publisher);
620
g_object_unref (priv->publisher);
621
priv->publisher = NULL;
624
if (priv->gsettings != NULL)
625
g_object_unref (priv->gsettings);
626
priv->gsettings = NULL;
629
totem_remove_sidebar_page (priv->totem, "neighbours");
631
g_object_unref (priv->totem);
635
if (priv->settings) {
636
gtk_widget_destroy (priv->settings);
637
priv->settings = NULL;
640
if (priv->playlist) {
641
g_slist_foreach (priv->playlist, (GFunc) g_free, NULL);
642
g_slist_free (priv->playlist);
643
priv->playlist = NULL;
646
G_UNLOCK (totem_publish_plugin_lock);
648
priv->scanning = NULL;
652
impl_create_configure_widget (PeasGtkConfigurable *configurable)
654
TotemPublishPlugin *self = TOTEM_PUBLISH_PLUGIN (configurable);
655
gchar *service_name, *protocol_name;
658
EpcProtocol protocol;
661
/* This function has to be independent of the rest of the plugin, as it's executed under a different plugin instance.
662
* FIXME: bgo#624073 */
663
builder = totem_plugin_load_interface ("publish", "publish-plugin.ui", TRUE, NULL, self);
665
settings = g_settings_new (TOTEM_PUBLISH_SCHEMA);
666
service_name = g_settings_get_string (settings, "name-format");
667
protocol_name = g_settings_get_string (settings, "protocol");
668
g_object_unref (settings);
670
protocol = epc_protocol_from_name (protocol_name, EPC_PROTOCOL_HTTPS);
671
g_free (protocol_name);
673
widget = g_object_ref (gtk_builder_get_object (builder, "publish-settings-vbox"));
675
gtk_entry_set_text (GTK_ENTRY (gtk_builder_get_object (builder, "service-name-entry")), service_name);
676
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, "encryption-button")), EPC_PROTOCOL_HTTPS == protocol);
678
g_free (service_name);