CHONKY BOY

This commit is contained in:
Core 2022-08-04 05:27:29 +01:00
parent 31ed921a1a
commit c15f55d0ee
No known key found for this signature in database
GPG key ID: FE9BF1B547F8F3C6
213 changed files with 64188 additions and 55736 deletions

View file

@ -1 +1,2 @@
bracketSameLine: true bracketSameLine: true
printWidth: 240

View file

@ -1,4 +1,5 @@
let i = 1, k = 1; let i = 1,
k = 1;
class ExamplePlugin { class ExamplePlugin {
/** /**
* Private variables for interaction in plugins * Private variables for interaction in plugins
@ -10,10 +11,10 @@ class ExamplePlugin {
/** /**
* Base Plugin Details (Eventually implemented into a GUI in settings) * Base Plugin Details (Eventually implemented into a GUI in settings)
*/ */
public name: string = 'examplePlugin'; public name: string = "examplePlugin";
public description: string = 'Example plugin'; public description: string = "Example plugin";
public version: string = '1.0.0'; public version: string = "1.0.0";
public author: string = 'Example author'; public author: string = "Example author";
/** /**
* Runs on plugin load (Currently run on application start) * Runs on plugin load (Currently run on application start)
@ -44,8 +45,8 @@ class ExamplePlugin {
* @param attributes Music Attributes (attributes.status = current state) * @param attributes Music Attributes (attributes.status = current state)
*/ */
onPlaybackStateDidChange(attributes: object): void { onPlaybackStateDidChange(attributes: object): void {
console.log('onPlaybackStateDidChange has been called ' + i + ' times'); console.log("onPlaybackStateDidChange has been called " + i + " times");
i++ i++;
} }
/** /**
@ -53,10 +54,9 @@ class ExamplePlugin {
* @param attributes Music Attributes * @param attributes Music Attributes
*/ */
onNowPlayingItemDidChange(attributes: object): void { onNowPlayingItemDidChange(attributes: object): void {
console.log('onNowPlayingDidChange has been called ' + k + ' times'); console.log("onNowPlayingDidChange has been called " + k + " times");
k++ k++;
} }
} }
module.exports = ExamplePlugin; module.exports = ExamplePlugin;

View file

@ -2,10 +2,10 @@ class sendSongToTitlebar {
/** /**
* Base Plugin Details (Eventually implemented into a GUI in settings) * Base Plugin Details (Eventually implemented into a GUI in settings)
*/ */
public name: string = 'sendSongToTitlebar'; public name: string = "sendSongToTitlebar";
public description: string = 'Sets the app\'s titlebar to the Song title'; public description: string = "Sets the app's titlebar to the Song title";
public version: string = '0.0.1'; public version: string = "0.0.1";
public author: string = 'Cider Collective (credit to 8times9 via #147)'; public author: string = "Cider Collective (credit to 8times9 via #147)";
/** /**
* Runs on plugin load (Currently run on application start) * Runs on plugin load (Currently run on application start)
*/ */
@ -27,7 +27,7 @@ class sendSongToTitlebar {
* @param attributes Music Attributes (attributes.status = current state) * @param attributes Music Attributes (attributes.status = current state)
*/ */
onPlaybackStateDidChange(attributes: any): void { onPlaybackStateDidChange(attributes: any): void {
this._win.setTitle(`${(attributes != null && attributes.name != null && attributes.name.length > 0) ? (attributes.name + " - ") : ''}Cider`) this._win.setTitle(`${attributes != null && attributes.name != null && attributes.name.length > 0 ? attributes.name + " - " : ""}Cider`);
} }
/** /**
* Runs on song change * Runs on song change

View file

@ -8,29 +8,13 @@
"protocols": [ "protocols": [
{ {
"name": "Cider", "name": "Cider",
"schemes": [ "schemes": ["ame", "cider", "itms", "itmss", "musics", "music"]
"ame",
"cider",
"itms",
"itmss",
"musics",
"music"
]
} }
], ],
"extends": null, "extends": null,
"files": [ "files": ["**/*", "./src/**/*", "./resources/icons/icon.*"],
"**/*",
"./src/**/*",
"./resources/icons/icon.*"
],
"linux": { "linux": {
"target": [ "target": ["AppImage", "deb", "snap", "rpm"],
"AppImage",
"deb",
"snap",
"rpm"
],
"synopsis": "A new look into listening and enjoying music in style and performance. ", "synopsis": "A new look into listening and enjoying music in style and performance. ",
"category": "AudioVideo", "category": "AudioVideo",
"icon": "cider", "icon": "cider",
@ -45,9 +29,7 @@
"setBuildNumber": true "setBuildNumber": true
}, },
"win": { "win": {
"target": [ "target": ["appx"],
"appx"
],
"icon": "resources/icons/icon.ico" "icon": "resources/icons/icon.ico"
}, },
"directories": { "directories": {

View file

@ -35,51 +35,48 @@
"msft": "yarn build && electron-builder -c msft-package.json", "msft": "yarn build && electron-builder -c msft-package.json",
"mstest": "yarn build && electron-builder -c msft-test.json", "mstest": "yarn build && electron-builder -c msft-test.json",
"postinstall": "electron-builder install-app-deps", "postinstall": "electron-builder install-app-deps",
"prettier": "npx prettier --write '**/*.{js,json,ts}'" "prettier": "npx prettier --write '**/*.{js,json,ts,css,less}'"
}, },
"dependencies": { "dependencies": {
"@sentry/electron": "^3.0.7", "@sentry/electron": "^3.0.7",
"@sentry/integrations": "^6.19.6", "@sentry/integrations": "^7.8.1",
"@types/pouchdb": "^6.4.0", "@types/pouchdb": "^6.4.0",
"@types/pouchdb-node": "^6.1.4", "@types/pouchdb-node": "^6.1.4",
"adm-zip": "0.4.10", "adm-zip": "0.4.10",
"airtunes2": "git+https://github.com/ciderapp/node_airtunes2.git", "airtunes2": "git+https://github.com/ciderapp/node_airtunes2.git",
"castv2-client": "^1.2.0", "castv2-client": "^1.2.0",
"chokidar": "^3.5.3", "chokidar": "^3.5.3",
"discord-auto-rpc": "^1.0.16", "discord-auto-rpc": "^1.0.17",
"dns-js": "git+https://github.com/ciderapp/node-dns-js.git", "dns-js": "git+https://github.com/ciderapp/node-dns-js.git",
"ejs": "^3.1.6", "ejs": "^3.1.8",
"electron-fetch": "^1.7.4", "electron-fetch": "^1.7.4",
"electron-log": "^4.4.6", "electron-log": "^4.4.8",
"electron-notarize": "^1.2.1", "electron-notarize": "^1.2.1",
"electron-store": "^8.0.1", "electron-store": "^8.1.0",
"electron-updater": "^5.0.1", "electron-updater": "^5.2.1",
"electron-window-state": "^5.0.3", "electron-window-state": "^5.0.3",
"express": "^4.17.3", "express": "^4.18.1",
"get-port": "^5.1.1", "get-port": "^5.1.1",
"jimp": "^0.16.1", "jimp": "^0.16.1",
"jsonc": "^2.0.0",
"lastfmapi": "^0.1.1", "lastfmapi": "^0.1.1",
"level": "^8.0.0", "level": "^8.0.0",
"leveldown": "^6.1.1", "leveldown": "^6.1.1",
"mdns-js": "git+https://github.com/ciderapp/node-mdns-js.git", "mdns-js": "git+https://github.com/ciderapp/node-mdns-js.git",
"mpris-service": "^2.1.2", "mpris-service": "^2.1.2",
"music-metadata": "^7.12.4", "music-metadata": "^7.12.5",
"node-gyp": "^9.0.0", "node-gyp": "^9.1.0",
"node-ssdp": "^4.0.1", "node-ssdp": "^4.0.1",
"pouchdb-adapter-leveldb": "^7.3.0", "pouchdb-adapter-leveldb": "^7.3.0",
"pouchdb-node": "^7.3.0", "pouchdb-node": "^7.3.0",
"pouchdb-upsert": "^2.2.0", "pouchdb-upsert": "^2.2.0",
"qrcode": "^1.5.0", "qrcode": "^1.5.1",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"run-script-os": "^1.1.6",
"request": "^2.88.2", "request": "^2.88.2",
"run-script-os": "^1.1.6",
"source-map-support": "^0.5.21", "source-map-support": "^0.5.21",
"ts-md5": "^1.2.11", "ts-md5": "^1.2.11",
"v8-compile-cache": "^2.3.0", "v8-compile-cache": "^2.3.0",
"wallpaper": "5.0.1", "wallpaper": "5.0.1",
"ws": "^8.5.0", "ws": "^8.8.1",
"xml2js": "^0.4.23", "xml2js": "^0.4.23",
"youtube-search-without-api-key": "^1.0.7" "youtube-search-without-api-key": "^1.0.7"
}, },
@ -90,14 +87,14 @@
"@types/qrcode-terminal": "^0.12.0", "@types/qrcode-terminal": "^0.12.0",
"@types/ws": "^8.5.3", "@types/ws": "^8.5.3",
"electron": "git+https://github.com/castlabs/electron-releases.git#18-x-y", "electron": "git+https://github.com/castlabs/electron-releases.git#18-x-y",
"electron-builder": "^23.0.3", "electron-builder": "^23.3.3",
"electron-builder-notarize-pkg": "^1.2.0", "electron-builder-notarize-pkg": "^1.2.0",
"electron-webpack": "^2.8.2", "electron-webpack": "^2.8.2",
"less": "^4.1.3", "less": "^4.1.3",
"musickit-typescript": "^1.2.4", "musickit-typescript": "^1.2.4",
"typescript": "^4.6.4", "typescript": "^4.7.4",
"vue-devtools": "^5.1.4", "vue-devtools": "^5.1.4",
"webpack": "~5.72.0" "webpack": "~5.74.0"
}, },
"fileAssociations": [ "fileAssociations": [
{ {

View file

@ -1,21 +1,27 @@
exports.default = function (context) { exports.default = function (context) {
const { execSync } = require('child_process') const { execSync } = require("child_process");
const fs = require('fs') const fs = require("fs");
if (process.platform !== 'darwin') if (process.platform !== "darwin") return;
return
if (fs.existsSync('dist/mac-universal--x64/Cider.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/Electron Framework.sig')) if (fs.existsSync("dist/mac-universal--x64/Cider.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/Electron Framework.sig"))
fs.unlinkSync('dist/mac-universal--x64/Cider.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/Electron Framework.sig') fs.unlinkSync("dist/mac-universal--x64/Cider.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/Electron Framework.sig");
if (fs.existsSync('dist/mac-universal--arm64/Cider.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/Electron Framework.sig')) if (fs.existsSync("dist/mac-universal--arm64/Cider.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/Electron Framework.sig"))
fs.unlinkSync('dist/mac-universal--arm64/Cider.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/Electron Framework.sig') fs.unlinkSync("dist/mac-universal--arm64/Cider.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/Electron Framework.sig");
// console.log('Castlabs-evs update start') // console.log('Castlabs-evs update start')
// execSync('python3 -m pip install --upgrade castlabs-evs') // execSync('python3 -m pip install --upgrade castlabs-evs')
// console.log('Castlabs-evs update complete') // console.log('Castlabs-evs update complete')
// xcode 13 // xcode 13
if (fs.existsSync('dist/mac-universal--x64') && fs.existsSync('dist/mac-universal--arm64') && fs.existsSync('dist/mac-universal--x64/Cider.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/MainMenu.nib/keyedobjects-101300.nib')) if (
execSync("cp 'dist/mac-universal--x64/Cider.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/MainMenu.nib/keyedobjects-101300.nib' 'dist/mac-universal--arm64/Cider.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/MainMenu.nib/keyedobjects-101300.nib'",{stdio: 'inherit'}) fs.existsSync("dist/mac-universal--x64") &&
fs.existsSync("dist/mac-universal--arm64") &&
fs.existsSync("dist/mac-universal--x64/Cider.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/MainMenu.nib/keyedobjects-101300.nib")
)
execSync(
"cp 'dist/mac-universal--x64/Cider.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/MainMenu.nib/keyedobjects-101300.nib' 'dist/mac-universal--arm64/Cider.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/MainMenu.nib/keyedobjects-101300.nib'",
{ stdio: "inherit" }
);
// console.log('VMP signing start') // console.log('VMP signing start')
// if (fs.existsSync('dist/mac-universal')) // if (fs.existsSync('dist/mac-universal'))
@ -26,5 +32,4 @@ exports.default = function(context) {
// execSync('python3 -m castlabs_evs.vmp -n sign-pkg dist/mac-arm64 -z',{stdio: 'inherit'}) // execSync('python3 -m castlabs_evs.vmp -n sign-pkg dist/mac-arm64 -z',{stdio: 'inherit'})
// console.log('VMP signing complete') // console.log('VMP signing complete')
};
}

View file

@ -6,6 +6,4 @@
"lang": ["en-US"] "lang": ["en-US"]
} }
] ]
} }

View file

@ -24,16 +24,20 @@ class MacPackager extends platformPackager_1.PlatformPackager {
this.codeSigningInfo = new lazy_val_1.Lazy(() => { this.codeSigningInfo = new lazy_val_1.Lazy(() => {
const cscLink = this.getCscLink(); const cscLink = this.getCscLink();
if (cscLink == null || process.platform !== "darwin") { if (cscLink == null || process.platform !== "darwin") {
return Promise.resolve({ keychainFile: process.env.CSC_KEYCHAIN || null }); return Promise.resolve({
keychainFile: process.env.CSC_KEYCHAIN || null,
});
} }
return macCodeSign_1.createKeychain({ return macCodeSign_1
.createKeychain({
tmpDir: this.info.tempDirManager, tmpDir: this.info.tempDirManager,
cscLink, cscLink,
cscKeyPassword: this.getCscPassword(), cscKeyPassword: this.getCscPassword(),
cscILink: platformPackager_1.chooseNotNull(this.platformSpecificBuildOptions.cscInstallerLink, process.env.CSC_INSTALLER_LINK), cscILink: platformPackager_1.chooseNotNull(this.platformSpecificBuildOptions.cscInstallerLink, process.env.CSC_INSTALLER_LINK),
cscIKeyPassword: platformPackager_1.chooseNotNull(this.platformSpecificBuildOptions.cscInstallerKeyPassword, process.env.CSC_INSTALLER_KEY_PASSWORD), cscIKeyPassword: platformPackager_1.chooseNotNull(this.platformSpecificBuildOptions.cscInstallerKeyPassword, process.env.CSC_INSTALLER_KEY_PASSWORD),
currentDir: this.projectDir, currentDir: this.projectDir,
}).then(result => { })
.then((result) => {
const keychainFile = result.keychainFile; const keychainFile = result.keychainFile;
if (keychainFile != null) { if (keychainFile != null) {
this.info.disposeOnBuildFinish(() => macCodeSign_1.removeKeychain(keychainFile)); this.info.disposeOnBuildFinish(() => macCodeSign_1.removeKeychain(keychainFile));
@ -61,18 +65,18 @@ class MacPackager extends platformPackager_1.PlatformPackager {
case "dmg": { case "dmg": {
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
const { DmgTarget } = require("dmg-builder"); const { DmgTarget } = require("dmg-builder");
mapper(name, outDir => new DmgTarget(this, outDir)); mapper(name, (outDir) => new DmgTarget(this, outDir));
break; break;
} }
case "zip": case "zip":
// https://github.com/electron-userland/electron-builder/issues/2313 // https://github.com/electron-userland/electron-builder/issues/2313
mapper(name, outDir => new ArchiveTarget_1.ArchiveTarget(name, outDir, this, true)); mapper(name, (outDir) => new ArchiveTarget_1.ArchiveTarget(name, outDir, this, true));
break; break;
case "pkg": case "pkg":
mapper(name, outDir => new pkg_1.PkgTarget(this, outDir)); mapper(name, (outDir) => new pkg_1.PkgTarget(this, outDir));
break; break;
default: default:
mapper(name, outDir => (name === "mas" || name === "mas-dev" ? new targetFactory_1.NoOpTarget(name) : targetFactory_1.createCommonTarget(name, outDir, this))); mapper(name, (outDir) => (name === "mas" || name === "mas-dev" ? new targetFactory_1.NoOpTarget(name) : targetFactory_1.createCommonTarget(name, outDir, this)));
break; break;
} }
} }
@ -90,12 +94,15 @@ class MacPackager extends platformPackager_1.PlatformPackager {
const arm64AppOutPath = appOutDir + "--" + builder_util_1.Arch[arm64Arch]; const arm64AppOutPath = appOutDir + "--" + builder_util_1.Arch[arm64Arch];
await super.doPack(outDir, arm64AppOutPath, platformName, arm64Arch, platformSpecificBuildOptions, targets, false, true); await super.doPack(outDir, arm64AppOutPath, platformName, arm64Arch, platformSpecificBuildOptions, targets, false, true);
const framework = this.info.framework; const framework = this.info.framework;
builder_util_1.log.info({ builder_util_1.log.info(
{
platform: platformName, platform: platformName,
arch: builder_util_1.Arch[arch], arch: builder_util_1.Arch[arch],
[`${framework.name}`]: framework.version, [`${framework.name}`]: framework.version,
appOutDir: builder_util_1.log.filePath(appOutDir), appOutDir: builder_util_1.log.filePath(appOutDir),
}, `packaging`); },
`packaging`
);
const appFile = `${this.appInfo.productFilename}.app`; const appFile = `${this.appInfo.productFilename}.app`;
const { makeUniversalApp } = require("@electron/universal"); const { makeUniversalApp } = require("@electron/universal");
await makeUniversalApp({ await makeUniversalApp({
@ -113,10 +120,10 @@ class MacPackager extends platformPackager_1.PlatformPackager {
targets, targets,
packager: this, packager: this,
electronPlatformName: platformName, electronPlatformName: platformName,
} };
await this.info.afterPack(packContext) await this.info.afterPack(packContext);
if (framework.afterPack != null) { if (framework.afterPack != null) {
await framework.afterPack(packContext) await framework.afterPack(packContext);
} }
await this.doSignAfterPack(outDir, appOutDir, platformName, arch, platformSpecificBuildOptions, targets); await this.doSignAfterPack(outDir, appOutDir, platformName, arch, platformSpecificBuildOptions, targets);
break; break;
@ -125,13 +132,13 @@ class MacPackager extends platformPackager_1.PlatformPackager {
} }
async pack(outDir, arch, targets, taskManager) { async pack(outDir, arch, targets, taskManager) {
let nonMasPromise = null; let nonMasPromise = null;
const hasMas = targets.length !== 0 && targets.some(it => it.name === "mas" || it.name === "mas-dev"); const hasMas = targets.length !== 0 && targets.some((it) => it.name === "mas" || it.name === "mas-dev");
const prepackaged = this.packagerOptions.prepackaged; const prepackaged = this.packagerOptions.prepackaged;
if (!hasMas || targets.length > 1) { if (!hasMas || targets.length > 1) {
const appPath = prepackaged == null ? path.join(this.computeAppOutDir(outDir, arch), `${this.appInfo.productFilename}.app`) : prepackaged; const appPath = prepackaged == null ? path.join(this.computeAppOutDir(outDir, arch), `${this.appInfo.productFilename}.app`) : prepackaged;
nonMasPromise = (prepackaged nonMasPromise = (prepackaged ? Promise.resolve() : this.doPack(outDir, path.dirname(appPath), this.platform.nodeName, arch, this.platformSpecificBuildOptions, targets)).then(() =>
? Promise.resolve() this.packageInDistributableFormat(appPath, arch, targets, taskManager)
: this.doPack(outDir, path.dirname(appPath), this.platform.nodeName, arch, this.platformSpecificBuildOptions, targets)).then(() => this.packageInDistributableFormat(appPath, arch, targets, taskManager)); );
} }
for (const target of targets) { for (const target of targets) {
const targetName = target.name; const targetName = target.name;
@ -148,8 +155,7 @@ class MacPackager extends platformPackager_1.PlatformPackager {
if (prepackaged == null) { if (prepackaged == null) {
await this.doPack(outDir, targetOutDir, "mas", arch, masBuildOptions, [target]); await this.doPack(outDir, targetOutDir, "mas", arch, masBuildOptions, [target]);
await this.sign(path.join(targetOutDir, `${this.appInfo.productFilename}.app`), targetOutDir, masBuildOptions, arch); await this.sign(path.join(targetOutDir, `${this.appInfo.productFilename}.app`), targetOutDir, masBuildOptions, arch);
} } else {
else {
await this.sign(prepackaged, targetOutDir, masBuildOptions, arch); await this.sign(prepackaged, targetOutDir, masBuildOptions, arch);
} }
} }
@ -203,20 +209,21 @@ class MacPackager extends platformPackager_1.PlatformPackager {
if (filter.length == 0) { if (filter.length == 0) {
filter = null; filter = null;
} }
} } else if (filter != null) {
else if (filter != null) {
filter = filter.length === 0 ? null : [filter]; filter = filter.length === 0 ? null : [filter];
} }
const filterRe = filter == null ? null : filter.map(it => new RegExp(it)); const filterRe = filter == null ? null : filter.map((it) => new RegExp(it));
let binaries = options.binaries || undefined; let binaries = options.binaries || undefined;
if (binaries) { if (binaries) {
// Accept absolute paths for external binaries, else resolve relative paths from the artifact's app Contents path. // Accept absolute paths for external binaries, else resolve relative paths from the artifact's app Contents path.
const userDefinedBinaries = await Promise.all(binaries.map(async (destination) => { const userDefinedBinaries = await Promise.all(
binaries.map(async (destination) => {
if (await fs_1.statOrNull(destination)) { if (await fs_1.statOrNull(destination)) {
return destination; return destination;
} }
return path.resolve(appPath, destination); return path.resolve(appPath, destination);
})); })
);
// Insert at front to prioritize signing. We still sort by depth next // Insert at front to prioritize signing. We still sort by depth next
binaries = userDefinedBinaries.concat(binaries); binaries = userDefinedBinaries.concat(binaries);
builder_util_1.log.info("Signing addtional user-defined binaries: " + JSON.stringify(userDefinedBinaries, null, 1)); builder_util_1.log.info("Signing addtional user-defined binaries: " + JSON.stringify(userDefinedBinaries, null, 1));
@ -233,11 +240,13 @@ class MacPackager extends platformPackager_1.PlatformPackager {
} }
} }
} }
return (file.endsWith(".kext") || return (
file.endsWith(".kext") ||
file.startsWith("/Contents/PlugIns", appPath.length) || file.startsWith("/Contents/PlugIns", appPath.length) ||
file.includes("/node_modules/puppeteer/.local-chromium") || file.includes("/node_modules/puppeteer/.local-chromium") ||
file.includes("/node_modules/playwright-firefox/.local-browsers") || file.includes("/node_modules/playwright-firefox/.local-browsers") ||
file.includes("/node_modules/playwright/.local-browsers")); file.includes("/node_modules/playwright/.local-browsers")
);
/* Those are browser automating modules, browser (chromium, nightly) cannot be signed /* Those are browser automating modules, browser (chromium, nightly) cannot be signed
https://github.com/electron-userland/electron-builder/issues/2010 https://github.com/electron-userland/electron-builder/issues/2010
https://github.com/electron-userland/electron-builder/issues/5383 https://github.com/electron-userland/electron-builder/issues/5383
@ -250,7 +259,7 @@ class MacPackager extends platformPackager_1.PlatformPackager {
app: appPath, app: appPath,
keychain: keychainFile || undefined, keychain: keychainFile || undefined,
binaries, binaries,
timestamp: isMas ? masOptions === null || masOptions === void 0 ? void 0 : masOptions.timestamp : options.timestamp, timestamp: isMas ? (masOptions === null || masOptions === void 0 ? void 0 : masOptions.timestamp) : options.timestamp,
requirements: isMas || this.platformSpecificBuildOptions.requirements == null ? undefined : await this.getResource(this.platformSpecificBuildOptions.requirements), requirements: isMas || this.platformSpecificBuildOptions.requirements == null ? undefined : await this.getResource(this.platformSpecificBuildOptions.requirements),
// https://github.com/electron-userland/electron-osx-sign/issues/196 // https://github.com/electron-userland/electron-osx-sign/issues/196
// will fail on 10.14.5+ because a signed but unnotarized app is also rejected. // will fail on 10.14.5+ because a signed but unnotarized app is also rejected.
@ -260,12 +269,15 @@ class MacPackager extends platformPackager_1.PlatformPackager {
hardenedRuntime: isMas ? masOptions && masOptions.hardenedRuntime === true : options.hardenedRuntime !== false, hardenedRuntime: isMas ? masOptions && masOptions.hardenedRuntime === true : options.hardenedRuntime !== false,
}; };
await this.adjustSignOptions(signOptions, masOptions); await this.adjustSignOptions(signOptions, masOptions);
builder_util_1.log.info({ builder_util_1.log.info(
{
file: builder_util_1.log.filePath(appPath), file: builder_util_1.log.filePath(appPath),
identityName: identity.name, identityName: identity.name,
identityHash: identity.hash, identityHash: identity.hash,
provisioningProfile: signOptions["provisioning-profile"] || "none", provisioningProfile: signOptions["provisioning-profile"] || "none",
}, "signing"); },
"signing"
);
await this.doSign(signOptions); await this.doSign(signOptions);
// https://github.com/electron-userland/electron-builder/issues/1196#issuecomment-312310209 // https://github.com/electron-userland/electron-builder/issues/1196#issuecomment-312310209
if (masOptions != null && !isDevelopment) { if (masOptions != null && !isDevelopment) {
@ -290,8 +302,7 @@ class MacPackager extends platformPackager_1.PlatformPackager {
const p = `entitlements.${entitlementsSuffix}.plist`; const p = `entitlements.${entitlementsSuffix}.plist`;
if (resourceList.includes(p)) { if (resourceList.includes(p)) {
entitlements = path.join(this.info.buildResourcesDir, p); entitlements = path.join(this.info.buildResourcesDir, p);
} } else {
else {
entitlements = pathManager_1.getTemplatePath("entitlements.mac.plist"); entitlements = pathManager_1.getTemplatePath("entitlements.mac.plist");
} }
} }
@ -301,8 +312,7 @@ class MacPackager extends platformPackager_1.PlatformPackager {
const p = `entitlements.${entitlementsSuffix}.inherit.plist`; const p = `entitlements.${entitlementsSuffix}.inherit.plist`;
if (resourceList.includes(p)) { if (resourceList.includes(p)) {
entitlementsInherit = path.join(this.info.buildResourcesDir, p); entitlementsInherit = path.join(this.info.buildResourcesDir, p);
} } else {
else {
entitlementsInherit = pathManager_1.getTemplatePath("entitlements.mac.plist"); entitlementsInherit = pathManager_1.getTemplatePath("entitlements.mac.plist");
} }
} }
@ -357,7 +367,7 @@ class MacPackager extends platformPackager_1.PlatformPackager {
appPlist.CFBundleIdentifier = appInfo.macBundleIdentifier; appPlist.CFBundleIdentifier = appInfo.macBundleIdentifier;
appPlist.CFBundleShortVersionString = this.platformSpecificBuildOptions.bundleShortVersion || appInfo.version; appPlist.CFBundleShortVersionString = this.platformSpecificBuildOptions.bundleShortVersion || appInfo.version;
appPlist.CFBundleVersion = appInfo.buildVersion; appPlist.CFBundleVersion = appInfo.buildVersion;
builder_util_1.use(this.platformSpecificBuildOptions.category || this.config.category, it => (appPlist.LSApplicationCategoryType = it)); builder_util_1.use(this.platformSpecificBuildOptions.category || this.config.category, (it) => (appPlist.LSApplicationCategoryType = it));
appPlist.NSHumanReadableCopyright = appInfo.copyright; appPlist.NSHumanReadableCopyright = appInfo.copyright;
if (this.platformSpecificBuildOptions.darkModeSupport) { if (this.platformSpecificBuildOptions.darkModeSupport) {
appPlist.NSRequiresAquaSystemAppearance = false; appPlist.NSRequiresAquaSystemAppearance = false;
@ -382,8 +392,7 @@ class MacPackager extends platformPackager_1.PlatformPackager {
await bluebird_lst_1.default.map(promise_1.orIfFileNotExist(promises_1.readdir(outResourcesDir), []), (file) => { await bluebird_lst_1.default.map(promise_1.orIfFileNotExist(promises_1.readdir(outResourcesDir), []), (file) => {
if (file.endsWith(".app")) { if (file.endsWith(".app")) {
return this.sign(path.join(outResourcesDir, file), null, null, null); return this.sign(path.join(outResourcesDir, file), null, null, null);
} } else {
else {
return null; return null;
} }
}); });

View file

@ -1,18 +1,16 @@
require("dotenv").config();
const { notarize } = require("electron-notarize");
require('dotenv').config();
const { notarize } = require('electron-notarize');
exports.default = async function notarizing(context) { exports.default = async function notarizing(context) {
const { electronPlatformName, appOutDir } = context; const { electronPlatformName, appOutDir } = context;
if (electronPlatformName !== 'darwin') { if (electronPlatformName !== "darwin") {
return; return;
} }
const appName = context.packager.appInfo.productFilename; const appName = context.packager.appInfo.productFilename;
return await notarize({ return await notarize({
appBundleId: 'com.ciderapp.cider', appBundleId: "com.ciderapp.cider",
appPath: `${appOutDir}/${appName}.app`, appPath: `${appOutDir}/${appName}.app`,
appleId: process.env.APPLEID, appleId: process.env.APPLEID,
appleIdPassword: process.env.APPLEIDPASS, appleIdPassword: process.env.APPLEIDPASS,

View file

@ -3,24 +3,24 @@ const CiderKit = {
musickit: { musickit: {
async mkv3(route, body, options) { async mkv3(route, body, options) {
let opts = { let opts = {
method: 'POST', method: "POST",
cache: 'no-cache', cache: "no-cache",
credentials: 'same-origin', credentials: "same-origin",
headers: { headers: {
'Content-Type': 'application/json' "Content-Type": "application/json",
}, },
redirect: 'follow', redirect: "follow",
referrerPolicy: 'no-referrer', referrerPolicy: "no-referrer",
body: {} body: {},
} };
opts.body = JSON.stringify({ opts.body = JSON.stringify({
route: route, route: route,
body: body, body: body,
options: options options: options,
}) });
let response = await fetch("./api/musickit/v3", opts); let response = await fetch("./api/musickit/v3", opts);
return response.json() return response.json();
} },
} },
} },
} };

View file

@ -310,4 +310,3 @@
"share.platform.songLink": "Kopioi song.link", "share.platform.songLink": "Kopioi song.link",
"share.platform.clipboard": "Kopioi linkki" "share.platform.clipboard": "Kopioi linkki"
} }

View file

@ -1,8 +1,8 @@
import {app, Menu, nativeImage, Tray, ipcMain, clipboard, shell} from 'electron'; import { app, Menu, nativeImage, Tray, ipcMain, clipboard, shell } from "electron";
import { readFileSync } from "fs"; import { readFileSync } from "fs";
import * as path from 'path'; import * as path from "path";
import * as log from 'electron-log'; import * as log from "electron-log";
import {utils} from './utils'; import { utils } from "./utils";
/** /**
* @file Creates App instance * @file Creates App instance
@ -11,14 +11,7 @@ import {utils} from './utils';
/** @namespace */ /** @namespace */
export class AppEvents { export class AppEvents {
private protocols: string[] = [ private protocols: string[] = ["ame", "cider", "itms", "itmss", "musics", "music"];
"ame",
"cider",
"itms",
"itmss",
"musics",
"music"
]
private plugin: any = undefined; private plugin: any = undefined;
private tray: any = undefined; private tray: any = undefined;
private i18n: any = undefined; private i18n: any = undefined;
@ -33,68 +26,68 @@ export class AppEvents {
* @returns {void} * @returns {void}
*/ */
private start(): void { private start(): void {
AppEvents.initLogging() AppEvents.initLogging();
console.info('[AppEvents] App started'); console.info("[AppEvents] App started");
/********************************************************************************************************************** /**********************************************************************************************************************
* Startup arguments handling * Startup arguments handling
**********************************************************************************************************************/ **********************************************************************************************************************/
if (app.commandLine.hasSwitch('version') || app.commandLine.hasSwitch('v')) { if (app.commandLine.hasSwitch("version") || app.commandLine.hasSwitch("v")) {
console.log(app.getVersion()) console.log(app.getVersion());
app.exit() app.exit();
} }
// Verbose Check // Verbose Check
if (app.commandLine.hasSwitch('verbose')) { if (app.commandLine.hasSwitch("verbose")) {
console.log("[Cider] User has launched the application with --verbose"); console.log("[Cider] User has launched the application with --verbose");
} }
// Log File Location // Log File Location
if (app.commandLine.hasSwitch('log') || app.commandLine.hasSwitch('l')) { if (app.commandLine.hasSwitch("log") || app.commandLine.hasSwitch("l")) {
console.log(path.join(app.getPath('userData'), 'logs')) console.log(path.join(app.getPath("userData"), "logs"));
app.exit() app.exit();
} }
// Try limiting JS memory to 350MB. // Try limiting JS memory to 350MB.
app.commandLine.appendSwitch('js-flags', '--max-old-space-size=350'); app.commandLine.appendSwitch("js-flags", "--max-old-space-size=350");
// Expose GC // Expose GC
app.commandLine.appendSwitch('js-flags', '--expose_gc') app.commandLine.appendSwitch("js-flags", "--expose_gc");
if (process.platform === "win32") { if (process.platform === "win32") {
app.setAppUserModelId(app.getName()) // For notification name app.setAppUserModelId(app.getName()); // For notification name
} }
/*********************************************************************************************************************** /***********************************************************************************************************************
* Commandline arguments * Commandline arguments
**********************************************************************************************************************/ **********************************************************************************************************************/
switch (utils.getStoreValue('visual.hw_acceleration') as string) { switch (utils.getStoreValue("visual.hw_acceleration") as string) {
default: default:
case "default": case "default":
app.commandLine.appendSwitch('enable-accelerated-mjpeg-decode') app.commandLine.appendSwitch("enable-accelerated-mjpeg-decode");
app.commandLine.appendSwitch('enable-accelerated-video') app.commandLine.appendSwitch("enable-accelerated-video");
app.commandLine.appendSwitch('disable-gpu-driver-bug-workarounds') app.commandLine.appendSwitch("disable-gpu-driver-bug-workarounds");
app.commandLine.appendSwitch('ignore-gpu-blacklist') app.commandLine.appendSwitch("ignore-gpu-blacklist");
app.commandLine.appendSwitch('enable-native-gpu-memory-buffers') app.commandLine.appendSwitch("enable-native-gpu-memory-buffers");
app.commandLine.appendSwitch('enable-accelerated-video-decode'); app.commandLine.appendSwitch("enable-accelerated-video-decode");
app.commandLine.appendSwitch('enable-gpu-rasterization'); app.commandLine.appendSwitch("enable-gpu-rasterization");
app.commandLine.appendSwitch('enable-native-gpu-memory-buffers'); app.commandLine.appendSwitch("enable-native-gpu-memory-buffers");
app.commandLine.appendSwitch('enable-oop-rasterization'); app.commandLine.appendSwitch("enable-oop-rasterization");
break; break;
case "webgpu": case "webgpu":
console.info("WebGPU is enabled."); console.info("WebGPU is enabled.");
app.commandLine.appendSwitch('enable-unsafe-webgpu') app.commandLine.appendSwitch("enable-unsafe-webgpu");
break; break;
case "disabled": case "disabled":
console.info("Hardware acceleration is disabled."); console.info("Hardware acceleration is disabled.");
app.commandLine.appendSwitch('disable-gpu') app.commandLine.appendSwitch("disable-gpu");
break; break;
} }
if (process.platform === "linux") { if (process.platform === "linux") {
app.commandLine.appendSwitch('disable-features', 'MediaSessionService'); app.commandLine.appendSwitch("disable-features", "MediaSessionService");
} }
/*********************************************************************************************************************** /***********************************************************************************************************************
@ -104,48 +97,50 @@ export class AppEvents {
if (process.defaultApp) { if (process.defaultApp) {
if (process.argv.length >= 2) { if (process.argv.length >= 2) {
this.protocols.forEach((protocol: string) => { this.protocols.forEach((protocol: string) => {
app.setAsDefaultProtocolClient(protocol, process.execPath, [path.resolve(process.argv[1])]) app.setAsDefaultProtocolClient(protocol, process.execPath, [path.resolve(process.argv[1])]);
}) });
} }
} else { } else {
this.protocols.forEach((protocol: string) => { this.protocols.forEach((protocol: string) => {
app.setAsDefaultProtocolClient(protocol) app.setAsDefaultProtocolClient(protocol);
}) });
} }
} }
public quit() { public quit() {
console.log('[AppEvents] App quit'); console.log("[AppEvents] App quit");
} }
public ready(plug: any) { public ready(plug: any) {
this.plugin = plug this.plugin = plug;
console.log('[AppEvents] App ready'); console.log("[AppEvents] App ready");
AppEvents.setLoginSettings() AppEvents.setLoginSettings();
} }
public bwCreated() { public bwCreated() {
app.on('open-url', (event, url) => { app.on("open-url", (event, url) => {
event.preventDefault() event.preventDefault();
if (this.protocols.some((protocol: string) => url.includes(protocol))) { if (this.protocols.some((protocol: string) => url.includes(protocol))) {
this.LinkHandler(url) this.LinkHandler(url);
console.log(url) console.log(url);
} }
}) });
if (process.platform === "darwin") { if (process.platform === "darwin") {
app.setUserActivity('8R23J2835D.com.ciderapp.webremote.play', { app.setUserActivity(
title: 'Web Remote', "8R23J2835D.com.ciderapp.webremote.play",
description: 'Connect to your Web Remote', {
}, "https://webremote.cider.sh") title: "Web Remote",
description: "Connect to your Web Remote",
},
"https://webremote.cider.sh"
);
} }
this.InstanceHandler() this.InstanceHandler();
if (process.platform !== "darwin") { if (process.platform !== "darwin") {
this.InitTray() this.InitTray();
} }
} }
@ -161,58 +156,61 @@ export class AppEvents {
if (!arg) return; if (!arg) return;
// LastFM Auth URL // LastFM Auth URL
if (arg.includes('auth')) { if (arg.includes("auth")) {
const authURI = arg.split('/auth/')[1] const authURI = arg.split("/auth/")[1];
if (authURI.startsWith('lastfm')) { // If we wanted more auth options if (authURI.startsWith("lastfm")) {
console.log('token: ', authURI.split('lastfm?token=')[1]) // If we wanted more auth options
utils.getWindow().webContents.executeJavaScript(`ipcRenderer.send('lastfm:auth', "${authURI.split('lastfm?token=')[1]}")`).catch(console.error) console.log("token: ", authURI.split("lastfm?token=")[1]);
utils
.getWindow()
.webContents.executeJavaScript(`ipcRenderer.send('lastfm:auth', "${authURI.split("lastfm?token=")[1]}")`)
.catch(console.error);
} }
} } else if (arg.includes("playpause")) {
else if (arg.includes('playpause')) {
//language=JS //language=JS
utils.getWindow().webContents.executeJavaScript('MusicKitInterop.playPause()') utils.getWindow().webContents.executeJavaScript("MusicKitInterop.playPause()");
} } else if (arg.includes("nextitem")) {
else if (arg.includes('nextitem')) {
//language=JS //language=JS
utils.getWindow().webContents.executeJavaScript('app.mk.skipToNextItem()') utils.getWindow().webContents.executeJavaScript("app.mk.skipToNextItem()");
} }
// Play // Play
else if (arg.includes('/play/')) { //Steer away from protocol:// specific conditionals else if (arg.includes("/play/")) {
const playParam = arg.split('/play/')[1] //Steer away from protocol:// specific conditionals
const playParam = arg.split("/play/")[1];
const mediaType = { const mediaType = {
"s/": "song", "s/": "song",
"a/": "album", "a/": "album",
"p/": "playlist" "p/": "playlist",
} };
for (const [key, value] of Object.entries(mediaType)) { for (const [key, value] of Object.entries(mediaType)) {
if (playParam.includes(key)) { if (playParam.includes(key)) {
const id = playParam.split(key)[1] const id = playParam.split(key)[1];
utils.getWindow().webContents.send('play', value, id) utils.getWindow().webContents.send("play", value, id);
console.debug(`[LinkHandler] Attempting to load ${value} by id: ${id}`) console.debug(`[LinkHandler] Attempting to load ${value} by id: ${id}`);
} }
} }
} else if (arg.includes("music.apple.com")) {
} else if (arg.includes('music.apple.com')) { // URL (used with itms/itmss/music/musics uris) // URL (used with itms/itmss/music/musics uris)
console.log(arg) console.log(arg);
let url = arg.split('//')[1] let url = arg.split("//")[1];
console.warn(`[LinkHandler] Attempting to load url: ${url}`); console.warn(`[LinkHandler] Attempting to load url: ${url}`);
utils.getWindow().webContents.send('play', 'url', url) utils.getWindow().webContents.send("play", "url", url);
} else if (arg.includes('/debug/appdata')) { } else if (arg.includes("/debug/appdata")) {
shell.openPath(app.getPath('userData')) shell.openPath(app.getPath("userData"));
} else if (arg.includes('/debug/logs')) { } else if (arg.includes("/debug/logs")) {
shell.openPath(app.getPath('logs')) shell.openPath(app.getPath("logs"));
} else if (arg.includes('/discord')) { } else if (arg.includes("/discord")) {
shell.openExternal('https://discord.gg/applemusic') shell.openExternal("https://discord.gg/applemusic");
} else if (arg.includes('/github')) { } else if (arg.includes("/github")) {
shell.openExternal('https://github.com/ciderapp/cider') shell.openExternal("https://github.com/ciderapp/cider");
} else if (arg.includes('/donate')) { } else if (arg.includes("/donate")) {
shell.openExternal('https://opencollective.com/ciderapp') shell.openExternal("https://opencollective.com/ciderapp");
} else if (arg.includes('/beep')) { } else if (arg.includes("/beep")) {
shell.beep() shell.beep();
} else { } else {
utils.getWindow().webContents.executeJavaScript(`app.appRoute('${arg.split('//')[1]}')`) utils.getWindow().webContents.executeJavaScript(`app.appRoute('${arg.split("//")[1]}')`);
} }
} }
@ -221,32 +219,33 @@ export class AppEvents {
*/ */
private InstanceHandler() { private InstanceHandler() {
// Detects of an existing instance is running (So if the lock has been achieved, no existing instance has been found) // Detects of an existing instance is running (So if the lock has been achieved, no existing instance has been found)
const gotTheLock = app.requestSingleInstanceLock() const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) { // Runs on the new instance if another instance has been found if (!gotTheLock) {
console.log('[Cider] Another instance has been found, quitting.') // Runs on the new instance if another instance has been found
app.quit() console.log("[Cider] Another instance has been found, quitting.");
} else { // Runs on the first instance if no other instance has been found app.quit();
app.on('second-instance', (_event, startArgs) => { } else {
console.log("[InstanceHandler] (second-instance) Instance started with " + startArgs.toString()) // Runs on the first instance if no other instance has been found
app.on("second-instance", (_event, startArgs) => {
console.log("[InstanceHandler] (second-instance) Instance started with " + startArgs.toString());
startArgs.forEach(arg => { startArgs.forEach((arg) => {
console.log(arg) console.log(arg);
if (arg.includes("cider://")) { if (arg.includes("cider://")) {
console.debug('[InstanceHandler] (second-instance) Link detected with ' + arg) console.debug("[InstanceHandler] (second-instance) Link detected with " + arg);
this.LinkHandler(arg) this.LinkHandler(arg);
} else if (arg.includes("--force-quit")) { } else if (arg.includes("--force-quit")) {
console.warn('[InstanceHandler] (second-instance) Force Quit found. Quitting App.'); console.warn("[InstanceHandler] (second-instance) Force Quit found. Quitting App.");
app.quit() app.quit();
} else if (utils.getWindow()) { } else if (utils.getWindow()) {
if (utils.getWindow().isMinimized()) utils.getWindow().restore() if (utils.getWindow().isMinimized()) utils.getWindow().restore();
utils.getWindow().show() utils.getWindow().show();
utils.getWindow().focus() utils.getWindow().focus();
} }
}) });
}) });
} }
} }
/** /**
@ -254,48 +253,48 @@ export class AppEvents {
*/ */
private InitTray() { private InitTray() {
const icons = { const icons = {
"win32": nativeImage.createFromPath(path.join(__dirname, `../../resources/icons/icon.ico`)).resize({ win32: nativeImage.createFromPath(path.join(__dirname, `../../resources/icons/icon.ico`)).resize({
width: 32, width: 32,
height: 32 height: 32,
}), }),
"linux": nativeImage.createFromPath(path.join(__dirname, `../../resources/icons/icon.png`)).resize({ linux: nativeImage.createFromPath(path.join(__dirname, `../../resources/icons/icon.png`)).resize({
width: 32, width: 32,
height: 32 height: 32,
}), }),
"darwin": nativeImage.createFromPath(path.join(__dirname, `../../resources/icons/icon.png`)).resize({ darwin: nativeImage.createFromPath(path.join(__dirname, `../../resources/icons/icon.png`)).resize({
width: 20, width: 20,
height: 20 height: 20,
}), }),
} };
this.tray = new Tray(process.platform === 'win32' ? icons.win32 : (process.platform === 'darwin' ? icons.darwin : icons.linux)) this.tray = new Tray(process.platform === "win32" ? icons.win32 : process.platform === "darwin" ? icons.darwin : icons.linux);
this.tray.setToolTip(app.getName()) this.tray.setToolTip(app.getName());
this.setTray(false) this.setTray(false);
this.tray.on('double-click', () => { this.tray.on("double-click", () => {
if (utils.getWindow()) { if (utils.getWindow()) {
if (utils.getWindow().isVisible()) { if (utils.getWindow().isVisible()) {
utils.getWindow().focus() utils.getWindow().focus();
} else { } else {
utils.getWindow().show() utils.getWindow().show();
} }
} }
}) });
utils.getWindow().on('show', () => { utils.getWindow().on("show", () => {
this.setTray(true) this.setTray(true);
}) });
utils.getWindow().on('restore', () => { utils.getWindow().on("restore", () => {
this.setTray(true) this.setTray(true);
}) });
utils.getWindow().on('hide', () => { utils.getWindow().on("hide", () => {
this.setTray(false) this.setTray(false);
}) });
utils.getWindow().on('minimize', () => { utils.getWindow().on("minimize", () => {
this.setTray(false) this.setTray(false);
}) });
} }
/** /**
@ -303,23 +302,21 @@ export class AppEvents {
* @param visible - BrowserWindow Visibility * @param visible - BrowserWindow Visibility
*/ */
private setTray(visible: boolean = utils.getWindow().isVisible()) { private setTray(visible: boolean = utils.getWindow().isVisible()) {
this.i18n = utils.getLocale(utils.getStoreValue('general.language')) this.i18n = utils.getLocale(utils.getStoreValue("general.language"));
const ciderIcon = nativeImage.createFromPath(path.join(__dirname, `../../resources/icons/icon.png`)).resize({ const ciderIcon = nativeImage.createFromPath(path.join(__dirname, `../../resources/icons/icon.png`)).resize({
width: 24, width: 24,
height: 24 height: 24,
}) });
const menu = Menu.buildFromTemplate([ const menu = Menu.buildFromTemplate([
{ {
label: app.getName(), label: app.getName(),
enabled: false, enabled: false,
icon: ciderIcon, icon: ciderIcon,
}, },
{type: 'separator'}, { type: "separator" },
/* For now only idea i dont know if posible to implement /* For now only idea i dont know if posible to implement
@ -340,50 +337,50 @@ export class AppEvents {
{ {
visible: !visible, visible: !visible,
label: this.i18n['term.playpause'], label: this.i18n["term.playpause"],
click: () => { click: () => {
utils.getWindow().webContents.executeJavaScript('MusicKitInterop.playPause()') utils.getWindow().webContents.executeJavaScript("MusicKitInterop.playPause()");
} },
}, },
{ {
visible: !visible, visible: !visible,
label: this.i18n['term.next'], label: this.i18n["term.next"],
click: () => { click: () => {
utils.getWindow().webContents.executeJavaScript(`MusicKitInterop.next()`) utils.getWindow().webContents.executeJavaScript(`MusicKitInterop.next()`);
} },
}, },
{ {
visible: !visible, visible: !visible,
label: this.i18n['term.previous'], label: this.i18n["term.previous"],
click: () => { click: () => {
utils.getWindow().webContents.executeJavaScript(`MusicKitInterop.previous()`) utils.getWindow().webContents.executeJavaScript(`MusicKitInterop.previous()`);
} },
}, },
{type: 'separator', visible: !visible}, { type: "separator", visible: !visible },
{ {
label: (visible ? this.i18n['action.tray.minimize'] : `${this.i18n['action.tray.show']}`), label: visible ? this.i18n["action.tray.minimize"] : `${this.i18n["action.tray.show"]}`,
click: () => { click: () => {
if (utils.getWindow()) { if (utils.getWindow()) {
if (visible) { if (visible) {
utils.getWindow().hide() utils.getWindow().hide();
} else { } else {
utils.getWindow().show() utils.getWindow().show();
}
} }
} }
}, },
},
{ {
label: this.i18n['term.quit'], label: this.i18n["term.quit"],
click: () => { click: () => {
app.quit() app.quit();
} },
} },
]) ]);
this.tray.setContextMenu(menu) this.tray.setContextMenu(menu);
} }
/** /**
@ -391,18 +388,21 @@ export class AppEvents {
* @private * @private
*/ */
private static initLogging() { private static initLogging() {
log.transports.console.format = '[{h}:{i}:{s}.{ms}] [{level}] {text}'; log.transports.console.format = "[{h}:{i}:{s}.{ms}] [{level}] {text}";
Object.assign(console, log.functions); Object.assign(console, log.functions);
console.debug = function (...args: any[]) { console.debug = function (...args: any[]) {
if (!app.isPackaged) { if (!app.isPackaged) {
log.debug(...args) log.debug(...args);
} }
}; };
ipcMain.on('fetch-log', (_event) => { ipcMain.on("fetch-log", (_event) => {
const data = readFileSync(log.transports.file.getFile().path, {encoding: 'utf8', flag: 'r'}); const data = readFileSync(log.transports.file.getFile().path, {
clipboard.writeText(data) encoding: "utf8",
}) flag: "r",
});
clipboard.writeText(data);
});
} }
/** /**
@ -410,17 +410,17 @@ export class AppEvents {
* @private * @private
*/ */
private static setLoginSettings() { private static setLoginSettings() {
if (utils.getStoreValue('general.onStartup.enabled')) { if (utils.getStoreValue("general.onStartup.enabled")) {
app.setLoginItemSettings({ app.setLoginItemSettings({
openAtLogin: true, openAtLogin: true,
path: app.getPath('exe'), path: app.getPath("exe"),
args: [`${utils.getStoreValue('general.onStartup.hidden') ? '--hidden' : ''}`] args: [`${utils.getStoreValue("general.onStartup.hidden") ? "--hidden" : ""}`],
}) });
} else { } else {
app.setLoginItemSettings({ app.setLoginItemSettings({
openAtLogin: false, openAtLogin: false,
path: app.getPath('exe') path: app.getPath("exe"),
}) });
} }
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,10 @@
var util = require('util'); var util = require("util");
var castv2Cli = require('castv2-client'); var castv2Cli = require("castv2-client");
var RequestResponseController = castv2Cli.RequestResponseController; var RequestResponseController = castv2Cli.RequestResponseController;
function CiderCastController(client, sourceId, destinationId) { function CiderCastController(client, sourceId, destinationId) {
RequestResponseController.call(this, client, sourceId, destinationId, 'urn:x-cast:com.ciderapp.customdata'); RequestResponseController.call(this, client, sourceId, destinationId, "urn:x-cast:com.ciderapp.customdata");
this.once('close', onclose); this.once("close", onclose);
var self = this; var self = this;
function onclose() { function onclose() {
self.stop(); self.stop();
@ -16,16 +16,16 @@ util.inherits(CiderCastController, RequestResponseController);
CiderCastController.prototype.sendIp = function (ip) { CiderCastController.prototype.sendIp = function (ip) {
// TODO: Implement Callback // TODO: Implement Callback
let data = { let data = {
ip : ip ip: ip,
} };
this.request(data); this.request(data);
}; };
CiderCastController.prototype.kill = function () { CiderCastController.prototype.kill = function () {
// TODO: Implement Callback // TODO: Implement Callback
let data = { let data = {
action : "stop" action: "stop",
} };
this.request(data); this.request(data);
}; };

View file

@ -1,9 +1,9 @@
//@ts-nocheck //@ts-nocheck
var util = require('util'); var util = require("util");
// var debug = require('debug')('castv2-client'); // var debug = require('debug')('castv2-client');
var Application = require('castv2-client').Application; var Application = require("castv2-client").Application;
var MediaController = require('castv2-client').MediaController; var MediaController = require("castv2-client").MediaController;
var CiderCastController = require('./castcontroller'); var CiderCastController = require("./castcontroller");
function CiderReceiver(client, session) { function CiderReceiver(client, session) {
Application.apply(this, arguments); Application.apply(this, arguments);
@ -11,18 +11,17 @@ function CiderReceiver(client, session) {
this.media = this.createController(MediaController); this.media = this.createController(MediaController);
this.mediaReceiver = this.createController(CiderCastController); this.mediaReceiver = this.createController(CiderCastController);
this.media.on('status', onstatus); this.media.on("status", onstatus);
var self = this; var self = this;
function onstatus(status) { function onstatus(status) {
self.emit('status', status); self.emit("status", status);
} }
} }
// FE96A351 // FE96A351
// 27E1334F // 27E1334F
CiderReceiver.APP_ID = 'FE96A351'; CiderReceiver.APP_ID = "FE96A351";
util.inherits(CiderReceiver, Application); util.inherits(CiderReceiver, Application);

View file

@ -1,7 +1,7 @@
import * as fs from 'fs'; import * as fs from "fs";
import * as path from 'path'; import * as path from "path";
import * as electron from 'electron' import * as electron from "electron";
import {utils} from './utils'; import { utils } from "./utils";
// //
// Hello, this is our loader for the various plugins that the Cider Development Team built for our // Hello, this is our loader for the various plugins that the Cider Development Team built for our
@ -17,8 +17,8 @@ import {utils} from './utils';
*/ */
export class Plugins { export class Plugins {
private static PluginMap: any = {}; private static PluginMap: any = {};
private basePluginsPath = path.join(__dirname, '../plugins'); private basePluginsPath = path.join(__dirname, "../plugins");
private userPluginsPath = path.join(electron.app.getPath('userData'), 'Plugins'); private userPluginsPath = path.join(electron.app.getPath("userData"), "Plugins");
private readonly pluginsList: any = {}; private readonly pluginsList: any = {};
constructor() { constructor() {
@ -36,10 +36,9 @@ export class Plugins {
public getPlugins(): any { public getPlugins(): any {
let plugins: any = {}; let plugins: any = {};
if (fs.existsSync(this.basePluginsPath)) { if (fs.existsSync(this.basePluginsPath)) {
fs.readdirSync(this.basePluginsPath).forEach(file => { fs.readdirSync(this.basePluginsPath).forEach((file) => {
if (file.endsWith('.ts') || file.endsWith('.js')) { if (file.endsWith(".ts") || file.endsWith(".js")) {
const plugin = require(path.join(this.basePluginsPath, file)).default; const plugin = require(path.join(this.basePluginsPath, file)).default;
if (plugins[file] || plugin.name in plugins) { if (plugins[file] || plugin.name in plugins) {
console.log(`[${plugin.name}] Plugin already loaded / Duplicate Class Name`); console.log(`[${plugin.name}] Plugin already loaded / Duplicate Class Name`);
@ -50,14 +49,13 @@ export class Plugins {
}); });
} }
if (fs.existsSync(this.userPluginsPath)) { if (fs.existsSync(this.userPluginsPath)) {
fs.readdirSync(this.userPluginsPath).forEach(file => { fs.readdirSync(this.userPluginsPath).forEach((file) => {
// Plugins V1 // Plugins V1
if (file.endsWith('.ts') || file.endsWith('.js')) { if (file.endsWith(".ts") || file.endsWith(".js")) {
if (!electron.app.isPackaged) { if (!electron.app.isPackaged) {
const plugin = require(path.join(this.userPluginsPath, file)).default; const plugin = require(path.join(this.userPluginsPath, file)).default;
file = file.replace('.ts', '').replace('.js', ''); file = file.replace(".ts", "").replace(".js", "");
if (plugins[file] || plugin in plugins) { if (plugins[file] || plugin in plugins) {
console.log(`[${plugin.name}] Plugin already loaded / Duplicate Class Name`); console.log(`[${plugin.name}] Plugin already loaded / Duplicate Class Name`);
} else { } else {
@ -65,7 +63,7 @@ export class Plugins {
} }
} else { } else {
const plugin = require(path.join(this.userPluginsPath, file)); const plugin = require(path.join(this.userPluginsPath, file));
file = file.replace('.ts', '').replace('.js', ''); file = file.replace(".ts", "").replace(".js", "");
if (plugins[file] || plugin in plugins) { if (plugins[file] || plugin in plugins) {
console.log(`[${plugin.name}] Plugin already loaded / Duplicate Class Name`); console.log(`[${plugin.name}] Plugin already loaded / Duplicate Class Name`);
} else { } else {
@ -76,7 +74,7 @@ export class Plugins {
// Plugins V2 // Plugins V2
else if (fs.lstatSync(path.join(this.userPluginsPath, file)).isDirectory()) { else if (fs.lstatSync(path.join(this.userPluginsPath, file)).isDirectory()) {
const pluginPath = path.join(this.userPluginsPath, file); const pluginPath = path.join(this.userPluginsPath, file);
if (fs.existsSync(path.join(pluginPath, 'package.json'))) { if (fs.existsSync(path.join(pluginPath, "package.json"))) {
const pluginPackage = require(path.join(pluginPath, "package.json")); const pluginPackage = require(path.join(pluginPath, "package.json"));
const plugin = require(path.join(pluginPath, pluginPackage.main)); const plugin = require(path.join(pluginPath, pluginPackage.main));
if (plugins[plugin.name] || plugin.name in plugins) { if (plugins[plugin.name] || plugin.name in plugins) {
@ -89,15 +87,15 @@ export class Plugins {
utils: utils, utils: utils,
win: utils.getWindow(), win: utils.getWindow(),
dir: pluginPath, dir: pluginPath,
dirName: file dirName: file,
} };
plugins[plugin.name] = new plugin(pluginEnv); plugins[plugin.name] = new plugin(pluginEnv);
} }
} }
} }
}); });
} }
console.log('[PluginHandler] Loaded plugins:', Object.keys(plugins)); console.log("[PluginHandler] Loaded plugins:", Object.keys(plugins));
return plugins; return plugins;
} }
@ -108,7 +106,7 @@ export class Plugins {
this.pluginsList[plugin][event](...args); this.pluginsList[plugin][event](...args);
} catch (e) { } catch (e) {
console.error(`[${plugin}] An error was encountered: ${e}`); console.error(`[${plugin}] An error was encountered: ${e}`);
console.error(e) console.error(e);
} }
} }
} }
@ -119,5 +117,4 @@ export class Plugins {
this.pluginsList[plugin][event](...args); this.pluginsList[plugin][event](...args);
} }
} }
} }

View file

@ -1,4 +1,4 @@
import * as ElectronStore from 'electron-store'; import * as ElectronStore from "electron-store";
import * as electron from "electron"; import * as electron from "electron";
import { app } from "electron"; import { app } from "electron";
import fetch from "electron-fetch"; import fetch from "electron-fetch";
@ -7,327 +7,273 @@ export class Store {
static cfg: ElectronStore; static cfg: ElectronStore;
private defaults: any = { private defaults: any = {
"main": { main: {
"PLATFORM": process.platform, PLATFORM: process.platform,
"UPDATABLE": app.isPackaged && (!process.mas || !process.windowsStore || !process.env.FLATPAK_ID) UPDATABLE: app.isPackaged && (!process.mas || !process.windowsStore || !process.env.FLATPAK_ID),
}, },
"general": { general: {
"close_button_hide": false, close_button_hide: false,
"language": "en_US", // electron.app.getLocale().replace('-', '_') this can be used in future language: "en_US", // electron.app.getLocale().replace('-', '_') this can be used in future
"playbackNotifications": true, playbackNotifications: true,
"resumeOnStartupBehavior": "local", resumeOnStartupBehavior: "local",
"privateEnabled": false, privateEnabled: false,
"themeUpdateNotification": true, themeUpdateNotification: true,
"sidebarItems": { sidebarItems: {
"recentlyAdded": true, recentlyAdded: true,
"songs": true, songs: true,
"albums": true, albums: true,
"artists": true, artists: true,
"videos": true, videos: true,
"podcasts": true podcasts: true,
}, },
"sidebarCollapsed": { sidebarCollapsed: {
"cider": false, cider: false,
"applemusic": false, applemusic: false,
"library": false, library: false,
"amplaylists": false, amplaylists: false,
"playlists": false, playlists: false,
"localLibrary": false localLibrary: false,
}, },
"onStartup": { onStartup: {
"enabled": false, enabled: false,
"hidden": false, hidden: false,
}, },
"resumeTabs": { resumeTabs: {
"tab": "home", tab: "home",
"dynamicData": "" dynamicData: "",
}, },
"keybindings": { keybindings: {
"search": [ search: ["CommandOrControl", "F"],
"CommandOrControl", listnow: ["CommandOrControl", "L"],
"F" browse: ["CommandOrControl", "B"],
], recentAdd: ["CommandOrControl", "G"],
"listnow": [ songs: ["CommandOrControl", "J"],
"CommandOrControl", albums: ["CommandOrControl", process.platform == "darwin" ? "Option" : process.platform == "linux" ? "Shift" : "Alt", "A"],
"L" artists: ["CommandOrControl", "D"],
], togglePrivateSession: ["CommandOrControl", "P"],
"browse": [ webRemote: ["CommandOrControl", process.platform == "darwin" ? "Option" : process.platform == "linux" ? "Shift" : "Alt", "W"],
"CommandOrControl", audioSettings: ["CommandOrControl", process.platform == "darwin" ? "Option" : process.platform == "linux" ? "Shift" : "Alt", "A"],
"B" pluginMenu: ["CommandOrControl", process.platform == "darwin" ? "Option" : process.platform == "linux" ? "Shift" : "Alt", "P"],
], castToDevices: ["CommandOrControl", process.platform == "darwin" ? "Option" : process.platform == "linux" ? "Shift" : "Alt", "C"],
"recentAdd": [ settings: [
"CommandOrControl",
"G"
],
"songs": [
"CommandOrControl",
"J"
],
"albums": [
"CommandOrControl",
process.platform == "darwin" ? "Option" : (process.platform == "linux" ? "Shift" : "Alt"),
"A"
],
"artists": [
"CommandOrControl",
"D"
],
"togglePrivateSession": [
"CommandOrControl",
"P"
],
"webRemote": [
"CommandOrControl",
process.platform == "darwin" ? "Option" : (process.platform == "linux" ? "Shift" : "Alt"),
"W"
],
"audioSettings": [
"CommandOrControl",
process.platform == "darwin" ? "Option" : (process.platform == "linux" ? "Shift" : "Alt"),
"A"
],
"pluginMenu": [
"CommandOrControl",
process.platform == "darwin" ? "Option" : (process.platform == "linux" ? "Shift" : "Alt"),
"P"
],
"castToDevices": [
"CommandOrControl",
process.platform == "darwin" ? "Option" : (process.platform == "linux" ? "Shift" : "Alt"),
"C"
],
"settings": [
"CommandOrControl", // Who the hell uses a different key for this? Fucking Option? "CommandOrControl", // Who the hell uses a different key for this? Fucking Option?
"," ",",
], ],
"zoomn": [ zoomn: ["Control", "numadd"],
"Control", zoomt: ["Control", "numsub"],
"numadd", zoomrst: ["Control", "num0"],
], openDeveloperTools: ["CommandOrControl", "Shift", "I"],
"zoomt": [
"Control",
"numsub",
],
"zoomrst": [
"Control",
"num0",
],
"openDeveloperTools": [
"CommandOrControl",
"Shift",
"I"
]
}, },
"showLovedTracksInline": true showLovedTracksInline: true,
}, },
"connectivity": { connectivity: {
"discord_rpc": { discord_rpc: {
"enabled": true, enabled: true,
"client": "Cider", client: "Cider",
"clear_on_pause": true, clear_on_pause: true,
"hide_buttons": false, hide_buttons: false,
"hide_timestamp": false, hide_timestamp: false,
"state_format": "by {artist}", state_format: "by {artist}",
"details_format": "{title}", details_format: "{title}",
}, },
"lastfm": { lastfm: {
"enabled": false, enabled: false,
"scrobble_after": 50, scrobble_after: 50,
"filter_loop": false, filter_loop: false,
"filter_types": {}, filter_types: {},
"remove_featured": false, remove_featured: false,
"secrets": { secrets: {
"username": "", username: "",
"key": "" key: "",
}
}, },
}, },
"home": {
"followedArtists": [],
"favoriteItems": []
}, },
"libraryPrefs": { home: {
"songs": { followedArtists: [],
"scroll": "paged", favoriteItems: [],
"sort": "name",
"sortOrder": "asc",
"size": "normal"
}, },
"albums": { libraryPrefs: {
"scroll": "paged", songs: {
"sort": "name", scroll: "paged",
"sortOrder": "asc", sort: "name",
"viewAs": "covers" sortOrder: "asc",
size: "normal",
}, },
"playlists": { albums: {
"scroll": "infinite" scroll: "paged",
sort: "name",
sortOrder: "asc",
viewAs: "covers",
}, },
"localPaths": [], playlists: {
"pageSize": 250 scroll: "infinite",
}, },
"audio": { localPaths: [],
"volume": 1, pageSize: 250,
"volumeStep": 0.05,
"maxVolume": 1,
"lastVolume": 1,
"muted": false,
"playbackRate": 1,
"quality": "HIGH",
"seamless_audio": true,
"normalization": true,
"dBSPL": false,
"dBSPLcalibration": 90,
"maikiwiAudio": {
"ciderPPE": true,
"ciderPPE_value": "MAIKIWI",
"opportunisticCorrection_state": "OFF",
"atmosphereRealizer1": false,
"atmosphereRealizer1_value": "NATURAL_STANDARD",
"atmosphereRealizer2": false,
"atmosphereRealizer2_value": "NATURAL_STANDARD",
"spatial": false,
"spatialProfile": "BPLK",
"vibrantBass": { // Hard coded into the app. Don't include any of this config into exporting presets in store.ts
'frequencies': [17.182, 42.169, 53.763, 112.69, 119.65, 264.59, 336.57, 400.65, 505.48, 612.7, 838.7, 1155.3, 1175.6, 3406.8, 5158.6, 5968.1, 6999.9, 7468.6, 8862.9, 9666, 10109],
'Q': [2.5, 0.388, 5, 5, 2.5, 7.071, 14.14, 10, 7.071, 14.14, 8.409, 0.372, 7.071, 10, 16.82, 7.071, 28.28, 20, 8.409, 40, 40],
'gain': [-0.34, 2.49, 0.23, -0.49, 0.23, -0.12, 0.32, -0.29, 0.33, 0.19, -0.18, -1.27, -0.11, 0.25, -0.18, -0.53, 0.34, 1.32, 1.78, 0.41, -0.28]
}
}, },
"spatial": false, audio: {
"spatial_properties": { volume: 1,
"presets": [], volumeStep: 0.05,
"gain": 0.8, maxVolume: 1,
"listener_position": [0, 0, 0], lastVolume: 1,
"audio_position": [0, 0, 0], muted: false,
"room_dimensions": { playbackRate: 1,
"width": 32, quality: "HIGH",
"height": 12, seamless_audio: true,
"depth": 32 normalization: true,
}, dBSPL: false,
"room_materials": { dBSPLcalibration: 90,
"left": 'metal', maikiwiAudio: {
"right": 'metal', ciderPPE: true,
"front": 'brick-bare', ciderPPE_value: "MAIKIWI",
"back": 'brick-bare', opportunisticCorrection_state: "OFF",
"down": 'acoustic-ceiling-tiles', atmosphereRealizer1: false,
"up": 'acoustic-ceiling-tiles', atmosphereRealizer1_value: "NATURAL_STANDARD",
} atmosphereRealizer2: false,
}, atmosphereRealizer2_value: "NATURAL_STANDARD",
"equalizer": { spatial: false,
'preset': "default", spatialProfile: "BPLK",
'frequencies': [32, 63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000], vibrantBass: {
'gain': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // Hard coded into the app. Don't include any of this config into exporting presets in store.ts
'Q': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], frequencies: [17.182, 42.169, 53.763, 112.69, 119.65, 264.59, 336.57, 400.65, 505.48, 612.7, 838.7, 1155.3, 1175.6, 3406.8, 5158.6, 5968.1, 6999.9, 7468.6, 8862.9, 9666, 10109],
'mix': 1, Q: [2.5, 0.388, 5, 5, 2.5, 7.071, 14.14, 10, 7.071, 14.14, 8.409, 0.372, 7.071, 10, 16.82, 7.071, 28.28, 20, 8.409, 40, 40],
'vibrantBass': 0, gain: [-0.34, 2.49, 0.23, -0.49, 0.23, -0.12, 0.32, -0.29, 0.33, 0.19, -0.18, -1.27, -0.11, 0.25, -0.18, -0.53, 0.34, 1.32, 1.78, 0.41, -0.28],
'presets': [],
'userGenerated': false
}, },
}, },
"visual": { spatial: false,
"theme": "", spatial_properties: {
"styles": [], presets: [],
"scrollbars": 0, // 0 = show on hover, 2 = always hide, 3 = always show gain: 0.8,
"refresh_rate": 0, listener_position: [0, 0, 0],
"window_background_style": "none", // "none", "artwork", "color" audio_position: [0, 0, 0],
"animated_artwork": "limited", // 0 = always, 1 = limited, 2 = never room_dimensions: {
"animated_artwork_qualityLevel": 1, width: 32,
"bg_artwork_rotation": false, height: 12,
"hw_acceleration": "default", // default, webgpu, disabled depth: 32,
"showuserinfo": true,
"transparent": false,
"miniplayer_top_toggle": true,
"directives": {
"windowLayout": "default"
}, },
"windowControlPosition": 0, // 0 default right room_materials: {
"nativeTitleBar": false, left: "metal",
"windowColor": "#000000", right: "metal",
"customAccentColor": false, front: "brick-bare",
"accentColor": "#fc3c44", back: "brick-bare",
"purplePodcastPlaybackBar": false, down: "acoustic-ceiling-tiles",
"maxElementScale": -1 // -1 default, anything else is a custom scale up: "acoustic-ceiling-tiles",
}, },
"lyrics": {
"enable_mxm": true,
"mxm_karaoke": false,
"mxm_language": "disabled",
"enable_qq": false,
"enable_yt": false,
}, },
"advanced": { equalizer: {
"AudioContext": true, preset: "default",
"experiments": [], frequencies: [32, 63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000],
"playlistTrackMapping": true, gain: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"ffmpegLocation": "", Q: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
"disableLogging": true mix: 1,
vibrantBass: 0,
presets: [],
userGenerated: false,
}, },
"connectUser": { },
"auth": null, visual: {
"sync": { theme: "",
styles: [],
scrollbars: 0, // 0 = show on hover, 2 = always hide, 3 = always show
refresh_rate: 0,
window_background_style: "none", // "none", "artwork", "color"
animated_artwork: "limited", // 0 = always, 1 = limited, 2 = never
animated_artwork_qualityLevel: 1,
bg_artwork_rotation: false,
hw_acceleration: "default", // default, webgpu, disabled
showuserinfo: true,
transparent: false,
miniplayer_top_toggle: true,
directives: {
windowLayout: "default",
},
windowControlPosition: 0, // 0 default right
nativeTitleBar: false,
windowColor: "#000000",
customAccentColor: false,
accentColor: "#fc3c44",
purplePodcastPlaybackBar: false,
maxElementScale: -1, // -1 default, anything else is a custom scale
},
lyrics: {
enable_mxm: true,
mxm_karaoke: false,
mxm_language: "disabled",
enable_qq: false,
enable_yt: false,
},
advanced: {
AudioContext: true,
experiments: [],
playlistTrackMapping: true,
ffmpegLocation: "",
disableLogging: true,
},
connectUser: {
auth: null,
sync: {
themes: false, themes: false,
plugins: false, plugins: false,
settings: false, settings: false,
}
}, },
} },
private migrations: any = {} };
private migrations: any = {};
private schema: ElectronStore.Schema<any> = { private schema: ElectronStore.Schema<any> = {
"connectivity.discord_rpc": { "connectivity.discord_rpc": {
type: 'object' type: "object",
}, },
} };
constructor() { constructor() {
Store.cfg = new ElectronStore({ Store.cfg = new ElectronStore({
name: 'cider-config', name: "cider-config",
defaults: this.defaults, defaults: this.defaults,
schema: this.schema, schema: this.schema,
migrations: this.migrations, migrations: this.migrations,
clearInvalidConfig: false //disabled for now clearInvalidConfig: false, //disabled for now
}); });
Store.cfg.set(this.mergeStore(this.defaults, Store.cfg.store)) Store.cfg.set(this.mergeStore(this.defaults, Store.cfg.store));
this.ipcHandler(); this.ipcHandler();
} }
static pushToCloud(): void { static pushToCloud(): void {
if (Store.cfg.get('connectUser.auth') === null) return; if (Store.cfg.get("connectUser.auth") === null) return;
var syncData = Object(); var syncData = Object();
if (Store.cfg.get('connectUser.sync.themes')) { if (Store.cfg.get("connectUser.sync.themes")) {
syncData.push({ syncData.push({
themes: Store.cfg.store.themes themes: Store.cfg.store.themes,
}) });
} }
if (Store.cfg.get('connectUser.sync.plugins')) { if (Store.cfg.get("connectUser.sync.plugins")) {
syncData.push({ syncData.push({
plugins: Store.cfg.store.plugins plugins: Store.cfg.store.plugins,
}) });
} }
if (Store.cfg.get('connectUser.sync.settings')) { if (Store.cfg.get("connectUser.sync.settings")) {
syncData.push({ syncData.push({
general: Store.cfg.get('general'), general: Store.cfg.get("general"),
home: Store.cfg.get('home'), home: Store.cfg.get("home"),
libraryPrefs: Store.cfg.get('libraryPrefs'), libraryPrefs: Store.cfg.get("libraryPrefs"),
advanced: Store.cfg.get('advanced'), advanced: Store.cfg.get("advanced"),
}) });
} }
let postBody = { let postBody = {
id: Store.cfg.get('connectUser.id'), id: Store.cfg.get("connectUser.id"),
app: electron.app.getName(), app: electron.app.getName(),
version: electron.app.isPackaged ? electron.app.getVersion() : 'dev', version: electron.app.isPackaged ? electron.app.getVersion() : "dev",
syncData: syncData syncData: syncData,
} };
fetch('https://connect.cidercollective.dev/api/v1/setttings/set', { fetch("https://connect.cidercollective.dev/api/v1/setttings/set", {
method: 'POST', method: "POST",
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
body: JSON.stringify(postBody) body: JSON.stringify(postBody),
}) });
} }
/** /**
@ -335,40 +281,40 @@ export class Store {
* @param target The target configuration * @param target The target configuration
* @param source The source configuration * @param source The source configuration
*/ */
private mergeStore = (target: { [x: string]: any; }, source: { [x: string]: any; }) => { private mergeStore = (target: { [x: string]: any }, source: { [x: string]: any }) => {
// Iterate through `source` properties and if an `Object` set property to merge of `target` and `source` properties // Iterate through `source` properties and if an `Object` set property to merge of `target` and `source` properties
for (const key of Object.keys(source)) { for (const key of Object.keys(source)) {
if (key.includes('migrations')) { if (key.includes("migrations")) {
continue; continue;
} }
if (source[key] instanceof Array) { if (source[key] instanceof Array) {
continue continue;
} }
if (source[key] instanceof Object) Object.assign(source[key], this.mergeStore(target[key], source[key])) if (source[key] instanceof Object) Object.assign(source[key], this.mergeStore(target[key], source[key]));
} }
// Join `target` and modified `source` // Join `target` and modified `source`
Object.assign(target || {}, source) Object.assign(target || {}, source);
return target return target;
} };
/** /**
* IPC Handler * IPC Handler
*/ */
private ipcHandler(): void { private ipcHandler(): void {
electron.ipcMain.handle('getStoreValue', (_event, key, defaultValue) => { electron.ipcMain.handle("getStoreValue", (_event, key, defaultValue) => {
return (defaultValue ? Store.cfg.get(key, true) : Store.cfg.get(key)); return defaultValue ? Store.cfg.get(key, true) : Store.cfg.get(key);
}); });
electron.ipcMain.handle('setStoreValue', (_event, key, value) => { electron.ipcMain.handle("setStoreValue", (_event, key, value) => {
Store.cfg.set(key, value); Store.cfg.set(key, value);
}); });
electron.ipcMain.on('getStore', (event) => { electron.ipcMain.on("getStore", (event) => {
event.returnValue = Store.cfg.store event.returnValue = Store.cfg.store;
}) });
electron.ipcMain.on('setStore', (_event, store) => { electron.ipcMain.on("setStore", (_event, store) => {
Store.cfg.store = store Store.cfg.store = store;
}) });
} }
} }

View file

@ -7,30 +7,29 @@ import fetch from "electron-fetch";
import ElectronStore from "electron-store"; import ElectronStore from "electron-store";
export class utils { export class utils {
/** /**
* Playback Functions * Playback Functions
*/ */
static playback = { static playback = {
pause: () => { pause: () => {
bw.win.webContents.executeJavaScript("MusicKitInterop.pause()") bw.win.webContents.executeJavaScript("MusicKitInterop.pause()");
}, },
play: () => { play: () => {
bw.win.webContents.executeJavaScript("MusicKitInterop.play()") bw.win.webContents.executeJavaScript("MusicKitInterop.play()");
}, },
playPause: () => { playPause: () => {
bw.win.webContents.executeJavaScript("MusicKitInterop.playPause()") bw.win.webContents.executeJavaScript("MusicKitInterop.playPause()");
}, },
next: () => { next: () => {
bw.win.webContents.executeJavaScript("MusicKitInterop.next()") bw.win.webContents.executeJavaScript("MusicKitInterop.next()");
}, },
previous: () => { previous: () => {
bw.win.webContents.executeJavaScript("MusicKitInterop.previous()") bw.win.webContents.executeJavaScript("MusicKitInterop.previous()");
}, },
seek: (seconds: number) => { seek: (seconds: number) => {
bw.win.webContents.executeJavaScript(`MusicKit.getInstance().seekToTime(${seconds})`) bw.win.webContents.executeJavaScript(`MusicKit.getInstance().seekToTime(${seconds})`);
} },
} };
/** /**
* Paths for the application to use * Paths for the application to use
*/ */
@ -68,7 +67,7 @@ export class utils {
* Get the IPCMain * Get the IPCMain
*/ */
static getIPCMain(): Electron.IpcMain { static getIPCMain(): Electron.IpcMain {
return ipcMain return ipcMain;
} }
/* /*
@ -76,7 +75,7 @@ export class utils {
* @returns {any} * @returns {any}
*/ */
static getExpress(): any { static getExpress(): any {
return bw.express return bw.express;
} }
/** /**
@ -105,9 +104,9 @@ export class utils {
} */ } */
if (key) { if (key) {
return i18n[key] return i18n[key];
} else { } else {
return i18n return i18n;
} }
} }
@ -117,7 +116,7 @@ export class utils {
* @returns store value * @returns store value
*/ */
static getStoreValue(key: string): any { static getStoreValue(key: string): any {
return Store.cfg.get(key) return Store.cfg.get(key);
} }
/** /**
@ -125,7 +124,7 @@ export class utils {
* @returns store * @returns store
*/ */
static getStore(): Object { static getStore(): Object {
return Store.cfg.store return Store.cfg.store;
} }
/** /**
@ -133,7 +132,7 @@ export class utils {
* @returns {Store} * @returns {Store}
*/ */
static getStoreInstance(): ElectronStore { static getStoreInstance(): ElectronStore {
return Store.cfg return Store.cfg;
} }
/** /**
@ -142,7 +141,7 @@ export class utils {
* @param value * @param value
*/ */
static setStoreValue(key: string, value: any): void { static setStoreValue(key: string, value: any): void {
Store.cfg.set(key, value) Store.cfg.set(key, value);
} }
/** /**
@ -150,7 +149,7 @@ export class utils {
* @return Function * @return Function
*/ */
static pushStoreToConnect(): Function { static pushStoreToConnect(): Function {
return Store.pushToCloud return Store.pushToCloud;
} }
/** /**
@ -158,15 +157,13 @@ export class utils {
*/ */
static getWindow(): Electron.BrowserWindow { static getWindow(): Electron.BrowserWindow {
if (bw.win) { if (bw.win) {
return bw.win return bw.win;
} else { } else {
return BrowserWindow.getAllWindows()[0] return BrowserWindow.getAllWindows()[0];
} }
} }
static loadPluginFrontend(path: string): void { static loadPluginFrontend(path: string): void {}
}
static loadJSFrontend(path: string): void { static loadJSFrontend(path: string): void {
bw.win.webContents.executeJavaScript(fs.readFileSync(path, "utf8")); bw.win.webContents.executeJavaScript(fs.readFileSync(path, "utf8"));

View file

@ -100,7 +100,8 @@
"component": "<cider-groupings :data=\"browsepage\"></cider-groupings>", "component": "<cider-groupings :data=\"browsepage\"></cider-groupings>",
"condition": "page == 'groupings'", "condition": "page == 'groupings'",
"onEnter": "" "onEnter": ""
},{ },
{
"page": "charts", "page": "charts",
"component": "<cider-charts :data=\"browsepage\"></cider-charts>", "component": "<cider-charts :data=\"browsepage\"></cider-charts>",
"condition": "page == 'charts'", "condition": "page == 'charts'",

View file

@ -4,73 +4,71 @@ import * as electron from "electron";
const WebSocketServer = ws.Server; const WebSocketServer = ws.Server;
interface standardResponse { interface standardResponse {
status?: Number, status?: Number;
message?: String, message?: String;
data?: any, data?: any;
type?: string, type?: string;
} }
export class wsapi { export class wsapi {
static clients: any; static clients: any;
port: any = 26369 port: any = 26369;
wss: any = null wss: any = null;
clients: any = [] clients: any = [];
private _win: any; private _win: any;
constructor(win: any) { constructor(win: any) {
this._win = win; this._win = win;
} }
createId() { createId() {
// create random guid // create random guid
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, var r = (Math.random() * 16) | 0,
v = c == 'x' ? r : (r & 0x3 | 0x8); v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16); return v.toString(16);
}); });
} }
public async InitWebSockets() { public async InitWebSockets() {
electron.ipcMain.on('wsapi-updatePlaybackState', (_event: any, arg: any) => { electron.ipcMain.on("wsapi-updatePlaybackState", (_event: any, arg: any) => {
this.updatePlaybackState(arg); this.updatePlaybackState(arg);
}) });
electron.ipcMain.on('wsapi-returnQueue', (_event: any, arg: any) => { electron.ipcMain.on("wsapi-returnQueue", (_event: any, arg: any) => {
this.returnQueue(JSON.parse(arg)); this.returnQueue(JSON.parse(arg));
}); });
electron.ipcMain.on('wsapi-returnSearch', (_event: any, arg: any) => { electron.ipcMain.on("wsapi-returnSearch", (_event: any, arg: any) => {
console.log("SEARCH") console.log("SEARCH");
this.returnSearch(JSON.parse(arg)); this.returnSearch(JSON.parse(arg));
}); });
electron.ipcMain.on('wsapi-returnSearchLibrary', (_event: any, arg: any) => { electron.ipcMain.on("wsapi-returnSearchLibrary", (_event: any, arg: any) => {
this.returnSearchLibrary(JSON.parse(arg)); this.returnSearchLibrary(JSON.parse(arg));
}); });
electron.ipcMain.on('wsapi-returnDynamic', (_event: any, arg: any, type: any) => { electron.ipcMain.on("wsapi-returnDynamic", (_event: any, arg: any, type: any) => {
this.returnDynamic(JSON.parse(arg), type); this.returnDynamic(JSON.parse(arg), type);
}); });
electron.ipcMain.on('wsapi-returnMusicKitApi', (_event: any, arg: any, method: any) => { electron.ipcMain.on("wsapi-returnMusicKitApi", (_event: any, arg: any, method: any) => {
this.returnMusicKitApi(JSON.parse(arg), method); this.returnMusicKitApi(JSON.parse(arg), method);
}); });
electron.ipcMain.on('wsapi-returnLyrics', (_event: any, arg: any) => { electron.ipcMain.on("wsapi-returnLyrics", (_event: any, arg: any) => {
this.returnLyrics(JSON.parse(arg)); this.returnLyrics(JSON.parse(arg));
}); });
electron.ipcMain.on('wsapi-returnvolumeMax', (_event: any, arg: any) => { electron.ipcMain.on("wsapi-returnvolumeMax", (_event: any, arg: any) => {
this.returnmaxVolume(JSON.parse(arg)); this.returnmaxVolume(JSON.parse(arg));
}); });
electron.ipcMain.on('wsapi-libraryStatus', (_event: any, inLibrary: boolean, rating: number) => { electron.ipcMain.on("wsapi-libraryStatus", (_event: any, inLibrary: boolean, rating: number) => {
this.returnLibraryStatus(inLibrary, rating); this.returnLibraryStatus(inLibrary, rating);
}); });
electron.ipcMain.on('wsapi-rate', (_event: any, kind: string, id: string, rating: number) => { electron.ipcMain.on("wsapi-rate", (_event: any, kind: string, id: string, rating: number) => {
this.returnRatingStatus(kind, id, rating); this.returnRatingStatus(kind, id, rating);
}); });
electron.ipcMain.on('wsapi-change-library', (_event: any, kind: string, id: string, shouldAdd: boolean) => { electron.ipcMain.on("wsapi-change-library", (_event: any, kind: string, id: string, shouldAdd: boolean) => {
this.returnLibraryChange(kind, id, shouldAdd); this.returnLibraryChange(kind, id, shouldAdd);
}); });
this.wss = new WebSocketServer({ this.wss = new WebSocketServer({
@ -80,10 +78,10 @@ export class wsapi {
// See zlib defaults. // See zlib defaults.
chunkSize: 1024, chunkSize: 1024,
memLevel: 7, memLevel: 7,
level: 3 level: 3,
}, },
zlibInflateOptions: { zlibInflateOptions: {
chunkSize: 10 * 1024 chunkSize: 10 * 1024,
}, },
// Other options settable: // Other options settable:
clientNoContextTakeover: true, // Defaults to negotiated value. clientNoContextTakeover: true, // Defaults to negotiated value.
@ -91,26 +89,33 @@ export class wsapi {
serverMaxWindowBits: 10, // Defaults to negotiated value. serverMaxWindowBits: 10, // Defaults to negotiated value.
// Below options specified as default values. // Below options specified as default values.
concurrencyLimit: 10, // Limits zlib concurrency for perf. concurrencyLimit: 10, // Limits zlib concurrency for perf.
threshold: 1024 // Size (in bytes) below which messages threshold: 1024, // Size (in bytes) below which messages
// should not be compressed if context takeover is disabled. // should not be compressed if context takeover is disabled.
} },
}) });
console.log(`WebSocketServer started on port: ${this.port}`); console.log(`WebSocketServer started on port: ${this.port}`);
const defaultResponse: standardResponse = {status: 0, data: {}, message: "OK", type: "generic"}; const defaultResponse: standardResponse = {
status: 0,
data: {},
message: "OK",
type: "generic",
};
this.wss.on("connection", (ws: any) => {
this.wss.on('connection', (ws: any) => {
ws.id = this.createId(); ws.id = this.createId();
console.log(`Client ${ws.id} connected`) console.log(`Client ${ws.id} connected`);
this.clients.push(ws); this.clients.push(ws);
ws.on('message', function incoming(_message: any) { ws.on("message", function incoming(_message: any) {});
});
// ws on message // ws on message
ws.on('message', (message: any) => { ws.on("message", (message: any) => {
let data = JSON.parse(message); let data = JSON.parse(message);
let response: standardResponse = {status: 0, data: {}, message: "OK", type: "generic"}; let response: standardResponse = {
status: 0,
data: {},
message: "OK",
type: "generic",
};
if (data.action) { if (data.action) {
data.action.toLowerCase(); data.action.toLowerCase();
} }
@ -119,16 +124,16 @@ export class wsapi {
response.message = "Action not found"; response.message = "Action not found";
break; break;
case "identify": case "identify":
response.message = "Thanks for identifying!" response.message = "Thanks for identifying!";
response.data = { response.data = {
id: ws.id id: ws.id,
} };
ws.identity = { ws.identity = {
name: data.name, name: data.name,
author: data.author, author: data.author,
description: data.description, description: data.description,
version: data.version version: data.version,
} };
break; break;
case "play-next": case "play-next":
this._win.webContents.executeJavaScript(`wsapi.playNext(\`${data.type}\`,\`${data.id}\`)`); this._win.webContents.executeJavaScript(`wsapi.playNext(\`${data.type}\`,\`${data.id}\`)`);
@ -169,7 +174,7 @@ export class wsapi {
case "playpause": case "playpause":
this._win.webContents.executeJavaScript(`MusicKitInterop.playPause()`); this._win.webContents.executeJavaScript(`MusicKitInterop.playPause()`);
response.message = "Play/Pause"; response.message = "Play/Pause";
break break;
case "play": case "play":
this._win.webContents.executeJavaScript(`MusicKit.getInstance().play()`); this._win.webContents.executeJavaScript(`MusicKit.getInstance().play()`);
response.message = "Playing"; response.message = "Playing";
@ -203,7 +208,9 @@ export class wsapi {
response.message = "Next"; response.message = "Next";
break; break;
case "previous": case "previous":
this._win.webContents.executeJavaScript(`if (MusicKit.getInstance().queue.previousPlayableItemIndex != -1 && MusicKit.getInstance().queue.previousPlayableItemIndex != null) {MusicKit.getInstance().changeToMediaAtIndex(MusicKit.getInstance().queue.previousPlayableItemIndex)}`); this._win.webContents.executeJavaScript(
`if (MusicKit.getInstance().queue.previousPlayableItemIndex != -1 && MusicKit.getInstance().queue.previousPlayableItemIndex != null) {MusicKit.getInstance().changeToMediaAtIndex(MusicKit.getInstance().queue.previousPlayableItemIndex)}`
);
response.message = "Previous"; response.message = "Previous";
break; break;
case "musickit-api": case "musickit-api":
@ -233,10 +240,10 @@ export class wsapi {
this._win.webContents.executeJavaScript(`wsapi.searchLibrary(\`${data.term}\`, \`${data.limit}\`)`); this._win.webContents.executeJavaScript(`wsapi.searchLibrary(\`${data.term}\`, \`${data.limit}\`)`);
break; break;
case "show-window": case "show-window":
this._win.show() this._win.show();
break; break;
case "hide-window": case "hide-window":
this._win.hide() this._win.hide();
break; break;
case "play-mediaitem": case "play-mediaitem":
this._win.webContents.executeJavaScript(`wsapi.playTrackById("${data.id}", \`${data.kind}\`)`); this._win.webContents.executeJavaScript(`wsapi.playTrackById("${data.id}", \`${data.kind}\`)`);
@ -244,7 +251,7 @@ export class wsapi {
break; break;
case "get-status": case "get-status":
response.data = { response.data = {
isAuthorized: true isAuthorized: true,
}; };
response.message = "Status"; response.message = "Status";
break; break;
@ -267,7 +274,7 @@ export class wsapi {
ws.send(JSON.stringify(response)); ws.send(JSON.stringify(response));
}); });
ws.on('close', () => { ws.on("close", () => {
// remove client from list // remove client from list
this.clients.splice(wsapi.clients.indexOf(ws), 1); this.clients.splice(wsapi.clients.indexOf(ws), 1);
console.log(`Client ${ws.id} disconnected`); console.log(`Client ${ws.id} disconnected`);
@ -281,56 +288,96 @@ export class wsapi {
} }
updatePlaybackState(attr: any) { updatePlaybackState(attr: any) {
const response: standardResponse = {status: 0, data: attr, message: "OK", type: "playbackStateUpdate"}; const response: standardResponse = {
status: 0,
data: attr,
message: "OK",
type: "playbackStateUpdate",
};
this.clients.forEach(function each(client: any) { this.clients.forEach(function each(client: any) {
client.send(JSON.stringify(response)); client.send(JSON.stringify(response));
}); });
} }
returnMusicKitApi(results: any, method: any) { returnMusicKitApi(results: any, method: any) {
const response: standardResponse = {status: 0, data: results, message: "OK", type: `musickitapi.${method}`}; const response: standardResponse = {
status: 0,
data: results,
message: "OK",
type: `musickitapi.${method}`,
};
this.clients.forEach(function each(client: any) { this.clients.forEach(function each(client: any) {
client.send(JSON.stringify(response)); client.send(JSON.stringify(response));
}); });
} }
returnDynamic(results: any, type: any) { returnDynamic(results: any, type: any) {
const response: standardResponse = {status: 0, data: results, message: "OK", type: type}; const response: standardResponse = {
status: 0,
data: results,
message: "OK",
type: type,
};
this.clients.forEach(function each(client: any) { this.clients.forEach(function each(client: any) {
client.send(JSON.stringify(response)); client.send(JSON.stringify(response));
}); });
} }
returnLyrics(results: any) { returnLyrics(results: any) {
const response: standardResponse = {status: 0, data: results, message: "OK", type: "lyrics"}; const response: standardResponse = {
status: 0,
data: results,
message: "OK",
type: "lyrics",
};
this.clients.forEach(function each(client: any) { this.clients.forEach(function each(client: any) {
client.send(JSON.stringify(response)); client.send(JSON.stringify(response));
}); });
} }
returnSearch(results: any) { returnSearch(results: any) {
const response: standardResponse = {status: 0, data: results, message: "OK", type: "searchResults"}; const response: standardResponse = {
status: 0,
data: results,
message: "OK",
type: "searchResults",
};
this.clients.forEach(function each(client: any) { this.clients.forEach(function each(client: any) {
client.send(JSON.stringify(response)); client.send(JSON.stringify(response));
}); });
} }
returnSearchLibrary(results: any) { returnSearchLibrary(results: any) {
const response: standardResponse = {status: 0, data: results, message: "OK", type: "searchResultsLibrary"}; const response: standardResponse = {
status: 0,
data: results,
message: "OK",
type: "searchResultsLibrary",
};
this.clients.forEach(function each(client: any) { this.clients.forEach(function each(client: any) {
client.send(JSON.stringify(response)); client.send(JSON.stringify(response));
}); });
} }
returnQueue(queue: any) { returnQueue(queue: any) {
const response: standardResponse = {status: 0, data: queue, message: "OK", type: "queue"}; const response: standardResponse = {
status: 0,
data: queue,
message: "OK",
type: "queue",
};
this.clients.forEach(function each(client: any) { this.clients.forEach(function each(client: any) {
client.send(JSON.stringify(response)); client.send(JSON.stringify(response));
}); });
} }
returnmaxVolume(vol: any) { returnmaxVolume(vol: any) {
const response: standardResponse = {status: 0, data: vol, message: "OK", type: "maxVolume"}; const response: standardResponse = {
status: 0,
data: vol,
message: "OK",
type: "maxVolume",
};
this.clients.forEach(function each(client: any) { this.clients.forEach(function each(client: any) {
client.send(JSON.stringify(response)); client.send(JSON.stringify(response));
}); });
@ -338,10 +385,14 @@ export class wsapi {
returnLibraryStatus(inLibrary: boolean, rating: number) { returnLibraryStatus(inLibrary: boolean, rating: number) {
const response: standardResponse = { const response: standardResponse = {
status: 0, data: { status: 0,
inLibrary, rating data: {
}, message: "OK", type: "libraryStatus" inLibrary,
} rating,
},
message: "OK",
type: "libraryStatus",
};
this.clients.forEach(function each(client: any) { this.clients.forEach(function each(client: any) {
client.send(JSON.stringify(response)); client.send(JSON.stringify(response));
}); });
@ -349,8 +400,10 @@ export class wsapi {
returnRatingStatus(kind: string, id: string, rating: number) { returnRatingStatus(kind: string, id: string, rating: number) {
const response: standardResponse = { const response: standardResponse = {
status: 0, data: { kind, id, rating }, status: 0,
message: "OK", type: "rate" data: { kind, id, rating },
message: "OK",
type: "rate",
}; };
this.clients.forEach(function each(client: any) { this.clients.forEach(function each(client: any) {
client.send(JSON.stringify(response)); client.send(JSON.stringify(response));
@ -359,8 +412,10 @@ export class wsapi {
returnLibraryChange(kind: string, id: string, shouldAdd: boolean) { returnLibraryChange(kind: string, id: string, shouldAdd: boolean) {
const response: standardResponse = { const response: standardResponse = {
status: 0, data: { kind, id, add: shouldAdd }, status: 0,
message: "OK", type: "change-library" data: { kind, id, add: shouldAdd },
message: "OK",
type: "change-library",
}; };
this.clients.forEach(function each(client: any) { this.clients.forEach(function each(client: any) {
client.send(JSON.stringify(response)); client.send(JSON.stringify(response));

View file

@ -1,7 +1,7 @@
require("v8-compile-cache"); require("v8-compile-cache");
import { join } from "path"; import { join } from "path";
import {app} from "electron" import { app } from "electron";
if (!app.isPackaged) { if (!app.isPackaged) {
app.setPath("userData", join(app.getPath("appData"), "Cider")); app.setPath("userData", join(app.getPath("appData"), "Cider"));
} }
@ -12,7 +12,7 @@ import {Plugins} from "./base/plugins";
import { BrowserWindow } from "./base/browserwindow"; import { BrowserWindow } from "./base/browserwindow";
import { init as Sentry } from "@sentry/electron"; import { init as Sentry } from "@sentry/electron";
import { RewriteFrames } from "@sentry/integrations"; import { RewriteFrames } from "@sentry/integrations";
import {components, ipcMain} from "electron" import { components, ipcMain } from "electron";
// Analytics for debugging fun yeah. // Analytics for debugging fun yeah.
Sentry({ Sentry({
@ -35,29 +35,28 @@ const CiderPlug = new Plugins();
app.on("ready", () => { app.on("ready", () => {
Cider.ready(CiderPlug); Cider.ready(CiderPlug);
console.log("[Cider] Application is Ready. Creating Window.") console.log("[Cider] Application is Ready. Creating Window.");
if (!app.isPackaged) { if (!app.isPackaged) {
console.info("[Cider] Running in development mode.") console.info("[Cider] Running in development mode.");
require("vue-devtools").install() require("vue-devtools").install();
} }
components.whenReady().then(async () => { components.whenReady().then(async () => {
const bw = new BrowserWindow() const bw = new BrowserWindow();
const win = await bw.createWindow() const win = await bw.createWindow();
app.getGPUInfo("complete").then(gpuInfo => { app.getGPUInfo("complete").then((gpuInfo) => {
console.log(gpuInfo) console.log(gpuInfo);
}) });
console.log("[Cider][Widevine] Status:", components.status()); console.log("[Cider][Widevine] Status:", components.status());
Cider.bwCreated(); Cider.bwCreated();
win.on("ready-to-show", () => { win.on("ready-to-show", () => {
console.debug("[Cider] Window is Ready.") console.debug("[Cider] Window is Ready.");
CiderPlug.callPlugins("onReady", win); CiderPlug.callPlugins("onReady", win);
win.show(); win.show();
}); });
}); });
}); });
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -66,7 +65,7 @@ app.on("ready", () => {
ipcMain.handle("renderer-ready", (event) => { ipcMain.handle("renderer-ready", (event) => {
CiderPlug.callPlugins("onRendererReady", event); CiderPlug.callPlugins("onRendererReady", event);
}) });
ipcMain.on("playbackStateDidChange", (_event, attributes) => { ipcMain.on("playbackStateDidChange", (_event, attributes) => {
CiderPlug.callPlugins("onPlaybackStateDidChange", attributes); CiderPlug.callPlugins("onPlaybackStateDidChange", attributes);
@ -88,19 +87,19 @@ app.on("before-quit", () => {
// @ts-ignore // @ts-ignore
app.on("widevine-ready", (version, lastVersion) => { app.on("widevine-ready", (version, lastVersion) => {
if (null !== lastVersion) { if (null !== lastVersion) {
console.log("[Cider][Widevine] Widevine " + version + ", upgraded from " + lastVersion + ", is ready to be used!") console.log("[Cider][Widevine] Widevine " + version + ", upgraded from " + lastVersion + ", is ready to be used!");
} else { } else {
console.log("[Cider][Widevine] Widevine " + version + " is ready to be used!") console.log("[Cider][Widevine] Widevine " + version + " is ready to be used!");
} }
}) });
// @ts-ignore // @ts-ignore
app.on("widevine-update-pending", (currentVersion, pendingVersion) => { app.on("widevine-update-pending", (currentVersion, pendingVersion) => {
console.log("[Cider][Widevine] Widevine " + currentVersion + " is ready to be upgraded to " + pendingVersion + "!") console.log("[Cider][Widevine] Widevine " + currentVersion + " is ready to be upgraded to " + pendingVersion + "!");
}) });
// @ts-ignore // @ts-ignore
app.on("widevine-error", (error) => { app.on("widevine-error", (error) => {
console.log("[Cider][Widevine] Widevine installation encountered an error: " + error) console.log("[Cider][Widevine] Widevine installation encountered an error: " + error);
app.exit() app.exit();
}) });

View file

@ -1,10 +1,9 @@
import * as electron from 'electron'; import * as electron from "electron";
import * as os from 'os'; import * as os from "os";
import {resolve} from 'path'; import { resolve } from "path";
import * as CiderReceiver from '../base/castreceiver'; import * as CiderReceiver from "../base/castreceiver";
export default class ChromecastPlugin { export default class ChromecastPlugin {
/** /**
* Private variables for interaction in plugins * Private variables for interaction in plugins
*/ */
@ -13,8 +12,8 @@ export default class ChromecastPlugin {
private _lastfm: any; private _lastfm: any;
private _store: any; private _store: any;
private _timer: any; private _timer: any;
private audioClient = require('castv2-client').Client; private audioClient = require("castv2-client").Client;
private mdns = require('mdns-js'); private mdns = require("mdns-js");
private devices: any = []; private devices: any = [];
private castDevices: any = []; private castDevices: any = [];
@ -34,42 +33,39 @@ export default class ChromecastPlugin {
// private bufcount2 = 0; // private bufcount2 = 0;
// private headerSent = false; // private headerSent = false;
private searchForGCDevices() { private searchForGCDevices() {
try { try {
let browser = this.mdns.createBrowser(this.mdns.tcp("googlecast"));
browser.on("ready", browser.discover);
let browser = this.mdns.createBrowser(this.mdns.tcp('googlecast')); browser.on("update", (service: any) => {
browser.on('ready', browser.discover); if (service.addresses && service.fullname && service.fullname.includes("_googlecast._tcp")) {
let a = service.txt.filter((u: any) => String(u).startsWith("fn="));
browser.on('update', (service: any) => { let name = (a[0] ?? "").substring(3) != "" ? (a[0] ?? "").substring(3) : service.fullname.substring(0, service.fullname.indexOf("._googlecast"));
if (service.addresses && service.fullname && service.fullname.includes('_googlecast._tcp')) { this.ondeviceup(service.addresses[0], name + " (" + (service.type[0]?.description ?? "") + ")", "", "googlecast");
let a = service.txt.filter((u: any) => String(u).startsWith('fn='))
let name = (((a[0] ?? "").substring(3)) != "") ? ((a[0] ?? "").substring(3)) : (service.fullname.substring(0, service.fullname.indexOf("._googlecast")) )
this.ondeviceup(service.addresses[0], name+ " (" + (service.type[0]?.description ?? "") + ")" , '', 'googlecast');
} }
}); });
const Client = require('node-ssdp').Client; const Client = require("node-ssdp").Client;
// also do a SSDP/UPnP search // also do a SSDP/UPnP search
let ssdpBrowser = new Client(); let ssdpBrowser = new Client();
ssdpBrowser.on('response', (headers: any, statusCode: any, rinfo: any) => { ssdpBrowser.on("response", (headers: any, statusCode: any, rinfo: any) => {
var location = getLocation(headers); var location = getLocation(headers);
if (location != null) { if (location != null) {
this.getServiceDescription(location, rinfo.address); this.getServiceDescription(location, rinfo.address);
} }
}); });
function getLocation(headers: any) { function getLocation(headers: any) {
let location = null; let location = null;
if (headers["LOCATION"] != null) { if (headers["LOCATION"] != null) {
location = headers["LOCATION"] location = headers["LOCATION"];
} else if (headers["Location"] != null) { } else if (headers["Location"] != null) {
location = headers["Location"] location = headers["Location"];
} }
return location; return location;
} }
ssdpBrowser.search('urn:dial-multiscreen-org:device:dial:1'); ssdpBrowser.search("urn:dial-multiscreen-org:device:dial:1");
// // actual upnp devices // // actual upnp devices
// if (app.cfg.get("audio.enableDLNA")) { // if (app.cfg.get("audio.enableDLNA")) {
@ -84,15 +80,13 @@ export default class ChromecastPlugin {
// ssdpBrowser2.search('urn:schemas-upnp-org:device:MediaRenderer:1'); // ssdpBrowser2.search('urn:schemas-upnp-org:device:MediaRenderer:1');
// } // }
} catch (e) { } catch (e) {
console.log('Search GC err', e); console.log("Search GC err", e);
} }
} }
private getServiceDescription(url: any, address: any) { private getServiceDescription(url: any, address: any) {
const request = require('request'); const request = require("request");
request.get(url, (error: any, response: any, body: any) => { request.get(url, (error: any, response: any, body: any) => {
if (!error && response.statusCode === 200) { if (!error && response.statusCode === 200) {
this.parseServiceDescription(body, address, url); this.parseServiceDescription(body, address, url);
@ -106,7 +100,7 @@ export default class ChromecastPlugin {
name: name, name: name,
host: host, host: host,
location: location, location: location,
type: type type: type,
}); });
if (this.devices.indexOf(host) === -1) { if (this.devices.indexOf(host) === -1) {
this.devices.push(host); this.devices.push(host);
@ -122,22 +116,21 @@ export default class ChromecastPlugin {
} }
private parseServiceDescription(body: any, address: any, url: any) { private parseServiceDescription(body: any, address: any, url: any) {
const parseString = require('xml2js').parseString; const parseString = require("xml2js").parseString;
parseString(body, (err: any, result: any) => { parseString(body, (err: any, result: any) => {
if (!err && result && result.root && result.root.device) { if (!err && result && result.root && result.root.device) {
const device = result.root.device[0]; const device = result.root.device[0];
console.log('device', device); console.log("device", device);
let devicetype = 'googlecast'; let devicetype = "googlecast";
console.log() console.log();
if (device.deviceType && device.deviceType.toString() === 'urn:schemas-upnp-org:device:MediaRenderer:1') { if (device.deviceType && device.deviceType.toString() === "urn:schemas-upnp-org:device:MediaRenderer:1") {
devicetype = 'upnp'; devicetype = "upnp";
} }
this.ondeviceup(address, device.friendlyName.toString(), url, devicetype); this.ondeviceup(address, device.friendlyName.toString(), url, devicetype);
} }
}); });
} }
private loadMedia(client: any, song: any, artist: any, album: any, albumart: any, cb?: any) { private loadMedia(client: any, song: any, artist: any, album: any, albumart: any, cb?: any) {
// const u = 'http://' + this.getIp() + ':' + server.address().port + '/'; // const u = 'http://' + this.getIp() + ':' + server.address().port + '/';
// const DefaultMediaReceiver : any = require('castv2-client').DefaultMediaReceiver; // const DefaultMediaReceiver : any = require('castv2-client').DefaultMediaReceiver;
@ -148,9 +141,9 @@ export default class ChromecastPlugin {
} }
let media = { let media = {
// Here you can plug an URL to any mp4, webm, mp3 or jpg file with the proper contentType. // Here you can plug an URL to any mp4, webm, mp3 or jpg file with the proper contentType.
contentId: 'http://' + this.getIp() + ':'+ this.ciderPort +'/audio.wav', contentId: "http://" + this.getIp() + ":" + this.ciderPort + "/audio.wav",
contentType: 'audio/wav', contentType: "audio/wav",
streamType: 'LIVE', // or LIVE streamType: "LIVE", // or LIVE
// Title and cover displayed while buffering // Title and cover displayed while buffering
metadata: { metadata: {
@ -159,23 +152,25 @@ export default class ChromecastPlugin {
title: song ?? "", title: song ?? "",
albumName: album ?? "", albumName: album ?? "",
artist: artist ?? "", artist: artist ?? "",
images: [ images: [{ url: albumart ?? "" }],
{url: albumart ?? ""}] },
}
}; };
player.on('status', (status: any) => { player.on("status", (status: any) => {
console.log('status broadcast playerState=%s', status); console.log("status broadcast playerState=%s", status);
}); });
console.log('app "%s" launched, loading media %s ...', player, media); console.log('app "%s" launched, loading media %s ...', player, media);
player.load(media, { player.load(
autoplay: true media,
}, (err: any, status: any) => { {
console.log('media loaded playerState=%s', status); autoplay: true,
}); },
(err: any, status: any) => {
console.log("media loaded playerState=%s", status);
}
);
client.getStatus((x: any, status: any) => { client.getStatus((x: any, status: any) => {
if (status && status.volume) { if (status && status.volume) {
@ -183,40 +178,37 @@ export default class ChromecastPlugin {
client.muted = status.volume.muted; client.muted = status.volume.muted;
client.stepInterval = status.volume.stepInterval; client.stepInterval = status.volume.stepInterval;
} }
}) });
// send websocket ip // send websocket ip
player.sendIp("ws://" + this.getIp() + ":26369"); player.sendIp("ws://" + this.getIp() + ":26369");
electron.ipcMain.on('stopGCast', (_event) => { electron.ipcMain.on("stopGCast", (_event) => {
player.kill(); player.kill();
}) });
electron.app.on('before-quit', (_event) => { electron.app.on("before-quit", (_event) => {
player.kill(); player.kill();
}) });
}); });
} }
private getIp() { private getIp() {
let ip: string = ''; let ip: string = "";
let ip2: any = []; let ip2: any = [];
let alias = 0; let alias = 0;
const ifaces: any = os.networkInterfaces(); const ifaces: any = os.networkInterfaces();
for (let dev in ifaces) { for (let dev in ifaces) {
ifaces[dev].forEach((details: any) => { ifaces[dev].forEach((details: any) => {
if (details.family === 'IPv4' && !details.internal) { if (details.family === "IPv4" && !details.internal) {
if (!/(loopback|vmware|internal|hamachi|vboxnet|virtualbox)/gi.test(dev + (alias ? ':' + alias : ''))) { if (!/(loopback|vmware|internal|hamachi|vboxnet|virtualbox)/gi.test(dev + (alias ? ":" + alias : ""))) {
if (details.address.substring(0, 8) === '192.168.' || if (details.address.substring(0, 8) === "192.168." || details.address.substring(0, 7) === "172.16." || details.address.substring(0, 3) === "10.") {
details.address.substring(0, 7) === '172.16.' || if (
details.address.substring(0, 3) === '10.' !ip.startsWith("192.168.") ||
(ip2.startsWith("192.168.") && !ip.startsWith("192.168.") && ip2.startsWith("172.16.") && !ip.startsWith("192.168.") && !ip.startsWith("172.16.")) ||
(ip2.startsWith("10.") && !ip.startsWith("192.168.") && !ip.startsWith("172.16.") && !ip.startsWith("10."))
) { ) {
if (!ip.startsWith('192.168.') || ip = details.address;
(ip2.startsWith('192.168.') && !ip.startsWith('192.168.')) && }
(ip2.startsWith('172.16.') && !ip.startsWith('192.168.') && !ip.startsWith('172.16.')) ||
(ip2.startsWith('10.') && !ip.startsWith('192.168.') && !ip.startsWith('172.16.') && !ip.startsWith('10.'))
){ip = details.address;}
++alias; ++alias;
} }
} }
@ -227,13 +219,13 @@ export default class ChromecastPlugin {
} }
private stream(device: any, song: any, artist: any, album: any, albumart: any) { private stream(device: any, song: any, artist: any, album: any, albumart: any) {
let castMode = 'googlecast'; let castMode = "googlecast";
let UPNPDesc = ''; let UPNPDesc = "";
castMode = device.type; castMode = device.type;
UPNPDesc = device.location; UPNPDesc = device.location;
let client; let client;
if (castMode === 'googlecast') { if (castMode === "googlecast") {
let client = new this.audioClient(); let client = new this.audioClient();
client.volume = 100; client.volume = 100;
client.stepInterval = 0.5; client.stepInterval = 0.5;
@ -248,7 +240,7 @@ export default class ChromecastPlugin {
this.loadMedia(client, song, artist, album, albumart); this.loadMedia(client, song, artist, album, albumart);
}); });
client.on('close', () => { client.on("close", () => {
console.info("Client Closed"); console.info("Client Closed");
for (let i = this.activeConnections.length - 1; i >= 0; i--) { for (let i = this.activeConnections.length - 1; i >= 0; i--) {
if (this.activeConnections[i] === client) { if (this.activeConnections[i] === client) {
@ -258,12 +250,11 @@ export default class ChromecastPlugin {
} }
}); });
client.on('error', (err: any) => { client.on("error", (err: any) => {
console.log('Error: %s', err.message); console.log("Error: %s", err.message);
client.close(); client.close();
delete this.connectedHosts[device.host]; delete this.connectedHosts[device.host];
}); });
} else { } else {
// upnp devices // upnp devices
//try { //try {
@ -280,36 +271,33 @@ export default class ChromecastPlugin {
// // protocolInfo: 'DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000; // // protocolInfo: 'DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000;
// } // }
// }; // };
// client.load('http://' + getIp() + ':' + server.address().port + '/a.wav', options, function (err, _result) { // client.load('http://' + getIp() + ':' + server.address().port + '/a.wav', options, function (err, _result) {
// if (err) throw err; // if (err) throw err;
// console.log('playing ...'); // console.log('playing ...');
// }); // });
// } catch (e) { // } catch (e) {
// } // }
} }
} }
private async setupGCServer() { private async setupGCServer() {
return '' return "";
} }
/** /**
* Base Plugin Details (Eventually implemented into a GUI in settings) * Base Plugin Details (Eventually implemented into a GUI in settings)
*/ */
public name: string = 'Chromecast'; public name: string = "Chromecast";
public description: string = 'LastFM plugin for Cider'; public description: string = "LastFM plugin for Cider";
public version: string = '0.0.1'; public version: string = "0.0.1";
public author: string = 'vapormusic / Cider Collective'; public author: string = "vapormusic / Cider Collective";
/** /**
* Runs on plugin load (Currently run on application start) * Runs on plugin load (Currently run on application start)
*/ */
constructor(utils: { getApp: () => any; getStore: () => any; }) { constructor(utils: { getApp: () => any; getStore: () => any }) {
this._app = utils.getApp(); this._app = utils.getApp();
this._store = utils.getStore() this._store = utils.getStore();
} }
/** /**
@ -317,11 +305,11 @@ export default class ChromecastPlugin {
*/ */
onReady(win: any): void { onReady(win: any): void {
this._win = win; this._win = win;
electron.ipcMain.on('getKnownCastDevices', (event) => { electron.ipcMain.on("getKnownCastDevices", (event) => {
event.returnValue = this.castDevices event.returnValue = this.castDevices;
}); });
electron.ipcMain.on('performGCCast', (event, device, song, artist, album, albumart) => { electron.ipcMain.on("performGCCast", (event, device, song, artist, album, albumart) => {
// this.setupGCServer().then( () => { // this.setupGCServer().then( () => {
this._win.webContents.setAudioMuted(true); this._win.webContents.setAudioMuted(true);
console.log(device); console.log(device);
@ -329,44 +317,36 @@ export default class ChromecastPlugin {
// }) // })
}); });
electron.ipcMain.on('getChromeCastDevices', (_event, _data) => { electron.ipcMain.on("getChromeCastDevices", (_event, _data) => {
this.searchForGCDevices(); this.searchForGCDevices();
}); });
electron.ipcMain.on('stopGCast', (_event) => { electron.ipcMain.on("stopGCast", (_event) => {
this._win.webContents.setAudioMuted(false); this._win.webContents.setAudioMuted(false);
this.activeConnections.forEach((client: any) => { this.activeConnections.forEach((client: any) => {
try { try {
client.stop(); client.stop();
} catch (e) {} } catch (e) {}
}) });
this.activeConnections = []; this.activeConnections = [];
this.connectedHosts = {}; this.connectedHosts = {};
});
})
} }
/** /**
* Runs on app stop * Runs on app stop
*/ */
onBeforeQuit(): void { onBeforeQuit(): void {}
}
/** /**
* Runs on song change * Runs on song change
* @param attributes Music Attributes * @param attributes Music Attributes
*/ */
onNowPlayingItemDidChange(attributes: any): void { onNowPlayingItemDidChange(attributes: any): void {}
}
onRendererReady(): void { onRendererReady(): void {
this._win.webContents.executeJavaScript( this._win.webContents.executeJavaScript(`ipcRenderer.sendSync('get-port')`).then((result: any) => {
`ipcRenderer.sendSync('get-port')`
).then((result: any) => {
this.ciderPort = result; this.ciderPort = result;
}); });
} }
} }

View file

@ -1,16 +1,15 @@
import {AutoClient} from 'discord-auto-rpc' import { AutoClient } from "discord-auto-rpc";
import { ipcMain } from "electron"; import { ipcMain } from "electron";
import fetch from 'electron-fetch' import fetch from "electron-fetch";
export default class DiscordRPC { export default class DiscordRPC {
/** /**
* Base Plugin Details (Eventually implemented into a GUI in settings) * Base Plugin Details (Eventually implemented into a GUI in settings)
*/ */
public name: string = 'Discord Rich Presence'; public name: string = "Discord Rich Presence";
public description: string = 'Discord RPC plugin for Cider'; public description: string = "Discord RPC plugin for Cider";
public version: string = '1.1.0'; public version: string = "1.1.0";
public author: string = 'vapormusic/Core (Cider Collective)'; public author: string = "vapormusic/Core (Cider Collective)";
/** /**
* Private variables for interaction in plugins * Private variables for interaction in plugins
@ -24,13 +23,13 @@ export default class DiscordRPC {
*/ */
private _client: any = null; private _client: any = null;
private _activityCache: any = { private _activityCache: any = {
details: '', details: "",
state: '', state: "",
largeImageKey: '', largeImageKey: "",
largeImageText: '', largeImageText: "",
smallImageKey: '', smallImageKey: "",
smallImageText: '', smallImageText: "",
instance: false instance: false,
}; };
/******************************************************************************************* /*******************************************************************************************
@ -49,74 +48,71 @@ export default class DiscordRPC {
* Runs on app ready * Runs on app ready
*/ */
onReady(_win: any): void { onReady(_win: any): void {
const self = this const self = this;
this.connect(); this.connect();
console.debug(`[Plugin][${this.name}] Ready.`); console.debug(`[Plugin][${this.name}] Ready.`);
ipcMain.on('updateRPCImage', async (_event, imageurl) => { ipcMain.on("updateRPCImage", async (_event, imageurl) => {
if (!this._utils.getStoreValue("general.privateEnabled")) { if (!this._utils.getStoreValue("general.privateEnabled")) {
let b64data = "" let b64data = "";
let postbody = "" let postbody = "";
if (imageurl.startsWith("/ciderlocalart")) { if (imageurl.startsWith("/ciderlocalart")) {
let port = await _win.webContents.executeJavaScript( let port = await _win.webContents.executeJavaScript(`app.clientPort`);
`app.clientPort` console.log("http://localhost:" + port + imageurl);
); const response = await fetch("http://localhost:" + port + imageurl);
console.log("http://localhost:"+port+imageurl) b64data = (await response.buffer()).toString("base64");
const response = await fetch("http://localhost:"+port+imageurl) postbody = JSON.stringify({ data: b64data });
b64data = (await response.buffer()).toString('base64'); fetch("https://api.cider.sh/v1/images", {
postbody = JSON.stringify({data: b64data}) method: "POST",
fetch('https://api.cider.sh/v1/images', {
method: 'POST',
body: postbody, body: postbody,
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
'User-Agent': _win.webContents.getUserAgent() "User-Agent": _win.webContents.getUserAgent(),
}, },
}) })
.then(res => res.json()) .then((res) => res.json())
.then(function (json) { .then(function (json) {
self._attributes["artwork"]["url"] = json.url self._attributes["artwork"]["url"] = json.url;
self.setActivity(self._attributes) self.setActivity(self._attributes);
}) });
} else { } else {
postbody = JSON.stringify({url: imageurl}) postbody = JSON.stringify({ url: imageurl });
fetch('https://api.cider.sh/v1/images', { fetch("https://api.cider.sh/v1/images", {
method: "POST",
method: 'POST',
body: postbody, body: postbody,
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
'User-Agent': _win.webContents.getUserAgent() "User-Agent": _win.webContents.getUserAgent(),
}, },
}) })
.then(res => res.json()) .then((res) => res.json())
.then(function (json) { .then(function (json) {
self._attributes["artwork"]["url"] = json.url self._attributes["artwork"]["url"] = json.url;
self.setActivity(self._attributes) self.setActivity(self._attributes);
}) });
} }
} }
}) });
ipcMain.on("reloadRPC", () => { ipcMain.on("reloadRPC", () => {
console.log(`[DiscordRPC][reload] Reloading DiscordRPC.`); console.log(`[DiscordRPC][reload] Reloading DiscordRPC.`);
this._client.destroy() this._client.destroy();
this._client.endlessLogin({clientId: this._utils.getStoreValue("connectivity.discord_rpc.client") === "Cider" ? '911790844204437504' : '886578863147192350'}) this._client
.endlessLogin({
clientId: this._utils.getStoreValue("connectivity.discord_rpc.client") === "Cider" ? "911790844204437504" : "886578863147192350",
})
.then(() => { .then(() => {
this.ready = true this.ready = true;
this._utils.getWindow().webContents.send("rpcReloaded", this._client.user) this._utils.getWindow().webContents.send("rpcReloaded", this._client.user);
if (this._activityCache && this._activityCache.details && this._activityCache.state) { if (this._activityCache && this._activityCache.details && this._activityCache.state) {
console.info(`[DiscordRPC][reload] Restoring activity cache.`); console.info(`[DiscordRPC][reload] Restoring activity cache.`);
this._client.setActivity(this._activityCache) this._client.setActivity(this._activityCache);
} }
}) })
.catch((e: any) => console.error(`[DiscordRPC][reload] ${e}`)); .catch((e: any) => console.error(`[DiscordRPC][reload] ${e}`));
// this.connect(true) // this.connect(true)
}) });
} }
/** /**
* Runs on app stop * Runs on app stop
*/ */
@ -129,9 +125,8 @@ export default class DiscordRPC {
* @param attributes Music Attributes (attributes.status = current state) * @param attributes Music Attributes (attributes.status = current state)
*/ */
onPlaybackStateDidChange(attributes: object): void { onPlaybackStateDidChange(attributes: object): void {
this._attributes = attributes this._attributes = attributes;
this.setActivity(attributes) this.setActivity(attributes);
} }
/** /**
@ -139,12 +134,10 @@ export default class DiscordRPC {
* @param attributes Music Attributes * @param attributes Music Attributes
*/ */
onNowPlayingItemDidChange(attributes: object): void { onNowPlayingItemDidChange(attributes: object): void {
this._attributes = attributes this._attributes = attributes;
this.setActivity(attributes) this.setActivity(attributes);
} }
/******************************************************************************************* /*******************************************************************************************
* Private Methods * Private Methods
* ****************************************************************************************/ * ****************************************************************************************/
@ -162,19 +155,22 @@ export default class DiscordRPC {
this._client = new AutoClient({ transport: "ipc" }); this._client = new AutoClient({ transport: "ipc" });
// Runs on Ready // Runs on Ready
this._client.once('ready', () => { this._client.once("ready", () => {
console.info(`[DiscordRPC][connect] Successfully Connected to Discord. Authed for user: ${this._client.user.id}.`); console.info(`[DiscordRPC][connect] Successfully Connected to Discord. Authed for user: ${this._client.user.id}.`);
if (this._activityCache && this._activityCache.details && this._activityCache.state) { if (this._activityCache && this._activityCache.details && this._activityCache.state) {
console.info(`[DiscordRPC][connect] Restoring activity cache.`); console.info(`[DiscordRPC][connect] Restoring activity cache.`);
this._client.setActivity(this._activityCache) this._client.setActivity(this._activityCache);
} }
}) });
// Login to Discord // Login to Discord
this._client.endlessLogin({clientId: this._utils.getStoreValue("connectivity.discord_rpc.client") === "Cider" ? '911790844204437504' : '886578863147192350'}) this._client
.endlessLogin({
clientId: this._utils.getStoreValue("connectivity.discord_rpc.client") === "Cider" ? "911790844204437504" : "886578863147192350",
})
.then(() => { .then(() => {
this.ready = true this.ready = true;
}) })
.catch((e: any) => console.error(`[DiscordRPC][connect] ${e}`)); .catch((e: any) => console.error(`[DiscordRPC][connect] ${e}`));
} }
@ -185,31 +181,31 @@ export default class DiscordRPC {
*/ */
private setActivity(attributes: any) { private setActivity(attributes: any) {
if (!this._client) { if (!this._client) {
return return;
} }
// Check if show buttons is (true) or (false) // Check if show buttons is (true) or (false)
let activity: Object = { let activity: Object = {
details: this._utils.getStoreValue("connectivity.discord_rpc.details_format"), details: this._utils.getStoreValue("connectivity.discord_rpc.details_format"),
state: this._utils.getStoreValue("connectivity.discord_rpc.state_format"), state: this._utils.getStoreValue("connectivity.discord_rpc.state_format"),
largeImageKey: attributes?.artwork?.url?.replace('{w}', '1024').replace('{h}', '1024'), largeImageKey: attributes?.artwork?.url?.replace("{w}", "1024").replace("{h}", "1024"),
largeImageText: attributes.albumName, largeImageText: attributes.albumName,
instance: false // Whether the activity is in a game session instance: false, // Whether the activity is in a game session
} };
// Filter the activity // Filter the activity
activity = this.filterActivity(activity, attributes) activity = this.filterActivity(activity, attributes);
if (!this.ready) { if (!this.ready) {
this._activityCache = activity this._activityCache = activity;
return return;
} }
// Set the activity // Set the activity
if (!attributes.status && this._utils.getStoreValue("connectivity.discord_rpc.clear_on_pause")) { if (!attributes.status && this._utils.getStoreValue("connectivity.discord_rpc.clear_on_pause")) {
this._client.clearActivity() this._client.clearActivity();
} else if (activity && this._activityCache !== activity) { } else if (activity && this._activityCache !== activity) {
this._client.setActivity(activity) this._client.setActivity(activity);
} }
this._activityCache = activity; this._activityCache = activity;
} }
@ -218,25 +214,24 @@ export default class DiscordRPC {
* Filter the Discord activity object * Filter the Discord activity object
*/ */
private filterActivity(activity: any, attributes: any): Object { private filterActivity(activity: any, attributes: any): Object {
// Add the buttons if people want them // Add the buttons if people want them
if (!this._utils.getStoreValue("connectivity.discord_rpc.hide_buttons")) { if (!this._utils.getStoreValue("connectivity.discord_rpc.hide_buttons")) {
activity.buttons = [ activity.buttons = [
{label: 'Listen on Cider', url: attributes.url.cider}, { label: "Listen on Cider", url: attributes.url.cider },
{label: 'View on Apple Music', url: attributes.url.appleMusic} { label: "View on Apple Music", url: attributes.url.appleMusic },
] //To change attributes.url => preload/cider-preload.js ]; //To change attributes.url => preload/cider-preload.js
} }
// Add the timestamp if its playing and people want them // Add the timestamp if its playing and people want them
if (!this._utils.getStoreValue("connectivity.discord_rpc.hide_timestamp") && attributes.status) { if (!this._utils.getStoreValue("connectivity.discord_rpc.hide_timestamp") && attributes.status) {
activity.startTimestamp = Date.now() - (attributes?.durationInMillis - attributes?.remainingTime) activity.startTimestamp = Date.now() - (attributes?.durationInMillis - attributes?.remainingTime);
activity.endTimestamp = attributes.endTime activity.endTimestamp = attributes.endTime;
} }
// If the user wants to keep the activity when paused // If the user wants to keep the activity when paused
if (!this._utils.getStoreValue("connectivity.discord_rpc.clear_on_pause")) { if (!this._utils.getStoreValue("connectivity.discord_rpc.clear_on_pause")) {
activity.smallImageKey = attributes.status ? 'play' : 'pause'; activity.smallImageKey = attributes.status ? "play" : "pause";
activity.smallImageText = attributes.status ? 'Playing' : 'Paused'; activity.smallImageText = attributes.status ? "Playing" : "Paused";
} }
/** /**
@ -248,36 +243,36 @@ export default class DiscordRPC {
* {trackNumber} * {trackNumber}
*/ */
const rpcVars: any = { const rpcVars: any = {
"artist": attributes.artistName, artist: attributes.artistName,
"composer": attributes.composerName, composer: attributes.composerName,
"title": attributes.name, title: attributes.name,
"album": attributes.albumName, album: attributes.albumName,
"trackNumber": attributes.trackNumber trackNumber: attributes.trackNumber,
} };
// Replace the variables // Replace the variables
Object.keys(rpcVars).forEach((key) => { Object.keys(rpcVars).forEach((key) => {
if (activity.details.includes(`{${key}}`)) { if (activity.details.includes(`{${key}}`)) {
activity.details = activity.details.replace(`{${key}}`, rpcVars[key]) activity.details = activity.details.replace(`{${key}}`, rpcVars[key]);
} }
if (activity.state.includes(`{${key}}`)) { if (activity.state.includes(`{${key}}`)) {
activity.state = activity.state.replace(`{${key}}`, rpcVars[key]) activity.state = activity.state.replace(`{${key}}`, rpcVars[key]);
} }
}) });
// Checks if the details is greater than 128 because some songs can be that long // Checks if the details is greater than 128 because some songs can be that long
if (activity.details && activity.details.length >= 128) { if (activity.details && activity.details.length >= 128) {
activity.details = activity.details.substring(0, 125) + '...' activity.details = activity.details.substring(0, 125) + "...";
} }
// Checks if the state is greater than 128 because some songs can be that long // Checks if the state is greater than 128 because some songs can be that long
if (activity.state && activity.state.length >= 128) { if (activity.state && activity.state.length >= 128) {
activity.state = activity.state.substring(0, 125) + '...' activity.state = activity.state.substring(0, 125) + "...";
} }
// Checks if the state is greater than 128 because some songs can be that long // Checks if the state is greater than 128 because some songs can be that long
if (activity.largeImageText && activity.largeImageText.length >= 128) { if (activity.largeImageText && activity.largeImageText.length >= 128) {
activity.largeImageText = activity.largeImageText.substring(0, 125) + '...' activity.largeImageText = activity.largeImageText.substring(0, 125) + "...";
} }
// Check large image // Check large image
@ -287,8 +282,8 @@ export default class DiscordRPC {
// Timestamp // Timestamp
if (new Date(attributes.endTime).getTime() < 0) { if (new Date(attributes.endTime).getTime() < 0) {
delete activity.startTime delete activity.startTime;
delete activity.endTime delete activity.endTime;
} }
// not sure // not sure
@ -297,8 +292,8 @@ export default class DiscordRPC {
} }
if (!activity.largeImageText || activity.largeImageText.length < 2) { if (!activity.largeImageText || activity.largeImageText.length < 2) {
delete activity.largeImageText delete activity.largeImageText;
} }
return activity return activity;
} }
} }

View file

@ -1,17 +1,15 @@
export default class lastfm { export default class lastfm {
/** /**
* Base Plugin Information * Base Plugin Information
*/ */
public name: string = 'LastFM Plugin'; public name: string = "LastFM Plugin";
public version: string = '2.0.0'; public version: string = "2.0.0";
public author: string = 'Core (Cider Collective)'; public author: string = "Core (Cider Collective)";
private _apiCredentials = { private _apiCredentials = {
key: "f9986d12aab5a0fe66193c559435ede3", key: "f9986d12aab5a0fe66193c559435ede3",
secret: "acba3c29bd5973efa38cc2f0b63cc625" secret: "acba3c29bd5973efa38cc2f0b63cc625",
} };
/** /**
* Plugin Initialization * Plugin Initialization
*/ */
@ -31,42 +29,41 @@ export default class lastfm {
} }
onReady(_win: Electron.BrowserWindow): void { onReady(_win: Electron.BrowserWindow): void {
this.initializeLastFM("", this._apiCredentials) this.initializeLastFM("", this._apiCredentials);
// Register the ipcMain handlers // Register the ipcMain handlers
this._utils.getIPCMain().handle('lastfm:url', (event: any) => { this._utils.getIPCMain().handle("lastfm:url", (event: any) => {
console.debug(`[${lastfm.name}:url] Called.`) console.debug(`[${lastfm.name}:url] Called.`);
return this._lfm.getAuthenticationUrl({"cb": "cider://auth/lastfm"}) return this._lfm.getAuthenticationUrl({ cb: "cider://auth/lastfm" });
}) });
this._utils.getIPCMain().on('lastfm:auth', (event: any, token: string) => { this._utils.getIPCMain().on("lastfm:auth", (event: any, token: string) => {
console.debug(`[${lastfm.name}:auth] Token: `, token) console.debug(`[${lastfm.name}:auth] Token: `, token);
this.authenticateLastFM(token) this.authenticateLastFM(token);
}) });
this._utils.getIPCMain().on('lastfm:disconnect', (_event: any) => { this._utils.getIPCMain().on("lastfm:disconnect", (_event: any) => {
this._lfm.setSessionCredentials(null, null); this._lfm.setSessionCredentials(null, null);
this._authenticated = false; this._authenticated = false;
console.debug(`[${lastfm.name}:disconnect] Disconnected`) console.debug(`[${lastfm.name}:disconnect] Disconnected`);
}) });
this._utils.getIPCMain().on('lastfm:nowPlayingChange', (event: any, attributes: any) => { this._utils.getIPCMain().on("lastfm:nowPlayingChange", (event: any, attributes: any) => {
if (this._utils.getStoreValue("connectivity.lastfm.filter_loop") || this._utils.getStoreValue("general.privateEnabled")) return; if (this._utils.getStoreValue("connectivity.lastfm.filter_loop") || this._utils.getStoreValue("general.privateEnabled")) return;
this.updateNowPlayingTrack(attributes) this.updateNowPlayingTrack(attributes);
}) });
this._utils.getIPCMain().on('lastfm:scrobbleTrack', (event: any, attributes: any) => { this._utils.getIPCMain().on("lastfm:scrobbleTrack", (event: any, attributes: any) => {
if (this._utils.getStoreValue("general.privateEnabled")) return; if (this._utils.getStoreValue("general.privateEnabled")) return;
this.scrobbleTrack(attributes) this.scrobbleTrack(attributes);
}) });
} }
/** /**
* Runs on playback State Change * Runs on playback State Change
* @param attributes Music Attributes (attributes.status = current state) * @param attributes Music Attributes (attributes.status = current state)
*/ */
onPlaybackStateDidChange(attributes: object): void { onPlaybackStateDidChange(attributes: object): void {}
}
/** /**
* Runs on song change * Runs on song change
@ -75,7 +72,7 @@ export default class lastfm {
*/ */
onNowPlayingItemDidChange(attributes: any, scrobble = false): void { onNowPlayingItemDidChange(attributes: any, scrobble = false): void {
if (this._utils.getStoreValue("general.privateEnabled")) return; if (this._utils.getStoreValue("general.privateEnabled")) return;
this.updateNowPlayingTrack(attributes) this.updateNowPlayingTrack(attributes);
} }
/** /**
@ -84,19 +81,19 @@ export default class lastfm {
* @param api * @param api
* @private * @private
*/ */
private initializeLastFM(token: string, api: { key: string, secret: string }): void { private initializeLastFM(token: string, api: { key: string; secret: string }): void {
console.debug(`[${lastfm.name}:initialize] Initializing LastFM`) console.debug(`[${lastfm.name}:initialize] Initializing LastFM`);
const LastfmAPI = require("lastfmapi") const LastfmAPI = require("lastfmapi");
this._lfm = new LastfmAPI({ this._lfm = new LastfmAPI({
'api_key': api.key, api_key: api.key,
'secret': api.secret, secret: api.secret,
}); });
if (this._utils.getStoreValue("connectivity.lastfm.secrets.username") && this._utils.getStoreValue("connectivity.lastfm.secrets.key")) { if (this._utils.getStoreValue("connectivity.lastfm.secrets.username") && this._utils.getStoreValue("connectivity.lastfm.secrets.key")) {
this._lfm.setSessionCredentials(this._utils.getStoreValue("connectivity.lastfm.secrets.username"), this._utils.getStoreValue("connectivity.lastfm.secrets.key")); this._lfm.setSessionCredentials(this._utils.getStoreValue("connectivity.lastfm.secrets.username"), this._utils.getStoreValue("connectivity.lastfm.secrets.key"));
this._authenticated = true; this._authenticated = true;
} else { } else {
this.authenticateLastFM(token) this.authenticateLastFM(token);
} }
} }
@ -111,12 +108,12 @@ export default class lastfm {
if (err) { if (err) {
console.error(`[${lastfm.name}:authenticate] Error: ${typeof err === "string" ? err : err.message}`); console.error(`[${lastfm.name}:authenticate] Error: ${typeof err === "string" ? err : err.message}`);
this._utils.getWindow().webContents.executeJavaScript(`app.notyf.error("${err.message}");`) this._utils.getWindow().webContents.executeJavaScript(`app.notyf.error("${err.message}");`);
return; return;
} }
this._utils.getWindow().webContents.send('lastfm:authenticated', session) this._utils.getWindow().webContents.send("lastfm:authenticated", session);
this._authenticated = true; this._authenticated = true;
console.debug(`[${lastfm.name}:authenticate] Authenticated as ${session.username}`) console.debug(`[${lastfm.name}:authenticate] Authenticated as ${session.username}`);
}); });
} }
@ -130,33 +127,34 @@ export default class lastfm {
if (!attributes) return attributes; if (!attributes) return attributes;
if (!attributes.lfmAlbum) { if (!attributes.lfmAlbum) {
this._lfm.album.getInfo({ this._lfm.album.getInfo(
"artist": attributes.primaryArtist, {
"album": attributes.albumName artist: attributes.primaryArtist,
}, (err: any, data: any) => { album: attributes.albumName,
},
(err: any, data: any) => {
if (err) { if (err) {
console.error(`[${lastfm.name}] [album.getInfo] Error: ${typeof err === "string" ? err : err.message}`) console.error(`[${lastfm.name}] [album.getInfo] Error: ${typeof err === "string" ? err : err.message}`);
return {}; return {};
} }
if (data) { if (data) {
attributes.lfmAlbum = data attributes.lfmAlbum = data;
callback(attributes) callback(attributes);
} }
}) }
);
} else { } else {
this._lfm.track.getCorrection(attributes.primaryArtist, attributes.name, (err: any, data: any) => { this._lfm.track.getCorrection(attributes.primaryArtist, attributes.name, (err: any, data: any) => {
if (err) { if (err) {
console.error(`[${lastfm.name}] [track.getCorrection] Error: ${typeof err === "string" ? err : err.message}`) console.error(`[${lastfm.name}] [track.getCorrection] Error: ${typeof err === "string" ? err : err.message}`);
return {}; return {};
} }
if (data) { if (data) {
attributes.lfmTrack = data.correction.track attributes.lfmTrack = data.correction.track;
callback(attributes) callback(attributes);
} }
}) });
} }
} }
/** /**
@ -167,26 +165,32 @@ export default class lastfm {
private scrobbleTrack(attributes: any): void { private scrobbleTrack(attributes: any): void {
if (!attributes?.lfmTrack || !attributes?.lfmAlbum) { if (!attributes?.lfmTrack || !attributes?.lfmAlbum) {
this.verifyTrack(attributes, (a: any) => { this.verifyTrack(attributes, (a: any) => {
this.scrobbleTrack(a) this.scrobbleTrack(a);
}) });
return return;
} }
if (!this._authenticated || !attributes || this._utils.getStoreValue("connectivity.lastfm.filter_types")[attributes.playParams.kind] || (this._utils.getStoreValue("connectivity.lastfm.filter_loop") && this._scrobbleCache.track === attributes.lfmTrack.name)) return; if (
!this._authenticated ||
!attributes ||
this._utils.getStoreValue("connectivity.lastfm.filter_types")[attributes.playParams.kind] ||
(this._utils.getStoreValue("connectivity.lastfm.filter_loop") && this._scrobbleCache.track === attributes.lfmTrack.name)
)
return;
// Scrobble // Scrobble
const scrobble = { const scrobble = {
'artist': attributes.lfmTrack.artist.name, artist: attributes.lfmTrack.artist.name,
'track': attributes.lfmTrack.name, track: attributes.lfmTrack.name,
'album': attributes.lfmAlbum.name, album: attributes.lfmAlbum.name,
'albumArtist': attributes.lfmAlbum.artist, albumArtist: attributes.lfmAlbum.artist,
'timestamp': new Date().getTime() / 1000, timestamp: new Date().getTime() / 1000,
'trackNumber': attributes.trackNumber, trackNumber: attributes.trackNumber,
'duration': attributes.durationInMillis / 1000, duration: attributes.durationInMillis / 1000,
} };
// Easy Debugging // Easy Debugging
console.debug(`[${lastfm.name}:scrobble] Scrobbling ${scrobble.artist} - ${scrobble.track}`) console.debug(`[${lastfm.name}:scrobble] Scrobbling ${scrobble.artist} - ${scrobble.track}`);
// Scrobble the track // Scrobble the track
this._lfm.track.scrobble(scrobble, (err: any, _res: any) => { this._lfm.track.scrobble(scrobble, (err: any, _res: any) => {
@ -194,7 +198,7 @@ export default class lastfm {
console.error(`[${lastfm.name}:scrobble] Scrobble failed: ${err.message}`); console.error(`[${lastfm.name}:scrobble] Scrobble failed: ${err.message}`);
} else { } else {
console.debug(`[${lastfm.name}:scrobble] Track scrobbled: ${scrobble.artist} - ${scrobble.track}`); console.debug(`[${lastfm.name}:scrobble] Track scrobbled: ${scrobble.artist} - ${scrobble.track}`);
this._scrobbleCache = scrobble this._scrobbleCache = scrobble;
} }
}); });
} }
@ -207,30 +211,35 @@ export default class lastfm {
private updateNowPlayingTrack(attributes: any): void { private updateNowPlayingTrack(attributes: any): void {
if (!attributes?.lfmTrack || !attributes?.lfmAlbum) { if (!attributes?.lfmTrack || !attributes?.lfmAlbum) {
this.verifyTrack(attributes, (a: any) => { this.verifyTrack(attributes, (a: any) => {
this.updateNowPlayingTrack(a) this.updateNowPlayingTrack(a);
}) });
return return;
} }
if (!this._authenticated || !attributes || this._utils.getStoreValue("connectivity.lastfm.filter_types")[attributes.playParams.kind] || (this._utils.getStoreValue("connectivity.lastfm.filter_loop") && this._nowPlayingCache.track === attributes.lfmTrack.name)) return; if (
!this._authenticated ||
!attributes ||
this._utils.getStoreValue("connectivity.lastfm.filter_types")[attributes.playParams.kind] ||
(this._utils.getStoreValue("connectivity.lastfm.filter_loop") && this._nowPlayingCache.track === attributes.lfmTrack.name)
)
return;
const nowPlaying = { const nowPlaying = {
'artist': attributes.lfmTrack.artist.name, artist: attributes.lfmTrack.artist.name,
'track': attributes.lfmTrack.name, track: attributes.lfmTrack.name,
'album': attributes.lfmAlbum.name, album: attributes.lfmAlbum.name,
'trackNumber': attributes.trackNumber, trackNumber: attributes.trackNumber,
'duration': attributes.durationInMillis / 1000, duration: attributes.durationInMillis / 1000,
'albumArtist': attributes.lfmAlbum.artist, albumArtist: attributes.lfmAlbum.artist,
} };
this._lfm.track.updateNowPlaying(nowPlaying, (err: any, res: any) => { this._lfm.track.updateNowPlaying(nowPlaying, (err: any, res: any) => {
if (err) { if (err) {
console.error(`[${lastfm.name}:updateNowPlaying] Now Playing Update failed: ${err.message}`); console.error(`[${lastfm.name}:updateNowPlaying] Now Playing Update failed: ${err.message}`);
} else { } else {
console.debug(`[${lastfm.name}:updateNowPlaying] Now Playing Updated: ${nowPlaying.artist} - ${nowPlaying.track}`); console.debug(`[${lastfm.name}:updateNowPlaying] Now Playing Updated: ${nowPlaying.artist} - ${nowPlaying.track}`);
this._nowPlayingCache = nowPlaying this._nowPlayingCache = nowPlaying;
} }
}); });
} }
} }

View file

@ -2,312 +2,300 @@ import {app, Menu, shell} from "electron";
import { utils } from "../base/utils"; import { utils } from "../base/utils";
export default class Thumbar { export default class Thumbar {
/** /**
* Base Plugin Details (Eventually implemented into a GUI in settings) * Base Plugin Details (Eventually implemented into a GUI in settings)
*/ */
public name: string = 'Menubar Plugin'; public name: string = "Menubar Plugin";
public description: string = 'Creates the menubar'; public description: string = "Creates the menubar";
public version: string = '1.0.0'; public version: string = "1.0.0";
public author: string = 'Core'; public author: string = "Core";
public contributors: string[] = ['Core', 'Qwack', 'Monochromish']; public contributors: string[] = ["Core", "Qwack", "Monochromish"];
/** /**
* Menubar Assets * Menubar Assets
* @private * @private
*/ */
private isNotMac: boolean = process.platform !== 'darwin'; private isNotMac: boolean = process.platform !== "darwin";
private isMac: boolean = process.platform === 'darwin'; private isMac: boolean = process.platform === "darwin";
private _menuTemplate: any = [ private _menuTemplate: any = [
{ {
label: app.getName(), label: app.getName(),
submenu: [ submenu: [
{ {
label: `${utils.getLocale(utils.getStoreValue('general.language'), 'term.about')} ${app.getName()}`, label: `${utils.getLocale(utils.getStoreValue("general.language"), "term.about")} ${app.getName()}`,
click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('about')`) click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('about')`),
}, },
{type: 'separator'}, { type: "separator" },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.toggleprivate'), label: utils.getLocale(utils.getStoreValue("general.language"), "term.toggleprivate"),
accelerator: utils.getStoreValue("general.keybindings.togglePrivateSession").join('+'), accelerator: utils.getStoreValue("general.keybindings.togglePrivateSession").join("+"),
click: () => utils.getWindow().webContents.executeJavaScript(`app.cfg.general.privateEnabled = !app.cfg.general.privateEnabled`) click: () => utils.getWindow().webContents.executeJavaScript(`app.cfg.general.privateEnabled = !app.cfg.general.privateEnabled`),
}, },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.settings'), label: utils.getLocale(utils.getStoreValue("general.language"), "term.settings"),
accelerator: utils.getStoreValue("general.keybindings.settings").join('+'), accelerator: utils.getStoreValue("general.keybindings.settings").join("+"),
click: () => utils.getWindow().webContents.executeJavaScript(`app.openSettingsPage()`) click: () => utils.getWindow().webContents.executeJavaScript(`app.openSettingsPage()`),
}, },
...(this.isMac ? [ ...(this.isMac ? [{ type: "separator" }, { role: "services" }, { type: "separator" }, { role: "hide" }, { role: "hideOthers" }, { role: "unhide" }, { type: "separator" }, { role: "quit" }] : []),
{type: 'separator'}, ...(this.isNotMac
{role: 'services'}, ? [
{type: 'separator'}, { type: "separator" },
{role: 'hide'},
{role: 'hideOthers'},
{role: 'unhide'},
{type: 'separator'},
{role: 'quit'}
] : []),
...(this.isNotMac ? [
{type: 'separator'},
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.quit'), label: utils.getLocale(utils.getStoreValue("general.language"), "term.quit"),
accelerator: 'Control+Q', accelerator: "Control+Q",
click: () => app.quit() click: () => app.quit(),
}
] : [])
]
},
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.view'),
submenu: [
...(this.isMac ? [
{role: 'reload'},
{role: 'forceReload'},
{role: 'toggleDevTools'},
{type: 'separator'},
{role: 'resetZoom'},
{role: 'zoomIn'},
{role: 'zoomOut'},
{type: 'separator'},
{role: 'togglefullscreen'},
{type: 'separator'},
] : []),
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.search'),
accelerator: utils.getStoreValue("general.keybindings.search").join('+'),
click: () => utils.getWindow().webContents.executeJavaScript('app.focusSearch()')
},
{type:'separator'},
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.listenNow'),
accelerator: utils.getStoreValue('general.keybindings.listnow').join('+'),
click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('listen_now')`)
},
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.browse'),
accelerator: utils.getStoreValue("general.keybindings.browse").join('+'),
click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('browse')`)
},
{type: 'separator'},
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.recentlyAdded')
,accelerator: utils.getStoreValue("general.keybindings.recentAdd").join('+'),
click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('library-recentlyadded')`)
},
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.songs'),
accelerator: utils.getStoreValue("general.keybindings.songs").join('+'),
click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('library-songs')`)
},
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.albums'),
accelerator: utils.getStoreValue("general.keybindings.albums").join('+'),
click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('library-albums')`)
},
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.artists'),
accelerator: utils.getStoreValue("general.keybindings.artists").join('+'),
click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('library-artists')`)
}, },
] ]
: []),
],
}, },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.window'), label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.view"),
submenu: [ submenu: [
{role: 'minimize', label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.minimize')}, ...(this.isMac
{type: 'separator'}, ? [
...(this.isMac ? [ { role: "reload" },
{ { role: "forceReload" },
label: 'Show', { role: "toggleDevTools" },
click: () => utils.getWindow().show() { type: "separator" },
}, { role: "resetZoom" },
{role: 'zoom'}, { role: "zoomIn" },
{type: 'separator'}, { role: "zoomOut" },
{role: 'front'}, { type: "separator" },
{role: 'close'}, { role: "togglefullscreen" },
{ { type: "separator" },
label: 'Edit',
submenu: [
{role: 'undo'},
{role: 'redo'},
{type: 'separator'},
{role: 'cut'},
{role: 'copy'},
{role: 'paste'},
] ]
}, : []),
{type: 'separator'},
] : [ ]),
...(this.isNotMac ? [
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.zoom'), label: utils.getLocale(utils.getStoreValue("general.language"), "term.search"),
accelerator: utils.getStoreValue("general.keybindings.search").join("+"),
click: () => utils.getWindow().webContents.executeJavaScript("app.focusSearch()"),
},
{ type: "separator" },
{
label: utils.getLocale(utils.getStoreValue("general.language"), "term.listenNow"),
accelerator: utils.getStoreValue("general.keybindings.listnow").join("+"),
click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('listen_now')`),
},
{
label: utils.getLocale(utils.getStoreValue("general.language"), "term.browse"),
accelerator: utils.getStoreValue("general.keybindings.browse").join("+"),
click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('browse')`),
},
{ type: "separator" },
{
label: utils.getLocale(utils.getStoreValue("general.language"), "term.recentlyAdded"),
accelerator: utils.getStoreValue("general.keybindings.recentAdd").join("+"),
click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('library-recentlyadded')`),
},
{
label: utils.getLocale(utils.getStoreValue("general.language"), "term.songs"),
accelerator: utils.getStoreValue("general.keybindings.songs").join("+"),
click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('library-songs')`),
},
{
label: utils.getLocale(utils.getStoreValue("general.language"), "term.albums"),
accelerator: utils.getStoreValue("general.keybindings.albums").join("+"),
click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('library-albums')`),
},
{
label: utils.getLocale(utils.getStoreValue("general.language"), "term.artists"),
accelerator: utils.getStoreValue("general.keybindings.artists").join("+"),
click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('library-artists')`),
},
],
},
{
label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.window"),
submenu: [ submenu: [
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.zoomin'), role: "minimize",
role: 'zoomIn', label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.minimize"),
accelerator: utils.getStoreValue("general.keybindings.zoomn").join('+')
}, },
{ type: "separator" },
...(this.isMac
? [
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.zoomout'), label: "Show",
role: 'zoomOut', click: () => utils.getWindow().show(),
accelerator: utils.getStoreValue("general.keybindings.zoomt").join('+')
}, },
{ role: "zoom" },
{ type: "separator" },
{ role: "front" },
{ role: "close" },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.zoomreset'), label: "Edit",
role: 'resetZoom', submenu: [{ role: "undo" }, { role: "redo" }, { type: "separator" }, { role: "cut" }, { role: "copy" }, { role: "paste" }],
accelerator: utils.getStoreValue("general.keybindings.zoomrst").join('+') },
} { type: "separator" },
] ]
}, : []),
{type: 'separator'}, ...(this.isNotMac
? [
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.fullscreen'), label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.zoom"),
accelerator: 'Control+Enter', submenu: [
role: 'togglefullscreen'
},
{type: 'separator'},
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'action.close'), label: utils.getLocale(utils.getStoreValue("general.language"), "term.zoomin"),
accelerator: 'Control+W', role: "zoomIn",
role: 'close' accelerator: utils.getStoreValue("general.keybindings.zoomn").join("+"),
},
{type:'separator'},
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.reload'),
accelerator: 'Control+R',
role: 'reload'
}, },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.forcereload'), label: utils.getLocale(utils.getStoreValue("general.language"), "term.zoomout"),
accelerator: 'Control+Shift+R', role: "zoomOut",
role: 'forceReload' accelerator: utils.getStoreValue("general.keybindings.zoomt").join("+"),
}, },
] : []), {
label: utils.getLocale(utils.getStoreValue("general.language"), "term.zoomreset"),
role: "resetZoom",
accelerator: utils.getStoreValue("general.keybindings.zoomrst").join("+"),
},
],
},
{ type: "separator" },
{
label: utils.getLocale(utils.getStoreValue("general.language"), "term.fullscreen"),
accelerator: "Control+Enter",
role: "togglefullscreen",
},
{ type: "separator" },
{
label: utils.getLocale(utils.getStoreValue("general.language"), "action.close"),
accelerator: "Control+W",
role: "close",
},
{ type: "separator" },
{
label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.reload"),
accelerator: "Control+R",
role: "reload",
},
{
label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.forcereload"),
accelerator: "Control+Shift+R",
role: "forceReload",
},
]
: []),
], ],
}, },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.controls'), label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.controls"),
submenu: [ submenu: [
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.playpause'), label: utils.getLocale(utils.getStoreValue("general.language"), "term.playpause"),
accelerator: 'Space', accelerator: "Space",
click: () => utils.getWindow().webContents.executeJavaScript(`app.SpacePause()`) click: () => utils.getWindow().webContents.executeJavaScript(`app.SpacePause()`),
}, },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.next'), label: utils.getLocale(utils.getStoreValue("general.language"), "term.next"),
accelerator: 'CommandOrControl+Right', accelerator: "CommandOrControl+Right",
click: () => utils.getWindow().webContents.executeJavaScript(`MusicKitInterop.next()`) click: () => utils.getWindow().webContents.executeJavaScript(`MusicKitInterop.next()`),
}, },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.previous'), label: utils.getLocale(utils.getStoreValue("general.language"), "term.previous"),
accelerator: 'CommandOrControl+Left', accelerator: "CommandOrControl+Left",
click: () => utils.getWindow().webContents.executeJavaScript(`MusicKitInterop.previous()`) click: () => utils.getWindow().webContents.executeJavaScript(`MusicKitInterop.previous()`),
}, },
{type: 'separator'}, { type: "separator" },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.volumeup'), label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.volumeup"),
accelerator: 'CommandOrControl+Up', accelerator: "CommandOrControl+Up",
click: () => utils.getWindow().webContents.executeJavaScript(`app.volumeUp()`) click: () => utils.getWindow().webContents.executeJavaScript(`app.volumeUp()`),
}, },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.volumedown'), label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.volumedown"),
accelerator: 'CommandOrControl+Down', accelerator: "CommandOrControl+Down",
click: () => utils.getWindow().webContents.executeJavaScript(`app.volumeDown()`) click: () => utils.getWindow().webContents.executeJavaScript(`app.volumeDown()`),
}, },
{type: 'separator'}, { type: "separator" },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.cast2'), label: utils.getLocale(utils.getStoreValue("general.language"), "term.cast2"),
accelerator: utils.getStoreValue("general.keybindings.castToDevices").join('+'), accelerator: utils.getStoreValue("general.keybindings.castToDevices").join("+"),
click: () => utils.getWindow().webContents.executeJavaScript(`app.modals.castMenu = true`) click: () => utils.getWindow().webContents.executeJavaScript(`app.modals.castMenu = true`),
}, },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.webremote'), label: utils.getLocale(utils.getStoreValue("general.language"), "term.webremote"),
accelerator: utils.getStoreValue("general.keybindings.webRemote").join('+'), accelerator: utils.getStoreValue("general.keybindings.webRemote").join("+"),
sublabel: 'Opens in external window', sublabel: "Opens in external window",
click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('remote-pair')`) click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('remote-pair')`),
}, },
{type: 'separator'}, { type: "separator" },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.audioSettings'), label: utils.getLocale(utils.getStoreValue("general.language"), "term.audioSettings"),
accelerator: utils.getStoreValue("general.keybindings.audioSettings").join('+'), accelerator: utils.getStoreValue("general.keybindings.audioSettings").join("+"),
click: () => utils.getWindow().webContents.executeJavaScript(`app.modals.audioSettings = true`) click: () => utils.getWindow().webContents.executeJavaScript(`app.modals.audioSettings = true`),
}, },
{type: 'separator'}, { type: "separator" },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.plugins'), label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.plugins"),
accelerator: utils.getStoreValue("general.keybindings.pluginMenu").join('+'), accelerator: utils.getStoreValue("general.keybindings.pluginMenu").join("+"),
click: () => utils.getWindow().webContents.executeJavaScript(`app.modals.pluginMenu = true`) click: () => utils.getWindow().webContents.executeJavaScript(`app.modals.pluginMenu = true`),
} },
],
]
}, },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.account'), label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.account"),
submenu: [ submenu: [
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.accountSettings'), label: utils.getLocale(utils.getStoreValue("general.language"), "term.accountSettings"),
click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('apple-account-settings')`) click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('apple-account-settings')`),
}, },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.signout'), label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.signout"),
click: () => utils.getWindow().webContents.executeJavaScript(`app.unauthorize()`) click: () => utils.getWindow().webContents.executeJavaScript(`app.unauthorize()`),
} },
] ],
}, },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.support'), label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.support"),
role: 'help', role: "help",
submenu: [ submenu: [
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.discord'), label: utils.getLocale(utils.getStoreValue("general.language"), "term.discord"),
click: () => shell.openExternal("https://discord.gg/AppleMusic").catch(console.error) click: () => shell.openExternal("https://discord.gg/AppleMusic").catch(console.error),
}, },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.github'), label: utils.getLocale(utils.getStoreValue("general.language"), "term.github"),
click: () => shell.openExternal("https://github.com/ciderapp/Cider/wiki/Troubleshooting").catch(console.error) click: () => shell.openExternal("https://github.com/ciderapp/Cider/wiki/Troubleshooting").catch(console.error),
}, },
{type: 'separator'}, { type: "separator" },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.report'), label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.report"),
submenu: [ submenu: [
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.bug'), label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.bug"),
click: () => shell.openExternal("https://github.com/ciderapp/Cider/issues/new?assignees=&labels=bug%2Ctriage&template=bug_report.yaml&title=%5BBug%5D%3A+").catch(console.error) click: () => shell.openExternal("https://github.com/ciderapp/Cider/issues/new?assignees=&labels=bug%2Ctriage&template=bug_report.yaml&title=%5BBug%5D%3A+").catch(console.error),
}, },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.feature'), label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.feature"),
click: () => shell.openExternal("https://github.com/ciderapp/Cider/discussions/new?category=feature-request").catch(console.error) click: () => shell.openExternal("https://github.com/ciderapp/Cider/discussions/new?category=feature-request").catch(console.error),
}, },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.trans'), label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.trans"),
click: () => shell.openExternal("https://github.com/ciderapp/Cider/issues/new?assignees=&labels=%F0%9F%8C%90+Translations&template=translation.yaml&title=%5BTranslation%5D%3A+").catch(console.error) click: () => shell.openExternal("https://github.com/ciderapp/Cider/issues/new?assignees=&labels=%F0%9F%8C%90+Translations&template=translation.yaml&title=%5BTranslation%5D%3A+").catch(console.error),
}, },
] ],
}, },
{type: 'separator'}, { type: "separator" },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.license'), label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.license"),
click: () => shell.openExternal("https://github.com/ciderapp/Cider/blob/main/LICENSE").catch(console.error) click: () => shell.openExternal("https://github.com/ciderapp/Cider/blob/main/LICENSE").catch(console.error),
}, },
{type: 'separator'}, { type: "separator" },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.toggledevtools'), label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.toggledevtools"),
accelerator: utils.getStoreValue("general.keybindings.openDeveloperTools").join('+'), accelerator: utils.getStoreValue("general.keybindings.openDeveloperTools").join("+"),
click: () => utils.getWindow().webContents.openDevTools() click: () => utils.getWindow().webContents.openDevTools(),
}, },
{ {
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.conf'), label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.conf"),
click: () => utils.getStoreInstance().openInEditor() click: () => utils.getStoreInstance().openInEditor(),
} },
] ],
} },
]; ];
/******************************************************************************************* /*******************************************************************************************
@ -326,7 +314,7 @@ export default class Thumbar {
*/ */
onReady(_win: Electron.BrowserWindow): void { onReady(_win: Electron.BrowserWindow): void {
const menu = Menu.buildFromTemplate(this._menuTemplate); const menu = Menu.buildFromTemplate(this._menuTemplate);
Menu.setApplicationMenu(menu) Menu.setApplicationMenu(menu);
} }
/** /**
@ -340,16 +328,11 @@ export default class Thumbar {
* Runs on playback State Change * Runs on playback State Change
* @param attributes Music Attributes (attributes.status = current state) * @param attributes Music Attributes (attributes.status = current state)
*/ */
onPlaybackStateDidChange(attributes: object): void { onPlaybackStateDidChange(attributes: object): void {}
}
/** /**
* Runs on song change * Runs on song change
* @param attributes Music Attributes * @param attributes Music Attributes
*/ */
onNowPlayingItemDidChange(attributes: object): void { onNowPlayingItemDidChange(attributes: object): void {}
}
} }

View file

@ -1,5 +1,5 @@
// @ts-ignore // @ts-ignore
import * as Player from 'mpris-service'; import * as Player from "mpris-service";
export default class mpris { export default class mpris {
/** /**
@ -13,10 +13,10 @@ export default class mpris {
/** /**
* Base Plugin Details (Eventually implemented into a GUI in settings) * Base Plugin Details (Eventually implemented into a GUI in settings)
*/ */
public name: string = 'MPRIS Service'; public name: string = "MPRIS Service";
public description: string = 'Handles MPRIS service calls for Linux systems.'; public description: string = "Handles MPRIS service calls for Linux systems.";
public version: string = '1.0.0'; public version: string = "1.0.0";
public author: string = 'Core'; public author: string = "Core";
/******************************************************************************************* /*******************************************************************************************
* Private Methods * Private Methods
@ -26,7 +26,7 @@ export default class mpris {
* Runs on plugin load (Currently run on application start) * Runs on plugin load (Currently run on application start)
*/ */
constructor(utils: any) { constructor(utils: any) {
mpris.utils = utils mpris.utils = utils;
console.debug(`[Plugin][${mpris.name}] Loading Complete.`); console.debug(`[Plugin][${mpris.name}] Loading Complete.`);
} }
@ -37,10 +37,10 @@ export default class mpris {
* @decorator * @decorator
*/ */
private static linuxOnly(_target: any, _propertyKey: string, descriptor: PropertyDescriptor) { private static linuxOnly(_target: any, _propertyKey: string, descriptor: PropertyDescriptor) {
if (process.platform !== 'linux') { if (process.platform !== "linux") {
descriptor.value = function () { descriptor.value = function () {
return return;
} };
} }
} }
@ -48,37 +48,36 @@ export default class mpris {
* Connects to MPRIS Service * Connects to MPRIS Service
*/ */
private static connect() { private static connect() {
const player = Player({ const player = Player({
name: 'cider', name: "cider",
identity: 'Cider', identity: "Cider",
supportedInterfaces: ['player'] supportedInterfaces: ["player"],
}); });
console.debug(`[${mpris.name}:connect] Successfully connected.`); console.debug(`[${mpris.name}:connect] Successfully connected.`);
const renderer = mpris.utils.getWindow().webContents const renderer = mpris.utils.getWindow().webContents;
const loopType: { [key: string]: number; } = { const loopType: { [key: string]: number } = {
'none': 0, none: 0,
'track': 1, track: 1,
'playlist': 2, playlist: 2,
} };
player.on('next', () => mpris.utils.playback.next()) player.on("next", () => mpris.utils.playback.next());
player.on('previous', () => mpris.utils.playback.previous()) player.on("previous", () => mpris.utils.playback.previous());
player.on('playpause', () => mpris.utils.playback.playPause()) player.on("playpause", () => mpris.utils.playback.playPause());
player.on('play', () => mpris.utils.playback.play()) player.on("play", () => mpris.utils.playback.play());
player.on('pause', () => mpris.utils.playback.pause()) player.on("pause", () => mpris.utils.playback.pause());
player.on('quit', () => mpris.utils.getApp().exit()) player.on("quit", () => mpris.utils.getApp().exit());
player.on('position', (args: { position: any; }) => mpris.utils.playback.seek(args.position / 1000 / 1000)) player.on("position", (args: { position: any }) => mpris.utils.playback.seek(args.position / 1000 / 1000));
player.on('loopStatus', (status: string) => renderer.executeJavaScript(`app.mk.repeatMode = ${loopType[status.toLowerCase()]}`)) player.on("loopStatus", (status: string) => renderer.executeJavaScript(`app.mk.repeatMode = ${loopType[status.toLowerCase()]}`));
player.on('shuffle', () => renderer.executeJavaScript('app.mk.shuffleMode = (app.mk.shuffleMode === 0) ? 1 : 0')) player.on("shuffle", () => renderer.executeJavaScript("app.mk.shuffleMode = (app.mk.shuffleMode === 0) ? 1 : 0"));
mpris.utils.getIPCMain().on('mpris:playbackTimeDidChange', (event: any, time: number) => { mpris.utils.getIPCMain().on("mpris:playbackTimeDidChange", (event: any, time: number) => {
player.getPosition = () => time; player.getPosition = () => time;
}) });
mpris.utils.getIPCMain().on('repeatModeDidChange', (_e: any, mode: number) => { mpris.utils.getIPCMain().on("repeatModeDidChange", (_e: any, mode: number) => {
switch (mode) { switch (mode) {
case 0: case 0:
player.loopStatus = Player.LOOP_STATUS_NONE; player.loopStatus = Player.LOOP_STATUS_NONE;
@ -90,11 +89,11 @@ export default class mpris {
player.loopStatus = Player.LOOP_STATUS_PLAYLIST; player.loopStatus = Player.LOOP_STATUS_PLAYLIST;
break; break;
} }
}) });
mpris.utils.getIPCMain().on('shuffleModeDidChange', (_e: any, mode: number) => { mpris.utils.getIPCMain().on("shuffleModeDidChange", (_e: any, mode: number) => {
player.shuffle = mode === 1 player.shuffle = mode === 1;
}) });
mpris.player = player; mpris.player = player;
} }
@ -103,15 +102,14 @@ export default class mpris {
* Update M.P.R.I.S Player Attributes * Update M.P.R.I.S Player Attributes
*/ */
private static updateMetaData(attributes: any) { private static updateMetaData(attributes: any) {
mpris.player.metadata = { mpris.player.metadata = {
'mpris:trackid': mpris.player.objectPath(`track/${attributes.playParams.id.replace(/[.]+/g, "")}`), "mpris:trackid": mpris.player.objectPath(`track/${attributes.playParams.id.replace(/[.]+/g, "")}`),
'mpris:length': attributes.durationInMillis * 1000, // In microseconds "mpris:length": attributes.durationInMillis * 1000, // In microseconds
'mpris:artUrl': (attributes.artwork.url.replace('/{w}x{h}bb', '/512x512bb')).replace('/2000x2000bb', '/35x35bb'), "mpris:artUrl": attributes.artwork.url.replace("/{w}x{h}bb", "/512x512bb").replace("/2000x2000bb", "/35x35bb"),
'xesam:title': `${attributes.name}`, "xesam:title": `${attributes.name}`,
'xesam:album': `${attributes.albumName}`, "xesam:album": `${attributes.albumName}`,
'xesam:artist': [`${attributes.artistName}`], "xesam:artist": [`${attributes.artistName}`],
'xesam:genre': attributes.genreNames "xesam:genre": attributes.genreNames,
}; };
} }
@ -125,9 +123,11 @@ export default class mpris {
*/ */
private static clearState() { private static clearState() {
if (!mpris.player) { if (!mpris.player) {
return return;
} }
mpris.player.metadata = {'mpris:trackid': '/org/mpris/MediaPlayer2/TrackList/NoTrack'} mpris.player.metadata = {
"mpris:trackid": "/org/mpris/MediaPlayer2/TrackList/NoTrack",
};
mpris.player.playbackStatus = Player.PLAYBACK_STATUS_STOPPED; mpris.player.playbackStatus = Player.PLAYBACK_STATUS_STOPPED;
} }
@ -144,7 +144,7 @@ export default class mpris {
*/ */
@mpris.linuxOnly @mpris.linuxOnly
onRendererReady(): void { onRendererReady(): void {
mpris.connect() mpris.connect();
} }
/** /**
@ -153,7 +153,7 @@ export default class mpris {
@mpris.linuxOnly @mpris.linuxOnly
onBeforeQuit(): void { onBeforeQuit(): void {
console.debug(`[Plugin][${mpris.name}] Stopped.`); console.debug(`[Plugin][${mpris.name}] Stopped.`);
mpris.clearState() mpris.clearState();
} }
/** /**
@ -162,7 +162,7 @@ export default class mpris {
*/ */
@mpris.linuxOnly @mpris.linuxOnly
onPlaybackStateDidChange(attributes: any): void { onPlaybackStateDidChange(attributes: any): void {
mpris.player.playbackStatus = attributes?.status ? Player.PLAYBACK_STATUS_PLAYING : Player.PLAYBACK_STATUS_PAUSED mpris.player.playbackStatus = attributes?.status ? Player.PLAYBACK_STATUS_PLAYING : Player.PLAYBACK_STATUS_PAUSED;
} }
/** /**
@ -173,5 +173,4 @@ export default class mpris {
onNowPlayingItemDidChange(attributes: object): void { onNowPlayingItemDidChange(attributes: object): void {
mpris.updateMetaData(attributes); mpris.updateMetaData(attributes);
} }
} }

View file

@ -5,16 +5,14 @@ import {createWriteStream} from "fs";
import { join } from "path"; import { join } from "path";
export default class playbackNotifications { export default class playbackNotifications {
/** /**
* Base Plugin Details (Eventually implemented into a GUI in settings) * Base Plugin Details (Eventually implemented into a GUI in settings)
*/ */
public name: string = 'Playback Notifications'; public name: string = "Playback Notifications";
public description: string = 'Creates notifications on playback.'; public description: string = "Creates notifications on playback.";
public version: string = '1.0.0'; public version: string = "1.0.0";
public author: string = 'Core'; public author: string = "Core";
public contributors: string[] = ['Core', 'Monochromish']; public contributors: string[] = ["Core", "Monochromish"];
private _utils: any; private _utils: any;
private _notification: Notification | undefined; private _notification: Notification | undefined;
@ -35,50 +33,48 @@ export default class playbackNotifications {
body: `${a.artistName}${a.albumName}`, body: `${a.artistName}${a.albumName}`,
silent: true, silent: true,
icon: this._artworkImage[a.artwork.url], icon: this._artworkImage[a.artwork.url],
urgency: 'low', urgency: "low",
actions: [ actions: [
{ {
'type': 'button', type: "button",
'text': `${this._utils.getLocale(this._utils.getStoreValue('general.language'), 'term.skip')}` text: `${this._utils.getLocale(this._utils.getStoreValue("general.language"), "term.skip")}`,
} },
], ],
toastXml: ` toastXml: `
<toast> <toast>
<audio silent="true" /> <audio silent="true" />
<visual> <visual>
<binding template="ToastImageAndText02"> <binding template="ToastImageAndText02">
<image id="1" src="${join(app.getPath("temp"), `${a.songId}-${a.artwork.url.split('/').pop()}`)}" name="Image" /> <image id="1" src="${join(app.getPath("temp"), `${a.songId}-${a.artwork.url.split("/").pop()}`)}" name="Image" />
<text id="1">${a?.name.replace(/&/g, '&amp;')}</text> <text id="1">${a?.name.replace(/&/g, "&amp;")}</text>
<text id="2">${a?.artistName.replace(/&/g, '&amp;')} ${a?.albumName.replace(/&/g, '&amp;')}</text> <text id="2">${a?.artistName.replace(/&/g, "&amp;")} ${a?.albumName.replace(/&/g, "&amp;")}</text>
</binding> </binding>
</visual> </visual>
<actions> <actions>
<action content="${this._utils.getLocale(this._utils.getStoreValue('general.language'), 'term.playpause')}" activationType="protocol" arguments="cider://playpause/"/> <action content="${this._utils.getLocale(this._utils.getStoreValue("general.language"), "term.playpause")}" activationType="protocol" arguments="cider://playpause/"/>
<action content="${this._utils.getLocale(this._utils.getStoreValue('general.language'), 'term.next')}" activationType="protocol" arguments="cider://nextitem/"/> <action content="${this._utils.getLocale(this._utils.getStoreValue("general.language"), "term.next")}" activationType="protocol" arguments="cider://nextitem/"/>
</actions> </actions>
</toast>` </toast>`,
}); });
console.log(this._notification.toastXml); console.log(this._notification.toastXml);
this._notification.on('click', (_: any) => { this._notification.on("click", (_: any) => {
this._utils.getWindow().show() this._utils.getWindow().show();
this._utils.getWindow().focus() this._utils.getWindow().focus();
}) });
this._notification.on('close', (_: any) => { this._notification.on("close", (_: any) => {
this._notification = undefined; this._notification = undefined;
}) });
this._notification.on('action', (event: any, action: any) => { this._notification.on("action", (event: any, action: any) => {
this._utils.playback.next() this._utils.playback.next();
}) });
this._notification.show(); this._notification.show();
} }
/******************************************************************************************* /*******************************************************************************************
* Public Methods * Public Methods
* ****************************************************************************************/ * ****************************************************************************************/
@ -90,8 +86,8 @@ export default class playbackNotifications {
this._utils = utils; this._utils = utils;
console.debug(`[Plugin][${this.name}] Loading Complete.`); console.debug(`[Plugin][${this.name}] Loading Complete.`);
utils.getIPCMain().on('playbackNotifications:create', (event: any, a: any) => { utils.getIPCMain().on("playbackNotifications:create", (event: any, a: any) => {
a.artwork.url = a.artwork.url.replace('/{w}x{h}bb', '/512x512bb').replace('/2000x2000bb', '/35x35bb'); a.artwork.url = a.artwork.url.replace("/{w}x{h}bb", "/512x512bb").replace("/2000x2000bb", "/35x35bb");
if (this._artworkNums.length > 20) { if (this._artworkNums.length > 20) {
delete this._artworkImage[this._artworkNums[0]]; delete this._artworkImage[this._artworkNums[0]];
@ -102,23 +98,21 @@ export default class playbackNotifications {
this.createNotification(a); this.createNotification(a);
} else { } else {
if (process.platform === "win32") { if (process.platform === "win32") {
fetch(a.artwork.url) fetch(a.artwork.url).then((res) => {
.then(res => { console.log(join(app.getPath("temp"), `${a.songId}-${a.artwork.url.split("/").pop()}`));
console.log(join(app.getPath("temp"), `${a.songId}-${a.artwork.url.split('/').pop()}`)); const dest = createWriteStream(join(app.getPath("temp"), `${a.songId}-${a.artwork.url.split("/").pop()}`));
const dest = createWriteStream(join(app.getPath("temp"), `${a.songId}-${a.artwork.url.split('/').pop()}`));
// @ts-ignore // @ts-ignore
res.body.pipe(dest) res.body.pipe(dest);
this.createNotification(a); this.createNotification(a);
}) });
} else { } else {
fetch(a.artwork.url).then(async blob => { fetch(a.artwork.url).then(async (blob) => {
this._artworkImage[a.artwork.url] = nativeImage.createFromBuffer(Buffer.from(await blob.arrayBuffer())); this._artworkImage[a.artwork.url] = nativeImage.createFromBuffer(Buffer.from(await blob.arrayBuffer()));
this._artworkNums[this._artworkNums.length] = a.artwork.url; this._artworkNums[this._artworkNums.length] = a.artwork.url;
this.createNotification(a); this.createNotification(a);
}); });
} }
} }
}) });
} }
} }

View file

@ -1,17 +1,15 @@
import * as electron from 'electron'; import * as electron from "electron";
import * as os from 'os'; import * as os from "os";
import * as fs from 'fs'; import * as fs from "fs";
import { join, resolve } from 'path'; import { join, resolve } from "path";
import * as CiderReceiver from '../base/castreceiver'; import * as CiderReceiver from "../base/castreceiver";
import fetch from 'electron-fetch'; import fetch from "electron-fetch";
import { Stream } from "stream"; import { Stream } from "stream";
import {spawn} from 'child_process'; import { spawn } from "child_process";
import {Worker} from 'worker_threads'; import { Worker } from "worker_threads";
import { Blob } from 'buffer'; import { Blob } from "buffer";
export default class RAOP { export default class RAOP {
/** /**
* Private variables for interaction in plugins * Private variables for interaction in plugins
*/ */
@ -26,7 +24,7 @@ export default class RAOP {
private airtunes: any; private airtunes: any;
private device: any; private device: any;
private mdns = require('mdns-js'); private mdns = require("mdns-js");
private ok: any = 1; private ok: any = 1;
private devices: any = []; private devices: any = [];
private castDevices: any = []; private castDevices: any = [];
@ -35,7 +33,6 @@ export default class RAOP {
private ffmpeg: any = null; private ffmpeg: any = null;
private worker: any = null; private worker: any = null;
private processNode = ` private processNode = `
import {parentPort, workerData} from "worker_threads"; import {parentPort, workerData} from "worker_threads";
function getAudioConv (buffers) { function getAudioConv (buffers) {
@ -90,14 +87,18 @@ export default class RAOP {
private ondeviceup(name: any, host: any, port: any, addresses: any, text: any, airplay2: any = null) { private ondeviceup(name: any, host: any, port: any, addresses: any, text: any, airplay2: any = null) {
// console.log(this.castDevices.findIndex((item: any) => {return (item.name == host.replace(".local","") && item.port == port )})) // console.log(this.castDevices.findIndex((item: any) => {return (item.name == host.replace(".local","") && item.port == port )}))
if (this.castDevices.findIndex((item: any) => {return (item != null && item.name == (host ?? "Unknown").replace(".local","") && item.port == port )}) == -1) { if (
this.castDevices.findIndex((item: any) => {
return item != null && item.name == (host ?? "Unknown").replace(".local", "") && item.port == port;
}) == -1
) {
this.castDevices.push({ this.castDevices.push({
name: (host ?? "Unknown").replace(".local", ""), name: (host ?? "Unknown").replace(".local", ""),
host: addresses ? addresses[0] : '', host: addresses ? addresses[0] : "",
port: port, port: port,
addresses: addresses, addresses: addresses,
txt: text, txt: text,
airplay2: airplay2 airplay2: airplay2,
}); });
if (this.devices.indexOf(host) === -1) { if (this.devices.indexOf(host) === -1) {
this.devices.push(host); this.devices.push(host);
@ -115,41 +116,40 @@ export default class RAOP {
/** /**
* Base Plugin Details (Eventually implemented into a GUI in settings) * Base Plugin Details (Eventually implemented into a GUI in settings)
*/ */
public name: string = 'RAOP'; public name: string = "RAOP";
public description: string = 'RAOP Plugin'; public description: string = "RAOP Plugin";
public version: string = '0.0.1'; public version: string = "0.0.1";
public author: string = 'vapormusic / Cider Collective'; public author: string = "vapormusic / Cider Collective";
/** /**
* Runs on plugin load (Currently run on application start) * Runs on plugin load (Currently run on application start)
*/ */
constructor(utils: { getStore: () => any; getApp: () => any; }) { constructor(utils: { getStore: () => any; getApp: () => any }) {
this._utils = utils; this._utils = utils;
console.debug(`[Plugin][${this.name}] Loading Complete.`); console.debug(`[Plugin][${this.name}] Loading Complete.`);
this._app = utils.getApp(); this._app = utils.getApp();
} }
/** /**
* Runs on app ready * Runs on app ready
*/ */
onReady(win: any): void { onReady(win: any): void {
this.u = require('airtunes2'); this.u = require("airtunes2");
this._win = win; this._win = win;
electron.ipcMain.on('getKnownAirplayDevices', (event) => { electron.ipcMain.on("getKnownAirplayDevices", (event) => {
event.returnValue = this.castDevices event.returnValue = this.castDevices;
}); });
electron.ipcMain.on("getAirplayDevice", (event, data) => { electron.ipcMain.on("getAirplayDevice", (event, data) => {
this.castDevices = []; this.castDevices = [];
console.log("scan for airplay devices"); console.log("scan for airplay devices");
const browser = this.mdns.createBrowser(this.mdns.tcp('raop')); const browser = this.mdns.createBrowser(this.mdns.tcp("raop"));
browser.on('ready', browser.discover); browser.on("ready", browser.discover);
browser.on('update', (service: any) => { browser.on("update", (service: any) => {
if (service.addresses && service.fullname && (service.fullname.includes('_raop._tcp'))) { if (service.addresses && service.fullname && service.fullname.includes("_raop._tcp")) {
// console.log(service.txt) // console.log(service.txt)
this._win.webContents.executeJavaScript(`console.log( this._win.webContents.executeJavaScript(`console.log(
"${service.name} ${service.host}:${service.port} ${service.addresses}" "${service.name} ${service.host}:${service.port} ${service.addresses}"
@ -158,11 +158,11 @@ export default class RAOP {
} }
}); });
const browser2 = this.mdns.createBrowser(this.mdns.tcp('airplay')); const browser2 = this.mdns.createBrowser(this.mdns.tcp("airplay"));
browser2.on('ready', browser2.discover); browser2.on("ready", browser2.discover);
browser2.on('update', (service: any) => { browser2.on("update", (service: any) => {
if (service.addresses && service.fullname && (service.fullname.includes('_airplay._tcp'))) { if (service.addresses && service.fullname && service.fullname.includes("_airplay._tcp")) {
// console.log(service.txt) // console.log(service.txt)
this._win.webContents.executeJavaScript(`console.log( this._win.webContents.executeJavaScript(`console.log(
"${service.name} ${service.host}:${service.port} ${service.addresses}" "${service.name} ${service.host}:${service.port} ${service.addresses}"
@ -183,15 +183,13 @@ export default class RAOP {
// this.ondeviceup(service.name, service.host, service.port, service.addresses, service.txt); // this.ondeviceup(service.name, service.host, service.port, service.addresses, service.txt);
// } // }
// }); // });
}); });
electron.ipcMain.on("performAirplayPCM", (event, ipv4, ipport, sepassword, title, artist, album, artworkURL, txt, airplay2dv) => { electron.ipcMain.on("performAirplayPCM", (event, ipv4, ipport, sepassword, title, artist, album, artworkURL, txt, airplay2dv) => {
if (ipv4 != this.ipairplay || ipport != this.portairplay) { if (ipv4 != this.ipairplay || ipport != this.portairplay) {
if (this.airtunes == null) { this.airtunes = new this.u()} if (this.airtunes == null) {
this.airtunes = new this.u();
}
this.ipairplay = ipv4; this.ipairplay = ipv4;
this.portairplay = ipport; this.portairplay = ipport;
this.device = this.airtunes.add(ipv4, { this.device = this.airtunes.add(ipv4, {
@ -200,63 +198,55 @@ export default class RAOP {
password: sepassword, password: sepassword,
txt: txt, txt: txt,
airplay2: airplay2dv, airplay2: airplay2dv,
debug: true debug: true,
}); });
// console.log('lol',txt) // console.log('lol',txt)
this.device.on('status', (status: any) => { this.device.on("status", (status: any) => {
console.log('device status', status); console.log("device status", status);
if (status == "ready") { if (status == "ready") {
this._win.webContents.setAudioMuted(true); this._win.webContents.setAudioMuted(true);
this._win.webContents.executeJavaScript(`CiderAudio.sendAudio()`).catch((err: any) => console.error(err)); this._win.webContents.executeJavaScript(`CiderAudio.sendAudio()`).catch((err: any) => console.error(err));
} }
if (status == "need_password") { if (status == "need_password") {
this._win.webContents.executeJavaScript(`app.setAirPlayCodeUI()`) this._win.webContents.executeJavaScript(`app.setAirPlayCodeUI()`);
} }
if (status == "pair_success") { if (status == "pair_success") {
this._win.webContents.executeJavaScript(`app.sendAirPlaySuccess()`) this._win.webContents.executeJavaScript(`app.sendAirPlaySuccess()`);
} }
if (status == "pair_failed") { if (status == "pair_failed") {
this._win.webContents.executeJavaScript(`app.sendAirPlayFailed()`) this._win.webContents.executeJavaScript(`app.sendAirPlayFailed()`);
} }
if (status == 'stopped') { if (status == "stopped") {
this.airtunes.stopAll(() => { this.airtunes.stopAll(() => {
console.log('end'); console.log("end");
}); });
this.airtunes = null; this.airtunes = null;
this.device = null; this.device = null;
this.ipairplay = ''; this.ipairplay = "";
this.portairplay = ''; this.portairplay = "";
this.ok = 1; this.ok = 1;
} else { } else {
setTimeout(() => { setTimeout(() => {
if (this.ok == 1) { if (this.ok == 1) {
console.log(this.device.key, title ?? '', artist ?? '', album ?? ''); console.log(this.device.key, title ?? "", artist ?? "", album ?? "");
this.airtunes.setTrackInfo(this.device.key, title ?? '', artist?? '', album?? ''); this.airtunes.setTrackInfo(this.device.key, title ?? "", artist ?? "", album ?? "");
this.uploadImageAirplay(artworkURL); this.uploadImageAirplay(artworkURL);
console.log('done'); console.log("done");
this.ok == 2 this.ok == 2;
} }
}, 1000); }, 1000);
} }
}); });
} }
}); });
electron.ipcMain.on('setAirPlayPasscode', (event, passcode) => { electron.ipcMain.on("setAirPlayPasscode", (event, passcode) => {
if (this.device) { if (this.device) {
this.device.setPasscode(passcode) this.device.setPasscode(passcode);
} }
}) });
electron.ipcMain.on('writeWAV', (event, leftbuffer, rightbuffer) => { electron.ipcMain.on("writeWAV", (event, leftbuffer, rightbuffer) => {
if (this.airtunes != null) { if (this.airtunes != null) {
if (this.worker == null) { if (this.worker == null) {
try { try {
@ -278,8 +268,9 @@ export default class RAOP {
console.log("bruh", error); console.log("bruh", error);
}); });
this.worker.postMessage({ buffer: [leftbuffer, rightbuffer] }); this.worker.postMessage({ buffer: [leftbuffer, rightbuffer] });
} catch (e){console.log(e)} } catch (e) {
console.log(e);
}
// this.ffmpeg != null ? this.ffmpeg.kill() : null; // this.ffmpeg != null ? this.ffmpeg.kill() : null;
// this.ffmpeg = spawn(this._utils.getStoreValue("advanced.ffmpegLocation"), [ // this.ffmpeg = spawn(this._utils.getStoreValue("advanced.ffmpegLocation"), [
@ -302,61 +293,56 @@ export default class RAOP {
this.worker.postMessage({ buffer: [leftbuffer, rightbuffer] }); this.worker.postMessage({ buffer: [leftbuffer, rightbuffer] });
} }
} }
}); });
electron.ipcMain.on("disconnectAirplay", (event) => {
electron.ipcMain.on('disconnectAirplay', (event) => {
this._win.webContents.setAudioMuted(false); this._win.webContents.setAudioMuted(false);
this.airtunes.stopAll(function () { this.airtunes.stopAll(function () {
console.log('end'); console.log("end");
}); });
this.airtunes = null; this.airtunes = null;
this.device = null; this.device = null;
this.ipairplay = ''; this.ipairplay = "";
this.portairplay = ''; this.portairplay = "";
this.ok = 1; this.ok = 1;
this.i = false; this.i = false;
}); });
electron.ipcMain.on('updateAirplayInfo', (event, title, artist, album, artworkURL) => { electron.ipcMain.on("updateAirplayInfo", (event, title, artist, album, artworkURL) => {
if (this.airtunes && this.device) { if (this.airtunes && this.device) {
console.log(this.device.key, title, artist, album); console.log(this.device.key, title, artist, album);
this.airtunes.setTrackInfo(this.device.key, title, artist, album); this.airtunes.setTrackInfo(this.device.key, title, artist, album);
this.uploadImageAirplay(artworkURL) this.uploadImageAirplay(artworkURL);
} }
}); });
electron.ipcMain.on('updateRPCImage', (_event, imageurl) => { electron.ipcMain.on("updateRPCImage", (_event, imageurl) => {
this.uploadImageAirplay(imageurl) this.uploadImageAirplay(imageurl);
}) });
} }
private uploadImageAirplay = (url: any) => { private uploadImageAirplay = (url: any) => {
try { try {
if (url != null && url != '') { if (url != null && url != "") {
//console.log(join(this._app.getPath('userData'), 'temp.png'), url); //console.log(join(this._app.getPath('userData'), 'temp.png'), url);
fetch(url) fetch(url)
.then(res => res.buffer()) .then((res) => res.buffer())
.then((buffer) => { .then((buffer) => {
this.airtunes.setArtwork(this.device.key, buffer, "image/png"); this.airtunes.setArtwork(this.device.key, buffer, "image/png");
}).catch(err => { })
console.log(err) .catch((err) => {
console.log(err);
}); });
} }
} catch (e) { console.log(e) } } catch (e) {
console.log(e);
} }
};
/** /**
* Runs on app stop * Runs on app stop
*/ */
onBeforeQuit(): void { onBeforeQuit(): void {}
}
// /** // /**
// * Runs on song change // * Runs on song change
@ -381,15 +367,15 @@ export default class RAOP {
*/ */
onPlaybackStateDidChange(attributes: any): void { onPlaybackStateDidChange(attributes: any): void {
if (this.airtunes && this.device) { if (this.airtunes && this.device) {
let title = attributes?.name ?? ''; let title = attributes?.name ?? "";
let artist = attributes?.artistName ?? ''; let artist = attributes?.artistName ?? "";
let album = attributes?.albumName ?? ''; let album = attributes?.albumName ?? "";
let artworkURL = attributes?.artwork?.url ?? null; let artworkURL = attributes?.artwork?.url ?? null;
console.log(this.device.key, title, artist, album); console.log(this.device.key, title, artist, album);
this.airtunes.setTrackInfo(this.device.key, title, artist, album); this.airtunes.setTrackInfo(this.device.key, title, artist, album);
if (artworkURL != null){} if (artworkURL != null) {
this.uploadImageAirplay(artworkURL.replace('{w}', '1024').replace('{h}', '1024')) }
this.uploadImageAirplay(artworkURL.replace("{w}", "1024").replace("{h}", "1024"));
} }
} }
} }

View file

@ -12,20 +12,20 @@ export default class Thumbar {
/** /**
* Base Plugin Details (Eventually implemented into a GUI in settings) * Base Plugin Details (Eventually implemented into a GUI in settings)
*/ */
public name: string = 'Thumbnail Toolbar Plugin'; public name: string = "Thumbnail Toolbar Plugin";
public description: string = 'Creates and managed the thumbnail toolbar buttons and their events'; public description: string = "Creates and managed the thumbnail toolbar buttons and their events";
public version: string = '1.0.0'; public version: string = "1.0.0";
public author: string = 'Core'; public author: string = "Core";
/** /**
* Thumbnail Toolbar Assets * Thumbnail Toolbar Assets
*/ */
private icons: { [key: string]: Electron.NativeImage } = { private icons: { [key: string]: Electron.NativeImage } = {
pause: nativeImage.createFromPath(join(utils.getPath('resourcePath'), 'icons/thumbar', `${nativeTheme.shouldUseDarkColors ? 'light' : 'dark'}_pause.png`)), pause: nativeImage.createFromPath(join(utils.getPath("resourcePath"), "icons/thumbar", `${nativeTheme.shouldUseDarkColors ? "light" : "dark"}_pause.png`)),
play: nativeImage.createFromPath(join(utils.getPath('resourcePath'), 'icons/thumbar', `${nativeTheme.shouldUseDarkColors ? 'light' : 'dark'}_play.png`)), play: nativeImage.createFromPath(join(utils.getPath("resourcePath"), "icons/thumbar", `${nativeTheme.shouldUseDarkColors ? "light" : "dark"}_play.png`)),
next: nativeImage.createFromPath(join(utils.getPath('resourcePath'), 'icons/thumbar', `${nativeTheme.shouldUseDarkColors ? 'light' : 'dark'}_next.png`)), next: nativeImage.createFromPath(join(utils.getPath("resourcePath"), "icons/thumbar", `${nativeTheme.shouldUseDarkColors ? "light" : "dark"}_next.png`)),
previous: nativeImage.createFromPath(join(utils.getPath('resourcePath'), 'icons/thumbar', `${nativeTheme.shouldUseDarkColors ? 'light' : 'dark'}_previous.png`)), previous: nativeImage.createFromPath(join(utils.getPath("resourcePath"), "icons/thumbar", `${nativeTheme.shouldUseDarkColors ? "light" : "dark"}_previous.png`)),
} };
/******************************************************************************************* /*******************************************************************************************
* Private Methods * Private Methods
@ -37,10 +37,10 @@ export default class Thumbar {
* @decorator * @decorator
*/ */
private static windowsOnly(_target: any, _propertyKey: string, descriptor: PropertyDescriptor) { private static windowsOnly(_target: any, _propertyKey: string, descriptor: PropertyDescriptor) {
if (process.platform !== 'win32') { if (process.platform !== "win32") {
descriptor.value = function () { descriptor.value = function () {
return return;
} };
} }
} }
@ -48,39 +48,38 @@ export default class Thumbar {
* Update the thumbnail toolbar * Update the thumbnail toolbar
*/ */
private updateButtons(attributes: any) { private updateButtons(attributes: any) {
console.log(attributes);
console.log(attributes)
if (!attributes) { if (!attributes) {
return return;
} }
const buttons = [ const buttons = [
{ {
tooltip: 'Previous', tooltip: "Previous",
icon: this.icons.previous, icon: this.icons.previous,
click() { click() {
utils.playback.previous() utils.playback.previous();
} },
}, },
{ {
tooltip: attributes.status ? 'Pause' : 'Play', tooltip: attributes.status ? "Pause" : "Play",
icon: attributes.status ? this.icons.pause : this.icons.play, icon: attributes.status ? this.icons.pause : this.icons.play,
click() { click() {
utils.playback.playPause() utils.playback.playPause();
} },
}, },
{ {
tooltip: 'Next', tooltip: "Next",
icon: this.icons.next, icon: this.icons.next,
click() { click() {
utils.playback.next() utils.playback.next();
} },
} },
]; ];
if (!attributes.playParams || attributes.playParams.id === 'no-id-found') { if (!attributes.playParams || attributes.playParams.id === "no-id-found") {
this._win.setThumbarButtons([]) this._win.setThumbarButtons([]);
} else { } else {
this._win.setThumbarButtons(buttons); this._win.setThumbarButtons(buttons);
} }
@ -93,7 +92,7 @@ export default class Thumbar {
/** /**
* Runs on plugin load (Currently run on application start) * Runs on plugin load (Currently run on application start)
*/ */
constructor(a: { getApp: () => any; }) { constructor(a: { getApp: () => any }) {
this._app = utils.getApp(); this._app = utils.getApp();
console.debug(`[Plugin][${this.name}] Loading Complete.`); console.debug(`[Plugin][${this.name}] Loading Complete.`);
} }
@ -121,7 +120,7 @@ export default class Thumbar {
*/ */
@Thumbar.windowsOnly @Thumbar.windowsOnly
onPlaybackStateDidChange(attributes: object): void { onPlaybackStateDidChange(attributes: object): void {
this.updateButtons(attributes) this.updateButtons(attributes);
} }
/** /**
@ -130,7 +129,6 @@ export default class Thumbar {
*/ */
@Thumbar.windowsOnly @Thumbar.windowsOnly
onNowPlayingItemDidChange(attributes: object): void { onNowPlayingItemDidChange(attributes: object): void {
this.updateButtons(attributes) this.updateButtons(attributes);
} }
} }

View file

@ -1,4 +1,4 @@
import * as WebSocket from 'ws'; import * as WebSocket from "ws";
/** /**
* 0-pad a number. * 0-pad a number.
@ -6,7 +6,7 @@ import * as WebSocket from 'ws';
* @param {Number} length * @param {Number} length
* @returns String * @returns String
*/ */
const pad = (number: number, length: number) => String(number).padStart(length, '0'); const pad = (number: number, length: number) => String(number).padStart(length, "0");
/** /**
* Convert seconds to a time string acceptable to Rainmeter * Convert seconds to a time string acceptable to Rainmeter
@ -20,21 +20,21 @@ const convertTimeToString = (timeInSeconds: number) => {
return timeInMinutes + ":" + pad(Math.floor(timeInSeconds % 60), 2); return timeInMinutes + ":" + pad(Math.floor(timeInSeconds % 60), 2);
} }
return Math.floor(timeInMinutes / 60) + ":" + pad(Math.floor(timeInMinutes % 60), 2) + ":" + pad(Math.floor(timeInSeconds % 60), 2); return Math.floor(timeInMinutes / 60) + ":" + pad(Math.floor(timeInMinutes % 60), 2) + ":" + pad(Math.floor(timeInSeconds % 60), 2);
} };
export default class WebNowPlaying { export default class WebNowPlaying {
/** /**
* Base Plugin Details (Eventually implemented into a GUI in settings) * Base Plugin Details (Eventually implemented into a GUI in settings)
*/ */
public name: string = 'WebNowPlaying'; public name: string = "WebNowPlaying";
public description: string = 'Song info and playback control for the Rainmeter WebNowPlaying plugin.'; public description: string = "Song info and playback control for the Rainmeter WebNowPlaying plugin.";
public version: string = '1.0.1'; public version: string = "1.0.1";
public author: string = 'Zennn <me@jozen.blue>'; public author: string = "Zennn <me@jozen.blue>";
private _win: any; private _win: any;
private ws?: WebSocket; private ws?: WebSocket;
private wsapiConn?: WebSocket; private wsapiConn?: WebSocket;
private playerName: string = 'Cider'; private playerName: string = "Cider";
constructor() { constructor() {
console.debug(`[Plugin][${this.name}] Loading Complete.`); console.debug(`[Plugin][${this.name}] Loading Complete.`);
@ -46,7 +46,7 @@ export default class WebNowPlaying {
* @decorator * @decorator
*/ */
private static windowsOnly(_target: any, _propertyKey: string, descriptor: PropertyDescriptor) { private static windowsOnly(_target: any, _propertyKey: string, descriptor: PropertyDescriptor) {
if (process.platform !== 'win32') { if (process.platform !== "win32") {
descriptor.value = () => void 0; descriptor.value = () => void 0;
} }
} }
@ -54,39 +54,39 @@ export default class WebNowPlaying {
private sendSongInfo(attributes: any) { private sendSongInfo(attributes: any) {
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return; if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
const fields = ['STATE', 'TITLE', 'ARTIST', 'ALBUM', 'COVER', 'DURATION', 'POSITION', 'VOLUME', 'REPEAT', 'SHUFFLE']; const fields = ["STATE", "TITLE", "ARTIST", "ALBUM", "COVER", "DURATION", "POSITION", "VOLUME", "REPEAT", "SHUFFLE"];
fields.forEach((field) => { fields.forEach((field) => {
try { try {
let value: any = ''; let value: any = "";
switch (field) { switch (field) {
case 'STATE': case "STATE":
value = attributes.status ? 1 : 2; value = attributes.status ? 1 : 2;
break; break;
case 'TITLE': case "TITLE":
value = attributes.name; value = attributes.name;
break; break;
case 'ARTIST': case "ARTIST":
value = attributes.artistName; value = attributes.artistName;
break; break;
case 'ALBUM': case "ALBUM":
value = attributes.albumName; value = attributes.albumName;
break; break;
case 'COVER': case "COVER":
value = attributes.artwork.url.replace('{w}', attributes.artwork.width).replace('{h}', attributes.artwork.height); value = attributes.artwork.url.replace("{w}", attributes.artwork.width).replace("{h}", attributes.artwork.height);
break; break;
case 'DURATION': case "DURATION":
value = convertTimeToString(attributes.durationInMillis / 1000); value = convertTimeToString(attributes.durationInMillis / 1000);
break; break;
case 'POSITION': case "POSITION":
value = convertTimeToString((attributes.durationInMillis - attributes.remainingTime) / 1000); value = convertTimeToString((attributes.durationInMillis - attributes.remainingTime) / 1000);
break; break;
case 'VOLUME': case "VOLUME":
value = attributes.volume * 100; value = attributes.volume * 100;
break; break;
case 'REPEAT': case "REPEAT":
value = attributes.repeatMode; value = attributes.repeatMode;
break; break;
case 'SHUFFLE': case "SHUFFLE":
value = attributes.shuffleMode; value = attributes.shuffleMode;
break; break;
} }
@ -104,42 +104,42 @@ export default class WebNowPlaying {
if (!evt.data) return; if (!evt.data) return;
const data = <string>evt.data; const data = <string>evt.data;
let value: string = ''; let value: string = "";
if (data.split(/ (.+)/).length > 1) { if (data.split(/ (.+)/).length > 1) {
value = data.split(/ (.+)/)[1]; value = data.split(/ (.+)/)[1];
} }
const eventName = data.split(' ')[0].toLowerCase(); const eventName = data.split(" ")[0].toLowerCase();
try { try {
switch (eventName) { switch (eventName) {
case 'playpause': case "playpause":
this._win.webContents.executeJavaScript('MusicKitInterop.playPause()').catch(console.error); this._win.webContents.executeJavaScript("MusicKitInterop.playPause()").catch(console.error);
break; break;
case 'next': case "next":
this._win.webContents.executeJavaScript('MusicKitInterop.next()').catch(console.error); this._win.webContents.executeJavaScript("MusicKitInterop.next()").catch(console.error);
break; break;
case 'previous': case "previous":
this._win.webContents.executeJavaScript('MusicKitInterop.previous()').catch(console.error); this._win.webContents.executeJavaScript("MusicKitInterop.previous()").catch(console.error);
break; break;
case 'setposition': case "setposition":
this._win.webContents.executeJavaScript(`MusicKit.getInstance().seekToTime(${parseFloat(value)})`); this._win.webContents.executeJavaScript(`MusicKit.getInstance().seekToTime(${parseFloat(value)})`);
break; break;
case 'setvolume': case "setvolume":
this._win.webContents.executeJavaScript(`MusicKit.getInstance().volume = ${parseFloat(value) / 100}`); this._win.webContents.executeJavaScript(`MusicKit.getInstance().volume = ${parseFloat(value) / 100}`);
break; break;
case 'repeat': case "repeat":
this._win.webContents.executeJavaScript('wsapi.toggleRepeat()').catch(console.error); this._win.webContents.executeJavaScript("wsapi.toggleRepeat()").catch(console.error);
break; break;
case 'shuffle': case "shuffle":
this._win.webContents.executeJavaScript('wsapi.toggleShuffle()').catch(console.error); this._win.webContents.executeJavaScript("wsapi.toggleShuffle()").catch(console.error);
break; break;
case 'togglethumbsup': case "togglethumbsup":
// not implemented // not implemented
break; break;
case 'togglethumbsdown': case "togglethumbsdown":
// not implemented // not implemented
break; break;
case 'rating': case "rating":
// not implemented // not implemented
break; break;
} }
@ -162,10 +162,10 @@ export default class WebNowPlaying {
// Connect to Rainmeter plugin and retry on disconnect. // Connect to Rainmeter plugin and retry on disconnect.
const init = () => { const init = () => {
try { try {
this.ws = new WebSocket('ws://127.0.0.1:8974/'); this.ws = new WebSocket("ws://127.0.0.1:8974/");
let retry: NodeJS.Timeout; let retry: NodeJS.Timeout;
this.ws.onopen = () => { this.ws.onopen = () => {
console.info('[WebNowPlaying] Connected to Rainmeter'); console.info("[WebNowPlaying] Connected to Rainmeter");
this.ws?.send(`PLAYER:${this.playerName}`); this.ws?.send(`PLAYER:${this.playerName}`);
}; };
@ -189,15 +189,15 @@ export default class WebNowPlaying {
// Connect to wsapi. Only used to update progress. // Connect to wsapi. Only used to update progress.
try { try {
this.wsapiConn = new WebSocket('ws://127.0.0.1:26369/'); this.wsapiConn = new WebSocket("ws://127.0.0.1:26369/");
this.wsapiConn.onopen = () => { this.wsapiConn.onopen = () => {
console.info('[WebNowPlaying] Connected to wsapi'); console.info("[WebNowPlaying] Connected to wsapi");
}; };
this.wsapiConn.onmessage = (evt: WebSocket.MessageEvent) => { this.wsapiConn.onmessage = (evt: WebSocket.MessageEvent) => {
const response = JSON.parse(<string>evt.data); const response = JSON.parse(<string>evt.data);
if (response.type === 'playbackStateUpdate') { if (response.type === "playbackStateUpdate") {
this.sendSongInfo(response.data); this.sendSongInfo(response.data);
} }
}; };
@ -214,7 +214,7 @@ export default class WebNowPlaying {
@WebNowPlaying.windowsOnly @WebNowPlaying.windowsOnly
public onBeforeQuit() { public onBeforeQuit() {
if (this.ws) { if (this.ws) {
this.ws.send('STATE:0'); this.ws.send("STATE:0");
this.ws.onclose = () => void 0; // disable onclose handler first to stop it from retrying this.ws.onclose = () => void 0; // disable onclose handler first to stop it from retrying
this.ws.close(); this.ws.close();
} }

View file

@ -1,12 +1,12 @@
import * as PouchDB from 'pouchdb-node'; import * as PouchDB from "pouchdb-node";
import {join} from 'path'; import { join } from "path";
import { app } from "electron"; import { app } from "electron";
PouchDB.plugin(require('pouchdb-upsert')); PouchDB.plugin(require("pouchdb-upsert"));
export class ProviderDB { export class ProviderDB {
public static db: any = null public static db: any = null;
static init() { static init() {
if (ProviderDB.db == null) { if (ProviderDB.db == null) {
ProviderDB.db = new PouchDB(join(app.getPath('userData'), 'tracksdb')) ProviderDB.db = new PouchDB(join(app.getPath("userData"), "tracksdb"));
} }
} }
} }

View file

@ -1,11 +1,11 @@
import { ProviderDB } from "./db"; import { ProviderDB } from "./db";
import * as path from 'path'; import * as path from "path";
const { readdir } = require('fs').promises; const { readdir } = require("fs").promises;
import { utils } from '../../base/utils'; import { utils } from "../../base/utils";
import * as mm from 'music-metadata'; import * as mm from "music-metadata";
import {Md5} from 'ts-md5/dist/md5'; import { Md5 } from "ts-md5/dist/md5";
import e from "express"; import e from "express";
import { EventEmitter } from 'events'; import { EventEmitter } from "events";
export class LocalFiles { export class LocalFiles {
static localSongs: any = []; static localSongs: any = [];
@ -14,37 +14,40 @@ export class LocalFiles {
static eventEmitter = new EventEmitter(); static eventEmitter = new EventEmitter();
static getDataType(item_id: String | any) { static getDataType(item_id: String | any) {
if ((item_id ?? ('')).startsWith('ciderlocalart')) if ((item_id ?? "").startsWith("ciderlocalart")) return "artwork";
return 'artwork' else if ((item_id ?? "").startsWith("ciderlocal")) return "track";
else if ((item_id ?? ('')).startsWith('ciderlocal'))
return 'track'
} }
static async sendOldLibrary() { static async sendOldLibrary() {
ProviderDB.init() ProviderDB.init();
let rows = (await ProviderDB.db.allDocs({include_docs: true, let rows = (await ProviderDB.db.allDocs({ include_docs: true, attachments: true })).rows.map((item: any) => {
attachments: true})).rows.map((item: any)=>{return item.doc}) return item.doc;
let tracks = rows.filter((item: any) => {return this.getDataType(item._id) == "track"}) });
let arts = rows.filter((item: any) => {return this.getDataType(item._id) == "artwork"}) let tracks = rows.filter((item: any) => {
return this.getDataType(item._id) == "track";
});
let arts = rows.filter((item: any) => {
return this.getDataType(item._id) == "artwork";
});
this.localSongs = tracks; this.localSongs = tracks;
this.localSongsArts = arts; this.localSongsArts = arts;
return tracks; return tracks;
} }
static async scanLibrary() { static async scanLibrary() {
ProviderDB.init() ProviderDB.init();
let folders = utils.getStoreValue("libraryPrefs.localPaths") let folders = utils.getStoreValue("libraryPrefs.localPaths");
if (folders == null || folders.length == null || folders.length == 0) folders = [] if (folders == null || folders.length == null || folders.length == 0) folders = [];
let files: any[] = [] let files: any[] = [];
for (var folder of folders) { for (var folder of folders) {
// get files from the Music folder // get files from the Music folder
files = files.concat(await LocalFiles.getFiles(folder)) files = files.concat(await LocalFiles.getFiles(folder));
} }
let supporttedformats = ["mp3", "aac", "webm", "flac", "m4a", "ogg", "wav", "opus"] let supporttedformats = ["mp3", "aac", "webm", "flac", "m4a", "ogg", "wav", "opus"];
let audiofiles = files.filter(f => supporttedformats.includes(f.substring(f.lastIndexOf('.') + 1))); let audiofiles = files.filter((f) => supporttedformats.includes(f.substring(f.lastIndexOf(".") + 1)));
let metadatalist = [] let metadatalist = [];
let metadatalistart = [] let metadatalistart = [];
let numid = 0; let numid = 0;
for (var audio of audiofiles) { for (var audio of audiofiles) {
try { try {
@ -52,46 +55,44 @@ export class LocalFiles {
let lochash = Md5.hashStr(audio) ?? numid; let lochash = Md5.hashStr(audio) ?? numid;
if (metadata != null) { if (metadata != null) {
let form = { let form = {
"id": "ciderlocal" + lochash, id: "ciderlocal" + lochash,
"_id": "ciderlocal" + lochash, _id: "ciderlocal" + lochash,
"type": "podcast-episodes", type: "podcast-episodes",
"href": audio, href: audio,
"attributes": { attributes: {
"artwork": { artwork: {
"width": 3000, width: 3000,
"height": 3000, height: 3000,
"url": "/ciderlocalart/" + "ciderlocal" + lochash, url: "/ciderlocalart/" + "ciderlocal" + lochash,
}, },
"topics": [], topics: [],
"url": "", url: "",
"subscribable": true, subscribable: true,
"mediaKind": "audio", mediaKind: "audio",
"genreNames": [ genreNames: [""],
""
],
// "playParams": { // "playParams": {
// "id": "ciderlocal" + numid, // "id": "ciderlocal" + numid,
// "kind": "podcast", // "kind": "podcast",
// "isLibrary": true, // "isLibrary": true,
// "reporting": false }, // "reporting": false },
"trackNumber": metadata.common.track?.no ?? 0, trackNumber: metadata.common.track?.no ?? 0,
"discNumber": metadata.common.disk?.no ?? 0, discNumber: metadata.common.disk?.no ?? 0,
"name": metadata.common.title ?? audio.substring(audio.lastIndexOf('\\') + 1), name: metadata.common.title ?? audio.substring(audio.lastIndexOf("\\") + 1),
"albumName": metadata.common.album, albumName: metadata.common.album,
"artistName": metadata.common.artist, artistName: metadata.common.artist,
"copyright": metadata.common.copyright ?? "", copyright: metadata.common.copyright ?? "",
"assetUrl": "file:///" + audio, assetUrl: "file:///" + audio,
"contentAdvisory": "", contentAdvisory: "",
"releaseDateTime": `${metadata?.common?.year ?? '2022'}-05-13T00:23:00Z`, releaseDateTime: `${metadata?.common?.year ?? "2022"}-05-13T00:23:00Z`,
"durationInMillis": Math.floor((metadata.format.duration ?? 0) * 1000), durationInMillis: Math.floor((metadata.format.duration ?? 0) * 1000),
"bitrate": Math.floor((metadata.format?.bitrate ?? 0) / 1000), bitrate: Math.floor((metadata.format?.bitrate ?? 0) / 1000),
"offers": [ offers: [
{ {
"kind": "get", kind: "get",
"type": "STDQ" type: "STDQ",
} },
], ],
"contentRating": "clean" contentRating: "clean",
}, },
flavor: Math.floor((metadata.format?.bitrate ?? 0) / 1000), flavor: Math.floor((metadata.format?.bitrate ?? 0) / 1000),
localFilesMetadata: { localFilesMetadata: {
@ -104,18 +105,22 @@ export class LocalFiles {
let art = { let art = {
id: "ciderlocal" + lochash, id: "ciderlocal" + lochash,
_id: "ciderlocalart" + lochash, _id: "ciderlocalart" + lochash,
url: metadata.common.picture != undefined ? metadata.common.picture[0].data.toString('base64') : "", url: metadata.common.picture != undefined ? metadata.common.picture[0].data.toString("base64") : "",
} };
metadatalistart.push(art) metadatalistart.push(art);
numid += 1; numid += 1;
ProviderDB.db.putIfNotExists(form) ProviderDB.db.putIfNotExists(form);
ProviderDB.db.putIfNotExists(art) ProviderDB.db.putIfNotExists(art);
metadatalist.push(form) metadatalist.push(form);
if (this.localSongs.length === 0 && numid % 10 === 0) { // send updated chunks only if there is no previous database if (this.localSongs.length === 0 && numid % 10 === 0) {
this.eventEmitter.emit('newtracks', metadatalist)} // send updated chunks only if there is no previous database
this.eventEmitter.emit("newtracks", metadatalist);
}
}
} catch (e) {
console.error("localfiles error:", e);
} }
} catch (e) {console.error("localfiles error:", e)}
} }
// console.log('metadatalist', metadatalist); // console.log('metadatalist', metadatalist);
this.localSongs = metadatalist; this.localSongs = metadatalist;
@ -124,40 +129,53 @@ export class LocalFiles {
} }
static async getFiles(dir: any) { static async getFiles(dir: any) {
const dirents = await readdir(dir, { withFileTypes: true }); const dirents = await readdir(dir, { withFileTypes: true });
const files = await Promise.all(dirents.map((dirent: any) => { const files = await Promise.all(
dirents.map((dirent: any) => {
const res = path.resolve(dir, dirent.name); const res = path.resolve(dir, dirent.name);
return dirent.isDirectory() ? this.getFiles(res) : res; return dirent.isDirectory() ? this.getFiles(res) : res;
})); })
);
return Array.prototype.concat(...files); return Array.prototype.concat(...files);
} }
static async cleanUpDB() { static async cleanUpDB() {
let folders = utils.getStoreValue("libraryPrefs.localPaths") let folders = utils.getStoreValue("libraryPrefs.localPaths");
let rows = (await ProviderDB.db.allDocs({include_docs: true, let rows = (await ProviderDB.db.allDocs({ include_docs: true, attachments: true })).rows.map((item: any) => {
attachments: true})).rows.map((item: any)=>{return item.doc}) return item.doc;
let tracks = rows.filter((item: any) => {return this.getDataType(item._id) == "track" && !folders.some((i: String) => {return item["attributes"]["assetUrl"].startsWith("file:///" + i)})}) });
let hashs = tracks.map((i: any) => {return i._id}) let tracks = rows.filter((item: any) => {
return (
this.getDataType(item._id) == "track" &&
!folders.some((i: String) => {
return item["attributes"]["assetUrl"].startsWith("file:///" + i);
})
);
});
let hashs = tracks.map((i: any) => {
return i._id;
});
for (let hash of hashs) { for (let hash of hashs) {
try { try {
ProviderDB.db.get(hash).then(function (doc: any) { ProviderDB.db.get(hash).then(function (doc: any) {
return ProviderDB.db.remove(doc); return ProviderDB.db.remove(doc);
});} catch(e){} });
} catch (e) {}
try { try {
ProviderDB.db.get(hash.replace('ciderlocal','ciderlocalart')).then(function (doc: any) { ProviderDB.db.get(hash.replace("ciderlocal", "ciderlocalart")).then(function (doc: any) {
return ProviderDB.db.remove(doc); return ProviderDB.db.remove(doc);
});} catch(e){} });
} catch (e) {}
} }
} }
static setupHandlers() { static setupHandlers() {
const app = utils.getExpress() const app = utils.getExpress();
console.log("Setting up handlers for local files") console.log("Setting up handlers for local files");
app.get("/ciderlocal/:songs", (req: any, res: any) => { app.get("/ciderlocal/:songs", (req: any, res: any) => {
const audio = atob(req.params.songs.replace(/_/g, '/').replace(/-/g, '+')); const audio = atob(req.params.songs.replace(/_/g, "/").replace(/-/g, "+"));
//console.log('auss', audio) //console.log('auss', audio)
let data = { let data = {
data: data: LocalFiles.localSongs.filter((f: any) => audio.split(",").includes(f.id)),
LocalFiles.localSongs.filter((f: any) => audio.split(',').includes(f.id))
}; };
res.send(data); res.send(data);
}); });
@ -166,15 +184,14 @@ export class LocalFiles {
const audio = req.params.songs; const audio = req.params.songs;
// metadata.common.picture[0].data.toString('base64') // metadata.common.picture[0].data.toString('base64')
res.setHeader('Cache-Control', 'public, max-age=31536000'); res.setHeader("Cache-Control", "public, max-age=31536000");
res.setHeader('Expires', new Date(Date.now() + 31536000000).toUTCString()); res.setHeader("Expires", new Date(Date.now() + 31536000000).toUTCString());
res.setHeader('Content-Type', 'image/jpeg'); res.setHeader("Content-Type", "image/jpeg");
let data = let data = LocalFiles.localSongsArts.filter((f: any) => f.id == audio);
LocalFiles.localSongsArts.filter((f: any) => f.id == audio); res.status(200).send(Buffer.from(data[0]?.url, "base64"));
res.status(200).send(Buffer.from(data[0]?.url, 'base64'));
}); });
return app return app;
} }
} }

View file

@ -1,5 +1,5 @@
global.ipcRenderer = require('electron').ipcRenderer; global.ipcRenderer = require("electron").ipcRenderer;
console.info('Loaded Preload') console.info("Loaded Preload");
let cache = { playParams: { id: 0 }, status: null, remainingTime: 0 }, let cache = { playParams: { id: 0 }, status: null, remainingTime: 0 },
playbackCache = { status: null, time: Date.now() }; playbackCache = { status: null, time: Date.now() };
@ -8,44 +8,44 @@ const MusicKitInterop = {
init: function () { init: function () {
/* MusicKit.Events.playbackStateDidChange */ /* MusicKit.Events.playbackStateDidChange */
MusicKit.getInstance().addEventListener(MusicKit.Events.playbackStateDidChange, () => { MusicKit.getInstance().addEventListener(MusicKit.Events.playbackStateDidChange, () => {
const attributes = MusicKitInterop.getAttributes() const attributes = MusicKitInterop.getAttributes();
if (MusicKitInterop.filterTrack(attributes, true, false)) { if (MusicKitInterop.filterTrack(attributes, true, false)) {
global.ipcRenderer.send('playbackStateDidChange', attributes) global.ipcRenderer.send("playbackStateDidChange", attributes);
global.ipcRenderer.send('wsapi-updatePlaybackState', attributes); global.ipcRenderer.send("wsapi-updatePlaybackState", attributes);
} }
}); });
/* MusicKit.Events.playbackProgressDidChange */ /* MusicKit.Events.playbackProgressDidChange */
MusicKit.getInstance().addEventListener(MusicKit.Events.playbackProgressDidChange, async () => { MusicKit.getInstance().addEventListener(MusicKit.Events.playbackProgressDidChange, async () => {
const attributes = MusicKitInterop.getAttributes() const attributes = MusicKitInterop.getAttributes();
// wsapi call // wsapi call
ipcRenderer.send('wsapi-updatePlaybackState', attributes); ipcRenderer.send("wsapi-updatePlaybackState", attributes);
// lastfm call // lastfm call
if (app.mk.currentPlaybackProgress === (app.cfg.connectivity.lastfm.scrobble_after / 100)) { if (app.mk.currentPlaybackProgress === app.cfg.connectivity.lastfm.scrobble_after / 100) {
attributes.primaryArtist = (app.cfg.connectivity.lastfm.enabled && app.cfg.connectivity.lastfm.remove_featured) ? await this.fetchPrimaryArtist(attributes.artistName) : attributes.artistName; attributes.primaryArtist = app.cfg.connectivity.lastfm.enabled && app.cfg.connectivity.lastfm.remove_featured ? await this.fetchPrimaryArtist(attributes.artistName) : attributes.artistName;
ipcRenderer.send('lastfm:scrobbleTrack', attributes); ipcRenderer.send("lastfm:scrobbleTrack", attributes);
} }
}); });
/* MusicKit.Events.playbackTimeDidChange */ /* MusicKit.Events.playbackTimeDidChange */
MusicKit.getInstance().addEventListener(MusicKit.Events.playbackTimeDidChange, () => { MusicKit.getInstance().addEventListener(MusicKit.Events.playbackTimeDidChange, () => {
ipcRenderer.send('mpris:playbackTimeDidChange', (MusicKit.getInstance()?.currentPlaybackTime * 1000 * 1000) ?? 0); ipcRenderer.send("mpris:playbackTimeDidChange", MusicKit.getInstance()?.currentPlaybackTime * 1000 * 1000 ?? 0);
}); });
/* MusicKit.Events.nowPlayingItemDidChange */ /* MusicKit.Events.nowPlayingItemDidChange */
MusicKit.getInstance().addEventListener(MusicKit.Events.nowPlayingItemDidChange, async () => { MusicKit.getInstance().addEventListener(MusicKit.Events.nowPlayingItemDidChange, async () => {
console.debug('[cider:preload] nowPlayingItemDidChange') console.debug("[cider:preload] nowPlayingItemDidChange");
const attributes = MusicKitInterop.getAttributes() const attributes = MusicKitInterop.getAttributes();
attributes.primaryArtist = (app.cfg.connectivity.lastfm.enabled && app.cfg.connectivity.lastfm.remove_featured) ? await this.fetchPrimaryArtist(attributes.artistName) : attributes.artistName; attributes.primaryArtist = app.cfg.connectivity.lastfm.enabled && app.cfg.connectivity.lastfm.remove_featured ? await this.fetchPrimaryArtist(attributes.artistName) : attributes.artistName;
if (MusicKitInterop.filterTrack(attributes, false, true)) { if (MusicKitInterop.filterTrack(attributes, false, true)) {
global.ipcRenderer.send('nowPlayingItemDidChange', attributes); global.ipcRenderer.send("nowPlayingItemDidChange", attributes);
} else if (attributes.name !== 'no-title-found' && attributes.playParams.id !== "no-id-found") { } else if (attributes.name !== "no-title-found" && attributes.playParams.id !== "no-id-found") {
global.ipcRenderer.send('lastfm:nowPlayingChange', attributes); global.ipcRenderer.send("lastfm:nowPlayingChange", attributes);
} }
if (app.cfg.general.playbackNotifications && !document.hasFocus() && attributes.artistName && attributes.artwork && attributes.name) { if (app.cfg.general.playbackNotifications && !document.hasFocus() && attributes.artistName && attributes.artwork && attributes.name) {
global.ipcRenderer.send('playbackNotifications:create', attributes); global.ipcRenderer.send("playbackNotifications:create", attributes);
} }
if (MusicKit.getInstance().nowPlayingItem) { if (MusicKit.getInstance().nowPlayingItem) {
@ -56,7 +56,7 @@ const MusicKitInterop = {
/* MusicKit.Events.authorizationStatusDidChange */ /* MusicKit.Events.authorizationStatusDidChange */
MusicKit.getInstance().addEventListener(MusicKit.Events.authorizationStatusDidChange, () => { MusicKit.getInstance().addEventListener(MusicKit.Events.authorizationStatusDidChange, () => {
global.ipcRenderer.send('authorizationStatusDidChange', MusicKit.getInstance().authorizationStatus) global.ipcRenderer.send("authorizationStatusDidChange", MusicKit.getInstance().authorizationStatus);
}); });
/* MusicKit.Events.mediaPlaybackError */ /* MusicKit.Events.mediaPlaybackError */
@ -66,12 +66,12 @@ const MusicKitInterop = {
/* MusicKit.Events.shuffleModeDidChange */ /* MusicKit.Events.shuffleModeDidChange */
MusicKit.getInstance().addEventListener(MusicKit.Events.shuffleModeDidChange, () => { MusicKit.getInstance().addEventListener(MusicKit.Events.shuffleModeDidChange, () => {
global.ipcRenderer.send('shuffleModeDidChange', MusicKit.getInstance().shuffleMode) global.ipcRenderer.send("shuffleModeDidChange", MusicKit.getInstance().shuffleMode);
}); });
/* MusicKit.Events.repeatModeDidChange */ /* MusicKit.Events.repeatModeDidChange */
MusicKit.getInstance().addEventListener(MusicKit.Events.repeatModeDidChange, () => { MusicKit.getInstance().addEventListener(MusicKit.Events.repeatModeDidChange, () => {
global.ipcRenderer.send('repeatModeDidChange', MusicKit.getInstance().repeatMode) global.ipcRenderer.send("repeatModeDidChange", MusicKit.getInstance().repeatMode);
}); });
}, },
@ -83,61 +83,56 @@ const MusicKitInterop = {
async fetchPrimaryArtist(artist) { async fetchPrimaryArtist(artist) {
if (app.mk.nowPlayingItem?.relationships?.artists) { if (app.mk.nowPlayingItem?.relationships?.artists) {
const artist = await app.mk.api.artist(app.mk.nowPlayingItem.relationships.artists.data[0].id) const artist = await app.mk.api.artist(app.mk.nowPlayingItem.relationships.artists.data[0].id);
return artist.attributes.name return artist.attributes.name;
} else { } else {
return artist return artist;
} }
}, },
getAttributes: function () { getAttributes: function () {
const mk = MusicKit.getInstance() const mk = MusicKit.getInstance();
const nowPlayingItem = mk.nowPlayingItem; const nowPlayingItem = mk.nowPlayingItem;
const isPlayingExport = mk.isPlaying; const isPlayingExport = mk.isPlaying;
const remainingTimeExport = mk.currentPlaybackTimeRemaining; const remainingTimeExport = mk.currentPlaybackTimeRemaining;
const currentPlaybackProgress = mk.currentPlaybackProgress; const currentPlaybackProgress = mk.currentPlaybackProgress;
const attributes = (nowPlayingItem != null ? nowPlayingItem.attributes : {}); const attributes = nowPlayingItem != null ? nowPlayingItem.attributes : {};
attributes.songId = attributes.songId ?? attributes.playParams?.catalogId ?? attributes.playParams?.id attributes.songId = attributes.songId ?? attributes.playParams?.catalogId ?? attributes.playParams?.id;
attributes.status = isPlayingExport ?? null; attributes.status = isPlayingExport ?? null;
attributes.name = attributes?.name ?? 'no-title-found'; attributes.name = attributes?.name ?? "no-title-found";
attributes.artwork = attributes?.artwork ?? {url: ''}; attributes.artwork = attributes?.artwork ?? { url: "" };
attributes.artwork.url = (attributes?.artwork?.url ?? '').replace(`{f}`, "png"); attributes.artwork.url = (attributes?.artwork?.url ?? "").replace(`{f}`, "png");
attributes.playParams = attributes?.playParams ?? {id: 'no-id-found'}; attributes.playParams = attributes?.playParams ?? { id: "no-id-found" };
attributes.playParams.id = attributes?.playParams?.id ?? 'no-id-found'; attributes.playParams.id = attributes?.playParams?.id ?? "no-id-found";
attributes.url = { attributes.url = {
cider: `https://cider.sh/link?play/s/${nowPlayingItem?._songId ?? (nowPlayingItem?.songId ?? 'no-id-found')}`, cider: `https://cider.sh/link?play/s/${nowPlayingItem?._songId ?? nowPlayingItem?.songId ?? "no-id-found"}`,
appleMusic: attributes.websiteUrl ? attributes.websiteUrl : `https://music.apple.com/${mk.storefrontId}/song/${nowPlayingItem?._songId ?? (nowPlayingItem?.songId ?? 'no-id-found')}` appleMusic: attributes.websiteUrl ? attributes.websiteUrl : `https://music.apple.com/${mk.storefrontId}/song/${nowPlayingItem?._songId ?? nowPlayingItem?.songId ?? "no-id-found"}`,
};
if (attributes.playParams.id === "no-id-found") {
attributes.playParams.id = nowPlayingItem?.id ?? "no-id-found";
} }
if (attributes.playParams.id === 'no-id-found') { attributes.albumName = attributes?.albumName ?? "";
attributes.playParams.id = nowPlayingItem?.id ?? 'no-id-found'; attributes.artistName = attributes?.artistName ?? "";
}
attributes.albumName = attributes?.albumName ?? '';
attributes.artistName = attributes?.artistName ?? '';
attributes.genreNames = attributes?.genreNames ?? []; attributes.genreNames = attributes?.genreNames ?? [];
attributes.remainingTime = remainingTimeExport attributes.remainingTime = remainingTimeExport ? remainingTimeExport * 1000 : 0;
? remainingTimeExport * 1000
: 0;
attributes.durationInMillis = attributes?.durationInMillis ?? 0; attributes.durationInMillis = attributes?.durationInMillis ?? 0;
attributes.currentPlaybackTime = mk?.currentPlaybackTime ?? 0; attributes.currentPlaybackTime = mk?.currentPlaybackTime ?? 0;
attributes.currentPlaybackProgress = currentPlaybackProgress ?? 0; attributes.currentPlaybackProgress = currentPlaybackProgress ?? 0;
attributes.startTime = Date.now(); attributes.startTime = Date.now();
attributes.endTime = Math.round( attributes.endTime = Math.round(attributes?.playParams?.id === cache.playParams.id ? Date.now() + attributes?.remainingTime : attributes?.startTime + attributes?.durationInMillis);
attributes?.playParams?.id === cache.playParams.id
? Date.now() + attributes?.remainingTime
: attributes?.startTime + attributes?.durationInMillis
);
return attributes; return attributes;
}, },
filterTrack: function (a, playbackCheck, mediaCheck) { filterTrack: function (a, playbackCheck, mediaCheck) {
if (a.name === 'no-title-found' || a.playParams.id === "no-id-found") { if (a.name === "no-title-found" || a.playParams.id === "no-id-found") {
return; return;
} else if (mediaCheck && a.playParams.id === cache.playParams.id) { } else if (mediaCheck && a.playParams.id === cache.playParams.id) {
return; return;
} else if (playbackCheck && a.status === playbackCache.status) { } else if (playbackCheck && a.status === playbackCache.status) {
return; return;
} else if (playbackCheck && !a.status && a.remainingTime === playbackCache.time) { /* Pretty much have to do this to prevent multiple runs when a song starts playing */ } else if (playbackCheck && !a.status && a.remainingTime === playbackCache.time) {
/* Pretty much have to do this to prevent multiple runs when a song starts playing */
return; return;
} }
cache = a; cache = a;
@ -167,19 +162,21 @@ const MusicKitInterop = {
// } catch (e) { } // } catch (e) { }
// if (MusicKit.getInstance().queue.nextPlayableItemIndex != -1 && MusicKit.getInstance().queue.nextPlayableItemIndex != null) // if (MusicKit.getInstance().queue.nextPlayableItemIndex != -1 && MusicKit.getInstance().queue.nextPlayableItemIndex != null)
// MusicKit.getInstance().changeToMediaAtIndex(MusicKit.getInstance().queue.nextPlayableItemIndex); // MusicKit.getInstance().changeToMediaAtIndex(MusicKit.getInstance().queue.nextPlayableItemIndex);
MusicKit.getInstance().skipToNextItem().then(r => console.debug(`[cider:preload] [next] Skipping to Next ${r}`)); MusicKit.getInstance()
.skipToNextItem()
.then((r) => console.debug(`[cider:preload] [next] Skipping to Next ${r}`));
}, },
previous: () => { previous: () => {
// if (MusicKit.getInstance().queue.previousPlayableItemIndex != -1 && MusicKit.getInstance().queue.previousPlayableItemIndex != null) // if (MusicKit.getInstance().queue.previousPlayableItemIndex != -1 && MusicKit.getInstance().queue.previousPlayableItemIndex != null)
// MusicKit.getInstance().changeToMediaAtIndex(MusicKit.getInstance().queue.previousPlayableItemIndex); // MusicKit.getInstance().changeToMediaAtIndex(MusicKit.getInstance().queue.previousPlayableItemIndex);
MusicKit.getInstance().skipToPreviousItem().then(r => console.debug(`[cider:preload] [previous] Skipping to Previous ${r}`)); MusicKit.getInstance()
} .skipToPreviousItem()
.then((r) => console.debug(`[cider:preload] [previous] Skipping to Previous ${r}`));
},
};
} process.once("loaded", () => {
console.debug("[cider:preload] IPC Listeners Created!");
process.once('loaded', () => {
console.debug("[cider:preload] IPC Listeners Created!")
global.MusicKitInterop = MusicKitInterop; global.MusicKitInterop = MusicKitInterop;
}); });

View file

@ -1,5 +0,0 @@
{
"js": {
"beautify.ignore": "src/renderer/index.js"
}
}

View file

@ -1,154 +1,136 @@
@font-face { @font-face {
font-family: 'Inter'; font-family: "Inter";
font-style: normal; font-style: normal;
font-weight: 100; font-weight: 100;
font-display: swap; font-display: swap;
src: url("Inter-Thin.woff2?v=3.19") format("woff2"), src: url("Inter-Thin.woff2?v=3.19") format("woff2"), url("Inter-Thin.woff?v=3.19") format("woff");
url("Inter-Thin.woff?v=3.19") format("woff");
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: "Inter";
font-style: italic; font-style: italic;
font-weight: 100; font-weight: 100;
font-display: swap; font-display: swap;
src: url("Inter-ThinItalic.woff2?v=3.19") format("woff2"), src: url("Inter-ThinItalic.woff2?v=3.19") format("woff2"), url("Inter-ThinItalic.woff?v=3.19") format("woff");
url("Inter-ThinItalic.woff?v=3.19") format("woff");
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: "Inter";
font-style: normal; font-style: normal;
font-weight: 200; font-weight: 200;
font-display: swap; font-display: swap;
src: url("Inter-ExtraLight.woff2?v=3.19") format("woff2"), src: url("Inter-ExtraLight.woff2?v=3.19") format("woff2"), url("Inter-ExtraLight.woff?v=3.19") format("woff");
url("Inter-ExtraLight.woff?v=3.19") format("woff");
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: "Inter";
font-style: italic; font-style: italic;
font-weight: 200; font-weight: 200;
font-display: swap; font-display: swap;
src: url("Inter-ExtraLightItalic.woff2?v=3.19") format("woff2"), src: url("Inter-ExtraLightItalic.woff2?v=3.19") format("woff2"), url("Inter-ExtraLightItalic.woff?v=3.19") format("woff");
url("Inter-ExtraLightItalic.woff?v=3.19") format("woff");
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: "Inter";
font-style: normal; font-style: normal;
font-weight: 300; font-weight: 300;
font-display: swap; font-display: swap;
src: url("Inter-Light.woff2?v=3.19") format("woff2"), src: url("Inter-Light.woff2?v=3.19") format("woff2"), url("Inter-Light.woff?v=3.19") format("woff");
url("Inter-Light.woff?v=3.19") format("woff");
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: "Inter";
font-style: italic; font-style: italic;
font-weight: 300; font-weight: 300;
font-display: swap; font-display: swap;
src: url("Inter-LightItalic.woff2?v=3.19") format("woff2"), src: url("Inter-LightItalic.woff2?v=3.19") format("woff2"), url("Inter-LightItalic.woff?v=3.19") format("woff");
url("Inter-LightItalic.woff?v=3.19") format("woff");
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: "Inter";
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-display: swap; font-display: swap;
src: url("Inter-Regular.woff2?v=3.19") format("woff2"), src: url("Inter-Regular.woff2?v=3.19") format("woff2"), url("Inter-Regular.woff?v=3.19") format("woff");
url("Inter-Regular.woff?v=3.19") format("woff");
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: "Inter";
font-style: italic; font-style: italic;
font-weight: 400; font-weight: 400;
font-display: swap; font-display: swap;
src: url("Inter-Italic.woff2?v=3.19") format("woff2"), src: url("Inter-Italic.woff2?v=3.19") format("woff2"), url("Inter-Italic.woff?v=3.19") format("woff");
url("Inter-Italic.woff?v=3.19") format("woff");
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: "Inter";
font-style: normal; font-style: normal;
font-weight: 500; font-weight: 500;
font-display: swap; font-display: swap;
src: url("Inter-Medium.woff2?v=3.19") format("woff2"), src: url("Inter-Medium.woff2?v=3.19") format("woff2"), url("Inter-Medium.woff?v=3.19") format("woff");
url("Inter-Medium.woff?v=3.19") format("woff");
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: "Inter";
font-style: italic; font-style: italic;
font-weight: 500; font-weight: 500;
font-display: swap; font-display: swap;
src: url("Inter-MediumItalic.woff2?v=3.19") format("woff2"), src: url("Inter-MediumItalic.woff2?v=3.19") format("woff2"), url("Inter-MediumItalic.woff?v=3.19") format("woff");
url("Inter-MediumItalic.woff?v=3.19") format("woff");
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: "Inter";
font-style: normal; font-style: normal;
font-weight: 600; font-weight: 600;
font-display: swap; font-display: swap;
src: url("Inter-SemiBold.woff2?v=3.19") format("woff2"), src: url("Inter-SemiBold.woff2?v=3.19") format("woff2"), url("Inter-SemiBold.woff?v=3.19") format("woff");
url("Inter-SemiBold.woff?v=3.19") format("woff");
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: "Inter";
font-style: italic; font-style: italic;
font-weight: 600; font-weight: 600;
font-display: swap; font-display: swap;
src: url("Inter-SemiBoldItalic.woff2?v=3.19") format("woff2"), src: url("Inter-SemiBoldItalic.woff2?v=3.19") format("woff2"), url("Inter-SemiBoldItalic.woff?v=3.19") format("woff");
url("Inter-SemiBoldItalic.woff?v=3.19") format("woff");
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: "Inter";
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
font-display: swap; font-display: swap;
src: url("Inter-Bold.woff2?v=3.19") format("woff2"), src: url("Inter-Bold.woff2?v=3.19") format("woff2"), url("Inter-Bold.woff?v=3.19") format("woff");
url("Inter-Bold.woff?v=3.19") format("woff");
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: "Inter";
font-style: italic; font-style: italic;
font-weight: 700; font-weight: 700;
font-display: swap; font-display: swap;
src: url("Inter-BoldItalic.woff2?v=3.19") format("woff2"), src: url("Inter-BoldItalic.woff2?v=3.19") format("woff2"), url("Inter-BoldItalic.woff?v=3.19") format("woff");
url("Inter-BoldItalic.woff?v=3.19") format("woff");
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: "Inter";
font-style: normal; font-style: normal;
font-weight: 800; font-weight: 800;
font-display: swap; font-display: swap;
src: url("Inter-ExtraBold.woff2?v=3.19") format("woff2"), src: url("Inter-ExtraBold.woff2?v=3.19") format("woff2"), url("Inter-ExtraBold.woff?v=3.19") format("woff");
url("Inter-ExtraBold.woff?v=3.19") format("woff");
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: "Inter";
font-style: italic; font-style: italic;
font-weight: 800; font-weight: 800;
font-display: swap; font-display: swap;
src: url("Inter-ExtraBoldItalic.woff2?v=3.19") format("woff2"), src: url("Inter-ExtraBoldItalic.woff2?v=3.19") format("woff2"), url("Inter-ExtraBoldItalic.woff?v=3.19") format("woff");
url("Inter-ExtraBoldItalic.woff?v=3.19") format("woff");
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: "Inter";
font-style: normal; font-style: normal;
font-weight: 900; font-weight: 900;
font-display: swap; font-display: swap;
src: url("Inter-Black.woff2?v=3.19") format("woff2"), src: url("Inter-Black.woff2?v=3.19") format("woff2"), url("Inter-Black.woff?v=3.19") format("woff");
url("Inter-Black.woff?v=3.19") format("woff");
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: "Inter";
font-style: italic; font-style: italic;
font-weight: 900; font-weight: 900;
font-display: swap; font-display: swap;
src: url("Inter-BlackItalic.woff2?v=3.19") format("woff2"), src: url("Inter-BlackItalic.woff2?v=3.19") format("woff2"), url("Inter-BlackItalic.woff?v=3.19") format("woff");
url("Inter-BlackItalic.woff?v=3.19") format("woff");
} }
/* ------------------------------------------------------- /* -------------------------------------------------------
@ -161,23 +143,22 @@ Usage:
} }
*/ */
@font-face { @font-face {
font-family: 'Inter var'; font-family: "Inter var";
font-weight: 100 900; font-weight: 100 900;
font-display: swap; font-display: swap;
font-style: normal; font-style: normal;
font-named-instance: 'Regular'; font-named-instance: "Regular";
src: url("Inter-roman.var.woff2?v=3.19") format("woff2"); src: url("Inter-roman.var.woff2?v=3.19") format("woff2");
} }
@font-face { @font-face {
font-family: 'Inter var'; font-family: "Inter var";
font-weight: 100 900; font-weight: 100 900;
font-display: swap; font-display: swap;
font-style: italic; font-style: italic;
font-named-instance: 'Italic'; font-named-instance: "Italic";
src: url("Inter-italic.var.woff2?v=3.19") format("woff2"); src: url("Inter-italic.var.woff2?v=3.19") format("woff2");
} }
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
[EXPERIMENTAL] Multi-axis, single variable font. [EXPERIMENTAL] Multi-axis, single variable font.
@ -192,7 +173,7 @@ explicitly, e.g.
*/ */
@font-face { @font-face {
font-family: 'Inter var experimental'; font-family: "Inter var experimental";
font-weight: 100 900; font-weight: 100 900;
font-display: swap; font-display: swap;
font-style: oblique 0deg 10deg; font-style: oblique 0deg 10deg;

View file

@ -8,9 +8,9 @@ http://scripts.sil.org/OFL
*/ */
@font-face { @font-face {
font-family: 'Pretendard Variable'; font-family: "Pretendard Variable";
font-weight: 45 920; font-weight: 45 920;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
src: local('Pretendard Variable'), url('./woff2/PretendardVariable.woff2') format('woff2-variations'); src: local("Pretendard Variable"), url("./woff2/PretendardVariable.woff2") format("woff2-variations");
} }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -58,10 +58,10 @@ Vue.component("animated-number", {
function initMusicKit() { function initMusicKit() {
if (!this.responseText) { if (!this.responseText) {
console.log("Using stored token") console.log("Using stored token");
this.responseText = JSON.stringify({ this.responseText = JSON.stringify({
token: localStorage.getItem("lastToken") token: localStorage.getItem("lastToken"),
}) });
} }
let parsedJson = JSON.parse(this.responseText); let parsedJson = JSON.parse(this.responseText);
localStorage.setItem("lastToken", parsedJson.token); localStorage.setItem("lastToken", parsedJson.token);
@ -96,9 +96,9 @@ function capiInit() {
request.onreadystatechange = function (aEvt) { request.onreadystatechange = function (aEvt) {
if (request.readyState == 4 && request.status != 200) { if (request.readyState == 4 && request.status != 200) {
if (localStorage.getItem("lastToken") != null) { if (localStorage.getItem("lastToken") != null) {
initMusicKit() initMusicKit();
} else { } else {
console.error(`Failed to load capi, cannot get token [${request.status}]`) console.error(`Failed to load capi, cannot get token [${request.status}]`);
} }
} }
}; };
@ -110,7 +110,7 @@ document.addEventListener("musickitloaded", function () {
if (showOobe()) return; if (showOobe()) return;
console.log("MusicKit loaded"); console.log("MusicKit loaded");
// MusicKit global is now defined // MusicKit global is now defined
capiInit() capiInit();
}); });
window.addEventListener("drmUnsupported", function () { window.addEventListener("drmUnsupported", function () {
initMusicKit(); initMusicKit();
@ -140,12 +140,7 @@ function Clone(obj) {
} }
function uuidv4() { function uuidv4() {
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16));
(
c ^
(crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
).toString(16)
);
} }
function xmlToJson(xml) { function xmlToJson(xml) {
@ -196,9 +191,7 @@ async function asyncForEach(array, callback) {
var checkIfScrollIsStatic = setInterval(() => { var checkIfScrollIsStatic = setInterval(() => {
try { try {
if ( if (position === document.getElementsByClassName("lyric-body")[0].scrollTop) {
position === document.getElementsByClassName("lyric-body")[0].scrollTop
) {
clearInterval(checkIfScrollIsStatic); clearInterval(checkIfScrollIsStatic);
// do something // do something
} }
@ -210,12 +203,7 @@ var checkIfScrollIsStatic = setInterval(() => {
async function webGPU() { async function webGPU() {
try { try {
const currentGPU = await navigator.gpu.requestAdapter(); const currentGPU = await navigator.gpu.requestAdapter();
console.log( console.log("WebGPU enabled on", currentGPU.name, "with feature ID", currentGPU.features.size);
"WebGPU enabled on",
currentGPU.name,
"with feature ID",
currentGPU.features.size
);
} catch (e) { } catch (e) {
console.log("WebGPU disabled / WebGPU initialization failed"); console.log("WebGPU disabled / WebGPU initialization failed");
} }
@ -240,9 +228,9 @@ function isJson(item) {
webGPU().then(); webGPU().then();
function showOobe() { function showOobe() {
return false return false;
if (localStorage.getItem("music.ampwebplay.media-user-token") && localStorage.getItem("seenOOBE")) { if (localStorage.getItem("music.ampwebplay.media-user-token") && localStorage.getItem("seenOOBE")) {
return false return false;
} else { } else {
function waitForApp() { function waitForApp() {
if (typeof app.init !== "undefined") { if (typeof app.init !== "undefined") {
@ -252,7 +240,7 @@ function showOobe() {
} }
} }
waitForApp(); waitForApp();
return true return true;
} }
} }
@ -266,13 +254,7 @@ document.addEventListener("DOMContentLoaded", async function () {
document.addEventListener( document.addEventListener(
"contextmenu", "contextmenu",
function (e) { function (e) {
if ( if (e.target.tagName.toLowerCase() == "textarea" || (e.target.tagName.toLowerCase() == "input" && e.target.type != "checkbox" && e.target.type != "radio" && e.target.disabled == false)) {
e.target.tagName.toLowerCase() == "textarea" ||
(e.target.tagName.toLowerCase() == "input" &&
e.target.type != "checkbox" &&
e.target.type != "radio" &&
e.target.disabled == false)
) {
e.preventDefault(); e.preventDefault();
const menuPanel = { const menuPanel = {
items: { items: {

View file

@ -1,6 +1,6 @@
:root { :root {
--appleEase: cubic-bezier(.42, 0, .58, 1); --appleEase: cubic-bezier(0.42, 0, 0.58, 1);
--appleTransition: .2s var(--appleEase); --appleTransition: 0.2s var(--appleEase);
} }
/* Simple CSS framework for Apple Music Electron */ /* Simple CSS framework for Apple Music Electron */
@ -57,7 +57,7 @@ input[type="range"].md-slider::-webkit-slider-thumb {
box-shadow: 0 0 2px 0 #555; box-shadow: 0 0 2px 0 #555;
} }
input[type=range].md-slider::-webkit-slider-runnable-track { input[type="range"].md-slider::-webkit-slider-runnable-track {
-webkit-appearance: none; -webkit-appearance: none;
box-shadow: none; box-shadow: none;
border: none; border: none;
@ -102,9 +102,6 @@ input[type=range].md-slider::-webkit-slider-runnable-track {
text-align: center; text-align: center;
} }
.md-transparent { .md-transparent {
background: transparent; background: transparent;
} }
@ -142,19 +139,23 @@ input[type=range].md-slider::-webkit-slider-runnable-track {
} }
/* Vue transitions */ /* Vue transitions */
.fade_simple-enter-active, .fade_simple-leave-active { .fade_simple-enter-active,
transition: all .5s; .fade_simple-leave-active {
transition: all 0.5s;
} }
.fade_simple-enter, .fade_simple-leave-to { .fade_simple-enter,
.fade_simple-leave-to {
opacity: 0; opacity: 0;
} }
.fade-enter-active, .fade-leave-active { .fade-enter-active,
transition: all .5s; .fade-leave-active {
transition: all 0.5s;
} }
.fade-enter, .fade-leave-to { .fade-enter,
.fade-leave-to {
opacity: 0; opacity: 0;
transform: scale(0.95); transform: scale(0.95);
} }

View file

@ -245,16 +245,19 @@
background-color: transparent; background-color: transparent;
border: 0; border: 0;
} }
.dropdown-item:hover, .dropdown-item:focus { .dropdown-item:hover,
.dropdown-item:focus {
color: #1e2125; color: #1e2125;
background-color: #e9ecef; background-color: #e9ecef;
} }
.dropdown-item.active, .dropdown-item:active { .dropdown-item.active,
.dropdown-item:active {
color: #fff; color: #fff;
text-decoration: none; text-decoration: none;
background-color: #0d6efd; background-color: #0d6efd;
} }
.dropdown-item.disabled, .dropdown-item:disabled { .dropdown-item.disabled,
.dropdown-item:disabled {
color: #adb5bd; color: #adb5bd;
pointer-events: none; pointer-events: none;
background-color: transparent; background-color: transparent;
@ -287,15 +290,18 @@
.dropdown-menu-dark .dropdown-item { .dropdown-menu-dark .dropdown-item {
color: #dee2e6; color: #dee2e6;
} }
.dropdown-menu-dark .dropdown-item:hover, .dropdown-menu-dark .dropdown-item:focus { .dropdown-menu-dark .dropdown-item:hover,
.dropdown-menu-dark .dropdown-item:focus {
color: #fff; color: #fff;
background-color: rgba(255, 255, 255, 0.15); background-color: rgba(255, 255, 255, 0.15);
} }
.dropdown-menu-dark .dropdown-item.active, .dropdown-menu-dark .dropdown-item:active { .dropdown-menu-dark .dropdown-item.active,
.dropdown-menu-dark .dropdown-item:active {
color: #fff; color: #fff;
background-color: #0d6efd; background-color: #0d6efd;
} }
.dropdown-menu-dark .dropdown-item.disabled, .dropdown-menu-dark .dropdown-item:disabled { .dropdown-menu-dark .dropdown-item.disabled,
.dropdown-menu-dark .dropdown-item:disabled {
color: #adb5bd; color: #adb5bd;
} }
.dropdown-menu-dark .dropdown-divider { .dropdown-menu-dark .dropdown-divider {
@ -308,7 +314,6 @@
color: #adb5bd; color: #adb5bd;
} }
// List Group // List Group
.list-group { .list-group {
display: flex; display: flex;
@ -943,7 +948,6 @@
} }
} }
.modal { .modal {
position: fixed; position: fixed;
top: 0; top: 0;
@ -957,7 +961,6 @@
outline: 0; outline: 0;
user-select: none; user-select: none;
.close { .close {
width: 50px; width: 50px;
height: 42px; height: 42px;
@ -973,7 +976,7 @@
right: 0; right: 0;
&:hover { &:hover {
background-color: rgb(196, 43, 28) background-color: rgb(196, 43, 28);
} }
} }
} }
@ -1134,7 +1137,6 @@
} }
@media (min-width: 992px) { @media (min-width: 992px) {
.modal-lg, .modal-lg,
.modal-xl { .modal-xl {
max-width: 800px; max-width: 800px;
@ -1307,7 +1309,6 @@
} }
} }
.btn { .btn {
display: inline-block; display: inline-block;
font-weight: 400; font-weight: 400;
@ -2127,19 +2128,23 @@ fieldset:disabled .btn {
padding-right: 0.5625rem; padding-right: 0.5625rem;
padding-left: 0.5625rem; padding-left: 0.5625rem;
} }
.dropdown-toggle-split::after, .dropup .dropdown-toggle-split::after, .dropend .dropdown-toggle-split::after { .dropdown-toggle-split::after,
.dropup .dropdown-toggle-split::after,
.dropend .dropdown-toggle-split::after {
margin-left: 0; margin-left: 0;
} }
.dropstart .dropdown-toggle-split::before { .dropstart .dropdown-toggle-split::before {
margin-right: 0; margin-right: 0;
} }
.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split { .btn-sm + .dropdown-toggle-split,
.btn-group-sm > .btn + .dropdown-toggle-split {
padding-right: 0.375rem; padding-right: 0.375rem;
padding-left: 0.375rem; padding-left: 0.375rem;
} }
.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split { .btn-lg + .dropdown-toggle-split,
.btn-group-lg > .btn + .dropdown-toggle-split {
padding-right: 0.75rem; padding-right: 0.75rem;
padding-left: 0.75rem; padding-left: 0.75rem;
} }
@ -2387,7 +2392,8 @@ fieldset:disabled .btn {
text-decoration: none; text-decoration: none;
} }
.nav-link:hover, .nav-link:focus { .nav-link:hover,
.nav-link:focus {
text-decoration: none; text-decoration: none;
} }
@ -2397,7 +2403,6 @@ fieldset:disabled .btn {
cursor: default; cursor: default;
} }
.nav-tabs { .nav-tabs {
border-bottom: 1px solid #dee2e6; border-bottom: 1px solid #dee2e6;
} }
@ -2408,7 +2413,8 @@ fieldset:disabled .btn {
border-top-left-radius: 0.25rem; border-top-left-radius: 0.25rem;
border-top-right-radius: 0.25rem; border-top-right-radius: 0.25rem;
} }
.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus { .nav-tabs .nav-link:hover,
.nav-tabs .nav-link:focus {
border-color: #e9ecef #e9ecef #dee2e6; border-color: #e9ecef #e9ecef #dee2e6;
isolation: isolate; isolation: isolate;
} }
@ -2549,7 +2555,6 @@ fieldset:disabled .btn {
} }
@media (min-width: 576px) { @media (min-width: 576px) {
.container-sm, .container-sm,
.container { .container {
max-width: 540px; max-width: 540px;
@ -2557,7 +2562,6 @@ fieldset:disabled .btn {
} }
@media (min-width: 768px) { @media (min-width: 768px) {
.container-md, .container-md,
.container-sm, .container-sm,
.container { .container {
@ -2566,7 +2570,6 @@ fieldset:disabled .btn {
} }
@media (min-width: 992px) { @media (min-width: 992px) {
.container-lg, .container-lg,
.container-md, .container-md,
.container-sm, .container-sm,
@ -2576,7 +2579,6 @@ fieldset:disabled .btn {
} }
@media (min-width: 1200px) { @media (min-width: 1200px) {
.container-xl, .container-xl,
.container-lg, .container-lg,
.container-md, .container-md,
@ -2587,7 +2589,6 @@ fieldset:disabled .btn {
} }
@media (min-width: 1400px) { @media (min-width: 1400px) {
.container-xxl, .container-xxl,
.container-xl, .container-xl,
.container-lg, .container-lg,
@ -7593,72 +7594,72 @@ fieldset:disabled .btn {
} }
.bs-tooltip-top, .bs-tooltip-top,
.bs-tooltip-auto[data-popper-placement^=top] { .bs-tooltip-auto[data-popper-placement^="top"] {
padding: 0.4rem 0; padding: 0.4rem 0;
} }
.bs-tooltip-top .tooltip-arrow, .bs-tooltip-top .tooltip-arrow,
.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow { .bs-tooltip-auto[data-popper-placement^="top"] .tooltip-arrow {
bottom: 0; bottom: 0;
} }
.bs-tooltip-top .tooltip-arrow::before, .bs-tooltip-top .tooltip-arrow::before,
.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before { .bs-tooltip-auto[data-popper-placement^="top"] .tooltip-arrow::before {
top: -1px; top: -1px;
border-width: 0.4rem 0.4rem 0; border-width: 0.4rem 0.4rem 0;
border-top-color: #000; border-top-color: #000;
} }
.bs-tooltip-end, .bs-tooltip-end,
.bs-tooltip-auto[data-popper-placement^=right] { .bs-tooltip-auto[data-popper-placement^="right"] {
padding: 0 0.4rem; padding: 0 0.4rem;
} }
.bs-tooltip-end .tooltip-arrow, .bs-tooltip-end .tooltip-arrow,
.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow { .bs-tooltip-auto[data-popper-placement^="right"] .tooltip-arrow {
left: 0; left: 0;
width: 0.4rem; width: 0.4rem;
height: 0.8rem; height: 0.8rem;
} }
.bs-tooltip-end .tooltip-arrow::before, .bs-tooltip-end .tooltip-arrow::before,
.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before { .bs-tooltip-auto[data-popper-placement^="right"] .tooltip-arrow::before {
right: -1px; right: -1px;
border-width: 0.4rem 0.4rem 0.4rem 0; border-width: 0.4rem 0.4rem 0.4rem 0;
border-right-color: #000; border-right-color: #000;
} }
.bs-tooltip-bottom, .bs-tooltip-bottom,
.bs-tooltip-auto[data-popper-placement^=bottom] { .bs-tooltip-auto[data-popper-placement^="bottom"] {
padding: 0.4rem 0; padding: 0.4rem 0;
} }
.bs-tooltip-bottom .tooltip-arrow, .bs-tooltip-bottom .tooltip-arrow,
.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow { .bs-tooltip-auto[data-popper-placement^="bottom"] .tooltip-arrow {
top: 0; top: 0;
} }
.bs-tooltip-bottom .tooltip-arrow::before, .bs-tooltip-bottom .tooltip-arrow::before,
.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before { .bs-tooltip-auto[data-popper-placement^="bottom"] .tooltip-arrow::before {
bottom: -1px; bottom: -1px;
border-width: 0 0.4rem 0.4rem; border-width: 0 0.4rem 0.4rem;
border-bottom-color: #000; border-bottom-color: #000;
} }
.bs-tooltip-start, .bs-tooltip-start,
.bs-tooltip-auto[data-popper-placement^=left] { .bs-tooltip-auto[data-popper-placement^="left"] {
padding: 0 0.4rem; padding: 0 0.4rem;
} }
.bs-tooltip-start .tooltip-arrow, .bs-tooltip-start .tooltip-arrow,
.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow { .bs-tooltip-auto[data-popper-placement^="left"] .tooltip-arrow {
right: 0; right: 0;
width: 0.4rem; width: 0.4rem;
height: 0.8rem; height: 0.8rem;
} }
.bs-tooltip-start .tooltip-arrow::before, .bs-tooltip-start .tooltip-arrow::before,
.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before { .bs-tooltip-auto[data-popper-placement^="left"] .tooltip-arrow::before {
left: -1px; left: -1px;
border-width: 0.4rem 0 0.4rem 0.4rem; border-width: 0.4rem 0 0.4rem 0.4rem;
border-left-color: #000; border-left-color: #000;
@ -7710,7 +7711,8 @@ fieldset:disabled .btn {
width: 1rem; width: 1rem;
height: 0.5rem; height: 0.5rem;
} }
.popover .popover-arrow::before, .popover .popover-arrow::after { .popover .popover-arrow::before,
.popover .popover-arrow::after {
position: absolute; position: absolute;
display: block; display: block;
content: ""; content: "";
@ -7718,50 +7720,60 @@ fieldset:disabled .btn {
border-style: solid; border-style: solid;
} }
.bs-popover-top > .popover-arrow, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow { .bs-popover-top > .popover-arrow,
.bs-popover-auto[data-popper-placement^="top"] > .popover-arrow {
bottom: calc(-0.5rem - 1px); bottom: calc(-0.5rem - 1px);
} }
.bs-popover-top > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::before { .bs-popover-top > .popover-arrow::before,
.bs-popover-auto[data-popper-placement^="top"] > .popover-arrow::before {
bottom: 0; bottom: 0;
border-width: 0.5rem 0.5rem 0; border-width: 0.5rem 0.5rem 0;
border-top-color: rgba(0, 0, 0, 0.25); border-top-color: rgba(0, 0, 0, 0.25);
} }
.bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::after { .bs-popover-top > .popover-arrow::after,
.bs-popover-auto[data-popper-placement^="top"] > .popover-arrow::after {
bottom: 1px; bottom: 1px;
border-width: 0.5rem 0.5rem 0; border-width: 0.5rem 0.5rem 0;
border-top-color: #fff; border-top-color: #fff;
} }
.bs-popover-end > .popover-arrow, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow { .bs-popover-end > .popover-arrow,
.bs-popover-auto[data-popper-placement^="right"] > .popover-arrow {
left: calc(-0.5rem - 1px); left: calc(-0.5rem - 1px);
width: 0.5rem; width: 0.5rem;
height: 1rem; height: 1rem;
} }
.bs-popover-end > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::before { .bs-popover-end > .popover-arrow::before,
.bs-popover-auto[data-popper-placement^="right"] > .popover-arrow::before {
left: 0; left: 0;
border-width: 0.5rem 0.5rem 0.5rem 0; border-width: 0.5rem 0.5rem 0.5rem 0;
border-right-color: rgba(0, 0, 0, 0.25); border-right-color: rgba(0, 0, 0, 0.25);
} }
.bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::after { .bs-popover-end > .popover-arrow::after,
.bs-popover-auto[data-popper-placement^="right"] > .popover-arrow::after {
left: 1px; left: 1px;
border-width: 0.5rem 0.5rem 0.5rem 0; border-width: 0.5rem 0.5rem 0.5rem 0;
border-right-color: #fff; border-right-color: #fff;
} }
.bs-popover-bottom > .popover-arrow, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow { .bs-popover-bottom > .popover-arrow,
.bs-popover-auto[data-popper-placement^="bottom"] > .popover-arrow {
top: calc(-0.5rem - 1px); top: calc(-0.5rem - 1px);
} }
.bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::before { .bs-popover-bottom > .popover-arrow::before,
.bs-popover-auto[data-popper-placement^="bottom"] > .popover-arrow::before {
top: 0; top: 0;
border-width: 0 0.5rem 0.5rem 0.5rem; border-width: 0 0.5rem 0.5rem 0.5rem;
border-bottom-color: rgba(0, 0, 0, 0.25); border-bottom-color: rgba(0, 0, 0, 0.25);
} }
.bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::after { .bs-popover-bottom > .popover-arrow::after,
.bs-popover-auto[data-popper-placement^="bottom"] > .popover-arrow::after {
top: 1px; top: 1px;
border-width: 0 0.5rem 0.5rem 0.5rem; border-width: 0 0.5rem 0.5rem 0.5rem;
border-bottom-color: #fff; border-bottom-color: #fff;
} }
.bs-popover-bottom .popover-header::before, .bs-popover-auto[data-popper-placement^=bottom] .popover-header::before { .bs-popover-bottom .popover-header::before,
.bs-popover-auto[data-popper-placement^="bottom"] .popover-header::before {
position: absolute; position: absolute;
top: 0; top: 0;
left: 50%; left: 50%;
@ -7772,17 +7784,20 @@ fieldset:disabled .btn {
border-bottom: 1px solid #f0f0f0; border-bottom: 1px solid #f0f0f0;
} }
.bs-popover-start > .popover-arrow, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow { .bs-popover-start > .popover-arrow,
.bs-popover-auto[data-popper-placement^="left"] > .popover-arrow {
right: calc(-0.5rem - 1px); right: calc(-0.5rem - 1px);
width: 0.5rem; width: 0.5rem;
height: 1rem; height: 1rem;
} }
.bs-popover-start > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::before { .bs-popover-start > .popover-arrow::before,
.bs-popover-auto[data-popper-placement^="left"] > .popover-arrow::before {
right: 0; right: 0;
border-width: 0.5rem 0 0.5rem 0.5rem; border-width: 0.5rem 0 0.5rem 0.5rem;
border-left-color: rgba(0, 0, 0, 0.25); border-left-color: rgba(0, 0, 0, 0.25);
} }
.bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::after { .bs-popover-start > .popover-arrow::after,
.bs-popover-auto[data-popper-placement^="left"] > .popover-arrow::after {
right: 1px; right: 1px;
border-width: 0.5rem 0 0.5rem 0.5rem; border-width: 0.5rem 0 0.5rem 0.5rem;
border-left-color: #fff; border-left-color: #fff;
@ -7922,7 +7937,8 @@ fieldset:disabled .btn {
} }
} }
.carousel-control-prev:hover, .carousel-control-prev:focus, .carousel-control-prev:hover,
.carousel-control-prev:focus,
.carousel-control-next:hover, .carousel-control-next:hover,
.carousel-control-next:focus { .carousel-control-next:focus {
color: #fff; color: #fff;
@ -7985,7 +8001,7 @@ fieldset:disabled .btn {
background-clip: padding-box; background-clip: padding-box;
border-top: 10px solid transparent; border-top: 10px solid transparent;
border-bottom: 10px solid transparent; border-bottom: 10px solid transparent;
opacity: .5; opacity: 0.5;
transition: opacity 0.6s ease; transition: opacity 0.6s ease;
} }

File diff suppressed because it is too large Load diff

View file

@ -12,7 +12,6 @@
} }
.app-chrome { .app-chrome {
&:not(.chrome-bottom) { &:not(.chrome-bottom) {
.app-chrome--center { .app-chrome--center {
flex: 1; flex: 1;
@ -43,7 +42,7 @@
&:before { &:before {
--dist: 1px; --dist: 1px;
content : ''; content: "";
position: absolute; position: absolute;
top: var(--dist); top: var(--dist);
left: var(--dist); left: var(--dist);
@ -65,7 +64,7 @@
&:before { &:before {
transition: transform 0.1s ease-in-out, opacity 0.1s ease-in-out; transition: transform 0.1s ease-in-out, opacity 0.1s ease-in-out;
opacity : .1; opacity: 0.1;
transform: scale(1); transform: scale(1);
} }
} }
@ -74,7 +73,7 @@
background-color: transparent; background-color: transparent;
&:before { &:before {
opacity : .2; opacity: 0.2;
transform: scale(1); transform: scale(1);
} }
} }
@ -104,8 +103,7 @@
background: var(--color2); background: var(--color2);
-webkit-app-region: no-drag; -webkit-app-region: no-drag;
height: var(--chromeHeight2); height: var(--chromeHeight2);
box-shadow : 0px -2px 6px rgb(20 20 20 / 12%), box-shadow: 0px -2px 6px rgb(20 20 20 / 12%), 0px -1px 0px 0px rgb(200 200 200 / 12%);
0px -1px 0px 0px rgb(200 200 200 / 12%);
z-index: 4; z-index: 4;
.app-chrome-playback-duration-bottom { .app-chrome-playback-duration-bottom {
@ -123,7 +121,7 @@
font-size: 0.8em; font-size: 0.8em;
} }
input[type=range] { input[type="range"] {
appearance: none; appearance: none;
width: 100%; width: 100%;
height: 5px; height: 5px;
@ -142,7 +140,7 @@
border-radius: 100%; border-radius: 100%;
background: var(--keyColor); background: var(--keyColor);
cursor: default; cursor: default;
transition : opacity .10s var(--appleEase), transform .10s var(--appleEase); transition: opacity 0.1s var(--appleEase), transform 0.1s var(--appleEase);
} }
&:hover { &:hover {
@ -201,7 +199,6 @@
background: @bgColor; background: @bgColor;
z-index: 0; z-index: 0;
.song-duration { .song-duration {
display: flex; display: flex;
} }
@ -216,7 +213,7 @@
} }
&:hover { &:hover {
>input[type=range] { > input[type="range"] {
&::-webkit-slider-thumb { &::-webkit-slider-thumb {
opacity: 1; opacity: 1;
transform: scale(1); transform: scale(1);
@ -225,7 +222,7 @@
} }
} }
input[type=range] { input[type="range"] {
appearance: none; appearance: none;
width: 100%; width: 100%;
height: 4px; height: 4px;
@ -242,12 +239,11 @@
border-radius: 100%; border-radius: 100%;
background: var(--keyColor); background: var(--keyColor);
cursor: default; cursor: default;
transition : opacity .10s var(--appleEase), transform .10s var(--appleEase); transition: opacity 0.1s var(--appleEase), transform 0.1s var(--appleEase);
} }
} }
} }
} }
} }
.app-chrome--left { .app-chrome--left {
@ -310,7 +306,6 @@
} }
} }
width: 100%; width: 100%;
height: 100%; height: 100%;
max-width: 100%; max-width: 100%;
@ -334,7 +329,6 @@
} }
} }
// screen width is less than 768px // screen width is less than 768px
@media (max-width: 1100px) { @media (max-width: 1100px) {
#app.twopanel .app-chrome:not(.chrome-bottom) .app-chrome--center { #app.twopanel .app-chrome:not(.chrome-bottom) .app-chrome--center {

View file

@ -26,7 +26,6 @@
} }
} }
// Buttons // Buttons
.md-btn { .md-btn {
font-family: inherit; font-family: inherit;
@ -115,7 +114,6 @@
} }
} }
.md-close-btn { .md-close-btn {
-webkit-mask-image: url("ameres://icons/webui/close.svg"); -webkit-mask-image: url("ameres://icons/webui/close.svg");
-webkit-mask-repeat: no-repeat; -webkit-mask-repeat: no-repeat;
@ -174,19 +172,19 @@
} }
.md-ico-first { .md-ico-first {
content: url('./assets/angles-left.svg'); content: url("./assets/angles-left.svg");
} }
.md-ico-prev { .md-ico-prev {
content: url('./assets/chevron-left.svg'); content: url("./assets/chevron-left.svg");
} }
.md-ico-next { .md-ico-next {
content: url('./assets/chevron-right.svg'); content: url("./assets/chevron-right.svg");
} }
.md-ico-last { .md-ico-last {
content: url('./assets/angles-right.svg'); content: url("./assets/angles-right.svg");
} }
.reload-btn { .reload-btn {
@ -234,7 +232,7 @@
&:hover { &:hover {
cursor: pointer; cursor: pointer;
background: rgb(200 200 200 / 10%) background: rgb(200 200 200 / 10%);
} }
} }
@ -315,7 +313,6 @@
} }
} }
#artworkLCD img { #artworkLCD img {
image-rendering: auto; image-rendering: auto;
} }
@ -514,7 +511,7 @@
.subtitle { .subtitle {
width: 90%; width: 90%;
font-size: .8em; font-size: 0.8em;
opacity: 0.7; opacity: 0.7;
} }
@ -570,39 +567,39 @@
*/ */
@keyframes load-bar { @keyframes load-bar {
10% { 10% {
box-shadow: inset 0 -4px 0 box-shadow: inset 0 -4px 0;
} }
20% { 20% {
box-shadow: inset 0 -10px 0 box-shadow: inset 0 -10px 0;
} }
30% { 30% {
box-shadow: inset 0 -12px 0 box-shadow: inset 0 -12px 0;
} }
40% { 40% {
box-shadow: inset 0 -8px 0 box-shadow: inset 0 -8px 0;
} }
50% { 50% {
box-shadow: inset 0 -4px 0 box-shadow: inset 0 -4px 0;
} }
60% { 60% {
box-shadow: inset 0 -6px 0 box-shadow: inset 0 -6px 0;
} }
80% { 80% {
box-shadow: inset 0 -12px 0 box-shadow: inset 0 -12px 0;
} }
90% { 90% {
box-shadow: inset 0 -6px 0 box-shadow: inset 0 -6px 0;
} }
to { to {
box-shadow: inset 0 -2px 0 box-shadow: inset 0 -2px 0;
} }
} }
@ -629,17 +626,17 @@
.loadbar-sound::before { .loadbar-sound::before {
content: ""; content: "";
position: absolute; position: absolute;
bottom: 0 bottom: 0;
} }
.loadbar-sound::before { .loadbar-sound::before {
left: -4.5px; left: -4.5px;
animation-delay: -2.4s animation-delay: -2.4s;
} }
.loadbar-sound::after { .loadbar-sound::after {
right: -4.2px; right: -4.2px;
animation-delay: -3.7s animation-delay: -3.7s;
} }
.isLibrary { .isLibrary {
@ -670,7 +667,6 @@
box-shadow: var(--mediaItemShadow); box-shadow: var(--mediaItemShadow);
} }
&:active { &:active {
background: var(--selected-click); background: var(--selected-click);
box-shadow: var(--mediaItemShadow); box-shadow: var(--mediaItemShadow);
@ -805,7 +801,6 @@
&:hover + .cd-mediaitem-square-large-overlay { &:hover + .cd-mediaitem-square-large-overlay {
opacity: 1; opacity: 1;
} }
&:hover { &:hover {
@ -813,7 +808,6 @@
} }
} }
/* mediaitem-square-large */ /* mediaitem-square-large */
.cd-mediaitem-square-large { .cd-mediaitem-square-large {
width: 190px; width: 190px;
@ -855,12 +849,10 @@
margin: 10px; margin: 10px;
margin-top: 0px; margin-top: 0px;
opacity: 0; opacity: 0;
} }
.cd-mediaitem-square-large-overlay > * { .cd-mediaitem-square-large-overlay > * {
pointer-events: auto; pointer-events: auto;
} }
.cd-mediaitem-square-large > .cd-mediaitem-square-large-overlay { .cd-mediaitem-square-large > .cd-mediaitem-square-large-overlay {
@ -873,15 +865,12 @@
.cd-mediaitem-square-large + .cd-mediaitem-square-large-overlay { .cd-mediaitem-square-large + .cd-mediaitem-square-large-overlay {
pointer-events: none; pointer-events: none;
} }
.cd-mediaitem-square-large:hover + .cd-mediaitem-square-large-overlay { .cd-mediaitem-square-large:hover + .cd-mediaitem-square-large-overlay {
opacity: 1; opacity: 1;
} }
.cd-mediaitem-square-large .artwork.round { .cd-mediaitem-square-large .artwork.round {
border-radius: var(--mediaItemRadiusRound); border-radius: var(--mediaItemRadiusRound);
} }
@ -940,12 +929,10 @@
margin: 10px; margin: 10px;
margin-top: 0px; margin-top: 0px;
opacity: 0; opacity: 0;
} }
.cd-mediaitem-mvview-overlay > * { .cd-mediaitem-mvview-overlay > * {
pointer-events: auto; pointer-events: auto;
} }
.cd-mediaitem-mvview > .cd-mediaitem-mvview-overlay { .cd-mediaitem-mvview > .cd-mediaitem-mvview-overlay {
@ -958,15 +945,12 @@
.cd-mediaitem-mvview + .cd-mediaitem-mvview-overlay { .cd-mediaitem-mvview + .cd-mediaitem-mvview-overlay {
pointer-events: none; pointer-events: none;
} }
.cd-mediaitem-mvview:hover + .cd-mediaitem-mvview-overlay { .cd-mediaitem-mvview:hover + .cd-mediaitem-mvview-overlay {
opacity: 1; opacity: 1;
} }
.cd-mediaitem-mvview .artwork.round { .cd-mediaitem-mvview .artwork.round {
border-radius: var(--mediaItemRadiusRound); border-radius: var(--mediaItemRadiusRound);
} }
@ -982,10 +966,9 @@
font-size: 12px; font-size: 12px;
} }
/* mediaitem-square */ /* mediaitem-square */
.cd-mediaitem-square { .cd-mediaitem-square {
--transitionDuration: .5s; --transitionDuration: 0.5s;
--scaleRate: 1.25; --scaleRate: 1.25;
--scaleRateArtwork: 1; --scaleRateArtwork: 1;
width: calc(160px * var(--windowRelativeScale)); width: calc(160px * var(--windowRelativeScale));
@ -1062,7 +1045,6 @@
bottom: 14px; bottom: 14px;
left: 14px; left: 14px;
z-index: 2; z-index: 2;
} }
> .menu-btn { > .menu-btn {
@ -1110,7 +1092,6 @@
// } // }
// } // }
.info-rect { .info-rect {
width: 90%; width: 90%;
height: 100%; height: 100%;
@ -1119,7 +1100,6 @@
align-items: center; align-items: center;
} }
.title { .title {
width: 100%; width: 100%;
text-align: center; text-align: center;
@ -1430,7 +1410,6 @@
} }
&:hover { &:hover {
> .play-btn, > .play-btn,
> .menu-btn { > .menu-btn {
opacity: 1; opacity: 1;
@ -1438,7 +1417,6 @@
} }
} }
.title { .title {
width: 90%; width: 90%;
text-align: center; text-align: center;
@ -1471,7 +1449,6 @@
} }
} }
.listitem-horizontal { .listitem-horizontal {
.cd-mediaitem-list-item { .cd-mediaitem-list-item {
width: 350px; width: 350px;
@ -1497,7 +1474,6 @@
&:hover::-webkit-scrollbar { &:hover::-webkit-scrollbar {
display: initial; display: initial;
} }
} }
@ -1536,9 +1512,8 @@
} }
} }
/* Switch Checkbox */ /* Switch Checkbox */
input[type=checkbox][switch] { input[type="checkbox"][switch] {
width: 38px; width: 38px;
appearance: none; appearance: none;
border-radius: 32px; border-radius: 32px;
@ -1554,12 +1529,12 @@ input[type=checkbox][switch] {
margin: 0; margin: 0;
} }
input[type=checkbox][switch]:focus, input[type="checkbox"][switch]:focus,
input[type=checkbox][switch]:active { input[type="checkbox"][switch]:active {
outline: none; outline: none;
} }
input[type=checkbox][switch]:checked { input[type="checkbox"][switch]:checked {
background: var(--keyColor); background: var(--keyColor);
border: 0 solid var(--keyColor); border: 0 solid var(--keyColor);
mix-blend-mode: unset; mix-blend-mode: unset;
@ -1573,43 +1548,41 @@ input[type=checkbox][switch]:checked {
} }
} }
input[type=checkbox][switch]::before { input[type="checkbox"][switch]::before {
background: white; background: white;
width: 26px; width: 26px;
height: 26px; height: 26px;
top: -1px; top: -1px;
left: -1px; left: -1px;
position: absolute; position: absolute;
content: ' '; content: " ";
border-radius: 32px; border-radius: 32px;
transition: .10s left var(--appleEase); transition: 0.1s left var(--appleEase);
transform: scale(.75); transform: scale(0.75);
} }
input[type="checkbox"][switch]:checked::before {
input[type=checkbox][switch]:checked::before {
background: white; background: white;
top: -1px; top: -1px;
left: 13px; left: 13px;
transition: .10s left var(--appleEase); transition: 0.1s left var(--appleEase);
transform: scale(.75); transform: scale(0.75);
} }
input[type=checkbox][switch]:disabled::before { input[type="checkbox"][switch]:disabled::before {
opacity: .5; opacity: 0.5;
} }
input[type=checkbox][switch]:active::before { input[type="checkbox"][switch]:active::before {
left: 13px; left: 13px;
} }
input[type=checkbox][switch]:checked:active::before { input[type="checkbox"][switch]:checked:active::before {
left: -1px; left: -1px;
} }
/* End Switch Checkbox */ /* End Switch Checkbox */
.header-text { .header-text {
margin: 0px; margin: 0px;
} }
@ -1649,7 +1622,7 @@ input[type=checkbox][switch]:checked:active::before {
.media-item--small .text { .media-item--small .text {
font-weight: 600; font-weight: 600;
font-size: 0.90em; font-size: 0.9em;
} }
.media-item--small .subtext { .media-item--small .subtext {
@ -1684,11 +1657,11 @@ input[type=checkbox][switch]:checked:active::before {
background-repeat: no-repeat; background-repeat: no-repeat;
border-radius: 8px; border-radius: 8px;
box-shadow: inset 0px 0px 0px 1px rgb(200 200 200 / 16%), 0 8px 40px rgb(0 0 0 / 0.55); box-shadow: inset 0px 0px 0px 1px rgb(200 200 200 / 16%), 0 8px 40px rgb(0 0 0 / 0.55);
transition: transform .10s var(--appleEase); transition: transform 0.1s var(--appleEase);
} }
.media-artwork.paused { .media-artwork.paused {
transition: transform .35s var(--appleEase); transition: transform 0.35s var(--appleEase);
transform: scale(0.85); transform: scale(0.85);
} }
@ -1727,7 +1700,7 @@ input[type=checkbox][switch]:checked:active::before {
background-size: 12px; background-size: 12px;
background-position: center; background-position: center;
background-repeat: no-repeat; background-repeat: no-repeat;
opacity: 0.70; opacity: 0.7;
border-radius: 6px; border-radius: 6px;
position: relative; position: relative;
@ -1745,7 +1718,7 @@ input[type=checkbox][switch]:checked:active::before {
z-index: -1; z-index: -1;
transform: scale(0.5); transform: scale(0.5);
pointer-events: none; pointer-events: none;
transition: opacity .10s var(--appleEase), transform .10s var(--appleEase); transition: opacity 0.1s var(--appleEase), transform 0.1s var(--appleEase);
} }
&:hover { &:hover {
@ -1768,7 +1741,7 @@ input[type=checkbox][switch]:checked:active::before {
height: 32px; height: 32px;
border: 0px; border: 0px;
box-shadow: unset; box-shadow: unset;
opacity: 0.70; opacity: 0.7;
position: relative; position: relative;
&:before { &:before {
@ -1785,7 +1758,7 @@ input[type=checkbox][switch]:checked:active::before {
z-index: -1; z-index: -1;
transform: scale(0.5); transform: scale(0.5);
pointer-events: none; pointer-events: none;
transition: opacity .10s var(--appleEase), transform .10s var(--appleEase); transition: opacity 0.1s var(--appleEase), transform 0.1s var(--appleEase);
} }
&:hover { &:hover {
@ -1845,7 +1818,7 @@ input[type=checkbox][switch]:checked:active::before {
} }
.playback-button.stop { .playback-button.stop {
background-image: url('./assets/cider-icons/stop.svg'); background-image: url("./assets/cider-icons/stop.svg");
background-size: 38px; background-size: 38px;
background-position: center; background-position: center;
} }
@ -1863,25 +1836,25 @@ input[type=checkbox][switch]:checked:active::before {
} }
.playback-button.pause { .playback-button.pause {
background-image: url('./assets/cider-icons/pause.svg'); background-image: url("./assets/cider-icons/pause.svg");
background-size: 38px; background-size: 38px;
background-position: center; background-position: center;
} }
.playback-button.play { .playback-button.play {
background-image: url('./assets/cider-icons/play.svg'); background-image: url("./assets/cider-icons/play.svg");
background-size: 38px; background-size: 38px;
background-position: center; background-position: center;
} }
.playback-button.next { .playback-button.next {
background-image: url('./assets/cider-icons/forward.svg'); background-image: url("./assets/cider-icons/forward.svg");
background-size: 60%; background-size: 60%;
background-position: center; background-position: center;
} }
.playback-button.previous { .playback-button.previous {
background-image: url('./assets/cider-icons/backward.svg'); background-image: url("./assets/cider-icons/backward.svg");
background-size: 60%; background-size: 60%;
background-position: center; background-position: center;
} }
@ -1950,7 +1923,7 @@ input[type=checkbox][switch]:checked:active::before {
} }
.player-song-artist { .player-song-artist {
font-size: 1.0em; font-size: 1em;
text-align: left; text-align: left;
margin: 0 auto; margin: 0 auto;
color: var(--keyColor); color: var(--keyColor);
@ -1999,10 +1972,8 @@ input[type=checkbox][switch]:checked:active::before {
height: 40px; height: 40px;
display: flex; display: flex;
align-items: center; align-items: center;
} }
.list-entry-header { .list-entry-header {
display: flex; display: flex;
align-items: center; align-items: center;
@ -2206,7 +2177,7 @@ input[type=checkbox][switch]:checked:active::before {
position: relative; position: relative;
.nav-link { .nav-link {
transition: transform .3s var(--appleEase); transition: transform 0.3s var(--appleEase);
position: relative; position: relative;
background-color: transparent; background-color: transparent;
border: 0; border: 0;
@ -2216,7 +2187,6 @@ input[type=checkbox][switch]:checked:active::before {
font-weight: 500; font-weight: 500;
margin: 0px 4px; margin: 0px 4px;
&:after { &:after {
--dist: 1px; --dist: 1px;
content: ""; content: "";
@ -2231,10 +2201,9 @@ input[type=checkbox][switch]:checked:active::before {
border-radius: 50px; border-radius: 50px;
z-index: -1; z-index: -1;
opacity: 0; opacity: 0;
transition: background-color .5s var(--appleEase), opacity 0.25s var(--appleEase), border-radius .32s var(--appleEase); transition: background-color 0.5s var(--appleEase), opacity 0.25s var(--appleEase), border-radius 0.32s var(--appleEase);
} }
&:hover { &:hover {
outline: none; outline: none;
transform: scale(1.1); transform: scale(1.1);
@ -2245,10 +2214,7 @@ input[type=checkbox][switch]:checked:active::before {
&:after { &:after {
opacity: 1; opacity: 1;
background-color: #eee; background-color: #eee;
transition: background-color .25s var(--appleEase), transition: background-color 0.25s var(--appleEase), border-radius 0.25s var(--appleEase), color 0s var(--appleEase), opacity 0s var(--appleEase);
border-radius .25s var(--appleEase),
color .0s var(--appleEase),
opacity 0.0s var(--appleEase);
} }
} }
@ -2265,17 +2231,15 @@ input[type=checkbox][switch]:checked:active::before {
background-color: #eee; background-color: #eee;
} }
} }
} }
&:hover { &:hover {
.nav-link.active { .nav-link.active {
outline: none; outline: none;
transform: scale(1.0); transform: scale(1);
background: transparent; background: transparent;
color: #eee; color: #eee;
transform: scale(1.0); transform: scale(1);
&:after { &:after {
background: rgb(200 200 200 / 15%); background: rgb(200 200 200 / 15%);
@ -2300,7 +2264,7 @@ input[type=checkbox][switch]:checked:active::before {
} }
&:after { &:after {
content: ''; content: "";
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
@ -2402,5 +2366,4 @@ input[type=checkbox][switch]:checked:active::before {
.md-input-number { .md-input-number {
min-width: 12em; min-width: 12em;
} }
} }

View file

@ -29,7 +29,6 @@
height: 100%; height: 100%;
.fs-search { .fs-search {
.search-input--icon { .search-input--icon {
width: 4em; width: 4em;
background-size: 40%; background-size: 40%;
@ -38,7 +37,7 @@
input { input {
padding-left: 2em; padding-left: 2em;
font-size: 2em; font-size: 2em;
border-radius: var(--mediaItemRadius) border-radius: var(--mediaItemRadius);
} }
} }
} }
@ -64,7 +63,6 @@
width: 90%; width: 90%;
backdrop-filter: var(--glassFilter); backdrop-filter: var(--glassFilter);
.app-sidebar-item { .app-sidebar-item {
background-color: #1e1e1e00; background-color: #1e1e1e00;
border-radius: 10px !important; border-radius: 10px !important;
@ -81,7 +79,7 @@
&:before { &:before {
--dist: 1px; --dist: 1px;
content : ''; content: "";
position: absolute; position: absolute;
top: var(--dist); top: var(--dist);
left: var(--dist); left: var(--dist);
@ -103,7 +101,7 @@
&:before { &:before {
transition: transform 0.1s ease-in-out, opacity 0.1s ease-in-out; transition: transform 0.1s ease-in-out, opacity 0.1s ease-in-out;
opacity : .1; opacity: 0.1;
transform: scale(1); transform: scale(1);
} }
} }
@ -112,7 +110,7 @@
background-color: transparent; background-color: transparent;
&:before { &:before {
opacity : .2; opacity: 0.2;
transform: scale(1); transform: scale(1);
} }
} }
@ -164,7 +162,7 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
width: 100% width: 100%;
} }
.volume-button--small { .volume-button--small {
@ -178,7 +176,7 @@
width: 30px; width: 30px;
border: 0px; border: 0px;
box-shadow: unset; box-shadow: unset;
opacity: 0.70; opacity: 0.7;
background-image: url("./assets/feather/volume-2.svg"); background-image: url("./assets/feather/volume-2.svg");
} }
@ -190,7 +188,7 @@
background-image: url("./assets/feather/volume.svg"); background-image: url("./assets/feather/volume.svg");
} }
input[type=range] { input[type="range"] {
-webkit-appearance: none; -webkit-appearance: none;
height: 4px; height: 4px;
background: rgba(255, 255, 255, 0.4); background: rgba(255, 255, 255, 0.4);
@ -228,7 +226,6 @@
} }
} }
.background { .background {
position: absolute; position: absolute;
background-size: cover; background-size: cover;
@ -247,13 +244,10 @@
.bg-artwork-container .bg-artwork { .bg-artwork-container .bg-artwork {
filter: brightness(85%) saturate(95%) blur(180px) contrast(0.9) opacity(0.9); filter: brightness(85%) saturate(95%) blur(180px) contrast(0.9) opacity(0.9);
} }
} }
} }
.lyrics-col { .lyrics-col {
height: 62vh; height: 62vh;
display: flex; display: flex;
justify-content: center; justify-content: center;
@ -278,11 +272,9 @@
.lyric-line { .lyric-line {
font-size: 35px; font-size: 35px;
} }
} }
.queue-col { .queue-col {
width: 60vh; width: 60vh;
height: 62vh; height: 62vh;
@ -361,7 +353,8 @@
} }
.app-playback-controls { .app-playback-controls {
.song-artist, .song-name { .song-artist,
.song-name {
font-weight: 600; font-weight: 600;
text-align: center; text-align: center;
font-size: 0.9em; font-size: 0.9em;
@ -414,8 +407,6 @@
width: 100%; width: 100%;
text-align: center; text-align: center;
} }
} }
.app-playback-controls .song-progress { .app-playback-controls .song-progress {
@ -436,7 +427,7 @@
} }
&:hover { &:hover {
> input[type=range] { > input[type="range"] {
&::-webkit-slider-thumb { &::-webkit-slider-thumb {
opacity: 1; opacity: 1;
transform: scale(1); transform: scale(1);
@ -445,7 +436,7 @@
} }
} }
input[type=range] { input[type="range"] {
appearance: none; appearance: none;
width: 100%; width: 100%;
height: 4px; height: 4px;
@ -462,7 +453,7 @@
border-radius: 100%; border-radius: 100%;
background: var(--songProgressColor); background: var(--songProgressColor);
cursor: default; cursor: default;
transition: opacity .10s var(--appleEase), transform .10s var(--appleEase); transition: opacity 0.1s var(--appleEase), transform 0.1s var(--appleEase);
} }
&::-moz-range-thumb { &::-moz-range-thumb {
@ -482,7 +473,6 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
} }
.cd-mediaitem-square { .cd-mediaitem-square {

View file

@ -156,8 +156,7 @@
} }
.close-btn { .close-btn {
.menu-panel.menu-header-text.close-btn .menu-panel.menu-header-text.close-btn;
} }
} }
} }
@ -180,7 +179,7 @@
} }
.close-btn { .close-btn {
.menu-panel.menu-header-text.close-btn .menu-panel.menu-header-text.close-btn;
} }
} }
@ -294,7 +293,6 @@
overflow: hidden; overflow: hidden;
font-size: 13px; font-size: 13px;
.menu-option { .menu-option {
text-align: left; text-align: left;
display: flex; display: flex;
@ -323,7 +321,7 @@
opacity: 0; opacity: 0;
transform: scale(0.98); transform: scale(0.98);
z-index: -1; z-index: -1;
transition: transform .25s ease-out, opacity .25s ease-out; transition: transform 0.25s ease-out, opacity 0.25s ease-out;
} }
&:hover { &:hover {
@ -336,7 +334,7 @@
&:active { &:active {
&::before { &::before {
transition: transform .1s ease-in-out, opacity .1s ease-in-out; transition: transform 0.1s ease-in-out, opacity 0.1s ease-in-out;
opacity: 1; opacity: 1;
transform: scale(0.98); transform: scale(0.98);
background: var(--selected-click); background: var(--selected-click);
@ -372,7 +370,7 @@
} }
&:hover { &:hover {
background-color: rgb(196, 43, 28) background-color: rgb(196, 43, 28);
} }
} }
} }
@ -430,7 +428,6 @@
} }
} }
.moreinfo-modal { .moreinfo-modal {
.modal-window { .modal-window {
height: 70%; height: 70%;
@ -496,7 +493,8 @@
font-weight: 600; font-weight: 600;
} }
.song-artist, .song-album { .song-artist,
.song-album {
opacity: 0.75; opacity: 0.75;
cursor: pointer; cursor: pointer;

View file

@ -24,7 +24,7 @@ body[platform="linux"] {
height: 36px !important; height: 36px !important;
width: 36px !important; width: 36px !important;
border-radius: 50px; border-radius: 50px;
transition: background-color .1s ease-in-out; transition: background-color 0.1s ease-in-out;
&:hover { &:hover {
background: rgb(200 200 200 / 10%) !important; background: rgb(200 200 200 / 10%) !important;

View file

@ -44,7 +44,7 @@ body[platform="darwin"] {
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
box-shadow: inset 0px 0px .5px 1px rgb(200 200 200 / 40%); box-shadow: inset 0px 0px 0.5px 1px rgb(200 200 200 / 40%);
border-radius: 10px; border-radius: 10px;
content: " "; content: " ";
z-index: 999999; z-index: 999999;
@ -67,7 +67,7 @@ body[platform="darwin"] {
.settings-window.maxed { .settings-window.maxed {
.tabs > .col-auto { .tabs > .col-auto {
transition: padding-top .3s linear; transition: padding-top 0.3s linear;
padding-top: var(--chromeHeight1); padding-top: var(--chromeHeight1);
} }
} }

View file

@ -1,7 +1,4 @@
#app.macosemu { #app.macosemu {
.app-chrome .app-chrome-item > .window-controls-macos { .app-chrome .app-chrome-item > .window-controls-macos {
@controlSize: 12px; @controlSize: 12px;
display: flex; display: flex;

View file

@ -65,7 +65,7 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
width: 100% width: 100%;
} }
.volume-button--small { .volume-button--small {
@ -79,7 +79,7 @@
width: 30px; width: 30px;
border: 0px; border: 0px;
box-shadow: unset; box-shadow: unset;
opacity: 0.70; opacity: 0.7;
background-image: url("./assets/feather/volume-2.svg"); background-image: url("./assets/feather/volume-2.svg");
} }
@ -91,7 +91,7 @@
background-image: url("./assets/feather/volume.svg"); background-image: url("./assets/feather/volume.svg");
} }
input[type=range] { input[type="range"] {
-webkit-appearance: none; -webkit-appearance: none;
height: 4px; height: 4px;
background: rgba(255, 255, 255, 0.4); background: rgba(255, 255, 255, 0.4);
@ -129,7 +129,6 @@
} }
} }
.background { .background {
position: absolute; position: absolute;
background-size: cover; background-size: cover;
@ -157,9 +156,7 @@
} }
} }
.lyrics-col { .lyrics-col {
height: 62vh; height: 62vh;
display: flex; display: flex;
justify-content: center; justify-content: center;
@ -184,11 +181,9 @@
.lyric-line { .lyric-line {
font-size: 35px; font-size: 35px;
} }
} }
.queue-col { .queue-col {
width: 60vh; width: 60vh;
height: 50vh; height: 50vh;
@ -281,11 +276,11 @@
} }
} }
.app-playback-controls { .app-playback-controls {
-webkit-app-region: no-drag; -webkit-app-region: no-drag;
.song-artist, .song-name { .song-artist,
.song-name {
font-weight: 600; font-weight: 600;
text-align: center; text-align: center;
font-size: 0.9em; font-size: 0.9em;
@ -338,8 +333,6 @@
width: 100%; width: 100%;
text-align: center; text-align: center;
} }
} }
.app-playback-controls .song-progress { .app-playback-controls .song-progress {
@ -360,7 +353,7 @@
} }
&:hover { &:hover {
> input[type=range] { > input[type="range"] {
&::-webkit-slider-thumb { &::-webkit-slider-thumb {
opacity: 1; opacity: 1;
transform: scale(1); transform: scale(1);
@ -369,7 +362,7 @@
} }
} }
input[type=range] { input[type="range"] {
appearance: none; appearance: none;
width: 100%; width: 100%;
height: 4px; height: 4px;
@ -386,7 +379,7 @@
border-radius: 100%; border-radius: 100%;
background: var(--songProgressColor); background: var(--songProgressColor);
cursor: default; cursor: default;
transition: opacity .10s var(--appleEase), transform .10s var(--appleEase); transition: opacity 0.1s var(--appleEase), transform 0.1s var(--appleEase);
} }
&::-moz-range-thumb { &::-moz-range-thumb {
@ -405,6 +398,5 @@
width: 100%; width: 100%;
justify-content: center; justify-content: center;
} }
} }
} }

View file

@ -1,116 +1,116 @@
@-webkit-keyframes notyf-fadeinup { @-webkit-keyframes notyf-fadeinup {
0% { 0% {
opacity: 0; opacity: 0;
transform: translateY(25%) transform: translateY(25%);
} }
to { to {
opacity: 1; opacity: 1;
transform: translateY(0) transform: translateY(0);
} }
} }
@keyframes notyf-fadeinup { @keyframes notyf-fadeinup {
0% { 0% {
opacity: 0; opacity: 0;
transform: translateY(25%) transform: translateY(25%);
} }
to { to {
opacity: 1; opacity: 1;
transform: translateY(0) transform: translateY(0);
} }
} }
@-webkit-keyframes notyf-fadeinleft { @-webkit-keyframes notyf-fadeinleft {
0% { 0% {
opacity: 0; opacity: 0;
transform: translateX(25%) transform: translateX(25%);
} }
to { to {
opacity: 1; opacity: 1;
transform: translateX(0) transform: translateX(0);
} }
} }
@keyframes notyf-fadeinleft { @keyframes notyf-fadeinleft {
0% { 0% {
opacity: 0; opacity: 0;
transform: translateX(25%) transform: translateX(25%);
} }
to { to {
opacity: 1; opacity: 1;
transform: translateX(0) transform: translateX(0);
} }
} }
@-webkit-keyframes notyf-fadeoutright { @-webkit-keyframes notyf-fadeoutright {
0% { 0% {
opacity: 1; opacity: 1;
transform: translateX(0) transform: translateX(0);
} }
to { to {
opacity: 0; opacity: 0;
transform: translateX(25%) transform: translateX(25%);
} }
} }
@keyframes notyf-fadeoutright { @keyframes notyf-fadeoutright {
0% { 0% {
opacity: 1; opacity: 1;
transform: translateX(0) transform: translateX(0);
} }
to { to {
opacity: 0; opacity: 0;
transform: translateX(25%) transform: translateX(25%);
} }
} }
@-webkit-keyframes notyf-fadeoutdown { @-webkit-keyframes notyf-fadeoutdown {
0% { 0% {
opacity: 1; opacity: 1;
transform: translateY(0) transform: translateY(0);
} }
to { to {
opacity: 0; opacity: 0;
transform: translateY(25%) transform: translateY(25%);
} }
} }
@keyframes notyf-fadeoutdown { @keyframes notyf-fadeoutdown {
0% { 0% {
opacity: 1; opacity: 1;
transform: translateY(0) transform: translateY(0);
} }
to { to {
opacity: 0; opacity: 0;
transform: translateY(25%) transform: translateY(25%);
} }
} }
@-webkit-keyframes ripple { @-webkit-keyframes ripple {
0% { 0% {
transform: scale(0) translateY(-45%) translateX(13%) transform: scale(0) translateY(-45%) translateX(13%);
} }
to { to {
transform: scale(1) translateY(-45%) translateX(13%) transform: scale(1) translateY(-45%) translateX(13%);
} }
} }
@keyframes ripple { @keyframes ripple {
0% { 0% {
transform: scale(0) translateY(-45%) translateX(13%) transform: scale(0) translateY(-45%) translateX(13%);
} }
to { to {
transform: scale(1) translateY(-45%) translateX(13%) transform: scale(1) translateY(-45%) translateX(13%);
} }
} }
@ -128,7 +128,7 @@
justify-content: flex-end; justify-content: flex-end;
pointer-events: none; pointer-events: none;
box-sizing: border-box; box-sizing: border-box;
padding: 20px padding: 20px;
} }
.notyf__icon--error, .notyf__icon--error,
@ -139,7 +139,7 @@
border-radius: 50%; border-radius: 50%;
display: block; display: block;
margin: 0 auto; margin: 0 auto;
position: relative position: relative;
} }
.notyf__icon--error:after, .notyf__icon--error:after,
@ -152,15 +152,15 @@
border-radius: 3px; border-radius: 3px;
left: 9px; left: 9px;
height: 12px; height: 12px;
top: 5px top: 5px;
} }
.notyf__icon--error:after { .notyf__icon--error:after {
transform: rotate(-45deg) transform: rotate(-45deg);
} }
.notyf__icon--error:before { .notyf__icon--error:before {
transform: rotate(45deg) transform: rotate(45deg);
} }
.notyf__icon--success:after, .notyf__icon--success:after,
@ -170,77 +170,77 @@
display: block; display: block;
position: absolute; position: absolute;
width: 3px; width: 3px;
border-radius: 3px border-radius: 3px;
} }
.notyf__icon--success:after { .notyf__icon--success:after {
height: 6px; height: 6px;
transform: rotate(-45deg); transform: rotate(-45deg);
top: 9px; top: 9px;
left: 6px left: 6px;
} }
.notyf__icon--success:before { .notyf__icon--success:before {
height: 11px; height: 11px;
transform: rotate(45deg); transform: rotate(45deg);
top: 5px; top: 5px;
left: 10px left: 10px;
} }
.notyf__toast { .notyf__toast {
display: block; display: block;
overflow: hidden; overflow: hidden;
pointer-events: auto; pointer-events: auto;
-webkit-animation: notyf-fadeinup .3s ease-in forwards; -webkit-animation: notyf-fadeinup 0.3s ease-in forwards;
animation: notyf-fadeinup .3s ease-in forwards; animation: notyf-fadeinup 0.3s ease-in forwards;
box-shadow: 0 3px 7px 0 rgba(0, 0, 0, .25); box-shadow: 0 3px 7px 0 rgba(0, 0, 0, 0.25);
position: relative; position: relative;
padding: 0 15px; padding: 0 15px;
border-radius: 2px; border-radius: 2px;
max-width: 300px; max-width: 300px;
transform: translateY(25%); transform: translateY(25%);
box-sizing: border-box; box-sizing: border-box;
flex-shrink: 0 flex-shrink: 0;
} }
.notyf__toast--disappear { .notyf__toast--disappear {
transform: translateY(0); transform: translateY(0);
-webkit-animation: notyf-fadeoutdown .3s forwards; -webkit-animation: notyf-fadeoutdown 0.3s forwards;
animation: notyf-fadeoutdown .3s forwards; animation: notyf-fadeoutdown 0.3s forwards;
-webkit-animation-delay: .25s; -webkit-animation-delay: 0.25s;
animation-delay: .25s animation-delay: 0.25s;
} }
.notyf__toast--disappear .notyf__icon, .notyf__toast--disappear .notyf__icon,
.notyf__toast--disappear .notyf__message { .notyf__toast--disappear .notyf__message {
-webkit-animation: notyf-fadeoutdown .3s forwards; -webkit-animation: notyf-fadeoutdown 0.3s forwards;
animation: notyf-fadeoutdown .3s forwards; animation: notyf-fadeoutdown 0.3s forwards;
opacity: 1; opacity: 1;
transform: translateY(0) transform: translateY(0);
} }
.notyf__toast--disappear .notyf__dismiss { .notyf__toast--disappear .notyf__dismiss {
-webkit-animation: notyf-fadeoutright .3s forwards; -webkit-animation: notyf-fadeoutright 0.3s forwards;
animation: notyf-fadeoutright .3s forwards; animation: notyf-fadeoutright 0.3s forwards;
opacity: 1; opacity: 1;
transform: translateX(0) transform: translateX(0);
} }
.notyf__toast--disappear .notyf__message { .notyf__toast--disappear .notyf__message {
-webkit-animation-delay: .05s; -webkit-animation-delay: 0.05s;
animation-delay: .05s animation-delay: 0.05s;
} }
.notyf__toast--upper { .notyf__toast--upper {
margin-bottom: 20px margin-bottom: 20px;
} }
.notyf__toast--lower { .notyf__toast--lower {
margin-top: 20px margin-top: 20px;
} }
.notyf__toast--dismissible .notyf__wrapper { .notyf__toast--dismissible .notyf__wrapper {
padding-right: 30px padding-right: 30px;
} }
.notyf__ripple { .notyf__ripple {
@ -253,8 +253,8 @@
border-radius: 50%; border-radius: 50%;
transform: scale(0) translateY(-51%) translateX(13%); transform: scale(0) translateY(-51%) translateX(13%);
z-index: 5; z-index: 5;
-webkit-animation: ripple .4s ease-out forwards; -webkit-animation: ripple 0.4s ease-out forwards;
animation: ripple .4s ease-out forwards animation: ripple 0.4s ease-out forwards;
} }
.notyf__wrapper { .notyf__wrapper {
@ -265,7 +265,7 @@
padding-right: 15px; padding-right: 15px;
border-radius: 3px; border-radius: 3px;
position: relative; position: relative;
z-index: 10 z-index: 10;
} }
.notyf__icon { .notyf__icon {
@ -273,11 +273,11 @@
text-align: center; text-align: center;
font-size: 1.3em; font-size: 1.3em;
opacity: 0; opacity: 0;
-webkit-animation: notyf-fadeinup .3s forwards; -webkit-animation: notyf-fadeinup 0.3s forwards;
animation: notyf-fadeinup .3s forwards; animation: notyf-fadeinup 0.3s forwards;
-webkit-animation-delay: .3s; -webkit-animation-delay: 0.3s;
animation-delay: .3s; animation-delay: 0.3s;
margin-right: 13px margin-right: 13px;
} }
.notyf__dismiss { .notyf__dismiss {
@ -287,22 +287,22 @@
height: 100%; height: 100%;
width: 26px; width: 26px;
margin-right: -15px; margin-right: -15px;
-webkit-animation: notyf-fadeinleft .3s forwards; -webkit-animation: notyf-fadeinleft 0.3s forwards;
animation: notyf-fadeinleft .3s forwards; animation: notyf-fadeinleft 0.3s forwards;
-webkit-animation-delay: .35s; -webkit-animation-delay: 0.35s;
animation-delay: .35s; animation-delay: 0.35s;
opacity: 0 opacity: 0;
} }
.notyf__dismiss-btn { .notyf__dismiss-btn {
background-color: rgba(0, 0, 0, .25); background-color: rgba(0, 0, 0, 0.25);
border: none; border: none;
cursor: pointer; cursor: pointer;
transition: opacity .2s ease, background-color .2s ease; transition: opacity 0.2s ease, background-color 0.2s ease;
outline: none; outline: none;
opacity: .35; opacity: 0.35;
height: 100%; height: 100%;
width: 100% width: 100%;
} }
.notyf__dismiss-btn:after, .notyf__dismiss-btn:after,
@ -314,57 +314,57 @@
border-radius: 3px; border-radius: 3px;
position: absolute; position: absolute;
left: calc(50% - 1px); left: calc(50% - 1px);
top: calc(50% - 5px) top: calc(50% - 5px);
} }
.notyf__dismiss-btn:after { .notyf__dismiss-btn:after {
transform: rotate(-45deg) transform: rotate(-45deg);
} }
.notyf__dismiss-btn:before { .notyf__dismiss-btn:before {
transform: rotate(45deg) transform: rotate(45deg);
} }
.notyf__dismiss-btn:hover { .notyf__dismiss-btn:hover {
opacity: .7; opacity: 0.7;
background-color: rgba(0, 0, 0, .15) background-color: rgba(0, 0, 0, 0.15);
} }
.notyf__dismiss-btn:active { .notyf__dismiss-btn:active {
opacity: .8 opacity: 0.8;
} }
.notyf__message { .notyf__message {
vertical-align: middle; vertical-align: middle;
position: relative; position: relative;
opacity: 0; opacity: 0;
-webkit-animation: notyf-fadeinup .3s forwards; -webkit-animation: notyf-fadeinup 0.3s forwards;
animation: notyf-fadeinup .3s forwards; animation: notyf-fadeinup 0.3s forwards;
-webkit-animation-delay: .25s; -webkit-animation-delay: 0.25s;
animation-delay: .25s; animation-delay: 0.25s;
line-height: 1.5em line-height: 1.5em;
} }
@media only screen and (max-width: 480px) { @media only screen and (max-width: 480px) {
.notyf { .notyf {
padding: 0 padding: 0;
} }
.notyf__ripple { .notyf__ripple {
height: 600px; height: 600px;
width: 600px; width: 600px;
-webkit-animation-duration: .5s; -webkit-animation-duration: 0.5s;
animation-duration: .5s animation-duration: 0.5s;
} }
.notyf__toast { .notyf__toast {
max-width: none; max-width: none;
border-radius: 0; border-radius: 0;
box-shadow: 0 -2px 7px 0 rgba(0, 0, 0, .13); box-shadow: 0 -2px 7px 0 rgba(0, 0, 0, 0.13);
width: 100% width: 100%;
} }
.notyf__dismiss { .notyf__dismiss {
width: 56px width: 56px;
} }
} }

View file

@ -1277,7 +1277,7 @@
} }
.audiolabs-page .spprofile-line .spprofile-viewport .spprev:before, .audiolabs-page .spprofile-line .spprofile-viewport .spprev:before,
.audiolabs-page .spprofile-line .spprofile-viewport .nextprev:before { .audiolabs-page .spprofile-line .spprofile-viewport .nextprev:before {
content: ''; content: "";
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
@ -1309,7 +1309,7 @@
background: black; background: black;
} }
.audiolabs-page .spprofile-line .spprofile-viewport .spslide > img { .audiolabs-page .spprofile-line .spprofile-viewport .spslide > img {
WIDTH: 100%; width: 100%;
height: 100%; height: 100%;
object-fit: cover; object-fit: cover;
} }

View file

@ -242,7 +242,6 @@
} }
.list-group-item { .list-group-item {
&:hover { &:hover {
cursor: grab; cursor: grab;
} }
@ -294,7 +293,6 @@
// Search Page // Search Page
&.search-page { &.search-page {
.searchToggle { .searchToggle {
float: right; float: right;
@ -452,7 +450,7 @@
right: 0; right: 0;
&:hover { &:hover {
background-color: rgb(196, 43, 28) background-color: rgb(196, 43, 28);
} }
} }
@ -488,10 +486,7 @@
display: block; display: block;
line-break: anywhere; line-break: anywhere;
} }
} }
} }
// Podcast Page // Podcast Page
@ -622,7 +617,7 @@
right: 0; right: 0;
&:hover { &:hover {
background-color: rgb(196, 43, 28) background-color: rgb(196, 43, 28);
} }
} }
@ -657,10 +652,7 @@
display: block; display: block;
line-break: anywhere; line-break: anywhere;
} }
} }
} }
@media only screen and (max-width: 1230px) { @media only screen and (max-width: 1230px) {
@ -828,7 +820,7 @@
margin-bottom: -10px; margin-bottom: -10px;
padding: 0; padding: 0;
-webkit-mask-image: radial-gradient(at top left, black, transparent 70%), radial-gradient(at top right, black, transparent 70%), linear-gradient(180deg, rgb(200 200 200), transparent 98%); -webkit-mask-image: radial-gradient(at top left, black, transparent 70%), radial-gradient(at top right, black, transparent 70%), linear-gradient(180deg, rgb(200 200 200), transparent 98%);
opacity: .7; opacity: 0.7;
animation: playlistArtworkFadeIn 1s var(--appleEase); animation: playlistArtworkFadeIn 1s var(--appleEase);
.artworkMaterial img { .artworkMaterial img {
@ -898,7 +890,7 @@
} }
.search-input::placeholder { .search-input::placeholder {
color: var(--heroplaceholdercolor) color: var(--heroplaceholdercolor);
} }
.nameEdit { .nameEdit {
@ -939,7 +931,7 @@
} }
.playlist-desc { .playlist-desc {
transition: height .2s ease-in-out, opacity .2s ease-in-out; transition: height 0.2s ease-in-out, opacity 0.2s ease-in-out;
box-sizing: border-box; box-sizing: border-box;
font-size: 14px; font-size: 14px;
flex-shrink: unset; flex-shrink: unset;
@ -1043,8 +1035,6 @@
} }
} }
} }
} }
.friends-info { .friends-info {
@ -1061,7 +1051,7 @@
border-radius: 100%; border-radius: 100%;
overflow: hidden; overflow: hidden;
box-shadow: var(--mediaItemShadow-ShadowSubtle); box-shadow: var(--mediaItemShadow-ShadowSubtle);
transition: transform .2s var(--appleEase); transition: transform 0.2s var(--appleEase);
margin: 6px; margin: 6px;
&:hover { &:hover {
@ -1081,7 +1071,7 @@
font-size: 0.9em; font-size: 0.9em;
margin: 6px; margin: 6px;
opacity: 0.7; opacity: 0.7;
transition: height .2s ease-in-out, opacity .2s ease-in-out; transition: height 0.2s ease-in-out, opacity 0.2s ease-in-out;
height: 0.9em; height: 0.9em;
} }
@ -1151,13 +1141,13 @@
} }
.playlist-time { .playlist-time {
transition: height .2s ease-in-out, opacity .2s ease-in-out; transition: height 0.2s ease-in-out, opacity 0.2s ease-in-out;
height: 0px; height: 0px;
opacity: 0; opacity: 0;
} }
.playlist-desc { .playlist-desc {
transition: height .2s ease-in-out, opacity .2s ease-in-out; transition: height 0.2s ease-in-out, opacity 0.2s ease-in-out;
height: 0px !important; height: 0px !important;
opacity: 0; opacity: 0;
} }
@ -1272,7 +1262,6 @@
} }
} }
.artworkContainer { .artworkContainer {
position: absolute; position: absolute;
top: 0; top: 0;
@ -1282,7 +1271,7 @@
margin: 0; margin: 0;
padding: 0; padding: 0;
-webkit-mask-image: radial-gradient(at top left, black, transparent 70%), radial-gradient(at top right, black, transparent 70%), linear-gradient(180deg, rgb(200 200 200), transparent 98%); -webkit-mask-image: radial-gradient(at top left, black, transparent 70%), radial-gradient(at top right, black, transparent 70%), linear-gradient(180deg, rgb(200 200 200), transparent 98%);
opacity: .7; opacity: 0.7;
animation: playlistArtworkFadeIn 1s var(--appleEase); animation: playlistArtworkFadeIn 1s var(--appleEase);
.artworkMaterial img { .artworkMaterial img {
@ -1412,7 +1401,6 @@
} }
.artist-title { .artist-title {
.artist-play { .artist-play {
transform: translateY(3px); transform: translateY(3px);
margin: 14px; margin: 14px;
@ -1490,7 +1478,6 @@
width: 90%; width: 90%;
margin: 16px auto 0px; margin: 16px auto 0px;
} }
} }
// AudioLabs page // AudioLabs page
@ -1502,7 +1489,7 @@
border-bottom: unset; border-bottom: unset;
border-top: unset; border-top: unset;
font-weight: 600; font-weight: 600;
font-size: 1.0em; font-size: 1em;
background: rgb(255 255 255 / 3%); background: rgb(255 255 255 / 3%);
} }
@ -1548,7 +1535,7 @@
} }
&:before { &:before {
content: ''; content: "";
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
@ -1568,7 +1555,6 @@
&:before { &:before {
-webkit-mask-image: url("./views/svg/chevron-left.svg"); -webkit-mask-image: url("./views/svg/chevron-left.svg");
} }
} }
.nextprev { .nextprev {
@ -1577,7 +1563,6 @@
&:before { &:before {
-webkit-mask-image: url("./views/svg/chevron-right.svg"); -webkit-mask-image: url("./views/svg/chevron-right.svg");
} }
} }
.spslide { .spslide {
@ -1588,7 +1573,7 @@
background: black; background: black;
> img { > img {
WIDTH: 100%; width: 100%;
height: 100%; height: 100%;
object-fit: cover; object-fit: cover;
} }
@ -1646,7 +1631,6 @@
//Home //Home
.home-page { .home-page {
.md-btn-replay { .md-btn-replay {
background-image: linear-gradient(-45deg, #2e2173, #925042); background-image: linear-gradient(-45deg, #2e2173, #925042);
animation: gradient-animation 5s ease-in-out infinite; animation: gradient-animation 5s ease-in-out infinite;
@ -1738,8 +1722,8 @@
border-radius: var(--mediaItemRadius); border-radius: var(--mediaItemRadius);
overflow: hidden; overflow: hidden;
cursor: pointer; cursor: pointer;
transition: transform .2s var(--appleEase); transition: transform 0.2s var(--appleEase);
transition-delay: .1s; transition-delay: 0.1s;
align-self: center; align-self: center;
&:hover { &:hover {
@ -1780,7 +1764,6 @@
} }
.top-genres-container { .top-genres-container {
.genre-name { .genre-name {
font-size: 0.9em; font-size: 0.9em;
margin: 6px 0px; margin: 6px 0px;
@ -1810,11 +1793,11 @@
.cd-mediaitem-square { .cd-mediaitem-square {
.mediaitem-artwork { .mediaitem-artwork {
animation: replayFadeIn .5s var(--appleEase); animation: replayFadeIn 0.5s var(--appleEase);
} }
transition: transform .2s var(--appleEase); transition: transform 0.2s var(--appleEase);
transition-delay: .1s; transition-delay: 0.1s;
&:hover { &:hover {
transform: scale(1.1); transform: scale(1.1);
@ -1883,7 +1866,6 @@
height: 100%; height: 100%;
width: 100%; width: 100%;
.oobe-header { .oobe-header {
font-size: 3em; font-size: 3em;
text-shadow: var(--replayTextShadow); text-shadow: var(--replayTextShadow);
@ -1927,7 +1909,7 @@
.visualPreview { .visualPreview {
pointer-events: none; pointer-events: none;
transition: .25s all; transition: 0.25s all;
width: 100%; width: 100%;
} }
@ -1954,9 +1936,8 @@
outline: 4px solid var(--keyColor); outline: 4px solid var(--keyColor);
} }
&:hover { &:hover {
transform: scale(1.10) translateZ(-1px) translateY(10px); transform: scale(1.1) translateZ(-1px) translateY(10px);
z-index: 1; z-index: 1;
box-shadow: 0px 12px 16px rgb(0 0 0 / 25%); box-shadow: 0px 12px 16px rgb(0 0 0 / 25%);
} }
@ -1981,10 +1962,8 @@
text-align: center; text-align: center;
} }
} }
} }
.oobe-titlebar { .oobe-titlebar {
position: absolute; position: absolute;
top: 0; top: 0;
@ -2125,7 +2104,6 @@
.nav-pills { .nav-pills {
gap: 6px; gap: 6px;
} }
.nav-pills .nav-link { .nav-pills .nav-link {
@ -2139,7 +2117,6 @@
} }
} }
.md-option-header { .md-option-header {
padding: 0px 26px; padding: 0px 26px;
border-bottom: unset; border-bottom: unset;
@ -2192,7 +2169,7 @@
} }
&:hover { &:hover {
background-color: rgb(196, 43, 28) background-color: rgb(196, 43, 28);
} }
&.back-btn { &.back-btn {
@ -2246,7 +2223,8 @@
border-left: 1px solid var(--borderColor); border-left: 1px solid var(--borderColor);
} }
.github-themes-page, .installed-themes-page { .github-themes-page,
.installed-themes-page {
.header-text { .header-text {
font-size: 1.25em; font-size: 1.25em;
} }

View file

@ -1,23 +1,22 @@
import { app } from "./vueapp.js" import { app } from "./vueapp.js";
import {CiderCache} from './cidercache.js' import { CiderCache } from "./cidercache.js";
import {CiderFrontAPI} from './ciderfrontapi.js' import { CiderFrontAPI } from "./ciderfrontapi.js";
import {simulateGamepad} from './gamepad.js' import { simulateGamepad } from "./gamepad.js";
import {CiderAudio} from '../audio/cideraudio.js' import { CiderAudio } from "../audio/cideraudio.js";
import {Events} from './events.js' import { Events } from "./events.js";
import { wsapi } from "./wsapi_interop.js" import { wsapi } from "./wsapi_interop.js";
import { MusicKitTools } from "./musickittools.js" import { MusicKitTools } from "./musickittools.js";
import { spawnMica } from "./mica.js" import { spawnMica } from "./mica.js";
import { svgIcon } from './components/svg-icon.js' import { svgIcon } from "./components/svg-icon.js";
import { sidebarLibraryItem } from './components/sidebar-library-item.js' import { sidebarLibraryItem } from "./components/sidebar-library-item.js";
// Define window objects // Define window objects
window.app = app window.app = app;
window.MusicKitTools = MusicKitTools window.MusicKitTools = MusicKitTools;
window.CiderAudio = CiderAudio window.CiderAudio = CiderAudio;
window.CiderCache = CiderCache window.CiderCache = CiderCache;
window.CiderFrontAPI = CiderFrontAPI window.CiderFrontAPI = CiderFrontAPI;
window.wsapi = wsapi window.wsapi = wsapi;
if (app.cfg.advanced.disableLogging === true) { if (app.cfg.advanced.disableLogging === true) {
window.console = { window.console = {
@ -25,13 +24,12 @@ if (app.cfg.advanced.disableLogging === true) {
error: function () {}, error: function () {},
warn: function () {}, warn: function () {},
assert: function () {}, assert: function () {},
debug: function() {} debug: function () {},
};
} }
}
// Mount Vue to #app // Mount Vue to #app
app.$mount("#app") app.$mount("#app");
// Init CiderAudio and force audiocontext // Init CiderAudio and force audiocontext
if (app.cfg.advanced.AudioContext != true) { if (app.cfg.advanced.AudioContext != true) {
@ -39,10 +37,10 @@ if (app.cfg.advanced.AudioContext != true) {
window.location.reload(); window.location.reload();
} }
CiderAudio.init() CiderAudio.init();
// Import gamepad support // Import gamepad support
app.simulateGamepad = simulateGamepad app.simulateGamepad = simulateGamepad;
app.spawnMica = spawnMica app.spawnMica = spawnMica;
Events.InitEvents() Events.InitEvents();

View file

@ -1,24 +1,24 @@
const CiderCache = { const CiderCache = {
async getCache(file) { async getCache(file) {
let cache = await ipcRenderer.sendSync("get-cache", file) let cache = await ipcRenderer.sendSync("get-cache", file);
if (isJson(cache)) { if (isJson(cache)) {
cache = JSON.parse(cache) cache = JSON.parse(cache);
if (Object.keys(cache).length === 0) { if (Object.keys(cache).length === 0) {
cache = false cache = false;
} }
} else { } else {
cache = false cache = false;
} }
return cache return cache;
}, },
async putCache(file, data) { async putCache(file, data) {
console.log(`Caching ${file}`) console.log(`Caching ${file}`);
ipcRenderer.invoke("put-cache", { ipcRenderer.invoke("put-cache", {
file: file, file: file,
data: JSON.stringify(data) data: JSON.stringify(data),
}) });
return true return true;
} },
} };
export {CiderCache} export { CiderCache };

View file

@ -1,32 +1,31 @@
const CiderFrontAPI = { const CiderFrontAPI = {
Objects: { Objects: {
MenuEntry: function () { MenuEntry: function () {
this.id = "" this.id = "";
this.name = "" this.name = "";
this.onClick = () => { this.onClick = () => {};
} },
}
}, },
AddMenuEntry(entry) { AddMenuEntry(entry) {
app.pluginMenuEntries.push(entry) app.pluginMenuEntries.push(entry);
app.pluginInstalled = true app.pluginInstalled = true;
}, },
StyleSheets: { StyleSheets: {
Add(href) { Add(href) {
console.log("Adding stylesheet: " + href) console.log("Adding stylesheet: " + href);
let id = uuidv4() let id = uuidv4();
let link = document.createElement("link") let link = document.createElement("link");
link.rel = "stylesheet/less" link.rel = "stylesheet/less";
link.type = "text/css" link.type = "text/css";
link.href = href link.href = href;
link.setAttribute("css-id", id) link.setAttribute("css-id", id);
// insert the link before document.querySelector("#userTheme") in head // insert the link before document.querySelector("#userTheme") in head
document.querySelector("head").insertBefore(link, document.querySelector("#userTheme")) document.querySelector("head").insertBefore(link, document.querySelector("#userTheme"));
less.registerStylesheetsImmediately() less.registerStylesheetsImmediately();
less.refresh(true, true, true) less.refresh(true, true, true);
return link return link;
} },
} },
} };
export {CiderFrontAPI} export { CiderFrontAPI };

View file

@ -1,9 +1,8 @@
import {html} from "../html.js" import { html } from "../html.js";
export const sidebarLibraryItem = Vue.component("sidebar-library-item", { export const sidebarLibraryItem = Vue.component("sidebar-library-item", {
template: html` template: html`
<button class="app-sidebar-item" <button class="app-sidebar-item" :class="$root.getSidebarItemClass(page)" @click="$root.setWindowHash(page)">
:class="$root.getSidebarItemClass(page)" @click="$root.setWindowHash(page)">
<svg-icon :url="svgIconData" :name="'sidebar-' + svgIconName" v-if="svgIconData != ''" /> <svg-icon :url="svgIconData" :name="'sidebar-' + svgIconName" v-if="svgIconData != ''" />
<span class="sidebar-item-text">{{ name }}</span> <span class="sidebar-item-text">{{ name }}</span>
</button> </button>
@ -24,7 +23,7 @@ export const sidebarLibraryItem = Vue.component("sidebar-library-item", {
}, },
svgIconName: { svgIconName: {
type: String, type: String,
required: false required: false,
}, },
cdClick: { cdClick: {
type: Function, type: Function,
@ -43,4 +42,4 @@ export const sidebarLibraryItem = Vue.component("sidebar-library-item", {
} }
}, },
methods: {}, methods: {},
}) });

View file

@ -1,22 +1,20 @@
import {html} from "../html.js" import { html } from "../html.js";
export const svgIcon = Vue.component("svg-icon", { export const svgIcon = Vue.component("svg-icon", {
template: html` template: html` <div class="_svg-icon" :class="classes" :svg-name="name" :style="{'--icon': 'url(' + url + ')'}"></div> `,
<div class="_svg-icon" :class="classes" :svg-name="name" :style="{'--icon': 'url(' + url + ')'}"></div>
`,
props: { props: {
name: { name: {
type: String, type: String,
required: false required: false,
}, },
classes: { classes: {
type: String, type: String,
required: false required: false,
}, },
url: { url: {
type: String, type: String,
required: true, required: true,
default: "./assets/repeat.svg" default: "./assets/repeat.svg",
} },
} },
}) });

View file

@ -1,98 +1,94 @@
const Events = { const Events = {
InitEvents() { InitEvents() {
const app = window.app const app = window.app;
// add event listener for when window.location.hash changes // add event listener for when window.location.hash changes
window.addEventListener("hashchange", function () { window.addEventListener("hashchange", function () {
app.page = "blank" app.page = "blank";
setTimeout(() => { setTimeout(() => {
app.appRoute(window.location.hash) app.appRoute(window.location.hash);
}, 100) }, 100);
}); });
window.addEventListener("mouseup", (e) => { window.addEventListener("mouseup", (e) => {
if (e.button === 3) { if (e.button === 3) {
e.preventDefault() e.preventDefault();
app.navigateBack() app.navigateBack();
} else if (e.button === 4) { } else if (e.button === 4) {
e.preventDefault() e.preventDefault();
app.navigateForward() app.navigateForward();
} }
}); });
document.addEventListener('keydown', async function (event) { document.addEventListener("keydown", async function (event) {
// CTRL + R // CTRL + R
if (event.keyCode === 82 && event.ctrlKey) { if (event.keyCode === 82 && event.ctrlKey) {
event.preventDefault() event.preventDefault();
app.confirm(app.getLz('term.reload'), (res)=>{ app.confirm(app.getLz("term.reload"), (res) => {
if (res) { if (res) {
window.location.reload() window.location.reload();
} }
}) });
} }
// CTRL + SHIFT + R // CTRL + SHIFT + R
if (event.keyCode === 82 && event.ctrlKey && event.shiftKey) { if (event.keyCode === 82 && event.ctrlKey && event.shiftKey) {
event.preventDefault() event.preventDefault();
window.location.reload() window.location.reload();
} }
// CTRL + E // CTRL + E
if (event.keyCode === 69 && event.ctrlKey) { if (event.keyCode === 69 && event.ctrlKey) {
app.invokeDrawer('queue') app.invokeDrawer("queue");
} }
// CTRL+H // CTRL+H
if (event.keyCode === 72 && event.ctrlKey) { if (event.keyCode === 72 && event.ctrlKey) {
app.appRoute("home") app.appRoute("home");
} }
// CTRL+SHIFT+H // CTRL+SHIFT+H
if (event.ctrlKey && event.shiftKey && event.keyCode == 72) { if (event.ctrlKey && event.shiftKey && event.keyCode == 72) {
let hist = await app.mk.api.v3.music(`/v1/me/recent/played/tracks`, { let hist = await app.mk.api.v3.music(`/v1/me/recent/played/tracks`, {
l: app.mklang l: app.mklang,
}) });
app.showCollection(hist.data, app.getLz('term.history')) app.showCollection(hist.data, app.getLz("term.history"));
} }
if (event.ctrlKey && event.keyCode == 121) { if (event.ctrlKey && event.keyCode == 121) {
try { try {
app.mk._services.mediaItemPlayback._currentPlayer.stop() app.mk._services.mediaItemPlayback._currentPlayer.stop();
} catch (e) { } catch (e) {}
}
try { try {
app.mk._services.mediaItemPlayback._currentPlayer.destroy() app.mk._services.mediaItemPlayback._currentPlayer.destroy();
} catch (e) { } catch (e) {}
}
} }
if (event.ctrlKey && event.keyCode == 122) { if (event.ctrlKey && event.keyCode == 122) {
try { try {
ipcRenderer.send('detachDT', '') ipcRenderer.send("detachDT", "");
} catch (e) { } catch (e) {}
}
} }
// Prevent Scrolling on spacebar // Prevent Scrolling on spacebar
if (event.keyCode === 32 && event.target === document.body) { if (event.keyCode === 32 && event.target === document.body) {
event.preventDefault() event.preventDefault();
app.SpacePause() app.SpacePause();
} }
}); });
// Hang Timer // Hang Timer
app.hangtimer = setTimeout(() => { app.hangtimer = setTimeout(() => {
if (confirm("Cider is not responding. Reload the app?")) { if (confirm("Cider is not responding. Reload the app?")) {
window.location.reload() window.location.reload();
} }
}, 10000) }, 10000);
// Refresh Focus // Refresh Focus
function refreshFocus() { function refreshFocus() {
if (document.hasFocus() == false) { if (document.hasFocus() == false) {
app.windowFocus(false) app.windowFocus(false);
} else { } else {
app.windowFocus(true) app.windowFocus(true);
} }
setTimeout(refreshFocus, 200); setTimeout(refreshFocus, 200);
} }
refreshFocus(); refreshFocus();
} },
} };
export {Events} export { Events };

View file

@ -1,35 +1,33 @@
function simulateGamepad() { function simulateGamepad() {
const app = window.app const app = window.app;
app.chrome.showCursor = true app.chrome.showCursor = true;
let cursorPos = [0, 0]; let cursorPos = [0, 0];
let intTabIndex = 0 let intTabIndex = 0;
const cursorSpeedPvt = 8 const cursorSpeedPvt = 8;
const cursorSize = 16 const cursorSize = 16;
let scrollSpeed = 8 let scrollSpeed = 8;
let buttonPressDelay = 500 let buttonPressDelay = 500;
let stickDeadZone = 0.2 let stickDeadZone = 0.2;
let scrollGroup = null let scrollGroup = null;
let scrollGroupY = null let scrollGroupY = null;
let elementFocusEnabled = true let elementFocusEnabled = true;
let start; let start;
let cursorSpeed = cursorSpeedPvt let cursorSpeed = cursorSpeedPvt;
let lastButtonPress = { let lastButtonPress = {};
}
var sounds = { var sounds = {
Confirm: new Audio("./sounds/confirm.ogg"), Confirm: new Audio("./sounds/confirm.ogg"),
Menu: new Audio("./sounds/btn1.ogg"), Menu: new Audio("./sounds/btn1.ogg"),
Hover: new Audio("./sounds/hover.ogg") Hover: new Audio("./sounds/hover.ogg"),
} };
let element = document.elementFromPoint(0, 0) let element = document.elementFromPoint(0, 0);
let elementType = 0 let elementType = 0;
function appLoop() { function appLoop() {
var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []); var gamepads = navigator.getGamepads ? navigator.getGamepads() : navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : [];
if (!gamepads) { if (!gamepads) {
return; return;
} }
@ -38,218 +36,207 @@ function simulateGamepad () {
// LEFT STICK // LEFT STICK
if (gp.axes[0] > stickDeadZone) { if (gp.axes[0] > stickDeadZone) {
cursorPos[0] += (gp.axes[0] * cursorSpeed) cursorPos[0] += gp.axes[0] * cursorSpeed;
} else if (gp.axes[0] < -stickDeadZone) { } else if (gp.axes[0] < -stickDeadZone) {
cursorPos[0] += (gp.axes[0] * cursorSpeed) cursorPos[0] += gp.axes[0] * cursorSpeed;
} }
if (gp.axes[1] > stickDeadZone) { if (gp.axes[1] > stickDeadZone) {
cursorPos[1] += (gp.axes[1] * cursorSpeed) cursorPos[1] += gp.axes[1] * cursorSpeed;
} else if (gp.axes[1] < -stickDeadZone) { } else if (gp.axes[1] < -stickDeadZone) {
cursorPos[1] += (gp.axes[1] * cursorSpeed) cursorPos[1] += gp.axes[1] * cursorSpeed;
} }
if (cursorPos[0] < cursorSize) { if (cursorPos[0] < cursorSize) {
cursorPos[0] = cursorSize cursorPos[0] = cursorSize;
} }
if (cursorPos[1] < cursorSize) { if (cursorPos[1] < cursorSize) {
cursorPos[1] = cursorSize cursorPos[1] = cursorSize;
} }
if (cursorPos[0] > window.innerWidth - cursorSize) { if (cursorPos[0] > window.innerWidth - cursorSize) {
cursorPos[0] = window.innerWidth - cursorSize cursorPos[0] = window.innerWidth - cursorSize;
} }
if (cursorPos[1] > window.innerHeight - cursorSize) { if (cursorPos[1] > window.innerHeight - cursorSize) {
cursorPos[1] = window.innerHeight - cursorSize cursorPos[1] = window.innerHeight - cursorSize;
} }
// RIGHT STICK. // RIGHT STICK.
if (scrollGroupY) { if (scrollGroupY) {
if (gp.axes[3] > stickDeadZone) { if (gp.axes[3] > stickDeadZone) {
$(scrollGroupY).scrollTop($(scrollGroupY).scrollTop() + (gp.axes[3] * scrollSpeed)) $(scrollGroupY).scrollTop($(scrollGroupY).scrollTop() + gp.axes[3] * scrollSpeed);
elementFocusEnabled = false elementFocusEnabled = false;
} else if (gp.axes[3] < -stickDeadZone) { } else if (gp.axes[3] < -stickDeadZone) {
$(scrollGroupY).scrollTop($(scrollGroupY).scrollTop() + (gp.axes[3] * scrollSpeed)) $(scrollGroupY).scrollTop($(scrollGroupY).scrollTop() + gp.axes[3] * scrollSpeed);
elementFocusEnabled = false elementFocusEnabled = false;
} else { } else {
elementFocusEnabled = true elementFocusEnabled = true;
} }
} }
if (scrollGroup) { if (scrollGroup) {
if (gp.axes[2] > stickDeadZone) { if (gp.axes[2] > stickDeadZone) {
$(scrollGroup).scrollLeft($(scrollGroup).scrollLeft() + (gp.axes[2] * scrollSpeed)) $(scrollGroup).scrollLeft($(scrollGroup).scrollLeft() + gp.axes[2] * scrollSpeed);
elementFocusEnabled = false elementFocusEnabled = false;
} else if (gp.axes[2] < -stickDeadZone) { } else if (gp.axes[2] < -stickDeadZone) {
$(scrollGroup).scrollLeft($(scrollGroup).scrollLeft() + (gp.axes[2] * scrollSpeed)) $(scrollGroup).scrollLeft($(scrollGroup).scrollLeft() + gp.axes[2] * scrollSpeed);
elementFocusEnabled = false elementFocusEnabled = false;
} else { } else {
elementFocusEnabled = true elementFocusEnabled = true;
} }
} }
$(".cursor").css({ $(".cursor").css({
top: cursorPos[1] + "px", top: cursorPos[1] + "px",
left: cursorPos[0] + "px", left: cursorPos[0] + "px",
display: "block" display: "block",
}) });
// A BUTTON // A BUTTON
if (gp.buttons[0].pressed) { if (gp.buttons[0].pressed) {
if (!lastButtonPress["A"]) { if (!lastButtonPress["A"]) {
lastButtonPress["A"] = 0 lastButtonPress["A"] = 0;
} }
if (Date.now() - lastButtonPress["A"] > buttonPressDelay) { if (Date.now() - lastButtonPress["A"] > buttonPressDelay) {
lastButtonPress["A"] = Date.now() lastButtonPress["A"] = Date.now();
sounds.Confirm.play() sounds.Confirm.play();
if (elementType == 0) { if (elementType == 0) {
document.activeElement.dispatchEvent(new Event("click")) document.activeElement.dispatchEvent(new Event("click"));
document.activeElement.dispatchEvent(new Event("controller-click")) document.activeElement.dispatchEvent(new Event("controller-click"));
} else { } else {
element.dispatchEvent(new Event("click")) element.dispatchEvent(new Event("click"));
element.dispatchEvent(new Event("controller-click")) element.dispatchEvent(new Event("controller-click"));
} }
} }
} }
// B BUTTON // B BUTTON
if (gp.buttons[1].pressed) { if (gp.buttons[1].pressed) {
if (!lastButtonPress["B"]) { if (!lastButtonPress["B"]) {
lastButtonPress["B"] = 0 lastButtonPress["B"] = 0;
} }
if (Date.now() - lastButtonPress["B"] > buttonPressDelay) { if (Date.now() - lastButtonPress["B"] > buttonPressDelay) {
lastButtonPress["B"] = Date.now() lastButtonPress["B"] = Date.now();
if (elementType == 0) { if (elementType == 0) {
document.activeElement.dispatchEvent(new Event("contextmenu")) document.activeElement.dispatchEvent(new Event("contextmenu"));
setTimeout(() => { setTimeout(() => {
if ($(".menu-option").length > 0) { if ($(".menu-option").length > 0) {
let bounds = $(".menu-option")[0].getBoundingClientRect() let bounds = $(".menu-option")[0].getBoundingClientRect();
cursorPos[0] = bounds.left + (bounds.width / 2) cursorPos[0] = bounds.left + bounds.width / 2;
cursorPos[1] = bounds.top + (bounds.height / 2) cursorPos[1] = bounds.top + bounds.height / 2;
} }
}, 100) }, 100);
} else { } else {
element.dispatchEvent(new Event("contextmenu")) element.dispatchEvent(new Event("contextmenu"));
} }
} }
} }
// right bumper // right bumper
if (gp.buttons[5].pressed) { if (gp.buttons[5].pressed) {
if (!lastButtonPress["RB"]) { if (!lastButtonPress["RB"]) {
lastButtonPress["RB"] = 0 lastButtonPress["RB"] = 0;
} }
if (Date.now() - lastButtonPress["RB"] > buttonPressDelay) { if (Date.now() - lastButtonPress["RB"] > buttonPressDelay) {
lastButtonPress["RB"] = Date.now() lastButtonPress["RB"] = Date.now();
app.navigateForward() app.navigateForward();
} }
} }
// left bumper // left bumper
if (gp.buttons[4].pressed) { if (gp.buttons[4].pressed) {
if (!lastButtonPress["LB"]) { if (!lastButtonPress["LB"]) {
lastButtonPress["LB"] = 0 lastButtonPress["LB"] = 0;
} }
if (Date.now() - lastButtonPress["LB"] > buttonPressDelay) { if (Date.now() - lastButtonPress["LB"] > buttonPressDelay) {
lastButtonPress["LB"] = Date.now() lastButtonPress["LB"] = Date.now();
app.navigateBack() app.navigateBack();
} }
} }
// cursor hover // cursor hover
if (elementFocusEnabled) { if (elementFocusEnabled) {
element = document.elementFromPoint(cursorPos[0], cursorPos[1]) element = document.elementFromPoint(cursorPos[0], cursorPos[1]);
} }
if (element) { if (element) {
let closest = element.closest("[tabindex], input, button, a");
let closest = element.closest("[tabindex], input, button, a")
// VERT SCROLL // VERT SCROLL
let scrollGroupCloY = element.closest(`[scrollaxis="y"]`) let scrollGroupCloY = element.closest(`[scrollaxis="y"]`);
if (scrollGroupCloY) { if (scrollGroupCloY) {
scrollGroupY = scrollGroupCloY scrollGroupY = scrollGroupCloY;
} }
// HOZ SCROLL // HOZ SCROLL
let scrollGroupClo = element.closest(".v-hl-container") let scrollGroupClo = element.closest(".v-hl-container");
if (scrollGroupClo) { if (scrollGroupClo) {
if (scrollGroupClo.classList.contains("v-hl-container")) { if (scrollGroupClo.classList.contains("v-hl-container")) {
scrollGroup = scrollGroupClo scrollGroup = scrollGroupClo;
scrollGroup.style["scroll-snap-type"] = "unset" scrollGroup.style["scroll-snap-type"] = "unset";
} else { } else {
scrollGroup.style["scroll-snap-type"] = "" scrollGroup.style["scroll-snap-type"] = "";
scrollGroup = null scrollGroup = null;
} }
} }
if (closest) { if (closest) {
elementType = 0 elementType = 0;
closest.focus() closest.focus();
} else { } else {
if (closest) { if (closest) {
closest.blur() closest.blur();
} }
elementType = 1 elementType = 1;
element.focus() element.focus();
} }
cursorSpeed = cursorSpeedPvt cursorSpeed = cursorSpeedPvt;
if (!element.classList.contains("app-chrome") if (!element.classList.contains("app-chrome") && !element.classList.contains("app-content")) {
&& !element.classList.contains("app-content")) { cursorSpeed = cursorSpeedPvt;
cursorSpeed = cursorSpeedPvt
} }
// console.log($._data($(element), "events")) // console.log($._data($(element), "events"))
} else { } else {
cursorSpeed = 12 cursorSpeed = 12;
} }
// console.log(gp.axes[0], gp.axes[1]) // console.log(gp.axes[0], gp.axes[1])
start = requestAnimationFrame(appLoop); start = requestAnimationFrame(appLoop);
} }
// controller pairing // controller pairing
notyf.error("Press the button on your controller to pair it to Cider.") notyf.error("Press the button on your controller to pair it to Cider.");
window.addEventListener("gamepadconnected", function (e) { window.addEventListener(
console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.", "gamepadconnected",
e.gamepad.index, e.gamepad.id, function (e) {
e.gamepad.buttons.length, e.gamepad.axes.length); console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.", e.gamepad.index, e.gamepad.id, e.gamepad.buttons.length, e.gamepad.axes.length);
notyf.success("Pairing successful!") notyf.success("Pairing successful!");
appLoop() appLoop();
}, { once: true }); },
{ once: true }
);
document.addEventListener("keydown", (e) => { document.addEventListener("keydown", (e) => {
sounds.Confirm.currentTime = 0 sounds.Confirm.currentTime = 0;
sounds.Menu.currentTime = 0 sounds.Menu.currentTime = 0;
sounds.Hover.currentTime = 0 sounds.Hover.currentTime = 0;
let tabbable = $("[tabindex]") let tabbable = $("[tabindex]");
console.log(e.key) console.log(e.key);
switch (e.key) { switch (e.key) {
default: default:
break; break;
case "ArrowLeft": case "ArrowLeft":
e.preventDefault() e.preventDefault();
cursorPos[0] -= cursorSpeed cursorPos[0] -= cursorSpeed;
break; break;
case "ArrowRight": case "ArrowRight":
e.preventDefault() e.preventDefault();
cursorPos[0] += cursorSpeed cursorPos[0] += cursorSpeed;
break; break;
case "ArrowUp": case "ArrowUp":
e.preventDefault() e.preventDefault();
cursorPos[1] -= cursorSpeed cursorPos[1] -= cursorSpeed;
// sounds.Hover.play() // sounds.Hover.play()
// if (intTabIndex <= 0) { // if (intTabIndex <= 0) {
// intTabIndex = 0 // intTabIndex = 0
@ -260,9 +247,9 @@ function simulateGamepad () {
// $("#app-content").scrollTop($(document.activeElement).offset().top) // $("#app-content").scrollTop($(document.activeElement).offset().top)
break; break;
case "ArrowDown": case "ArrowDown":
e.preventDefault() e.preventDefault();
cursorPos[1] += cursorSpeed cursorPos[1] += cursorSpeed;
// if (intTabIndex < tabbable.length) { // if (intTabIndex < tabbable.length) {
// intTabIndex++ // intTabIndex++
// } else { // } else {
@ -272,56 +259,55 @@ function simulateGamepad () {
// $("#app-content").scrollTop($(document.activeElement).offset().top) // $("#app-content").scrollTop($(document.activeElement).offset().top)
break; break;
case "c": case "c":
app.resetState() app.resetState();
break; break;
case "x": case "x":
// set cursorPos to the top right of the screen // set cursorPos to the top right of the screen
// sounds.Menu.play() // sounds.Menu.play()
if (elementType == 0) { if (elementType == 0) {
document.activeElement.dispatchEvent(new Event("contextmenu")) document.activeElement.dispatchEvent(new Event("contextmenu"));
} else { } else {
element.dispatchEvent(new Event("contextmenu")) element.dispatchEvent(new Event("contextmenu"));
} }
e.preventDefault() e.preventDefault();
break; break;
case "z": case "z":
sounds.Confirm.play() sounds.Confirm.play();
if (elementType == 0) { if (elementType == 0) {
document.activeElement.dispatchEvent(new Event("click")) document.activeElement.dispatchEvent(new Event("click"));
document.activeElement.dispatchEvent(new Event("controller-click")) document.activeElement.dispatchEvent(new Event("controller-click"));
} else { } else {
element.dispatchEvent(new Event("click")) element.dispatchEvent(new Event("click"));
element.dispatchEvent(new Event("controller-click")) element.dispatchEvent(new Event("controller-click"));
} }
e.preventDefault() e.preventDefault();
break; break;
} }
$(".cursor").css({ $(".cursor").css({
top: cursorPos[1] + "px", top: cursorPos[1] + "px",
left: cursorPos[0] + "px" left: cursorPos[0] + "px",
}) });
function lerp(a, b, n) { function lerp(a, b, n) {
return (1 - n) * a + n * b return (1 - n) * a + n * b;
} }
element = document.elementFromPoint(cursorPos[0], cursorPos[1]);
element = document.elementFromPoint(cursorPos[0], cursorPos[1])
if (element) { if (element) {
let closest = element.closest("[tabindex], input, button, a") let closest = element.closest("[tabindex], input, button, a");
if (closest) { if (closest) {
elementType = 0 elementType = 0;
closest.focus() closest.focus();
} else { } else {
elementType = 1 elementType = 1;
element.focus() element.focus();
} }
} }
console.log(element) console.log(element);
}); });
} }
export {simulateGamepad} export { simulateGamepad };

View file

@ -1,3 +1,3 @@
export function html(str) { export function html(str) {
return str[0] return str[0];
} }

View file

@ -33,7 +33,7 @@ async function spawnMica() {
imgSrc = micaCache; imgSrc = micaCache;
} else { } else {
imgSrc = await ipcRenderer.sendSync("get-wallpaper", { imgSrc = await ipcRenderer.sendSync("get-wallpaper", {
blurAmount: 256 blurAmount: 256,
}); });
CiderCache.putCache("mica-cache", imgSrc); CiderCache.putCache("mica-cache", imgSrc);
} }
@ -51,10 +51,7 @@ async function spawnMica() {
cb(); cb();
} }
// window size change // window size change
if ( if (lastScreenWidth !== window.innerWidth || lastScreenHeight !== window.innerHeight) {
lastScreenWidth !== window.innerWidth ||
lastScreenHeight !== window.innerHeight
) {
lastScreenWidth = window.innerWidth; lastScreenWidth = window.innerWidth;
lastScreenHeight = window.innerHeight; lastScreenHeight = window.innerHeight;
cb(); cb();

View file

@ -1,56 +1,44 @@
const MusicKitTools = { const MusicKitTools = {
async v3Backend({ async v3Backend({ route = "", getBody = {}, options = {} }) {
route = "", getBody = {}, options = {} return await await ipcRenderer.invoke("mkv3", {
}) {
return await (await ipcRenderer.invoke("mkv3", {
token: MusicKit.getInstance().developerToken, token: MusicKit.getInstance().developerToken,
route: route, route: route,
mediaToken: MusicKit.getInstance().musicUserToken, mediaToken: MusicKit.getInstance().musicUserToken,
GETBody: getBody GETBody: getBody,
})) });
}, },
async v3Continuous({ async v3Continuous({ href, options = {}, reqOptions = {}, onProgress = () => {}, onError = () => {}, onSuccess = () => {} } = {}) {
href, let returnData = [];
options = {},
reqOptions = {},
onProgress = () => {
},
onError = () => {
},
onSuccess = () => {
}
} = {}) {
let returnData = []
async function sendReq(href, options) { async function sendReq(href, options) {
const response = await app.mk.api.v3.music(href, options).catch(error => onError) const response = await app.mk.api.v3.music(href, options).catch((error) => onError);
returnData = returnData.concat(response.data.data) returnData = returnData.concat(response.data.data);
if (response.data.next) { if (response.data.next) {
onProgress({ onProgress({
response: response, response: response,
total: returnData.length total: returnData.length,
}) });
try { try {
await sendReq(response.data.next, options) await sendReq(response.data.next, options);
} catch (e) { } catch (e) {
await sendReq(response.data.next, options) await sendReq(response.data.next, options);
} }
} }
} }
await sendReq(href, options) await sendReq(href, options);
onSuccess(returnData) onSuccess(returnData);
return returnData return returnData;
}, },
getHeader() { getHeader() {
return new Headers({ return new Headers({
Authorization: 'Bearer ' + MusicKit.getInstance().developerToken, Authorization: "Bearer " + MusicKit.getInstance().developerToken,
Accept: 'application/json', Accept: "application/json",
'Content-Type': 'application/json', "Content-Type": "application/json",
'Music-User-Token': '' + MusicKit.getInstance().musicUserToken "Music-User-Token": "" + MusicKit.getInstance().musicUserToken,
}); });
} },
} };
export {MusicKitTools} export { MusicKitTools };

File diff suppressed because it is too large Load diff

View file

@ -12,16 +12,16 @@ const store = new Vuex.Store({
loaded: false, loaded: false,
nextUrl: null, nextUrl: null,
items: [], items: [],
size: "normal" size: "normal",
}, },
settings: { settings: {
currentTabIndex: 0, currentTabIndex: 0,
fullscreen: false fullscreen: false,
} },
}, },
artwork: { artwork: {
playerLCD: "" playerLCD: "",
} },
}, },
mutations: { mutations: {
resetRecentlyAdded(state) { resetRecentlyAdded(state) {
@ -30,9 +30,9 @@ const store = new Vuex.Store({
state.pageState.recentlyAdded.items = []; state.pageState.recentlyAdded.items = [];
}, },
setLCDArtwork(state, artwork) { setLCDArtwork(state, artwork) {
state.artwork.playerLCD = artwork state.artwork.playerLCD = artwork;
} },
} },
}) });
export {store} export { store };

View file

@ -4,38 +4,48 @@ const wsapi = {
async v3(encoded = "") { async v3(encoded = "") {
let decoded = atob(encoded); let decoded = atob(encoded);
let json = JSON.parse(decoded); let json = JSON.parse(decoded);
console.log(json) console.log(json);
let response = await (await MusicKit.getInstance().api.v3.music(json.route, json.body, json.options)) let response = await await MusicKit.getInstance().api.v3.music(json.route, json.body, json.options);
let ret = response.data let ret = response.data;
return JSON.stringify(ret) return JSON.stringify(ret);
}, },
search(term, limit) { search(term, limit) {
MusicKit.getInstance().api.search(term, {limit: limit, types: 'songs,artists,albums,playlists'}).then((results)=>{ MusicKit.getInstance()
ipcRenderer.send('wsapi-returnSearch', JSON.stringify(results)) .api.search(term, {
limit: limit,
types: "songs,artists,albums,playlists",
}) })
.then((results) => {
ipcRenderer.send("wsapi-returnSearch", JSON.stringify(results));
});
}, },
searchLibrary(term, limit) { searchLibrary(term, limit) {
MusicKit.getInstance().api.library.search(term, {limit: limit, types: 'library-songs,library-artists,library-albums,library-playlists'}).then((results)=>{ MusicKit.getInstance()
ipcRenderer.send('wsapi-returnSearchLibrary', JSON.stringify(results)) .api.library.search(term, {
limit: limit,
types: "library-songs,library-artists,library-albums,library-playlists",
}) })
.then((results) => {
ipcRenderer.send("wsapi-returnSearchLibrary", JSON.stringify(results));
});
}, },
getAttributes: function () { getAttributes: function () {
const mk = MusicKit.getInstance(); const mk = MusicKit.getInstance();
const nowPlayingItem = mk.nowPlayingItem; const nowPlayingItem = mk.nowPlayingItem;
const isPlayingExport = mk.isPlaying; const isPlayingExport = mk.isPlaying;
const remainingTimeExport = mk.currentPlaybackTimeRemaining; const remainingTimeExport = mk.currentPlaybackTimeRemaining;
const attributes = (nowPlayingItem != null ? nowPlayingItem.attributes : {}); const attributes = nowPlayingItem != null ? nowPlayingItem.attributes : {};
attributes.status = isPlayingExport ? isPlayingExport : false; attributes.status = isPlayingExport ? isPlayingExport : false;
attributes.name = attributes.name ? attributes.name : 'No Title Found'; attributes.name = attributes.name ? attributes.name : "No Title Found";
attributes.artwork = attributes.artwork ? attributes.artwork : {url: ''}; attributes.artwork = attributes.artwork ? attributes.artwork : { url: "" };
attributes.artwork.url = attributes.artwork.url ? attributes.artwork.url : ''; attributes.artwork.url = attributes.artwork.url ? attributes.artwork.url : "";
attributes.playParams = attributes.playParams ? attributes.playParams : {id: 'no-id-found'}; attributes.playParams = attributes.playParams ? attributes.playParams : { id: "no-id-found" };
attributes.playParams.id = attributes.playParams.id ? attributes.playParams.id : 'no-id-found'; attributes.playParams.id = attributes.playParams.id ? attributes.playParams.id : "no-id-found";
attributes.albumName = attributes.albumName ? attributes.albumName : ''; attributes.albumName = attributes.albumName ? attributes.albumName : "";
attributes.artistName = attributes.artistName ? attributes.artistName : ''; attributes.artistName = attributes.artistName ? attributes.artistName : "";
attributes.genreNames = attributes.genreNames ? attributes.genreNames : []; attributes.genreNames = attributes.genreNames ? attributes.genreNames : [];
attributes.remainingTime = remainingTimeExport ? (remainingTimeExport * 1000) : 0; attributes.remainingTime = remainingTimeExport ? remainingTimeExport * 1000 : 0;
attributes.durationInMillis = attributes.durationInMillis ? attributes.durationInMillis : 0; attributes.durationInMillis = attributes.durationInMillis ? attributes.durationInMillis : 0;
attributes.startTime = Date.now(); attributes.startTime = Date.now();
attributes.endTime = attributes.endTime ? attributes.endTime : Date.now(); attributes.endTime = attributes.endTime ? attributes.endTime : Date.now();
@ -43,152 +53,178 @@ const wsapi = {
attributes.shuffleMode = mk.shuffleMode; attributes.shuffleMode = mk.shuffleMode;
attributes.repeatMode = mk.repeatMode; attributes.repeatMode = mk.repeatMode;
attributes.autoplayEnabled = mk.autoplayEnabled; attributes.autoplayEnabled = mk.autoplayEnabled;
return attributes return attributes;
}, },
moveQueueItem(oldPosition, newPosition) { moveQueueItem(oldPosition, newPosition) {
MusicKit.getInstance().queue._queueItems.splice(newPosition,0,MusicKit.getInstance().queue._queueItems.splice(oldPosition,1)[0]) MusicKit.getInstance().queue._queueItems.splice(newPosition, 0, MusicKit.getInstance().queue._queueItems.splice(oldPosition, 1)[0]);
MusicKit.getInstance().queue._reindex() MusicKit.getInstance().queue._reindex();
}, },
setAutoplay(value) { setAutoplay(value) {
MusicKit.getInstance().autoplayEnabled = value MusicKit.getInstance().autoplayEnabled = value;
}, },
returnDynamic(data, type) { returnDynamic(data, type) {
ipcRenderer.send('wsapi-returnDynamic', JSON.stringify(data), type) ipcRenderer.send("wsapi-returnDynamic", JSON.stringify(data), type);
}, },
musickitApi(method, id, params, library = false) { musickitApi(method, id, params, library = false) {
if (library) { if (library) {
MusicKit.getInstance().api.library[method](id, params).then((results)=>{ MusicKit.getInstance()
ipcRenderer.send('wsapi-returnMusicKitApi', JSON.stringify(results), method) .api.library[method](id, params)
}) .then((results) => {
ipcRenderer.send("wsapi-returnMusicKitApi", JSON.stringify(results), method);
});
} else { } else {
MusicKit.getInstance().api[method](id, params).then((results)=>{ MusicKit.getInstance()
ipcRenderer.send('wsapi-returnMusicKitApi', JSON.stringify(results), method) .api[method](id, params)
}) .then((results) => {
ipcRenderer.send("wsapi-returnMusicKitApi", JSON.stringify(results), method);
});
} }
}, },
getPlaybackState() { getPlaybackState() {
ipcRenderer.send('wsapi-updatePlaybackState', MusicKitInterop.getAttributes()); ipcRenderer.send("wsapi-updatePlaybackState", MusicKitInterop.getAttributes());
}, },
getLyrics() { getLyrics() {
ipcRenderer.send('wsapi-returnLyrics',JSON.stringify(app.lyrics)); ipcRenderer.send("wsapi-returnLyrics", JSON.stringify(app.lyrics));
}, },
getQueue() { getQueue() {
ipcRenderer.send('wsapi-returnQueue', JSON.stringify(MusicKit.getInstance().queue)) ipcRenderer.send("wsapi-returnQueue", JSON.stringify(MusicKit.getInstance().queue));
}, },
playNext(type, id) { playNext(type, id) {
var request = {} var request = {};
request[type] = id request[type] = id;
MusicKit.getInstance().playNext(request) MusicKit.getInstance().playNext(request);
}, },
playLater(type, id) { playLater(type, id) {
var request = {} var request = {};
request[type] = id request[type] = id;
MusicKit.getInstance().playLater(request) MusicKit.getInstance().playLater(request);
},
love() {
}, },
love() {},
playTrackById(id, kind = "song") { playTrackById(id, kind = "song") {
MusicKit.getInstance().setQueue({ [kind]: id , parameters : {l : app.mklang}}).then(function (queue) { MusicKit.getInstance()
MusicKit.getInstance().play() .setQueue({ [kind]: id, parameters: { l: app.mklang } })
}) .then(function (queue) {
MusicKit.getInstance().play();
});
}, },
quickPlay(term) { quickPlay(term) {
// Quick play by song name // Quick play by song name
MusicKit.getInstance().api.search(term, { limit: 2, types: 'songs' }).then(function (data) { MusicKit.getInstance()
MusicKit.getInstance().setQueue({ song: data["songs"][0]["id"],parameters : {l : app.mklang} }).then(function (queue) { .api.search(term, { limit: 2, types: "songs" })
MusicKit.getInstance().play() .then(function (data) {
}) MusicKit.getInstance()
.setQueue({
song: data["songs"][0]["id"],
parameters: { l: app.mklang },
}) })
.then(function (queue) {
MusicKit.getInstance().play();
});
});
}, },
toggleShuffle() { toggleShuffle() {
MusicKit.getInstance().shuffleMode = MusicKit.getInstance().shuffleMode === 0 ? 1 : 0 MusicKit.getInstance().shuffleMode = MusicKit.getInstance().shuffleMode === 0 ? 1 : 0;
}, },
togglePlayPause() { togglePlayPause() {
app.mk.isPlaying ? app.mk.pause() : app.mk.play() app.mk.isPlaying ? app.mk.pause() : app.mk.play();
}, },
toggleRepeat() { toggleRepeat() {
if (MusicKit.getInstance().repeatMode == 0) { if (MusicKit.getInstance().repeatMode == 0) {
MusicKit.getInstance().repeatMode = 1 MusicKit.getInstance().repeatMode = 1;
} else if (MusicKit.getInstance().repeatMode == 1) { } else if (MusicKit.getInstance().repeatMode == 1) {
MusicKit.getInstance().repeatMode = 2 MusicKit.getInstance().repeatMode = 2;
} else { } else {
MusicKit.getInstance().repeatMode = 0 MusicKit.getInstance().repeatMode = 0;
} }
}, },
getmaxVolume() { getmaxVolume() {
ipcRenderer.send('wsapi-returnvolumeMax',JSON.stringify(app.cfg.audio.maxVolume)); ipcRenderer.send("wsapi-returnvolumeMax", JSON.stringify(app.cfg.audio.maxVolume));
}, },
getLibraryStatus(kind, id) { getLibraryStatus(kind, id) {
if (kind === undefined || id === "no-id-found") return; if (kind === undefined || id === "no-id-found") return;
let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind; let truekind = !kind.endsWith("s") ? kind + "s" : kind;
app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/?ids[${truekind}]=${id}`, { app.mk.api.v3
.music(`/v1/catalog/${app.mk.storefrontId}/?ids[${truekind}]=${id}`, {
relate: "library", relate: "library",
fields: "inLibrary" fields: "inLibrary",
}).then(data => { })
.then((data) => {
const res = data.data.data[0]; const res = data.data.data[0];
const inLibrary = res && res.attributes && res.attributes.inLibrary; const inLibrary = res && res.attributes && res.attributes.inLibrary;
app.getRating({ type: truekind, id: id }).then(rating => { app.getRating({ type: truekind, id: id }).then((rating) => {
ipcRenderer.send('wsapi-libraryStatus', inLibrary, rating); ipcRenderer.send("wsapi-libraryStatus", inLibrary, rating);
}) });
}) });
}, },
rate(kind, id, rating) { rate(kind, id, rating) {
if (kind === undefined || id === "no-id-found") return; if (kind === undefined || id === "no-id-found") return;
let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind; let truekind = !kind.endsWith("s") ? kind + "s" : kind;
if (rating === 0) { if (rating === 0) {
app.mk.api.v3.music(`/v1/me/ratings/${truekind}/${id}`, {}, { app.mk.api.v3
.music(
`/v1/me/ratings/${truekind}/${id}`,
{},
{
fetchOptions: { fetchOptions: {
method: "DELETE", method: "DELETE",
},
} }
}).then(function () { )
ipcRenderer.send('wsapi-rate', kind, id, rating); .then(function () {
}) ipcRenderer.send("wsapi-rate", kind, id, rating);
});
} else { } else {
app.mk.api.v3.music(`/v1/me/ratings/${truekind}/${id}`, {}, { app.mk.api.v3
.music(
`/v1/me/ratings/${truekind}/${id}`,
{},
{
fetchOptions: { fetchOptions: {
method: "PUT", method: "PUT",
body: JSON.stringify({ body: JSON.stringify({
"type": "rating", type: "rating",
"attributes": { attributes: {
"value": rating value: rating,
},
}),
},
} }
}) )
} .then(function () {
}).then(function () { ipcRenderer.send("wsapi-rate", kind, id, rating);
ipcRenderer.send('wsapi-rate', kind, id, rating); });
})
} }
}, },
changeLibrary(kind, id, shouldAdd) { changeLibrary(kind, id, shouldAdd) {
if (shouldAdd) { if (shouldAdd) {
app.addToLibrary(id); app.addToLibrary(id);
ipcRenderer.send('wsapi-change-library', kind, id, shouldAdd); ipcRenderer.send("wsapi-change-library", kind, id, shouldAdd);
} else { } else {
let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind; let truekind = !kind.endsWith("s") ? kind + "s" : kind;
app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/?ids[${truekind}]=${id}`, { app.mk.api.v3
.music(`/v1/catalog/${app.mk.storefrontId}/?ids[${truekind}]=${id}`, {
relate: "library", relate: "library",
fields: "inLibrary" fields: "inLibrary",
}) })
.then(res => { .then((res) => {
res = res.data.data[0] res = res.data.data[0];
if (res && res.relationships && res.relationships.library && res.relationships.library.data) { if (res && res.relationships && res.relationships.library && res.relationships.library.data) {
const item = res.relationships.library.data[0]; const item = res.relationships.library.data[0];
if (item) { if (item) {
app.removeFromLibrary(kind, item.id) app.removeFromLibrary(kind, item.id);
} }
ipcRenderer.send('wsapi-change-library', kind, id, shouldAdd); ipcRenderer.send("wsapi-change-library", kind, id, shouldAdd);
} }
}); });
} }
} },
} };
export {wsapi} export { wsapi };

View file

@ -72,11 +72,10 @@ body {
background-size: cover; background-size: cover;
background-position: center; background-position: center;
background: #0000; background: #0000;
font-family : "Pretendard Variable", "Noto Sans JP", "Noto Sans KR", "Noto Sans TC", "Noto Sans SC", -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; font-family: "Pretendard Variable", "Noto Sans JP", "Noto Sans KR", "Noto Sans TC", "Noto Sans SC", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
transition : opacity .10s var(--appleEase); transition: opacity 0.1s var(--appleEase);
} }
a:-webkit-any-link { a:-webkit-any-link {
color: var(--keyColor); color: var(--keyColor);
} }
@ -120,7 +119,6 @@ body.notransparency::before {
box-sizing: inherit; box-sizing: inherit;
} }
/* Modern style overlay scrollbars */ /* Modern style overlay scrollbars */
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 16px; width: 16px;
@ -230,7 +228,6 @@ input[type="number"] {
font-family: inherit; font-family: inherit;
} }
.bg-artwork--placeholder { .bg-artwork--placeholder {
position: absolute; position: absolute;
top: 0; top: 0;
@ -241,7 +238,7 @@ input[type="number"] {
z-index: -1; z-index: -1;
background-size: cover; background-size: cover;
background-position: center; background-position: center;
opacity : 0.70; opacity: 0.7;
} }
a.dropdown-item { a.dropdown-item {
@ -280,7 +277,6 @@ a.dropdown-item {
} }
} }
.bg-artwork-container { .bg-artwork-container {
position: absolute; position: absolute;
top: 0; top: 0;
@ -337,9 +333,8 @@ a.dropdown-item {
} }
} }
[artwork-hidden] { [artwork-hidden] {
transition: opacity .25s var(--appleEase); transition: opacity 0.25s var(--appleEase);
opacity: 0; opacity: 0;
} }
@ -362,7 +357,7 @@ input[type="range"].web-slider::-webkit-slider-thumb {
box-shadow: 0 0 2px 0 #555; box-shadow: 0 0 2px 0 #555;
} }
input[type=range].web-slider::-webkit-slider-runnable-track { input[type="range"].web-slider::-webkit-slider-runnable-track {
-webkit-appearance: none; -webkit-appearance: none;
box-shadow: none; box-shadow: none;
border: none; border: none;
@ -380,7 +375,7 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
flex-direction: column; flex-direction: column;
opacity: 1; opacity: 1;
overflow: hidden; overflow: hidden;
background-color: rgba(20 20 20 / .7); background-color: rgba(20 20 20 / 0.7);
} }
#app-sidebar { #app-sidebar {
@ -449,7 +444,7 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
} }
} }
transition: .3s var(--appleEase); transition: 0.3s var(--appleEase);
} }
.search-input-container { .search-input-container {
@ -478,14 +473,14 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
} }
.search-input--icon { .search-input--icon {
content : ''; content: "";
width: 100%; width: 100%;
height: 100%; height: 100%;
display: block; display: block;
position: absolute; position: absolute;
top: 0px; top: 0px;
left: 0px; left: 0px;
background-image : url('assets/search.svg'); background-image: url("assets/search.svg");
background-position: 10px; background-position: 10px;
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: 12px; background-size: 12px;
@ -563,7 +558,7 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
} }
&:after { &:after {
content : ''; content: "";
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
@ -594,7 +589,7 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
&.active { &.active {
background: rgb(200 200 200 / 15%); background: rgb(200 200 200 / 15%);
animation : usermenuBtnClick .30s cubic-bezier(0.36, 0, 0.66, -0.56); animation: usermenuBtnClick 0.3s cubic-bezier(0.36, 0, 0.66, -0.56);
} }
} }
@ -694,7 +689,7 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
width: 100%; width: 100%;
box-shadow: var(--ciderShadow-Generic); box-shadow: var(--ciderShadow-Generic);
backdrop-filter: var(--glassFilter); backdrop-filter: var(--glassFilter);
animation : cmenuBodyIn .5s var(--appleEase); animation: cmenuBodyIn 0.5s var(--appleEase);
@keyframes cmenuBodyIn { @keyframes cmenuBodyIn {
0% { 0% {
@ -774,7 +769,7 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
background: rgba(0, 0, 0, 0.0); background: rgba(0, 0, 0, 0);
z-index: 100; z-index: 100;
.context-menu-item { .context-menu-item {
@ -808,14 +803,14 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
box-shadow: var(--ciderShadow-Generic); box-shadow: var(--ciderShadow-Generic);
&.context-menu-open { &.context-menu-open {
animation-duration : .10s; animation-duration: 0.1s;
animation-name: contextMenuIn; animation-name: contextMenuIn;
animation-iteration-count: 1; animation-iteration-count: 1;
animation-easings: var(--appleEase); animation-easings: var(--appleEase);
} }
&.context-menu-close { &.context-menu-close {
animation-duration : .10s; animation-duration: 0.1s;
animation-name: contextMenuOut; animation-name: contextMenuOut;
animation-iteration-count: 1; animation-iteration-count: 1;
animation-easings: var(--appleEase); animation-easings: var(--appleEase);
@ -908,7 +903,6 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
> ._svg-icon { > ._svg-icon {
--size: var(--iconSize); --size: var(--iconSize);
} }
} }
.app-sidebar-item:hover { .app-sidebar-item:hover {
@ -934,8 +928,7 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
.app-chrome { .app-chrome {
background-color: var(--baseColorMix); background-color: var(--baseColorMix);
box-shadow : 0px 3px 6px rgb(20 20 20 / 12%), box-shadow: 0px 3px 6px rgb(20 20 20 / 12%), 0px 1px 0px 0px rgb(200 200 200 / 12%);
0px 1px 0px 0px rgb(200 200 200 / 12%);
width: 100%; width: 100%;
height: var(--chromeHeight1); height: var(--chromeHeight1);
display: flex; display: flex;
@ -1023,7 +1016,6 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
} }
.app-chrome .app-chrome--center { .app-chrome .app-chrome--center {
//width: 40%; //width: 40%;
.app-title-text { .app-title-text {
font-size: 0.8em; font-size: 0.8em;
@ -1044,10 +1036,9 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
-webkit-app-region: no-drag; -webkit-app-region: no-drag;
height: auto; height: auto;
&.generic { &.generic {
width: 50px; width: 50px;
opacity: 0.70; opacity: 0.7;
} }
&.volume { &.volume {
@ -1072,7 +1063,7 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
background-size: 12px; background-size: 12px;
background-position: center; background-position: center;
background-repeat: no-repeat; background-repeat: no-repeat;
opacity : 0.70; opacity: 0.7;
border-radius: 6px; border-radius: 6px;
} }
@ -1097,32 +1088,32 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
width: 30px; width: 30px;
border: 0px; border: 0px;
box-shadow: unset; box-shadow: unset;
opacity : 0.70; opacity: 0.7;
background-image: url("./assets/feather/volume-2.svg"); background-image: url("./assets/feather/volume-2.svg");
} }
.app-chrome .app-chrome-item.volume>input[type=range]::-webkit-slider-thumb { .app-chrome .app-chrome-item.volume > input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none; -webkit-appearance: none;
height: 14px; height: 14px;
width: 14px; width: 14px;
border-radius: 50%; border-radius: 50%;
background : #A5A8BA; background: #a5a8ba;
box-shadow: 0px 0px 0px 1px rgba(0 0 0 / 10%); box-shadow: 0px 0px 0px 1px rgba(0 0 0 / 10%);
cursor: default; cursor: default;
transition: all var(--appleTransition); transition: all var(--appleTransition);
} }
.app-chrome .app-chrome-item.volume>input[type=range] { .app-chrome .app-chrome-item.volume > input[type="range"] {
-webkit-appearance: none; -webkit-appearance: none;
height: 4px; height: 4px;
background: rgba(255, 255, 255, 0.4); background: rgba(255, 255, 255, 0.4);
border-radius: 5px; border-radius: 5px;
background-size: 70% 100%; background-size: 70% 100%;
background-repeat: no-repeat; background-repeat: no-repeat;
width : 100%, width: 100%;
} }
.app-chrome .app-chrome-item.volume>input[type=range]::-webkit-slider-runnable-track { .app-chrome .app-chrome-item.volume > input[type="range"]::-webkit-slider-runnable-track {
-webkit-appearance: none; -webkit-appearance: none;
box-shadow: none; box-shadow: none;
border: none; border: none;
@ -1151,7 +1142,7 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
margin-top: 1.5px; margin-top: 1.5px;
border: 0px; border: 0px;
border-radius: 6px; border-radius: 6px;
transition : transform .1s var(--appleEase); transition: transform 0.1s var(--appleEase);
position: relative; position: relative;
// &:after { // &:after {
@ -1208,7 +1199,7 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
-webkit-app-region: no-drag; -webkit-app-region: no-drag;
&:hover { &:hover {
background-color: rgb(196, 43, 28) background-color: rgb(196, 43, 28);
} }
} }
@ -1401,7 +1392,6 @@ div[data-type="musicVideo"] .info-rect .title::before {
} }
} }
.marquee { .marquee {
animation: marquee 15s linear 2s infinite; animation: marquee 15s linear 2s infinite;
@ -1422,7 +1412,6 @@ div[data-type="musicVideo"] .info-rect .title::before {
} }
} }
.app-chrome .app-chrome-item > .app-playback-controls .song-progress { .app-chrome .app-chrome-item > .app-playback-controls .song-progress {
@bgColor: transparent; @bgColor: transparent;
//height: 16px; //height: 16px;
@ -1432,7 +1421,7 @@ div[data-type="musicVideo"] .info-rect .title::before {
background: @bgColor; background: @bgColor;
&:hover { &:hover {
>input[type=range] { > input[type="range"] {
&::-webkit-slider-thumb { &::-webkit-slider-thumb {
opacity: 1; opacity: 1;
transform: scale(1); transform: scale(1);
@ -1441,7 +1430,7 @@ div[data-type="musicVideo"] .info-rect .title::before {
} }
} }
>input[type=range] { > input[type="range"] {
appearance: none; appearance: none;
width: 100%; width: 100%;
height: 4px; height: 4px;
@ -1459,7 +1448,7 @@ div[data-type="musicVideo"] .info-rect .title::before {
border-radius: 100%; border-radius: 100%;
background: var(--songProgressColor); background: var(--songProgressColor);
cursor: default; cursor: default;
transition : opacity .10s var(--appleEase), transform .10s var(--appleEase); transition: opacity 0.1s var(--appleEase), transform 0.1s var(--appleEase);
} }
&::-moz-range-thumb { &::-moz-range-thumb {
@ -1537,7 +1526,7 @@ div[data-type="musicVideo"] .info-rect .title::before {
} }
.svg-icon { .svg-icon {
--url: url('views/svg/more.svg') !important; --url: url("views/svg/more.svg") !important;
} }
} }
} }
@ -1591,7 +1580,6 @@ div[data-type="musicVideo"] .info-rect .title::before {
} }
} }
&.song-artist-marquee { &.song-artist-marquee {
> marquee { > marquee {
margin-bottom: -3px; margin-bottom: -3px;
@ -1621,8 +1609,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
@media only screen and (max-width: 1120px) { @media only screen and (max-width: 1120px) {
.display--small { .display--small {
display: inherit !important; display: inherit !important;
;
.slider { .slider {
width: 100%; width: 100%;
z-index: 1; z-index: 1;
@ -1636,7 +1622,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
padding-bottom: 10px; padding-bottom: 10px;
} }
input[type=range] { input[type="range"] {
-webkit-appearance: none; -webkit-appearance: none;
height: 4px; height: 4px;
background: rgba(255, 255, 255, 0.4); background: rgba(255, 255, 255, 0.4);
@ -1674,7 +1660,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
} }
} }
.display--large { .display--large {
display: none !important; display: none !important;
} }
@ -1691,14 +1676,13 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
position: relative; position: relative;
} }
.lyric-body { .lyric-body {
-webkit-mask-image: -webkit-gradient(linear, left 95%, left bottom, from(rgba(0, 0, 0, 1)), to(rgba(0, 0, 0, 0))); -webkit-mask-image: -webkit-gradient(linear, left 95%, left bottom, from(rgba(0, 0, 0, 1)), to(rgba(0, 0, 0, 0)));
overflow-y: scroll; overflow-y: scroll;
overflow-x: hidden; overflow-x: hidden;
display: flex; display: flex;
flex-flow: column; flex-flow: column;
font-family : "Pretendard Variable", "Noto Sans JP", "Noto Sans KR", "Noto Sans TC", "Noto Sans SC", -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; font-family: "Pretendard Variable", "Noto Sans JP", "Noto Sans KR", "Noto Sans TC", "Noto Sans SC", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
} }
.lyric-body .no-lyrics { .lyric-body .no-lyrics {
@ -1740,7 +1724,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
} }
.lyric-line:hover::after { .lyric-line:hover::after {
content : ' '; content: " ";
width: 100%; width: 100%;
height: 100%; height: 100%;
position: absolute; position: absolute;
@ -1762,7 +1746,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
} }
.lyric-line:not(.active) { .lyric-line:not(.active) {
filter: blur(1px) filter: blur(1px);
} }
.lyric-line:not(.active).unsynced { .lyric-line:not(.active).unsynced {
@ -1773,7 +1757,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
filter: none !important; filter: none !important;
} }
.lyric-body:hover > .lyric-line:not(.active) { .lyric-body:hover > .lyric-line:not(.active) {
filter: none !important; filter: none !important;
} }
@ -1792,7 +1775,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
animation: lyricWaitingLine 6s cubic-bezier(0.42, 0, 0.58, 1) infinite; animation: lyricWaitingLine 6s cubic-bezier(0.42, 0, 0.58, 1) infinite;
} }
.lyric-line.active .lyricWaiting > div { .lyric-line.active .lyricWaiting > div {
width: 10px; width: 10px;
height: 10px; height: 10px;
@ -1804,7 +1786,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.lyrics-translation { .lyrics-translation {
font-size: 1.6rem; font-size: 1.6rem;
font-weight: 450; font-weight: 450;
font-family: "Pretendard Variable", "Noto Sans JP", "Noto Sans KR", "Noto Sans TC", "Noto Sans SC", -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; font-family: "Pretendard Variable", "Noto Sans JP", "Noto Sans KR", "Noto Sans TC", "Noto Sans SC", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
filter: contrast(0.5); filter: contrast(0.5);
} }
@ -1869,10 +1851,9 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
} }
} }
.lyric-line2:before { .lyric-line2:before {
background: var(--keyColor); background: var(--keyColor);
content : ''; content: "";
width: 0%; width: 0%;
height: 6px; height: 6px;
position: absolute; position: absolute;
@ -1976,11 +1957,11 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
} }
&.githubBtn { &.githubBtn {
background-color: #211F1F; background-color: #211f1f;
} }
&.kofiBtn { &.kofiBtn {
background-color: #FBAA19; background-color: #fbaa19;
} }
&.opencollectiveBtn { &.opencollectiveBtn {
@ -1988,16 +1969,15 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
} }
&.discordBtn { &.discordBtn {
background-color: #5865F2; background-color: #5865f2;
} }
&.twitterBtn { &.twitterBtn {
background-color: #1D9BF0; background-color: #1d9bf0;
} }
} }
} }
.sidebar-playlist { .sidebar-playlist {
.folder-button-active { .folder-button-active {
background: rgb(255 255 255 / 12%); background: rgb(255 255 255 / 12%);
@ -2045,7 +2025,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
color: rgba(200, 200, 200, 0.8); color: rgba(200, 200, 200, 0.8);
margin: 2px; margin: 2px;
border-radius: 6px; border-radius: 6px;
transition : transform .10s var(--appleEase); transition: transform 0.1s var(--appleEase);
&:active { &:active {
background: var(--selected-click); background: var(--selected-click);
@ -2062,7 +2042,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
background: var(--selected); background: var(--selected);
> svg { > svg {
color: rgba(200, 200, 200, 1.0); color: rgba(200, 200, 200, 1);
} }
} }
} }
@ -2074,7 +2054,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
padding: var(--contentInnerPadding); padding: var(--contentInnerPadding);
margin-top: 16px; margin-top: 16px;
&.itemContainer { &.itemContainer {
display: flex; display: flex;
flex-flow: wrap; flex-flow: wrap;
@ -2164,7 +2143,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
} }
} }
@keyframes rotate { @keyframes rotate {
from { from {
transform: rotate(0deg); transform: rotate(0deg);
@ -2204,8 +2182,8 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
&::before { &::before {
top: -50%; top: -50%;
left: -20%; left: -20%;
width : 200VH; width: 200vh;
height: 200VH; height: 200vh;
} }
.bg-artwork-container { .bg-artwork-container {
@ -2226,7 +2204,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
font-weight: 600; font-weight: 600;
font-size: 2em; font-size: 2em;
transform-origin: center; transform-origin: center;
animation : fsLyricIn var(--appleEase) .2s; animation: fsLyricIn var(--appleEase) 0.2s;
opacity: 0.9; opacity: 0.9;
&:not(.active) { &:not(.active) {
@ -2247,7 +2225,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
@keyframes fsLyricIn { @keyframes fsLyricIn {
0% { 0% {
opacity: 0; opacity: 0;
transform: scale(0.98) transform: scale(0.98);
} }
100% { 100% {
@ -2262,7 +2240,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.replaycard-enter-active, .replaycard-enter-active,
.replaycard-leave-active { .replaycard-leave-active {
transition: opacity .5s var(--appleEase), transform .5s var(--appleEase); transition: opacity 0.5s var(--appleEase), transform 0.5s var(--appleEase);
} }
.replaycard-enter, .replaycard-enter,
@ -2273,7 +2251,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.fade-enter-active, .fade-enter-active,
.fade-leave-active { .fade-leave-active {
transition: opacity .15s var(--appleEase); transition: opacity 0.15s var(--appleEase);
} }
.fade-enter, .fade-enter,
@ -2283,18 +2261,18 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.modal-enter-active, .modal-enter-active,
.modal-leave-active { .modal-leave-active {
transition: opacity .1s var(--appleEase), transform .1s var(--appleEase); transition: opacity 0.1s var(--appleEase), transform 0.1s var(--appleEase);
} }
.modal-enter, .modal-enter,
.modal-leave-to { .modal-leave-to {
opacity: 0; opacity: 0;
transform: scale(1.10); transform: scale(1.1);
} }
.wpfade-enter-active, .wpfade-enter-active,
.wpfade-leave-active { .wpfade-leave-active {
transition: opacity .1s var(--appleEase); transition: opacity 0.1s var(--appleEase);
} }
.wpfade-enter, .wpfade-enter,
@ -2321,7 +2299,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
will-change: opacity; will-change: opacity;
} }
.wpfade_transform_backwards-enter-active, .wpfade_transform_backwards-enter-active,
.wpfade_transform_backwards-leave-active { .wpfade_transform_backwards-leave-active {
--transitionTime: 0.2s; --transitionTime: 0.2s;
@ -2342,7 +2319,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.fabfade-enter-active, .fabfade-enter-active,
.fabfade-leave-active { .fabfade-leave-active {
transition: transform .1s var(--appleEase), opacity .1s var(--appleEase); transition: transform 0.1s var(--appleEase), opacity 0.1s var(--appleEase);
} }
.fabfade-enter, .fabfade-enter,
@ -2358,13 +2335,13 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.fsModeSwitch-enter, .fsModeSwitch-enter,
.fsModeSwitch-leave-to { .fsModeSwitch-leave-to {
transform: scale(1.10); transform: scale(1.1);
opacity: 0; opacity: 0;
} }
.sidebartransition-enter-active, .sidebartransition-enter-active,
.sidebartransition-leave-active { .sidebartransition-leave-active {
transition: margin-left .35s var(--appleEase); transition: margin-left 0.35s var(--appleEase);
} }
.sidebartransition-enter, .sidebartransition-enter,
@ -2374,7 +2351,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.drawertransition-enter-active, .drawertransition-enter-active,
.drawertransition-leave-active { .drawertransition-leave-active {
transition: transform .25s var(--appleEase); transition: transform 0.25s var(--appleEase);
} }
.drawertransition-enter, .drawertransition-enter,
@ -2382,13 +2359,14 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
transform: translateX(400px); transform: translateX(400px);
} }
@media (prefers-color-scheme: dark) {} @media (prefers-color-scheme: dark) {
}
:root { :root {
--gfx-closeBtn : url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAIn2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iCiAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgZGM6Zm9ybWF0PSJpbWFnZS9wbmciCiAgIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiCiAgIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIKICAgdGlmZjpJbWFnZUxlbmd0aD0iMTAiCiAgIHRpZmY6SW1hZ2VXaWR0aD0iMTAiCiAgIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiCiAgIHRpZmY6WFJlc29sdXRpb249IjcyLjAiCiAgIHRpZmY6WVJlc29sdXRpb249IjcyLjAiCiAgIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTAyLTE3VDEyOjU1OjM3WiIKICAgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjEtMTAtMDVUMTQ6Mjc6MzYtMDc6MDAiCiAgIHhtcDpNb2RpZnlEYXRlPSIyMDIxLTEwLTA1VDE0OjI3OjM2LTA3OjAwIgogICB4bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6ZTk5OWM2NWYtNDhhOS0wNjQyLWI2MTktZmJlYTExMmUxOGZiIgogICB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjY5MzMyOWNhLWNkNjctMzY0Zi04MzU1LTY5N2ZmYzI0ZDdlZCIKICAgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjgyZjQwYmU3LTE0YzItZjc0Ni1hZmE1LWQxYmIxNzAyMjM4OCIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjEwIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iMTAiCiAgIGV4aWY6Q29sb3JTcGFjZT0iMSI+CiAgIDxwaG90b3Nob3A6VGV4dExheWVycz4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgcGhvdG9zaG9wOkxheWVyTmFtZT0i7qSiIgogICAgICBwaG90b3Nob3A6TGF5ZXJUZXh0PSLupKIiLz4KICAgIDwvcmRmOlNlcT4KICAgPC9waG90b3Nob3A6VGV4dExheWVycz4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHhtcE1NOmFjdGlvbj0iY3JlYXRlZCIKICAgICAgeG1wTU06aW5zdGFuY2VJRD0ieG1wLmlpZDo4MmY0MGJlNy0xNGMyLWY3NDYtYWZhNS1kMWJiMTcwMjIzODgiCiAgICAgIHhtcE1NOnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIKICAgICAgeG1wTU06d2hlbj0iMjAyMC0wMi0xN1QxMjo1NTozN1oiLz4KICAgICA8cmRmOmxpCiAgICAgIHhtcE1NOmFjdGlvbj0ic2F2ZWQiCiAgICAgIHhtcE1NOmNoYW5nZWQ9Ii8iCiAgICAgIHhtcE1NOmluc3RhbmNlSUQ9InhtcC5paWQ6NjkzMzI5Y2EtY2Q2Ny0zNjRmLTgzNTUtNjk3ZmZjMjRkN2VkIgogICAgICB4bXBNTTpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiCiAgICAgIHhtcE1NOndoZW49IjIwMjAtMDItMTdUMTI6NTU6MzdaIi8+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InByb2R1Y2VkIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZmZpbml0eSBQaG90byAxLjEwLjEiCiAgICAgIHN0RXZ0OndoZW49IjIwMjEtMTAtMDVUMTQ6Mjc6MzYtMDc6MDAiLz4KICAgIDwvcmRmOlNlcT4KICAgPC94bXBNTTpIaXN0b3J5PgogIDwvcmRmOkRlc2NyaXB0aW9uPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KPD94cGFja2V0IGVuZD0iciI/PmN2D9EAAAGCaUNDUHNSR0IgSUVDNjE5NjYtMi4xAAAokXWRv0tCURTHP2lhmGFRQUODhDVZlELU0qD0C6pBDbJa9OWPQO3xnhHRGrQKBVFLv4b6C2oNmoOgKIJoC5qLWkpe56mgRJ7Luedzv/eew73ngiWcVjJ6/QBksjktOOF3zUcWXLZX7DTQQSu+qKKrM6HxMDXt64E6M971mbVqn/vXmpbjugJ1jcKjiqrlhCeFp9dzqsm7wu1KKrosfC7s0eSCwvemHivxm8nJEv+YrIWDAbC0CLuSVRyrYiWlZYTl5bgz6TWlfB/zJY54di4ksVu8C50gE/hxMcUYAYYYZETmIfrw0i8rauQPFPNnWZVcRWaVDTRWSJIih0fUNakel5gQPS4jzYbZ/7991RM+b6m6ww8NL4bx0QO2HSjkDeP72DAKJ2B9hqtsJX/1CIY/Rc9XNPchOLfg4rqixfbgchs6n9SoFi1KVnFLIgHvZ9AcgbZbsC+Welbe5/QRwpvyVTewfwC9ct659At2bGftHD0UJwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAEtJREFUGJWNkMENwDAIA1FGY/8hkn8HOAqPfBsFKvz1yZYtbqwAlUIB6saUAH2NJ4MvL4PLgK/x13LAGTSqEaVa1a0x7XvcmI3D1wbntaRbB2haYwAAAABJRU5ErkJggg=='); --gfx-closeBtn: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAIn2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iCiAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgZGM6Zm9ybWF0PSJpbWFnZS9wbmciCiAgIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiCiAgIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIKICAgdGlmZjpJbWFnZUxlbmd0aD0iMTAiCiAgIHRpZmY6SW1hZ2VXaWR0aD0iMTAiCiAgIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiCiAgIHRpZmY6WFJlc29sdXRpb249IjcyLjAiCiAgIHRpZmY6WVJlc29sdXRpb249IjcyLjAiCiAgIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTAyLTE3VDEyOjU1OjM3WiIKICAgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjEtMTAtMDVUMTQ6Mjc6MzYtMDc6MDAiCiAgIHhtcDpNb2RpZnlEYXRlPSIyMDIxLTEwLTA1VDE0OjI3OjM2LTA3OjAwIgogICB4bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6ZTk5OWM2NWYtNDhhOS0wNjQyLWI2MTktZmJlYTExMmUxOGZiIgogICB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjY5MzMyOWNhLWNkNjctMzY0Zi04MzU1LTY5N2ZmYzI0ZDdlZCIKICAgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjgyZjQwYmU3LTE0YzItZjc0Ni1hZmE1LWQxYmIxNzAyMjM4OCIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjEwIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iMTAiCiAgIGV4aWY6Q29sb3JTcGFjZT0iMSI+CiAgIDxwaG90b3Nob3A6VGV4dExheWVycz4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgcGhvdG9zaG9wOkxheWVyTmFtZT0i7qSiIgogICAgICBwaG90b3Nob3A6TGF5ZXJUZXh0PSLupKIiLz4KICAgIDwvcmRmOlNlcT4KICAgPC9waG90b3Nob3A6VGV4dExheWVycz4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHhtcE1NOmFjdGlvbj0iY3JlYXRlZCIKICAgICAgeG1wTU06aW5zdGFuY2VJRD0ieG1wLmlpZDo4MmY0MGJlNy0xNGMyLWY3NDYtYWZhNS1kMWJiMTcwMjIzODgiCiAgICAgIHhtcE1NOnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIKICAgICAgeG1wTU06d2hlbj0iMjAyMC0wMi0xN1QxMjo1NTozN1oiLz4KICAgICA8cmRmOmxpCiAgICAgIHhtcE1NOmFjdGlvbj0ic2F2ZWQiCiAgICAgIHhtcE1NOmNoYW5nZWQ9Ii8iCiAgICAgIHhtcE1NOmluc3RhbmNlSUQ9InhtcC5paWQ6NjkzMzI5Y2EtY2Q2Ny0zNjRmLTgzNTUtNjk3ZmZjMjRkN2VkIgogICAgICB4bXBNTTpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiCiAgICAgIHhtcE1NOndoZW49IjIwMjAtMDItMTdUMTI6NTU6MzdaIi8+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InByb2R1Y2VkIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZmZpbml0eSBQaG90byAxLjEwLjEiCiAgICAgIHN0RXZ0OndoZW49IjIwMjEtMTAtMDVUMTQ6Mjc6MzYtMDc6MDAiLz4KICAgIDwvcmRmOlNlcT4KICAgPC94bXBNTTpIaXN0b3J5PgogIDwvcmRmOkRlc2NyaXB0aW9uPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KPD94cGFja2V0IGVuZD0iciI/PmN2D9EAAAGCaUNDUHNSR0IgSUVDNjE5NjYtMi4xAAAokXWRv0tCURTHP2lhmGFRQUODhDVZlELU0qD0C6pBDbJa9OWPQO3xnhHRGrQKBVFLv4b6C2oNmoOgKIJoC5qLWkpe56mgRJ7Luedzv/eew73ngiWcVjJ6/QBksjktOOF3zUcWXLZX7DTQQSu+qKKrM6HxMDXt64E6M971mbVqn/vXmpbjugJ1jcKjiqrlhCeFp9dzqsm7wu1KKrosfC7s0eSCwvemHivxm8nJEv+YrIWDAbC0CLuSVRyrYiWlZYTl5bgz6TWlfB/zJY54di4ksVu8C50gE/hxMcUYAYYYZETmIfrw0i8rauQPFPNnWZVcRWaVDTRWSJIih0fUNakel5gQPS4jzYbZ/7991RM+b6m6ww8NL4bx0QO2HSjkDeP72DAKJ2B9hqtsJX/1CIY/Rc9XNPchOLfg4rqixfbgchs6n9SoFi1KVnFLIgHvZ9AcgbZbsC+Welbe5/QRwpvyVTewfwC9ct659At2bGftHD0UJwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAEtJREFUGJWNkMENwDAIA1FGY/8hkn8HOAqPfBsFKvz1yZYtbqwAlUIB6saUAH2NJ4MvL4PLgK/x13LAGTSqEaVa1a0x7XvcmI3D1wbntaRbB2haYwAAAABJRU5ErkJggg==");
--gfx-maxBtn : url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAIn2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iCiAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgZGM6Zm9ybWF0PSJpbWFnZS9wbmciCiAgIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiCiAgIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIKICAgdGlmZjpJbWFnZUxlbmd0aD0iMTAiCiAgIHRpZmY6SW1hZ2VXaWR0aD0iMTAiCiAgIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiCiAgIHRpZmY6WFJlc29sdXRpb249IjcyLjAiCiAgIHRpZmY6WVJlc29sdXRpb249IjcyLjAiCiAgIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTAyLTE3VDEyOjU1OjM3WiIKICAgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjEtMTAtMDVUMTQ6Mjc6NTgtMDc6MDAiCiAgIHhtcDpNb2RpZnlEYXRlPSIyMDIxLTEwLTA1VDE0OjI3OjU4LTA3OjAwIgogICB4bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6ZTk5OWM2NWYtNDhhOS0wNjQyLWI2MTktZmJlYTExMmUxOGZiIgogICB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjY5MzMyOWNhLWNkNjctMzY0Zi04MzU1LTY5N2ZmYzI0ZDdlZCIKICAgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjgyZjQwYmU3LTE0YzItZjc0Ni1hZmE1LWQxYmIxNzAyMjM4OCIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjEwIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iMTAiCiAgIGV4aWY6Q29sb3JTcGFjZT0iMSI+CiAgIDxwaG90b3Nob3A6VGV4dExheWVycz4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgcGhvdG9zaG9wOkxheWVyTmFtZT0i7qSiIgogICAgICBwaG90b3Nob3A6TGF5ZXJUZXh0PSLupKIiLz4KICAgIDwvcmRmOlNlcT4KICAgPC9waG90b3Nob3A6VGV4dExheWVycz4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHhtcE1NOmFjdGlvbj0iY3JlYXRlZCIKICAgICAgeG1wTU06aW5zdGFuY2VJRD0ieG1wLmlpZDo4MmY0MGJlNy0xNGMyLWY3NDYtYWZhNS1kMWJiMTcwMjIzODgiCiAgICAgIHhtcE1NOnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIKICAgICAgeG1wTU06d2hlbj0iMjAyMC0wMi0xN1QxMjo1NTozN1oiLz4KICAgICA8cmRmOmxpCiAgICAgIHhtcE1NOmFjdGlvbj0ic2F2ZWQiCiAgICAgIHhtcE1NOmNoYW5nZWQ9Ii8iCiAgICAgIHhtcE1NOmluc3RhbmNlSUQ9InhtcC5paWQ6NjkzMzI5Y2EtY2Q2Ny0zNjRmLTgzNTUtNjk3ZmZjMjRkN2VkIgogICAgICB4bXBNTTpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiCiAgICAgIHhtcE1NOndoZW49IjIwMjAtMDItMTdUMTI6NTU6MzdaIi8+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InByb2R1Y2VkIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZmZpbml0eSBQaG90byAxLjEwLjEiCiAgICAgIHN0RXZ0OndoZW49IjIwMjEtMTAtMDVUMTQ6Mjc6NTgtMDc6MDAiLz4KICAgIDwvcmRmOlNlcT4KICAgPC94bXBNTTpIaXN0b3J5PgogIDwvcmRmOkRlc2NyaXB0aW9uPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KPD94cGFja2V0IGVuZD0iciI/PlwQMBUAAAGCaUNDUHNSR0IgSUVDNjE5NjYtMi4xAAAokXWRv0tCURTHP2lhmGFRQUODhDVZlELU0qD0C6pBDbJa9OWPQO3xnhHRGrQKBVFLv4b6C2oNmoOgKIJoC5qLWkpe56mgRJ7Luedzv/eew73ngiWcVjJ6/QBksjktOOF3zUcWXLZX7DTQQSu+qKKrM6HxMDXt64E6M971mbVqn/vXmpbjugJ1jcKjiqrlhCeFp9dzqsm7wu1KKrosfC7s0eSCwvemHivxm8nJEv+YrIWDAbC0CLuSVRyrYiWlZYTl5bgz6TWlfB/zJY54di4ksVu8C50gE/hxMcUYAYYYZETmIfrw0i8rauQPFPNnWZVcRWaVDTRWSJIih0fUNakel5gQPS4jzYbZ/7991RM+b6m6ww8NL4bx0QO2HSjkDeP72DAKJ2B9hqtsJX/1CIY/Rc9XNPchOLfg4rqixfbgchs6n9SoFi1KVnFLIgHvZ9AcgbZbsC+Welbe5/QRwpvyVTewfwC9ct659At2bGftHD0UJwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAFBJREFUGJXV0LERgCAUBNHVsQADM3uwWWbojQIs47MEGhgAuS/eSw41qeFYqGlRA7iAm74DKLyrfRABoLrOgq+/hJXngi71BOoGZKBMHqhAbtMvQzel9pREAAAAAElFTkSuQmCC'); --gfx-maxBtn: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAIn2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iCiAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgZGM6Zm9ybWF0PSJpbWFnZS9wbmciCiAgIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiCiAgIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIKICAgdGlmZjpJbWFnZUxlbmd0aD0iMTAiCiAgIHRpZmY6SW1hZ2VXaWR0aD0iMTAiCiAgIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiCiAgIHRpZmY6WFJlc29sdXRpb249IjcyLjAiCiAgIHRpZmY6WVJlc29sdXRpb249IjcyLjAiCiAgIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTAyLTE3VDEyOjU1OjM3WiIKICAgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjEtMTAtMDVUMTQ6Mjc6NTgtMDc6MDAiCiAgIHhtcDpNb2RpZnlEYXRlPSIyMDIxLTEwLTA1VDE0OjI3OjU4LTA3OjAwIgogICB4bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6ZTk5OWM2NWYtNDhhOS0wNjQyLWI2MTktZmJlYTExMmUxOGZiIgogICB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjY5MzMyOWNhLWNkNjctMzY0Zi04MzU1LTY5N2ZmYzI0ZDdlZCIKICAgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjgyZjQwYmU3LTE0YzItZjc0Ni1hZmE1LWQxYmIxNzAyMjM4OCIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjEwIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iMTAiCiAgIGV4aWY6Q29sb3JTcGFjZT0iMSI+CiAgIDxwaG90b3Nob3A6VGV4dExheWVycz4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgcGhvdG9zaG9wOkxheWVyTmFtZT0i7qSiIgogICAgICBwaG90b3Nob3A6TGF5ZXJUZXh0PSLupKIiLz4KICAgIDwvcmRmOlNlcT4KICAgPC9waG90b3Nob3A6VGV4dExheWVycz4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHhtcE1NOmFjdGlvbj0iY3JlYXRlZCIKICAgICAgeG1wTU06aW5zdGFuY2VJRD0ieG1wLmlpZDo4MmY0MGJlNy0xNGMyLWY3NDYtYWZhNS1kMWJiMTcwMjIzODgiCiAgICAgIHhtcE1NOnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIKICAgICAgeG1wTU06d2hlbj0iMjAyMC0wMi0xN1QxMjo1NTozN1oiLz4KICAgICA8cmRmOmxpCiAgICAgIHhtcE1NOmFjdGlvbj0ic2F2ZWQiCiAgICAgIHhtcE1NOmNoYW5nZWQ9Ii8iCiAgICAgIHhtcE1NOmluc3RhbmNlSUQ9InhtcC5paWQ6NjkzMzI5Y2EtY2Q2Ny0zNjRmLTgzNTUtNjk3ZmZjMjRkN2VkIgogICAgICB4bXBNTTpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiCiAgICAgIHhtcE1NOndoZW49IjIwMjAtMDItMTdUMTI6NTU6MzdaIi8+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InByb2R1Y2VkIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZmZpbml0eSBQaG90byAxLjEwLjEiCiAgICAgIHN0RXZ0OndoZW49IjIwMjEtMTAtMDVUMTQ6Mjc6NTgtMDc6MDAiLz4KICAgIDwvcmRmOlNlcT4KICAgPC94bXBNTTpIaXN0b3J5PgogIDwvcmRmOkRlc2NyaXB0aW9uPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KPD94cGFja2V0IGVuZD0iciI/PlwQMBUAAAGCaUNDUHNSR0IgSUVDNjE5NjYtMi4xAAAokXWRv0tCURTHP2lhmGFRQUODhDVZlELU0qD0C6pBDbJa9OWPQO3xnhHRGrQKBVFLv4b6C2oNmoOgKIJoC5qLWkpe56mgRJ7Luedzv/eew73ngiWcVjJ6/QBksjktOOF3zUcWXLZX7DTQQSu+qKKrM6HxMDXt64E6M971mbVqn/vXmpbjugJ1jcKjiqrlhCeFp9dzqsm7wu1KKrosfC7s0eSCwvemHivxm8nJEv+YrIWDAbC0CLuSVRyrYiWlZYTl5bgz6TWlfB/zJY54di4ksVu8C50gE/hxMcUYAYYYZETmIfrw0i8rauQPFPNnWZVcRWaVDTRWSJIih0fUNakel5gQPS4jzYbZ/7991RM+b6m6ww8NL4bx0QO2HSjkDeP72DAKJ2B9hqtsJX/1CIY/Rc9XNPchOLfg4rqixfbgchs6n9SoFi1KVnFLIgHvZ9AcgbZbsC+Welbe5/QRwpvyVTewfwC9ct659At2bGftHD0UJwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAFBJREFUGJXV0LERgCAUBNHVsQADM3uwWWbojQIs47MEGhgAuS/eSw41qeFYqGlRA7iAm74DKLyrfRABoLrOgq+/hJXngi71BOoGZKBMHqhAbtMvQzel9pREAAAAAElFTkSuQmCC");
--gfx-restoreBtn: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAIn2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iCiAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgZGM6Zm9ybWF0PSJpbWFnZS9wbmciCiAgIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiCiAgIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIKICAgdGlmZjpJbWFnZUxlbmd0aD0iMTAiCiAgIHRpZmY6SW1hZ2VXaWR0aD0iMTAiCiAgIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiCiAgIHRpZmY6WFJlc29sdXRpb249IjcyLjAiCiAgIHRpZmY6WVJlc29sdXRpb249IjcyLjAiCiAgIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTAyLTE3VDEyOjU1OjM3WiIKICAgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjEtMTAtMDVUMTQ6Mjc6MjQtMDc6MDAiCiAgIHhtcDpNb2RpZnlEYXRlPSIyMDIxLTEwLTA1VDE0OjI3OjI0LTA3OjAwIgogICB4bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6ZTk5OWM2NWYtNDhhOS0wNjQyLWI2MTktZmJlYTExMmUxOGZiIgogICB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjY5MzMyOWNhLWNkNjctMzY0Zi04MzU1LTY5N2ZmYzI0ZDdlZCIKICAgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjgyZjQwYmU3LTE0YzItZjc0Ni1hZmE1LWQxYmIxNzAyMjM4OCIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjEwIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iMTAiCiAgIGV4aWY6Q29sb3JTcGFjZT0iMSI+CiAgIDxwaG90b3Nob3A6VGV4dExheWVycz4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgcGhvdG9zaG9wOkxheWVyTmFtZT0i7qSiIgogICAgICBwaG90b3Nob3A6TGF5ZXJUZXh0PSLupKIiLz4KICAgIDwvcmRmOlNlcT4KICAgPC9waG90b3Nob3A6VGV4dExheWVycz4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHhtcE1NOmFjdGlvbj0iY3JlYXRlZCIKICAgICAgeG1wTU06aW5zdGFuY2VJRD0ieG1wLmlpZDo4MmY0MGJlNy0xNGMyLWY3NDYtYWZhNS1kMWJiMTcwMjIzODgiCiAgICAgIHhtcE1NOnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIKICAgICAgeG1wTU06d2hlbj0iMjAyMC0wMi0xN1QxMjo1NTozN1oiLz4KICAgICA8cmRmOmxpCiAgICAgIHhtcE1NOmFjdGlvbj0ic2F2ZWQiCiAgICAgIHhtcE1NOmNoYW5nZWQ9Ii8iCiAgICAgIHhtcE1NOmluc3RhbmNlSUQ9InhtcC5paWQ6NjkzMzI5Y2EtY2Q2Ny0zNjRmLTgzNTUtNjk3ZmZjMjRkN2VkIgogICAgICB4bXBNTTpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiCiAgICAgIHhtcE1NOndoZW49IjIwMjAtMDItMTdUMTI6NTU6MzdaIi8+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InByb2R1Y2VkIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZmZpbml0eSBQaG90byAxLjEwLjEiCiAgICAgIHN0RXZ0OndoZW49IjIwMjEtMTAtMDVUMTQ6Mjc6MjQtMDc6MDAiLz4KICAgIDwvcmRmOlNlcT4KICAgPC94bXBNTTpIaXN0b3J5PgogIDwvcmRmOkRlc2NyaXB0aW9uPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KPD94cGFja2V0IGVuZD0iciI/PqiFCFwAAAGCaUNDUHNSR0IgSUVDNjE5NjYtMi4xAAAokXWRv0tCURTHP2lhmGFRQUODhDVZlELU0qD0C6pBDbJa9OWPQO3xnhHRGrQKBVFLv4b6C2oNmoOgKIJoC5qLWkpe56mgRJ7Luedzv/eew73ngiWcVjJ6/QBksjktOOF3zUcWXLZX7DTQQSu+qKKrM6HxMDXt64E6M971mbVqn/vXmpbjugJ1jcKjiqrlhCeFp9dzqsm7wu1KKrosfC7s0eSCwvemHivxm8nJEv+YrIWDAbC0CLuSVRyrYiWlZYTl5bgz6TWlfB/zJY54di4ksVu8C50gE/hxMcUYAYYYZETmIfrw0i8rauQPFPNnWZVcRWaVDTRWSJIih0fUNakel5gQPS4jzYbZ/7991RM+b6m6ww8NL4bx0QO2HSjkDeP72DAKJ2B9hqtsJX/1CIY/Rc9XNPchOLfg4rqixfbgchs6n9SoFi1KVnFLIgHvZ9AcgbZbsC+Welbe5/QRwpvyVTewfwC9ct659At2bGftHD0UJwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAHNJREFUGJWtkKEOwlAMRc+QM5AwQYJFoPjZCWb2YRPIaeRTLwfTLQs0UxzX3tumtxCog78UdVTbZmM8AmsdXIABeKH2ak221dDuamnUCjyA+WtbB0zAGXgT0ycSFk31kBky/moUeBLpbsl91wi6Nnbfs/g+7XOQq6ifjfkAAAAASUVORK5CYII='); --gfx-restoreBtn: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAIn2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iCiAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgZGM6Zm9ybWF0PSJpbWFnZS9wbmciCiAgIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiCiAgIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIKICAgdGlmZjpJbWFnZUxlbmd0aD0iMTAiCiAgIHRpZmY6SW1hZ2VXaWR0aD0iMTAiCiAgIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiCiAgIHRpZmY6WFJlc29sdXRpb249IjcyLjAiCiAgIHRpZmY6WVJlc29sdXRpb249IjcyLjAiCiAgIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTAyLTE3VDEyOjU1OjM3WiIKICAgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjEtMTAtMDVUMTQ6Mjc6MjQtMDc6MDAiCiAgIHhtcDpNb2RpZnlEYXRlPSIyMDIxLTEwLTA1VDE0OjI3OjI0LTA3OjAwIgogICB4bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6ZTk5OWM2NWYtNDhhOS0wNjQyLWI2MTktZmJlYTExMmUxOGZiIgogICB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjY5MzMyOWNhLWNkNjctMzY0Zi04MzU1LTY5N2ZmYzI0ZDdlZCIKICAgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjgyZjQwYmU3LTE0YzItZjc0Ni1hZmE1LWQxYmIxNzAyMjM4OCIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjEwIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iMTAiCiAgIGV4aWY6Q29sb3JTcGFjZT0iMSI+CiAgIDxwaG90b3Nob3A6VGV4dExheWVycz4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgcGhvdG9zaG9wOkxheWVyTmFtZT0i7qSiIgogICAgICBwaG90b3Nob3A6TGF5ZXJUZXh0PSLupKIiLz4KICAgIDwvcmRmOlNlcT4KICAgPC9waG90b3Nob3A6VGV4dExheWVycz4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHhtcE1NOmFjdGlvbj0iY3JlYXRlZCIKICAgICAgeG1wTU06aW5zdGFuY2VJRD0ieG1wLmlpZDo4MmY0MGJlNy0xNGMyLWY3NDYtYWZhNS1kMWJiMTcwMjIzODgiCiAgICAgIHhtcE1NOnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIKICAgICAgeG1wTU06d2hlbj0iMjAyMC0wMi0xN1QxMjo1NTozN1oiLz4KICAgICA8cmRmOmxpCiAgICAgIHhtcE1NOmFjdGlvbj0ic2F2ZWQiCiAgICAgIHhtcE1NOmNoYW5nZWQ9Ii8iCiAgICAgIHhtcE1NOmluc3RhbmNlSUQ9InhtcC5paWQ6NjkzMzI5Y2EtY2Q2Ny0zNjRmLTgzNTUtNjk3ZmZjMjRkN2VkIgogICAgICB4bXBNTTpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiCiAgICAgIHhtcE1NOndoZW49IjIwMjAtMDItMTdUMTI6NTU6MzdaIi8+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InByb2R1Y2VkIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZmZpbml0eSBQaG90byAxLjEwLjEiCiAgICAgIHN0RXZ0OndoZW49IjIwMjEtMTAtMDVUMTQ6Mjc6MjQtMDc6MDAiLz4KICAgIDwvcmRmOlNlcT4KICAgPC94bXBNTTpIaXN0b3J5PgogIDwvcmRmOkRlc2NyaXB0aW9uPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KPD94cGFja2V0IGVuZD0iciI/PqiFCFwAAAGCaUNDUHNSR0IgSUVDNjE5NjYtMi4xAAAokXWRv0tCURTHP2lhmGFRQUODhDVZlELU0qD0C6pBDbJa9OWPQO3xnhHRGrQKBVFLv4b6C2oNmoOgKIJoC5qLWkpe56mgRJ7Luedzv/eew73ngiWcVjJ6/QBksjktOOF3zUcWXLZX7DTQQSu+qKKrM6HxMDXt64E6M971mbVqn/vXmpbjugJ1jcKjiqrlhCeFp9dzqsm7wu1KKrosfC7s0eSCwvemHivxm8nJEv+YrIWDAbC0CLuSVRyrYiWlZYTl5bgz6TWlfB/zJY54di4ksVu8C50gE/hxMcUYAYYYZETmIfrw0i8rauQPFPNnWZVcRWaVDTRWSJIih0fUNakel5gQPS4jzYbZ/7991RM+b6m6ww8NL4bx0QO2HSjkDeP72DAKJ2B9hqtsJX/1CIY/Rc9XNPchOLfg4rqixfbgchs6n9SoFi1KVnFLIgHvZ9AcgbZbsC+Welbe5/QRwpvyVTewfwC9ct659At2bGftHD0UJwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAHNJREFUGJWtkKEOwlAMRc+QM5AwQYJFoPjZCWb2YRPIaeRTLwfTLQs0UxzX3tumtxCog78UdVTbZmM8AmsdXIABeKH2ak221dDuamnUCjyA+WtbB0zAGXgT0ycSFk31kBky/moUeBLpbsl91wi6Nnbfs/g+7XOQq6ifjfkAAAAASUVORK5CYII=");
--gfx-minBtn : url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAGOmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTAyLTE3VDEzOjAwOjMyWiIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMC0wMi0xN1QxMzowMDozMloiIHhtcDpNb2RpZnlEYXRlPSIyMDIwLTAyLTE3VDEzOjAwOjMyWiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo4NWQwZWRiMC1mZDAwLWI2NGYtOWVmYi1hMmI0NTg3MDVhOGEiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDphMzAwMWUxYS0yOTE5LWU0NDktYjk0Yy1jMjEyMjQ4YTlmOGEiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3ODdmNzk5Yy00YjExLWU1NGEtYjIwZC02ODYxN2VkOWM1ZTIiIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo3ODdmNzk5Yy00YjExLWU1NGEtYjIwZC02ODYxN2VkOWM1ZTIiIHN0RXZ0OndoZW49IjIwMjAtMDItMTdUMTM6MDA6MzJaIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjg1ZDBlZGIwLWZkMDAtYjY0Zi05ZWZiLWEyYjQ1ODcwNWE4YSIgc3RFdnQ6d2hlbj0iMjAyMC0wMi0xN1QxMzowMDozMloiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPHBob3Rvc2hvcDpUZXh0TGF5ZXJzPiA8cmRmOkJhZz4gPHJkZjpsaSBwaG90b3Nob3A6TGF5ZXJOYW1lPSLupKEiIHBob3Rvc2hvcDpMYXllclRleHQ9Iu6koSIvPiA8L3JkZjpCYWc+IDwvcGhvdG9zaG9wOlRleHRMYXllcnM+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+FwvRXAAAABdJREFUGNNj/P//PwMxgHGIKPw/XDwDAOr1HuzlELLnAAAAAElFTkSuQmCC'); --gfx-minBtn: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAGOmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTAyLTE3VDEzOjAwOjMyWiIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMC0wMi0xN1QxMzowMDozMloiIHhtcDpNb2RpZnlEYXRlPSIyMDIwLTAyLTE3VDEzOjAwOjMyWiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo4NWQwZWRiMC1mZDAwLWI2NGYtOWVmYi1hMmI0NTg3MDVhOGEiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDphMzAwMWUxYS0yOTE5LWU0NDktYjk0Yy1jMjEyMjQ4YTlmOGEiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3ODdmNzk5Yy00YjExLWU1NGEtYjIwZC02ODYxN2VkOWM1ZTIiIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo3ODdmNzk5Yy00YjExLWU1NGEtYjIwZC02ODYxN2VkOWM1ZTIiIHN0RXZ0OndoZW49IjIwMjAtMDItMTdUMTM6MDA6MzJaIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjg1ZDBlZGIwLWZkMDAtYjY0Zi05ZWZiLWEyYjQ1ODcwNWE4YSIgc3RFdnQ6d2hlbj0iMjAyMC0wMi0xN1QxMzowMDozMloiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPHBob3Rvc2hvcDpUZXh0TGF5ZXJzPiA8cmRmOkJhZz4gPHJkZjpsaSBwaG90b3Nob3A6TGF5ZXJOYW1lPSLupKEiIHBob3Rvc2hvcDpMYXllclRleHQ9Iu6koSIvPiA8L3JkZjpCYWc+IDwvcGhvdG9zaG9wOlRleHRMYXllcnM+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+FwvRXAAAABdJREFUGNNj/P//PwMxgHGIKPw/XDwDAOr1HuzlELLnAAAAAElFTkSuQmCC");
} }
#apple-music-video-container { #apple-music-video-container {
@ -2403,7 +2381,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
} }
#apple-music-video-container { #apple-music-video-container {
// AM Web Style Fullscreen Button // AM Web Style Fullscreen Button
#player-fullscreen { #player-fullscreen {
background-size: 50%; background-size: 50%;
@ -2458,7 +2435,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
input[type="range"] { input[type="range"] {
align-self: center; align-self: center;
height: 4px; height: 4px;
border-radius: .5rem; border-radius: 0.5rem;
margin-inline: 10px; margin-inline: 10px;
} }
@ -2467,7 +2444,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
&::-webkit-slider-thumb { &::-webkit-slider-thumb {
box-shadow: 0px 0px 0px #000000; box-shadow: 0px 0px 0px #000000;
border : 1px solid #39404D; border: 1px solid #39404d;
background: #fff; background: #fff;
height: 0.7rem; height: 0.7rem;
width: 0.7rem; width: 0.7rem;
@ -2547,14 +2524,12 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
} }
} }
&.song-artist-marquee { &.song-artist-marquee {
> marquee { > marquee {
margin-bottom: -3px; margin-bottom: -3px;
} }
} }
} }
} }
} }
@ -2626,11 +2601,11 @@ div#captions {
background: rgba(0, 0, 0, 0.6); background: rgba(0, 0, 0, 0.6);
color: yellow; color: yellow;
white-space: pre-line; white-space: pre-line;
font-family: 'Inter', 'Noto Sans JP', 'Source Han Sans SC', 'Source Han Sans HK', 'Source Han Sans SC', 'Source Han Sans HK', 'Noto Sans SC', 'Noto Sans TC', 'Noto Sans HK', 'Noto Sans KR', sans-serif; font-family: "Inter", "Noto Sans JP", "Source Han Sans SC", "Source Han Sans HK", "Source Han Sans SC", "Source Han Sans HK", "Noto Sans SC", "Noto Sans TC", "Noto Sans HK", "Noto Sans KR", sans-serif;
} }
[v-cloak] { [v-cloak] {
display: none !important display: none !important;
} }
.item-navigate:hover { .item-navigate:hover {
@ -2740,8 +2715,6 @@ body.no-gpu {
} }
} }
.qrimg { .qrimg {
width: -webkit-fill-available; width: -webkit-fill-available;
max-block-size: -webkit-fill-available; max-block-size: -webkit-fill-available;
@ -2759,7 +2732,6 @@ body.no-gpu {
padding-block: 25px; padding-block: 25px;
} }
} }
} }
.equalizer-panel { .equalizer-panel {
@ -2781,7 +2753,6 @@ body.no-gpu {
overflow: hidden; overflow: hidden;
} }
.modal-header { .modal-header {
padding: 16px; padding: 16px;
position: relative; position: relative;
@ -2792,7 +2763,7 @@ body.no-gpu {
} }
.close-btn { .close-btn {
.menu-panel.menu-header-text.close-btn .menu-panel.menu-header-text.close-btn;
} }
} }
@ -2851,7 +2822,7 @@ body.no-gpu {
} }
} }
body[platform='darwin'] { body[platform="darwin"] {
#window-controls-container { #window-controls-container {
display: none; display: none;
} }
@ -2872,7 +2843,7 @@ body[platform='darwin'] {
.percent::after { .percent::after {
position: relative; position: relative;
right: 2em; right: 2em;
transition: all .05s ease-in-out; transition: all 0.05s ease-in-out;
} }
.percent:hover::after, .percent:hover::after,
@ -2881,7 +2852,7 @@ body[platform='darwin'] {
} }
.percent::after { .percent::after {
content: '%'; content: "%";
} }
.playbackrate-text { .playbackrate-text {
@ -2912,7 +2883,6 @@ body[platform='darwin'] {
} }
.keybinds-page { .keybinds-page {
.md-option-header { .md-option-header {
padding: 10px 0px; padding: 10px 0px;
border-bottom: unset; border-bottom: unset;
@ -2933,7 +2903,7 @@ body[platform='darwin'] {
.md-option-line { .md-option-line {
padding: 15px 20px; padding: 15px 20px;
font-size: 0.90em; font-size: 0.9em;
} }
} }

View file

@ -1,2 +1,68 @@
if(!self.define){let e,i={};const s=(s,r)=>(s=new URL(s+".js",r).href,i[s]||new Promise((i=>{if("document"in self){const e=document.createElement("script");e.src=s,e.onload=i,document.head.appendChild(e)}else e=s,importScripts(s),i()})).then((()=>{let e=i[s];if(!e)throw new Error(`Module ${s} didnt register its module`);return e})));self.define=(r,c)=>{const n=e||("document"in self?document.currentScript.src:"")||location.href;if(i[n])return;let o={};const t=e=>s(e,n),a={module:{uri:n},exports:o,require:t};i[n]=Promise.all(r.map((e=>a[e]||t(e)))).then((e=>(c(...e),o)))}}define(["./workbox-962786f2"],(function(e){"use strict";self.addEventListener("message",(e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()})),e.precacheAndRoute([{url:"ameframework.css",revision:"4bcc8646bb5742638fad52b94e231601"},{url:"apple-hls.js",revision:"2b74055662676b0fcc2d4a4bf994a9dc"},{url:"hlscider.js",revision:"cf7f512e83e32694f2c94f904714fe4c"},{url:"index_old.html",revision:"c21f3e9c5b015599d3ab07639f64a7a8"},{url:"index.js",revision:"8591a69fc9c975a063eb264b7447f173"},{url:"less.js",revision:"b6e574e4d680686786a28e7e71a17bbc"},{url:"musickit.js",revision:"211d80891c3336c1795cb83df58d4b63"},{url:"sortable.min.js",revision:"5cbc31ebec32adf60e27b76418e79d93"},{url:"style-old.css",revision:"aea9ea49df13f2deee42b68654aeea06"},{url:"todo.js",revision:"18d49fabcb96de8bd11455877d8eacb6"},{url:"vue-observe-visibility.min.js",revision:"5a52e761f6aa71b4f65a7b458f698b95"},{url:"vue.js",revision:"0a9a4681294d8c5f476687eea6e74842"},{url:"vuedraggable.umd.min.js",revision:"9a84fec5263bb510cee88e1c3b9583cc"}],{ignoreURLParametersMatching:[/^utm_/,/^fbclid$/,/^X-Amz-Algorithm/,/^X-Amz-Date/,/^X-Amz-SignedHeaders/,/^X-Amz-Expires/,/^X-Amz-Credential/,/^X-Amz-Signature/]}),e.registerRoute(/\.(?:png|jpg|jpeg|svg|webp)$/,new e.CacheFirst({cacheName:"imageinternet",plugins:[]}),"GET"),e.registerRoute(/https:\/\/is[0-9]-ssl\.mzstatic\.com\/image+/,new e.CacheFirst,"GET"),e.registerRoute(/^https:\/\/store-\d{3}\.blobstore\.apple\.com\/.{65}\/image+/,new e.CacheFirst,"GET")})); if (!self.define) {
let e,
i = {};
const s = (s, r) => (
(s = new URL(s + ".js", r).href),
i[s] ||
new Promise((i) => {
if ("document" in self) {
const e = document.createElement("script");
(e.src = s), (e.onload = i), document.head.appendChild(e);
} else (e = s), importScripts(s), i();
}).then(() => {
let e = i[s];
if (!e) throw new Error(`Module ${s} didnt register its module`);
return e;
})
);
self.define = (r, c) => {
const n = e || ("document" in self ? document.currentScript.src : "") || location.href;
if (i[n]) return;
let o = {};
const t = (e) => s(e, n),
a = { module: { uri: n }, exports: o, require: t };
i[n] = Promise.all(r.map((e) => a[e] || t(e))).then((e) => (c(...e), o));
};
}
define(["./workbox-962786f2"], function (e) {
"use strict";
self.addEventListener("message", (e) => {
e.data && "SKIP_WAITING" === e.data.type && self.skipWaiting();
}),
e.precacheAndRoute(
[
{
url: "ameframework.css",
revision: "4bcc8646bb5742638fad52b94e231601",
},
{ url: "apple-hls.js", revision: "2b74055662676b0fcc2d4a4bf994a9dc" },
{ url: "hlscider.js", revision: "cf7f512e83e32694f2c94f904714fe4c" },
{ url: "index_old.html", revision: "c21f3e9c5b015599d3ab07639f64a7a8" },
{ url: "index.js", revision: "8591a69fc9c975a063eb264b7447f173" },
{ url: "less.js", revision: "b6e574e4d680686786a28e7e71a17bbc" },
{ url: "musickit.js", revision: "211d80891c3336c1795cb83df58d4b63" },
{
url: "sortable.min.js",
revision: "5cbc31ebec32adf60e27b76418e79d93",
},
{ url: "style-old.css", revision: "aea9ea49df13f2deee42b68654aeea06" },
{ url: "todo.js", revision: "18d49fabcb96de8bd11455877d8eacb6" },
{
url: "vue-observe-visibility.min.js",
revision: "5a52e761f6aa71b4f65a7b458f698b95",
},
{ url: "vue.js", revision: "0a9a4681294d8c5f476687eea6e74842" },
{
url: "vuedraggable.umd.min.js",
revision: "9a84fec5263bb510cee88e1c3b9583cc",
},
],
{
ignoreURLParametersMatching: [/^utm_/, /^fbclid$/, /^X-Amz-Algorithm/, /^X-Amz-Date/, /^X-Amz-SignedHeaders/, /^X-Amz-Expires/, /^X-Amz-Credential/, /^X-Amz-Signature/],
}
),
e.registerRoute(/\.(?:png|jpg|jpeg|svg|webp)$/, new e.CacheFirst({ cacheName: "imageinternet", plugins: [] }), "GET"),
e.registerRoute(/https:\/\/is[0-9]-ssl\.mzstatic\.com\/image+/, new e.CacheFirst(), "GET"),
e.registerRoute(/^https:\/\/store-\d{3}\.blobstore\.apple\.com\/.{65}\/image+/, new e.CacheFirst(), "GET");
});
//# sourceMappingURL=sw.js.map //# sourceMappingURL=sw.js.map

View file

@ -13,7 +13,7 @@
.drawertransition-enter-active, .drawertransition-enter-active,
.drawertransition-leave-active { .drawertransition-leave-active {
transition: margin .25s var(--appleEase), opacity .25s var(--appleEase); transition: margin 0.25s var(--appleEase), opacity 0.25s var(--appleEase);
} }
.drawertransition-enter, .drawertransition-enter,
@ -29,7 +29,7 @@
.drawertransition-enter-active, .drawertransition-enter-active,
.drawertransition-leave-active { .drawertransition-leave-active {
transition: right .25s var(--appleEase), opacity .25s var(--appleEase); transition: right 0.25s var(--appleEase), opacity 0.25s var(--appleEase);
} }
.drawertransition-enter, .drawertransition-enter,
@ -37,5 +37,4 @@
right: -300px; right: -300px;
} }
} }
} }

View file

@ -13,7 +13,6 @@ body {
} }
} }
.app-chrome:not(.chrome-bottom) { .app-chrome:not(.chrome-bottom) {
backdrop-filter: unset; backdrop-filter: unset;
background-color: var(--baseColor); background-color: var(--baseColor);
@ -30,7 +29,8 @@ body {
transition: unset !important; transition: unset !important;
} }
.playback-button:before, .playback-button--small:before { .playback-button:before,
.playback-button--small:before {
transition: unset !important; transition: unset !important;
} }
@ -58,12 +58,12 @@ body {
.modal-enter, .modal-enter,
.modal-leave-to { .modal-leave-to {
opacity: 0; opacity: 0;
transform: scale(1.10); transform: scale(1.1);
} }
.wpfade-enter-active, .wpfade-enter-active,
.wpfade-leave-active { .wpfade-leave-active {
transition: opacity .1s var(--appleEase); transition: opacity 0.1s var(--appleEase);
} }
.wpfade-enter, .wpfade-enter,
@ -89,7 +89,6 @@ body {
will-change: unset; will-change: unset;
} }
.wpfade_transform_backwards-enter-active, .wpfade_transform_backwards-enter-active,
.wpfade_transform_backwards-leave-active { .wpfade_transform_backwards-leave-active {
transition: unset; transition: unset;
@ -125,11 +124,10 @@ body {
.fsModeSwitch-enter, .fsModeSwitch-enter,
.fsModeSwitch-leave-to { .fsModeSwitch-leave-to {
transform: scale(1.10); transform: scale(1.1);
opacity: 0; opacity: 0;
} }
.drawertransition-enter-active, .drawertransition-enter-active,
.drawertransition-leave-active { .drawertransition-leave-active {
transition: unset; transition: unset;

View file

@ -7,13 +7,12 @@
backdrop-filter: blur(32px) saturate(180%); backdrop-filter: blur(32px) saturate(180%);
&.menu-panel-body-down { &.menu-panel-body-down {
animation: menuInDown .10s var(--appleEase); animation: menuInDown 0.1s var(--appleEase);
} }
&.menu-panel-body-up { &.menu-panel-body-up {
animation: menuInUp .10s var(--appleEase); animation: menuInUp 0.1s var(--appleEase);
} }
} }
@keyframes menuInUp { @keyframes menuInUp {
@ -43,7 +42,6 @@
background: @panelColors; background: @panelColors;
} }
} }
} }
.cd-mediaitem-square { .cd-mediaitem-square {
@ -55,46 +53,45 @@
} }
.cd-mediaitem-square:not(.mediaitem-card) { .cd-mediaitem-square:not(.mediaitem-card) {
transition : transform .2s var(--appleEase); transition: transform 0.2s var(--appleEase);
transition-delay: .1s; transition-delay: 0.1s;
.artwork-container {
}
.artwork-container {} .info-rect {
}
.info-rect {}
.artwork-container, .artwork-container,
.info-rect { .info-rect {
transition : transform .22s var(--appleEase); transition: transform 0.22s var(--appleEase);
transition-delay: .05s; transition-delay: 0.05s;
} }
.artwork-container { .artwork-container {
transform: scale(0.962) translateZ(0); transform: scale(0.962) translateZ(0);
transition : transform .1s var(--appleEase); transition: transform 0.1s var(--appleEase);
transition-delay: 0s; transition-delay: 0s;
transform-origin: center; transform-origin: center;
} }
&:hover { &:hover {
.artwork-container { .artwork-container {
transform : scale(1.0); transform: scale(1);
transition : transform .1s var(--appleEase); transition: transform 0.1s var(--appleEase);
transition-delay: 0s; transition-delay: 0s;
transform-origin: center; transform-origin: center;
} }
.info-rect { .info-rect {
z-index: 1; z-index: 1;
transition : transform .1s var(--appleEase); transition: transform 0.1s var(--appleEase);
transition-delay: 0s; transition-delay: 0s;
transform: translateY(8px) translate3d(0, 0, 0); transform: translateY(8px) translate3d(0, 0, 0);
} }
} }
&:active { &:active {
} }
} }
@ -116,7 +113,6 @@
will-change: opacity, transform; will-change: opacity, transform;
} }
.wpfade_transform_backwards-enter-active, .wpfade_transform_backwards-enter-active,
.wpfade_transform_backwards-leave-active { .wpfade_transform_backwards-leave-active {
--transitionTime: 0.2s; --transitionTime: 0.2s;

View file

@ -15,7 +15,8 @@
// &types=artists,albums,editorial-items,library-albums,library-playlists,music-movies,music-videos,playlists,stations,uploaded-audios,uploaded-videos,activities,apple-curators,curators,tv-shows,social-profiles,social-upsells // &types=artists,albums,editorial-items,library-albums,library-playlists,music-movies,music-videos,playlists,stations,uploaded-audios,uploaded-videos,activities,apple-curators,curators,tv-shows,social-profiles,social-upsells
// &l=en-gb&platform=web // &l=en-gb&platform=web
await app.mk.api.personalRecommendations("", await app.mk.api.personalRecommendations(
"",
{ {
name: "listen-now", name: "listen-now",
with: "friendsMix,library,social", with: "friendsMix,library,social",
@ -38,16 +39,16 @@ await app.mk.api.personalRecommendations("",
"meta[stations]": "inflectionPoints", "meta[stations]": "inflectionPoints",
types: "artists,albums,editorial-items,library-albums,library-playlists,music-movies,music-videos,playlists,stations,uploaded-audios,uploaded-videos,activities,apple-curators,curators,tv-shows,social-profiles,social-upsells", types: "artists,albums,editorial-items,library-albums,library-playlists,music-movies,music-videos,playlists,stations,uploaded-audios,uploaded-videos,activities,apple-curators,curators,tv-shows,social-profiles,social-upsells",
l: "en-gb", l: "en-gb",
platform:"web" platform: "web",
}, },
{ {
includeResponseMeta: !0, includeResponseMeta: !0,
reload: !0 reload: !0,
}); }
);
// Browse page // Browse page
await app.mk.api.groupings("", await app.mk.api.groupings("", {
{
platform: "web", platform: "web",
name: "music", name: "music",
l: "en-gb", l: "en-gb",
@ -57,100 +58,112 @@ await app.mk.api.groupings("",
"include[music-videos]": "artists", "include[music-videos]": "artists",
extend: "editorialArtwork,artistUrl", extend: "editorialArtwork,artistUrl",
"fields[artists]": "name,url,artwork,editorialArtwork,genreNames,editorialNotes", "fields[artists]": "name,url,artwork,editorialArtwork,genreNames,editorialNotes",
"art[url]": "f" "art[url]": "f",
}); });
// Radio page // Radio page
await app.mk.api.recentRadioStations("", await app.mk.api.recentRadioStations("", {
{l: "en-gb", l: "en-gb",
"platform": "web", platform: "web",
"art[url]": "f"}); "art[url]": "f",
});
// Recently Added // Recently Added
await app.mk.api.library.recentlyAdded({ await app.mk.api.library.recentlyAdded(
"platform": "web", {
platform: "web",
include: { include: {
"library-albums": ["artists"], "library-albums": ["artists"],
"library-artists": ["catalog"] "library-artists": ["catalog"],
}, },
fields: { fields: {
artists: ["url"], artists: ["url"],
albums: "artistName,artistUrl,artwork,contentRating,editorialArtwork,name,playParams,releaseDate,url" albums: "artistName,artistUrl,artwork,contentRating,editorialArtwork,name,playParams,releaseDate,url",
}, },
includeOnly: ["catalog", "artists"], includeOnly: ["catalog", "artists"],
limit: 25 limit: 25,
}, { },
{
reload: !0, reload: !0,
includePagination: !0 includePagination: !0,
}) }
);
// Songs // Songs
await app.mk.api.library.songs({ limit: 100 }).then((data) => { await app.mk.api.library.songs({ limit: 100 }).then((data) => {
console.log(data) console.log(data);
}) });
// Artists // Artists
await app.mk.api.library.artists({ limit: 100 }).then((data) => { await app.mk.api.library.artists({ limit: 100 }).then((data) => {
console.log(data) console.log(data);
}) });
// Artists // Artists
await app.mk.api.library.albums({ limit: 100 }).then((data) => { await app.mk.api.library.albums({ limit: 100 }).then((data) => {
console.log(data) console.log(data);
}) });
// Albums // Albums
// does not like limit = 100 for some reason // does not like limit = 100 for some reason
await app.mk.api.library.albums({ limit: 50 }).then((data) => { await app.mk.api.library.albums({ limit: 50 }).then((data) => {
console.log(data) console.log(data);
}) });
// Made For You // Made For You
app.mk.api.recommendations("",{extend: "editorialArtwork,artistUrl"}) app.mk.api.recommendations("", { extend: "editorialArtwork,artistUrl" });
// Library with library length // Library with library length
await app.mk.api.library.songs("", { limit: 100 }, { includeResponseMeta: !0 }).then((data) => { await app.mk.api.library.songs("", { limit: 100 }, { includeResponseMeta: !0 }).then((data) => {
console.log(data) console.log(data);
}) });
// Artist View Top Songs // Artist View Top Songs
app.mk.api.artistView("325096253", "top-songs", {}, {view: "top-songs", includeResponseMeta: !0}) app.mk.api.artistView("325096253", "top-songs", {}, { view: "top-songs", includeResponseMeta: !0 });
// Artist Page Data // Artist Page Data
app.mkapi("artists", false, "412778295", { app
"views": "featured-release,full-albums,appears-on-albums,featured-albums,featured-on-albums,singles,compilation-albums,live-albums,latest-release,top-music-videos,similar-artists,top-songs,playlists,more-to-hear,more-to-see", .mkapi(
"extend": "artistBio,bornOrFormed,editorialArtwork,editorialVideo,isGroup,origin,hero", "artists",
false,
"412778295",
{
views: "featured-release,full-albums,appears-on-albums,featured-albums,featured-on-albums,singles,compilation-albums,live-albums,latest-release,top-music-videos,similar-artists,top-songs,playlists,more-to-hear,more-to-see",
extend: "artistBio,bornOrFormed,editorialArtwork,editorialVideo,isGroup,origin,hero",
"extend[playlists]": "trackCount", "extend[playlists]": "trackCount",
"omit[resource:songs]": "relationships", "omit[resource:songs]": "relationships",
"fields[albums]": "artistName,artistUrl,artwork,contentRating,editorialArtwork,name,playParams,releaseDate,url,trackCount", "fields[albums]": "artistName,artistUrl,artwork,contentRating,editorialArtwork,name,playParams,releaseDate,url,trackCount",
"limit[artists:top-songs]": 20, "limit[artists:top-songs]": 20,
"art[url]": "f" "art[url]": "f",
}, {includeResponseMeta: !0}).then((data)=>{ },
console.log(data) { includeResponseMeta: !0 }
}) )
.then((data) => {
console.log(data);
});
// download entire library // download entire library
var library = [] var library = [];
var downloaded = null; var downloaded = null;
function downloadChunk() { function downloadChunk() {
if (downloaded == null) { if (downloaded == null) {
app.mk.api.library.songs("", { limit: 100 }, { includeResponseMeta: !0 }).then((response) => { app.mk.api.library.songs("", { limit: 100 }, { includeResponseMeta: !0 }).then((response) => {
processChunk(response) processChunk(response);
}) });
} else { } else {
downloaded.next("", { limit: 100 }, { includeResponseMeta: !0 }).then((response) => { downloaded.next("", { limit: 100 }, { includeResponseMeta: !0 }).then((response) => {
processChunk(response) processChunk(response);
}) });
} }
} }
function processChunk(response) { function processChunk(response) {
downloaded = response downloaded = response;
library = library.concat(downloaded.data) library = library.concat(downloaded.data);
if (downloaded.meta.total > library.length) { if (downloaded.meta.total > library.length) {
console.log(`downloading next chunk - ${library.length} songs so far`) console.log(`downloading next chunk - ${library.length} songs so far`);
downloadChunk() downloadChunk();
} else { } else {
console.log(library) console.log(library);
} }
} }
@ -158,23 +171,23 @@ function processChunk (response) {
// recentPlayed() -> recently played songs ? // recentPlayed() -> recently played songs ?
// create Artist / Song/ Album stations: // create Artist / Song/ Album stations:
app.mk.setStationQueue({artist:"1258279972"}) app.mk.setStationQueue({ artist: "1258279972" });
app.mk.setStationQueue({song:"1437308307"}) // yes the song id here can be the albumId, but just keep using the song: app.mk.setStationQueue({ song: "1437308307" }); // yes the song id here can be the albumId, but just keep using the song:
// Sorting Playlists, send an array of tracks in the format below // Sorting Playlists, send an array of tracks in the format below
// playlist must be fully recursively downloaded first before sorting // playlist must be fully recursively downloaded first before sorting
app.mk.api.library.putPlaylistTracklisting(app.showingPlaylist.attributes.playParams.id, [ app.mk.api.library.putPlaylistTracklisting(app.showingPlaylist.attributes.playParams.id, [
{ {
"id": relationships.tracks.data[X].id, id: relationships.tracks.data[X].id,
"type": relationships.tracks.data[X].type type: relationships.tracks.data[X].type,
}, },
{ {
"id": relationships.tracks.data[X].id, id: relationships.tracks.data[X].id,
"type": relationships.tracks.data[X].type type: relationships.tracks.data[X].type,
}, },
{ {
"id": relationships.tracks.data[X].id, id: relationships.tracks.data[X].id,
"type": relationships.tracks.data[X].type type: relationships.tracks.data[X].type,
}, },
]) ]);

View file

@ -141,9 +141,12 @@
v-if="drawer.open && drawer.panel == 'lyrics' && lyrics && lyrics != [] && lyrics.length > 0"> v-if="drawer.open && drawer.panel == 'lyrics' && lyrics && lyrics != [] && lyrics.length > 0">
<div class="bgArtworkMaterial"> <div class="bgArtworkMaterial">
<div class="bg-artwork-container"> <div class="bg-artwork-container">
<img v-if="(cfg.visual.bg_artwork_rotation && animateBackground)" class="bg-artwork a" :src="$store.state.artwork.playerLCD"> <img v-if="(cfg.visual.bg_artwork_rotation && animateBackground)" class="bg-artwork a"
<img v-if="(cfg.visual.bg_artwork_rotation && animateBackground)" class="bg-artwork b" :src="$store.state.artwork.playerLCD"> :src="$store.state.artwork.playerLCD">
<img v-if="!(cfg.visual.bg_artwork_rotation && animateBackground)" class="bg-artwork no-animation" :src="$store.state.artwork.playerLCD"> <img v-if="(cfg.visual.bg_artwork_rotation && animateBackground)" class="bg-artwork b"
:src="$store.state.artwork.playerLCD">
<img v-if="!(cfg.visual.bg_artwork_rotation && animateBackground)" class="bg-artwork no-animation"
:src="$store.state.artwork.playerLCD">
</div> </div>
</div> </div>
<lyrics-view v-if="drawer.panel == 'lyrics'" :time="mk.currentPlaybackTime - lyricOffset" :lyrics="lyrics" <lyrics-view v-if="drawer.panel == 'lyrics'" :time="mk.currentPlaybackTime - lyricOffset" :lyrics="lyrics"

View file

@ -1,4 +1,5 @@
<div class="app-chrome chrome-bottom" v-if="getThemeDirective('windowLayout') == 'twopanel'" :style="{'display': chrome.topChromeVisible ? '' : 'none'}"> <div class="app-chrome chrome-bottom" v-if="getThemeDirective('windowLayout') == 'twopanel'"
:style="{'display': chrome.topChromeVisible ? '' : 'none'}">
<div class="app-chrome--left"> <div class="app-chrome--left">
<div class="app-chrome-item playback-controls"> <div class="app-chrome-item playback-controls">
<template v-if="mkReady()"> <template v-if="mkReady()">
@ -16,15 +17,23 @@
<mediaitem-artwork :url="currentArtUrl"></mediaitem-artwork> <mediaitem-artwork :url="currentArtUrl"></mediaitem-artwork>
</div> </div>
<div class="song-name">{{ mk.nowPlayingItem["attributes"]["name"] }}</div> <div class="song-name">{{ mk.nowPlayingItem["attributes"]["name"] }}</div>
<div class="song-artist" @click="getNowPlayingItemDetailed(`artist`)">{{ mk.nowPlayingItem["attributes"]["artistName"] }}</div> <div class="song-artist" @click="getNowPlayingItemDetailed(`artist`)">{{
mk.nowPlayingItem["attributes"]["artistName"] }}
</div>
<div class="song-album" @click="getNowPlayingItemDetailed(`album`)"> <div class="song-album" @click="getNowPlayingItemDetailed(`album`)">
{{(mk.nowPlayingItem["attributes"]["albumName"]) ? {{(mk.nowPlayingItem["attributes"]["albumName"]) ?
(mk.nowPlayingItem["attributes"]["albumName"]) : "" }} (mk.nowPlayingItem["attributes"]["albumName"]) : "" }}
</div> </div>
<hr> <hr>
<div class="btn-group" style="width:100%;"> <div class="btn-group" style="width:100%;">
<button class="md-btn md-btn-small" style="width: 100%;" @click="drawer.open = false; miniPlayer(true)">{{ $root.getLz("term.miniplayer") }}</button> <button class="md-btn md-btn-small" style="width: 100%;"
<button class="md-btn md-btn-small" style="width: 100%;" @click="drawer.open = false; fullscreen(true)">{{ $root.getLz("term.fullscreenView") }}</button> @click="drawer.open = false; miniPlayer(true)">{{ $root.getLz("term.miniplayer")
}}
</button>
<button class="md-btn md-btn-small" style="width: 100%;"
@click="drawer.open = false; fullscreen(true)">{{
$root.getLz("term.fullscreenView") }}
</button>
</div> </div>
</div> </div>
</b-popover> </b-popover>
@ -39,20 +48,26 @@
<div class="song-artist" @click="getNowPlayingItemDetailed(`artist`)"> <div class="song-artist" @click="getNowPlayingItemDetailed(`artist`)">
{{ mk.nowPlayingItem["attributes"]["artistName"] }} {{ mk.nowPlayingItem["attributes"]["artistName"] }}
</div> </div>
<div class="song-album" @click="getNowPlayingItemDetailed('album')" v-if='mk.nowPlayingItem["attributes"]["albumName"]'> <div class="song-album" @click="getNowPlayingItemDetailed('album')"
v-if='mk.nowPlayingItem["attributes"]["albumName"]'>
{{(mk.nowPlayingItem["attributes"]["albumName"]) ? {{(mk.nowPlayingItem["attributes"]["albumName"]) ?
(mk.nowPlayingItem["attributes"]["albumName"]) : "" }} (mk.nowPlayingItem["attributes"]["albumName"]) : "" }}
</div> </div>
<div class="chrome-icon-container"> <div class="chrome-icon-container">
<div class="audio-type private-icon" v-if="cfg.general.privateEnabled === true"></div> <div class="audio-type private-icon" v-if="cfg.general.privateEnabled === true"></div>
<div class="audio-type spatial-icon" v-if="cfg.audio.maikiwiAudio.spatial === true" <div class="audio-type spatial-icon" v-if="cfg.audio.maikiwiAudio.spatial === true"
:title="$root.getLz('settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization') + ' (' + getProfileLz('CTS', cfg.audio.maikiwiAudio.spatialProfile) + ')'" v-b-tooltip.hover :title="$root.getLz('settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization') + ' (' + getProfileLz('CTS', cfg.audio.maikiwiAudio.spatialProfile) + ')'"
v-b-tooltip.hover
></div> ></div>
<div class="audio-type lossless-icon" v-if="(mk.nowPlayingItem?.localFilesMetadata?.lossless ?? false) === true" <div class="audio-type lossless-icon"
:title="mk.nowPlayingItem?.localFilesMetadata?.bitDepth +'-bit / '+ mk.nowPlayingItem?.localFilesMetadata?.sampleRate/1000 + ' kHz ' + mk.nowPlayingItem.localFilesMetadata.container" v-b-tooltip.hover v-if="(mk.nowPlayingItem?.localFilesMetadata?.lossless ?? false) === true"
:title="mk.nowPlayingItem?.localFilesMetadata?.bitDepth +'-bit / '+ mk.nowPlayingItem?.localFilesMetadata?.sampleRate/1000 + ' kHz ' + mk.nowPlayingItem.localFilesMetadata.container"
v-b-tooltip.hover
></div> ></div>
<div class="audio-type ppe-icon" v-if="mk.nowPlayingItem.localFilesMetadata == null && cfg.audio.maikiwiAudio.ciderPPE === true" <div class="audio-type ppe-icon"
:title="$root.getLz('settings.option.audio.enableAdvancedFunctionality.ciderPPE')" v-b-tooltip.hover v-if="mk.nowPlayingItem.localFilesMetadata == null && cfg.audio.maikiwiAudio.ciderPPE === true"
:title="$root.getLz('settings.option.audio.enableAdvancedFunctionality.ciderPPE')"
v-b-tooltip.hover
></div> ></div>
</div> </div>
</div> </div>
@ -87,7 +102,8 @@
<div class="app-chrome--center"> <div class="app-chrome--center">
<div class="app-chrome-playback-duration-bottom"> <div class="app-chrome-playback-duration-bottom">
<b-row v-if="mkReady()"> <b-row v-if="mkReady()">
<b-col sm="auto" v-if="!mk.nowPlayingItem?.isLiveRadioStation">{{ convertTime(getSongProgress()) }}</b-col> <b-col sm="auto" v-if="!mk.nowPlayingItem?.isLiveRadioStation">{{ convertTime(getSongProgress()) }}
</b-col>
<b-col sm="auto" v-else>--:--</b-col> <b-col sm="auto" v-else>--:--</b-col>
<b-col> <b-col>
<input type="range" step="0.01" min="0" :style="progressBarStyle()" <input type="range" step="0.01" min="0" :style="progressBarStyle()"
@ -96,13 +112,16 @@
@touchend="mk.seekToTime($event.target.value);setTimeout(()=>{playerLCD.desiredDuration = 0;playerLCD.userInteraction = false}, 1000);" @touchend="mk.seekToTime($event.target.value);setTimeout(()=>{playerLCD.desiredDuration = 0;playerLCD.userInteraction = false}, 1000);"
:max="mk.currentPlaybackDuration" :value="getSongProgress()"> :max="mk.currentPlaybackDuration" :value="getSongProgress()">
</b-col> </b-col>
<b-col sm="auto" v-if="!mk.nowPlayingItem?.isLiveRadioStation">{{ convertTime(mk.currentPlaybackDuration) }}</b-col> <b-col sm="auto" v-if="!mk.nowPlayingItem?.isLiveRadioStation">{{
convertTime(mk.currentPlaybackDuration) }}
</b-col>
<b-col sm="auto" v-else>{{ getLz("term.live") }}</b-col> <b-col sm="auto" v-else>{{ getLz("term.live") }}</b-col>
</b-row> </b-row>
</div> </div>
<div class="app-chrome-playback-controls"> <div class="app-chrome-playback-controls">
<div class="app-chrome-item"> <div class="app-chrome-item">
<button class="playback-button--small shuffle" v-if="mk.shuffleMode == 0" :class="isDisabled() && 'disabled'" <button class="playback-button--small shuffle" v-if="mk.shuffleMode == 0"
:class="isDisabled() && 'disabled'"
@click="mk.shuffleMode = 1" :title="$root.getLz('term.enableShuffle')" @click="mk.shuffleMode = 1" :title="$root.getLz('term.enableShuffle')"
v-b-tooltip.hover></button> v-b-tooltip.hover></button>
<button class="playback-button--small shuffle active" v-else :class="isDisabled() && 'disabled'" <button class="playback-button--small shuffle active" v-else :class="isDisabled() && 'disabled'"
@ -114,7 +133,8 @@
:title="$root.getLz('term.previous')" v-b-tooltip.hover></button> :title="$root.getLz('term.previous')" v-b-tooltip.hover></button>
</div> </div>
<div class="app-chrome-item"> <div class="app-chrome-item">
<button class="playback-button stop" @click="mk.stop()" v-if="mk.isPlaying && mk.nowPlayingItem.attributes.playParams.kind == 'radioStation'" <button class="playback-button stop" @click="mk.stop()"
v-if="mk.isPlaying && mk.nowPlayingItem.attributes.playParams.kind == 'radioStation'"
:title="$root.getLz('term.stop')" v-b-tooltip.hover></button> :title="$root.getLz('term.stop')" v-b-tooltip.hover></button>
<button class="playback-button pause" @click="mk.pause()" v-else-if="mk.isPlaying" <button class="playback-button pause" @click="mk.pause()" v-else-if="mk.isPlaying"
:title="$root.getLz('term.pause')" v-b-tooltip.hover></button> :title="$root.getLz('term.pause')" v-b-tooltip.hover></button>
@ -126,13 +146,16 @@
:title="$root.getLz('term.next')" v-b-tooltip.hover></button> :title="$root.getLz('term.next')" v-b-tooltip.hover></button>
</div> </div>
<div class="app-chrome-item"> <div class="app-chrome-item">
<button class="playback-button--small repeat" v-if="mk.repeatMode == 0" :class="isDisabled() && 'disabled'" <button class="playback-button--small repeat" v-if="mk.repeatMode == 0"
:class="isDisabled() && 'disabled'"
@click="mk.repeatMode = 1" :title="$root.getLz('term.enableRepeatOne')" @click="mk.repeatMode = 1" :title="$root.getLz('term.enableRepeatOne')"
v-b-tooltip.hover></button> v-b-tooltip.hover></button>
<button class="playback-button--small repeat repeatOne" @click="mk.repeatMode = 2" :class="isDisabled() && 'disabled'" <button class="playback-button--small repeat repeatOne" @click="mk.repeatMode = 2"
:class="isDisabled() && 'disabled'"
v-else-if="mk.repeatMode == 1" :title="$root.getLz('term.disableRepeatOne')" v-else-if="mk.repeatMode == 1" :title="$root.getLz('term.disableRepeatOne')"
v-b-tooltip.hover></button> v-b-tooltip.hover></button>
<button class="playback-button--small repeat active" @click="mk.repeatMode = 0" :class="isDisabled() && 'disabled'" <button class="playback-button--small repeat active" @click="mk.repeatMode = 0"
:class="isDisabled() && 'disabled'"
v-else-if="mk.repeatMode == 2" :title="$root.getLz('term.disableRepeat')" v-else-if="mk.repeatMode == 2" :title="$root.getLz('term.disableRepeat')"
v-b-tooltip.hover></button> v-b-tooltip.hover></button>
</div> </div>

View file

@ -1,6 +1,7 @@
<div class="app-chrome" :style="{'display': chrome.topChromeVisible ? '' : 'none'}"> <div class="app-chrome" :style="{'display': chrome.topChromeVisible ? '' : 'none'}">
<div class="app-chrome--left"> <div class="app-chrome--left">
<div class="app-chrome-item full-height" v-if="chrome.windowControlPosition == 'left' && !chrome.nativeControls"> <div class="app-chrome-item full-height"
v-if="chrome.windowControlPosition == 'left' && !chrome.nativeControls">
<div class="window-controls-macos"> <div class="window-controls-macos">
<div class="close" @click="ipcRenderer.send('close')"></div> <div class="close" @click="ipcRenderer.send('close')"></div>
<div class="minimize" @click="ipcRenderer.send('minimize')"></div> <div class="minimize" @click="ipcRenderer.send('minimize')"></div>
@ -16,7 +17,8 @@
<template v-if="getThemeDirective('appNavigation') != 'seperate'"> <template v-if="getThemeDirective('appNavigation') != 'seperate'">
<div class="vdiv" v-if="getThemeDirective('windowLayout') == 'twopanel'"></div> <div class="vdiv" v-if="getThemeDirective('windowLayout') == 'twopanel'"></div>
<div class="app-chrome-item"> <div class="app-chrome-item">
<button class="playback-button navigation" @click="navigateBack()" :title="$root.getLz('term.navigateBack')" <button class="playback-button navigation" @click="navigateBack()"
:title="$root.getLz('term.navigateBack')"
v-b-tooltip.hover> v-b-tooltip.hover>
<svg-icon url="./views/svg/chevron-left.svg"></svg-icon> <svg-icon url="./views/svg/chevron-left.svg"></svg-icon>
</button> </button>
@ -45,13 +47,17 @@
<template v-if="getThemeDirective('windowLayout') != 'twopanel'"> <template v-if="getThemeDirective('windowLayout') != 'twopanel'">
<div class="app-chrome-item playback-control-buttons"> <div class="app-chrome-item playback-control-buttons">
<div class="app-chrome-item display--large"> <div class="app-chrome-item display--large">
<button class="playback-button--small shuffle" v-if="mk.shuffleMode == 0" :class="isDisabled() && 'disabled'" <button class="playback-button--small shuffle" v-if="mk.shuffleMode == 0"
@click="mk.shuffleMode = 1" :title="$root.getLz('term.enableShuffle')" v-b-tooltip.hover></button> :class="isDisabled() && 'disabled'"
@click="mk.shuffleMode = 1" :title="$root.getLz('term.enableShuffle')"
v-b-tooltip.hover></button>
<button class="playback-button--small shuffle active" v-else :class="isDisabled() && 'disabled'" <button class="playback-button--small shuffle active" v-else :class="isDisabled() && 'disabled'"
@click="mk.shuffleMode = 0" :title="$root.getLz('term.disableShuffle')" v-b-tooltip.hover></button> @click="mk.shuffleMode = 0" :title="$root.getLz('term.disableShuffle')"
v-b-tooltip.hover></button>
</div> </div>
<div class="app-chrome-item display--large"> <div class="app-chrome-item display--large">
<button class="playback-button previous" @click="prevButton()" :class="isPrevDisabled() && 'disabled'" <button class="playback-button previous" @click="prevButton()"
:class="isPrevDisabled() && 'disabled'"
:title="$root.getLz('term.previous')" v-b-tooltip.hover></button> :title="$root.getLz('term.previous')" v-b-tooltip.hover></button>
</div> </div>
<div class="app-chrome-item display--large"> <div class="app-chrome-item display--large">
@ -64,17 +70,21 @@
v-b-tooltip.hover></button> v-b-tooltip.hover></button>
</div> </div>
<div class="app-chrome-item display--large"> <div class="app-chrome-item display--large">
<button class="playback-button next" @click="skipToNextItem()" :class="isNextDisabled() && 'disabled'" <button class="playback-button next" @click="skipToNextItem()"
:class="isNextDisabled() && 'disabled'"
:title="$root.getLz('term.next')" v-b-tooltip.hover></button> :title="$root.getLz('term.next')" v-b-tooltip.hover></button>
</div> </div>
<div class="app-chrome-item display--large"> <div class="app-chrome-item display--large">
<button class="playback-button--small repeat" v-if="mk.repeatMode == 0" :class="isDisabled() && 'disabled'" <button class="playback-button--small repeat" v-if="mk.repeatMode == 0"
@click="mk.repeatMode = 1" :title="$root.getLz('term.enableRepeatOne')" v-b-tooltip.hover></button> :class="isDisabled() && 'disabled'"
@click="mk.repeatMode = 1" :title="$root.getLz('term.enableRepeatOne')"
v-b-tooltip.hover></button>
<button class="playback-button--small repeat repeatOne" @click="mk.repeatMode = 2" <button class="playback-button--small repeat repeatOne" @click="mk.repeatMode = 2"
:class="isDisabled() && 'disabled'" v-else-if="mk.repeatMode == 1" :class="isDisabled() && 'disabled'" v-else-if="mk.repeatMode == 1"
:title="$root.getLz('term.disableRepeatOne')" v-b-tooltip.hover></button> :title="$root.getLz('term.disableRepeatOne')" v-b-tooltip.hover></button>
<button class="playback-button--small repeat active" @click="mk.repeatMode = 0" <button class="playback-button--small repeat active" @click="mk.repeatMode = 0"
:class="isDisabled() && 'disabled'" v-else-if="mk.repeatMode == 2" :title="$root.getLz('term.disableRepeat')" :class="isDisabled() && 'disabled'" v-else-if="mk.repeatMode == 2"
:title="$root.getLz('term.disableRepeat')"
v-b-tooltip.hover></button> v-b-tooltip.hover></button>
</div> </div>
</div> </div>
@ -111,10 +121,12 @@
</div> </div>
<hr /> <hr />
<div class="btn-group" style="width: 100%"> <div class="btn-group" style="width: 100%">
<button class="md-btn md-btn-small" style="width: 100%" @click="drawer.open = false; miniPlayer(true)"> <button class="md-btn md-btn-small" style="width: 100%"
@click="drawer.open = false; miniPlayer(true)">
{{ $root.getLz("term.miniplayer") }} {{ $root.getLz("term.miniplayer") }}
</button> </button>
<button class="md-btn md-btn-small" style="width: 100%" @click="drawer.open = false; fullscreen(true)"> <button class="md-btn md-btn-small" style="width: 100%"
@click="drawer.open = false; fullscreen(true)">
{{ $root.getLz("term.fullscreenView") }} {{ $root.getLz("term.fullscreenView") }}
</button> </button>
</div> </div>
@ -124,20 +136,26 @@
<div class="chrome-icon-container"> <div class="chrome-icon-container">
<div class="audio-type private-icon" v-if="cfg.general.privateEnabled === true"></div> <div class="audio-type private-icon" v-if="cfg.general.privateEnabled === true"></div>
<div class="audio-type spatial-icon" v-if="cfg.audio.maikiwiAudio.spatial === true" <div class="audio-type spatial-icon" v-if="cfg.audio.maikiwiAudio.spatial === true"
:title="$root.getLz('settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization') + ' (' + getProfileLz('CTS', cfg.audio.maikiwiAudio.spatialProfile) + ')'" v-b-tooltip.hover :title="$root.getLz('settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization') + ' (' + getProfileLz('CTS', cfg.audio.maikiwiAudio.spatialProfile) + ')'"
v-b-tooltip.hover
></div> ></div>
<div class="audio-type lossless-icon" v-if="(mk.nowPlayingItem?.localFilesMetadata?.lossless ?? false) === true" <div class="audio-type lossless-icon"
:title="mk.nowPlayingItem?.localFilesMetadata?.bitDepth +'-bit / '+ mk.nowPlayingItem?.localFilesMetadata?.sampleRate/1000 + ' kHz ' + mk.nowPlayingItem.localFilesMetadata.container" v-b-tooltip.hover v-if="(mk.nowPlayingItem?.localFilesMetadata?.lossless ?? false) === true"
:title="mk.nowPlayingItem?.localFilesMetadata?.bitDepth +'-bit / '+ mk.nowPlayingItem?.localFilesMetadata?.sampleRate/1000 + ' kHz ' + mk.nowPlayingItem.localFilesMetadata.container"
v-b-tooltip.hover
></div> ></div>
<div class="audio-type ppe-icon" v-if="mk.nowPlayingItem.localFilesMetadata == null && cfg.audio.maikiwiAudio.ciderPPE === true" <div class="audio-type ppe-icon"
:title="$root.getLz('settings.option.audio.enableAdvancedFunctionality.ciderPPE')" v-b-tooltip.hover v-if="mk.nowPlayingItem.localFilesMetadata == null && cfg.audio.maikiwiAudio.ciderPPE === true"
:title="$root.getLz('settings.option.audio.enableAdvancedFunctionality.ciderPPE')"
v-b-tooltip.hover
></div> ></div>
</div> </div>
<div class="info-rect"> <div class="info-rect">
<div class="song-name" <div class="song-name"
:class="[isElementOverflowing('#app-main > div.app-chrome > div.app-chrome--center > div > div > div.playback-info > div.song-name') ? 'marquee' : '']"> :class="[isElementOverflowing('#app-main > div.app-chrome > div.app-chrome--center > div > div > div.playback-info > div.song-name') ? 'marquee' : '']">
{{ mk.nowPlayingItem["attributes"]["name"] }} {{ mk.nowPlayingItem["attributes"]["name"] }}
<div class="explicit-icon" v-if="mk.nowPlayingItem['attributes']['contentRating'] == 'explicit'" <div class="explicit-icon"
v-if="mk.nowPlayingItem['attributes']['contentRating'] == 'explicit'"
style="display: inline-block"></div> style="display: inline-block"></div>
</div> </div>
<div class="song-artist-album"> <div class="song-artist-album">
@ -208,13 +226,17 @@
</div> </div>
<div class="app-chrome-item" v-else> <div class="app-chrome-item" v-else>
<div class="top-nav-group"> <div class="top-nav-group">
<sidebar-library-item :name="$root.getLz('home.title')" svg-icon="./assets/feather/home.svg" svg-icon-name="home" page="home"> <sidebar-library-item :name="$root.getLz('home.title')" svg-icon="./assets/feather/home.svg"
svg-icon-name="home" page="home">
</sidebar-library-item> </sidebar-library-item>
<sidebar-library-item :name="$root.getLz('term.listenNow')" svg-icon="./assets/feather/play-circle.svg" svg-icon-name="listenNow" <sidebar-library-item :name="$root.getLz('term.listenNow')" svg-icon="./assets/feather/play-circle.svg"
svg-icon-name="listenNow"
page="listen_now"></sidebar-library-item> page="listen_now"></sidebar-library-item>
<sidebar-library-item :name="$root.getLz('term.browse')" svg-icon="./assets/feather/globe.svg" svg-icon-name="browse" page="browse"> <sidebar-library-item :name="$root.getLz('term.browse')" svg-icon="./assets/feather/globe.svg"
svg-icon-name="browse" page="browse">
</sidebar-library-item> </sidebar-library-item>
<sidebar-library-item :name="$root.getLz('term.radio')" svg-icon="./assets/feather/radio.svg" svg-icon-name="radio" page="radio"> <sidebar-library-item :name="$root.getLz('term.radio')" svg-icon="./assets/feather/radio.svg"
svg-icon-name="radio" page="radio">
</sidebar-library-item> </sidebar-library-item>
</div> </div>
</div> </div>
@ -224,9 +246,11 @@
<div class="app-chrome-item volume display--large"> <div class="app-chrome-item volume display--large">
<button class="volume-button--small volume" @click="muteButtonPressed()" <button class="volume-button--small volume" @click="muteButtonPressed()"
:class="{'active': this.cfg.audio.volume == 0}" :class="{'active': this.cfg.audio.volume == 0}"
:title="cfg.audio.muted ? $root.getLz('term.unmute') : $root.getLz('term.mute')" v-b-tooltip.hover></button> :title="cfg.audio.muted ? $root.getLz('term.unmute') : $root.getLz('term.mute')"
v-b-tooltip.hover></button>
<input type="range" @wheel="volumeWheel" :step="cfg.audio.volumeStep" min="0" :max="cfg.audio.maxVolume" <input type="range" @wheel="volumeWheel" :step="cfg.audio.volumeStep" min="0" :max="cfg.audio.maxVolume"
v-model="mk.volume" v-if="typeof mk.volume != 'undefined'" @change="checkMuteChange()" v-b-tooltip.hover v-model="mk.volume" v-if="typeof mk.volume != 'undefined'" @change="checkMuteChange()"
v-b-tooltip.hover
:title="formatVolumeTooltip()" /> :title="formatVolumeTooltip()" />
</div> </div>
<div class="app-chrome-item generic"> <div class="app-chrome-item generic">
@ -244,7 +268,8 @@
:class="{'active': drawer.panel == 'lyrics'}" @click="invokeDrawer('lyrics')"></button> :class="{'active': drawer.panel == 'lyrics'}" @click="invokeDrawer('lyrics')"></button>
</template> </template>
<template v-else> <template v-else>
<button class="playback-button--small lyrics" :style="{'opacity': 0.3, 'pointer-events': 'none'}" ></button> <button class="playback-button--small lyrics"
:style="{'opacity': 0.3, 'pointer-events': 'none'}"></button>
</template> </template>
</div> </div>
</template> </template>
@ -252,9 +277,11 @@
<div class="app-chrome-item search"> <div class="app-chrome-item search">
<div class="search-input-container"> <div class="search-input-container">
<div class="search-input--icon"></div> <div class="search-input--icon"></div>
<input type="search" spellcheck="false" @click="$root.appRoute('search');" @focus="search.showHints = true" <input type="search" spellcheck="false" @click="$root.appRoute('search');"
@focus="search.showHints = true"
@blur="setTimeout(()=>{search.showHints = false}, 300)" @blur="setTimeout(()=>{search.showHints = false}, 300)"
v-on:keyup.enter="searchQuery();search.showHints = false" @change="$root.appRoute('search');" @input="getSearchHints()" v-on:keyup.enter="searchQuery();search.showHints = false" @change="$root.appRoute('search');"
@input="getSearchHints()"
:placeholder="$root.getLz('term.search') + '...'" v-model="search.term" ref="searchInput" :placeholder="$root.getLz('term.search') + '...'" v-model="search.term" ref="searchInput"
class="search-input" /> class="search-input" />

View file

@ -93,7 +93,8 @@
:class="{'active': this.cfg.audio.volume == 0}" :class="{'active': this.cfg.audio.volume == 0}"
:title="cfg.audio.muted ? $root.getLz('term.unmute') : $root.getLz('term.mute')" :title="cfg.audio.muted ? $root.getLz('term.unmute') : $root.getLz('term.mute')"
v-b-tooltip.hover></button> v-b-tooltip.hover></button>
<input type="range" @wheel="volumeWheel" :step="cfg.audio.volumeStep" min="0" :max="cfg.audio.maxVolume" <input type="range" @wheel="volumeWheel" :step="cfg.audio.volumeStep" min="0"
:max="cfg.audio.maxVolume"
v-model="mk.volume" v-if="typeof mk.volume != 'undefined'" @change="checkMuteChange()" v-model="mk.volume" v-if="typeof mk.volume != 'undefined'" @change="checkMuteChange()"
v-b-tooltip.hover :title="formatVolumeTooltip()"> v-b-tooltip.hover :title="formatVolumeTooltip()">
</div> </div>

View file

@ -1,5 +1,6 @@
<script type="text/x-template" id="add-to-playlist"> <script type="text/x-template" id="add-to-playlist">
<div class="modal-fullscreen addtoplaylist-panel" @click.self="app.resetState()" @contextmenu.self="app.resetState()"> <div class="modal-fullscreen addtoplaylist-panel" @click.self="app.resetState()"
@contextmenu.self="app.resetState()">
<div class="modal-window"> <div class="modal-window">
<div class="modal-header"> <div class="modal-header">
<div class="modal-title">{{app.getLz('action.addToPlaylist')}}</div> <div class="modal-title">{{app.getLz('action.addToPlaylist')}}</div>
@ -11,7 +12,8 @@
<div class="icon"><%- include("../svg/plus.svg") %></div> <div class="icon"><%- include("../svg/plus.svg") %></div>
<div class="name">{{app.getLz('action.createPlaylist')}}</div> <div class="name">{{app.getLz('action.createPlaylist')}}</div>
</button> </button>
<sidebar-playlist :playlist-select="playlistSelect" :relate-media-items="relateItems" v-for="item in $root.getPlaylistFolderChildren('p.playlistsroot')" :item="item"> <sidebar-playlist :playlist-select="playlistSelect" :relate-media-items="relateItems"
v-for="item in $root.getPlaylistFolderChildren('p.playlistsroot')" :item="item">
</sidebar-playlist> </sidebar-playlist>
</div> </div>
<div class="modal-search"> <div class="modal-search">

View file

@ -1,5 +1,6 @@
<script type="text/x-template" id="airplay-modal"> <script type="text/x-template" id="airplay-modal">
<div class="spatialproperties-panel castmenu modal-fullscreen airplay-modal" @click.self="close()" @contextmenu.self="close()"> <div class="spatialproperties-panel castmenu modal-fullscreen airplay-modal" @click.self="close()"
@contextmenu.self="close()">
<div class="modal-window airplay-modal"> <div class="modal-window airplay-modal">
<div class="modal-header"> <div class="modal-header">
<div class="modal-title">{{'Enter password'}}</div> <div class="modal-title">{{'Enter password'}}</div>
@ -11,7 +12,9 @@
<div class="md-footer"> <div class="md-footer">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<button style="width:100%" @click="enterPassword()" class="md-btn md-btn-block md-btn-primary">{{'OK'}}</button> <button style="width:100%" @click="enterPassword()" class="md-btn md-btn-block md-btn-primary">
{{'OK'}}
</button>
</div> </div>
</div> </div>
</div> </div>

View file

@ -63,7 +63,9 @@
let u = this.hls; let u = this.hls;
var quality = app.cfg.visual.animated_artwork_qualityLevel; var quality = app.cfg.visual.animated_artwork_qualityLevel;
setTimeout(() => { setTimeout(() => {
let levelsnum = u.levels.map((level)=>{return level.width}) let levelsnum = u.levels.map((level) => {
return level.width
})
if (levelsnum.length > 0) { if (levelsnum.length > 0) {
let qualities = [] let qualities = []
let qualities2 = [] let qualities2 = []
@ -82,7 +84,10 @@
} }
} }
try { try {
this.hls.loadLevel = parseInt( quality || 1);} catch(e){}},200) this.hls.loadLevel = parseInt(quality || 1);
} catch (e) {
}
}, 200)
} }
}) })
} }

View file

@ -15,14 +15,14 @@
<transition <transition
<% if(env.appRoutes[i].onEnter) { <% if(env.appRoutes[i].onEnter) {
%> %>
v-on:enter="<%- env.appRoutes[i].onEnter %>" v-on:enter="<%- env.appRoutes[i].onEnter; %>"
<% <%
} }
%> %>
:name="$root.chrome.desiredPageTransition"> :name="$root.chrome.desiredPageTransition">
<template <template
v-if="<%- env.appRoutes[i].condition %>"> v-if="<%- env.appRoutes[i].condition; %>">
<%- env.appRoutes[i].component %> <%- env.appRoutes[i].component; %>
</template> </template>
</transition> </transition>
<% } %> <% } %>
@ -45,7 +45,6 @@
scrollPos: 0 scrollPos: 0
} }
}, },
methods: { methods: {}
}
}); });
</script> </script>

View file

@ -1,15 +1,19 @@
<script type="text/x-template" id="artist-chip"> <script type="text/x-template" id="artist-chip">
<div class="artist-chip" @click.self="route" tabindex="0"> <div class="artist-chip" @click.self="route" tabindex="0">
<div class="artist-chip__image" v-if="image" :style="{backgroundColor: '#' + (artist.attributes.artwork?.bgColor ?? '000')}"> <div class="artist-chip__image" v-if="image"
<mediaitem-artwork v-if="artist.id != null" :url="artist.attributes.artwork.url" :size="80"></mediaitem-artwork> :style="{backgroundColor: '#' + (artist.attributes.artwork?.bgColor ?? '000')}">
<mediaitem-artwork v-if="artist.id != null" :url="artist.attributes.artwork.url"
:size="80"></mediaitem-artwork>
</div> </div>
<div class="artist-chip__image" v-else> <div class="artist-chip__image" v-else>
</div> </div>
<div class="artist-chip__name"> <div class="artist-chip__name">
<span>{{ item.attributes.name }}</span> <span>{{ item.attributes.name }}</span>
</div> </div>
<button @click="$root.setArtistFavorite(artist.id, true)" title="Follow" v-if="!$root.followingArtist(artist.id)" class="artist-chip__follow codicon codicon-add"></button> <button @click="$root.setArtistFavorite(artist.id, true)" title="Follow"
<button @click="$root.setArtistFavorite(artist.id, false)" title="Following" v-else class="artist-chip__follow codicon codicon-check"></button> v-if="!$root.followingArtist(artist.id)" class="artist-chip__follow codicon codicon-add"></button>
<button @click="$root.setArtistFavorite(artist.id, false)" title="Following" v-else
class="artist-chip__follow codicon codicon-check"></button>
</div> </div>
</script> </script>

View file

@ -31,7 +31,6 @@
default: '2' default: '2'
} }
}, },
methods: { methods: {}
}
}); });
</script> </script>

View file

@ -4,7 +4,8 @@
<div class="modal-window"> <div class="modal-window">
<div class="modal-header"> <div class="modal-header">
<div class="modal-title">{{app.getLz('term.audioControls')}}</div> <div class="modal-title">{{app.getLz('term.audioControls')}}</div>
<button class="close-btn" @click="app.modals.audioControls = false" :aria-label="app.getLz('action.close')"></button> <button class="close-btn" @click="app.modals.audioControls = false"
:aria-label="app.getLz('action.close')"></button>
</div> </div>
<div class="modal-content"> <div class="modal-content">
<div class="md-option-line"> <div class="md-option-line">

View file

@ -4,7 +4,8 @@
<div class="modal-window"> <div class="modal-window">
<div class="modal-header"> <div class="modal-header">
<div class="modal-title">{{app.getLz('settings.option.audio.changePlaybackRate')}}</div> <div class="modal-title">{{app.getLz('settings.option.audio.changePlaybackRate')}}</div>
<button class="close-btn" @click="app.modals.audioPlaybackRate = false" :aria-label="app.getLz('action.close')"></button> <button class="close-btn" @click="app.modals.audioPlaybackRate = false"
:aria-label="app.getLz('action.close')"></button>
</div> </div>
<div class="modal-content"> <div class="modal-content">
<div class="md-option-line"> <div class="md-option-line">
@ -15,7 +16,8 @@
{{playbackRate}} × {{playbackRate}} ×
</div> </div>
<div class="md-option-segment md-option-segment_auto"> <div class="md-option-segment md-option-segment_auto">
<input type="range" :step="0.05" min="0.25" :max="2" @wheel="playbackRateWheel" v-model="playbackRate"> <input type="range" :step="0.05" min="0.25" :max="2" @wheel="playbackRateWheel"
v-model="playbackRate">
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,10 +1,12 @@
<script type="text/x-template" id="audio-settings"> <script type="text/x-template" id="audio-settings">
<template> <template>
<div class="modal-fullscreen addtoplaylist-panel" @click.self="app.modals.audioSettings = false" @contextmenu.self="app.modals.audioSettings = false"> <div class="modal-fullscreen addtoplaylist-panel" @click.self="app.modals.audioSettings = false"
@contextmenu.self="app.modals.audioSettings = false">
<div class="modal-window"> <div class="modal-window">
<div class="modal-header"> <div class="modal-header">
<div class="modal-title">{{app.getLz('term.audioSettings')}}</div> <div class="modal-title">{{app.getLz('term.audioSettings')}}</div>
<button class="close-btn" @click="app.modals.audioSettings = false" :aria-label="app.getLz('action.close')"></button> <button class="close-btn" @click="app.modals.audioSettings = false"
:aria-label="app.getLz('action.close')"></button>
</div> </div>
<div class="modal-content"> <div class="modal-content">
<button class="playlist-item" <button class="playlist-item"
@ -42,7 +44,8 @@
} }
}, },
props: {}, props: {},
mounted() {}, mounted() {
},
methods: { methods: {
openEqualizer() { openEqualizer() {
app.modals.equalizer = true app.modals.equalizer = true

View file

@ -16,11 +16,18 @@
<br> <br>
<small>{{ device.host }}</small> <small>{{ device.host }}</small>
</div> </div>
<div class="md-option-segment_auto" style="display: flex;justify-content: center;align-items: center" v-if="activeCasts.includes(device)"> <div class="md-option-segment_auto"
style="display: flex;justify-content: center;align-items: center"
v-if="activeCasts.includes(device)">
Connected Connected
</div> </div>
<div class="md-option-segment_auto" v-else style="display: flex;justify-content: center;align-items: center"> <div class="md-option-segment_auto" v-else
<svg width="20" height="20" viewBox="0 0 34 34" fill="#fff" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" class="castPlayIndicator"><path d="M28.228,18.327l-16.023,8.983c-0.99,0.555 -2.205,-0.17 -2.205,-1.318l0,-17.984c0,-1.146 1.215,-1.873 2.205,-1.317l16.023,8.982c1.029,0.577 1.029,2.077 0,2.654Z" style="fill-rule:nonzero"></path></svg> style="display: flex;justify-content: center;align-items: center">
<svg width="20" height="20" viewBox="0 0 34 34" fill="#fff" version="1.1"
xmlns="http://www.w3.org/2000/svg"
xml:space="preserve" class="castPlayIndicator"><path
d="M28.228,18.327l-16.023,8.983c-0.99,0.555 -2.205,-0.17 -2.205,-1.318l0,-17.984c0,-1.146 1.215,-1.873 2.205,-1.317l16.023,8.982c1.029,0.577 1.029,2.077 0,2.654Z"
style="fill-rule:nonzero"></path></svg>
</div> </div>
</div> </div>
</template> </template>
@ -37,7 +44,8 @@
<div class="md-option-container" style="margin-top: 12px;margin-bottom: 12px;overflow-y: scroll;"> <div class="md-option-container" style="margin-top: 12px;margin-bottom: 12px;overflow-y: scroll;">
<div class="md-option-line"> <div class="md-option-line">
<div class="md-option-segment"> <div class="md-option-segment">
{{'EXPERIMENTAL!!! Supports Homepods / Apple TVs / Shairport for now, AirPlay on Samsung/LG/Sony devices will be added later'}} {{'EXPERIMENTAL!!! Supports Homepods / Apple TVs / Shairport for now, AirPlay on
Samsung/LG/Sony devices will be added later'}}
<!-- {{$root.getLz('action.cast.airplay.underdevelopment')}} --> <!-- {{$root.getLz('action.cast.airplay.underdevelopment')}} -->
<template v-if="true" v-for="(device) in devices.airplay"> <template v-if="true" v-for="(device) in devices.airplay">
<div class="md-option-line" style="cursor: pointer" @click="setAirPlayCast(device)"> <div class="md-option-line" style="cursor: pointer" @click="setAirPlayCast(device)">
@ -46,11 +54,19 @@
<br> <br>
<small>{{ device.host }}</small> <small>{{ device.host }}</small>
</div> </div>
<div class="md-option-segment_auto" style="display: flex;justify-content: center;align-items: center" v-if="activeCasts.includes(device)"> <div class="md-option-segment_auto"
style="display: flex;justify-content: center;align-items: center"
v-if="activeCasts.includes(device)">
Connected Connected
</div> </div>
<div class="md-option-segment_auto" v-else style="display: flex;justify-content: center;align-items: center"> <div class="md-option-segment_auto" v-else
<svg width="20" height="20" viewBox="0 0 34 34" fill="#fff" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" class="castPlayIndicator"><path d="M28.228,18.327l-16.023,8.983c-0.99,0.555 -2.205,-0.17 -2.205,-1.318l0,-17.984c0,-1.146 1.215,-1.873 2.205,-1.317l16.023,8.982c1.029,0.577 1.029,2.077 0,2.654Z" style="fill-rule:nonzero"></path></svg> style="display: flex;justify-content: center;align-items: center">
<svg width="20" height="20" viewBox="0 0 34 34" fill="#fff" version="1.1"
xmlns="http://www.w3.org/2000/svg"
xml:space="preserve"
class="castPlayIndicator"><path
d="M28.228,18.327l-16.023,8.983c-0.99,0.555 -2.205,-0.17 -2.205,-1.318l0,-17.984c0,-1.146 1.215,-1.873 2.205,-1.317l16.023,8.982c1.029,0.577 1.029,2.077 0,2.654Z"
style="fill-rule:nonzero"></path></svg>
</div> </div>
</div> </div>
</template> </template>
@ -61,10 +77,14 @@
<div class="md-footer"> <div class="md-footer">
<div class="row"> <div class="row">
<div class="col" v-if="activeCasts.length != 0"> <div class="col" v-if="activeCasts.length != 0">
<button style="width:100%" @click="stopCasting()" class="md-btn md-btn-block md-btn-primary">{{$root.getLz('action.cast.stop')}}</button> <button style="width:100%" @click="stopCasting()" class="md-btn md-btn-block md-btn-primary">
{{$root.getLz('action.cast.stop')}}
</button>
</div> </div>
<div class="col"> <div class="col">
<button style="width:100%" class="md-btn md-btn-block" @click="scan()">{{$root.getLz('action.cast.scan')}}</button> <button style="width:100%" class="md-btn md-btn-block" @click="scan()">
{{$root.getLz('action.cast.scan')}}
</button>
</div> </div>
</div> </div>
</div> </div>
@ -91,7 +111,8 @@
watch: { watch: {
activeCasts: function(newVal, oldVal) { activeCasts: function(newVal, oldVal) {
this.$root.activeCasts = this.activeCasts; this.$root.activeCasts = this.activeCasts;
}}, }
},
methods: { methods: {
close() { close() {
this.$root.modals.castMenu = false this.$root.modals.castMenu = false

View file

@ -4,12 +4,14 @@
<div class="modal-window"> <div class="modal-window">
<div class="modal-header"> <div class="modal-header">
<div class="modal-title">{{app.getLz('action.addToLibrary')}}</div> <div class="modal-title">{{app.getLz('action.addToLibrary')}}</div>
<button class="close-btn" @click="app.resetState()" :aria-label="app.getLz('action.close')"></button> <button class="close-btn" @click="app.resetState()"
:aria-label="app.getLz('action.close')"></button>
</div> </div>
<div class="modal-content"> <div class="modal-content">
<button class="playlist-item" <button class="playlist-item"
:class="{ focused: playlist.id == focused }" :class="{ focused: playlist.id == focused }"
@click="addToPlaylist(playlist.id)" style="width:100%;" v-for="playlist in playlistSorted" v-if="playlist.attributes.canEdit && playlist.type != 'library-playlist-folders'"> @click="addToPlaylist(playlist.id)" style="width:100%;" v-for="playlist in playlistSorted"
v-if="playlist.attributes.canEdit && playlist.type != 'library-playlist-folders'">
<div class="icon"><%- include("../svg/playlist.svg") %></div> <div class="icon"><%- include("../svg/playlist.svg") %></div>
<div class="name">{{ playlist.attributes.name }}</div> <div class="name">{{ playlist.attributes.name }}</div>
</button> </button>

View file

@ -5,9 +5,13 @@
<div class="modal-title">{{$root.getLz('term.equalizer')}}</div> <div class="modal-title">{{$root.getLz('term.equalizer')}}</div>
<button class="close-btn" @click="close()" :aria-label="$root.getLz('action.close')"></button> <button class="close-btn" @click="close()" :aria-label="$root.getLz('action.close')"></button>
<div class="md-option-segment md-option-segment_auto"> <div class="md-option-segment md-option-segment_auto">
<select class="md-select" style="width:220px;text-align:center;margin-right:245px" v-model="$root.cfg.audio.equalizer.preset" v-on:change="changePreset($root.cfg.audio.equalizer.preset)"> <select class="md-select" style="width:220px;text-align:center;margin-right:245px"
v-model="$root.cfg.audio.equalizer.preset"
v-on:change="changePreset($root.cfg.audio.equalizer.preset)">
<optgroup :label="$root.getLz('term.userPresets')"> <optgroup :label="$root.getLz('term.userPresets')">
<option v-for="preset in $root.cfg.audio.equalizer.presets" :value="preset.preset">{{preset.name}}</option> <option v-for="preset in $root.cfg.audio.equalizer.presets" :value="preset.preset">
{{preset.name}}
</option>
</optgroup> </optgroup>
<optgroup :label="$root.getLz('term.defaultPresets')"> <optgroup :label="$root.getLz('term.defaultPresets')">
<option v-for="preset in defaultPresets" :value="preset.preset">{{preset.name}}</option> <option v-for="preset in defaultPresets" :value="preset.preset">{{preset.name}}</option>
@ -20,12 +24,14 @@
<div class="inputs-container"> <div class="inputs-container">
<div class="input-container mini"> <div class="input-container mini">
{{$root.cfg.audio.equalizer.vibrantBass}} {{$root.cfg.audio.equalizer.vibrantBass}}
<input tabindex="0" type="range" class="eq-slider mini" orient="vertical" min="-15" max="15" step="1" v-model="$root.cfg.audio.equalizer.vibrantBass" @change="changeVibrantBass()"> <input tabindex="0" type="range" class="eq-slider mini" orient="vertical" min="-15" max="15"
step="1" v-model="$root.cfg.audio.equalizer.vibrantBass" @change="changeVibrantBass()">
Vibrant Bass Vibrant Bass
</div> </div>
<div class="input-container mini"> <div class="input-container mini">
{{$root.cfg.audio.equalizer.mix}} {{$root.cfg.audio.equalizer.mix}}
<input tabindex="0" type="range" class="eq-slider mini" orient="vertical" min="0" max="2" step="0.1" v-model="$root.cfg.audio.equalizer.mix" @change="changeMix()"> <input tabindex="0" type="range" class="eq-slider mini" orient="vertical" min="0" max="2"
step="0.1" v-model="$root.cfg.audio.equalizer.mix" @change="changeMix()">
Mix Mix
</div> </div>
<div class="input-container header mini"> <div class="input-container header mini">
@ -35,64 +41,104 @@
<div>Q</div> <div>Q</div>
</div> </div>
<div class="input-container"> <div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[0]" @change="changeGain(0)"> <input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12"
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[0]" @change="changeGain(0)"> step="0.1" v-model="$root.cfg.audio.equalizer.gain[0]" @change="changeGain(0)">
<input type="number" class="eq-freq" orient="vertical" min="22" max="44" step="2" v-model="$root.cfg.audio.equalizer.frequencies[0]" @change="changeFreq(0)"> <input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12"
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[0]" @change="changeQ(0)"> step="0.1" v-model="$root.cfg.audio.equalizer.gain[0]" @change="changeGain(0)">
<input type="number" class="eq-freq" orient="vertical" min="22" max="44" step="2"
v-model="$root.cfg.audio.equalizer.frequencies[0]" @change="changeFreq(0)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1"
v-model="$root.cfg.audio.equalizer.Q[0]" @change="changeQ(0)">
</div> </div>
<div class="input-container"> <div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[1]" @change="changeGain(1)"> <input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12"
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[1]" @change="changeGain(1)"> step="0.1" v-model="$root.cfg.audio.equalizer.gain[1]" @change="changeGain(1)">
<input type="number" class="eq-freq" orient="vertical" min="44" max="88" step="4" v-model="$root.cfg.audio.equalizer.frequencies[1]" @change="changeFreq(1)"> <input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12"
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[1]" @change="changeQ(1)"> step="0.1" v-model="$root.cfg.audio.equalizer.gain[1]" @change="changeGain(1)">
<input type="number" class="eq-freq" orient="vertical" min="44" max="88" step="4"
v-model="$root.cfg.audio.equalizer.frequencies[1]" @change="changeFreq(1)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1"
v-model="$root.cfg.audio.equalizer.Q[1]" @change="changeQ(1)">
</div> </div>
<div class="input-container"> <div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[2]" @change="changeGain(2)"> <input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12"
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[2]" @change="changeGain(2)"> step="0.1" v-model="$root.cfg.audio.equalizer.gain[2]" @change="changeGain(2)">
<input type="number" class="eq-freq" orient="vertical" min="88" max="177" step="8" v-model="$root.cfg.audio.equalizer.frequencies[2]" @change="changeFreq(2)"> <input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12"
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[2]" @change="changeQ(2)"> step="0.1" v-model="$root.cfg.audio.equalizer.gain[2]" @change="changeGain(2)">
<input type="number" class="eq-freq" orient="vertical" min="88" max="177" step="8"
v-model="$root.cfg.audio.equalizer.frequencies[2]" @change="changeFreq(2)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1"
v-model="$root.cfg.audio.equalizer.Q[2]" @change="changeQ(2)">
</div> </div>
<div class="input-container"> <div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[3]" @change="changeGain(3)"> <input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12"
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[3]" @change="changeGain(3)"> step="0.1" v-model="$root.cfg.audio.equalizer.gain[3]" @change="changeGain(3)">
<input type="number" class="eq-freq" orient="vertical" min="177" max="355" step="16" v-model="$root.cfg.audio.equalizer.frequencies[3]" @change="changeFreq(3)"> <input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12"
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[3]" @change="changeQ(3)"> step="0.1" v-model="$root.cfg.audio.equalizer.gain[3]" @change="changeGain(3)">
<input type="number" class="eq-freq" orient="vertical" min="177" max="355" step="16"
v-model="$root.cfg.audio.equalizer.frequencies[3]" @change="changeFreq(3)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1"
v-model="$root.cfg.audio.equalizer.Q[3]" @change="changeQ(3)">
</div> </div>
<div class="input-container"> <div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[4]" @change="changeGain(4)"> <input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12"
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[4]" @change="changeGain(4)"> step="0.1" v-model="$root.cfg.audio.equalizer.gain[4]" @change="changeGain(4)">
<input type="number" class="eq-freq" orient="vertical" min="355" max="710" step="32" v-model="$root.cfg.audio.equalizer.frequencies[4]" @change="changeFreq(4)"> <input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12"
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[4]" @change="changeQ(4)"> step="0.1" v-model="$root.cfg.audio.equalizer.gain[4]" @change="changeGain(4)">
<input type="number" class="eq-freq" orient="vertical" min="355" max="710" step="32"
v-model="$root.cfg.audio.equalizer.frequencies[4]" @change="changeFreq(4)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1"
v-model="$root.cfg.audio.equalizer.Q[4]" @change="changeQ(4)">
</div> </div>
<div class="input-container"> <div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[5]" @change="changeGain(5)"> <input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12"
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[5]" @change="changeGain(5)"> step="0.1" v-model="$root.cfg.audio.equalizer.gain[5]" @change="changeGain(5)">
<input type="number" class="eq-freq" orient="vertical" min="710" max="1420" step="64" v-model="$root.cfg.audio.equalizer.frequencies[5]" @change="changeFreq(5)"> <input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12"
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[5]" @change="changeQ(5)"> step="0.1" v-model="$root.cfg.audio.equalizer.gain[5]" @change="changeGain(5)">
<input type="number" class="eq-freq" orient="vertical" min="710" max="1420" step="64"
v-model="$root.cfg.audio.equalizer.frequencies[5]" @change="changeFreq(5)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1"
v-model="$root.cfg.audio.equalizer.Q[5]" @change="changeQ(5)">
</div> </div>
<div class="input-container"> <div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[6]" @change="changeGain(6)"> <input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12"
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[6]" @change="changeGain(6)"> step="0.1" v-model="$root.cfg.audio.equalizer.gain[6]" @change="changeGain(6)">
<input type="number" class="eq-freq" orient="vertical" min="1420" max="2840" step="128" v-model="$root.cfg.audio.equalizer.frequencies[6]" @change="changeFreq(6)"> <input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12"
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[6]" @change="changeQ(6)"> step="0.1" v-model="$root.cfg.audio.equalizer.gain[6]" @change="changeGain(6)">
<input type="number" class="eq-freq" orient="vertical" min="1420" max="2840" step="128"
v-model="$root.cfg.audio.equalizer.frequencies[6]" @change="changeFreq(6)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1"
v-model="$root.cfg.audio.equalizer.Q[6]" @change="changeQ(6)">
</div> </div>
<div class="input-container"> <div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[7]" @change="changeGain(7)"> <input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12"
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[7]" @change="changeGain(7)"> step="0.1" v-model="$root.cfg.audio.equalizer.gain[7]" @change="changeGain(7)">
<input type="number" class="eq-freq" orient="vertical" min="2840" max="5680" step="256" v-model="$root.cfg.audio.equalizer.frequencies[7]" @change="changeFreq(7)"> <input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12"
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[7]" @change="changeQ(7)"> step="0.1" v-model="$root.cfg.audio.equalizer.gain[7]" @change="changeGain(7)">
<input type="number" class="eq-freq" orient="vertical" min="2840" max="5680" step="256"
v-model="$root.cfg.audio.equalizer.frequencies[7]" @change="changeFreq(7)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1"
v-model="$root.cfg.audio.equalizer.Q[7]" @change="changeQ(7)">
</div> </div>
<div class="input-container"> <div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[8]" @change="changeGain(8)"> <input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12"
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[8]" @change="changeGain(8)"> step="0.1" v-model="$root.cfg.audio.equalizer.gain[8]" @change="changeGain(8)">
<input type="number" class="eq-freq" orient="vertical" min="5680" max="11360" step="512" v-model="$root.cfg.audio.equalizer.frequencies[8]" @change="changeFreq(8)"> <input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12"
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[8]" @change="changeQ(8)"> step="0.1" v-model="$root.cfg.audio.equalizer.gain[8]" @change="changeGain(8)">
<input type="number" class="eq-freq" orient="vertical" min="5680" max="11360" step="512"
v-model="$root.cfg.audio.equalizer.frequencies[8]" @change="changeFreq(8)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1"
v-model="$root.cfg.audio.equalizer.Q[8]" @change="changeQ(8)">
</div> </div>
<div class="input-container"> <div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[9]" @change="changeGain(9)"> <input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12"
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[9]" @change="changeGain(9)"> step="0.1" v-model="$root.cfg.audio.equalizer.gain[9]" @change="changeGain(9)">
<input type="number" class="eq-freq" orient="vertical" min="11360" max="22720" step="1024" v-model="$root.cfg.audio.equalizer.frequencies[9]" @change="changeFreq(9)"> <input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12"
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[9]" @change="changeQ(9)"> step="0.1" v-model="$root.cfg.audio.equalizer.gain[9]" @change="changeGain(9)">
<input type="number" class="eq-freq" orient="vertical" min="11360" max="22720" step="1024"
v-model="$root.cfg.audio.equalizer.frequencies[9]" @change="changeFreq(9)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1"
v-model="$root.cfg.audio.equalizer.Q[9]" @change="changeQ(9)">
</div> </div>
</div> </div>
@ -100,10 +146,13 @@
<div class="modal-lowercontent"> <div class="modal-lowercontent">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<button class="md-btn" style="width:100%" @click="resetGain()">{{$root.getLz('term.reset')}}</button> <button class="md-btn" style="width:100%" @click="resetGain()">{{$root.getLz('term.reset')}}
</button>
</div> </div>
<div class="col"> <div class="col">
<button class="md-btn" style="width:100%" @click="presetOptions($event)">{{$root.getLz('term.menu')}}</button> <button class="md-btn" style="width:100%" @click="presetOptions($event)">
{{$root.getLz('term.menu')}}
</button>
</div> </div>
</div> </div>
</div> </div>
@ -298,14 +347,12 @@
try { try {
for (var i = 0; i < 21; i++) { for (var i = 0; i < 21; i++) {
CiderAudio.audioNodes.vibrantbassNode[i].gain.value = app.cfg.audio.maikiwiAudio.vibrantBass.gain[i] * (app.cfg.audio.equalizer.vibrantBass / 10); CiderAudio.audioNodes.vibrantbassNode[i].gain.value = app.cfg.audio.maikiwiAudio.vibrantBass.gain[i] * (app.cfg.audio.equalizer.vibrantBass / 10);
} CiderAudio.intelliGainComp_n0_0();
} }
catch (e) { CiderAudio.intelliGainComp_n0_0();
} catch (e) {
CiderAudio.hierarchical_loading(); CiderAudio.hierarchical_loading();
} }
} } else {
else {
CiderAudio.hierarchical_loading(); CiderAudio.hierarchical_loading();
} }
}, },
@ -316,7 +363,9 @@
CiderAudio.audioNodes.audioBands[i].gain.value = app.cfg.audio.equalizer.gain[i] * app.cfg.audio.equalizer.mix CiderAudio.audioNodes.audioBands[i].gain.value = app.cfg.audio.equalizer.gain[i] * app.cfg.audio.equalizer.mix
} }
CiderAudio.intelliGainComp_n0_0(); CiderAudio.intelliGainComp_n0_0();
} catch (e) { CiderAudio.hierarchical_loading(); } } catch (e) {
CiderAudio.hierarchical_loading();
}
} }
}, },
changeGain(i) { changeGain(i) {
@ -324,10 +373,10 @@
try { try {
CiderAudio.audioNodes.audioBands[i].gain.value = app.cfg.audio.equalizer.gain[i] * app.cfg.audio.equalizer.mix CiderAudio.audioNodes.audioBands[i].gain.value = app.cfg.audio.equalizer.gain[i] * app.cfg.audio.equalizer.mix
CiderAudio.intelliGainComp_n0_0(); CiderAudio.intelliGainComp_n0_0();
} catch (e) {
CiderAudio.hierarchical_loading();
} }
catch (e) { CiderAudio.hierarchical_loading(); } } else {
}
else {
CiderAudio.hierarchical_loading(); CiderAudio.hierarchical_loading();
} }
}, },
@ -393,8 +442,7 @@
// self.applyPreset(preset) // self.applyPreset(preset)
app.cfg.audio.equalizer.presets.push(preset) app.cfg.audio.equalizer.presets.push(preset)
notyf.success(`${preset.name} has been imported.`) notyf.success(`${preset.name} has been imported.`)
} } else {
else {
notyf.error("Invalid Preset") notyf.error("Invalid Preset")
} }
} }
@ -404,8 +452,9 @@
Object.assign(this.$root.cfg.audio.equalizer, preset) Object.assign(this.$root.cfg.audio.equalizer, preset)
this.changeVibrantBass() this.changeVibrantBass()
for (var i = 0; i < 10; i++) { for (var i = 0; i < 10; i++) {
try { CiderAudio.audioNodes.audioBands[i].gain.value = app.cfg.audio.equalizer.gain[i] * app.cfg.audio.equalizer.mix } try {
catch (e) { CiderAudio.audioNodes.audioBands[i].gain.value = app.cfg.audio.equalizer.gain[i] * app.cfg.audio.equalizer.mix
} catch (e) {
CiderAudio.hierarchical_loading(); CiderAudio.hierarchical_loading();
CiderAudio.audioNodes.audioBands[i].gain.value = app.cfg.audio.equalizer.gain[i] * app.cfg.audio.equalizer.mix CiderAudio.audioNodes.audioBands[i].gain.value = app.cfg.audio.equalizer.gain[i] * app.cfg.audio.equalizer.mix
} }

View file

@ -14,19 +14,26 @@
</div> </div>
<div class="fs-header" v-if="immersiveEnabled"> <div class="fs-header" v-if="immersiveEnabled">
<div class="top-nav-group"> <div class="top-nav-group">
<sidebar-library-item @click.native="tabMode = 'catalog'" :name="$root.getLz('home.title')" svg-icon="./assets/feather/home.svg" svg-icon-name="home" page="home"> <sidebar-library-item @click.native="tabMode = 'catalog'" :name="$root.getLz('home.title')"
svg-icon="./assets/feather/home.svg" svg-icon-name="home" page="home">
</sidebar-library-item> </sidebar-library-item>
<sidebar-library-item @click.native="tabMode = 'catalog'" :name="$root.getLz('term.listenNow')" svg-icon="./assets/feather/play-circle.svg" svg-icon-name="listenNow" <sidebar-library-item @click.native="tabMode = 'catalog'" :name="$root.getLz('term.listenNow')"
svg-icon="./assets/feather/play-circle.svg" svg-icon-name="listenNow"
page="listen_now"></sidebar-library-item> page="listen_now"></sidebar-library-item>
<sidebar-library-item @click.native="tabMode = 'catalog'" :name="$root.getLz('term.browse')" svg-icon="./assets/feather/globe.svg" svg-icon-name="browse" page="browse"> <sidebar-library-item @click.native="tabMode = 'catalog'" :name="$root.getLz('term.browse')"
svg-icon="./assets/feather/globe.svg" svg-icon-name="browse" page="browse">
</sidebar-library-item> </sidebar-library-item>
<sidebar-library-item @click.native="tabMode = 'catalog'" :name="$root.getLz('term.radio')" svg-icon="./assets/feather/radio.svg" svg-icon-name="radio" page="radio"> <sidebar-library-item @click.native="tabMode = 'catalog'" :name="$root.getLz('term.radio')"
svg-icon="./assets/feather/radio.svg" svg-icon-name="radio" page="radio">
</sidebar-library-item> </sidebar-library-item>
<sidebar-library-item @click.native="tabMode = 'catalog'" :name="$root.getLz('term.library')" svg-icon="./assets/feather/radio.svg" svg-icon-name="library" page="library"> <sidebar-library-item @click.native="tabMode = 'catalog'" :name="$root.getLz('term.library')"
svg-icon="./assets/feather/radio.svg" svg-icon-name="library" page="library">
</sidebar-library-item> </sidebar-library-item>
<sidebar-library-item @click.native="tabMode = ''" :name="$root.getLz('term.nowPlaying')" svg-icon="./assets/play.svg" svg-icon-name="nowPlaying" page="nowPlaying"> <sidebar-library-item @click.native="tabMode = ''" :name="$root.getLz('term.nowPlaying')"
svg-icon="./assets/play.svg" svg-icon-name="nowPlaying" page="nowPlaying">
</sidebar-library-item> </sidebar-library-item>
<sidebar-library-item @click.native="tabMode = 'catalog'" name="" svg-icon="./assets/search.svg" svg-icon-name="search" page="search"> <sidebar-library-item @click.native="tabMode = 'catalog'" name="" svg-icon="./assets/search.svg"
svg-icon-name="search" page="search">
</sidebar-library-item> </sidebar-library-item>
</div> </div>
</div> </div>
@ -59,7 +66,8 @@
</div> </div>
<div class="song-artist item-navigate" style="display: inline-block;" <div class="song-artist item-navigate" style="display: inline-block;"
@click="app.getNowPlayingItemDetailed('album') && app.fullscreen(false)"> @click="app.getNowPlayingItemDetailed('album') && app.fullscreen(false)">
{{ (app.mk.nowPlayingItem["attributes"]["albumName"]) ? (app.mk.nowPlayingItem["attributes"]["albumName"]) : "" }} {{ (app.mk.nowPlayingItem["attributes"]["albumName"]) ?
(app.mk.nowPlayingItem["attributes"]["albumName"]) : "" }}
</div> </div>
</div> </div>

View file

@ -14,11 +14,13 @@
<mediaitem-square v-else :item="item" :type="getKind(item)"></mediaitem-square> <mediaitem-square v-else :item="item" :type="getKind(item)"></mediaitem-square>
</template> </template>
</template> </template>
<button v-if="triggerEnabled" style="opacity:0;height: 32px;" v-observe-visibility="{callback: visibilityChanged}">{{this.app.getLz('term.showMore')}} <button v-if="triggerEnabled" style="opacity:0;height: 32px;"
v-observe-visibility="{callback: visibilityChanged}">{{this.app.getLz('term.showMore')}}
</button> </button>
</div> </div>
<transition name="fabfade"> <transition name="fabfade">
<button class="top-fab" v-show="showFab" @click="scrollToTop()" :aria-label="app.getLz('action.scrollToTop')"> <button class="top-fab" v-show="showFab" @click="scrollToTop()"
:aria-label="app.getLz('action.scrollToTop')">
<%- include("../svg/arrow-up.svg") %> <%- include("../svg/arrow-up.svg") %>
</button> </button>
</transition> </transition>

View file

@ -69,8 +69,10 @@
getArtwork() { getArtwork() {
let u = "" let u = ""
try { try {
u = this.item.relationships.catalog.data[0].attributes.artwork.url} u = this.item.relationships.catalog.data[0].attributes.artwork.url
catch (e){}; } catch (e) {
}
;
return u; return u;
}, },
contextMenu(event) { contextMenu(event) {
@ -119,10 +121,13 @@
"action": function() { "action": function() {
if (!self.item.attributes.url && self.item.relationships) { if (!self.item.attributes.url && self.item.relationships) {
if (self.item.relationships.catalog) { if (self.item.relationships.catalog) {
app.mkapi(self.item.attributes.playParams.kind, false, self.item.relationships.catalog.data[0].id).then(u => {self.app.copyToClipboard((u.data.data.length && u.data.data.length > 0)? u.data.data[0].attributes.url : u.data.data.attributes.url)}) app.mkapi(self.item.attributes.playParams.kind, false, self.item.relationships.catalog.data[0].id).then(u => {
self.app.copyToClipboard((u.data.data.length && u.data.data.length > 0) ? u.data.data[0].attributes.url : u.data.data.attributes.url)
})
} }
} else { } else {
self.app.copyToClipboard(self.item.attributes.url)} self.app.copyToClipboard(self.item.attributes.url)
}
} }
}, },
] ]

View file

@ -2,25 +2,39 @@
<div v-observe-visibility="{callback: visibilityChanged}"> <div v-observe-visibility="{callback: visibilityChanged}">
<template v-if="isVisible && recom.attributes.display.kind != 'MusicSuperHeroShelf'"> <template v-if="isVisible && recom.attributes.display.kind != 'MusicSuperHeroShelf'">
<div class="row"> <div class="row">
<div class="col" v-if="recom?.relationships['primary-content']?.data?.length > 0" style="display: flex; margin-block:1rem;"> <div class="col" v-if="recom?.relationships['primary-content']?.data?.length > 0"
<div @click="navigateContent(recom?.relationships['primary-content']?.data[0] ?? recom?.attributes?.title?.contentIds[0] ?? '')" class="listennow-chip" style="height: 40px;width: 40px;align-self: center;margin-right: 10px;" :class="{ 'circle': recom?.relationships['primary-content']?.data[0]?.type == 'artists' }"> style="display: flex; margin-block:1rem;">
<mediaitem-artwork v-if="recom?.relationships['primary-content']?.data[0]?.attributes?.artwork != null" :url="recom?.relationships['primary-content']?.data[0]?.attributes?.artwork?.url" :size="100"></mediaitem-artwork> <div @click="navigateContent(recom?.relationships['primary-content']?.data[0] ?? recom?.attributes?.title?.contentIds[0] ?? '')"
class="listennow-chip" style="height: 40px;width: 40px;align-self: center;margin-right: 10px;"
:class="{ 'circle': recom?.relationships['primary-content']?.data[0]?.type == 'artists' }">
<mediaitem-artwork
v-if="recom?.relationships['primary-content']?.data[0]?.attributes?.artwork != null"
:url="recom?.relationships['primary-content']?.data[0]?.attributes?.artwork?.url"
:size="100"></mediaitem-artwork>
</div> </div>
<div @click="navigateContent(recom?.relationships['primary-content']?.data[0] ?? recom?.attributes?.title?.contentIds[0] ?? '')" style="width: fit-content;" :class="{'item-navigate' : (recom?.attributes?.title?.contentIds?.length ?? 0) > 0 | recom?.relationships['primary-content']?.data?.length > 0}"> <div @click="navigateContent(recom?.relationships['primary-content']?.data[0] ?? recom?.attributes?.title?.contentIds[0] ?? '')"
style="width: fit-content;"
:class="{'item-navigate' : (recom?.attributes?.title?.contentIds?.length ?? 0) > 0 | recom?.relationships['primary-content']?.data?.length > 0}">
<span style="opacity: 0.5; font-weight: bold;"> {{ recom.attributes.titleWithoutName.stringForDisplay }} </span> <span style="opacity: 0.5; font-weight: bold;"> {{ recom.attributes.titleWithoutName.stringForDisplay }} </span>
<h3 style="margin-block: 0"> {{ recom?.relationships['primary-content']?.data[0].attributes?.name ?? recom.attributes.title.stringForDisplay.replace(recom.attributes.titleWithoutName.stringForDisplay, '') }}</h3> <h3 style="margin-block: 0"> {{
recom?.relationships['primary-content']?.data[0].attributes?.name ??
recom.attributes.title.stringForDisplay.replace(recom.attributes.titleWithoutName.stringForDisplay,
'') }}</h3>
</div> </div>
</div> </div>
<div class="col" v-else style="display: flex; margin-block:1rem;"> <div class="col" v-else style="display: flex; margin-block:1rem;">
<h3 @click="navigateContent(recom?.relationships['primary-content']?.data[0] ?? recom?.attributes?.title?.contentIds[0] ?? '')" style="width: fit-content; margin-block:0;" :class="{'item-navigate' : (recom?.attributes?.title?.contentIds?.length ?? 0) > 0 | recom?.relationships['primary-content']?.data?.length > 0}"> <h3 @click="navigateContent(recom?.relationships['primary-content']?.data[0] ?? recom?.attributes?.title?.contentIds[0] ?? '')"
style="width: fit-content; margin-block:0;"
:class="{'item-navigate' : (recom?.attributes?.title?.contentIds?.length ?? 0) > 0 | recom?.relationships['primary-content']?.data?.length > 0}">
{{ recom.attributes.title ? recom.attributes.title.stringForDisplay : " "}}</h3> {{ recom.attributes.title ? recom.attributes.title.stringForDisplay : " "}}</h3>
</div> </div>
<div class="col-auto cider-flex-center" v-if="recom.relationships.contents.data.length >= 10"> <div class="col-auto cider-flex-center" v-if="recom.relationships.contents.data.length >= 10">
<button class="cd-btn-seeall" @click="showCollection(recom)">{{app.getLz('term.seeAll')}}</button> <button class="cd-btn-seeall" @click="showCollection(recom)">{{app.getLz('term.seeAll')}}</button>
</div> </div>
</div> </div>
<template v-if="recom.attributes.display.kind == 'MusicCoverShelf' || recom.attributes.display.kind == 'MusicCircleCoverShelf'"> <template
v-if="recom.attributes.display.kind == 'MusicCoverShelf' || recom.attributes.display.kind == 'MusicCircleCoverShelf'">
<mediaitem-scroller-horizontal-large <mediaitem-scroller-horizontal-large
:items="recom.relationships.contents.data.limit(10)"></mediaitem-scroller-horizontal-large> :items="recom.relationships.contents.data.limit(10)"></mediaitem-scroller-horizontal-large>
</template> </template>

View file

@ -3,7 +3,8 @@
<vue-horizontal> <vue-horizontal>
<div v-for="items in itemPages"> <div v-for="items in itemPages">
<mediaitem-list-item <mediaitem-list-item
v-for="(song, index) in items" :show-library-status="showLibraryStatus" :parent="'listitem-hr' + simplifiedParent" v-for="(song, index) in items" :show-library-status="showLibraryStatus"
:parent="'listitem-hr' + simplifiedParent"
:index="song.index" :index="song.index"
:item="song"></mediaitem-list-item> :item="song"></mediaitem-list-item>
</div> </div>
@ -39,10 +40,12 @@
// split items into pages // split items into pages
this.itemPages = app.arrayToChunk(this.items, 4); this.itemPages = app.arrayToChunk(this.items, 4);
try { try {
this.simplifiedParent = JSON.stringify(this.items.map ( function(x){return x.attributes.playParams})); this.simplifiedParent = JSON.stringify(this.items.map(function(x) {
return x.attributes.playParams
}));
console.debug("simplifiedParent: " + this.simplifiedParent); console.debug("simplifiedParent: " + this.simplifiedParent);
} catch (e) {
} }
catch (e){}
}, },
watch: { watch: {
@ -54,10 +57,12 @@
// split items into pages // split items into pages
this.itemPages = app.arrayToChunk(this.items, 4); this.itemPages = app.arrayToChunk(this.items, 4);
try { try {
this.simplifiedParent = JSON.stringify(this.items.map ( function(x){return x.attributes.playParams})); this.simplifiedParent = JSON.stringify(this.items.map(function(x) {
return x.attributes.playParams
}));
console.log("simplifiedParent: " + this.simplifiedParent); console.log("simplifiedParent: " + this.simplifiedParent);
} catch (e) {
} }
catch (e){}
} }
}, },
methods: { methods: {

View file

@ -7,7 +7,8 @@
<template v-if="richlyrics && richlyrics != [] && richlyrics.length > 0"> <template v-if="richlyrics && richlyrics != [] && richlyrics.length > 0">
<div class="richl"> <div class="richl">
<template v-for="verse in getVerseLine(index-1)"> <template v-for="verse in getVerseLine(index-1)">
<span class="verse" :lyricstart="lyric.startTime" :versestart="verse.o" >{{ verse.c }}</span> <span class="verse" :lyricstart="lyric.startTime"
:versestart="verse.o">{{ verse.c }}</span>
</template> </template>
</div> </div>
</template> </template>
@ -35,7 +36,8 @@
</template> </template>
<template v-else> <template v-else>
<div class="no-lyrics"> <div class="no-lyrics">
{{app.getLz('term.noLyrics')}}</div> {{app.getLz('term.noLyrics')}}
</div>
</template> </template>
</div> </div>
</script> </script>
@ -125,7 +127,9 @@
if (app.currentLyricsLine != i) { if (app.currentLyricsLine != i) {
app.currentLyricsLine = i; app.currentLyricsLine = i;
if (((app.lyricon && app.drawer.open) || app.appMode == 'fullscreen') && this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${i}"]`)) { if (((app.lyricon && app.drawer.open) || app.appMode == 'fullscreen') && this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${i}"]`)) {
if (this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${prevLine}"]`)) {this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${prevLine}"]`).classList.remove("active");} if (this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${prevLine}"]`)) {
this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${prevLine}"]`).classList.remove("active");
}
this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${i}"]`).classList.add("active") this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${i}"]`).classList.add("active")
if (this.checkIfScrollIsStatic) { if (this.checkIfScrollIsStatic) {
let lyricElement = this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${i}"]`) let lyricElement = this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${i}"]`)
@ -163,13 +167,20 @@
} }
try { try {
if ((app.drawer.open) || app.appMode == 'fullscreen') { if ((app.drawer.open) || app.appMode == 'fullscreen') {
try{this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${prevLine}"]`).childNodes.classList.remove("verse-active");} catch(e){} try {
this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${prevLine}"]`).childNodes.classList.remove("verse-active");
} catch (e) {
}
for (child of this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${app.currentLyricsLine}"]`).querySelectorAll(".verse")) { for (child of this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${app.currentLyricsLine}"]`).querySelectorAll(".verse")) {
if (this.time + 0.1 >= child.getAttribute("lyricstart") * 1 + child.getAttribute("versestart") * 1) { if (this.time + 0.1 >= child.getAttribute("lyricstart") * 1 + child.getAttribute("versestart") * 1) {
child.classList.add("verse-active"); child.classList.add("verse-active");
} else {child.classList.remove("verse-active");}} } else {
child.classList.remove("verse-active");
}
}
}
} catch (e) {
} }
} catch(e){}
}, },
getActiveVerse(timeStart, timeEnd, verseTime) { getActiveVerse(timeStart, timeEnd, verseTime) {
@ -180,8 +191,7 @@
getVerseLine(index) { getVerseLine(index) {
if (this.richlyrics[index] != null && this.richlyrics[index].l != null) { if (this.richlyrics[index] != null && this.richlyrics[index].l != null) {
return this.richlyrics[index].l return this.richlyrics[index].l
} } else return []
else return []
}, },
qqInstrumental(lyrics) { qqInstrumental(lyrics) {
for (lyric of lyrics) { for (lyric of lyrics) {

View file

@ -1,5 +1,6 @@
<script type="text/x-template" id="mediaitem-artwork"> <script type="text/x-template" id="mediaitem-artwork">
<div class="mediaitem-artwork" :style="awStyle" @contextmenu="contextMenu" :class="[{'rounded': (type == 'artists')}, classes]" :key="url"> <div class="mediaitem-artwork" :style="awStyle" @contextmenu="contextMenu"
:class="[{'rounded': (type == 'artists')}, classes]" :key="url">
<img :src="imgSrc" <img :src="imgSrc"
ref="image" ref="image"
decoding="async" decoding="async"

View file

@ -17,26 +17,37 @@
tabindex="0" tabindex="0"
:class="[{'mediaitem-selected': app.select_hasMediaItem(guid)}, addClasses]"> :class="[{'mediaitem-selected': app.select_hasMediaItem(guid)}, addClasses]">
<div v-show="isVisible" class="listitem-content"> <div v-show="isVisible" class="listitem-content">
<div class="popular" v-if="!showInLibrary && item?.meta?.popularity != null && item?.meta?.popularity > 0.7"></div> <div class="popular"
v-if="!showInLibrary && item?.meta?.popularity != null && item?.meta?.popularity > 0.7"></div>
<div class="isLibrary" v-if="showLibraryStatus == true"> <div class="isLibrary" v-if="showLibraryStatus == true">
<div v-if="showInLibrary" :style="{display: (showInLibrary ? 'block' : 'none'), 'margin-left':'11px'}"> <div v-if="showInLibrary" :style="{display: (showInLibrary ? 'block' : 'none'), 'margin-left':'11px'}">
<button @click="addToLibrary()" v-if="!addedToLibrary && (showIndex == false ||(showIndex == true && showIndexPlaylist != false))" :aria-label="$root.getLz('action.addToLibrary')"> <button @click="addToLibrary()"
<div class="svg-icon addIcon" :style="{'--color': 'var(--keyColor)', '--url': 'url(./assets/feather/plus.svg)'}"></div> v-if="!addedToLibrary && (showIndex == false ||(showIndex == true && showIndexPlaylist != false))"
:aria-label="$root.getLz('action.addToLibrary')">
<div class="svg-icon addIcon"
:style="{'--color': 'var(--keyColor)', '--url': 'url(./assets/feather/plus.svg)'}"></div>
</button> </button>
<button v-else-if='!(showArtwork == true && (showIndex == false ||(showIndex == true && showIndexPlaylist != false)))' @click="playTrack()" :aria-label="$root.getLz('term.play')"> <button v-else-if='!(showArtwork == true && (showIndex == false ||(showIndex == true && showIndexPlaylist != false)))'
<div class="svg-icon playIcon" :style="{'--color': 'var(--keyColor)', '--url': 'url(./assets/feather/play.svg)'}"></div> @click="playTrack()" :aria-label="$root.getLz('term.play')">
<div class="svg-icon playIcon"
:style="{'--color': 'var(--keyColor)', '--url': 'url(./assets/feather/play.svg)'}"></div>
</button> </button>
</div> </div>
<div v-if="!(app.mk.isPlaying && (((app.mk.nowPlayingItem._songId ?? (app.mk.nowPlayingItem.songId ?? app.mk.nowPlayingItem.id )) == itemId) || (app.mk.nowPlayingItem.id == item.id ))) && showIndex" :style="{display: ((showIndex && !showInLibrary) ? 'block' : 'none'), 'margin-left':'11px'}"> <div v-if="!(app.mk.isPlaying && (((app.mk.nowPlayingItem._songId ?? (app.mk.nowPlayingItem.songId ?? app.mk.nowPlayingItem.id )) == itemId) || (app.mk.nowPlayingItem.id == item.id ))) && showIndex"
:style="{display: ((showIndex && !showInLibrary) ? 'block' : 'none'), 'margin-left':'11px'}">
<div> <div>
<div>{{ (item.attributes && !showIndexPlaylist) ? (item.attributes.trackNumber ?? '') : ((index * 1 + 1 ) ?? '')}}</div> <div>{{ (item.attributes && !showIndexPlaylist) ? (item.attributes.trackNumber ?? '') : ((index
* 1 + 1 ) ?? '')}}
</div> </div>
</div> </div>
<div v-if="app.mk.isPlaying && (((app.mk.nowPlayingItem._songId ?? (app.mk.nowPlayingItem.songId ?? app.mk.nowPlayingItem.id )) == itemId) || (app.mk.nowPlayingItem.id == item.id))" :style="{display: (showInLibrary ? 'none' : 'block')}"> </div>
<div v-if="app.mk.isPlaying && (((app.mk.nowPlayingItem._songId ?? (app.mk.nowPlayingItem.songId ?? app.mk.nowPlayingItem.id )) == itemId) || (app.mk.nowPlayingItem.id == item.id))"
:style="{display: (showInLibrary ? 'none' : 'block')}">
<div class="loadbar-sound"></div> <div class="loadbar-sound"></div>
</div> </div>
</div> </div>
<div class="artwork" v-if="showArtwork == true && (showIndex == false ||(showIndex == true && showIndexPlaylist != false)) "> <div class="artwork"
v-if="showArtwork == true && (showIndex == false ||(showIndex == true && showIndexPlaylist != false)) ">
<mediaitem-artwork <mediaitem-artwork
:url="item.attributes.artwork ? item.attributes.artwork.url : ''" :url="item.attributes.artwork ? item.attributes.artwork.url : ''"
:size="48" :size="48"
@ -51,7 +62,8 @@
<div class="title text-overflow-elipsis" :title="item.attributes.name"> <div class="title text-overflow-elipsis" :title="item.attributes.name">
{{ item.attributes.name }} {{ item.attributes.name }}
</div> </div>
<div class="subtitle text-overflow-elipsis" :title="item.attributes.artistName" style="-webkit-box-orient: horizontal;"> <div class="subtitle text-overflow-elipsis" :title="item.attributes.artistName"
style="-webkit-box-orient: horizontal;">
<template v-if="item.attributes.artistName"> <template v-if="item.attributes.artistName">
<div class="artist item-navigate text-overflow-elipsis" <div class="artist item-navigate text-overflow-elipsis"
@click="app.searchAndNavigate(item,'artist')"> @click="app.searchAndNavigate(item,'artist')">
@ -69,7 +81,8 @@
</div> </div>
<div class="heart-icon"> <div class="heart-icon">
<!-- <div class="heart-unfilled" v-if="isLoved == false" :style="{'--url': 'url(./assets/feather/heart.svg)'}" /> --> <!-- <div class="heart-unfilled" v-if="isLoved == false" :style="{'--url': 'url(./assets/feather/heart.svg)'}" /> -->
<div class="heart-filled" v-if="isLoved == true" :style="{'--url': 'url(./assets/feather/heart-fill.svg)'}" /> <div class="heart-filled" v-if="isLoved == true"
:style="{'--url': 'url(./assets/feather/heart-fill.svg)'}" />
</div> </div>
<div class="explicit-icon" v-if="item.attributes && item.attributes.contentRating == 'explicit'"></div> <div class="explicit-icon" v-if="item.attributes && item.attributes.contentRating == 'explicit'"></div>
<template v-if="showMetaData == true" @dblclick="route()"> <template v-if="showMetaData == true" @dblclick="route()">
@ -146,7 +159,9 @@
if ((this.item?.id ?? '').toString().startsWith('ciderlocal')) { if ((this.item?.id ?? '').toString().startsWith('ciderlocal')) {
return true return true
} }
if (this.addedToLibrary) { return this.addedToLibrary } if (this.addedToLibrary) {
return this.addedToLibrary
}
if (this.item.type.includes("library-playlists") || this.item.type.includes("station")) { if (this.item.type.includes("library-playlists") || this.item.type.includes("station")) {
this.addedToLibrary = true this.addedToLibrary = true
return return
@ -408,7 +423,9 @@
"icon": "./assets/arrow-bend-up.svg", "icon": "./assets/arrow-bend-up.svg",
"action": function() { "action": function() {
let type = self.item.attributes.playParams?.kind ?? self.item.type let type = self.item.attributes.playParams?.kind ?? self.item.type
if (type == "podcast-episodes") { type = "episode" } if (type == "podcast-episodes") {
type = "episode"
}
app.mk.playNext({ [type]: self.item.attributes.playParams?.id ?? self.item.id }) app.mk.playNext({ [type]: self.item.attributes.playParams?.id ?? self.item.id })
app.mk.queue._reindex() app.mk.queue._reindex()
app.selectedMediaItems = [] app.selectedMediaItems = []
@ -419,7 +436,9 @@
"icon": "./assets/arrow-bend-down.svg", "icon": "./assets/arrow-bend-down.svg",
"action": function() { "action": function() {
let type = self.item.attributes.playParams?.kind ?? self.item.type let type = self.item.attributes.playParams?.kind ?? self.item.type
if (type == "podcast-episodes") { type = "episode" } if (type == "podcast-episodes") {
type = "episode"
}
app.mk.playLater({ [type]: self.item.attributes.playParams?.id ?? self.item.id }) app.mk.playLater({ [type]: self.item.attributes.playParams?.id ?? self.item.id })
app.mk.queue._reindex() app.mk.queue._reindex()
app.selectedMediaItems = [] app.selectedMediaItems = []
@ -584,15 +603,15 @@
let parent = this.parent let parent = this.parent
let childIndex = this.index let childIndex = this.index
let kind = (item.attributes.playParams ? (item.attributes.playParams?.kind ?? (item.type ?? '')) : (item.type ?? '')); let kind = (item.attributes.playParams ? (item.attributes.playParams?.kind ?? (item.type ?? '')) : (item.type ?? ''));
let id = (item.attributes.playParams ? (item.attributes.playParams?.id ?? (item.id ?? '')) : (item.id ?? ''));; let id = (item.attributes.playParams ? (item.attributes.playParams?.id ?? (item.id ?? '')) : (item.id ?? ''));
;
let isLibrary = item.attributes.playParams ? (item.attributes.playParams?.isLibrary ?? false) : false; let isLibrary = item.attributes.playParams ? (item.attributes.playParams?.isLibrary ?? false) : false;
let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind; let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
console.log(item, parent, childIndex, kind, id, isLibrary, kind == "playlists", id.startsWith("p.") || id.startsWith("pl.u")) console.log(item, parent, childIndex, kind, id, isLibrary, kind == "playlists", id.startsWith("p.") || id.startsWith("pl.u"))
app.mk.stop().then(() => { app.mk.stop().then(() => {
if (parent != null && childIndex != null) { if (parent != null && childIndex != null) {
app.queueParentandplayChild(parent, childIndex, item); app.queueParentandplayChild(parent, childIndex, item);
} } else if (kind.includes("playlist") && (id.startsWith("p.") || id.startsWith("pl."))) {
else if (kind.includes("playlist") && (id.startsWith("p.") || id.startsWith("pl."))) {
function shuffleArray(array) { function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) { for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1)); var j = Math.floor(Math.random() * (i + 1));
@ -601,14 +620,22 @@
array[j] = temp; array[j] = temp;
} }
} }
app.mk.setQueue({ [truekind]: [item.attributes.playParams?.id ?? item.id], parameters: { l: this.app.mklang } }).then(function () {
app.mk.setQueue({
[truekind]: [item.attributes.playParams?.id ?? item.id],
parameters: { l: this.app.mklang }
}).then(function() {
app.mk.play().then(function() { app.mk.play().then(function() {
var playlistId = id var playlistId = id
function getPlaylist(id, isLibrary) { function getPlaylist(id, isLibrary) {
if (isLibrary) { if (isLibrary) {
return this.app.mk.api.v3.music(`/v1/me/library/playlists/${id}`) return this.app.mk.api.v3.music(`/v1/me/library/playlists/${id}`)
} else { return this.app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/playlists/${id}`) } } else {
return this.app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/playlists/${id}`)
} }
}
try { try {
getPlaylist(id, isLibrary).then(res => { getPlaylist(id, isLibrary).then(res => {
//let query = res.relationships.tracks.data.map(item => new MusicKit.MediaItem(item)); //let query = res.relationships.tracks.data.map(item => new MusicKit.MediaItem(item));
@ -628,7 +655,10 @@
// } // }
console.log('nextres', res) console.log('nextres', res)
let query = res.data.map(item => new MusicKit.MediaItem(item)) let query = res.data.map(item => new MusicKit.MediaItem(item))
if (app.mk.shuffleMode == 1) { shuffleArray(query); console.log('shf') } if (app.mk.shuffleMode == 1) {
shuffleArray(query);
console.log('shf')
}
app.mk.queue.append(query) app.mk.queue.append(query)
if (res.next) { if (res.next) {
@ -637,16 +667,15 @@
}) })
} }
}) })
} catch (e) { } } catch (e) {
})
})
} }
else {
})
})
} else {
app.playMediaItemById(item.attributes.playParams?.id ?? item.id, item.attributes.playParams?.kind ?? item.type, item.attributes.playParams?.isLibrary ?? false, item.attributes.url) app.playMediaItemById(item.attributes.playParams?.id ?? item.id, item.attributes.playParams?.kind ?? item.type, item.attributes.playParams?.isLibrary ?? false, item.attributes.url)
} }
}) })

View file

@ -27,7 +27,9 @@
<div class="title text-overflow-elipsis" @click='app.routeView(item)'> <div class="title text-overflow-elipsis" @click='app.routeView(item)'>
{{ item.attributes.name ?? '' }} {{ item.attributes.name ?? '' }}
</div> </div>
<div class="subtitle text-overflow-elipsis item-navigate" v-if="item.attributes.artistName" :style = "{'z-index': ((item.attributes.editorialNotes == null) && item.attributes.artistName) ? '4' : ''}" @click="if (item.attributes.artistName)app.searchAndNavigate(item,'artist')"> <div class="subtitle text-overflow-elipsis item-navigate" v-if="item.attributes.artistName"
:style="{'z-index': ((item.attributes.editorialNotes == null) && item.attributes.artistName) ? '4' : ''}"
@click="if (item.attributes.artistName)app.searchAndNavigate(item,'artist')">
{{ item.attributes.artistName ?? '' }} {{ item.attributes.artistName ?? '' }}
</div> </div>

View file

@ -45,7 +45,8 @@
<div class="title" <div class="title"
:title="item.attributes?.name ?? (item.relationships?.contents?.data[0]?.attributes?.name ?? (item.attributes?.editorialNotes?.name ?? ''))" :title="item.attributes?.name ?? (item.relationships?.contents?.data[0]?.attributes?.name ?? (item.attributes?.editorialNotes?.name ?? ''))"
v-if="item.attributes.artistNames == null || kind != 'card'" @click='app.routeView(item)'> v-if="item.attributes.artistNames == null || kind != 'card'" @click='app.routeView(item)'>
<div class="item-navigate text-overflow-elipsis">{{ item.attributes?.name.replace(/&nbsp;/g, ' ').replace(/Apple Music |^Apple |/g, '') ?? <div class="item-navigate text-overflow-elipsis">{{ item.attributes?.name.replace(/&nbsp;/g, '
').replace(/Apple Music |^Apple |/g, '') ??
(item.relationships?.contents?.data[0]?.attributes?.name ?? (item.relationships?.contents?.data[0]?.attributes?.name ??
(item.attributes?.editorialNotes?.name ?? '')) }} (item.attributes?.editorialNotes?.name ?? '')) }}
</div> </div>

View file

@ -2,18 +2,23 @@
<div class="mini-view" tabindex="0"> <div class="mini-view" tabindex="0">
<div class="background"> <div class="background">
</div> </div>
<div class="player-pin" title="Pin to Top" v-if="app.cfg.visual.miniplayer_top_toggle === false" @click="app.pinMiniPlayer()"> <div class="player-pin" title="Pin to Top" v-if="app.cfg.visual.miniplayer_top_toggle === false"
@click="app.pinMiniPlayer()">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" fill="none" class="feather feather-pin"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" fill="none" class="feather feather-pin">
<path d="M7.05664 16.3613C7.05664 17.1523 7.59277 17.6797 8.42773 17.6797H13.1299V21.8369C13.1299 23.0762 13.7539 24.3242 14 24.3242C14.2373 24.3242 14.8613 23.0762 14.8613 21.8369V17.6797H19.5635C20.3984 17.6797 20.9346 17.1523 20.9346 16.3613C20.9346 14.4717 19.4316 12.5293 16.9531 11.6152L16.6631 7.52832C17.9727 6.78125 19.0098 5.96387 19.4668 5.38379C19.7041 5.06738 19.8271 4.75098 19.8271 4.46973C19.8271 3.88965 19.3789 3.45898 18.7197 3.45898H9.27148C8.6123 3.45898 8.16406 3.88965 8.16406 4.46973C8.16406 4.75098 8.28711 5.06738 8.52441 5.38379C8.98145 5.96387 10.0186 6.78125 11.3281 7.52832L11.0469 11.6152C8.55957 12.5293 7.05664 14.4717 7.05664 16.3613Z" fill="#ff2654"/> <path d="M7.05664 16.3613C7.05664 17.1523 7.59277 17.6797 8.42773 17.6797H13.1299V21.8369C13.1299 23.0762 13.7539 24.3242 14 24.3242C14.2373 24.3242 14.8613 23.0762 14.8613 21.8369V17.6797H19.5635C20.3984 17.6797 20.9346 17.1523 20.9346 16.3613C20.9346 14.4717 19.4316 12.5293 16.9531 11.6152L16.6631 7.52832C17.9727 6.78125 19.0098 5.96387 19.4668 5.38379C19.7041 5.06738 19.8271 4.75098 19.8271 4.46973C19.8271 3.88965 19.3789 3.45898 18.7197 3.45898H9.27148C8.6123 3.45898 8.16406 3.88965 8.16406 4.46973C8.16406 4.75098 8.28711 5.06738 8.52441 5.38379C8.98145 5.96387 10.0186 6.78125 11.3281 7.52832L11.0469 11.6152C8.55957 12.5293 7.05664 14.4717 7.05664 16.3613Z"
fill="#ff2654" />
</svg> </svg>
</div> </div>
<div class="player-pin" title="Unpin to Top" v-if="app.cfg.visual.miniplayer_top_toggle === true" @click="app.pinMiniPlayer(false)"> <div class="player-pin" title="Unpin to Top" v-if="app.cfg.visual.miniplayer_top_toggle === true"
@click="app.pinMiniPlayer(false)">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" fill="none" class="feather feather-pin-slashed"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" fill="none" class="feather feather-pin-slashed">
<path d="M9.271 3.459c-.659 0-1.107.43-1.107 1.01 0 .282.114.59.352.897.448.59 1.494 1.415 2.777 2.162l-.07 1.02 8.99 8.991c.458-.202.722-.615.722-1.178 0-1.89-1.503-3.832-3.947-4.746l-.29-4.087c1.275-.747 2.312-1.555 2.76-2.144.246-.308.37-.633.37-.914 0-.58-.45-1.011-1.108-1.011H9.27ZM5.15 6.061l16.076 16.057c.272.281.73.273.993 0a.703.703 0 0 0 0-.984L6.15 5.076a.716.716 0 0 0-1.002 0 .711.711 0 0 0 0 .985Zm1.908 10.3c0 .791.536 1.319 1.37 1.319h4.703v4.157c0 1.24.624 2.487.861 2.487.246 0 .87-1.248.87-2.487V17.81h.413l-5.537-5.545c-1.678 1.002-2.68 2.557-2.68 4.095Z" fill="#ff2654"/> <path d="M9.271 3.459c-.659 0-1.107.43-1.107 1.01 0 .282.114.59.352.897.448.59 1.494 1.415 2.777 2.162l-.07 1.02 8.99 8.991c.458-.202.722-.615.722-1.178 0-1.89-1.503-3.832-3.947-4.746l-.29-4.087c1.275-.747 2.312-1.555 2.76-2.144.246-.308.37-.633.37-.914 0-.58-.45-1.011-1.108-1.011H9.27ZM5.15 6.061l16.076 16.057c.272.281.73.273.993 0a.703.703 0 0 0 0-.984L6.15 5.076a.716.716 0 0 0-1.002 0 .711.711 0 0 0 0 .985Zm1.908 10.3c0 .791.536 1.319 1.37 1.319h4.703v4.157c0 1.24.624 2.487.861 2.487.246 0 .87-1.248.87-2.487V17.81h.413l-5.537-5.545c-1.678 1.002-2.68 2.557-2.68 4.095Z"
fill="#ff2654" />
</svg> </svg>
</div> </div>
<div class="player-exit" title="Close" @click="app.miniPlayer(false)"> <div class="player-exit" title="Close" @click="app.miniPlayer(false)">
<svg fill="#323232e3" width="21" height="21" viewBox="0 0 21 21" aria-role="presentation" focusable="false" xmlns="http://www.w3.org/2000/svg"> <svg fill="#323232e3" width="21" height="21" viewBox="0 0 21 21" aria-role="presentation" focusable="false"
xmlns="http://www.w3.org/2000/svg">
<defs> <defs>
<radialGradient gradientUnits="userSpaceOnUse" cx="10.5" cy="10.5" r="10.5" id="gradient-0"> <radialGradient gradientUnits="userSpaceOnUse" cx="10.5" cy="10.5" r="10.5" id="gradient-0">
<stop offset="0" style="stop-color: rgba(168, 163, 163, 1)" /> <stop offset="0" style="stop-color: rgba(168, 163, 163, 1)" />
@ -21,7 +26,8 @@
</radialGradient> </radialGradient>
</defs> </defs>
<path d="M10.5 21C4.724 21 0 16.275 0 10.5S4.724 0 10.5 0 21 4.725 21 10.5 16.276 21 10.5 21zm-3.543-5.967a.96.96 0 00.693-.295l2.837-2.842 2.85 2.842c.167.167.41.295.693.295.552 0 1.001-.461 1.001-1.012 0-.281-.115-.512-.295-.704L11.899 10.5l2.85-2.855a.875.875 0 00.295-.68c0-.55-.45-.998-1.001-.998a.871.871 0 00-.668.295l-2.888 2.855-2.862-2.843a.891.891 0 00-.668-.281.99.99 0 00-1.001.986c0 .269.116.512.295.678L9.088 10.5l-2.837 2.843a.926.926 0 00-.295.678c0 .551.45 1.012 1.001 1.012z" <path d="M10.5 21C4.724 21 0 16.275 0 10.5S4.724 0 10.5 0 21 4.725 21 10.5 16.276 21 10.5 21zm-3.543-5.967a.96.96 0 00.693-.295l2.837-2.842 2.85 2.842c.167.167.41.295.693.295.552 0 1.001-.461 1.001-1.012 0-.281-.115-.512-.295-.704L11.899 10.5l2.85-2.855a.875.875 0 00.295-.68c0-.55-.45-.998-1.001-.998a.871.871 0 00-.668.295l-2.888 2.855-2.862-2.843a.891.891 0 00-.668-.281.99.99 0 00-1.001.986c0 .269.116.512.295.678L9.088 10.5l-2.837 2.843a.926.926 0 00-.295.678c0 .551.45 1.012 1.001 1.012z"
fill-rule="nonzero" style="stroke-miterlimit: 11; vector-effect: non-scaling-stroke; stroke-width: 31px; fill: url(#gradient-0);"/> fill-rule="nonzero"
style="stroke-miterlimit: 11; vector-effect: non-scaling-stroke; stroke-width: 31px; fill: url(#gradient-0);" />
</svg> </svg>
</div> </div>
<div class="col artwork-col"> <div class="col artwork-col">
@ -53,7 +59,8 @@
</div> </div>
<div class="song-progress"> <div class="song-progress">
<div class="song-duration" style="justify-content: space-between; height: 1px; margin-bottom: 1px;" <div class="song-duration"
style="justify-content: space-between; height: 1px; margin-bottom: 1px;"
:style="[app.chrome.progresshover ? {'display': 'flex'} : {'display' : 'none'} ]"> :style="[app.chrome.progresshover ? {'display': 'flex'} : {'display' : 'none'} ]">
<p style="width: auto">{{ app.convertTime(app.getSongProgress()) }}</p> <p style="width: auto">{{ app.convertTime(app.getSongProgress()) }}</p>
<p style="width: auto">{{ app.convertTime(app.mk.currentPlaybackDuration) }}</p> <p style="width: auto">{{ app.convertTime(app.mk.currentPlaybackDuration) }}</p>
@ -67,43 +74,57 @@
</div> </div>
<div class="control-buttons"> <div class="control-buttons">
<div class="app-chrome-item display--large"> <div class="app-chrome-item display--large">
<button class="playback-button--small shuffle" v-if="$root.mk.shuffleMode == 0" :class="$root.isDisabled() && 'disabled'" <button class="playback-button--small shuffle" v-if="$root.mk.shuffleMode == 0"
@click="$root.mk.shuffleMode = 1" :title="$root.getLz('term.enableShuffle')" v-b-tooltip.hover></button> :class="$root.isDisabled() && 'disabled'"
<button class="playback-button--small shuffle active" v-else :class="$root.isDisabled() && 'disabled'" @click="$root.mk.shuffleMode = 1" :title="$root.getLz('term.enableShuffle')"
@click="$root.mk.shuffleMode = 0" :title="$root.getLz('term.disableShuffle')" v-b-tooltip.hover></button> v-b-tooltip.hover></button>
<button class="playback-button--small shuffle active" v-else
:class="$root.isDisabled() && 'disabled'"
@click="$root.mk.shuffleMode = 0" :title="$root.getLz('term.disableShuffle')"
v-b-tooltip.hover></button>
</div> </div>
<div class="app-chrome-item display--large"> <div class="app-chrome-item display--large">
<button class="playback-button previous" @click="$root.prevButton()" :class="$root.isPrevDisabled() && 'disabled'" <button class="playback-button previous" @click="$root.prevButton()"
:class="$root.isPrevDisabled() && 'disabled'"
:title="$root.getLz('term.previous')" v-b-tooltip.hover></button> :title="$root.getLz('term.previous')" v-b-tooltip.hover></button>
</div> </div>
<div class="app-chrome-item display--large"> <div class="app-chrome-item display--large">
<button class="playback-button stop" @click="$root.mk.stop()" <button class="playback-button stop" @click="$root.mk.stop()"
v-if="$root.mk.isPlaying && $root.mk.nowPlayingItem.attributes.playParams.kind == 'radioStation'" v-if="$root.mk.isPlaying && $root.mk.nowPlayingItem.attributes.playParams.kind == 'radioStation'"
:title="$root.getLz('term.stop')" v-b-tooltip.hover></button> :title="$root.getLz('term.stop')" v-b-tooltip.hover></button>
<button class="playback-button pause" @click="$root.mk.pause()" v-else-if="$root.mk.isPlaying" <button class="playback-button pause" @click="$root.mk.pause()"
v-else-if="$root.mk.isPlaying"
:title="$root.getLz('term.pause')" v-b-tooltip.hover></button> :title="$root.getLz('term.pause')" v-b-tooltip.hover></button>
<button class="playback-button play" @click="$root.mk.play()" v-else :title="$root.getLz('term.play')" <button class="playback-button play" @click="$root.mk.play()" v-else
:title="$root.getLz('term.play')"
v-b-tooltip.hover></button> v-b-tooltip.hover></button>
</div> </div>
<div class="app-chrome-item display--large"> <div class="app-chrome-item display--large">
<button class="playback-button next" @click="$root.skipToNextItem()" :class="$root.isNextDisabled() && 'disabled'" <button class="playback-button next" @click="$root.skipToNextItem()"
:class="$root.isNextDisabled() && 'disabled'"
:title="$root.getLz('term.next')" v-b-tooltip.hover></button> :title="$root.getLz('term.next')" v-b-tooltip.hover></button>
</div> </div>
<div class="app-chrome-item display--large"> <div class="app-chrome-item display--large">
<button class="playback-button--small repeat" v-if="$root.mk.repeatMode == 0" :class="$root.isDisabled() && 'disabled'" <button class="playback-button--small repeat" v-if="$root.mk.repeatMode == 0"
@click="$root.mk.repeatMode = 1" :title="$root.getLz('term.enableRepeatOne')" v-b-tooltip.hover></button> :class="$root.isDisabled() && 'disabled'"
@click="$root.mk.repeatMode = 1" :title="$root.getLz('term.enableRepeatOne')"
v-b-tooltip.hover></button>
<button class="playback-button--small repeat repeatOne" @click="mk.repeatMode = 2" <button class="playback-button--small repeat repeatOne" @click="mk.repeatMode = 2"
:class="$root.isDisabled() && 'disabled'" v-else-if="$root.mk.repeatMode == 1" :class="$root.isDisabled() && 'disabled'" v-else-if="$root.mk.repeatMode == 1"
:title="$root.getLz('term.disableRepeatOne')" v-b-tooltip.hover></button> :title="$root.getLz('term.disableRepeatOne')" v-b-tooltip.hover></button>
<button class="playback-button--small repeat active" @click="$root.mk.repeatMode = 0" <button class="playback-button--small repeat active" @click="$root.mk.repeatMode = 0"
:class="$root.isDisabled() && 'disabled'" v-else-if="$root.mk.repeatMode == 2" :title="$root.getLz('term.disableRepeat')" :class="$root.isDisabled() && 'disabled'" v-else-if="$root.mk.repeatMode == 2"
:title="$root.getLz('term.disableRepeat')"
v-b-tooltip.hover></button> v-b-tooltip.hover></button>
</div> </div>
</div> </div>
<div class="app-chrome-item volume display--large"> <div class="app-chrome-item volume display--large">
<div class="input-container"> <div class="input-container">
<button class="volume-button--small volume" @click="app.muteButtonPressed()" :class="{'active': app.cfg.audio.volume == 0}"></button> <button class="volume-button--small volume" @click="app.muteButtonPressed()"
<input type="range" class="slider" @wheel="app.volumeWheel" :step="app.cfg.audio.volumeStep" min="0" :max="app.cfg.audio.maxVolume" v-model="app.mk.volume" :class="{'active': app.cfg.audio.volume == 0}"></button>
<input type="range" class="slider" @wheel="app.volumeWheel"
:step="app.cfg.audio.volumeStep" min="0" :max="app.cfg.audio.maxVolume"
v-model="app.mk.volume"
v-if="typeof app.mk.volume != 'undefined'" @change="app.checkMuteChange()"> v-if="typeof app.mk.volume != 'undefined'" @change="app.checkMuteChange()">
</div> </div>
</div> </div>
@ -136,7 +157,6 @@
</div> </div>
</script> </script>
<script> <script>

View file

@ -18,7 +18,8 @@
:class="`md-btn page-btn${ isCurrentPage(page) ? ' md-btn-primary': ''}`" :class="`md-btn page-btn${ isCurrentPage(page) ? ' md-btn-primary': ''}`"
@click="goToPage(page)" @click="goToPage(page)"
v-for="page in pagesToShow" v-for="page in pagesToShow"
>{{ page }}</button> >{{ page }}
</button>
<button <button
class="md-btn page-btn next" class="md-btn page-btn next"
:disabled="effectivePage === numPages" :disabled="effectivePage === numPages"

Some files were not shown because too many files have changed in this diff Show more