diff --git a/package-lock.json b/package-lock.json index 26fcbe8..8a42dd6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -414,9 +414,9 @@ } }, "node_modules/@discordjs/rest": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.4.0.tgz", - "integrity": "sha512-Xb2irDqNcq+O8F0/k/NaDp7+t091p+acb51iA4bCKfIn+WFWd6HrNvcsSbMMxIR9NjcMZS6NReTKygqiQN+ntw==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.4.3.tgz", + "integrity": "sha512-+SO4RKvWsM+y8uFHgYQrcTl/3+cY02uQOH7/7bKbVZsTfrfpoE62o5p+mmV+s7FVhTX82/kQUGGbu4YlV60RtA==", "license": "Apache-2.0", "dependencies": { "@discordjs/collection": "^2.1.1", @@ -424,10 +424,10 @@ "@sapphire/async-queue": "^1.5.3", "@sapphire/snowflake": "^3.5.3", "@vladfrangu/async_event_emitter": "^2.4.6", - "discord-api-types": "0.37.97", + "discord-api-types": "^0.37.119", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", - "undici": "6.19.8" + "undici": "6.21.1" }, "engines": { "node": ">=18" @@ -436,12 +436,6 @@ "url": "https://github.com/discordjs/discord.js?sponsor" } }, - "node_modules/@discordjs/rest/node_modules/discord-api-types": { - "version": "0.37.97", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.97.tgz", - "integrity": "sha512-No1BXPcVkyVD4ZVmbNgDKaBoqgeQ+FJpzZ8wqHkfmBnTZig1FcH3iPPersiK1TUIAzgClh2IvOuVUYfcWLQAOA==", - "license": "MIT" - }, "node_modules/@discordjs/util": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.1.1.tgz", @@ -3196,10 +3190,11 @@ "license": "MIT" }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -3490,9 +3485,9 @@ } }, "node_modules/discord-api-types": { - "version": "0.37.103", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.103.tgz", - "integrity": "sha512-r+qitxXKe2l6KFw5odPdZSSqdEou+7eNC7BfbZ7mny5Me/K06wCTeKUMVeH/YsI9+4QQudskeQ307kr/7ppQ1A==", + "version": "0.37.119", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.119.tgz", + "integrity": "sha512-WasbGFXEB+VQWXlo6IpW3oUv73Yuau1Ig4AZF/m13tXcTKnMpc/mHjpztIlz4+BM9FG9BHQkEXiPto3bKduQUg==", "license": "MIT" }, "node_modules/dmg-builder": { @@ -6223,9 +6218,9 @@ "optional": true }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "dev": true, "funding": [ { @@ -6233,6 +6228,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -8825,9 +8821,9 @@ "dev": true }, "node_modules/undici": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.8.tgz", - "integrity": "sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==", + "version": "6.21.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.1.tgz", + "integrity": "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==", "license": "MIT", "engines": { "node": ">=18.17" diff --git a/src/TidalControllers/DomController/DomControllerOptions.ts b/src/TidalControllers/DomController/DomControllerOptions.ts new file mode 100644 index 0000000..71357cb --- /dev/null +++ b/src/TidalControllers/DomController/DomControllerOptions.ts @@ -0,0 +1,6 @@ +export type DomControllerOptions = { + /** + * Interval that tidal-hifi scrapes the dom for information + */ + refreshInterval: number; +}; diff --git a/src/TidalControllers/DomTidalController.ts b/src/TidalControllers/DomController/DomTidalController.ts similarity index 73% rename from src/TidalControllers/DomTidalController.ts rename to src/TidalControllers/DomController/DomTidalController.ts index 8ed6f7a..6eb735d 100644 --- a/src/TidalControllers/DomTidalController.ts +++ b/src/TidalControllers/DomController/DomTidalController.ts @@ -1,18 +1,15 @@ -import { MediaStatus } from "../models/mediaStatus"; -import { RepeatState } from "../models/repeatState"; -import { TidalController } from "./TidalController"; +import { convertDurationToSeconds } from "../../features/time/parse"; +import { MediaInfo } from "../../models/mediaInfo"; +import { MediaStatus } from "../../models/mediaStatus"; +import { RepeatState } from "../../models/repeatState"; +import { TidalController } from "../TidalController"; +import { DomControllerOptions } from "./DomControllerOptions"; -export class DomTidalController implements TidalController { - private currentPlayStatus = MediaStatus.paused; - - /** - * Convert the duration from MM:SS to seconds - * @param {*} duration - */ - private convertDuration(duration: string) { - const parts = duration.split(":"); - return parseInt(parts[1]) + 60 * parseInt(parts[0]); - } +export class DomTidalController implements TidalController { + private updateSubscriber: (state: Partial) => void; + private currentlyPlaying = MediaStatus.paused; + private currentRepeatState: RepeatState = RepeatState.off; + private currentShuffleState = false; private readonly elements = { play: '*[data-test="play"]', @@ -106,7 +103,7 @@ export class DomTidalController implements TidalController { globalThis.location.href.includes("/playlist/") || globalThis.location.href.includes("/mix/") ) { - if (this.currentPlayStatus === MediaStatus.playing) { + if (this.getCurrentlyPlayingStatus() === MediaStatus.playing) { // find the currently playing element from the list (which might be in an album icon), traverse back up to the mediaItem (row) and select the album cell. // document.querySelector("[class^='isPlayingIcon'], [data-test-is-playing='true']").closest('[data-type="mediaItem"]').querySelector('[class^="album"]').textContent const row = window.document.querySelector(this.currentlyPlaying).closest(this.mediaItem); @@ -160,6 +157,65 @@ export class DomTidalController implements TidalController { }, }; + onMediaInfoUpdate(callback: (state: Partial) => void): void { + this.updateSubscriber = callback; + } + + bootstrap(options: DomControllerOptions): void { + /** + * Checks if Tidal is playing a video or song by grabbing the "a" element from the title. + * If it's a song it returns the track URL, if not it will return undefined + */ + const getTrackURL = () => { + const id = this.getTrackId(); + return `https://tidal.com/browse/track/${id}`; + }; + + setInterval(async () => { + const title = this.getTitle(); + const artistsArray = this.getArtists(); + const artistsString = this.getArtistsString(); + + const current = this.getCurrentTime(); + const currentStatus = this.getCurrentlyPlayingStatus(); + const shuffleState = this.getCurrentShuffleState(); + const repeatState = this.getCurrentRepeatState(); + + const playStateChanged = currentStatus != this.currentlyPlaying; + const shuffleStateChanged = shuffleState != this.currentShuffleState; + const repeatStateChanged = repeatState != this.currentRepeatState; + + if (playStateChanged) this.currentlyPlaying = currentStatus; + if (shuffleStateChanged) this.currentShuffleState = shuffleState; + if (repeatStateChanged) this.currentRepeatState = repeatState; + + const album = this.getAlbumName(); + const duration = this.getDuration(); + const updatedInfo = { + title, + artists: artistsString, + artistsArray, + album: album, + playingFrom: this.getPlayingFrom(), + status: currentStatus, + url: getTrackURL(), + current, + currentInSeconds: convertDurationToSeconds(current), + duration, + durationInSeconds: convertDurationToSeconds(duration), + image: this.getSongIcon(), + favorite: this.isFavorite(), + player: { + status: currentStatus, + shuffle: shuffleState, + repeat: repeatState, + }, + }; + + this.updateSubscriber(updatedInfo); + }, options.refreshInterval); + } + playPause = (): void => { const play = this.elements.get("play"); @@ -246,7 +302,7 @@ export class DomTidalController implements TidalController { return this.elements.getText("current"); } getCurrentPositionInSeconds(): number { - return this.convertDuration(this.getCurrentPosition()) * 1000 * 1000; + return convertDurationToSeconds(this.getCurrentPosition()); } getTrackId(): string { @@ -289,7 +345,4 @@ export class DomTidalController implements TidalController { getSongIcon(): string { return this.elements.getSongIcon(); } - setPlayStatus(status: MediaStatus): void { - this.currentPlayStatus = status; - } } diff --git a/src/TidalControllers/MediaSessionTidalController.ts b/src/TidalControllers/MediaSessionTidalController.ts index a474759..b783351 100644 --- a/src/TidalControllers/MediaSessionTidalController.ts +++ b/src/TidalControllers/MediaSessionTidalController.ts @@ -1,6 +1,7 @@ +import { MediaInfo } from "../models/mediaInfo"; import { MediaStatus } from "../models/mediaStatus"; import { RepeatState } from "../models/repeatState"; -import { DomTidalController } from "./DomTidalController"; +import { DomTidalController } from "./DomController/DomTidalController"; import { TidalController } from "./TidalController"; export class MediaSessionTidalController implements TidalController { @@ -9,15 +10,20 @@ export class MediaSessionTidalController implements TidalController { constructor() { this.domMediaController = new DomTidalController(); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onMediaInfoUpdate(callback: (state: Partial) => void): void { + globalThis.alert("method not implemented"); + throw new Error("Method not implemented."); + } + bootstrap(): void { + globalThis.alert("Method not implemented: "); + throw new Error("Method not implemented."); + } // example of using the original domMediaController as a fallback goToHome(): void { this.domMediaController.goToHome(); } - setPlayStatus(status: MediaStatus): void { - globalThis.alert("Method not implemented: " + status); - throw new Error("Method not implemented."); - } getDuration(): string { globalThis.alert("Method not implemented"); throw new Error("Method not implemented."); diff --git a/src/TidalControllers/TidalController.ts b/src/TidalControllers/TidalController.ts index 7bb1b9d..e1cee69 100644 --- a/src/TidalControllers/TidalController.ts +++ b/src/TidalControllers/TidalController.ts @@ -1,7 +1,8 @@ +import { MediaInfo } from "../models/mediaInfo"; import { MediaStatus } from "../models/mediaStatus"; import { RepeatState } from "../models/repeatState"; -export interface TidalController { +export interface TidalController { goToHome(): void; openSettings(): void; @@ -20,6 +21,17 @@ export interface TidalController { previous(): void; toggleShuffle(): void; + /** + * Optional setup/startup/bootstrap for this controller + */ + bootstrap(options: TBootstrapOptions): void; + + /** + * Method that triggers every time the MediaInfo updates + * @param callback function that receives the updated media info + */ + onMediaInfoUpdate(callback: (state: Partial) => void): void; + /** * Update the current status of tidal (e.g playing or paused) */ @@ -37,11 +49,5 @@ export interface TidalController { getArtistsString(): string; getPlayingFrom(): string; getSongIcon(): string; - isFavorite(): boolean; - // add an observable to react on instead of a hookup function - // onMediaChange(): any; - - // this can probably be removed after ^ - setPlayStatus(status: MediaStatus): void; } diff --git a/src/features/icon/downloadIcon.ts b/src/features/icon/downloadIcon.ts new file mode 100644 index 0000000..9b149f3 --- /dev/null +++ b/src/features/icon/downloadIcon.ts @@ -0,0 +1,15 @@ +import { downloadFile } from "../../scripts/download"; +import { Logger } from "../logger"; + +export const downloadIcon = async (imagePath: string, destination: string): Promise => { + if (imagePath.startsWith("http")) { + try { + return await downloadFile(imagePath, destination); + } catch (error) { + Logger.log("Downloading file failed", { error }); + return ""; + } + } + + return ""; +}; diff --git a/src/features/tidal/url.ts b/src/features/tidal/url.ts new file mode 100644 index 0000000..be30903 --- /dev/null +++ b/src/features/tidal/url.ts @@ -0,0 +1,15 @@ +/** + * Build a track url given the id + */ +export const getTrackURL = (trackId: string) => { + return `https://tidal.com/browse/track/${trackId}`; +}; + +/** + * Retrieve the universal link given a regular track link + * @param trackLink + * @returns + */ +export const getUniversalLink = (trackLink: string) => { + return `${trackLink}?u`; +}; diff --git a/src/models/mediaInfo.ts b/src/models/mediaInfo.ts index f7c2e5e..b996147 100644 --- a/src/models/mediaInfo.ts +++ b/src/models/mediaInfo.ts @@ -1,9 +1,11 @@ import { MediaPlayerInfo } from "./mediaPlayerInfo"; import { MediaStatus } from "./mediaStatus"; +import { RepeatState } from "./repeatState"; export interface MediaInfo { title: string; artists: string; + artistsArray?: string[]; album: string; icon: string; status: MediaStatus; @@ -17,3 +19,30 @@ export interface MediaInfo { favorite: boolean; player?: MediaPlayerInfo; } + +export const getEmptyMediaInfo = () => { + const emptyState: MediaInfo = { + title: "", + artists: "", + artistsArray: [], + album: "", + playingFrom: "", + status: MediaStatus.playing, + url: "", + current: "", + currentInSeconds: 100, + duration: "", + durationInSeconds: 100, + image: "", + icon: "", + favorite: true, + + player: { + status: MediaStatus.playing, + shuffle: true, + repeat: RepeatState.all, + }, + }; + + return emptyState; +}; diff --git a/src/preload.ts b/src/preload.ts index d7262f1..172a148 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -3,6 +3,7 @@ import { clipboard, ipcRenderer } from "electron"; import Player from "mpris-service"; import { globalEvents } from "./constants/globalEvents"; import { settings } from "./constants/settings"; +import { downloadIcon } from "./features/icon/downloadIcon"; import { ListenBrainz, ListenBrainzConstants, @@ -10,39 +11,42 @@ import { } from "./features/listenbrainz/listenbrainz"; import { StoreData } from "./features/listenbrainz/models/storeData"; import { Logger } from "./features/logger"; -import { SharingService } from "./features/sharingService/sharingService"; import { addCustomCss } from "./features/theming/theming"; +import { getTrackURL, getUniversalLink } from "./features/tidal/url"; import { convertDurationToSeconds } from "./features/time/parse"; -import { MediaInfo } from "./models/mediaInfo"; +import { getEmptyMediaInfo, MediaInfo } from "./models/mediaInfo"; import { MediaStatus } from "./models/mediaStatus"; -import { RepeatState } from "./models/repeatState"; -import { downloadFile } from "./scripts/download"; import { addHotkey } from "./scripts/hotkeys"; import { ObjectToDotNotation } from "./scripts/objectUtilities"; import { settingsStore } from "./scripts/settings"; import { setTitle } from "./scripts/window-functions"; -import { DomTidalController } from "./TidalControllers/DomTidalController"; +import { DomControllerOptions } from "./TidalControllers/DomController/DomControllerOptions"; +import { DomTidalController } from "./TidalControllers/DomController/DomTidalController"; import { MediaSessionTidalController } from "./TidalControllers/MediaSessionTidalController"; import { TidalController } from "./TidalControllers/TidalController"; const notificationPath = `${app.getPath("userData")}/notification.jpg`; +const staticTitle = "TIDAL Hi-Fi"; + let currentSong = ""; let player: Player; let currentListenBrainzDelayId: ReturnType; let scrobbleWaitingForDelay = false; -let currentlyPlaying = MediaStatus.paused; -let currentRepeatState: RepeatState = RepeatState.off; -let currentShuffleState = false; -let currentMediaInfo: MediaInfo; let currentNotification: Electron.Notification; let tidalController: TidalController; +let controllerOptions = {}; +let currentMediaInfo = getEmptyMediaInfo(); // TODO: replace with setting // eslint-disable-next-line no-constant-condition if (true) { tidalController = new DomTidalController(); + const domControllerOptions: DomControllerOptions = { + refreshInterval: getDomUpdateFrequency(), + }; + controllerOptions = domControllerOptions; } else { tidalController = new MediaSessionTidalController(); } @@ -51,7 +55,7 @@ if (true) { * Get the update frequency from the store * make sure it returns a number, if not use the default */ -function getUpdateFrequency() { +function getDomUpdateFrequency() { const storeValue = settingsStore.get(settings.updateFrequency); const defaultValue = 500; @@ -105,7 +109,7 @@ function addHotKeys() { tidalController.repeat(); }); addHotkey("control+w", async function () { - const url = SharingService.getUniversalLink(getTrackURL()); + const url = getUniversalLink(getTrackURL(tidalController.getTrackId())); clipboard.writeText(url); new Notification({ title: `Universal link generated: `, @@ -193,15 +197,6 @@ function addIPCEventListeners() { }); } -/** - * Convert the duration from MM:SS to seconds - * @param {*} duration - */ -function convertDuration(duration: string) { - const parts = duration.split(":"); - return parseInt(parts[1]) + 60 * parseInt(parts[0]); -} - /** * Update Tidal-hifi's media info * @@ -209,7 +204,6 @@ function convertDuration(duration: string) { */ function updateMediaInfo(mediaInfo: MediaInfo, notify: boolean) { if (mediaInfo) { - currentMediaInfo = mediaInfo; ipcRenderer.send(globalEvents.updateInfo, mediaInfo); updateMpris(mediaInfo); updateListenBrainz(mediaInfo); @@ -320,7 +314,7 @@ function updateMpris(mediaInfo: MediaInfo) { "xesam:artist": [mediaInfo.artists], "xesam:album": mediaInfo.album, "mpris:artUrl": mediaInfo.image, - "mpris:length": convertDuration(mediaInfo.duration) * 1000 * 1000, + "mpris:length": convertDurationToSeconds(mediaInfo.duration) * 1000 * 1000, "mpris:trackid": "/org/mpris/MediaPlayer2/track/" + tidalController.getTrackId(), }, ...ObjectToDotNotation(mediaInfo, "custom:"), @@ -348,7 +342,7 @@ function updateListenBrainz(mediaInfo: MediaInfo) { mediaInfo.title, mediaInfo.artists, mediaInfo.status, - convertDuration(mediaInfo.duration) + convertDurationToSeconds(mediaInfo.duration) ); scrobbleWaitingForDelay = false; }, @@ -359,103 +353,36 @@ function updateListenBrainz(mediaInfo: MediaInfo) { } } -/** - * Checks if Tidal is playing a video or song by grabbing the "a" element from the title. - * If it's a song it returns the track URL, if not it will return undefined - */ -function getTrackURL() { - const id = tidalController.getTrackId(); - return `https://tidal.com/browse/track/${id}`; -} +tidalController.bootstrap(controllerOptions); +tidalController.onMediaInfoUpdate(async (newState) => { + currentMediaInfo = { ...currentMediaInfo, ...newState }; -/** - * Watch for song changes and update title + notify - */ -setInterval(function () { - const title = tidalController.getTitle(); - const artistsArray = tidalController.getArtists(); - const artistsString = tidalController.getArtistsString(); - const songDashArtistTitle = `${title} - ${artistsString}`; - const staticTitle = "TIDAL Hi-Fi"; - const titleOrArtistsChanged = currentSong !== songDashArtistTitle; - const current = tidalController.getCurrentTime(); - const currentStatus = tidalController.getCurrentlyPlayingStatus(); - const shuffleState = tidalController.getCurrentShuffleState(); - const repeatState = tidalController.getCurrentRepeatState(); + const songDashArtistTitle = `${currentMediaInfo.title} - ${currentMediaInfo.artists}`; + const isNewSong = currentSong !== songDashArtistTitle; - const playStateChanged = currentStatus != currentlyPlaying; - const shuffleStateChanged = shuffleState != currentShuffleState; - const repeatStateChanged = repeatState != currentRepeatState; + if (isNewSong) { + // check whether one of the artists is in the "skip artist" array, if so, skip... + skipArtistsIfFoundInSkippedArtistsList(currentMediaInfo.artistsArray ?? []); - const hasStateChanged = playStateChanged || shuffleStateChanged || repeatStateChanged; + // update the currently playing song + currentSong = songDashArtistTitle; - // update info if song changed or was just paused/resumed - if (titleOrArtistsChanged || hasStateChanged) { - if (playStateChanged) currentlyPlaying = currentStatus; - if (shuffleStateChanged) currentShuffleState = shuffleState; - if (repeatStateChanged) currentRepeatState = repeatState; - - skipArtistsIfFoundInSkippedArtistsList(artistsArray); - const album = tidalController.getAlbumName(); - const duration = tidalController.getDuration(); - const options: MediaInfo = { - title, - artists: artistsString, - album: album, - playingFrom: tidalController.getPlayingFrom(), - status: currentStatus, - url: getTrackURL(), - current, - currentInSeconds: convertDurationToSeconds(current), - duration, - durationInSeconds: convertDurationToSeconds(duration), - image: "", - icon: "", - favorite: tidalController.isFavorite(), - - player: { - status: currentStatus, - shuffle: shuffleState, - repeat: repeatState, - }, - }; - - // update title, url and play info with new info + // update the window title with the new info settingsStore.get(settings.staticWindowTitle) ? setTitle(staticTitle) - : setTitle(songDashArtistTitle); - getTrackURL(); - currentSong = songDashArtistTitle; - tidalController.setPlayStatus(currentStatus); + : setTitle(`${currentMediaInfo.title} - ${currentMediaInfo.artists}`); - const image = tidalController.getSongIcon(); + // download a new icon and use it for the media info + if (!newState.icon && newState.image) { + currentMediaInfo.icon = await downloadIcon(currentMediaInfo.image, notificationPath); + } else { + currentMediaInfo.icon = ""; + } - new Promise((resolve) => { - if (image.startsWith("http")) { - options.image = image; - downloadFile(image, notificationPath).then( - () => { - options.icon = notificationPath; - resolve(); - }, - () => { - // if the image can't be downloaded then continue without it - resolve(); - } - ); - } else { - // if the image can't be found on the page continue without it - resolve(); - } - }).then(() => { - updateMediaInfo(options, titleOrArtistsChanged); - }); + updateMediaInfo(currentMediaInfo, true); } else { - // just update the time - updateMediaInfo( - { ...currentMediaInfo, ...{ current, currentInSeconds: convertDurationToSeconds(current) } }, - false - ); + // if titleOrArtists didn't change then only minor mediaInfo (like timings) changed, so don't bother the user with notifications + updateMediaInfo(currentMediaInfo, false); } /** @@ -475,8 +402,7 @@ setInterval(function () { } } } -}, getUpdateFrequency()); - +}); addMPRIS(); addCustomCss(app); addHotKeys(); diff --git a/src/scripts/download.ts b/src/scripts/download.ts index 01f365c..4261607 100644 --- a/src/scripts/download.ts +++ b/src/scripts/download.ts @@ -6,8 +6,8 @@ import request from "request"; * @param {string} fileUrl url to download * @param {string} targetPath path to save it at */ -export const downloadFile = function (fileUrl: string, targetPath: string) { - return new Promise((resolve, reject) => { +export const downloadFile = function (fileUrl: string, targetPath: string): Promise { + return new Promise((resolve, reject) => { const req = request({ method: "GET", uri: fileUrl, @@ -16,7 +16,9 @@ export const downloadFile = function (fileUrl: string, targetPath: string) { const out = fs.createWriteStream(targetPath); req.pipe(out); - req.on("end", resolve); + req.on("end", () => { + resolve(targetPath); + }); req.on("error", reject); }); diff --git a/src/scripts/mediaInfo.ts b/src/scripts/mediaInfo.ts index 7352bce..a1e0d06 100644 --- a/src/scripts/mediaInfo.ts +++ b/src/scripts/mediaInfo.ts @@ -1,28 +1,6 @@ -import { MediaInfo } from "../models/mediaInfo"; -import { MediaStatus } from "../models/mediaStatus"; -import { RepeatState } from "../models/repeatState"; +import { getEmptyMediaInfo, MediaInfo } from "../models/mediaInfo"; -const defaultInfo: MediaInfo = { - title: "", - artists: "", - album: "", - icon: "", - playingFrom: "", - status: MediaStatus.paused, - url: "", - current: "", - currentInSeconds: 0, - duration: "", - durationInSeconds: 0, - image: "tidal-hifi-icon", - favorite: false, - - player: { - status: MediaStatus.paused, - shuffle: false, - repeat: RepeatState.off, - }, -}; +const defaultInfo: MediaInfo = getEmptyMediaInfo(); export let mediaInfo: MediaInfo = { ...defaultInfo };