From 9faa952bc40ec2c35a2f2d57b0c5fdc3053da1af Mon Sep 17 00:00:00 2001 From: Chaphasilor Date: Sat, 7 Dec 2024 16:39:28 +0100 Subject: [PATCH 01/15] improve onboarding - Playback Reporting server restart - show placeholder based on config --- src/auth.js | 4 ++-- src/jelly-helper.js | 12 ++++++++++ src/onboarding.js | 53 +++++++++++++++++++++++++++++++++------------ src/setup.js | 2 +- 4 files changed, 54 insertions(+), 17 deletions(-) diff --git a/src/auth.js b/src/auth.js index f2becf6..7753af2 100644 --- a/src/auth.js +++ b/src/auth.js @@ -66,7 +66,7 @@ export default class Auth { const token = `MediaBrowser Client="${deviceProfile.clientName}", Device="${deviceProfile.deviceName}", DeviceId="${deviceProfile.deviceId}", Version="${deviceProfile.clientVersion}", Token="${accessToken}"`; - this.config.defaultHeaders['X-Emby-Authorization'] = token; + this.config.defaultHeaders['Authorization'] = token; } async fetchUsers() { @@ -138,7 +138,7 @@ export default class Auth { const json = await response.json() if (response.status !== 200) { - throw new Error(`Authentication failed: ${response.json()}`); + throw new Error(`Authentication failed: ${JSON.stringify(json)}`); } this.config.user = { diff --git a/src/jelly-helper.js b/src/jelly-helper.js index 9ea8e56..c765aaa 100644 --- a/src/jelly-helper.js +++ b/src/jelly-helper.js @@ -295,6 +295,18 @@ export default class JellyHelper { return response.ok } + // requires admin permissions + async restartServer() { + + const response = await fetch(`${this.auth.config.baseUrl}/System/Restart`, { + method: `POST`, + headers: { + ...this.auth.config.defaultHeaders, + }, + }) + return response.ok + } + } function blurhashToDataURI(blurhash) { diff --git a/src/onboarding.js b/src/onboarding.js index 87a0fe7..6bcd373 100644 --- a/src/onboarding.js +++ b/src/onboarding.js @@ -24,6 +24,8 @@ export const state = reactive({ importingLastYearsReport: false, staleReport: false, progress: 0, + waitingForRestart: false, + playbackReportingInspectionAttempts: 0, auth: null, error: null, playbackReportingInspectionResult: null, @@ -52,6 +54,7 @@ export async function init(auth) { }) state.auth = auth + state.server.url = state.auth?.config?.baseUrl // MediaQueryList const darkModePreference = window.matchMedia("(prefers-color-scheme: dark)"); @@ -85,7 +88,11 @@ export async function init(auth) { // determine which view to show await checkPlaybackReportingSetup() } else { - state.currentView = `placeholder` + if (JSON.parse(import.meta.env.VITE_SHOW_PLACEHOLDER)) { + state.currentView = `placeholder` + } else { + state.currentView = `start` + } } } @@ -150,7 +157,13 @@ const viewStart = html` ` @@ -429,7 +446,7 @@ async function login() { async function loginAuthToken() { const token = document.querySelector(`#onboarding-auth-token`).value try { - let userInfo = await loginViaAuthToken(state.auth, username, token) + let userInfo = await loginViaAuthToken(state.auth, token) // state.currentView = `importLastYearsReport` checkPlaybackReportingSetup() } catch (err) { @@ -470,21 +487,22 @@ function inspectPlaybackReportingSetup(playbackReportingSetup, nextScreen) { } else if (playbackReportingSetup.restartRequired) { inspection.valid = false inspection.issue = `The Playback Reporting plugin is installed, but the Jellyfin server needs to be restarted in order to activate it.` + state.waitingForRestart = false if (window.helper && state.auth.config.user.isAdmin) { inspection.action = html` -

For most setups, the server will automatically restart after shutting it down.

+ }}">${() => state.waitingForRestart ? `Server is restarting...` : `Restart Jellyfin Server`} ` } } else if (playbackReportingSetup.disabled) { @@ -572,6 +590,7 @@ async function checkPlaybackReportingSetup(nextScreen = `importLastYearsReport`) const inspection = inspectPlaybackReportingSetup(playbackReportingSetup, nextScreen) console.log(`inspection:`, inspection) + state.waitingForRestart = false if (!inspection.valid) { state.currentView = `playbackReportingIssues` @@ -582,9 +601,15 @@ async function checkPlaybackReportingSetup(nextScreen = `importLastYearsReport`) } catch (err) { console.error(`Failed to check the playback reporting setup, continuing without it:`, err) - state.currentView = nextScreen + if (state.playbackReportingInspectionAttempts > 3) { + state.currentView = nextScreen + } else { + setTimeout(() => { + checkPlaybackReportingSetup(nextScreen) + }, 5000) + } } - + } const viewPlaybackReportingIssues = html` diff --git a/src/setup.js b/src/setup.js index 24bc137..79be941 100644 --- a/src/setup.js +++ b/src/setup.js @@ -9,7 +9,7 @@ export async function connectToServer(auth, serverUrl) { console.info(`Users:`, userInfo) } catch (err) { console.error(`Error while connecting to the server:`, err) - throw new Error(`Error while connecting to the server. Make sure your using the same protocol (https or http) as your server is using. Make sure you're not using a local IP address or mDNS hostname. For example, you could use your server's Tailscale IP address, if you use Tailscale as your VPN.`) + throw new Error(`Error while connecting to the server. Make sure your using the same protocol (https or http) as your server is using. Make sure you're not using a local IP address or mDNS hostname. For example, you could use your server's Tailscale IP address, if you use Tailscale as your VPN. Original Error: ${err}`) } return userInfo } From 7ad6f227b5838de34db64e1d5137693589bc79b1 Mon Sep 17 00:00:00 2001 From: Chaphasilor Date: Sat, 7 Dec 2024 16:39:59 +0100 Subject: [PATCH 02/15] add config, housekeeping --- index.html | 62 +-------- main.js | 335 +++------------------------------------------ public/config.json | 9 ++ 3 files changed, 31 insertions(+), 375 deletions(-) create mode 100644 public/config.json diff --git a/index.html b/index.html index 40793e4..28019d1 100644 --- a/index.html +++ b/index.html @@ -7,11 +7,11 @@ - + - + @@ -25,64 +25,6 @@
- -
diff --git a/main.js b/main.js index c0c52bb..a982310 100644 --- a/main.js +++ b/main.js @@ -29,44 +29,36 @@ document.querySelector('#app').innerHTML = ` let userInfo = null; let helper = null; -const connectToServerButton = document.querySelector(`#connectToServer`) -const serverUrl = document.querySelector(`#serverUrl`) -const serverConfig = document.querySelector(`#server-config`) -const userSelect = document.querySelector(`#user-select`) -const userLogin = document.querySelector(`#user-login`) -const usernameInput = document.querySelector(`#username-input`) -const authTokenInput = document.querySelector(`#auth-token-input`) -const passwordInput = document.querySelector(`#password-input`) -const username = document.querySelector(`#username`) -const password = document.querySelector(`#password`) -const authToken = document.querySelector(`#auth-token`) -const authenticateUser = document.querySelector(`#authenticateUser`) -const showReport = document.querySelector(`#show-report`) -const generateReport = document.querySelector(`#generate-report`) -const loadingSpinner = document.querySelector(`#loading-spinner`) -const output = document.querySelector(`#output`) -const loggedInInfo = document.querySelector(`#logged-in-info`) -const logOutButton = document.querySelector(`#log-out`) -const loggedInUserInfo = document.querySelector(`#logged-in-user`) -const loggedInServerInfo = document.querySelector(`#logged-in-server`) -const infoOutput = document.querySelector(`#info`) - -let selectedUsername = `` let featuresInitialized = false -let staleReport = false window.onload = async () => { + let config = { + targetRange: { + start: `${import.meta.env.VITE_TARGET_YEAR}-01-01`, + end: `${import.meta.env.VITE_TARGET_YEAR}-12-31` + }, + server: { + url: ``, + apiKey: null, + } + } + try { + config = await (await fetch(`/config.json`)).json() + } catch (err) { + console.warn(`Couldn't fetch config:`, err); + } + console.log(`config:`, config) + window.jellyfinRewind = jellyfinRewind - console.log(`target year:`, import.meta.env.VITE_TARGET_YEAR) + // console.log(`target year:`, import.meta.env.VITE_TARGET_YEAR) console.log(`commit hash:`, __COMMITHASH__) + jellyfinRewind.auth.config.baseUrl = config?.server?.url ?? `` + if (jellyfinRewind.auth.restoreSession()) { console.info(`Session restored!`) - enableLogout() - serverConfig.classList.add(`hidden`) - init() } helper = new JellyHelper(jellyfinRewind.auth) @@ -77,293 +69,6 @@ window.onload = async () => { } -function enableLogout() { - loggedInInfo.classList.remove(`hidden`) - logOutButton.addEventListener(`click`, () => { - jellyfinRewind.auth.destroySession() - window.location.reload() - }) - - loggedInUserInfo.innerText = jellyfinRewind.auth.config.user.name - loggedInServerInfo.innerText = jellyfinRewind.auth.config.serverInfo.ServerName -} - -connectToServerButton.addEventListener(`click`, connectToServer) -serverUrl.addEventListener(`keydown`, (event) => { - if (event.key === `Enter`) { - connectToServer() - } -}) - -async function connectToServer() { - try { - await jellyfinRewind.auth.connectToServer(serverUrl.value) - userInfo = await jellyfinRewind.auth.fetchUsers() - console.info(`Connected to server!`) - console.info(`Users:`, userInfo) - serverConfig.classList.add(`hidden`) - showUsers() - } catch (err) { - console.error(`Error while connecting to the server:`, err) - infoOutput.innerText = `Error while connecting to the server. Make sure your using the same protocol (https or http) as your server is using. Make sure you're not using a local IP address or mDNS hostname. For example, you could use your server's Tailscale IP address, if you use Tailscale as your VPN.` - } -} - -function showUsers() { - - userSelect.classList.remove(`hidden`) - - html` - ${() => userInfo.map(user => html` -
  • - -
  • - `)} - -
  • - -
  • - -
  • - -
  • - `(userSelect) - - // manualUser.addEventListener(`click`, () => { - // userLogin.classList.remove(`hidden`) - // usernameInput.classList.remove(`hidden`) - // userSelect.classList.add(`hidden`) - // username.addEventListener(`keyup`, () => { - // selectedUsername = username.value - // }) - // authenticateUser.removeEventListener(`click`, login(``)) - // authenticateUser.addEventListener(`click`, login(``)) - // password.removeEventListener(`keydown`, login(``)) - // password.addEventListener(`keydown`, login(``)) - // }) - - // userInfo.forEach(user => { - // const li = document.createElement(`li`) - // const button = document.createElement(`button`) - // button.textContent = user.Name - // button.setAttribute(`data-user-id`, user.Id) - // button.classList.add(`rounded-md`, `p-2`, `cursor-pointer`, `focus:bg-blue-200`, `hover:bg-blue-200`) - // button.addEventListener(`click`, () => { - // userLogin.classList.remove(`hidden`) - // selectedUsername = user.Name - // console.info(`User selected:`, user) - // userSelect.classList.add(`hidden`) - // authenticateUser.removeEventListener(`click`, login(user.Id)) - // authenticateUser.addEventListener(`click`, login(user.Id)) - // password.removeEventListener(`keydown`, login(user.Id)) - // password.addEventListener(`keydown`, login(user.Id)) - // }) - // li.appendChild(button) - // userSelect.appendChild(li) - // }) -} - -const login = (userId) => async (event) => { - - if (event.type !== `click` && event.key !== `Enter`) { - return - } - - try { - - await jellyfinRewind.auth.authenticateUser(selectedUsername, password.value) - console.info(`Successfully logged in as ${selectedUsername}`) - jellyfinRewind.auth.saveSession() - enableLogout() - - userLogin.classList.add(`hidden`) - init() - - } catch (err) { - console.error(`Error while logging in:`, err) - } -} - -const loginWithAuthToken = () => async (event) => { - - if (event.type !== `click` && event.key !== `Enter`) { - return - } - - try { - - await jellyfinRewind.auth.authenticateUserViaToken(authToken.value) - console.info(`Successfully logged in as ${jellyfinRewind.auth.config.name}`) - jellyfinRewind.auth.saveSession() - enableLogout() - - userLogin.classList.add(`hidden`) - init() - - } catch (err) { - console.error(`Error while logging in:`, err) - } -} - -function init() { - showReport.addEventListener(`click`, async () => { - let rewindReportData = null - try { - loadingSpinner.classList.remove(`hidden`) - rewindReportData = { - jellyfinRewindReport: jellyfinRewind.restoreRewindReport() - } - - if (rewindReportData.jellyfinRewindReport.commit !== __COMMITHASH__) { - staleReport = true - } - // check if the report is for the previous year and it's after February - if (rewindReportData.jellyfinRewindReport.year < new Date().getFullYear() && new Date().getMonth() > 1) { - staleReport = true - } - - loadingSpinner.classList.add(`hidden`) - } catch (err) { - console.warn(`Couldn't restore Rewind report:`, err) - console.info(`Generating new report...`) - rewindReportData = await generateRewindReport() - } - - if (staleReport) { - infoOutput.innerText = `The stored rewind report is stale. Please re-generate it for the best experience.` - } - - // showRewindReport(rewindReport) - initializeFeatureStory(rewindReportData) - - }) - generateReport.addEventListener(`click`, async () => { - console.info(`Generating new report...`) - featuresInitialized = false // reset features - let rewindReportData = await generateRewindReport() - - // showRewindReport(rewindReport) - initializeFeatureStory(rewindReportData) - - }) - generateReport.classList.remove(`hidden`) - showReport.classList.remove(`hidden`) -} - -async function generateRewindReport() { - - let reportData - try { - - loadingSpinner.classList.remove(`hidden`) - reportData = await jellyfinRewind.generateRewindReport({ - year: Number(import.meta.env.VITE_TARGET_YEAR) - }) - console.info(`Report generated successfully!`) - loadingSpinner.classList.add(`hidden`) - - } catch (err) { - console.error(`Error while generating the report:`, err) - } - - try { - jellyfinRewind.saveRewindReport() - console.info(`Report saved successfully!`) - } catch (err) { - console.error(`Couldn't save Rewind report:`, err) - } - - return reportData - -} - -function testShowRewindReport(report) { - - output.value = JSON.stringify(report, null, 2) - - const topSongByDuration = report.jellyfinRewindReport.tracks?.[`topTracksByPlayCount`]?.[0] - - if (topSongByDuration) { - document.querySelector('#app').innerHTML += ` -
    -

    Your Top Song of the Year

    -
    - - ${topSongByDuration?.name} - by ${topSongByDuration.artistsBaseInfo.reduce((acc, cur, index) => index > 0 ? `${acc} & ${cur.name}` : cur.name, ``)} -
    -
    - ` - const testImage = document.querySelector(`#test-image`) - helper.loadImage(testImage, topSongByDuration.image) - } - -} - -function initializeFeatureStory(report) { - - Features.openFeatures() - - Features.init(report, helper, jellyfinRewind.auth) - if (!featuresInitialized) { - Features.render() - featuresInitialized = true - } - -} - function downloadRewindReportData(reportData, skipVerification) { if (reportData.rawData || skipVerification || confirm(`The report you're about to download is incomplete and missing some data. Please re-generate and download the report without reloading the page in-between. Do you want to download the incomplete report anyway?`)) { const reportDataString = JSON.stringify(reportData, null, 2) diff --git a/public/config.json b/public/config.json new file mode 100644 index 0000000..ab14fd5 --- /dev/null +++ b/public/config.json @@ -0,0 +1,9 @@ +{ + "targetRange": { + "start": "2024-01-01", + "end": "2024-12-31" + }, + "server": { + "url": "http://192.168.31.40:30013" + } +} From 7d002089a8d7ae973fb8049f33198d426541feca Mon Sep 17 00:00:00 2001 From: Chaphasilor Date: Sat, 7 Dec 2024 16:41:16 +0100 Subject: [PATCH 03/15] fix plugin URLs --- src/features.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/features.js b/src/features.js index 85afe78..22941f0 100644 --- a/src/features.js +++ b/src/features.js @@ -1219,10 +1219,10 @@ function showOverlayDataAccuracy() {

    However, if you have the "Playback Reporting" plugin installed, significantly more information can be collected, such as the date and durations of each playback. This results in better stats, although it isn't perfect either. Playback reporting depends on applications properly reporting the current playback states, and currently most music players that are compatible with Jellyfin seem to struggle with this in one way or another. Offline playback is not recorded at all.

    Still, the best solution is to install the Playback Reporting plugin into your Jellyfin server if you haven't done so already. It won't take longer than 2 minutes, so why not do it right now? Your Jellyfin Rewind isn't going anywhere!

    ${() => state.auth.config?.user?.isAdmin ? html` - Open Plugins Page! + Open Plugins Page! ` : html` Open Plugins Page! -

    You're not logged in with an administrator account, but you need to be an admin in order to install plugins. If you are logged into Jellyfin with an admin account, you can click here to open the plugins page. If nothing happens or Jellyfin just keeps loading, that means the logged-in account is not an administrator.

    +

    You're not logged in with an administrator account, but you need to be an admin in order to install plugins. If you are logged into Jellyfin with an admin account, you can click here to open the plugins page. If nothing happens or Jellyfin just keeps loading, that means the logged-in account is not an administrator.

    `}

    By default, the Playback Reporting plugin only stores the last 3 months worth of playback data, so you definitely want to change that in the settings. I'd suggest keeping at least the last two years, just to be safe. The button below will take you directly to the settings page.

    ${() => state.auth.config?.user?.isAdmin ? html` @@ -1247,14 +1247,14 @@ function showOverlayFeatureUnavailableMissingPlaybackReporting() {

    This feature depends on the 'Playback Reporting' plugin, which is either not installed on your Jellyfin server or hasn't been installed for long enough.

    You can install the Playback Reporting plugin into your Jellyfin server by clicking the button below. It won't take longer than 5 minutes, so why not do it right now? Your Jellyfin Rewind isn't going anywhere!

    ${() => state.auth.config?.user?.isAdmin ? html` - Open Plugins Page! + Open Plugins Page! ` : html` Open Plugins Page! -

    You're not logged in with an administrator account, but you need to be an admin in order to install plugins. If you are logged into Jellyfin with an admin account, you can click here to open the plugins page. If nothing happens or Jellyfin just keeps loading, that means the logged-in account is not an administrator.

    +

    You're not logged in with an administrator account, but you need to be an admin in order to install plugins. If you are logged into Jellyfin with an admin account, you can click here to open the plugins page. If nothing happens or Jellyfin just keeps loading, that means the logged-in account is not an administrator.

    `}

    By default, the Playback Reporting plugin only stores the last 3 months worth of playback data, so you definitely want to change that in the settings. I'd suggest keeping at least the last two years, just to be safe. The button below will take you directly to the settings page.

    ${() => state.auth.config?.user?.isAdmin ? html` - Open Settings + Open Settings ` : html` Open Settings!

    You're not logged in with an administrator account, but you need to be an admin in order to install plugins. If you are logged into Jellyfin with an admin account and already have the Playback Reporting plugin installed, you can click here to open the plugin settings. If nothing happens or Jellyfin just keeps loading, that means the logged-in account is not an administrator.

    From 22cb67c1a20dc97b926042d34767da1c21fa552a Mon Sep 17 00:00:00 2001 From: Chaphasilor Date: Sat, 7 Dec 2024 16:41:28 +0100 Subject: [PATCH 04/15] rewording - mostly song -> track --- src/aggregate.js | 2 +- src/features.js | 164 +++++++++++++++++++++++------------------------ 2 files changed, 83 insertions(+), 83 deletions(-) diff --git a/src/aggregate.js b/src/aggregate.js index 6e1868f..6b8cf99 100644 --- a/src/aggregate.js +++ b/src/aggregate.js @@ -19,7 +19,7 @@ export function generateTopTrackInfo(itemInfo, playbackReportJSON) { // } const track = new Track({ - name: item.Name || `Unknown Song`, + name: item.Name || `Unknown Track`, id: item.Id, artistsBaseInfo: item.ArtistItems.map(artist => ({id: artist.Id, name: artist.Name || `Unknown Artist`})), albumBaseInfo: { diff --git a/src/features.js b/src/features.js index 22941f0..21b11c3 100644 --- a/src/features.js +++ b/src/features.js @@ -163,7 +163,7 @@ state.features = [

    Welcome to
    Jellyfin Rewind ${() => state.rewindReport?.year}!

    -

    This is your personal overview over your listening habits of the past year. See your most-listened songs, artists and albums, as well as other awesome stats!

    +

    This is your personal overview over your listening habits of the past year. See your most-listened tracks, artists and albums, as well as other awesome stats!

    If you have any questions or feedback, please reach out to me on GitHub or Reddit.

    @@ -228,10 +228,10 @@ state.features = [ `, `bg-[#00A4DC]/10 dark:bg-[#000B25] dark:text-white`), - // top song - buildFeature(`top song`, html` + // top track + buildFeature(`top track`, html`
    -

    Your Top Song
    of ${() => state.rewindReport?.year}:

    +

    Your Top Track
    of ${() => state.rewindReport?.year}:

    @@ -255,10 +255,10 @@ state.features = [
    `), - // top songs of the year - buildFeature(`top songs of the year`, html` + // top tracks of the year + buildFeature(`top tracks of the year`, html`
    -

    Your Top Songs
    of the year

    +

    Your Top Tracks
    of the year

      ${() => state.rewindReport.tracks?.[state.settings.rankingMetric]?.slice(0, 5).map((track, index) => html`
    1. @@ -389,7 +389,7 @@ state.features = [
    Streamed ${() => showAsNumber(state.rewindReport.artists?.[state.settings.rankingMetric]?.[0]?.playCount[state.settings.dataSource])} times.
    -
    Listened to ${() => showAsNumber(state.rewindReport.artists?.[state.settings.rankingMetric]?.[0]?.uniqueTracks)} unique songs
    for ${() => showAsNumber(state.rewindReport.artists?.[state.settings.rankingMetric]?.[0]?.totalPlayDuration[state.settings.dataSource].toFixed(0))} minutes.
    +
    Listened to ${() => showAsNumber(state.rewindReport.artists?.[state.settings.rankingMetric]?.[0]?.uniqueTracks)} unique tracks
    for ${() => showAsNumber(state.rewindReport.artists?.[state.settings.rankingMetric]?.[0]?.totalPlayDuration[state.settings.dataSource].toFixed(0))} minutes.
    @@ -420,7 +420,7 @@ state.features = [ -
    ${() => showAsNumber(artist.uniqueTracks)} songs
    +
    ${() => showAsNumber(artist.uniqueTracks)} tracks
    @@ -477,15 +477,15 @@ state.features = [
    -
    ${() => showAsNumber(state.rewindReport?.libraryStats?.tracks?.favorite)} favorite songs.
    -
    ${() => showAsNumber(state.rewindReport?.libraryStats?.tracks?.total)} unique songs.
    +
    ${() => showAsNumber(state.rewindReport?.libraryStats?.tracks?.favorite)} favorite tracks.
    +
    ${() => showAsNumber(state.rewindReport?.libraryStats?.tracks?.total)} unique tracks.
    ${() => showAsNumber(state.rewindReport?.libraryStats?.albums?.total)} albums.
    ${() => showAsNumber(state.rewindReport?.libraryStats?.artists?.total)} artists.
    Impressive.
    -
    Average song length: ${() => showAsNumber(state.rewindReport?.libraryStats?.trackLength?.mean.toFixed(0))} seconds.
    +
    Average track length: ${() => showAsNumber(state.rewindReport?.libraryStats?.trackLength?.mean.toFixed(0))} seconds.
    (Median: ${() => showAsNumber(state.rewindReport?.libraryStats?.trackLength?.median.toFixed(0))})
    Shortest: ${() => showAsNumber(state.rewindReport?.libraryStats?.trackLength?.min.toFixed(0))} seconds.
    Longest: ${() => showAsNumber(state.rewindReport?.libraryStats?.trackLength?.max.toFixed(0))} seconds.
    @@ -604,12 +604,12 @@ state.features = [

    ...compared to last year!

    -
    This year, you played ${() => state.extraFeatures.listeningActivityDifference ? showAsNumber(Math.abs(state.rewindReport?.featureDelta?.listeningActivityDifference?.totalPlays[state.settings.dataSource]).toFixed(0)) : `???`} songs ${() => (!state.extraFeatures.listeningActivityDifference || state.rewindReport?.featureDelta?.listeningActivityDifference?.totalPlays[state.settings.dataSource] >= 0) ? `more` : `less`} than in ${() => state.rewindReport?.year}.
    +
    This year, you played ${() => state.extraFeatures.listeningActivityDifference ? showAsNumber(Math.abs(state.rewindReport?.featureDelta?.listeningActivityDifference?.totalPlays[state.settings.dataSource]).toFixed(0)) : `???`} tracks ${() => (!state.extraFeatures.listeningActivityDifference || state.rewindReport?.featureDelta?.listeningActivityDifference?.totalPlays[state.settings.dataSource] >= 0) ? `more` : `less`} than in ${() => state.rewindReport?.year}.
    Additionally, you listened to -
    ${() => state.extraFeatures.listeningActivityDifference ? showAsNumber(Math.abs(state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.tracks)) : `???`} ${() => (!state.extraFeatures.listeningActivityDifference || state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.tracks >= 0) ? `more` : `less`} songs.
    +
    ${() => state.extraFeatures.listeningActivityDifference ? showAsNumber(Math.abs(state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.tracks)) : `???`} ${() => (!state.extraFeatures.listeningActivityDifference || state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.tracks >= 0) ? `more` : `less`} tracks.
    ${() => state.extraFeatures.listeningActivityDifference ? showAsNumber(Math.abs(state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.artists)) : `???`} ${() => (!state.extraFeatures.listeningActivityDifference || state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.artists >= 0) ? `more` : `less`} artists.
    ${() => state.extraFeatures.listeningActivityDifference ? showAsNumber(Math.abs(state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.albums)) : `???`} ${() => (!state.extraFeatures.listeningActivityDifference || state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.albums >= 0) ? `more` : `less`} albums.
    @@ -664,7 +664,7 @@ state.features = [ -
    ${() => showAsNumber(genre.uniqueTracks)} songs
    +
    ${() => showAsNumber(genre.uniqueTracks)} tracks
    @@ -709,7 +709,7 @@ state.features = [ // least skipped buildFeature(`least skipped`, html`
    -

    Til the end:
    Songs you never skipped

    +

    Til the end:
    Tracks you never skipped

    ${() => !state.extraFeatures.leastSkippedTracks ? html`
    @@ -811,7 +811,7 @@ state.features = [ // most skipped buildFeature(`most skipped`, html`
    -

    Sick of it:
    Songs you skipped the most

    +

    Sick of it:
    Tracks you skipped the most

    ${() => !state.extraFeatures.mostSkippedTracks ? html`
    @@ -974,8 +974,8 @@ state.features = [
    ${() => showAsNumber(state.rewindReport.generalStats.totalPlaybackDurationMinutes[state.settings.dataSource].toFixed(0))} minutes listened
    - -

    Top Song

    + +

    Top Track

    ${() => state.rewindReport.tracks?.[state.settings.rankingMetric]?.[0]?.name} @@ -998,7 +998,7 @@ state.features = [ ` : null}
    -
    ${() => showAsNumber(state.rewindReport.generalStats.uniqueTracksPlayed)} unique songs
    +
    ${() => showAsNumber(state.rewindReport.generalStats.uniqueTracksPlayed)} unique tracks
    ${() => showAsNumber(state.rewindReport.generalStats.uniqueArtistsPlayed)} unique artists
    ${() => showAsNumber(state.rewindReport.generalStats.uniqueAlbumsPlayed)} unique albums
    @@ -1016,7 +1016,7 @@ state.features = [
    Library Duration: ${() => showAsNumber((state.rewindReport?.libraryStats?.totalRuntime / 60.0 / 60.0 / 24.0).toFixed(1))} days
    -
    Average song length: ${() => showAsNumber(state.rewindReport?.libraryStats?.trackLength?.mean.toFixed(0))} seconds
    +
    Average track length: ${() => showAsNumber(state.rewindReport?.libraryStats?.trackLength?.mean.toFixed(0))} seconds
    ${() => state.extraFeatures.listeningActivityDifference ? html`
    @@ -1024,7 +1024,7 @@ state.features = [
    ${() => showAsNumber(Math.abs(state.rewindReport?.featureDelta?.listeningActivityDifference?.totalPlays[state.settings.dataSource]).toFixed(0))} more streams than last year
    ${() => (!state.extraFeatures.listeningActivityDifference || state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.tracks >= 0) ? html` -
    ${() =>showAsNumber(Math.abs(state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.tracks))}
    more songs
    +
    ${() =>showAsNumber(Math.abs(state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.tracks))}
    more tracks
    ` : null} ${() => (!state.extraFeatures.listeningActivityDifference || state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.artists >= 0) ? html`
    ${() =>showAsNumber(Math.abs(state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.artists))}
    more artists
    @@ -1215,7 +1215,7 @@ function showOverlayDataAccuracy() { title: `About the accuracy of this report`, content: html`
    -

    Jellyfin doesn't save any information about played tracks other than the number of times they were played. This means that e.g. the total playtime is only an approximation. It also means that it is not possible to limit the data to ${() => state.rewindReport?.year} without plugins!

    +

    Jellyfin doesn't save any information about played tracks other than the number of times they were played. This means that things like the total playtime are only an approximation. It also means that it is not possible to limit the data to ${() => state.rewindReport?.year} without plugins!

    However, if you have the "Playback Reporting" plugin installed, significantly more information can be collected, such as the date and durations of each playback. This results in better stats, although it isn't perfect either. Playback reporting depends on applications properly reporting the current playback states, and currently most music players that are compatible with Jellyfin seem to struggle with this in one way or another. Offline playback is not recorded at all.

    Still, the best solution is to install the Playback Reporting plugin into your Jellyfin server if you haven't done so already. It won't take longer than 2 minutes, so why not do it right now? Your Jellyfin Rewind isn't going anywhere!

    ${() => state.auth.config?.user?.isAdmin ? html` @@ -1226,7 +1226,7 @@ function showOverlayDataAccuracy() { `}

    By default, the Playback Reporting plugin only stores the last 3 months worth of playback data, so you definitely want to change that in the settings. I'd suggest keeping at least the last two years, just to be safe. The button below will take you directly to the settings page.

    ${() => state.auth.config?.user?.isAdmin ? html` - Open Settings + Open Settings ` : html` Open Settings!

    You're not logged in with an administrator account, but you need to be an admin in order to install plugins. If you are logged into Jellyfin with an admin account and already have the Playback Reporting plugin installed, you can click here to open the plugin settings. If nothing happens or Jellyfin just keeps loading, that means the logged-in account is not an administrator.

    @@ -1345,7 +1345,7 @@ ${() =>
  • ${() => buildOptionChooser({ title: `Rank by...`, - description: `Choose which metric should be used for ranking the top songs, artists, albums, genres, etc.`, + description: `Choose which metric should be used for ranking the top tracks, artists, albums, genres, etc.`, settingsKey: `rankingMetric`, options: [ { @@ -1364,12 +1364,12 @@ ${() =>
  • ${() => buildOptionChooser({ title: `Artist Type`, - description: `Toggle between (song) artists and album artists`, + description: `Toggle between (track) artists and album artists`, settingsKey: `useAlbumArtists`, options: [ { - name: `(Song) Artists`, - description: `Using (song) artists`, + name: `(Track) Artists`, + description: `Using (track) artists`, value: false, }, { @@ -1922,12 +1922,12 @@ function destroyPlayTimeByMonthChart() { function loadTopTrackMedia() { - const topSongPrimaryImage = document.querySelector(`#top-track-image`); - const topSongBackgroundImage = document.querySelector(`#top-track-background-image`); - console.log(`img:`, topSongPrimaryImage) - const topSongByDuration = state.rewindReport.tracks?.[state.settings.rankingMetric]?.[0] - console.log(`topSongByDuration:`, topSongByDuration) - state.jellyHelper.loadImage([topSongPrimaryImage, topSongBackgroundImage], topSongByDuration.image, `track`, state.settings.darkMode) + const topTrackPrimaryImage = document.querySelector(`#top-track-image`); + const topTrackBackgroundImage = document.querySelector(`#top-track-background-image`); + console.log(`img:`, topTrackPrimaryImage) + const topTrackByDuration = state.rewindReport.tracks?.[state.settings.rankingMetric]?.[0] + console.log(`topTrackByDuration:`, topTrackByDuration) + state.jellyHelper.loadImage([topTrackPrimaryImage, topTrackBackgroundImage], topTrackByDuration.image, `track`, state.settings.darkMode) } @@ -1935,13 +1935,13 @@ function loadTopTracksMedia() { console.log(`topTracksMedia`) - const topSongs = state.rewindReport.tracks?.[state.settings.rankingMetric]?.slice(0, 5) + const topTracks = state.rewindReport.tracks?.[state.settings.rankingMetric]?.slice(0, 5) - topSongs.forEach((song, index) => { - const songPrimaryImage = document.querySelector(`#top-tracks-image-${index}`); - const songBackgroundImage = document.querySelector(`#top-tracks-background-image-${index}`); - console.log(`img:`, songPrimaryImage) - state.jellyHelper.loadImage([songPrimaryImage, songBackgroundImage], song.image, `track`, state.settings.darkMode) + topTracks.forEach((track, index) => { + const trackPrimaryImage = document.querySelector(`#top-tracks-image-${index}`); + const trackBackgroundImage = document.querySelector(`#top-tracks-background-image-${index}`); + console.log(`img:`, trackPrimaryImage) + state.jellyHelper.loadImage([trackPrimaryImage, trackBackgroundImage], track.image, `track`, state.settings.darkMode) }) } @@ -1951,9 +1951,9 @@ function loadTopArtistMedia() { const topArtistPrimaryImage = document.querySelector(`#top-artist-image`); const topArtistBackgroundImage = document.querySelector(`#top-artist-background-image`); console.log(`img:`, topArtistPrimaryImage) - const topSongByDuration = state.rewindReport.artists?.[state.settings.rankingMetric]?.[0] - console.log(`topSongByDuration:`, topSongByDuration) - state.jellyHelper.loadImage([topArtistPrimaryImage, topArtistBackgroundImage], topSongByDuration.images.primary, `artist`, state.settings.darkMode) + const topTrackByDuration = state.rewindReport.artists?.[state.settings.rankingMetric]?.[0] + console.log(`topTrackByDuration:`, topTrackByDuration) + state.jellyHelper.loadImage([topArtistPrimaryImage, topArtistBackgroundImage], topTrackByDuration.images.primary, `artist`, state.settings.darkMode) } @@ -1998,13 +1998,13 @@ function loadLeastSkippedTracksMedia() { console.log(`leastSkippedTracksMedia`) - const topSongs = state.rewindReport.tracks?.[`leastSkipped`]?.slice(0, 5) + const topTracks = state.rewindReport.tracks?.[`leastSkipped`]?.slice(0, 5) - topSongs.forEach((song, index) => { - const songPrimaryImage = document.querySelector(`#least-skipped-tracks-image-${index}`); - const songBackgroundImage = document.querySelector(`#least-skipped-tracks-background-image-${index}`); - console.log(`img:`, songPrimaryImage) - state.jellyHelper.loadImage([songPrimaryImage, songBackgroundImage], song.image, `track`, state.settings.darkMode) + topTracks.forEach((track, index) => { + const trackPrimaryImage = document.querySelector(`#least-skipped-tracks-image-${index}`); + const trackBackgroundImage = document.querySelector(`#least-skipped-tracks-background-image-${index}`); + console.log(`img:`, trackPrimaryImage) + state.jellyHelper.loadImage([trackPrimaryImage, trackBackgroundImage], track.image, `track`, state.settings.darkMode) }) } @@ -2013,13 +2013,13 @@ function loadMostSkippedTracksMedia() { console.log(`mostSkippedTracksMedia`) - const topSongs = state.rewindReport.tracks?.[`mostSkipped`]?.slice(0, 5) + const topTracks = state.rewindReport.tracks?.[`mostSkipped`]?.slice(0, 5) - topSongs.forEach((song, index) => { - const songPrimaryImage = document.querySelector(`#most-skipped-tracks-image-${index}`); - const songBackgroundImage = document.querySelector(`#most-skipped-tracks-background-image-${index}`); - console.log(`img:`, songPrimaryImage) - state.jellyHelper.loadImage([songPrimaryImage, songBackgroundImage], song.image, `track`, state.settings.darkMode) + topTracks.forEach((track, index) => { + const trackPrimaryImage = document.querySelector(`#most-skipped-tracks-image-${index}`); + const trackBackgroundImage = document.querySelector(`#most-skipped-tracks-background-image-${index}`); + console.log(`img:`, trackPrimaryImage) + state.jellyHelper.loadImage([trackPrimaryImage, trackBackgroundImage], track.image, `track`, state.settings.darkMode) }) } @@ -2039,11 +2039,11 @@ function loadSummary() { console.log(`summary`) - // top song - const topSongPrimaryImage = document.querySelector(`#summary-top-track-image`); - console.log(`img:`, topSongPrimaryImage) - const topSong = state.rewindReport.tracks?.[state.settings.rankingMetric]?.[0] - console.log(`topSong:`, topSong) + // top track + const topTrackPrimaryImage = document.querySelector(`#summary-top-track-image`); + console.log(`img:`, topTrackPrimaryImage) + const topTrack = state.rewindReport.tracks?.[state.settings.rankingMetric]?.[0] + console.log(`topTrack:`, topTrack) // top artist const topArtistPrimaryImage = document.querySelector(`#summary-top-artist-image`); @@ -2058,7 +2058,7 @@ function loadSummary() { console.log(`mostSuccessivePlaysTrack:`, mostSuccessivePlaysTrack) // load images - state.jellyHelper.loadImage([topSongPrimaryImage], topSong.image, `track`, state.settings.darkMode) + state.jellyHelper.loadImage([topTrackPrimaryImage], topTrack.image, `track`, state.settings.darkMode) state.jellyHelper.loadImage([topArtistPrimaryImage], topArtist.images.primary, `artist`, state.settings.darkMode) if (state.extraFeatures.mostSuccessivePlays) { state.jellyHelper.loadImage([mostSuccessivePlaysTrackPrimaryImage], mostSuccessivePlaysTrack.image, `track`, state.settings.darkMode) @@ -2069,22 +2069,22 @@ function loadSummary() { function playTopTrack() { - const topSongByDuration = state.rewindReport.tracks?.[state.settings.rankingMetric]?.[0] - console.log(`topSongByDuration:`, topSongByDuration) - fadeToNextTrack(topSongByDuration) + const topTrackByDuration = state.rewindReport.tracks?.[state.settings.rankingMetric]?.[0] + console.log(`topTrackByDuration:`, topTrackByDuration) + fadeToNextTrack(topTrackByDuration) } // plays a random track from the top 5 tracks (excluding the top track) function playTopTracks() { - const topSongs = state.rewindReport.tracks?.[state.settings.rankingMetric]?.slice(1, 5) // first track excluded - const randomSongId = Math.floor(Math.random() * topSongs.length) - const randomSong = topSongs[randomSongId] - showPlaying(`#top-tracks-visualizer`, randomSongId+1, 5) + const topTracks = state.rewindReport.tracks?.[state.settings.rankingMetric]?.slice(1, 5) // first track excluded + const randomTrackId = Math.floor(Math.random() * topTracks.length) + const randomTrack = topTracks[randomTrackId] + showPlaying(`#top-tracks-visualizer`, randomTrackId+1, 5) - console.log(`randomSong:`, randomSong) - fadeToNextTrack(randomSong) + console.log(`randomTrack:`, randomTrack) + fadeToNextTrack(randomTrack) } @@ -2173,26 +2173,26 @@ async function playTopGenres() { // plays a random track from the 5 least skipped tracks function playLeastSkippedTracks() { - const leastSkippedSongs = state.rewindReport.tracks?.[`leastSkipped`]?.slice(0, 5) // first track excluded - const randomSongId = Math.floor(Math.random() * leastSkippedSongs.length) - const randomSong = leastSkippedSongs[randomSongId] - showPlaying(`#least-skipped-tracks-visualizer`, randomSongId, 5) + const leastSkippedTracks = state.rewindReport.tracks?.[`leastSkipped`]?.slice(0, 5) // first track excluded + const randomTrackId = Math.floor(Math.random() * leastSkippedTracks.length) + const randomTrack = leastSkippedTracks[randomTrackId] + showPlaying(`#least-skipped-tracks-visualizer`, randomTrackId, 5) - console.log(`randomSong:`, randomSong) - fadeToNextTrack(randomSong) + console.log(`randomTrack:`, randomTrack) + fadeToNextTrack(randomTrack) } // plays a random track from the 5 most skipped tracks function playMostSkippedTracks() { - const mostSkippedSongs = state.rewindReport.tracks?.[`mostSkipped`]?.slice(0, 5) // first track excluded - const randomSongId = Math.floor(Math.random() * mostSkippedSongs.length) - const randomSong = mostSkippedSongs[randomSongId] - showPlaying(`#most-skipped-tracks-visualizer`, randomSongId, 5) + const mostSkippedTracks = state.rewindReport.tracks?.[`mostSkipped`]?.slice(0, 5) // first track excluded + const randomTrackId = Math.floor(Math.random() * mostSkippedTracks.length) + const randomTrack = mostSkippedTracks[randomTrackId] + showPlaying(`#most-skipped-tracks-visualizer`, randomTrackId, 5) - console.log(`randomSong:`, randomSong) - fadeToNextTrack(randomSong) + console.log(`randomTrack:`, randomTrack) + fadeToNextTrack(randomTrack) } From 097761f78dc77091b31818705193eccf2d40fd9b Mon Sep 17 00:00:00 2001 From: Chaphasilor Date: Sat, 28 Dec 2024 23:29:57 +0100 Subject: [PATCH 05/15] implemented offline playback data parsing - no import/upload yet --- src/features.js | 16 +++++++ src/jelly-helper.js | 30 ++++++------- src/offline-import.js | 101 ++++++++++++++++++++++++++++++++++++++++++ src/onboarding.js | 63 ++++++++++++++++++++++++-- 4 files changed, 191 insertions(+), 19 deletions(-) create mode 100644 src/offline-import.js diff --git a/src/features.js b/src/features.js index 21b11c3..ea1576b 100644 --- a/src/features.js +++ b/src/features.js @@ -1608,6 +1608,8 @@ export function init(rewindReportData, jellyHelper, auth) { } }); + document.addEventListener(`keydown`, handleArrowKeys) + state.settings.darkMode = darkModePreference.matches console.log(`init finished`) @@ -1825,6 +1827,20 @@ function handleFeatureClick(event) { } } +function handleArrowKeys(event) { + // call `previous()` or `next()` depending on which side of the feature was clicked + console.log(event) + if (!event) { + return + } + + if (event.key?.toLowerCase?.() === `arrowleft`) { + previous() + } else if (event.key?.toLowerCase?.() === `arrowright`) { + next() + } +} + function showPlaytimeByMonthChart() { console.log(`Loading chart...`) diff --git a/src/jelly-helper.js b/src/jelly-helper.js index c765aaa..ec7679b 100644 --- a/src/jelly-helper.js +++ b/src/jelly-helper.js @@ -45,9 +45,9 @@ export default class JellyHelper { } if (primaryTag && (parentItemId || type === `user`)) { - let url = `${this.auth.config.baseUrl}/Items/${parentItemId}/Images/Primary?tag=${primaryTag}&MaxWidth=${resolution}&MaxHeight=${resolution}` + let url = `${this.auth.config.serverInfo.PublicAddress}/Items/${parentItemId}/Images/Primary?tag=${primaryTag}&MaxWidth=${resolution}&MaxHeight=${resolution}` if (type === `user`) { - url = `${this.auth.config.baseUrl}/Users/${parentItemId}/Images/Primary?tag=${primaryTag}&MaxWidth=${resolution}&MaxHeight=${resolution}` + url = `${this.auth.config.serverInfo.PublicAddress}/Users/${parentItemId}/Images/Primary?tag=${primaryTag}&MaxWidth=${resolution}&MaxHeight=${resolution}` } fetch(url, { method: `GET`, @@ -116,7 +116,7 @@ export default class JellyHelper { 'EnableRemoteMedia': `true`, } - element.src = `${this.auth.config.baseUrl}/Audio/${audioInfo.id}/universal?${Object.entries(params).map(([key, value]) => `${key}=${value}`).join(`&`)}` + element.src = `${this.auth.config.serverInfo.PublicAddress}/Audio/${audioInfo.id}/universal?${Object.entries(params).map(([key, value]) => `${key}=${value}`).join(`&`)}` await element.load() } @@ -127,16 +127,16 @@ export default class JellyHelper { switch (groupType) { case `artist`: - url = `${this.auth.config.baseUrl}/Users/${this.auth.config.user.id}/Items?ArtistIds=${groupId}&Filters=IsNotFolder&Recursive=true&SortBy=PlayCount&MediaTypes=Audio&Limit=20&Fields=Chapters&ExcludeLocationTypes=Virtual&EnableTotalRecordCount=false&CollapseBoxSetItems=false` + url = `${this.auth.config.serverInfo.PublicAddress}/Users/${this.auth.config.user.id}/Items?ArtistIds=${groupId}&Filters=IsNotFolder&Recursive=true&SortBy=PlayCount&MediaTypes=Audio&Limit=20&Fields=Chapters&ExcludeLocationTypes=Virtual&EnableTotalRecordCount=false&CollapseBoxSetItems=false` break; case `album`: - url = `${this.auth.config.baseUrl}/Users/${this.auth.config.user.id}/Items?ParentId=${groupId}&Filters=IsNotFolder&Recursive=true&SortBy=PlayCount&MediaTypes=Audio&Limit=20&Fields=Chapters&ExcludeLocationTypes=Virtual&EnableTotalRecordCount=false&CollapseBoxSetItems=false` + url = `${this.auth.config.serverInfo.PublicAddress}/Users/${this.auth.config.user.id}/Items?ParentId=${groupId}&Filters=IsNotFolder&Recursive=true&SortBy=PlayCount&MediaTypes=Audio&Limit=20&Fields=Chapters&ExcludeLocationTypes=Virtual&EnableTotalRecordCount=false&CollapseBoxSetItems=false` break; case `genre`: - url = `${this.auth.config.baseUrl}/Users/${this.auth.config.user.id}/Items?GenreIds=${groupId}&Filters=IsNotFolder&Recursive=true&SortBy=PlayCount&MediaTypes=Audio&Limit=20&Fields=Chapters&ExcludeLocationTypes=Virtual&EnableTotalRecordCount=false&CollapseBoxSetItems=false` + url = `${this.auth.config.serverInfo.PublicAddress}/Users/${this.auth.config.user.id}/Items?GenreIds=${groupId}&Filters=IsNotFolder&Recursive=true&SortBy=PlayCount&MediaTypes=Audio&Limit=20&Fields=Chapters&ExcludeLocationTypes=Virtual&EnableTotalRecordCount=false&CollapseBoxSetItems=false` break; case `playlist`: - url = `${this.auth.config.baseUrl}/Users/${this.auth.config.user.id}/Items?ParentId=${groupId}&Filters=IsNotFolder&Recursive=true&SortBy=PlayCount&MediaTypes=Audio&Limit=20&Fields=Chapters&ExcludeLocationTypes=Virtual&EnableTotalRecordCount=false&CollapseBoxSetItems=false` + url = `${this.auth.config.serverInfo.PublicAddress}/Users/${this.auth.config.user.id}/Items?ParentId=${groupId}&Filters=IsNotFolder&Recursive=true&SortBy=PlayCount&MediaTypes=Audio&Limit=20&Fields=Chapters&ExcludeLocationTypes=Virtual&EnableTotalRecordCount=false&CollapseBoxSetItems=false` break; default: @@ -160,7 +160,7 @@ export default class JellyHelper { async checkIfPlaybackReportingInstalled() { - const pluginsResponse = await fetch(`${this.auth.config.baseUrl}/Plugins`, { + const pluginsResponse = await fetch(`${this.auth.config.serverInfo.PublicAddress}/Plugins`, { method: `GET`, headers: { ...this.auth.config.defaultHeaders, @@ -197,7 +197,7 @@ export default class JellyHelper { } } - const playbackReportingSettingsResponse = await fetch(`${this.auth.config.baseUrl}/System/Configuration/playback_reporting`, { + const playbackReportingSettingsResponse = await fetch(`${this.auth.config.serverInfo.PublicAddress}/System/Configuration/playback_reporting`, { method: `GET`, headers: { ...this.auth.config.defaultHeaders, @@ -207,7 +207,7 @@ export default class JellyHelper { let playbackReportingIgnoredUsersJson = [] try { - const playbackReportingIgnoredUsersResponse = await fetch(`${this.auth.config.baseUrl}/user_usage_stats/user_list`, { + const playbackReportingIgnoredUsersResponse = await fetch(`${this.auth.config.serverInfo.PublicAddress}/user_usage_stats/user_list`, { method: `GET`, headers: { ...this.auth.config.defaultHeaders, @@ -236,7 +236,7 @@ export default class JellyHelper { // requires administrator account async installPlaybackReportingPlugin() { - const response = await fetch(`${this.auth.config.baseUrl}/Packages/Installed/Playback%20Reporting?AssemblyGuid=5c53438191a343cb907a35aa02eb9d2c`, { + const response = await fetch(`${this.auth.config.serverInfo.PublicAddress}/Packages/Installed/Playback%20Reporting?AssemblyGuid=5c53438191a343cb907a35aa02eb9d2c`, { method: `POST`, headers: { ...this.auth.config.defaultHeaders, @@ -254,7 +254,7 @@ export default class JellyHelper { // requires administrator account async enablePlaybackReportingPlugin(setup) { - const response = await fetch(`${this.auth.config.baseUrl}/Plugins/${setup.id}/${setup.version}/Enable`, { + const response = await fetch(`${this.auth.config.serverInfo.PublicAddress}/Plugins/${setup.id}/${setup.version}/Enable`, { method: `POST`, headers: { ...this.auth.config.defaultHeaders, @@ -272,7 +272,7 @@ export default class JellyHelper { // requires administrator account async updatePlaybackReportingSettings(settings) { - const response = await fetch(`${this.auth.config.baseUrl}/System/Configuration/playback_reporting`, { + const response = await fetch(`${this.auth.config.serverInfo.PublicAddress}/System/Configuration/playback_reporting`, { method: `POST`, headers: { ...this.auth.config.defaultHeaders, @@ -286,7 +286,7 @@ export default class JellyHelper { // requires admin permissions async shutdownServer() { - const response = await fetch(`${this.auth.config.baseUrl}/System/Shutdown`, { + const response = await fetch(`${this.auth.config.serverInfo.PublicAddress}/System/Shutdown`, { method: `POST`, headers: { ...this.auth.config.defaultHeaders, @@ -298,7 +298,7 @@ export default class JellyHelper { // requires admin permissions async restartServer() { - const response = await fetch(`${this.auth.config.baseUrl}/System/Restart`, { + const response = await fetch(`${this.auth.config.serverInfo.PublicAddress}/System/Restart`, { method: `POST`, headers: { ...this.auth.config.defaultHeaders, diff --git a/src/offline-import.js b/src/offline-import.js new file mode 100644 index 0000000..e404cee --- /dev/null +++ b/src/offline-import.js @@ -0,0 +1,101 @@ +export function importOfflinePlayback(fileHandle) { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = async () => { + + console.log(`Parsing offline data`) + const offlinePlaybackData = await parseOfflinePlaybackData(reader.result) + console.log(`offlinePlaybackData:`, offlinePlaybackData) + + resolve(offlinePlaybackData); + }; + reader.onerror = () => { + reader.abort(); + reject(new Error("Error loading offline data")); + }; + reader.readAsText(fileHandle); + }); +} + +async function parseOfflinePlaybackData(fileContent) { + + console.log(`raw offline data:`, fileContent) + + let content + try { + content = JSON.parse(fileContent) + } catch (err) { + content = null + } + + if (!!content) { + console.warning(`Regular JSON detected, unsupported`) + } + + console.log(`fileContent.split(/\r\n|\r|\n/):`, fileContent.split(/\r\n|\r|\n/)) + const contentLines = fileContent.split(/\r\n|\r|\n/).filter(x => x.trim().length > 0) + console.log(`contentLines:`, contentLines) + // detect supported formats + if (contentLines.length > 0) { + try { + JSON.parse(contentLines[0]) + console.info(`Detected Finamp offline playback data format, attempting to parse`) + return await parseFinampOfflinePlaybackData(contentLines) + } catch (err) { + console.error(`Error while parsing Finamp offline playback data:`, err) + } + + } + +} + +async function parseFinampOfflinePlaybackData(contentLines) { + + console.log(`contentLines:`, contentLines) + + const plays = [] + // parsed successfully, now parse all lines + plays.push( + ...contentLines.map(line => { + try { + const parsedLine = JSON.parse(line) + return new Play({ + timestamp: parsedLine[`timestamp`], + itemId: parsedLine[`item_id`], + title: parsedLine[`title`], + artist: parsedLine[`artist`], + album: parsedLine[`album`], + userId: parsedLine[`user_id`], + musicBrainzId: parsedLine[`track_mbid`], + }) + } catch (err) { + return null + } + }).filter(x => !!x) + ) + + return plays + +} + +export class Play { + + constructor({ + timestamp, + itemId, + title, + artist, + album, + userId, + musicBrainzId, + }) { + this.timestamp = timestamp + this.itemId = itemId + this.title = title + this.artist = artist + this.album = album + this.userId = userId + this.musicBrainzId = musicBrainzId + } + +} diff --git a/src/onboarding.js b/src/onboarding.js index 6bcd373..bf0bc0f 100644 --- a/src/onboarding.js +++ b/src/onboarding.js @@ -2,6 +2,7 @@ import { reactive, watch, html, } from '@arrow-js/core' import { connectToServer, generateRewindReport, initializeFeatureStory, loginViaAuthToken, loginViaPassword, restoreAndPrepareRewind, deleteRewind } from './setup'; import { getFeatureDelta, importRewindReport } from './delta'; +import { importOfflinePlayback, Play } from './offline-import'; export const state = reactive({ currentView: `start`, @@ -20,8 +21,10 @@ export const state = reactive({ rewindGenerating: false, rewindReport: null, oldReport: null, + offlinePlayback: null, importingExistingReport: false, importingLastYearsReport: false, + importingOfflinePlayback: false, staleReport: false, progress: 0, waitingForRestart: false, @@ -48,6 +51,7 @@ export async function init(auth) { importReportForViewing: viewImportReportForViewing, importLastYearsReport: viewImportLastYearsReport, launchExistingReport: viewLaunchExistingReport, + importOfflinePlayback: viewImportOfflinePlayback, load: viewLoad, revisit: viewRevisit, rewindGenerationError: viewRewindGenerationError, @@ -173,10 +177,10 @@ const viewStart = html`
  • @@ -575,10 +579,11 @@ function inspectPlaybackReportingSetup(playbackReportingSetup, nextScreen) { } -async function checkPlaybackReportingSetup(nextScreen = `importLastYearsReport`) { +// async function checkPlaybackReportingSetup(nextScreen = `importLastYearsReport`) { +async function checkPlaybackReportingSetup(nextScreen = `importOfflinePlayback`) { // there's nothing we can do without the helper - if (!window.helper) { + if (!window.helper || !!state.auth?.config?.user?.isAdmin) { state.currentView = nextScreen } @@ -601,6 +606,7 @@ async function checkPlaybackReportingSetup(nextScreen = `importLastYearsReport`) } catch (err) { console.error(`Failed to check the playback reporting setup, continuing without it:`, err) + state.playbackReportingInspectionAttempts++ if (state.playbackReportingInspectionAttempts > 3) { state.currentView = nextScreen } else { @@ -733,6 +739,8 @@ const viewImportReportForViewing = html` state.importingExistingReport = true input.disabled = true state.rewindReport = await importRewindReport(e.target.files[0]) + state.auth.config.serverInfo = state.rewindReport.jellyfinRewindReport.server + console.log(`state.auth.serverInfo:`, state.auth.serverInfo) console.log(`state.rewindReport:`, state.rewindReport) state.currentView = `launchExistingReport` // state.currentView = `load` @@ -791,6 +799,53 @@ const viewLaunchExistingReport = html`
    ` +const viewImportOfflinePlayback = html` +
    + + ${() => header} + +
    +

    We noticed you've been using Finamp's beta version to listen to music

    +

    Finamp keeps track of your playback history even when you're not connected to your server, and you can now import that history!

    +

    Imported plays will only be added to the Playback Reporting addon's database, but can then used to generate a more accurate Rewind report for you in the following steps.

    +
    + +
    + + + + ${() => state.importingOfflinePlayback ? html` +

    Importing, please wait a few seconds...

    + ` : html` + + ` + } +
    + + ${() => buttonLogOut} + +
    +` + const viewImportLastYearsReport = html`
    From 3427c76f9a38dd70fdab3a2ccaccd48c7d36f2e3 Mon Sep 17 00:00:00 2001 From: Chaphasilor Date: Tue, 31 Dec 2024 16:18:32 +0100 Subject: [PATCH 06/15] only consider played tracks in artist summary --- src/aggregate.js | 38 ++++++++++++++++++++++++++++++++++++++ src/features.js | 2 +- src/types.js | 16 ++++++++++++++-- 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/aggregate.js b/src/aggregate.js index 6b8cf99..5a51077 100644 --- a/src/aggregate.js +++ b/src/aggregate.js @@ -164,6 +164,11 @@ export function generateArtistInfo(topTrackInfo, artistInfo) { average: cur.playCount?.average, }, uniqueTracks: new Set([{id: cur.id, name: cur.name}]), + uniquePlayedTracks: { + jellyfin: new Set(cur.playCount?.jellyfin > 0 ? [{id: cur.id, name: cur.name}] : []), + playbackReport: new Set(cur.playCount?.playbackReport > 0 ? [{id: cur.id, name: cur.name}] : []), + average: new Set(cur.playCount?.average > 0 ? [{id: cur.id, name: cur.name}] : []), + }, plays: cur.plays, lastPlayed: cur.lastPlayed, totalPlayDuration: { @@ -179,6 +184,15 @@ export function generateArtistInfo(topTrackInfo, artistInfo) { acc[artistId].playCount.playbackReport += Number(cur.playCount?.playbackReport) acc[artistId].playCount.average = Math.ceil(((acc[artistId]?.playCount?.jellyfin || 0) + (acc[artistId]?.playCount?.playbackReport || 0))/2) acc[artistId].uniqueTracks.add({id: cur.id, name: cur.name}) + if (cur.playCount?.jellyfin > 0) { + acc[artistId].uniquePlayedTracks.jellyfin.add({id: cur.id, name: cur.name}) + } + if (cur.playCount?.playbackReport > 0) { + acc[artistId].uniquePlayedTracks.playbackReport.add({id: cur.id, name: cur.name}) + } + if (cur.playCount?.average > 0) { + acc[artistId].uniquePlayedTracks.average.add({id: cur.id, name: cur.name}) + } acc[artistId].plays.concat(cur.plays) acc[artistId].lastPlayed = (acc[artistId]?.lastPlayed || 0) > cur.lastPlayed ? (acc[artistId]?.lastPlayed || 0) : cur.lastPlayed acc[artistId].totalPlayDuration.jellyfin += Number(cur.totalPlayDuration.jellyfin) @@ -191,6 +205,11 @@ export function generateArtistInfo(topTrackInfo, artistInfo) { Object.entries(topArtistInfo).forEach(([artistId, artist]) => { artist.uniqueTracks = Array.from(artist.uniqueTracks) + artist.uniquePlayedTracks = { + jellyfin: Array.from(artist.uniquePlayedTracks.jellyfin), + playbackReport: Array.from(artist.uniquePlayedTracks.playbackReport), + average: Array.from(artist.uniquePlayedTracks.average), + } }) return topArtistInfo @@ -212,6 +231,11 @@ export function generateGenreInfo(topTrackInfo) { average: cur.playCount?.average, }, uniqueTracks: new Set([{id: cur.id, name: cur.name}]), + uniquePlayedTracks: { + jellyfin: new Set(cur.playCount?.jellyfin > 0 ? [{id: cur.id, name: cur.name}] : []), + playbackReport: new Set(cur.playCount?.playbackReport > 0 ? [{id: cur.id, name: cur.name}] : []), + average: new Set(cur.playCount?.average > 0 ? [{id: cur.id, name: cur.name}] : []), + }, plays: cur.plays, lastPlayed: cur.lastPlayed, totalPlayDuration: { @@ -226,6 +250,15 @@ export function generateGenreInfo(topTrackInfo) { acc[genreId].playCount.playbackReport += Number(cur.playCount?.playbackReport) acc[genreId].playCount.average = Math.ceil(((acc[genreId]?.playCount?.jellyfin || 0) + (acc[genreId]?.playCount?.playbackReport || 0))/2) acc[genreId].uniqueTracks.add({id: cur.id, name: cur.name}) + if (cur.playCount?.jellyfin > 0) { + acc[genreId].uniquePlayedTracks.jellyfin.add({id: cur.id, name: cur.name}) + } + if (cur.playCount?.playbackReport > 0) { + acc[genreId].uniquePlayedTracks.playbackReport.add({id: cur.id, name: cur.name}) + } + if (cur.playCount?.average > 0) { + acc[genreId].uniquePlayedTracks.average.add({id: cur.id, name: cur.name}) + } acc[genreId].plays.concat(cur.plays) acc[genreId].lastPlayed = (acc[genreId]?.lastPlayed || 0) > cur.lastPlayed ? (acc[genreId]?.lastPlayed || 0) : cur.lastPlayed acc[genreId].totalPlayDuration.jellyfin += Number(cur.totalPlayDuration.jellyfin) @@ -238,6 +271,11 @@ export function generateGenreInfo(topTrackInfo) { Object.entries(topGenreInfo).forEach(([genreId, genre]) => { genre.uniqueTracks = Array.from(genre.uniqueTracks) + genre.uniquePlayedTracks = { + jellyfin: Array.from(genre.uniquePlayedTracks.jellyfin), + playbackReport: Array.from(genre.uniquePlayedTracks.playbackReport), + average: Array.from(genre.uniquePlayedTracks.average), + } }) return topGenreInfo diff --git a/src/features.js b/src/features.js index ea1576b..d331d1b 100644 --- a/src/features.js +++ b/src/features.js @@ -389,7 +389,7 @@ state.features = [
    Streamed ${() => showAsNumber(state.rewindReport.artists?.[state.settings.rankingMetric]?.[0]?.playCount[state.settings.dataSource])} times.
    -
    Listened to ${() => showAsNumber(state.rewindReport.artists?.[state.settings.rankingMetric]?.[0]?.uniqueTracks)} unique tracks
    for ${() => showAsNumber(state.rewindReport.artists?.[state.settings.rankingMetric]?.[0]?.totalPlayDuration[state.settings.dataSource].toFixed(0))} minutes.
    +
    Listened to ${() => showAsNumber(state.rewindReport.artists?.[state.settings.rankingMetric]?.[0]?.uniquePlayedTracks[state.settings.dataSource])} unique tracks
    for ${() => showAsNumber(state.rewindReport.artists?.[state.settings.rankingMetric]?.[0]?.totalPlayDuration[state.settings.dataSource].toFixed(0))} minutes.
    diff --git a/src/types.js b/src/types.js index 914dc8d..44bf197 100644 --- a/src/types.js +++ b/src/types.js @@ -22,13 +22,14 @@ export class BackdropImage { } export class Artist { - constructor({ id, name, tracks, images, playCount, uniqueTracks, plays, lastPlayed, totalPlayDuration }) { + constructor({ id, name, tracks, images, playCount, uniqueTracks, uniquePlayedTracks, plays, lastPlayed, totalPlayDuration }) { this.name = name this.id = id this.tracks = tracks this.images = images this.playCount = playCount this.uniqueTracks = uniqueTracks + this.uniquePlayedTracks = uniquePlayedTracks this.plays = plays this.lastPlayed = lastPlayed this.totalPlayDuration = totalPlayDuration @@ -46,6 +47,11 @@ export class Artist { images: this.images, playCount: this.playCount, uniqueTracks: this.uniqueTracks.length, + uniquePlayedTracks: { + jellyfin: this.uniquePlayedTracks.jellyfin.length, + playbackReport: this.uniquePlayedTracks.playbackReport.length, + average: this.uniquePlayedTracks.average.length, + }, plays: this.plays.length, lastPlayed: this.lastPlayed, totalPlayDuration: this.totalPlayDuration, @@ -138,13 +144,14 @@ export class Track { } export class Genre { - constructor({ id, name, tracks, image, playCount, uniqueTracks, plays, lastPlayed, totalPlayDuration }) { + constructor({ id, name, tracks, image, playCount, uniqueTracks, uniquePlayedTracks, plays, lastPlayed, totalPlayDuration }) { this.name = name this.id = id this.tracks = tracks this.image = image this.playCount = playCount this.uniqueTracks = uniqueTracks + this.uniquePlayedTracks = uniquePlayedTracks this.plays = plays this.lastPlayed = lastPlayed this.totalPlayDuration = totalPlayDuration @@ -162,6 +169,11 @@ export class Genre { image: this.image, playCount: this.playCount, uniqueTracks: this.uniqueTracks.length, + uniquePlayedTracks: { + jellyfin: this.uniquePlayedTracks.jellyfin.length, + playbackReport: this.uniquePlayedTracks.playbackReport.length, + average: this.uniquePlayedTracks.average.length, + }, plays: this.plays.length, lastPlayed: this.lastPlayed, totalPlayDuration: this.totalPlayDuration From 23751c6d354ed9ae7d99952ed1624ee8057840aa Mon Sep 17 00:00:00 2001 From: Chaphasilor Date: Tue, 31 Dec 2024 16:19:00 +0100 Subject: [PATCH 07/15] include favorite difference in delta --- src/delta.js | 4 ++++ src/features.js | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/delta.js b/src/delta.js index a95c0b4..cc80288 100644 --- a/src/delta.js +++ b/src/delta.js @@ -30,6 +30,9 @@ export async function getFeatureDelta(oldReport, newReport) { playbackReport: newReport.jellyfinRewindReport.generalStats.totalPlays.playbackReport - oldReport.jellyfinRewindReport.generalStats.totalPlays.playbackReport, } + // favorites difference + const favoriteDifference = (newReport.jellyfinRewindReport?.libraryStats?.tracks?.favorite ?? 0) - (oldReport.jellyfinRewindReport?.libraryStats?.tracks?.favorite ?? 0) + const listeningActivityDifference = { uniquePlays: uniquePlays, totalPlays: totalPlays, @@ -37,6 +40,7 @@ export async function getFeatureDelta(oldReport, newReport) { return { listeningActivityDifference, + favoriteDifference, } } diff --git a/src/features.js b/src/features.js index d331d1b..eaf9a94 100644 --- a/src/features.js +++ b/src/features.js @@ -614,6 +614,10 @@ state.features = [
    ${() => state.extraFeatures.listeningActivityDifference ? showAsNumber(Math.abs(state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.albums)) : `???`} ${() => (!state.extraFeatures.listeningActivityDifference || state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.albums >= 0) ? `more` : `less`} albums.
    +
    +
    You ${() => (!state.extraFeatures.listeningActivityDifference || state.rewindReport?.featureDelta?.favoriteDifference >= 0) ? `added` : `removed`} ${() => state.extraFeatures.listeningActivityDifference ? showAsNumber(Math.abs(state.rewindReport?.featureDelta?.favoriteDifference)) : `???`} favorites.
    +
    + ${() => state.extraFeatures.listeningActivityDifference ? html`
    ${() => state.rewindReport?.featureDelta?.listeningActivityDifference?.totalPlays[state.settings.dataSource] >= 0 ? html` From 92fc2021f80068fb330e6cec12e08ddc1ad88e75 Mon Sep 17 00:00:00 2001 From: Chaphasilor Date: Tue, 31 Dec 2024 16:19:31 +0100 Subject: [PATCH 08/15] import offline plays to server --- requests.txt | 6 ++++ src/jelly-helper.js | 12 +++++++ src/offline-import.js | 84 ++++++++++++++++++++++++++++++++++++++++++- src/onboarding.js | 38 ++++++++++++++------ src/rewind.js | 4 +-- 5 files changed, 131 insertions(+), 13 deletions(-) diff --git a/requests.txt b/requests.txt index e5dc1d5..ed7df75 100644 --- a/requests.txt +++ b/requests.txt @@ -39,3 +39,9 @@ WHERE ItemType="Audio" AND datetime(DateCreated) >= datetime('2022-01-01') AND datetime(DateCreated) <= datetime('2022-12-31') ORDER BY DateCreated ASC LIMIT 30 + +INSERT INTO PlaybackActivity + (DateCreated, UserId, ItemId, ItemType, ItemName, PlaybackMethod, ClientName, DeviceName, PlayDuration) + VALUES + ( '2024-12-29 17:55:07.0000000', 'c0ffee', '22ed98039ef8db9016f64bba02b36ad5', 'Audio', 'Crankdat, Ace Aura - The Feeling (The Feeling)', 'OfflinePlay', 'Finamp', 'Unknown', 420), + ( '2024-12-29 17:55:12.0000000', 'c0ffee', 'e5262768cd8d18bf6c25dff4a97f2fda', 'Audio', 'Fabian Mazur - Buckwild (The FifthGuys & Coffeeshop Remix) (Buckwild (The FifthGuys & Coffeeshop Remix))', 'OfflinePlay', 'Finamp', 'Unknown', 420) diff --git a/src/jelly-helper.js b/src/jelly-helper.js index ec7679b..67f648f 100644 --- a/src/jelly-helper.js +++ b/src/jelly-helper.js @@ -283,6 +283,18 @@ export default class JellyHelper { return response.status === 204 } + // requires admin permissions + async fetchDevices() { + const response = await fetch(`${this.auth.config.serverInfo.PublicAddress}/Devices`, { + method: `GET`, + headers: { + ...this.auth.config.defaultHeaders, + 'Content-Type': `application/json`, + }, + }) + return await response.json() + } + // requires admin permissions async shutdownServer() { diff --git a/src/offline-import.js b/src/offline-import.js index e404cee..6b425c7 100644 --- a/src/offline-import.js +++ b/src/offline-import.js @@ -1,3 +1,5 @@ +import { loadItemInfo } from "./rewind"; + export function importOfflinePlayback(fileHandle) { return new Promise((resolve, reject) => { const reader = new FileReader(); @@ -6,6 +8,21 @@ export function importOfflinePlayback(fileHandle) { console.log(`Parsing offline data`) const offlinePlaybackData = await parseOfflinePlaybackData(reader.result) console.log(`offlinePlaybackData:`, offlinePlaybackData) + + // fetch item data to get track durations + const itemInfo = await loadItemInfo(offlinePlaybackData.map(play => play.itemId)) + console.log(`itemInfo:`, itemInfo) + if (itemInfo[`Items`]?.length > 0) { + // create map of item ID and duration for reduced time complexity + const itemDurations = itemInfo[`Items`].reduce((all, cur) => { + all[cur[`Id`]] = !isNaN(Math.round(cur[`RunTimeTicks`] / 10000000)) ? Math.round(cur[`RunTimeTicks`] / 10000000) : 0 + return all + }, {}) + // enrich offline plays with fetched track durations + for (const index in offlinePlaybackData) { + offlinePlaybackData[index].playDuration = itemDurations[offlinePlaybackData[index].itemId] + } + } resolve(offlinePlaybackData); }; @@ -60,13 +77,15 @@ async function parseFinampOfflinePlaybackData(contentLines) { try { const parsedLine = JSON.parse(line) return new Play({ - timestamp: parsedLine[`timestamp`], + timestamp: new Date(Number(parsedLine[`timestamp`]) * 1000), itemId: parsedLine[`item_id`], title: parsedLine[`title`], artist: parsedLine[`artist`], album: parsedLine[`album`], userId: parsedLine[`user_id`], musicBrainzId: parsedLine[`track_mbid`], + client: `Finamp`, + //TODO add device information to exported offline plays file }) } catch (err) { return null @@ -78,6 +97,63 @@ async function parseFinampOfflinePlaybackData(contentLines) { } +const uploadOfflinePlaybackQuery = (offlinePlays, auth) => { + return ` + INSERT INTO PlaybackActivity + (DateCreated, UserId, ItemId, ItemType, ItemName, PlaybackMethod, ClientName, DeviceName, PlayDuration) + VALUES + ${offlinePlays.map(play => + `( '${play.timestamp.toISOString().slice(0, 19).replace(`T`, ` `)}.0000000', '${auth.config.user.id}', '${play.itemId}', 'Audio', '${play.artist.replaceAll(`'`, `''`)} - ${play.title.replaceAll(`'`, `''`)} (${play.album.replaceAll(`'`, `''`)})', 'OfflinePlay', '${play.client.replaceAll(`'`, `''`)}', '${play.device.replaceAll(`'`, `''`)}', ${play.playDuration})` + ).join(`,`)} + ` +} + // GROUP BY ItemId -- don't group so that we can filter out wrong durations + // LIMIT 200 + +export async function uploadOfflinePlayback(offlinePlays, auth) { + + console.info(`Importing offline playback to server`) + + const response = await fetch(`${auth.config.baseUrl}/user_usage_stats/submit_custom_query?stamp=${Date.now()}`, { + method: 'POST', + headers: { + ...auth.config.defaultHeaders, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + "CustomQueryString": uploadOfflinePlaybackQuery(offlinePlays, auth), + }) + }) + const json = await response.json() + + if (json[`message`]?.toLowerCase?.()?.includes?.(`query executed`)) { + console.info(`Successfully imported offline playback to server`) + return json + } else { + console.error(`Error while importing offline playback to server:`, json[`message`]) + throw new Error(`Error while importing offline playback to server: ${json[`message`]}`) + } + +} + +export async function checkIfOfflinePlaybackImportAvailable() { + try { + const devices = await window.helper.fetchDevices() + const finampBetaMatch = devices[`Items`].find(device => { + if (device[`AppName`] === `Finamp`) { + const versionString = device[`AppVersion`] + const versionRegex = /^(\d+)\.(\d+)\.(\d+)$/; + const [, major, minor, patch] = versionString.match(versionRegex) || []; + return Number(major) > 0 || Number(minor) >= 9 + } + }) + return finampBetaMatch + } catch (err) { + console.error(`Error while checking if offline playback import is available:`, err) + } + return false +} + export class Play { constructor({ @@ -88,6 +164,9 @@ export class Play { album, userId, musicBrainzId, + playDuration, + client, + device, }) { this.timestamp = timestamp this.itemId = itemId @@ -96,6 +175,9 @@ export class Play { this.album = album this.userId = userId this.musicBrainzId = musicBrainzId + this.playDuration = playDuration + this.client = client + this.device = device ?? `Unknown Device` } } diff --git a/src/onboarding.js b/src/onboarding.js index bf0bc0f..1b17ff2 100644 --- a/src/onboarding.js +++ b/src/onboarding.js @@ -2,7 +2,7 @@ import { reactive, watch, html, } from '@arrow-js/core' import { connectToServer, generateRewindReport, initializeFeatureStory, loginViaAuthToken, loginViaPassword, restoreAndPrepareRewind, deleteRewind } from './setup'; import { getFeatureDelta, importRewindReport } from './delta'; -import { importOfflinePlayback, Play } from './offline-import'; +import { checkIfOfflinePlaybackImportAvailable, importOfflinePlayback, Play, uploadOfflinePlayback } from './offline-import'; export const state = reactive({ currentView: `start`, @@ -579,14 +579,27 @@ function inspectPlaybackReportingSetup(playbackReportingSetup, nextScreen) { } -// async function checkPlaybackReportingSetup(nextScreen = `importLastYearsReport`) { -async function checkPlaybackReportingSetup(nextScreen = `importOfflinePlayback`) { - +async function checkPlaybackReportingSetup(nextScreen) { + // there's nothing we can do without the helper - if (!window.helper || !!state.auth?.config?.user?.isAdmin) { - state.currentView = nextScreen + if (!window.helper) { + state.currentView = nextScreen ?? `importLastYearsReport` + return + } + + if (!state.auth?.config?.user?.isAdmin) { + state.currentView = nextScreen ?? `importLastYearsReport` + return } + // check if eligible for offline playback import + if (await checkIfOfflinePlaybackImportAvailable(state.auth)) { + state.currentView = `importOfflinePlayback` + return + } + + nextScreen = nextScreen ?? `importLastYearsReport` + try { const playbackReportingSetup = await window.helper.checkIfPlaybackReportingInstalled() @@ -805,9 +818,10 @@ const viewImportOfflinePlayback = html` ${() => header}
    -

    We noticed you've been using Finamp's beta version to listen to music

    +

    We noticed you've been using Finamp's beta version to listen to music.

    Finamp keeps track of your playback history even when you're not connected to your server, and you can now import that history!

    Imported plays will only be added to the Playback Reporting addon's database, but can then used to generate a more accurate Rewind report for you in the following steps.

    +

    Make sure to only import this once!

    @@ -820,6 +834,10 @@ const viewImportOfflinePlayback = html` input.disabled = true state.offlinePlayback = await importOfflinePlayback(e.target.files[0]) console.log(`state.offlinePlayback:`, state.offlinePlayback) + + // import plays to server + await uploadOfflinePlayback(state.offlinePlayback, state.auth) + state.currentView = `importLastYearsReport` } catch (err) { console.error(`Error while importing offline playback data:`, err) @@ -831,11 +849,12 @@ const viewImportOfflinePlayback = html` ${() => state.importingOfflinePlayback ? html`

    Importing, please wait a few seconds...

    ` : html` +

    Already imported your offline plays or don't have any?

    ` } @@ -852,7 +871,6 @@ const viewImportLastYearsReport = html` ${() => header}
    -

    Awesome, you're logged in!

    You can now import last year's Jellyfin Rewind report, if you have one.

    This will give you more, and more reliable, statistics about your listening activity.

    diff --git a/src/rewind.js b/src/rewind.js index 8cf06d5..2d6583a 100644 --- a/src/rewind.js +++ b/src/rewind.js @@ -213,7 +213,7 @@ function indexAlbums(albumInfoJSON) { return items } -async function loadItemInfo(items) { +export async function loadItemInfo(items) { const params = { // 'SortBy': `Album,SortName`, @@ -222,7 +222,7 @@ async function loadItemInfo(items) { 'Recursive': `true`, 'Fields': `AudioInfo,ParentId,Ak,Genres`, 'EnableImageTypes': `Primary`, - // 'Ids': [...new Set(items.map(item => item.ItemId))].join(','), + 'Ids': !!items ? [...new Set(items)].join(',') : undefined, } const queryParams = Object.entries(params).map(([key, value]) => `${key}=${value}`).join('&') From adf0cb5dd602887ca1458427330e50ab417eadef Mon Sep 17 00:00:00 2001 From: Chaphasilor Date: Tue, 31 Dec 2024 16:39:22 +0100 Subject: [PATCH 09/15] added animations --- src/features.js | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/src/features.js b/src/features.js index eaf9a94..7102e15 100644 --- a/src/features.js +++ b/src/features.js @@ -59,7 +59,13 @@ state.featureSideEffects = { }, 3: { load: loadTopTracksMedia, - enter: playTopTracks, + enter: () => { + playTopTracks() + + // slide in top tracks + let topItems = document.querySelectorAll(`#top-tracks-main-feature li`) + animate(topItems, { opacity: [0, 1], x: [500, 0] }, { duration: 0.35, ease: `easeOut`, delay: stagger(0.1) }); + }, }, 4: { enter: () => { @@ -85,7 +91,13 @@ state.featureSideEffects = { }, 6: { load: loadTopArtistsMedia, - enter: playTopArtists, + enter: () => { + playTopArtists() + + // slide in top artists + let topItems = document.querySelectorAll(`#top-artists-main-feature li`) + animate(topItems, { opacity: [0, 1], x: [500, 0] }, { duration: 0.35, ease: `easeOut`, delay: stagger(0.1) }); + }, }, 7: { }, @@ -95,7 +107,13 @@ state.featureSideEffects = { }, 9: { load: loadTopAlbumsMedia, - enter: playTopAlbums, + enter: () => { + playTopAlbums() + + // slide in top albums + let topItems = document.querySelectorAll(`#top-albums-main-feature li`) + animate(topItems, { opacity: [0, 1], x: [500, 0] }, { duration: 0.35, ease: `easeOut`, delay: stagger(0.1) }); + }, }, 10: { }, @@ -104,8 +122,8 @@ state.featureSideEffects = { playTopGenres() // slide in top genres - let topCategories = document.querySelectorAll(`#top-genres-main-feature li`) - animate(topCategories, { opacity: [0, 1], x: [500, 0] }, { duration: 0.35, ease: `easeOut`, delay: stagger(0.1) }); + let topItems = document.querySelectorAll(`#top-genres-main-feature li`) + animate(topItems, { opacity: [0, 1], x: [500, 0] }, { duration: 0.35, ease: `easeOut`, delay: stagger(0.1) }); }, }, 12: { @@ -117,6 +135,9 @@ state.featureSideEffects = { enter: () => { if (state.extraFeatures.leastSkippedTracks) { playLeastSkippedTracks() + // slide in least skipped tracks + let topItems = document.querySelectorAll(`#least-skipped-tracks-main-feature li`) + animate(topItems, { opacity: [0, 1], x: [500, 0] }, { duration: 0.35, ease: `easeOut`, delay: stagger(0.1) }); } }, }, @@ -129,6 +150,9 @@ state.featureSideEffects = { enter: () => { if (state.extraFeatures.mostSkippedTracks) { playMostSkippedTracks() + // slide in most skipped tracks + let topItems = document.querySelectorAll(`#most-skipped-tracks-main-feature li`) + animate(topItems, { opacity: [0, 1], x: [500, 0] }, { duration: 0.35, ease: `easeOut`, delay: stagger(0.1) }); } } }, @@ -259,7 +283,7 @@ state.features = [ buildFeature(`top tracks of the year`, html`

    Your Top Tracks
    of the year

    -
      +
        ${() => state.rewindReport.tracks?.[state.settings.rankingMetric]?.slice(0, 5).map((track, index) => html`
      1. @@ -400,7 +424,7 @@ state.features = [ buildFeature(`top artists of the year`, html`

        Your Top Artists
        of the year

        -
          +
            ${() => state.rewindReport.artists?.[state.settings.rankingMetric]?.slice(0, 5).map((artist, index) => html`
          1. @@ -525,7 +549,7 @@ state.features = [ buildFeature(`top albums of the year`, html`

            Your Top Albums
            of the year

            -
              +
                ${() => state.rewindReport.albums?.[state.settings.rankingMetric]?.slice(0, 5).map((album, index) => html`
              1. @@ -730,7 +754,7 @@ state.features = [
            ` : html` -
              +
                ${() => state.rewindReport.tracks?.[`leastSkipped`]?.slice(0, 5).map((track, index) => html`
              1. @@ -832,7 +856,7 @@ state.features = [
            ` : html` -
              +
                ${() => state.rewindReport.tracks?.[`mostSkipped`]?.slice(0, 5).map((track, index) => html`
              1. From 500f1073a50e92f8d6877f6d6861679fbba10a1f Mon Sep 17 00:00:00 2001 From: Chaphasilor Date: Tue, 31 Dec 2024 16:39:32 +0100 Subject: [PATCH 10/15] small fixes --- src/features.js | 10 ++++++---- src/onboarding.js | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/features.js b/src/features.js index 7102e15..03a7966 100644 --- a/src/features.js +++ b/src/features.js @@ -1049,16 +1049,18 @@ state.features = [ ${() => state.extraFeatures.listeningActivityDifference ? html`
                -
                ${() => showAsNumber(Math.abs(state.rewindReport?.featureDelta?.listeningActivityDifference?.totalPlays[state.settings.dataSource]).toFixed(0))} more streams than last year
                + ${() => state.rewindReport?.featureDelta?.listeningActivityDifference?.totalPlays[state.settings.dataSource] > 0 ? html` +
                ${() => showAsNumber(Math.abs(state.rewindReport?.featureDelta?.listeningActivityDifference?.totalPlays[state.settings.dataSource]).toFixed(0))} more streams than last year
                + ` : null}
                ${() => (!state.extraFeatures.listeningActivityDifference || state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.tracks >= 0) ? html` -
                ${() =>showAsNumber(Math.abs(state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.tracks))}
                more tracks
                +
                ${() =>showAsNumber(Math.abs(state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.tracks))} more tracks
                ` : null} ${() => (!state.extraFeatures.listeningActivityDifference || state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.artists >= 0) ? html` -
                ${() =>showAsNumber(Math.abs(state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.artists))}
                more artists
                +
                ${() =>showAsNumber(Math.abs(state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.artists))} more artists
                ` : null} ${() => (!state.extraFeatures.listeningActivityDifference || state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.albums >= 0) ? html` -
                ${() =>showAsNumber(Math.abs(state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.albums))}
                more albums
                +
                ${() =>showAsNumber(Math.abs(state.rewindReport?.featureDelta?.listeningActivityDifference?.uniquePlays.albums))} more albums
                ` : null}
                diff --git a/src/onboarding.js b/src/onboarding.js index 1b17ff2..3151afa 100644 --- a/src/onboarding.js +++ b/src/onboarding.js @@ -593,7 +593,7 @@ async function checkPlaybackReportingSetup(nextScreen) { } // check if eligible for offline playback import - if (await checkIfOfflinePlaybackImportAvailable(state.auth)) { + if (await checkIfOfflinePlaybackImportAvailable(state.auth) && nextScreen !== `revisit`) { state.currentView = `importOfflinePlayback` return } From b28e3ecc1c7ed5790b7f5ad46c1063ec253d184a Mon Sep 17 00:00:00 2001 From: Chaphasilor Date: Tue, 31 Dec 2024 17:07:35 +0100 Subject: [PATCH 11/15] update branding with new icon --- index.html | 4 ++-- public/media/banner-dark.png | Bin 24908 -> 26625 bytes public/media/banner-dark.svg | 26 ++++++++++++++++++++++++++ public/media/banner-light.png | Bin 23177 -> 24887 bytes public/media/banner-light.svg | 26 ++++++++++++++++++++++++++ public/media/jellyfin-rewind-icon.png | Bin 0 -> 85037 bytes public/media/jellyfin-rewind-icon.svg | 18 ++++++++++++++++++ src/features.js | 6 ++---- src/onboarding.js | 5 ++--- 9 files changed, 76 insertions(+), 9 deletions(-) create mode 100644 public/media/banner-dark.svg create mode 100644 public/media/banner-light.svg create mode 100644 public/media/jellyfin-rewind-icon.png create mode 100644 public/media/jellyfin-rewind-icon.svg diff --git a/index.html b/index.html index 28019d1..52f53ff 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,7 @@ - + @@ -10,7 +10,7 @@ - + diff --git a/public/media/banner-dark.png b/public/media/banner-dark.png index 6e7bd8149245e4f5982ef96eee691a63ab583c20..7b7ae45799e8a42c390b36c7d135f9d424ee4772 100644 GIT binary patch literal 26625 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 literal 24908 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 VcUCn0mwRqvZen9xW90tue*oup_(uQ$ diff --git a/public/media/banner-dark.svg b/public/media/banner-dark.svg new file mode 100644 index 0000000..b485b44 --- /dev/null +++ b/public/media/banner-dark.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/media/banner-light.png b/public/media/banner-light.png index fa4ff5b450a1c74e7183efccd05fa580c0caa779..ce1554694cd6871b11cfcb0b0c1f7d0641a1d8d2 100644 GIT binary patch literal 24887 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 e|KAqye(gCix8bF4lKPOZCJsTzr-g1 diff --git a/public/media/banner-light.svg b/public/media/banner-light.svg new file mode 100644 index 0000000..e642d9e --- /dev/null +++ b/public/media/banner-light.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/media/jellyfin-rewind-icon.png b/public/media/jellyfin-rewind-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e025904413f78fa1aa95236e8ec32880e79404a3 GIT binary patch literal 85037 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 rT;L(Q_QvW@+qz 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 + + + + + + + + + + + + + + + + + diff --git a/src/features.js b/src/features.js index 03a7966..06fe3fa 100644 --- a/src/features.js +++ b/src/features.js @@ -178,12 +178,10 @@ state.features = [ buildFeature(`intro`, html`
                -
                - Jellyfin Rewind Logo -

                Rewind

                +
                + Jellyfin Rewind Logo
                -

                Welcome to
                Jellyfin Rewind ${() => state.rewindReport?.year}!

                diff --git a/src/onboarding.js b/src/onboarding.js index 3151afa..5871b07 100644 --- a/src/onboarding.js +++ b/src/onboarding.js @@ -139,8 +139,7 @@ watch(() => [ const header = html`
                - Jellyfin Rewind Logo -

                Rewind

                + Jellyfin Rewind Logo
                ` @@ -932,7 +931,7 @@ const viewImportLastYearsReport = html` const progressBar = html`
                - +
                Date: Tue, 31 Dec 2024 18:16:05 +0100 Subject: [PATCH 12/15] added offline plays export help dialog --- src/onboarding.js | 49 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/onboarding.js b/src/onboarding.js index 5871b07..f2c8d3c 100644 --- a/src/onboarding.js +++ b/src/onboarding.js @@ -34,6 +34,7 @@ export const state = reactive({ playbackReportingInspectionResult: null, connectionHelpDialogOpen: false, playbackReportingDialogOpen: false, + finampOfflineExportDialogOpen: false, featuresInitialized: false, darkMode: null, selectedAction: null, @@ -81,6 +82,7 @@ export async function init(auth) { state.rewindReport = restored.rewindReportData state.staleReport = restored.staleReport + console.log(`state.rewindReport:`, state.rewindReport) console.log(`state.auth.config.user:`, state.auth.config.user) if (state.auth?.config?.user) { await checkPlaybackReportingSetup(`revisit`) @@ -129,6 +131,7 @@ export function render() { ${() => state.views[state.currentView]} ${() => state.connectionHelpDialogOpen ? connectionHelpDialog : null} ${() => state.playbackReportingDialogOpen ? playbackReportingDialog : null} + ${() => state.finampOfflineExportDialogOpen ? finampOfflineExportDialog : null}
                `(onboardingElement) } @@ -295,6 +298,43 @@ const playbackReportingDialog = html`
                ` +const finampOfflineExportDialog = html` +
                +
                +
                +
                +

                Importing Offline Plays from Finamp (Beta)

                + +
                +
                +
                +

                If you're using Finamp's beta version, you can easily export your offline plays to a file, and then import that file here into Jellyfin Rewind. Just follow these steps:

                +
                  +
                1. 1. Open Finamp
                2. +
                3. 2. Open the side menu / drawer
                4. +
                5. 3. Go to the "Playback History" screen
                6. +
                7. 4. Click the "Share" icon at the top right
                8. +
                9. 5. Save the file, for example by sharing it to a file manager or sending it to yourself via email
                10. +
                +

                Once you're ready, click the button below, and then click the "Import Offline Playback History" button and select the file you just exported

                + +

                I'll try to make this less complicated next year...

                +
                +
                +
                +
                +
                +` async function connect(url) { state.error = null @@ -823,6 +863,13 @@ const viewImportOfflinePlayback = html`

                Make sure to only import this once!

                + +
                Regenerate Rewind From 3ad9080a568536f28fccb7da1d1091156895aad4 Mon Sep 17 00:00:00 2001 From: Chaphasilor Date: Tue, 31 Dec 2024 18:33:04 +0100 Subject: [PATCH 13/15] account for negative durations, fix plausibility check --- src/rewind.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/rewind.js b/src/rewind.js index 2d6583a..6346749 100644 --- a/src/rewind.js +++ b/src/rewind.js @@ -67,6 +67,10 @@ function adjustPlaybackReportJSON(playbackReportJSON, indexedItemInfo) { playbackReportJSON.notFound = [] // deleted or otherwise not found items that should still contribute to the general stats for (const index in playbackReportJSON.items) { const item = playbackReportJSON.items[index] //!!! don't use this as a left-hand expression + + if (Number(item.PlayDuration) < 0) { + console.error(`item duration:`, item) + } const itemInfo = indexedItemInfo[item.ItemId] if (!itemInfo) { console.warn(`Item info not found: ${item.ItemId} (${item.ItemName})`) @@ -78,20 +82,21 @@ function adjustPlaybackReportJSON(playbackReportJSON, indexedItemInfo) { // adjust playback duration if necessary let playbackReportDuration = Number(item.PlayDuration) - if (isNaN(playbackReportDuration)) { + if (isNaN(playbackReportDuration) || playbackReportDuration < 0) { playbackReportDuration = 0 } const jellyfinItemDuration = Math.ceil(itemInfo.RunTimeTicks / 10000000) // get duration in seconds - if (playbackReportDuration > jellyfinItemDuration + 1) { - console.debug(`Wrong duration for ${item.ItemId} (${item.ItemName}), adjusting from ${playbackReportDuration} to ${jellyfinItemDuration}`) + playbackReportJSON.items[index].PlayDuration = playbackReportDuration + if (playbackReportJSON.items[index].PlayDuration > jellyfinItemDuration + 1) { + console.debug(`Wrong duration for ${item.ItemId} (${item.ItemName}), adjusting from ${playbackReportJSON.items[index].PlayDuration} to ${jellyfinItemDuration}`) playbackReportJSON.items[index].PlayDuration = jellyfinItemDuration playbackReportJSON.items[index].FullySkipped = false playbackReportJSON.items[index].PartiallySkipped = false - } else if (playbackReportDuration < (jellyfinItemDuration - 1) * 0.3) { + } else if (playbackReportJSON.items[index].PlayDuration < (jellyfinItemDuration - 1) * 0.3) { playbackReportJSON.items[index].FullySkipped = true playbackReportJSON.items[index].PartiallySkipped = true - } else if (playbackReportDuration < (jellyfinItemDuration - 1) * 0.7) { + } else if (playbackReportJSON.items[index].PlayDuration < (jellyfinItemDuration - 1) * 0.7) { playbackReportJSON.items[index].FullySkipped = false playbackReportJSON.items[index].PartiallySkipped = true } From 019fd9435b89065cc7c76e5d73fb1686476fe8c8 Mon Sep 17 00:00:00 2001 From: Chaphasilor Date: Tue, 31 Dec 2024 18:33:20 +0100 Subject: [PATCH 14/15] change default ranking metric if playback reporting is available --- src/features.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/features.js b/src/features.js index 06fe3fa..57add50 100644 --- a/src/features.js +++ b/src/features.js @@ -1604,11 +1604,11 @@ export function init(rewindReportData, jellyHelper, auth) { console.log(`dataSource:`, state.settings.dataSource) - if (state.settings.dataSource === `playbackReport`) { - state.settings.rankingMetric = `duration` - } else { - state.settings.rankingMetric = `playCount` - } + // if (state.settings.dataSource === `playbackReport`) { + // state.settings.rankingMetric = `duration` + // } else { + // state.settings.rankingMetric = `playCount` + // } if (state.settings.dataSource === `jellyfin`) { state.extraFeatures.leastSkippedTracks = false From 3466095eaeac54c353ac4f7d56d27902d3706605 Mon Sep 17 00:00:00 2001 From: Chaphasilor Date: Tue, 31 Dec 2024 18:33:29 +0100 Subject: [PATCH 15/15] remove default server URL --- public/config.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/public/config.json b/public/config.json index ab14fd5..21c294f 100644 --- a/public/config.json +++ b/public/config.json @@ -2,8 +2,5 @@ "targetRange": { "start": "2024-01-01", "end": "2024-12-31" - }, - "server": { - "url": "http://192.168.31.40:30013" } }