mirror of
https://github.com/Mastermindzh/tidal-hifi.git
synced 2024-11-24 22:42:42 +01:00
PoC: separated tidal interface controller into separate controllers
This commit is contained in:
parent
b2afd44dd6
commit
01c5ec7ebb
168
src/TidalControllers/DomTidalController.ts
Normal file
168
src/TidalControllers/DomTidalController.ts
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
import { TidalController } from "./MediaController";
|
||||||
|
|
||||||
|
export class DomTidalController implements TidalController {
|
||||||
|
public elements = {
|
||||||
|
play: '*[data-test="play"]',
|
||||||
|
pause: '*[data-test="pause"]',
|
||||||
|
next: '*[data-test="next"]',
|
||||||
|
previous: 'button[data-test="previous"]',
|
||||||
|
title: '*[data-test^="footer-track-title"]',
|
||||||
|
artists: '*[data-test^="grid-item-detail-text-title-artist"]',
|
||||||
|
home: '*[data-test="menu--home"]',
|
||||||
|
back: '[title^="Back"]',
|
||||||
|
forward: '[title^="Next"]',
|
||||||
|
search: '[class^="searchField"]',
|
||||||
|
shuffle: '*[data-test="shuffle"]',
|
||||||
|
repeat: '*[data-test="repeat"]',
|
||||||
|
account: '*[data-test^="profile-image-button"]',
|
||||||
|
settings: '*[data-test^="sidebar-menu-button"]',
|
||||||
|
openSettings: '*[data-test^="open-settings"]',
|
||||||
|
media: '*[data-test="current-media-imagery"]',
|
||||||
|
image: "img",
|
||||||
|
current: '*[data-test="current-time"]',
|
||||||
|
duration: '*[class^=playbackControlsContainer] *[data-test="duration"]',
|
||||||
|
bar: '*[data-test="progress-bar"]',
|
||||||
|
footer: "#footerPlayer",
|
||||||
|
mediaItem: "[data-type='mediaItem']",
|
||||||
|
album_header_title: '*[class^="playingFrom"] span:nth-child(2)',
|
||||||
|
playing_from: '*[class^="playingFrom"] span:nth-child(2)',
|
||||||
|
queue_album: "*[class^=playQueueItemsContainer] *[class^=groupTitle] span:nth-child(2)",
|
||||||
|
currentlyPlaying: "[class^='isPlayingIcon'], [data-test-is-playing='true']",
|
||||||
|
album_name_cell: '[class^="album"]',
|
||||||
|
tracklist_row: '[data-test="tracklist-row"]',
|
||||||
|
volume: '*[data-test="volume"]',
|
||||||
|
favorite: '*[data-test="footer-favorite-button"]',
|
||||||
|
/**
|
||||||
|
* Get an element from the dom
|
||||||
|
* @param {*} key key in elements object to fetch
|
||||||
|
*/
|
||||||
|
get: function (key: string) {
|
||||||
|
return globalThis.document.querySelector(this[key.toLowerCase()]);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the icon of the current media
|
||||||
|
*/
|
||||||
|
getSongIcon: function () {
|
||||||
|
const figure = this.get("media");
|
||||||
|
|
||||||
|
if (figure) {
|
||||||
|
const mediaElement = figure.querySelector(this["image"]);
|
||||||
|
if (mediaElement) {
|
||||||
|
return mediaElement.src.replace("80x80", "640x640");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns an array of all artists in the current media
|
||||||
|
* @returns {Array} artists
|
||||||
|
*/
|
||||||
|
getArtistsArray: function () {
|
||||||
|
const footer = this.get("footer");
|
||||||
|
|
||||||
|
if (footer) {
|
||||||
|
const artists = footer.querySelectorAll(this.artists);
|
||||||
|
if (artists)
|
||||||
|
return Array.from(artists).map((artist) => (artist as HTMLElement).textContent);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unify the artists array into a string separated by commas
|
||||||
|
* @param {Array} artistsArray
|
||||||
|
* @returns {String} artists
|
||||||
|
*/
|
||||||
|
getArtistsString: function (artistsArray: string[]) {
|
||||||
|
if (artistsArray.length > 0) return artistsArray.join(", ");
|
||||||
|
return "unknown artist(s)";
|
||||||
|
},
|
||||||
|
|
||||||
|
getAlbumName: function () {
|
||||||
|
//If listening to an album, get its name from the header title
|
||||||
|
if (globalThis.location.href.includes("/album/")) {
|
||||||
|
const albumName = globalThis.document.querySelector(this.album_header_title);
|
||||||
|
if (albumName) {
|
||||||
|
return albumName.textContent;
|
||||||
|
}
|
||||||
|
//If listening to a playlist or a mix, get album name from the list
|
||||||
|
} else if (
|
||||||
|
globalThis.location.href.includes("/playlist/") ||
|
||||||
|
globalThis.location.href.includes("/mix/")
|
||||||
|
) {
|
||||||
|
// TODO: fix
|
||||||
|
// if (currentPlayStatus === 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);
|
||||||
|
// if (row) {
|
||||||
|
// return row.querySelector(this.album_name_cell).textContent;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// see whether we're on the queue page and get it from there
|
||||||
|
const queueAlbumName = this.getText("queue_album");
|
||||||
|
if (queueAlbumName) {
|
||||||
|
return queueAlbumName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
},
|
||||||
|
|
||||||
|
isMuted: function () {
|
||||||
|
return this.get("volume").getAttribute("aria-checked") === "false"; // it's muted if aria-checked is false
|
||||||
|
},
|
||||||
|
|
||||||
|
isFavorite: function () {
|
||||||
|
return this.get("favorite").getAttribute("aria-checked") === "true";
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shorthand function to get the text of a dom element
|
||||||
|
* @param {*} key key in elements object to fetch
|
||||||
|
*/
|
||||||
|
getText: function (key: string) {
|
||||||
|
const element = this.get(key);
|
||||||
|
return element ? element.textContent : "";
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shorthand function to click a dom element
|
||||||
|
* @param {*} key key in elements object to fetch
|
||||||
|
*/
|
||||||
|
click: function (key: string) {
|
||||||
|
this.get(key).click();
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shorthand function to focus a dom element
|
||||||
|
* @param {*} key key in elements object to fetch
|
||||||
|
*/
|
||||||
|
focus: function (key: string) {
|
||||||
|
return this.get(key).focus();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
playPause = (): void => {
|
||||||
|
const play = this.elements.get("play");
|
||||||
|
|
||||||
|
if (play) {
|
||||||
|
this.elements.click("play");
|
||||||
|
} else {
|
||||||
|
this.elements.click("pause");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
goToHome(): void {
|
||||||
|
this.elements.click("home");
|
||||||
|
}
|
||||||
|
|
||||||
|
hookup = (): void => {
|
||||||
|
throw new Error("Method not implemented.");
|
||||||
|
};
|
||||||
|
}
|
13
src/TidalControllers/MediaController.ts
Normal file
13
src/TidalControllers/MediaController.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export interface TidalController {
|
||||||
|
/**
|
||||||
|
* Play or pause the current media
|
||||||
|
*/
|
||||||
|
playPause(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook up the controller to the current web instance
|
||||||
|
*/
|
||||||
|
hookup(): void;
|
||||||
|
|
||||||
|
goToHome(): void;
|
||||||
|
}
|
19
src/TidalControllers/MediaSessionTidalController.ts
Normal file
19
src/TidalControllers/MediaSessionTidalController.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { DomTidalController } from "./DomTidalController";
|
||||||
|
import { TidalController } from "./MediaController";
|
||||||
|
|
||||||
|
export class MediaSessionTidalController implements TidalController {
|
||||||
|
public domMediaController: TidalController;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.domMediaController = new DomTidalController();
|
||||||
|
}
|
||||||
|
goToHome(): void {
|
||||||
|
this.domMediaController.goToHome();
|
||||||
|
}
|
||||||
|
playPause(): void {
|
||||||
|
globalThis.alert("Method not implemented");
|
||||||
|
}
|
||||||
|
hookup(): void {
|
||||||
|
globalThis.alert("Method not implemented");
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,9 @@ import { addHotkey } from "./scripts/hotkeys";
|
|||||||
import { ObjectToDotNotation } from "./scripts/objectUtilities";
|
import { ObjectToDotNotation } from "./scripts/objectUtilities";
|
||||||
import { settingsStore } from "./scripts/settings";
|
import { settingsStore } from "./scripts/settings";
|
||||||
import { setTitle } from "./scripts/window-functions";
|
import { setTitle } from "./scripts/window-functions";
|
||||||
|
import { DomTidalController } from "./TidalControllers/DomTidalController";
|
||||||
|
import { MediaSessionTidalController } from "./TidalControllers/MediaSessionTidalController";
|
||||||
|
import { TidalController } from "./TidalControllers/MediaController";
|
||||||
|
|
||||||
const notificationPath = `${app.getPath("userData")}/notification.jpg`;
|
const notificationPath = `${app.getPath("userData")}/notification.jpg`;
|
||||||
let currentSong = "";
|
let currentSong = "";
|
||||||
@ -35,6 +38,16 @@ let currentShuffleState = false;
|
|||||||
let currentMediaInfo: MediaInfo;
|
let currentMediaInfo: MediaInfo;
|
||||||
let currentNotification: Electron.Notification;
|
let currentNotification: Electron.Notification;
|
||||||
|
|
||||||
|
let tidalController: TidalController;
|
||||||
|
|
||||||
|
// TODO: replace with setting
|
||||||
|
// eslint-disable-next-line no-constant-condition
|
||||||
|
if (true) {
|
||||||
|
tidalController = new DomTidalController();
|
||||||
|
} else {
|
||||||
|
tidalController = new MediaSessionTidalController();
|
||||||
|
}
|
||||||
|
|
||||||
const elements = {
|
const elements = {
|
||||||
play: '*[data-test="play"]',
|
play: '*[data-test="play"]',
|
||||||
pause: '*[data-test="pause"]',
|
pause: '*[data-test="pause"]',
|
||||||
@ -195,19 +208,6 @@ function getUpdateFrequency() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Play or pause the current media
|
|
||||||
*/
|
|
||||||
function playPause() {
|
|
||||||
const play = elements.get("play");
|
|
||||||
|
|
||||||
if (play) {
|
|
||||||
elements.click("play");
|
|
||||||
} else {
|
|
||||||
elements.click("pause");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the old listenbrainz data on launch
|
* Clears the old listenbrainz data on launch
|
||||||
*/
|
*/
|
||||||
@ -235,7 +235,7 @@ function addHotKeys() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
addHotkey("Control+h", function () {
|
addHotkey("Control+h", function () {
|
||||||
elements.click("home");
|
tidalController.goToHome();
|
||||||
});
|
});
|
||||||
|
|
||||||
addHotkey("backspace", function () {
|
addHotkey("backspace", function () {
|
||||||
@ -319,7 +319,7 @@ function addIPCEventListeners() {
|
|||||||
case globalEvents.playPause:
|
case globalEvents.playPause:
|
||||||
case globalEvents.play:
|
case globalEvents.play:
|
||||||
case globalEvents.pause:
|
case globalEvents.pause:
|
||||||
playPause();
|
tidalController.playPause();
|
||||||
break;
|
break;
|
||||||
case globalEvents.next:
|
case globalEvents.next:
|
||||||
elements.click("next");
|
elements.click("next");
|
||||||
@ -455,7 +455,7 @@ function addMPRIS() {
|
|||||||
const eventValue = events[eventName];
|
const eventValue = events[eventName];
|
||||||
switch (events[eventValue]) {
|
switch (events[eventValue]) {
|
||||||
case events.playpause:
|
case events.playpause:
|
||||||
playPause();
|
tidalController.playPause();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
elements.click(eventValue);
|
elements.click(eventValue);
|
||||||
|
Loading…
Reference in New Issue
Block a user