♻️ refactor(spicetify): using marketplace for extensions and themes

This commit is contained in:
Sergio Laín 2024-06-19 20:27:56 +02:00
parent 972d458ac8
commit 1a6432a27b
No known key found for this signature in database
GPG key ID: 8429B2EE312F8150
38 changed files with 6100 additions and 8552 deletions

View file

@ -1,3 +0,0 @@
<div align="center">
<a href=""><img src="./title.png"></a>
</div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View file

@ -0,0 +1,84 @@
# Better local files
View your local songs, albums and artists.
![preview](https://raw.githubusercontent.com/Pithaya/spicetify-apps/main/custom-apps/better-local-files/preview.png)
> **Note**
> There is a specific way to format artists so that the custom app recognizes them correctly: see [limitations](#limitations).
>
> If you have an issue with albums with the same name being merged, see [Albums with the same name from different artists](#albums-with-the-same-name-from-different-artists).
## Features
- Tracks, albums, and artists views
- Supports filtering and sorting
- Supports all languages available in Spotify.
![preview](https://raw.githubusercontent.com/Pithaya/spicetify-apps/main/custom-apps/better-local-files/docs/tracks.png)
## Installation
1. Run `spicetify config-dir` to open the spicetify folder.
2. Go to the `CustomApps` folder.
3. Create a `better-local-files` folder.
4. Download the custom app files as a zip from [here](https://github.com/Pithaya/spicetify-apps-dist/archive/refs/heads/dist/better-local-files.zip).
5. Extract the zip and put the files inside the folder you created in step 3.
Then, run the following commands:
```sh
spicetify config custom_apps better-local-files
spicetify apply
```
## Limitations
### Song artists
Spotify doesn't seem to recognize separate artist tags on songs as different artists, and will instead use the concatenation of the artist's names. So a track with artists 'A' and 'B' will be considered as having a single artist: 'A, B'.
The custom app parses these strings with the following delimiters: ', ' and '; '. You should use this format to get artists recognized properly.
**Note**: This means that artists with the same name will be **considered the same artist**.
### Album artists
Album artists tags are not recognized, so the custom app will use the list of all artists from the album's tracks as the album's artists.
### Artist images
As artist images are not available, the artist's first album's image will be used instead.
## Albums with the same name from different artists
To identify an album, Spotify uses the current track's artist(s) name and the album name. This means that albums with tracks from different artists will be considered separate albums.
To fix this, the custom app groups albums by name.
This means that albums with the same name will be **considered the same album**.
If you find that you have albums with the same name that are being merged when they shouldn't, you can use the **settings menu** to **rebuild the local album cache**. This will try to group tracks that share the same album name correctly by comparing the album covers.
Note that for performance reasons this behaviour is **not enabled by default**. If you add new albums that have the same issue later on, you will need to **rebuild the cache manually again**.
![preview](https://raw.githubusercontent.com/Pithaya/spicetify-apps/main/custom-apps/better-local-files/docs/build-cache.PNG)
The cache can take some time to be built depending on how many tracks you have and how large the cover images are.
The **clear album cache** menu item allows you to clear the cache and go back to the default behavior.
## Upcoming features
- Artist's album list
- Multi track selection
- Keep search, sort, and scroll information on navigation
## Uninstall
1. Run `spicetify config-dir` to open the spicetify folder
2. Go to the `CustomApps` folder
3. Delete the `better-local-files` folder
Then, run the following commands:
```sh
spicetify config custom_apps better-local-files-
spicetify apply
```

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,9 @@
{
"name": "Local Files",
"icon": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"stroke-width: 2px !important;\"><path d=\"M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z\"></path></svg>",
"active-icon": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"stroke-width: 2px !important;\"><path d=\"M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z\"></path></svg>",
"subfiles": [],
"subfiles_extension": [
"extension.js"
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,156 @@
(async function() {
while (!Spicetify.React || !Spicetify.ReactDOM) {
await new Promise(resolve => setTimeout(resolve, 10));
}
"use strict";
var library = (() => {
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/extensions/collection_wrapper.tsx
var collection_wrapper_exports = {};
__export(collection_wrapper_exports, {
default: () => collection_wrapper_default
});
// node_modules/uuid/dist/esm-browser/rng.js
var getRandomValues;
var rnds8 = new Uint8Array(16);
function rng() {
if (!getRandomValues) {
getRandomValues = typeof crypto !== "undefined" && crypto.getRandomValues && crypto.getRandomValues.bind(crypto);
if (!getRandomValues) {
throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");
}
}
return getRandomValues(rnds8);
}
// node_modules/uuid/dist/esm-browser/stringify.js
var byteToHex = [];
for (let i = 0; i < 256; ++i) {
byteToHex.push((i + 256).toString(16).slice(1));
}
function unsafeStringify(arr, offset = 0) {
return byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]];
}
// node_modules/uuid/dist/esm-browser/native.js
var randomUUID = typeof crypto !== "undefined" && crypto.randomUUID && crypto.randomUUID.bind(crypto);
var native_default = {
randomUUID
};
// node_modules/uuid/dist/esm-browser/v4.js
function v4(options, buf, offset) {
if (native_default.randomUUID && !buf && !options) {
return native_default.randomUUID();
}
options = options || {};
const rnds = options.random || (options.rng || rng)();
rnds[6] = rnds[6] & 15 | 64;
rnds[8] = rnds[8] & 63 | 128;
if (buf) {
offset = offset || 0;
for (let i = 0; i < 16; ++i) {
buf[offset + i] = rnds[i];
}
return buf;
}
return unsafeStringify(rnds);
}
var v4_default = v4;
// src/extensions/collection_wrapper.tsx
var CollectionWrapper = class {
constructor() {
this.getCollections = () => {
return this._collections;
};
this.createCollection = (name) => {
const collection = {
id: v4_default(),
name,
items: []
};
this._collections.push(collection);
this.saveCollections();
Spicetify.showNotification("Collection Created");
return collection;
};
this.deleteCollection = (collectionID) => {
this._collections = this._collections.filter((collection) => collection.id !== collectionID);
this.saveCollections();
Spicetify.showNotification("Collection Deleted");
};
this.getCollection = (collectionID) => {
return this._collections.find((collection) => collection.id === collectionID);
};
this.renameCollection = (collectionID, name) => {
const collection = this.getCollection(collectionID);
if (!collection)
throw new Error("Collection is not defined");
collection.name = name;
this.saveCollections();
Spicetify.showNotification("Collection Renamed");
};
this.addToCollection = (collectionID, albumURI) => {
const collection = this.getCollection(collectionID);
if (!collection)
throw new Error("Collection is not defined");
Spicetify.GraphQL.Request(Spicetify.GraphQL.Definitions.getAlbum, {
uri: albumURI,
locale: "en",
offset: 0,
limit: 1
}).then((res) => {
var _a, _b, _c, _d, _e, _f, _g;
const data = res.data.albumUnion;
const albumItem = {
uri: data.uri,
name: data.name,
artist: (_d = (_c = (_b = (_a = data.artists) == null ? void 0 : _a.items) == null ? void 0 : _b[0]) == null ? void 0 : _c.profile) == null ? void 0 : _d.name,
image: ((_g = (_f = (_e = data.coverArt) == null ? void 0 : _e.sources) == null ? void 0 : _f[0]) == null ? void 0 : _g.url) || ""
};
collection.items.push(albumItem);
this.saveCollections();
});
Spicetify.showNotification("Item Added to Collection");
};
this.removeFromCollection = (collectionID, albumURI) => {
const collection = this.getCollection(collectionID);
if (!collection)
throw new Error("Collection is not defined");
collection.items = collection.items.filter((album) => album.uri !== albumURI);
this.saveCollections();
Spicetify.showNotification("Item Removed from Collection");
};
this.getCollectionForItem = (albumURI) => {
return this._collections.filter((collection) => collection.items.some((item) => item.uri === albumURI));
};
this.saveCollections = () => {
localStorage.setItem("library:collections", JSON.stringify(this._collections));
};
this._collections = JSON.parse(localStorage.getItem("library:collections") || "[]");
}
};
var collection_wrapper_default = CollectionWrapper;
return __toCommonJS(collection_wrapper_exports);
})();
})();

View file

@ -0,0 +1,218 @@
(async function() {
while (!Spicetify.React || !Spicetify.ReactDOM) {
await new Promise(resolve => setTimeout(resolve, 10));
}
"use strict";
var library = (() => {
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/extensions/collections_wrapper.ts
var collections_wrapper_exports = {};
__export(collections_wrapper_exports, {
default: () => collections_wrapper_default
});
// ../node_modules/uuid/dist/esm-browser/rng.js
var getRandomValues;
var rnds8 = new Uint8Array(16);
function rng() {
if (!getRandomValues) {
getRandomValues = typeof crypto !== "undefined" && crypto.getRandomValues && crypto.getRandomValues.bind(crypto);
if (!getRandomValues) {
throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");
}
}
return getRandomValues(rnds8);
}
// ../node_modules/uuid/dist/esm-browser/stringify.js
var byteToHex = [];
for (let i = 0; i < 256; ++i) {
byteToHex.push((i + 256).toString(16).slice(1));
}
function unsafeStringify(arr, offset = 0) {
return byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]];
}
// ../node_modules/uuid/dist/esm-browser/native.js
var randomUUID = typeof crypto !== "undefined" && crypto.randomUUID && crypto.randomUUID.bind(crypto);
var native_default = {
randomUUID
};
// ../node_modules/uuid/dist/esm-browser/v4.js
function v4(options, buf, offset) {
if (native_default.randomUUID && !buf && !options) {
return native_default.randomUUID();
}
options = options || {};
const rnds = options.random || (options.rng || rng)();
rnds[6] = rnds[6] & 15 | 64;
rnds[8] = rnds[8] & 63 | 128;
if (buf) {
offset = offset || 0;
for (let i = 0; i < 16; ++i) {
buf[offset + i] = rnds[i];
}
return buf;
}
return unsafeStringify(rnds);
}
var v4_default = v4;
// src/extensions/collections_wrapper.ts
var CollectionWrapper = class extends EventTarget {
_collections;
constructor() {
super();
this._collections = JSON.parse(localStorage.getItem("library:collections") || "[]");
}
saveCollections() {
localStorage.setItem("library:collections", JSON.stringify(this._collections));
this.dispatchEvent(new CustomEvent("update", { detail: this._collections }));
}
getCollection(uri) {
return this._collections.find((collection) => collection.uri === uri);
}
async requestAlbums({ sortOrder, textFilter }) {
const albums = await Spicetify.Platform.LibraryAPI.getContents({
filters: ["0"],
sortOrder,
textFilter,
offset: 0,
limit: 9999
});
return albums;
}
async getCollectionItems(props) {
const { collectionUri, textFilter, sortOrder, rootlist, limit = 9999, offset = 0 } = props;
let collectionItems = this._collections;
let albumItems = [];
let unfilteredLength = this._collections.length;
let openedCollection = "";
if (collectionUri) {
const collection = this.getCollection(collectionUri);
const res = await this.requestAlbums({ sortOrder, textFilter });
const collectionSet = new Set(collection.items);
const commonElements = res.items.filter((item) => collectionSet.has(item.uri));
const collections = this._collections.filter((collection2) => collection2.parentCollection === collectionUri);
openedCollection = collection.name;
collectionItems = collections;
albumItems = commonElements;
unfilteredLength = collection.totalLength;
}
if (textFilter) {
let regex = new RegExp("\\b" + textFilter, "i");
collectionItems = collectionItems.filter((item) => {
return regex.test(item.name);
});
}
if (rootlist && !collectionUri) {
const res = await this.requestAlbums({ sortOrder, textFilter });
albumItems = res.items;
if (!textFilter) {
const collectionSet = new Set(this._collections.map((collection) => collection.items).flat());
const uncommonElements = res.items.filter((item) => !collectionSet.has(item.uri));
collectionItems = this._collections.filter((collection) => !collection.parentCollection);
albumItems = uncommonElements;
unfilteredLength = this._collections.length + uncommonElements.length;
}
}
if (offset > 0)
collectionItems = [];
return {
openedCollection,
items: [...collectionItems, ...albumItems.slice(offset, offset + limit)],
totalLength: albumItems.length + collectionItems.length,
unfilteredLength
};
}
createCollection(name, parentCollection = "") {
const uri = v4_default();
const collection = {
type: "collection",
uri,
name,
items: [],
totalLength: 0,
imgUrl: "",
parentCollection
};
this._collections.push(collection);
this.saveCollections();
Spicetify.showNotification("Collection created");
}
deleteCollection(uri) {
this._collections = this._collections.filter((collection) => collection.uri !== uri);
this.saveCollections();
Spicetify.showNotification("Collection deleted");
}
async addAlbumToCollection(collectionUri, albumUri) {
const collection = this.getCollection(collectionUri);
if (!collection)
return;
collection.items.push(albumUri);
collection.totalLength++;
this.saveCollections();
Spicetify.showNotification("Album added to collection");
}
removeAlbumFromCollection(collectionUri, albumUri) {
const collection = this.getCollection(collectionUri);
if (!collection)
return;
collection.items = collection.items.filter((item) => item !== albumUri);
collection.totalLength--;
this.saveCollections();
Spicetify.showNotification("Album removed from collection");
}
getCollectionsWithAlbum(albumUri) {
return this._collections.filter((collection) => {
return collection.items.some((item) => item === albumUri);
});
}
renameCollection(uri, newName) {
const collection = this.getCollection(uri);
if (!collection)
return;
collection.name = newName;
this.saveCollections();
Spicetify.showNotification("Collection renamed");
}
setCollectionImage(uri, imgUrl) {
const collection = this.getCollection(uri);
if (!collection)
return;
collection.imgUrl = imgUrl;
this.saveCollections();
Spicetify.showNotification("Collection image set");
}
removeCollectionImage(uri) {
const collection = this.getCollection(uri);
if (!collection)
return;
collection.imgUrl = "";
this.saveCollections();
Spicetify.showNotification("Collection image removed");
}
};
var collections_wrapper_default = CollectionWrapper;
return __toCommonJS(collections_wrapper_exports);
})();
})();

View file

@ -0,0 +1,280 @@
(async function() {
while (!Spicetify.React || !Spicetify.ReactDOM) {
await new Promise(resolve => setTimeout(resolve, 10));
}
"use strict";
var library = (() => {
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// external-global-plugin:react
var require_react = __commonJS({
"external-global-plugin:react"(exports, module) {
module.exports = Spicetify.React;
}
});
// src/extensions/config_loader.tsx
var config_loader_exports = {};
__export(config_loader_exports, {
default: () => config_loader_default
});
// src/components/settings_modal.tsx
var import_react = __toESM(require_react());
var TextInput = (props) => {
const textId = `text-input:${props.storageKey}`;
return /* @__PURE__ */ import_react.default.createElement("label", {
className: "text-input-wrapper"
}, /* @__PURE__ */ import_react.default.createElement("input", {
className: "text-input",
type: "text",
value: props.value || "",
"data-storage-key": props.storageKey,
placeholder: props.placeholder,
id: textId,
title: `Text input for ${props.storageKey}`,
onChange: props.onChange
}));
};
var Dropdown = (props) => {
const dropdownId = `dropdown:${props.storageKey}`;
return /* @__PURE__ */ import_react.default.createElement("label", {
className: "dropdown-wrapper"
}, /* @__PURE__ */ import_react.default.createElement("select", {
className: "dropdown-input",
value: props.value,
"data-storage-key": props.storageKey,
id: dropdownId,
title: `Dropdown for ${props.storageKey}`,
onChange: props.onChange
}, props.options.map((option, index) => /* @__PURE__ */ import_react.default.createElement("option", {
key: index,
value: option
}, option))));
};
var TooltipIcon = () => {
return /* @__PURE__ */ import_react.default.createElement("svg", {
role: "img",
height: "16",
width: "16",
className: "Svg-sc-ytk21e-0 uPxdw nW1RKQOkzcJcX6aDCZB4",
viewBox: "0 0 16 16"
}, /* @__PURE__ */ import_react.default.createElement("path", {
d: "M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8z"
}), /* @__PURE__ */ import_react.default.createElement("path", {
d: "M7.25 12.026v-1.5h1.5v1.5h-1.5zm.884-7.096A1.125 1.125 0 007.06 6.39l-1.431.448a2.625 2.625 0 115.13-.784c0 .54-.156 1.015-.503 1.488-.3.408-.7.652-.973.818l-.112.068c-.185.116-.26.203-.302.283-.046.087-.097.245-.097.57h-1.5c0-.47.072-.898.274-1.277.206-.385.507-.645.827-.846l.147-.092c.285-.177.413-.257.526-.41.169-.23.213-.397.213-.602 0-.622-.503-1.125-1.125-1.125z"
}));
};
var ConfigRow = (props) => {
console.log(props);
const enabled = !!props.modalConfig[props.storageKey];
const value = props.modalConfig[props.storageKey];
const updateItem = (storageKey, state) => {
props.modalConfig[storageKey] = state;
console.debug(`toggling ${storageKey} to ${state}`);
localStorage.setItem(`library:config:${storageKey}`, String(state));
props.updateConfig(props.modalConfig);
};
const settingsToggleChange = (newValue, storageKey) => {
updateItem(storageKey, newValue);
if (props.callback)
props.callback(newValue);
};
const settingsTextChange = (event) => {
console.log("yoohoo");
updateItem(event.target.dataset.storageKey, event.target.value);
console.log(props.callback);
if (props.callback)
props.callback(event.target.value);
};
const settingsDropdownChange = (event) => {
updateItem(event.target.dataset.storageKey, event.target.value);
if (props.callback)
props.callback(event.target.value);
};
const element = () => {
switch (props.type) {
case "dropdown":
return /* @__PURE__ */ import_react.default.createElement(Dropdown, {
name: props.name,
storageKey: props.storageKey,
value,
options: props.options || [],
onChange: settingsDropdownChange
});
case "text":
return /* @__PURE__ */ import_react.default.createElement(TextInput, {
name: props.name,
storageKey: props.storageKey,
value,
placeholder: props.placeholder,
onChange: settingsTextChange
});
default:
return /* @__PURE__ */ import_react.default.createElement(Spicetify.ReactComponent.Toggle, {
id: `toggle:${props.storageKey}`,
value: enabled,
onSelected: (newValue) => {
settingsToggleChange(newValue, props.storageKey);
}
});
}
};
return /* @__PURE__ */ import_react.default.createElement("div", {
className: "setting-row"
}, /* @__PURE__ */ import_react.default.createElement("label", {
className: "col description"
}, props.name, props.desc && /* @__PURE__ */ import_react.default.createElement(Spicetify.ReactComponent.TooltipWrapper, {
label: /* @__PURE__ */ import_react.default.createElement("div", {
dangerouslySetInnerHTML: { __html: props.desc }
}),
renderInline: true,
showDelay: 10,
placement: "top",
labelClassName: "tooltip",
disabled: false
}, /* @__PURE__ */ import_react.default.createElement("div", {
className: "tooltip-icon"
}, /* @__PURE__ */ import_react.default.createElement(TooltipIcon, null)))), /* @__PURE__ */ import_react.default.createElement("div", {
className: "col action"
}, element()));
};
var SettingsModal = ({ CONFIG, settings, updateAppConfig }) => {
const [modalConfig, setModalConfig] = import_react.default.useState(__spreadValues({}, CONFIG));
const updateConfig = (CONFIG2) => {
updateAppConfig(__spreadValues({}, CONFIG2));
setModalConfig(__spreadValues({}, CONFIG2));
};
const configRows = settings.map((setting, index) => {
console.log(setting);
if (setting.sectionHeader) {
return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, index != 0 ? /* @__PURE__ */ import_react.default.createElement("br", null) : /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null), /* @__PURE__ */ import_react.default.createElement("h2", {
className: "section-header"
}, setting.sectionHeader), /* @__PURE__ */ import_react.default.createElement(ConfigRow, {
name: setting.name,
storageKey: setting.key,
type: setting.type,
options: setting.options,
placeholder: setting.placeholder,
desc: setting.desc,
modalConfig,
updateConfig,
callback: setting.callback
}));
}
return /* @__PURE__ */ import_react.default.createElement(ConfigRow, {
name: setting.name,
storageKey: setting.key,
type: setting.type,
options: setting.options,
placeholder: setting.placeholder,
desc: setting.desc,
modalConfig,
updateConfig,
callback: setting.callback
});
});
return /* @__PURE__ */ import_react.default.createElement("div", {
id: "stats-config-container"
}, configRows);
};
var settings_modal_default = SettingsModal;
// src/extensions/config_loader.tsx
var import_react2 = __toESM(require_react());
var getLocalStorageDataFromKey = (key, fallback) => {
const data = localStorage.getItem(key);
if (data) {
try {
return JSON.parse(data);
} catch (err) {
return data;
}
} else {
return fallback;
}
};
(function wait() {
const { LocalStorageAPI } = Spicetify == null ? void 0 : Spicetify.Platform;
if (!LocalStorageAPI) {
setTimeout(wait, 100);
return;
}
})();
async function loadConfig(configSettings) {
const { PopupModal } = Spicetify;
await new Promise((resolve) => {
(function checkPopupModal() {
if (PopupModal) {
resolve(void 0);
} else {
setTimeout(checkPopupModal, 100);
}
})();
});
const settingsArray = configSettings.map((setting) => {
return { [setting.key]: getLocalStorageDataFromKey(`library:config:${setting.key}`, setting.def) };
});
let CONFIG = window.CONFIG = Object.assign({}, ...settingsArray);
const updateConfig = (config) => {
window.CONFIG = __spreadValues({}, config);
console.log("updated config", config);
};
const launchModal = window.launchModal = () => {
console.log(settingsArray);
PopupModal.display({
title: "Library Settings",
content: /* @__PURE__ */ import_react2.default.createElement(settings_modal_default, {
CONFIG,
settings: configSettings,
updateAppConfig: updateConfig
}),
isLarge: true
});
};
return { CONFIG, launchModal };
}
var config_loader_default = loadConfig;
return __toCommonJS(config_loader_exports);
})();
})();

View file

@ -0,0 +1,269 @@
(async function() {
while (!Spicetify.React || !Spicetify.ReactDOM) {
await new Promise(resolve => setTimeout(resolve, 10));
}
"use strict";
var library = (() => {
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// external-global-plugin:react
var require_react = __commonJS({
"external-global-plugin:react"(exports, module) {
module.exports = Spicetify.React;
}
});
// src/extensions/config_wrapper.tsx
var config_wrapper_exports = {};
__export(config_wrapper_exports, {
default: () => config_wrapper_default
});
var import_react2 = __toESM(require_react());
// src/components/config/config_modal.tsx
var import_react = __toESM(require_react());
var TextInput = (props) => {
const handleTextChange = (event) => {
props.callback(event.target.value);
};
return /* @__PURE__ */ import_react.default.createElement("label", {
className: "text-input-wrapper"
}, /* @__PURE__ */ import_react.default.createElement("input", {
className: "text-input",
type: "text",
value: props.value || "",
"data-storage-key": props.storageKey,
placeholder: props.placeholder,
id: `text-input:${props.storageKey}`,
title: `Text input for ${props.storageKey}`,
onChange: handleTextChange
}));
};
var Dropdown = (props) => {
const handleDropdownChange = (event) => {
props.callback(event.target.value);
};
return /* @__PURE__ */ import_react.default.createElement("label", {
className: "dropdown-wrapper"
}, /* @__PURE__ */ import_react.default.createElement("select", {
className: "dropdown-input",
value: props.value,
"data-storage-key": props.storageKey,
id: `dropdown:${props.storageKey}`,
title: `Dropdown for ${props.storageKey}`,
onChange: handleDropdownChange
}, props.options.map((option, index) => /* @__PURE__ */ import_react.default.createElement("option", {
key: index,
value: option
}, option))));
};
var ToggleInput = (props) => {
const { Toggle } = Spicetify.ReactComponent;
const handleToggleChange = (newValue) => {
props.callback(newValue);
};
return /* @__PURE__ */ import_react.default.createElement(Toggle, {
id: `toggle:${props.storageKey}`,
value: props.value,
onSelected: (newValue) => handleToggleChange(newValue)
});
};
var SliderInput = (props) => {
const { Slider } = Spicetify.ReactComponent;
const handleSliderChange = (newValue) => {
const calculatedValue = props.min + newValue * (props.max - props.min);
props.callback(calculatedValue);
};
const value = (props.value - props.min) / (props.max - props.min);
return /* @__PURE__ */ import_react.default.createElement(Slider, {
id: `slider:${props.storageKey}`,
value,
min: 0,
max: 1,
step: 0.1,
onDragMove: (newValue) => handleSliderChange(newValue),
onDragStart: () => {
},
onDragEnd: () => {
}
});
};
var TooltipIcon = () => {
return /* @__PURE__ */ import_react.default.createElement("svg", {
role: "img",
height: "16",
width: "16",
className: "Svg-sc-ytk21e-0 uPxdw nW1RKQOkzcJcX6aDCZB4",
viewBox: "0 0 16 16"
}, /* @__PURE__ */ import_react.default.createElement("path", {
d: "M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8z"
}), /* @__PURE__ */ import_react.default.createElement("path", {
d: "M7.25 12.026v-1.5h1.5v1.5h-1.5zm.884-7.096A1.125 1.125 0 007.06 6.39l-1.431.448a2.625 2.625 0 115.13-.784c0 .54-.156 1.015-.503 1.488-.3.408-.7.652-.973.818l-.112.068c-.185.116-.26.203-.302.283-.046.087-.097.245-.097.57h-1.5c0-.47.072-.898.274-1.277.206-.385.507-.645.827-.846l.147-.092c.285-.177.413-.257.526-.41.169-.23.213-.397.213-.602 0-.622-.503-1.125-1.125-1.125z"
}));
};
var ConfigRow = (props) => {
return /* @__PURE__ */ import_react.default.createElement("div", {
className: "setting-row"
}, /* @__PURE__ */ import_react.default.createElement("label", {
className: "col description"
}, props.name, props.desc && /* @__PURE__ */ import_react.default.createElement(Spicetify.ReactComponent.TooltipWrapper, {
label: /* @__PURE__ */ import_react.default.createElement("div", {
dangerouslySetInnerHTML: { __html: props.desc }
}),
renderInline: true,
showDelay: 10,
placement: "top",
labelClassName: "tooltip",
disabled: false
}, /* @__PURE__ */ import_react.default.createElement("div", {
className: "tooltip-icon"
}, /* @__PURE__ */ import_react.default.createElement(TooltipIcon, null)))), /* @__PURE__ */ import_react.default.createElement("div", {
className: "col action"
}, props.children));
};
var ConfigModal = (props) => {
const { config, structure, updateAppConfig } = props;
const [modalConfig, setModalConfig] = import_react.default.useState(__spreadValues({}, config));
const modalRows = structure.map((modalRow, index) => {
const key = modalRow.key;
const currentValue = modalConfig[key];
const updateItem = (state) => {
console.debug(`toggling ${key} to ${state}`);
localStorage.setItem(`library:config:${key}`, String(state));
if (modalRow.callback)
modalRow.callback(state);
const newConfig = __spreadValues({}, modalConfig);
newConfig[key] = state;
updateAppConfig(newConfig);
setModalConfig(newConfig);
};
const header = modalRow.sectionHeader;
const element = () => {
switch (modalRow.type) {
case "toggle":
return /* @__PURE__ */ import_react.default.createElement(ToggleInput, {
storageKey: key,
value: currentValue,
callback: updateItem
});
case "text":
return /* @__PURE__ */ import_react.default.createElement(TextInput, {
storageKey: key,
value: currentValue,
callback: updateItem
});
case "dropdown":
return /* @__PURE__ */ import_react.default.createElement(Dropdown, {
storageKey: key,
value: currentValue,
options: modalRow.options,
callback: updateItem
});
case "slider":
return /* @__PURE__ */ import_react.default.createElement(SliderInput, {
storageKey: key,
value: currentValue,
min: modalRow.min,
max: modalRow.max,
step: modalRow.step,
callback: updateItem
});
}
};
return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, header && index !== 0 && /* @__PURE__ */ import_react.default.createElement("br", null), header && /* @__PURE__ */ import_react.default.createElement("h2", {
className: "section-header"
}, modalRow.sectionHeader), /* @__PURE__ */ import_react.default.createElement(ConfigRow, {
name: modalRow.name,
desc: modalRow.desc
}, element()));
});
return /* @__PURE__ */ import_react.default.createElement("div", {
id: "library-config-container"
}, modalRows);
};
var config_modal_default = ConfigModal;
// src/extensions/config_wrapper.tsx
var _ConfigWrapper = class {
constructor(modalStructure) {
const config = modalStructure.map((modalStructureRow) => {
var _a;
const value = _ConfigWrapper.getLocalStorageDataFromKey(`library:config:${modalStructureRow.key}`, modalStructureRow.def);
(_a = modalStructureRow.callback) == null ? void 0 : _a.call(modalStructureRow, value);
return { [modalStructureRow.key]: value };
});
this.Config = Object.assign({}, ...config);
this.launchModal = (callback) => {
const updateConfig = (config2) => {
this.Config = __spreadValues({}, config2);
callback == null ? void 0 : callback(config2);
};
Spicetify.PopupModal.display({
title: "Library Settings",
content: /* @__PURE__ */ import_react2.default.createElement(config_modal_default, {
config: this.Config,
structure: modalStructure,
updateAppConfig: updateConfig
}),
isLarge: true
});
};
}
};
var ConfigWrapper = _ConfigWrapper;
ConfigWrapper.getLocalStorageDataFromKey = (key, fallback) => {
const data = localStorage.getItem(key);
if (data) {
try {
return JSON.parse(data);
} catch (err) {
return data;
}
} else {
return fallback;
}
};
var config_wrapper_default = ConfigWrapper;
return __toCommonJS(config_wrapper_exports);
})();
})();

View file

@ -0,0 +1,19 @@
(async function() {
while (!Spicetify.React || !Spicetify.ReactDOM) {
await new Promise(resolve => setTimeout(resolve, 10));
}
"use strict";
var library = (() => {
// src/extensions/context_menu_handler.tsx
var observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.addedNodes.length) {
const node = mutation.addedNodes[0];
console.log(node);
}
});
});
observer.observe(document.body, { childList: true, subtree: false });
})();
})();

