Merge branch 'ciderapp:develop' into develop
This commit is contained in:
commit
4105f84e6f
26 changed files with 872 additions and 433 deletions
|
@ -42,7 +42,7 @@
|
||||||
"airtunes2": "git+https://github.com/vapormusic/node_airtunes2.git#hap",
|
"airtunes2": "git+https://github.com/vapormusic/node_airtunes2.git#hap",
|
||||||
"castv2-client": "^1.2.0",
|
"castv2-client": "^1.2.0",
|
||||||
"chokidar": "^3.5.3",
|
"chokidar": "^3.5.3",
|
||||||
"discord-rpc": "^4.0.1",
|
"discord-auto-rpc": "^1.0.16",
|
||||||
"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.6",
|
||||||
"electron-fetch": "^1.7.4",
|
"electron-fetch": "^1.7.4",
|
||||||
|
@ -82,7 +82,7 @@
|
||||||
"electron-builder-notarize-pkg": "^1.2.0",
|
"electron-builder-notarize-pkg": "^1.2.0",
|
||||||
"electron-webpack": "^2.8.2",
|
"electron-webpack": "^2.8.2",
|
||||||
"musickit-typescript": "^1.2.4",
|
"musickit-typescript": "^1.2.4",
|
||||||
"typescript": "^4.6.3",
|
"typescript": "^4.6.4",
|
||||||
"vue-devtools": "^5.1.4",
|
"vue-devtools": "^5.1.4",
|
||||||
"webpack": "~5.72.0"
|
"webpack": "~5.72.0"
|
||||||
},
|
},
|
||||||
|
@ -109,9 +109,9 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"build": {
|
"build": {
|
||||||
"electronVersion": "18.2.0",
|
"electronVersion": "18.2.1",
|
||||||
"electronDownload": {
|
"electronDownload": {
|
||||||
"version": "18.2.0+wvcus",
|
"version": "18.2.1+wvcus",
|
||||||
"mirror": "https://github.com/castlabs/electron-releases/releases/download/v"
|
"mirror": "https://github.com/castlabs/electron-releases/releases/download/v"
|
||||||
},
|
},
|
||||||
"appId": "cider",
|
"appId": "cider",
|
||||||
|
|
|
@ -428,6 +428,11 @@
|
||||||
"settings.header.visual.theme.github.page": "Themes from GitHub",
|
"settings.header.visual.theme.github.page": "Themes from GitHub",
|
||||||
"settings.option.visual.theme.github.install.confirm": "Are you sure you want to install {{ repo }}?",
|
"settings.option.visual.theme.github.install.confirm": "Are you sure you want to install {{ repo }}?",
|
||||||
"settings.prompt.visual.theme.github.URL": "Enter the URL of the theme you want to install",
|
"settings.prompt.visual.theme.github.URL": "Enter the URL of the theme you want to install",
|
||||||
|
"settings.prompt.visual.theme.uninstallTheme": "Are you sure you want to uninstall {{ theme }}?",
|
||||||
|
"settings.option.visual.theme.checkForUpdates": "Check for updates",
|
||||||
|
"settings.option.visual.theme.manageStyles": "Manage Styles",
|
||||||
|
"settings.option.visual.theme.uninstall": "Uninstall",
|
||||||
|
"settings.option.visual.theme.viewInfo": "View Info",
|
||||||
"settings.notyf.visual.theme.install.success": "Theme installed successfully",
|
"settings.notyf.visual.theme.install.success": "Theme installed successfully",
|
||||||
"settings.notyf.visual.theme.install.error": "Theme installation failed",
|
"settings.notyf.visual.theme.install.error": "Theme installation failed",
|
||||||
"settings.header.visual.plugin": "Plugin",
|
"settings.header.visual.plugin": "Plugin",
|
||||||
|
|
|
@ -428,6 +428,11 @@
|
||||||
"settings.header.visual.theme.github.page": "Themes from GitHub",
|
"settings.header.visual.theme.github.page": "Themes from GitHub",
|
||||||
"settings.option.visual.theme.github.install.confirm": "Are you sure you want to install {{ repo }}?",
|
"settings.option.visual.theme.github.install.confirm": "Are you sure you want to install {{ repo }}?",
|
||||||
"settings.prompt.visual.theme.github.URL": "Enter the URL of the theme you want to install",
|
"settings.prompt.visual.theme.github.URL": "Enter the URL of the theme you want to install",
|
||||||
|
"settings.prompt.visual.theme.uninstallTheme": "Are you sure you want to uninstall {{ theme }}?",
|
||||||
|
"settings.option.visual.theme.checkForUpdates": "Check for updates",
|
||||||
|
"settings.option.visual.theme.manageStyles": "Manage Styles",
|
||||||
|
"settings.option.visual.theme.uninstall": "Uninstall",
|
||||||
|
"settings.option.visual.theme.viewInfo": "View Info",
|
||||||
"settings.notyf.visual.theme.install.success": "Theme installed successfully",
|
"settings.notyf.visual.theme.install.success": "Theme installed successfully",
|
||||||
"settings.notyf.visual.theme.install.error": "Theme installation failed",
|
"settings.notyf.visual.theme.install.error": "Theme installation failed",
|
||||||
"settings.header.visual.plugin": "Plugin",
|
"settings.header.visual.plugin": "Plugin",
|
||||||
|
|
|
@ -4,7 +4,18 @@ import * as windowStateKeeper from "electron-window-state";
|
||||||
import * as express from "express";
|
import * as express from "express";
|
||||||
import * as getPort from "get-port";
|
import * as getPort from "get-port";
|
||||||
import {search} from "youtube-search-without-api-key";
|
import {search} from "youtube-search-without-api-key";
|
||||||
import {existsSync, rmSync, mkdirSync, readdirSync, readFileSync, writeFileSync, statSync} from "fs";
|
import {
|
||||||
|
existsSync,
|
||||||
|
rmSync,
|
||||||
|
mkdirSync,
|
||||||
|
readdirSync,
|
||||||
|
readFileSync,
|
||||||
|
writeFileSync,
|
||||||
|
statSync,
|
||||||
|
unlinkSync,
|
||||||
|
rmdirSync,
|
||||||
|
lstatSync
|
||||||
|
} from "fs";
|
||||||
import {Stream} from "stream";
|
import {Stream} from "stream";
|
||||||
import {networkInterfaces} from "os";
|
import {networkInterfaces} from "os";
|
||||||
import * as mm from 'music-metadata';
|
import * as mm from 'music-metadata';
|
||||||
|
@ -46,6 +57,7 @@ export class BrowserWindow {
|
||||||
"pages/library-artists",
|
"pages/library-artists",
|
||||||
"pages/browse",
|
"pages/browse",
|
||||||
"pages/settings",
|
"pages/settings",
|
||||||
|
"pages/installed-themes",
|
||||||
"pages/listen_now",
|
"pages/listen_now",
|
||||||
"pages/home",
|
"pages/home",
|
||||||
"pages/artist-feed",
|
"pages/artist-feed",
|
||||||
|
@ -178,6 +190,10 @@ export class BrowserWindow {
|
||||||
page: "settings",
|
page: "settings",
|
||||||
component: `<cider-settings></cider-settings>`,
|
component: `<cider-settings></cider-settings>`,
|
||||||
condition: `page == 'settings'`
|
condition: `page == 'settings'`
|
||||||
|
}, {
|
||||||
|
page: "installed-themes",
|
||||||
|
component: `<installed-themes></installed-themes>`,
|
||||||
|
condition: `page == 'installed-themes'`
|
||||||
}, {
|
}, {
|
||||||
page: "search",
|
page: "search",
|
||||||
component: `<cider-search :search="search"></cider-search>`,
|
component: `<cider-search :search="search"></cider-search>`,
|
||||||
|
@ -256,8 +272,10 @@ export class BrowserWindow {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static watcher: any;
|
||||||
|
|
||||||
StartWatcher(path: string) {
|
StartWatcher(path: string) {
|
||||||
const watcher = watch(path, {
|
BrowserWindow.watcher = watch(path, {
|
||||||
ignored: /[\/\\]\./,
|
ignored: /[\/\\]\./,
|
||||||
persistent: true
|
persistent: true
|
||||||
});
|
});
|
||||||
|
@ -267,7 +285,7 @@ export class BrowserWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Declare the listeners of the watcher
|
// Declare the listeners of the watcher
|
||||||
watcher
|
BrowserWindow.watcher
|
||||||
.on('add', function (path: string) {
|
.on('add', function (path: string) {
|
||||||
// console.log('File', path, 'has been added');
|
// console.log('File', path, 'has been added');
|
||||||
})
|
})
|
||||||
|
@ -294,6 +312,10 @@ export class BrowserWindow {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async StopWatcher() {
|
||||||
|
await BrowserWindow.watcher.close();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the browser window
|
* Creates the browser window
|
||||||
* @generator
|
* @generator
|
||||||
|
@ -698,6 +720,50 @@ export class BrowserWindow {
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ipcMain.handle("uninstall-theme", async (event, path) => {
|
||||||
|
await this.StopWatcher()
|
||||||
|
const themesDir = utils.getPath("themes")
|
||||||
|
// validate the path is in the themes directory
|
||||||
|
try {
|
||||||
|
if (path.startsWith(themesDir)) {
|
||||||
|
// get last dir in path, can be either / or \ and may have a trailing slash
|
||||||
|
const themeName = path.split(/[\\\/]/).pop()
|
||||||
|
if (themeName == "Themes" || themeName == "themes") {
|
||||||
|
BrowserWindow.win.webContents.send("theme-uninstalled", {
|
||||||
|
path: path,
|
||||||
|
status: 3
|
||||||
|
});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// if path is directory, delete it
|
||||||
|
if (lstatSync(path).isDirectory()) {
|
||||||
|
await rmdirSync(path, {recursive: true});
|
||||||
|
} else {
|
||||||
|
// if path is file, delete it
|
||||||
|
await unlinkSync(path);
|
||||||
|
}
|
||||||
|
// return the path
|
||||||
|
BrowserWindow.win.webContents.send("theme-uninstalled", {
|
||||||
|
path: path,
|
||||||
|
status: 0
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
BrowserWindow.win.webContents.send("theme-uninstalled", {
|
||||||
|
path: path,
|
||||||
|
status: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
BrowserWindow.win.webContents.send("theme-uninstalled", {
|
||||||
|
path: path,
|
||||||
|
message: e.message,
|
||||||
|
status: 2
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.StartWatcher(utils.getPath('themes'))
|
||||||
|
})
|
||||||
|
|
||||||
ipcMain.handle("reinstall-widevine-cdm", () => {
|
ipcMain.handle("reinstall-widevine-cdm", () => {
|
||||||
// remove WidevineCDM from appdata folder
|
// remove WidevineCDM from appdata folder
|
||||||
const widevineCdmPath = join(app.getPath("userData"), "./WidevineCdm");
|
const widevineCdmPath = join(app.getPath("userData"), "./WidevineCdm");
|
||||||
|
@ -813,7 +879,7 @@ export class BrowserWindow {
|
||||||
} else if (statSync(join(utils.getPath("themes"), file)).isDirectory()) {
|
} else if (statSync(join(utils.getPath("themes"), file)).isDirectory()) {
|
||||||
let subFiles = readdirSync(join(utils.getPath("themes"), file));
|
let subFiles = readdirSync(join(utils.getPath("themes"), file));
|
||||||
for (let subFile of subFiles) {
|
for (let subFile of subFiles) {
|
||||||
if (subFile.endsWith(".less")) {
|
if (subFile.endsWith("index.less")) {
|
||||||
themes.push(join(file, subFile));
|
themes.push(join(file, subFile));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -832,6 +898,7 @@ export class BrowserWindow {
|
||||||
themePath = themePath.slice(0, -10);
|
themePath = themePath.slice(0, -10);
|
||||||
}
|
}
|
||||||
if (existsSync(join(themePath, "theme.json"))) {
|
if (existsSync(join(themePath, "theme.json"))) {
|
||||||
|
try {
|
||||||
let themeJson = JSON.parse(readFileSync(join(themePath, "theme.json"), "utf8"));
|
let themeJson = JSON.parse(readFileSync(join(themePath, "theme.json"), "utf8"));
|
||||||
themeObjects.push({
|
themeObjects.push({
|
||||||
name: themeJson.name || themeName,
|
name: themeJson.name || themeName,
|
||||||
|
@ -839,8 +906,12 @@ export class BrowserWindow {
|
||||||
path: themePath,
|
path: themePath,
|
||||||
file: theme,
|
file: theme,
|
||||||
github_repo: themeJson.github_repo || "",
|
github_repo: themeJson.github_repo || "",
|
||||||
commit: themeJson.commit || ""
|
commit: themeJson.commit || "",
|
||||||
|
pack: themeJson.pack || false,
|
||||||
});
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
themeObjects.push({
|
themeObjects.push({
|
||||||
name: themeName,
|
name: themeName,
|
||||||
|
@ -848,7 +919,8 @@ export class BrowserWindow {
|
||||||
path: themePath,
|
path: themePath,
|
||||||
file: theme,
|
file: theme,
|
||||||
github_repo: "",
|
github_repo: "",
|
||||||
commit: ""
|
commit: "",
|
||||||
|
pack: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -971,6 +1043,11 @@ export class BrowserWindow {
|
||||||
BrowserWindow.win.setResizable(!lock);
|
BrowserWindow.win.setResizable(!lock);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Move window
|
||||||
|
ipcMain.on("windowmove", (_event, x, y) => {
|
||||||
|
BrowserWindow.win.setBounds({x, y});
|
||||||
|
});
|
||||||
|
|
||||||
//Fullscreen
|
//Fullscreen
|
||||||
ipcMain.on('setFullScreen', (_event, flag) => {
|
ipcMain.on('setFullScreen', (_event, flag) => {
|
||||||
BrowserWindow.win.setFullScreen(flag)
|
BrowserWindow.win.setFullScreen(flag)
|
||||||
|
@ -1279,7 +1356,7 @@ export class BrowserWindow {
|
||||||
BrowserWindow.win.webContents.executeJavaScript(`
|
BrowserWindow.win.webContents.executeJavaScript(`
|
||||||
window.localStorage.setItem("currentTrack", JSON.stringify(app.mk.nowPlayingItem));
|
window.localStorage.setItem("currentTrack", JSON.stringify(app.mk.nowPlayingItem));
|
||||||
window.localStorage.setItem("currentTime", JSON.stringify(app.mk.currentPlaybackTime));
|
window.localStorage.setItem("currentTime", JSON.stringify(app.mk.currentPlaybackTime));
|
||||||
window.localStorage.setItem("currentQueue", JSON.stringify(app.mk.queue.items));
|
window.localStorage.setItem("currentQueue", JSON.stringify(app.mk.queue._unplayedQueueItems));
|
||||||
ipcRenderer.send('stopGCast','');`)
|
ipcRenderer.send('stopGCast','');`)
|
||||||
BrowserWindow.win.destroy();
|
BrowserWindow.win.destroy();
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,11 +52,11 @@ export class Store {
|
||||||
"keybindings": {
|
"keybindings": {
|
||||||
"search": [
|
"search": [
|
||||||
process.platform == "darwin" ? "Command" : "Control",
|
process.platform == "darwin" ? "Command" : "Control",
|
||||||
"S"
|
"F"
|
||||||
],
|
],
|
||||||
"albums": [
|
"albums": [
|
||||||
process.platform == "darwin" ? "Command" : "Control",
|
process.platform == "darwin" ? "Command" : "Control",
|
||||||
"F"
|
"S"
|
||||||
],
|
],
|
||||||
"artists": [
|
"artists": [
|
||||||
process.platform == "darwin" ? "Command" : "Control",
|
process.platform == "darwin" ? "Command" : "Control",
|
||||||
|
@ -123,6 +123,8 @@ export class Store {
|
||||||
"quality": "HIGH",
|
"quality": "HIGH",
|
||||||
"seamless_audio": true,
|
"seamless_audio": true,
|
||||||
"normalization": false,
|
"normalization": false,
|
||||||
|
"dBSPL": false,
|
||||||
|
"dBSPLcalibration": 90,
|
||||||
"maikiwiAudio": {
|
"maikiwiAudio": {
|
||||||
"ciderPPE": false,
|
"ciderPPE": false,
|
||||||
"ciderPPE_value": "MAIKIWI",
|
"ciderPPE_value": "MAIKIWI",
|
||||||
|
|
|
@ -1,30 +1,29 @@
|
||||||
import * as RPC from 'discord-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 {
|
||||||
|
|
||||||
/**
|
|
||||||
* Private variables for interaction in plugins
|
|
||||||
*/
|
|
||||||
private _utils: any;
|
|
||||||
private _app: any;
|
|
||||||
private _attributes: any;
|
|
||||||
private _connection: boolean = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.0.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 _utils: any;
|
||||||
|
private _attributes: any;
|
||||||
|
private ready: boolean = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin Initialization
|
* Plugin Initialization
|
||||||
*/
|
*/
|
||||||
private _client: any = null;
|
private _client: any = null;
|
||||||
private _activity: RPC.Presence = {
|
private _activityCache: any = {
|
||||||
details: '',
|
details: '',
|
||||||
state: '',
|
state: '',
|
||||||
largeImageKey: '',
|
largeImageKey: '',
|
||||||
|
@ -34,15 +33,72 @@ export default class DiscordRPC {
|
||||||
instance: false
|
instance: false
|
||||||
};
|
};
|
||||||
|
|
||||||
private _activityCache: RPC.Presence = {
|
/*******************************************************************************************
|
||||||
details: '',
|
* Public Methods
|
||||||
state: '',
|
* ****************************************************************************************/
|
||||||
largeImageKey: '',
|
|
||||||
largeImageText: '',
|
/**
|
||||||
smallImageKey: '',
|
* Runs on plugin load (Currently run on application start)
|
||||||
smallImageText: '',
|
*/
|
||||||
instance: false
|
constructor(utils: any) {
|
||||||
};
|
this._utils = utils;
|
||||||
|
console.debug(`[Plugin][${this.name}] Loading Complete.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs on app ready
|
||||||
|
*/
|
||||||
|
onReady(_win: any): void {
|
||||||
|
const self = this
|
||||||
|
this.connect();
|
||||||
|
console.debug(`[Plugin][${this.name}] Ready.`);
|
||||||
|
ipcMain.on('updateRPCImage', (_event, imageurl) => {
|
||||||
|
if (!this._utils.getStoreValue("general.privateEnabled")) {
|
||||||
|
fetch('https://api.cider.sh/v1/images', {
|
||||||
|
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({url: imageurl}),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'User-Agent': _win.webContents.getUserAgent()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(function (json) {
|
||||||
|
self._attributes["artwork"]["url"] = json.url
|
||||||
|
self.setActivity(self._attributes)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs on app stop
|
||||||
|
*/
|
||||||
|
onBeforeQuit(): void {
|
||||||
|
console.debug(`[Plugin][${this.name}] Stopped.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs on playback State Change
|
||||||
|
* @param attributes Music Attributes (attributes.status = current state)
|
||||||
|
*/
|
||||||
|
onPlaybackStateDidChange(attributes: object): void {
|
||||||
|
this._attributes = attributes
|
||||||
|
this.setActivity(attributes)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs on song change
|
||||||
|
* @param attributes Music Attributes
|
||||||
|
*/
|
||||||
|
onNowPlayingItemDidChange(attributes: object): void {
|
||||||
|
this._attributes = attributes
|
||||||
|
this.setActivity(attributes)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************************
|
/*******************************************************************************************
|
||||||
* Private Methods
|
* Private Methods
|
||||||
|
@ -58,58 +114,86 @@ export default class DiscordRPC {
|
||||||
}
|
}
|
||||||
const clientId = this._utils.getStoreValue("general.discordrpc.client") === "Cider" ? '911790844204437504' : '886578863147192350';
|
const clientId = this._utils.getStoreValue("general.discordrpc.client") === "Cider" ? '911790844204437504' : '886578863147192350';
|
||||||
|
|
||||||
// Apparently needed for ask to join, join, spectate etc.
|
|
||||||
RPC.register(clientId)
|
|
||||||
|
|
||||||
// Create the client
|
// Create the client
|
||||||
this._client = new RPC.Client({transport: "ipc"});
|
this._client = new AutoClient({transport: "ipc"});
|
||||||
|
|
||||||
// Runs on Ready
|
// Runs on Ready
|
||||||
this._client.on('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) {
|
||||||
|
console.info(`[DiscordRPC][connect] Restoring activity cache.`);
|
||||||
|
this._client.setActivity(this._activityCache)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Handles Errors
|
|
||||||
this._client.on('error', (err: any) => {
|
|
||||||
console.error(`[DiscordRPC] ${err}`);
|
|
||||||
this.disconnect()
|
|
||||||
});
|
|
||||||
|
|
||||||
// If Discord is closed, allow reconnecting
|
|
||||||
this._client.transport.once('close', () => {
|
|
||||||
console.info(`[DiscordRPC] Connection closed`);
|
|
||||||
this.disconnect()
|
|
||||||
});
|
|
||||||
|
|
||||||
// Login to Discord
|
// Login to Discord
|
||||||
this._client.login({clientId})
|
this._client.endlessLogin({clientId: clientId})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this._connection = true;
|
this.ready = true
|
||||||
})
|
})
|
||||||
.catch((e: any) => console.error(`[DiscordRPC][connect] ${e}`));
|
.catch((e: any) => console.error(`[DiscordRPC][connect] ${e}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disconnects from Discord RPC
|
* Sets the activity
|
||||||
|
* @param attributes Music Attributes
|
||||||
*/
|
*/
|
||||||
private disconnect() {
|
private setActivity(attributes: any) {
|
||||||
if (!this._client) {
|
if (!this._client) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this._client.destroy().then(() => {
|
// Check if show buttons is (true) or (false)
|
||||||
this._connection = false;
|
let activity: Object = {
|
||||||
console.log('[DiscordRPC][disconnect] Disconnected from discord.')
|
details: this._utils.getStoreValue("general.discordrpc.details_format"),
|
||||||
}).catch((e: any) => console.error(`[DiscordRPC][disconnect] ${e}`));
|
state: this._utils.getStoreValue("general.discordrpc.state_format"),
|
||||||
|
largeImageKey: attributes?.artwork?.url?.replace('{w}', '1024').replace('{h}', '1024'),
|
||||||
|
largeImageText: attributes.albumName,
|
||||||
|
instance: false // Whether the activity is in a game session
|
||||||
|
}
|
||||||
|
|
||||||
// Clean up, allow creating a new connection
|
// Filter the activity
|
||||||
this._client = null;
|
activity = this.filterActivity(activity, attributes)
|
||||||
|
|
||||||
|
if (!this.ready) {
|
||||||
|
this._activityCache = activity
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the activity
|
||||||
|
if (!attributes.status && this._utils.getStoreValue("general.discordrpc.clear_on_pause")) {
|
||||||
|
this._client.clearActivity()
|
||||||
|
} else if (activity && this._activityCache !== activity) {
|
||||||
|
this._client.setActivity(activity)
|
||||||
|
}
|
||||||
|
this._activityCache = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter the Discord activity object
|
* Filter the Discord activity object
|
||||||
*/
|
*/
|
||||||
private static filterActivity(activity: any, attributes: any): Object {
|
private filterActivity(activity: any, attributes: any): Object {
|
||||||
|
|
||||||
|
// Add the buttons if people want them
|
||||||
|
if (!this._utils.getStoreValue("general.discordrpc.hide_buttons")) {
|
||||||
|
activity.buttons = [
|
||||||
|
{label: 'Listen on Cider', url: attributes.url.cider},
|
||||||
|
{label: 'View on Apple Music', url: attributes.url.appleMusic}
|
||||||
|
] //To change attributes.url => preload/cider-preload.js
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the timestamp if its playing
|
||||||
|
if (attributes.status) {
|
||||||
|
activity.startTimestamp = Date.now() - (attributes?.durationInMillis - attributes?.remainingTime)
|
||||||
|
activity.endTimestamp = attributes.endTime
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the user wants to keep the activity when paused
|
||||||
|
if (!this._utils.getStoreValue("general.discordrpc.clear_on_pause")) {
|
||||||
|
activity.smallImageKey = attributes.status ? 'play' : 'pause';
|
||||||
|
activity.smallImageText = attributes.status ? 'Playing' : 'Paused';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Works with:
|
* Works with:
|
||||||
|
@ -173,138 +257,4 @@ export default class DiscordRPC {
|
||||||
}
|
}
|
||||||
return activity
|
return activity
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the activity
|
|
||||||
* @param {activity} activity
|
|
||||||
*/
|
|
||||||
private setActivity(activity: any) {
|
|
||||||
if (!this._connection || !this._client || !activity) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter the activity
|
|
||||||
activity = DiscordRPC.filterActivity(activity, this._attributes)
|
|
||||||
|
|
||||||
// Set the activity
|
|
||||||
if (!this._attributes.status && this._utils.getStoreValue("general.discordrpc.clear_on_pause")) {
|
|
||||||
this._client.clearActivity()
|
|
||||||
} else if (this._activity && this._activityCache !== this._activity && this._activity.details) {
|
|
||||||
this._client.setActivity(activity)
|
|
||||||
this._activityCache = this._activity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the activity of the client
|
|
||||||
* @param {object} attributes
|
|
||||||
*/
|
|
||||||
private updateActivity(attributes: any) {
|
|
||||||
if (!this._utils.getStoreValue("general.discordrpc.enabled") || this._utils.getStoreValue("general.privateEnabled")) {
|
|
||||||
return
|
|
||||||
} else if (!this._client || !this._connection) {
|
|
||||||
this.connect()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if show buttons is (true) or (false)
|
|
||||||
this._activity = {
|
|
||||||
details: this._utils.getStoreValue("general.discordrpc.details_format"),
|
|
||||||
state: this._utils.getStoreValue("general.discordrpc.state_format"),
|
|
||||||
largeImageKey: attributes?.artwork?.url?.replace('{w}', '1024').replace('{h}', '1024'),
|
|
||||||
largeImageText: attributes.albumName,
|
|
||||||
instance: false // Whether the activity is in a game session
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the buttons if people want them
|
|
||||||
if (!this._utils.getStoreValue("general.discordrpc.hide_buttons")) {
|
|
||||||
this._activity.buttons = [
|
|
||||||
{label: 'Listen on Cider', url: attributes.url.cider},
|
|
||||||
{label: 'View on Apple Music', url: attributes.url.appleMusic}
|
|
||||||
] //To change attributes.url => preload/cider-preload.js
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the timestamp if its playing
|
|
||||||
if (attributes.status) {
|
|
||||||
this._activity.startTimestamp = Date.now() - (attributes?.durationInMillis - attributes?.remainingTime)
|
|
||||||
this._activity.endTimestamp = attributes.endTime
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the user wants to keep the activity when paused
|
|
||||||
if (!this._utils.getStoreValue("general.discordrpc.clear_on_pause")) {
|
|
||||||
this._activity.smallImageKey = attributes.status ? 'play' : 'pause';
|
|
||||||
this._activity.smallImageText = attributes.status ? 'Playing' : 'Paused';
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setActivity(this._activity)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*******************************************************************************************
|
|
||||||
* Public Methods
|
|
||||||
* ****************************************************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs on plugin load (Currently run on application start)
|
|
||||||
*/
|
|
||||||
constructor(utils: { getStore: () => any; getApp: () => any; }) {
|
|
||||||
this._utils = utils;
|
|
||||||
console.debug(`[Plugin][${this.name}] Loading Complete.`);
|
|
||||||
this._app = utils.getApp();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs on app ready
|
|
||||||
*/
|
|
||||||
onReady(_win: any): void {
|
|
||||||
let self = this
|
|
||||||
this.connect();
|
|
||||||
console.debug(`[Plugin][${this.name}] Ready.`);
|
|
||||||
ipcMain.on('updateRPCImage', (_event, imageurl) => {
|
|
||||||
if (!this._utils.getStoreValue("general.privateEnabled")) {
|
|
||||||
fetch('https://api.cider.sh/v1/images', {
|
|
||||||
|
|
||||||
method: 'POST',
|
|
||||||
body: JSON.stringify({url: imageurl}),
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'User-Agent': _win.webContents.getUserAgent()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then(res => res.json())
|
|
||||||
.then(function (json) {
|
|
||||||
self._attributes["artwork"]["url"] = json.url
|
|
||||||
self.updateActivity(self._attributes)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs on app stop
|
|
||||||
*/
|
|
||||||
onBeforeQuit(): void {
|
|
||||||
if (this._client) {
|
|
||||||
this.disconnect()
|
|
||||||
}
|
|
||||||
console.debug(`[Plugin][${this.name}] Stopped.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs on playback State Change
|
|
||||||
* @param attributes Music Attributes (attributes.status = current state)
|
|
||||||
*/
|
|
||||||
onPlaybackStateDidChange(attributes: object): void {
|
|
||||||
this._attributes = attributes
|
|
||||||
this.updateActivity(attributes)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs on song change
|
|
||||||
* @param attributes Music Attributes
|
|
||||||
*/
|
|
||||||
onNowPlayingItemDidChange(attributes: object): void {
|
|
||||||
this._attributes = attributes
|
|
||||||
this.updateActivity(attributes)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,7 +181,7 @@ export default class RAOP {
|
||||||
this.portairplay = ipport;
|
this.portairplay = ipport;
|
||||||
this.device = this.airtunes.add(ipv4, {
|
this.device = this.airtunes.add(ipv4, {
|
||||||
port: ipport,
|
port: ipport,
|
||||||
volume: 60,
|
volume: 50,
|
||||||
password: sepassword,
|
password: sepassword,
|
||||||
txt: txt
|
txt: txt
|
||||||
});
|
});
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-heart"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-heart"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path></svg>
|
Before Width: | Height: | Size: 379 B After Width: | Height: | Size: 373 B |
|
@ -1 +1 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-heart"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-heart"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path></svg>
|
Before Width: | Height: | Size: 371 B After Width: | Height: | Size: 364 B |
|
@ -364,6 +364,21 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
.cd-mediaitem-list-item .heart-unfilled {
|
||||||
|
background-image: url("assets/feather/heart.svg");
|
||||||
|
height: 12px;
|
||||||
|
width: 36px;
|
||||||
|
filter: contrast(0);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-mediaitem-list-item .heart-filled {
|
||||||
|
background-image: url("assets/feather/heart-fill.svg");
|
||||||
|
height: 12px;
|
||||||
|
width: 36px;
|
||||||
|
filter: contrast(0);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
.cd-mediaitem-list-item .explicit-icon {
|
.cd-mediaitem-list-item .explicit-icon {
|
||||||
background-image: url("assets/explicit.svg");
|
background-image: url("assets/explicit.svg");
|
||||||
height: 12px;
|
height: 12px;
|
||||||
|
@ -372,9 +387,7 @@
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
.heart-icon {
|
.heart-icon {
|
||||||
position: absolute;
|
display: flex
|
||||||
filter: contrast(0);
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
}
|
||||||
@keyframes load-bar {
|
@keyframes load-bar {
|
||||||
10% {
|
10% {
|
||||||
|
|
|
@ -359,7 +359,11 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-radius: var(--mediaItemRadius);
|
border-radius: var(--mediaItemRadius);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
&:hover{
|
||||||
|
.heart-icon{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
.popular {
|
.popular {
|
||||||
background-image: url(assets/star.svg);
|
background-image: url(assets/star.svg);
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
@ -448,6 +452,22 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.heart-unfilled {
|
||||||
|
-webkit-mask-image: url("assets/feather/heart.svg");
|
||||||
|
height: 12px;
|
||||||
|
width: 12px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heart-filled {
|
||||||
|
-webkit-mask-image: url("assets/feather/heart-fill.svg");
|
||||||
|
height: 12px;
|
||||||
|
width: 12px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
.explicit-icon {
|
.explicit-icon {
|
||||||
background-image: url("./assets/explicit.svg");
|
background-image: url("./assets/explicit.svg");
|
||||||
height: 12px;
|
height: 12px;
|
||||||
|
@ -457,10 +477,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.heart-icon {
|
.heart-icon {
|
||||||
|
display: flex;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right:0;
|
left: 20px;
|
||||||
filter: contrast(0);
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* CSS.gg
|
/* CSS.gg
|
||||||
|
|
|
@ -353,7 +353,7 @@
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
&::before {
|
&::before {
|
||||||
transition: transform .1s ease-in, opacity .1s ease-in;
|
transition: transform 0s ease-in, opacity 0s ease-in;
|
||||||
opacity : 1;
|
opacity : 1;
|
||||||
transform : scale(1);
|
transform : scale(1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -498,6 +498,18 @@
|
||||||
height : 100%;
|
height : 100%;
|
||||||
overflow : hidden;
|
overflow : hidden;
|
||||||
|
|
||||||
|
.cd-mediaitem-list-item {
|
||||||
|
&:hover {
|
||||||
|
.heart-icon {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.heart-icon {
|
||||||
|
left: -25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.editTracksBtn {
|
.editTracksBtn {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top : 20px;
|
top : 20px;
|
||||||
|
@ -512,7 +524,8 @@
|
||||||
|
|
||||||
.mediaContainer {
|
.mediaContainer {
|
||||||
transition: width 0.5s ease-in-out, height 0.5s ease-in-out;
|
transition: width 0.5s ease-in-out, height 0.5s ease-in-out;
|
||||||
width: 260px;height:260px;
|
width : 260px;
|
||||||
|
height : 260px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.playlist-body {
|
.playlist-body {
|
||||||
|
@ -537,12 +550,15 @@
|
||||||
height : 100%;
|
height : 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin : 0px;
|
margin : 0px;
|
||||||
|
|
||||||
.tab-pane {
|
.tab-pane {
|
||||||
height : 100%;
|
height : 100%;
|
||||||
overflow-y : overlay;
|
overflow-y : overlay;
|
||||||
overflow-x : hidden;
|
overflow-x : hidden;
|
||||||
padding : var(--contentInnerPadding);
|
padding : var(--contentInnerPadding);
|
||||||
|
padding-inline : 40px;
|
||||||
-webkit-mask-image: linear-gradient(180deg, transparent, white 20px);
|
-webkit-mask-image: linear-gradient(180deg, transparent, white 20px);
|
||||||
|
|
||||||
.well {
|
.well {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
}
|
}
|
||||||
|
@ -813,9 +829,7 @@
|
||||||
transition: min-height 0.5s ease-in-out;
|
transition: min-height 0.5s ease-in-out;
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
|
|
||||||
.playlistInfo {
|
.playlistInfo {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.mediaContainer {
|
.mediaContainer {
|
||||||
transition: width 0.5s ease-in-out, height 0.5s ease-in-out;
|
transition: width 0.5s ease-in-out, height 0.5s ease-in-out;
|
||||||
|
@ -1112,9 +1126,56 @@
|
||||||
|
|
||||||
/* Artist Page End */
|
/* Artist Page End */
|
||||||
|
|
||||||
// Settings page
|
|
||||||
.settings-page {
|
.installed-themes-page {
|
||||||
|
|
||||||
|
.themeContextMenu {
|
||||||
|
background: transparent;
|
||||||
|
color : var(--keyColor);
|
||||||
|
border : 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-group-item {
|
||||||
|
&.addon {
|
||||||
|
background: rgb(86 86 86 / 20%);
|
||||||
|
}
|
||||||
|
&.applied {
|
||||||
|
background: var(--keyColor-disabled);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-header {
|
||||||
|
font-size : 16px;
|
||||||
|
position : sticky;
|
||||||
|
top : 0;
|
||||||
|
left : 0;
|
||||||
|
right : 0;
|
||||||
|
width : 100%;
|
||||||
|
height : 50px;
|
||||||
|
z-index : 1;
|
||||||
|
background : rgba(36, 36, 36, 0.5);
|
||||||
|
display : flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items : center;
|
||||||
|
backdrop-filter: var(--glassFilter);
|
||||||
|
overflow : hidden;
|
||||||
|
border-bottom : 1px solid rgb(0 0 0 / 18%);
|
||||||
|
border-top : 1px solid rgb(135 135 135 / 18%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.style-editor-container {
|
||||||
|
height : 100%;
|
||||||
|
flex : 1;
|
||||||
|
background: var(--color2);
|
||||||
padding : 0px;
|
padding : 0px;
|
||||||
|
overflow-y: overlay;
|
||||||
|
|
||||||
|
.list-group-item {
|
||||||
|
border-radius: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.stylestack-editor {
|
.stylestack-editor {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -1129,6 +1190,23 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.handle {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-group-item {
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
&:active {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.removeItem {
|
.removeItem {
|
||||||
border : 0px;
|
border : 0px;
|
||||||
background : transparent;
|
background : transparent;
|
||||||
|
@ -1145,7 +1223,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Settings page
|
||||||
|
.settings-page {
|
||||||
|
padding: 0px;
|
||||||
|
|
||||||
.nav {
|
.nav {
|
||||||
width : 90%;
|
width : 90%;
|
||||||
|
@ -1165,6 +1247,7 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width : 100%;
|
width : 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-option-body {
|
.settings-option-body {
|
||||||
margin: 16px;
|
margin: 16px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,6 +148,10 @@ const app = new Vue({
|
||||||
},
|
},
|
||||||
tmpHeight: '',
|
tmpHeight: '',
|
||||||
tmpWidth: '',
|
tmpWidth: '',
|
||||||
|
tmpX: '',
|
||||||
|
tmpY: '',
|
||||||
|
miniTmpX: '',
|
||||||
|
miniTmpY: '',
|
||||||
tmpVar: [],
|
tmpVar: [],
|
||||||
notification: false,
|
notification: false,
|
||||||
chrome: {
|
chrome: {
|
||||||
|
@ -284,6 +288,9 @@ const app = new Vue({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
formatVolumeTooltip() {
|
||||||
|
return this.cfg.audio.dBSPL ? (Number(this.cfg.audio.dBSPLcalibration) + (Math.log10(this.mk.volume) * 20)).toFixed(2) + ' dB SPL' : (Math.log10(this.mk.volume) * 20).toFixed(2) + ' dBFS'
|
||||||
|
},
|
||||||
mainMenuVisibility(val) {
|
mainMenuVisibility(val) {
|
||||||
if (val) {
|
if (val) {
|
||||||
(this.mk.isAuthorized) ? this.chrome.menuOpened = !this.chrome.menuOpened : false;
|
(this.mk.isAuthorized) ? this.chrome.menuOpened = !this.chrome.menuOpened : false;
|
||||||
|
@ -593,9 +600,7 @@ const app = new Vue({
|
||||||
},
|
},
|
||||||
async init() {
|
async init() {
|
||||||
let self = this
|
let self = this
|
||||||
if (this.cfg.visual.theme != "default.less" && this.cfg.visual.theme != "") {
|
|
||||||
this.setTheme(this.cfg.visual.theme)
|
|
||||||
}
|
|
||||||
if (this.cfg.visual.styles.length != 0) {
|
if (this.cfg.visual.styles.length != 0) {
|
||||||
await this.reloadStyles()
|
await this.reloadStyles()
|
||||||
}
|
}
|
||||||
|
@ -703,6 +708,7 @@ const app = new Vue({
|
||||||
let lastItem = window.localStorage.getItem("currentTrack")
|
let lastItem = window.localStorage.getItem("currentTrack")
|
||||||
let time = window.localStorage.getItem("currentTime")
|
let time = window.localStorage.getItem("currentTime")
|
||||||
let queue = window.localStorage.getItem("currentQueue")
|
let queue = window.localStorage.getItem("currentQueue")
|
||||||
|
app.mk.queue.position = 0; // Reset queue position.
|
||||||
if (lastItem != null) {
|
if (lastItem != null) {
|
||||||
lastItem = JSON.parse(lastItem)
|
lastItem = JSON.parse(lastItem)
|
||||||
let kind = lastItem.attributes.playParams.kind;
|
let kind = lastItem.attributes.playParams.kind;
|
||||||
|
@ -722,7 +728,7 @@ const app = new Vue({
|
||||||
if (queue != null) {
|
if (queue != null) {
|
||||||
queue = JSON.parse(queue)
|
queue = JSON.parse(queue)
|
||||||
if (queue && queue.length > 0) {
|
if (queue && queue.length > 0) {
|
||||||
let ids = queue.map(e => (e.playParams ? e.playParams.id : (e.attributes.playParams ? e.attributes.playParams.id : '')))
|
let ids = queue.map(e => (e.playParams ? e.playParams.id : (e.item.attributes.playParams ? e.item.attributes.playParams.id : '')))
|
||||||
let i = 0;
|
let i = 0;
|
||||||
if (ids.length > 0) {
|
if (ids.length > 0) {
|
||||||
for (let id of ids) {
|
for (let id of ids) {
|
||||||
|
@ -833,6 +839,14 @@ const app = new Vue({
|
||||||
ipcRenderer.send('wsapi-updatePlaybackState', wsapi.getAttributes());
|
ipcRenderer.send('wsapi-updatePlaybackState', wsapi.getAttributes());
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.mk.addEventListener(MusicKit.Events.queueItemsDidChange, ()=>{
|
||||||
|
if (self.$refs.queue) {
|
||||||
|
setTimeout(()=>{
|
||||||
|
self.$refs.queue.updateQueue();
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
this.mk.addEventListener(MusicKit.Events.nowPlayingItemDidChange, (a) => {
|
this.mk.addEventListener(MusicKit.Events.nowPlayingItemDidChange, (a) => {
|
||||||
if (self.$refs.queue) {
|
if (self.$refs.queue) {
|
||||||
self.$refs.queue.updateQueue();
|
self.$refs.queue.updateQueue();
|
||||||
|
@ -1148,8 +1162,10 @@ const app = new Vue({
|
||||||
|
|
||||||
async function deepScan(parent = "p.playlistsroot") {
|
async function deepScan(parent = "p.playlistsroot") {
|
||||||
console.debug(`scanning ${parent}`)
|
console.debug(`scanning ${parent}`)
|
||||||
const playlistData = await app.mk.api.v3.music(`/v1/me/library/playlist-folders/${parent}/children/`)
|
// const playlistData = await app.mk.api.v3.music(`/v1/me/library/playlist-folders/${parent}/children/`)
|
||||||
await asyncForEach(playlistData.data.data, async (playlist) => {
|
const playlistData = await MusicKitTools.v3Continuous({href: `/v1/me/library/playlist-folders/${parent}/children/`})
|
||||||
|
console.log(playlistData)
|
||||||
|
await asyncForEach(playlistData, async (playlist) => {
|
||||||
playlist.parent = parent
|
playlist.parent = parent
|
||||||
if (
|
if (
|
||||||
playlist.type != "library-playlist-folders" &&
|
playlist.type != "library-playlist-folders" &&
|
||||||
|
@ -4107,13 +4123,19 @@ const app = new Vue({
|
||||||
if (flag) {
|
if (flag) {
|
||||||
this.tmpWidth = window.innerWidth;
|
this.tmpWidth = window.innerWidth;
|
||||||
this.tmpHeight = window.innerHeight;
|
this.tmpHeight = window.innerHeight;
|
||||||
|
this.tmpX = window.screenX;
|
||||||
|
this.tmpY = window.screenY;
|
||||||
ipcRenderer.send('unmaximize');
|
ipcRenderer.send('unmaximize');
|
||||||
ipcRenderer.send('windowmin', 250, 250)
|
ipcRenderer.send('windowmin', 250, 250)
|
||||||
|
if (this.miniTmpX !== '' && this.miniTmpY !== '') ipcRenderer.send('windowmove', this.miniTmpX, this.miniTmpY)
|
||||||
ipcRenderer.send('windowresize', 300, 300, false)
|
ipcRenderer.send('windowresize', 300, 300, false)
|
||||||
app.appMode = 'mini';
|
app.appMode = 'mini';
|
||||||
} else {
|
} else {
|
||||||
|
this.miniTmpX = window.screenX;
|
||||||
|
this.miniTmpY = window.screenY;
|
||||||
ipcRenderer.send('windowmin', 844, 410)
|
ipcRenderer.send('windowmin', 844, 410)
|
||||||
ipcRenderer.send('windowresize', this.tmpWidth, this.tmpHeight, false)
|
ipcRenderer.send('windowresize', this.tmpWidth, this.tmpHeight, false)
|
||||||
|
ipcRenderer.send('windowmove', this.tmpX, this.tmpY)
|
||||||
ipcRenderer.send('windowontop', false)
|
ipcRenderer.send('windowontop', false)
|
||||||
//this.cfg.visual.miniplayer_top_toggle = true;
|
//this.cfg.visual.miniplayer_top_toggle = true;
|
||||||
app.appMode = 'player';
|
app.appMode = 'player';
|
||||||
|
|
|
@ -12944,6 +12944,7 @@ body[platform='darwin'] #window-controls-container {
|
||||||
}
|
}
|
||||||
body[platform='darwin'] .app-chrome .app-chrome-item > .app-mainmenu {
|
body[platform='darwin'] .app-chrome .app-chrome-item > .app-mainmenu {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
width: 52px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
-webkit-app-region: drag;
|
-webkit-app-region: drag;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3289,6 +3289,7 @@ body[platform='darwin'] {
|
||||||
|
|
||||||
.app-chrome .app-chrome-item > .app-mainmenu {
|
.app-chrome .app-chrome-item > .app-mainmenu {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
width: 52px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
-webkit-app-region: drag;
|
-webkit-app-region: drag;
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,7 +101,7 @@
|
||||||
:class="{'active': this.cfg.audio.volume == 0}"></button>
|
:class="{'active': this.cfg.audio.volume == 0}"></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="`${(Math.log10(mk.volume) * 20).toFixed(2)} dB`">
|
v-b-tooltip.hover :title="formatVolumeTooltip()">
|
||||||
</div>
|
</div>
|
||||||
<div class="app-chrome-item generic">
|
<div class="app-chrome-item generic">
|
||||||
<button class="playback-button--small miniplayer"
|
<button class="playback-button--small miniplayer"
|
||||||
|
|
|
@ -134,7 +134,7 @@
|
||||||
:class="{'active': this.cfg.audio.volume == 0}"></button>
|
:class="{'active': this.cfg.audio.volume == 0}"></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="`${(Math.log10(mk.volume) * 20).toFixed(2)} dB`">
|
v-b-tooltip.hover :title="formatVolumeTooltip()">
|
||||||
</div>
|
</div>
|
||||||
<div class="app-chrome-item generic">
|
<div class="app-chrome-item generic">
|
||||||
<button class="playback-button--small miniplayer"
|
<button class="playback-button--small miniplayer"
|
||||||
|
|
|
@ -216,7 +216,7 @@
|
||||||
<input type="range" class="" @wheel="volumeWheel" :step="cfg.audio.volumeStep" min="0"
|
<input type="range" class="" @wheel="volumeWheel" :step="cfg.audio.volumeStep" min="0"
|
||||||
:max="cfg.audio.maxVolume" v-model="mk.volume" v-if="typeof mk.volume != 'undefined'"
|
:max="cfg.audio.maxVolume" v-model="mk.volume" v-if="typeof mk.volume != 'undefined'"
|
||||||
@change="checkMuteChange()" v-b-tooltip.hover
|
@change="checkMuteChange()" v-b-tooltip.hover
|
||||||
:title="`${(Math.log10(mk.volume) * 20).toFixed(2)} dB`">
|
:title="formatVolumeTooltip()">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -14,7 +14,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="5" v-model="volume"/>
|
step="2" v-model="volume"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-option-line">
|
<div class="md-option-line">
|
||||||
|
|
|
@ -82,7 +82,7 @@
|
||||||
<button class="volume-button--small volume" @click="app.muteButtonPressed()" :class="{'active': app.cfg.audio.volume == 0}"></button>
|
<button class="volume-button--small volume" @click="app.muteButtonPressed()" :class="{'active': app.cfg.audio.volume == 0}"></button>
|
||||||
<input type="range" class="slider" @wheel="app.volumeWheel" :step="app.cfg.audio.volumeStep" min="0" :max="app.cfg.audio.maxVolume" v-model="app.mk.volume"
|
<input type="range" class="slider" @wheel="app.volumeWheel" :step="app.cfg.audio.volumeStep" min="0" :max="app.cfg.audio.maxVolume" v-model="app.mk.volume"
|
||||||
v-if="typeof app.mk.volume != 'undefined'" @change="app.checkMuteChange()"
|
v-if="typeof app.mk.volume != 'undefined'" @change="app.checkMuteChange()"
|
||||||
v-b-tooltip.hover :title="`${(Math.log10(app.mk.volume) * 20).toFixed(2)} dB`">
|
v-b-tooltip.hover :title="$root.formatVolumeTooltip()">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -65,8 +65,9 @@
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="heart-icon" v-if="isLoved">
|
<div class="heart-icon" v-if="!(app.mk.isPlaying && (((app.mk.nowPlayingItem._songId ?? (app.mk.nowPlayingItem.songId ?? app.mk.nowPlayingItem.id )) == itemId) || (app.mk.nowPlayingItem.id == item.id)))">
|
||||||
<div class="svg-icon" :style="{'--url': 'url(./assets/feather/heart-fill.svg)'}"></div>
|
<!-- <div class="heart-unfilled" v-if="isLoved == false" :style="{'--url': 'url(./assets/feather/heart.svg)'}" /> -->
|
||||||
|
<div class="heart-filled" v-if="isLoved == true" :style="{'--url': 'url(./assets/feather/heart-fill.svg)'}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="explicit-icon" v-if="item.attributes && item.attributes.contentRating == 'explicit'"></div>
|
<div class="explicit-icon" v-if="item.attributes && item.attributes.contentRating == 'explicit'"></div>
|
||||||
<template v-if="showMetaData == true" @dblclick="route()">
|
<template v-if="showMetaData == true" @dblclick="route()">
|
||||||
|
|
368
src/renderer/views/pages/installed-themes.ejs
Normal file
368
src/renderer/views/pages/installed-themes.ejs
Normal file
|
@ -0,0 +1,368 @@
|
||||||
|
<script type="text/x-template" id="installed-themes">
|
||||||
|
<div class="content-inner github-themes-page installed-themes-page">
|
||||||
|
<div class="gh-header">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col nopadding">
|
||||||
|
<h1 class="header-text">
|
||||||
|
{{ $root.getLz("settings.option.visual.theme.manageStyles") }}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto nopadding flex-center">
|
||||||
|
<button class="md-btn md-btn-small md-btn-block" @click="$root.appRoute('themes-github')">
|
||||||
|
{{$root.getLz('settings.option.visual.theme.github.explore')}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto flex-center">
|
||||||
|
<button class="md-btn md-btn-small md-btn-block" @click="$root.checkForThemeUpdates()">
|
||||||
|
{{ $root.getLz('settings.option.visual.theme.checkForUpdates') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto nopadding flex-center">
|
||||||
|
<button class="md-btn md-btn-small md-btn-block" @click="openThemesFolder()">
|
||||||
|
{{$root.getLz('settings.option.visual.theme.github.openfolder')}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="gh-content">
|
||||||
|
<div class="repos-list">
|
||||||
|
<div class="repo-header">
|
||||||
|
<h4>Available</h4>
|
||||||
|
</div>
|
||||||
|
<ul class="list-group list-group-flush">
|
||||||
|
<template v-for="theme in themes">
|
||||||
|
<li @click="addStyle(theme.file)"
|
||||||
|
@contextmenu="contextMenu($event, theme)"
|
||||||
|
class="list-group-item list-group-item-dark"
|
||||||
|
:class="{'applied': $root.cfg.visual.styles.includes(theme.file)}">
|
||||||
|
|
||||||
|
<b-row>
|
||||||
|
<b-col class="themeLabel">{{theme.name}}</b-col>
|
||||||
|
<template v-if="$root.cfg.visual.styles.includes(theme.file)">
|
||||||
|
<b-col sm="auto" v-if="theme.pack">
|
||||||
|
<button class="themeContextMenu codicon codicon-package"></button>
|
||||||
|
</b-col>
|
||||||
|
<b-col sm="auto">
|
||||||
|
<button class="themeContextMenu codicon codicon-check"></button>
|
||||||
|
</b-col>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<b-col sm="auto" v-if="theme.pack">
|
||||||
|
<button class="themeContextMenu codicon codicon-package"></button>
|
||||||
|
</b-col>
|
||||||
|
<b-col sm="auto">
|
||||||
|
<button @click.stop="contextMenu($event, theme)" class="themeContextMenu codicon codicon-list-unordered"></button>
|
||||||
|
</b-col>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</b-row>
|
||||||
|
</li>
|
||||||
|
<li @click="addStyle(packEntry.file)"
|
||||||
|
@contextmenu="contextMenu($event, theme)"
|
||||||
|
class="list-group-item list-group-item-dark addon"
|
||||||
|
v-for="packEntry in theme.pack"
|
||||||
|
:class="{'applied': $root.cfg.visual.styles.includes(packEntry.file)}"
|
||||||
|
v-if="theme.pack">
|
||||||
|
|
||||||
|
<b-row>
|
||||||
|
<b-col class="themeLabel">{{packEntry.name}}</b-col>
|
||||||
|
<template v-if="$root.cfg.visual.styles.includes(packEntry.file)">
|
||||||
|
<b-col sm="auto">
|
||||||
|
<button class="themeContextMenu codicon codicon-check"></button>
|
||||||
|
</b-col>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<b-col sm="auto">
|
||||||
|
<button class="themeContextMenu codicon codicon-diff-added"></button>
|
||||||
|
</b-col>
|
||||||
|
</template>
|
||||||
|
</b-row>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="style-editor-container">
|
||||||
|
<div class="repo-header">
|
||||||
|
<h4>Applied</h4>
|
||||||
|
</div>
|
||||||
|
<stylestack-editor ref="stackEditor" v-if="themes.length != 0" :themes="themes"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// do not translate
|
||||||
|
Vue.component('stylestack-editor', {
|
||||||
|
/*html*/
|
||||||
|
template: `
|
||||||
|
<div class="stylestack-editor" >
|
||||||
|
<draggable class="list-group" v-model="$root.cfg.visual.styles" @end="$root.reloadStyles()">
|
||||||
|
<b-list-group-item variant="dark" v-for="theme in $root.cfg.visual.styles" :key="theme">
|
||||||
|
<b-row>
|
||||||
|
<b-col sm="auto">
|
||||||
|
<div class="handle codicon codicon-grabber"></div>
|
||||||
|
</b-col>
|
||||||
|
<b-col class="themeLabel">{{getThemeName(theme)}}</b-col>
|
||||||
|
<b-col sm="auto">
|
||||||
|
<button class="removeItem codicon codicon-close" @click="remove(theme)"></button>
|
||||||
|
</b-col>
|
||||||
|
</b-row>
|
||||||
|
</b-list-group-item>
|
||||||
|
</draggable>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
props: {
|
||||||
|
themes: {
|
||||||
|
type: Array,
|
||||||
|
default: [],
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
selected: null,
|
||||||
|
newTheme: null,
|
||||||
|
themeList: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
console.log(this.themes)
|
||||||
|
this.themeList = [...this.themes]
|
||||||
|
|
||||||
|
this.themeList.forEach(theme => {
|
||||||
|
if (theme.pack) {
|
||||||
|
theme.pack.forEach(packEntry => {
|
||||||
|
packEntry.file = theme.file.replace('index.less', '') + packEntry.file
|
||||||
|
this.themeList.push(packEntry)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
gitHubExplore() {
|
||||||
|
this.$root.appRoute("themes-github")
|
||||||
|
},
|
||||||
|
getThemeName(filename) {
|
||||||
|
try {
|
||||||
|
return this.themeList.find(theme => theme.file === filename).name;
|
||||||
|
} catch (e) {
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
moveUp() {
|
||||||
|
const styles = this.$root.cfg.visual.styles
|
||||||
|
const index = styles.indexOf(this.selected)
|
||||||
|
if (index > 0) {
|
||||||
|
styles.splice(index, 1)
|
||||||
|
styles.splice(index - 1, 0, this.selected)
|
||||||
|
}
|
||||||
|
this.$root.reloadStyles()
|
||||||
|
},
|
||||||
|
moveDown() {
|
||||||
|
const styles = this.$root.cfg.visual.styles
|
||||||
|
const index = styles.indexOf(this.selected)
|
||||||
|
if (index < styles.length - 1) {
|
||||||
|
styles.splice(index, 1)
|
||||||
|
styles.splice(index + 1, 0, this.selected)
|
||||||
|
}
|
||||||
|
this.$root.reloadStyles()
|
||||||
|
},
|
||||||
|
remove(style) {
|
||||||
|
const styles = this.$root.cfg.visual.styles
|
||||||
|
const index = styles.indexOf(style)
|
||||||
|
styles.splice(index, 1)
|
||||||
|
this.$root.reloadStyles()
|
||||||
|
},
|
||||||
|
addStyle(style) {
|
||||||
|
const styles = this.$root.cfg.visual.styles
|
||||||
|
styles.push(style)
|
||||||
|
this.$root.reloadStyles()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
Vue.component('installed-themes', {
|
||||||
|
template: "#installed-themes",
|
||||||
|
props: [],
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
repos: [],
|
||||||
|
openRepo: {
|
||||||
|
id: -1,
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
html_url: '',
|
||||||
|
stargazers_count: 0,
|
||||||
|
owner: {
|
||||||
|
avatar_url: ''
|
||||||
|
},
|
||||||
|
readme: ""
|
||||||
|
},
|
||||||
|
themesInstalled: [],
|
||||||
|
themes: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getThemesList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getThemesList() {
|
||||||
|
let self = this
|
||||||
|
let themes = ipcRenderer.sendSync("get-themes")
|
||||||
|
themes.unshift({
|
||||||
|
name: "Acrylic Grain",
|
||||||
|
file: "grain.less"
|
||||||
|
})
|
||||||
|
themes.unshift({
|
||||||
|
name: "Sweetener",
|
||||||
|
file: "sweetener.less"
|
||||||
|
})
|
||||||
|
themes.unshift({
|
||||||
|
name: "Reduce Visuals",
|
||||||
|
file: "reduce_visuals.less"
|
||||||
|
})
|
||||||
|
themes.unshift({
|
||||||
|
name: "Inline Drawer",
|
||||||
|
file: "inline_drawer.less"
|
||||||
|
})
|
||||||
|
themes.unshift({
|
||||||
|
name: "Dark",
|
||||||
|
file: "dark.less"
|
||||||
|
})
|
||||||
|
this.themes = themes
|
||||||
|
},
|
||||||
|
contextMenu(event, theme) {
|
||||||
|
let self = this
|
||||||
|
let menu = {
|
||||||
|
items: {
|
||||||
|
"uninstall": {
|
||||||
|
name: app.getLz("settings.option.visual.theme.uninstall"),
|
||||||
|
disabled: true,
|
||||||
|
action: () => {
|
||||||
|
bootbox.confirm(app.stringTemplateParser(app.getLz("settings.prompt.visual.theme.uninstallTheme"), {
|
||||||
|
theme: theme.name ?? theme.file
|
||||||
|
}), (res) => {
|
||||||
|
if (res) {
|
||||||
|
console.debug(theme)
|
||||||
|
ipcRenderer.once("theme-uninstalled", (event, args) => {
|
||||||
|
console.debug(event, args)
|
||||||
|
self.getThemesList()
|
||||||
|
})
|
||||||
|
ipcRenderer.invoke("uninstall-theme", theme.path)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"viewInfo": {
|
||||||
|
name: app.getLz("settings.option.visual.theme.viewInfo"),
|
||||||
|
disabled: true,
|
||||||
|
action: () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (theme.path) {
|
||||||
|
menu.items.uninstall.disabled = false
|
||||||
|
}
|
||||||
|
this.$root.showMenuPanel(menu, event)
|
||||||
|
},
|
||||||
|
openThemesFolder() {
|
||||||
|
ipcRenderer.invoke("open-path", "themes")
|
||||||
|
},
|
||||||
|
getInstalledThemes() {
|
||||||
|
let self = this
|
||||||
|
const themes = ipcRenderer.sendSync("get-themes")
|
||||||
|
// for each theme, get the github_repo property and push it to the themesInstalled array, if not blank
|
||||||
|
themes.forEach(theme => {
|
||||||
|
if (theme.github_repo !== "" && typeof theme.commit != "") {
|
||||||
|
self.themesInstalled.push(theme.github_repo.toLowerCase())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
addStyle(filename) {
|
||||||
|
this.$refs.stackEditor.addStyle(filename)
|
||||||
|
},
|
||||||
|
showRepo(repo) {
|
||||||
|
const self = this
|
||||||
|
const readmeUrl = `https://raw.githubusercontent.com/${repo.full_name}/main/README.md`;
|
||||||
|
var requestOptions = {
|
||||||
|
method: 'GET',
|
||||||
|
redirect: 'follow'
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch(readmeUrl, requestOptions)
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(result => {
|
||||||
|
self.openRepo = repo
|
||||||
|
self.openRepo.readme = self.convertReadMe(result);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
self.openRepo = repo
|
||||||
|
self.openRepo.readme = `This repository doesn't have a README.md file.`;
|
||||||
|
console.log('error', error)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
convertReadMe(text) {
|
||||||
|
return marked.parse(text)
|
||||||
|
},
|
||||||
|
installThemeRepo(repo) {
|
||||||
|
let self = this
|
||||||
|
let msg = app.stringTemplateParser(app.getLz('settings.option.visual.theme.github.install.confirm'), {
|
||||||
|
repo: repo.full_name
|
||||||
|
});
|
||||||
|
bootbox.confirm(msg, (res) => {
|
||||||
|
if (res) {
|
||||||
|
ipcRenderer.once("theme-installed", (event, arg) => {
|
||||||
|
if (arg.success) {
|
||||||
|
self.themes = ipcRenderer.sendSync("get-themes")
|
||||||
|
self.getInstalledThemes()
|
||||||
|
notyf.success(app.getLz('settings.notyf.visual.theme.install.success'));
|
||||||
|
} else {
|
||||||
|
notyf.error(app.getLz('settings.notyf.visual.theme.install.error'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ipcRenderer.invoke("get-github-theme", repo.html_url)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
installThemeURL() {
|
||||||
|
let self = this
|
||||||
|
bootbox.prompt(app.getLz('settings.prompt.visual.theme.github.URL'), (result) => {
|
||||||
|
if (result) {
|
||||||
|
ipcRenderer.once("theme-installed", (event, arg) => {
|
||||||
|
if (arg.success) {
|
||||||
|
self.themes = ipcRenderer.sendSync("get-themes")
|
||||||
|
notyf.success(app.getLz('settings.notyf.visual.theme.install.success'));
|
||||||
|
} else {
|
||||||
|
notyf.error(app.getLz('settings.notyf.visual.theme.install.error'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ipcRenderer.invoke("get-github-theme", result)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getRepos() {
|
||||||
|
let self = this
|
||||||
|
var requestOptions = {
|
||||||
|
method: 'GET',
|
||||||
|
redirect: 'follow'
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch("https://api.github.com/search/repositories?q=topic:cidermusictheme fork:true", requestOptions)
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(result => {
|
||||||
|
let items = JSON.parse(result).items
|
||||||
|
self.repos = items
|
||||||
|
})
|
||||||
|
.catch(error => console.log('error', error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
|
@ -22,7 +22,7 @@
|
||||||
<hr>
|
<hr>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<h4>{{ loaded.attributes.listenTimeInMinutes }} {{$root.getLz('term.time.minutes')}}</h4>
|
<h4>{{ convertToHours(loaded.attributes.listenTimeInMinutes) }} {{$root.getLz('term.time.hours')}}</h4>
|
||||||
<h4>{{ loaded.attributes.uniqueAlbumCount }} {{$root.getLz('term.uniqueAlbums')}}</h4>
|
<h4>{{ loaded.attributes.uniqueAlbumCount }} {{$root.getLz('term.uniqueAlbums')}}</h4>
|
||||||
<h4>{{ loaded.attributes.uniqueArtistCount }} {{$root.getLz('term.uniqueArtists')}}</h4>
|
<h4>{{ loaded.attributes.uniqueArtistCount }} {{$root.getLz('term.uniqueArtists')}}</h4>
|
||||||
<h4>{{ loaded.attributes.uniqueSongCount }} {{$root.getLz('term.uniqueSongs')}}</h4>
|
<h4>{{ loaded.attributes.uniqueSongCount }} {{$root.getLz('term.uniqueSongs')}}</h4>
|
||||||
|
@ -40,7 +40,7 @@
|
||||||
<mediaitem-square :item="artistData.relationships.artist.data[0]"></mediaitem-square>
|
<mediaitem-square :item="artistData.relationships.artist.data[0]"></mediaitem-square>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer">
|
<div class="card-footer">
|
||||||
{{ artistData.attributes.listenTimeInMinutes }} {{$root.getLz('term.time.minutes', {'count': artistData.attributes.listenTimeInMinutes})}}<br>
|
{{ convertToHours(artistData.attributes.listenTimeInMinutes) }} {{$root.getLz('term.time.hours', {'count': convertToHours(artistData.attributes.listenTimeInMinutes) })}}<br>
|
||||||
{{$root.getLz('term.listenedTo')}} {{ artistData.attributes.playCount }} {{$root.getLz('term.times')}}
|
{{$root.getLz('term.listenedTo')}} {{ artistData.attributes.playCount }} {{$root.getLz('term.times')}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -55,7 +55,7 @@
|
||||||
<mediaitem-square :item="albumData.relationships.album.data[0]"></mediaitem-square>
|
<mediaitem-square :item="albumData.relationships.album.data[0]"></mediaitem-square>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer">
|
<div class="card-footer">
|
||||||
{{ albumData.attributes.listenTimeInMinutes }} {{$root.getLz('term.time.minutes', {'count': albumData.attributes.listenTimeInMinutes})}}<br>
|
{{ convertToHours(albumData.attributes.listenTimeInMinutes) }} {{$root.getLz('term.time.hours', {'count': convertToHours(albumData.attributes.listenTimeInMinutes)})}}<br>
|
||||||
{{ albumData.attributes.playCount }} {{$root.getLz('term.plays')}}
|
{{ albumData.attributes.playCount }} {{$root.getLz('term.plays')}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -151,6 +151,9 @@
|
||||||
let playlist = await app.mk.api.v3.music(replayData.relationships.playlist.data[0].href, {extend: "editorialArtwork,editorialVideo"})
|
let playlist = await app.mk.api.v3.music(replayData.relationships.playlist.data[0].href, {extend: "editorialArtwork,editorialVideo"})
|
||||||
replayData.playlist = playlist.data.data[0]
|
replayData.playlist = playlist.data.data[0]
|
||||||
this.loaded = replayData
|
this.loaded = replayData
|
||||||
|
},
|
||||||
|
convertToHours(minutes) {
|
||||||
|
return Math.floor(minutes / 60)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -441,6 +441,30 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="md-option-line" v-show="app.cfg.advanced.AudioContext && app.cfg.audio.normalization">
|
||||||
|
<div class="md-option-segment">
|
||||||
|
dB SPL Display
|
||||||
|
<br>
|
||||||
|
<small>(Advanced users only) Display dB SPL instead of dBFS on the volume slider.</small>
|
||||||
|
</div>
|
||||||
|
<div class="md-option-segment md-option-segment_auto">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" v-model="app.cfg.audio.dBSPL" switch/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="md-option-line" v-show="app.cfg.audio.dBSPL">
|
||||||
|
<div class="md-option-segment">
|
||||||
|
0 dBFS Calibration
|
||||||
|
<br>
|
||||||
|
<small>Enter the peak Z-weighted dB SPL when Cider is at 0 dBFS.</small>
|
||||||
|
</div>
|
||||||
|
<div class="md-option-segment md-option-segment_auto">
|
||||||
|
<label>
|
||||||
|
<input type="number" v-model="app.cfg.audio.dBSPLcalibration"/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</b-tab>
|
</b-tab>
|
||||||
|
@ -456,20 +480,8 @@
|
||||||
{{$root.getLz('settings.header.visual.theme')}}
|
{{$root.getLz('settings.header.visual.theme')}}
|
||||||
</div>
|
</div>
|
||||||
<div class="md-option-segment md-option-segment_auto">
|
<div class="md-option-segment md-option-segment_auto">
|
||||||
<label>
|
<button class="md-btn md-btn-block" @click="$root.appRoute('installed-themes')">
|
||||||
<select class="md-select" @change="$root.setTheme($root.cfg.visual.theme)"
|
{{$root.getLz('settings.option.visual.theme.manageStyles')}}
|
||||||
v-model="$root.cfg.visual.theme">
|
|
||||||
<option value="default.less">
|
|
||||||
{{$root.getLz('settings.option.visual.theme.default')}}
|
|
||||||
</option>
|
|
||||||
<option value="dark.less">{{$root.getLz('settings.option.visual.theme.dark')}}
|
|
||||||
</option>
|
|
||||||
<option v-for="theme in themes" :value="theme.file">{{ theme.name }}</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
<button class="md-btn md-btn-small md-btn-block" @click="gitHubExplore()"
|
|
||||||
style="margin-top: 8px">
|
|
||||||
{{$root.getLz('settings.option.visual.theme.github.explore')}}
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1192,19 +1204,6 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="md-option-line">
|
|
||||||
<!-- Do not translate -->
|
|
||||||
<div class="md-option-segment">
|
|
||||||
Style Editor<br>
|
|
||||||
<small>Mix and match various theme components to get Cider looking exactly how you
|
|
||||||
want.</small>
|
|
||||||
</div>
|
|
||||||
<div class="md-option-segment">
|
|
||||||
<stylestack-editor :themes="themes"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="md-option-line">
|
<div class="md-option-line">
|
||||||
<div class="md-option-segment">
|
<div class="md-option-segment">
|
||||||
{{$root.getLz('settings.option.experimental.unknownPlugin')}}
|
{{$root.getLz('settings.option.experimental.unknownPlugin')}}
|
||||||
|
@ -1412,112 +1411,6 @@
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
|
||||||
// do not translate
|
|
||||||
Vue.component('stylestack-editor', {
|
|
||||||
/*html*/
|
|
||||||
template: `
|
|
||||||
<div class="stylestack-editor">
|
|
||||||
<draggable class="list-group" v-model="$root.cfg.visual.styles" @end="$root.reloadStyles()">
|
|
||||||
<b-list-group-item variant="dark" v-for="theme in $root.cfg.visual.styles" :key="theme">
|
|
||||||
<b-row>
|
|
||||||
<b-col class="themeLabel">{{getThemeName(theme)}}</b-col>
|
|
||||||
<b-col sm="auto">
|
|
||||||
<button class="removeItem codicon codicon-close" @click="remove(theme)"></button>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-list-group-item>
|
|
||||||
<b-list-group-item slot="footer" style="-webkit-user-drag: none" variant="dark">
|
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<b-dropdown class="stylesDropdown" variant="primary" text="Add Style...">
|
|
||||||
<b-dropdown-item v-for="theme in themeList" @click="addStyle(theme.file)">{{theme.name}}</b-dropdown-item>
|
|
||||||
</b-dropdown>
|
|
||||||
</b-col>
|
|
||||||
<b-col>
|
|
||||||
<b-btn @click="gitHubExplore()">{{$root.getLz('settings.option.visual.theme.github.explore')}}</b-btn>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-list-group-item>
|
|
||||||
</draggable>
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
props: {
|
|
||||||
themes: {
|
|
||||||
type: Array,
|
|
||||||
default: []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data: function () {
|
|
||||||
return {
|
|
||||||
selected: null,
|
|
||||||
newTheme: null,
|
|
||||||
themeList: []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.themeList = [...this.themes]
|
|
||||||
this.themeList.unshift({
|
|
||||||
name: "Acrylic Grain",
|
|
||||||
file: "grain.less"
|
|
||||||
})
|
|
||||||
this.themeList.unshift({
|
|
||||||
name: "Sweetener",
|
|
||||||
file: "sweetener.less"
|
|
||||||
})
|
|
||||||
this.themeList.unshift({
|
|
||||||
name: "Reduce Visuals",
|
|
||||||
file: "reduce_visuals.less"
|
|
||||||
})
|
|
||||||
this.themeList.unshift({
|
|
||||||
name: "Inline Drawer",
|
|
||||||
file: "inline_drawer.less"
|
|
||||||
})
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
gitHubExplore() {
|
|
||||||
this.$root.appRoute("themes-github")
|
|
||||||
},
|
|
||||||
getThemeName(filename) {
|
|
||||||
try {
|
|
||||||
return this.themeList.find(theme => theme.file === filename).name;
|
|
||||||
} catch (e) {
|
|
||||||
return filename;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
moveUp() {
|
|
||||||
const styles = this.$root.cfg.visual.styles
|
|
||||||
const index = styles.indexOf(this.selected)
|
|
||||||
if (index > 0) {
|
|
||||||
styles.splice(index, 1)
|
|
||||||
styles.splice(index - 1, 0, this.selected)
|
|
||||||
}
|
|
||||||
this.$root.reloadStyles()
|
|
||||||
},
|
|
||||||
moveDown() {
|
|
||||||
const styles = this.$root.cfg.visual.styles
|
|
||||||
const index = styles.indexOf(this.selected)
|
|
||||||
if (index < styles.length - 1) {
|
|
||||||
styles.splice(index, 1)
|
|
||||||
styles.splice(index + 1, 0, this.selected)
|
|
||||||
}
|
|
||||||
this.$root.reloadStyles()
|
|
||||||
},
|
|
||||||
remove(style) {
|
|
||||||
const styles = this.$root.cfg.visual.styles
|
|
||||||
const index = styles.indexOf(style)
|
|
||||||
styles.splice(index, 1)
|
|
||||||
this.$root.reloadStyles()
|
|
||||||
},
|
|
||||||
addStyle(style) {
|
|
||||||
const styles = this.$root.cfg.visual.styles
|
|
||||||
styles.push(style)
|
|
||||||
this.$root.reloadStyles()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
Vue.component('cider-settings', {
|
Vue.component('cider-settings', {
|
||||||
template: "#cider-settings",
|
template: "#cider-settings",
|
||||||
|
@ -1629,10 +1522,10 @@
|
||||||
app.cfg.general.keybindings.browse = [app.platform == "darwin" ? "Command" : "Control", "B"];
|
app.cfg.general.keybindings.browse = [app.platform == "darwin" ? "Command" : "Control", "B"];
|
||||||
app.cfg.general.keybindings.togglePrivateSession = [app.platform == "darwin" ? "Command" : "Control", "P"];
|
app.cfg.general.keybindings.togglePrivateSession = [app.platform == "darwin" ? "Command" : "Control", "P"];
|
||||||
app.cfg.general.keybindings.webRemote = [app.platform == "darwin" ? "Command" : "Control", "W"];
|
app.cfg.general.keybindings.webRemote = [app.platform == "darwin" ? "Command" : "Control", "W"];
|
||||||
app.cfg.general.keybindings.audioSettings = [app.platform == "darwin" ? "Option" : "Shift", "A"];
|
app.cfg.general.keybindings.audioSettings = [app.platform == "darwin" ? "Option" : "Alt", "A"];
|
||||||
app.cfg.general.keybindings.pluginMenu = [app.platform == "darwin" ? "Option" : "Shift", "P"];
|
app.cfg.general.keybindings.pluginMenu = [app.platform == "darwin" ? "Option" : "Alt", "P"];
|
||||||
app.cfg.general.keybindings.castToDevices = [app.platform == "darwin" ? "Option" : "Shift", "C"];
|
app.cfg.general.keybindings.castToDevices = [app.platform == "darwin" ? "Option" : "Alt", "C"];
|
||||||
app.cfg.general.keybindings.settings = [app.platform == "darwin" ? "Option" : "Shift", "S"];
|
app.cfg.general.keybindings.settings = [app.platform == "darwin" ? "Option" : "Alt", "S"];
|
||||||
app.cfg.general.keybindings.openDeveloperTools = [app.platform == "darwin" ? "Command" : "Control", app.platform == "darwin" ? "Option" : "Shift", "I"];
|
app.cfg.general.keybindings.openDeveloperTools = [app.platform == "darwin" ? "Command" : "Control", app.platform == "darwin" ? "Option" : "Shift", "I"];
|
||||||
notyf.success(app.getLz('settings.notyf.general.keybindings.update.success'));
|
notyf.success(app.getLz('settings.notyf.general.keybindings.update.success'));
|
||||||
bootbox.confirm(app.getLz("settings.prompt.general.keybindings.update.success"), (ok) => {
|
bootbox.confirm(app.getLz("settings.prompt.general.keybindings.update.success"), (ok) => {
|
||||||
|
|
|
@ -5,17 +5,14 @@
|
||||||
<div class="col nopadding">
|
<div class="col nopadding">
|
||||||
<h1 class="header-text">{{$root.getLz('settings.header.visual.theme.github.page')}}</h1>
|
<h1 class="header-text">{{$root.getLz('settings.header.visual.theme.github.page')}}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto flex-center">
|
<div class="col-auto nopadding flex-center">
|
||||||
<select class="md-select" @change="$root.setTheme($root.cfg.visual.theme)"
|
<button class="md-btn md-btn-small md-btn-block" @click="$root.appRoute('installed-themes')">
|
||||||
v-model="$root.cfg.visual.theme">
|
{{$root.getLz('settings.option.visual.theme.manageStyles')}}
|
||||||
<option value="default.less">{{$root.getLz('settings.option.visual.theme.default')}}</option>
|
</button>
|
||||||
<option value="dark.less">{{$root.getLz('settings.option.visual.theme.dark')}}</option>
|
|
||||||
<option v-for="theme in themes" :value="theme.file">{{ theme.name }}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto flex-center">
|
<div class="col-auto flex-center">
|
||||||
<button class="md-btn md-btn-small md-btn-block" @click="openThemesFolder()">
|
<button class="md-btn md-btn-small md-btn-block" @click="$root.checkForThemeUpdates()">
|
||||||
{{$root.getLz('settings.option.visual.theme.github.openfolder')}}
|
Check for updates
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto nopadding flex-center">
|
<div class="col-auto nopadding flex-center">
|
||||||
|
@ -106,7 +103,6 @@
|
||||||
this.themes = ipcRenderer.sendSync("get-themes")
|
this.themes = ipcRenderer.sendSync("get-themes")
|
||||||
this.getRepos();
|
this.getRepos();
|
||||||
this.getInstalledThemes();
|
this.getInstalledThemes();
|
||||||
app.checkForThemeUpdates()
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
openThemesFolder() {
|
openThemesFolder() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue