From 198f18e5a274f34afc4db043363a679c8fbbc6f9 Mon Sep 17 00:00:00 2001 From: Randall Winkhart Date: Sat, 10 Oct 2026 18:19:07 -0400 Subject: [PATCH] Extend lookupMap to contain artist to album mapping --- README.md | 20 ++++++++++++-------- src/pkjs/index.js | 46 ++++++++++++++++++++++++++++------------------ 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index d8dfd80..9d5f45d 100644 --- a/README.md +++ b/README.md @@ -2,22 +2,26 @@ Control Jellyfin music playback from your Pebble with an iPod-like interface. # Supported Phone Clients -jellyPod requires a Jellyfin client to be running on your phone to handle playback. Not all clients support all features of jellyPod. Tested clients are detailed below. +`jellyPod` requires a Jellyfin client to be running on your phone to handle playback. Not all clients support all features of `jellyPod`. Tested clients are detailed below. ## Legend -- ✅ = verified (supports all features of jellyPod and prefers offline playback of local media over online streaming) +- ✅ = verified (supports all features of `jellyPod` and prefers offline playback of local media over online streaming) - ⚠️ = usable (functional with missing features/bugs detailed below) -- ⛔ = unsupported (does not work well or at all with jellyPod) +- ⛔ = unsupported (does not work well or at all with `jellyPod`) ## iOS - ✅ [Finamp Beta/Redesign](https://github.com/finamp-app/finamp/tree/redesign) (last tested release: 1.0.1) -# Special Features +# Opinionated Design Philosophy +I made `jellyPod` to suite my tastes. Everything listed below is non-negotiable and will not be changed. +- No playlist support + - Playlists encourage poor listening habits that lead to portions of your library being ignored while others are overplayed - True sequential playback - Your music continues to play in order even after you exhaust the tracks on the selected album and by the current artist - The next album is chosen by release date and the next artist is chosen alphanumerically +- Shuffle at every scope + - You can easily shuffle artists, albums (from a given artist), or all tracks - Multi-client/device support - - jellyPod is not just a remote for your phone - - Determines which client to control based on the most recently active session under your Jellyfin user + - `jellyPod` is not just a remote for your phone + - The client to control is determined by your most recently active session under your Jellyfin user - Classic design - Inspired by the 4th generation iPod -- Support for ALL Pebbles -- Absolutely NO AI was used in the creation of this application +- NONE of this application, nor any art/assets used in its creation, was generated by AI diff --git a/src/pkjs/index.js b/src/pkjs/index.js index 3c03909..89e7748 100644 --- a/src/pkjs/index.js +++ b/src/pkjs/index.js @@ -21,7 +21,7 @@ Pebble.addEventListener("ready", function () { // get library response from Jellyfin callAPI("GET", cfg.PRIV_BASE_URL + "/Items?userId=" + userId + "&recursive=true&includeItemTypes=Audio&sortBy=AlbumArtist,Artist,ProductionYear,Album,IndexNumber,Name", function (libResp) { - if (libResp === null) { return; } + if (libResp === null) return; const libObj = JSON.parse(libResp); // format response for jellyPod @@ -30,31 +30,41 @@ Pebble.addEventListener("ready", function () { const lookupMap = new Map(); let lastAlbumInfo = {AlbumArtist: "", AlbumName: "", AlbumYear: 0}; for (const track of libObj.Items) { - if (!track.Name) { - continue; - } + if (!track.Name) continue; + let albumArtist = "unknown artist"; if (track.AlbumArtist) { albumArtist = track.AlbumArtist; } else if (track.Artists.length > 0) { - albumArtist = track.Artists[0] + albumArtist = track.Artists[0]; } const albumName = track.Album ? track.Album : "unknown album"; - if (lastAlbumInfo.AlbumArtist != albumArtist) { - firstChar = Array.from(albumArtist)[0].toUpperCase(); + + // register album in lookupMap whenever the album changes (new artist OR new album) + if (lastAlbumInfo.AlbumArtist !== albumArtist || lastAlbumInfo.AlbumName !== albumName) { + const firstChar = Array.from(albumArtist)[0].toUpperCase(); if (!lookupMap.has(firstChar)) { - lookupMap.set(firstChar, [albumArtist]); - } else { - lookupMap.get(firstChar).push(albumArtist); - }; - if (lastAlbumInfo.AlbumName != albumName) { - lastAlbumInfo = { - AlbumArtist: albumArtist, - AlbumName: track.Album ? track.Album : "unknown album", - AlbumYear: track.ProductionYear ? track.ProductionYear : 1970 - }; + lookupMap.set(firstChar, new Map()); + } + const artistMap = lookupMap.get(firstChar); + if (!artistMap.has(albumArtist)) { + artistMap.set(albumArtist, []); + } + const albums = artistMap.get(albumArtist); + if (!albums.includes(albumName)) { + albums.push(albumName); } } + + // keep lastAlbumInfo in sync for every album change + if (lastAlbumInfo.AlbumArtist !== albumArtist || lastAlbumInfo.AlbumName !== albumName) { + lastAlbumInfo = { + AlbumArtist: albumArtist, + AlbumName: albumName, + AlbumYear: track.ProductionYear ? track.ProductionYear : 1970 + }; + } + jpTracksArr.push({ TrackName: track.Name, JfId: track.Id, @@ -64,7 +74,7 @@ Pebble.addEventListener("ready", function () { //console.log(jpTracksArr); //console.log("Items:", libObj.Items.length, "Tracks:", jpTracksArr.length); - //console.log(...lookupMap); + console.log(JSON.stringify([...lookupMap].map(([ch, m]) => [ch, [...m]]))); Pebble.sendAppMessage({ PKJS_READY: 1 }); });