Hi folks, need a little help with my first module which is mainly AI generated because my programming skills are slim to none (learning by failure ;).
Here´s the idea: Showing the top-played tracks scrobbled via LastFM including the coverart sorted by week, month, year or overall.
The module is already working and configurable via custom.css but those damn covers aren´t showing up. AI doesn´t work here anymore. I need some real skills and therfore I´m asking for a little help. Maybee you like the idea of that module too and could provide some support! That would be highly appreciated!
Here is what I´ve got so far:
MMM-LastfmTopTracks.js
Module.register("MMM-LastfmTopTracks", {
defaults: {
apiKey: "YOUR_LASTFM_API_KEY",
username: "YOUR_LASTFM_USERNAME",
period: "7day",
limit: 10,
rotateInterval: 10000,
fallbackImage: "modules/MMM-LastfmTopTracks/default_cover.png"
},
start() {
this.tracks = [];
this.currentIndex = 0;
this.loaded = false;
this.getTopTracks();
this.scheduleRotation();
},
scheduleRotation() {
setInterval(() => {
if (this.loaded && this.tracks.length > 0) {
this.currentIndex = (this.currentIndex + 1) % this.tracks.length;
this.updateDom();
}
}, this.config.rotateInterval);
},
getTopTracks() {
const url = `https://ws.audioscrobbler.com/2.0/?method=user.gettoptracks&user=${this.config.username}&api_key=${this.config.apiKey}&format=json&period=${this.config.period}&limit=${this.config.limit}`;
fetch(url)
.then(res => res.json())
.then(data => {
this.tracks = data.toptracks?.track || [];
this.loaded = true;
this.updateDom();
})
.catch(err => {
console.error("MMM-LastfmTopTracks: API error", err);
});
},
getDom() {
const wrapper = document.createElement("div");
wrapper.className = "MMM-LastfmTopTracks";
const header = document.createElement("header");
header.innerText = "Last FM Top Tracks";
header.className = "module-header";
wrapper.appendChild(header);
if (!this.loaded) {
const loading = document.createElement("div");
loading.innerHTML = "Lade Top-Tracks...";
loading.className = "track-loading";
wrapper.appendChild(loading);
return wrapper;
}
if (this.tracks.length === 0) {
const noData = document.createElement("div");
noData.innerHTML = "Keine Daten verfügbar.";
noData.className = "track-nodata";
wrapper.appendChild(noData);
return wrapper;
}
const track = this.tracks[this.currentIndex];
const title = track.name;
const artist = track.artist.name;
const playcount = track.playcount;
const imgObj = track.image?.find(img => img.size === "extralarge") || {};
const rawUrl = imgObj["#text"]?.trim() || "";
const imgUrl = rawUrl.startsWith("http://") ? rawUrl.replace("http://", "https://") : rawUrl;
const finalImg = imgUrl || this.config.fallbackImage;
const container = document.createElement("div");
container.className = "track-container";
const text = document.createElement("div");
text.className = "track-info";
text.innerHTML = `<strong>${title}</strong><br>${artist}<br><small>${playcount} Plays</small>`;
const img = document.createElement("img");
img.src = finalImg;
img.className = "track-cover";
container.appendChild(text);
container.appendChild(img);
wrapper.appendChild(container);
return wrapper;
},
getStyles() {
return ["MMM-LastfmTopTracks.css"];
}
});
MMM-LastfmTopTracks.css
.MMM-LastfmTopTracks {
max-width: 300px;
color: #ccc;
}
.MMM-LastfmTopTracks .module-header {
color: #fff;
font-weight: bold;
font-size: 1.2em;
margin-bottom: 10px;
}
.track-container {
display: flex;
justify-content: space-between;
align-items: center;
}
.track-info {
flex-grow: 1;
padding-right: 10px;
}
.track-cover {
width: 80px;
height: 80px;
object-fit: cover;
border-radius: 5px;
}
Any ideas how I get those cover-suckers out of hiding ;) ? Thanks in advance and kind regards!