summaryrefslogtreecommitdiff
path: root/src/plugins/external/rygel-external-plugin-factory.vala
blob: cceb77d469570422626107f0b989b7b02b4c4267 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/*
 * Copyright (C) 2009 Zeeshan Ali (Khattak) <zeeshanak@gnome.org>.
 * Copyright (C) 2009,2010 Nokia Corporation.
 *
 * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
 *                               <zeeshan.ali@nokia.com>
 *
 * This file is part of Rygel.
 *
 * Rygel 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 of the License, or
 * (at your option) any later version.
 *
 * Rygel 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 program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

using Rygel;
using Gee;
using FreeDesktop;

private External.PluginFactory plugin_factory;

public void module_init (PluginLoader loader) {
    try {
        plugin_factory = new External.PluginFactory (loader);
    } catch (Error error) {
        critical ("Failed to fetch list of external services: %s\n",
                error.message);
    }
}

public class Rygel.External.PluginFactory {
    private const string SERVICE_PREFIX = "org.gnome.UPnP.MediaServer2.";
    private const string GRILO_UPNP_PREFIX = SERVICE_PREFIX + "grl_upnp";

    DBusObject   dbus_obj;
    PluginLoader loader;
    IconFactory  icon_factory;

    public PluginFactory (PluginLoader loader) throws IOError, DBusError {
        this.icon_factory = new IconFactory ();

        this.dbus_obj = Bus.get_proxy_sync
                                        (BusType.SESSION,
                                         DBUS_SERVICE,
                                         DBUS_OBJECT,
                                         DBusProxyFlags.DO_NOT_LOAD_PROPERTIES);
        this.loader = loader;

        this.load_plugins.begin ();
    }

    private async void load_plugins () throws DBusError {
        var services = yield this.dbus_obj.list_names ();

        foreach (var service in services) {
            if (service.has_prefix (SERVICE_PREFIX) &&
                this.loader.get_plugin_by_name (service) == null) {
                yield this.load_plugin_n_handle_error (service);
            }
        }

        yield this.load_activatable_plugins ();
    }

    private async void load_activatable_plugins () throws DBusError {
        var services = yield this.dbus_obj.list_activatable_names ();

        foreach (var service in services) {
            if (service.has_prefix (SERVICE_PREFIX) &&
                this.loader.get_plugin_by_name (service) == null) {
                yield this.load_plugin_n_handle_error (service);
            }
        }

        this.dbus_obj.name_owner_changed.connect (this.name_owner_changed);
    }

    private void name_owner_changed (DBusObject dbus_obj,
                                     string     name,
                                     string     old_owner,
                                     string     new_owner) {
        var plugin = this.loader.get_plugin_by_name (name);

        if (plugin != null) {
            if (old_owner != "" && new_owner == "") {
                debug ("Service '%s' going down, deactivating it",
                       name);
                plugin.active = false;
            } else if (old_owner == "" && new_owner != "") {
                debug ("Service '%s' up again, activating it", name);
                plugin.active = true;
            }
        } else if (name.has_prefix (SERVICE_PREFIX)) {
            // Ah, new plugin available, lets use it
            this.load_plugin_n_handle_error.begin (name);
        }
    }

    private async void load_plugin_n_handle_error (string service_name) {
        try {
            yield this.load_plugin (service_name);
        } catch (Error error) {
            warning ("Failed to load external plugin '%s': %s",
                     service_name,
                     error.message);
        }
    }

    private async void load_plugin (string service_name)
                                    throws IOError, DBusError {
        if (this.loader.plugin_disabled (service_name)) {
            message ("Plugin '%s' disabled by user, ignoring..", service_name);

            return;
        }

        if (service_name.has_prefix (GRILO_UPNP_PREFIX)) {
            // We don't entertain UPnP sources
            return;
        }

        // org.gnome.UPnP.MediaServer1.NAME => /org/gnome/UPnP/MediaServer1/NAME
        var root_object = "/" + service_name.replace (".", "/");

        // Create proxy to MediaObject iface to get the display name through
        Properties props = yield Bus.get_proxy
                                        (BusType.SESSION,
                                         service_name,
                                         root_object,
                                         DBusProxyFlags.DO_NOT_LOAD_PROPERTIES);

        HashTable<string,Variant> object_props;
        HashTable<string,Variant> container_props;

        object_props = yield props.get_all (MediaObjectProxy.IFACE);
        container_props = yield props.get_all (MediaContainerProxy.IFACE);

        var icon = yield this.icon_factory.create (service_name,
                                                   container_props);

        string title;
        var value = object_props.lookup ("DisplayName");
        if (value != null) {
            title = (string) value;
        } else {
            title = service_name;
        }

        var child_count = (uint) container_props.lookup ("ChildCount");
        var searchable = (bool) container_props.lookup ("Searchable");

        try {
            var plugin = new External.Plugin (service_name,
                                              title,
                                              child_count,
                                              searchable,
                                              root_object,
                                              icon);

            this.loader.add_plugin (plugin);
        } catch (Error err) {
            critical ("Failed to create root container for '%s': %s. " +
                      "Ignoring",
                      service_name,
                      err.message);
        }
    }
}