From c6bac08eb62aabf53b590a3a8efa50b7709fb0fd Mon Sep 17 00:00:00 2001 From: Ian McFarlane <70479099+the-argus@users.noreply.github.com> Date: Tue, 9 Aug 2022 05:27:59 +0000 Subject: [PATCH] Version 1.0 (#1) Restructured the module. It now only evaluated the selected theme. Themes, extensions, and apps all have types now, which can be used to define custom ones. A majority of themes and extensions have been packaged for easy installation. Additionally, you can define custom color schemes for your theme, as shown in the README's "maximum configuration." --- CUSTOMAPPS.md | 17 ++ EXTENSIONS.md | 126 +++++++++ README.md | 163 +++++++++--- THEMES.md | 73 +++++ flake.nix | 7 + lib/default.nix | 65 +++++ lib/types.nix | 171 ++++++++++++ module.nix | 433 +++++++++++++++--------------- pkgs/default.nix | 672 +++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 1481 insertions(+), 246 deletions(-) create mode 100644 CUSTOMAPPS.md create mode 100644 EXTENSIONS.md create mode 100644 THEMES.md create mode 100644 lib/default.nix create mode 100644 lib/types.nix create mode 100644 pkgs/default.nix diff --git a/CUSTOMAPPS.md b/CUSTOMAPPS.md new file mode 100644 index 00000000..bc2bb422 --- /dev/null +++ b/CUSTOMAPPS.md @@ -0,0 +1,17 @@ +# Custom Apps + +## Official Apps + +### new-releases +Add a page showing new releases from artists you follow. + +### reddit +Add a page showing popular music on certain music subreddits. + +### lyrics-plus +Add a page with pretty scrolling lyrics. + +## Community Apps + +### localFiles +Add a shortcut to see just your local files. diff --git a/EXTENSIONS.md b/EXTENSIONS.md new file mode 100644 index 00000000..43d78db6 --- /dev/null +++ b/EXTENSIONS.md @@ -0,0 +1,126 @@ +# Extensions +Not all of these work properly. See the warning in the README. + +## Official Extensions + +### fullAppDisplay.js +Shows the song cover, title, and artist in fullscreen. + +### autoSkipExplicit.js +Christian spotify! + +### autoSkipVideo.js +Video playback (ads and stuff) causes problems in some regions where the videos can't be played. Just skip them with this extension. + +### bookmark.js +Store and browse pages for looking at later. + +### keyboardShortcut.js +Vimium-like navigation of spotify. See keyboard shortcuts [here.](https://spicetify.app/docs/advanced-usage/extensions#keyboard-shortcut) + +### loopyLoop.js +Specify a portion of track to loop over. + +### popupLyrics.js +Open a popup window with the current song's lyrics scrolling across it. + +### shuffle+.js +Shuffle properly, using Fisher-Yates with zero bias. + +### trashbin.js +Throw artists or songs in the trash, and permanently skip them. + +### webnowplaying.js +Only useful to windows/rainmeter users, I think. [Reference](https://spicetify.app/docs/advanced-usage/extensions#web-now-playing) + +## Community Extensions + +### groupSession.js +Allows you to create a link to share with your friends to listen along with you. + +### powerBar.js +Spotlight-like search bar for spotify. + +### seekSong.js +Allows for youtube-like seeking with keyboard shortcuts. + +### skipOrPlayLikedSongs.js +Set spotify to automatically skip liked songs, or to only play liked songs. + +### playlistIcons.js +Give your playlists icons in the left sidebar. + +### fullAlbumDate.js +Display the day and month of an album's release, as well as the year. + +### fullAppDisplayMod.js +Same as fullAppDisplay.js, but with slight offset, and scrolling lyrics if using the lyrics-plus customapp. + +### goToSong.js +Adds an option to the profile menu to go to the currently playing song or playlist. + +### listPlaylistsWithSong.js +Adds an option to the context menu for songs to show all of your account's playlists which contain that song. + +### playlistIntersection.js +Compare two playlists, and create a new playlist containing their common songs, or only the songs unique to one playlist. + +### skipStats.js +Track your skips. + +### phraseToPlaylist.js +Given a phrase, this extension will make a playlist containing a series of songs which make up that phrase. + +### fixEnhance.js +A patch to the "enhance playlist" option which spotify themselves has already fixed, but could be useful in older versions. + +### wikify.js +Show an artist's wikipedia entry. + +### featureShuffle.js +Create a playlist based off another playlist's audio features. + +### songStats.js +Show a song's stats, like dancability, tempo, and key. + +### autoVolume.js +Automatically adjust volume over long periods of time, to reduce ear strain. + +### showQueueDuration.js +Show the total length of all songs currently queued. + +### copyToClipboard.js +Adds an option in the context menu to copy a song's name to your clipboard. + +### volumeProfiles.js +Edit and save settings related to volume to different "profiles." + +### history.js +Adds a page that shows your listening history. + +### lastfm.js +Integration with last.fm. Login to show your listening stats for a song, and get its last.fm link. + +### genre.js +Display the genre of an artist or song while playing. + +### hidePodcasts.js +Remove everything from the spotify UI relating to podcasts. + +### adblock.js +Remove ads. + +### savePlaylists.js +More than just just following a playlist, this extension allows you to also create a duplicate of the playlist in your own library. + +### autoSkip.js +Automatically skip certain songs by category, such as remixes, or christmas songs. + +### fullScreen.js +Similar to fullAppDisplay.js. + +### playNext.js +Add track to the *top* of the queue. + +### volumePercentage.js +Adds a percentage number next to the volume adjustment slider, and allows for more fine control of volume. diff --git a/README.md b/README.md index fa034508..8ab82cba 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,168 @@ -# Spicetify-Nix +# Warning: Spotify Sucks +Linux is a second class citizen on Spotify's release plan, but because it isn't FOSS, there's nothing we can do. Right now a lot of themes and extensions don't work because spotify isn't up to date on Linux. This includes Dribbblish :( -# Warning: Spotify sucks -Spotify isn't FOSS and linux is a second class citizen on its release plan. -Right now a lot of themes and extensions don't work because spotify isn't up -to date on Linux. This includes Dribbblish :( +# Spicetify-Nix Modifies Spotify using [spicetify-cli](https://github.com/khanhas/spicetify-cli). [spicetify-themes](https://github.com/morpheusthewhite/spicetify-themes) are included and available. +# Usage + To use, add this flake to your home-manager configuration flake inputs, like so: ```nix { - inputs.spicetify-nix = { - url = "github:the-argus/spicetify-nix"; - }; + inputs.spicetify-nix.url = "github:the-argus/spicetify-nix"; } ``` +## Configuration examples -An example of a file which configures spicetify when imported into -a users home-manager configuration: +Here are two examples of files which configures spicetify when imported into a users home-manager configuration. + +### Minimal Configuration ```nix -{ pkgs, spicetify-nix, ... }: +{ pkgs, unstable, lib, spicetify-nix, ... }: { + # allow spotify to be installed if you don't have unfree enabled already + nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ + "spotify-unwrapped" + ]; + # import the flake's module imports = [ spicetify-nix.homeManagerModule ]; # configure spicetify :) programs.spicetify = - let - hidePodcasts = pkgs.fetchgit { - url = "https://github.com/theRealPadster/spicetify-hide-podcasts"; - rev = "cfda4ce0c3397b0ec38a971af4ff06daba71964d"; - sha256 = "146bz9v94dk699bshbc21yq4y5yc38lq2kkv7w3sjk4x510i0v3q"; - }; - in { enable = true; - theme = "Dribbblish"; - colorScheme = "rosepine"; - enabledCustomApps = [ ]; + # recommended to use newest version of spicetify unless you know what you're doing + spicetifyPackage = unstable.spicetify-cli; + theme = "catppuccin-mocha"; + # OR + # theme = spicetify-nix.pkgs.themes.catppuccin-mocha; + colorScheme = "flamingo"; + enabledExtensions = [ "fullAppDisplay.js" "shuffle+.js" "hidePodcasts.js" ]; - # - # thirdPartyCustomApps = { - # localFiles = "${localFiles}"; - # }; - # - thirdPartyExtensions = { - hidePodcasts = "${hidePodcasts}/hidePodcasts.js"; + }; +} +``` + +### MAXIMUM CONFIGURATION +WARNING: Do not copy + paste this configuration. The "enabledCustomApps" causes spotify to give the "something went wrong" page eternally. This configuration is only meant as an example. +```nix +{ pkgs, unstable, lib, spicetify-nix, ... }: +{ + # allow spotify to be installed if you don't have unfree enabled already + nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ + "spotify-unwrapped" + ]; + + # import the flake's module + imports = [ spicetify-nix.homeManagerModule ]; + + # configure spicetify :) + programs.spicetify = + let + # use a different version of spicetify-themes than the one provided by + # spicetify-nix + officialThemesOLD = pkgs.fetchgit { + url = "https://github.com/spicetify/spicetify-themes"; + rev = "c2751b48ff9693867193fe65695a585e3c2e2133"; + sha256 = "0rbqaxvyfz2vvv3iqik5rpsa3aics5a7232167rmyvv54m475agk"; + }; + # pin a certain version of the localFiles custom app + localFilesSrc = pkgs.fetchgit { + url = "https://github.com/hroland/spicetify-show-local-files/"; + rev = "1bfd2fc80385b21ed6dd207b00a371065e53042e"; + sha256 = "01gy16b69glqcalz1wm8kr5wsh94i419qx4nfmsavm4rcvcr3qlx"; + }; + in + { + # use spotify from the nixpkgs master branch + spotifyPackage = unstable.spotify-unwrapped; + # use a custom build of spicetify, also an old version. + spicetifyPackage = import ../../packages/spicetify-cli-2.9.9.nix { inherit pkgs; }; + + # actually enable the installation of spotify and spicetify + enable = true; + + # custom Dribbblish theme + theme = { + name = "Dribbblish"; + src = officialThemesOLD; + requiredExtensions = [ + # define extensions that will be installed with this theme + { + # extension is "${src}/Dribbblish/dribbblish.js" + filename = "dribbblish.js"; + src = "${officialThemesOLD}/Dribbblish"; + } + ]; + appendName = true; # theme is located at "${src}/Dribbblish" not just "${src}" + + # changes to make to config-xpui.ini for this theme: + patches = { + "xpui.js_find_8008" = ",(\\w+=)32,"; + "xpui.js_repl_8008" = ",$\{1}56,"; + }; + injectCss = true; + replaceColors = true; + overwriteAssets = true; + sidebarConfig = true; }; + + # specify that we want to use our custom colorscheme + colorScheme = "custom"; + + # color definition for custom color scheme. (rosepine) + customColorScheme = { + text = "ebbcba"; + subtext = "F0F0F0"; + sidebar-text = "e0def4"; + main = "191724"; + sidebar = "2a2837"; + player = "191724"; + card = "191724"; + shadow = "1f1d2e"; + selected-row = "797979"; + button = "31748f"; + button-active = "31748f"; + button-disabled = "555169"; + tab-active = "ebbcba"; + notification = "1db954"; + notification-error = "eb6f92"; + misc = "6e6a86"; + }; + + # BROKEN AT TIME OF WRITING + enabledCustomApps = with spicetify-nix.pkgs.apps; [ + new-releases + { + name = "localFiles"; + src = localFilesSrc; + appendName = false; + } + ]; + enabledExtensions = with spicetify-nix.pkgs.extensions; [ + "playlistIcons.js" + "lastfm.js" + "genre.js" + "historyShortcut.js" + "hidePodcasts.js" + "fullAppDisplay.js" + "shuffle+.js" + ]; }; } ``` -To add third-party themes, extensions or custom apps use `thirdPartyThemes`, `thirdParyExtensions` or `thirdParyCustomApps`. These expect a set, where the key is the name of the new theme/extension and the value the path. Don't forget to enable it separately. +## Themes, Extensions, and CustomApps -For all available options, check module.nix or package.nix and the spicetify repo. Everything is optional and will revert to the defaults from spicetify. +Are found in [THEMES.md](./THEMES.md), [EXTENSIONS.md](./EXTENSIONS.md), and [CUSTOMAPPS.md](./CUSTOMAPPS.md), respectively. ## macOS This package has no macOS support, because Spotify in nixpkgs has no macOS support. diff --git a/THEMES.md b/THEMES.md new file mode 100644 index 00000000..742b6cad --- /dev/null +++ b/THEMES.md @@ -0,0 +1,73 @@ +# Themes +These are pre-packaged and somewhat tested themes. Provided you don't use a broken version of spicetify, you should be able to at least *use* spotify with these themes installed. Some of them won't necessarily look good due to mismatched and out of date versions (see the warning in the README). + +## Official Themes +All of these themes can be found [here.](https://github.com/spicetify/spicetify-themes) +### Default +Identical to the default look of spotify, but with other colorschemes available. +![preview](https://github.com/spicetify/spicetify-themes/blob/master/Default/ocean.png) +### BurntSienna +Grey and orange theme using the montserrat typeface. +![preview](https://github.com/spicetify/spicetify-themes/blob/master/BurntSienna/screenshot.png) +### Dreary +Flat design, with lots of thick line borders. +![preview](https://github.com/spicetify/spicetify-themes/blob/master/Dreary/bib.png) +### Dribbblish +Modern, rounded, and minimal design, with fading effects and gradients. +![preview](https://github.com/spicetify/spicetify-themes/blob/master/Dribbblish/base.png) +### Flow +Monochromatic colorschemes with subtle differences between colors, and soft vertical gradients. +![preview](https://raw.githubusercontent.com/spicetify/spicetify-themes/master/Flow/screenshots/ocean.png) +### Glaze +Simple theme which gives spotify a simpler design and a single highlight color. +![preview](https://github.com/CharlieS1103/Glaze-theme/blob/main/screenshots/base.png) +### Onepunch +Gruvbox. +![preview](https://github.com/spicetify/spicetify-themes/blob/master/Onepunch/screenshots/dark_home.png) +### Sleek +Flat design, with dark blues for the background and single highlight colors in each scheme. +![preview](https://github.com/spicetify/spicetify-themes/blob/master/Sleek/bladerunner.png) +### Turntable +Default spotify, but provides a spinning image of a record when used with fullAppDisplay.js +![preview](https://github.com/spicetify/spicetify-themes/blob/master/Turntable/screenshots/fad.png) +### Ziro +Inspired by the Zorin GTK theme. +![preview](https://raw.githubusercontent.com/schnensch0/ziro/main/preview/album-blue-dark.png) + +## Community Themes +### Catppuccin* +A soothing pastel theme for spotify. Comes in four variations, each with their own color schemes: catppuccin-mocha, catppuccin-frappe, catppuccin-latte, and catppuccin-macchiato. [Source](https://github.com/catppuccin/spicetify) +![preview](https://github.com/catppuccin/spicetify/blob/main/assets/preview.png) +### Comfy +Stay comfy while listening to music. Rounded corners and dark blues. Comes in two additional variations: Comfy-Chromatic and Comfy-Mono. [Source](https://github.com/Comfy-Themes/Spicetify) +![preview](https://camo.githubusercontent.com/6c6a1ee2d5b2585b51e2b6f02ab6eba1a702e9912c46a775781ff4febb1b4720/68747470733a2f2f636f6d66792d7468656d65732e6769746875622e696f2f5370696365746966792f436f6d66792f6173736574732f707265766965772e706e67) +### Dracula +Default spotify with the colors of the popular scheme. [Source](https://github.com/Darkempire78/Dracula-Spicetify) +![preview](https://github.com/Darkempire78/Dracula-Spicetify/blob/master/screenshot.png) +### Nord +Default spotify with the colors of the popular scheme. [Source](https://github.com/Tetrax-10/Nord-Spotify) +![preview](https://raw.githubusercontent.com/Tetrax-10/Spicetify-Themes/master/assets/screenshot_1.png) +### SpotifyCanvas +A theme attempting to bring canvas, the video/clip player, to spotify desktop. It normally is only available on mobile. [Source](https://github.com/itsmeow/Spicetify-Canvas) +![preview](https://camo.githubusercontent.com/824738caeda9df907e11a83825e2dea7d7673e044b963c2d672d6efc5d190c38/68747470733a2f2f692e696d6775722e636f6d2f653575734164422e706e67) +### SpotifyNoPremium +Same as default spotify but without ads and anything related to getting premium. [Source](https://github.com/Daksh777/SpotifyNoPremium) +![preview](https://camo.githubusercontent.com/31d933c55d8aa67041b6e13e30e92442e38fcb9d4134061adc7853630df13a3e/68747470733a2f2f692e696d6775722e636f6d2f48566a5448544f2e706e67) +### Fluent +Inspired by the design of Windows 11. [Source](https://github.com/williamckha/spicetify-fluent) +![preview](https://github.com/williamckha/spicetify-fluent/blob/master/screenshots/dark-1.png) +### DefaultDynamic +Same as default spotify, but colors change dynamically with album art. [Source](https://github.com/JulienMaille/spicetify-dynamic-theme) +![preview](https://github.com/JulienMaille/spicetify-dynamic-theme/blob/main/Dark.gif) +### RetroBlur +Synthwave theme with a lot of blur and effects. [Source](https://github.com/Motschen/Retroblur) +![preview](https://github.com/Motschen/Retroblur/blob/main/preview/playlist.png) +### Omni +Dark theme created by Rocketseat. [Source](https://github.com/getomni/spicetify) +![preview](https://github.com/getomni/spicetify/blob/main/screenshot.png) +### Bloom +Another Spicetify theme inspired by Microsoft's Fluent Design System. [Source](https://github.com/nimsandu/spicetify-bloom) +![preview](https://raw.githubusercontent.com/nimsandu/spicetify-bloom/main/Dark-1.png) +### Orchis +Simple dark green/grey theme. [Source](https://github.com/canbeardig/Spicetify-Orchis-Colours-v2) +![preview](https://github.com/canbeardig/Spicetify-Orchis-Colours-v2/blob/main/screenshot.png) diff --git a/flake.nix b/flake.nix index c08f9749..0246901d 100644 --- a/flake.nix +++ b/flake.nix @@ -6,7 +6,14 @@ }; outputs = { self, nixpkgs, ... }@inputs: + let + pkgs = import nixpkgs { system = "x86_64-linux"; }; + in { homeManagerModule = import ./module.nix; + + lib = import ./lib { inherit pkgs; lib = pkgs.lib; }; + + pkgs = import ./pkgs { inherit pkgs; lib = pkgs.lib; }; }; } diff --git a/lib/default.nix b/lib/default.nix new file mode 100644 index 00000000..cf6fddd3 --- /dev/null +++ b/lib/default.nix @@ -0,0 +1,65 @@ +{ pkgs, lib, ... }: +let + customToINI = lib.generators.toINI { + # specifies how to format a key/value pair + mkKeyValue = lib.generators.mkKeyValueDefault + { + # specifies the generated string for a subset of nix values + mkValueString = v: + if v == true then "1" + else if v == false then "0" + # else if isString v then ''"${v}"'' + # and delegates all other values to the default generator + else lib.generators.mkValueStringDefault { } v; + } "="; + }; + + spicePkgs = import ../pkgs { inherit pkgs lib; }; +in +{ + types = import ./types.nix { inherit pkgs lib; }; + + createXpuiINI = xpui: (customToINI xpui); + + getThemePath = theme: + if (builtins.hasAttr "appendName" theme) then + (if theme.appendName then + "${theme.src}/${theme.name}" + else + theme.src) + else + theme.src; + + # same thing but if its a string it looks it up in the default pkgs + getTheme = theme: + if builtins.typeOf theme == "string" then + ( + if builtins.hasAttr theme spicePkgs.themes then + spicePkgs.themes.${theme} + else + throw "Unknown theme ${theme}. Try using the lib.theme type instead of a string." + ) + else theme; + + getExtension = ext: + if builtins.typeOf ext == "string" then + ( + if builtins.hasAttr ext spicePkgs.extensions then + spicePkgs.extensions.${ext} + else + throw "Unknown extension ${ext}. Try using the lib.extension type instead of a string." + ) + else + ext; + + getApp = app: + if builtins.typeOf app == "string" then + ( + if builtins.hasAttr app spicePkgs.apps then + spicePkgs.apps.${app} + else + throw "Unknown CustomApp ${app}. Try using the lib.app type instead of a string." + ) + else + app; +} diff --git a/lib/types.nix b/lib/types.nix new file mode 100644 index 00000000..14a23533 --- /dev/null +++ b/lib/types.nix @@ -0,0 +1,171 @@ +{ pkgs, lib, ... }: +let + inherit (lib) mkOption types; + + xpui = types.submodule { + options = + { + AdditionalOptions = mkOption { + type = (types.submodule { + options = { + home = mkOption { type = types.bool; default = false; }; + experimental_features = mkOption { type = types.bool; default = false; }; + extensions = mkOption { type = (types.listOf types.str); default = [ ]; }; + custom_apps = mkOption { type = (types.listOf types.str); default = [ ]; }; + sidebar_config = mkOption { type = types.bool; default = true; }; + }; + }); + default = { }; + }; + Patch = { }; + Setting = mkOption { + type = (types.submodule { + options = { + spotify_path = mkOption { type = types.str; default = "__REPLACEME__"; }; + prefs_path = mkOption { type = types.str; default = "__REPLACEME2__"; }; + current_theme = mkOption { type = types.str; default = ""; }; + color_scheme = mkOption { type = types.str; default = ""; }; + spotify_launch_flags = mkOption { type = types.str; default = ""; }; + check_spicetify_upgrade = mkOption { type = types.bool; default = false; }; + inject_css = mkOption { type = types.bool; default = false; }; + replace_colors = mkOption { type = types.bool; default = false; }; + overwrite_assets = mkOption { type = types.bool; default = false; }; + }; + }); + default = { }; + }; + Preprocesses = mkOption { + type = (types.submodule { + options = { + disable_upgrade_check = mkOption { type = types.bool; default = true; }; + disable_sentry = mkOption { type = types.bool; default = true; }; + disable_ui_logging = mkOption { type = types.bool; default = true; }; + remove_rtl_rule = mkOption { type = types.bool; default = true; }; + expose_apis = mkOption { type = types.bool; default = true; }; + }; + }); + default = { }; + }; + Backup = mkOption { + type = (types.submodule { + options = { + version = mkOption { type = types.str; default = ""; }; + "with" = mkOption { type = types.str; default = "Dev"; }; + }; + }); + default = { }; + }; + }; + }; + + extension = types.submodule { + options = { + src = mkOption { + type = types.oneOf [ types.path types.str ]; + description = "Path to the folder which contains the .js file."; + }; + filename = mkOption { + type = types.str; + description = "Name of the .js file to enable."; + example = "dribbblish.js"; + }; + experimentalFeatures = mkOption { + type = types.bool; + description = "Value to set AdditionalOptions/experimental_features to."; + default = false; + }; + }; + }; + + theme = types.submodule { + options = { + name = mkOption { + type = types.str; + description = "The name of the theme as it will be copied into the spicetify themes directory."; + example = ''Dribbblish''; + }; + src = mkOption { + type = types.oneOf [ types.path types.str ]; + description = "Path to folder containing the theme."; + example = ''pkgs.fetchgit { + url = "https://github.com/spicetify/spicetify-themes"; + rev = "5d3d42f913467f413be9b0159f5df5023adf89af"; + submodules = true; + };''; + }; + requiredExtensions = mkOption { + type = types.listOf (types.oneOf [ extension types.str ]); + default = [ ]; + }; + + appendName = mkOption { + type = types.bool; + default = true; + description = ''Whether or not to append the name of the theme + to the src file path when copying. For example: + (with appendName on) + cp /nix/store/2309435394589320fjirjf032-spicetify-themes/Dribbblish Themes + (with appendName off) + cp /nix/store/2309435394589320fjirjf032-spicetify-themes Themes + ''; + }; + + patches = mkOption { + type = types.attrs; + example = '' + { + "xpui.js_find_8008" = ",(\\w+=)32"; + "xpui.js_repl_8008" = ",$\{1}56"; + }; + ''; + description = "INI entries to add in the [Patch] section of config-xpui.ini"; + default = {}; + }; + + extraCommands = mkOption { + type = types.nullOr types.lines; + default = null; + description = "A bash script to run from the spicetify config directory if this theme is installed."; + }; + + # some config values that can be specified per-theme + injectCss = mkOption { + type = types.nullOr types.bool; + }; + overwriteAssets = mkOption { + type = types.nullOr types.bool; + }; + replaceColors = mkOption { + type = types.nullOr types.bool; + }; + sidebarConfig = mkOption { + type = types.nullOr types.bool; + }; + }; + }; + + app = types.submodule { + options = { + src = mkOption { + type = types.oneOf [ types.path types.str ]; + description = "Path to the folder containing the app code."; + example = + '' + pkgs.fetchgit { + url = "https://github.com/hroland/spicetify-show-local-files/"; + rev = "1bfd2fc80385b21ed6dd207b00a371065e53042e"; + sha256 = "01gy16b69glqcalz1wm8kr5wsh94i419qx4nfmsavm4rcvcr3qlx"; + }; + ''; + }; + name = mkOption { + type = types.nullOr types.str; + description = "Name of the app. No spaces or special characters please :)"; + }; + appendName = mkOption { type = types.bool; default = true; }; + }; + }; +in +{ + inherit theme extension xpui app; +} diff --git a/module.nix b/module.nix index c8bcbd5d..85b087de 100644 --- a/module.nix +++ b/module.nix @@ -2,12 +2,19 @@ with lib; let cfg = config.programs.spicetify; + spiceLib = import ./lib { inherit pkgs lib; }; + spiceTypes = spiceLib.types; + spicePkgs = import ./pkgs { inherit pkgs lib; }; + + ifTrue = lib.attrsets.optionalAttrs; + ifTrueList = lib.lists.optionals; in { options.programs.spicetify = { enable = mkEnableOption "A modded Spotify"; + theme = mkOption { - type = types.str; + type = types.oneOf [ types.str spiceTypes.theme ]; default = ""; }; @@ -23,254 +30,248 @@ in description = "The nix package containing spicetify-cli."; }; - themesSrc = mkOption { - type = types.package; - default = builtins.fetchGit { - url = "https://github.com/spicetify/spicetify-themes"; - rev = "5d3d42f913467f413be9b0159f5df5023adf89af"; - submodules = true; - }; - description = "A package which contains, at its root, a Themes directory, - which should be copied into the spicetify themes directory."; - }; - extraCommands = mkOption { type = types.lines; default = ""; description = "Extra commands to be run during the setup of spicetify."; }; - colorScheme = mkOption { - type = types.str; - default = ""; - }; - thirdPartyThemes = mkOption { - type = types.attrs; - default = { }; - description = "A set of themes, indexed by name and containing the path to the theme."; - example = '' - { - Dribbblish = $\{spicetify-themes-git}/Dribbblish; - } - ''; - }; - thirdPartyExtensions = mkOption { - type = types.attrs; - default = { }; - }; - thirdPartyCustomApps = mkOption { - type = types.attrs; - default = { }; - }; enabledExtensions = mkOption { - type = types.listOf types.str; + type = types.listOf (types.oneOf [ spiceTypes.extension types.str ]); default = [ ]; + description = "A list of extensions. Official extensions such as \"dribbblish.js\" can be referenced by string alone."; + example = '' + [ + "dribbblish.js" + "shuffle+.js" + { + src = pkgs.fetchgit { + url = "https://github.com/LucasBares/spicetify-last-fm"; + rev = "0f905b49362ea810b6247ac1950a2951dd35632e"; + sha256 = "1b0l2g5cyjj1nclw1ff7as9q94606mkq1k8l2s34zzdsx3m2zv81"; + }; + filename = "lastfm.js"; + } + ] + ''; }; enabledCustomApps = mkOption { - type = types.listOf types.str; + type = types.listOf (types.oneOf [ spiceTypes.app types.str ]); default = [ ]; }; - spotifyLaunchFlags = mkOption { - type = types.str; - default = ""; - }; - injectCss = mkOption { - type = types.bool; - default = false; - }; - replaceColors = mkOption { - type = types.bool; - default = false; - }; - overwriteAssets = mkOption { - type = types.bool; - default = false; - }; - disableSentry = mkOption { - type = types.bool; - default = true; - }; - disableUiLogging = mkOption { - type = types.bool; - default = true; - }; - removeRtlRule = mkOption { - type = types.bool; - default = true; - }; - exposeApis = mkOption { - type = types.bool; - default = true; - }; - disableUpgradeCheck = mkOption { - type = types.bool; - default = true; - }; - fastUserSwitching = mkOption { - type = types.bool; - default = false; - }; - visualizationHighFramerate = mkOption { - type = types.bool; - default = false; - }; - radio = mkOption { - type = types.bool; - default = false; - }; - songPage = mkOption { - type = types.bool; - default = false; - }; - experimentalFeatures = mkOption { - type = types.bool; - default = false; - }; - home = mkOption { - type = types.bool; - default = false; + + xpui = mkOption { + type = spiceTypes.xpui; + default = { }; }; - lyricAlwaysShow = mkOption { - type = types.bool; - default = false; + + # legacy/ease of use options (commonly set for themes like Dribbblish) + # injectCss = xpui.Setting.inject_css; + injectCss = mkOption { type = lib.types.nullOr lib.types.bool; default = null; }; + replaceColors = mkOption { type = lib.types.nullOr lib.types.bool; default = null; }; + overwriteAssets = mkOption { type = lib.types.nullOr lib.types.bool; default = null; }; + sidebarConfig = mkOption { type = lib.types.nullOr lib.types.bool; default = null; }; + colorScheme = mkOption { + type = lib.types.nullOr lib.types.str; + default = null; }; - lyricForceNoSync = mkOption { - type = types.bool; - default = false; + customColorScheme = mkOption { + type = lib.types.nullOr lib.types.attrs; + default = null; }; }; - config = mkIf cfg.enable { - # install necessary packages for this user - home.packages = with cfg; - let - # turn certain values on by default if we know the theme needs it - isDribbblish = cfg.theme == "Dribbblish"; - isTurntable = cfg.theme == "Turntable"; - injectCSSReal = boolToString (isDribbblish || cfg.injectCss); - replaceColorsReal = boolToString (isDribbblish || cfg.replaceColors); - overwriteAssetsReal = boolToString (isDribbblish || cfg.overwriteAssets); + config = + let + actualTheme = spiceLib.getTheme cfg.theme; + # helper functions + pipeConcat = foldr (a: b: a + "|" + b) ""; + lineBreakConcat = foldr (a: b: a + "\n" + b) ""; - pipeConcat = foldr (a: b: a + "|" + b) ""; - extensionString = pipeConcat ( - (if isDribbblish then [ "dribbblish.js" ] else [ ]) - ++ (if isTurntable then [ "turntable.js" ] else [ ]) - ++ cfg.enabledExtensions - ); - customAppsString = pipeConcat cfg.enabledCustomApps; + # take the list of extensions and turn strings into actual extensions + allExtensions = map spiceLib.getExtension (cfg.enabledExtensions ++ + (ifTrueList (builtins.hasAttr "requiredExtensions" actualTheme) + actualTheme.requiredExtensions + ) ++ cfg.xpui.AdditionalOptions.extensions); + allExtensionFiles = map (item: item.filename) allExtensions; + extensionString = pipeConcat allExtensionFiles; - customToINI = lib.generators.toINI { - # specifies how to format a key/value pair - mkKeyValue = lib.generators.mkKeyValueDefault - { - # specifies the generated string for a subset of nix values - mkValueString = v: - if v == true then "1" - else if v == false then "0" - # else if isString v then ''"${v}"'' - # and delegats all other values to the default generator - else lib.generators.mkValueStringDefault { } v; - } "="; - }; + # do the same thing again but for customapps this time + allApps = map spiceLib.getApp (cfg.enabledCustomApps ++ cfg.xpui.AdditionalOptions.custom_apps); + allAppsNames = map (item: item.name) allApps; + customAppsString = pipeConcat allAppsNames; + + # cfg.theme.injectCss is a submodule but cfg.injectCss is not, so we + # have to have two different override functions for each case + # (one value is null while the other is undefined...) + createBoolOverride = set: attrName: cfgName: + ifTrue (set.${attrName} != null) (ifTrue (builtins.typeOf set.${attrName} == "bool") + { ${cfgName} = set.${attrName}; }); + createBoolOverrideFromSubmodule = set: attrName: cfgName: + ifTrue (builtins.hasAttr attrName set) + (ifTrue (builtins.typeOf set.${attrName} == "bool") + { ${cfgName} = set.${attrName}; }); - config-xpui = builtins.toFile "config-xpui.ini" (customToINI { + # determine whether or not any extension requires experimental_features + needExperimental = lib.lists.any + (item: (if (builtins.hasAttr "experimentalFeatures" item) then + item.experimentalFeatures + else + false) + ) + allExtensions; + + mkXpuiOverrides = + let + createOverride = set: attrName: cfgName: + ifTrue (set.${attrName} != null) + { ${cfgName} = set.${attrName}; }; + in + container: boolOverrideFunc: { AdditionalOptions = { - home = cfg.home; - experimental_features = cfg.experimentalFeatures; extensions = extensionString; custom_apps = customAppsString; - sidebar_config = 0; # i dont know what this does - }; - Patch = { }; - Setting = { - spotify_path = "__REPLACEME__"; # to be replaced in the spotify postInstall - prefs_path = "__REPLACEME2__"; - current_theme = cfg.theme; - color_scheme = cfg.colorScheme; - spotify_launch_flags = cfg.spotifyLaunchFlags; - check_spicetify_upgrade = 0; - inject_css = injectCSSReal; - replace_colors = replaceColorsReal; - overwrite_assets = overwriteAssetsReal; + experimental_features = needExperimental; }; - Preprocesses = { - disable_upgrade_check = cfg.disableUpgradeCheck; - disable_sentry = cfg.disableSentry; - disable_ui_logging = cfg.disableUiLogging; - remove_rtl_rule = cfg.removeRtlRule; - expose_apis = cfg.exposeApis; - }; - Backup = { - version = cfg.spotifyPackage.version; - "with" = "Dev"; - }; - }); + Setting = { } + // boolOverrideFunc container "injectCss" "inject_css" + // boolOverrideFunc container "replaceColors" "replace_colors" + // boolOverrideFunc container "overwriteAssets" "overwrite_assets" + // boolOverrideFunc container "sidebarConfig" "sidebar_config" + # also add the colorScheme as an override if defined in cfg + // (ifTrue (container == cfg) (createOverride container "colorScheme" "color_scheme")) + # and turn the theme into a string of its name + // (ifTrue (container == cfg) { current_theme = actualTheme.name; }); + Patch = (ifTrue (container == actualTheme) (ifTrue (builtins.hasAttr "patches" actualTheme) actualTheme.patches)); + Backup = { version = (cfg.spotifyPackage.version or "Unknown"); }; + }; - # INI created, now create the postInstall that runs spicetify - inherit (pkgs.lib.lists) foldr; - inherit (pkgs.lib.attrsets) mapAttrsToList; + # override any values defined by the user in cfg.xpui with values defined by the theme + overridenXpui1 = builtins.mapAttrs + (name: value: (lib.trivial.mergeAttrs cfg.xpui.${name} value)) + (mkXpuiOverrides actualTheme createBoolOverrideFromSubmodule); + # override any values defined by the theme with values defined in cfg + setToString = set: lineBreakConcat (lib.attrsets.mapAttrsToList (name: value: "${name}") set); + overridenXpui2 = builtins.mapAttrs + (name: value: (lib.trivial.mergeAttrs overridenXpui1.${name} value)) + (mkXpuiOverrides cfg createBoolOverride); - # Helper functions - lineBreakConcat = foldr (a: b: a + "\n" + b) ""; - boolToString = x: if x then "1" else "0"; - makeCpCommands = type: (mapAttrsToList (name: path: - let - extension = if type == "Extensions" then ".js" else ""; - in - "cp -r ${path} ./${type}/${name}${extension} && ${pkgs.coreutils-full}/bin/chmod -R a+wr ./${type}/${name}${extension}")); + config-xpui = builtins.toFile "config-xpui.ini" (spiceLib.createXpuiINI overridenXpui2); - spicetify = "${cfg.spicetifyPackage}/bin/spicetify-cli --no-restart"; + # INI created, now create the postInstall that runs spicetify + inherit (pkgs.lib.lists) foldr; + inherit (pkgs.lib.attrsets) mapAttrsToList; - # custom spotify package with spicetify integrated in - spiced-spotify = cfg.spotifyPackage.overrideAttrs (oldAttrs: rec { - postInstall = '' - export SPICETIFY_CONFIG=$out/spicetify - mkdir -p $SPICETIFY_CONFIG - pushd $SPICETIFY_CONFIG - - # create config and prefs - cp ${config-xpui} config-xpui.ini - ${pkgs.coreutils-full}/bin/chmod a+wr config-xpui.ini - touch $out/share/spotify/prefs - - # replace the spotify path with the current derivation's path - sed -i "s|__REPLACEME__|$out/share/spotify|g" config-xpui.ini - sed -i "s|__REPLACEME2__|$out/share/spotify/prefs|g" config-xpui.ini + extensionCommands = lineBreakConcat (map + (item: + let + command = "cp -rn ${item.src}/${item.filename} ./Extensions/${item.filename}"; + in + "${command} && echo \"Cp command for ${item.filename} succeeded!\"" + ) + allExtensions); - cp -r ${cfg.themesSrc} Themes - ${pkgs.coreutils-full}/bin/chmod -R a+wr Themes + customAppCommands = lineBreakConcat (map + (item: "cp -rn ${(if (builtins.hasAttr "appendName" item) then + if (item.appendName) then + "${item.src}/${item.name}" + else + "${item.src}" + else + "${item.src}")} ./CustomApps/${item.name}") + allApps); - # the following command will link themes, but we want to copy so we can have w/r - # find ${cfg.themesSrc} -maxdepth 1 -type d -exec ln -s {} Themes \; - mkdir -p Extensions - ${cfg.extraCommands} - ${if isDribbblish then "cp ./Themes/Dribbblish/dribbblish.js ./Extensions/dribbblish.js \n" else ""} - ${if isTurntable then "cp ./Themes/Turntable/turntable.js ./Extensions/turntable.js \n" else ""} - # copy themes into Themes folder - ${lineBreakConcat (makeCpCommands "Themes" cfg.thirdPartyThemes)} - # copy extensions into Extensions folder - ${lineBreakConcat (makeCpCommands "Extensions" cfg.thirdPartyExtensions)} - # copy custom apps into CustomApps folder - ${lineBreakConcat (makeCpCommands "CustomApps" cfg.thirdPartyCustomApps)} - - popd + spicetify = "${cfg.spicetifyPackage}/bin/spicetify-cli --no-restart"; - pushd $out/share/spotify - ${lineBreakConcat (makeCpCommands "Apps" thirdPartyCustomApps)} - popd + theme = spiceLib.getTheme cfg.theme; + themePath = spiceLib.getThemePath theme; + + customColorSchemeINI = (builtins.toFile "dummy-color.ini" + (spiceLib.createXpuiINI + { custom = cfg.customColorScheme; })); + + customColorSchemeScript = + if (cfg.customColorScheme != null) then '' + COLORINI=./Themes/${actualTheme.name}/color.ini + if [ -e $COLORINI ]; then + echo "\n" >> $COLORINI + # finally, use cat for its actual purpose: concatenation + cat ${customColorSchemeINI} >> $COLORINI + fi + '' else ""; + + finalScript = '' + export SPICETIFY_CONFIG=$out/spicetify + mkdir -p $SPICETIFY_CONFIG + pushd $SPICETIFY_CONFIG + + # create config and prefs + cp ${config-xpui} config-xpui.ini + ${pkgs.coreutils-full}/bin/chmod a+wr config-xpui.ini + touch $out/share/spotify/prefs + + # replace the spotify path with the current derivation's path + sed -i "s|__REPLACEME__|$out/share/spotify|g" config-xpui.ini + sed -i "s|__REPLACEME2__|$out/share/spotify/prefs|g" config-xpui.ini - ${spicetify} backup apply + mkdir -p Themes + mkdir -p Extensions + mkdir -p CustomApps + cp -r ${themePath} ./Themes/${theme.name} + ${pkgs.coreutils-full}/bin/chmod -R a+wr Themes + echo "copied theme" + # copy extensions into Extensions folder + ${extensionCommands} + ${pkgs.coreutils-full}/bin/chmod -R a+wr Extensions + # copy custom apps into CustomApps folder + ${customAppCommands} + ${pkgs.coreutils-full}/bin/chmod -R a+wr CustomApps + # completed app and extension installation + # add a custom color scheme if necessary + ${customColorSchemeScript} + # completed custom color scheme addition + ${cfg.extraCommands} + + # extra commands that the theme might need + ${if (builtins.hasAttr "extraCommands" actualTheme) then + (if actualTheme.extraCommands != null then + actualTheme.extraCommands + else + "") + else ""} + popd + ${spicetify} backup apply - # fix config to point to home directory (not necessary I don't think, but whatever) - # sed -i "s|$out/share/spotify/prefs|${config.home.homeDirectory}/.config/spotify/prefs|g" $SPICETIFY_CONFIG/config-xpui.ini - ''; - }); - in - [ - spiced-spotify - cfg.spicetifyPackage - ]; - }; + # fix config to point to home directory (not necessary I don't think, but whatever) + # sed -i "s|$out/share/spotify/prefs|${config.home.homeDirectory}/.config/spotify/prefs|g" $SPICETIFY_CONFIG/config-xpui.ini + ''; + + + # custom spotify package with spicetify integrated in + spiced-spotify = cfg.spotifyPackage.overrideAttrs (oldAttrs: rec { + postInstall = finalScript; + }); + in + mkIf cfg.enable { + # install necessary packages for this user + home.packages = with cfg; + [ + spiced-spotify + cfg.spicetifyPackage + ] ++ + # need montserrat for the BurntSienna theme + (ifTrueList + (actualTheme == spicePkgs.official.themes.BurntSienna) + [ pkgs.montserrat ] + ) ++ + (ifTrueList + (cfg.theme == spicePkgs.themes.Orchis) + [ pkgs.fira ] + ); + home.sessionVariables = { + SPICETIFY_CONFIG = "${spiced-spotify}/spicetify"; + }; + }; } diff --git a/pkgs/default.nix b/pkgs/default.nix new file mode 100644 index 00000000..6d199216 --- /dev/null +++ b/pkgs/default.nix @@ -0,0 +1,672 @@ +{ pkgs, lib, ... }: +let + spiceTypes = (import ../lib { inherit pkgs lib; }).types; + + # SOURCE -------------------------------------------------------------------- + officialThemes = pkgs.fetchgit { + url = "https://github.com/spicetify/spicetify-themes"; + rev = "9a2fcb5a545da368e4bf1d8189f58d0f664f3115"; + sha256 = "18gmhahw7k4labygq3a4igqbkwqzlr67s7xvnf75521ynnzpnhca"; + }; + + officialSrc = pkgs.fetchgit { + url = "https://github.com/spicetify/spicetify-cli"; + rev = "6f473f28151c75e08e83fb280dd30fadd22d9c04"; + sha256 = "0vw0271vbvpgyb0y97lafc5hqpfy5947zm7r2wlg17f8w94vsfhv"; + }; + + catppuccinSrc = pkgs.fetchgit { + url = "https://github.com/catppuccin/spicetify"; + rev = "8aaacc4b762fb507b3cf7d4d1b757eb849fcbb52"; + sha256 = "185fbh958k985ci3sf4rdxxkwbk61qmzjhd6m54h9rrsrmh5px69"; + }; + + spotifyNoPremiumSrc = pkgs.fetchgit { + url = "https://github.com/Daksh777/SpotifyNoPremium"; + rev = "a2daa7a9ec3e21ebba3c6ab0ad1eb5bd8e51a3ca"; + sha256 = "1sr6pjaygxxx6majmk5zg8967jry53z6xd6zc31ns2g4r5sy4k8d"; + }; + + comfySrc = pkgs.fetchgit { + url = "https://github.com/Comfy-Themes/Spicetify"; + rev = "45830ed853cc212dec0c053deb34da6aefc25ce5"; + sha256 = "1hb9f1nwf0jw5yvrzy2bshpb89h1aaysf18zvs0g5fmhmvn7ba6s"; + }; + + fluentSrc = pkgs.fetchgit { + url = "https://github.com/williamckha/spicetify-fluent"; + rev = "47c13bfa2983643a14229c5ecbb88d5001c91c6b"; + sha256 = "0pcx9wshrx0hp3rcjrhi7676baskp8r10bcahp6nr105s42d8x5z"; + }; + + defaultDynamicSrc = pkgs.fetchgit { + url = "https://github.com/JulienMaille/spicetify-dynamic-theme"; + rev = "b21c35c0695b1baebbbe446a0a02ec40d4c5279e"; + sha256 = "0qlkvazciqr62z7vc6fdvy6hn2mgn3blj13fi3a82vg5jb70mgxm"; + }; + + retroBlurSrc = pkgs.fetchgit { + url = "https://github.com/Motschen/Retroblur"; + rev = "a1add2945cf753bbc32108b561faa09ef8af7183"; + sha256 = "1g7aqg21arl05s69ywb1qkiva17gldisdmvxin85yiv14pahj06p"; + }; + + omniSrc = pkgs.fetchgit { + url = "https://github.com/getomni/spicetify"; + rev = "1c8cbf99cdea93f3a0e8297ddfb681e58551d51d"; + sha256 = "0s9avj0gq206hcj8qri025avv12pmmlswyffkxq6s2y2mi9wp0h7"; + }; + + bloomSrc = pkgs.fetchgit { + url = "https://github.com/nimsandu/spicetify-bloom"; + rev = "c8f69180a3bcd0cc27b9e6bd84fc5c0996b5ccc0"; + sha256 = "1g11n6qf8xqgpr5jy5wswdf0cy128mwrxixm713291ac3jcdl8in"; + }; + + orchisSrc = pkgs.fetchgit { + url = "https://github.com/canbeardig/Spicetify-Orchis-Colours-v2"; + rev = "5bf3fcf0696514dcf3e95f4ae3fd00261ccc5dcc"; + sha256 = "1fzmxgjb3l6qn6a7zc621pqhh5m5xzjj1wqplk4rwnrrb1d3digm"; + }; + + draculaSrc = pkgs.fetchgit { + url = "https://github.com/Darkempire78/Dracula-Spicetify"; + rev = "97bf149e7afbe408509862591a57f1d8e2dfc5d7"; + sha256 = "0l7la5hmhzfzf0n6lk3zxc4bc9f2h2dcwx02r6yqnrnkkkzh0b91"; + }; + + nordSrc = pkgs.fetchgit { + url = "https://github.com/Tetrax-10/Nord-Spotify"; + rev = "54808ec21a87db3c7c11e2a4e86fca6d45c50c9e"; + sha256 = "0sqgbdkd3cjjh5zdfadnsd4zfscqhx2pbinzfn26v99fsla18kv3"; + }; + + dakshExtensions = pkgs.fetchgit { + url = "https://github.com/daksh2k/Spicetify-stuff"; + rev = "2a4d0be5fecf449c1f6dd57950c2ca3ba2e71635"; + sha256 = "12axk85h30i5a6b0sa75g85bamhcvnyqj6zgv2irgk9f3m5018ck"; + }; + + hidePodcastsSrc = pkgs.fetchgit { + url = "https://github.com/theRealPadster/spicetify-hide-podcasts"; + rev = "cfda4ce0c3397b0ec38a971af4ff06daba71964d"; + sha256 = "146bz9v94dk699bshbc21yq4y5yc38lq2kkv7w3sjk4x510i0v3q"; + }; + + historySrc = pkgs.fetchgit { + url = "https://github.com/einzigartigerName/spicetify-history"; + rev = "577e34f364127f18d917d2fe2e8c8f2a1af9f6ae"; + sha256 = "0fv5fb6k9zc446a1lznhmd68m47sil5pqabv4dmrqk6cvfhba49r"; + }; + + genreSrc = pkgs.fetchgit { + url = "https://github.com/Shinyhero36/Spicetify-Genre"; + rev = "4ab66852825525869ef5ced5747e7e84ddd0a8bb"; + sha256 = "09b69dcknqvj9nc5ayfqcdg63vc5yshn0wa23gyachzicwalq30m"; + }; + + lastfmSrc = pkgs.fetchgit { + url = "https://github.com/LucasBares/spicetify-last-fm"; + rev = "0f905b49362ea810b6247ac1950a2951dd35632e"; + sha256 = "1b0l2g5cyjj1nclw1ff7as9q94606mkq1k8l2s34zzdsx3m2zv81"; + }; + + localFilesSrc = pkgs.fetchgit { + url = "https://github.com/hroland/spicetify-show-local-files/"; + rev = "1bfd2fc80385b21ed6dd207b00a371065e53042e"; + sha256 = "01gy16b69glqcalz1wm8kr5wsh94i419qx4nfmsavm4rcvcr3qlx"; + }; + + autoVolumeSrc = pkgs.fetchFromGitHub { + owner = "amanharwara"; + repo = "spicetify-autoVolume"; + rev = "d7f7962724b567a8409ef2898602f2c57abddf5a"; + sha256 = "1pnya2j336f847h3vgiprdys4pl0i61ivbii1wyb7yx3wscq7ass"; + }; + + customAppsExtensionsSrc = pkgs.fetchgit { + url = "https://github.com/3raxton/spicetify-custom-apps-and-extensions"; + rev = "0f5e79fe43abf57f714d7d00bd288870d5b6f718"; + sha256 = "1kjzaczp9p88jkf9jxkh3wrdydz9vhfljh6yaywzqsa2qz7zycp3"; + }; + + spotifyCanvasSrc = pkgs.fetchgit { + url = "https://github.com/itsmeow/Spicetify-Canvas"; + rev = "052926a51d7586fc107ac650a61dfa0b1669c3eb"; + sha256 = "1xczz5zd7275hcdg4hgqvcynbrkn4mx6g5vz6fylddvp275h3fn6"; + }; + + charlieS1103Src = pkgs.fetchgit { + url = "https://github.com/CharlieS1103/spicetify-extensions"; + rev = "40b8c13722ca92ce71ccbd27645e47da92ee25ec"; + sha256 = "05v9c9vzdk2maqhzyzr0fd5nhv5im1cvspmly3j04p8k92hic33v"; + }; + + huhExtensionsSrc = pkgs.fetchgit { + url = "https://github.com/huhridge/huh-spicetify-extensions"; + rev = "332c6ec9089aa6d5afd911644a649467e4698852"; + sha256 = "0yrsicfdb571z7p6fayh9f4zy65y18ff63g6lcs74ir0pv2hqllq"; + }; + + playlistIconsSrc = pkgs.fetchgit { + url = "https://github.com/jeroentvb/spicetify-playlist-icons"; + rev = "9ccb8b677eb139dbc3bdd10f1d9bec25c3a6144e"; + sha256 = "18ai0l0j3kswy33m7w7bdw5k39f0cfm4hbxndgk4dkljsi5k31nv"; + }; + + tetraxSrc = pkgs.fetchgit { + url = "https://github.com/Tetrax-10/Spicetify-Extensions"; + rev = "928ea55b6129da2ff68a1bc28e1054a8380a6d1e"; + sha256 = "001h2f137vq8a6l9id05nkh7nw7cbajn5s6a80wlhmm2g03rhm7k"; + }; + + powerBarSrc = pkgs.fetchgit { + url = "https://github.com/jeroentvb/spicetify-power-bar"; + rev = "bea0d0d05271c543a41e7e464e8b9a3bd3de3003"; + sha256 = "09g72nlapqaal1rsd4x1nh0g6dg9286hqfxq1439r7k7fqa5hwc7"; + }; + + groupSessionSrc = pkgs.fetchgit { + url = "https://github.com/timll/spotify-group-session"; + rev = "06c5b218ccec3e50b8b1d5f067898727d96e672b"; + sha256 = "1kcpkvijjkmgq9gl62a93yhgs77z7c1xyv3x6h03bhpd94ccyycd"; + }; + + startPageSrc = pkgs.fetchgit { + url = "https://github.com/Resxt/startup-page"; + rev = "cca2b29e690dad4d8b89f0ba994b2f9a714f4e6a"; + sha256 = "1qqar6lcq1djbiwgsjd7sd5r8061fkwfy92yfvh3b7i9q939djf5"; + }; + + # EXTENSIONS ---------------------------------------------------------------- + + dribbblishExt = { + filename = "dribbblish.js"; + src = "${officialThemes}/Dribbblish"; + }; + + turntableExt = { + filename = "turntable.js"; + src = "${officialThemes}/Turntable"; + }; + + adblock = { + src = spotifyNoPremiumSrc; + filename = "adblock.js"; + }; + + savePlaylists = { + src = "${dakshExtensions}/Extensions"; + filename = "savePlaylists.js"; + }; + fullScreen = { + src = "${dakshExtensions}/Extensions"; + filename = "fullScreen.js"; + }; + autoSkip = { + src = "${dakshExtensions}/Extensions"; + filename = "autoSkip.js"; + }; + playNext = { + src = "${dakshExtensions}/Extensions"; + filename = "playNext.js"; + }; + volumePercentage = { + src = "${dakshExtensions}/Extensions"; + filename = "volumePercentage.js"; + }; + + hidePodcasts = { + src = hidePodcastsSrc; + filename = "hidePodcasts.js"; + }; + history = { + src = historySrc; + filename = "historyShortcut.js"; + }; + genre = { + src = genreSrc; + filename = "genre.js"; + }; + lastfm = { + src = "${lastfmSrc}/src"; + filename = "lastfm.js"; + }; + + autoVolume = { + src = autoVolumeSrc; + filename = "autoVolume.js"; + }; + + copyToClipboard = { + src = "${customAppsExtensionsSrc}/v2/copy-to-clipboard"; + filename = "copytoclipboard2.js"; + }; + showQueueDuration = { + src = "${customAppsExtensionsSrc}/v2/show-queue-duration"; + filename = "showQueueDuration.js"; + }; + volumeProfiles = { + src = "${customAppsExtensionsSrc}/v2/volume-profiles/dist"; + filename = "volume-profiles.js"; + }; + + songStats = { + src = "${charlieS1103Src}/songstats"; + filename = "songstats.js"; + }; + featureShuffle = { + src = "${charlieS1103Src}/featureshuffle"; + filename = "featureshuffle.js"; + }; + wikify = { + src = "${charlieS1103Src}/wikify"; + filename = "wikify.js"; + }; + fixEnhance = { + src = "${charlieS1103Src}/fixEnhance"; + filename = "fixEnhance.js"; + experimentalFeatures = true; + }; + phraseToPlaylist = { + src = "${charlieS1103Src}/phraseToPlaylist"; + filename = "phraseToPlaylist.js"; + }; + + fullAlbumDate = { + src = "${huhExtensionsSrc}/fullAlbumDate"; + filename = "fullAlbumDate.js"; + }; + fullAppDisplayMod = { + src = "${huhExtensionsSrc}/fullAppDisplayMod"; + filename = "fullAppDisplayMod.js"; + }; + goToSong = { + src = "${huhExtensionsSrc}/goToSong"; + filename = "goToSong.js"; + }; + listPlaylistsWithSong = { + src = "${huhExtensionsSrc}/listPlaylistsWithSong"; + filename = "listPlaylistsWithSong.js"; + }; + playlistIntersection = { + src = "${huhExtensionsSrc}/playlistIntersection"; + filename = "playlistIntersection.js"; + }; + skipStats = { + src = "${huhExtensionsSrc}/skipStats"; + filename = "skipStats.js"; + }; + playlistIcons = { + src = playlistIconsSrc; + filename = "playlist-icons.js"; + }; + + seekSong = { + src = "${tetraxSrc}/Seek-Song"; + filename = "seekSong.js"; + }; + skipOrPlayLikedSongs = { + src = "${tetraxSrc}/Skip-or-Play-Liked-Songs"; + filename = "skipOrPlayLikedSongs.js"; + }; + + powerBar = { + src = powerBarSrc; + filename = "power-bar.js"; + }; + # TODO: add user.css additions as part of extensions, for snippets + # powerBar can by styled with the following CSS: + # #power-bar-container { + # --power-bar-background-color: #333333; + # --power-bar-main-text-color: #ffffff; + # --power-bar-subtext-color: #b3b3b3; + # --power-bar-active-background-color: #1db954; + # --power-bar-active-text-color: #121212; + # --power-bar-border-color: #000000; + # } + + groupSession = { + src = "${groupSessionSrc}/src"; + filename = "group-session.js"; + }; + + + # UNUSED + # we already have an adblock + charliesAdblock = { + src = "${charlieS1103Src}/adblock"; + filename = "adblock.js"; + }; + # startpage needs r/w + startPage = { + src = "${startPageSrc}/dist"; + filename = "startup-page.js"; + }; + + + # THEME GENERATORS ---------------------------------------------------------- + + mkCatppuccinTheme = name: { + ${name} = { + inherit name; + src = catppuccinSrc; + appendName = true; + requiredExtensions = [ + { + src = "${catppuccinSrc}/js"; + filename = "${name}.js"; + } + ]; + injectCss = true; + replaceColors = true; + overwriteAssets = true; + }; + }; + mkComfyTheme = name: { + ${name} = + let lname = lib.strings.toLower name; in + { + inherit name; + src = comfySrc; + appendName = true; + injectCss = true; + replaceColors = true; + overwriteAssets = true; + requiredExtensions = [ + { + src = "${comfySrc}/${name}"; + filename = "${lname}.js"; + } + ]; + extraCommands = '' + # remove the auto-update functionality + echo "\n" >> ./Extensions/${lname}.js + cat ./Themes/${name}/${lname}.script.js >> ./Extensions/${lname}.js + ''; + }; + }; + + # THEMES -------------------------------------------------------------------- + + SpotifyNoPremium = { + name = "SpotifyNoPremium"; + src = spotifyNoPremiumSrc; + appendName = false; + requiredExtensions = [ adblock ]; + }; + + Fluent = { + name = "Fluent"; + src = fluentSrc; + appendName = false; + injectCss = true; + overwriteAssets = true; + replaceColors = true; + patches = { + "xpui.js_find_8008" = ",(\\w+=)32"; + "xpui.js_repl_8008" = ",$\{1}56"; + }; + requiredExtensions = [ + { + src = fluentSrc; + filename = "fluent.js"; + } + ]; + }; + + DefaultDynamic = { + name = "DefaultDynamic"; + src = defaultDynamicSrc; + appendName = false; + injectCss = true; + replaceColors = true; + requiredExtensions = [ + { + src = defaultDynamicSrc; + filename = "default-dynamic.js"; + } + { + src = defaultDynamicSrc; + filename = "Vibrant.min.js"; + } + ]; + patches = { + "xpui.js_find_8008" = ",(\\w+=)32,"; + "xpui.js_repl_8008" = ",$\{1}28,"; + }; + }; + + RetroBlur = { + name = "RetroBlur"; + src = retroBlurSrc; + appendName = false; + injectCss = true; + replaceColors = true; + }; + + # BROKEN. no clue why + Omni = { + name = "Omni"; + src = omniSrc; + appendName = false; + injectCss = true; + overwriteAssets = true; + replaceColors = true; + requiredExtensions = [ + { + src = omniSrc; + filename = "omni.js"; + } + ]; + }; + + # light colorscheme is broken, think that's the theme's fault + Bloom = { + name = "Bloom"; + src = bloomSrc; + appendName = false; + injectCss = true; + replaceColors = true; + overwriteAssets = true; + patches = { + "xpui.js_find_8008" = ",(\\w+=)32,"; + "xpui.js_repl_8008" = ",$\{1}56,"; + }; + requiredExtensions = [ + { + src = bloomSrc; + filename = "bloom.js"; + } + ]; + }; + + Orchis = { + name = "DarkGreen"; + src = orchisSrc; + appendName = true; + injectCss = true; + replaceColors = true; + }; + + Dracula = { + name = "Dracula"; + src = draculaSrc; + appendName = true; + replaceColors = true; + }; + + Nord = { + name = "Nord"; + src = nordSrc; + appendName = false; + injectCss = true; + replaceColors = true; + }; + + # theres a thing at https://github.com/itsmeow/Spicetify-Canvas + # about getting a custom build of chromium or something. I am NOT doing that + # ... but maybe one day if someone asks + # TODO: add the ability to append this user.css to any other user.css + # for installation in any theme + SpotifyCanvas = { + name = "SpotifyCanvas"; + src = "${spotifyCanvasSrc}/Themes/canvas"; + appendName = false; + injectCss = true; + + requiredExtensions = [ + { + src = "${spotifyCanvasSrc}/Extensions"; + filename = "getCanvas.js"; + } + ]; + }; + + # CUSTOMAPPS ---------------------------------------------------------------- + localFiles = { + name = "localFiles"; + src = localFilesSrc; + appendName = false; + }; + + # OFFICIAL THEMES AND EXTENSIONS -------------------------------------------- + + official = { + themes = + let + mkOfficialTheme = themeName: { ${themeName} = { name = themeName; src = officialThemes; }; }; + in + { + Dribbblish = { + name = "Dribbblish"; + src = officialThemes; + requiredExtensions = [ dribbblishExt ]; + patches = { + "xpui.js_find_8008" = ",(\\w+=)32"; + "xpui.js_repl_8008" = ",$\{1}56"; + }; + injectCss = true; + replaceColors = true; + overwriteAssets = true; + appendName = true; + sidebarConfig = true; + }; + + Dreary = { + name = "Dreary"; + src = officialThemes; + sidebarConfig = true; + appendName = true; + }; + Glaze = { + name = "Glaze"; + src = officialThemes; + sidebarConfig = true; + appendName = true; + }; + Turntable = { + name = "Turntable"; + src = officialThemes; + requiredExtensions = [ "fullAppDisplay.js" turntableExt ]; + }; + } // + mkOfficialTheme "Ziro" // + mkOfficialTheme "Sleek" // + mkOfficialTheme "Onepunch" // + mkOfficialTheme "Flow" // + mkOfficialTheme "Default" // + mkOfficialTheme "BurntSienna"; + + extensions = + let + mkOfficialExt = name: { "${name}.js" = { src = "${officialSrc}/Extensions"; filename = "${name}.js"; }; }; + in + { + "dribbblish.js" = dribbblishExt; + "turntable.js" = turntableExt; + } + // mkOfficialExt "autoSkipExplicit" + // mkOfficialExt "autoSkipVideo" + // mkOfficialExt "bookmark" + // mkOfficialExt "fullAppDisplay" + // mkOfficialExt "keyboardShortcut" + // mkOfficialExt "loopyLoop" + // mkOfficialExt "popupLyrics" + // mkOfficialExt "shuffle+" + // mkOfficialExt "trashbin" + // mkOfficialExt "webnowplaying"; + + apps = { + new-releases = { + src = "${officialSrc}/CustomApps"; + name = "new-releases"; + }; + reddit = { + src = "${officialSrc}/CustomApps"; + name = "reddit"; + }; + lyrics-plus = { + src = "${officialSrc}/CustomApps"; + name = "lyrics-plus"; + }; + }; + }; + appendJS = ext: { ${ext.filename} = ext; }; +in +{ + inherit official; + themes = { + inherit SpotifyNoPremium Fluent DefaultDynamic RetroBlur Omni Bloom Orchis + Dracula Nord SpotifyCanvas; + } // official.themes + // mkCatppuccinTheme "catppuccin-mocha" + // mkCatppuccinTheme "catppuccin-frappe" + // mkCatppuccinTheme "catppuccin-latte" + // mkCatppuccinTheme "catppuccin-macchiato" + // mkComfyTheme "Comfy" + // mkComfyTheme "Comfy-Chromatic" + // mkComfyTheme "Comfy-Mono"; + extensions = { + # aliases for weirdly named extension files + "history.js" = history; + "volumeProfiles.js" = volumeProfiles; + "copyToClipboard.js" = copyToClipboard; + "songStats.js" = songStats; + "featureShuffle.js" = featureShuffle; + "playlistIcons.js" = playlistIcons; + "powerBar.js" = powerBar; + "groupSession.js" = groupSession; + } // official.extensions + // appendJS groupSession + // appendJS powerBar + // appendJS seekSong + // appendJS skipOrPlayLikedSongs + // appendJS playlistIcons + // appendJS fullAlbumDate + // appendJS fullAppDisplayMod + // appendJS goToSong + // appendJS listPlaylistsWithSong + // appendJS playlistIntersection + // appendJS skipStats + // appendJS phraseToPlaylist + // appendJS fixEnhance + // appendJS wikify + // appendJS featureShuffle + // appendJS songStats + // appendJS showQueueDuration + // appendJS copyToClipboard + // appendJS volumeProfiles + // appendJS autoVolume + // appendJS history + // appendJS lastfm + // appendJS genre + // appendJS hidePodcasts + // appendJS adblock + // appendJS savePlaylists + // appendJS autoSkip + // appendJS fullScreen + // appendJS playNext + // appendJS volumePercentage; + apps = { inherit localFiles; } // official.apps; +}