CHONKY BOY

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -303,10 +303,10 @@
"settings.option.general.resumebehavior.locally.description": "Cider wird die letzte Sitzung auf diesem Rechner fortsetzen.", "settings.option.general.resumebehavior.locally.description": "Cider wird die letzte Sitzung auf diesem Rechner fortsetzen.",
"settings.option.general.resumebehavior.history": "Verlauf", "settings.option.general.resumebehavior.history": "Verlauf",
"settings.option.general.resumebehavior.history.description": "Cider wird den letzten Song aus dem geräteübergreifenden Apple-Music-Verlauf in die Warteschlange stellen.", "settings.option.general.resumebehavior.history.description": "Cider wird den letzten Song aus dem geräteübergreifenden Apple-Music-Verlauf in die Warteschlange stellen.",
"settings.option.general.resumetabs" : "Tab beim Start öffnen", "settings.option.general.resumetabs": "Tab beim Start öffnen",
"settings.option.general.resumetabs.description" : "Wähle welcher Tab beim Starten von Cider geöffnet werden soll.", "settings.option.general.resumetabs.description": "Wähle welcher Tab beim Starten von Cider geöffnet werden soll.",
"settings.option.general.resumetabs.dynamic" : "Dynamisch", "settings.option.general.resumetabs.dynamic": "Dynamisch",
"settings.option.general.resumetabs.dynamic.description" : "Cider wird den zuletzt genutzten Tab öffnen.", "settings.option.general.resumetabs.dynamic.description": "Cider wird den zuletzt genutzten Tab öffnen.",
"settings.option.general.keybindings": "Tastenkombinationen", "settings.option.general.keybindings": "Tastenkombinationen",
"settings.option.general.keybindings.pressCombination": "Drücke eine Kombination aus zwei Tasten um die Tastenkombination zu aktualisieren.", "settings.option.general.keybindings.pressCombination": "Drücke eine Kombination aus zwei Tasten um die Tastenkombination zu aktualisieren.",
"settings.option.general.keybindings.pressEscape": "Drücke Escape um zurückzukehren.", "settings.option.general.keybindings.pressEscape": "Drücke Escape um zurückzukehren.",
@ -396,7 +396,7 @@
"action.selectAll": "Alles auswählen", "action.selectAll": "Alles auswählen",
"action.delete": "Löschen", "action.delete": "Löschen",
"home.syncFavorites": "Sync Favoriten", "home.syncFavorites": "Sync Favoriten",
"term.quit" : "Beenden", "term.quit": "Beenden",
"settings.option.connectivity.lastfmScrobble.filterLoop.description": "Verhindert, dass geloopte Titel gescrobbelt oder in der (Hört Gerade)-Liste auf Last.fm angezeigt werden", "settings.option.connectivity.lastfmScrobble.filterLoop.description": "Verhindert, dass geloopte Titel gescrobbelt oder in der (Hört Gerade)-Liste auf Last.fm angezeigt werden",
"settings.option.connectivity.lastfmScrobble.filterTypes": "Medientypen filtern (Last.fm)", "settings.option.connectivity.lastfmScrobble.filterTypes": "Medientypen filtern (Last.fm)",
"settings.option.connectivity.lastfmScrobble.manualToken": "Last.fm-Token manuell eingeben" "settings.option.connectivity.lastfmScrobble.manualToken": "Last.fm-Token manuell eingeben"

View file

@ -12,8 +12,8 @@
"action.tray.minimize": "Minimise to Tray", "action.tray.minimize": "Minimise to Tray",
"term.tracks": "songs", "term.tracks": "songs",
"term.track": { "term.track": {
"one" : "song", "one": "song",
"other" : "songs" "other": "songs"
}, },
"home.syncFavorites": "Sync Favourites", "home.syncFavorites": "Sync Favourites",
"home.syncFavorites.gettingArtists": "Getting Favourited Artists...", "home.syncFavorites.gettingArtists": "Getting Favourited Artists...",

View file

@ -202,16 +202,16 @@
"term.confirmLogout": "Are you sure you want to logout?", "term.confirmLogout": "Are you sure you want to logout?",
"term.creditDesignedBy": "Designed by ${authorUsername}", "term.creditDesignedBy": "Designed by ${authorUsername}",
"term.discNumber": "Disc ${discNumber}", "term.discNumber": "Disc ${discNumber}",
"term.reload" : "Reload Cider?", "term.reload": "Reload Cider?",
"term.toggleprivate" : "Toggle Private Session", "term.toggleprivate": "Toggle Private Session",
"term.webremote" : "Web Remote", "term.webremote": "Web Remote",
"term.cast" : "Cast", "term.cast": "Cast",
"term.cast2" : "Cast to Devices", "term.cast2": "Cast to Devices",
"term.quit" : "Quit", "term.quit": "Quit",
"term.zoomin" : "Zoom In", "term.zoomin": "Zoom In",
"term.zoomout" : "Zoom Out", "term.zoomout": "Zoom Out",
"term.zoomreset" : "Reset Zoom", "term.zoomreset": "Reset Zoom",
"term.fullscreen" : "Fullscreen", "term.fullscreen": "Fullscreen",
"term.nowPlaying": "Now Playing", "term.nowPlaying": "Now Playing",
"home.syncFavorites": "Sync Favorites", "home.syncFavorites": "Sync Favorites",
"home.syncFavorites.gettingArtists": "Getting Favorited Artists...", "home.syncFavorites.gettingArtists": "Getting Favorited Artists...",
@ -340,10 +340,10 @@
"settings.option.general.resumebehavior.locally.description": "Cider will resume your last session on this machine.", "settings.option.general.resumebehavior.locally.description": "Cider will resume your last session on this machine.",
"settings.option.general.resumebehavior.history": "History", "settings.option.general.resumebehavior.history": "History",
"settings.option.general.resumebehavior.history.description": "Cider will queue the last song from your overall Apple Music history, across devices.", "settings.option.general.resumebehavior.history.description": "Cider will queue the last song from your overall Apple Music history, across devices.",
"settings.option.general.resumetabs" : "Open Tab on Launch", "settings.option.general.resumetabs": "Open Tab on Launch",
"settings.option.general.resumetabs.description" : "You can choose what tab you want to open when you launch Cider.", "settings.option.general.resumetabs.description": "You can choose what tab you want to open when you launch Cider.",
"settings.option.general.resumetabs.dynamic" : "Dynamic", "settings.option.general.resumetabs.dynamic": "Dynamic",
"settings.option.general.resumetabs.dynamic.description" : "Cider will open the tab that you last used.", "settings.option.general.resumetabs.dynamic.description": "Cider will open the tab that you last used.",
"settings.option.general.language.main": "Languages", "settings.option.general.language.main": "Languages",
"settings.option.general.language.fun": "Fun Languages", "settings.option.general.language.fun": "Fun Languages",
"settings.option.general.language.unsorted": "Unsorted", "settings.option.general.language.unsorted": "Unsorted",

View file

@ -201,16 +201,16 @@
"term.confirmLogout": "¿Estás seguro de que quieres cerrar sesión?", "term.confirmLogout": "¿Estás seguro de que quieres cerrar sesión?",
"term.creditDesignedBy": "Diseñado por ${authorUsername}", "term.creditDesignedBy": "Diseñado por ${authorUsername}",
"term.discNumber": "Disco ${discNumber}", "term.discNumber": "Disco ${discNumber}",
"term.reload" : "¿ Recargar Cider ?", "term.reload": "¿ Recargar Cider ?",
"term.toggleprivate" : "Cambiar a Sesión Privada", "term.toggleprivate": "Cambiar a Sesión Privada",
"term.webremote" : "Web Remoto", "term.webremote": "Web Remoto",
"term.cast" : "Transmitir", "term.cast": "Transmitir",
"term.cast2" : "Transmitir a los Dispositivos", "term.cast2": "Transmitir a los Dispositivos",
"term.quit" : "Salir", "term.quit": "Salir",
"term.zoomin" : "Acercar", "term.zoomin": "Acercar",
"term.zoomout" : "Alejar", "term.zoomout": "Alejar",
"term.zoomreset" : "Restablecer", "term.zoomreset": "Restablecer",
"term.fullscreen" : "Pantalla Completa", "term.fullscreen": "Pantalla Completa",
"term.nowPlaying": "Reproduciendo Ahora", "term.nowPlaying": "Reproduciendo Ahora",
"home.syncFavorites": "Sincronizar Favoritos", "home.syncFavorites": "Sincronizar Favoritos",
"home.syncFavorites.gettingArtists": "Consiguiendo Artistas Favoritos...", "home.syncFavorites.gettingArtists": "Consiguiendo Artistas Favoritos...",

View file

@ -201,16 +201,16 @@
"term.confirmLogout": "¿Estás seguro de que quieres cerrar sesión?", "term.confirmLogout": "¿Estás seguro de que quieres cerrar sesión?",
"term.creditDesignedBy": "Diseñado por ${authorUsername}", "term.creditDesignedBy": "Diseñado por ${authorUsername}",
"term.discNumber": "Disco ${discNumber}", "term.discNumber": "Disco ${discNumber}",
"term.reload" : "¿ Recargar Cider ?", "term.reload": "¿ Recargar Cider ?",
"term.toggleprivate" : "Cambiar a Sesión Privada", "term.toggleprivate": "Cambiar a Sesión Privada",
"term.webremote" : "Web Remoto", "term.webremote": "Web Remoto",
"term.cast" : "Transmitir", "term.cast": "Transmitir",
"term.cast2" : "Transmitir a los Dispositivos", "term.cast2": "Transmitir a los Dispositivos",
"term.quit" : "Salir", "term.quit": "Salir",
"term.zoomin" : "Acercar", "term.zoomin": "Acercar",
"term.zoomout" : "Alejar", "term.zoomout": "Alejar",
"term.zoomreset" : "Restablecer", "term.zoomreset": "Restablecer",
"term.fullscreen" : "Pantalla Completa", "term.fullscreen": "Pantalla Completa",
"term.nowPlaying": "Reproduciendo Ahora", "term.nowPlaying": "Reproduciendo Ahora",
"home.syncFavorites": "Sincronizar Favoritos", "home.syncFavorites": "Sincronizar Favoritos",
"home.syncFavorites.gettingArtists": "Consiguiendo Artistas Favoritos...", "home.syncFavorites.gettingArtists": "Consiguiendo Artistas Favoritos...",

View file

@ -309,5 +309,4 @@
"share.platform.email": "Sähköposti", "share.platform.email": "Sähköposti",
"share.platform.songLink": "Kopioi song.link", "share.platform.songLink": "Kopioi song.link",
"share.platform.clipboard": "Kopioi linkki" "share.platform.clipboard": "Kopioi linkki"
} }

View file

