Last active
April 13, 2020 08:48
-
-
Save sn123/dc903fb6d19ad793505fecc1b52536f7 to your computer and use it in GitHub Desktop.
A #Mopidy client for Mustard. Displays the currently playing tracks along with album art & tags from last.fm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<div class="center mopidy"> | |
<h4>Now Playing</h4> | |
<div :v-if="model.name"> | |
<img :src="model.albumArt" /> | |
<div> | |
<h4>{{ model.artist }}</h4> | |
<h3>{{ model.name }}</h3> | |
<p>{{ model.tags }}</p> | |
</div> | |
</div> | |
</div> | |
</template> | |
<script lang="ts"> | |
import { Component, Prop, Vue } from "vue-property-decorator"; | |
import { scriptLoader } from "../script-loader"; | |
type TrackModel = { | |
name: string; | |
artist: string; | |
album: string; | |
albumArt: string; | |
tags: string; | |
}; | |
type NameType = { name: string }; | |
type TrackInfo = { name: string; album: NameType; artists: Array<NameType> }; | |
type T1Track = { track: TrackInfo }; | |
type MopidyTrack = { tl_track: T1Track }; | |
@Component | |
export default class MopidyClient extends Vue { | |
@Prop() private mopidyWsUrl?: string; | |
private lastFmKey?: string = process.env.VUE_APP_LASTFM_KEY; | |
private model: TrackModel = { | |
name: "", | |
artist: "", | |
album: "", | |
albumArt: "https://via.placeholder.com/150", | |
tags: "" | |
}; | |
mounted() { | |
scriptLoader( | |
"https://github.com/mopidy/mopidy.js/releases/download/v1.1.0/mopidy-1.1.0.js", | |
true | |
).then(() => { | |
const options = { | |
webSocketUrl: this.mopidyWsUrl | |
}, | |
mopidy = new (window as any).Mopidy(options); | |
mopidy.on("event:trackPlaybackStarted", (track: MopidyTrack) => { | |
// eslint-disable-next-line @typescript-eslint/camelcase | |
const { tl_track, ...rest } = track, | |
// eslint-disable-next-line @typescript-eslint/camelcase | |
trackInfo = tl_track, | |
nowPlaying: TrackModel = { | |
name: trackInfo.track.name, | |
album: trackInfo.track.album ? trackInfo.track.album.name : "", | |
artist: Array.isArray(trackInfo.track.artists) | |
? trackInfo.track.artists[0].name | |
: "", | |
albumArt: "https://via.placeholder.com/150", | |
tags: "" | |
}; | |
this.model = { ...nowPlaying }; | |
if ((this.lastFmKey || "").length > 0) { | |
(async () => { | |
const lastQuery = { | |
format: "json", | |
method: "album.getInfo", | |
album: this.model.album, | |
artist: this.model.artist, | |
// eslint-disable-next-line @typescript-eslint/camelcase | |
api_key: this.lastFmKey | |
}, | |
qs = this.objectToQs(lastQuery); | |
try { | |
const response = await fetch( | |
`https://ws.audioscrobbler.com/2.0/?${qs}`, | |
{ | |
method: "GET" | |
} | |
), | |
jsonResponse = await response.json(); | |
if (jsonResponse.error) { | |
// console.warn("received error", jsonResponse); | |
return; | |
} | |
if ( | |
!jsonResponse.album || | |
!Array.isArray(jsonResponse.album.image) | |
) { | |
// console.warn("didn't get album", jsonResponse.album); | |
return; | |
} | |
const image = (jsonResponse.album.image as []).find( | |
(el: any) => el.size === "large" | |
), | |
tags = (jsonResponse.album.tags.tag as []) | |
.map((t: any) => t.name) | |
.join(", "); | |
if (!image) { | |
return; | |
} | |
const albumArt = image["#text"]; | |
this.model = { ...this.model, albumArt, tags }; | |
} catch (err) { | |
// console.warn(err); | |
} | |
})(); | |
} | |
}); | |
}); | |
} | |
objectToQs(object: Record<string, any>) { | |
return Object.keys(object) | |
.map(key => { | |
return encodeURIComponent(key) + "=" + encodeURIComponent(object[key]); | |
}) | |
.join("&"); | |
} | |
} | |
</script> | |
<!-- Add "scoped" attribute to limit CSS to this component only --> | |
<style scoped lang="scss"> | |
.mopidy { | |
background: #2a9d8f; | |
flex-direction: column; | |
> h4 { | |
padding-bottom: 10px; | |
} | |
> div { | |
display: flex; | |
flex-direction: row; | |
> img { | |
margin-left: 5px; | |
border-radius: 5px; | |
max-width: 150px; | |
} | |
} | |
} | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
A Now playing widget for Mustard .
Setting up
Install this gist by running
$ mustard -gist=dc903fb6d19ad793505fecc1b52536f7
Configuration
In App.Vue add this component and set your Mopidy WebSockets Url. If the Mopidy has Cors protection, than mustard host and port have to be whitelisted in mopidy http configuration.
Album info and art
Requires last.fm API key - these need to be set in .env file in /client folder:
.env:
VUE_APP_LASTFM_KEY=VUE_APP_LASTFM_KEY