|
| 1 | +/* global window */ |
| 2 | +/* eslint no-console:off, camelcase:off */ |
| 3 | + |
| 4 | +import Mopidy from "../mopidy"; |
| 5 | + |
| 6 | +const mopidy = new Mopidy({ |
| 7 | + webSocketUrl: "ws://localhost:6680/mopidy/ws", |
| 8 | +}); |
| 9 | + |
| 10 | +// Make instance available through developer console |
| 11 | +window.mopidy = mopidy; |
| 12 | + |
| 13 | +// Utilities |
| 14 | + |
| 15 | +function el(id) { |
| 16 | + return document.getElementById(id); |
| 17 | +} |
| 18 | + |
| 19 | +function hide(selector) { |
| 20 | + document.querySelectorAll(selector).forEach(e => { |
| 21 | + e.hidden = true; |
| 22 | + }); |
| 23 | +} |
| 24 | + |
| 25 | +function show(selector) { |
| 26 | + document.querySelectorAll(selector).forEach(e => { |
| 27 | + e.hidden = false; |
| 28 | + }); |
| 29 | +} |
| 30 | + |
| 31 | +// Event log |
| 32 | + |
| 33 | +function appendToEventLog(type, data) { |
| 34 | + const log = el("event-log"); |
| 35 | + log.insertAdjacentHTML( |
| 36 | + "beforeend", |
| 37 | + `<strong>${new Date().toISOString()} ${type}</strong><br>` |
| 38 | + ); |
| 39 | + if (data) { |
| 40 | + log.insertAdjacentHTML("beforeend", JSON.stringify(data, null, 2)); |
| 41 | + log.insertAdjacentHTML("beforeend", "<br>"); |
| 42 | + } |
| 43 | + log.scrollTop = log.scrollHeight; |
| 44 | +} |
| 45 | +mopidy.on("state", appendToEventLog); |
| 46 | +mopidy.on("event", appendToEventLog); |
| 47 | +mopidy.on("websocket:incomingMessage", msg => |
| 48 | + appendToEventLog("websocket:incomingMessage", JSON.parse(msg.data)) |
| 49 | +); |
| 50 | +mopidy.on("websocket:outgoingMessage", data => |
| 51 | + appendToEventLog("websocket:outgoingMessage", data) |
| 52 | +); |
| 53 | + |
| 54 | +// Player |
| 55 | + |
| 56 | +function updatePlaybackState(state, time_position) { |
| 57 | + if (time_position) { |
| 58 | + el("playback-state").innerText = `${state} at ${time_position / 1000}s`; |
| 59 | + } else { |
| 60 | + el("playback-state").innerText = state; |
| 61 | + } |
| 62 | + |
| 63 | + switch (state) { |
| 64 | + case "playing": |
| 65 | + el("play").hidden = true; |
| 66 | + el("pause").hidden = false; |
| 67 | + break; |
| 68 | + case "paused": |
| 69 | + case "stopped": |
| 70 | + el("play").hidden = false; |
| 71 | + el("pause").hidden = true; |
| 72 | + break; |
| 73 | + default: |
| 74 | + } |
| 75 | +} |
| 76 | + |
| 77 | +function updateCover(trackUri, images) { |
| 78 | + const [image] = images[trackUri]; |
| 79 | + el("cover").setAttribute("src", image.uri); |
| 80 | + el("cover").setAttribute("height", image.height); |
| 81 | + el("cover").setAttribute("width", image.width); |
| 82 | +} |
| 83 | + |
| 84 | +function updateCurrentTrack(track) { |
| 85 | + const artists = track.artists.map(a => a.name).join(", "); |
| 86 | + let albumName = track.album.name; |
| 87 | + if (track.album.date) { |
| 88 | + albumName = `${albumName} (${track.album.date})`; |
| 89 | + } |
| 90 | + |
| 91 | + el("current-artist").innerText = artists; |
| 92 | + el("current-album").innerText = albumName; |
| 93 | + el("current-track").innerText = track.name; |
| 94 | + el("current-uri").innerText = track.uri; |
| 95 | + |
| 96 | + mopidy.library |
| 97 | + .getImages([[track.uri]]) |
| 98 | + .then(result => updateCover(track.uri, result)); |
| 99 | +} |
| 100 | + |
| 101 | +// Event handlers |
| 102 | + |
| 103 | +mopidy.on("state:online", () => { |
| 104 | + hide(".offline-only"); |
| 105 | + show(".online-only"); |
| 106 | + |
| 107 | + mopidy.playback.getState().then(updatePlaybackState); |
| 108 | + mopidy.playback.getCurrentTrack().then(updateCurrentTrack); |
| 109 | + |
| 110 | + el("play").onclick = () => mopidy.playback.play(); |
| 111 | + el("pause").onclick = () => mopidy.playback.pause(); |
| 112 | + el("previous").onclick = () => mopidy.playback.previous(); |
| 113 | + el("next").onclick = () => mopidy.playback.next(); |
| 114 | + |
| 115 | + el("repeat").onclick = e => |
| 116 | + mopidy.tracklist.getRepeat().then(state => |
| 117 | + mopidy.tracklist.setRepeat([!state]).then(() => { |
| 118 | + e.className = "active"; |
| 119 | + }) |
| 120 | + ); |
| 121 | + el("random").onclick = () => |
| 122 | + mopidy.tracklist |
| 123 | + .getRandom() |
| 124 | + .then(state => mopidy.tracklist.setRandom([!state])); |
| 125 | + el("single").onclick = () => |
| 126 | + mopidy.tracklist |
| 127 | + .getSingle() |
| 128 | + .then(state => mopidy.tracklist.setSingle([!state])) |
| 129 | + .catch(console.error) |
| 130 | + .done(); |
| 131 | + el("consume").onclick = () => |
| 132 | + mopidy.tracklist |
| 133 | + .getConsume() |
| 134 | + .then(state => mopidy.tracklist.setConsume([!state])); |
| 135 | +}); |
| 136 | + |
| 137 | +mopidy.on("state:offline", () => { |
| 138 | + hide(".online-only"); |
| 139 | + show(".offline-only"); |
| 140 | +}); |
| 141 | + |
| 142 | +mopidy.on("event:playbackStateChanged", ({ new_state }) => { |
| 143 | + updatePlaybackState(new_state); |
| 144 | +}); |
| 145 | + |
| 146 | +mopidy.on("event:trackPlaybackStarted", ({ tl_track }) => { |
| 147 | + updateCurrentTrack(tl_track.track); |
| 148 | +}); |
| 149 | + |
| 150 | +mopidy.on("event:trackPlaybackStopped", () => { |
| 151 | + updatePlaybackState("stopped"); |
| 152 | +}); |
| 153 | + |
| 154 | +mopidy.on("event:trackPlaybackPaused", ({ time_position }) => { |
| 155 | + updatePlaybackState("paused", time_position); |
| 156 | +}); |
| 157 | + |
| 158 | +mopidy.on("event:trackPlaybackResumed", () => {}); |
| 159 | + |
| 160 | +window.onload = () => { |
| 161 | + el("host").innerText = document.location.host; |
| 162 | +}; |
0 commit comments