@ -195,16 +195,16 @@
"term.confirmLogout": "Apakah Anda yakin ingin keluar??", "term.confirmLogout": "Apakah Anda yakin ingin keluar??",
"term.creditDesignedBy": "Dirancang oleh ${authorUsername}", "term.creditDesignedBy": "Dirancang oleh ${authorUsername}",
"term.discNumber": "Kaset ${discNumber}", "term.discNumber": "Kaset ${discNumber}",
"term.reload" : "Muat ulang Cider?", "term.reload": "Muat ulang Cider?",
"term.toggleprivate" : "Nyalakan Sesi Pribadi", "term.toggleprivate": "Nyalakan Sesi Pribadi",
"term.webremote" : "Remot Web", "term.webremote": "Remot Web",
"term.cast" : "Transmisi", "term.cast": "Transmisi",
"term.cast2" : "Transmisikan ke Perangkat", "term.cast2": "Transmisikan ke Perangkat",
"term.quit" : "Keluar", "term.quit": "Keluar",
"term.zoomin" : "Perbesar", "term.zoomin": "Perbesar",
"term.zoomout" : "Perkecil", "term.zoomout": "Perkecil",
"term.zoomreset" : "Atur Ulang", "term.zoomreset": "Atur Ulang",
"term.fullscreen" : "Layar Penuh", "term.fullscreen": "Layar Penuh",
"home.syncFavorites": "Sinkronkan Favorit", "home.syncFavorites": "Sinkronkan Favorit",
"home.syncFavorites.gettingArtists": "Mendapatkan artis favorit", "home.syncFavorites.gettingArtists": "Mendapatkan artis favorit",
"home.title": "Beranda", "home.title": "Beranda",
@ -331,10 +331,10 @@
"settings.option.general.resumebehavior.locally.description": "Cider akan melanjutkan sesi terakhir Anda di perangkat ini.", "settings.option.general.resumebehavior.locally.description": "Cider akan melanjutkan sesi terakhir Anda di perangkat ini.",
"settings.option.general.resumebehavior.history": "Riwayat", "settings.option.general.resumebehavior.history": "Riwayat",
"settings.option.general.resumebehavior.history.description": "Cider akan melanjutkan lagu terakhir dari riwayat Apple Music di seluruh perangkat Anda.", "settings.option.general.resumebehavior.history.description": "Cider akan melanjutkan lagu terakhir dari riwayat Apple Music di seluruh perangkat Anda.",
"settings.option.general.resumetabs" : "Buka Tab ketika Diluncurkan", "settings.option.general.resumetabs": "Buka Tab ketika Diluncurkan",
"settings.option.general.resumetabs.description" : "Anda dapat memilih tab apa yang akan dibuka ketika Anda membuka Cider.", "settings.option.general.resumetabs.description": "Anda dapat memilih tab apa yang akan dibuka ketika Anda membuka Cider.",
"settings.option.general.resumetabs.dynamic" : "Dinamis", "settings.option.general.resumetabs.dynamic": "Dinamis",
"settings.option.general.resumetabs.dynamic.description" : "Cider akan membuka tab yang terakhir digunakan", "settings.option.general.resumetabs.dynamic.description": "Cider akan membuka tab yang terakhir digunakan",
"settings.option.general.language.main": "Bahasa", "settings.option.general.language.main": "Bahasa",
"settings.option.general.language.fun": "Bahasa Candaan", "settings.option.general.language.fun": "Bahasa Candaan",
"settings.option.general.language.unsorted": "Tidak disortir", "settings.option.general.language.unsorted": "Tidak disortir",

View file

@ -341,10 +341,10 @@
"settings.option.general.resumebehavior.locally.description": "このコンピューターでの最終セッションを復元", "settings.option.general.resumebehavior.locally.description": "このコンピューターでの最終セッションを復元",
"settings.option.general.resumebehavior.history": "履歴", "settings.option.general.resumebehavior.history": "履歴",
"settings.option.general.resumebehavior.history.description": "Apple Musicの履歴から曲を復元", "settings.option.general.resumebehavior.history.description": "Apple Musicの履歴から曲を復元",
"settings.option.general.resumetabs" : "起動時にタブを開く", "settings.option.general.resumetabs": "起動時にタブを開く",
"settings.option.general.resumetabs.description" : "Ciderを起動したときに開くタブを選択することができます", "settings.option.general.resumetabs.description": "Ciderを起動したときに開くタブを選択することができます",
"settings.option.general.resumetabs.dynamic" : "ダイナミック", "settings.option.general.resumetabs.dynamic": "ダイナミック",
"settings.option.general.resumetabs.dynamic.description" : "最後のセッションで開いていたタブを開きます", "settings.option.general.resumetabs.dynamic.description": "最後のセッションで開いていたタブを開きます",
"settings.option.general.language.main": "メイン", "settings.option.general.language.main": "メイン",
"settings.option.general.language.fun": "荒らし", "settings.option.general.language.fun": "荒らし",
"settings.option.general.language.unsorted": "未分類", "settings.option.general.language.unsorted": "未分類",

View file

@ -209,16 +209,16 @@
"term.confirmLogout": "Вы уверены, что хотите выйти?", "term.confirmLogout": "Вы уверены, что хотите выйти?",
"term.creditDesignedBy": "Разработано ${authorUsername}", "term.creditDesignedBy": "Разработано ${authorUsername}",
"term.discNumber": "Диск ${discNumber}", "term.discNumber": "Диск ${discNumber}",
"term.reload" : "Перезагрузить Cider?", "term.reload": "Перезагрузить Cider?",
"term.toggleprivate" : "Переключить частную сессию", "term.toggleprivate": "Переключить частную сессию",
"term.webremote" : "Web Remote", "term.webremote": "Web Remote",
"term.cast" : "Транслировать", "term.cast": "Транслировать",
"term.cast2" : "Трансляция на устройства", "term.cast2": "Трансляция на устройства",
"term.quit" : "Выход", "term.quit": "Выход",
"term.zoomin" : "Приблизить", "term.zoomin": "Приблизить",
"term.zoomout" : "Отдалить", "term.zoomout": "Отдалить",
"term.zoomreset" : "Сбросить масштаб", "term.zoomreset": "Сбросить масштаб",
"term.fullscreen" : "Полный экран", "term.fullscreen": "Полный экран",
"term.nowPlaying": "Сейчас играет", "term.nowPlaying": "Сейчас играет",
"home.syncFavorites": "Синхронизировать", "home.syncFavorites": "Синхронизировать",
"home.syncFavorites.gettingArtists": "Получение отслеживаемых исполнителей...", "home.syncFavorites.gettingArtists": "Получение отслеживаемых исполнителей...",
@ -347,10 +347,10 @@
"settings.option.general.resumebehavior.locally.description": "Cider возобновит ваш последний сеанс на этом компьютере.", "settings.option.general.resumebehavior.locally.description": "Cider возобновит ваш последний сеанс на этом компьютере.",
"settings.option.general.resumebehavior.history": "История", "settings.option.general.resumebehavior.history": "История",
"settings.option.general.resumebehavior.history.description": "Cider поставит в очередь последнюю песню из вашей общей истории Apple Music на разных устройствах.", "settings.option.general.resumebehavior.history.description": "Cider поставит в очередь последнюю песню из вашей общей истории Apple Music на разных устройствах.",
"settings.option.general.resumetabs" : "Раздел при запуске", "settings.option.general.resumetabs": "Раздел при запуске",
"settings.option.general.resumetabs.description" : "Вы можете выбрать, какой раздел будет открыться при запуске Cider.", "settings.option.general.resumetabs.description": "Вы можете выбрать, какой раздел будет открыться при запуске Cider.",
"settings.option.general.resumetabs.dynamic" : "Динамически", "settings.option.general.resumetabs.dynamic": "Динамически",
"settings.option.general.resumetabs.dynamic.description" : "Cider откроет последний использованный раздел.", "settings.option.general.resumetabs.dynamic.description": "Cider откроет последний использованный раздел.",
"settings.option.general.language.main": "Языки", "settings.option.general.language.main": "Языки",
"settings.option.general.language.fun": "Забавные языки", "settings.option.general.language.fun": "Забавные языки",
"settings.option.general.language.unsorted": "Неотсортированные", "settings.option.general.language.unsorted": "Неотсортированные",

View file

@ -202,16 +202,16 @@
"term.confirmLogout": "Are you sure you want to logout?", "term.confirmLogout": "Are you sure you want to logout?",
"term.creditDesignedBy": "Designed by ${authorUsername}", "term.creditDesignedBy": "Designed by ${authorUsername}",
"term.discNumber": "Disc ${discNumber}", "term.discNumber": "Disc ${discNumber}",
"term.reload" : "Reload Cider?", "term.reload": "Reload Cider?",
"term.toggleprivate" : "Toggle Private Session", "term.toggleprivate": "Toggle Private Session",
"term.webremote" : "Web Remote", "term.webremote": "Web Remote",
"term.cast" : "Cast", "term.cast": "Cast",
"term.cast2" : "Cast to Devices", "term.cast2": "Cast to Devices",
"term.quit" : "Quit", "term.quit": "Quit",
"term.zoomin" : "Zoom In", "term.zoomin": "Zoom In",
"term.zoomout" : "Zoom Out", "term.zoomout": "Zoom Out",
"term.zoomreset" : "Reset Zoom", "term.zoomreset": "Reset Zoom",
"term.fullscreen" : "Fullscreen", "term.fullscreen": "Fullscreen",
"term.nowPlaying": "Now Playing", "term.nowPlaying": "Now Playing",
"home.syncFavorites": "Sync Favorites", "home.syncFavorites": "Sync Favorites",
"home.syncFavorites.gettingArtists": "Getting Favorited Artists...", "home.syncFavorites.gettingArtists": "Getting Favorited Artists...",
@ -340,10 +340,10 @@
"settings.option.general.resumebehavior.locally.description": "Cider will resume your last session on this machine.", "settings.option.general.resumebehavior.locally.description": "Cider will resume your last session on this machine.",
"settings.option.general.resumebehavior.history": "History", "settings.option.general.resumebehavior.history": "History",
"settings.option.general.resumebehavior.history.description": "Cider will queue the last song from your overall Apple Music history, across devices.", "settings.option.general.resumebehavior.history.description": "Cider will queue the last song from your overall Apple Music history, across devices.",
"settings.option.general.resumetabs" : "Open Tab on Launch", "settings.option.general.resumetabs": "Open Tab on Launch",
"settings.option.general.resumetabs.description" : "You can choose what tab you want to open when you launch Cider.", "settings.option.general.resumetabs.description": "You can choose what tab you want to open when you launch Cider.",
"settings.option.general.resumetabs.dynamic" : "Dynamic", "settings.option.general.resumetabs.dynamic": "Dynamic",
"settings.option.general.resumetabs.dynamic.description" : "Cider will open the tab that you last used.", "settings.option.general.resumetabs.dynamic.description": "Cider will open the tab that you last used.",
"settings.option.general.language.main": "Languages", "settings.option.general.language.main": "Languages",
"settings.option.general.language.fun": "Fun Languages", "settings.option.general.language.fun": "Fun Languages",
"settings.option.general.language.unsorted": "Unsorted", "settings.option.general.language.unsorted": "Unsorted",

