Bläddra i källkod

Add dbus media source plugin

Change-Id: I5eb65ec17afb299e231d061c48ba5cfd4eb1ecb6
Reviewed-on: http://gerrit.dmdirc.com/280
Reviewed-by: Shane Mc Cormack <shane@dmdirc.com>
Tested-by: Gregory Holmes <greboid@dmdirc.com>
tags/0.6.3
Chris Smith 14 år sedan
förälder
incheckning
876f9874e3

+ 175
- 0
src/com/dmdirc/addons/mediasource_dbus/BansheeSource.java Visa fil

@@ -0,0 +1,175 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.mediasource_dbus;
24
+
25
+import com.dmdirc.addons.nowplaying.MediaSource;
26
+import com.dmdirc.addons.nowplaying.MediaSourceState;
27
+
28
+import java.util.List;
29
+import java.util.Map;
30
+
31
+/**
32
+ * A media source for Banshee.
33
+ *
34
+ * @author chris
35
+ */
36
+public class BansheeSource implements MediaSource {
37
+
38
+    /** The dbus service name. */
39
+    private static final String SERVICE = "org.bansheeproject.Banshee";
40
+    /** The dbus interface name. */
41
+    private static final String IFACE = "/org/bansheeproject/Banshee/PlayerEngine";
42
+    /** The method to get the current state. */
43
+    private static final String STATE = "org.bansheeproject.Banshee.PlayerEngine.GetCurrentState";
44
+    /** The method to get the current track. */
45
+    private static final String TRACK = "org.bansheeproject.Banshee.PlayerEngine.GetCurrentTrack";
46
+    /** The method to get the current position. */
47
+    private static final String POSITION = "org.bansheeproject.Banshee.PlayerEngine.GetPosition";
48
+
49
+    /** The media source that owns this source. */
50
+    private final DBusMediaSource source;
51
+    /** A cache of track information. */
52
+    private Map<String, String> trackInfo;
53
+
54
+    public BansheeSource(final DBusMediaSource source) {
55
+        this.source = source;
56
+    }
57
+
58
+    /** {@inheritDoc} */
59
+    @Override
60
+    public MediaSourceState getState() {
61
+        final List<String> res = source.doDBusCall(SERVICE, IFACE, STATE);
62
+        
63
+        if (res.isEmpty()) {
64
+            trackInfo = null;
65
+            return MediaSourceState.CLOSED;
66
+        } else if ("playing".equals(res.get(0))) {
67
+            trackInfo = getTrackInfo();
68
+            return MediaSourceState.PLAYING;
69
+        } else if ("paused".equals(res.get(0))) {
70
+            trackInfo = getTrackInfo();
71
+            return MediaSourceState.PAUSED;
72
+        } else if ("idle".equals(res.get(0))) {
73
+            trackInfo = getTrackInfo();
74
+            return MediaSourceState.STOPPED;
75
+        } else {
76
+            trackInfo = null;
77
+            return MediaSourceState.NOTKNOWN;
78
+        }
79
+    }
80
+
81
+    /**
82
+     * Retrieves a map of track information from the dbus service.
83
+     *
84
+     * @return A map of track information
85
+     */
86
+    protected Map<String, String> getTrackInfo() {
87
+        return DBusMediaSource.parseDictionary(source.doDBusCall(SERVICE, IFACE, TRACK));
88
+    }
89
+
90
+    /** {@inheritDoc} */
91
+    @Override
92
+    public String getAppName() {
93
+        return "Banshee";
94
+    }
95
+
96
+    /** {@inheritDoc} */
97
+    @Override
98
+    public String getArtist() {
99
+        return trackInfo == null ? "Unknown" : trackInfo.get("artist");
100
+    }
101
+
102
+    /** {@inheritDoc} */
103
+    @Override
104
+    public String getTitle() {
105
+        return trackInfo == null ? "Unknown" : trackInfo.get("name");
106
+    }
107
+
108
+    /** {@inheritDoc} */
109
+    @Override
110
+    public String getAlbum() {
111
+        return trackInfo == null ? "Unknown" : trackInfo.get("album");
112
+    }
113
+
114
+    /** {@inheritDoc} */
115
+    @Override
116
+    public String getLength() {
117
+        return trackInfo == null ? "Unknown" : duration((long)
118
+                Double.parseDouble(trackInfo.get("length")));
119
+    }
120
+
121
+    /** {@inheritDoc} */
122
+    @Override
123
+    public String getTime() {
124
+        return duration((long) Double.parseDouble(source.doDBusCall(SERVICE,
125
+                IFACE, POSITION).get(0)) / 1000);
126
+    }
127
+
128
+    /** {@inheritDoc} */
129
+    @Override
130
+    public String getFormat() {
131
+        return trackInfo == null ? "Unknown" : trackInfo.get("mime-type");
132
+    }
133
+
134
+    /** {@inheritDoc} */
135
+    @Override
136
+    public String getBitrate() {
137
+        return trackInfo == null ? "Unknown" : trackInfo.get("bit-rate");
138
+    }
139
+
140
+    /**
141
+     * Get the duration in seconds as a string.
142
+     *
143
+     * @param seconds Input to get duration for
144
+     * @return Duration as a string
145
+     */
146
+    private String duration(final long secondsInput) {
147
+        final StringBuilder result = new StringBuilder();
148
+        final long hours = secondsInput / 3600;
149
+        final long minutes = secondsInput / 60 % 60;
150
+        final long seconds = secondsInput % 60;
151
+
152
+        if (hours > 0) {
153
+            if (hours < 10) {
154
+                result.append('0');
155
+            }
156
+
157
+            result.append(hours).append(":");
158
+        }
159
+
160
+        if (minutes < 10) {
161
+            result.append('0');
162
+        }
163
+
164
+        result.append(minutes).append(":");
165
+
166
+        if (seconds < 10) {
167
+            result.append('0');
168
+        }
169
+
170
+        result.append(seconds);
171
+
172
+        return result.toString();
173
+    }
174
+
175
+}

