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
printWidth: 240

View file

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

View file

@ -2,10 +2,10 @@ class sendSongToTitlebar {
/**
* Base Plugin Details (Eventually implemented into a GUI in settings)
*/
public name: string = 'sendSongToTitlebar';
public description: string = 'Sets the app\'s titlebar to the Song title';
public version: string = '0.0.1';
public author: string = 'Cider Collective (credit to 8times9 via #147)';
public name: string = "sendSongToTitlebar";
public description: string = "Sets the app's titlebar to the Song title";
public version: string = "0.0.1";
public author: string = "Cider Collective (credit to 8times9 via #147)";
/**
* Runs on plugin load (Currently run on application start)
*/
@ -27,7 +27,7 @@ class sendSongToTitlebar {
* @param attributes Music Attributes (attributes.status = current state)
*/
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

View file

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

View file

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

View file

@ -1,21 +1,27 @@
exports.default = function (context) {
const { execSync } = require('child_process')
const fs = require('fs')
const { execSync } = require("child_process");
const fs = require("fs");
if (process.platform !== 'darwin')
return
if (process.platform !== "darwin") return;
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')
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')
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");
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");
// console.log('Castlabs-evs update start')
// execSync('python3 -m pip install --upgrade castlabs-evs')
// console.log('Castlabs-evs update complete')
// 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'))
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'})
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")
)
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')
// 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'})
// console.log('VMP signing complete')
}
};

View file

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

View file