View file

@ -186,16 +186,16 @@
"term.confirmLogout": "你确定要退出登录吗?", "term.confirmLogout": "你确定要退出登录吗?",
"term.creditDesignedBy": "由 ${authorUsername} 设计", "term.creditDesignedBy": "由 ${authorUsername} 设计",
"term.discNumber": "碟 ${discNumber}", "term.discNumber": "碟 ${discNumber}",
"term.reload" : "重新载入 Cider?", "term.reload": "重新载入 Cider?",
"term.toggleprivate": "切换隐身聆听", "term.toggleprivate": "切换隐身聆听",
"term.webremote": "远程控制", "term.webremote": "远程控制",
"term.cast": "投射", "term.cast": "投射",
"term.cast2" : "投射到设备", "term.cast2": "投射到设备",
"term.quit" : "退出应用", "term.quit": "退出应用",
"term.zoomin" : "放大", "term.zoomin": "放大",
"term.zoomout" : "缩小", "term.zoomout": "缩小",
"term.zoomreset" : "重置缩放", "term.zoomreset": "重置缩放",
"term.fullscreen" : "全屏模式", "term.fullscreen": "全屏模式",
"term.nowPlaying": "正在播放", "term.nowPlaying": "正在播放",
"home.syncFavorites": "同步喜爱艺人", "home.syncFavorites": "同步喜爱艺人",
"home.syncFavorites.gettingArtists": "获取喜爱艺人...", "home.syncFavorites.gettingArtists": "获取喜爱艺人...",

View file

@ -19,10 +19,10 @@
"term.accountSettings": "帳戶設定", "term.accountSettings": "帳戶設定",
"term.logout": "登出", "term.logout": "登出",
"term.login": "登入", "term.login": "登入",
"term.quit" : "結束", "term.quit": "結束",
"term.about": "關於", "term.about": "關於",
"term.cast" : "投影", "term.cast": "投影",
"term.cast2" : "投影到裝置", "term.cast2": "投影到裝置",
"term.privateSession": "私人時間", "term.privateSession": "私人時間",
"term.queue": "待播清單", "term.queue": "待播清單",
"term.lyrics": "歌詞", "term.lyrics": "歌詞",
@ -146,14 +146,14 @@
"term.plugins": "模組", "term.plugins": "模組",
"term.pluginMenu": "模組選單", "term.pluginMenu": "模組選單",
"term.pluginMenu.none": "沒有交互式模組", "term.pluginMenu.none": "沒有交互式模組",
"term.fullscreen" : "全螢幕模式", "term.fullscreen": "全螢幕模式",
"home.title": "首頁", "home.title": "首頁",
"home.recentlyPlayed": "最近播放", "home.recentlyPlayed": "最近播放",
"home.recentlyAdded": "最近加入", "home.recentlyAdded": "最近加入",
"home.artistsFeed": "藝人追蹤", "home.artistsFeed": "藝人追蹤",
"home.artistsFeed.noArtist": "追蹤你喜愛的藝人來取得他們的最新發行歌曲。", "home.artistsFeed.noArtist": "追蹤你喜愛的藝人來取得他們的最新發行歌曲。",
"home.syncFavorites" : "同步追蹤" , "home.syncFavorites": "同步追蹤",
"home.syncFavorites.gettingArtists" : "取得追蹤的藝人歌手列表... " , "home.syncFavorites.gettingArtists": "取得追蹤的藝人歌手列表... ",
"home.madeForYou": "為您推薦", "home.madeForYou": "為您推薦",
"home.friendsListeningTo": "朋友正在聆聽", "home.friendsListeningTo": "朋友正在聆聽",
"home.followedArtists": "追蹤的藝人", "home.followedArtists": "追蹤的藝人",

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

