Merge branch 'ciderapp:develop' into develop
This commit is contained in:
commit
0eef2851a7
19 changed files with 589 additions and 17 deletions
|
@ -38,9 +38,11 @@
|
|||
"@sentry/electron": "^3.0.7",
|
||||
"@sentry/integrations": "^6.19.6",
|
||||
"adm-zip": "0.4.10",
|
||||
"airtunes2": "git+https://github.com/vapormusic/node_airtunes2.git",
|
||||
"castv2-client": "^1.2.0",
|
||||
"chokidar": "^3.5.3",
|
||||
"discord-rpc": "^4.0.1",
|
||||
"dns-js": "git+https://github.com/ciderapp/node-dns-js.git",
|
||||
"ejs": "^3.1.6",
|
||||
"electron-fetch": "^1.7.4",
|
||||
"electron-log": "^4.4.6",
|
||||
|
@ -52,7 +54,6 @@
|
|||
"get-port": "^5.1.1",
|
||||
"jsonc": "^2.0.0",
|
||||
"lastfmapi": "^0.1.1",
|
||||
"dns-js": "git+https://github.com/ciderapp/node-dns-js.git",
|
||||
"mdns-js": "git+https://github.com/ciderapp/node-mdns-js.git",
|
||||
"mpris-service": "^2.1.2",
|
||||
"music-metadata": "^7.12.3",
|
||||
|
@ -70,11 +71,11 @@
|
|||
"youtube-search-without-api-key": "^1.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/adm-zip": "^0.5.0",
|
||||
"@types/discord-rpc": "4.0.2",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/qrcode-terminal": "^0.12.0",
|
||||
"@types/ws": "^8.5.3",
|
||||
"@types/adm-zip": "^0.5.0",
|
||||
"electron": "git+https://github.com/castlabs/electron-releases.git",
|
||||
"electron-builder": "^23.0.3",
|
||||
"electron-builder-notarize-pkg": "^1.2.0",
|
||||
|
@ -107,9 +108,9 @@
|
|||
}
|
||||
],
|
||||
"build": {
|
||||
"electronVersion": "18.0.3",
|
||||
"electronVersion": "18.0.4",
|
||||
"electronDownload": {
|
||||
"version": "18.0.3+wvcus",
|
||||
"version": "18.0.4+wvcus",
|
||||
"mirror": "https://github.com/castlabs/electron-releases/releases/download/v"
|
||||
},
|
||||
"appId": "cider",
|
||||
|
|
|
@ -262,3 +262,8 @@ Update 16/04/2022 9:30 UTC
|
|||
|
||||
* `settings.header.connect`: Added for `en_US`
|
||||
|
||||
Update 22/04/2022 13:00 UTC
|
||||
|
||||
* `settings.option.general.keybindings`: Added for `en_US`
|
||||
* `settings.option.general.keybindings.open`: Added for `en_US`
|
||||
|
||||
|
|
|
@ -279,6 +279,8 @@
|
|||
"settings.option.general.updateCider.branch.develop": "Development",
|
||||
"settings.option.general.customizeSidebar": "Customize Sidebar Items",
|
||||
"settings.option.general.customizeSidebar.customize": "Customize",
|
||||
"settings.option.general.keybindings": "Keybindings",
|
||||
"settings.option.general.keybindings.open": "Open",
|
||||
"settings.notyf.updateCider.update-not-available": "No update available",
|
||||
"settings.notyf.updateCider.update-downloaded": "Update has been downloaded, restart to apply",
|
||||
"settings.notyf.updateCider.update-error": "Error updating Cider",
|
||||
|
@ -422,7 +424,7 @@
|
|||
"settings.option.advanced.playlistTrackMapping": "Playlist Track Mapping",
|
||||
"settings.option.advanced.playlistTrackMapping.description": "Enables deep scanning of playlists to determine which tracks are in which playlists. Playlist cache build times can increase significantly.",
|
||||
"settings.option.visual.transparent": "Transparent frame",
|
||||
"settings.option.visual.transparent.description": "Transparent frame (needs Theme Support , requires relaunch)",
|
||||
"settings.option.visual.transparent.description": "Transparent frame (needs Theme Support, requires relaunch)",
|
||||
"settings.header.advanced": "Advanced",
|
||||
"settings.header.connect": "Sync",
|
||||
"settings.option.connect.link_account": "Enable Sync with Cider Connect",
|
||||
|
|
|
@ -279,6 +279,8 @@
|
|||
"settings.option.general.updateCider.branch.develop": "Fejlesztői",
|
||||
"settings.option.general.customizeSidebar": "Oldalsáv elemeinek testreszabása",
|
||||
"settings.option.general.customizeSidebar.customize": "Testreszabás",
|
||||
"settings.option.general.keybindings": "Billentyűparancsok",
|
||||
"settings.option.general.keybindings.open": "Megnyitás",
|
||||
"settings.notyf.updateCider.update-not-available": "Nem található frissítés",
|
||||
"settings.notyf.updateCider.update-downloaded": "A frissítés le lett töltve, a telepítéshez indítsa újra az alkalmazást",
|
||||
"settings.notyf.updateCider.update-error": "Hiba történt a frissítés közben",
|
||||
|
|
|
@ -279,6 +279,8 @@
|
|||
"settings.option.general.updateCider.branch.develop": "Development",
|
||||
"settings.option.general.customizeSidebar": "Customize Sidebar Items",
|
||||
"settings.option.general.customizeSidebar.customize": "Customize",
|
||||
"settings.option.general.keybindings": "Keybindings",
|
||||
"settings.option.general.keybindings.open": "Open",
|
||||
"settings.notyf.updateCider.update-not-available": "No update available",
|
||||
"settings.notyf.updateCider.update-downloaded": "Update has been downloaded, restart to apply",
|
||||
"settings.notyf.updateCider.update-error": "Error updating Cider",
|
||||
|
@ -422,7 +424,7 @@
|
|||
"settings.option.advanced.playlistTrackMapping": "Playlist Track Mapping",
|
||||
"settings.option.advanced.playlistTrackMapping.description": "Enables deep scanning of playlists to determine which tracks are in which playlists. Playlist cache build times can increase significantly.",
|
||||
"settings.option.visual.transparent": "Transparent frame",
|
||||
"settings.option.visual.transparent.description": "Transparent frame (needs Theme Support , requires relaunch)",
|
||||
"settings.option.visual.transparent.description": "Transparent frame (needs Theme Support, requires relaunch)",
|
||||
"settings.header.advanced": "Advanced",
|
||||
"settings.header.connect": "Connect",
|
||||
"spatial.notTurnedOn": "Audio Spatialization is disabled. To use, please enable it first.",
|
||||
|
|
|
@ -528,6 +528,7 @@ export class BrowserWindow {
|
|||
app.get("/connect/set-cc-user/:data", (req, res) => {
|
||||
//utils.getStoreValue('connectUser', JSON.parse()) // [Connect] Save user in store
|
||||
utils.setStoreValue('connectUser', JSON.parse(req.params.data))
|
||||
utils.getWindow().reload()
|
||||
res.redirect(`https://connect.cidercollective.dev/linked.html`)
|
||||
});
|
||||
// [Connect] Set auth URL in store for `shell.openExternal`
|
||||
|
@ -1202,6 +1203,13 @@ export class BrowserWindow {
|
|||
ipcMain.on('cc-auth', (_event) => {
|
||||
shell.openExternal(String(utils.getStoreValue('cc_authURL')));
|
||||
});
|
||||
|
||||
ipcMain.on('cc-logout', (_event) => {
|
||||
utils.setStoreValue('connectUser', {
|
||||
auth: null
|
||||
});
|
||||
utils.getWindow().reload();
|
||||
});
|
||||
/* *********************************************************************************************
|
||||
* Window Events
|
||||
* **********************************************************************************************/
|
||||
|
|
|
@ -154,8 +154,12 @@ export class Store {
|
|||
"advanced": {
|
||||
"AudioContext": false,
|
||||
"experiments": [],
|
||||
"playlistTrackMapping": true
|
||||
}
|
||||
"playlistTrackMapping": true,
|
||||
"ffmpegLocation": ""
|
||||
},
|
||||
"connectUser": {
|
||||
"auth": null,
|
||||
},
|
||||
}
|
||||
private migrations: any = {
|
||||
'>=1.4.3': (store: ElectronStore) => {
|
||||
|
|
|
@ -121,7 +121,7 @@ export default class Thumbar {
|
|||
},
|
||||
{
|
||||
label: 'Plug-in Menu',
|
||||
accelerator: 'CommandOrControl++Shift+P',
|
||||
accelerator: 'CommandOrControl+Shift+P',
|
||||
click: () => this._win.webContents.executeJavaScript(`app.modals.pluginMenu = true`)
|
||||
}
|
||||
|
||||
|
|
344
src/main/plugins/raop.ts
Normal file
344
src/main/plugins/raop.ts
Normal file
|
@ -0,0 +1,344 @@
|
|||
import * as electron from 'electron';
|
||||
import * as os from 'os';
|
||||
import * as fs from 'fs';
|
||||
import { join, resolve } from 'path';
|
||||
import * as CiderReceiver from '../base/castreceiver';
|
||||
import fetch from 'electron-fetch';
|
||||
import {Stream} from "stream";
|
||||
import {spawn} from 'child_process';
|
||||
import {Worker} from 'worker_threads';
|
||||
import { Blob } from 'buffer';
|
||||
|
||||
|
||||
export default class RAOP {
|
||||
|
||||
/**
|
||||
* Private variables for interaction in plugins
|
||||
*/
|
||||
private _utils: any;
|
||||
private _win: any;
|
||||
private _app: any;
|
||||
private _store: any;
|
||||
private _cacheAttr: any;
|
||||
|
||||
private ipairplay: any = "";
|
||||
private portairplay: any = "";
|
||||
private u = require('airtunes2');
|
||||
private airtunes: any;
|
||||
private device: any;
|
||||
private mdns = require('mdns-js');
|
||||
private ok: any = 1;
|
||||
private devices: any = [];
|
||||
private castDevices: any = [];
|
||||
private i: any = false;
|
||||
private audioStream: any = new Stream.PassThrough();
|
||||
private ffmpeg: any = null;
|
||||
private worker: any = null;
|
||||
|
||||
|
||||
private processNode = `
|
||||
import {parentPort, workerData} from "worker_threads";
|
||||
function getAudioConv (buffers) {
|
||||
|
||||
function interleave16(leftChannel, rightChannel) {
|
||||
var length = leftChannel.length + rightChannel.length;
|
||||
var result = new Int16Array(length);
|
||||
|
||||
var inputIndex = 0;
|
||||
|
||||
for (var index = 0; index < length;) {
|
||||
result[index++] = leftChannel[inputIndex];
|
||||
result[index++] = rightChannel[inputIndex];
|
||||
inputIndex++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function convert(n) {
|
||||
var v = n < 0 ? n * 32768 : n * 32767; // convert in range [-32768, 32767]
|
||||
return Math.max(-32768, Math.min(32768, v)); // clamp
|
||||
}
|
||||
|
||||
function bitratechange(e) {
|
||||
var t = e.length;
|
||||
let sampleRate = 96.0;
|
||||
let outputSampleRate = 44.1;
|
||||
var s = 0,
|
||||
o = sampleRate / outputSampleRate,
|
||||
u = Math.ceil(t * outputSampleRate / sampleRate),
|
||||
a = new Int16Array(u);
|
||||
for (let i = 0; i < u; i++) {
|
||||
a[i] = e[Math.floor(s)];
|
||||
s += o;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
let newaudio = buffers;
|
||||
|
||||
let pcmData = new Int8Array(interleave16(bitratechange(Int16Array.from(newaudio[0], x => convert(x))), bitratechange(Int16Array.from(newaudio[1], x => convert(x)))).buffer);
|
||||
return pcmData;
|
||||
}
|
||||
parentPort.on("message", data => {
|
||||
parentPort.postMessage({buffer: data.buffer, outbuffer: getAudioConv(data.buffer)});
|
||||
});
|
||||
|
||||
`;
|
||||
|
||||
private ondeviceup(name: any, host: any, port: any, addresses: any) {
|
||||
if (this.castDevices.findIndex((item: any) => item.name === host && item.port === port && item.addresses === addresses) === -1) {
|
||||
this.castDevices.push({
|
||||
name: host,
|
||||
host: addresses ? addresses[0] : '',
|
||||
port: port,
|
||||
addresses: addresses
|
||||
});
|
||||
if (this.devices.indexOf(host) === -1) {
|
||||
this.devices.push(host);
|
||||
}
|
||||
if (name) {
|
||||
this._win.webContents.executeJavaScript(`console.log('deviceFound','ip: ${host} name:${name}')`).catch((err: any) => console.error(err));
|
||||
console.log("deviceFound", host, name);
|
||||
}
|
||||
} else {
|
||||
this._win.webContents.executeJavaScript(`console.log('deviceFound (added)','ip: ${host} name:${name}')`).catch((err: any) => console.error(err));
|
||||
console.log("deviceFound (added)", host, name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base Plugin Details (Eventually implemented into a GUI in settings)
|
||||
*/
|
||||
public name: string = 'RAOP';
|
||||
public description: string = 'RAOP Plugin';
|
||||
public version: string = '0.0.1';
|
||||
public author: string = 'vapormusic / Cider Collective';
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
this._win = win;
|
||||
|
||||
electron.ipcMain.on('getKnownAirplayDevices', (event) => {
|
||||
event.returnValue = this.castDevices
|
||||
});
|
||||
|
||||
electron.ipcMain.on("getAirplayDevice", (event, data) => {
|
||||
this.castDevices = [];
|
||||
console.log("scan for airplay devices");
|
||||
|
||||
const browser = this.mdns.createBrowser(this.mdns.tcp('raop'));
|
||||
browser.on('ready', browser.discover);
|
||||
|
||||
browser.on('update', (service: any) => {
|
||||
if (service.addresses && service.fullname && service.fullname.includes('_raop._tcp')) {
|
||||
this._win.webContents.executeJavaScript(`console.log(
|
||||
"${service.name} ${service.host}:${service.port} ${service.addresses}"
|
||||
)`);}
|
||||
this.ondeviceup(service.name, service.host, service.port, service.addresses);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
electron.ipcMain.on("performAirplayPCM", (event, ipv4, ipport, sepassword, title, artist, album, artworkURL) => {
|
||||
|
||||
if (ipv4 != this.ipairplay || ipport != this.portairplay) {
|
||||
if (this.airtunes == null) { this.airtunes = new this.u()}
|
||||
this.ipairplay = ipv4;
|
||||
this.portairplay = ipport;
|
||||
this.device = this.airtunes.add(ipv4, {
|
||||
port: ipport,
|
||||
volume: 60,
|
||||
password: sepassword,
|
||||
});
|
||||
this.device.on('status', (status: any) => {
|
||||
console.log('device status', status);
|
||||
if (status == "ready"){
|
||||
this._win.webContents.setAudioMuted(true);
|
||||
this._win.webContents.executeJavaScript(`CiderAudio.sendAudio()`).catch((err: any) => console.error(err));
|
||||
}
|
||||
if (status == 'stopped') {
|
||||
this.airtunes.stopAll(() => {
|
||||
console.log('end');
|
||||
});
|
||||
this.airtunes = null;
|
||||
this.device = null;
|
||||
this.ipairplay = '';
|
||||
this.portairplay = '';
|
||||
this.ok = 1;
|
||||
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
if (this.ok == 1) {
|
||||
console.log(this.device.key, title ?? '', artist ?? '', album ?? '');
|
||||
this.airtunes.setTrackInfo(this.device.key, title ?? '', artist?? '', album?? '');
|
||||
this.uploadImageAirplay(artworkURL);
|
||||
console.log('done');
|
||||
this.ok == 2
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
electron.ipcMain.on('writeWAV', (event, leftbuffer, rightbuffer) => {
|
||||
if (this.airtunes != null) {
|
||||
if (this.worker == null) {
|
||||
try{
|
||||
const toDataUrl = (js: any) => new URL(`data:text/javascript,${encodeURIComponent(js)}`);
|
||||
// let blob = new Blob([this.processNode], { type: 'application/javascript' });
|
||||
//Create new worker
|
||||
this.worker = new Worker(toDataUrl(this.processNode));
|
||||
|
||||
//Listen for a message from worker
|
||||
this.worker.on("message", (result: any) => {
|
||||
// fs.writeFile(join(electron.app.getPath('userData'), 'buffer.raw'), Buffer.from(Int8Array.from(result.outbuffer)),{flag: 'a+'}, function (err) {
|
||||
// if (err) throw err;
|
||||
// console.log('It\'s saved!');
|
||||
// });
|
||||
this.airtunes.circularBuffer.write(Buffer.from(Int8Array.from(result.outbuffer)));
|
||||
});
|
||||
|
||||
this.worker.on("error", (error: any) => {
|
||||
console.log("bruh",error);
|
||||
});
|
||||
this.worker.postMessage({buffer: [leftbuffer, rightbuffer]});
|
||||
} catch (e){console.log(e)}
|
||||
|
||||
|
||||
// this.ffmpeg != null ? this.ffmpeg.kill() : null;
|
||||
// this.ffmpeg = spawn(this._utils.getStoreValue("advanced.ffmpegLocation"), [
|
||||
// '-f', 's16le', // PCM 16bits, little-endian
|
||||
// '-ar', '48000',
|
||||
// '-ac', "2",
|
||||
// '-err_detect','ignore_err',
|
||||
// '-i', "http://localhost:9000/audio.wav",
|
||||
// '-acodec', 'pcm_s16le',
|
||||
// '-f', 's16le', // PCM 16bits, little-endian
|
||||
// '-ar', '44100', // Sampling rate
|
||||
// '-ac', "2", // Stereo
|
||||
// 'pipe:1' // Output on stdout
|
||||
// ]);
|
||||
|
||||
// // pipe data to AirTunes
|
||||
// this.ffmpeg.stdout.pipe(this.airtunes);
|
||||
// this.i = true;
|
||||
} else {
|
||||
this.worker.postMessage({buffer: [leftbuffer, rightbuffer]});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
electron.ipcMain.on('disconnectAirplay', (event) => {
|
||||
this._win.webContents.setAudioMuted(false);
|
||||
this.airtunes.stopAll(function () {
|
||||
console.log('end');
|
||||
});
|
||||
this.airtunes = null;
|
||||
this.device = null;
|
||||
this.ipairplay = '';
|
||||
this.portairplay = '';
|
||||
this.ok = 1;
|
||||
this.i = false;
|
||||
});
|
||||
|
||||
electron.ipcMain.on('updateAirplayInfo', (event, title, artist, album, artworkURL) => {
|
||||
if (this.airtunes && this.device) {
|
||||
console.log(this.device.key, title, artist, album);
|
||||
this.airtunes.setTrackInfo(this.device.key, title, artist, album);
|
||||
this.uploadImageAirplay(artworkURL)
|
||||
}
|
||||
});
|
||||
|
||||
electron.ipcMain.on('updateRPCImage', (_event, imageurl) => {
|
||||
this.uploadImageAirplay(imageurl)
|
||||
})
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private uploadImageAirplay = (url: any) => {
|
||||
try {
|
||||
if (url != null && url != '') {
|
||||
//console.log(join(this._app.getPath('userData'), 'temp.png'), url);
|
||||
fetch(url)
|
||||
.then(res => res.buffer())
|
||||
.then((buffer) => {
|
||||
this.airtunes.setArtwork(this.device.key, buffer, "image/png");
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
});
|
||||
}
|
||||
} catch (e) { console.log(e) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs on app stop
|
||||
*/
|
||||
onBeforeQuit(): void {
|
||||
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Runs on song change
|
||||
// * @param attributes Music Attributes
|
||||
// */
|
||||
// onNowPlayingItemDidChange(attributes: any): void {
|
||||
// if (this.airtunes && this.device) {
|
||||
// let title = attributes.name ? attributes.name : '';
|
||||
// let artist = attributes.artistName ? attributes.artistName : '';
|
||||
// let album = attributes.albumName ? attributes.albumName : '';
|
||||
// let artworkURL = attributes?.artwork?.url?.replace('{w}', '1024').replace('{h}', '1024') ?? null;
|
||||
// console.log(this.device.key, title, artist, album);
|
||||
// this.airtunes.setTrackInfo(this.device.key, title, artist, album);
|
||||
// if (artworkURL)
|
||||
// this.uploadImageAirplay(artworkURL)
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Runs on playback State Change
|
||||
* @param attributes Music Attributes (attributes.status = current state)
|
||||
*/
|
||||
onPlaybackStateDidChange(attributes: any): void {
|
||||
if (this.airtunes && this.device) {
|
||||
let title = attributes?.name ?? '';
|
||||
let artist = attributes?.artistName ?? '';
|
||||
let album = attributes?.albumName ?? '';
|
||||
let artworkURL = attributes?.artwork?.url ?? null;
|
||||
console.log(this.device.key, title, artist, album);
|
||||
this.airtunes.setTrackInfo(this.device.key, title, artist, album);
|
||||
if (artworkURL != null){}
|
||||
this.uploadImageAirplay(artworkURL.replace('{w}', '1024').replace('{h}', '1024'))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
1
src/renderer/assets/check.svg
Normal file
1
src/renderer/assets/check.svg
Normal file
|
@ -0,0 +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-check"><polyline points="20 6 9 17 4 12"></polyline></svg>
|
After Width: | Height: | Size: 262 B |
|
@ -290,5 +290,5 @@ let screenHeight = screen.height;
|
|||
|
||||
window.onerror = function (error) {
|
||||
console.log(error)
|
||||
bootbox.alert("Error occured: " + error)
|
||||
bootbox.alert("Error occurred: " + error)
|
||||
};
|
||||
|
|
|
@ -1149,6 +1149,10 @@
|
|||
font-size : 2em;
|
||||
}
|
||||
|
||||
.settings-option-body-webview {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.settings-option-body {
|
||||
margin: 16px;
|
||||
}
|
||||
|
|
|
@ -85,6 +85,12 @@ const Events = {
|
|||
} catch (e) {
|
||||
}
|
||||
}
|
||||
// Prevent Scrolling on spacebar
|
||||
if (event.keyCode === 32 && event.target === document.body) {
|
||||
event.preventDefault()
|
||||
app.SpacePause()
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// Hang Timer
|
||||
|
|
|
@ -12826,6 +12826,19 @@ body.no-gpu .drawertransition-leave-to {
|
|||
body.no-gpu .lyric-line:hover::after {
|
||||
display: none;
|
||||
}
|
||||
.keybindings-border {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
border-style: solid;
|
||||
border-radius: 5px;
|
||||
border-color: #CBCBCB;
|
||||
}
|
||||
.keybinding-text {
|
||||
width: 95px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.qrimg {
|
||||
width: -webkit-fill-available;
|
||||
max-block-size: -webkit-fill-available;
|
||||
|
|
|
@ -1228,6 +1228,18 @@ body[platform="darwin"] .app-chrome .app-chrome-item > .window-controls > div.cl
|
|||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
// Add Music Video Icons to Songs that are Music Videos
|
||||
div[data-type="library-music-videos"] .info-rect .title::before,
|
||||
div[data-type="musicVideo"] .info-rect .title::before {
|
||||
content: "";
|
||||
background-image: url(./assets/feather/video.svg);
|
||||
background-size: contain;
|
||||
filter:invert(0.6);
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-bottom: -4px;
|
||||
}
|
||||
|
||||
.app-chrome .app-chrome-item > .app-playback-controls .song-duration p {
|
||||
font-weight: 400;
|
||||
|
@ -3123,6 +3135,21 @@ body.no-gpu {
|
|||
}
|
||||
}
|
||||
|
||||
.keybindings-border {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
border-style: solid;
|
||||
border-radius: 5px;
|
||||
border-color: #CBCBCB;
|
||||
}
|
||||
|
||||
.keybinding-text {
|
||||
width: 95px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.qrimg {
|
||||
width: -webkit-fill-available;
|
||||
max-block-size: -webkit-fill-available;
|
||||
|
|
|
@ -33,11 +33,27 @@
|
|||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="md-labeltext" style="opacity:0.5;">{{$root.getLz('action.cast.airplay')}}</div>
|
||||
<div class="md-option-container" style="margin-top: 12px;margin-bottom: 12px;opacity:0.5;">
|
||||
<div class="md-labeltext" >{{$root.getLz('action.cast.airplay')}}</div>
|
||||
<div class="md-option-container" style="margin-top: 12px;margin-bottom: 12px;">
|
||||
<div class="md-option-line">
|
||||
<div class="md-option-segment">
|
||||
{{$root.getLz('action.cast.airplay.underdevelopment')}}
|
||||
{{true ? 'Homepods only for now! (NO PASSWORD PLEASE!)' : 'Please add FFmpeg location in Settings -> Advanced'}}
|
||||
<!-- {{$root.getLz('action.cast.airplay.underdevelopment')}} -->
|
||||
<template v-if="true" v-for="(device) in devices.airplay">
|
||||
<div class="md-option-line" style="cursor: pointer" @click="setAirPlayCast(device)">
|
||||
<div class="md-option-segment">
|
||||
{{ device.name }}
|
||||
<br>
|
||||
<small>{{ device.host }}</small>
|
||||
</div>
|
||||
<div class="md-option-segment_auto" style="display: flex;justify-content: center;align-items: center" v-if="activeCasts.includes(device)">
|
||||
Connected
|
||||
</div>
|
||||
<div class="md-option-segment_auto" v-else style="display: flex;justify-content: center;align-items: center">
|
||||
<svg width="20" height="20" viewBox="0 0 34 34" fill="#fff" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" class="castPlayIndicator"><path d="M28.228,18.327l-16.023,8.983c-0.99,0.555 -2.205,-0.17 -2.205,-1.318l0,-17.984c0,-1.146 1.215,-1.873 2.205,-1.317l16.023,8.982c1.029,0.577 1.029,2.077 0,2.654Z" style="fill-rule:nonzero"></path></svg>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -84,8 +100,10 @@
|
|||
let self = this;
|
||||
this.scanning = true;
|
||||
ipcRenderer.send('getChromeCastDevices', '');
|
||||
ipcRenderer.send("getAirplayDevice","")
|
||||
setTimeout(() => {
|
||||
self.devices.cast = ipcRenderer.sendSync("getKnownCastDevices");
|
||||
self.devices.airplay = ipcRenderer.sendSync("getKnownAirplayDevices");
|
||||
self.scanning = false;
|
||||
}, 2000);
|
||||
console.log(this.devices);
|
||||
|
@ -96,8 +114,13 @@
|
|||
this.activeCasts.push(device);
|
||||
ipcRenderer.send('performGCCast', device, "Cider", "Playing ...", "Test build", '');
|
||||
},
|
||||
setAirPlayCast(device) {
|
||||
this.activeCasts.push(device);
|
||||
ipcRenderer.send("performAirplayPCM",device.host,device.port,null,"","","","")
|
||||
},
|
||||
stopCasting() {
|
||||
CiderAudio.stopAudio();
|
||||
ipcRenderer.send('disconnectAirplay', '');
|
||||
ipcRenderer.send('stopGCast', '');
|
||||
this.activeCasts = [];
|
||||
// vm.$forceUpdate();
|
||||
|
|
|
@ -123,6 +123,68 @@
|
|||
</div>
|
||||
</b-modal>
|
||||
</div>
|
||||
<div class="md-option-line">
|
||||
<div class="md-option-segment">
|
||||
{{$root.getLz('settings.option.general.keybindings')}}
|
||||
</div>
|
||||
<div class="md-option-segment md-option-segment_auto">
|
||||
<button class="md-btn" v-b-modal.modal-2>
|
||||
{{$root.getLz('settings.option.general.keybindings.open')}}
|
||||
</button>
|
||||
</div>
|
||||
<b-modal id="modal-2" centered size="lg" v-title="$root.getLz('settings.option.general.keybindings')" ok-only>
|
||||
<div class="settings-option-body">
|
||||
<div class="md-option-line">
|
||||
<div class="md-option-segment">
|
||||
Toggle Private Session
|
||||
</div>
|
||||
<div class="md-option-segment md-option-segment_auto keybindings-border">
|
||||
<p class="keybinding-text">{{ getCommandOrControl() }} + P</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="md-option-line">
|
||||
<div class="md-option-segment">
|
||||
Web Remote
|
||||
</div>
|
||||
<div class="md-option-segment md-option-segment_auto keybindings-border">
|
||||
<p class="keybinding-text">{{ getCommandOrControl() }} + Shift + W</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="md-option-line">
|
||||
<div class="md-option-segment">
|
||||
Audio Settings
|
||||
</div>
|
||||
<div class="md-option-segment md-option-segment_auto keybindings-border">
|
||||
<p class="keybinding-text">{{ getCommandOrControl() }} + Shift + A</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="md-option-line">
|
||||
<div class="md-option-segment">
|
||||
Plugin Menu
|
||||
</div>
|
||||
<div class="md-option-segment md-option-segment_auto keybindings-border">
|
||||
<p class="keybinding-text">{{ getCommandOrControl() }} + Shift + P</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="md-option-line">
|
||||
<div class="md-option-segment">
|
||||
Cast to Devices
|
||||
</div>
|
||||
<div class="md-option-segment md-option-segment_auto keybindings-border">
|
||||
<p class="keybinding-text">{{ getCommandOrControl() }} + Shift + C</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="md-option-line">
|
||||
<div class="md-option-segment">
|
||||
Open Developer Tools
|
||||
</div>
|
||||
<div class="md-option-segment md-option-segment_auto keybindings-border">
|
||||
<p class="keybinding-text">{{ getCommandOrControl() }} + {{ getOptionOrShift() }} + I</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</b-modal>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</b-tab>
|
||||
|
@ -853,6 +915,17 @@
|
|||
</div>
|
||||
<div class="settings-option-body">
|
||||
|
||||
<div class="md-option-line" v-show="app.cfg.advanced.AudioContext">
|
||||
<div class="md-option-segment">
|
||||
FFmpeg location<br/>
|
||||
<small>Restart needed to work. Required for AirPlay. (For example: C:\ffmpeg-4.4-essentials_build\bin\ffmpeg.exe)</small><br/>
|
||||
<small>You can look at the internet on how to install it.</small>
|
||||
</div>
|
||||
<div class="md-option-segment md-option-segment_auto">
|
||||
<input type="text" v-model="app.cfg.advanced.ffmpegLocation"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="md-option-line">
|
||||
<div class="md-option-segment">
|
||||
{{$root.getLz('settings.option.visual.plugin.github.explore')}}
|
||||
|
@ -976,6 +1049,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</b-tab>
|
||||
<!-- Connect Settings -->
|
||||
<b-tab :title="$root.getLz('settings.header.connect')">
|
||||
<div class="md-option-container">
|
||||
<!-- Cider Connect / Linking Settings -->
|
||||
|
@ -983,7 +1057,7 @@
|
|||
<span>{{$root.getLz('settings.header.connect')}}</span>
|
||||
</div>
|
||||
<div class="settings-option-body">
|
||||
<div class="md-option-line update-check">
|
||||
<div class="md-option-line update-check" v-if="app.cfg.connectUser.auth == null">
|
||||
<div class="md-option-segment">
|
||||
{{$root.getLz('settings.option.connect.link_account')}}
|
||||
<small>{{$root.getLz('settings.option.connect.link_account.description')}}</small>
|
||||
|
@ -996,6 +1070,52 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="app.cfg.connectUser.auth != null">
|
||||
<div class="md-option-line">
|
||||
<div class="md-option-segment">
|
||||
{{$root.getLz('settings.option.connect.link_account')}}
|
||||
<small>{{$root.getLz('settings.option.connect.link_account.description')}}</small>
|
||||
<br>
|
||||
</div>
|
||||
<div class="md-option-segment md-option-segment_auto">
|
||||
<button class="md-btn" id='settings.option.general.updateCider.check' @click="logoutCC()" style="display: flex;align-items: center;gap: 0.4em;">
|
||||
<%- include("../svg/check.svg") %>
|
||||
<div v>Connected</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="md-option-header" style="margin-left: -0.55em;">
|
||||
<span>{{app.cfg.connectUser.username}}</span>
|
||||
<img :src="'https://cdn.discordapp.com/avatars/' + app.cfg.connectUser.id + '/' + app.cfg.connectUser.avatar + '.png?size=32'"></img>
|
||||
</div>
|
||||
|
||||
<div class="md-option-line">
|
||||
<div class="md-option-segment">
|
||||
Sync Settings
|
||||
</div>
|
||||
<div class="md-option-segment md-option-segment_auto">
|
||||
<input type="checkbox" disabled a-v-model="app.cfg.connectUser.sync.settings" @click="app.cfg.connectUser.sync.settings = !app.cfg.connectUser.sync.settings" switch/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="md-option-line">
|
||||
<div class="md-option-segment">
|
||||
Sync Themes
|
||||
</div>
|
||||
<div class="md-option-segment md-option-segment_auto">
|
||||
<input type="checkbox" disabled a-v-model="app.cfg.connectUser.sync.themes" @click="app.cfg.connectUser.sync.themes = !app.cfg.connectUser.sync.themes" switch/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="md-option-line">
|
||||
<div class="md-option-segment">
|
||||
Sync Plugins
|
||||
</div>
|
||||
<div class="md-option-segment md-option-segment_auto">
|
||||
<input type="checkbox" disabled a-v-model="app.cfg.connectUser.sync.plugins" @click="app.cfg.connectUser.sync.plugins = !app.cfg.connectUser.sync.plugins" switch/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1124,6 +1244,12 @@
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
getCommandOrControl() {
|
||||
return app.platform == "darwin" ? "⌘" : "Ctrl";
|
||||
},
|
||||
getOptionOrShift() {
|
||||
return app.platform == "darwin" ? "⌥" : "Shift";
|
||||
},
|
||||
windowBgStyleChange() {
|
||||
this.$root.getNowPlayingArtworkBG(undefined, true)
|
||||
if (this.$root.cfg.visual.window_background_style == "mica") {
|
||||
|
@ -1240,6 +1366,9 @@
|
|||
authCC() {
|
||||
ipcRenderer.send('cc-auth')
|
||||
},
|
||||
logoutCC() {
|
||||
ipcRenderer.send('cc-logout')
|
||||
},
|
||||
}
|
||||
})
|
||||
</script>
|
1
src/renderer/views/svg/check.svg
Normal file
1
src/renderer/views/svg/check.svg
Normal file
|
@ -0,0 +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-check"><polyline points="20 6 9 17 4 12"></polyline></svg>
|
After Width: | Height: | Size: 262 B |
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"electronVersion": "16.0.07",
|
||||
"electronVersion": "18.0.4",
|
||||
"electronDownload": {
|
||||
"version": "16.0.7+wvcus",
|
||||
"version": "18.0.4+wvcus",
|
||||
"mirror": "https://github.com/castlabs/electron-releases/releases/download/v"
|
||||
},
|
||||
"appId": "cider",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue