diff --git a/extension.js b/extension.js index 548b3f4..ae56b1d 100644 --- a/extension.js +++ b/extension.js @@ -6,7 +6,10 @@ const Me = ExtensionUtils.getCurrentExtension(); const Mainloop = imports.mainloop; const Main = imports.ui.main; -const { playerAction, getPlayers, getMetadata, getStatus, isValidPlayer, hasMetadataChanged } = +const PanelMenu = imports.ui.panelMenu; +const PopupMenu = imports.ui.popupMenu; + +const { playerAction, getPlayers, getMetadata, getStatus, updatePlayers, isValidPlayer, hasMetadataChanged } = Me.imports.utils; let maxDisplayLength, @@ -48,13 +51,14 @@ let buttonNext, iconPrev, iconPlayer, labelSeperatorStart, - labelSeperatorEnd; + labelSeperatorEnd, + sourceMenu; let mainloop, settings, positions, playerIcons; let currentPlayer, currentMetadata, currentLabel, currentStatus; -let loopFinished, contentRemoved, mouseHovered; +let loopFinished, contentRemoved, mouseHovered, changedSource; const init = () => { playerIcons = ["chromium", "firefox"]; @@ -209,6 +213,17 @@ const enable = () => { style: "padding: 3px", }); + sourceMenu = new PanelMenu.Button(1); + sourceMenu.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM)); + sourceMenu.menu.connect("open-state-changed", (menu, open) => { + if (open) { + (() => { + updatePlayers(sourceMenu, changeSource); + })(); + } + }); + sourceMenu.menu.addMenuItem(new PopupMenu.PopupMenuItem("Players", { reactive: false })); + buttonNext.set_child(iconNext); buttonNext.connect("button-release-event", () => { (() => { @@ -281,8 +296,10 @@ const disable = () => { iconPause.destroy(); iconPlay.destroy(); iconPrev.destroy(); + iconPlayer.destroy(); labelSeperatorStart.destroy(); labelSeperatorEnd.destroy(); + sourceMenu.destroy(); currentMetadata = null; currentPlayer = null; @@ -301,12 +318,15 @@ const mainLoop = async () => { if (players.includes(currentPlayer)) { // log("Current player is in list"); let status = await getStatus(currentPlayer); - if (status === "Playing") { + if (status === "Playing" || changedSource === currentPlayer) { // log("Player is playing"); - currentStatus = "Playing"; + currentStatus = status; let metadata = await getMetadata(currentPlayer); if (isValidPlayer(metadata)) { - if (hasMetadataChanged(metadata, currentMetadata)) { + if ( + hasMetadataChanged(metadata, currentMetadata) || + changedSource === currentPlayer + ) { log("Metadata is not equal, updating em"); currentMetadata = metadata; currentLabel = currentMetadata["title"] || currentMetadata["id"]; @@ -330,7 +350,7 @@ const mainLoop = async () => { } if (currentPlayer) { - log("not nulling player", currentPlayer); + // log("not nulling player", currentPlayer); currentStatus = _status; _metadata = await getMetadata(currentPlayer); if (isValidPlayer(_metadata)) { @@ -493,6 +513,7 @@ const addContent = () => { index++; } } + Main.panel.addToStatusArea("sourceMenu", sourceMenu, extensionIndex + index, extensionPosition); contentRemoved = false; } }; @@ -507,6 +528,7 @@ const removeContent = () => { Main.panel[positions[extensionPosition]].remove_actor(buttonPlayer); Main.panel[positions[extensionPosition]].remove_actor(labelSeperatorStart); Main.panel[positions[extensionPosition]].remove_actor(labelSeperatorEnd); + delete Main.panel.statusArea["sourceMenu"]; contentRemoved = true; } }; @@ -538,3 +560,9 @@ const mouseAction = (event) => { playerAction(currentPlayer, mouseActions[1]); } }; + +const changeSource = (player) => { + currentPlayer = player; + currentMetadata = null; + changedSource = player; +}; diff --git a/utils.js b/utils.js index 9b8e326..48397ed 100644 --- a/utils.js +++ b/utils.js @@ -1,10 +1,14 @@ -const { GLib } = imports.gi; +const { GLib, Gio, St } = imports.gi; + +const PopupMenu = imports.ui.popupMenu; const ExtensionUtils = imports.misc.extensionUtils; const Me = ExtensionUtils.getCurrentExtension(); const { dbusMethod } = Me.imports.dbus; +let players; + var playerAction = async (player, action) => { switch (action) { case "play": @@ -59,10 +63,12 @@ var getMetadata = async (player) => { let id = metadata[0]["mpris:trackid"]; let title = metadata[0]["xesam:title"]; let artist = metadata[0]["xesam:artist"]; + let image = metadata[0]["mpris:artUrl"]; return { id, title, artist, + image, }; } catch (error) { logError(error); @@ -84,6 +90,36 @@ var getStatus = async (player) => { } }; +const updatePlayers = async (sourceMenu, callback) => { + sourceMenu.menu.removeAll(); + players = await getPlayers(); + if (players.length > 0) { + for (player of players) { + let metadata = await getMetadata(player); + if (isValidPlayer(metadata)) { + let image = metadata["image"]; + if (image) { + image = image.replace("https://open.spotify.com/image/", "https://i.scdn.co/image/"); + } else { + image = "audio-x-generic-symbolic"; + } + let title = + (metadata["title"] || metadata["id"]) + + (metadata["artist"] ? " - " + metadata["artist"] : ""); + let icon = Gio.icon_new_for_string(image); + let item = new PopupMenu.PopupImageMenuItem(title, icon); + let playerIndex = players.indexOf(player); + item.connect("activate", () => { + callback(players[playerIndex]); + }); + sourceMenu.menu.addMenuItem(item); + } + } + } else { + sourceMenu.menu.addMenuItem(new PopupMenu.PopupMenuItem("No players found", { reactive: false })); + } +}; + var isValidPlayer = ({ id, title }) => { if (title || (id && id !== "/org/mpris/MediaPlayer2/TrackList/NoTrack")) { return true; @@ -92,7 +128,7 @@ var isValidPlayer = ({ id, title }) => { }; var hasMetadataChanged = (metadata, _metadata) => { - if (Object.keys(metadata).every((key) => metadata[key] !== _metadata[key])) { + if (metadata && _metadata && Object.keys(metadata).every((key) => metadata[key] !== _metadata[key])) { return true; } return false;