View file

@ -0,0 +1,926 @@
(async function() {
while (!Spicetify.React || !Spicetify.ReactDOM) {
await new Promise(resolve => setTimeout(resolve, 10));
}
"use strict";
var library = (() => {
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
// external-global-plugin:react
var require_react = __commonJS({
"external-global-plugin:react"(exports, module) {
module.exports = Spicetify.React;
}
});
// external-global-plugin:react-dom
var require_react_dom = __commonJS({
"external-global-plugin:react-dom"(exports, module) {
module.exports = Spicetify.ReactDOM;
}
});
// ../shared/config/config_wrapper.tsx
var import_react2 = __toESM(require_react());
// ../shared/config/config_modal.tsx
var import_react = __toESM(require_react());
var TextInput = (props) => {
const handleTextChange = (event) => {
props.callback(event.target.value);
};
return /* @__PURE__ */ import_react.default.createElement("label", {
className: "text-input-wrapper"
}, /* @__PURE__ */ import_react.default.createElement("input", {
className: "text-input",
type: "text",
value: props.value || "",
"data-storage-key": props.storageKey,
placeholder: props.placeholder,
id: `text-input:${props.storageKey}`,
title: `Text input for ${props.storageKey}`,
onChange: handleTextChange
}));
};
var Dropdown = (props) => {
const handleDropdownChange = (event) => {
props.callback(event.target.value);
};
return /* @__PURE__ */ import_react.default.createElement("label", {
className: "dropdown-wrapper"
}, /* @__PURE__ */ import_react.default.createElement("select", {
className: "dropdown-input",
value: props.value,
"data-storage-key": props.storageKey,
id: `dropdown:${props.storageKey}`,
title: `Dropdown for ${props.storageKey}`,
onChange: handleDropdownChange
}, props.options.map((option, index) => /* @__PURE__ */ import_react.default.createElement("option", {
key: index,
value: option
}, option))));
};
var ToggleInput = (props) => {
const { Toggle } = Spicetify.ReactComponent;
const handleToggleChange = (newValue) => {
props.callback(newValue);
};
return /* @__PURE__ */ import_react.default.createElement(Toggle, {
id: `toggle:${props.storageKey}`,
value: props.value,
onSelected: (newValue) => handleToggleChange(newValue)
});
};
var SliderInput = (props) => {
const { Slider } = Spicetify.ReactComponent;
const handleSliderChange = (newValue) => {
const calculatedValue = props.min + newValue * (props.max - props.min);
props.callback(calculatedValue);
};
const value = (props.value - props.min) / (props.max - props.min);
return /* @__PURE__ */ import_react.default.createElement(Slider, {
id: `slider:${props.storageKey}`,
value,
min: 0,
max: 1,
step: 0.1,
onDragMove: (newValue) => handleSliderChange(newValue),
onDragStart: () => {
},
onDragEnd: () => {
}
});
};
var TooltipIcon = () => {
return /* @__PURE__ */ import_react.default.createElement("svg", {
role: "img",
height: "16",
width: "16",
className: "Svg-sc-ytk21e-0 uPxdw nW1RKQOkzcJcX6aDCZB4",
viewBox: "0 0 16 16"
}, /* @__PURE__ */ import_react.default.createElement("path", {
d: "M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8z"
}), /* @__PURE__ */ import_react.default.createElement("path", {
d: "M7.25 12.026v-1.5h1.5v1.5h-1.5zm.884-7.096A1.125 1.125 0 007.06 6.39l-1.431.448a2.625 2.625 0 115.13-.784c0 .54-.156 1.015-.503 1.488-.3.408-.7.652-.973.818l-.112.068c-.185.116-.26.203-.302.283-.046.087-.097.245-.097.57h-1.5c0-.47.072-.898.274-1.277.206-.385.507-.645.827-.846l.147-.092c.285-.177.413-.257.526-.41.169-.23.213-.397.213-.602 0-.622-.503-1.125-1.125-1.125z"
}));
};
var ConfigRow = (props) => {
return /* @__PURE__ */ import_react.default.createElement("div", {
className: "setting-row"
}, /* @__PURE__ */ import_react.default.createElement("label", {
className: "col description"
}, props.name, props.desc && /* @__PURE__ */ import_react.default.createElement(Spicetify.ReactComponent.TooltipWrapper, {
label: /* @__PURE__ */ import_react.default.createElement("div", {
dangerouslySetInnerHTML: { __html: props.desc }
}),
renderInline: true,
showDelay: 10,
placement: "top",
labelClassName: "tooltip",
disabled: false
}, /* @__PURE__ */ import_react.default.createElement("div", {
className: "tooltip-icon"
}, /* @__PURE__ */ import_react.default.createElement(TooltipIcon, null)))), /* @__PURE__ */ import_react.default.createElement("div", {
className: "col action"
}, props.children));
};
var ConfigModal = (props) => {
const { config, structure, appKey, updateAppConfig } = props;
const [modalConfig, setModalConfig] = import_react.default.useState({ ...config });
const modalRows = structure.map((modalRow, index) => {
const key = modalRow.key;
const currentValue = modalConfig[key];
const updateItem = (state) => {
console.debug(`toggling ${key} to ${state}`);
localStorage.setItem(`${appKey}:config:${key}`, String(state));
if (modalRow.callback)
modalRow.callback(state);
const newConfig = { ...modalConfig };
newConfig[key] = state;
updateAppConfig(newConfig);
setModalConfig(newConfig);
};
const header = modalRow.sectionHeader;
const element = () => {
switch (modalRow.type) {
case "toggle":
return /* @__PURE__ */ import_react.default.createElement(ToggleInput, {
storageKey: key,
value: currentValue,
callback: updateItem
});
case "text":
return /* @__PURE__ */ import_react.default.createElement(TextInput, {
storageKey: key,
value: currentValue,
callback: updateItem
});
case "dropdown":
return /* @__PURE__ */ import_react.default.createElement(Dropdown, {
storageKey: key,
value: currentValue,
options: modalRow.options,
callback: updateItem
});
case "slider":
return /* @__PURE__ */ import_react.default.createElement(SliderInput, {
storageKey: key,
value: currentValue,
min: modalRow.min,
max: modalRow.max,
step: modalRow.step,
callback: updateItem
});
}
};
return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, header && index !== 0 && /* @__PURE__ */ import_react.default.createElement("br", null), header && /* @__PURE__ */ import_react.default.createElement("h2", {
className: "section-header"
}, modalRow.sectionHeader), /* @__PURE__ */ import_react.default.createElement(ConfigRow, {
name: modalRow.name,
desc: modalRow.desc
}, element()));
});
return /* @__PURE__ */ import_react.default.createElement("div", {
className: "config-container"
}, modalRows);
};
var config_modal_default = ConfigModal;
// ../shared/config/config_wrapper.tsx
var _ConfigWrapper = class {
Config;
launchModal;
constructor(modalStructure, key) {
const config = modalStructure.map((modalStructureRow) => {
const value = _ConfigWrapper.getLocalStorageDataFromKey(
`${key}:config:${modalStructureRow.key}`,
modalStructureRow.def
);
modalStructureRow.callback?.(value);
return { [modalStructureRow.key]: value };
});
this.Config = Object.assign({}, ...config);
this.launchModal = (callback) => {
const updateConfig = (config2) => {
this.Config = { ...config2 };
callback?.(config2);
};
Spicetify.PopupModal.display({
title: `${key.charAt(0).toUpperCase() + key.slice(1)} Settings`,
content: /* @__PURE__ */ import_react2.default.createElement(config_modal_default, {
config: this.Config,
structure: modalStructure,
appKey: key,
updateAppConfig: updateConfig
}),
isLarge: true
});
};
}
};
var ConfigWrapper = _ConfigWrapper;
__publicField(ConfigWrapper, "getLocalStorageDataFromKey", (key, fallback) => {
const data = localStorage.getItem(key);
if (data) {
try {
return JSON.parse(data);
} catch (err) {
return data;
}
} else {
return fallback;
}
});
var config_wrapper_default = ConfigWrapper;
// src/extensions/extension.tsx
var import_react10 = __toESM(require_react());
var import_react_dom = __toESM(require_react_dom());
// src/components/toggle_filters.tsx
var import_react3 = __toESM(require_react());
var UpIcon = () => {
const { IconComponent } = Spicetify.ReactComponent;
return /* @__PURE__ */ import_react3.default.createElement(IconComponent, {
semanticColor: "textSubdued",
dangerouslySetInnerHTML: {
__html: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M.998 8.81A.749.749 0 0 1 .47 7.53L7.99 0l7.522 7.53a.75.75 0 1 1-1.06 1.06L8.74 2.87v12.38a.75.75 0 1 1-1.498 0V2.87L1.528 8.59a.751.751 0 0 1-.53.22z"></path></svg>'
},
iconSize: 16
});
};
var DownIcon = () => {
const { IconComponent } = Spicetify.ReactComponent;
return /* @__PURE__ */ import_react3.default.createElement(IconComponent, {
semanticColor: "textSubdued",
dangerouslySetInnerHTML: {
__html: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M.998 7.19A.749.749 0 0 0 .47 8.47L7.99 16l7.522-7.53a.75.75 0 1 0-1.06-1.06L8.74 13.13V.75a.75.75 0 1 0-1.498 0v12.38L1.528 7.41a.749.749 0 0 0-.53-.22z"></path></svg>'
},
iconSize: 16
});
};
var ToggleFiltersButton = () => {
const [direction, setDirection] = import_react3.default.useState(document.body.classList.contains("show-ylx-filters") ? "up" : "down");
const { ButtonTertiary } = Spicetify.ReactComponent;
const toggleDirection = () => {
if (direction === "down") {
document.body.classList.add("show-ylx-filters");
setDirection("up");
} else {
setDirection("down");
document.body.classList.remove("show-ylx-filters");
}
};
const Icon = direction === "down" ? DownIcon : UpIcon;
return /* @__PURE__ */ import_react3.default.createElement(ButtonTertiary, {
buttonSize: "sm",
"aria-label": "Show Filters",
iconOnly: Icon,
onClick: toggleDirection
});
};
var toggle_filters_default = ToggleFiltersButton;
// src/components/collapse_button.tsx
var import_react4 = __toESM(require_react());
var collapseLibrary = () => {
Spicetify.Platform.LocalStorageAPI.setItem("ylx-sidebar-state", 1);
};
var CollapseIcon = () => {
const { IconComponent } = Spicetify.ReactComponent;
return /* @__PURE__ */ import_react4.default.createElement(IconComponent, {
semanticColor: "textSubdued",
dangerouslySetInnerHTML: {
__html: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M8.81 1A.749.749 0 0 0 7.53.47L0 7.99l7.53 7.521a.75.75 0 0 0 1.234-.815.75.75 0 0 0-.174-.243L2.87 8.74h12.38a.75.75 0 1 0 0-1.498H2.87l5.72-5.713c.14-.14.22-.331.22-.53z"></path></svg>'
},
iconSize: 16
});
};
var CollapseButton = () => {
const { ButtonTertiary } = Spicetify.ReactComponent;
return /* @__PURE__ */ import_react4.default.createElement(ButtonTertiary, {
buttonSize: "sm",
"aria-label": "Show Filters",
iconOnly: CollapseIcon,
onClick: collapseLibrary
});
};
var collapse_button_default = CollapseButton;
// src/components/expand_button.tsx
var import_react5 = __toESM(require_react());
var expandLibrary = () => {
Spicetify.Platform.LocalStorageAPI.setItem("ylx-sidebar-state", 0);
};
var ExpandIcon = () => {
const { IconComponent } = Spicetify.ReactComponent;
return /* @__PURE__ */ import_react5.default.createElement(IconComponent, {
semanticColor: "textSubdued",
dangerouslySetInnerHTML: {
__html: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" ><path d="M7.19 1A.749.749 0 0 1 8.47.47L16 7.99l-7.53 7.521a.75.75 0 0 1-1.234-.815.75.75 0 0 1 .174-.243l5.72-5.714H.75a.75.75 0 1 1 0-1.498h12.38L7.41 1.529a.749.749 0 0 1-.22-.53z"></path></svg>'
},
iconSize: 16
});
};
var ExpandButton = () => {
const { ButtonTertiary } = Spicetify.ReactComponent;
return /* @__PURE__ */ import_react5.default.createElement(ButtonTertiary, {
buttonSize: "sm",
"aria-label": "Show Filters",
iconOnly: ExpandIcon,
onClick: expandLibrary
});
};
var expand_button_default = ExpandButton;
// ../node_modules/uuid/dist/esm-browser/rng.js
var getRandomValues;
var rnds8 = new Uint8Array(16);
function rng() {
if (!getRandomValues) {
getRandomValues = typeof crypto !== "undefined" && crypto.getRandomValues && crypto.getRandomValues.bind(crypto);
if (!getRandomValues) {
throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");
}
}
return getRandomValues(rnds8);
}
// ../node_modules/uuid/dist/esm-browser/stringify.js
var byteToHex = [];
for (let i = 0; i < 256; ++i) {
byteToHex.push((i + 256).toString(16).slice(1));
}
function unsafeStringify(arr, offset = 0) {
return byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]];
}
// ../node_modules/uuid/dist/esm-browser/native.js
var randomUUID = typeof crypto !== "undefined" && crypto.randomUUID && crypto.randomUUID.bind(crypto);
var native_default = {
randomUUID
};
// ../node_modules/uuid/dist/esm-browser/v4.js
function v4(options, buf, offset) {
if (native_default.randomUUID && !buf && !options) {
return native_default.randomUUID();
}
options = options || {};
const rnds = options.random || (options.rng || rng)();
rnds[6] = rnds[6] & 15 | 64;
rnds[8] = rnds[8] & 63 | 128;
if (buf) {
offset = offset || 0;
for (let i = 0; i < 16; ++i) {
buf[offset + i] = rnds[i];
}
return buf;
}
return unsafeStringify(rnds);
}
var v4_default = v4;
// src/extensions/collections_wrapper.ts
var CollectionWrapper = class extends EventTarget {
_collections;
constructor() {
super();
this._collections = JSON.parse(localStorage.getItem("library:collections") || "[]");
}
saveCollections() {
localStorage.setItem("library:collections", JSON.stringify(this._collections));
this.dispatchEvent(new CustomEvent("update", { detail: this._collections }));
}
getCollection(uri) {
return this._collections.find((collection) => collection.uri === uri);
}
async requestAlbums({ sortOrder, textFilter }) {
const albums = await Spicetify.Platform.LibraryAPI.getContents({
filters: ["0"],
sortOrder,
textFilter,
offset: 0,
limit: 9999
});
return albums;
}
async getCollectionItems(props) {
const { collectionUri, textFilter, sortOrder, rootlist, limit = 9999, offset = 0 } = props;
let collectionItems = this._collections;
let albumItems = [];
let unfilteredLength = this._collections.length;
let openedCollection = "";
if (collectionUri) {
const collection = this.getCollection(collectionUri);
const res = await this.requestAlbums({ sortOrder, textFilter });
const collectionSet = new Set(collection.items);
const commonElements = res.items.filter((item) => collectionSet.has(item.uri));
const collections = this._collections.filter((collection2) => collection2.parentCollection === collectionUri);
openedCollection = collection.name;
collectionItems = collections;
albumItems = commonElements;
unfilteredLength = collection.totalLength;
}
if (textFilter) {
let regex = new RegExp("\\b" + textFilter, "i");
collectionItems = collectionItems.filter((item) => {
return regex.test(item.name);
});
}
if (rootlist && !collectionUri) {
const res = await this.requestAlbums({ sortOrder, textFilter });
albumItems = res.items;
if (!textFilter) {
const collectionSet = new Set(this._collections.map((collection) => collection.items).flat());
const uncommonElements = res.items.filter((item) => !collectionSet.has(item.uri));
collectionItems = this._collections.filter((collection) => !collection.parentCollection);
albumItems = uncommonElements;
unfilteredLength = this._collections.length + uncommonElements.length;
}
}
if (offset > 0)
collectionItems = [];
return {
openedCollection,
items: [...collectionItems, ...albumItems.slice(offset, offset + limit)],
totalLength: albumItems.length + collectionItems.length,
unfilteredLength
};
}
createCollection(name, parentCollection = "") {
const uri = v4_default();
const collection = {
type: "collection",
uri,
name,
items: [],
totalLength: 0,
imgUrl: "",
parentCollection
};
this._collections.push(collection);
this.saveCollections();
Spicetify.showNotification("Collection created");
}
deleteCollection(uri) {
this._collections = this._collections.filter((collection) => collection.uri !== uri);
this.saveCollections();
Spicetify.showNotification("Collection deleted");
}
async addAlbumToCollection(collectionUri, albumUri) {
const collection = this.getCollection(collectionUri);
if (!collection)
return;
collection.items.push(albumUri);
collection.totalLength++;
this.saveCollections();
Spicetify.showNotification("Album added to collection");
}
removeAlbumFromCollection(collectionUri, albumUri) {
const collection = this.getCollection(collectionUri);
if (!collection)
return;
collection.items = collection.items.filter((item) => item !== albumUri);
collection.totalLength--;
this.saveCollections();
Spicetify.showNotification("Album removed from collection");
}
getCollectionsWithAlbum(albumUri) {
return this._collections.filter((collection) => {
return collection.items.some((item) => item === albumUri);
});
}
renameCollection(uri, newName) {
const collection = this.getCollection(uri);
if (!collection)
return;
collection.name = newName;
this.saveCollections();
Spicetify.showNotification("Collection renamed");
}
setCollectionImage(uri, imgUrl) {
const collection = this.getCollection(uri);
if (!collection)
return;
collection.imgUrl = imgUrl;
this.saveCollections();
Spicetify.showNotification("Collection image set");
}
removeCollectionImage(uri) {
const collection = this.getCollection(uri);
if (!collection)
return;
collection.imgUrl = "";
this.saveCollections();
Spicetify.showNotification("Collection image removed");
}
};
var collections_wrapper_default = CollectionWrapper;
// src/components/album_menu_item.tsx
var import_react9 = __toESM(require_react());
// src/components/leading_icon.tsx
var import_react6 = __toESM(require_react());
var LeadingIcon = ({ path }) => {
return /* @__PURE__ */ import_react6.default.createElement(Spicetify.ReactComponent.IconComponent, {
semanticColor: "textSubdued",
dangerouslySetInnerHTML: {
__html: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">${path}</svg>`
},
iconSize: 16
});
};
var leading_icon_default = LeadingIcon;
// src/components/text_input_dialog.tsx
var import_react7 = __toESM(require_react());
var TextInputDialog = (props) => {
const { ButtonPrimary } = Spicetify.ReactComponent;
const { def, placeholder, onSave } = props;
const [value, setValue] = import_react7.default.useState(def);
const onSubmit = (e) => {
e.preventDefault();
Spicetify.PopupModal.hide();
onSave(value);
};
return /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, /* @__PURE__ */ import_react7.default.createElement("form", {
className: "text-input-form",
onSubmit
}, /* @__PURE__ */ import_react7.default.createElement("label", {
className: "text-input-wrapper"
}, /* @__PURE__ */ import_react7.default.createElement("input", {
className: "text-input",
type: "text",
value,
placeholder,
onChange: (e) => setValue(e.target.value)
})), /* @__PURE__ */ import_react7.default.createElement("button", {
type: "submit",
"data-encore-id": "buttonPrimary",
className: "Button-sc-qlcn5g-0 Button-small-buttonPrimary"
}, /* @__PURE__ */ import_react7.default.createElement("span", {
className: "ButtonInner-sc-14ud5tc-0 ButtonInner-small encore-bright-accent-set"
}, "Save"))));
};
var text_input_dialog_default = TextInputDialog;
// src/components/searchbar.tsx
var import_react8 = __toESM(require_react());
var SearchBar = (props) => {
const { setSearch, placeholder } = props;
const handleChange = (e) => {
setSearch(e.target.value);
};
return /* @__PURE__ */ import_react8.default.createElement("div", {
className: "x-filterBox-filterInputContainer x-filterBox-expandedOrHasFilter",
role: "search"
}, /* @__PURE__ */ import_react8.default.createElement("input", {
type: "text",
className: "x-filterBox-filterInput",
role: "searchbox",
maxLength: 80,
autoCorrect: "off",
autoCapitalize: "off",
spellCheck: "false",
placeholder: `Search ${placeholder}`,
"aria-hidden": "false",
onChange: handleChange
}), /* @__PURE__ */ import_react8.default.createElement("div", {
className: "x-filterBox-overlay"
}, /* @__PURE__ */ import_react8.default.createElement("span", {
className: "x-filterBox-searchIconContainer"
}, /* @__PURE__ */ import_react8.default.createElement("svg", {
"data-encore-id": "icon",
role: "img",
"aria-hidden": "true",
className: "Svg-sc-ytk21e-0 Svg-img-icon-small x-filterBox-searchIcon",
viewBox: "0 0 16 16"
}, /* @__PURE__ */ import_react8.default.createElement("path", {
d: "M7 1.75a5.25 5.25 0 1 0 0 10.5 5.25 5.25 0 0 0 0-10.5zM.25 7a6.75 6.75 0 1 1 12.096 4.12l3.184 3.185a.75.75 0 1 1-1.06 1.06L11.304 12.2A6.75 6.75 0 0 1 .25 7z"
})))), /* @__PURE__ */ import_react8.default.createElement("button", {
className: "x-filterBox-expandButton",
"aria-hidden": "false",
"aria-label": "Search Playlists"
}, /* @__PURE__ */ import_react8.default.createElement("svg", {
"data-encore-id": "icon",
role: "img",
"aria-hidden": "true",
className: "Svg-sc-ytk21e-0 Svg-img-icon-small x-filterBox-searchIcon",
viewBox: "0 0 16 16"
}, /* @__PURE__ */ import_react8.default.createElement("path", {
d: "M7 1.75a5.25 5.25 0 1 0 0 10.5 5.25 5.25 0 0 0 0-10.5zM.25 7a6.75 6.75 0 1 1 12.096 4.12l3.184 3.185a.75.75 0 1 1-1.06 1.06L11.304 12.2A6.75 6.75 0 0 1 .25 7z"
}))));
};
var searchbar_default = SearchBar;
// src/components/album_menu_item.tsx
var createCollection = () => {
const onSave = (value) => {
SpicetifyLibrary.CollectionWrapper.createCollection(value);
};
Spicetify.PopupModal.display({
title: "Create Collection",
content: /* @__PURE__ */ import_react9.default.createElement(text_input_dialog_default, {
def: "New Collection",
placeholder: "Collection Name",
onSave
})
});
};
var CollectionSearchMenu = () => {
const { MenuItem } = Spicetify.ReactComponent;
const { SVGIcons } = Spicetify;
const [textFilter, setTextFilter] = import_react9.default.useState("");
const [collections, setCollections] = import_react9.default.useState(null);
const context = import_react9.default.useContext(Spicetify.ContextMenuV2._context);
const uri = context?.props?.uri;
import_react9.default.useEffect(() => {
const fetchCollections = async () => {
const res = await SpicetifyLibrary.CollectionWrapper.getCollectionItems({ textFilter });
setCollections(res);
};
fetchCollections();
}, [textFilter]);
if (!collections)
return /* @__PURE__ */ import_react9.default.createElement(import_react9.default.Fragment, null);
const addToCollection = (collectionUri) => {
SpicetifyLibrary.CollectionWrapper.addAlbumToCollection(collectionUri, uri);
};
const activeCollections = SpicetifyLibrary.CollectionWrapper.getCollectionsWithAlbum(uri);
const hasCollections = activeCollections.length > 0;
const removeFromCollections = () => {
activeCollections.forEach((collection) => {
SpicetifyLibrary.CollectionWrapper.removeAlbumFromCollection(collection.uri, uri);
});
};
const allCollectionsLength = collections.unfilteredLength;
const menuItems = collections.items.map((collection, index) => {
return /* @__PURE__ */ import_react9.default.createElement(MenuItem, {
key: collection.uri,
onClick: () => {
addToCollection(collection.uri);
},
divider: index === 0 ? "before" : void 0
}, collection.name);
});
const menuLength = allCollectionsLength + (hasCollections ? 1 : 0);
return /* @__PURE__ */ import_react9.default.createElement("div", {
className: "main-contextMenu-filterPlaylistSearchContainer",
style: { "--context-menu-submenu-length": `${menuLength}` }
}, /* @__PURE__ */ import_react9.default.createElement("li", {
role: "presentation",
className: "main-contextMenu-filterPlaylistSearch"
}, /* @__PURE__ */ import_react9.default.createElement("div", {
role: "menuitem"
}, /* @__PURE__ */ import_react9.default.createElement(searchbar_default, {
setSearch: setTextFilter,
placeholder: "collections"
}))), /* @__PURE__ */ import_react9.default.createElement(MenuItem, {
key: "new-collection",
leadingIcon: /* @__PURE__ */ import_react9.default.createElement(leading_icon_default, {
path: SVGIcons["plus2px"]
}),
onClick: createCollection
}, "Create collection"), hasCollections && /* @__PURE__ */ import_react9.default.createElement(MenuItem, {
key: "remove-collection",
leadingIcon: /* @__PURE__ */ import_react9.default.createElement(leading_icon_default, {
path: SVGIcons["minus"]
}),
onClick: removeFromCollections
}, "Remove from all"), menuItems);
};
var AlbumMenuItem = () => {
const { MenuSubMenuItem } = Spicetify.ReactComponent;
const { SVGIcons } = Spicetify;
return /* @__PURE__ */ import_react9.default.createElement(MenuSubMenuItem, {
displayText: "Add to collection",
divider: "after",
leadingIcon: /* @__PURE__ */ import_react9.default.createElement(leading_icon_default, {
path: SVGIcons["plus2px"]
})
}, /* @__PURE__ */ import_react9.default.createElement(CollectionSearchMenu, null));
};
var album_menu_item_default = AlbumMenuItem;
// src/extensions/folder_image_wrapper.ts
var FolderImageWrapper = class extends EventTarget {
_folderImages;
constructor() {
super();
this._folderImages = JSON.parse(localStorage.getItem("library:folderImages") || "{}");
}
getFolderImage(uri) {
return this._folderImages[uri];
}
getFolderImages() {
return this._folderImages;
}
setFolderImage({ uri, url }) {
this._folderImages[uri] = url;
this.saveFolderImages();
Spicetify.showNotification("Folder image updated");
}
removeFolderImage(uri) {
delete this._folderImages[uri];
this.saveFolderImages();
Spicetify.showNotification("Folder image removed");
}
saveFolderImages() {
this.dispatchEvent(new CustomEvent("update", { detail: this._folderImages }));
localStorage.setItem("library:folderImages", JSON.stringify(this._folderImages));
}
};
var folder_image_wrapper_default = FolderImageWrapper;
// src/extensions/extension.tsx
var styleLink = document.createElement("link");
styleLink.rel = "stylesheet";
styleLink.href = "/spicetify-routes-library.css";
document.head.appendChild(styleLink);
var setCardSize = (size) => {
document.documentElement.style.setProperty("--library-card-size", `${size}px`);
};
var setSearchBarSize = (enlarged) => {
const size = enlarged ? 300 : 200;
document.documentElement.style.setProperty("--library-searchbar-size", `${size}px`);
};
var FolderImage = ({ url }) => {
return /* @__PURE__ */ import_react10.default.createElement("img", {
"aria-hidden": "true",
draggable: "false",
loading: "eager",
src: url,
className: "main-image-image x-entityImage-image main-image-loading main-image-loaded"
});
};
var FolderPlaceholder = () => {
return /* @__PURE__ */ import_react10.default.createElement("div", {
className: "x-entityImage-imagePlaceholder"
}, /* @__PURE__ */ import_react10.default.createElement("svg", {
"data-encore-id": "icon",
role: "img",
"aria-hidden": "true",
className: "Svg-sc-ytk21e-0 Svg-img-icon-medium",
viewBox: "0 0 24 24"
}, /* @__PURE__ */ import_react10.default.createElement("path", {
d: "M1 4a2 2 0 0 1 2-2h5.155a3 3 0 0 1 2.598 1.5l.866 1.5H21a2 2 0 0 1 2 2v13a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V4zm7.155 0H3v16h18V7H10.464L9.021 4.5a1 1 0 0 0-.866-.5z"
})));
};
var SpicetifyLibrary2 = class {
ConfigWrapper = new config_wrapper_default(
[
{
name: "Card Size",
key: "cardSize",
type: "slider",
min: 100,
max: 200,
step: 0.05,
def: 180,
callback: setCardSize
},
{
name: "Extend Search Bar",
key: "extendSearchBar",
type: "toggle",
def: false,
callback: setSearchBarSize
}
],
"library"
);
CollectionWrapper = new collections_wrapper_default();
FolderImageWrapper = new folder_image_wrapper_default();
};
window.SpicetifyLibrary = new SpicetifyLibrary2();
(function wait() {
const { LocalStorageAPI } = Spicetify?.Platform;
if (!LocalStorageAPI) {
setTimeout(wait, 100);
return;
}
main(LocalStorageAPI);
})();
function main(LocalStorageAPI) {
const isAlbum = (props) => {
return props.uri?.includes("album");
};
Spicetify.ContextMenuV2.registerItem(/* @__PURE__ */ import_react10.default.createElement(album_menu_item_default, null), isAlbum);
function injectFolderImages() {
const rootlist = document.querySelector(".main-rootlist-wrapper > div:nth-child(2)");
if (!rootlist)
return setTimeout(injectFolderImages, 100);
setTimeout(() => {
Array.from(rootlist.children).forEach((el) => {
const uri = el.querySelector(".main-yourLibraryX-listItemGroup")?.getAttribute("aria-labelledby")?.slice(14);
if (uri?.includes("folder")) {
const imageBox = el.querySelector(".x-entityImage-imageContainer");
if (!imageBox)
return;
const imageUrl = window.SpicetifyLibrary.FolderImageWrapper.getFolderImage(uri);
if (!imageUrl)
import_react_dom.default.render(/* @__PURE__ */ import_react10.default.createElement(FolderPlaceholder, null), imageBox);
else
import_react_dom.default.render(/* @__PURE__ */ import_react10.default.createElement(FolderImage, {
url: imageUrl
}), imageBox);
}
});
}, 500);
}
injectFolderImages();
window.SpicetifyLibrary.FolderImageWrapper.addEventListener("update", () => {
injectFolderImages();
});
function injectYLXButtons() {
const ylx_filter = document.querySelector(
".main-yourLibraryX-libraryRootlist > .main-yourLibraryX-libraryFilter"
);
if (!ylx_filter) {
return setTimeout(injectYLXButtons, 100);
}
injectFiltersButton(ylx_filter);
injectCollapseButton(ylx_filter);
}
function injectFiltersButton(ylx_filter) {
const toggleFiltersButton = document.createElement("span");
toggleFiltersButton.classList.add("toggle-filters-button");
ylx_filter.appendChild(toggleFiltersButton);
import_react_dom.default.render(/* @__PURE__ */ import_react10.default.createElement(toggle_filters_default, null), toggleFiltersButton);
}
function injectCollapseButton(ylx_filter) {
const collapseButton = document.createElement("span");
collapseButton.classList.add("collapse-button");
ylx_filter.appendChild(collapseButton);
import_react_dom.default.render(
/* @__PURE__ */ import_react10.default.createElement(Spicetify.ReactComponent.TooltipWrapper, {
label: "Collapse Sidebar",
placement: "top"
}, /* @__PURE__ */ import_react10.default.createElement(collapse_button_default, null)),
collapseButton
);
}
function injectExpandButton() {
const sidebarHeader = document.querySelector("li.main-yourLibraryX-navItem[data-id='/library']");
if (!sidebarHeader) {
return setTimeout(injectExpandButton, 100);
}
const expandButton = document.createElement("span");
expandButton.classList.add("expand-button");
sidebarHeader.appendChild(expandButton);
import_react_dom.default.render(/* @__PURE__ */ import_react10.default.createElement(expand_button_default, null), expandButton);
}
function removeExpandButton() {
const expandButton = document.querySelector(".expand-button");
if (expandButton)
expandButton.remove();
}
const state = LocalStorageAPI.getItem("ylx-sidebar-state");
if (state === 0) {
injectYLXButtons();
} else if (state === 1) {
injectExpandButton();
}
LocalStorageAPI.getEvents()._emitter.addListener("update", (e) => {
const { key, value } = e.data;
if (key === "ylx-sidebar-state" && value === 0) {
injectFolderImages();
injectYLXButtons();
removeExpandButton();
}
if (key === "ylx-sidebar-state" && value === 1) {
injectFolderImages();
injectExpandButton();
}
});
}
})();
})();

