Add ListenBrainz implementation

This commit is contained in:
Mar0xy 2023-07-30 02:38:01 +02:00
parent 11cc209025
commit 3571289d28
No known key found for this signature in database
GPG Key ID: 56569BBE47D2C828
6 changed files with 136 additions and 1 deletions

View File

@ -20,6 +20,12 @@ export const settings = {
disableHardwareMediaKeys: "disableHardwareMediaKeys", disableHardwareMediaKeys: "disableHardwareMediaKeys",
enableCustomHotkeys: "enableCustomHotkeys", enableCustomHotkeys: "enableCustomHotkeys",
enableDiscord: "enableDiscord", enableDiscord: "enableDiscord",
ListenBrainz: {
root: "ListenBrainz",
enabled: "ListenBrainz.enabled",
api: "ListenBrainz.api",
token: "ListenBrainz.token",
},
flags: { flags: {
root: "flags", root: "flags",
disableHardwareMediaKeys: "flags.disableHardwareMediaKeys", disableHardwareMediaKeys: "flags.disableHardwareMediaKeys",

View File

@ -0,0 +1,83 @@
import axios from "axios";
import { settingsStore } from "../../scripts/settings";
import { settings } from "../../constants/settings";
import { MediaStatus } from "../../models/mediaStatus";
import Store from "electron-store";
const ListenBrainzStore = new Store();
export class ListenBrainz {
/**
* Call the ListenBrainz API and create playing now payload
* @param title
* @param artists
* @param status
* @param duration
*/
public static async scrobble(title: string, artists: string, status: string, duration: number): Promise<any> {
try {
if (status == MediaStatus.paused) {
return false;
} else {
// Fetches the OldData required for scrobbling and proceeds to construct a playing_now data payload for the Playing Now area
const OldData = ListenBrainzStore.get("OldData") as string[];
const playing_data = {
listen_type: "playing_now",
payload: [
{
track_metadata: {
additional_info: {
media_player: "Tidal Hi-Fi",
submission_client: "Tidal Hi-Fi",
music_service: "listen.tidal.com",
duration: duration,
},
artist_name: artists,
track_name: title,
}
}
]
};
await axios.post(`${settingsStore.get(settings.ListenBrainz.api)}/1/submit-listens`, playing_data, {
headers:{
"Content-Type": "application/json",
"Authorization": `Token ${settingsStore.get(settings.ListenBrainz.token)}`
}
});
if (!OldData) {
ListenBrainzStore.set("OldData", [Math.floor(new Date().getTime() / 1000), title, artists, duration]);
} else if (OldData[1] != title) {
// This constructs the data required to scrobble the data after the song finishes
const scrobble_data = {
listen_type: "single",
payload: [
{
listened_at: OldData[0],
track_metadata: {
additional_info: {
media_player: "Tidal Hi-Fi",
submission_client: "Tidal Hi-Fi",
music_service: "listen.tidal.com",
duration: OldData[3],
},
artist_name: OldData[2],
track_name: OldData[1],
}
}
]
};
await axios.post(`${settingsStore.get(settings.ListenBrainz.api)}/1/submit-listens`, scrobble_data, {
headers:{
"Content-Type": "application/json",
"Authorization": `Token ${settingsStore.get(settings.ListenBrainz.token)}`
}
});
ListenBrainzStore.set("OldData", [Math.floor(new Date().getTime() / 1000), title, artists, duration]);
}
}
} catch (error) {
console.log(JSON.stringify(error));
}
}
}

View File

@ -25,7 +25,11 @@ let adBlock: HTMLInputElement,
skippedArtists: HTMLInputElement, skippedArtists: HTMLInputElement,
theme: HTMLSelectElement, theme: HTMLSelectElement,
trayIcon: HTMLInputElement, trayIcon: HTMLInputElement,
updateFrequency: HTMLInputElement; updateFrequency: HTMLInputElement,
enableListenBrainz: HTMLInputElement,
ListenBrainzAPI: HTMLInputElement,
ListenBrainzToken: HTMLInputElement;
function getThemeFiles() { function getThemeFiles() {
const selectElement = document.getElementById("themesList") as HTMLSelectElement; const selectElement = document.getElementById("themesList") as HTMLSelectElement;
const builtInThemes = getThemeListFromDirectory(process.resourcesPath); const builtInThemes = getThemeListFromDirectory(process.resourcesPath);
@ -87,6 +91,9 @@ function refreshSettings() {
skippedArtists.value = settingsStore.get<string, string[]>(settings.skippedArtists).join("\n"); skippedArtists.value = settingsStore.get<string, string[]>(settings.skippedArtists).join("\n");
trayIcon.checked = settingsStore.get(settings.trayIcon); trayIcon.checked = settingsStore.get(settings.trayIcon);
updateFrequency.value = settingsStore.get(settings.updateFrequency); updateFrequency.value = settingsStore.get(settings.updateFrequency);
enableListenBrainz.checked = settingsStore.get(settings.ListenBrainz.enabled);
ListenBrainzAPI.value = settingsStore.get(settings.ListenBrainz.api);
ListenBrainzToken.value = settingsStore.get(settings.ListenBrainz.token);
} }
/** /**
@ -183,6 +190,9 @@ window.addEventListener("DOMContentLoaded", () => {
skippedArtists = get("skippedArtists"); skippedArtists = get("skippedArtists");
singleInstance = get("singleInstance"); singleInstance = get("singleInstance");
updateFrequency = get("updateFrequency"); updateFrequency = get("updateFrequency");
enableListenBrainz = get("enableListenBrainz");
ListenBrainzAPI = get("ListenBrainzAPI");
ListenBrainzToken = get("ListenBrainzToken");
refreshSettings(); refreshSettings();
@ -206,4 +216,7 @@ window.addEventListener("DOMContentLoaded", () => {
addSelectListener(theme, settings.theme); addSelectListener(theme, settings.theme);
addInputListener(trayIcon, settings.trayIcon); addInputListener(trayIcon, settings.trayIcon);
addInputListener(updateFrequency, settings.updateFrequency); addInputListener(updateFrequency, settings.updateFrequency);
addInputListener(enableListenBrainz, settings.ListenBrainz.enabled);
addTextAreaListener(ListenBrainzAPI, settings.ListenBrainz.api);
addTextAreaListener(ListenBrainzToken, settings.ListenBrainz.token);
}); });

View File

@ -211,6 +211,30 @@
<span class="switch__slider"></span> <span class="switch__slider"></span>
</label> </label>
</div> </div>
<div class="group__option">
<div class="group__description">
<h4>ListenBrainz</h4>
<p>Scrobble your listens directly to ListenBrainz.</p>
</div>
<label class="switch">
<input id="enableListenBrainz" type="checkbox" />
<span class="switch__slider"></span>
</label>
</div>
<div class="group__option">
<div class="group__description">
<h4>ListenBrainz API Url</h4>
<p>There are multiple instances for ListenBrainz you can set the corresponding API url below.</p>
</div>
</div>
<textarea id="ListenBrainzAPI" class="textarea" cols="1" rows="1" spellcheck="false"></textarea>
<div class="group__option">
<div class="group__description">
<h4>ListenBrainz User Token</h4>
<p>Provide the user token you can get from the settings page.</p>
</div>
</div>
<textarea id="ListenBrainzToken" class="textarea" cols="1" rows="1" spellcheck="false"></textarea>
</div> </div>
</section> </section>

View File

@ -6,6 +6,7 @@ import { globalEvents } from "./constants/globalEvents";
import { settings } from "./constants/settings"; import { settings } from "./constants/settings";
import { statuses } from "./constants/statuses"; import { statuses } from "./constants/statuses";
import { Songwhip } from "./features/songwhip/songwhip"; import { Songwhip } from "./features/songwhip/songwhip";
import { ListenBrainz } from "./features/listenbrainz/listenbrainz";
import { Options } from "./models/options"; import { Options } from "./models/options";
import { downloadFile } from "./scripts/download"; import { downloadFile } from "./scripts/download";
import { addHotkey } from "./scripts/hotkeys"; import { addHotkey } from "./scripts/hotkeys";
@ -468,6 +469,9 @@ setInterval(function () {
updateMediaInfo(options, titleOrArtistsChanged); updateMediaInfo(options, titleOrArtistsChanged);
if (titleOrArtistsChanged) { if (titleOrArtistsChanged) {
updateMediaSession(options); updateMediaSession(options);
if (settingsStore.get(settings.ListenBrainz.enabled)) {
ListenBrainz.scrobble(options.title, options.artists, options.status, convertDuration(options.duration));
}
} }
}); });

View File

@ -18,6 +18,11 @@ export const settingsStore = new Store({
disableHardwareMediaKeys: false, disableHardwareMediaKeys: false,
enableCustomHotkeys: false, enableCustomHotkeys: false,
enableDiscord: false, enableDiscord: false,
ListenBrainz: {
enabled: false,
api: "https://api.listenbrainz.org",
token: "",
},
flags: { flags: {
gpuRasterization: true, gpuRasterization: true,
disableHardwareMediaKeys: false, disableHardwareMediaKeys: false,