simplified mediaInfo & Options

This commit is contained in:
Rick van Lieshout 2024-06-09 12:33:48 +02:00
parent b49bd925da
commit 0a8efc730d
7 changed files with 52 additions and 80 deletions

View File

@ -2,6 +2,7 @@
"cSpell.words": [ "cSpell.words": [
"Brainz", "Brainz",
"Castlabs", "Castlabs",
"Fi's",
"flac", "flac",
"Flatpak", "Flatpak",
"geqnfr", "geqnfr",

View File

@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Next]
- Simplified `MediaInfo` & `Options` types
- Added `playingFrom` information to the info API
- also changed the way we update Album info since Playing From now shows the correct Album.
## [5.13.1] ## [5.13.1]
- removed Swagger generation step in favor of pre-generated file. - removed Swagger generation step in favor of pre-generated file.

View File

@ -25,7 +25,8 @@
bar: '*[data-test="progress-bar"]', bar: '*[data-test="progress-bar"]',
footer: "#footerPlayer", footer: "#footerPlayer",
mediaItem: "[data-type='mediaItem']", mediaItem: "[data-type='mediaItem']",
album_header_title: '.header-details [data-test="title"]', album_header_title: '*[class^="playingFrom"] span:nth-child(2)',
playingFrom: '*[class^="playingFrom"] span:nth-child(2)',
currentlyPlaying: "[class^='isPlayingIcon'], [data-test-is-playing='true']", currentlyPlaying: "[class^='isPlayingIcon'], [data-test-is-playing='true']",
album_name_cell: '[class^="album"]', album_name_cell: '[class^="album"]',
tracklist_row: '[data-test="tracklist-row"]', tracklist_row: '[data-test="tracklist-row"]',

View File

@ -1,5 +1,5 @@
import { MediaStatus } from "./mediaStatus";
import { MediaPlayerInfo } from "./mediaPlayerInfo"; import { MediaPlayerInfo } from "./mediaPlayerInfo";
import { MediaStatus } from "./mediaStatus";
export interface MediaInfo { export interface MediaInfo {
title: string; title: string;
@ -8,6 +8,7 @@ export interface MediaInfo {
icon: string; icon: string;
status: MediaStatus; status: MediaStatus;
url: string; url: string;
playingFrom: string;
current: string; current: string;
currentInSeconds?: number; currentInSeconds?: number;
duration: string; duration: string;

View File

@ -1,15 +0,0 @@
export interface Options {
title: string;
artists: string;
album: string;
status: string;
url: string;
current: string;
currentInSeconds: number;
duration: string;
durationInSeconds: number;
"app-name": string;
image: string;
icon: string;
favorite: boolean;
}

View File

@ -13,16 +13,15 @@ import { Logger } from "./features/logger";
import { Songwhip } from "./features/songwhip/songwhip"; import { Songwhip } from "./features/songwhip/songwhip";
import { addCustomCss } from "./features/theming/theming"; import { addCustomCss } from "./features/theming/theming";
import { convertDurationToSeconds } from "./features/time/parse"; import { convertDurationToSeconds } from "./features/time/parse";
import { MediaInfo } from "./models/mediaInfo";
import { MediaStatus } from "./models/mediaStatus"; import { MediaStatus } from "./models/mediaStatus";
import { Options } from "./models/options"; import { RepeatState } from "./models/repeatState";
import { downloadFile } from "./scripts/download"; import { downloadFile } from "./scripts/download";
import { addHotkey } from "./scripts/hotkeys"; import { addHotkey } from "./scripts/hotkeys";
import { settingsStore } from "./scripts/settings"; import { settingsStore } from "./scripts/settings";
import { setTitle } from "./scripts/window-functions"; import { setTitle } from "./scripts/window-functions";
import { RepeatState } from "./models/repeatState";
const notificationPath = `${app.getPath("userData")}/notification.jpg`; const notificationPath = `${app.getPath("userData")}/notification.jpg`;
const appName = "TIDAL Hi-Fi";
let currentSong = ""; let currentSong = "";
let player: Player; let player: Player;
let currentPlayStatus = MediaStatus.paused; let currentPlayStatus = MediaStatus.paused;
@ -32,7 +31,7 @@ let scrobbleWaitingForDelay = false;
let currentlyPlaying = MediaStatus.paused; let currentlyPlaying = MediaStatus.paused;
let currentRepeatState: RepeatState = RepeatState.off; let currentRepeatState: RepeatState = RepeatState.off;
let currentShuffleState = false; let currentShuffleState = false;
let currentMediaInfo: Options; let currentMediaInfo: MediaInfo;
let currentNotification: Electron.Notification; let currentNotification: Electron.Notification;
const elements = { const elements = {
@ -57,7 +56,8 @@ const elements = {
bar: '*[data-test="progress-bar"]', bar: '*[data-test="progress-bar"]',
footer: "#footerPlayer", footer: "#footerPlayer",
mediaItem: "[data-type='mediaItem']", mediaItem: "[data-type='mediaItem']",
album_header_title: '.header-details [data-test="title"]', album_header_title: '*[class^="playingFrom"] span:nth-child(2)',
playing_from: '*[class^="playingFrom"] span:nth-child(2)',
currentlyPlaying: "[class^='isPlayingIcon'], [data-test-is-playing='true']", currentlyPlaying: "[class^='isPlayingIcon'], [data-test-is-playing='true']",
album_name_cell: '[class^="album"]', album_name_cell: '[class^="album"]',
tracklist_row: '[data-test="tracklist-row"]', tracklist_row: '[data-test="tracklist-row"]',
@ -380,23 +380,24 @@ function convertDuration(duration: string) {
/** /**
* Update Tidal-hifi's media info * Update Tidal-hifi's media info
* *
* @param {*} options * @param {*} mediaInfo
*/ */
function updateMediaInfo(options: Options, notify: boolean) { function updateMediaInfo(mediaInfo: MediaInfo, notify: boolean) {
if (options) { if (mediaInfo) {
currentMediaInfo = options; currentMediaInfo = mediaInfo;
ipcRenderer.send(globalEvents.updateInfo, options); ipcRenderer.send(globalEvents.updateInfo, mediaInfo);
if (settingsStore.get(settings.notifications) && notify) { if (settingsStore.get(settings.notifications) && notify) {
if (currentNotification) currentNotification.close(); if (currentNotification) currentNotification.close();
currentNotification = new Notification({ currentNotification = new Notification({
title: options.title, title: mediaInfo.title,
body: options.artists, body: mediaInfo.artists,
icon: options.icon, icon: mediaInfo.icon,
}); });
currentNotification.show(); currentNotification.show();
} }
updateMpris(options);
updateListenBrainz(options); updateMpris(mediaInfo);
updateListenBrainz(mediaInfo);
} }
} }
@ -454,32 +455,32 @@ function addMPRIS() {
} }
} }
function updateMpris(options: Options) { function updateMpris(mediaInfo: MediaInfo) {
if (player) { if (player) {
player.metadata = { player.metadata = {
...player.metadata, ...player.metadata,
...{ ...{
"xesam:title": options.title, "xesam:title": mediaInfo.title,
"xesam:artist": [options.artists], "xesam:artist": [mediaInfo.artists],
"xesam:album": options.album, "xesam:album": mediaInfo.album,
"mpris:artUrl": options.image, "mpris:artUrl": mediaInfo.image,
"mpris:length": convertDuration(options.duration) * 1000 * 1000, "mpris:length": convertDuration(mediaInfo.duration) * 1000 * 1000,
"mpris:trackid": "/org/mpris/MediaPlayer2/track/" + getTrackID(), "mpris:trackid": "/org/mpris/MediaPlayer2/track/" + getTrackID(),
}, },
}; };
player.playbackStatus = options.status === MediaStatus.paused ? "Paused" : "Playing"; player.playbackStatus = mediaInfo.status === MediaStatus.paused ? "Paused" : "Playing";
} }
} }
/** /**
* Update the listenbrainz service with new data based on a few conditions * Update the listenbrainz service with new data based on a few conditions
*/ */
function updateListenBrainz(options: Options) { function updateListenBrainz(mediaInfo: MediaInfo) {
if (settingsStore.get(settings.ListenBrainz.enabled)) { if (settingsStore.get(settings.ListenBrainz.enabled)) {
const oldData = ListenBrainzStore.get(ListenBrainzConstants.oldData) as StoreData; const oldData = ListenBrainzStore.get(ListenBrainzConstants.oldData) as StoreData;
if ( if (
(!oldData && options.status === MediaStatus.playing) || (!oldData && mediaInfo.status === MediaStatus.playing) ||
(oldData && oldData.title !== options.title) (oldData && oldData.title !== mediaInfo.title)
) { ) {
if (!scrobbleWaitingForDelay) { if (!scrobbleWaitingForDelay) {
scrobbleWaitingForDelay = true; scrobbleWaitingForDelay = true;
@ -487,10 +488,10 @@ function updateListenBrainz(options: Options) {
currentListenBrainzDelayId = setTimeout( currentListenBrainzDelayId = setTimeout(
() => { () => {
ListenBrainz.scrobble( ListenBrainz.scrobble(
options.title, mediaInfo.title,
options.artists, mediaInfo.artists,
options.status, mediaInfo.status,
convertDuration(options.duration) convertDuration(mediaInfo.duration)
); );
scrobbleWaitingForDelay = false; scrobbleWaitingForDelay = false;
}, },
@ -547,20 +548,19 @@ setInterval(function () {
if (repeatStateChanged) currentRepeatState = repeatState; if (repeatStateChanged) currentRepeatState = repeatState;
skipArtistsIfFoundInSkippedArtistsList(artistsArray); skipArtistsIfFoundInSkippedArtistsList(artistsArray);
const album = elements.getAlbumName(); const album = elements.getAlbumName();
const duration = elements.getText("duration"); const duration = elements.getText("duration");
const options = { const options: MediaInfo = {
title, title,
artists: artistsString, artists: artistsString,
album: album, album: album,
playingFrom: elements.getText("playing_from"),
status: currentStatus, status: currentStatus,
url: getTrackURL(), url: getTrackURL(),
current, current,
currentInSeconds: convertDurationToSeconds(current), currentInSeconds: convertDurationToSeconds(current),
duration, duration,
durationInSeconds: convertDurationToSeconds(duration), durationInSeconds: convertDurationToSeconds(duration),
"app-name": appName,
image: "", image: "",
icon: "", icon: "",
favorite: elements.isFavorite(), favorite: elements.isFavorite(),

View File

@ -2,12 +2,13 @@ import { MediaInfo } from "../models/mediaInfo";
import { MediaStatus } from "../models/mediaStatus"; import { MediaStatus } from "../models/mediaStatus";
import { RepeatState } from "../models/repeatState"; import { RepeatState } from "../models/repeatState";
export const mediaInfo = { const defaultInfo: MediaInfo = {
title: "", title: "",
artists: "", artists: "",
album: "", album: "",
icon: "", icon: "",
status: MediaStatus.paused as string, playingFrom: "",
status: MediaStatus.paused,
url: "", url: "",
current: "", current: "",
currentInSeconds: 0, currentInSeconds: 0,
@ -17,41 +18,18 @@ export const mediaInfo = {
favorite: false, favorite: false,
player: { player: {
status: MediaStatus.paused as string, status: MediaStatus.paused,
shuffle: false, shuffle: false,
repeat: RepeatState.off as string, repeat: RepeatState.off,
}, },
}; };
export let mediaInfo: MediaInfo = { ...defaultInfo };
export const updateMediaInfo = (arg: MediaInfo) => { export const updateMediaInfo = (arg: MediaInfo) => {
mediaInfo.title = propOrDefault(arg.title); mediaInfo = { ...defaultInfo, ...arg };
mediaInfo.artists = propOrDefault(arg.artists); mediaInfo.url = toUniversalUrl(mediaInfo.url);
mediaInfo.album = propOrDefault(arg.album);
mediaInfo.icon = propOrDefault(arg.icon);
mediaInfo.url = toUniversalUrl(propOrDefault(arg.url));
mediaInfo.status = propOrDefault(arg.status);
mediaInfo.current = propOrDefault(arg.current);
mediaInfo.currentInSeconds = arg.currentInSeconds ?? 0;
mediaInfo.duration = propOrDefault(arg.duration);
mediaInfo.durationInSeconds = arg.durationInSeconds ?? 0;
mediaInfo.image = propOrDefault(arg.image);
mediaInfo.favorite = arg.favorite;
mediaInfo.player = {
status: propOrDefault(arg.player?.status),
shuffle: arg.player?.shuffle ?? false,
repeat: propOrDefault(arg.player?.repeat),
}; };
};
/**
* Return the property or a default value
* @param {*} prop property to check
* @param {*} defaultValue defaults to ""
*/
function propOrDefault(prop: string, defaultValue = "") {
return prop || defaultValue;
}
/** /**
* Append the universal link syntax (?u) to any url * Append the universal link syntax (?u) to any url