View file

@ -0,0 +1,61 @@
(async function() {
while (!Spicetify.React || !Spicetify.ReactDOM) {
await new Promise(resolve => setTimeout(resolve, 10));
}
"use strict";
var library = (() => {
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/extensions/folder_image_wrapper.ts
var folder_image_wrapper_exports = {};
__export(folder_image_wrapper_exports, {
default: () => folder_image_wrapper_default
});
var FolderImageWrapper = class extends EventTarget {
_folderImages;
constructor() {
super();
this._folderImages = JSON.parse(localStorage.getItem("library:folderImages") || "{}");
}
getFolderImage(uri) {
return this._folderImages[uri];
}
getFolderImages() {
return this._folderImages;
}
setFolderImage({ uri, url }) {
this._folderImages[uri] = url;
this.saveFolderImages();
Spicetify.showNotification("Folder image updated");
}
removeFolderImage(uri) {
delete this._folderImages[uri];
this.saveFolderImages();
Spicetify.showNotification("Folder image removed");
}
saveFolderImages() {
this.dispatchEvent(new CustomEvent("update", { detail: this._folderImages }));
localStorage.setItem("library:folderImages", JSON.stringify(this._folderImages));
}
};
var folder_image_wrapper_default = FolderImageWrapper;
return __toCommonJS(folder_image_wrapper_exports);
})();
})();

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,11 @@
{
"name": "Your Library",
"icon": "<svg height=\"24\" width=\"24\" viewBox=\"0 0 24 24\">\r\n<path d=\"M14.5 2.134a1 1 0 0 1 1 0l6 3.464a1 1 0 0 1 .5.866V21a1 1 0 0 1-1 1h-6a1 1 0 0 1-1-1V3a1 1 0 0 1 .5-.866zM16 4.732V20h4V7.041l-4-2.309zM3 22a1 1 0 0 1-1-1V3a1 1 0 0 1 2 0v18a1 1 0 0 1-1 1zm6 0a1 1 0 0 1-1-1V3a1 1 0 0 1 2 0v18a1 1 0 0 1-1 1z\"></path>\r\n</svg>\r\n",
"active-icon": "<svg height=\"24\" width=\"24\" viewBox=\"0 0 24 24\">\r\n<path d=\"M3 22a1 1 0 0 1-1-1V3a1 1 0 0 1 2 0v18a1 1 0 0 1-1 1zM15.5 2.134A1 1 0 0 0 14 3v18a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V6.464a1 1 0 0 0-.5-.866l-6-3.464zM9 2a1 1 0 0 0-1 1v18a1 1 0 1 0 2 0V3a1 1 0 0 0-1-1z\"></path>\r\n</svg>",
"subfiles": [],
"subfiles_extension": [
"collections_wrapper.js",
"extension.js",
"folder_image_wrapper.js"
]
}

