diff --git a/README.md b/README.md
index f187f6a..00d4ec0 100644
--- a/README.md
+++ b/README.md
@@ -1,42 +1,80 @@
# Tidal-hifi
-![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.
-![tidal-hifi preview](./docs/preview.png)
+![tidal-hifi preview](./docs/images/preview.png)
## Table of Contents
-- [Installation](#installation)
- - [Dependencies](#dependencies)
- - [Using releases](#using-releases)
- - [Snap](#snap)
- - [Arch Linux](#arch-linux)
- - [Flatpak](#flatpak)
- - [Nix](#nix)
- - [Using source](#using-source)
-- [Features](#features)
-- [Integrations](#integrations)
- - [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)
-- [Why](#why)
-- [Why not extend existing projects?](#why-not-extend-existing-projects)
-- [Special thanks to](#special-thanks-to)
-- [Buy me a coffee? Please don't](#buy-me-a-coffee-please-dont)
-- [Images](#images)
- - [Settings window](#settings-window)
- - [User setups](#user-setups)
+- [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)
+ - [Using releases](#using-releases)
+ - [Snap](#snap)
+ - [Arch Linux](#arch-linux)
+ - [Flatpak](#flatpak)
+ - [Nix](#nix)
+ - [Using source](#using-source)
+ - [Integrations](#integrations)
+ - [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)
+ - [Special thanks to](#special-thanks-to)
+ - [Donations](#donations)
+ - [Images](#images)
+ - [Settings window](#settings-window)
+ - [User setups](#user-setups)
+## 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
### 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
@@ -48,15 +86,15 @@ To install with `snap` you need to download the pre-packaged snap-package from t
1. Download
-```sh
-wget #for instance: https://github.com/Mastermindzh/tidal-hifi/releases/download/1.0/tidal-hifi_1.0.0_amd64.snap
-```
+ ```sh
+ wget #for instance: https://github.com/Mastermindzh/tidal-hifi/releases/download/1.0/tidal-hifi_1.0.0_amd64.snap
+ ```
2. Install
-```sh
-snap install --dangerous #for instance: tidal-hifi_1.0.0_amd64.snap
-```
+ ```sh
+ snap install --dangerous #for instance: tidal-hifi_1.0.0_amd64.snap
+ ```
### Arch Linux
@@ -91,23 +129,12 @@ To install and work with the code on this project follow these steps:
- npm install
- 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
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.
-![integrations menu, showing a list of integrations](./docs/integrations.png)
+![integrations menu, showing a list of integrations](./docs/images/integrations.png)
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).
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
- [Castlabs](https://castlabs.com/)
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).
-Inspired by [haydenjames' issue](https://github.com/Mastermindzh/tidal-hifi/issues/27#issuecomment-704198429)
+You can find my Github sponsorship page at: [https://github.com/sponsors/Mastermindzh](https://github.com/sponsors/Mastermindzh)
## Images
### Settings window
-![settings window](./docs/settings-preview.png)
+![settings window](./docs/images/settings-preview.png)
### User setups
diff --git a/docs/images/customcss-config.png b/docs/images/customcss-config.png
new file mode 100644
index 0000000..4104a2a
Binary files /dev/null and b/docs/images/customcss-config.png differ
diff --git a/docs/images/customcss.png b/docs/images/customcss.png
new file mode 100644
index 0000000..8cff474
Binary files /dev/null and b/docs/images/customcss.png differ
diff --git a/docs/images/discord.png b/docs/images/discord.png
new file mode 100644
index 0000000..c4ee7fa
Binary files /dev/null and b/docs/images/discord.png differ
diff --git a/docs/integrations.png b/docs/images/integrations.png
similarity index 100%
rename from docs/integrations.png
rename to docs/images/integrations.png
diff --git a/docs/preview.png b/docs/images/preview.png
similarity index 100%
rename from docs/preview.png
rename to docs/images/preview.png
diff --git a/docs/settings-preview.png b/docs/images/settings-preview.png
similarity index 100%
rename from docs/settings-preview.png
rename to docs/images/settings-preview.png
diff --git a/docs/settings.png b/docs/images/settings.png
similarity index 100%
rename from docs/settings.png
rename to docs/images/settings.png
diff --git a/docs/images/theming.png b/docs/images/theming.png
new file mode 100644
index 0000000..5792a55
Binary files /dev/null and b/docs/images/theming.png differ
diff --git a/docs/theming.md b/docs/theming.md
new file mode 100644
index 0000000..2599ca4
--- /dev/null
+++ b/docs/theming.md
@@ -0,0 +1,38 @@
+# Theming tidal-hifi
+
+## Table of contents
+
+
+
+- [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)
+
+
+
+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).
diff --git a/src/pages/settings/preload.ts b/src/pages/settings/preload.ts
index d6f51f6..965ccf8 100644
--- a/src/pages/settings/preload.ts
+++ b/src/pages/settings/preload.ts
@@ -1,9 +1,10 @@
-import remote from "@electron/remote";
+import remote, { app } from "@electron/remote";
import { ipcRenderer, shell } from "electron";
import fs from "fs";
import { globalEvents } from "../../constants/globalEvents";
import { settings } from "../../constants/settings";
import { settingsStore } from "./../../scripts/settings";
+import { getOptions, getOptionsHeader, getThemeListFromDirectory } from "./theming";
let adBlock: HTMLInputElement,
api: HTMLInputElement,
@@ -25,23 +26,25 @@ let adBlock: HTMLInputElement,
theme: HTMLSelectElement,
trayIcon: HTMLInputElement,
updateFrequency: HTMLInputElement;
-
function getThemeFiles() {
const selectElement = document.getElementById("themesList") as HTMLSelectElement;
- const fileNames = fs
- .readdirSync(process.resourcesPath)
- .filter((file) => file.endsWith(".css"))
- .sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
+ const builtInThemes = getThemeListFromDirectory(process.resourcesPath);
+ const userThemes = getThemeListFromDirectory(`${app.getPath("userData")}/themes`);
- const options = fileNames.map((name) => {
- return new Option(name.replace(".css", ""), name);
- });
+ let allThemes = [
+ 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
const oldOptions = document.querySelectorAll("#themesList option");
oldOptions.forEach((o) => o.remove());
- [new Option("Tidal - Default", "none")].concat(options).forEach((option) => {
+ allThemes.forEach((option) => {
selectElement.add(option, null);
});
}
@@ -52,7 +55,7 @@ function handleFileUploads() {
document.getElementById("theme-files").addEventListener("change", function (e: any) {
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);
});
fileMessage.innerText = `${e.target.files.length} files successfully uploaded`;
diff --git a/src/pages/settings/settings.scss b/src/pages/settings/settings.scss
index 75c99b1..8e7318a 100644
--- a/src/pages/settings/settings.scss
+++ b/src/pages/settings/settings.scss
@@ -434,5 +434,12 @@ html {
option {
background-color: $tidal-grey-darkest;
+
+ &:disabled {
+ font-size: 1.2em;
+ line-height: 1.5em;
+ text-align: center;
+ color: $white;
+ }
}
}
diff --git a/src/pages/settings/theming.ts b/src/pages/settings/theming.ts
new file mode 100644
index 0000000..1eb10e2
--- /dev/null
+++ b/src/pages/settings/theming.ts
@@ -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);
+ }
+};
diff --git a/src/scripts/settings.ts b/src/scripts/settings.ts
index 9fa27b4..2d4ed7b 100644
--- a/src/scripts/settings.ts
+++ b/src/scripts/settings.ts
@@ -55,7 +55,7 @@ export const createSettingsWindow = function () {
settingsWindow = new BrowserWindow({
width: 700,
height: 600,
- resizable: false,
+ resizable: true,
show: false,
transparent: true,
frame: false,