diff --git a/extension/manifest.json b/extension/manifest.json index 2799325..c86f68b 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -5,7 +5,7 @@ "version": "0.2", "background": { "scripts": ["background.js"], - "persistent": false + "persistent": true }, "content_scripts": [ { diff --git a/extension/providers/youtube-music.js b/extension/providers/youtube-music.js index 68f4b1d..84762db 100644 --- a/extension/providers/youtube-music.js +++ b/extension/providers/youtube-music.js @@ -12,27 +12,25 @@ class YoutubeMusic extends MprisBase { } getPosition(callback) { - console.log('YoutubeMusic', 'getPosition', arguments) - - if (!$('ytmusic-player-bar .time-info').length) - return callback(0); - - var text = $('ytmusic-player-bar .time-info').text(); - - text = text.split('/'); - - if (text.length != 2) - return 0; - - callback(this.microSeconds(text[0].trim())); + const video = document.querySelector('video'); + if (video) { + callback(Math.round(video.currentTime * 1e6)); + } else { + callback(0); + } } setRate(callback) { // Not supported } - setVolume(callback) { - + setVolume(callback, value) { + const video = document.querySelector('video'); + if (video) { + video.volume = value; + } + callback() + this.update() } setShuffle(callback, value) { @@ -49,9 +47,7 @@ class YoutubeMusic extends MprisBase { next(callback) { console.log('YoutubeMusic', 'next', arguments) - - if ($('[aria-label="Next song"]').length) - $('[aria-label="Next song"]').click() + $('.next-button').click() callback() this.update() @@ -59,9 +55,7 @@ class YoutubeMusic extends MprisBase { previous(callback) { console.log('YoutubeMusic', 'previous', arguments) - - if ($('[aria-label="Previous song"]').length) - $('[aria-label="Previous song"]').click() + $('.previous-button').click() callback() this.update() @@ -69,12 +63,10 @@ class YoutubeMusic extends MprisBase { playPause(callback) { console.log('YoutubeMusic', 'playPause', arguments) - - if ($('[title="Pause"]').length) - $('[title="Pause"]').click() - - if ($('[title="Play"]').length) - $('[title="Play"]').click() + const video = document.querySelector('video'); + if (video) { + if (video.paused) video.play(); else video.pause(); + } callback() this.update() @@ -82,9 +74,8 @@ class YoutubeMusic extends MprisBase { pause(callback) { console.log('YoutubeMusic', 'pause', arguments) - - if ($('[title="Pause"]').length) - $('[title="Pause"]').click() + const video = document.querySelector('video'); + if (video) video.pause(); callback() this.update() @@ -92,85 +83,99 @@ class YoutubeMusic extends MprisBase { play(callback) { console.log('YoutubeMusic', 'play', arguments) - - if ($('[title="Play"]').length) - $('[title="Play"]').click() + const video = document.querySelector('video'); + if (video) video.play(); callback() this.update() } - update(callback) { - if ($('[title="Play"]').length) { - if ($('[title="Play"]').prop('disabled')) - this.media.PlaybackStatus = 'Stopped' - else - this.media.PlaybackStatus = 'Paused' - } else if ($('[title="Pause"]').length) { - this.media.PlaybackStatus = 'Playing' + seek(callback, offset) { + console.log('YoutubeMusic', 'seek', arguments) + const video = document.querySelector('video'); + if (video) { + video.currentTime += offset / 1e6; } + callback() + this.update() + } - if ($('[aria-label="Next song"]').length && ! $('[aria-label="Next song"]').prop('disabled')) - this.media.CanGoNext = true - else - this.media.CanGoNext = false - - if ($('[aria-label="Previous song"]').length && ! $('[aria-label="Previous song"]').prop('disabled')) - this.media.CanGoPrevious = true - else - this.media.CanGoPrevious = false - - if ($('ytmusic-player-bar .content-info-wrapper .title').length) { - this.media.Metadata["mpris:trackid"] = $('ytmusic-player-bar .content-info-wrapper .title').text() - this.media.Metadata["xesam:title"] = $('ytmusic-player-bar .content-info-wrapper .title').text() + setPosition(callback, trackId, position) { + console.log('YoutubeMusic', 'setPosition', arguments) + const video = document.querySelector('video'); + if (video) { + video.currentTime = position / 1e6; } + callback() + this.update() + } + + update(callback) { + const video = document.querySelector('video'); + if (!video) return; - if ($('ytmusic-player-bar .image').length) - this.media.Metadata["mpris:artUrl"] = $('ytmusic-player-bar .image').attr("src"); + this.media.PlaybackStatus = video.paused ? 'Paused' : 'Playing'; + this.media.Volume = video.volume; + + const playerBar = $('ytmusic-player-bar'); + if (!playerBar.length) return; - this.media.Metadata["xesam:url"] = location.href + const title = playerBar.find('.title').text().trim(); + const subtitle = playerBar.find('.subtitle').text().trim(); + const image = playerBar.find('.image').attr('src'); + + if (title) { + this.media.Metadata["mpris:trackid"] = title; + this.media.Metadata["xesam:title"] = title; + } + + if (image) { + this.media.Metadata["mpris:artUrl"] = image; + } - if ($('ytmusic-player-bar .content-info-wrapper .subtitle').length && $('ytmusic-player-bar .content-info-wrapper .subtitle').text() && $('ytmusic-player-bar .content-info-wrapper .subtitle').text().split('•').length > 2) { - this.media.Metadata["xesam:album"] = $('ytmusic-player-bar .content-info-wrapper .subtitle').text().split('•')[1].trim() - this.media.Metadata["xesam:artist"] = [$('ytmusic-player-bar .content-info-wrapper .subtitle').text().split('•')[0].trim()] - this.media.Metadata["xesam:albumArtist"] = [$('ytmusic-player-bar .content-info-wrapper .subtitle').text().split('•')[0].trim()] + this.media.Metadata["xesam:url"] = window.location.href; + + if (video.duration) { + this.media.Metadata["mpris:length"] = Math.round(video.duration * 1e6); } - - if ($('ytmusic-player-bar .time-info').length) { - var timestamp = $('ytmusic-player-bar .time-info').text().split('/'); - if (timestamp.length == 2) { - this.media.Metadata["mpris:length"] = this.microSeconds(timestamp[1].trim()) - } + if (subtitle) { + const parts = subtitle.split('•').map(p => p.trim()); + if (parts.length >= 2) { + this.media.Metadata["xesam:artist"] = [parts[0].replace(/\n/g, ' ')]; + this.media.Metadata["xesam:album"] = parts[1].replace(/\n/g, ' '); + this.media.Metadata["xesam:albumArtist"] = [parts[0].replace(/\n/g, ' ')]; + } } + this.media.CanGoNext = !playerBar.find('.next-button').prop('disabled'); + this.media.CanGoPrevious = !playerBar.find('.previous-button').prop('disabled'); + console.log('this.media', this.media); - if(! _.isEqual(this.oldMedia, this.media)){ + if (!_.isEqual(this.oldMedia, this.media)) { this.oldMedia = _.cloneDeep(this.media); - this.changed(this.media) + this.changed(this.media); } } microSeconds(position) { - position = position.split(':'); - - if (position.length != 2) - return 0; - - position = parseInt(position[0]) * 60 + parseInt(position[1]) - position = position * 1e6 - return position + const parts = position.split(':').reverse(); + let seconds = 0; + if (parts.length >= 1) seconds += parseInt(parts[0]); + if (parts.length >= 2) seconds += parseInt(parts[1]) * 60; + if (parts.length >= 3) seconds += parseInt(parts[2]) * 3600; + return seconds * 1e6; } } -var youtubeMusic = new YoutubeMusic() +// Assign to window to prevent garbage collection as per user hint +window.youtubeMusic = new YoutubeMusic(); var checkExist = setInterval(function() { if ($("ytmusic-player-bar").length) { console.log('YoutubeMusic', 'checkExist', arguments) clearInterval(checkExist); - youtubeMusic.init() + window.youtubeMusic.init() } }, 100); - diff --git a/install.py b/install.py index b872cf5..64df773 100755 --- a/install.py +++ b/install.py @@ -25,7 +25,7 @@ def main(args): if len(args) < 1: args.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'mpris2')) - ext_id = "ojjjidifjmbbckdjfiagdfdepbcmnicg" + ext_id = "mdgmciedjpifcefejkmhjlomlachicpl" prog_path = args[0] # Chrome's extension IDs are in hexadecimal but using a-p, referred diff --git a/mpris2 b/mpris2 index e05fde1..2520788 100755 --- a/mpris2 +++ b/mpris2 @@ -448,11 +448,9 @@ def main(): conn = Gio.DBusConnection.new_for_address_sync(addr, connflags) conn.set_exit_on_close(True) - name = "org.mpris.MediaPlayer2.chrome" - if players: - # if we are exposing more than one player we need to give them - # unique names - name += ".tab%d" % tabid + # Always include the tabId in the name to avoid a generic "chrome" instance + # which can conflict with the browser's built-in MPRIS. + name = "org.mpris.MediaPlayer2.chrome.tab%d" % tabid player = TabPlayer(conn, name, tabid, msg["source"]) players[tabid] = player