View file

@ -0,0 +1,356 @@
/* ../../../AppData/Local/Temp/tmp-5152-1Ex7Bvh4Tu6u/18f3cb8bc8b4/navBar.module.css */
.navBar-module__topBarHeaderItem___piw4C_library {
-webkit-app-region: no-drag;
display: inline-block;
pointer-events: auto;
}
.navBar-module__topBarHeaderItemLink___xA4uv_library {
margin: 0 8px 0 0;
}
.navBar-module__topBarActive___XhWpm_library {
background-color: var(--spice-tab-active);
border-radius: 4px;
}
.navBar-module__topBarHeaderItemLink___xA4uv_library {
border-radius: 4px;
color: var(--spice-text);
display: inline-block;
margin: 0 8px;
padding: 8px 16px;
position: relative;
text-decoration: none !important;
cursor: pointer;
}
.navBar-module__topBarNav___qWGeZ_library {
-webkit-app-region: drag;
pointer-events: none;
width: 100%;
}
.navBar-module__topBarHeaderItem___piw4C_library .navBar-module__optionsMenuDropBox___pzfNI_library {
color: var(--spice-text);
border: 0;
max-width: 150px;
height: 42px;
padding: 0 30px 0 12px;
background-color: initial;
cursor: pointer;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
.navBar-module__topBarHeaderItem___piw4C_library .navBar-module__optionsMenuDropBox___pzfNI_library svg {
position: absolute;
margin-left: 8px;
}
div.navBar-module__topBarHeaderItemLink___xA4uv_library {
padding: 0;
}
/* ../../../AppData/Local/Temp/tmp-5152-1Ex7Bvh4Tu6u/18f3cb8baff0/app.css */
:root {
--library-card-size: 180px;
--library-searchbar-size: 200px;
}
#library-app .header-right .x-filterBox-expandedOrHasFilter .x-filterBox-filterInput {
width: var(--library-searchbar-size);
}
#library-app .grid {
grid-template-columns: repeat(auto-fill, minmax(var(--library-card-size), 1fr)) !important;
}
#library-app .load-more-card {
display: flex;
gap: 10px;
flex-direction: column;
justify-content: center;
}
#library-app .load-more-card div:nth-child(2) {
text-align: center;
font-size: var(--encore-text-size-base);
}
#library-app .load-more-card div:first-child {
fill: var(--text-subdued);
width: 80%;
margin: 0 auto;
}
#library-app .load-more-card:hover {
cursor: pointer;
}
.text-input-form {
display: flex;
flex-direction: column;
gap: 18px;
}
.text-input-form .text-input {
background: rgba(var(--spice-rgb-selected-row), 0.1);
border: 1px solid transparent;
border-radius: 4px;
color: var(--spice-text);
font-family: inherit;
font-size: 14px;
height: 32px;
padding: 0 12px;
width: 100%;
}
.text-input-form .text-input:focus {
background-color: var(--spice-tab-active);
border: 1px solid var(--spice-button-disabled);
outline: none;
}
.text-input-form button {
align-self: end;
}
/* ../../../AppData/Local/Temp/tmp-5152-1Ex7Bvh4Tu6u/18f3cb8bc361/external.css */
body:not(.show-ylx-filters) .main-yourLibraryX-filterArea:not(:has(> .main-yourLibraryX-libraryFilter)),
.main-yourLibraryX-header:not(:has(> .main-yourLibraryX-headerContent > .main-yourLibraryX-collapseButton > button:nth-child(2))),
.main-yourLibraryX-collapseButton > button:first-child,
.main-yourLibraryX-headerContent > button {
display: none;
}
.main-yourLibraryX-library {
padding-top: 8px;
}
.main-yourLibraryX-header {
margin-top: -8px;
}
.main-yourLibraryX-librarySortWrapper button span:first-child {
display: none;
}
.main-yourLibraryX-librarySortWrapper {
margin-left: auto;
}
.toggle-filters-button > button:after,
.collapse-button > button:after,
.expand-button > button:after {
display: none;
}
.expand-button {
display: flex;
align-items: center;
z-index: 1;
margin-left: 5px;
}
.expand-button > button {
visibility: hidden;
margin: 0 5px;
}
li.main-yourLibraryX-navItem[data-id="/library"] {
display: flex;
}
li.main-yourLibraryX-navItem[data-id="/library"] > a {
flex-grow: 1;
}
.toggle-filters-button > button,
.collapse-button > button,
.main-yourLibraryX-librarySortWrapper > button {
padding: 0;
}
.toggle-filters-button,
.collapse-button,
.main-yourLibraryX-librarySortWrapper {
display: flex;
flex-basis: 32px;
justify-content: center;
min-width: 24px;
flex-shrink: 10;
}
.main-yourLibraryX-librarySortWrapper > button > span:nth-child(2) {
margin: 0;
}
.LayoutResizer__resize-bar {
opacity: 0 !important;
}
.Root__nav-bar .x-filterBox-expandedOrHasFilter .x-filterBox-filterInput {
width: 100%;
}
.Root__nav-bar .x-filterBox-expandedOrHasFilter {
flex-grow: 1;
margin-right: 5px;
}
.Root__nav-bar:has(> .LayoutResizer__resize-bar:hover) .expand-button > button,
.Root__nav-bar .expand-button:hover > button {
visibility: visible;
height: 32px;
background-color: black;
}
.text-input-form .Button-small-buttonPrimary {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
font-size: 0.875rem;
font-weight: 700;
font-family: var(--font-family, CircularSp, CircularSp-Arab, CircularSp-Hebr, CircularSp-Cyrl, CircularSp-Grek, CircularSp-Deva, var(--fallback-fonts, sans-serif));
background-color: transparent;
border: 0px;
border-radius: 9999px;
cursor: pointer;
display: inline-block;
position: relative;
text-align: center;
text-decoration: none;
text-transform: none;
touch-action: manipulation;
transition-duration: 33ms;
transition-property:
background-color,
border-color,
color,
box-shadow,
filter,
transform;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
vertical-align: middle;
transform: translate3d(0px, 0px, 0px);
padding: 0px;
min-inline-size: 0px;
}
.text-input-form .ButtonInner-small {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
position: relative;
background-color: var(--background-base, #1ed760);
color: var(--text-base, #000000);
display: flex;
border-radius: 9999px;
font-size: inherit;
min-block-size: var(--encore-control-size-smaller, 32px);
align-items: center;
justify-content: center;
padding-block-start: var(--encore-spacing-tighter-4, 4px);
padding-block-end: var(--encore-spacing-tighter-4, 4px);
padding-inline-start: var(--encore-spacing-base, 16px);
padding-inline-end: var(--encore-spacing-base, 16px);
}
.text-input-form .Button-small-buttonPrimary:hover .ButtonInner-sc-14ud5tc-0,
.text-input-form .Button-small-buttonPrimary:hover .ButtonFocus-sc-2hq6ey-0 {
transform: scale(1.04);
}
/* ../../../AppData/Local/Temp/tmp-5152-1Ex7Bvh4Tu6u/18f3cb8bc5f2/config_modal.css */
.config-container {
gap: 10px;
display: flex;
flex-direction: column;
}
.config-container .section-header {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
margin-block: 0px;
font-size: 1.125rem;
font-weight: 700;
color: var(--spice-text);
}
.config-container .col.description {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
margin-block: 0px;
font-size: 0.875rem;
font-weight: 400;
color: var(--spice-subtext);
}
.config-container .disabled {
opacity: 0;
pointer-events: none;
}
.config-container .text-input {
background: rgba(var(--spice-rgb-selected-row), 0.1);
border: 1px solid transparent;
border-radius: 4px;
color: var(--spice-text);
font-family: inherit;
font-size: 14px;
height: 32px;
padding: 0 12px;
width: 100%;
}
.config-container .text-input:focus {
background-color: var(--spice-tab-active);
border: 1px solid var(--spice-button-disabled);
outline: none;
}
.config-container .dropdown-input {
background-color: var(--spice-tab-active);
border: 0;
border-radius: 4px;
color: rgba(var(--spice-rgb-selected-row), 0.7);
font-size: 14px;
font-weight: 400;
height: 32px;
letter-spacing: 0.24px;
line-height: 20px;
padding: 0 32px 0 12px;
width: 100%;
}
.config-container .tooltip-icon {
float: right;
margin-left: 10px;
display: flex;
align-items: center;
height: 22px;
fill: var(--spice-subtext);
}
.config-container .tooltip-icon:hover {
fill: var(--spice-text);
}
.config-container .tooltip {
text-align: center;
}
.config-container .setting-row {
display: flex;
justify-content: space-between;
}
.config-container .playback-progressbar {
width: 200px;
}
/* ../../../AppData/Local/Temp/tmp-5152-1Ex7Bvh4Tu6u/18f3cb8bc713/shared.css */
.grid {
--grid-gap: 24px;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)) !important;
}
.loadingWrapper {
display: flex;
justify-content: center;
align-items: center;
min-height: 60vh;
flex-direction: column;
gap: 16px;
}
.loadingWrapper .status-icon {
width: 40px;
height: 40px;
fill: currentColor;
}
.page-content {
display: flex;
flex-direction: column;
gap: 24px;
}
.page-header {
align-content: space-between;
align-items: center;
display: flex;
justify-content: space-between;
margin: 16px 0;
}
.page-header .header-right,
.page-header .header-left {
display: flex;
align-items: center;
gap: 8px;
}
.page-header .header-right {
justify-content: flex-end;
}
.page-header .header-left {
justify-content: flex-start;
}
.new-update {
background-color: var(--spice-player);
color: var(--spice-text);
border-radius: 8px;
padding: 2px 12px;
margin: 0 24px;
border: 0px;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,76 @@
/* ../../../AppData/Local/Temp/tmp-15744-866d0PjWRxih/18d53b11b111/config_modal.css */
#config-container {
gap: 10px;
display: flex;
flex-direction: column;
}
#config-container .section-header {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
margin-block: 0px;
font-size: 1.125rem;
font-weight: 700;
color: var(--spice-text);
}
#config-container .col.description {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
margin-block: 0px;
font-size: 0.875rem;
font-weight: 400;
color: var(--spice-subtext);
}
#config-container .disabled {
opacity: 0;
pointer-events: none;
}
#config-container .text-input {
background: rgba(var(--spice-rgb-selected-row), 0.1);
border: 1px solid transparent;
border-radius: 4px;
color: var(--spice-text);
font-family: inherit;
font-size: 14px;
height: 32px;
padding: 0 12px;
width: 100%;
}
#config-container .text-input:focus {
background-color: var(--spice-tab-active);
border: 1px solid var(--spice-button-disabled);
outline: none;
}
#config-container .dropdown-input {
background-color: var(--spice-tab-active);
border: 0;
border-radius: 4px;
color: rgba(var(--spice-rgb-selected-row), 0.7);
font-size: 14px;
font-weight: 400;
height: 32px;
letter-spacing: 0.24px;
line-height: 20px;
padding: 0 32px 0 12px;
width: 100%;
}
#config-container .tooltip-icon {
float: right;
margin-left: 10px;
display: flex;
align-items: center;
height: 22px;
fill: var(--spice-subtext);
}
#config-container .tooltip-icon:hover {
fill: var(--spice-text);
}
#config-container .tooltip {
text-align: center;
}
#config-container .setting-row {
display: flex;
justify-content: space-between;
}
#config-container .playback-progressbar {
width: 200px;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,17 +1,17 @@
/* ../../AppData/Local/Temp/tmp-6912-LESOepgMxajG/18ca59b28c22/navBar.module.css */
.navBar-module__topBarHeaderItem___v29bR_stats {
/* ../../../AppData/Local/Temp/tmp-17120-n3iWRzissBjg/18f3cbbb4bc3/navBar.module.css */
.navBar-module__topBarHeaderItem___piw4C_stats {
-webkit-app-region: no-drag;
display: inline-block;
pointer-events: auto;
}
.navBar-module__topBarHeaderItemLink___VeyBY_stats {
.navBar-module__topBarHeaderItemLink___xA4uv_stats {
margin: 0 8px 0 0;
}
.navBar-module__topBarActive___-qYPu_stats {
.navBar-module__topBarActive___XhWpm_stats {
background-color: var(--spice-tab-active);
border-radius: 4px;
}
.navBar-module__topBarHeaderItemLink___VeyBY_stats {
.navBar-module__topBarHeaderItemLink___xA4uv_stats {
border-radius: 4px;
color: var(--spice-text);
display: inline-block;
@ -21,12 +21,12 @@
text-decoration: none !important;
cursor: pointer;
}
.navBar-module__topBarNav___1OtdR_stats {
.navBar-module__topBarNav___qWGeZ_stats {
-webkit-app-region: drag;
pointer-events: none;
width: 100%;
}
.navBar-module__topBarHeaderItem___v29bR_stats .navBar-module__optionsMenuDropBox___tD9mA_stats {
.navBar-module__topBarHeaderItem___piw4C_stats .navBar-module__optionsMenuDropBox___pzfNI_stats {
color: var(--spice-text);
border: 0;
max-width: 150px;
@ -38,74 +38,45 @@
-moz-appearance: none;
appearance: none;
}
.navBar-module__topBarHeaderItem___v29bR_stats .navBar-module__optionsMenuDropBox___tD9mA_stats svg {
.navBar-module__topBarHeaderItem___piw4C_stats .navBar-module__optionsMenuDropBox___pzfNI_stats svg {
position: absolute;
margin-left: 8px;
}
div.navBar-module__topBarHeaderItemLink___VeyBY_stats {
div.navBar-module__topBarHeaderItemLink___xA4uv_stats {
padding: 0;
}
/* ../../AppData/Local/Temp/tmp-6912-LESOepgMxajG/18ca59b275b0/app.css */
.stats-grid {
--grid-gap: 24px;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)) !important;
}
.stats-refreshButton {
width: 32px;
height: 32px;
}
.collection-searchBar-searchBar {
display: flex;
gap: 8px;
}
.stats-specialGrid {
grid-template-columns: repeat(11, 180px) !important;
}
.stats-gridInline {
/* ../../../AppData/Local/Temp/tmp-17120-n3iWRzissBjg/18f3cbbb33e0/app.css */
#stats-app .stats-gridInline {
--grid-gap: 24px;
grid-template-columns: repeat(10, 180px) !important;
overflow-x: hidden;
scroll-behavior: smooth;
margin-top: 5px;
}
[data-scroll=both] {
#stats-app [data-scroll=both] {
-webkit-mask-image: linear-gradient(to right, transparent, black 10%, black 90%, transparent);
mask-image: linear-gradient(to right, transparent, black 10%, black 90%, transparent);
}
[data-scroll=end] {
#stats-app [data-scroll=end] {
-webkit-mask-image: linear-gradient(to right, transparent, black 10%);
mask-image: linear-gradient(to right, transparent, black 10%);
}
[data-scroll=start] {
#stats-app [data-scroll=start] {
-webkit-mask-image: linear-gradient(to right, black 90%, transparent);
mask-image: linear-gradient(to right, black 90%, transparent);
}
.stats-loadingWrapper {
display: flex;
justify-content: center;
align-items: center;
min-height: 60vh;
flex-direction: column;
gap: 16px;
}
.stats-libraryOverview {
display: flex;
gap: 24px;
align-items: center;
margin-bottom: 16px;
}
.stats-page {
display: flex;
flex-direction: column;
gap: 24px;
}
.stats-trackPageTitle {
#stats-app .stats-libraryOverview {
display: flex;
gap: 24px;
align-items: center;
}
.stats-scrollButton {
#stats-app .stats-trackPageTitle {
display: flex;
gap: 24px;
align-items: center;
}
#stats-app .stats-scrollButton {
width: 40px;
border-radius: 8px;
border: none;
@ -114,69 +85,20 @@ div.navBar-module__topBarHeaderItemLink___VeyBY_stats {
background-color: var(--spice-player);
color: var(--spice-subtext);
}
.stats-scrollButton:hover {
#stats-app .stats-scrollButton:hover {
background-color: var(--spice-card);
}
.stats-createPlaylistButton {
margin-left: 24px;
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
font-size: 0.8125rem;
font-weight: 700;
font-family: var(--font-family, CircularSp, CircularSp-Arab, CircularSp-Hebr, CircularSp-Cyrl, CircularSp-Grek, CircularSp-Deva, var(--fallback-fonts, sans-serif));
background-color: transparent;
border-radius: 500px;
cursor: pointer;
position: relative;
text-align: center;
text-decoration: none;
text-transform: none;
touch-action: manipulation;
transition-duration: 33ms;
transition-property:
background-color,
border-color,
color,
box-shadow,
filter,
transform;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
vertical-align: middle;
transform: translate3d(0px, 0px, 0px);
padding-block: 3px;
padding-inline: 15px;
border: 1px solid var(--essential-subdued, #878787);
color: var(--text-base, #000000);
min-block-size: 32px;
display: inline-flex;
align-items: center;
justify-content: center;
}
.stats-createPlaylistButton:hover {
transform: scale(1.04);
border-color: var(--essential-base, #000000);
}
.stats-header {
-webkit-box-pack: justify;
-ms-flex-pack: justify;
-webkit-box-align: center;
-ms-flex-align: center;
align-content: space-between;
align-items: center;
color: var(--spice-text);
display: flex;
justify-content: space-between;
margin: 16px 0;
}
.stats-tracklistHeader > div {
#stats-app .stats-tracklistHeader > div {
display: flex;
-webkit-app-region: no-drag;
gap: 20px;
align-items: center;
}
.stats-genreCard {
#stats-app .stats-make-playlist-button {
margin-inline-start: 12px;
}
#stats-app .stats-genreCard {
display: flex;
flex-direction: column;
gap: 10px;
@ -185,44 +107,36 @@ div.navBar-module__topBarHeaderItemLink___VeyBY_stats {
background: var(--spice-player);
position: relative;
}
.stats-genreRow {
#stats-app .stats-genreRow {
width: 100%;
height: 20px;
display: flex;
gap: 10px;
}
.stats-genreRowFill {
#stats-app .stats-genreRowFill {
background: var(--spice-button);
height: 100%;
border-radius: 8px;
display: flex;
align-items: center;
}
.stats-genreText {
#stats-app .stats-genreText {
color: var(--spice-player);
font-size: 0.875rem;
margin-left: 7px;
font-weight: bold;
}
.stats-genreValue {
#stats-app .stats-genreValue {
color: var(--spice-text);
font-size: 0.875rem;
}
.stats-cardValue {
font-size: 2rem;
font-weight: bold;
color: var(--spice-text);
#stats-app .stats-genreCard + .stats-gridInlineSection {
margin-top: 3px;
}
.stats-cardText {
color: var(--spice-text);
}
.new-update {
background-color: var(--spice-player);
color: var(--spice-text);
border-radius: 8px;
padding: 2px 12px;
margin: 0 24px;
border: 0px;
#stats-app .main-trackList-rowHeartButton,
#stats-app .main-trackList-rowMoreButton {
background-color: transparent;
border: none;
}
.GenericModal[aria-label="Playlist Stats"] .main-embedWidgetGenerator-container {
width: 80vw;
@ -232,25 +146,14 @@ div.navBar-module__topBarHeaderItemLink___VeyBY_stats {
.GenericModal[aria-label="Playlist Stats"] .main-shelf-title {
color: var(--spice-text);
}
.status-icon {
width: 40px;
height: 40px;
fill: currentColor;
}
/* ../../AppData/Local/Temp/tmp-6912-LESOepgMxajG/18ca59b28921/settings_modal.css */
#stats-config-container {
/* ../../../AppData/Local/Temp/tmp-17120-n3iWRzissBjg/18f3cbbb48b1/config_modal.css */
.config-container {
gap: 10px;
display: flex;
flex-direction: column;
}
#stats-config-container .toggle-wrapper {
display: inline-flex;
position: relative;
align-items: center;
cursor: pointer;
}
#stats-config-container .section-header {
.config-container .section-header {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
margin-block: 0px;
@ -258,7 +161,7 @@ div.navBar-module__topBarHeaderItemLink___VeyBY_stats {
font-weight: 700;
color: var(--spice-text);
}
#stats-config-container .col.description {
.config-container .col.description {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
margin-block: 0px;
@ -266,60 +169,11 @@ div.navBar-module__topBarHeaderItemLink___VeyBY_stats {
font-weight: 400;
color: var(--spice-subtext);
}
#stats-config-container .disabled {
.config-container .disabled {
opacity: 0;
pointer-events: none;
}
#stats-config-container .toggle-input {
opacity: 0;
pointer-events: none;
position: absolute;
}
#stats-config-container .toggle-input:checked ~ .toggle-indicator-wrapper {
background-color: var(--spice-text);
}
#stats-config-container .toggle-input:checked ~ .toggle-indicator-wrapper .toggle-indicator {
background-color: #fff;
left: auto;
right: 2px;
right: 3px;
}
#stats-config-container .toggle-input:hover ~ .toggle-indicator-wrapper {
filter: brightness(1.3);
}
#stats-config-container .toggle-input:hover:checked ~ .toggle-indicator-wrapper {
filter: brightness(1.15);
}
#stats-config-container .toggle-input:active:not([disabled]) ~ .toggle-indicator-wrapper .toggle-indicator {
width: 20px;
}
#stats-config-container .toggle-indicator-wrapper {
background-color: #535353;
border-radius: 24px;
height: 24px;
position: relative;
width: 42px;
}
#stats-config-container .toggle-indicator {
background: #fff;
border-radius: inherit;
height: 20px;
left: 2px;
position: absolute;
top: 2px;
transition:
background-color,
left,
right,
width 0.1s ease;
width: 20px;
height: 18px;
width: 18px;
top: 3px;
left: 3px;
background: var(--spice-shadow) !important;
}
#stats-config-container .text-input {
.config-container .text-input {
background: rgba(var(--spice-rgb-selected-row), 0.1);
border: 1px solid transparent;
border-radius: 4px;
@ -330,12 +184,12 @@ div.navBar-module__topBarHeaderItemLink___VeyBY_stats {
padding: 0 12px;
width: 100%;
}
#stats-config-container .text-input:focus {
.config-container .text-input:focus {
background-color: var(--spice-tab-active);
border: 1px solid var(--spice-button-disabled);
outline: none;
}
#stats-config-container .dropdown-input {
.config-container .dropdown-input {
background-color: var(--spice-tab-active);
border: 0;
border-radius: 4px;
@ -348,7 +202,7 @@ div.navBar-module__topBarHeaderItemLink___VeyBY_stats {
padding: 0 32px 0 12px;
width: 100%;
}
#stats-config-container .tooltip-icon {
.config-container .tooltip-icon {
float: right;
margin-left: 10px;
display: flex;
@ -356,13 +210,67 @@ div.navBar-module__topBarHeaderItemLink___VeyBY_stats {
height: 22px;
fill: var(--spice-subtext);
}
#stats-config-container .tooltip-icon:hover {
.config-container .tooltip-icon:hover {
fill: var(--spice-text);
}
#stats-config-container .tooltip {
.config-container .tooltip {
text-align: center;
}
#stats-config-container .setting-row {
.config-container .setting-row {
display: flex;
justify-content: space-between;
}
.config-container .playback-progressbar {
width: 200px;
}
/* ../../../AppData/Local/Temp/tmp-17120-n3iWRzissBjg/18f3cbbb49f2/shared.css */
.grid {
--grid-gap: 24px;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)) !important;
}
.loadingWrapper {
display: flex;
justify-content: center;
align-items: center;
min-height: 60vh;
flex-direction: column;
gap: 16px;
}
.loadingWrapper .status-icon {
width: 40px;
height: 40px;
fill: currentColor;
}
.page-content {
display: flex;
flex-direction: column;
gap: 24px;
}
.page-header {
align-content: space-between;
align-items: center;
display: flex;
justify-content: space-between;
margin: 16px 0;
}
.page-header .header-right,
.page-header .header-left {
display: flex;
align-items: center;
gap: 8px;
}
.page-header .header-right {
justify-content: flex-end;
}
.page-header .header-left {
justify-content: flex-start;
}
.new-update {
background-color: var(--spice-player);
color: var(--spice-text);
border-radius: 8px;
padding: 2px 12px;
margin: 0 24px;
border: 0px;
}

View file

@ -1,59 +0,0 @@
//@ts-check
// NAME: adblock
// AUTHOR: CharlieS1103
// DESCRIPTION: Block all audio and UI ads on Spotify
/// <reference path="../../spicetify-cli/globals.d.ts" />
(function adblock() {
const { Platform} = Spicetify;
if (!(Platform)) {
setTimeout(adblock, 300)
return
}
var styleSheet = document.createElement("style")
styleSheet.innerHTML =
`
.MnW5SczTcbdFHxLZ_Z8j, .WiPggcPDzbwGxoxwLWFf, .ReyA3uE3K7oEz7PTTnAn, .main-leaderboardComponent-container, .sponsor-container, a.link-subtle.main-navBar-navBarLink.GKnnhbExo0U9l7Jz2rdc{
display: none !important;
}
`
document.body.appendChild(styleSheet)
delayAds()
var billboard = Spicetify.Platform.AdManagers.billboard.displayBillboard;
Spicetify.Platform.AdManagers.billboard.displayBillboard = function (arguments) {
Spicetify.Platform.AdManagers.billboard.finish()
// hook before call
var ret = billboard.apply(this, arguments);
// hook after call
console.log("Adblock.js: Billboard blocked! Leave a star!")
Spicetify.Platform.AdManagers.billboard.finish()
const observer = new MutationObserver((mutations, obs) => {
const billboardAd = document.getElementById('view-billboard-ad');
if (billboardAd) {
Spicetify.Platform.AdManagers.billboard.finish()
obs.disconnect();
return;
}
});
observer.observe(document, {
childList: true,
subtree: true
});
return ret;
};
function delayAds() {
console.log("Ads delayed: Adblock.js")
Spicetify.Platform.AdManagers.audio.audioApi.cosmosConnector.increaseStreamTime(-100000000000)
Spicetify.Platform.AdManagers.billboard.billboardApi.cosmosConnector.increaseStreamTime(-100000000000)
}
setInterval(delayAds, 720 *10000);
})()

View file

@ -1,654 +0,0 @@
// NAME: Copy Playlists
// AUTHOR: einzigartigerName
// DESCRIPTION: copy/combine playlist/queue directly in Spotify
(function CopyPlaylist() {
const { CosmosAPI, BridgeAPI, LocalStorage, PlaybackControl, ContextMenu, URI } = Spicetify
if (!(CosmosAPI || BridgeAPI)) {
setTimeout(CopyPlaylist, 1000);
return;
}
const STORAGE_KEY = "combine_buffer_spicetify"
const TOP_BTN_TOOLTIP = "Combine Playlists"
const MENU_BTN_CREATE_NEW = "Create Playlist"
const MENU_BTN_INSERT_BUFFER = "Copy to Buffer"
class PlaylistCollection {
constructor() {
const menu = createMenu()
this.container = menu.container
this.items = menu.menu
this.lastScroll = 0
this.container.onclick = () => {
this.storeScroll()
this.container.remove()
}
this.pattern
this.apply()
}
apply() {
this.items.textContent = '' // Remove all childs
this.items.append(createMenuItem("Create Playlist", () => highjackCreateDialog(mergePlaylists(this.pattern))))
this.items.append(createMenuItem("Clear Buffer", () => LIST.clearStorage()))
const select = createPatternSelect(this.filter);
select.onchange = (event) => {
this.pattern = event.srcElement.selectedIndex;
}
this.items.append(select);
const collection = this.getStorage();
collection.forEach((item) => this.items.append(new CardContainer(item)))
}
getStorage() {
const storageRaw = LocalStorage.get(STORAGE_KEY);
let storage = [];
if (storageRaw) {
storage = JSON.parse(storageRaw);
} else {
LocalStorage.set(STORAGE_KEY, "[]")
}
return storage;
}
addToStorage(data) {
/** @type {Object[]} */
const storage = this.getStorage();
storage.push(data);
LocalStorage.set(STORAGE_KEY, JSON.stringify(storage));
this.apply()
}
removeFromStorage(id) {
const storage = this.getStorage()
.filter(item => item.id !== id)
LocalStorage.set(STORAGE_KEY, JSON.stringify(storage));
this.apply()
}
clearStorage() {
LocalStorage.set(STORAGE_KEY, "[]");
this.apply()
}
moveItem(uri, direction) {
var storage = this.getStorage()
var from;
for (var i = 0; i < storage.length; i++) {
if (storage[i].uri === uri) {
from = i
break;
}
}
if (!from) { return }
var to = from + direction
if (to < 0 || to >= storage.length) { return }
var tmp = storage[from]
storage[from] = storage[to]
storage[to] = tmp
LocalStorage.set(STORAGE_KEY, JSON.stringify(storage));
this.apply()
}
changePosition(x, y) {
this.items.style.left = x + "px"
this.items.style.top = y + 10 + "px"
}
storeScroll() {
this.lastScroll = this.items.scrollTop
}
setScroll() {
this.items.scrollTop = this.lastScroll
}
}
/*
* Displays Stored Playlist
* {id, uri, name, tracks, imgUri, owner}
*/
class CardContainer extends HTMLElement {
constructor(info) {
super()
this.innerHTML = `
<div class="card card-horizontal card-type-album ${info.imgUri ? "" : "card-hidden-image"}" data-uri="${info.uri}" data-contextmenu="">
<div class="card-attention-highlight-box"></div>
<div class="card-horizontal-interior-wrapper">
${info.imgUri ? `
<div class="card-image-wrapper">
<div class="card-image-hit-area">
<a class="card-image-link" link="${info.uri}">
<div class="card-hit-area-counter-scale-left"></div>
<div class="card-image-content-wrapper">
<div class="card-image" style="background-image: url('${info.imgUri}')"></div>
</div>
</a>
<div class="card-overlay"></div>
</div>
</div>
` : ""}
<div class="card-info-wrapper">
<div class="order-controls">
<div class="order-controls-up">
<button class="button button-green button-icon-only spoticon-chevron-up-16" data-tooltip="Move Up"></button>
</div>
<div class="order-controls-remove">
<button class="button button-green button-icon-only spoticon-x-16" data-tooltip="Remove"></button>
</div>
<div class="order-controls-down">
<button class="button button-green button-icon-only spoticon-chevron-down-16" data-tooltip="Move Down"></button>
</div>
</div>
<a class="card-info-link" ${info.uri}>
<div class="card-info-content-wrapper">
<div class="card-info-title"><span class="card-info-title-text">${info.name}</span></div>
<div class="card-info-subtitle-owner"><span>${info.owner}</span></div>
<div class="card-info-subtitle-tracks"><span>${info.tracks.length === 1 ? "1 Track" : `${info.tracks.length} Tracks`}</span></div>
</div>
</a>
</div>
</div>
</div>`
const up = this.querySelector(".order-controls-up")
up.onclick = (event) => {
LIST.moveItem(info.uri, -1)
event.stopPropagation()
}
const remove = this.querySelector(".order-controls-remove")
remove.onclick = (event) => {
LIST.removeFromStorage(info.id)
event.stopPropagation()
}
const down = this.querySelector(".order-controls-down")
down.onclick = (event) => {
LIST.moveItem(info.uri, +1)
event.stopPropagation()
}
const imageLink = this.querySelector(".card-image-link");
const infoLink = this.querySelector(".card-info-link");
if (imageLink) imageLink.addEventListener("click", ((e) => showPlaylist(e)));
if (infoLink) infoLink.addEventListener("click", ((e) => showPlaylist(e)));
}
}
customElements.define("combine-buffer-card-container", CardContainer)
const LIST = new PlaylistCollection()
// New Playlist Button
const playlistDialogButton = document.querySelector("#new-playlist-button-mount-point > div > button")
if (!playlistDialogButton) return;
document.querySelector("#view-browser-navigation-top-bar")
.append(createTopBarButton())
createPlaylistContextMenu().register()
/**************************************************************************
UI Building
**************************************************************************/
// If Queue Page add Buttons
const iframeInterval = setInterval(() => {
/** @type {HTMLIFrameElement} */
const currentIframe = document.querySelector("iframe.active");
if (!currentIframe ||
currentIframe.id !== "app-queue"
) {
return;
}
const headers = currentIframe.contentDocument.querySelectorAll(
".glue-page-header__buttons"
);
for (const e of headers) {
e.append(createQueueButton(
"Save as Playlist",
"Save the current Queue as a new Playlist",
() => {
let tracks = getQueueTracks();
highjackCreateDialog(tracks);
},
));
e.append(createQueueButton(
"Copy into Buffer",
"Insert the current Queue into the Buffer",
() => { queueToBuffer() },
));
}
if (headers.length > 0) clearInterval(iframeInterval);
}, 500)
// Creates the Main Menu
function createMenu() {
const container = document.createElement("div")
container.id = "combine-playlist-spicetify"
container.className = "context-menu-container"
container.style.zIndex = "1029"
const style = document.createElement("style")
style.textContent = `
#combine-menu {
display: inline-block;
width: 33%;
max-height: 70%;
overflow: hidden auto;
padding: 10px
}
.combine-pattern {
margin-top: 7px;
}
.order-controls {
position: absolute;
right: 0;
padding: 0 5px 5px 0;
z-index: 3
}
.button.button-icon-only::before {
color: var(--modspotify_main_fg);
}
.order-controls-up {
position: relative;
top: 100%;
}
.order-controls-remove {
position: relative;
top: 50%;
}
.order-controls-down {
position: relative;
bottom: 100%;
}
.card-info-subtitle-owner {
color: var(--modspotify_secondary_fg);
}
.card-info-subtitle-tracks {
font-weight: lighter;
color: var(--modspotify_secondary_fg);
}
`
const menu = document.createElement("ul")
menu.id = "combine-menu"
menu.className = "context-menu"
container.append(style, menu)
return { container, menu }
}
// Creates a Button in the Combine Menu
function createMenuItem(name, callback) {
const item = document.createElement("div");
item.classList.add("item");
item.onclick = callback;
item.onmouseover = () => item.classList.add("hover");
item.onmouseleave = () => item.classList.remove("hover");
const text = document.createElement("span");
text.classList.add("text");
text.innerText = name;
item.append(text);
return item;
}
// Creates the SubMenu in Playlist Context
function createPlaylistContextMenu() {
var createFromCurrent = new Spicetify.ContextMenu.Item(
MENU_BTN_CREATE_NEW,
(uris) => {
if (uris.length === 1) {
fetchPlaylist(uris[0])
.then((buffer) => highjackCreateDialog(buffer.tracks))
.catch((err) => Spicetify.showNotification(`${err}`));
return;
} else {
Spicetify.showNotification("Unable to find Playlist URI")
}
},
(_) => true
)
var insertIntoBuffer = new Spicetify.ContextMenu.Item(
MENU_BTN_INSERT_BUFFER,
(uris) => {
if (uris.length === 1) {
fetchPlaylist(uris[0])
.then((buffer) => {LIST.addToStorage(buffer)})
.catch((err) => Spicetify.showNotification(`${err}`));
return;
}
},
(_) => true
)
return new Spicetify.ContextMenu.SubMenu(
"Copy Playlist",
[ createFromCurrent, insertIntoBuffer],
(uris) => {
if (uris.length === 1) {
const uriObj = Spicetify.URI.fromString(uris[0]);
switch (uriObj.type) {
case Spicetify.URI.Type.PLAYLIST:
case Spicetify.URI.Type.PLAYLIST_V2:
return true;
}
return false;
}
// Multiple Items selected.
return false;
}
)
}
// Creates the Button to View Merge Buffer
function createTopBarButton() {
const button = document.createElement("button")
button.classList.add("button", "spoticon-copy-16", "merge-button")
button.setAttribute("data-tooltip", TOP_BTN_TOOLTIP)
button.setAttribute("data-contextmenu", "")
button.setAttribute("data-uri", "spotify:special:copy")
button.onclick = () => {
const bound = button.getBoundingClientRect()
LIST.changePosition(bound.left, bound.top)
document.body.append(LIST.container)
LIST.setScroll()
}
return button
}
// Creates the Dropdown Menu for Merge Pattern
function createPatternSelect(defaultOpt = 0) {
const select = document.createElement("select");
select.className = "GlueDropdown combine-pattern";
const appendOpt = document.createElement("option");
appendOpt.text = "Append";
const shuffleOpt = document.createElement("option");
shuffleOpt.text = "Shuffle";
const alternateOpt = document.createElement("option");
alternateOpt.text = "Alternate";
select.onclick = (ev) => ev.stopPropagation();
select.append(appendOpt, shuffleOpt, alternateOpt);
select.options[defaultOpt].selected = true;
return select;
}
// Queue button
function createQueueButton(name, tooltip, callback) {
const b = document.createElement("button");
b.classList.add("button", "button-green");
b.innerText = name;
b.setAttribute("data-tooltip", tooltip);
b.onclick = callback;
return b;
}
// Highjack Spotifies 'New Playlist' Dialog
function highjackCreateDialog(tracks) {
playlistDialogButton.click()
var createButton = document.querySelector("body > div.Modal__portal > div > div > div > div.PlaylistAnnotationModal__submit-button-container > button")
var buttonContainer = document.querySelector("body > div.Modal__portal > div > div > div > div.PlaylistAnnotationModal__submit-button-container")
var highjackedButton = createButton.cloneNode(true)
highjackedButton.addEventListener("click", () => onCreateNewPlaylist(tracks))
window.addEventListener("keypress", (event) => {
if (event.code === `Enter`) {
// Cancel the default action, if needed
event.preventDefault();
// Trigger the button element with a click
createButton.click();
}
});
createButton.remove()
buttonContainer.insertAdjacentElement("afterbegin", highjackedButton)
}
/**************************************************************************
OnCLick Functions
**************************************************************************/
// Create a new Playlist from Inputs
function onCreateNewPlaylist(tracks) {
var exitButton = document.querySelector("body > div.Modal__portal > div > div > div > div.PlaylistAnnotationModal__close-button > button");
var nameInput = document.querySelector("body > div.Modal__portal > div > div > div > div.PlaylistAnnotationModal__content > div.PlaylistAnnotationModal__playlist-name > input")
var descInput = document.querySelector("body > div.Modal__portal > div > div > div > div.PlaylistAnnotationModal__content > div.PlaylistAnnotationModal__playlist-description > textarea")
var imageInput = document.querySelector("body > div.Modal__portal > div > div > div > div.PlaylistAnnotationModal__content > div.PlaylistAnnotationModal__img-container > div > div.PlaylistAnnotationModal__img-holder > img")
var name = nameInput.value
if (!name) {
name = nameInput.getAttribute("placeholder")
}
var desc = descInput.value
var img;
if (imageInput) {
img = imageInput.getAttribute("src")
}
createPlaylist(name)
.then(res => addTracks(res.uri, tracks))
.then((_) => Spicetify.showNotification(`Created Playlist: "${name}"`))
.catch((err) => Spicetify.showNotification(`${err}`));
exitButton.click()
if (exitButton) {
exitButton.click()
}
}
// Get All Tracks in Queue and remove delimiter
function getQueueTracks() {
return Spicetify.Queue.next_tracks
.map((t) => t.uri)
.filter((t) => { return t != "spotify:delimiter"; })
}
// Copy the Queue to the Combine Buffer
function queueToBuffer() {
let tracks = getQueueTracks();
var date = new Date()
var year = new Intl.DateTimeFormat('en', { year: 'numeric' }).format(date);
var month = new Intl.DateTimeFormat('en', { month: 'short' }).format(date);
var day = new Intl.DateTimeFormat('en', { day: '2-digit' }).format(date);
const timeOptions = { hour: 'numeric', minute: 'numeric', hour12: false};
var time = new Intl.DateTimeFormat(`en`, timeOptions).format(date);
let queue = {
id: `spotify:queue-${date}`,
uri: `spotify:queue`,
name: "Queue",
imgUri: undefined,
tracks: tracks,
owner: `${time} - ${day} ${month} ${year}`,
}
LIST.addToStorage(queue);
}
// Show the clicked Playlist
async function showPlaylist(event) {
console.log(event)
}
/**************************************************************************
Merge Playlists
**************************************************************************/
// Merge all Playlists
function mergePlaylists(pattern) {
var tracks = LIST.getStorage().map((pl) => pl.tracks)
switch (pattern) {
case 1: return shuffle(tracks);
case 2: return alternate(tracks);
default: return append(tracks);
}
}
// Alternate Playlists
function alternate(arrays) {
var combined = []
while (arrays.length != 0) {
var current = arrays.shift()
if (current.length != 0) {
combined.push(current.shift())
if (current.length != 0) {
arrays.push(current)
}
}
}
return combined
}
// Shuffle all tracks using the Durstenfeld Shuffle
function shuffle(arrays) {
var combined = append(arrays)
for (var i = combined.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = combined[i];
combined[i] = combined[j];
combined[j] = temp;
}
return combined;
}
// Simply Concat all Playlist
function append(arrays) {
var combined = []
arrays.forEach((arr) => combined = combined.concat(arr))
return combined;
}
/**************************************************************************
Calls to the CosmosAPI
**************************************************************************/
// Fetch all Track from Playlist URI
async function fetchPlaylist(uri) {
return await new Promise((resolve, reject) => {
Spicetify.BridgeAPI.cosmosJSON(
{
method: "GET",
uri: `sp://core-playlist/v1/playlist/${uri}/`,
body: {
policy: {
link: true,
},
},
},
(error, res) => {
if (error) {
reject(error);
return;
}
let id = `${uri}-${new Date()}`
let tracks = res.items.map((track) => track.link)
let img = res.playlist.picture
let name = res.playlist.name
let owner = res.playlist.owner.name
let playlist = {id: id, uri: uri, name: name, tracks: tracks, imgUri: img, owner: owner}
resolve(playlist);
}
);
});
}
// Create a new Playlist
async function createPlaylist(name) {
return await new Promise((resolve, reject) => {
Spicetify.BridgeAPI.cosmosJSON(
{
method: "POST",
uri: `sp://core-playlist/v1/rootlist`,
body: {
operation: "create",
playlist: !0,
before: "start",
name: name,
},
},
(error, res) => {
if (error) {
reject(error);
return;
}
resolve(res);
}
);
});
}
// add track list to playlist
async function addTracks(uri, tracks) {
return await new Promise((resolve, reject) => {
Spicetify.BridgeAPI.cosmosJSON(
{
method: "POST",
uri: `sp://core-playlist/v1/playlist/${uri}`,
body: {
operation: "add",
uris: tracks,
after: "end"
}
},
(error, res) => {
if (error) {
reject(error);
return;
}
resolve(res);
}
);
});
}
})();

File diff suppressed because it is too large Load diff

View file

@ -1,125 +0,0 @@
// <reference path="./globals.d.ts" />
(function Genre() {
const { CosmosAsync, Player } = Spicetify;
/**
* Fetch genre from artist
*
* @param artistURI {string}
* @return {Promise<Array>}
*/
const fetchGenres = async (artistURI) => {
const res = await CosmosAsync.get(
`https://api.spotify.com/v1/artists/${artistURI}`
);
// noinspection JSUnresolvedVariable
return res.genres.slice(0, 3) // Only keep the first 3 genres
};
/**
* Fetch playlist from The Sound of Spotify for a given genre
* @param {String} genre
* @return {String|null}
*/
const fetchSoundOfSpotifyPlaylist = async (genre) => {
const query = encodeURIComponent(`The Sound of ${genre}`);
// Check localStorage for playlist
const cached = localStorage.getItem(`everynoise:${query}`);
if (cached !== null) {
return cached;
}
// Search for playlist and check results for the everynoise account
const re = new RegExp(`^the sound of ${genre.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`, 'i');
const res = await CosmosAsync.get(`https://api.spotify.com/v1/search?q=${query}&type=playlist`)
for (const item of res.playlists.items) {
if (item.owner.id === "thesoundsofspotify" && re.test(item.name)) {
localStorage.setItem(`everynoise:${genre}`, item.uri);
return item.uri
}
}
return null;
};
// Store the current playback id
let playback = null;
/**
*
* @type {Node}
*/
let genreContainer = null;
let infoContainer = document.querySelector('div.main-trackInfo-container');
/**
* Remove genre injection in the UI
*/
const cleanInjection = () => {
if (genreContainer !== null) {
try {
infoContainer.removeChild(genreContainer);
} catch (e) {}
}
};
/**
* Inject genres to UI
*/
const inject = () => {
Player.addEventListener('onprogress', async () => {
if (Player.data.track.metadata.hasOwnProperty('artist_uri')) {
// If the registered song isn't the same as the one currently being played then fetch genres
if (playback !== Player.data.playback_id) {
// Save the new track
playback = Player.data.playback_id;
const id = Player.data.track.metadata.artist_uri.split(':')[2];
const genres = await fetchGenres(id);
cleanInjection();
genreContainer = document.createElement('div');
// noinspection JSUndefinedPropertyAssignment
genreContainer.className = 'main-trackInfo-genres ellipsis-one-line main-type-finale';
// noinspection JSUnresolvedVariable
genreContainer.style.color = 'var(--spice-extratext)';
for (const i in genres) {
let element;
const uri = await fetchSoundOfSpotifyPlaylist(genres[i]);
if (uri !== null) {
element = document.createElement('a');
element.innerHTML = genres[i];
element.href = uri;
} else {
element = document.createElement('span');
}
element.innerHTML = genres[i];
element.style.fontSize ="11px";
genreContainer.appendChild(element);
if (i < genres.length-1) {
const separator = document.createElement('span');
separator.innerHTML = ', ';
genreContainer.appendChild(separator);
}
}
infoContainer = document.querySelector('div.main-trackInfo-container');
if(!infoContainer) cleanInjection();
infoContainer.appendChild(genreContainer);
}
} else {
cleanInjection();
}
});
};
if (!CosmosAsync) {
setTimeout(Genre, 500);
} else {
inject();
}
})();

File diff suppressed because one or more lines are too long

View file

@ -1,127 +0,0 @@
// NAME: History Shortcut
// AUTHOR: einzigartigerName
// DESCRIPTION: Adds a Shortcut to your Listening History to the Sidebar
(function HistoryShortcut() {
const { CosmosAPI, Player, LocalStorage, PlaybackControl, ContextMenu, URI } = Spicetify
if (!(CosmosAPI && Player && LocalStorage && PlaybackControl && ContextMenu && URI)) {
setTimeout(HistoryShortcut, 300)
return
}
const ITEM_LABEL = "History"
const HISTORY_DIV_CLASS = "SidebarListItem"
const HISTORY_DIV_CLASS_ACTIVE = "SidebarListItem SidebarListItem--is-active"
const HISTORY_ANKER_CLASS = "SidebarListItemLink SidebarListItemLink--tall spoticon-time-24"
const HISTORY_ANKER_CLASS_ACTIVE = "SidebarListItemLink SidebarListItemLink--is-highlighted SidebarListItemLink--tall spoticon-time-24"
let historyItem = createHistoyItem()
// Get Sidebar Lists
var topicList = document.querySelector("#view-navigation-bar > div > div.LeftSidebar__section > div > ul")
if (topicList) {
// Add to first in list
// On default layout this would be the Home/Browse/Radio List
topicList.appendChild(historyItem.div)
} else {
return
}
const toCheckMutate = document.getElementById('view-content');
const config = { attributes: true, childList: true, subtree: true };
let observerCallback = function(_, _) {
appQueue = document.getElementById("app-queue")
if (!appQueue){ return }
if (appQueue.getAttribute("class") === "active"
&& appQueue.getAttribute("data-app-uri") === "spotify:app:queue:history"
) {
onClickHistory()
} else {
onLeaveHistory()
}
};
let observer = new MutationObserver(observerCallback)
observer.observe(toCheckMutate, config)
// Deactivate Active Status for History Item
function onLeaveHistory() {
historyItem.div.setAttribute("class",HISTORY_DIV_CLASS)
historyItem.anker.setAttribute("class", HISTORY_ANKER_CLASS)
}
// Activate Active Status for History Item
function onClickHistory() {
historyItem.div.setAttribute("class", HISTORY_DIV_CLASS_ACTIVE)
historyItem.anker.setAttribute("class", HISTORY_ANKER_CLASS_ACTIVE)
}
// Construct the List Item
function createHistoyItem() {
/* List Item
* <li class="SidebarListItem">
*/
let listItem = document.createElement("li")
listItem.setAttribute("class", HISTORY_DIV_CLASS)
/* Outer Div Element
* <div class="DropTarget SidebarListItem__drop-target DropTarget--tracks DropTarget--albums DropTarget--artists DropTarget--playlists">
*/
let outer = document.createElement("div")
outer.setAttribute("class", "DropTarget SidebarListItem__drop-target")
/* Middle Div Element
* <div class="SidebarListItem__inner">
*/
let inner = document.createElement("div")
inner.setAttribute("class", "SidebarListItem__inner")
/* Link Div Element
* <div class="SidebarListItem__link">
*/
let link = document.createElement("div")
link.setAttribute("class", "SidebarListItem__link")
/* Anker
* <a class="SidebarListItemLink SidebarListItemLink--tall spoticon-time-24"
* draggable="false"
* href="spotify:app:queue:history"
* data-sidebar-list-item-uri="spotify:app:queue:history"
* data-ta-id="sidebar-list-item-link">
*/
anker = document.createElement("a")
anker.setAttribute("class", HISTORY_ANKER_CLASS)
anker.setAttribute("draggable", "false")
anker.setAttribute("href", "spotify:app:queue:history")
anker.setAttribute("data-sidebar-list-item-uri", "spotify:app:queue:history")
anker.setAttribute("data-ta-id", "sidebar-list-item-link")
/* Item Text
* <span class="SidebarListItem__label"
* dir="auto">
* History
* </span>
*/
span = document.createElement("span")
span.setAttribute("class", "SidebarListItem__label")
span.setAttribute("dir", "auto")
span.textContent = ITEM_LABEL
anker.appendChild(span)
link.appendChild(anker)
inner.appendChild(link)
outer.appendChild(inner)
listItem.appendChild(outer)
listItem.addEventListener("click", onClickHistory)
return {div: listItem, anker: anker}
}
})();

View file

@ -1,532 +0,0 @@
//@ts-check
// NAME: Keyboard Shortcut
// AUTHOR: dax
// DESCRIPTION: Register a few more keybinds to support keyboard-driven navigation in Spotify client.
/// <reference path="../spicetify-cli/globals.d.ts" />
(function KeyboardShortcutMy() {
if (!Spicetify.Keyboard) {
setTimeout(KeyboardShortcutMy, 1000);
return;
}
const SCROLL_STEP = 50;
/**
* Register your own keybind with function `registerBind`
*
* Syntax:
* registerBind(keyName, ctrl, shift, alt, callback)
*
* ctrl, shift and alt are boolean, true or false
*
* Valid keyName:
* - BACKSPACE - C - Y - F3
* - TAB - D - Z - F4
* - ENTER - E - WINDOW_LEFT - F5
* - SHIFT - F - WINDOW_RIGHT - F6
* - CTRL - G - SELECT - F7
* - ALT - H - NUMPAD_0 - F8
* - PAUSE/BREAK - I - NUMPAD_1 - F9
* - CAPS - J - NUMPAD_2 - F10
* - ESCAPE - K - NUMPAD_3 - F11
* - SPACE - L - NUMPAD_4 - F12
* - PAGE_UP - M - NUMPAD_5 - NUM_LOCK
* - PAGE_DOWN - N - NUMPAD_6 - SCROLL_LOCK
* - END - O - NUMPAD_7 - ;
* - HOME - P - NUMPAD_8 - =
* - ARROW_LEFT - Q - NUMPAD_9 - ,
* - ARROW_UP - R - MULTIPLY - -
* - ARROW_RIGHT - S - ADD - /
* - ARROW_DOWN - T - SUBTRACT - `
* - INSERT - U - DECIMAL_POINT - [
* - DELETE - V - DIVIDE - \
* - A - W - F1 - ]
* - B - X - F2 - "
*
* Use one of keyName as a string. If key that you want isn't in that list,
* you can also put its keycode number in keyName as a number.
*
* callback is name of function you want your shortcut to bind to. It also
* returns one KeyboardEvent parameter.
*
* Following are my default keybinds, use them as examples.
*/
//My Personal Binds----------------------------------------------
// Seek to progress percent of song
registerBind("NUMPAD_0", false, false, false, ()=>Spicetify.Player.seek(0));
registerBind("NUMPAD_1", false, false, false, ()=>Spicetify.Player.seek(.1));
registerBind("NUMPAD_2", false, false, false, ()=>Spicetify.Player.seek(.2));
registerBind("NUMPAD_3", false, false, false, ()=>Spicetify.Player.seek(.3));
registerBind("NUMPAD_4", false, false, false, ()=>Spicetify.Player.seek(.4));
registerBind("NUMPAD_5", false, false, false, ()=>Spicetify.Player.seek(.5));
registerBind("NUMPAD_6", false, false, false, ()=>Spicetify.Player.seek(.6));
registerBind("NUMPAD_7", false, false, false, ()=>Spicetify.Player.seek(.7));
registerBind("NUMPAD_8", false, false, false, ()=>Spicetify.Player.seek(.8));
registerBind("NUMPAD_9", false, false, false, ()=>Spicetify.Player.seek(.9));
// Q to open Queue page
registerBind("Q", false, false, false, clickQueueButton);
// C to open current playing context, L to open Lyrics, H to open Home Tab , Y to Open the Ypur Library, S to open the Lyrics Plus Custom App
registerBind("C", false, false, false, openContext)
registerBind("L", false, false, false, toggleLyrics);
registerBind("H", false, false, false, openHome);
registerBind("Y", false, false, false, openLibrary);
registerBind("S", false, false, false, openLyrics);
// Arrow keys to change volume
registerBind("ARROW_DOWN", false, false, false, decreaseVolume);
registerBind("ARROW_UP" , false, false, false, increaseVolume);
// Arrow keys to seek track
registerBind("ARROW_RIGHT", false, false, false, seekForward);
registerBind("ARROW_LEFT", false, false, false, seekBack);
function sleep (time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
function clickQueueButton() {
document.querySelector('.control-button-wrapper>button[Aria-label="Queue"]').click();
}
function openContext(){
big = document.querySelector("#main > div > div.Root__top-container > nav > div.main-navBar-navBar > div:nth-child(3) > div > div > a > div")
small = document.querySelector("#main > div > div.Root__top-container > div.Root__now-playing-bar > footer > div > div.main-nowPlayingBar-left > div > div.main-coverSlotCollapsed-container > div > a > div")
if(big)
big.click();
else
small.click()
}
function toggleLyrics() {
document.querySelector('button[title="Popup Lyrics"]').click()
}
function openHome(){
ele = document.querySelector(`.main-navBar-navBar a[href="/"]`)
if(ele)
ele.click();
}
function openLyrics(){
ele = document.querySelector(`.main-navBar-navBar a[href="/lyrics-plus"]`)
if(ele)
ele.click();
}
function openLibrary(){
ele = document.querySelector(`.main-navBar-navBar a[href="/collection"]`)
if(ele)
ele.click();
}
function seekForward(){
Spicetify.Player.skipForward(5000)
}
function seekBack(){
Spicetify.Player.skipBack(5000)
}
async function decreaseVolume(){
if(!document.querySelector(".main-trackList-selected")){
if(Spicetify.Platform?.PlaybackAPI === undefined) Spicetify.Player?.origin?.setVolume(getVolume() - 0.05)
else await Spicetify.Platform.PlaybackAPI.setVolume(getVolume() - 0.05)
}
}
async function increaseVolume(){
if(!document.querySelector(".main-trackList-selected")){
if(Spicetify.Platform?.PlaybackAPI === undefined) Spicetify.Player?.origin?.setVolume(getVolume() + 0.05)
else await Spicetify.Platform.PlaybackAPI.setVolume(getVolume() + 0.05)
}
}
function getVolume(){
return (Spicetify.Player?.origin?._volume?._volume ?? Spicetify.Platform?.PlaybackAPI?._volume)
}
// ---------------------------------------------------------------------------------------
// Ctrl + Tab and Ctrl + Shift + Tab to switch sidebar items
registerBind("TAB", true, false, false, rotateSidebarDown);
registerBind("TAB", true, true, false, rotateSidebarUp);
// Ctrl + Q to open Queue page
// registerBind("Q", true, false, false, clickQueueButton);
// Shift + H and Shift + L to go back and forward page
registerBind("H", false, true, false, clickNavigatingBackButton);
registerBind("L", false, true, false, clickNavigatingForwardButton);
// PageUp, PageDown to focus on iframe app before scrolling
registerBind("PAGE_UP", false, true, false, focusOnApp);
registerBind("PAGE_DOWN", false, true, false, focusOnApp);
// J and K to vertically scroll app
registerBind("J", false, false, false, appScrollDown);
registerBind("K", false, false, false, appScrollUp);
// G and Shift + G to scroll to top and to bottom
registerBind("G", false, false, false, appScrollTop);
registerBind("G", false, true, false, appScrollBottom);
// M to Like/Unlike track
registerBind("M", false, false, false, Spicetify.Player.toggleHeart);
// Forward Slash to open search page
registerBind("/", false, false, false, openSearchPage);
// A to activate Link Follow function
const vim = new VimBind();
registerBind("A", false, false, false, vim.activate.bind(vim));
// Esc to cancel Link Follow
vim.setCancelKey("ESCAPE")
vim.setCancelKey("Z")
function rotateSidebarDown() {
rotateSidebar(1)
}
function rotateSidebarUp() {
rotateSidebar(-1)
}
// function clickQueueButton() {
// document.querySelector(".control-button-wrapper .spoticon-queue-16").click();
// }
function clickNavigatingBackButton() {
document.querySelector(".main-topBar-historyButtons .main-topBar-back").click();
}
function clickNavigatingForwardButton() {
document.querySelector(".main-topBar-historyButtons .main-topBar-forward").click();
}
function appScrollDown() {
const app = focusOnApp();
if (app) {
app.scrollBy(0, SCROLL_STEP);
}
}
function appScrollUp() {
const app = focusOnApp();
if (app) {
app.scrollBy(0, -SCROLL_STEP);
}
}
function appScrollBottom() {
const app = focusOnApp();
app.scroll(0, app.scrollHeight);
}
function appScrollTop() {
const app = focusOnApp();
app.scroll(0, 0);
}
/**
*
* @param {KeyboardEvent} event
*/
function openSearchPage(event) {
const searchInput = document.querySelector(".main-topBar-topbarContentWrapper input");
if (searchInput) {
searchInput.focus();
} else {
const sidebarItem = document.querySelector(`.main-navBar-navBar a[href="/search"]`);
if (sidebarItem) {
sidebarItem.click();
}
}
event.preventDefault();
}
/**
*
* @param {Spicetify.Keyboard.ValidKey} keyName
* @param {boolean} ctrl
* @param {boolean} shift
* @param {boolean} alt
* @param {(event: KeyboardEvent) => void} callback
*/
function registerBind(keyName, ctrl, shift, alt, callback) {
const key = Spicetify.Keyboard.KEYS[keyName];
Spicetify.Keyboard.registerShortcut(
{
key,
ctrl,
shift,
alt,
},
(event) => {
if (!vim.isActive) {
callback(event);
}
},
);
}
function focusOnApp() {
return document.querySelector("main .os-viewport");
}
/**
* @returns {number}
*/
function findActiveIndex(allItems) {
const active = document.querySelector(
".main-navBar-navBarLinkActive, .main-collectionLinkButton-selected, .main-rootlist-rootlistItemLinkActive"
);
if (!active) {
return -1;
}
let index = 0;
for (const item of allItems) {
if (item === active) {
return index;
}
index++;
}
}
/**
*
* @param {1 | -1} direction
*/
function rotateSidebar(direction) {
const allItems = document.querySelectorAll(
".main-navBar-navBarLink, .main-collectionLinkButton-collectionLinkButton, .main-rootlist-rootlistItemLink"
);
const maxIndex = allItems.length - 1;
let index = findActiveIndex(allItems) + direction;
if (index < 0) index = maxIndex;
else if (index > maxIndex) index = 0;
let toClick = allItems[index];
if (!toClick.hasAttribute("href")) {
toClick = toClick.querySelector(".main-rootlist-rootlistItemLink");
}
toClick.click();
}
})();
function VimBind() {
const elementQuery = ["[href]", "button", "td.tl-play", "td.tl-number", "tr.TableRow"].join(",");
const keyList = "qwertasdfgzxcvyuiophjklbnm".split("");
const lastKeyIndex = keyList.length - 1;
this.isActive = false;
const vimOverlay = document.createElement("div");
vimOverlay.id = "vim-overlay";
vimOverlay.style.zIndex = "9999";
vimOverlay.style.position = "absolute";
vimOverlay.style.width = "100%";
vimOverlay.style.height = "100%";
vimOverlay.style.display = "none";
vimOverlay.innerHTML = `<style>
.vim-key {
position: fixed;
padding: 3px 6px;
background-color: black;
border-radius: 3px;
border: solid 2px white;
color: white;
text-transform: lowercase;
line-height: normal;
font-size: 14px;
font-weight: 500;
}
</style>`;
document.body.append(vimOverlay);
const mousetrap = new Spicetify.Mousetrap(document);
mousetrap.bind(keyList, listenToKeys.bind(this), "keypress");
// Pause mousetrap event emitter
const orgStopCallback = mousetrap.stopCallback;
mousetrap.stopCallback = () => true;
/**
*
* @param {KeyboardEvent} event
*/
this.activate = function (event) {
vimOverlay.style.display = "block";
const vimkey = getVims();
if (vimkey.length > 0) {
vimkey.forEach((e) => e.remove());
return;
}
let firstKey = 0;
let secondKey = 0;
getLinks().forEach((e) => {
if (e.style.display === "none" || e.style.visibility === "hidden" || e.style.opacity === "0") {
return;
}
const bound = e.getBoundingClientRect();
let owner = document.body;
let top = bound.top;
let left = bound.left;
if (
bound.bottom > owner.clientHeight ||
bound.left > owner.clientWidth ||
bound.right < 0 ||
bound.top < 0 ||
bound.width === 0 ||
bound.height === 0
) {
return;
}
vimOverlay.append(createKey(e, keyList[firstKey] + keyList[secondKey], top, left));
secondKey++;
if (secondKey > lastKeyIndex) {
secondKey = 0;
firstKey++;
}
});
this.isActive = true;
setTimeout(() => (mousetrap.stopCallback = orgStopCallback.bind(mousetrap)), 100);
};
/**
*
* @param {KeyboardEvent} event
*/
this.deactivate = function (event) {
mousetrap.stopCallback = () => true;
this.isActive = false;
vimOverlay.style.display = "none";
getVims().forEach((e) => e.remove());
};
function getLinks() {
const elements = Array.from(document.querySelectorAll(elementQuery));
return elements;
}
function getVims() {
return Array.from(vimOverlay.getElementsByClassName("vim-key"));
}
/**
* @param {KeyboardEvent} event
*/
function listenToKeys(event) {
if (!this.isActive) {
return;
}
const vimkey = getVims();
if (vimkey.length === 0) {
this.deactivate(event);
return;
}
for (const div of vimkey) {
const text = div.innerText.toLowerCase();
if (text[0] !== event.key) {
div.remove();
continue;
}
const newText = text.slice(1);
if (newText.length === 0) {
click(div.target);
this.deactivate(event);
return;
}
div.innerText = newText;
}
if (vimOverlay.childNodes.length === 1) {
this.deactivate(event);
}
}
function click(element) {
if (element.hasAttribute("href") || element.tagName === "BUTTON") {
element.click();
return;
}
const findButton = element.querySelector(`button[data-ta-id="play-button"]`) || element.querySelector(`button[data-button="play"]`);
if (findButton) {
findButton.click();
return;
}
alert("Let me know where you found this button, please. I can't click this for you without that information.");
return;
// TableCell case where play button is hidden
// Index number is in first column
const index = parseInt(element.firstChild.innerText) - 1;
const context = getContextUri();
if (index >= 0 && context) {
console.log(index);
console.log(context);
//Spicetify.PlaybackControl.playFromResolver(context, { index }, () => {});
return;
}
}
function createKey(target, key, top, left) {
const div = document.createElement("span");
div.classList.add("vim-key");
div.innerText = key;
div.style.top = top + "px";
div.style.left = left + "px";
div.target = target;
return div;
}
function getContextUri() {
const username = __spotify.username;
const activeApp = localStorage.getItem(username + ":activeApp");
if (activeApp) {
try {
return JSON.parse(activeApp).uri.replace("app:", "");
} catch {
return null;
}
}
return null;
}
/**
*
* @param {Spicetify.Keyboard.ValidKey} key
*/
this.setCancelKey = function (key) {
mousetrap.bind(Spicetify.Keyboard.KEYS[key], this.deactivate.bind(this));
};
return this;
}

View file

@ -1,3 +0,0 @@
var playlistDicons=(()=>{var s=Object.create,o=Object.defineProperty,c=Object.getOwnPropertyDescriptor,p=Object.getOwnPropertyNames,d=Object.getPrototypeOf,m=Object.prototype.hasOwnProperty,e=(e=>"undefined"!=typeof require?require:"undefined"!=typeof Proxy?new Proxy(e,{get:(e,t)=>("undefined"!=typeof require?require:e)[t]}):e)(function(e){if("react"===e)return Spicetify.React;if("react-dom"===e)return Spicetify.ReactDOM;if("undefined"!=typeof require)return require.apply(this,arguments);throw new Error('Dynamic require of "'+e+'" is not supported')}),t=(e,t,i)=>{i=null!=e?s(d(e)):{};var a=!t&&e&&e.__esModule?i:o(i,"default",{value:e,enumerable:!0}),n=e,l=void 0,r=void 0;if(n&&"object"==typeof n||"function"==typeof n)for(let e of p(n))m.call(a,e)||e===l||o(a,e,{get:()=>n[e],enumerable:!(r=c(n,e))||r.enumerable});return a},u=t(e("react")),y=t(e("react-dom"));var i=t(e("react"));function g(){return i.default.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",height:"24px",viewBox:"0 0 24 24",width:"24px",fill:"#FFFFFF"},i.default.createElement("path",{d:"M0 0h24v24H0V0z",fill:"none"}),i.default.createElement("path",{d:"M9.17 6l2 2H20v10H4V6h5.17M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"}))}var f=new Map;async function r(o){const e=await new Promise(t=>{const i=setInterval(()=>{var e=document.querySelectorAll("#spicetify-playlist-list li a");0<e.length&&(clearInterval(i),t(Array.from(e)))},100)});e.forEach(e=>{var t,i;const a=e.href.split("/").at(-1);var n=e.href.split("/").at(-2),l=f.get(a);if(null!=(t=e.parentElement)&&t.classList.add("playlist-item"),l)null!=(t=e.parentElement)&&t.prepend(l);else switch(n){case"playlist":var r=o.find(e=>e.id===a),r=function(e){const t=document.createElement(e?"img":"div");return t.classList.add("playlist-item__img"),e?t.setAttribute("src",e):t.classList.add("no-icon"),t}((null==(r=null==r?void 0:r.images[0])?void 0:r.url)||"");null!=(i=e.parentElement)&&i.prepend(r),f.set(a,r);break;case"folder":{const s=document.createElement("div");s.classList.add("playlist-item__img","folder"),y.default.render(u.default.createElement(g,null),s),null!=(i=e.parentElement)&&i.prepend(s),f.set(a,s);break}default:console.warn("[playlist-icons] playlist list anchor type not recognized: "+n)}})}var v="playlist-icons_big";var a=async function(){for(var e,a;null==Spicetify||!Spicetify.Platform||null==Spicetify||!Spicetify.CosmosAsync;)await new Promise(e=>setTimeout(e,100));const t=null!=(e=JSON.parse(localStorage.getItem(v)))&&e,i=await async function e(t){t=await Spicetify.CosmosAsync.get(t);return[...t.items,...t.next?await e(t.next):[]]}("https://api.spotify.com/v1/me/playlists?limit=50"),n=(a="#spicetify-playlist-list",await new Promise(t=>{const i=setInterval(()=>{var e=document.querySelector(a);e&&(clearInterval(i),t(e))},100)})),l=new MutationObserver(async()=>{l.disconnect(),await r(i),l.observe(n,{childList:!0,subtree:!0})});await r(i),l.observe(n,{childList:!0,subtree:!0}),t&&n.classList.add("big-icons"),new Spicetify.Menu.Item("Big playlist icons",t,e=>{e.setState(!e.isEnabled),localStorage.setItem(v,JSON.stringify(!t)),n.classList.toggle("big-icons")}).register()};(async()=>{await a()})()})();(async()=>{var e;document.getElementById("playlistDicons")||((e=document.createElement("style")).id="playlistDicons",e.textContent=String.raw`
:root{---playlist-img-spacing:6px}.playlist-item{padding-top:0;padding-bottom:0;align-items:center}.playlist-item__img{width:1.5em;height:1.5em;border-radius:2px;margin-right:12px;filter:brightness(85%)}.playlist-item__img.folder{background-color:var(--spice-tab-active);display:flex;align-items:center;justify-content:center}.playlist-item__img.folder svg{width:1.1em;height:1.1em}.playlist-item__img.no-icon{background-color:var(--spice-tab-active);height:1.5em}.playlist-item:hover .playlist-item__img{transition:.2s ease-out;filter:brightness(100%)}.big-icons .playlist-item{padding-top:var(---playlist-img-spacing);padding-bottom:var(---playlist-img-spacing)}.big-icons .playlist-item__img{border-radius:4px;width:2em;height:2em}.big-icons .playlist-item__img.folder{padding:4px}.big-icons .playlist-item__img.folder svg{width:1.5em;height:1.5em}.big-icons>div{contain:unset}
`.trim(),document.head.appendChild(e))})();

File diff suppressed because it is too large Load diff

View file

@ -1,32 +0,0 @@
// @ts-check
// NAME: Add Volume Percentage
// AUTHOR: daksh2k
// DESCRIPTION: Add the Volume Percentage to the Volume Bar
/// <reference path="../globals.d.ts" />
(function addVolumep() {
const volumeBar = document.querySelector(".volume-bar");
if (!(volumeBar && Spicetify.Player)) {
setTimeout(addVolumep, 200);
return;
}
const ele = document.createElement("span");
ele.classList.add("volume-percent");
ele.setAttribute("style", "font-size: 14px; padding-left: 10px; min-width: 45px;");
volumeBar.append(ele);
// @ts-ignore
volumeBar.style.flex = "0 1 180px";
updatePercentage();
function updatePercentage() {
const currVolume = Math.round((Spicetify.Player?.origin?._volume?._volume ?? Spicetify.Platform?.PlaybackAPI?._volume) * 100);
ele.innerText = currVolume == -100 ? `` : `${currVolume}%`;
// @ts-ignore
document.querySelector(".main-connectBar-connectBar")?.style.setProperty("--triangle-position", "229px");
}
if (Spicetify.Platform?.PlaybackAPI === undefined) Spicetify.Player.origin._events.addListener("volume", updatePercentage);
else Spicetify.Platform.PlaybackAPI._events.addListener("volume", updatePercentage);
})();

View file

@ -1,96 +0,0 @@
//@ts-check
// NAME: Wikify
// AUTHOR: CharlieS1103
// DESCRIPTION: View an artists wikipedia page to learn more about them
/// <reference path="../../spicetify-cli/globals.d.ts" />
(function WikiFy() {
if (!document.body.classList.contains('wikify-injected')) {
var styleSheet = document.createElement("style")
styleSheet.innerHTML =
`body > generic-modal > div > div {
background-color: beige !important;
color: black !important;
} `
document.body.appendChild(styleSheet)
document.body.classList.add('wikify-injected');
}
const {
CosmosAsync,
URI
} = Spicetify;
if (!(CosmosAsync && URI)) {
setTimeout(WikiFy, 10);
return;
}
const lang = Spicetify.Locale._locale;
const buttontxt = "View Wiki"
//Watch for when the song is changed
function error() {
Spicetify.PopupModal.display({
title: "Error",
content: "Selected artist does not have a WikiPedia page, Sorry."
});
}
async function getWikiText(uris) {
const rawUri = uris[0];
const uri = rawUri.split(":")[2]
const artistName = await CosmosAsync.get(`https://api.spotify.com/v1/artists/${uri}`)
const artistNameTrimmed = (artistName.name).replace(/\s/g, "%20");
if (artistName != null) {
try {
const wikiInfo = await CosmosAsync.get(`https://${lang}.wikipedia.org/w/api.php?action=query&format=json&prop=extracts%7Cdescription&titles=${artistNameTrimmed}`)
//TODO: option to choose local language or english / english fallback? / subcontextmenu to choose?
//https://en.wikipedia.org/w/api.php?action=query&format=json&uselang=en&list=search&srsearch=${artistNameTrimmed}
const wikiInfoArr = wikiInfo.query.pages
const page = Object.values(wikiInfoArr)[0];
if (page != null || page != undefined) {
const pageText = page.extract.replace(/<!--[\s\S]*?-->/g, '');
if (pageText != "\n") {
Spicetify.PopupModal.display({
title: "WikiFy",
content: page.extract
});
} else {
error();
}
} else {
error();
}
} catch {
Spicetify.PopupModal.display({
title: "Error",
content: "Request failed",
})
}
}
}
function shouldDisplayContextMenu(uris) {
if (uris.length > 1) {
return false;
}
const uri = uris[0];
const uriObj = Spicetify.URI.fromString(uri);
if (uriObj.type === Spicetify.URI.Type.TRACK || uriObj.type === Spicetify.URI.Type.ARTIST) {
return true;
}
return false;
}
const cntxMenu = new Spicetify.ContextMenu.Item(
buttontxt,
getWikiText,
shouldDisplayContextMenu,
);
cntxMenu.register();
})();

View file

@ -1,761 +0,0 @@
{
"Color-Scheme": "catppuccin-macchiato",
"Scheme-Features": "mono",
"Flatten-Colors": "normal",
"Dark-Modals": "dark",
"App-Title": "",
"Button-Radius": "10",
"Custom-Font": true,
"Home-Header-Snippet": true,
"Home-Header-Color": "",
"Topbar-Inside-Titlebar-Snippet": false,
"Horizontal-pageLinks-Snippet": false,
"visible-column-bar-Snippet": false,
"Tracklist-Gradient-Height": "",
"Hoverable-Timers-Snippet": false,
"Remove-Device-Picker-Notification-Snippet": true,
"Remove-Progress-Bar-Gradient-Snippet": false,
"Remove-Lyrics-Button-Snippet": true,
"Custom-Cover-Art-Dimensions": false,
"Right-Art-Snippet": false,
"Image-Blur": "",
"Apple-Music-Gradient-Snippet": true,
"Custom-Image": true,
"Color-Schemes": {
"Comfy": {
"text": "FFFFFF",
"subtext": "B9BBBE",
"main": "23283D",
"main-elevated": "32364A",
"main-transition": "1E2233",
"highlight": "45495B",
"highlight-elevated": "383D50",
"sidebar": "1E2233",
"player": "101320",
"card": "101320",
"shadow": "1E2233",
"selected-row": "F1F1F1",
"button": "7289DA",
"button-active": "5C6FB1",
"button-disabled": "4B588C",
"tab-active": "1E2233",
"notification": "7289DA",
"notification-error": "D25050",
"misc": "000000",
"play-button": "7289DA",
"play-button-active": "869ADF",
"progress-fg": "1ED760",
"progress-bg": "7289DA",
"heart": "D25050",
"pagelink-active": "5C6EB1",
"radio-btn-active": "7289DA"
},
"Spotify": {
"text": "FFFFFF",
"subtext": "B3B3B3",
"main": "121212",
"main-elevated": "242424",
"main-transition": "000000",
"highlight": "1A1A1A",
"highlight-elevated": "2A2A2A",
"sidebar": "000000",
"player": "181818",
"card": "282828",
"shadow": "000000",
"selected-row": "FFFFFF",
"button": "1DB954",
"button-active": "1ED760",
"button-disabled": "535353",
"tab-active": "333333",
"notification": "4687D6",
"notification-error": "E22134",
"misc": "7F7F7F",
"play-button": "1DB954",
"play-button-active": "1DB954",
"progress-fg": "1DB954",
"progress-bg": "636363",
"heart": "1DB954",
"pagelink-active": "333333",
"radio-btn-active": "1DB954"
},
"Nord": {
"text": "B2BCCC",
"subtext": "B2BCCC",
"main": "2E3440",
"main-elevated": "3C414C",
"main-transition": "262B35",
"highlight": "50555F",
"highlight-elevated": "424852",
"sidebar": "262B35",
"player": "2E3440",
"card": "363D4C",
"shadow": "1D2128",
"selected-row": "B2BCCC",
"button": "8A99AF",
"button-active": "718CAD",
"button-disabled": "434C5E",
"tab-active": "363D4C",
"notification": "363D4C",
"notification-error": "A9555E",
"misc": "FFFFFF",
"play-button": "8A99AF",
"play-button-active": "718CAD",
"progress-fg": "8A99AF",
"progress-bg": "4B566A",
"heart": "718CAD",
"pagelink-active": "B7C1D5",
"radio-btn-active": "363D4C"
},
"Everforest": {
"text": "D3C6AA",
"subtext": "9AA79D",
"main": "272E33",
"main-elevated": "30363A",
"main-transition": "495156",
"highlight": "444747",
"highlight-elevated": "34393D",
"sidebar": "2E383C",
"player": "272E33",
"card": "374145",
"shadow": "374145",
"selected-row": "D3C6AA",
"button": "A7C080",
"button-active": "AEC984",
"button-disabled": "374145",
"tab-active": "181C1E",
"notification": "86AF87",
"notification-error": "E67E80",
"misc": "000000",
"play-button": "A7C080",
"play-button-active": "AEC984",
"progress-fg": "A7C080",
"progress-bg": "9DA9A0",
"heart": "A7C080",
"pagelink-active": "181C1E",
"radio-btn-active": "86AF87"
},
"Kanagawa": {
"text": "545464",
"subtext": "545464",
"main": "F2EBBC",
"main-elevated": "E6DFB7",
"main-transition": "D5CEA3",
"highlight": "D5CfAf",
"highlight-elevated": "E0DAB4",
"sidebar": "E7DBA0",
"player": "F2EBBC",
"card": "D5CEA3",
"shadow": "E0DAB4",
"selected-row": "43436C",
"button": "43242B",
"button-active": "39395D",
"button-disabled": "D5CEA3",
"tab-active": "E7DBA0",
"notification": "43242B",
"notification-error": "E82423",
"misc": "000000",
"play-button": "43242B",
"play-button-active": "43242B",
"progress-fg": "43242B",
"progress-bg": "77713E",
"heart": "E82423",
"pagelink-active": "FFFFFF",
"radio-btn-active": "E7DBA0"
},
"Houjicha": {
"text": "B59F92",
"subtext": "997B6F",
"main": "473F3C",
"main-elevated": "534C49",
"main-transition": "5C514D",
"highlight": "574F4B",
"highlight-elevated": "59524F",
"sidebar": "514741",
"player": "473F3C",
"card": "78645C",
"shadow": "78645C",
"selected-row": "A49A96",
"button": "6E916F",
"button-active": "B3A49A",
"button-disabled": "67564D",
"tab-active": "6D5D54",
"notification": "86AF87",
"notification-error": "D25050",
"misc": "000000",
"play-button": "B59F92",
"play-button-active": "C7AfA1",
"progress-fg": "86AF87",
"progress-bg": "B59F92",
"heart": "86AF87",
"pagelink-active": "FFFFFF",
"radio-btn-active": "877063"
},
"Kitty": {
"text": "FFFFFF",
"subtext": "FFDDDC",
"main": "C94985",
"main-elevated": "CD548C",
"main-transition": "DE4B90",
"highlight": "DE4B90",
"highlight-elevated": "CF598D",
"sidebar": "E072A6",
"player": "941550",
"card": "DE5BA5",
"shadow": "2B0718",
"selected-row": "FFDDDC",
"button": "941550",
"button-active": "FFFFFF",
"button-disabled": "941550",
"tab-active": "E072A6",
"notification": "E072A6",
"notification-error": "994B6F",
"misc": "000000",
"play-button": "FFFFFF",
"play-button-active": "A13267",
"progress-fg": "DE4B90",
"progress-bg": "994B6F",
"heart": "A13267",
"pagelink-active": "DE4B90",
"radio-btn-active": "994B6F"
},
"Lunar": {
"text": "F3F3F3",
"subtext": "CECECE",
"main": "161616",
"main-elevated": "232323",
"main-transition": "101010",
"highlight": "2A2A2A",
"highlight-elevated": "292929",
"sidebar": "202020",
"player": "161616",
"card": "303030",
"shadow": "252525",
"selected-row": "CECECE",
"button": "3281EA",
"button-active": "0284E8",
"button-disabled": "303030",
"tab-active": "EBBCBA",
"notification": "3281EA",
"notification-error": "B10C0C",
"misc": "252525",
"play-button": "EBBCBA",
"play-button-active": "EBA9A7",
"progress-fg": "025CA1",
"progress-bg": "202020",
"heart": "EBBCBA",
"pagelink-active": "FFFFFF",
"radio-btn-active": "0284E8"
},
"Deep": {
"text": "4F9A87",
"subtext": "406560",
"main": "040614",
"main-elevated": "0A111D",
"main-transition": "0F111A",
"highlight": "0F1F28",
"highlight-elevated": "0C1520",
"sidebar": "0F111A",
"player": "040614",
"card": "0F1118",
"shadow": "0F1118",
"selected-row": "4F9A87",
"button": "106165",
"button-active": "4F9A87",
"button-disabled": "0C1C19",
"tab-active": "0A1527",
"notification": "051024",
"notification-error": "051024",
"misc": "406560",
"play-button": "106165",
"play-button-active": "4F9A87",
"progress-fg": "4F9A87",
"progress-bg": "106165",
"heart": "D25050",
"pagelink-active": "4F9A87",
"radio-btn-active": "106165"
},
"Velvet": {
"text": "AD434E",
"subtext": "762F37",
"main": "1E1E1E",
"main-elevated": "272021",
"main-transition": "161616",
"highlight": "322324",
"highlight-elevated": "2A2122",
"sidebar": "161616",
"player": "080808",
"card": "0F0F0F",
"shadow": "161616",
"selected-row": "973B45",
"button": "6B2B32",
"button-active": "552328",
"button-disabled": "262626",
"tab-active": "161616",
"notification": "6B2B32",
"notification-error": "60272D",
"misc": "000000",
"play-button": "552328",
"play-button-active": "6B2B32",
"progress-fg": "81333B",
"progress-bg": "A23F49",
"heart": "60272D",
"pagelink-active": "4A1F23",
"radio-btn-active": "6B2B32"
},
"Yami": {
"text": "D4D4D4",
"subtext": "D4D4D4",
"main": "0A0A10",
"main-elevated": "0D0D14",
"main-transition": "0A0A10",
"highlight": "191923",
"highlight-elevated": "0E0E15",
"sidebar": "0A0A10",
"player": "0A0A10",
"card": "191923",
"shadow": "AF8BF3",
"selected-row": "353447",
"button": "191923",
"button-active": "353447",
"button-disabled": "111117",
"tab-active": "191923",
"notification": "0A0A10",
"notification-error": "F7407B",
"misc": "0A0A10",
"play-button": "2B2A3D",
"play-button-active": "353447",
"progress-fg": "F7407B",
"progress-bg": "0A0A10",
"heart": "F7407B",
"pagelink-active": "F7407B",
"radio-btn-active": "191923"
},
"Hikari": {
"text": "CDBBF9",
"subtext": "8A7EA8",
"main": "050406",
"main-elevated": "0D0D14",
"main-transition": "0B0910",
"highlight": "1B1923",
"highlight-elevated": "0E0E15",
"sidebar": "0D0D14",
"player": "0D0D14",
"card": "1F132D",
"shadow": "1F132D",
"selected-row": "F1F1F1",
"button": "7F00E8",
"button-active": "7F00E8",
"button-disabled": "1C0033",
"tab-active": "1B1923",
"notification": "0A0A10",
"notification-error": "7F00E8",
"misc": "0A0A10",
"play-button": "CDBBF9",
"play-button-active": "8A7EA8",
"progress-fg": "7F00E8",
"progress-bg": "0D0D14",
"heart": "CDBBF9",
"pagelink-active": "1B1923",
"radio-btn-active": "191923"
},
"catppuccin-latte": {
"text": "4C4F69",
"subtext": "5C5F77",
"main": "E6E9EF",
"main-elevated": "DCE0E6",
"main-transition": "E6E9EF",
"highlight": "CED3DB",
"highlight-elevated": "D8DBE3",
"sidebar": "EFF1F5",
"player": "E6E9EF",
"card": "BCC0CC",
"shadow": "E6E9EF",
"selected-row": "5C5F77",
"button": "1E66F5",
"button-active": "209FB5",
"button-disabled": "BCC0CC",
"tab-active": "CCD0DA",
"notification": "1E66F5",
"notification-error": "D20F39",
"misc": "ACB0BE",
"play-button": "EA76CB",
"play-button-active": "DD7878",
"progress-fg": "04A5E5",
"progress-bg": "BCC0CC",
"heart": "FE640B",
"pagelink-active": "FFFFFF",
"radio-btn-active": "209FB5"
},
"catppuccin-frappe": {
"text": "C6D0F5",
"subtext": "B5BFE2",
"main": "292C3C",
"main-elevated": "333648",
"main-transition": "292C3C",
"highlight": "3B4058",
"highlight-elevated": "373B4D",
"sidebar": "303446",
"player": "292C3C",
"card": "51576D",
"shadow": "626880",
"selected-row": "B5BFE2",
"button": "8CAAEE",
"button-active": "85C1DC",
"button-disabled": "51576D",
"tab-active": "414559",
"notification": "8CAAEE",
"notification-error": "E78284",
"misc": "626880",
"play-button": "F4B8E4",
"play-button-active": "EECEBE",
"progress-fg": "99D1DB",
"progress-bg": "51576D",
"heart": "EF9F76",
"pagelink-active": "FFFFFF",
"radio-btn-active": "85C1DC"
},
"catppuccin-macchiato": {
"text": "CAD3F5",
"subtext": "B8C0E0",
"main": "181926",
"main-elevated": "232533",
"main-transition": "181926",
"highlight": "333645",
"highlight-elevated": "292A39",
"sidebar": "24273A",
"player": "181926",
"card": "494D64",
"shadow": "5B6078",
"selected-row": "B8C0E0",
"button": "8AADF4",
"button-active": "7DC4E4",
"button-disabled": "494D64",
"tab-active": "363A4F",
"notification": "8AADF4",
"notification-error": "ED8796",
"misc": "5B6078",
"play-button": "F5BDE6",
"play-button-active": "F0C6C6",
"progress-fg": "91D7E3",
"progress-bg": "494D64",
"heart": "F5A97F",
"pagelink-active": "FFFFFF",
"radio-btn-active": "7DC4E4"
},
"catppuccin-mocha": {
"text": "CDD6F4",
"subtext": "BAC2DE",
"main": "181825",
"main-elevated": "232432",
"main-transition": "181825",
"highlight": "333645",
"highlight-elevated": "292A38",
"sidebar": "1E1E2E",
"player": "181825",
"card": "45475A",
"shadow": "585B70",
"selected-row": "BAC2DE",
"button": "89B4FA",
"button-active": "74C7EC",
"button-disabled": "45475A",
"tab-active": "313244",
"notification": "89B4FA",
"notification-error": "F38BA8",
"misc": "585B70",
"play-button": "F5C2E7",
"play-button-active": "F2CDCD",
"progress-fg": "89DCEB",
"progress-bg": "45475A",
"heart": "FAB387",
"pagelink-active": "FFFFFF",
"radio-btn-active": "74C7EC"
},
"rose-pine": {
"text": "E0DEF4",
"subtext": "908CAA",
"main": "1F1D2E",
"main-elevated": "2D2B3C",
"main-transition": "26233A",
"highlight": "403D52",
"highlight-elevated": "333142",
"sidebar": "191724",
"player": "1F1D2E",
"card": "403D52",
"shadow": "524F67",
"selected-row": "E0DEF4",
"button": "EBBCBA",
"button-active": "EBBCBA",
"button-disabled": "6E6A86",
"tab-active": "524F67",
"notification": "9CCFD8",
"notification-error": "EB6F92",
"misc": "FFFFFF",
"play-button": "EBBCBA",
"play-button-active": "EBBCBA",
"progress-fg": "EBBCBA",
"progress-bg": "403D52",
"heart": "EB6F92",
"pagelink-active": "EBBCBA",
"radio-btn-active": "9CCFD8"
},
"rose-pine-moon": {
"text": "E0DEF4",
"subtext": "908CAA",
"main": "2A273F",
"main-elevated": "37344C",
"main-transition": "393552",
"highlight": "44415A",
"highlight-elevated": "3D3A52",
"sidebar": "232136",
"player": "2A273F",
"card": "44415A",
"shadow": "56526E",
"selected-row": "E0DEF4",
"button": "EA9A97",
"button-active": "EA9A97",
"button-disabled": "6E6A86",
"tab-active": "56526E",
"notification": "9CCFD8",
"notification-error": "EB6F92",
"misc": "FFFFFF",
"play-button": "EA9A97",
"play-button-active": "EA9A97",
"progress-fg": "EA9A97",
"progress-bg": "44415A",
"heart": "EB6F92",
"pagelink-active": "EA9A97",
"radio-btn-active": "9CCFD8"
},
"rose-pine-dawn": {
"text": "575279",
"subtext": "797593",
"main": "FFFAF3",
"main-elevated": "F3EEEB",
"main-transition": "F2E9E1",
"highlight": "DFDAD9",
"highlight-elevated": "EEE9E6",
"sidebar": "FAF4ED",
"player": "FFFAF3",
"card": "DFDAD9",
"shadow": "FAF4ED",
"selected-row": "575279",
"button": "D7827E",
"button-active": "D7827E",
"button-disabled": "9893A5",
"tab-active": "CECACD",
"notification": "56949F",
"notification-error": "B4637A",
"misc": "FFFFFF",
"play-button": "D7827E",
"play-button-active": "D7827E",
"progress-fg": "D7827E",
"progress-bg": "DFDAD9",
"heart": "B4637A",
"pagelink-active": "D7827E",
"radio-btn-active": "907AA9"
},
"Mono": {
"text": "FFFFFF",
"subtext": "B9BBBE",
"main": "171717",
"main-elevated": "262626",
"main-transition": "FFFFFF",
"highlight": "3B3B3B",
"highlight-elevated": "2E2E2E",
"sidebar": "101010",
"player": "171717",
"card": "343434",
"shadow": "595858",
"selected-row": "F1F1F1",
"button": "FFFFFF",
"button-active": "C5C5C5",
"button-disabled": "4A4949",
"tab-active": "303030",
"notification": "101010",
"notification-error": "D25050",
"misc": "000000",
"play-button": "FFFFFF",
"play-button-active": "FFFFFF",
"progress-fg": "FFFFFF",
"progress-bg": "343434",
"heart": "FFFFFF",
"pagelink-active": "787878",
"radio-btn-active": "737373"
},
"Sunset": {
"text": "FFCE3F",
"subtext": "FEF3BB",
"main": "171717",
"main-elevated": "272622",
"main-transition": "000000",
"highlight": "3D3C32",
"highlight-elevated": "2F2E28",
"sidebar": "101010",
"player": "171717",
"card": "CC9756",
"shadow": "E3B47B",
"selected-row": "FEF3BB",
"button": "FFCE3F",
"button-active": "BF9B30",
"button-disabled": "4A4949",
"tab-active": "303030",
"notification": "FFFFFF",
"notification-error": "D25050",
"misc": "000000",
"play-button": "FFCE3F",
"play-button-active": "FC9E3A",
"progress-fg": "FF8300",
"progress-bg": "343434",
"heart": "FF8300",
"pagelink-active": "FEF3BB",
"radio-btn-active": "FEF3BB"
},
"Neon": {
"text": "588BAE",
"subtext": "EAFFFF",
"main": "171717",
"main-elevated": "262626",
"main-transition": "000000",
"highlight": "3B3B3B",
"highlight-elevated": "2E2E2E",
"sidebar": "101010",
"player": "171717",
"card": "7FA1B5",
"shadow": "A9C9DB",
"selected-row": "F1F1F1",
"button": "588BAE",
"button-active": "3B5D75",
"button-disabled": "4A4949",
"tab-active": "303030",
"notification": "FFFFFF",
"notification-error": "D25050",
"misc": "000000",
"play-button": "588BAE",
"play-button-active": "5085AB",
"progress-fg": "00AFDB",
"progress-bg": "343434",
"heart": "00AFDB",
"pagelink-active": "BBE7FE",
"radio-btn-active": "EAFFFF"
},
"Forest": {
"text": "B2C5B3",
"subtext": "D5DDDE",
"main": "171717",
"main-elevated": "262626",
"main-transition": "000000",
"highlight": "3B3B3B",
"highlight-elevated": "2E2E2E",
"sidebar": "101010",
"player": "171717",
"card": "5C6E59",
"shadow": "3C5148",
"selected-row": "F1F1F1",
"button": "B2C5B3",
"button-active": "F1F1F1",
"button-disabled": "4A4949",
"tab-active": "303030",
"notification": "FFFFFF",
"notification-error": "D25050",
"misc": "000000",
"play-button": "3C5148",
"play-button-active": "43705D",
"progress-fg": "3C5148",
"progress-bg": "343434",
"heart": "3C5148",
"pagelink-active": "3C5148",
"radio-btn-active": "737373"
},
"Sakura": {
"text": "FCB4CA",
"subtext": "FFDCDC",
"main": "171717",
"main-elevated": "262626",
"main-transition": "000000",
"highlight": "3D3838",
"highlight-elevated": "2E2E2E",
"sidebar": "101010",
"player": "171717",
"card": "D68BA2",
"shadow": "FCB4CA",
"selected-row": "FFDCDC",
"button": "FCB4CA",
"button-active": "D48AA0",
"button-disabled": "4A4949",
"tab-active": "303030",
"notification": "FFFFFF",
"notification-error": "D25050",
"misc": "000000",
"play-button": "F42C38",
"play-button-active": "BA182B",
"progress-fg": "CFEEFA",
"progress-bg": "343434",
"heart": "F25477",
"pagelink-active": "F5BCDB",
"radio-btn-active": "FFDCDC"
},
"Vaporwave": {
"text": "01CDFE",
"subtext": "EAFFFF",
"main": "171717",
"main-elevated": "262626",
"main-transition": "000000",
"highlight": "3B3B3B",
"highlight-elevated": "2E2E2E",
"sidebar": "101010",
"player": "171717",
"card": "007F9E",
"shadow": "2EC2E6",
"selected-row": "F1F1F1",
"button": "01CDFE",
"button-active": "118BA8",
"button-disabled": "4A4949",
"tab-active": "303030",
"notification": "FFFFFF",
"notification-error": "D25050",
"misc": "000000",
"play-button": "FFD300",
"play-button-active": "E3C01B",
"progress-fg": "F706CF",
"progress-bg": "343434",
"heart": "F706CF",
"pagelink-active": "C0D6FA",
"radio-btn-active": "EAFFFF"
}
},
"Font": "JetBrainsMono Nerd Font",
"Home-Header-Snippet-Collapsed": true,
"Custom-Font-Collapsed": false,
"Gradient-Noise": "",
"Gradient-Blend": "",
"Gradient-Speed": "",
"Gradient-Size": "",
"Gradient-Radius": "",
"Apple-Music-Gradient-Snippet-Collapsed": true,
"Custom-Image-URL": "images/kurz.png",
"Custom-Playbar-Snippet": true,
"AM-Gradient-Include-Existing-Snippet": false,
"Banner-Enabled": true,
"Remove-Tracklist-Index": true,
"Remove-Column-Bar-Snippet": true,
"Tracklist-Gradient-Opacity": "",
"Collapse-Topbar-Snippet": true,
"Topbar-Inside-Titlebar-Snippet-Collapsed": true,
"Smooth-Progress-Bar-Snippet": true,
"Remove-Connect-Bar-Snippet": true,
"Remove-Tracklist-Gradient-Noise": false,
"App-Titlebar-Height": "",
"Replace-Existing-Banners": false,
"Prefer-Existing-Image": false,
"Header-Background": false,
"Tracklist-Header-Background": false,
"Tracklist-Header-Background-Opacity": "",
"Tracklist-Header-Background-Collapsed": false,
"Custom-Image-Collapsed": false,
"Cover-Art-Width": "",
"Cover-Art-Height": "",
"Cover-Art-Radius": "",
"Cover-Art-Left": "",
"Cover-Art-Bottom": "",
"Custom-Cover-Art-Dimensions-Collapsed": false
}

View file

@ -0,0 +1,2 @@
[Marketplace]

View file

@ -1,34 +1,32 @@
[Setting]
overwrite_assets = 0
spotify_launch_flags =
spotify_path = /opt/spotify/
prefs_path = /home/matt/.config/spotify/prefs
color_scheme =
inject_theme_js = 1
replace_colors = 1
current_theme = marketplace
inject_css = 1
check_spicetify_update = 1
always_enable_devtools = 0
[Preprocesses]
disable_ui_logging = 1
remove_rtl_rule = 1
expose_apis = 1
disable_upgrade_check = 1
disable_sentry = 1
expose_apis = 1
disable_sentry = 1
disable_ui_logging = 1
remove_rtl_rule = 1
[AdditionalOptions]
sidebar_config = 1
home_config = 1
experimental_features = 1
extensions = adblock.js|hidePodcasts.js|historyShortcut.js|copyPlaylist.js|wikify.js|fullScreen.js|volumePercentage.js|bookmark.js|loopyLoop.js|keyboardShortcutMy.js|genre.js|playlistIcons.js
custom_apps = lyrics-plus|marketplace|stats
extensions =
custom_apps = marketplace|stats|library|better-local-files
[Patch]
[Setting]
spotify_path = /opt/spotify
color_scheme = Comfy
overwrite_assets = 1
spotify_launch_flags =
check_spicetify_upgrade = 0
prefs_path = /home/matt/.config/spotify/prefs
current_theme = Comfy
inject_css = 1
replace_colors = 1
inject_theme_js = 1
check_spicetify_update = 1
always_enable_devtools = 0
; DO NOT CHANGE!
[Backup]
version = 1.1.72.439.gc253025e
with = 2.36.11
version = 1.2.37.701.ge66eb7bc
with = 2.36.13