fix: user uploaded themes are now stored in the config directory. Missing directories will be created and added docs for theming

This commit is contained in:
Rick van Lieshout 2023-05-14 23:48:13 +02:00
parent 822bdf401e
commit 90cf231c76
14 changed files with 185 additions and 73 deletions

109
README.md
View File

@ -1,16 +1,22 @@
# Tidal-hifi<img src = "./build/icon.png" height="40" align="right"/> # Tidal-hifi<img src = "./build/icon.png" height="40" align="right"/>
![GitHub release](https://img.shields.io/github/release/Mastermindzh/tidal-hifi.svg) ![GitHub release](https://img.shields.io/github/release/Mastermindzh/tidal-hifi.svg) [![Discord logo](./docs/images/discord.png)](https://discord.gg/yhNwf4v4He)
The web version of [listen.tidal.com](https://listen.tidal.com) running in electron with hifi support thanks to widevine. The web version of [listen.tidal.com](https://listen.tidal.com) running in electron with hifi support thanks to widevine.
![tidal-hifi preview](./docs/preview.png) ![tidal-hifi preview](./docs/images/preview.png)
## Table of Contents ## Table of Contents
<!-- toc --> <!-- toc -->
- [Installation](#installation) - [Tidal-hifi](#tidal-hifi)
- [Table of Contents](#table-of-contents)
- [Features](#features)
- [Contributions](#contributions)
- [Why did I create tidal-hifi?](#why-did-i-create-tidal-hifi)
- [Why not extend existing projects?](#why-not-extend-existing-projects)
- [Installation](#installation)
- [Dependencies](#dependencies) - [Dependencies](#dependencies)
- [Using releases](#using-releases) - [Using releases](#using-releases)
- [Snap](#snap) - [Snap](#snap)
@ -18,25 +24,57 @@ The web version of [listen.tidal.com](https://listen.tidal.com) running in elect
- [Flatpak](#flatpak) - [Flatpak](#flatpak)
- [Nix](#nix) - [Nix](#nix)
- [Using source](#using-source) - [Using source](#using-source)
- [Features](#features) - [Integrations](#integrations)
- [Integrations](#integrations)
- [Known bugs](#known-bugs) - [Known bugs](#known-bugs)
- [last.fm doesn't work out of the box. Use rescrobbler as a workaround](#lastfm-doesnt-work-out-of-the-box-use-rescrobbler-as-a-workaround) - [last.fm doesn't work out of the box. Use rescrobbler as a workaround](#lastfm-doesnt-work-out-of-the-box-use-rescrobbler-as-a-workaround)
- [Why](#why) - [Special thanks to](#special-thanks-to)
- [Why not extend existing projects?](#why-not-extend-existing-projects) - [Donations](#donations)
- [Special thanks to](#special-thanks-to) - [Images](#images)
- [Buy me a coffee? Please don't](#buy-me-a-coffee-please-dont)
- [Images](#images)
- [Settings window](#settings-window) - [Settings window](#settings-window)
- [User setups](#user-setups) - [User setups](#user-setups)
<!-- tocstop --> <!-- tocstop -->
## Features
- HiFi playback
- Notifications
- Custom [theming](./docs/theming.md)
- Custom hotkeys ([source](https://defkey.com/tidal-desktop-shortcuts))
- API for status and playback
- Disabled audio & visual ads, unlocked lyrics, suggested track, track info, and unlimited skips thanks to uBlockOrigin custom filters ([source](https://github.com/uBlockOrigin/uAssets/issues/17495))
- Custom [integrations](#integrations)
- [Settings feature](./docs/images/settings.png) to disable certain functionality. (`ctrl+=` or `ctrl+0`)
- AlbumArt in integrations ([best-effort](https://github.com/Mastermindzh/tidal-hifi/pull/88#pullrequestreview-840814847))
## Contributions
To contribute you can use the standard GitHub features (issues, prs, etc) or join the discord server to talk with like-minded individuals.
- ![Discord logo](./docs/images/discord.png) [Join the Discord server](https://discord.gg/yhNwf4v4He)
## Why did I create tidal-hifi?
I moved from Spotify over to Tidal and found Linux support to be lacking.
When I started this project there weren't any Linux apps that offered Tidal's "hifi" options nor any scripts to control it.
### Why not extend existing projects?
Whilst there are a handful of projects attempting to run Tidal on Electron they are all unappealing to me because of various reasons:
- Lack of maintainers/developers. (no hotfixes, no issues being handled etc)
- Most are simple web wrappers, not my cup of tea.
- Some are DE oriented. I want this to work on WM's too.
- None have widevine working at the moment
Sometimes it's just easier to start over, cover my own needs and then making it available to the public :)
## Installation ## Installation
### Dependencies ### Dependencies
Note that you **need** a notification library such as [libnotify](https://github.com/GNOME/libnotify) or [dunst](https://github.com/dunst-project/dunst) in order for the software to work properly. Note that you **need** a notification library such as [libnotify](https://github.com/GNOME/libnotify) or [dunst](https://github.com/dunst-project/dunst) for the software to work properly.
### Using releases ### Using releases
@ -48,15 +86,15 @@ To install with `snap` you need to download the pre-packaged snap-package from t
1. Download 1. Download
```sh ```sh
wget <URI> #for instance: https://github.com/Mastermindzh/tidal-hifi/releases/download/1.0/tidal-hifi_1.0.0_amd64.snap wget <URI> #for instance: https://github.com/Mastermindzh/tidal-hifi/releases/download/1.0/tidal-hifi_1.0.0_amd64.snap
``` ```
2. Install 2. Install
```sh ```sh
snap install --dangerous <path> #for instance: tidal-hifi_1.0.0_amd64.snap snap install --dangerous <path> #for instance: tidal-hifi_1.0.0_amd64.snap
``` ```
### Arch Linux ### Arch Linux
@ -91,23 +129,12 @@ To install and work with the code on this project follow these steps:
- npm install - npm install
- npm start - npm start
## Features
- HiFi playback
- Notifications
- Custom hotkeys ([source](https://defkey.com/tidal-desktop-shortcuts))
- API for status and playback
- Disabled audio & visual ads, unlocked lyrics, suggested track, track info, and unlimited skips thanks to uBlockOrigin custom filters ([source](https://github.com/uBlockOrigin/uAssets/issues/17495))
- Custom [integrations](#integrations)
- [Settings feature](./docs/settings.png) to disable certain functionality. (`ctrl+=` or `ctrl+0`)
- AlbumArt in integrations ([best-effort](https://github.com/Mastermindzh/tidal-hifi/pull/88#pullrequestreview-840814847))
## Integrations ## Integrations
Tidal-hifi comes with several integrations out of the box. Tidal-hifi comes with several integrations out of the box.
You can find these in the settings menu (`ctrl + =` by default) under the "integrations" tab. You can find these in the settings menu (`ctrl + =` by default) under the "integrations" tab.
![integrations menu, showing a list of integrations](./docs/integrations.png) ![integrations menu, showing a list of integrations](./docs/images/integrations.png)
It currently includes: It currently includes:
@ -126,38 +153,20 @@ The last.fm login doesn't work, as is evident from the following issue: [Last.fm
However, in that same issue you can read about a workaround using [rescrobbler](https://github.com/InputUsername/rescrobbled). However, in that same issue you can read about a workaround using [rescrobbler](https://github.com/InputUsername/rescrobbled).
For now that will be the default workaround. For now that will be the default workaround.
## Why
I moved from Spotify over to Tidal and found Linux support to be lacking.
When I started this project there weren't any Linux apps that offered Tidal's "hifi" options nor any scripts to control it.
## Why not extend existing projects?
Whilst there are a handful of projects attempting to run Tidal on Electron they are all unappealing to me because of various reasons:
- Lack of a maintainers/developers. (no hotfixes, no issues being handled etc)
- Most are simple web wrappers, not my cup of tea.
- Some are DE oriented. I want this to work on WM's too.
- None have widevine working at the moment
Sometimes it's just easier to start over, cover my own needs and then making it available to the public :)
## Special thanks to ## Special thanks to
- [Castlabs](https://castlabs.com/) - [Castlabs](https://castlabs.com/)
For maintaining Electron with Widevine CDM installation, Verified Media Path (VMP), and persistent licenses (StorageID) For maintaining Electron with Widevine CDM installation, Verified Media Path (VMP), and persistent licenses (StorageID)
## Buy me a coffee? Please don't ## Donations
Instead spend some money on a charity I care for: [kwf.nl](https://www.kwf.nl/donatie/donation). You can find my Github sponsorship page at: [https://github.com/sponsors/Mastermindzh](https://github.com/sponsors/Mastermindzh)
Inspired by [haydenjames' issue](https://github.com/Mastermindzh/tidal-hifi/issues/27#issuecomment-704198429)
## Images ## Images
### Settings window ### Settings window
![settings window](./docs/settings-preview.png) ![settings window](./docs/images/settings-preview.png)
### User setups ### User setups

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
docs/images/customcss.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
docs/images/discord.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

Before

Width:  |  Height:  |  Size: 800 KiB

After

Width:  |  Height:  |  Size: 800 KiB

View File

Before

Width:  |  Height:  |  Size: 726 KiB

After

Width:  |  Height:  |  Size: 726 KiB

View File

Before

Width:  |  Height:  |  Size: 317 KiB

After

Width:  |  Height:  |  Size: 317 KiB

BIN
docs/images/theming.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

38
docs/theming.md Normal file
View File

@ -0,0 +1,38 @@
# Theming tidal-hifi
## Table of contents
<!-- toc -->
- [Theming tidal-hifi](#theming-tidal-hifi)
- [Table of contents](#table-of-contents)
- [Custom CSS](#custom-css)
- [config](#config)
- [Warning! Themes might break](#warning-themes-might-break)
<!-- tocstop -->
By default tidal-hifi comes with a few themes.
You can select these in the settings window under the theming tab as shown below.
![Settings window with the theming tab opened](./images/theming.png)
## Custom CSS
The custom CSS will be added to the HTML document last.
This means that it will overwrite any existing CSS, even that of themes, unless the original has an access modifier such as `$important`.
![settings window on the theming tab with a custom CSS override](./images/customcss.png)
## config
The theme selector and customCSS are stored in the config file.
The custom CSS is stored as a list of lines.
![settings window on the theming tab next to the config file](./images/customcss-config.png)
## Warning! Themes might break
Themes might break at any point. Tidal changes their webpage structure a ton (they probably generate classNames and don't provide roles/ids/attributes.)
If one breaks you can create an Issue on GitHub or ask for assistance in the [Discord channel](https://discord.gg/yhNwf4v4He).

View File

@ -1,9 +1,10 @@
import remote from "@electron/remote"; import remote, { app } from "@electron/remote";
import { ipcRenderer, shell } from "electron"; import { ipcRenderer, shell } from "electron";
import fs from "fs"; import fs from "fs";
import { globalEvents } from "../../constants/globalEvents"; import { globalEvents } from "../../constants/globalEvents";
import { settings } from "../../constants/settings"; import { settings } from "../../constants/settings";
import { settingsStore } from "./../../scripts/settings"; import { settingsStore } from "./../../scripts/settings";
import { getOptions, getOptionsHeader, getThemeListFromDirectory } from "./theming";
let adBlock: HTMLInputElement, let adBlock: HTMLInputElement,
api: HTMLInputElement, api: HTMLInputElement,
@ -25,23 +26,25 @@ let adBlock: HTMLInputElement,
theme: HTMLSelectElement, theme: HTMLSelectElement,
trayIcon: HTMLInputElement, trayIcon: HTMLInputElement,
updateFrequency: HTMLInputElement; updateFrequency: HTMLInputElement;
function getThemeFiles() { function getThemeFiles() {
const selectElement = document.getElementById("themesList") as HTMLSelectElement; const selectElement = document.getElementById("themesList") as HTMLSelectElement;
const fileNames = fs const builtInThemes = getThemeListFromDirectory(process.resourcesPath);
.readdirSync(process.resourcesPath) const userThemes = getThemeListFromDirectory(`${app.getPath("userData")}/themes`);
.filter((file) => file.endsWith(".css"))
.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
const options = fileNames.map((name) => { let allThemes = [
return new Option(name.replace(".css", ""), name); getOptionsHeader("Built-in Themes"),
}); new Option("Tidal - Default", "none"),
].concat(getOptions(builtInThemes));
if (userThemes.length >= 1) {
allThemes = allThemes.concat([getOptionsHeader("User Themes")]).concat(getOptions(userThemes));
}
// empty old options // empty old options
const oldOptions = document.querySelectorAll("#themesList option"); const oldOptions = document.querySelectorAll("#themesList option");
oldOptions.forEach((o) => o.remove()); oldOptions.forEach((o) => o.remove());
[new Option("Tidal - Default", "none")].concat(options).forEach((option) => { allThemes.forEach((option) => {
selectElement.add(option, null); selectElement.add(option, null);
}); });
} }
@ -52,7 +55,7 @@ function handleFileUploads() {
document.getElementById("theme-files").addEventListener("change", function (e: any) { document.getElementById("theme-files").addEventListener("change", function (e: any) {
Array.from(e.target.files).forEach((file: File) => { Array.from(e.target.files).forEach((file: File) => {
const destination = `${process.resourcesPath}/${file.name}`; const destination = `${app.getPath("userData")}/themes/${file.name}`;
fs.copyFileSync(file.path, destination, null); fs.copyFileSync(file.path, destination, null);
}); });
fileMessage.innerText = `${e.target.files.length} files successfully uploaded`; fileMessage.innerText = `${e.target.files.length} files successfully uploaded`;

View File

@ -434,5 +434,12 @@ html {
option { option {
background-color: $tidal-grey-darkest; background-color: $tidal-grey-darkest;
&:disabled {
font-size: 1.2em;
line-height: 1.5em;
text-align: center;
color: $white;
}
} }
} }

View File

@ -0,0 +1,55 @@
import fs from "fs";
const cssFilter = (file: string) => file.endsWith(".css");
const sort = (a: string, b: string) => a.toLowerCase().localeCompare(b.toLowerCase());
/**
* Create an "options header" (disabled option) based on a bit of text
* @param text of the header
* @returns
*/
export const getOptionsHeader = (text: string): HTMLOptionElement => {
const opt = new Option(text, undefined, false, false);
opt.disabled = true;
return opt;
};
/**
* Maps a list of filenames to a list of HTMLOptionElements
* Will strip ".css" from the name but keeps it in the value
* @param array array of filenames
* @returns
*/
export const getOptions = (array: string[]) => {
return array.map((name) => {
return new Option(name.replace(".css", ""), name);
});
};
/**
* Read .css files from a directory and return them in a sorted array.
* @param directory to read from. Will be created if it doesn't exist
* @returns
*/
export const getThemeListFromDirectory = (directory: string) => {
try {
makeUserThemesDirectory(directory);
return fs.readdirSync(directory).filter(cssFilter).sort(sort);
} catch (err) {
console.error(err);
}
};
/**
* Create the directory to store user themes in
* @param directory directory to create
*/
export const makeUserThemesDirectory = (directory: string) => {
try {
fs.mkdir(directory, { recursive: true }, (err) => {
if (err) throw err;
});
} catch (err) {
console.error(err);
}
};

View File

@ -55,7 +55,7 @@ export const createSettingsWindow = function () {
settingsWindow = new BrowserWindow({ settingsWindow = new BrowserWindow({
width: 700, width: 700,
height: 600, height: 600,
resizable: false, resizable: true,
show: false, show: false,
transparent: true, transparent: true,
frame: false, frame: false,