@ -24,16 +24,20 @@ class MacPackager extends platformPackager_1.PlatformPackager {
this.codeSigningInfo = new lazy_val_1.Lazy(() => {
const cscLink = this.getCscLink();
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,
cscLink,
cscKeyPassword: this.getCscPassword(),
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),
currentDir: this.projectDir,
}).then(result => {
})
.then((result) => {
const keychainFile = result.keychainFile;
if (keychainFile != null) {
this.info.disposeOnBuildFinish(() => macCodeSign_1.removeKeychain(keychainFile));
@ -61,18 +65,18 @@ class MacPackager extends platformPackager_1.PlatformPackager {
case "dmg": {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { DmgTarget } = require("dmg-builder");
mapper(name, outDir => new DmgTarget(this, outDir));
mapper(name, (outDir) => new DmgTarget(this, outDir));
break;
}
case "zip":
// 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;
case "pkg":
mapper(name, outDir => new pkg_1.PkgTarget(this, outDir));
mapper(name, (outDir) => new pkg_1.PkgTarget(this, outDir));
break;
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;
}
}
@ -90,12 +94,15 @@ class MacPackager extends platformPackager_1.PlatformPackager {
const arm64AppOutPath = appOutDir + "--" + builder_util_1.Arch[arm64Arch];
await super.doPack(outDir, arm64AppOutPath, platformName, arm64Arch, platformSpecificBuildOptions, targets, false, true);
const framework = this.info.framework;
builder_util_1.log.info({
builder_util_1.log.info(
{
platform: platformName,
arch: builder_util_1.Arch[arch],
[`${framework.name}`]: framework.version,
appOutDir: builder_util_1.log.filePath(appOutDir),
}, `packaging`);
},
`packaging`
);
const appFile = `${this.appInfo.productFilename}.app`;
const { makeUniversalApp } = require("@electron/universal");
await makeUniversalApp({
@ -113,10 +120,10 @@ class MacPackager extends platformPackager_1.PlatformPackager {
targets,
packager: this,
electronPlatformName: platformName,
}
await this.info.afterPack(packContext)
};
await this.info.afterPack(packContext);
if (framework.afterPack != null) {
await framework.afterPack(packContext)
await framework.afterPack(packContext);
}
await this.doSignAfterPack(outDir, appOutDir, platformName, arch, platformSpecificBuildOptions, targets);
break;
@ -125,13 +132,13 @@ class MacPackager extends platformPackager_1.PlatformPackager {
}
async pack(outDir, arch, targets, taskManager) {
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;
if (!hasMas || targets.length > 1) {
const appPath = prepackaged == null ? path.join(this.computeAppOutDir(outDir, arch), `${this.appInfo.productFilename}.app`) : prepackaged;
nonMasPromise = (prepackaged
? Promise.resolve()
: this.doPack(outDir, path.dirname(appPath), this.platform.nodeName, arch, this.platformSpecificBuildOptions, targets)).then(() => this.packageInDistributableFormat(appPath, arch, targets, taskManager));
nonMasPromise = (prepackaged ? Promise.resolve() : 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) {
const targetName = target.name;
@ -148,8 +155,7 @@ class MacPackager extends platformPackager_1.PlatformPackager {
if (prepackaged == null) {
await this.doPack(outDir, targetOutDir, "mas", arch, masBuildOptions, [target]);
await this.sign(path.join(targetOutDir, `${this.appInfo.productFilename}.app`), targetOutDir, masBuildOptions, arch);
}
else {
} else {
await this.sign(prepackaged, targetOutDir, masBuildOptions, arch);
}
}
@ -203,20 +209,21 @@ class MacPackager extends platformPackager_1.PlatformPackager {
if (filter.length == 0) {
filter = null;
}
}
else if (filter != null) {
} else if (filter != null) {
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;
if (binaries) {
// 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)) {
return destination;
}
return path.resolve(appPath, destination);
}));
})
);
// Insert at front to prioritize signing. We still sort by depth next
binaries = userDefinedBinaries.concat(binaries);
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.includes("/node_modules/puppeteer/.local-chromium") ||
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
https://github.com/electron-userland/electron-builder/issues/2010
https://github.com/electron-userland/electron-builder/issues/5383
@ -250,7 +259,7 @@ class MacPackager extends platformPackager_1.PlatformPackager {
app: appPath,
keychain: keychainFile || undefined,
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),
// 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.
@ -260,12 +269,15 @@ class MacPackager extends platformPackager_1.PlatformPackager {
hardenedRuntime: isMas ? masOptions && masOptions.hardenedRuntime === true : options.hardenedRuntime !== false,
};
await this.adjustSignOptions(signOptions, masOptions);
builder_util_1.log.info({
builder_util_1.log.info(
{
file: builder_util_1.log.filePath(appPath),
identityName: identity.name,
identityHash: identity.hash,
provisioningProfile: signOptions["provisioning-profile"] || "none",
}, "signing");
},
"signing"
);
await this.doSign(signOptions);
// https://github.com/electron-userland/electron-builder/issues/1196#issuecomment-312310209
if (masOptions != null && !isDevelopment) {
@ -290,8 +302,7 @@ class MacPackager extends platformPackager_1.PlatformPackager {
const p = `entitlements.${entitlementsSuffix}.plist`;
if (resourceList.includes(p)) {
entitlements = path.join(this.info.buildResourcesDir, p);
}
else {
} else {
entitlements = pathManager_1.getTemplatePath("entitlements.mac.plist");
}
}
@ -301,8 +312,7 @@ class MacPackager extends platformPackager_1.PlatformPackager {
const p = `entitlements.${entitlementsSuffix}.inherit.plist`;
if (resourceList.includes(p)) {
entitlementsInherit = path.join(this.info.buildResourcesDir, p);
}
else {
} else {
entitlementsInherit = pathManager_1.getTemplatePath("entitlements.mac.plist");
}
}
@ -357,7 +367,7 @@ class MacPackager extends platformPackager_1.PlatformPackager {
appPlist.CFBundleIdentifier = appInfo.macBundleIdentifier;
appPlist.CFBundleShortVersionString = this.platformSpecificBuildOptions.bundleShortVersion || appInfo.version;
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;
if (this.platformSpecificBuildOptions.darkModeSupport) {
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) => {
if (file.endsWith(".app")) {
return this.sign(path.join(outResourcesDir, file), null, null, null);
}
else {
} else {
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) {
const { electronPlatformName, appOutDir } = context;
if (electronPlatformName !== 'darwin') {
if (electronPlatformName !== "darwin") {
return;
}
const appName = context.packager.appInfo.productFilename;
return await notarize({
appBundleId: 'com.ciderapp.cider',
appBundleId: "com.ciderapp.cider",
appPath: `${appOutDir}/${appName}.app`,
appleId: process.env.APPLEID,
appleIdPassword: process.env.APPLEIDPASS,

View file

@ -3,24 +3,24 @@ const CiderKit = {
musickit: {
async mkv3(route, body, options) {
let opts = {
method: 'POST',
cache: 'no-cache',
credentials: 'same-origin',
method: "POST",
cache: "no-cache",
credentials: "same-origin",
headers: {
'Content-Type': 'application/json'
"Content-Type": "application/json",
},
redirect: 'follow',
referrerPolicy: 'no-referrer',
body: {}
}
redirect: "follow",
referrerPolicy: "no-referrer",
body: {},
};
opts.body = JSON.stringify({
route: route,
body: body,
options: options
})
options: options,
});
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.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 * as path from 'path';
import * as log from 'electron-log';
import {utils} from './utils';
import * as path from "path";
import * as log from "electron-log";
import { utils } from "./utils";
/**
* @file Creates App instance
@ -11,14 +11,7 @@ import {utils} from './utils';
/** @namespace */
export class AppEvents {
private protocols: string[] = [
"ame",
"cider",
"itms",
"itmss",
"musics",
"music"
]
private protocols: string[] = ["ame", "cider", "itms", "itmss", "musics", "music"];
private plugin: any = undefined;
private tray: any = undefined;
private i18n: any = undefined;
@ -33,68 +26,68 @@ export class AppEvents {
* @returns {void}
*/
private start(): void {
AppEvents.initLogging()
console.info('[AppEvents] App started');
AppEvents.initLogging();
console.info("[AppEvents] App started");
/**********************************************************************************************************************
* Startup arguments handling
**********************************************************************************************************************/
if (app.commandLine.hasSwitch('version') || app.commandLine.hasSwitch('v')) {
console.log(app.getVersion())
app.exit()
if (app.commandLine.hasSwitch("version") || app.commandLine.hasSwitch("v")) {
console.log(app.getVersion());
app.exit();
}
// Verbose Check
if (app.commandLine.hasSwitch('verbose')) {
if (app.commandLine.hasSwitch("verbose")) {
console.log("[Cider] User has launched the application with --verbose");
}
// Log File Location
if (app.commandLine.hasSwitch('log') || app.commandLine.hasSwitch('l')) {
console.log(path.join(app.getPath('userData'), 'logs'))
app.exit()
if (app.commandLine.hasSwitch("log") || app.commandLine.hasSwitch("l")) {
console.log(path.join(app.getPath("userData"), "logs"));
app.exit();
}
// 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
app.commandLine.appendSwitch('js-flags', '--expose_gc')
app.commandLine.appendSwitch("js-flags", "--expose_gc");
if (process.platform === "win32") {
app.setAppUserModelId(app.getName()) // For notification name
app.setAppUserModelId(app.getName()); // For notification name
}
/***********************************************************************************************************************
* Commandline arguments
**********************************************************************************************************************/
switch (utils.getStoreValue('visual.hw_acceleration') as string) {
switch (utils.getStoreValue("visual.hw_acceleration") as string) {
default:
case "default":
app.commandLine.appendSwitch('enable-accelerated-mjpeg-decode')
app.commandLine.appendSwitch('enable-accelerated-video')
app.commandLine.appendSwitch('disable-gpu-driver-bug-workarounds')
app.commandLine.appendSwitch('ignore-gpu-blacklist')
app.commandLine.appendSwitch('enable-native-gpu-memory-buffers')
app.commandLine.appendSwitch('enable-accelerated-video-decode');
app.commandLine.appendSwitch('enable-gpu-rasterization');
app.commandLine.appendSwitch('enable-native-gpu-memory-buffers');
app.commandLine.appendSwitch('enable-oop-rasterization');
app.commandLine.appendSwitch("enable-accelerated-mjpeg-decode");
app.commandLine.appendSwitch("enable-accelerated-video");
app.commandLine.appendSwitch("disable-gpu-driver-bug-workarounds");
app.commandLine.appendSwitch("ignore-gpu-blacklist");
app.commandLine.appendSwitch("enable-native-gpu-memory-buffers");
app.commandLine.appendSwitch("enable-accelerated-video-decode");
app.commandLine.appendSwitch("enable-gpu-rasterization");
app.commandLine.appendSwitch("enable-native-gpu-memory-buffers");
app.commandLine.appendSwitch("enable-oop-rasterization");
break;
case "webgpu":
console.info("WebGPU is enabled.");
app.commandLine.appendSwitch('enable-unsafe-webgpu')
app.commandLine.appendSwitch("enable-unsafe-webgpu");
break;
case "disabled":
console.info("Hardware acceleration is disabled.");
app.commandLine.appendSwitch('disable-gpu')
app.commandLine.appendSwitch("disable-gpu");
break;
}
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.argv.length >= 2) {
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 {
this.protocols.forEach((protocol: string) => {
app.setAsDefaultProtocolClient(protocol)
})
app.setAsDefaultProtocolClient(protocol);
});
}
}
public quit() {
console.log('[AppEvents] App quit');
console.log("[AppEvents] App quit");
}
public ready(plug: any) {
this.plugin = plug
console.log('[AppEvents] App ready');
this.plugin = plug;
console.log("[AppEvents] App ready");
AppEvents.setLoginSettings()
AppEvents.setLoginSettings();
}
public bwCreated() {
app.on('open-url', (event, url) => {
event.preventDefault()
app.on("open-url", (event, url) => {
event.preventDefault();
if (this.protocols.some((protocol: string) => url.includes(protocol))) {
this.LinkHandler(url)
console.log(url)
this.LinkHandler(url);
console.log(url);
}
})
});
if (process.platform === "darwin") {
app.setUserActivity('8R23J2835D.com.ciderapp.webremote.play', {
title: 'Web Remote',
description: 'Connect to your Web Remote',
}, "https://webremote.cider.sh")
app.setUserActivity(
"8R23J2835D.com.ciderapp.webremote.play",
{
title: "Web Remote",
description: "Connect to your Web Remote",
},
"https://webremote.cider.sh"
);
}
this.InstanceHandler()
this.InstanceHandler();
if (process.platform !== "darwin") {
this.InitTray()
this.InitTray();
}
}
@ -161,58 +156,61 @@ export class AppEvents {
if (!arg) return;
// LastFM Auth URL
if (arg.includes('auth')) {
const authURI = arg.split('/auth/')[1]
if (authURI.startsWith('lastfm')) { // If we wanted more auth options
console.log('token: ', authURI.split('lastfm?token=')[1])
utils.getWindow().webContents.executeJavaScript(`ipcRenderer.send('lastfm:auth', "${authURI.split('lastfm?token=')[1]}")`).catch(console.error)
if (arg.includes("auth")) {
const authURI = arg.split("/auth/")[1];
if (authURI.startsWith("lastfm")) {
// If we wanted more auth options
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
utils.getWindow().webContents.executeJavaScript('MusicKitInterop.playPause()')
}
else if (arg.includes('nextitem')) {
utils.getWindow().webContents.executeJavaScript("MusicKitInterop.playPause()");
} else if (arg.includes("nextitem")) {
//language=JS
utils.getWindow().webContents.executeJavaScript('app.mk.skipToNextItem()')
utils.getWindow().webContents.executeJavaScript("app.mk.skipToNextItem()");
}
// Play
else if (arg.includes('/play/')) { //Steer away from protocol:// specific conditionals
const playParam = arg.split('/play/')[1]
else if (arg.includes("/play/")) {
//Steer away from protocol:// specific conditionals
const playParam = arg.split("/play/")[1];
const mediaType = {
"s/": "song",
"a/": "album",
"p/": "playlist"
}
"p/": "playlist",
};
for (const [key, value] of Object.entries(mediaType)) {
if (playParam.includes(key)) {
const id = playParam.split(key)[1]
utils.getWindow().webContents.send('play', value, id)
console.debug(`[LinkHandler] Attempting to load ${value} by id: ${id}`)
const id = playParam.split(key)[1];
utils.getWindow().webContents.send("play", value, id);
console.debug(`[LinkHandler] Attempting to load ${value} by id: ${id}`);
}
}
} else if (arg.includes('music.apple.com')) { // URL (used with itms/itmss/music/musics uris)
console.log(arg)
let url = arg.split('//')[1]
} else if (arg.includes("music.apple.com")) {
// URL (used with itms/itmss/music/musics uris)
console.log(arg);
let url = arg.split("//")[1];
console.warn(`[LinkHandler] Attempting to load url: ${url}`);
utils.getWindow().webContents.send('play', 'url', url)
} else if (arg.includes('/debug/appdata')) {
shell.openPath(app.getPath('userData'))
} else if (arg.includes('/debug/logs')) {
shell.openPath(app.getPath('logs'))
} else if (arg.includes('/discord')) {
shell.openExternal('https://discord.gg/applemusic')
} else if (arg.includes('/github')) {
shell.openExternal('https://github.com/ciderapp/cider')
} else if (arg.includes('/donate')) {
shell.openExternal('https://opencollective.com/ciderapp')
} else if (arg.includes('/beep')) {
shell.beep()
utils.getWindow().webContents.send("play", "url", url);
} else if (arg.includes("/debug/appdata")) {
shell.openPath(app.getPath("userData"));
} else if (arg.includes("/debug/logs")) {
shell.openPath(app.getPath("logs"));
} else if (arg.includes("/discord")) {
shell.openExternal("https://discord.gg/applemusic");
} else if (arg.includes("/github")) {
shell.openExternal("https://github.com/ciderapp/cider");
} else if (arg.includes("/donate")) {
shell.openExternal("https://opencollective.com/ciderapp");
} else if (arg.includes("/beep")) {
shell.beep();
} 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() {
// 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
console.log('[Cider] Another instance has been found, quitting.')
app.quit()
} else { // 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())
if (!gotTheLock) {
// Runs on the new instance if another instance has been found
console.log("[Cider] Another instance has been found, quitting.");
app.quit();
} else {
// 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 => {
console.log(arg)
startArgs.forEach((arg) => {
console.log(arg);
if (arg.includes("cider://")) {
console.debug('[InstanceHandler] (second-instance) Link detected with ' + arg)
this.LinkHandler(arg)
console.debug("[InstanceHandler] (second-instance) Link detected with " + arg);
this.LinkHandler(arg);
} else if (arg.includes("--force-quit")) {
console.warn('[InstanceHandler] (second-instance) Force Quit found. Quitting App.');
app.quit()
console.warn("[InstanceHandler] (second-instance) Force Quit found. Quitting App.");
app.quit();
} else if (utils.getWindow()) {
if (utils.getWindow().isMinimized()) utils.getWindow().restore()
utils.getWindow().show()
utils.getWindow().focus()
if (utils.getWindow().isMinimized()) utils.getWindow().restore();
utils.getWindow().show();
utils.getWindow().focus();
}
})
})
});
});
}
}
/**
@ -254,48 +253,48 @@ export class AppEvents {
*/
private InitTray() {
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,
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,
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,
height: 20
height: 20,
}),
}
this.tray = new Tray(process.platform === 'win32' ? icons.win32 : (process.platform === 'darwin' ? icons.darwin : icons.linux))
this.tray.setToolTip(app.getName())
this.setTray(false)
};
this.tray = new Tray(process.platform === "win32" ? icons.win32 : process.platform === "darwin" ? icons.darwin : icons.linux);
this.tray.setToolTip(app.getName());
this.setTray(false);
this.tray.on('double-click', () => {
this.tray.on("double-click", () => {
if (utils.getWindow()) {
if (utils.getWindow().isVisible()) {
utils.getWindow().focus()
utils.getWindow().focus();
} else {
utils.getWindow().show()
utils.getWindow().show();
}
}
})
});
utils.getWindow().on('show', () => {
this.setTray(true)
})
utils.getWindow().on("show", () => {
this.setTray(true);
});
utils.getWindow().on('restore', () => {
this.setTray(true)
})
utils.getWindow().on("restore", () => {
this.setTray(true);
});
utils.getWindow().on('hide', () => {
this.setTray(false)
})
utils.getWindow().on("hide", () => {
this.setTray(false);
});
utils.getWindow().on('minimize', () => {
this.setTray(false)
})
utils.getWindow().on("minimize", () => {
this.setTray(false);
});
}
/**
@ -303,23 +302,21 @@ export class AppEvents {
* @param visible - BrowserWindow Visibility
*/
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({
width: 24,
height: 24
})
height: 24,
});
const menu = Menu.buildFromTemplate([
{
label: app.getName(),
enabled: false,
icon: ciderIcon,
},
{type: 'separator'},
{ type: "separator" },
/* For now only idea i dont know if posible to implement
@ -340,50 +337,50 @@ export class AppEvents {
{
visible: !visible,
label: this.i18n['term.playpause'],
label: this.i18n["term.playpause"],
click: () => {
utils.getWindow().webContents.executeJavaScript('MusicKitInterop.playPause()')
}
utils.getWindow().webContents.executeJavaScript("MusicKitInterop.playPause()");
},
},
{
visible: !visible,
label: this.i18n['term.next'],
label: this.i18n["term.next"],
click: () => {
utils.getWindow().webContents.executeJavaScript(`MusicKitInterop.next()`)
}
utils.getWindow().webContents.executeJavaScript(`MusicKitInterop.next()`);
},
},
{
visible: !visible,
label: this.i18n['term.previous'],
label: this.i18n["term.previous"],
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: () => {
if (utils.getWindow()) {
if (visible) {
utils.getWindow().hide()
utils.getWindow().hide();
} else {
utils.getWindow().show()
}
utils.getWindow().show();
}
}
},
},
{
label: this.i18n['term.quit'],
label: this.i18n["term.quit"],
click: () => {
app.quit()
}
}
])
this.tray.setContextMenu(menu)
app.quit();
},
},
]);
this.tray.setContextMenu(menu);
}
/**
@ -391,18 +388,21 @@ export class AppEvents {
* @private
*/
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);
console.debug = function (...args: any[]) {
if (!app.isPackaged) {
log.debug(...args)
log.debug(...args);
}
};
ipcMain.on('fetch-log', (_event) => {
const data = readFileSync(log.transports.file.getFile().path, {encoding: 'utf8', flag: 'r'});
clipboard.writeText(data)
})
ipcMain.on("fetch-log", (_event) => {
const data = readFileSync(log.transports.file.getFile().path, {
encoding: "utf8",
flag: "r",
});
clipboard.writeText(data);
});
}
/**
@ -410,17 +410,17 @@ export class AppEvents {
* @private
*/
private static setLoginSettings() {
if (utils.getStoreValue('general.onStartup.enabled')) {
if (utils.getStoreValue("general.onStartup.enabled")) {
app.setLoginItemSettings({
openAtLogin: true,
path: app.getPath('exe'),
args: [`${utils.getStoreValue('general.onStartup.hidden') ? '--hidden' : ''}`]
})
path: app.getPath("exe"),
args: [`${utils.getStoreValue("general.onStartup.hidden") ? "--hidden" : ""}`],
});
} else {
app.setLoginItemSettings({
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 castv2Cli = require('castv2-client');
var util = require("util");
var castv2Cli = require("castv2-client");
var RequestResponseController = castv2Cli.RequestResponseController;
function CiderCastController(client, sourceId, destinationId) {
RequestResponseController.call(this, client, sourceId, destinationId, 'urn:x-cast:com.ciderapp.customdata');
this.once('close', onclose);
RequestResponseController.call(this, client, sourceId, destinationId, "urn:x-cast:com.ciderapp.customdata");
this.once("close", onclose);
var self = this;
function onclose() {
self.stop();
@ -16,16 +16,16 @@ util.inherits(CiderCastController, RequestResponseController);
CiderCastController.prototype.sendIp = function (ip) {
// TODO: Implement Callback
let data = {
ip : ip
}
ip: ip,
};
this.request(data);
};
CiderCastController.prototype.kill = function () {
// TODO: Implement Callback
let data = {
action : "stop"
}
action: "stop",
};
this.request(data);
};

View file

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

View file

@ -1,7 +1,7 @@
import * as fs from 'fs';
import * as path from 'path';
import * as electron from 'electron'
import {utils} from './utils';
import * as fs from "fs";
import * as path from "path";
import * as electron from "electron";
import { utils } from "./utils";
//
// 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 {
private static PluginMap: any = {};
private basePluginsPath = path.join(__dirname, '../plugins');
private userPluginsPath = path.join(electron.app.getPath('userData'), 'Plugins');
private basePluginsPath = path.join(__dirname, "../plugins");
private userPluginsPath = path.join(electron.app.getPath("userData"), "Plugins");
private readonly pluginsList: any = {};
constructor() {
@ -36,10 +36,9 @@ export class Plugins {
public getPlugins(): any {
let plugins: any = {};
if (fs.existsSync(this.basePluginsPath)) {
fs.readdirSync(this.basePluginsPath).forEach(file => {
if (file.endsWith('.ts') || file.endsWith('.js')) {
fs.readdirSync(this.basePluginsPath).forEach((file) => {
if (file.endsWith(".ts") || file.endsWith(".js")) {
const plugin = require(path.join(this.basePluginsPath, file)).default;
if (plugins[file] || plugin.name in plugins) {
console.log(`[${plugin.name}] Plugin already loaded / Duplicate Class Name`);
@ -50,14 +49,13 @@ export class Plugins {
});
}
if (fs.existsSync(this.userPluginsPath)) {
fs.readdirSync(this.userPluginsPath).forEach(file => {
fs.readdirSync(this.userPluginsPath).forEach((file) => {
// Plugins V1
if (file.endsWith('.ts') || file.endsWith('.js')) {
if (file.endsWith(".ts") || file.endsWith(".js")) {
if (!electron.app.isPackaged) {
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) {
console.log(`[${plugin.name}] Plugin already loaded / Duplicate Class Name`);
} else {
@ -65,7 +63,7 @@ export class Plugins {
}
} else {
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) {
console.log(`[${plugin.name}] Plugin already loaded / Duplicate Class Name`);
} else {
@ -76,7 +74,7 @@ export class Plugins {
// Plugins V2
else if (fs.lstatSync(path.join(this.userPluginsPath, file)).isDirectory()) {
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 plugin = require(path.join(pluginPath, pluginPackage.main));
if (plugins[plugin.name] || plugin.name in plugins) {
@ -89,15 +87,15 @@ export class Plugins {
utils: utils,
win: utils.getWindow(),
dir: pluginPath,
dirName: file
}
dirName: file,
};
plugins[plugin.name] = new plugin(pluginEnv);
}
}
}
});
}
console.log('[PluginHandler] Loaded plugins:', Object.keys(plugins));
console.log("[PluginHandler] Loaded plugins:", Object.keys(plugins));
return plugins;
}
@ -108,7 +106,7 @@ export class Plugins {
this.pluginsList[plugin][event](...args);
} catch (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);
}
}
}

View file

@ -1,4 +1,4 @@
import * as ElectronStore from 'electron-store';
import * as ElectronStore from "electron-store";
import * as electron from "electron";
import { app } from "electron";
import fetch from "electron-fetch";
@ -7,327 +7,273 @@ export class Store {
static cfg: ElectronStore;
private defaults: any = {
"main": {
"PLATFORM": process.platform,
"UPDATABLE": app.isPackaged && (!process.mas || !process.windowsStore || !process.env.FLATPAK_ID)
main: {
PLATFORM: process.platform,
UPDATABLE: app.isPackaged && (!process.mas || !process.windowsStore || !process.env.FLATPAK_ID),
},
"general": {
"close_button_hide": false,
"language": "en_US", // electron.app.getLocale().replace('-', '_') this can be used in future
"playbackNotifications": true,
"resumeOnStartupBehavior": "local",
"privateEnabled": false,
"themeUpdateNotification": true,
"sidebarItems": {
"recentlyAdded": true,
"songs": true,
"albums": true,
"artists": true,
"videos": true,
"podcasts": true
general: {
close_button_hide: false,
language: "en_US", // electron.app.getLocale().replace('-', '_') this can be used in future
playbackNotifications: true,
resumeOnStartupBehavior: "local",
privateEnabled: false,
themeUpdateNotification: true,
sidebarItems: {
recentlyAdded: true,
songs: true,
albums: true,
artists: true,
videos: true,
podcasts: true,
},
"sidebarCollapsed": {
"cider": false,
"applemusic": false,
"library": false,
"amplaylists": false,
"playlists": false,
"localLibrary": false
sidebarCollapsed: {
cider: false,
applemusic: false,
library: false,
amplaylists: false,
playlists: false,
localLibrary: false,
},
"onStartup": {
"enabled": false,
"hidden": false,
onStartup: {
enabled: false,
hidden: false,
},
"resumeTabs": {
"tab": "home",
"dynamicData": ""
resumeTabs: {
tab: "home",
dynamicData: "",
},
"keybindings": {
"search": [
"CommandOrControl",
"F"
],
"listnow": [
"CommandOrControl",
"L"
],
"browse": [
"CommandOrControl",
"B"
],
"recentAdd": [
"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": [
keybindings: {
search: ["CommandOrControl", "F"],
listnow: ["CommandOrControl", "L"],
browse: ["CommandOrControl", "B"],
recentAdd: ["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?
","
",",
],
"zoomn": [
"Control",
"numadd",
],
"zoomt": [
"Control",
"numsub",
],
"zoomrst": [
"Control",
"num0",
],
"openDeveloperTools": [
"CommandOrControl",
"Shift",
"I"
]
zoomn: ["Control", "numadd"],
zoomt: ["Control", "numsub"],
zoomrst: ["Control", "num0"],
openDeveloperTools: ["CommandOrControl", "Shift", "I"],
},
"showLovedTracksInline": true
showLovedTracksInline: true,
},
"connectivity": {
"discord_rpc": {
"enabled": true,
"client": "Cider",
"clear_on_pause": true,
"hide_buttons": false,
"hide_timestamp": false,
"state_format": "by {artist}",
"details_format": "{title}",
connectivity: {
discord_rpc: {
enabled: true,
client: "Cider",
clear_on_pause: true,
hide_buttons: false,
hide_timestamp: false,
state_format: "by {artist}",
details_format: "{title}",
},
"lastfm": {
"enabled": false,
"scrobble_after": 50,
"filter_loop": false,
"filter_types": {},
"remove_featured": false,
"secrets": {
"username": "",
"key": ""
}
lastfm: {
enabled: false,
scrobble_after: 50,
filter_loop: false,
filter_types: {},
remove_featured: false,
secrets: {
username: "",
key: "",
},
},
"home": {
"followedArtists": [],
"favoriteItems": []
},
"libraryPrefs": {
"songs": {
"scroll": "paged",
"sort": "name",
"sortOrder": "asc",
"size": "normal"
home: {
followedArtists: [],
favoriteItems: [],
},
"albums": {
"scroll": "paged",
"sort": "name",
"sortOrder": "asc",
"viewAs": "covers"
libraryPrefs: {
songs: {
scroll: "paged",
sort: "name",
sortOrder: "asc",
size: "normal",
},
"playlists": {
"scroll": "infinite"
albums: {
scroll: "paged",
sort: "name",
sortOrder: "asc",
viewAs: "covers",
},
"localPaths": [],
"pageSize": 250
playlists: {
scroll: "infinite",
},
"audio": {
"volume": 1,
"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]
}
localPaths: [],
pageSize: 250,
},
"spatial": false,
"spatial_properties": {
"presets": [],
"gain": 0.8,
"listener_position": [0, 0, 0],
"audio_position": [0, 0, 0],
"room_dimensions": {
"width": 32,
"height": 12,
"depth": 32
},
"room_materials": {
"left": 'metal',
"right": 'metal',
"front": 'brick-bare',
"back": 'brick-bare',
"down": 'acoustic-ceiling-tiles',
"up": 'acoustic-ceiling-tiles',
}
},
"equalizer": {
'preset': "default",
'frequencies': [32, 63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000],
'gain': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'Q': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
'mix': 1,
'vibrantBass': 0,
'presets': [],
'userGenerated': false
audio: {
volume: 1,
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],
},
},
"visual": {
"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"
spatial: false,
spatial_properties: {
presets: [],
gain: 0.8,
listener_position: [0, 0, 0],
audio_position: [0, 0, 0],
room_dimensions: {
width: 32,
height: 12,
depth: 32,
},
"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
room_materials: {
left: "metal",
right: "metal",
front: "brick-bare",
back: "brick-bare",
down: "acoustic-ceiling-tiles",
up: "acoustic-ceiling-tiles",
},
"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
equalizer: {
preset: "default",
frequencies: [32, 63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000],
gain: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
Q: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
mix: 1,
vibrantBass: 0,
presets: [],
userGenerated: false,
},
"connectUser": {
"auth": null,
"sync": {
},
visual: {
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,
plugins: false,
settings: false,
}
},
}
private migrations: any = {}
},
};
private migrations: any = {};
private schema: ElectronStore.Schema<any> = {
"connectivity.discord_rpc": {
type: 'object'
type: "object",
},
}
};
constructor() {
Store.cfg = new ElectronStore({
name: 'cider-config',
name: "cider-config",
defaults: this.defaults,
schema: this.schema,
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();
}
static pushToCloud(): void {
if (Store.cfg.get('connectUser.auth') === null) return;
if (Store.cfg.get("connectUser.auth") === null) return;
var syncData = Object();
if (Store.cfg.get('connectUser.sync.themes')) {
if (Store.cfg.get("connectUser.sync.themes")) {
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({
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({
general: Store.cfg.get('general'),
home: Store.cfg.get('home'),
libraryPrefs: Store.cfg.get('libraryPrefs'),
advanced: Store.cfg.get('advanced'),
})
general: Store.cfg.get("general"),
home: Store.cfg.get("home"),
libraryPrefs: Store.cfg.get("libraryPrefs"),
advanced: Store.cfg.get("advanced"),
});
}
let postBody = {
id: Store.cfg.get('connectUser.id'),
id: Store.cfg.get("connectUser.id"),
app: electron.app.getName(),
version: electron.app.isPackaged ? electron.app.getVersion() : 'dev',
syncData: syncData
}
version: electron.app.isPackaged ? electron.app.getVersion() : "dev",
syncData: syncData,
};
fetch('https://connect.cidercollective.dev/api/v1/setttings/set', {
method: 'POST',
fetch("https://connect.cidercollective.dev/api/v1/setttings/set", {
method: "POST",
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 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
for (const key of Object.keys(source)) {
if (key.includes('migrations')) {
if (key.includes("migrations")) {
continue;
}
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`
Object.assign(target || {}, source)
return target
}
Object.assign(target || {}, source);
return target;
};
/**
* IPC Handler
*/
private ipcHandler(): void {
electron.ipcMain.handle('getStoreValue', (_event, key, defaultValue) => {
return (defaultValue ? Store.cfg.get(key, true) : Store.cfg.get(key));
electron.ipcMain.handle("getStoreValue", (_event, key, defaultValue) => {
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);
});
electron.ipcMain.on('getStore', (event) => {
event.returnValue = Store.cfg.store
})
electron.ipcMain.on("getStore", (event) => {
event.returnValue = Store.cfg.store;
});
electron.ipcMain.on('setStore', (_event, store) => {
Store.cfg.store = store
})
electron.ipcMain.on("setStore", (_event, store) => {
Store.cfg.store = store;
});
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -1,10 +1,9 @@
import * as electron from 'electron';
import * as os from 'os';
import {resolve} from 'path';
import * as CiderReceiver from '../base/castreceiver';
import * as electron from "electron";
import * as os from "os";
import { resolve } from "path";
import * as CiderReceiver from "../base/castreceiver";
export default class ChromecastPlugin {
/**
* Private variables for interaction in plugins
*/
@ -13,8 +12,8 @@ export default class ChromecastPlugin {
private _lastfm: any;
private _store: any;
private _timer: any;
private audioClient = require('castv2-client').Client;
private mdns = require('mdns-js');
private audioClient = require("castv2-client").Client;
private mdns = require("mdns-js");
private devices: any = [];
private castDevices: any = [];
@ -34,42 +33,39 @@ export default class ChromecastPlugin {
// private bufcount2 = 0;
// private headerSent = false;
private searchForGCDevices() {
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('ready', browser.discover);
browser.on('update', (service: any) => {
if (service.addresses && service.fullname && service.fullname.includes('_googlecast._tcp')) {
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');
browser.on("update", (service: any) => {
if (service.addresses && service.fullname && service.fullname.includes("_googlecast._tcp")) {
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
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);
if (location != null) {
this.getServiceDescription(location, rinfo.address);
}
});
function getLocation(headers: any) {
let location = null;
if (headers["LOCATION"] != null) {
location = headers["LOCATION"]
location = headers["LOCATION"];
} else if (headers["Location"] != null) {
location = headers["Location"]
location = headers["Location"];
}
return location;
}
ssdpBrowser.search('urn:dial-multiscreen-org:device:dial:1');
ssdpBrowser.search("urn:dial-multiscreen-org:device:dial:1");
// // actual upnp devices
// if (app.cfg.get("audio.enableDLNA")) {
@ -84,15 +80,13 @@ export default class ChromecastPlugin {
// ssdpBrowser2.search('urn:schemas-upnp-org:device:MediaRenderer:1');
// }
} catch (e) {
console.log('Search GC err', e);
console.log("Search GC err", e);
}
}
private getServiceDescription(url: any, address: any) {
const request = require('request');
const request = require("request");
request.get(url, (error: any, response: any, body: any) => {
if (!error && response.statusCode === 200) {
this.parseServiceDescription(body, address, url);
@ -106,7 +100,7 @@ export default class ChromecastPlugin {
name: name,
host: host,
location: location,
type: type
type: type,
});
if (this.devices.indexOf(host) === -1) {
this.devices.push(host);
@ -122,22 +116,21 @@ export default class ChromecastPlugin {
}
private parseServiceDescription(body: any, address: any, url: any) {
const parseString = require('xml2js').parseString;
const parseString = require("xml2js").parseString;
parseString(body, (err: any, result: any) => {
if (!err && result && result.root && result.root.device) {
const device = result.root.device[0];
console.log('device', device);
let devicetype = 'googlecast';
console.log()
if (device.deviceType && device.deviceType.toString() === 'urn:schemas-upnp-org:device:MediaRenderer:1') {
devicetype = 'upnp';
console.log("device", device);
let devicetype = "googlecast";
console.log();
if (device.deviceType && device.deviceType.toString() === "urn:schemas-upnp-org:device:MediaRenderer:1") {
devicetype = "upnp";
}
this.ondeviceup(address, device.friendlyName.toString(), url, devicetype);
}
});
}
private loadMedia(client: any, song: any, artist: any, album: any, albumart: any, cb?: any) {
// const u = 'http://' + this.getIp() + ':' + server.address().port + '/';
// const DefaultMediaReceiver : any = require('castv2-client').DefaultMediaReceiver;
@ -148,9 +141,9 @@ export default class ChromecastPlugin {
}
let media = {
// 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',
contentType: 'audio/wav',
streamType: 'LIVE', // or LIVE
contentId: "http://" + this.getIp() + ":" + this.ciderPort + "/audio.wav",
contentType: "audio/wav",
streamType: "LIVE", // or LIVE
// Title and cover displayed while buffering
metadata: {
@ -159,23 +152,25 @@ export default class ChromecastPlugin {
title: song ?? "",
albumName: album ?? "",
artist: artist ?? "",
images: [
{url: albumart ?? ""}]
}
images: [{ url: albumart ?? "" }],
},
};
player.on('status', (status: any) => {
console.log('status broadcast playerState=%s', status);
player.on("status", (status: any) => {
console.log("status broadcast playerState=%s", status);
});
console.log('app "%s" launched, loading media %s ...', player, media);
player.load(media, {
autoplay: true
}, (err: any, status: any) => {
console.log('media loaded playerState=%s', status);
});
player.load(
media,
{
autoplay: true,
},
(err: any, status: any) => {
console.log("media loaded playerState=%s", status);
}
);
client.getStatus((x: any, status: any) => {
if (status && status.volume) {
@ -183,40 +178,37 @@ export default class ChromecastPlugin {
client.muted = status.volume.muted;
client.stepInterval = status.volume.stepInterval;
}
})
});
// send websocket ip
player.sendIp("ws://" + this.getIp() + ":26369");
electron.ipcMain.on('stopGCast', (_event) => {
electron.ipcMain.on("stopGCast", (_event) => {
player.kill();
})
electron.app.on('before-quit', (_event) => {
});
electron.app.on("before-quit", (_event) => {
player.kill();
})
});
});
}
private getIp() {
let ip: string = '';
let ip: string = "";
let ip2: any = [];
let alias = 0;
const ifaces: any = os.networkInterfaces();
for (let dev in ifaces) {
ifaces[dev].forEach((details: any) => {
if (details.family === 'IPv4' && !details.internal) {
if (!/(loopback|vmware|internal|hamachi|vboxnet|virtualbox)/gi.test(dev + (alias ? ':' + alias : ''))) {
if (details.address.substring(0, 8) === '192.168.' ||
details.address.substring(0, 7) === '172.16.' ||
details.address.substring(0, 3) === '10.'
if (details.family === "IPv4" && !details.internal) {
if (!/(loopback|vmware|internal|hamachi|vboxnet|virtualbox)/gi.test(dev + (alias ? ":" + alias : ""))) {
if (details.address.substring(0, 8) === "192.168." || details.address.substring(0, 7) === "172.16." || details.address.substring(0, 3) === "10.") {
if (
!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.') ||
(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;}
ip = details.address;
}
++alias;
}
}
@ -227,13 +219,13 @@ export default class ChromecastPlugin {
}
private stream(device: any, song: any, artist: any, album: any, albumart: any) {
let castMode = 'googlecast';
let UPNPDesc = '';
let castMode = "googlecast";
let UPNPDesc = "";
castMode = device.type;
UPNPDesc = device.location;
let client;
if (castMode === 'googlecast') {
if (castMode === "googlecast") {
let client = new this.audioClient();
client.volume = 100;
client.stepInterval = 0.5;
@ -248,7 +240,7 @@ export default class ChromecastPlugin {
this.loadMedia(client, song, artist, album, albumart);
});
client.on('close', () => {
client.on("close", () => {
console.info("Client Closed");
for (let i = this.activeConnections.length - 1; i >= 0; i--) {
if (this.activeConnections[i] === client) {
@ -258,12 +250,11 @@ export default class ChromecastPlugin {
}
});
client.on('error', (err: any) => {
console.log('Error: %s', err.message);
client.on("error", (err: any) => {
console.log("Error: %s", err.message);
client.close();
delete this.connectedHosts[device.host];
});
} else {
// upnp devices
//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;
// }
// };
// client.load('http://' + getIp() + ':' + server.address().port + '/a.wav', options, function (err, _result) {
// if (err) throw err;
// console.log('playing ...');
// });
// } catch (e) {
// }
}
}
private async setupGCServer() {
return ''
return "";
}
/**
* Base Plugin Details (Eventually implemented into a GUI in settings)
*/
public name: string = 'Chromecast';
public description: string = 'LastFM plugin for Cider';
public version: string = '0.0.1';
public author: string = 'vapormusic / Cider Collective';
public name: string = "Chromecast";
public description: string = "LastFM plugin for Cider";
public version: string = "0.0.1";
public author: string = "vapormusic / Cider Collective";
/**
* 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._store = utils.getStore()
this._store = utils.getStore();
}
/**
@ -317,11 +305,11 @@ export default class ChromecastPlugin {
*/
onReady(win: any): void {
this._win = win;
electron.ipcMain.on('getKnownCastDevices', (event) => {
event.returnValue = this.castDevices
electron.ipcMain.on("getKnownCastDevices", (event) => {
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._win.webContents.setAudioMuted(true);
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();
});
electron.ipcMain.on('stopGCast', (_event) => {
electron.ipcMain.on("stopGCast", (_event) => {
this._win.webContents.setAudioMuted(false);
this.activeConnections.forEach((client: any) => {
try {
client.stop();
} catch (e) {}
})
});
this.activeConnections = [];
this.connectedHosts = {};
})
});
}
/**
* Runs on app stop
*/
onBeforeQuit(): void {
}
onBeforeQuit(): void {}
/**
* Runs on song change
* @param attributes Music Attributes
*/
onNowPlayingItemDidChange(attributes: any): void {
}
onNowPlayingItemDidChange(attributes: any): void {}
onRendererReady(): void {
this._win.webContents.executeJavaScript(
`ipcRenderer.sendSync('get-port')`
).then((result: any) => {
this._win.webContents.executeJavaScript(`ipcRenderer.sendSync('get-port')`).then((result: any) => {
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 fetch from 'electron-fetch'
import fetch from "electron-fetch";
export default class DiscordRPC {
/**
* Base Plugin Details (Eventually implemented into a GUI in settings)
*/
public name: string = 'Discord Rich Presence';
public description: string = 'Discord RPC plugin for Cider';
public version: string = '1.1.0';
public author: string = 'vapormusic/Core (Cider Collective)';
public name: string = "Discord Rich Presence";
public description: string = "Discord RPC plugin for Cider";
public version: string = "1.1.0";
public author: string = "vapormusic/Core (Cider Collective)";
/**
* Private variables for interaction in plugins
@ -24,13 +23,13 @@ export default class DiscordRPC {
*/
private _client: any = null;
private _activityCache: any = {
details: '',
state: '',
largeImageKey: '',
largeImageText: '',
smallImageKey: '',
smallImageText: '',
instance: false
details: "",
state: "",
largeImageKey: "",
largeImageText: "",
smallImageKey: "",
smallImageText: "",
instance: false,
};
/*******************************************************************************************
@ -49,74 +48,71 @@ export default class DiscordRPC {
* Runs on app ready
*/
onReady(_win: any): void {
const self = this
const self = this;
this.connect();
console.debug(`[Plugin][${this.name}] Ready.`);
ipcMain.on('updateRPCImage', async (_event, imageurl) => {
ipcMain.on("updateRPCImage", async (_event, imageurl) => {
if (!this._utils.getStoreValue("general.privateEnabled")) {
let b64data = ""
let postbody = ""
let b64data = "";
let postbody = "";
if (imageurl.startsWith("/ciderlocalart")) {
let port = await _win.webContents.executeJavaScript(
`app.clientPort`
);
console.log("http://localhost:"+port+imageurl)
const response = await fetch("http://localhost:"+port+imageurl)
b64data = (await response.buffer()).toString('base64');
postbody = JSON.stringify({data: b64data})
fetch('https://api.cider.sh/v1/images', {
method: 'POST',
let port = await _win.webContents.executeJavaScript(`app.clientPort`);
console.log("http://localhost:" + port + imageurl);
const response = await fetch("http://localhost:" + port + imageurl);
b64data = (await response.buffer()).toString("base64");
postbody = JSON.stringify({ data: b64data });
fetch("https://api.cider.sh/v1/images", {
method: "POST",
body: postbody,
headers: {
'Content-Type': 'application/json',
'User-Agent': _win.webContents.getUserAgent()
"Content-Type": "application/json",
"User-Agent": _win.webContents.getUserAgent(),
},
})
.then(res => res.json())
.then((res) => res.json())
.then(function (json) {
self._attributes["artwork"]["url"] = json.url
self.setActivity(self._attributes)
})
self._attributes["artwork"]["url"] = json.url;
self.setActivity(self._attributes);
});
} else {
postbody = JSON.stringify({url: imageurl})
fetch('https://api.cider.sh/v1/images', {
method: 'POST',
postbody = JSON.stringify({ url: imageurl });
fetch("https://api.cider.sh/v1/images", {
method: "POST",
body: postbody,
headers: {
'Content-Type': 'application/json',
'User-Agent': _win.webContents.getUserAgent()
"Content-Type": "application/json",
"User-Agent": _win.webContents.getUserAgent(),
},
})
.then(res => res.json())
.then((res) => res.json())
.then(function (json) {
self._attributes["artwork"]["url"] = json.url
self.setActivity(self._attributes)
})
self._attributes["artwork"]["url"] = json.url;
self.setActivity(self._attributes);
});
}
}
})
});
ipcMain.on("reloadRPC", () => {
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(() => {
this.ready = true
this._utils.getWindow().webContents.send("rpcReloaded", this._client.user)
this.ready = true;
this._utils.getWindow().webContents.send("rpcReloaded", this._client.user);
if (this._activityCache && this._activityCache.details && this._activityCache.state) {
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}`));
// this.connect(true)
})
});
}
/**
* Runs on app stop
*/
@ -129,9 +125,8 @@ export default class DiscordRPC {
* @param attributes Music Attributes (attributes.status = current state)
*/
onPlaybackStateDidChange(attributes: object): void {
this._attributes = attributes
this.setActivity(attributes)
this._attributes = attributes;
this.setActivity(attributes);
}
/**
@ -139,12 +134,10 @@ export default class DiscordRPC {
* @param attributes Music Attributes
*/
onNowPlayingItemDidChange(attributes: object): void {
this._attributes = attributes
this.setActivity(attributes)
this._attributes = attributes;
this.setActivity(attributes);
}
/*******************************************************************************************
* Private Methods
* ****************************************************************************************/
@ -162,19 +155,22 @@ export default class DiscordRPC {
this._client = new AutoClient({ transport: "ipc" });
// 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}.`);
if (this._activityCache && this._activityCache.details && this._activityCache.state) {
console.info(`[DiscordRPC][connect] Restoring activity cache.`);
this._client.setActivity(this._activityCache)
this._client.setActivity(this._activityCache);
}
})
});
// 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(() => {
this.ready = true
this.ready = true;
})
.catch((e: any) => console.error(`[DiscordRPC][connect] ${e}`));
}
@ -185,31 +181,31 @@ export default class DiscordRPC {
*/
private setActivity(attributes: any) {
if (!this._client) {
return
return;
}
// Check if show buttons is (true) or (false)
let activity: Object = {
details: this._utils.getStoreValue("connectivity.discord_rpc.details_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,
instance: false // Whether the activity is in a game session
}
instance: false, // Whether the activity is in a game session
};
// Filter the activity
activity = this.filterActivity(activity, attributes)
activity = this.filterActivity(activity, attributes);
if (!this.ready) {
this._activityCache = activity
return
this._activityCache = activity;
return;
}
// Set the activity
if (!attributes.status && this._utils.getStoreValue("connectivity.discord_rpc.clear_on_pause")) {
this._client.clearActivity()
this._client.clearActivity();
} else if (activity && this._activityCache !== activity) {
this._client.setActivity(activity)
this._client.setActivity(activity);
}
this._activityCache = activity;
}
@ -218,25 +214,24 @@ export default class DiscordRPC {
* Filter the Discord activity object
*/
private filterActivity(activity: any, attributes: any): Object {
// Add the buttons if people want them
if (!this._utils.getStoreValue("connectivity.discord_rpc.hide_buttons")) {
activity.buttons = [
{label: 'Listen on Cider', url: attributes.url.cider},
{label: 'View on Apple Music', url: attributes.url.appleMusic}
] //To change attributes.url => preload/cider-preload.js
{ label: "Listen on Cider", url: attributes.url.cider },
{ label: "View on Apple Music", url: attributes.url.appleMusic },
]; //To change attributes.url => preload/cider-preload.js
}
// Add the timestamp if its playing and people want them
if (!this._utils.getStoreValue("connectivity.discord_rpc.hide_timestamp") && attributes.status) {
activity.startTimestamp = Date.now() - (attributes?.durationInMillis - attributes?.remainingTime)
activity.endTimestamp = attributes.endTime
activity.startTimestamp = Date.now() - (attributes?.durationInMillis - attributes?.remainingTime);
activity.endTimestamp = attributes.endTime;
}
// If the user wants to keep the activity when paused
if (!this._utils.getStoreValue("connectivity.discord_rpc.clear_on_pause")) {
activity.smallImageKey = attributes.status ? 'play' : 'pause';
activity.smallImageText = attributes.status ? 'Playing' : 'Paused';
activity.smallImageKey = attributes.status ? "play" : "pause";
activity.smallImageText = attributes.status ? "Playing" : "Paused";
}
/**
@ -248,36 +243,36 @@ export default class DiscordRPC {
* {trackNumber}
*/
const rpcVars: any = {
"artist": attributes.artistName,
"composer": attributes.composerName,
"title": attributes.name,
"album": attributes.albumName,
"trackNumber": attributes.trackNumber
}
artist: attributes.artistName,
composer: attributes.composerName,
title: attributes.name,
album: attributes.albumName,
trackNumber: attributes.trackNumber,
};
// Replace the variables
Object.keys(rpcVars).forEach((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}}`)) {
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
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
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
if (activity.largeImageText && activity.largeImageText.length >= 128) {
activity.largeImageText = activity.largeImageText.substring(0, 125) + '...'
activity.largeImageText = activity.largeImageText.substring(0, 125) + "...";
}
// Check large image
@ -287,8 +282,8 @@ export default class DiscordRPC {
// Timestamp
if (new Date(attributes.endTime).getTime() < 0) {
delete activity.startTime
delete activity.endTime
delete activity.startTime;
delete activity.endTime;
}
// not sure
@ -297,8 +292,8 @@ export default class DiscordRPC {
}
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 {
/**
* Base Plugin Information
*/
public name: string = 'LastFM Plugin';
public version: string = '2.0.0';
public author: string = 'Core (Cider Collective)';
public name: string = "LastFM Plugin";
public version: string = "2.0.0";
public author: string = "Core (Cider Collective)";
private _apiCredentials = {
key: "f9986d12aab5a0fe66193c559435ede3",
secret: "acba3c29bd5973efa38cc2f0b63cc625"
}
secret: "acba3c29bd5973efa38cc2f0b63cc625",
};
/**
* Plugin Initialization
*/
@ -31,42 +29,41 @@ export default class lastfm {
}
onReady(_win: Electron.BrowserWindow): void {
this.initializeLastFM("", this._apiCredentials)
this.initializeLastFM("", this._apiCredentials);
// Register the ipcMain handlers
this._utils.getIPCMain().handle('lastfm:url', (event: any) => {
console.debug(`[${lastfm.name}:url] Called.`)
return this._lfm.getAuthenticationUrl({"cb": "cider://auth/lastfm"})
})
this._utils.getIPCMain().handle("lastfm:url", (event: any) => {
console.debug(`[${lastfm.name}:url] Called.`);
return this._lfm.getAuthenticationUrl({ cb: "cider://auth/lastfm" });
});
this._utils.getIPCMain().on('lastfm:auth', (event: any, token: string) => {
console.debug(`[${lastfm.name}:auth] Token: `, token)
this.authenticateLastFM(token)
})
this._utils.getIPCMain().on("lastfm:auth", (event: any, token: string) => {
console.debug(`[${lastfm.name}:auth] Token: `, 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._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;
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;
this.scrobbleTrack(attributes)
})
this.scrobbleTrack(attributes);
});
}
/**
* Runs on playback State Change
* @param attributes Music Attributes (attributes.status = current state)
*/
onPlaybackStateDidChange(attributes: object): void {
}
onPlaybackStateDidChange(attributes: object): void {}
/**
* Runs on song change
@ -75,7 +72,7 @@ export default class lastfm {
*/
onNowPlayingItemDidChange(attributes: any, scrobble = false): void {
if (this._utils.getStoreValue("general.privateEnabled")) return;
this.updateNowPlayingTrack(attributes)
this.updateNowPlayingTrack(attributes);
}
/**
@ -84,19 +81,19 @@ export default class lastfm {
* @param api
* @private
*/
private initializeLastFM(token: string, api: { key: string, secret: string }): void {
console.debug(`[${lastfm.name}:initialize] Initializing LastFM`)
const LastfmAPI = require("lastfmapi")
private initializeLastFM(token: string, api: { key: string; secret: string }): void {
console.debug(`[${lastfm.name}:initialize] Initializing LastFM`);
const LastfmAPI = require("lastfmapi");
this._lfm = new LastfmAPI({
'api_key': api.key,
'secret': api.secret,
api_key: api.key,
secret: api.secret,
});
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._authenticated = true;
} else {
this.authenticateLastFM(token)
this.authenticateLastFM(token);
}
}
@ -111,12 +108,12 @@ export default class lastfm {
if (err) {
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;
}
this._utils.getWindow().webContents.send('lastfm:authenticated', session)
this._utils.getWindow().webContents.send("lastfm:authenticated", session);
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.lfmAlbum) {
this._lfm.album.getInfo({
"artist": attributes.primaryArtist,
"album": attributes.albumName
}, (err: any, data: any) => {
this._lfm.album.getInfo(
{
artist: attributes.primaryArtist,
album: attributes.albumName,
},
(err: any, data: any) => {
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 {};
}
if (data) {
attributes.lfmAlbum = data
callback(attributes)
attributes.lfmAlbum = data;
callback(attributes);
}
})
}
);
} else {
this._lfm.track.getCorrection(attributes.primaryArtist, attributes.name, (err: any, data: any) => {
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 {};
}
if (data) {
attributes.lfmTrack = data.correction.track
callback(attributes)
attributes.lfmTrack = data.correction.track;
callback(attributes);
}
})
});
}
}
/**
@ -167,26 +165,32 @@ export default class lastfm {
private scrobbleTrack(attributes: any): void {
if (!attributes?.lfmTrack || !attributes?.lfmAlbum) {
this.verifyTrack(attributes, (a: any) => {
this.scrobbleTrack(a)
})
return
this.scrobbleTrack(a);
});
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
const scrobble = {
'artist': attributes.lfmTrack.artist.name,
'track': attributes.lfmTrack.name,
'album': attributes.lfmAlbum.name,
'albumArtist': attributes.lfmAlbum.artist,
'timestamp': new Date().getTime() / 1000,
'trackNumber': attributes.trackNumber,
'duration': attributes.durationInMillis / 1000,
}
artist: attributes.lfmTrack.artist.name,
track: attributes.lfmTrack.name,
album: attributes.lfmAlbum.name,
albumArtist: attributes.lfmAlbum.artist,
timestamp: new Date().getTime() / 1000,
trackNumber: attributes.trackNumber,
duration: attributes.durationInMillis / 1000,
};
// 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
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}`);
} else {
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 {
if (!attributes?.lfmTrack || !attributes?.lfmAlbum) {
this.verifyTrack(attributes, (a: any) => {
this.updateNowPlayingTrack(a)
})
return
this.updateNowPlayingTrack(a);
});
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 = {
'artist': attributes.lfmTrack.artist.name,
'track': attributes.lfmTrack.name,
'album': attributes.lfmAlbum.name,
'trackNumber': attributes.trackNumber,
'duration': attributes.durationInMillis / 1000,
'albumArtist': attributes.lfmAlbum.artist,
}
artist: attributes.lfmTrack.artist.name,
track: attributes.lfmTrack.name,
album: attributes.lfmAlbum.name,
trackNumber: attributes.trackNumber,
duration: attributes.durationInMillis / 1000,
albumArtist: attributes.lfmAlbum.artist,
};
this._lfm.track.updateNowPlaying(nowPlaying, (err: any, res: any) => {
if (err) {
console.error(`[${lastfm.name}:updateNowPlaying] Now Playing Update failed: ${err.message}`);
} else {
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";
export default class Thumbar {
/**
* Base Plugin Details (Eventually implemented into a GUI in settings)
*/
public name: string = 'Menubar Plugin';
public description: string = 'Creates the menubar';
public version: string = '1.0.0';
public author: string = 'Core';
public contributors: string[] = ['Core', 'Qwack', 'Monochromish'];
public name: string = "Menubar Plugin";
public description: string = "Creates the menubar";
public version: string = "1.0.0";
public author: string = "Core";
public contributors: string[] = ["Core", "Qwack", "Monochromish"];
/**
* Menubar Assets
* @private
*/
private isNotMac: boolean = process.platform !== 'darwin';
private isMac: boolean = process.platform === 'darwin';
private isNotMac: boolean = process.platform !== "darwin";
private isMac: boolean = process.platform === "darwin";
private _menuTemplate: any = [
{
label: app.getName(),
submenu: [
{
label: `${utils.getLocale(utils.getStoreValue('general.language'), 'term.about')} ${app.getName()}`,
click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('about')`)
label: `${utils.getLocale(utils.getStoreValue("general.language"), "term.about")} ${app.getName()}`,
click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('about')`),
},
{type: 'separator'},
{ type: "separator" },
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.toggleprivate'),
accelerator: utils.getStoreValue("general.keybindings.togglePrivateSession").join('+'),
click: () => utils.getWindow().webContents.executeJavaScript(`app.cfg.general.privateEnabled = !app.cfg.general.privateEnabled`)
label: utils.getLocale(utils.getStoreValue("general.language"), "term.toggleprivate"),
accelerator: utils.getStoreValue("general.keybindings.togglePrivateSession").join("+"),
click: () => utils.getWindow().webContents.executeJavaScript(`app.cfg.general.privateEnabled = !app.cfg.general.privateEnabled`),
},
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.settings'),
accelerator: utils.getStoreValue("general.keybindings.settings").join('+'),
click: () => utils.getWindow().webContents.executeJavaScript(`app.openSettingsPage()`)
label: utils.getLocale(utils.getStoreValue("general.language"), "term.settings"),
accelerator: utils.getStoreValue("general.keybindings.settings").join("+"),
click: () => utils.getWindow().webContents.executeJavaScript(`app.openSettingsPage()`),
},
...(this.isMac ? [
{type: 'separator'},
{role: 'services'},
{type: 'separator'},
{role: 'hide'},
{role: 'hideOthers'},
{role: 'unhide'},
{type: 'separator'},
{role: 'quit'}
] : []),
...(this.isNotMac ? [
{type: 'separator'},
...(this.isMac ? [{ type: "separator" }, { role: "services" }, { 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'),
accelerator: 'Control+Q',
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"), "term.quit"),
accelerator: "Control+Q",
click: () => app.quit(),
},
]
: []),
],
},
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.window'),
label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.view"),
submenu: [
{role: 'minimize', label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.minimize')},
{type: 'separator'},
...(this.isMac ? [
{
label: 'Show',
click: () => utils.getWindow().show()
},
{role: 'zoom'},
{type: 'separator'},
{role: 'front'},
{role: 'close'},
{
label: 'Edit',
submenu: [
{role: 'undo'},
{role: 'redo'},
{type: 'separator'},
{role: 'cut'},
{role: 'copy'},
{role: 'paste'},
...(this.isMac
? [
{ role: "reload" },
{ role: "forceReload" },
{ role: "toggleDevTools" },
{ type: "separator" },
{ role: "resetZoom" },
{ role: "zoomIn" },
{ role: "zoomOut" },
{ type: "separator" },
{ role: "togglefullscreen" },
{ type: "separator" },
]
},
{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: [
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.zoomin'),
role: 'zoomIn',
accelerator: utils.getStoreValue("general.keybindings.zoomn").join('+')
role: "minimize",
label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.minimize"),
},
{ type: "separator" },
...(this.isMac
? [
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.zoomout'),
role: 'zoomOut',
accelerator: utils.getStoreValue("general.keybindings.zoomt").join('+')
label: "Show",
click: () => utils.getWindow().show(),
},
{ role: "zoom" },
{ type: "separator" },
{ role: "front" },
{ role: "close" },
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.zoomreset'),
role: 'resetZoom',
accelerator: utils.getStoreValue("general.keybindings.zoomrst").join('+')
}
label: "Edit",
submenu: [{ role: "undo" }, { role: "redo" }, { type: "separator" }, { role: "cut" }, { role: "copy" }, { role: "paste" }],
},
{ type: "separator" },
]
},
{type: 'separator'},
: []),
...(this.isNotMac
? [
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.fullscreen'),
accelerator: 'Control+Enter',
role: 'togglefullscreen'
},
{type: 'separator'},
label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.zoom"),
submenu: [
{
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"), "term.zoomin"),
role: "zoomIn",
accelerator: utils.getStoreValue("general.keybindings.zoomn").join("+"),
},
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.forcereload'),
accelerator: 'Control+Shift+R',
role: 'forceReload'
label: utils.getLocale(utils.getStoreValue("general.language"), "term.zoomout"),
role: "zoomOut",
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: [
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.playpause'),
accelerator: 'Space',
click: () => utils.getWindow().webContents.executeJavaScript(`app.SpacePause()`)
label: utils.getLocale(utils.getStoreValue("general.language"), "term.playpause"),
accelerator: "Space",
click: () => utils.getWindow().webContents.executeJavaScript(`app.SpacePause()`),
},
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.next'),
accelerator: 'CommandOrControl+Right',
click: () => utils.getWindow().webContents.executeJavaScript(`MusicKitInterop.next()`)
label: utils.getLocale(utils.getStoreValue("general.language"), "term.next"),
accelerator: "CommandOrControl+Right",
click: () => utils.getWindow().webContents.executeJavaScript(`MusicKitInterop.next()`),
},
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.previous'),
accelerator: 'CommandOrControl+Left',
click: () => utils.getWindow().webContents.executeJavaScript(`MusicKitInterop.previous()`)
label: utils.getLocale(utils.getStoreValue("general.language"), "term.previous"),
accelerator: "CommandOrControl+Left",
click: () => utils.getWindow().webContents.executeJavaScript(`MusicKitInterop.previous()`),
},
{type: 'separator'},
{ type: "separator" },
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.volumeup'),
accelerator: 'CommandOrControl+Up',
click: () => utils.getWindow().webContents.executeJavaScript(`app.volumeUp()`)
label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.volumeup"),
accelerator: "CommandOrControl+Up",
click: () => utils.getWindow().webContents.executeJavaScript(`app.volumeUp()`),
},
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.volumedown'),
accelerator: 'CommandOrControl+Down',
click: () => utils.getWindow().webContents.executeJavaScript(`app.volumeDown()`)
label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.volumedown"),
accelerator: "CommandOrControl+Down",
click: () => utils.getWindow().webContents.executeJavaScript(`app.volumeDown()`),
},
{type: 'separator'},
{ type: "separator" },
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.cast2'),
accelerator: utils.getStoreValue("general.keybindings.castToDevices").join('+'),
click: () => utils.getWindow().webContents.executeJavaScript(`app.modals.castMenu = true`)
label: utils.getLocale(utils.getStoreValue("general.language"), "term.cast2"),
accelerator: utils.getStoreValue("general.keybindings.castToDevices").join("+"),
click: () => utils.getWindow().webContents.executeJavaScript(`app.modals.castMenu = true`),
},
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.webremote'),
accelerator: utils.getStoreValue("general.keybindings.webRemote").join('+'),
sublabel: 'Opens in external window',
click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('remote-pair')`)
label: utils.getLocale(utils.getStoreValue("general.language"), "term.webremote"),
accelerator: utils.getStoreValue("general.keybindings.webRemote").join("+"),
sublabel: "Opens in external window",
click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('remote-pair')`),
},
{type: 'separator'},
{ type: "separator" },
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.audioSettings'),
accelerator: utils.getStoreValue("general.keybindings.audioSettings").join('+'),
click: () => utils.getWindow().webContents.executeJavaScript(`app.modals.audioSettings = true`)
label: utils.getLocale(utils.getStoreValue("general.language"), "term.audioSettings"),
accelerator: utils.getStoreValue("general.keybindings.audioSettings").join("+"),
click: () => utils.getWindow().webContents.executeJavaScript(`app.modals.audioSettings = true`),
},
{type: 'separator'},
{ type: "separator" },
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.plugins'),
accelerator: utils.getStoreValue("general.keybindings.pluginMenu").join('+'),
click: () => utils.getWindow().webContents.executeJavaScript(`app.modals.pluginMenu = true`)
}
]
label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.plugins"),
accelerator: utils.getStoreValue("general.keybindings.pluginMenu").join("+"),
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: [
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.accountSettings'),
click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('apple-account-settings')`)
label: utils.getLocale(utils.getStoreValue("general.language"), "term.accountSettings"),
click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('apple-account-settings')`),
},
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.signout'),
click: () => utils.getWindow().webContents.executeJavaScript(`app.unauthorize()`)
}
]
label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.signout"),
click: () => utils.getWindow().webContents.executeJavaScript(`app.unauthorize()`),
},
],
},
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.support'),
role: 'help',
label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.support"),
role: "help",
submenu: [
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.discord'),
click: () => shell.openExternal("https://discord.gg/AppleMusic").catch(console.error)
label: utils.getLocale(utils.getStoreValue("general.language"), "term.discord"),
click: () => shell.openExternal("https://discord.gg/AppleMusic").catch(console.error),
},
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'term.github'),
click: () => shell.openExternal("https://github.com/ciderapp/Cider/wiki/Troubleshooting").catch(console.error)
label: utils.getLocale(utils.getStoreValue("general.language"), "term.github"),
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: [
{
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)
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),
},
{
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)
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),
},
{
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)
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),
},
]
],
},
{type: 'separator'},
{ type: "separator" },
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.license'),
click: () => shell.openExternal("https://github.com/ciderapp/Cider/blob/main/LICENSE").catch(console.error)
label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.license"),
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'),
accelerator: utils.getStoreValue("general.keybindings.openDeveloperTools").join('+'),
click: () => utils.getWindow().webContents.openDevTools()
label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.toggledevtools"),
accelerator: utils.getStoreValue("general.keybindings.openDeveloperTools").join("+"),
click: () => utils.getWindow().webContents.openDevTools(),
},
{
label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.conf'),
click: () => utils.getStoreInstance().openInEditor()
}
]
}
label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.conf"),
click: () => utils.getStoreInstance().openInEditor(),
},
],
},
];
/*******************************************************************************************
@ -326,7 +314,7 @@ export default class Thumbar {
*/
onReady(_win: Electron.BrowserWindow): void {
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
* @param attributes Music Attributes (attributes.status = current state)
*/
onPlaybackStateDidChange(attributes: object): void {
}
onPlaybackStateDidChange(attributes: object): void {}
/**
* Runs on song change
* @param attributes Music Attributes
*/
onNowPlayingItemDidChange(attributes: object): void {
}
onNowPlayingItemDidChange(attributes: object): void {}
}

View file

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

View file

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

View file

@ -1,17 +1,15 @@
import * as electron from 'electron';
import * as os from 'os';
import * as fs from 'fs';
import { join, resolve } from 'path';
import * as CiderReceiver from '../base/castreceiver';
import fetch from 'electron-fetch';
import * as electron from "electron";
import * as os from "os";
import * as fs from "fs";
import { join, resolve } from "path";
import * as CiderReceiver from "../base/castreceiver";
import fetch from "electron-fetch";
import { Stream } from "stream";
import {spawn} from 'child_process';
import {Worker} from 'worker_threads';
import { Blob } from 'buffer';
import { spawn } from "child_process";
import { Worker } from "worker_threads";
import { Blob } from "buffer";
export default class RAOP {
/**
* Private variables for interaction in plugins
*/
@ -26,7 +24,7 @@ export default class RAOP {
private airtunes: any;
private device: any;
private mdns = require('mdns-js');
private mdns = require("mdns-js");
private ok: any = 1;
private devices: any = [];
private castDevices: any = [];
@ -35,7 +33,6 @@ export default class RAOP {
private ffmpeg: any = null;
private worker: any = null;
private processNode = `
import {parentPort, workerData} from "worker_threads";
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) {
// 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({
name: (host ?? "Unknown").replace(".local", ""),
host: addresses ? addresses[0] : '',
host: addresses ? addresses[0] : "",
port: port,
addresses: addresses,
txt: text,
airplay2: airplay2
airplay2: airplay2,
});
if (this.devices.indexOf(host) === -1) {
this.devices.push(host);
@ -115,41 +116,40 @@ export default class RAOP {
/**
* Base Plugin Details (Eventually implemented into a GUI in settings)
*/
public name: string = 'RAOP';
public description: string = 'RAOP Plugin';
public version: string = '0.0.1';
public author: string = 'vapormusic / Cider Collective';
public name: string = "RAOP";
public description: string = "RAOP Plugin";
public version: string = "0.0.1";
public author: string = "vapormusic / Cider Collective";
/**
* Runs on plugin load (Currently run on application start)
*/
constructor(utils: { getStore: () => any; getApp: () => any; }) {
constructor(utils: { getStore: () => any; getApp: () => any }) {
this._utils = utils;
console.debug(`[Plugin][${this.name}] Loading Complete.`);
this._app = utils.getApp();
}
/**
* Runs on app ready
*/
onReady(win: any): void {
this.u = require('airtunes2');
this.u = require("airtunes2");
this._win = win;
electron.ipcMain.on('getKnownAirplayDevices', (event) => {
event.returnValue = this.castDevices
electron.ipcMain.on("getKnownAirplayDevices", (event) => {
event.returnValue = this.castDevices;
});
electron.ipcMain.on("getAirplayDevice", (event, data) => {
this.castDevices = [];
console.log("scan for airplay devices");
const browser = this.mdns.createBrowser(this.mdns.tcp('raop'));
browser.on('ready', browser.discover);
const browser = this.mdns.createBrowser(this.mdns.tcp("raop"));
browser.on("ready", browser.discover);
browser.on('update', (service: any) => {
if (service.addresses && service.fullname && (service.fullname.includes('_raop._tcp'))) {
browser.on("update", (service: any) => {
if (service.addresses && service.fullname && service.fullname.includes("_raop._tcp")) {
// console.log(service.txt)
this._win.webContents.executeJavaScript(`console.log(
"${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'));
browser2.on('ready', browser2.discover);
const browser2 = this.mdns.createBrowser(this.mdns.tcp("airplay"));
browser2.on("ready", browser2.discover);
browser2.on('update', (service: any) => {
if (service.addresses && service.fullname && (service.fullname.includes('_airplay._tcp'))) {
browser2.on("update", (service: any) => {
if (service.addresses && service.fullname && service.fullname.includes("_airplay._tcp")) {
// console.log(service.txt)
this._win.webContents.executeJavaScript(`console.log(
"${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);
// }
// });
});
electron.ipcMain.on("performAirplayPCM", (event, ipv4, ipport, sepassword, title, artist, album, artworkURL, txt, airplay2dv) => {
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.portairplay = ipport;
this.device = this.airtunes.add(ipv4, {
@ -200,63 +198,55 @@ export default class RAOP {
password: sepassword,
txt: txt,
airplay2: airplay2dv,
debug: true
debug: true,
});
// console.log('lol',txt)
this.device.on('status', (status: any) => {
console.log('device status', status);
this.device.on("status", (status: any) => {
console.log("device status", status);
if (status == "ready") {
this._win.webContents.setAudioMuted(true);
this._win.webContents.executeJavaScript(`CiderAudio.sendAudio()`).catch((err: any) => console.error(err));
}
if (status == "need_password") {
this._win.webContents.executeJavaScript(`app.setAirPlayCodeUI()`)
this._win.webContents.executeJavaScript(`app.setAirPlayCodeUI()`);
}
if (status == "pair_success") {
this._win.webContents.executeJavaScript(`app.sendAirPlaySuccess()`)
this._win.webContents.executeJavaScript(`app.sendAirPlaySuccess()`);
}
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(() => {
console.log('end');
console.log("end");
});
this.airtunes = null;
this.device = null;
this.ipairplay = '';
this.portairplay = '';
this.ipairplay = "";
this.portairplay = "";
this.ok = 1;
} else {
setTimeout(() => {
if (this.ok == 1) {
console.log(this.device.key, title ?? '', artist ?? '', album ?? '');
this.airtunes.setTrackInfo(this.device.key, title ?? '', artist?? '', album?? '');
console.log(this.device.key, title ?? "", artist ?? "", album ?? "");
this.airtunes.setTrackInfo(this.device.key, title ?? "", artist ?? "", album ?? "");
this.uploadImageAirplay(artworkURL);
console.log('done');
this.ok == 2
console.log("done");
this.ok == 2;
}
}, 1000);
}
});
}
});
electron.ipcMain.on('setAirPlayPasscode', (event, passcode) => {
electron.ipcMain.on("setAirPlayPasscode", (event, passcode) => {
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.worker == null) {
try {
@ -278,8 +268,9 @@ export default class RAOP {
console.log("bruh", error);
});
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 = spawn(this._utils.getStoreValue("advanced.ffmpegLocation"), [
@ -302,61 +293,56 @@ export default class RAOP {
this.worker.postMessage({ buffer: [leftbuffer, rightbuffer] });
}
}
});
electron.ipcMain.on('disconnectAirplay', (event) => {
electron.ipcMain.on("disconnectAirplay", (event) => {
this._win.webContents.setAudioMuted(false);
this.airtunes.stopAll(function () {
console.log('end');
console.log("end");
});
this.airtunes = null;
this.device = null;
this.ipairplay = '';
this.portairplay = '';
this.ipairplay = "";
this.portairplay = "";
this.ok = 1;
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) {
console.log(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) => {
this.uploadImageAirplay(imageurl)
})
electron.ipcMain.on("updateRPCImage", (_event, imageurl) => {
this.uploadImageAirplay(imageurl);
});
}
private uploadImageAirplay = (url: any) => {
try {
if (url != null && url != '') {
if (url != null && url != "") {
//console.log(join(this._app.getPath('userData'), 'temp.png'), url);
fetch(url)
.then(res => res.buffer())
.then((res) => res.buffer())
.then((buffer) => {
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
*/
onBeforeQuit(): void {
}
onBeforeQuit(): void {}
// /**
// * Runs on song change
@ -381,15 +367,15 @@ export default class RAOP {
*/
onPlaybackStateDidChange(attributes: any): void {
if (this.airtunes && this.device) {
let title = attributes?.name ?? '';
let artist = attributes?.artistName ?? '';
let album = attributes?.albumName ?? '';
let title = attributes?.name ?? "";
let artist = attributes?.artistName ?? "";
let album = attributes?.albumName ?? "";
let artworkURL = attributes?.artwork?.url ?? null;
console.log(this.device.key, title, artist, album);
this.airtunes.setTrackInfo(this.device.key, title, artist, album);
if (artworkURL != null){}
this.uploadImageAirplay(artworkURL.replace('{w}', '1024').replace('{h}', '1024'))
if (artworkURL != null) {
}
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)
*/
public name: string = 'Thumbnail Toolbar Plugin';
public description: string = 'Creates and managed the thumbnail toolbar buttons and their events';
public version: string = '1.0.0';
public author: string = 'Core';
public name: string = "Thumbnail Toolbar Plugin";
public description: string = "Creates and managed the thumbnail toolbar buttons and their events";
public version: string = "1.0.0";
public author: string = "Core";
/**
* Thumbnail Toolbar Assets
*/
private icons: { [key: string]: Electron.NativeImage } = {
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`)),
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`)),
}
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`)),
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`)),
};
/*******************************************************************************************
* Private Methods
@ -37,10 +37,10 @@ export default class Thumbar {
* @decorator
*/
private static windowsOnly(_target: any, _propertyKey: string, descriptor: PropertyDescriptor) {
if (process.platform !== 'win32') {
if (process.platform !== "win32") {
descriptor.value = function () {
return
}
return;
};
}
}
@ -48,39 +48,38 @@ export default class Thumbar {
* Update the thumbnail toolbar
*/
private updateButtons(attributes: any) {
console.log(attributes)
console.log(attributes);
if (!attributes) {
return
return;
}
const buttons = [
{
tooltip: 'Previous',
tooltip: "Previous",
icon: this.icons.previous,
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,
click() {
utils.playback.playPause()
}
utils.playback.playPause();
},
},
{
tooltip: 'Next',
tooltip: "Next",
icon: this.icons.next,
click() {
utils.playback.next()
}
}
utils.playback.next();
},
},
];
if (!attributes.playParams || attributes.playParams.id === 'no-id-found') {
this._win.setThumbarButtons([])
if (!attributes.playParams || attributes.playParams.id === "no-id-found") {
this._win.setThumbarButtons([]);
} else {
this._win.setThumbarButtons(buttons);
}
@ -93,7 +92,7 @@ export default class Thumbar {
/**
* Runs on plugin load (Currently run on application start)
*/
constructor(a: { getApp: () => any; }) {
constructor(a: { getApp: () => any }) {
this._app = utils.getApp();
console.debug(`[Plugin][${this.name}] Loading Complete.`);
}
@ -121,7 +120,7 @@ export default class Thumbar {
*/
@Thumbar.windowsOnly
onPlaybackStateDidChange(attributes: object): void {
this.updateButtons(attributes)
this.updateButtons(attributes);
}
/**
@ -130,7 +129,6 @@ export default class Thumbar {
*/
@Thumbar.windowsOnly
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.
@ -6,7 +6,7 @@ import * as WebSocket from 'ws';
* @param {Number} length
* @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
@ -20,21 +20,21 @@ const convertTimeToString = (timeInSeconds: number) => {
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);
}
};
export default class WebNowPlaying {
/**
* Base Plugin Details (Eventually implemented into a GUI in settings)
*/
public name: string = 'WebNowPlaying';
public description: string = 'Song info and playback control for the Rainmeter WebNowPlaying plugin.';
public version: string = '1.0.1';
public author: string = 'Zennn <me@jozen.blue>';
public name: string = "WebNowPlaying";
public description: string = "Song info and playback control for the Rainmeter WebNowPlaying plugin.";
public version: string = "1.0.1";
public author: string = "Zennn <me@jozen.blue>";
private _win: any;
private ws?: WebSocket;
private wsapiConn?: WebSocket;
private playerName: string = 'Cider';
private playerName: string = "Cider";
constructor() {
console.debug(`[Plugin][${this.name}] Loading Complete.`);
@ -46,7 +46,7 @@ export default class WebNowPlaying {
* @decorator
*/
private static windowsOnly(_target: any, _propertyKey: string, descriptor: PropertyDescriptor) {
if (process.platform !== 'win32') {
if (process.platform !== "win32") {
descriptor.value = () => void 0;
}
}
@ -54,39 +54,39 @@ export default class WebNowPlaying {
private sendSongInfo(attributes: any) {
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) => {
try {
let value: any = '';
let value: any = "";
switch (field) {
case 'STATE':
case "STATE":
value = attributes.status ? 1 : 2;
break;
case 'TITLE':
case "TITLE":
value = attributes.name;
break;
case 'ARTIST':
case "ARTIST":
value = attributes.artistName;
break;
case 'ALBUM':
case "ALBUM":
value = attributes.albumName;
break;
case 'COVER':
value = attributes.artwork.url.replace('{w}', attributes.artwork.width).replace('{h}', attributes.artwork.height);
case "COVER":
value = attributes.artwork.url.replace("{w}", attributes.artwork.width).replace("{h}", attributes.artwork.height);
break;
case 'DURATION':
case "DURATION":
value = convertTimeToString(attributes.durationInMillis / 1000);
break;
case 'POSITION':
case "POSITION":
value = convertTimeToString((attributes.durationInMillis - attributes.remainingTime) / 1000);
break;
case 'VOLUME':
case "VOLUME":
value = attributes.volume * 100;
break;
case 'REPEAT':
case "REPEAT":
value = attributes.repeatMode;
break;
case 'SHUFFLE':
case "SHUFFLE":
value = attributes.shuffleMode;
break;
}
@ -104,42 +104,42 @@ export default class WebNowPlaying {
if (!evt.data) return;
const data = <string>evt.data;
let value: string = '';
let value: string = "";
if (data.split(/ (.+)/).length > 1) {
value = data.split(/ (.+)/)[1];
}
const eventName = data.split(' ')[0].toLowerCase();
const eventName = data.split(" ")[0].toLowerCase();
try {
switch (eventName) {
case 'playpause':
this._win.webContents.executeJavaScript('MusicKitInterop.playPause()').catch(console.error);
case "playpause":
this._win.webContents.executeJavaScript("MusicKitInterop.playPause()").catch(console.error);
break;
case 'next':
this._win.webContents.executeJavaScript('MusicKitInterop.next()').catch(console.error);
case "next":
this._win.webContents.executeJavaScript("MusicKitInterop.next()").catch(console.error);
break;
case 'previous':
this._win.webContents.executeJavaScript('MusicKitInterop.previous()').catch(console.error);
case "previous":
this._win.webContents.executeJavaScript("MusicKitInterop.previous()").catch(console.error);
break;
case 'setposition':
case "setposition":
this._win.webContents.executeJavaScript(`MusicKit.getInstance().seekToTime(${parseFloat(value)})`);
break;
case 'setvolume':
case "setvolume":
this._win.webContents.executeJavaScript(`MusicKit.getInstance().volume = ${parseFloat(value) / 100}`);
break;
case 'repeat':
this._win.webContents.executeJavaScript('wsapi.toggleRepeat()').catch(console.error);
case "repeat":
this._win.webContents.executeJavaScript("wsapi.toggleRepeat()").catch(console.error);
break;
case 'shuffle':
this._win.webContents.executeJavaScript('wsapi.toggleShuffle()').catch(console.error);
case "shuffle":
this._win.webContents.executeJavaScript("wsapi.toggleShuffle()").catch(console.error);
break;
case 'togglethumbsup':
case "togglethumbsup":
// not implemented
break;
case 'togglethumbsdown':
case "togglethumbsdown":
// not implemented
break;
case 'rating':
case "rating":
// not implemented
break;
}
@ -162,10 +162,10 @@ export default class WebNowPlaying {
// Connect to Rainmeter plugin and retry on disconnect.
const init = () => {
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;
this.ws.onopen = () => {
console.info('[WebNowPlaying] Connected to Rainmeter');
console.info("[WebNowPlaying] Connected to Rainmeter");
this.ws?.send(`PLAYER:${this.playerName}`);
};
@ -189,15 +189,15 @@ export default class WebNowPlaying {
// Connect to wsapi. Only used to update progress.
try {
this.wsapiConn = new WebSocket('ws://127.0.0.1:26369/');
this.wsapiConn = new WebSocket("ws://127.0.0.1:26369/");
this.wsapiConn.onopen = () => {
console.info('[WebNowPlaying] Connected to wsapi');
console.info("[WebNowPlaying] Connected to wsapi");
};
this.wsapiConn.onmessage = (evt: WebSocket.MessageEvent) => {
const response = JSON.parse(<string>evt.data);
if (response.type === 'playbackStateUpdate') {
if (response.type === "playbackStateUpdate") {
this.sendSongInfo(response.data);
}
};
@ -214,7 +214,7 @@ export default class WebNowPlaying {
@WebNowPlaying.windowsOnly
public onBeforeQuit() {
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.close();
}

View file

@ -1,12 +1,12 @@
import * as PouchDB from 'pouchdb-node';
import {join} from 'path';
import * as PouchDB from "pouchdb-node";
import { join } from "path";
import { app } from "electron";
PouchDB.plugin(require('pouchdb-upsert'));
PouchDB.plugin(require("pouchdb-upsert"));
export class ProviderDB {
public static db: any = null
public static db: any = null;
static init() {
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 * as path from 'path';
const { readdir } = require('fs').promises;
import { utils } from '../../base/utils';
import * as mm from 'music-metadata';
import {Md5} from 'ts-md5/dist/md5';
import * as path from "path";
const { readdir } = require("fs").promises;
import { utils } from "../../base/utils";
import * as mm from "music-metadata";
import { Md5 } from "ts-md5/dist/md5";
import e from "express";
import { EventEmitter } from 'events';
import { EventEmitter } from "events";
export class LocalFiles {
static localSongs: any = [];
@ -14,37 +14,40 @@ export class LocalFiles {
static eventEmitter = new EventEmitter();
static getDataType(item_id: String | any) {
if ((item_id ?? ('')).startsWith('ciderlocalart'))
return 'artwork'
else if ((item_id ?? ('')).startsWith('ciderlocal'))
return 'track'
if ((item_id ?? "").startsWith("ciderlocalart")) return "artwork";
else if ((item_id ?? "").startsWith("ciderlocal")) return "track";
}
static async sendOldLibrary() {
ProviderDB.init()
let rows = (await ProviderDB.db.allDocs({include_docs: true,
attachments: true})).rows.map((item: any)=>{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"})
ProviderDB.init();
let rows = (await ProviderDB.db.allDocs({ include_docs: true, attachments: true })).rows.map((item: any) => {
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";
});
this.localSongs = tracks;
this.localSongsArts = arts;
return tracks;
}
static async scanLibrary() {
ProviderDB.init()
let folders = utils.getStoreValue("libraryPrefs.localPaths")
if (folders == null || folders.length == null || folders.length == 0) folders = []
let files: any[] = []
ProviderDB.init();
let folders = utils.getStoreValue("libraryPrefs.localPaths");
if (folders == null || folders.length == null || folders.length == 0) folders = [];
let files: any[] = [];
for (var folder of folders) {
// 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 audiofiles = files.filter(f => supporttedformats.includes(f.substring(f.lastIndexOf('.') + 1)));
let metadatalist = []
let metadatalistart = []
let supporttedformats = ["mp3", "aac", "webm", "flac", "m4a", "ogg", "wav", "opus"];
let audiofiles = files.filter((f) => supporttedformats.includes(f.substring(f.lastIndexOf(".") + 1)));
let metadatalist = [];
let metadatalistart = [];
let numid = 0;
for (var audio of audiofiles) {
try {
@ -52,46 +55,44 @@ export class LocalFiles {
let lochash = Md5.hashStr(audio) ?? numid;
if (metadata != null) {
let form = {
"id": "ciderlocal" + lochash,
"_id": "ciderlocal" + lochash,
"type": "podcast-episodes",
"href": audio,
"attributes": {
"artwork": {
"width": 3000,
"height": 3000,
"url": "/ciderlocalart/" + "ciderlocal" + lochash,
id: "ciderlocal" + lochash,
_id: "ciderlocal" + lochash,
type: "podcast-episodes",
href: audio,
attributes: {
artwork: {
width: 3000,
height: 3000,
url: "/ciderlocalart/" + "ciderlocal" + lochash,
},
"topics": [],
"url": "",
"subscribable": true,
"mediaKind": "audio",
"genreNames": [
""
],
topics: [],
url: "",
subscribable: true,
mediaKind: "audio",
genreNames: [""],
// "playParams": {
// "id": "ciderlocal" + numid,
// "kind": "podcast",
// "isLibrary": true,
// "reporting": false },
"trackNumber": metadata.common.track?.no ?? 0,
"discNumber": metadata.common.disk?.no ?? 0,
"name": metadata.common.title ?? audio.substring(audio.lastIndexOf('\\') + 1),
"albumName": metadata.common.album,
"artistName": metadata.common.artist,
"copyright": metadata.common.copyright ?? "",
"assetUrl": "file:///" + audio,
"contentAdvisory": "",
"releaseDateTime": `${metadata?.common?.year ?? '2022'}-05-13T00:23:00Z`,
"durationInMillis": Math.floor((metadata.format.duration ?? 0) * 1000),
"bitrate": Math.floor((metadata.format?.bitrate ?? 0) / 1000),
"offers": [
trackNumber: metadata.common.track?.no ?? 0,
discNumber: metadata.common.disk?.no ?? 0,
name: metadata.common.title ?? audio.substring(audio.lastIndexOf("\\") + 1),
albumName: metadata.common.album,
artistName: metadata.common.artist,
copyright: metadata.common.copyright ?? "",
assetUrl: "file:///" + audio,
contentAdvisory: "",
releaseDateTime: `${metadata?.common?.year ?? "2022"}-05-13T00:23:00Z`,
durationInMillis: Math.floor((metadata.format.duration ?? 0) * 1000),
bitrate: Math.floor((metadata.format?.bitrate ?? 0) / 1000),
offers: [
{
"kind": "get",
"type": "STDQ"
}
kind: "get",
type: "STDQ",
},
],
"contentRating": "clean"
contentRating: "clean",
},
flavor: Math.floor((metadata.format?.bitrate ?? 0) / 1000),
localFilesMetadata: {
@ -104,18 +105,22 @@ export class LocalFiles {
let art = {
id: "ciderlocal" + lochash,
_id: "ciderlocalart" + lochash,
url: metadata.common.picture != undefined ? metadata.common.picture[0].data.toString('base64') : "",
}
metadatalistart.push(art)
url: metadata.common.picture != undefined ? metadata.common.picture[0].data.toString("base64") : "",
};
metadatalistart.push(art);
numid += 1;
ProviderDB.db.putIfNotExists(form)
ProviderDB.db.putIfNotExists(art)
metadatalist.push(form)
ProviderDB.db.putIfNotExists(form);
ProviderDB.db.putIfNotExists(art);
metadatalist.push(form);
if (this.localSongs.length === 0 && numid % 10 === 0) { // send updated chunks only if there is no previous database
this.eventEmitter.emit('newtracks', metadatalist)}
if (this.localSongs.length === 0 && numid % 10 === 0) {
// 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);
this.localSongs = metadatalist;
@ -124,40 +129,53 @@ export class LocalFiles {
}
static async getFiles(dir: any) {
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);
return dirent.isDirectory() ? this.getFiles(res) : res;
}));
})
);
return Array.prototype.concat(...files);
}
static async cleanUpDB() {
let folders = utils.getStoreValue("libraryPrefs.localPaths")
let rows = (await ProviderDB.db.allDocs({include_docs: true,
attachments: true})).rows.map((item: any)=>{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 folders = utils.getStoreValue("libraryPrefs.localPaths");
let rows = (await ProviderDB.db.allDocs({ include_docs: true, attachments: true })).rows.map((item: any) => {
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;
});
for (let hash of hashs) {
try {
ProviderDB.db.get(hash).then(function (doc: any) {
return ProviderDB.db.remove(doc);
});} catch(e){}
});
} catch (e) {}
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);
});} catch(e){}
});
} catch (e) {}
}
}
static setupHandlers() {
const app = utils.getExpress()
console.log("Setting up handlers for local files")
const app = utils.getExpress();
console.log("Setting up handlers for local files");
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)
let data = {
data:
LocalFiles.localSongs.filter((f: any) => audio.split(',').includes(f.id))
data: LocalFiles.localSongs.filter((f: any) => audio.split(",").includes(f.id)),
};
res.send(data);
});
@ -166,15 +184,14 @@ export class LocalFiles {
const audio = req.params.songs;
// metadata.common.picture[0].data.toString('base64')
res.setHeader('Cache-Control', 'public, max-age=31536000');
res.setHeader('Expires', new Date(Date.now() + 31536000000).toUTCString());
res.setHeader('Content-Type', 'image/jpeg');
res.setHeader("Cache-Control", "public, max-age=31536000");
res.setHeader("Expires", new Date(Date.now() + 31536000000).toUTCString());
res.setHeader("Content-Type", "image/jpeg");
let data =
LocalFiles.localSongsArts.filter((f: any) => f.id == audio);
res.status(200).send(Buffer.from(data[0]?.url, 'base64'));
let data = LocalFiles.localSongsArts.filter((f: any) => f.id == audio);
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;
console.info('Loaded Preload')
global.ipcRenderer = require("electron").ipcRenderer;
console.info("Loaded Preload");
let cache = { playParams: { id: 0 }, status: null, remainingTime: 0 },
playbackCache = { status: null, time: Date.now() };
@ -8,44 +8,44 @@ const MusicKitInterop = {
init: function () {
/* MusicKit.Events.playbackStateDidChange */
MusicKit.getInstance().addEventListener(MusicKit.Events.playbackStateDidChange, () => {
const attributes = MusicKitInterop.getAttributes()
const attributes = MusicKitInterop.getAttributes();
if (MusicKitInterop.filterTrack(attributes, true, false)) {
global.ipcRenderer.send('playbackStateDidChange', attributes)
global.ipcRenderer.send('wsapi-updatePlaybackState', attributes);
global.ipcRenderer.send("playbackStateDidChange", attributes);
global.ipcRenderer.send("wsapi-updatePlaybackState", attributes);
}
});
/* MusicKit.Events.playbackProgressDidChange */
MusicKit.getInstance().addEventListener(MusicKit.Events.playbackProgressDidChange, async () => {
const attributes = MusicKitInterop.getAttributes()
const attributes = MusicKitInterop.getAttributes();
// wsapi call
ipcRenderer.send('wsapi-updatePlaybackState', attributes);
ipcRenderer.send("wsapi-updatePlaybackState", attributes);
// lastfm call
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;
ipcRenderer.send('lastfm:scrobbleTrack', attributes);
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;
ipcRenderer.send("lastfm:scrobbleTrack", attributes);
}
});
/* 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.getInstance().addEventListener(MusicKit.Events.nowPlayingItemDidChange, async () => {
console.debug('[cider:preload] nowPlayingItemDidChange')
const attributes = MusicKitInterop.getAttributes()
attributes.primaryArtist = (app.cfg.connectivity.lastfm.enabled && app.cfg.connectivity.lastfm.remove_featured) ? await this.fetchPrimaryArtist(attributes.artistName) : attributes.artistName;
console.debug("[cider:preload] nowPlayingItemDidChange");
const attributes = MusicKitInterop.getAttributes();
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)) {
global.ipcRenderer.send('nowPlayingItemDidChange', attributes);
} else if (attributes.name !== 'no-title-found' && attributes.playParams.id !== "no-id-found") {
global.ipcRenderer.send('lastfm:nowPlayingChange', attributes);
global.ipcRenderer.send("nowPlayingItemDidChange", attributes);
} else if (attributes.name !== "no-title-found" && attributes.playParams.id !== "no-id-found") {
global.ipcRenderer.send("lastfm:nowPlayingChange", attributes);
}
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) {
@ -56,7 +56,7 @@ const MusicKitInterop = {
/* 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 */
@ -66,12 +66,12 @@ const MusicKitInterop = {
/* 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.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) {
if (app.mk.nowPlayingItem?.relationships?.artists) {
const artist = await app.mk.api.artist(app.mk.nowPlayingItem.relationships.artists.data[0].id)
return artist.attributes.name
const artist = await app.mk.api.artist(app.mk.nowPlayingItem.relationships.artists.data[0].id);
return artist.attributes.name;
} else {
return artist
return artist;
}
},
getAttributes: function () {
const mk = MusicKit.getInstance()
const mk = MusicKit.getInstance();
const nowPlayingItem = mk.nowPlayingItem;
const isPlayingExport = mk.isPlaying;
const remainingTimeExport = mk.currentPlaybackTimeRemaining;
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.name = attributes?.name ?? 'no-title-found';
attributes.artwork = attributes?.artwork ?? {url: ''};
attributes.artwork.url = (attributes?.artwork?.url ?? '').replace(`{f}`, "png");
attributes.playParams = attributes?.playParams ?? {id: 'no-id-found'};
attributes.playParams.id = attributes?.playParams?.id ?? 'no-id-found';
attributes.name = attributes?.name ?? "no-title-found";
attributes.artwork = attributes?.artwork ?? { url: "" };
attributes.artwork.url = (attributes?.artwork?.url ?? "").replace(`{f}`, "png");
attributes.playParams = attributes?.playParams ?? { id: "no-id-found" };
attributes.playParams.id = attributes?.playParams?.id ?? "no-id-found";
attributes.url = {
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')}`
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"}`,
};
if (attributes.playParams.id === "no-id-found") {
attributes.playParams.id = nowPlayingItem?.id ?? "no-id-found";
}
if (attributes.playParams.id === 'no-id-found') {
attributes.playParams.id = nowPlayingItem?.id ?? 'no-id-found';
}
attributes.albumName = attributes?.albumName ?? '';
attributes.artistName = attributes?.artistName ?? '';
attributes.albumName = attributes?.albumName ?? "";
attributes.artistName = attributes?.artistName ?? "";
attributes.genreNames = attributes?.genreNames ?? [];
attributes.remainingTime = remainingTimeExport
? remainingTimeExport * 1000
: 0;
attributes.remainingTime = remainingTimeExport ? remainingTimeExport * 1000 : 0;
attributes.durationInMillis = attributes?.durationInMillis ?? 0;
attributes.currentPlaybackTime = mk?.currentPlaybackTime ?? 0;
attributes.currentPlaybackProgress = currentPlaybackProgress ?? 0;
attributes.startTime = Date.now();
attributes.endTime = Math.round(
attributes?.playParams?.id === cache.playParams.id
? Date.now() + attributes?.remainingTime
: attributes?.startTime + attributes?.durationInMillis
);
attributes.endTime = Math.round(attributes?.playParams?.id === cache.playParams.id ? Date.now() + attributes?.remainingTime : attributes?.startTime + attributes?.durationInMillis);
return attributes;
},
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;
} else if (mediaCheck && a.playParams.id === cache.playParams.id) {
return;
} else if (playbackCheck && a.status === playbackCache.status) {
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;
}
cache = a;
@ -167,19 +162,21 @@ const MusicKitInterop = {
// } catch (e) { }
// if (MusicKit.getInstance().queue.nextPlayableItemIndex != -1 && MusicKit.getInstance().queue.nextPlayableItemIndex != null)
// 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: () => {
// if (MusicKit.getInstance().queue.previousPlayableItemIndex != -1 && MusicKit.getInstance().queue.previousPlayableItemIndex != null)
// 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;
});

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -44,7 +44,7 @@ body[platform="darwin"] {
left: 0;
right: 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;
content: " ";
z-index: 999999;
@ -67,7 +67,7 @@ body[platform="darwin"] {
.settings-window.maxed {
.tabs > .col-auto {
transition: padding-top .3s linear;
transition: padding-top 0.3s linear;
padding-top: var(--chromeHeight1);
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,32 +1,31 @@
const CiderFrontAPI = {
Objects: {
MenuEntry: function () {
this.id = ""
this.name = ""
this.onClick = () => {
}
}
this.id = "";
this.name = "";
this.onClick = () => {};
},
},
AddMenuEntry(entry) {
app.pluginMenuEntries.push(entry)
app.pluginInstalled = true
app.pluginMenuEntries.push(entry);
app.pluginInstalled = true;
},
StyleSheets: {
Add(href) {
console.log("Adding stylesheet: " + href)
let id = uuidv4()
let link = document.createElement("link")
link.rel = "stylesheet/less"
link.type = "text/css"
link.href = href
link.setAttribute("css-id", id)
console.log("Adding stylesheet: " + href);
let id = uuidv4();
let link = document.createElement("link");
link.rel = "stylesheet/less";
link.type = "text/css";
link.href = href;
link.setAttribute("css-id", id);
// insert the link before document.querySelector("#userTheme") in head
document.querySelector("head").insertBefore(link, document.querySelector("#userTheme"))
less.registerStylesheetsImmediately()
less.refresh(true, true, true)
return link
}
}
}
document.querySelector("head").insertBefore(link, document.querySelector("#userTheme"));
less.registerStylesheetsImmediately();
less.refresh(true, true, true);
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", {
template: html`
<button class="app-sidebar-item"
:class="$root.getSidebarItemClass(page)" @click="$root.setWindowHash(page)">
<button class="app-sidebar-item" :class="$root.getSidebarItemClass(page)" @click="$root.setWindowHash(page)">
<svg-icon :url="svgIconData" :name="'sidebar-' + svgIconName" v-if="svgIconData != ''" />
<span class="sidebar-item-text">{{ name }}</span>
</button>
@ -24,7 +23,7 @@ export const sidebarLibraryItem = Vue.component("sidebar-library-item", {
},
svgIconName: {
type: String,
required: false
required: false,
},
cdClick: {
type: Function,
@ -43,4 +42,4 @@ export const sidebarLibraryItem = Vue.component("sidebar-library-item", {
}
},
methods: {},
})
});

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,56 +1,44 @@
const MusicKitTools = {
async v3Backend({
route = "", getBody = {}, options = {}
}) {
return await (await ipcRenderer.invoke("mkv3", {
async v3Backend({ route = "", getBody = {}, options = {} }) {
return await await ipcRenderer.invoke("mkv3", {
token: MusicKit.getInstance().developerToken,
route: route,
mediaToken: MusicKit.getInstance().musicUserToken,
GETBody: getBody
}))
GETBody: getBody,
});
},
async v3Continuous({
href,
options = {},
reqOptions = {},
onProgress = () => {
},
onError = () => {
},
onSuccess = () => {
}
} = {}) {
let returnData = []
async v3Continuous({ href, options = {}, reqOptions = {}, onProgress = () => {}, onError = () => {}, onSuccess = () => {} } = {}) {
let returnData = [];
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) {
onProgress({
response: response,
total: returnData.length
})
total: returnData.length,
});
try {
await sendReq(response.data.next, options)
await sendReq(response.data.next, options);
} catch (e) {
await sendReq(response.data.next, options)
await sendReq(response.data.next, options);
}
}
}
await sendReq(href, options)
onSuccess(returnData)
return returnData
await sendReq(href, options);
onSuccess(returnData);
return returnData;
},
getHeader() {
return new Headers({
Authorization: 'Bearer ' + MusicKit.getInstance().developerToken,
Accept: 'application/json',
'Content-Type': 'application/json',
'Music-User-Token': '' + MusicKit.getInstance().musicUserToken
Authorization: "Bearer " + MusicKit.getInstance().developerToken,
Accept: "application/json",
"Content-Type": "application/json",
"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,
nextUrl: null,
items: [],
size: "normal"
size: "normal",
},
settings: {
currentTabIndex: 0,
fullscreen: false
}
fullscreen: false,
},
},
artwork: {
playerLCD: ""
}
playerLCD: "",
},
},
mutations: {
resetRecentlyAdded(state) {
@ -30,9 +30,9 @@ const store = new Vuex.Store({
state.pageState.recentlyAdded.items = [];
},
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 = "") {
let decoded = atob(encoded);
let json = JSON.parse(decoded);
console.log(json)
let response = await (await MusicKit.getInstance().api.v3.music(json.route, json.body, json.options))
let ret = response.data
return JSON.stringify(ret)
console.log(json);
let response = await await MusicKit.getInstance().api.v3.music(json.route, json.body, json.options);
let ret = response.data;
return JSON.stringify(ret);
},
search(term, limit) {
MusicKit.getInstance().api.search(term, {limit: limit, types: 'songs,artists,albums,playlists'}).then((results)=>{
ipcRenderer.send('wsapi-returnSearch', JSON.stringify(results))
MusicKit.getInstance()
.api.search(term, {
limit: limit,
types: "songs,artists,albums,playlists",
})
.then((results) => {
ipcRenderer.send("wsapi-returnSearch", JSON.stringify(results));
});
},
searchLibrary(term, limit) {
MusicKit.getInstance().api.library.search(term, {limit: limit, types: 'library-songs,library-artists,library-albums,library-playlists'}).then((results)=>{
ipcRenderer.send('wsapi-returnSearchLibrary', JSON.stringify(results))
MusicKit.getInstance()
.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 () {
const mk = MusicKit.getInstance();
const nowPlayingItem = mk.nowPlayingItem;
const isPlayingExport = mk.isPlaying;
const remainingTimeExport = mk.currentPlaybackTimeRemaining;
const attributes = (nowPlayingItem != null ? nowPlayingItem.attributes : {});
const attributes = nowPlayingItem != null ? nowPlayingItem.attributes : {};
attributes.status = isPlayingExport ? isPlayingExport : false;
attributes.name = attributes.name ? attributes.name : 'No Title Found';
attributes.artwork = attributes.artwork ? attributes.artwork : {url: ''};
attributes.artwork.url = attributes.artwork.url ? attributes.artwork.url : '';
attributes.playParams = attributes.playParams ? 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.artistName = attributes.artistName ? attributes.artistName : '';
attributes.name = attributes.name ? attributes.name : "No Title Found";
attributes.artwork = attributes.artwork ? attributes.artwork : { url: "" };
attributes.artwork.url = attributes.artwork.url ? attributes.artwork.url : "";
attributes.playParams = attributes.playParams ? 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.artistName = attributes.artistName ? attributes.artistName : "";
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.startTime = Date.now();
attributes.endTime = attributes.endTime ? attributes.endTime : Date.now();
@ -43,152 +53,178 @@ const wsapi = {
attributes.shuffleMode = mk.shuffleMode;
attributes.repeatMode = mk.repeatMode;
attributes.autoplayEnabled = mk.autoplayEnabled;
return attributes
return attributes;
},
moveQueueItem(oldPosition, newPosition) {
MusicKit.getInstance().queue._queueItems.splice(newPosition,0,MusicKit.getInstance().queue._queueItems.splice(oldPosition,1)[0])
MusicKit.getInstance().queue._reindex()
MusicKit.getInstance().queue._queueItems.splice(newPosition, 0, MusicKit.getInstance().queue._queueItems.splice(oldPosition, 1)[0]);
MusicKit.getInstance().queue._reindex();
},
setAutoplay(value) {
MusicKit.getInstance().autoplayEnabled = value
MusicKit.getInstance().autoplayEnabled = value;
},
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) {
if (library) {
MusicKit.getInstance().api.library[method](id, params).then((results)=>{
ipcRenderer.send('wsapi-returnMusicKitApi', JSON.stringify(results), method)
})
MusicKit.getInstance()
.api.library[method](id, params)
.then((results) => {
ipcRenderer.send("wsapi-returnMusicKitApi", JSON.stringify(results), method);
});
} else {
MusicKit.getInstance().api[method](id, params).then((results)=>{
ipcRenderer.send('wsapi-returnMusicKitApi', JSON.stringify(results), method)
})
MusicKit.getInstance()
.api[method](id, params)
.then((results) => {
ipcRenderer.send("wsapi-returnMusicKitApi", JSON.stringify(results), method);
});
}
},
getPlaybackState() {
ipcRenderer.send('wsapi-updatePlaybackState', MusicKitInterop.getAttributes());
ipcRenderer.send("wsapi-updatePlaybackState", MusicKitInterop.getAttributes());
},
getLyrics() {
ipcRenderer.send('wsapi-returnLyrics',JSON.stringify(app.lyrics));
ipcRenderer.send("wsapi-returnLyrics", JSON.stringify(app.lyrics));
},
getQueue() {
ipcRenderer.send('wsapi-returnQueue', JSON.stringify(MusicKit.getInstance().queue))
ipcRenderer.send("wsapi-returnQueue", JSON.stringify(MusicKit.getInstance().queue));
},
playNext(type, id) {
var request = {}
request[type] = id
MusicKit.getInstance().playNext(request)
var request = {};
request[type] = id;
MusicKit.getInstance().playNext(request);
},
playLater(type, id) {
var request = {}
request[type] = id
MusicKit.getInstance().playLater(request)
},
love() {
var request = {};
request[type] = id;
MusicKit.getInstance().playLater(request);
},
love() {},
playTrackById(id, kind = "song") {
MusicKit.getInstance().setQueue({ [kind]: id , parameters : {l : app.mklang}}).then(function (queue) {
MusicKit.getInstance().play()
})
MusicKit.getInstance()
.setQueue({ [kind]: id, parameters: { l: app.mklang } })
.then(function (queue) {
MusicKit.getInstance().play();
});
},
quickPlay(term) {
// Quick play by song name
MusicKit.getInstance().api.search(term, { limit: 2, types: 'songs' }).then(function (data) {
MusicKit.getInstance().setQueue({ song: data["songs"][0]["id"],parameters : {l : app.mklang} }).then(function (queue) {
MusicKit.getInstance().play()
})
MusicKit.getInstance()
.api.search(term, { limit: 2, types: "songs" })
.then(function (data) {
MusicKit.getInstance()
.setQueue({
song: data["songs"][0]["id"],
parameters: { l: app.mklang },
})
.then(function (queue) {
MusicKit.getInstance().play();
});
});
},
toggleShuffle() {
MusicKit.getInstance().shuffleMode = MusicKit.getInstance().shuffleMode === 0 ? 1 : 0
MusicKit.getInstance().shuffleMode = MusicKit.getInstance().shuffleMode === 0 ? 1 : 0;
},
togglePlayPause() {
app.mk.isPlaying ? app.mk.pause() : app.mk.play()
app.mk.isPlaying ? app.mk.pause() : app.mk.play();
},
toggleRepeat() {
if (MusicKit.getInstance().repeatMode == 0) {
MusicKit.getInstance().repeatMode = 1
MusicKit.getInstance().repeatMode = 1;
} else if (MusicKit.getInstance().repeatMode == 1) {
MusicKit.getInstance().repeatMode = 2
MusicKit.getInstance().repeatMode = 2;
} else {
MusicKit.getInstance().repeatMode = 0
MusicKit.getInstance().repeatMode = 0;
}
},
getmaxVolume() {
ipcRenderer.send('wsapi-returnvolumeMax',JSON.stringify(app.cfg.audio.maxVolume));
ipcRenderer.send("wsapi-returnvolumeMax", JSON.stringify(app.cfg.audio.maxVolume));
},
getLibraryStatus(kind, id) {
if (kind === undefined || id === "no-id-found") return;
let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/?ids[${truekind}]=${id}`, {
let truekind = !kind.endsWith("s") ? kind + "s" : kind;
app.mk.api.v3
.music(`/v1/catalog/${app.mk.storefrontId}/?ids[${truekind}]=${id}`, {
relate: "library",
fields: "inLibrary"
}).then(data => {
fields: "inLibrary",
})
.then((data) => {
const res = data.data.data[0];
const inLibrary = res && res.attributes && res.attributes.inLibrary;
app.getRating({ type: truekind, id: id }).then(rating => {
ipcRenderer.send('wsapi-libraryStatus', inLibrary, rating);
})
})
app.getRating({ type: truekind, id: id }).then((rating) => {
ipcRenderer.send("wsapi-libraryStatus", inLibrary, rating);
});
});
},
rate(kind, id, rating) {
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) {
app.mk.api.v3.music(`/v1/me/ratings/${truekind}/${id}`, {}, {
app.mk.api.v3
.music(
`/v1/me/ratings/${truekind}/${id}`,
{},
{
fetchOptions: {
method: "DELETE",
},
}
}).then(function () {
ipcRenderer.send('wsapi-rate', kind, id, rating);
})
)
.then(function () {
ipcRenderer.send("wsapi-rate", kind, id, rating);
});
} else {
app.mk.api.v3.music(`/v1/me/ratings/${truekind}/${id}`, {}, {
app.mk.api.v3
.music(
`/v1/me/ratings/${truekind}/${id}`,
{},
{
fetchOptions: {
method: "PUT",
body: JSON.stringify({
"type": "rating",
"attributes": {
"value": rating
type: "rating",
attributes: {
value: rating,
},
}),
},
}
})
}
}).then(function () {
ipcRenderer.send('wsapi-rate', kind, id, rating);
})
)
.then(function () {
ipcRenderer.send("wsapi-rate", kind, id, rating);
});
}
},
changeLibrary(kind, id, shouldAdd) {
if (shouldAdd) {
app.addToLibrary(id);
ipcRenderer.send('wsapi-change-library', kind, id, shouldAdd);
ipcRenderer.send("wsapi-change-library", kind, id, shouldAdd);
} 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",
fields: "inLibrary"
fields: "inLibrary",
})
.then(res => {
res = res.data.data[0]
.then((res) => {
res = res.data.data[0];
if (res && res.relationships && res.relationships.library && res.relationships.library.data) {
const item = res.relationships.library.data[0];
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-position: center;
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;
transition : opacity .10s var(--appleEase);
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 0.1s var(--appleEase);
}
a:-webkit-any-link {
color: var(--keyColor);
}
@ -120,7 +119,6 @@ body.notransparency::before {
box-sizing: inherit;
}
/* Modern style overlay scrollbars */
::-webkit-scrollbar {
width: 16px;
@ -230,7 +228,6 @@ input[type="number"] {
font-family: inherit;
}
.bg-artwork--placeholder {
position: absolute;
top: 0;
@ -241,7 +238,7 @@ input[type="number"] {
z-index: -1;
background-size: cover;
background-position: center;
opacity : 0.70;
opacity: 0.7;
}
a.dropdown-item {
@ -280,7 +277,6 @@ a.dropdown-item {
}
}
.bg-artwork-container {
position: absolute;
top: 0;
@ -337,9 +333,8 @@ a.dropdown-item {
}
}
[artwork-hidden] {
transition: opacity .25s var(--appleEase);
transition: opacity 0.25s var(--appleEase);
opacity: 0;
}
@ -362,7 +357,7 @@ input[type="range"].web-slider::-webkit-slider-thumb {
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;
box-shadow: none;
border: none;
@ -380,7 +375,7 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
flex-direction: column;
opacity: 1;
overflow: hidden;
background-color: rgba(20 20 20 / .7);
background-color: rgba(20 20 20 / 0.7);
}
#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 {
@ -478,14 +473,14 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
}
.search-input--icon {
content : '';
content: "";
width: 100%;
height: 100%;
display: block;
position: absolute;
top: 0px;
left: 0px;
background-image : url('assets/search.svg');
background-image: url("assets/search.svg");
background-position: 10px;
background-repeat: no-repeat;
background-size: 12px;
@ -563,7 +558,7 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
}
&:after {
content : '';
content: "";
display: flex;
justify-content: center;
align-items: center;
@ -594,7 +589,7 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
&.active {
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%;
box-shadow: var(--ciderShadow-Generic);
backdrop-filter: var(--glassFilter);
animation : cmenuBodyIn .5s var(--appleEase);
animation: cmenuBodyIn 0.5s var(--appleEase);
@keyframes cmenuBodyIn {
0% {
@ -774,7 +769,7 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.0);
background: rgba(0, 0, 0, 0);
z-index: 100;
.context-menu-item {
@ -808,14 +803,14 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
box-shadow: var(--ciderShadow-Generic);
&.context-menu-open {
animation-duration : .10s;
animation-duration: 0.1s;
animation-name: contextMenuIn;
animation-iteration-count: 1;
animation-easings: var(--appleEase);
}
&.context-menu-close {
animation-duration : .10s;
animation-duration: 0.1s;
animation-name: contextMenuOut;
animation-iteration-count: 1;
animation-easings: var(--appleEase);
@ -908,7 +903,6 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
> ._svg-icon {
--size: var(--iconSize);
}
}
.app-sidebar-item:hover {
@ -934,8 +928,7 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
.app-chrome {
background-color: var(--baseColorMix);
box-shadow : 0px 3px 6px rgb(20 20 20 / 12%),
0px 1px 0px 0px rgb(200 200 200 / 12%);
box-shadow: 0px 3px 6px rgb(20 20 20 / 12%), 0px 1px 0px 0px rgb(200 200 200 / 12%);
width: 100%;
height: var(--chromeHeight1);
display: flex;
@ -1023,7 +1016,6 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
}
.app-chrome .app-chrome--center {
//width: 40%;
.app-title-text {
font-size: 0.8em;
@ -1044,10 +1036,9 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
-webkit-app-region: no-drag;
height: auto;
&.generic {
width: 50px;
opacity: 0.70;
opacity: 0.7;
}
&.volume {
@ -1072,7 +1063,7 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
background-size: 12px;
background-position: center;
background-repeat: no-repeat;
opacity : 0.70;
opacity: 0.7;
border-radius: 6px;
}
@ -1097,32 +1088,32 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
width: 30px;
border: 0px;
box-shadow: unset;
opacity : 0.70;
opacity: 0.7;
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;
height: 14px;
width: 14px;
border-radius: 50%;
background : #A5A8BA;
background: #a5a8ba;
box-shadow: 0px 0px 0px 1px rgba(0 0 0 / 10%);
cursor: default;
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;
height: 4px;
background: rgba(255, 255, 255, 0.4);
border-radius: 5px;
background-size: 70% 100%;
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;
box-shadow: none;
border: none;
@ -1151,7 +1142,7 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
margin-top: 1.5px;
border: 0px;
border-radius: 6px;
transition : transform .1s var(--appleEase);
transition: transform 0.1s var(--appleEase);
position: relative;
// &:after {
@ -1208,7 +1199,7 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
-webkit-app-region: no-drag;
&: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 {
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 {
@bgColor: transparent;
//height: 16px;
@ -1432,7 +1421,7 @@ div[data-type="musicVideo"] .info-rect .title::before {
background: @bgColor;
&:hover {
>input[type=range] {
> input[type="range"] {
&::-webkit-slider-thumb {
opacity: 1;
transform: scale(1);
@ -1441,7 +1430,7 @@ div[data-type="musicVideo"] .info-rect .title::before {
}
}
>input[type=range] {
> input[type="range"] {
appearance: none;
width: 100%;
height: 4px;
@ -1459,7 +1448,7 @@ div[data-type="musicVideo"] .info-rect .title::before {
border-radius: 100%;
background: var(--songProgressColor);
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 {
@ -1537,7 +1526,7 @@ div[data-type="musicVideo"] .info-rect .title::before {
}
.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 {
> marquee {
margin-bottom: -3px;
@ -1621,8 +1609,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
@media only screen and (max-width: 1120px) {
.display--small {
display: inherit !important;
;
.slider {
width: 100%;
z-index: 1;
@ -1636,7 +1622,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
padding-bottom: 10px;
}
input[type=range] {
input[type="range"] {
-webkit-appearance: none;
height: 4px;
background: rgba(255, 255, 255, 0.4);
@ -1674,7 +1660,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
}
}
.display--large {
display: none !important;
}
@ -1691,14 +1676,13 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
position: relative;
}
.lyric-body {
-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-x: hidden;
display: flex;
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 {
@ -1740,7 +1724,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
}
.lyric-line:hover::after {
content : ' ';
content: " ";
width: 100%;
height: 100%;
position: absolute;
@ -1762,7 +1746,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
}
.lyric-line:not(.active) {
filter: blur(1px)
filter: blur(1px);
}
.lyric-line:not(.active).unsynced {
@ -1773,7 +1757,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
filter: none !important;
}
.lyric-body:hover > .lyric-line:not(.active) {
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;
}
.lyric-line.active .lyricWaiting > div {
width: 10px;
height: 10px;
@ -1804,7 +1786,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.lyrics-translation {
font-size: 1.6rem;
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);
}
@ -1869,10 +1851,9 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
}
}
.lyric-line2:before {
background: var(--keyColor);
content : '';
content: "";
width: 0%;
height: 6px;
position: absolute;
@ -1976,11 +1957,11 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
}
&.githubBtn {
background-color: #211F1F;
background-color: #211f1f;
}
&.kofiBtn {
background-color: #FBAA19;
background-color: #fbaa19;
}
&.opencollectiveBtn {
@ -1988,16 +1969,15 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
}
&.discordBtn {
background-color: #5865F2;
background-color: #5865f2;
}
&.twitterBtn {
background-color: #1D9BF0;
background-color: #1d9bf0;
}
}
}
.sidebar-playlist {
.folder-button-active {
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);
margin: 2px;
border-radius: 6px;
transition : transform .10s var(--appleEase);
transition: transform 0.1s var(--appleEase);
&:active {
background: var(--selected-click);
@ -2062,7 +2042,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
background: var(--selected);
> 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);
margin-top: 16px;
&.itemContainer {
display: flex;
flex-flow: wrap;
@ -2164,7 +2143,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
}
}
@keyframes rotate {
from {
transform: rotate(0deg);
@ -2204,8 +2182,8 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
&::before {
top: -50%;
left: -20%;
width : 200VH;
height: 200VH;
width: 200vh;
height: 200vh;
}
.bg-artwork-container {
@ -2226,7 +2204,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
font-weight: 600;
font-size: 2em;
transform-origin: center;
animation : fsLyricIn var(--appleEase) .2s;
animation: fsLyricIn var(--appleEase) 0.2s;
opacity: 0.9;
&:not(.active) {
@ -2247,7 +2225,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
@keyframes fsLyricIn {
0% {
opacity: 0;
transform: scale(0.98)
transform: scale(0.98);
}
100% {
@ -2262,7 +2240,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.replaycard-enter-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,
@ -2273,7 +2251,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.fade-enter-active,
.fade-leave-active {
transition: opacity .15s var(--appleEase);
transition: opacity 0.15s var(--appleEase);
}
.fade-enter,
@ -2283,18 +2261,18 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.modal-enter-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-leave-to {
opacity: 0;
transform: scale(1.10);
transform: scale(1.1);
}
.wpfade-enter-active,
.wpfade-leave-active {
transition: opacity .1s var(--appleEase);
transition: opacity 0.1s var(--appleEase);
}
.wpfade-enter,
@ -2321,7 +2299,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
will-change: opacity;
}
.wpfade_transform_backwards-enter-active,
.wpfade_transform_backwards-leave-active {
--transitionTime: 0.2s;
@ -2342,7 +2319,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.fabfade-enter-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,
@ -2358,13 +2335,13 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.fsModeSwitch-enter,
.fsModeSwitch-leave-to {
transform: scale(1.10);
transform: scale(1.1);
opacity: 0;
}
.sidebartransition-enter-active,
.sidebartransition-leave-active {
transition: margin-left .35s var(--appleEase);
transition: margin-left 0.35s var(--appleEase);
}
.sidebartransition-enter,
@ -2374,7 +2351,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.drawertransition-enter-active,
.drawertransition-leave-active {
transition: transform .25s var(--appleEase);
transition: transform 0.25s var(--appleEase);
}
.drawertransition-enter,
@ -2382,13 +2359,14 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
transform: translateX(400px);
}
@media (prefers-color-scheme: dark) {}
@media (prefers-color-scheme: dark) {
}
: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-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-minBtn : url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAGOmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTAyLTE3VDEzOjAwOjMyWiIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMC0wMi0xN1QxMzowMDozMloiIHhtcDpNb2RpZnlEYXRlPSIyMDIwLTAyLTE3VDEzOjAwOjMyWiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo4NWQwZWRiMC1mZDAwLWI2NGYtOWVmYi1hMmI0NTg3MDVhOGEiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDphMzAwMWUxYS0yOTE5LWU0NDktYjk0Yy1jMjEyMjQ4YTlmOGEiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3ODdmNzk5Yy00YjExLWU1NGEtYjIwZC02ODYxN2VkOWM1ZTIiIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo3ODdmNzk5Yy00YjExLWU1NGEtYjIwZC02ODYxN2VkOWM1ZTIiIHN0RXZ0OndoZW49IjIwMjAtMDItMTdUMTM6MDA6MzJaIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjg1ZDBlZGIwLWZkMDAtYjY0Zi05ZWZiLWEyYjQ1ODcwNWE4YSIgc3RFdnQ6d2hlbj0iMjAyMC0wMi0xN1QxMzowMDozMloiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPHBob3Rvc2hvcDpUZXh0TGF5ZXJzPiA8cmRmOkJhZz4gPHJkZjpsaSBwaG90b3Nob3A6TGF5ZXJOYW1lPSLupKEiIHBob3Rvc2hvcDpMYXllclRleHQ9Iu6koSIvPiA8L3JkZjpCYWc+IDwvcGhvdG9zaG9wOlRleHRMYXllcnM+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+FwvRXAAAABdJREFUGNNj/P//PwMxgHGIKPw/XDwDAOr1HuzlELLnAAAAAElFTkSuQmCC');
--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-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");
}
#apple-music-video-container {
@ -2403,7 +2381,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
}
#apple-music-video-container {
// AM Web Style Fullscreen Button
#player-fullscreen {
background-size: 50%;
@ -2458,7 +2435,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
input[type="range"] {
align-self: center;
height: 4px;
border-radius: .5rem;
border-radius: 0.5rem;
margin-inline: 10px;
}
@ -2467,7 +2444,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
&::-webkit-slider-thumb {
box-shadow: 0px 0px 0px #000000;
border : 1px solid #39404D;
border: 1px solid #39404d;
background: #fff;
height: 0.7rem;
width: 0.7rem;
@ -2547,14 +2524,12 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
}
}
&.song-artist-marquee {
> marquee {
margin-bottom: -3px;
}
}
}
}
}
@ -2626,11 +2601,11 @@ div#captions {
background: rgba(0, 0, 0, 0.6);
color: yellow;
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] {
display: none !important
display: none !important;
}
.item-navigate:hover {
@ -2740,8 +2715,6 @@ body.no-gpu {
}
}
.qrimg {
width: -webkit-fill-available;
max-block-size: -webkit-fill-available;
@ -2759,7 +2732,6 @@ body.no-gpu {
padding-block: 25px;
}
}
}
.equalizer-panel {
@ -2781,7 +2753,6 @@ body.no-gpu {
overflow: hidden;
}
.modal-header {
padding: 16px;
position: relative;
@ -2792,7 +2763,7 @@ body.no-gpu {
}
.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 {
display: none;
}
@ -2872,7 +2843,7 @@ body[platform='darwin'] {
.percent::after {
position: relative;
right: 2em;
transition: all .05s ease-in-out;
transition: all 0.05s ease-in-out;
}
.percent:hover::after,
@ -2881,7 +2852,7 @@ body[platform='darwin'] {
}
.percent::after {
content: '%';
content: "%";
}
.playbackrate-text {
@ -2912,7 +2883,6 @@ body[platform='darwin'] {
}
.keybinds-page {
.md-option-header {
padding: 10px 0px;
border-bottom: unset;
@ -2933,7 +2903,7 @@ body[platform='darwin'] {
.md-option-line {
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

View file

@ -13,7 +13,7 @@
.drawertransition-enter-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,
@ -29,7 +29,7 @@
.drawertransition-enter-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,
@ -37,5 +37,4 @@
right: -300px;
}
}
}

View file

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

View file

@ -7,13 +7,12 @@
backdrop-filter: blur(32px) saturate(180%);
&.menu-panel-body-down {
animation: menuInDown .10s var(--appleEase);
animation: menuInDown 0.1s var(--appleEase);
}
&.menu-panel-body-up {
animation: menuInUp .10s var(--appleEase);
animation: menuInUp 0.1s var(--appleEase);
}
}
@keyframes menuInUp {
@ -43,7 +42,6 @@
background: @panelColors;
}
}
}
.cd-mediaitem-square {
@ -55,46 +53,45 @@
}
.cd-mediaitem-square:not(.mediaitem-card) {
transition : transform .2s var(--appleEase);
transition-delay: .1s;
transition: transform 0.2s var(--appleEase);
transition-delay: 0.1s;
.artwork-container {
}
.artwork-container {}
.info-rect {}
.info-rect {
}
.artwork-container,
.info-rect {
transition : transform .22s var(--appleEase);
transition-delay: .05s;
transition: transform 0.22s var(--appleEase);
transition-delay: 0.05s;
}
.artwork-container {
transform: scale(0.962) translateZ(0);
transition : transform .1s var(--appleEase);
transition: transform 0.1s var(--appleEase);
transition-delay: 0s;
transform-origin: center;
}
&:hover {
.artwork-container {
transform : scale(1.0);
transition : transform .1s var(--appleEase);
transform: scale(1);
transition: transform 0.1s var(--appleEase);
transition-delay: 0s;
transform-origin: center;
}
.info-rect {
z-index: 1;
transition : transform .1s var(--appleEase);
transition: transform 0.1s var(--appleEase);
transition-delay: 0s;
transform: translateY(8px) translate3d(0, 0, 0);
}
}
&:active {
}
}
@ -116,7 +113,6 @@
will-change: opacity, transform;
}
.wpfade_transform_backwards-enter-active,
.wpfade_transform_backwards-leave-active {
--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
// &l=en-gb&platform=web
await app.mk.api.personalRecommendations("",
await app.mk.api.personalRecommendations(
"",
{
name: "listen-now",
with: "friendsMix,library,social",
@ -38,16 +39,16 @@ await app.mk.api.personalRecommendations("",
"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",
l: "en-gb",
platform:"web"
platform: "web",
},
{
includeResponseMeta: !0,
reload: !0
});
reload: !0,
}
);
// Browse page
await app.mk.api.groupings("",
{
await app.mk.api.groupings("", {
platform: "web",
name: "music",
l: "en-gb",
@ -57,100 +58,112 @@ await app.mk.api.groupings("",
"include[music-videos]": "artists",
extend: "editorialArtwork,artistUrl",
"fields[artists]": "name,url,artwork,editorialArtwork,genreNames,editorialNotes",
"art[url]": "f"
"art[url]": "f",
});
// Radio page
await app.mk.api.recentRadioStations("",
{l: "en-gb",
"platform": "web",
"art[url]": "f"});
await app.mk.api.recentRadioStations("", {
l: "en-gb",
platform: "web",
"art[url]": "f",
});
// Recently Added
await app.mk.api.library.recentlyAdded({
"platform": "web",
await app.mk.api.library.recentlyAdded(
{
platform: "web",
include: {
"library-albums": ["artists"],
"library-artists": ["catalog"]
"library-artists": ["catalog"],
},
fields: {
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"],
limit: 25
}, {
limit: 25,
},
{
reload: !0,
includePagination: !0
})
includePagination: !0,
}
);
// Songs
await app.mk.api.library.songs({ limit: 100 }).then((data) => {
console.log(data)
})
console.log(data);
});
// Artists
await app.mk.api.library.artists({ limit: 100 }).then((data) => {
console.log(data)
})
console.log(data);
});
// Artists
await app.mk.api.library.albums({ limit: 100 }).then((data) => {
console.log(data)
})
console.log(data);
});
// Albums
// does not like limit = 100 for some reason
await app.mk.api.library.albums({ limit: 50 }).then((data) => {
console.log(data)
})
console.log(data);
});
// Made For You
app.mk.api.recommendations("",{extend: "editorialArtwork,artistUrl"})
app.mk.api.recommendations("", { extend: "editorialArtwork,artistUrl" });
// Library with library length
await app.mk.api.library.songs("", { limit: 100 }, { includeResponseMeta: !0 }).then((data) => {
console.log(data)
})
console.log(data);
});
// 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
app.mkapi("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",
app
.mkapi(
"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",
"omit[resource:songs]": "relationships",
"fields[albums]": "artistName,artistUrl,artwork,contentRating,editorialArtwork,name,playParams,releaseDate,url,trackCount",
"limit[artists:top-songs]": 20,
"art[url]": "f"
}, {includeResponseMeta: !0}).then((data)=>{
console.log(data)
})
"art[url]": "f",
},
{ includeResponseMeta: !0 }
)
.then((data) => {
console.log(data);
});
// download entire library
var library = []
var library = [];
var downloaded = null;
function downloadChunk() {
if (downloaded == null) {
app.mk.api.library.songs("", { limit: 100 }, { includeResponseMeta: !0 }).then((response) => {
processChunk(response)
})
processChunk(response);
});
} else {
downloaded.next("", { limit: 100 }, { includeResponseMeta: !0 }).then((response) => {
processChunk(response)
})
processChunk(response);
});
}
}
function processChunk(response) {
downloaded = response
library = library.concat(downloaded.data)
downloaded = response;
library = library.concat(downloaded.data);
if (downloaded.meta.total > library.length) {
console.log(`downloading next chunk - ${library.length} songs so far`)
downloadChunk()
console.log(`downloading next chunk - ${library.length} songs so far`);
downloadChunk();
} else {
console.log(library)
console.log(library);
}
}
@ -158,23 +171,23 @@ function processChunk (response) {
// recentPlayed() -> recently played songs ?
// create Artist / Song/ Album stations:
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({ artist: "1258279972" });
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
// playlist must be fully recursively downloaded first before sorting
app.mk.api.library.putPlaylistTracklisting(app.showingPlaylist.attributes.playParams.id, [
{
"id": relationships.tracks.data[X].id,
"type": relationships.tracks.data[X].type
id: relationships.tracks.data[X].id,
type: relationships.tracks.data[X].type,
},
{
"id": relationships.tracks.data[X].id,
"type": relationships.tracks.data[X].type
id: relationships.tracks.data[X].id,
type: relationships.tracks.data[X].type,
},
{
"id": relationships.tracks.data[X].id,
"type": relationships.tracks.data[X].type
id: relationships.tracks.data[X].id,
type: relationships.tracks.data[X].type,
},
])
]);

View file

@ -141,9 +141,12 @@
v-if="drawer.open && drawer.panel == 'lyrics' && lyrics && lyrics != [] && lyrics.length > 0">
<div class="bgArtworkMaterial">
<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 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">
<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 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>
<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-item playback-controls">
<template v-if="mkReady()">
@ -16,15 +17,23 @@
<mediaitem-artwork :url="currentArtUrl"></mediaitem-artwork>
</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`)">
{{(mk.nowPlayingItem["attributes"]["albumName"]) ?
(mk.nowPlayingItem["attributes"]["albumName"]) : "" }}
</div>
<hr>
<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%;" @click="drawer.open = false; fullscreen(true)">{{ $root.getLz("term.fullscreenView") }}</button>
<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%;"
@click="drawer.open = false; fullscreen(true)">{{
$root.getLz("term.fullscreenView") }}
</button>
</div>
</div>
</b-popover>
@ -39,20 +48,26 @@
<div class="song-artist" @click="getNowPlayingItemDetailed(`artist`)">
{{ mk.nowPlayingItem["attributes"]["artistName"] }}
</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"]) : "" }}
</div>
<div class="chrome-icon-container">
<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"
: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 class="audio-type lossless-icon" 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 class="audio-type lossless-icon"
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 class="audio-type ppe-icon" v-if="mk.nowPlayingItem.localFilesMetadata == null && cfg.audio.maikiwiAudio.ciderPPE === true"
:title="$root.getLz('settings.option.audio.enableAdvancedFunctionality.ciderPPE')" v-b-tooltip.hover
<div class="audio-type ppe-icon"
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>
@ -87,7 +102,8 @@
<div class="app-chrome--center">
<div class="app-chrome-playback-duration-bottom">
<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>
<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);"
:max="mk.currentPlaybackDuration" :value="getSongProgress()">
</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-row>
</div>
<div class="app-chrome-playback-controls">
<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')"
v-b-tooltip.hover></button>
<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>
</div>
<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>
<button class="playback-button pause" @click="mk.pause()" v-else-if="mk.isPlaying"
:title="$root.getLz('term.pause')" v-b-tooltip.hover></button>
@ -126,13 +146,16 @@
:title="$root.getLz('term.next')" v-b-tooltip.hover></button>
</div>
<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')"
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-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-b-tooltip.hover></button>
</div>

View file

@ -1,6 +1,7 @@
<div class="app-chrome" :style="{'display': chrome.topChromeVisible ? '' : 'none'}">
<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="close" @click="ipcRenderer.send('close')"></div>
<div class="minimize" @click="ipcRenderer.send('minimize')"></div>
@ -16,7 +17,8 @@
<template v-if="getThemeDirective('appNavigation') != 'seperate'">
<div class="vdiv" v-if="getThemeDirective('windowLayout') == 'twopanel'"></div>
<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>
<svg-icon url="./views/svg/chevron-left.svg"></svg-icon>
</button>
@ -45,13 +47,17 @@
<template v-if="getThemeDirective('windowLayout') != 'twopanel'">
<div class="app-chrome-item playback-control-buttons">
<div class="app-chrome-item display--large">
<button class="playback-button--small shuffle" v-if="mk.shuffleMode == 0" :class="isDisabled() && 'disabled'"
@click="mk.shuffleMode = 1" :title="$root.getLz('term.enableShuffle')" v-b-tooltip.hover></button>
<button class="playback-button--small shuffle" v-if="mk.shuffleMode == 0"
: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'"
@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 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>
</div>
<div class="app-chrome-item display--large">
@ -64,17 +70,21 @@
v-b-tooltip.hover></button>
</div>
<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>
</div>
<div class="app-chrome-item display--large">
<button class="playback-button--small repeat" v-if="mk.repeatMode == 0" :class="isDisabled() && 'disabled'"
@click="mk.repeatMode = 1" :title="$root.getLz('term.enableRepeatOne')" v-b-tooltip.hover></button>
<button class="playback-button--small repeat" v-if="mk.repeatMode == 0"
: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"
:class="isDisabled() && 'disabled'" v-else-if="mk.repeatMode == 1"
:title="$root.getLz('term.disableRepeatOne')" v-b-tooltip.hover></button>
<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>
</div>
</div>
@ -111,10 +121,12 @@
</div>
<hr />
<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") }}
</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") }}
</button>
</div>
@ -124,20 +136,26 @@
<div class="chrome-icon-container">
<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"
: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 class="audio-type lossless-icon" 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 class="audio-type lossless-icon"
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 class="audio-type ppe-icon" v-if="mk.nowPlayingItem.localFilesMetadata == null && cfg.audio.maikiwiAudio.ciderPPE === true"
:title="$root.getLz('settings.option.audio.enableAdvancedFunctionality.ciderPPE')" v-b-tooltip.hover
<div class="audio-type ppe-icon"
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 class="info-rect">
<div class="song-name"
:class="[isElementOverflowing('#app-main > div.app-chrome > div.app-chrome--center > div > div > div.playback-info > div.song-name') ? 'marquee' : '']">
{{ 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>
</div>
<div class="song-artist-album">
@ -208,13 +226,17 @@
</div>
<div class="app-chrome-item" v-else>
<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 :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>
<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 :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>
</div>
</div>
@ -224,9 +246,11 @@
<div class="app-chrome-item volume display--large">
<button class="volume-button--small volume" @click="muteButtonPressed()"
: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"
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()" />
</div>
<div class="app-chrome-item generic">
@ -244,7 +268,8 @@
:class="{'active': drawer.panel == 'lyrics'}" @click="invokeDrawer('lyrics')"></button>
</template>
<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>
</div>
</template>
@ -252,9 +277,11 @@
<div class="app-chrome-item search">
<div class="search-input-container">
<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)"
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"
class="search-input" />

View file

@ -93,7 +93,8 @@
:class="{'active': this.cfg.audio.volume == 0}"
: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 :title="formatVolumeTooltip()">
</div>

View file

@ -1,5 +1,6 @@
<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-header">
<div class="modal-title">{{app.getLz('action.addToPlaylist')}}</div>
@ -11,7 +12,8 @@
<div class="icon"><%- include("../svg/plus.svg") %></div>
<div class="name">{{app.getLz('action.createPlaylist')}}</div>
</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>
</div>
<div class="modal-search">

View file

@ -1,5 +1,6 @@
<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-header">
<div class="modal-title">{{'Enter password'}}</div>
@ -11,7 +12,9 @@
<div class="md-footer">
<div class="row">
<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>

View file

@ -63,7 +63,9 @@
let u = this.hls;
var quality = app.cfg.visual.animated_artwork_qualityLevel;
setTimeout(() => {
let levelsnum = u.levels.map((level)=>{return level.width})
let levelsnum = u.levels.map((level) => {
return level.width
})
if (levelsnum.length > 0) {
let qualities = []
let qualities2 = []
@ -82,7 +84,10 @@
}
}
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
<% if(env.appRoutes[i].onEnter) {
%>
v-on:enter="<%- env.appRoutes[i].onEnter %>"
v-on:enter="<%- env.appRoutes[i].onEnter; %>"
<%
}
%>
:name="$root.chrome.desiredPageTransition">
<template
v-if="<%- env.appRoutes[i].condition %>">
<%- env.appRoutes[i].component %>
v-if="<%- env.appRoutes[i].condition; %>">
<%- env.appRoutes[i].component; %>
</template>
</transition>
<% } %>
@ -45,7 +45,6 @@
scrollPos: 0
}
},
methods: {
}
methods: {}
});
</script>

View file

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

View file

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

View file

@ -4,7 +4,8 @@
<div class="modal-window">
<div class="modal-header">
<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 class="modal-content">
<div class="md-option-line">

View file

@ -4,7 +4,8 @@
<div class="modal-window">
<div class="modal-header">
<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 class="modal-content">
<div class="md-option-line">
@ -15,7 +16,8 @@
{{playbackRate}} ×
</div>
<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>

View file

@ -1,10 +1,12 @@
<script type="text/x-template" id="audio-settings">
<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-header">
<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 class="modal-content">
<button class="playlist-item"
@ -42,7 +44,8 @@
}
},
props: {},
mounted() {},
mounted() {
},
methods: {
openEqualizer() {
app.modals.equalizer = true

View file

@ -16,11 +16,18 @@
<br>
<small>{{ device.host }}</small>
</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
</div>
<div class="md-option-segment_auto" v-else 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" 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>
<div class="md-option-segment_auto" v-else
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>
</template>
@ -37,7 +44,8 @@
<div class="md-option-container" style="margin-top: 12px;margin-bottom: 12px;overflow-y: scroll;">
<div class="md-option-line">
<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')}} -->
<template v-if="true" v-for="(device) in devices.airplay">
<div class="md-option-line" style="cursor: pointer" @click="setAirPlayCast(device)">
@ -46,11 +54,19 @@
<br>
<small>{{ device.host }}</small>
</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
</div>
<div class="md-option-segment_auto" v-else 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" 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>
<div class="md-option-segment_auto" v-else
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>
</template>
@ -61,10 +77,14 @@
<div class="md-footer">
<div class="row">
<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 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>
@ -91,7 +111,8 @@
watch: {
activeCasts: function(newVal, oldVal) {
this.$root.activeCasts = this.activeCasts;
}},
}
},
methods: {
close() {
this.$root.modals.castMenu = false

View file

@ -4,12 +4,14 @@
<div class="modal-window">
<div class="modal-header">
<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 class="modal-content">
<button class="playlist-item"
: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="name">{{ playlist.attributes.name }}</div>
</button>

View file

@ -5,9 +5,13 @@
<div class="modal-title">{{$root.getLz('term.equalizer')}}</div>
<button class="close-btn" @click="close()" :aria-label="$root.getLz('action.close')"></button>
<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')">
<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 :label="$root.getLz('term.defaultPresets')">
<option v-for="preset in defaultPresets" :value="preset.preset">{{preset.name}}</option>
@ -20,12 +24,14 @@
<div class="inputs-container">
<div class="input-container mini">
{{$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
</div>
<div class="input-container mini">
{{$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
</div>
<div class="input-container header mini">
@ -35,64 +41,104 @@
<div>Q</div>
</div>
<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="range" class="eq-slider" orient="vertical" min="-12" max="12" 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)">
<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="range" class="eq-slider" orient="vertical" min="-12" max="12"
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 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="range" class="eq-slider" orient="vertical" min="-12" max="12" 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)">
<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="range" class="eq-slider" orient="vertical" min="-12" max="12"
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 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="range" class="eq-slider" orient="vertical" min="-12" max="12" 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)">
<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="range" class="eq-slider" orient="vertical" min="-12" max="12"
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 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="range" class="eq-slider" orient="vertical" min="-12" max="12" 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)">
<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="range" class="eq-slider" orient="vertical" min="-12" max="12"
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 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="range" class="eq-slider" orient="vertical" min="-12" max="12" 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)">
<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="range" class="eq-slider" orient="vertical" min="-12" max="12"
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 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="range" class="eq-slider" orient="vertical" min="-12" max="12" 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)">
<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="range" class="eq-slider" orient="vertical" min="-12" max="12"
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 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="range" class="eq-slider" orient="vertical" min="-12" max="12" 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)">
<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="range" class="eq-slider" orient="vertical" min="-12" max="12"
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 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="range" class="eq-slider" orient="vertical" min="-12" max="12" 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)">
<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="range" class="eq-slider" orient="vertical" min="-12" max="12"
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 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="range" class="eq-slider" orient="vertical" min="-12" max="12" 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)">
<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="range" class="eq-slider" orient="vertical" min="-12" max="12"
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 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="range" class="eq-slider" orient="vertical" min="-12" max="12" 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)">
<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="range" class="eq-slider" orient="vertical" min="-12" max="12"
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>
@ -100,10 +146,13 @@
<div class="modal-lowercontent">
<div class="row">
<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 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>
@ -298,14 +347,12 @@
try {
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.intelliGainComp_n0_0();
}
catch (e) {
CiderAudio.intelliGainComp_n0_0();
} catch (e) {
CiderAudio.hierarchical_loading();
}
}
else {
} else {
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.intelliGainComp_n0_0();
} catch (e) { CiderAudio.hierarchical_loading(); }
} catch (e) {
CiderAudio.hierarchical_loading();
}
}
},
changeGain(i) {
@ -324,10 +373,10 @@
try {
CiderAudio.audioNodes.audioBands[i].gain.value = app.cfg.audio.equalizer.gain[i] * app.cfg.audio.equalizer.mix
CiderAudio.intelliGainComp_n0_0();
} catch (e) {
CiderAudio.hierarchical_loading();
}
catch (e) { CiderAudio.hierarchical_loading(); }
}
else {
} else {
CiderAudio.hierarchical_loading();
}
},
@ -393,8 +442,7 @@
// self.applyPreset(preset)
app.cfg.audio.equalizer.presets.push(preset)
notyf.success(`${preset.name} has been imported.`)
}
else {
} else {
notyf.error("Invalid Preset")
}
}
@ -404,8 +452,9 @@
Object.assign(this.$root.cfg.audio.equalizer, preset)
this.changeVibrantBass()
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 }
catch (e) {
try {
CiderAudio.audioNodes.audioBands[i].gain.value = app.cfg.audio.equalizer.gain[i] * app.cfg.audio.equalizer.mix
} catch (e) {
CiderAudio.hierarchical_loading();
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 class="fs-header" v-if="immersiveEnabled">
<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 @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>
<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 @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 @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 @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 @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>
</div>
</div>
@ -59,7 +66,8 @@
</div>
<div class="song-artist item-navigate" style="display: inline-block;"
@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>

View file

@ -14,11 +14,13 @@
<mediaitem-square v-else :item="item" :type="getKind(item)"></mediaitem-square>
</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>
</div>
<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") %>
</button>
</transition>

View file

@ -69,8 +69,10 @@
getArtwork() {
let u = ""
try {
u = this.item.relationships.catalog.data[0].attributes.artwork.url}
catch (e){};
u = this.item.relationships.catalog.data[0].attributes.artwork.url
} catch (e) {
}
;
return u;
},
contextMenu(event) {
@ -119,10 +121,13 @@
"action": function() {
if (!self.item.attributes.url && self.item.relationships) {
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 {
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}">
<template v-if="isVisible && recom.attributes.display.kind != 'MusicSuperHeroShelf'">
<div class="row">
<div class="col" v-if="recom?.relationships['primary-content']?.data?.length > 0" style="display: flex; margin-block:1rem;">
<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 class="col" v-if="recom?.relationships['primary-content']?.data?.length > 0"
style="display: flex; margin-block:1rem;">
<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 @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>
<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 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>
</div>
<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>
</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
:items="recom.relationships.contents.data.limit(10)"></mediaitem-scroller-horizontal-large>
</template>

View file

@ -3,7 +3,8 @@
<vue-horizontal>
<div v-for="items in itemPages">
<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"
:item="song"></mediaitem-list-item>
</div>
@ -39,10 +40,12 @@
// split items into pages
this.itemPages = app.arrayToChunk(this.items, 4);
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);
} catch (e) {
}
catch (e){}
},
watch: {
@ -54,10 +57,12 @@
// split items into pages
this.itemPages = app.arrayToChunk(this.items, 4);
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);
} catch (e) {
}
catch (e){}
}
},
methods: {

View file

@ -7,7 +7,8 @@
<template v-if="richlyrics && richlyrics != [] && richlyrics.length > 0">
<div class="richl">
<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>
</div>
</template>
@ -35,7 +36,8 @@
</template>
<template v-else>
<div class="no-lyrics">
{{app.getLz('term.noLyrics')}}</div>
{{app.getLz('term.noLyrics')}}
</div>
</template>
</div>
</script>
@ -125,7 +127,9 @@
if (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 (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")
if (this.checkIfScrollIsStatic) {
let lyricElement = this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${i}"]`)
@ -163,13 +167,20 @@
}
try {
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")) {
if (this.time + 0.1 >= child.getAttribute("lyricstart") * 1 + child.getAttribute("versestart") * 1) {
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) {
@ -180,8 +191,7 @@
getVerseLine(index) {
if (this.richlyrics[index] != null && this.richlyrics[index].l != null) {
return this.richlyrics[index].l
}
else return []
} else return []
},
qqInstrumental(lyrics) {
for (lyric of lyrics) {

View file

@ -1,5 +1,6 @@
<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"
ref="image"
decoding="async"

View file

@ -17,26 +17,37 @@
tabindex="0"
:class="[{'mediaitem-selected': app.select_hasMediaItem(guid)}, addClasses]">
<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 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')">
<div class="svg-icon addIcon" :style="{'--color': 'var(--keyColor)', '--url': 'url(./assets/feather/plus.svg)'}"></div>
<button @click="addToLibrary()"
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 v-else-if='!(showArtwork == true && (showIndex == false ||(showIndex == true && showIndexPlaylist != false)))' @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 v-else-if='!(showArtwork == true && (showIndex == false ||(showIndex == true && showIndexPlaylist != false)))'
@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>
</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>{{ (item.attributes && !showIndexPlaylist) ? (item.attributes.trackNumber ?? '') : ((index * 1 + 1 ) ?? '')}}</div>
<div>{{ (item.attributes && !showIndexPlaylist) ? (item.attributes.trackNumber ?? '') : ((index
* 1 + 1 ) ?? '')}}
</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>
</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
:url="item.attributes.artwork ? item.attributes.artwork.url : ''"
:size="48"
@ -51,7 +62,8 @@
<div class="title text-overflow-elipsis" :title="item.attributes.name">
{{ item.attributes.name }}
</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">
<div class="artist item-navigate text-overflow-elipsis"
@click="app.searchAndNavigate(item,'artist')">
@ -69,7 +81,8 @@
</div>
<div class="heart-icon">
<!-- <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 class="explicit-icon" v-if="item.attributes && item.attributes.contentRating == 'explicit'"></div>
<template v-if="showMetaData == true" @dblclick="route()">
@ -146,7 +159,9 @@
if ((this.item?.id ?? '').toString().startsWith('ciderlocal')) {
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")) {
this.addedToLibrary = true
return
@ -408,7 +423,9 @@
"icon": "./assets/arrow-bend-up.svg",
"action": function() {
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.queue._reindex()
app.selectedMediaItems = []
@ -419,7 +436,9 @@
"icon": "./assets/arrow-bend-down.svg",
"action": function() {
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.queue._reindex()
app.selectedMediaItems = []
@ -584,15 +603,15 @@
let parent = this.parent
let childIndex = this.index
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 truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
console.log(item, parent, childIndex, kind, id, isLibrary, kind == "playlists", id.startsWith("p.") || id.startsWith("pl.u"))
app.mk.stop().then(() => {
if (parent != null && childIndex != null) {
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) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
@ -601,14 +620,22 @@
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() {
var playlistId = id
function getPlaylist(id, isLibrary) {
if (isLibrary) {
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 {
getPlaylist(id, isLibrary).then(res => {
//let query = res.relationships.tracks.data.map(item => new MusicKit.MediaItem(item));
@ -628,7 +655,10 @@
// }
console.log('nextres', res)
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)
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)
}
})

View file

@ -27,7 +27,9 @@
<div class="title text-overflow-elipsis" @click='app.routeView(item)'>
{{ item.attributes.name ?? '' }}
</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 ?? '' }}
</div>

View file

@ -45,7 +45,8 @@
<div class="title"
: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)'>
<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.attributes?.editorialNotes?.name ?? '')) }}
</div>

View file

@ -2,18 +2,23 @@
<div class="mini-view" tabindex="0">
<div class="background">
</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">
<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>
</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">
<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>
</div>
<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>
<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)" />
@ -21,7 +26,8 @@
</radialGradient>
</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"
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>
</div>
<div class="col artwork-col">
@ -53,7 +59,8 @@
</div>
<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'} ]">
<p style="width: auto">{{ app.convertTime(app.getSongProgress()) }}</p>
<p style="width: auto">{{ app.convertTime(app.mk.currentPlaybackDuration) }}</p>
@ -67,43 +74,57 @@
</div>
<div class="control-buttons">
<div class="app-chrome-item display--large">
<button class="playback-button--small shuffle" v-if="$root.mk.shuffleMode == 0" :class="$root.isDisabled() && 'disabled'"
@click="$root.mk.shuffleMode = 1" :title="$root.getLz('term.enableShuffle')" 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>
<button class="playback-button--small shuffle" v-if="$root.mk.shuffleMode == 0"
:class="$root.isDisabled() && 'disabled'"
@click="$root.mk.shuffleMode = 1" :title="$root.getLz('term.enableShuffle')"
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 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>
</div>
<div class="app-chrome-item display--large">
<button class="playback-button stop" @click="$root.mk.stop()"
v-if="$root.mk.isPlaying && $root.mk.nowPlayingItem.attributes.playParams.kind == 'radioStation'"
: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>
<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>
</div>
<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>
</div>
<div class="app-chrome-item display--large">
<button class="playback-button--small repeat" v-if="$root.mk.repeatMode == 0" :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" v-if="$root.mk.repeatMode == 0"
: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"
:class="$root.isDisabled() && 'disabled'" v-else-if="$root.mk.repeatMode == 1"
:title="$root.getLz('term.disableRepeatOne')" v-b-tooltip.hover></button>
<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>
</div>
</div>
<div class="app-chrome-item volume display--large">
<div class="input-container">
<button class="volume-button--small volume" @click="app.muteButtonPressed()" :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"
<button class="volume-button--small volume" @click="app.muteButtonPressed()"
: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()">
</div>
</div>
@ -136,7 +157,6 @@
</div>
</script>
<script>

View file

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

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