Merge tag 'upstream/0_git20160830' into debian/master
Upstream version 0~git20160830
Simon McVittie
7 years ago
0 | commit 17091aca40ad003cbd28ad88ed9a914aea6dc0f4 | |
1 | Merge: fb5da1b 38a396f | |
2 | Author: Jean-Philippe Braun <eon@patapon.info> | |
3 | Date: 2016-08-30 12:16:25 +0200 | |
4 | ||
5 | Merge pull request #250 from fenryxo/nuvola-rating | |
6 | ||
7 | Add support for Nuvola Player's MPRIS rating API | |
8 | ||
9 | commit fb5da1bb7f55442d695b7abcb34b1690ed9dc86c | |
10 | Author: Jean-Philippe Braun <eon@patapon.info> | |
11 | Date: 2016-08-27 10:55:46 +0200 | |
12 | ||
13 | vlc is sending stop on track change | |
14 | ||
15 | commit 50741a8d3654a612840181da83b8fbfa3c16438c | |
16 | Author: Jean-Philippe Braun <eon@patapon.info> | |
17 | Date: 2016-08-27 10:46:31 +0200 | |
18 | ||
19 | Simplify cover management | |
20 | ||
21 | Fixes http cover updates with Spotify | |
22 | ||
23 | Refs #260 | |
24 | Refs #271 | |
25 | ||
0 | 26 | commit c5f9d905fd39dfc978d8416d2c1cb0f6694e0f09 |
1 | 27 | Author: Jean-Philippe Braun <jean-philippe.braun@cloudwatt.com> |
2 | 28 | Date: 2016-05-09 20:44:30 +0200 |
31 | 57 | Do not disable already disabled extension |
32 | 58 | |
33 | 59 | Causes 'manager is null' js error |
60 | ||
61 | commit 38a396fd4e449de282484423625c4f6ead74218f | |
62 | Author: Jiří Janoušek <janousek.jiri@gmail.com> | |
63 | Date: 2016-03-06 10:07:57 +0100 | |
64 | ||
65 | Add support for Nuvola Player's MPRIS rating API | |
66 | ||
67 | The Nuvola Player's MPRIS interface provides following non-standard | |
68 | vendor-prefixed rating API: | |
69 | ||
70 | * the Player.NuvolaCanRate Boolean property, which is true when rating | |
71 | is possible | |
72 | * the Player.NuvolaSetRating(Double rating) method to set rating with | |
73 | the double value having the same meaning as xesam:userRating in | |
74 | Metadata property. | |
75 | ||
76 | Upstream Issue: tiliado/nuvolaplayer#204 | |
77 | ||
78 | Signed-off-by: Jiří Janoušek <janousek.jiri@gmail.com> | |
34 | 79 | |
35 | 80 | commit 040807fce94f952364c0b1a253b1031e44d5ca26 |
36 | 81 | Merge: e14b8fe 6393aa8 |
2829 | 2874 | Merge branch 'playlists' into devel |
2830 | 2875 | |
2831 | 2876 | Conflicts: |
2832 | po/pl.po | |
2877 | po/pl.po | |
2833 | 2878 | |
2834 | 2879 | commit ffd0424b2e782b1d8f1bb0d736d5fbff9fe64c17 |
2835 | 2880 | Merge: 42a666e 02fe361 |
62 | 62 | trackArtist: null, |
63 | 63 | trackUrl: null, |
64 | 64 | trackCoverUrl: null, |
65 | trackCoverPath: null, | |
66 | 65 | trackLength: null, |
67 | 66 | trackObj: null, |
68 | 67 | trackRating: null, |
111 | 110 | this._currentPlaylist = ""; |
112 | 111 | this._trackTime = 0; |
113 | 112 | this._wantedSeekValue = 0; |
114 | this._trackCoverFileTmp = null; | |
115 | 113 | |
116 | 114 | this._timerId = 0; |
117 | 115 | this._statusId = 0; |
143 | 141 | })); |
144 | 142 | |
145 | 143 | this.connect("player-update", Lang.bind(this, function(player, state) { |
144 | //global.log(JSON.stringify(state)); | |
146 | 145 | this.state.update(state); |
147 | 146 | if (state.status) |
148 | 147 | this._onStatusChange(); |
360 | 359 | state.trackLength = metadata["mpris:length"] ? metadata["mpris:length"].unpack() / 1000000 : 0; |
361 | 360 | state.trackObj = metadata["mpris:trackid"] ? metadata["mpris:trackid"].unpack() : ""; |
362 | 361 | state.trackCoverUrl = metadata["mpris:artUrl"] ? metadata["mpris:artUrl"].unpack() : ""; |
363 | state.isRadio = false; | |
364 | ||
365 | if (state.trackCoverUrl !== this.state.trackCoverUrl) { | |
366 | if (state.trackCoverUrl) { | |
367 | let cover_path = ""; | |
368 | // Distant cover | |
369 | if (state.trackCoverUrl.match(/^http/)) { | |
370 | // Copy the cover to a tmp local file | |
371 | let cover = Gio.file_new_for_uri(decodeURIComponent(state.trackCoverUrl)); | |
372 | // Don't create multiple tmp files | |
373 | if (!this._trackCoverFileTmp) | |
374 | this._trackCoverFileTmp = Gio.file_new_tmp('XXXXXX.mediaplayer-cover')[0]; | |
375 | // asynchronous copy | |
376 | cover.read_async(null, null, Lang.bind(this, this._onReadCover)); | |
377 | } | |
378 | // Local cover | |
379 | else if (state.trackCoverUrl.match(/^file/)) { | |
380 | state.trackCoverPath = decodeURIComponent(state.trackCoverUrl.substr(7)); | |
381 | } | |
382 | } | |
383 | else { | |
384 | state.trackCoverPath = ''; | |
385 | } | |
386 | } | |
387 | 362 | |
388 | 363 | if (state.trackCoverUrl === '' && metadata["xesam:genre"]) { |
389 | 364 | let genres = metadata["xesam:genre"].deep_unpack(); |
408 | 383 | if (metadata.rating) |
409 | 384 | rating = metadata.rating.deep_unpack(); |
410 | 385 | state.trackRating = parseInt(rating); |
411 | }, | |
412 | ||
413 | _onReadCover: function(cover, result) { | |
414 | let inStream = cover.read_finish(result); | |
415 | let outStream = this._trackCoverFileTmp.replace(null, false, | |
416 | Gio.FileCreateFlags.REPLACE_DESTINATION, | |
417 | null, null); | |
418 | outStream.splice_async(inStream, Gio.OutputStreamSpliceFlags.CLOSE_TARGET, | |
419 | 0, null, Lang.bind(this, function(outStream, result) { | |
420 | outStream.splice_finish(result, null); | |
421 | this.emit('player-update', | |
422 | new PlayerState({trackCoverPath: this._trackCoverFileTmp.get_path()})); | |
423 | })); | |
424 | 386 | }, |
425 | 387 | |
426 | 388 | _refreshProperties: function() { |
56 | 56 | |
57 | 57 | const SEND_STOP_ON_CHANGE = [ |
58 | 58 | "org.mpris.MediaPlayer2.banshee", |
59 | "org.mpris.MediaPlayer2.vlc", | |
59 | 60 | "org.mpris.MediaPlayer2.pragha" |
60 | 61 | ]; |
61 | 62 |
130 | 130 | |
131 | 131 | this.activePlaylist = null; |
132 | 132 | |
133 | this.trackCoverUrl = false; | |
134 | this.trackCoverFileTmp = false; | |
135 | 133 | this.trackCover = new St.Button({style_class: 'track-cover-container', |
136 | 134 | x_align: St.Align.START, |
137 | 135 | y_align: St.Align.START, |
334 | 332 | this.setActivePlaylist(newState.playlist); |
335 | 333 | } |
336 | 334 | |
337 | if (newState.trackCoverPath !== null || newState.isRadio !== null) { | |
335 | if (newState.trackCoverUrl !== null || newState.isRadio !== null) { | |
338 | 336 | this.hideCover(); |
339 | 337 | this.showCover(newState); |
340 | 338 | } |
372 | 370 | icon_size: this.trackCover.child.icon_size}); |
373 | 371 | this.trackCover.child = coverIcon; |
374 | 372 | } |
375 | else if (! state.trackCoverPath || ! GLib.file_test(state.trackCoverPath, GLib.FileTest.EXISTS)) { | |
373 | else if (state.trackCoverUrl) { | |
374 | let file = Gio.File.new_for_uri(state.trackCoverUrl); | |
375 | let gicon = new Gio.FileIcon({file: file}); | |
376 | let coverIcon = new St.Icon({gicon: gicon, style_class: "track-cover", | |
377 | icon_size: this.trackCover.child.icon_size}); | |
378 | this.trackCover.child = coverIcon; | |
379 | } | |
380 | else { | |
376 | 381 | let coverIcon = new St.Icon({icon_name: "media-optical-cd-audio", |
377 | 382 | icon_size: this.trackCover.child.icon_size}); |
378 | 383 | this.trackCover.child = coverIcon; |
379 | 384 | } |
380 | else { | |
381 | let gicon = new Gio.FileIcon({file: Gio.File.new_for_path(state.trackCoverPath)}); | |
382 | let coverIcon = new St.Icon({gicon: gicon, style_class: "track-cover", | |
383 | icon_size: this.trackCover.child.icon_size}); | |
384 | this.trackCover.child = coverIcon; | |
385 | } | |
385 | ||
386 | 386 | // Show the new cover |
387 | 387 | Tweener.addTween(this.trackCover, { |
388 | 388 | opacity: 255, |
254 | 254 | this._player = player; |
255 | 255 | // Current value |
256 | 256 | this._value = value; |
257 | // Supported players | |
257 | // Supported players (except for Nuvola Player) | |
258 | 258 | this._supported = { |
259 | 259 | "org.mpris.MediaPlayer2.banshee": this.applyBansheeRating, |
260 | 260 | "org.mpris.MediaPlayer2.rhythmbox": this.applyRhythmbox3Rating, |
289 | 289 | }, |
290 | 290 | |
291 | 291 | newRating: function(button) { |
292 | if (this._supported[this._player.busName]) { | |
292 | if (this._supported[this._player.busName] || this.nuvolaRatingSupported()) { | |
293 | 293 | if (button.hover) |
294 | 294 | this.hoverRating(button._rateValue); |
295 | 295 | else |
336 | 336 | let applyFunc = Lang.bind(this, this._supported[this._player.busName]); |
337 | 337 | applied = applyFunc(rateValue); |
338 | 338 | } |
339 | else if (this.getNuvolaRatingProxy()) { | |
340 | applied = this.applyNuvolaRating(rateValue); | |
341 | } | |
339 | 342 | if (applied) { |
340 | 343 | this.setRating(rateValue); |
341 | 344 | } |
377 | 380 | return false; |
378 | 381 | }, |
379 | 382 | |
383 | getNuvolaRatingProxy: function() { | |
384 | if (this._nuvolaRatingProxy === false) { | |
385 | return false; | |
386 | } | |
387 | if (this._nuvolaRatingProxy) { | |
388 | return this._nuvolaRatingProxy; | |
389 | } | |
390 | /* Web apps running in the Nuvola Player runtime are named "org.mpris.MediaPlayer2.NuvolaAppFooBarBaz" */ | |
391 | if (this._player.busName.indexOf("org.mpris.MediaPlayer2.NuvolaApp") !== 0) { | |
392 | this._nuvolaRatingProxy = false; | |
393 | return false; | |
394 | } | |
395 | const NuvolaRatingIface = '<node>\ | |
396 | <interface name="org.mpris.MediaPlayer2.Player">\ | |
397 | <method name="NuvolaSetRating">\ | |
398 | <arg type="d" direction="in" />\ | |
399 | </method>\ | |
400 | <property name="NuvolaCanRate" type="b" access="read" />\ | |
401 | </interface>\ | |
402 | </node>'; | |
403 | const NuvolaRatingProxy = Gio.DBusProxy.makeProxyWrapper(NuvolaRatingIface); | |
404 | this._nuvolaRatingProxy = new NuvolaRatingProxy(Gio.DBus.session, this._player.busName, | |
405 | "/org/mpris/MediaPlayer2"); | |
406 | return this._nuvolaRatingProxy; | |
407 | }, | |
408 | ||
409 | nuvolaRatingSupported: function() { | |
410 | let proxy = this.getNuvolaRatingProxy(); | |
411 | if (proxy) { | |
412 | return proxy.NuvolaCanRate; | |
413 | } | |
414 | return false; | |
415 | }, | |
416 | ||
417 | applyNuvolaRating: function(value) { | |
418 | let proxy = this.getNuvolaRatingProxy(); | |
419 | if (proxy && proxy.NuvolaCanRate) { | |
420 | proxy.NuvolaSetRatingRemote(value / 5.0); | |
421 | return true; | |
422 | } | |
423 | return false; | |
424 | }, | |
425 | ||
380 | 426 | destroy: function() { |
381 | 427 | this.actor.destroy(); |
382 | 428 | }, |