+ 177
- 0
src/com/dmdirc/addons/mediasource_dbus/DBusMediaSource.java Visa fil

@@ -0,0 +1,177 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.mediasource_dbus;
24
+
25
+import com.dmdirc.addons.nowplaying.MediaSource;
26
+import com.dmdirc.addons.nowplaying.MediaSourceManager;
27
+import com.dmdirc.plugins.Plugin;
28
+
29
+import java.io.BufferedReader;
30
+import java.io.File;
31
+import java.io.IOException;
32
+import java.io.InputStreamReader;
33
+import java.util.ArrayList;
34
+import java.util.Arrays;
35
+import java.util.HashMap;
36
+import java.util.List;
37
+import java.util.Map;
38
+
39
+/**
40
+ * Provides a media source for dbus players.
41
+ */
42
+public class DBusMediaSource extends Plugin implements MediaSourceManager {
43
+
44
+    /** The sources used by this media source. */
45
+    private List<MediaSource> sources;
46
+    /** A map of discovered mpris sources. */
47
+    private final Map<String, MediaSource> mprisSources = new HashMap<String, MediaSource>();
48
+    /** The path to qdbus. */
49
+    private String qdbus;
50
+       
51
+    /**
52
+     * Creates a new instance of DBusMediaSource.
53
+     */
54
+    public DBusMediaSource() {
55
+        super();
56
+    }
57
+    
58
+    /** {@inheritDoc} */
59
+    @Override
60
+    public void onLoad() {
61
+        sources = new ArrayList<MediaSource>(Arrays.asList(new MediaSource[]{
62
+            new BansheeSource(this),
63
+        }));
64
+
65
+        if (new File("/usr/bin/qdbus").exists()) {
66
+            qdbus = "/usr/bin/qdbus";
67
+        } else if (new File("/bin/qdbus").exists()) {
68
+            qdbus = "/bin/qdbus";
69
+        }
70
+    }
71
+    
72
+    /** {@inheritDoc} */
73
+    @Override
74
+    public void onUnload() {
75
+        // Do nothing
76
+    }
77
+
78
+    /** {@inheritDoc} */
79
+    @Override
80
+    public List<MediaSource> getSources() {
81
+        for (String mpris : doDBusCall("org.mpris.*", "/", "/")) {
82
+            final String service = mpris.substring(10);
83
+
84
+            if (!mprisSources.containsKey(service)) {
85
+                mprisSources.put(service, new MPRISSource(this, service));
86
+                sources.add(mprisSources.get(service));
87
+            }
88
+        }
89
+
90
+        return sources;
91
+    }
92
+
93
+    /**
94
+     * Performs a dbus call.
95
+     *
96
+     * @param service The name of the service
97
+     * @param iface The name of the interface
98
+     * @param method The name of the method
99
+     * @param args Any arguments to the method
100
+     * @return A list of output (one entry per line)
101
+     */
102
+    public List<String> doDBusCall(final String service, final String iface,
103
+            final String method, final String ... args) {
104
+        final String[] exeArgs = new String[4 + args.length];
105
+        exeArgs[0] = qdbus;
106
+        exeArgs[1] = service;
107
+        exeArgs[2] = iface;
108
+        exeArgs[3] = method;
109
+
110
+        for (int i = 0; i < args.length; i++) {
111
+            exeArgs[4 + i] = args[i];
112
+        }
113
+
114
+        return getInfo(exeArgs);
115
+    }
116
+
117
+    /**
118
+     * Executes the specified command and arguments and returns the results.
119
+     *
120
+     * @param args The command/arguments to be executed
121
+     * @return The output of the specified command
122
+     */
123
+    protected static List<String> getInfo(final String[] args) {
124
+        final ArrayList<String> result = new ArrayList<String>();
125
+
126
+        InputStreamReader reader;
127
+        BufferedReader input;
128
+        Process process;
129
+
130
+        try {
131
+            process = Runtime.getRuntime().exec(args);
132
+
133
+            reader = new InputStreamReader(process.getInputStream());
134
+            input = new BufferedReader(reader);
135
+
136
+            String line = "";
137
+
138
+            while ((line = input.readLine()) != null) {
139
+                result.add(line);
140
+            }
141
+
142
+            reader.close();
143
+            input.close();
144
+            process.destroy();
145
+        } catch (IOException ex) {
146
+            ex.printStackTrace();
147
+        }
148
+
149
+        return result;
150
+    }
151
+
152
+    /**
153
+     * Parses a dbus dictionary into a {@link Map}.
154
+     *
155
+     * @param lines The lines to be parsed as a dictionary
156
+     * @return A map corresponding to the specified dictionary
157
+     */
158
+    protected static Map<String, String> parseDictionary(final List<String> lines) {
159
+        final Map<String, String> res = new HashMap<String, String>();
160
+
161
+        for (String line : lines) {
162
+            final int index = line.indexOf(':');
163
+
164
+            if (index == -1 || index >= line.length() - 2) {
165
+                continue;
166
+            }
167
+            
168
+            final String key = line.substring(0, index);
169
+            final String value = line.substring(index + 2);
170
+
171
+            res.put(key, value);
172
+        }
173
+
174
+        return res;
175
+    }
176
+
177
+}