@ -1,9 +1,9 @@
//@ts-nocheck //@ts-nocheck
var util = require('util'); var util = require("util");
// var debug = require('debug')('castv2-client'); // var debug = require('debug')('castv2-client');
var Application = require('castv2-client').Application; var Application = require("castv2-client").Application;
var MediaController = require('castv2-client').MediaController; var MediaController = require("castv2-client").MediaController;
var CiderCastController = require('./castcontroller'); var CiderCastController = require("./castcontroller");
function CiderReceiver(client, session) { function CiderReceiver(client, session) {
Application.apply(this, arguments); Application.apply(this, arguments);
@ -11,70 +11,69 @@ function CiderReceiver(client, session) {
this.media = this.createController(MediaController); this.media = this.createController(MediaController);
this.mediaReceiver = this.createController(CiderCastController); this.mediaReceiver = this.createController(CiderCastController);
this.media.on('status', onstatus); this.media.on("status", onstatus);
var self = this; var self = this;
function onstatus(status) { function onstatus(status) {
self.emit('status', status); self.emit("status", status);
} }
} }
// FE96A351 // FE96A351
// 27E1334F // 27E1334F
CiderReceiver.APP_ID = 'FE96A351'; CiderReceiver.APP_ID = "FE96A351";
util.inherits(CiderReceiver, Application); util.inherits(CiderReceiver, Application);
CiderReceiver.prototype.getStatus = function(callback) { CiderReceiver.prototype.getStatus = function (callback) {
this.media.getStatus.apply(this.media, arguments); this.media.getStatus.apply(this.media, arguments);
}; };
CiderReceiver.prototype.load = function(media, options, callback) { CiderReceiver.prototype.load = function (media, options, callback) {
this.media.load.apply(this.media, arguments); this.media.load.apply(this.media, arguments);
}; };
CiderReceiver.prototype.play = function(callback) { CiderReceiver.prototype.play = function (callback) {
this.media.play.apply(this.media, arguments); this.media.play.apply(this.media, arguments);
}; };
CiderReceiver.prototype.pause = function(callback) { CiderReceiver.prototype.pause = function (callback) {
this.media.pause.apply(this.media, arguments); this.media.pause.apply(this.media, arguments);
}; };
CiderReceiver.prototype.stop = function(callback) { CiderReceiver.prototype.stop = function (callback) {
this.media.stop.apply(this.media, arguments); this.media.stop.apply(this.media, arguments);
}; };
CiderReceiver.prototype.seek = function(currentTime, callback) { CiderReceiver.prototype.seek = function (currentTime, callback) {
this.media.seek.apply(this.media, arguments); this.media.seek.apply(this.media, arguments);
}; };
CiderReceiver.prototype.queueLoad = function(items, options, callback) { CiderReceiver.prototype.queueLoad = function (items, options, callback) {
this.media.queueLoad.apply(this.media, arguments); this.media.queueLoad.apply(this.media, arguments);
}; };
CiderReceiver.prototype.queueInsert = function(items, options, callback) { CiderReceiver.prototype.queueInsert = function (items, options, callback) {
this.media.queueInsert.apply(this.media, arguments); this.media.queueInsert.apply(this.media, arguments);
}; };
CiderReceiver.prototype.queueRemove = function(itemIds, options, callback) { CiderReceiver.prototype.queueRemove = function (itemIds, options, callback) {
this.media.queueRemove.apply(this.media, arguments); this.media.queueRemove.apply(this.media, arguments);
}; };
CiderReceiver.prototype.queueReorder = function(itemIds, options, callback) { CiderReceiver.prototype.queueReorder = function (itemIds, options, callback) {
this.media.queueReorder.apply(this.media, arguments); this.media.queueReorder.apply(this.media, arguments);
}; };
CiderReceiver.prototype.queueUpdate = function(items, callback) { CiderReceiver.prototype.queueUpdate = function (items, callback) {
this.media.queueUpdate.apply(this.media, arguments); this.media.queueUpdate.apply(this.media, arguments);
}; };
CiderReceiver.prototype.sendIp = function(opts){ CiderReceiver.prototype.sendIp = function (opts) {
this.mediaReceiver.sendIp.apply(this.mediaReceiver, arguments); this.mediaReceiver.sendIp.apply(this.mediaReceiver, arguments);
}; };
CiderReceiver.prototype.kill = function(opts){ CiderReceiver.prototype.kill = function (opts) {
this.mediaReceiver.kill.apply(this.mediaReceiver, arguments); this.mediaReceiver.kill.apply(this.mediaReceiver, arguments);
}; };

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

@ -1,7 +1,7 @@
@colorMixRate: 1%; @colorMixRate: 1%;
@transparencyRate: 50%; @transparencyRate: 50%;
@keyColor : #fc3c44; @keyColor: #fc3c44;
@ciderColor: #ff2654; @ciderColor: #ff2654;
@baseColor: #1e1e1e; @baseColor: #1e1e1e;
@baseColorMix: mix(@baseColor, transparent, @transparencyRate); @baseColorMix: mix(@baseColor, transparent, @transparencyRate);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,7 @@
zoom: 0.95; zoom: 0.95;
} }
.app-sidebar-content { .app-sidebar-content {
padding:0px; padding: 0px;
.app-sidebar-header-text { .app-sidebar-header-text {
padding: 6px 10px; padding: 6px 10px;

View file

@ -5,36 +5,35 @@
#app.twopanel { #app.twopanel {
--chromeHeight1: 46px; --chromeHeight1: 46px;
--chromeHeight2: 90px; --chromeHeight2: 90px;
--chromeHeight : calc(var(--chromeHeight1) + var(--chromeHeight2)); --chromeHeight: calc(var(--chromeHeight1) + var(--chromeHeight2));
.modular-fs .app-drawer .lyric-footer { .modular-fs .app-drawer .lyric-footer {
bottom: var(--chromeHeight2); bottom: var(--chromeHeight2);
} }
.app-chrome { .app-chrome {
&:not(.chrome-bottom) { &:not(.chrome-bottom) {
.app-chrome--center { .app-chrome--center {
flex: 1; flex: 1;
.top-nav-group { .top-nav-group {
background : #1e1e1e99; background: #1e1e1e99;
border : 1px solid lighten(@baseColor, 8); border: 1px solid lighten(@baseColor, 8);
border-radius: 12px; border-radius: 12px;
display : flex; display: flex;
height : 32px; height: 32px;
.app-sidebar-item { .app-sidebar-item {
background-color: #1e1e1e00; background-color: #1e1e1e00;
border-radius : 10px !important; border-radius: 10px !important;
border : 0px; border: 0px;
min-width : 120px; min-width: 120px;
padding : 6px; padding: 6px;
justify-content : center; justify-content: center;
align-items : center; align-items: center;
margin : 0px; margin: 0px;
height : 100%; height: 100%;
position : relative; position: relative;
white-space: nowrap; white-space: nowrap;
._svg-icon { ._svg-icon {
@ -42,18 +41,18 @@
} }
&:before { &:before {
--dist : 1px; --dist: 1px;
content : ''; content: "";
position : absolute; position: absolute;
top : var(--dist); top: var(--dist);
left : var(--dist); left: var(--dist);
right : var(--dist); right: var(--dist);
bottom : var(--dist); bottom: var(--dist);
background-color: #fff; background-color: #fff;
opacity : 0; opacity: 0;
border-radius : 10px; border-radius: 10px;
transform : scale(0.5); transform: scale(0.5);
transition : transform 0.2s ease-in-out, opacity 0.2s ease-in-out; transition: transform 0.2s ease-in-out, opacity 0.2s ease-in-out;
} }
&:after { &:after {
@ -65,8 +64,8 @@
&:before { &:before {
transition: transform 0.1s ease-in-out, opacity 0.1s ease-in-out; transition: transform 0.1s ease-in-out, opacity 0.1s ease-in-out;
opacity : .1; opacity: 0.1;
transform : scale(1); transform: scale(1);
} }
} }
@ -74,15 +73,15 @@
background-color: transparent; background-color: transparent;
&:before { &:before {
opacity : .2; opacity: 0.2;
transform: scale(1); transform: scale(1);
} }
} }
&.md-btn-primary { &.md-btn-primary {
box-shadow : 0px 0px 0px 1px lighten(@baseColor, @colorMixRate * 8); box-shadow: 0px 0px 0px 1px lighten(@baseColor, @colorMixRate * 8);
background-color: lighten(@baseColor, @colorMixRate * 5); background-color: lighten(@baseColor, @colorMixRate * 5);
z-index : 1; z-index: 1;
} }
} }
} }
@ -90,7 +89,7 @@
} }
.app-mainmenu { .app-mainmenu {
width : 30px; width: 30px;
height: 30px; height: 30px;
} }
@ -101,11 +100,10 @@
height: var(--chromeHeight1); height: var(--chromeHeight1);
&.chrome-bottom { &.chrome-bottom {
background : var(--color2); background: var(--color2);
-webkit-app-region: no-drag; -webkit-app-region: no-drag;
height : var(--chromeHeight2); height: var(--chromeHeight2);
box-shadow : 0px -2px 6px rgb(20 20 20 / 12%), box-shadow: 0px -2px 6px rgb(20 20 20 / 12%), 0px -1px 0px 0px rgb(200 200 200 / 12%);
0px -1px 0px 0px rgb(200 200 200 / 12%);
z-index: 4; z-index: 4;
.app-chrome-playback-duration-bottom { .app-chrome-playback-duration-bottom {
@ -116,33 +114,33 @@
} }
.col-sm-auto { .col-sm-auto {
width : 4em; width: 4em;
display : flex; display: flex;
justify-content: center; justify-content: center;
align-items : center; align-items: center;
font-size : 0.8em; font-size: 0.8em;
} }
input[type=range] { input[type="range"] {
appearance : none; appearance: none;
width : 100%; width: 100%;
height : 5px; height: 5px;
background-color: rgb(200 200 200 / 10%); background-color: rgb(200 200 200 / 10%);
border-radius : 6px; border-radius: 6px;
box-shadow : 0px 0px 0px 1px rgba(0 0 0 / 10%); box-shadow: 0px 0px 0px 1px rgba(0 0 0 / 10%);
align-self : center; align-self: center;
&::-webkit-slider-thumb { &::-webkit-slider-thumb {
opacity : 0; opacity: 0;
transform : scale(1); transform: scale(1);
-webkit-appearance: none; -webkit-appearance: none;
appearance : none; appearance: none;
width : 16px; width: 16px;
height : 16px; height: 16px;
border-radius : 100%; border-radius: 100%;
background : var(--keyColor); background: var(--keyColor);
cursor : default; cursor: default;
transition : opacity .10s var(--appleEase), transform .10s var(--appleEase); transition: opacity 0.1s var(--appleEase), transform 0.1s var(--appleEase);
} }
&:hover { &:hover {
@ -169,38 +167,37 @@
.playback-button.play, .playback-button.play,
.playback-button.pause, .playback-button.pause,
.playback-button.stop { .playback-button.stop {
width : 42px; width: 42px;
height : 42px; height: 42px;
border-radius: 50%; border-radius: 50%;
margin : 6px; margin: 6px;
} }
.app-chrome--center { .app-chrome--center {
display : flex; display: flex;
flex-direction: column; flex-direction: column;
.app-chrome-playback-controls { .app-chrome-playback-controls {
display : flex; display: flex;
z-index : 1; z-index: 1;
// margin-bottom: 12px; // margin-bottom: 12px;
} }
.app-chrome-playback-duration { .app-chrome-playback-duration {
position : relative; position: relative;
width : 80%; width: 80%;
-webkit-app-region: no-drag; -webkit-app-region: no-drag;
height : 16px; height: 16px;
.song-progress { .song-progress {
@bgColor : transparent; @bgColor: transparent;
height : 16px; height: 16px;
position : absolute; position: absolute;
bottom : 4px; bottom: 4px;
left : 0px; left: 0px;
right : 4px; right: 4px;
background: @bgColor; background: @bgColor;
z-index : 0; z-index: 0;
.song-duration { .song-duration {
display: flex; display: flex;
@ -208,64 +205,63 @@
.song-duration p { .song-duration p {
font-weight: 400; font-weight: 400;
font-size : 10px; font-size: 10px;
height : 1.2em; height: 1.2em;
line-height: 1.3em; line-height: 1.3em;
overflow : hidden; overflow: hidden;
margin : 0 0 0 0.25em; margin: 0 0 0 0.25em;
} }
&:hover { &:hover {
>input[type=range] { > input[type="range"] {
&::-webkit-slider-thumb { &::-webkit-slider-thumb {
opacity : 1; opacity: 1;
transform: scale(1); transform: scale(1);
z-index : 1; z-index: 1;
} }
} }
} }
input[type=range] { input[type="range"] {
appearance : none; appearance: none;
width : 100%; width: 100%;
height : 4px; height: 4px;
background-color: rgb(200 200 200 / 10%); background-color: rgb(200 200 200 / 10%);
border-radius : 2px; border-radius: 2px;
&::-webkit-slider-thumb { &::-webkit-slider-thumb {
opacity : 0; opacity: 0;
transform : scale(0.5); transform: scale(0.5);
-webkit-appearance: none; -webkit-appearance: none;
appearance : none; appearance: none;
width : 12px; width: 12px;
height : 12px; height: 12px;
border-radius : 100%; border-radius: 100%;
background : var(--keyColor); background: var(--keyColor);
cursor : default; cursor: default;
transition : opacity .10s var(--appleEase), transform .10s var(--appleEase); transition: opacity 0.1s var(--appleEase), transform 0.1s var(--appleEase);
} }
} }
} }
} }
} }
.app-chrome--left { .app-chrome--left {
width : 30%; width: 30%;
justify-content : flex-start; justify-content: flex-start;
align-items : flex-start; align-items: flex-start;
-webkit-app-region: no-drag !important; -webkit-app-region: no-drag !important;
.playback-controls { .playback-controls {
-webkit-app-region: no-drag !important; -webkit-app-region: no-drag !important;
.artwork { .artwork {
--offset : 20px; --offset: 20px;
--marginOffset: 2; --marginOffset: 2;
--size : calc(var(--chromeHeight2) - var(--offset)); --size: calc(var(--chromeHeight2) - var(--offset));
width : var(--size); width: var(--size);
height : var(--size); height: var(--size);
margin : 0 calc(var(--offset) / var(--marginOffset)) 0 calc(var(--offset) / var(--marginOffset)); margin: 0 calc(var(--offset) / var(--marginOffset)) 0 calc(var(--offset) / var(--marginOffset));
.mediaitem-artwork, .mediaitem-artwork,
img { img {
@ -275,13 +271,13 @@
.playback-info { .playback-info {
align-items: flex-start; align-items: flex-start;
margin : 6px; margin: 6px;
.song-name { .song-name {
text-align : left; text-align: left;
font-size : 0.8em; font-size: 0.8em;
font-weight : 500; font-weight: 500;
width : 100%; width: 100%;
-webkit-mask-image: linear-gradient(-90deg, transparent 0%, transparent 10%, black 20%); -webkit-mask-image: linear-gradient(-90deg, transparent 0%, transparent 10%, black 20%);
} }
@ -306,23 +302,22 @@
.song-artist-album-content { .song-artist-album-content {
text-align: left; text-align: left;
font-size : 12px; font-size: 12px;
} }
} }
width: 100%;
width : 100%; height: 100%;
height : 100%;
max-width: 100%; max-width: 100%;
border : 0px; border: 0px;
} }
flex: 0 0 auto; flex: 0 0 auto;
} }
.app-chrome--right { .app-chrome--right {
width : 30%; width: 30%;
flex : 0 0 auto; flex: 0 0 auto;
padding-right: 8px; padding-right: 8px;
} }
} }
@ -334,7 +329,6 @@
} }
} }
// screen width is less than 768px // screen width is less than 768px
@media (max-width: 1100px) { @media (max-width: 1100px) {
#app.twopanel .app-chrome:not(.chrome-bottom) .app-chrome--center { #app.twopanel .app-chrome:not(.chrome-bottom) .app-chrome--center {

View file

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

View file

@ -22,14 +22,13 @@
--chromeHeight1: 70px; --chromeHeight1: 70px;
.app-content-container { .app-content-container {
width:100%; width: 100%;
height:100%; height: 100%;
#app-content { #app-content {
width:100%; width: 100%;
height:100%; height: 100%;
.fs-search { .fs-search {
.search-input--icon { .search-input--icon {
width: 4em; width: 4em;
background-size: 40%; background-size: 40%;
@ -38,7 +37,7 @@
input { input {
padding-left: 2em; padding-left: 2em;
font-size: 2em; font-size: 2em;
border-radius: var(--mediaItemRadius) border-radius: var(--mediaItemRadius);
} }
} }
} }
@ -56,42 +55,41 @@
z-index: 9999; z-index: 9999;
.top-nav-group { .top-nav-group {
background : #1e1e1e99; background: #1e1e1e99;
border : 1px solid lighten(@baseColor, 8); border: 1px solid lighten(@baseColor, 8);
border-radius: 12px; border-radius: 12px;
display : flex; display: flex;
height : 55px; height: 55px;
width: 90%; width: 90%;
backdrop-filter: var(--glassFilter); backdrop-filter: var(--glassFilter);
.app-sidebar-item { .app-sidebar-item {
background-color: #1e1e1e00; background-color: #1e1e1e00;
border-radius : 10px !important; border-radius: 10px !important;
border : 0px; border: 0px;
min-width : 120px; min-width: 120px;
padding : 6px; padding: 6px;
justify-content : center; justify-content: center;
align-items : center; align-items: center;
margin : 0px; margin: 0px;
height : 100%; height: 100%;
position : relative; position: relative;
font-size: 1.1em; font-size: 1.1em;
font-weight: 500; font-weight: 500;
&:before { &:before {
--dist : 1px; --dist: 1px;
content : ''; content: "";
position : absolute; position: absolute;
top : var(--dist); top: var(--dist);
left : var(--dist); left: var(--dist);
right : var(--dist); right: var(--dist);
bottom : var(--dist); bottom: var(--dist);
background-color: #fff; background-color: #fff;
opacity : 0; opacity: 0;
border-radius : 10px; border-radius: 10px;
transform : scale(0.5); transform: scale(0.5);
transition : transform 0.2s ease-in-out, opacity 0.2s ease-in-out; transition: transform 0.2s ease-in-out, opacity 0.2s ease-in-out;
} }
&:after { &:after {
@ -103,8 +101,8 @@
&:before { &:before {
transition: transform 0.1s ease-in-out, opacity 0.1s ease-in-out; transition: transform 0.1s ease-in-out, opacity 0.1s ease-in-out;
opacity : .1; opacity: 0.1;
transform : scale(1); transform: scale(1);
} }
} }
@ -112,15 +110,15 @@
background-color: transparent; background-color: transparent;
&:before { &:before {
opacity : .2; opacity: 0.2;
transform: scale(1); transform: scale(1);
} }
} }
&.md-btn-primary { &.md-btn-primary {
box-shadow : 0px 0px 0px 1px lighten(@baseColor, @colorMixRate * 8); box-shadow: 0px 0px 0px 1px lighten(@baseColor, @colorMixRate * 8);
background-color: lighten(@baseColor, @colorMixRate * 5); background-color: lighten(@baseColor, @colorMixRate * 5);
z-index : 1; z-index: 1;
} }
} }
} }
@ -164,7 +162,7 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
width: 100% width: 100%;
} }
.volume-button--small { .volume-button--small {
@ -178,7 +176,7 @@
width: 30px; width: 30px;
border: 0px; border: 0px;
box-shadow: unset; box-shadow: unset;
opacity: 0.70; opacity: 0.7;
background-image: url("./assets/feather/volume-2.svg"); background-image: url("./assets/feather/volume-2.svg");
} }
@ -190,7 +188,7 @@
background-image: url("./assets/feather/volume.svg"); background-image: url("./assets/feather/volume.svg");
} }
input[type=range] { input[type="range"] {
-webkit-appearance: none; -webkit-appearance: none;
height: 4px; height: 4px;
background: rgba(255, 255, 255, 0.4); background: rgba(255, 255, 255, 0.4);
@ -228,7 +226,6 @@
} }
} }
.background { .background {
position: absolute; position: absolute;
background-size: cover; background-size: cover;
@ -247,13 +244,10 @@
.bg-artwork-container .bg-artwork { .bg-artwork-container .bg-artwork {
filter: brightness(85%) saturate(95%) blur(180px) contrast(0.9) opacity(0.9); filter: brightness(85%) saturate(95%) blur(180px) contrast(0.9) opacity(0.9);
} }
} }
} }
.lyrics-col { .lyrics-col {
height: 62vh; height: 62vh;
display: flex; display: flex;
justify-content: center; justify-content: center;
@ -278,11 +272,9 @@
.lyric-line { .lyric-line {
font-size: 35px; font-size: 35px;
} }
} }
.queue-col { .queue-col {
width: 60vh; width: 60vh;
height: 62vh; height: 62vh;
@ -361,7 +353,8 @@
} }
.app-playback-controls { .app-playback-controls {
.song-artist, .song-name { .song-artist,
.song-name {
font-weight: 600; font-weight: 600;
text-align: center; text-align: center;
font-size: 0.9em; font-size: 0.9em;
@ -414,8 +407,6 @@
width: 100%; width: 100%;
text-align: center; text-align: center;
} }
} }
.app-playback-controls .song-progress { .app-playback-controls .song-progress {
@ -436,7 +427,7 @@
} }
&:hover { &:hover {
> input[type=range] { > input[type="range"] {
&::-webkit-slider-thumb { &::-webkit-slider-thumb {
opacity: 1; opacity: 1;
transform: scale(1); transform: scale(1);
@ -445,7 +436,7 @@
} }
} }
input[type=range] { input[type="range"] {
appearance: none; appearance: none;
width: 100%; width: 100%;
height: 4px; height: 4px;
@ -462,7 +453,7 @@
border-radius: 100%; border-radius: 100%;
background: var(--songProgressColor); background: var(--songProgressColor);
cursor: default; cursor: default;
transition: opacity .10s var(--appleEase), transform .10s var(--appleEase); transition: opacity 0.1s var(--appleEase), transform 0.1s var(--appleEase);
} }
&::-moz-range-thumb { &::-moz-range-thumb {
@ -482,7 +473,6 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
} }
.cd-mediaitem-square { .cd-mediaitem-square {
@ -556,18 +546,18 @@
.playlist-page .playlist-display { .playlist-page .playlist-display {
width: 100%; width: 100%;
max-width: 500px; max-width: 500px;
flex:1; flex: 1;
text-align: center; text-align: center;
.playlistInfo { .playlistInfo {
>.row { > .row {
justify-content: center; justify-content: center;
} }
} }
.playlist-controls { .playlist-controls {
div { div {
width:100%; width: 100%;
} }
} }
} }

View file

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

View file

@ -6,53 +6,53 @@ body[platform="linux"] {
.window-controls { .window-controls {
justify-content: flex-end; justify-content: flex-end;
align-items : center; align-items: center;
padding-right : 6px; padding-right: 6px;
>div { > div {
--iconSize: 16px; --iconSize: 16px;
&.close, &.close,
&.minmax, &.minmax,
&.minimize, &.minimize,
&.minmax.restore { &.minmax.restore {
background-image: unset!important; background-image: unset !important;
position : relative; position: relative;
display : grid; display: grid;
align-content : center; align-content: center;
text-align : center; text-align: center;
height : 36px!important; height: 36px !important;
width : 36px!important; width: 36px !important;
border-radius : 50px; border-radius: 50px;
transition: background-color .1s ease-in-out; transition: background-color 0.1s ease-in-out;
&:hover { &:hover {
background: rgb(200 200 200 / 10%)!important; background: rgb(200 200 200 / 10%) !important;
} }
} }
&.close::before { &.close::before {
font-family: "codicon"; font-family: "codicon";
font-size : var(--iconSize); font-size: var(--iconSize);
content : ""; content: "";
} }
&.minmax::before { &.minmax::before {
font-family: "codicon"; font-family: "codicon";
font-size : var(--iconSize); font-size: var(--iconSize);
content : ""; content: "";
} }
&.minimize::before { &.minimize::before {
font-family: "codicon"; font-family: "codicon";
font-size : var(--iconSize); font-size: var(--iconSize);
content : ""; content: "";
} }
&.restore::before { &.restore::before {
font-family: "codicon"; font-family: "codicon";
font-size : var(--iconSize); font-size: var(--iconSize);
content : ""; content: "";
} }
} }
} }

View file

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

View file

@ -1,7 +1,4 @@
#app.macosemu { #app.macosemu {
.app-chrome .app-chrome-item > .window-controls-macos { .app-chrome .app-chrome-item > .window-controls-macos {
@controlSize: 12px; @controlSize: 12px;
display: flex; display: flex;
@ -42,7 +39,7 @@
} }
} }
} }
.usermenu-body{ .usermenu-body {
left: calc(100vw - 260px); left: calc(100vw - 260px);
position: relative; position: relative;
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

@ -1,19 +1,19 @@
&:not(.modular-fs) { &:not(.modular-fs) {
.app-drawer { .app-drawer {
border-radius: 0px; border-radius: 0px;
top : 0; top: 0;
right : 0; right: 0;
height : 100%; height: 100%;
box-shadow : unset; box-shadow: unset;
border-left : 1px solid var(--color2); border-left: 1px solid var(--color2);
background : var(--color1); background: var(--color1);
margin-right : 0px; margin-right: 0px;
position : relative; position: relative;
} }
.drawertransition-enter-active, .drawertransition-enter-active,
.drawertransition-leave-active { .drawertransition-leave-active {
transition: margin .25s var(--appleEase), opacity .25s var(--appleEase); transition: margin 0.25s var(--appleEase), opacity 0.25s var(--appleEase);
} }
.drawertransition-enter, .drawertransition-enter,
@ -24,12 +24,12 @@
@media screen and (max-width: 1120px) { @media screen and (max-width: 1120px) {
.app-drawer { .app-drawer {
margin-right: 0px; margin-right: 0px;
position : absolute; position: absolute;
} }
.drawertransition-enter-active, .drawertransition-enter-active,
.drawertransition-leave-active { .drawertransition-leave-active {
transition: right .25s var(--appleEase), opacity .25s var(--appleEase); transition: right 0.25s var(--appleEase), opacity 0.25s var(--appleEase);
} }
.drawertransition-enter, .drawertransition-enter,
@ -37,5 +37,4 @@
right: -300px; right: -300px;
} }
} }
} }

View file

@ -1,11 +1,11 @@
body { body {
--ciderShadow-Generic : var(--mediaItemShadow); --ciderShadow-Generic: var(--mediaItemShadow);
--mediaItemShadow-Shadow : var(--mediaItemShadow); --mediaItemShadow-Shadow: var(--mediaItemShadow);
--mediaItemShadow-ShadowSubtle: var(--mediaItemShadow); --mediaItemShadow-ShadowSubtle: var(--mediaItemShadow);
} }
.bg-artwork-container { .bg-artwork-container {
display : none; display: none;
animation: none !important; animation: none !important;
.bg-artwork { .bg-artwork {
@ -13,7 +13,6 @@ body {
} }
} }
.app-chrome:not(.chrome-bottom) { .app-chrome:not(.chrome-bottom) {
backdrop-filter: unset; backdrop-filter: unset;
background-color: var(--baseColor); background-color: var(--baseColor);
@ -23,20 +22,21 @@ body {
background: rgb(30 30 30); background: rgb(30 30 30);
} }
.menu-panel .menu-panel-body .menu-option::before { .menu-panel .menu-panel-body .menu-option::before {
transition: unset!important; transition: unset !important;
} }
#app.twopanel .app-chrome:not(.chrome-bottom) .app-chrome--center .top-nav-group .app-sidebar-item:before { #app.twopanel .app-chrome:not(.chrome-bottom) .app-chrome--center .top-nav-group .app-sidebar-item:before {
transition: unset!important; transition: unset !important;
} }
.playback-button:before, .playback-button--small:before { .playback-button:before,
transition: unset!important; .playback-button--small:before {
transition: unset !important;
} }
.floating-header { .floating-header {
backdrop-filter: unset!important; backdrop-filter: unset !important;
background: rgb(0 0 0 / 80%)!important; background: rgb(0 0 0 / 80%) !important;
} }
.replaycard-enter-active, .replaycard-enter-active,
@ -46,7 +46,7 @@ body {
.replaycard-enter, .replaycard-enter,
.replaycard-leave-to { .replaycard-leave-to {
opacity : 0; opacity: 0;
transform: translateY(20px); transform: translateY(20px);
} }
@ -57,13 +57,13 @@ body {
.modal-enter, .modal-enter,
.modal-leave-to { .modal-leave-to {
opacity : 0; opacity: 0;
transform: scale(1.10); transform: scale(1.1);
} }
.wpfade-enter-active, .wpfade-enter-active,
.wpfade-leave-active { .wpfade-leave-active {
transition: opacity .1s var(--appleEase); transition: opacity 0.1s var(--appleEase);
} }
.wpfade-enter, .wpfade-enter,
@ -73,37 +73,36 @@ body {
.wpfade_transform-enter-active, .wpfade_transform-enter-active,
.wpfade_transform-leave-active { .wpfade_transform-leave-active {
transition : unset; transition: unset;
will-change: unset; will-change: unset;
} }
.wpfade_transform-enter { .wpfade_transform-enter {
opacity : 0; opacity: 0;
transform : unset; transform: unset;
will-change: unset; will-change: unset;
} }
.wpfade_transform-leave-to { .wpfade_transform-leave-to {
opacity : 0; opacity: 0;
transform : unset; transform: unset;
will-change: unset; will-change: unset;
} }
.wpfade_transform_backwards-enter-active, .wpfade_transform_backwards-enter-active,
.wpfade_transform_backwards-leave-active { .wpfade_transform_backwards-leave-active {
transition: unset; transition: unset;
} }
.wpfade_transform_backwards-enter { .wpfade_transform_backwards-enter {
opacity : 0; opacity: 0;
transform : unset; transform: unset;
will-change: unset; will-change: unset;
} }
.wpfade_transform_backwards-leave-to { .wpfade_transform_backwards-leave-to {
opacity : 0; opacity: 0;
transform : unset; transform: unset;
will-change: unset; will-change: unset;
} }
@ -114,7 +113,7 @@ body {
.fabfade-enter, .fabfade-enter,
.fabfade-leave-to { .fabfade-leave-to {
opacity : 0; opacity: 0;
transform: scale(0.5); transform: scale(0.5);
} }
@ -125,11 +124,10 @@ body {
.fsModeSwitch-enter, .fsModeSwitch-enter,
.fsModeSwitch-leave-to { .fsModeSwitch-leave-to {
transform: scale(1.10); transform: scale(1.1);
opacity : 0; opacity: 0;
} }
.drawertransition-enter-active, .drawertransition-enter-active,
.drawertransition-leave-active { .drawertransition-leave-active {
transition: unset; transition: unset;

View file

@ -1,49 +1,47 @@
@panelColorsFallback: rgb(30 30 30); @panelColorsFallback: rgb(30 30 30);
@panelColors : rgb(30 30 30 / 45%); @panelColors: rgb(30 30 30 / 45%);
.menu-panel { .menu-panel {
.menu-panel-body { .menu-panel-body {
background-color: @panelColors; background-color: @panelColors;
backdrop-filter : blur(32px) saturate(180%); backdrop-filter: blur(32px) saturate(180%);
&.menu-panel-body-down { &.menu-panel-body-down {
animation: menuInDown .10s var(--appleEase); animation: menuInDown 0.1s var(--appleEase);
} }
&.menu-panel-body-up { &.menu-panel-body-up {
animation: menuInUp .10s var(--appleEase); animation: menuInUp 0.1s var(--appleEase);
} }
} }
@keyframes menuInUp { @keyframes menuInUp {
0% { 0% {
opacity : 0; opacity: 0;
transform : translateY(-10px) translate3d(0,0,0); transform: translateY(-10px) translate3d(0, 0, 0);
background: @panelColorsFallback; background: @panelColorsFallback;
} }
100% { 100% {
opacity : 1; opacity: 1;
transform : translateY(0); transform: translateY(0);
background: @panelColors; background: @panelColors;
} }
} }
@keyframes menuInDown { @keyframes menuInDown {
0% { 0% {
opacity : 0; opacity: 0;
transform : translateY(10px) translate3d(0,0,0); transform: translateY(10px) translate3d(0, 0, 0);
background: @panelColorsFallback; background: @panelColorsFallback;
} }
100% { 100% {
opacity : 1; opacity: 1;
transform : translateY(0); transform: translateY(0);
background: @panelColors; background: @panelColors;
} }
} }
} }
.cd-mediaitem-square { .cd-mediaitem-square {
@ -55,46 +53,45 @@
} }
.cd-mediaitem-square:not(.mediaitem-card) { .cd-mediaitem-square:not(.mediaitem-card) {
transition : transform .2s var(--appleEase); transition: transform 0.2s var(--appleEase);
transition-delay: .1s; transition-delay: 0.1s;
.artwork-container {
}
.artwork-container {} .info-rect {
}
.info-rect {}
.artwork-container, .artwork-container,
.info-rect { .info-rect {
transition : transform .22s var(--appleEase); transition: transform 0.22s var(--appleEase);
transition-delay: .05s; transition-delay: 0.05s;
} }
.artwork-container { .artwork-container {
transform : scale(0.962) translateZ(0); transform: scale(0.962) translateZ(0);
transition : transform .1s var(--appleEase); transition: transform 0.1s var(--appleEase);
transition-delay: 0s; transition-delay: 0s;
transform-origin: center; transform-origin: center;
} }
&:hover { &:hover {
.artwork-container { .artwork-container {
transform : scale(1.0); transform: scale(1);
transition : transform .1s var(--appleEase); transition: transform 0.1s var(--appleEase);
transition-delay: 0s; transition-delay: 0s;
transform-origin: center; transform-origin: center;
} }
.info-rect { .info-rect {
z-index : 1; z-index: 1;
transition : transform .1s var(--appleEase); transition: transform 0.1s var(--appleEase);
transition-delay: 0s; transition-delay: 0s;
transform : translateY(8px) translate3d(0,0,0); transform: translateY(8px) translate3d(0, 0, 0);
} }
} }
&:active { &:active {
} }
} }
@ -107,16 +104,15 @@
.wpfade_transform-enter { .wpfade_transform-enter {
opacity: 0; opacity: 0;
transform: translateX(50%) translate3d(0,0,0); transform: translateX(50%) translate3d(0, 0, 0);
will-change: opacity, transform; will-change: opacity, transform;
} }
.wpfade_transform-leave-to { .wpfade_transform-leave-to {
opacity: 0; opacity: 0;
transform: translateX(-50%) translate3d(0,0,0); transform: translateX(-50%) translate3d(0, 0, 0);
will-change: opacity, transform; will-change: opacity, transform;
} }
.wpfade_transform_backwards-enter-active, .wpfade_transform_backwards-enter-active,
.wpfade_transform_backwards-leave-active { .wpfade_transform_backwards-leave-active {
--transitionTime: 0.2s; --transitionTime: 0.2s;
@ -125,11 +121,11 @@
.wpfade_transform_backwards-enter { .wpfade_transform_backwards-enter {
opacity: 0; opacity: 0;
transform: translateX(-50%) translate3d(0,0,0); transform: translateX(-50%) translate3d(0, 0, 0);
will-change: opacity, transform; will-change: opacity, transform;
} }
.wpfade_transform_backwards-leave-to { .wpfade_transform_backwards-leave-to {
opacity: 0; opacity: 0;
transform: translateX(50%) translate3d(0,0,0); transform: translateX(50%) translate3d(0, 0, 0);
will-change: opacity, transform; will-change: opacity, transform;
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -53,7 +53,7 @@
aria-role="presentation" focusable="false" @click="exitMV();fullscreen(false);"> aria-role="presentation" focusable="false" @click="exitMV();fullscreen(false);">
<path <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" 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"/> fill-rule="nonzero" />
</svg> </svg>
</div> </div>
<div id="captions" v-if="lyricon">{{((lyricon) ? ((lyrics.length > 0 && lyrics[currentLyricsLine] && <div id="captions" v-if="lyricon">{{((lyricon) ? ((lyrics.length > 0 && lyrics[currentLyricsLine] &&
@ -93,7 +93,8 @@
:class="{'active': this.cfg.audio.volume == 0}" :class="{'active': this.cfg.audio.volume == 0}"
:title="cfg.audio.muted ? $root.getLz('term.unmute') : $root.getLz('term.mute')" :title="cfg.audio.muted ? $root.getLz('term.unmute') : $root.getLz('term.mute')"
v-b-tooltip.hover></button> v-b-tooltip.hover></button>
<input type="range" @wheel="volumeWheel" :step="cfg.audio.volumeStep" min="0" :max="cfg.audio.maxVolume" <input type="range" @wheel="volumeWheel" :step="cfg.audio.volumeStep" min="0"
:max="cfg.audio.maxVolume"
v-model="mk.volume" v-if="typeof mk.volume != 'undefined'" @change="checkMuteChange()" v-model="mk.volume" v-if="typeof mk.volume != 'undefined'" @change="checkMuteChange()"
v-b-tooltip.hover :title="formatVolumeTooltip()"> v-b-tooltip.hover :title="formatVolumeTooltip()">
</div> </div>

View file

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

View file

@ -1,27 +1,30 @@
<script type="text/x-template" id="airplay-modal"> <script type="text/x-template" id="airplay-modal">
<div class="spatialproperties-panel castmenu modal-fullscreen airplay-modal" @click.self="close()" @contextmenu.self="close()"> <div class="spatialproperties-panel castmenu modal-fullscreen airplay-modal" @click.self="close()"
@contextmenu.self="close()">
<div class="modal-window airplay-modal"> <div class="modal-window airplay-modal">
<div class="modal-header"> <div class="modal-header">
<div class="modal-title">{{'Enter password'}}</div> <div class="modal-title">{{'Enter password'}}</div>
<button class="close-btn" @click="close()" :aria-label="this.$root.getLz('action.close')"></button> <button class="close-btn" @click="close()" :aria-label="this.$root.getLz('action.close')"></button>
</div> </div>
<div class="modal-content" style="overflow-y: overlay; padding: 3%"> <div class="modal-content" style="overflow-y: overlay; padding: 3%">
<input type="text" v-model="passcode"/> <input type="text" v-model="passcode" />
</div> </div>
<div class="md-footer"> <div class="md-footer">
<div class="row"> <div class="row">
<div class="col" > <div class="col">
<button style="width:100%" @click="enterPassword()" class="md-btn md-btn-block md-btn-primary">{{'OK'}}</button> <button style="width:100%" @click="enterPassword()" class="md-btn md-btn-block md-btn-primary">
{{'OK'}}
</button>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
</script> </script>
<script> <script>
Vue.component('airplay-modal', { Vue.component('airplay-modal', {
template: '#airplay-modal', template: '#airplay-modal',
data: function () { data: function() {
return { return {
passcode: '', passcode: '',
} }
@ -34,7 +37,7 @@
}, },
enterPassword() { enterPassword() {
console.log('Entered passCode: ', this.passcode) console.log('Entered passCode: ', this.passcode)
ipcRenderer.send("setAirPlayPasscode",this.passcode) ipcRenderer.send("setAirPlayPasscode", this.passcode)
this.close() this.close()
} }
} }

View file

@ -7,7 +7,7 @@
<script> <script>
Vue.component('animatedartwork-view', { Vue.component('animatedartwork-view', {
template: '#animatedartwork-view', template: '#animatedartwork-view',
data: function () { data: function() {
return { return {
app: this.$root, app: this.$root,
hls: null, hls: null,
@ -30,7 +30,7 @@
return return
} }
if (this.video) { if (this.video) {
this.$nextTick(function () { this.$nextTick(function() {
var config = { var config = {
backBufferLength: 0, backBufferLength: 0,
enableWebVTT: false, enableWebVTT: false,
@ -54,8 +54,8 @@
persistentState: "required" persistentState: "required"
} }
let p = { let p = {
platformInfo: {requiresCDMAttachOnStart: !0, maxSecurityLevel: d, keySystemConfig: h}, platformInfo: { requiresCDMAttachOnStart: !0, maxSecurityLevel: d, keySystemConfig: h },
appData: {serviceName: "Apple Music"} appData: { serviceName: "Apple Music" }
} }
this.hls.attachMedia(this.$refs.video); this.hls.attachMedia(this.$refs.video);
@ -63,26 +63,31 @@
let u = this.hls; let u = this.hls;
var quality = app.cfg.visual.animated_artwork_qualityLevel; var quality = app.cfg.visual.animated_artwork_qualityLevel;
setTimeout(() => { setTimeout(() => {
let levelsnum = u.levels.map((level)=>{return level.width}) let levelsnum = u.levels.map((level) => {
return level.width
})
if (levelsnum.length > 0) { if (levelsnum.length > 0) {
let qualities = [] let qualities = []
let qualities2 = [] let qualities2 = []
for (let i = 0; i < levelsnum.length; i++) { for (let i = 0; i < levelsnum.length; i++) {
if (qualities2.indexOf(levelsnum[i]) == -1) { if (qualities2.indexOf(levelsnum[i]) == -1) {
qualities.push({level: i, quality: levelsnum[i]}) qualities.push({ level: i, quality: levelsnum[i] })
qualities2.push(levelsnum[i]) qualities2.push(levelsnum[i])
} }
} }
let actualnum = Math.floor(qualities[qualities.length -1 ].level * (quality / 4)) let actualnum = Math.floor(qualities[qualities.length - 1].level * (quality / 4))
if (quality != 0 ){ if (quality != 0) {
quality = qualities[Math.min(actualnum,qualities.length -1 )].level quality = qualities[Math.min(actualnum, qualities.length - 1)].level
} }
if (quality == 4 ){ if (quality == 4) {
quality = qualities[qualities.length - 1].level quality = qualities[qualities.length - 1].level
} }
} }
try{ try {
this.hls.loadLevel = parseInt( quality || 1);} catch(e){}},200) this.hls.loadLevel = parseInt(quality || 1);
} catch (e) {
}
}, 200)
} }
}) })
} }

View file

@ -15,14 +15,14 @@
<transition <transition
<% if(env.appRoutes[i].onEnter) { <% if(env.appRoutes[i].onEnter) {
%> %>
v-on:enter="<%- env.appRoutes[i].onEnter %>" v-on:enter="<%- env.appRoutes[i].onEnter; %>"
<% <%
} }
%> %>
:name="$root.chrome.desiredPageTransition"> :name="$root.chrome.desiredPageTransition">
<template <template
v-if="<%- env.appRoutes[i].condition %>"> v-if="<%- env.appRoutes[i].condition; %>">
<%- env.appRoutes[i].component %> <%- env.appRoutes[i].component; %>
</template> </template>
</transition> </transition>
<% } %> <% } %>
@ -40,12 +40,11 @@
<script> <script>
Vue.component('app-content-area', { Vue.component('app-content-area', {
template: '#app-content-area', template: '#app-content-area',
data: function () { data: function() {
return { return {
scrollPos: 0 scrollPos: 0
} }
}, },
methods: { methods: {}
}
}); });
</script> </script>

View file

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

View file

@ -1,13 +1,13 @@
<script type="text/x-template" id="artwork-material"> <script type="text/x-template" id="artwork-material">
<div class="artworkMaterial"> <div class="artworkMaterial">
<mediaitem-artwork :url="src" :size="500" v-for="image in images"/> <mediaitem-artwork :url="src" :size="500" v-for="image in images" />
</div> </div>
</script> </script>
<script> <script>
Vue.component('artwork-material', { Vue.component('artwork-material', {
template: '#artwork-material', template: '#artwork-material',
data: function () { data: function() {
return { return {
src: "" src: ""
} }
@ -31,7 +31,6 @@
default: '2' default: '2'
} }
}, },
methods: { methods: {}
}
}); });
</script> </script>

View file

@ -4,7 +4,8 @@
<div class="modal-window"> <div class="modal-window">
<div class="modal-header"> <div class="modal-header">
<div class="modal-title">{{app.getLz('term.audioControls')}}</div> <div class="modal-title">{{app.getLz('term.audioControls')}}</div>
<button class="close-btn" @click="app.modals.audioControls = false" :aria-label="app.getLz('action.close')"></button> <button class="close-btn" @click="app.modals.audioControls = false"
:aria-label="app.getLz('action.close')"></button>
</div> </div>
<div class="modal-content"> <div class="modal-content">
<div class="md-option-line"> <div class="md-option-line">
@ -14,7 +15,7 @@
<div class="md-option-segment md-option-segment_auto percent"> <div class="md-option-segment md-option-segment_auto percent">
<input type="number" <input type="number"
style="width: 100%; text-align: center; margin-right: 5px;" min="0" style="width: 100%; text-align: center; margin-right: 5px;" min="0"
step="2" v-model="volume"/> step="2" v-model="volume" />
</div> </div>
</div> </div>
<div class="md-option-line"> <div class="md-option-line">
@ -23,7 +24,7 @@
</div> </div>
<div class="md-option-segment md-option-segment_auto percent"> <div class="md-option-segment md-option-segment_auto percent">
<input type="number" style="width: 100%; text-align: center; margin-right: 5px;" min="0" <input type="number" style="width: 100%; text-align: center; margin-right: 5px;" min="0"
v-model="volumeStep"/> v-model="volumeStep" />
</div> </div>
</div> </div>
<div class="md-option-line"> <div class="md-option-line">
@ -32,7 +33,7 @@
</div> </div>
<div class="md-option-segment md-option-segment_auto percent"> <div class="md-option-segment md-option-segment_auto percent">
<input type="number" style="width: 100%; text-align: center; margin-right: 5px;" min="0" <input type="number" style="width: 100%; text-align: center; margin-right: 5px;" min="0"
v-model="maxVolume"/> v-model="maxVolume" />
</div> </div>
</div> </div>
<div class="md-option-line"> <div class="md-option-line">
@ -41,7 +42,7 @@
</div> </div>
<div class="md-option-segment md-option-segment_auto"> <div class="md-option-segment md-option-segment_auto">
<label> <label>
<input type="checkbox" v-model="app.cfg.audio.advanced" switch/> <input type="checkbox" v-model="app.cfg.audio.advanced" switch />
</label> </label>
</div> </div>
</div> </div>
@ -53,7 +54,7 @@
<script> <script>
Vue.component('audio-controls', { Vue.component('audio-controls', {
template: '#audio-controls', template: '#audio-controls',
data: function () { data: function() {
return { return {
app: this.$root, app: this.$root,
maxVolume: this.$root.cfg.audio.maxVolume * 100, maxVolume: this.$root.cfg.audio.maxVolume * 100,
@ -62,7 +63,7 @@
} }
}, },
watch: { watch: {
maxVolume: function (newValue, _oldValue) { maxVolume: function(newValue, _oldValue) {
if (newValue > 100) { if (newValue > 100) {
newValue = 100 newValue = 100
} else { } else {
@ -72,7 +73,7 @@
this.maxVolume = newValue this.maxVolume = newValue
console.log(newValue, _oldValue) console.log(newValue, _oldValue)
}, },
volume: function (newValue, _oldValue) { volume: function(newValue, _oldValue) {
if (newValue > this.maxVolume) { if (newValue > this.maxVolume) {
newValue = 100 newValue = 100
} else { } else {
@ -82,7 +83,7 @@
this.volume = newValue this.volume = newValue
console.log(newValue, _oldValue) console.log(newValue, _oldValue)
}, },
volumeStep: function (newValue, _oldValue) { volumeStep: function(newValue, _oldValue) {
if (newValue > this.maxVolume) { if (newValue > this.maxVolume) {
newValue = 100 newValue = 100
} else { } else {

View file

@ -4,7 +4,8 @@
<div class="modal-window"> <div class="modal-window">
<div class="modal-header"> <div class="modal-header">
<div class="modal-title">{{app.getLz('settings.option.audio.changePlaybackRate')}}</div> <div class="modal-title">{{app.getLz('settings.option.audio.changePlaybackRate')}}</div>
<button class="close-btn" @click="app.modals.audioPlaybackRate = false" :aria-label="app.getLz('action.close')"></button> <button class="close-btn" @click="app.modals.audioPlaybackRate = false"
:aria-label="app.getLz('action.close')"></button>
</div> </div>
<div class="modal-content"> <div class="modal-content">
<div class="md-option-line"> <div class="md-option-line">
@ -15,7 +16,8 @@
{{playbackRate}} × {{playbackRate}} ×
</div> </div>
<div class="md-option-segment md-option-segment_auto"> <div class="md-option-segment md-option-segment_auto">
<input type="range" :step="0.05" min="0.25" :max="2" @wheel="playbackRateWheel" v-model="playbackRate"> <input type="range" :step="0.05" min="0.25" :max="2" @wheel="playbackRateWheel"
v-model="playbackRate">
</div> </div>
</div> </div>
</div> </div>
@ -26,14 +28,14 @@
<script> <script>
Vue.component('audio-playbackrate', { Vue.component('audio-playbackrate', {
template: '#audio-playbackrate', template: '#audio-playbackrate',
data: function () { data: function() {
return { return {
app: this.$root, app: this.$root,
playbackRate: this.$root.cfg.audio.playbackRate playbackRate: this.$root.cfg.audio.playbackRate
} }
}, },
watch: { watch: {
playbackRate: function (newValue, _oldValue) { playbackRate: function(newValue, _oldValue) {
this.saveValue(newValue); this.saveValue(newValue);
} }
}, },

View file

@ -1,10 +1,12 @@
<script type="text/x-template" id="audio-settings"> <script type="text/x-template" id="audio-settings">
<template> <template>
<div class="modal-fullscreen addtoplaylist-panel" @click.self="app.modals.audioSettings = false" @contextmenu.self="app.modals.audioSettings = false"> <div class="modal-fullscreen addtoplaylist-panel" @click.self="app.modals.audioSettings = false"
@contextmenu.self="app.modals.audioSettings = false">
<div class="modal-window"> <div class="modal-window">
<div class="modal-header"> <div class="modal-header">
<div class="modal-title">{{app.getLz('term.audioSettings')}}</div> <div class="modal-title">{{app.getLz('term.audioSettings')}}</div>
<button class="close-btn" @click="app.modals.audioSettings = false" :aria-label="app.getLz('action.close')"></button> <button class="close-btn" @click="app.modals.audioSettings = false"
:aria-label="app.getLz('action.close')"></button>
</div> </div>
<div class="modal-content"> <div class="modal-content">
<button class="playlist-item" <button class="playlist-item"
@ -36,13 +38,14 @@
<script> <script>
Vue.component('audio-settings', { Vue.component('audio-settings', {
template: '#audio-settings', template: '#audio-settings',
data: function () { data: function() {
return { return {
app: this.$root, app: this.$root,
} }
}, },
props: {}, props: {},
mounted() {}, mounted() {
},
methods: { methods: {
openEqualizer() { openEqualizer() {
app.modals.equalizer = true app.modals.equalizer = true

View file

@ -1,5 +1,5 @@
<script type="text/x-template" id="castmenu"> <script type="text/x-template" id="castmenu">
<div class="spatialproperties-panel castmenu modal-fullscreen" @click.self="close()" @contextmenu.self="close()"> <div class="spatialproperties-panel castmenu modal-fullscreen" @click.self="close()" @contextmenu.self="close()">
<div class="modal-window"> <div class="modal-window">
<div class="modal-header"> <div class="modal-header">
<div class="modal-title">{{$root.getLz('action.cast.todevices')}}</div> <div class="modal-title">{{$root.getLz('action.cast.todevices')}}</div>
@ -16,11 +16,18 @@
<br> <br>
<small>{{ device.host }}</small> <small>{{ device.host }}</small>
</div> </div>
<div class="md-option-segment_auto" style="display: flex;justify-content: center;align-items: center" v-if="activeCasts.includes(device)"> <div class="md-option-segment_auto"
style="display: flex;justify-content: center;align-items: center"
v-if="activeCasts.includes(device)">
Connected Connected
</div> </div>
<div class="md-option-segment_auto" v-else style="display: flex;justify-content: center;align-items: center"> <div class="md-option-segment_auto" v-else
<svg width="20" height="20" viewBox="0 0 34 34" fill="#fff" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" class="castPlayIndicator"><path d="M28.228,18.327l-16.023,8.983c-0.99,0.555 -2.205,-0.17 -2.205,-1.318l0,-17.984c0,-1.146 1.215,-1.873 2.205,-1.317l16.023,8.982c1.029,0.577 1.029,2.077 0,2.654Z" style="fill-rule:nonzero"></path></svg> style="display: flex;justify-content: center;align-items: center">
<svg width="20" height="20" viewBox="0 0 34 34" fill="#fff" version="1.1"
xmlns="http://www.w3.org/2000/svg"
xml:space="preserve" class="castPlayIndicator"><path
d="M28.228,18.327l-16.023,8.983c-0.99,0.555 -2.205,-0.17 -2.205,-1.318l0,-17.984c0,-1.146 1.215,-1.873 2.205,-1.317l16.023,8.982c1.029,0.577 1.029,2.077 0,2.654Z"
style="fill-rule:nonzero"></path></svg>
</div> </div>
</div> </div>
</template> </template>
@ -33,11 +40,12 @@
</div> </div>
</template> </template>
</div> </div>
<div class="md-labeltext" >{{$root.getLz('action.cast.airplay')}}</div> <div class="md-labeltext">{{$root.getLz('action.cast.airplay')}}</div>
<div class="md-option-container" style="margin-top: 12px;margin-bottom: 12px;overflow-y: scroll;"> <div class="md-option-container" style="margin-top: 12px;margin-bottom: 12px;overflow-y: scroll;">
<div class="md-option-line"> <div class="md-option-line">
<div class="md-option-segment"> <div class="md-option-segment">
{{'EXPERIMENTAL!!! Supports Homepods / Apple TVs / Shairport for now, AirPlay on Samsung/LG/Sony devices will be added later'}} {{'EXPERIMENTAL!!! Supports Homepods / Apple TVs / Shairport for now, AirPlay on
Samsung/LG/Sony devices will be added later'}}
<!-- {{$root.getLz('action.cast.airplay.underdevelopment')}} --> <!-- {{$root.getLz('action.cast.airplay.underdevelopment')}} -->
<template v-if="true" v-for="(device) in devices.airplay"> <template v-if="true" v-for="(device) in devices.airplay">
<div class="md-option-line" style="cursor: pointer" @click="setAirPlayCast(device)"> <div class="md-option-line" style="cursor: pointer" @click="setAirPlayCast(device)">
@ -46,11 +54,19 @@
<br> <br>
<small>{{ device.host }}</small> <small>{{ device.host }}</small>
</div> </div>
<div class="md-option-segment_auto" style="display: flex;justify-content: center;align-items: center" v-if="activeCasts.includes(device)"> <div class="md-option-segment_auto"
style="display: flex;justify-content: center;align-items: center"
v-if="activeCasts.includes(device)">
Connected Connected
</div> </div>
<div class="md-option-segment_auto" v-else style="display: flex;justify-content: center;align-items: center"> <div class="md-option-segment_auto" v-else
<svg width="20" height="20" viewBox="0 0 34 34" fill="#fff" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" class="castPlayIndicator"><path d="M28.228,18.327l-16.023,8.983c-0.99,0.555 -2.205,-0.17 -2.205,-1.318l0,-17.984c0,-1.146 1.215,-1.873 2.205,-1.317l16.023,8.982c1.029,0.577 1.029,2.077 0,2.654Z" style="fill-rule:nonzero"></path></svg> style="display: flex;justify-content: center;align-items: center">
<svg width="20" height="20" viewBox="0 0 34 34" fill="#fff" version="1.1"
xmlns="http://www.w3.org/2000/svg"
xml:space="preserve"
class="castPlayIndicator"><path
d="M28.228,18.327l-16.023,8.983c-0.99,0.555 -2.205,-0.17 -2.205,-1.318l0,-17.984c0,-1.146 1.215,-1.873 2.205,-1.317l16.023,8.982c1.029,0.577 1.029,2.077 0,2.654Z"
style="fill-rule:nonzero"></path></svg>
</div> </div>
</div> </div>
</template> </template>
@ -61,20 +77,24 @@
<div class="md-footer"> <div class="md-footer">
<div class="row"> <div class="row">
<div class="col" v-if="activeCasts.length != 0"> <div class="col" v-if="activeCasts.length != 0">
<button style="width:100%" @click="stopCasting()" class="md-btn md-btn-block md-btn-primary">{{$root.getLz('action.cast.stop')}}</button> <button style="width:100%" @click="stopCasting()" class="md-btn md-btn-block md-btn-primary">
{{$root.getLz('action.cast.stop')}}
</button>
</div> </div>
<div class="col"> <div class="col">
<button style="width:100%" class="md-btn md-btn-block" @click="scan()">{{$root.getLz('action.cast.scan')}}</button> <button style="width:100%" class="md-btn md-btn-block" @click="scan()">
{{$root.getLz('action.cast.scan')}}
</button>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
</script> </script>
<script> <script>
Vue.component('castmenu', { Vue.component('castmenu', {
template: '#castmenu', template: '#castmenu',
data: function () { data: function() {
return { return {
devices: { devices: {
cast: [], cast: [],
@ -88,10 +108,11 @@
mounted() { mounted() {
this.scan(); this.scan();
}, },
watch:{ watch: {
activeCasts: function (newVal, oldVal) { activeCasts: function(newVal, oldVal) {
this.$root.activeCasts = this.activeCasts; this.$root.activeCasts = this.activeCasts;
}}, }
},
methods: { methods: {
close() { close() {
this.$root.modals.castMenu = false this.$root.modals.castMenu = false
@ -100,7 +121,7 @@
let self = this; let self = this;
this.scanning = true; this.scanning = true;
ipcRenderer.send('getChromeCastDevices', ''); ipcRenderer.send('getChromeCastDevices', '');
ipcRenderer.send("getAirplayDevice","") ipcRenderer.send("getAirplayDevice", "")
setTimeout(() => { setTimeout(() => {
self.devices.cast = ipcRenderer.sendSync("getKnownCastDevices"); self.devices.cast = ipcRenderer.sendSync("getKnownCastDevices");
self.devices.airplay = ipcRenderer.sendSync("getKnownAirplayDevices"); self.devices.airplay = ipcRenderer.sendSync("getKnownAirplayDevices");
@ -116,7 +137,7 @@
}, },
setAirPlayCast(device) { setAirPlayCast(device) {
this.activeCasts.push(device); this.activeCasts.push(device);
ipcRenderer.send("performAirplayPCM",device.host,device.port,null,"","","","",device.txt,device.airplay2) ipcRenderer.send("performAirplayPCM", device.host, device.port, null, "", "", "", "", device.txt, device.airplay2)
}, },
stopCasting() { stopCasting() {
CiderAudio.stopAudio(); CiderAudio.stopAudio();

View file

@ -4,12 +4,14 @@
<div class="modal-window"> <div class="modal-window">
<div class="modal-header"> <div class="modal-header">
<div class="modal-title">{{app.getLz('action.addToLibrary')}}</div> <div class="modal-title">{{app.getLz('action.addToLibrary')}}</div>
<button class="close-btn" @click="app.resetState()" :aria-label="app.getLz('action.close')"></button> <button class="close-btn" @click="app.resetState()"
:aria-label="app.getLz('action.close')"></button>
</div> </div>
<div class="modal-content"> <div class="modal-content">
<button class="playlist-item" <button class="playlist-item"
:class="{ focused: playlist.id == focused }" :class="{ focused: playlist.id == focused }"
@click="addToPlaylist(playlist.id)" style="width:100%;" v-for="playlist in playlistSorted" v-if="playlist.attributes.canEdit && playlist.type != 'library-playlist-folders'"> @click="addToPlaylist(playlist.id)" style="width:100%;" v-for="playlist in playlistSorted"
v-if="playlist.attributes.canEdit && playlist.type != 'library-playlist-folders'">
<div class="icon"><%- include("../svg/playlist.svg") %></div> <div class="icon"><%- include("../svg/playlist.svg") %></div>
<div class="name">{{ playlist.attributes.name }}</div> <div class="name">{{ playlist.attributes.name }}</div>
</button> </button>
@ -35,7 +37,7 @@
<script> <script>
Vue.component('add-to-playlist', { Vue.component('add-to-playlist', {
template: '#add-to-playlist', template: '#add-to-playlist',
data: function () { data: function() {
return { return {
playlistSorted: [], playlistSorted: [],
searchQuery: "", searchQuery: "",

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