+ 193
- 0
src/com/dmdirc/addons/mediasource_dbus/MPRISSource.java Visa fil

@@ -0,0 +1,193 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.mediasource_dbus;
24
+
25
+import com.dmdirc.addons.nowplaying.MediaSource;
26
+import com.dmdirc.addons.nowplaying.MediaSourceState;
27
+
28
+import java.util.List;
29
+import java.util.Map;
30
+
31
+/**
32
+ * A media source for anything that's compatible with MRPIS.
33
+ *
34
+ * @author chris
35
+ */
36
+public class MPRISSource implements MediaSource {
37
+
38
+    /** The media source manager. */
39
+    private final DBusMediaSource source;
40
+    /** The service name. */
41
+    private final String service;
42
+    /** The name of the source. */
43
+    private final String name;
44
+    /** A temporary cache of track data. */
45
+    private Map<String, String> data;
46
+
47
+    /**
48
+     * Creates a new MPRIS source for the specified service name.
49
+     *
50
+     * @param source The manager which owns this source
51
+     * @param service The service name of the MRPIS service
52
+     */
53
+    public MPRISSource(final DBusMediaSource source, final String service) {
54
+        this.source = source;
55
+        this.service = service;
56
+
57
+        this.name = source.doDBusCall("org.mpris." + service, "/",
58
+                "org.freedesktop.MediaPlayer.Identity").get(0).replace(' ', '_');
59
+    }
60
+
61
+    /** {@inheritDoc} */
62
+    @Override
63
+    public MediaSourceState getState() {
64
+        final char[] status = getStatus();
65
+
66
+        if (status == null) {
67
+            data = null;
68
+            return MediaSourceState.CLOSED;
69
+        }
70
+
71
+        data = getTrackInfo();
72
+
73
+        if (status[0] == '0') {
74
+            return MediaSourceState.PLAYING;
75
+        } else if (status[0] == '1') {
76
+            return MediaSourceState.PAUSED;
77
+        } else if (status[0] == '2') {
78
+            return MediaSourceState.STOPPED;
79
+        } else {
80
+            return MediaSourceState.NOTKNOWN;
81
+        }
82
+    }
83
+
84
+    /** {@inheritDoc} */
85
+    @Override
86
+    public String getAppName() {
87
+        return name;
88
+    }
89
+
90
+    /**
91
+     * Utility method to return the value of the specified key if it exists,
92
+     * or "Unknown" if it doesn't.
93
+     *
94
+     * @param key The key to be retrieved
95
+     * @return The value of the specified key or "Unknown".
96
+     */
97
+    protected String getData(final String key) {
98
+        return data == null || !data.containsKey(key) ? "Unknown" : data.get(key);
99
+    }
100
+
101
+    /** {@inheritDoc} */
102
+    @Override
103
+    public String getArtist() {
104
+        return getData("artist");
105
+    }
106
+
107
+    /** {@inheritDoc} */
108
+    @Override
109
+    public String getTitle() {
110
+        return getData("title");
111
+    }
112
+
113
+    /** {@inheritDoc} */
114
+    @Override
115
+    public String getAlbum() {
116
+        return getData("album");
117
+    }
118
+
119
+    /** {@inheritDoc} */
120
+    @Override
121
+    public String getLength() {
122
+        return getData("time");
123
+    }
124
+
125
+    /** {@inheritDoc} */
126
+    @Override
127
+    public String getTime() {
128
+        return "Unknown";
129
+    }
130
+
131
+    /** {@inheritDoc} */
132
+    @Override
133
+    public String getFormat() {
134
+        return "Unknown";
135
+    }
136
+
137
+    /** {@inheritDoc} */
138
+    @Override
139
+    public String getBitrate() {
140
+        return getData("audio-bitrate");
141
+    }
142
+
143
+    /**
144
+     * Retrieves a map of track information from the service.
145
+     *
146
+     * @return A map of metadata returned by the MPRIS service
147
+     */
148
+    protected Map<String, String> getTrackInfo() {
149
+        // If only there were a standard...
150
+        final List<String> list = source.doDBusCall("org.mpris." + service,
151
+                "/Player", "org.freedesktop.MediaPlayer.GetMetadata");
152
+        list.addAll(source.doDBusCall("org.mpris." + service,
153
+                "/Player", "org.freedesktop.MediaPlayer.GetMetaData"));
154
+        return DBusMediaSource.parseDictionary(list);
155
+    }
156
+
157
+    /**
158
+     * Retrieves the 'status' result from the MPRIS service.
159
+     *
160
+     * @return The returned status or null if the service isn't running
161
+     */
162
+    protected char[] getStatus() {
163
+        if (source.doDBusCall("org.mpris." + service, "/",
164
+                "org.freedesktop.MediaPlayer.Identity").isEmpty()) {
165
+            // Calling dbus-send can seemingly start applications that have
166
+            // quit (not entirely sure how), so check that it's running first.
167
+            return null;
168
+        }
169
+
170
+        final List<String> res = DBusMediaSource.getInfo(new String[]{
171
+            "/usr/bin/dbus-send", "--print-reply", "--dest=org.mpris." + service,
172
+            "/Player", "org.freedesktop.MediaPlayer.GetStatus"
173
+        });
174
+
175
+        if (res.isEmpty()) {
176
+            return null;
177
+        }
178
+
179
+        final char[] result = new char[4];
180
+        int i = 0;
181
+
182
+        for (String line : res) {
183
+            final String tline = line.trim();
184
+
185
+            if (tline.startsWith("int32")) {
186
+                result[i++] = tline.charAt(tline.length() - 1);
187
+            }
188
+        }
189
+
190
+        return result;
191
+    }
192
+
193
+}

+ 34
- 0
src/com/dmdirc/addons/mediasource_dbus/plugin.config Visa fil

@@ -0,0 +1,34 @@
1
+# This is a DMDirc configuration file.
2
+
3
+# This section indicates which sections below take key/value
4
+# pairs, rather than a simple list. It should be placed above
5
+# any sections that take key/values.
6
+keysections:
7
+  metadata
8
+  updates
9
+  version
10
+  requires
11
+
12
+metadata:
13
+  author=Chris <chris@dmdirc.com>
14
+  mainclass=com.dmdirc.addons.mediasource_dbus.DBusMediaSource
15
+  description=Provides a media source for dbus-enabled clients
16
+  name=dbusmediasource
17
+  nicename=DBus Media Source
18
+
19
+updates:
20
+  id=39
21
+
22
+version:
23
+  friendly=1.0
24
+
25
+provides:
26
+  banshee mediasource
27
+  mpris mediasource
28
+
29
+requires:
30
+  os=linux
31
+  files=/usr/bin/qdbus|/bin/qdbus
32
+
33
+required-services:
34
+  mediasource manager

Laddar…
Avbryt
Spara