Add base files from AME

This commit is contained in:
cryptofyre 2021-11-19 17:02:28 -06:00
parent e8f3881513
commit e3c3bded3a
129 changed files with 19056 additions and 0 deletions

View file

@ -0,0 +1,148 @@
// preload.js
const electron = require('electron');
let cache = {playParams: {id: 0}, status: null, remainingTime: 0},
playbackCache = {status: null, time: Date.now()};
const MusicKitInterop = {
init: function () {
const self = this;
MusicKit.getInstance().addEventListener(MusicKit.Events.playbackStateDidChange, () => {
/** wsapi */
ipcRenderer.send('wsapi-updatePlaybackState', self.getAttributes());
/** wsapi */
if (MusicKitInterop.filterTrack(MusicKitInterop.getAttributes(), true, false)) {
global.ipcRenderer.send('playbackStateDidChange', MusicKitInterop.getAttributes())
if (typeof _plugins != "undefined") {
_plugins.execute("OnPlaybackStateChanged", {Attributes: MusicKitInterop.getAttributes()})
}
const nowPlayingItem = MusicKit.getInstance().nowPlayingItem;
if (typeof nowPlayingItem != "undefined") {
if (nowPlayingItem["type"] === "musicVideo") {
document.querySelector(`.web-chrome`).setAttribute('style', 'height: 20px !important');
document.querySelector(`#MVLyricsBox`).style.display = 'block';
} else {
document.querySelector(`.web-chrome`).setAttribute('style', 'height: 55px !important');
if (nowPlayingItem["type"] !== "song"){
document.querySelector(`.web-chrome__grid-container`).setAttribute('style', 'margin: 15px auto 0')
} else {
document.querySelector(`.web-chrome__grid-container`).setAttribute('style', 'margin: dsds');}
document.querySelector(`#MVLyricsBox`).style.display = 'none';
}
}
} else {
document.querySelector(`.web-chrome`).setAttribute('style', 'height: 55px !important');
document.querySelector(`#MVLyricsBox`).style.display = 'none';
try {
const nowPlayingItem = MusicKit.getInstance().nowPlayingItem;
if (typeof nowPlayingItem != "undefined") {
if (nowPlayingItem["type"] === "musicVideo") {
document.querySelector(`.web-chrome`).setAttribute('style', 'height: 20px !important');
document.querySelector(`#MVLyricsBox`).style.display = 'block';
} else {
document.querySelector(`.web-chrome`).setAttribute('style', 'height: 55px !important');
if (nowPlayingItem["type"] !== "song"){
document.querySelector(`.web-chrome__grid-container`).setAttribute('style', 'margin: 15px auto 0')
} else {
document.querySelector(`.web-chrome__grid-container`).setAttribute('style', 'margin: dsds');}
document.querySelector(`#MVLyricsBox`).style.display = 'none';
}
}
} catch (e) {
console.error(e);
}
}
});
/** wsapi */
MusicKit.getInstance().addEventListener(MusicKit.Events.playbackTimeDidChange, () => {
ipcRenderer.send('wsapi-updatePlaybackState', self.getAttributes());
});
/** wsapi */
MusicKit.getInstance().addEventListener(MusicKit.Events.nowPlayingItemDidChange, () => {
if (MusicKitInterop.filterTrack(MusicKitInterop.getAttributes(), false, true)) {
global.ipcRenderer.send('nowPlayingItemDidChange', MusicKitInterop.getAttributes());
AMStyling.updateMeta()
}
});
MusicKit.getInstance().addEventListener(MusicKit.Events.authorizationStatusDidChange, () => {
global.ipcRenderer.send('authorizationStatusDidChange', MusicKit.getInstance().authorizationStatus)
})
MusicKit.getInstance().addEventListener(MusicKit.Events.mediaPlaybackError, (e) => {
console.warn(`[mediaPlaybackError] ${e}`);
})
},
getAttributes: function () {
const mk = MusicKit.getInstance();
const nowPlayingItem = mk.nowPlayingItem;
const isPlayingExport = mk.isPlaying;
const remainingTimeExport = mk.currentPlaybackTimeRemaining;
const attributes = (nowPlayingItem != null ? nowPlayingItem.attributes : {});
attributes.status = isPlayingExport ? isPlayingExport : false;
attributes.name = attributes.name ? attributes.name : 'No Title Found';
attributes.artwork = attributes.artwork ? attributes.artwork : {url: ''};
attributes.artwork.url = attributes.artwork.url ? attributes.artwork.url : '';
attributes.playParams = attributes.playParams ? attributes.playParams : {id: 'no-id-found'};
attributes.playParams.id = attributes.playParams.id ? attributes.playParams.id : 'no-id-found';
attributes.albumName = attributes.albumName ? attributes.albumName : '';
attributes.artistName = attributes.artistName ? attributes.artistName : '';
attributes.genreNames = attributes.genreNames ? attributes.genreNames : [];
attributes.remainingTime = remainingTimeExport ? (remainingTimeExport * 1000) : 0;
attributes.durationInMillis = attributes.durationInMillis ? attributes.durationInMillis : 0;
attributes.startTime = Date.now();
attributes.endTime = Math.round((attributes.playParams.id === cache.playParams.id ? (Date.now() + attributes.remainingTime) : (attributes.startTime + attributes.durationInMillis)));
attributes.endTime = attributes.endTime ? attributes.endTime : Date.now();
attributes.volume = mk.volume;
attributes.shuffleMode = mk.shuffleMode;
attributes.repeatMode = mk.repeatMode;
attributes.autoplayEnabled = mk.autoplayEnabled;
return attributes
},
filterTrack: function (a, playbackCheck, mediaCheck) {
if (a.title === "No Title Found" || a.playParams.id === "no-id-found") {
return;
} else if (mediaCheck && a.playParams.id === cache.playParams.id) {
return;
} else if (playbackCheck && a.status === playbackCache.status) {
return;
} else if (playbackCheck && !a.status && a.remainingTime === playbackCache.time) { /* Pretty much have to do this to prevent multiple runs when a song starts playing */
return;
}
cache = a;
if (playbackCheck) playbackCache = {status: a.status, time: a.remainingTime};
return true;
},
pausePlay: function () {
if (MusicKit.getInstance().isPlaying) {
MusicKit.getInstance().pause();
} else if (MusicKit.getInstance().nowPlayingItem != null) {
MusicKit.getInstance().play().then(r => console.log(`[MusicKitInterop] Playing ${r}`));
}
},
nextTrack: function () {
MusicKit.getInstance().skipToNextItem().then(r => console.log(`[MusicKitInterop] Skipping to Next ${r}`));
},
previousTrack: function () {
MusicKit.getInstance().skipToPreviousItem().then(r => console.log(`[MusicKitInterop] Skipping to Previous ${r}`));
}
}
process.once('loaded', () => {
global.ipcRenderer = electron.ipcRenderer;
global.MusicKitInterop = MusicKitInterop;
});
// MusicKit.getInstance().addEventListener( MusicKit.Events.queueItemsDidChange,logIt );
// MusicKit.getInstance().addEventListener( MusicKit.Events.queuePositionDidChange, logIt );

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,66 @@
const wsapi = {
search(term, limit) {
MusicKit.getInstance().api.search(term, {limit: limit, types: 'songs,artists,albums'}).then((results)=>{
ipcRenderer.send('wsapi-returnSearch', JSON.stringify(results))
})
},
searchLibrary(term, limit) {
MusicKit.getInstance().api.library.search(term, {limit: limit, types: 'library-songs,library-artists,library-albums'}).then((results)=>{
ipcRenderer.send('wsapi-returnSearchLibrary', JSON.stringify(results))
})
},
moveQueueItem(oldPosition, newPosition) {
MusicKit.getInstance().queue._queueItems.splice(newPosition,0,MusicKit.getInstance().queue._queueItems.splice(oldPosition,1)[0])
MusicKit.getInstance().queue._reindex()
},
setAutoplay(value) {
MusicKit.getInstance().autoplayEnabled = value
},
getPlaybackState () {
ipcRenderer.send('wsapi-updatePlaybackState', MusicKitInterop.getAttributes());
},
getLyrics() {
_lyrics.GetLyrics(1, false)
},
getQueue() {
ipcRenderer.send('wsapi-returnQueue', JSON.stringify(MusicKit.getInstance().queue))
},
playNext(type, id) {
var request = {}
request[type] = id
MusicKit.getInstance().playNext(request)
},
playLater(type, id) {
var request = {}
request[type] = id
MusicKit.getInstance().playLater(request)
},
love() {
},
playTrackById(id) {
MusicKit.getInstance().setQueue({ song: id }).then(function (queue) {
MusicKit.getInstance().play()
})
},
quickPlay(term) {
// Quick play by song name
MusicKit.getInstance().api.search(term, { limit: 2, types: 'songs' }).then(function (data) {
MusicKit.getInstance().setQueue({ song: data["songs"][0]["id"] }).then(function (queue) {
MusicKit.getInstance().play()
})
})
},
toggleShuffle() {
MusicKit.getInstance().shuffleMode = MusicKit.getInstance().shuffleMode === 0 ? 1 : 0
},
toggleRepeat() {
if(MusicKit.getInstance().repeatMode == 0) {
MusicKit.getInstance().repeatMode = 2
}else if(MusicKit.getInstance().repeatMode == 2){
MusicKit.getInstance().repeatMode = 1
}else{
MusicKit.getInstance().repeatMode = 0
}
}
}

View file

@ -0,0 +1,32 @@
try {
if (MusicKit.getInstance().isAuthorized) {
if (!document.querySelector('#backButtonBar')) {
document.getElementById('web-main').insertAdjacentHTML("afterbegin", `
<div id="backButtonBar">
<div class="button-area" onclick="ipcRenderer.send('back');">
<img src="https://developer.apple.com/design/human-interface-guidelines/macos/images/icons/system-images/control/chevron-backward.png" alt="Back Button">
</div>
</div>
`);
}
if (document.getElementsByClassName('dragDiv right-aligned').length > 0) {
document.getElementById('backButtonBar').style.top = '25px'
}
document.getElementById('web-main').addEventListener('scroll', function () {
if (document.getElementById('web-main').scrollTop > 80) {
document.getElementById('backButtonBar').style.backgroundColor = 'var(--playerBackground)';
document.getElementById('backButtonBar').style.position = 'fixed';
} else {
document.getElementById('backButtonBar').style.backgroundColor = 'transparent';
document.getElementById('backButtonBar').style.position = 'absolute';
}
});
}
} catch (e) {
console.error("[JS] Error while trying to apply backButton.js", e);
}

19
resources/js/checkAuth.js Normal file
View file

@ -0,0 +1,19 @@
try {
const preferences = ipcRenderer.sendSync('getStore');
if (MusicKit.getInstance().isAuthorized) {
let url = window.location.href;
if (preferences.general.startupPage !== "browse") {
if (preferences.general.startupPage.includes('library/')) {
url = `${window.location.origin}/${preferences.general.startupPage}`;
} else {
url = `${window.location.origin}/${MusicKit.getInstance().storefrontId}/${preferences.general.startupPage}`;
}
window.location.href = url;
}
ipcRenderer.send('userAuthorized', url);
}
} catch (e) {
console.error("[JS] Error while trying to apply CheckAuth.js", e);
}

1466
resources/js/custom.js Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

591
resources/js/eq.js Normal file
View file

@ -0,0 +1,591 @@
var EAOverride = true;
var AErecorderNode;
var GCOverride = true;
var outputID = -1;
var EAoutputID = -1;
var queueExclusive = false;
var queueChromecast = false;
var selectedGC ;
var MVsource;
var windowAudioNode;
const workerOptions = {
OggOpusEncoderWasmPath: 'https://cdn.jsdelivr.net/npm/opus-media-recorder@latest/OggOpusEncoder.wasm',
WebMOpusEncoderWasmPath: 'https://cdn.jsdelivr.net/npm/opus-media-recorder@latest/WebMOpusEncoder.wasm'
};
var recorder;
window.MediaRecorder = OpusMediaRecorder;
var audioWorklet = `class RecorderWorkletProcessor extends AudioWorkletProcessor {
static get parameterDescriptors() {
return [{
name: 'isRecording',
defaultValue: 0
},
{
name: 'numberOfChannels',
defaultValue: 2
}
];
}
constructor() {
super();
this._bufferSize = 32768;
this._buffers = null;
this._initBuffer();
}
_initBuffers(numberOfChannels) {
this._buffers = [];
for (let channel=0; channel < numberOfChannels; channel++) {
this._buffers.push(new Float32Array(this._bufferSize));
}
}
_initBuffer() {
this._bytesWritten = 0;
}
_isBufferEmpty() {
return this._bytesWritten === 0;
}
_isBufferFull() {
return this._bytesWritten === this._bufferSize;
}
_pushToBuffers(audioRawData, numberOfChannels) {
if (this._isBufferFull()) {
this._flush();
}
let dataLength = audioRawData[0].length;
for (let idx=0; idx<dataLength; idx++) {
for (let channel=0; channel < numberOfChannels; channel++) {
let value = audioRawData[channel][idx];
this._buffers[channel][this._bytesWritten] = value;
}
this._bytesWritten += 1;
}
}
_flush() {
let buffers = [];
this._buffers.forEach((buffer, channel) => {
if (this._bytesWritten < this._bufferSize) {
buffer = buffer.slice(0, this._bytesWritten);
}
buffers[channel] = buffer;
});
this.port.postMessage({
eventType: 'data',
audioBuffer: buffers,
bufferSize: this._bufferSize
});
this._initBuffer();
}
_recordingStopped() {
this.port.postMessage({
eventType: 'stop'
});
}
process(inputs, outputs, parameters) {
const isRecordingValues = parameters.isRecording;
const numberOfChannels = parameters.numberOfChannels[0];
if (this._buffers === null) {
this._initBuffers(numberOfChannels);
}
for (let dataIndex = 0; dataIndex < isRecordingValues.length; dataIndex++)
{
const shouldRecord = isRecordingValues[dataIndex] === 1;
if (!shouldRecord && !this._isBufferEmpty()) {
this._flush();
this._recordingStopped();
}
if (shouldRecord) {
let audioRawData = inputs[0];
this._pushToBuffers(audioRawData, numberOfChannels);
}
}
return true;
}
}
registerProcessor('recorder-worklet', RecorderWorkletProcessor);`;
var GCstream;
var searchInt;
var AMEx = {
context: new AudioContext(),
result: {},
filter: [],
EQRanges: [{
f: 32,
type: 'lowshelf'
}, {
f: 64,
type: 'peaking'
}, {
f: 125,
type: 'peaking'
}, {
f: 250,
type: 'peaking'
}, {
f: 500,
type: 'peaking'
}, {
f: 1000,
type: 'peaking'
}, {
f: 2000,
type: 'peaking'
}, {
f: 4000,
type: 'peaking'
}, {
f: 8000,
type: 'peaking'
}, {
f: 16000,
type: 'highshelf'
}]
};
var bassFilter;
var trebleFilter;
var AudioOutputs = {
fInit: false,
eqReady: false,
activeCasts: [],
castUI() {
AMJavaScript.getRequest("ameres://html/cast_device.html", (content) => {
var vm = new Vue({
data: {
devices: {
cast: [],
airplay: []
},
scanning: false,
activeCasts: AudioOutputs.activeCasts
},
methods: {
scan() {
let self = this;
this.scanning = true;
AudioOutputs.getGCDevices();
setTimeout(()=>{
self.devices.cast = ipcRenderer.sendSync("getKnownCastDevices");
self.scanning = false;
}, 2000);
console.log(this.devices);
vm.$forceUpdate();
},
setCast(device) {
console.log(`requesting: ${device}`);
AudioOutputs.playGC(device);
},
stopCasting() {
AudioOutputs.stopGC();
this.activeCasts = AudioOutputs.activeCasts;
vm.$forceUpdate();
},
close() {
modal.close();
}
}
});
var modal = new AMEModal({
content: content,
CloseButton: false,
Style: {
maxWidth: "600px"
},
OnCreate() {
vm.$mount("#castdevices-vue");
vm.scan();
},
OnClose() {
_vues.destroy(vm);
}
});
})
},
init: function (cb = function () {}) {
AudioOutputs.fInit = true;
searchInt = setInterval(function () {
if (document.getElementById("apple-music-player")) {
AudioOutputs.eqReady = true;
document.getElementById("apple-music-player").crossOrigin = "anonymous";
AudioOutputs.amplifyMedia(document.getElementById("apple-music-player"), 0);
var context = AMEx.context;
var source = AMEx.result.source;
bassFilter = context.createBiquadFilter();
bassFilter.type = "lowshelf";
bassFilter.frequency.value = 200;
bassFilter.gain.value = 0;
trebleFilter = context.createBiquadFilter();
trebleFilter.type = "highshelf";
trebleFilter.frequency.value = 2000;
trebleFilter.gain.value = 0;
source.connect(bassFilter);
bassFilter.connect(trebleFilter);
trebleFilter.connect(context.destination);
console.log("Attached EQ");
if (queueExclusive){
console.log('we good');
AudioOutputs.startExclusiveAudio(outputID); }
cb();
clearInterval(searchInt);
}
}, 1000);
waitFor(()=>{return queueChromecast &&
((document.getElementById("apple-music-player") != null&&
document.getElementById("apple-music-player").readyState == 4) || (
document.querySelector('apple-music-video-player') &&
document.querySelector('apple-music-video-player').shadowRoot &&
document.querySelector('apple-music-video-player').shadowRoot.querySelector('amp-video-player-internal') &&
document.querySelector('apple-music-video-player').shadowRoot.querySelector('amp-video-player-internal').shadowRoot &&
document.querySelector('apple-music-video-player').shadowRoot.querySelector('amp-video-player-internal').shadowRoot.querySelector('amp-video-player') &&
document.querySelector('apple-music-video-player').shadowRoot.querySelector('amp-video-player-internal').shadowRoot.querySelector('amp-video-player').shadowRoot &&
document.querySelector('apple-music-video-player').shadowRoot.querySelector('amp-video-player-internal').shadowRoot.querySelector('amp-video-player').shadowRoot.getElementById('apple-music-video-player') &&
document.querySelector('apple-music-video-player').shadowRoot.querySelector('amp-video-player-internal').shadowRoot.querySelector('amp-video-player').shadowRoot.getElementById('apple-music-video-player').readyState == 4)) ;},() => AudioOutputs.playGC(selectedGC))
},
amplifyMedia: function (mediaElem, multiplier) {
AMEx.context = new(window.AudioContext || window.webkitAudioContext),
AMEx.result = {
context: AMEx.context,
source: AMEx.context.createMediaElementSource(mediaElem),
gain: AMEx.context.createGain(),
media: mediaElem,
amplify: function (multiplier) {
AMEx.result.gain.gain.value = multiplier;
},
getAmpLevel: function () {
return AMEx.result.gain.gain.value;
}
};
AMEx.result.source.connect(AMEx.result.gain);
AMEx.result.gain.connect(AMEx.context.destination);
AMEx.result.amplify(multiplier);
return AMEx.result;
},
popup_generic: function ({
title = "",
content = document.createElement("div"),
closefn = function () {},
transparentBg = false,
windowStyle = {},
backdropStyle = {}
}) {
let backdrop = document.createElement("div");
backdrop.style.width = "100%";
backdrop.style.height = "100%";
backdrop.style.position = "fixed";
backdrop.style.top = 0;
backdrop.style.left = 0;
if (!transparentBg) {
backdrop.style.background = "rgba(0,0,0,0.5)";
} else {
backdrop.style.background = "rgba(0,0,0,0.0)";
};
backdrop.style.zIndex = 10000;
backdrop.style.display = "flex";
backdrop.style.alignItems = "center";
backdrop.style.justifyContent = "center";
let win = document.createElement("div");
win.style.width = "300px";
win.style.background = "var(--modalBGColor)";
win.style.zIndex = 10000;
win.style.padding = "16px";
win.style.borderRadius = "10px";
Object.assign(backdrop.style, backdropStyle);
Object.assign(win.style, windowStyle);
let closeBtn = document.createElement("button");
closeBtn.style.background = "var(--primaryColor)";
closeBtn.style.borderRadius = "4px";
closeBtn.style.padding = "8px 0px 8px 0px";
closeBtn.style.width = "100%";
closeBtn.style.fontWeight = "bold";
closeBtn.style.margin = "12px 0px 0px 0px";
closeBtn.innerHTML = "Close";
closeBtn.id = "eq-close";
closeBtn.addEventListener("click", function () {
backdrop.remove();
closefn();
});
let titleText = document.createElement("div");
titleText.innerHTML = (title);
titleText.style.fontWeight = "bold";
win.appendChild(titleText);
win.appendChild(content);
win.appendChild(closeBtn);
backdrop.appendChild(win);
document.body.appendChild(backdrop);
},
ShowEQ: function () {
if (!AudioOutputs.eqReady) {
alert("Audio is not ready, Play a song to use this function.");
};
let backdrop = document.createElement("div");
backdrop.style.width = "100%";
backdrop.style.height = "100%";
backdrop.style.position = "fixed";
backdrop.style.top = 0;
backdrop.style.left = 0;
backdrop.style.background = "rgba(0,0,0,0.5)";
backdrop.style.zIndex = 9999;
backdrop.style.display = "flex";
backdrop.style.alignItems = "center";
backdrop.style.justifyContent = "center";
backdrop.style.backdropFilter = "blur(12px) saturate(180%)";
let win = document.createElement("div");
win.style.width = "300px";
win.style.background = "var(--modalBGColor)";
win.style.zIndex = 10000;
win.style.padding = "16px";
win.style.borderRadius = "10px";
let closeBtn = document.createElement("button");
closeBtn.style.background = "var(--primaryColor)";
closeBtn.style.borderRadius = "4px";
closeBtn.style.padding = "8px 0px 8px 0px";
closeBtn.style.width = "100%";
closeBtn.style.fontWeight = "bold";
closeBtn.style.margin = "12px 0px 0px 0px";
closeBtn.innerHTML = "Close";
closeBtn.addEventListener("click", function () {
backdrop.remove()
});
let titleText = document.createElement("div");
let bassText = document.createElement("div");
let trebleText = document.createElement("div");
let gainText = document.createElement("div");
titleText.id = 'eq-menu';
titleText.innerHTML = (`Equalizer`);
titleText.style.fontWeight = "bold";
bassText.innerHTML = (`Bass (${bassFilter.gain.value})`);
trebleText.innerHTML = (`Treble (${trebleFilter.gain.value})`);
gainText.innerHTML = (`Gain (${AMEx.result.gain.gain.value})`);
let bassAdjust = document.createElement("input");
bassAdjust.style.width = "100%";
bassAdjust.type = "range";
bassAdjust.min = -10;
bassAdjust.max = 10;
bassAdjust.value = bassFilter.gain.value;
bassAdjust.addEventListener("input", function () {
bassFilter.gain.value = this.value;
bassText.innerHTML = `Bass (${bassFilter.gain.value})`;
});
let trebleAdjust = document.createElement("input");
trebleAdjust.style.width = "100%";
trebleAdjust.min = -10;
trebleAdjust.max = 10;
trebleAdjust.type = "range";
trebleAdjust.value = trebleFilter.gain.value;
trebleAdjust.addEventListener("input", function () {
trebleFilter.gain.value = this.value;
trebleText.innerHTML = `Treble (${trebleFilter.gain.value})`;
});
let gainAdjust = document.createElement("input");
gainAdjust.style.width = "100%";
gainAdjust.min = -1;
gainAdjust.max = 1;
gainAdjust.type = "range";
gainAdjust.value = AMEx.result.gain.gain.value;
gainAdjust.addEventListener("input", function () {
AMEx.result.gain.gain.value = this.value;
gainText.innerHTML = `Gain (${AMEx.result.gain.gain.value})`;
});
let bassLabel = document.createElement("label");
let trebleLabel = document.createElement("label");
let gainLabel = document.createElement("label");
bassLabel.appendChild(bassText);
trebleLabel.appendChild(trebleText);
gainLabel.appendChild(gainText);
bassLabel.appendChild(bassAdjust);
bassLabel.appendChild(document.createElement("br"));
trebleLabel.appendChild(trebleAdjust);
trebleLabel.appendChild(document.createElement("br"));
gainLabel.appendChild(gainAdjust);
win.appendChild(titleText);
win.appendChild(bassLabel);
win.appendChild(trebleLabel);
win.appendChild(gainLabel);
win.appendChild(closeBtn);
backdrop.appendChild(win);
document.body.appendChild(backdrop);
},
getAudioDevices: function(){
ipcRenderer.send('getAudioDevices','');
},
startExclusiveAudio: async function(id){
if(AMEx.result.source != null || MVsource != null){
if(EAoutputID!= id){
EAoutputID = id;
EAOverride = false;
ipcRenderer.send('muteAudio',true);
ipcRenderer.send('enableExclusiveAudio',id);
windowAudioNode = AMEx.context.createGain();
try{
AMEx.result.source.connect(windowAudioNode);}
catch(e){}
var options = {
mimeType : 'audio/wav'
};
var destnode = AMEx.context.createMediaStreamDestination();
windowAudioNode.connect(destnode);
if(!recorder){
recorder = new MediaRecorder(destnode.stream,options,workerOptions);
recorder.start(1);
recorder.ondataavailable = function(e) {
e.data.arrayBuffer().then(buffer => {
if(!GCOverride) {ipcRenderer.send('writeWAV',buffer,preferences.audio.castingBitDepth);}
if(!EAOverride) {ipcRenderer.send('writePCM',buffer);}
}
);
}}
} else {console.log('device already in exclusive mode');}
} else {
outputID = id;
queueExclusive = true;
}
},
stopExclusiveAudio: function(){
try{
recorder.stop();
recorder = null;
} catch(e){}
EAOverride = true;
EAoutputID = -1;
outputID = -1;
queueExclusive = false;
ipcRenderer.send('muteAudio',false);
ipcRenderer.send('disableExclusiveAudio','');
},
getGCDevices: function(){
ipcRenderer.send('getChromeCastDevices','');
},
playGC : async function(device){
console.log('wot');
AudioOutputs.activeCasts.push(device);
GCOverride = false;
if(AMEx.result.source != null || MVsource != null ){
queueChromecast = false;
const musicType = (MusicKit.getInstance().nowPlayingItem != null) ? MusicKit.getInstance().nowPlayingItem["type"] ?? '' : '';
const trackName = ((MusicKit.getInstance().nowPlayingItem != null) ? MusicKit.getInstance().nowPlayingItem.title ?? '' : '');
const artistName = ((MusicKit.getInstance().nowPlayingItem != null) ? MusicKit.getInstance().nowPlayingItem.artistName ?? '' : '');
const albumName = ((MusicKit.getInstance().nowPlayingItem != null) ? MusicKit.getInstance().nowPlayingItem.albumName ?? '' : '');
ipcRenderer.send('performGCCast',device, "Apple Music Electron","Playing ...","3.0.0 beta",'');
windowAudioNode = AMEx.context.createGain();
try{
AMEx.result.source.connect(windowAudioNode);}
catch(e){}
var options = {
mimeType : 'audio/wav'
};
var destnode = AMEx.context.createMediaStreamDestination();
windowAudioNode.connect(destnode);
if(!recorder){
recorder = new MediaRecorder(destnode.stream,options,workerOptions);
recorder.start(1);
recorder.ondataavailable = function(e) {
e.data.arrayBuffer().then(buffer => {
if(!GCOverride) {
ipcRenderer.send('writeWAV',buffer,preferences.audio.castingBitDepth);
}
if(!EAOverride) {ipcRenderer.send('writePCM',buffer);}
}
);
}}
} else {queueChromecast = true; selectedGC = device}
},
stopGC : function(){
queueChromecast = false;
try{
recorder.stop();
recorder = null;
} catch(e){}
GCOverride = true;
this.activeCasts = [];
ipcRenderer.send('stopGCast','');
}
};
document.addEventListener('keydown', function (event) {
if (event.ctrlKey || event.metaKey) {
switch (String.fromCharCode(event.which).toLowerCase()) {
case "2":
if (document.getElementById('eq-menu')){
document.getElementById('eq-menu').parentNode.getElementsByTagName('button')[0].click();
}
else{AudioOutputs.ShowEQ();}
break;
case "3":
(EAOverride) ? (EAOverride = false) : (EAOverride = true);
break;
}
}
});
function waitFor(condition, callback) {
if(condition() == null || !condition() ) {
window.setTimeout(waitFor.bind(null, condition, callback), 1000);
} else {
callback();
}
}
function setIntervalX(callback, delay, repetitions) {
var x = 0;
var intervalID = window.setInterval(function () {
callback();
if (++x === repetitions) {
window.clearInterval(intervalID);
}
}, delay);
}
AudioOutputs.init()

View file

@ -0,0 +1,91 @@
try {
if (document.querySelector('.web-nav-window-controls') === null && document.getElementsByClassName('web-nav-window-controls').length === 0) {
class ClassWatcher {
constructor(targetNode, classToWatch, classAddedCallback, classRemovedCallback) {
this.targetNode = targetNode;
this.classToWatch = classToWatch;
this.classAddedCallback = classAddedCallback;
this.classRemovedCallback = classRemovedCallback;
this.observer = null;
this.lastClassState = targetNode.classList.contains(this.classToWatch);
this.init();
};
init() {
this.observer = new MutationObserver(this.mutationCallback);
this.observe();
}
observe() {
this.observer.observe(this.targetNode, { attributes: true })
};
disconnect() {
this.observer.disconnect()
};
mutationCallback = mutationsList => {
for(let mutation of mutationsList) {
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
let currentClassState = mutation.target.classList.contains(this.classToWatch);
if(this.lastClassState !== currentClassState) {
this.lastClassState = currentClassState;
if(currentClassState) {
this.classAddedCallback();
}
else {
this.classRemovedCallback();
}
}
}
}
}
}
function UserAuthorized() { /* When user is authenticated (Signed In) and web-chrome appears */
document.getElementsByClassName('web-nav-window-controls')[0].classList.add('web-chrome-window-controls');
document.getElementsByClassName('web-main-drag')[0].style.zIndex = '1'; /* Hide the drag bar */
}
function UserUnauthorized() { /* When user is unauthenticated (Signed Out) and web-chrome display: none */
if (document.getElementsByClassName('web-chrome-window-controls').length !== 0) {
console.log(`Length: ${document.getElementsByClassName('web-chrome-window-controls').length}`);
document.getElementsByClassName('web-chrome-window-controls')[0].classList.remove('web-chrome-window-controls');
}
document.getElementsByClassName('web-main-drag')[0].style.zIndex = '2'; /* Show the drag bar */
document.getElementsByClassName('header-nav')[0].style.margin = '5px var(--bodyGutter) 0'
}
new ClassWatcher(document.body, 'not-authenticated', UserUnauthorized, UserAuthorized);
document.getElementsByClassName('web-navigation')[0].insertAdjacentHTML('afterbegin', `
<div class="web-main-drag">
</div>
<div class="web-nav-window-controls">
<span id="minimize" onclick="ipcRenderer.send('minimize')"></span>
<span id="maximize" onclick="ipcRenderer.send('maximize')"></span>
<span id="close" onclick="ipcRenderer.send('close')"></span>
</div>
`);
if ((document.getElementsByClassName('web-chrome')[0].style.display === 'none' || document.body.classList.contains('not-authenticated')) && document.getElementsByClassName('web-nav-window-controls').length > 0) {
UserUnauthorized();
} else {
UserAuthorized();
}
/* Clean Up Search bar */
if (document.getElementsByClassName('search-box dt-search-box web-navigation__search-box').length > 0) {
document.getElementsByClassName('search-box dt-search-box web-navigation__search-box')[0].style.marginTop = '15px';
}
if (document.getElementById('web-navigation-container')) {
document.getElementById('web-navigation-container').style.gridTemplateRows = 'auto auto 1fr auto'
}
}
} catch (e) {
console.error("[CSS] Error while trying to apply frame_Windows.js", e);
}

View file

@ -0,0 +1,30 @@
try {
if (document.getElementById('web-navigation-search-box') && !document.querySelector('.web-nav-window-controls')) {
document.getElementById('web-navigation-search-box').insertAdjacentHTML('beforebegin', `
<div class="web-nav-window-controls-outer macos" ondblclick="ipcRenderer.send('maximize')" style="width: 100%; height: 55px; -webkit-app-region: no-drag; background-color: transparent !important; -webkit-user-select: none; padding-left: 2px; padding-top: 2px;">
<div class="web-nav-window-controls" style="-webkit-app-region: drag; position: relative; height: 100%; width: 100%; display: flex; padding: 10px 0 0 10px;">
<span id="close" onclick="ipcRenderer.send('close')"></span>
<span id="minimize" onclick="ipcRenderer.send('minimize')"></span>
<span id="maximize" onclick="ipcRenderer.send('maximize')"></span>
</div>
</div>
`);
if (document.getElementById('web-navigation-search-box')) {
document.getElementById('web-navigation-search-box').style.gridArea = "auto !important";
document.getElementById('web-navigation-search-box').style.marginTop = '0px !important';
}
if (document.getElementById('web-navigation-container')) {
document.getElementById('web-navigation-container').style.gridTemplateRows = '55px auto 1fr auto !important';
}
if (document.querySelector('.web-chrome')) {
document.querySelector('.web-chrome').style.width = "calc(100vw - var(--web-navigation-width))";
}
}
} catch (e) {
console.error("[JS] Error while trying to apply frame_macOS.js", e);
}

213
resources/js/lyrics.js Normal file
View file

@ -0,0 +1,213 @@
(function () {
const lineClicked = function (self, id) {
return function () {
const detail = {"time": self.rangeLrc[id].startTime};
const e = new CustomEvent(self.clickEventName, {
'detail': detail,
"bubbles": true
});
const elem = document.getElementById(self.lineidPrefix + id);
elem.dispatchEvent(e);
};
};
const setHtml = function (self) {
let i;
self.currentLine = 0;
const container = document.getElementById(self.divID);
if(!container) {
return;
}
container.innerHTML = "";
const ul = document.createElement("ul");
container.appendChild(ul);
for (i = 0; i < self.totalLines; i++) {
const li = document.createElement("li");
if (self.rangeLrc[i].line === 'lrcInstrumental'){
li.innerHTML = `<div class="lyricWaiting"><div></div><div></div><div></div></div>`;
} else {
li.innerHTML = self.rangeLrc[i].line;
if (!li.innerHTML) {
li.innerHTML = "&nbsp;"
}}
li.setAttribute("id", self.lineidPrefix + i);
if (self.clickable) {
li.onclick = lineClicked(self, i);
li.style.cursor = 'pointer';
}
ul.appendChild(li);
}
/* hide the later ones*/
for (i = self.totalLines; i < self.totalLines; i++) {
document.getElementById(self.lineidPrefix + i).style.display = "block";
}
};
const moveToLine = function (self, line) {
const startShow = line - self.showLines;
const endShow = line + self.showLines;
for (let i = 0; i < self.totalLines; i++) {
const li = document.getElementById(self.lineidPrefix + i);
if (i >= startShow && i <= endShow) {
try{
li.style.display = "block";}
catch(e){}
} else {
li.style.display = "block";
}
if (i === line) {
li.classList.add(self.currentcss);
if(this.focus == 'start'){
li.scrollIntoView({behavior: 'smooth', block: 'start'});
}else{
li.scrollIntoView({behavior: 'smooth', block: 'center'})};
try{
if (li.innerText == '&nbsp;'){
document.querySelector(`#MVLyricsBox`).style.display = 'none';
} else if (MusicKit.getInstance().nowPlayingItem["type"] === "musicVideo"){
document.querySelector(`#MVLyricsBox`).style.display = 'block';
}
var u = '';
if (li.getElementsByClassName('lyrics-translation').length > 0 ){
try{
if(!li.innerText.includes('Instrumental. / Lyrics not found.')){
u = li.getElementsByClassName('lyrics-translation')[0].innerText;
document.querySelector(`#MVLyricsBox`).childNodes[1].innerHTML= li.getElementsByClassName('lyrics-translation')[0].innerText;
document.querySelector(`#MVLyricsBox`).childNodes[0].innerHTML= (li.innerText).replace(u,'');}
} catch(e){}
} else {
if(!li.innerText.includes('Instrumental. / Lyrics not found.')){
document.querySelector(`#MVLyricsBox`).childNodes[0].innerHTML= li.innerText;
document.querySelector(`#MVLyricsBox`).childNodes[1].innerHTML= ''; }
}
} catch(e){console.log('mverr',e);}
} else {
try{
li.classList.remove(self.currentcss);
} catch(e){}
}
}
};
/* The constructor can be empty or passed in the lrc string*/
const Lyricer = function (options) {
this.divID = "lyricer"; /* the default html container id */
this.currentcss = "lyricer-current-line"; /* this css for the line current playing*/
this.lineidPrefix = "lyricer-line"; /* the id prefix for each line*/
this.showLines = 8; /*lines showing before and after;*/
this.clickable = true;
this.clickEventName = "lyricerclick";
this.focus = 'center';
if (options) {
for (const prop in options) {
if (typeof this[prop] != "undefined" && options.hasOwnProperty(prop)) {
this[prop] = options[prop];
}
}
}
};
Lyricer.prototype.setFocus = function(focus2){
this.focus = focus2;
};
Lyricer.prototype.setLrc = function (rawLrc) {
let i;
this.tags = {};
this.lrc = [];
this.rangeLrc = [];
const tagRegex = /\[([a-z]+):(.*)].*/;
const lrcAllRegex = /(\[[0-9.:\[\]]*])+(.*)/;
const timeRegex = /\[([0-9]+):([0-9.]+)]/;
const rawLrcArray = rawLrc.split(/[\r\n]/);
for (i = 0; i < rawLrcArray.length; i++) {
/* handle tags first*/
const tag = tagRegex.exec(rawLrcArray[i]);
if (tag && tag[0]) {
this.tags[tag[1]] = tag[2];
continue;
}
/* handle lrc*/
const lrc = lrcAllRegex.exec(rawLrcArray[i]);
if (lrc && lrc[0]) {
const times = lrc[1].replace(/]\[/g, "],[").split(",");
for (let j = 0; j < times.length; j++) {
const time = timeRegex.exec(times[j]);
if (time && time[0]) {
this.lrc.push({"startTime": parseInt(time[1], 10) * 60 + parseFloat(time[2]), "line": lrc[2]});
}
}
}
}
/*sort lrc array*/
this.lrc.sort(function (a, b) {
return a.startTime - b.startTime;
});
/* crate the range lrc array*/
/* dummy lines*/
/* for (var i = 0; i < rawLrcArray.length; i++) {
/* this.rangeLrc.push( { "startTime": -1, "endTime": 0, "line": "&nbsp;" } );
/* };
/* real data*/
let startTime = 0;
let line = "";
for (i = 0; i < this.lrc.length; i++) {
endTime = parseFloat(this.lrc[i].startTime);
if (startTime == 10 && line == 'lrcInstrumental') startTime=0;
if (!this.rangeLrc.includes({"startTime": startTime, "endTime": endTime, "line": line})){
this.rangeLrc.push({"startTime": startTime, "endTime": endTime, "line": line});} else {console.log('blocked');}
startTime = endTime;
line = this.lrc[i].line;
}
this.rangeLrc.push({"startTime": startTime, "endTime": 999.99, "line": line});
/* dummy lines
/* for (var i = 0; i < this.showLines; i++) {
/* this.rangeLrc.push( { "startTime": -1, "endTime": 0, "line": "&nbsp;" } );
/* };*/
this.totalLines = this.rangeLrc.length;
console.log(this.rangeLrc);
/* set html and move to start*/
setHtml(this);
this.move(0);
if(typeof _lyrics !== "undefined") {
_lyrics.setLyrics(this.rangeLrc);
}
};
Lyricer.prototype.move = function (time) {
for (let i = 0; i < this.totalLines; i++) {
if (time >= this.rangeLrc[i].startTime && time < this.rangeLrc[i].endTime) {
if (this.currentLine !== i) {
this.currentLine = i;
moveToLine(this, this.currentLine);
}
return;
}
}
};
Lyricer.prototype.setMXMTranslation = function (translation_list) {
const container = document.getElementById(this.divID);
const lines = container.getElementsByTagName('li');
for (var line of lines){
for (var trans_line of translation_list){
if (line.textContent == " "+trans_line["translation"]["matched_line"]){
const trans = document.createElement("div");
trans.className = "lyrics-translation";
trans.textContent = trans_line["translation"]["description"];
line.appendChild(trans);
break;
}
}
}
};
window.Lyricer = Lyricer; /*exposed to global*/
})();

View file

@ -0,0 +1,118 @@
var _plugins = {
events: {
Start: [],
OnNavigation: [],
OnPlaybackStateChanged: [],
OnExit: [],
OnHide: [],
OnShow: []
},
plugins: [],
menuitems: [],
chromeitems: [],
loadPlugin(plugin = "") {
if (plugin == "") {
return
}
ipcRenderer.send("load-plugin", plugin)
},
execute(type = "Start", args = {}) {
let self = this
if (!this.events[type]) {
console.warn(`[Plugins] Event type: ${type} not found!`)
return
} else {
console.info(`[Plugins] Event type: ${type} called`) //info makes it more distingishable in the console (more Beginner friendly)
}
this.events[type].forEach(element => {
element(args)
});
}
};
class AMEPlugin_Menuitem {
constructor() {
this.Text = ""
this.Icon = ""
this.OnClick = () => {}
}
get() {
JSON.stringify(this)
}
}
class AMEPluginHelper {
constructor() {
/**
* Adds all events to the _plugins event queue
*/
_plugins.events.Start.push(this.Start)
_plugins.events.OnNavigation.push(this.OnNavigation)
_plugins.events.OnPlaybackStateChanged.push(this.OnPlaybackStateChanged)
_plugins.events.OnExit.push(this.OnExit)
_plugins.events.OnHide.push(this.OnHide)
_plugins.events.OnShow.push(this.OnShow)
this.name = "Plugin Name"
this.Start()
this.Announce()
}
/**
* Announces that the plugin has loaded in console
*/
Announce() {
console.info(`[Plugins] Plugin: ${this.name} loaded.`)
}
/**
* Excutes when the web player has fully loaded
*/
Start() {}
/**
* Executes when playback state is changed (WIP)
*/
OnPlaybackStateChanged() {}
/**
* Executes when the user changes pages on the site or opens a context menu
* ex: Songs to Playlist screen
*/
OnNavigation() {}
/**
* Executes when the application exits (WIP)
*/
OnExit() {}
/**
* Executes when the application is hidden to the taskbar
*/
OnHide() {}
/**
* Executes when the application is unhidden (WIP)
*/
OnShow() {}
/**
* Adds a menu item to the profile menu (WIP)
*/
AddMenuItem({
Text = "",
Icon = "",
OnClick = () => {}
}) {
var menuitem = new AMEPlugin_Menuitem()
menuitem.Text = Text
menuitem.Icon = Icon
menuitem.OnClick = OnClick
_plugins.menuitems.push(menuitem)
}
/**
* Adds a button to the web chrome after the volume meter (WIP)
*/
AddChromeButton({
text = "",
style = {},
onclick = () => {}
}) {
var btn = document.createElement("button")
btn.classList.add("button-reset")
var btnStyle = {
width: 38
}
}
}

View file

@ -0,0 +1,19 @@
try {
function matchRuleShort(str, rule) {
var escapeRegex = (str) => str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
return new RegExp("^" + rule.split("*").map(escapeRegex).join(".*") + "$").test(str);
}
if(AMJavaScript.getQuery("amesettings")) {
if (!storedInnerHTML && document.getElementsByTagName('footer').length !== 0) {
var storedInnerHTML = document.getElementsByTagName('footer')[0].innerHTML;
}
if (matchRuleShort(window.location.href, '*settings*') && document.getElementsByClassName('application-preferences').length === 0) {
AMSettings.CreateMenu('commerce-full-content');
} else if (document.getElementsByTagName('footer').length === 1) {
document.getElementsByTagName('footer')[0].innerHTML = storedInnerHTML; /* Revert the footer */
}
}
} catch (e) {
console.error("[JS] Error while trying to apply settingsPage.js", e);
}

542
resources/js/tests.js Normal file
View file

@ -0,0 +1,542 @@
var _tests = {
remoteUI() {
AMJavaScript.getRequest("ameres://html/itunes_remote.html", (content)=>{
var vm = new Vue({
data: {
passcode: {
0: "",
1: "",
2: "",
3: ""
},
state: "pin"
},
methods: {
close() {
modal.close()
},
jumpToNum(num) {
document.querySelector(`#passcode-num-${num}`).focus()
document.querySelector(`#passcode-num-${num}`).select()
},
retry() {
this.passcode = {0:"",1:"",2:"",3:""}
this.state = "pin"
},
connect() {
let self = this
this.state = "connecting"
setTimeout(()=>{
self.state = "success"
}, 2000)
}
}
})
var modal = new AMEModal({
content: content,
CloseButton: false,
Dismissible: false,
Style: {
maxWidth: "700px",
maxHeight: "400px"
},
OnCreate() {
vm.$mount("#itunes-remote-vue")
vm.jumpToNum(0)
},
OnClose() {
_vues.destroy(vm)
}
})
})
},
usermenuinit() {
// MOVE ME ONCE IMPLEMENTED!
// Clone the user menu
var umClone = document.querySelector(".web-chrome-controls-container>.web-navigation__auth").cloneNode(true)
// Hide the existing menu
document.querySelector(".web-chrome-controls-container>.web-navigation__auth").style.display = "none"
// Append cloned menu
document.querySelector(".web-chrome-controls-container").append(umClone)
// Set cloned menu events
umClone.addEventListener("click", (e)=>{
_tests.usermenu(e)
})
},
usermenu(e) {
// MOVE ME ONCE IMPLEMENTED!
AMJavaScript.getRequest("ameres://html/usermenu.html", (content) => {
var vm = new Vue({
data: {
menuitems: [
{
label: "Help",
visible: true,
icon: "",
svg: `<svg class="context-menu__option-icon" viewBox="0 0 64 64"><path d="M32.32 61.417c16.075 0 29.08-13.032 29.08-29.164 0-16.103-13.005-29.135-29.08-29.135C16.215 3.117 3.238 16.15 3.238 32.253c0 16.132 12.977 29.164 29.082 29.164zm0-5.672c-13.033 0-23.243-10.515-23.243-23.492 0-12.95 10.21-23.463 23.243-23.463 13.032 0 23.213 10.515 23.213 23.463 0 12.977-10.183 23.492-23.213 23.492zm-.665-17.985c1.522 0 2.517-.885 2.6-2.02v-.333c.083-1.437 1.08-2.379 2.878-3.54 2.74-1.8 4.484-3.377 4.484-6.585 0-4.594-4.15-7.222-9.077-7.222-4.758 0-7.967 2.13-8.827 4.732-.166.496-.276.966-.276 1.466 0 1.327 1.051 2.185 2.325 2.185 1.605 0 1.991-.83 2.821-1.8.887-1.355 2.075-2.156 3.709-2.156 2.185 0 3.596 1.245 3.596 3.071 0 1.715-1.161 2.6-3.486 4.234-1.964 1.355-3.404 2.793-3.404 5.256v.305c0 1.577.942 2.407 2.657 2.407zm-.027 8.495c1.77 0 3.237-1.3 3.237-3.043 0-1.772-1.438-3.045-3.237-3.045-1.826 0-3.266 1.3-3.266 3.045 0 1.715 1.466 3.043 3.266 3.043z"></path></svg>`,
onclick: () => {
window.open(`https://support.apple.com/guide/music-web`)
}
},
{
label: "Discord",
visible: true,
icon: "",
svg: `<svg xmlns="http://www.w3.org/2000/svg" width="28" height="20" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" clip-rule="evenodd" viewBox="0 0 28 20" xml:space="preserve" class="context-menu__option-icon">
<path d="M23.0212 1.67671C21.3107 0.879656 19.5079 0.318797 17.6584 0C17.4062 0.461742 17.1749 0.934541 16.9708 1.4184C15.003 1.12145 12.9974 1.12145 11.0283 1.4184C10.819 0.934541 10.589 0.461744 10.3368 0.00546311C8.48074 0.324393 6.67795 0.885118 4.96746 1.68231C1.56727 6.77853 0.649666 11.7538 1.11108 16.652C3.10102 18.1418 5.3262 19.2743 7.69177 20C8.22338 19.2743 8.69519 18.4993 9.09812 17.691C8.32996 17.3997 7.58522 17.0424 6.87684 16.6135C7.06531 16.4762 7.24726 16.3387 7.42403 16.1847C11.5911 18.1749 16.408 18.1749 20.5763 16.1847C20.7531 16.3332 20.9351 16.4762 21.1171 16.6135C20.41 17.0369 19.6639 17.3997 18.897 17.691C19.3052 18.4993 19.7718 19.2689 20.3021 19.9945C22.6677 19.2689 24.8929 18.1364 26.8828 16.6466H26.8893C27.43 10.9731 25.9665 6.04728 23.0212 1.67671ZM9.68041 13.6383C8.39754 13.6383 7.34085 12.4453 7.34085 10.994C7.34085 9.54272 8.37155 8.34973 9.68041 8.34973C10.9893 8.34973 12.0395 9.54272 12.0187 10.994C12.0187 12.4453 10.9828 13.6383 9.68041 13.6383ZM18.3161 13.6383C17.0332 13.6383 15.9765 12.4453 15.9765 10.994C15.9765 9.54272 17.0124 8.34973 18.3161 8.34973C19.6184 8.34973 20.6751 9.54272 20.6543 10.994C20.6543 12.4453 19.6184 13.6383 18.3161 13.6383Z"</path>
</svg>`,
onclick: () => {
window.open(`https://discord.gg/CezHYdXHEM`)
}
},
{
label: "Account Settings",
visible: true,
icon: "",
svg: `<svg width="24" height="24" viewBox="0 0 24 24" stroke="#212b36" stroke-width="2" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" clip-rule="evenodd" xml:space="preserve" class="context-menu__option-icon">
<circle cx="12" cy="8" r="5" />
<path d="M3,21 h18 C 21,12 3,12 3,21"/>
</svg>`,
onclick: () => {
history.pushState("settings", "Settings", "/account/settings/")
window.location.href = "#"
}
},
{
label: "Preferences",
visible: true,
icon: "",
svg: `<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" clip-rule="evenodd" version="1.1" viewBox="0 0 16 16" xml:space="preserve" class="context-menu__option-icon"><path fill-rule="nonzero" d="M31.475,57.622C32.114,57.622 32.702,57.571 33.34,57.52L34.77,60.228C35.077,60.891 35.766,61.249 36.558,61.12C37.298,60.994 37.809,60.43 37.912,59.665L38.346,56.651C39.546,56.32 40.696,55.885 41.845,55.4L44.066,57.392C44.629,57.928 45.369,58.031 46.085,57.648C46.749,57.263 47.005,56.575 46.877,55.809L46.238,52.82C47.26,52.115 48.218,51.321 49.098,50.445L51.883,51.62C52.597,51.928 53.313,51.748 53.849,51.11C54.334,50.548 54.411,49.806 53.977,49.143L52.367,46.537C53.079,45.524 53.695,44.446 54.207,43.318L57.247,43.472C58.012,43.498 58.677,43.089 58.907,42.349C59.163,41.658 58.933,40.876 58.345,40.432L55.945,38.543C56.249,37.393 56.505,36.143 56.606,34.866L59.468,33.946C60.234,33.716 60.694,33.128 60.694,32.362C60.694,31.571 60.234,31.008 59.468,30.752L56.606,29.834C56.505,28.557 56.249,27.357 55.945,26.157L58.318,24.265C58.932,23.806 59.162,23.091 58.906,22.375C58.676,21.66 58.012,21.226 57.245,21.252L54.206,21.38C53.645,20.23 53.083,19.183 52.368,18.135L53.977,15.555C54.387,14.965 54.334,14.17 53.849,13.64C53.312,12.977 52.597,12.825 51.883,13.105L49.098,14.254C48.198,13.4 47.242,12.606 46.237,11.878L46.877,8.915C47.005,8.149 46.723,7.434 46.085,7.077C45.369,6.692 44.629,6.769 44.066,7.332L41.846,9.298C40.702,8.821 39.535,8.404 38.348,8.048L37.914,5.058C37.834,4.32 37.267,3.722 36.534,3.603C35.768,3.5 35.078,3.833 34.772,4.471L33.342,7.178C32.703,7.154 32.115,7.102 31.477,7.102C30.838,7.102 30.251,7.154 29.612,7.178L28.182,4.471C27.849,3.832 27.186,3.501 26.394,3.603C25.654,3.731 25.144,4.293 25.04,5.058L24.606,8.048C23.406,8.405 22.231,8.814 21.108,9.298L18.885,7.332C18.298,6.769 17.583,6.694 16.842,7.077C16.203,7.434 15.949,8.149 16.075,8.942L16.714,11.878C15.709,12.606 14.753,13.4 13.852,14.254L11.069,13.104C10.329,12.824 9.639,12.977 9.077,13.64C8.618,14.179 8.566,14.96 8.949,15.555L10.559,18.135C9.875,19.17 9.269,20.255 8.746,21.38L5.68,21.252C4.941,21.219 4.264,21.677 4.02,22.375C3.79,23.091 3.995,23.806 4.608,24.266L6.982,26.154C6.7,27.354 6.445,28.554 6.343,29.831L3.457,30.751C2.717,30.981 2.282,31.569 2.282,32.36C2.282,33.126 2.717,33.714 3.457,33.945L6.343,34.889C6.445,36.14 6.675,37.392 6.982,38.542L4.606,40.43C4.022,40.877 3.785,41.65 4.018,42.347C4.253,43.052 4.938,43.515 5.68,43.47L8.745,43.316C9.28,44.466 9.868,45.539 10.557,46.534L8.947,49.141C8.551,49.751 8.604,50.554 9.077,51.107C9.64,51.745 10.329,51.924 11.069,51.617L13.828,50.442C14.722,51.312 15.688,52.107 16.714,52.817L16.075,55.807C15.948,56.573 16.203,57.262 16.868,57.645C17.583,58.028 18.298,57.925 18.86,57.415L21.106,55.397C22.231,55.882 23.406,56.317 24.606,56.648L25.04,59.664C25.143,60.428 25.654,60.991 26.394,61.144C27.186,61.247 27.849,60.888 28.182,60.225L29.612,57.517C30.251,57.568 30.838,57.619 31.477,57.619L31.475,57.621L31.475,57.622ZM38.168,30.345C36.891,27.049 34.337,25.262 31.322,25.262C30.762,25.261 30.205,25.33 29.662,25.466L22.766,13.64C25.492,12.368 28.466,11.714 31.474,11.724C42.254,11.724 50.732,19.822 51.729,30.344L38.168,30.344L38.168,30.345ZM11.145,32.362C11.145,25.543 14.286,19.515 19.242,15.762L26.188,27.637C24.834,29.145 24.222,30.752 24.222,32.438C24.222,34.074 24.809,35.58 26.188,37.138L19.063,48.835C14.209,45.055 11.145,39.105 11.145,32.362ZM28.283,32.412C28.283,30.702 29.738,29.374 31.373,29.374C33.085,29.374 34.489,30.702 34.489,32.412C34.481,34.114 33.076,35.509 31.374,35.505C29.738,35.505 28.282,34.125 28.282,32.412L28.283,32.412ZM31.475,53.025C28.257,53.025 25.218,52.285 22.562,50.982L29.637,39.386C30.377,39.565 30.887,39.616 31.322,39.616C34.362,39.616 36.915,37.776 38.168,34.405L51.729,34.405C50.732,44.903 42.252,53.025 31.475,53.025Z" transform="matrix(.27119 0 0 .27119 -.54 -.78)"></path></svg>`,
onclick: () => {
history.pushState("settings", "Settings", "/account/settings/?amesettings=1")
window.location.href = "#"
}
},
{
label: "Equalizer",
visible: AudioOutputs.eqReady,
svg: `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="context-menu__option-icon" width="100%" height="100%" viewBox="0 0 16 16" version="1.1">
<g>
<path style=" stroke:none;fill-rule:nonzero;fill:currentColor;fill-opacity:1;" d="M 12.050781 0 L 14.214844 0 L 14.214844 4.808594 L 12.050781 4.808594 Z M 12.050781 0 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:currentColor;fill-opacity:1;" d="M 12.050781 7.421875 L 14.214844 7.421875 L 14.214844 15.988281 L 12.050781 15.988281 Z M 12.050781 7.421875 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:currentColor;fill-opacity:1;" d="M 6.914062 0 L 9.078125 0 L 9.078125 8.910156 L 6.914062 8.910156 Z M 6.914062 0 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:currentColor;fill-opacity:1;" d="M 6.914062 11.527344 L 9.078125 11.527344 L 9.078125 15.988281 L 6.914062 15.988281 Z M 6.914062 11.527344 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:currentColor;fill-opacity:1;" d="M 1.773438 0 L 3.9375 0 L 3.9375 1.472656 L 1.773438 1.472656 Z M 1.773438 0 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:currentColor;fill-opacity:1;" d="M 1.773438 4.085938 L 3.9375 4.085938 L 3.9375 15.988281 L 1.773438 15.988281 Z M 1.773438 4.085938 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:currentColor;fill-opacity:1;" d="M 0.871094 1.921875 L 0.871094 3.636719 L 4.839844 3.636719 L 4.839844 1.921875 Z M 0.871094 1.921875 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:currentColor;fill-opacity:1;" d="M 6.011719 9.363281 L 6.011719 11.074219 L 9.976562 11.074219 L 9.976562 9.363281 Z M 6.011719 9.363281 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:currentColor;fill-opacity:1;" d="M 12.050781 5.257812 L 11.152344 5.257812 L 11.152344 6.972656 L 15.117188 6.972656 L 15.117188 5.257812 Z M 12.050781 5.257812 "/>
</g>
</svg>`,
onclick: () => {
_tests.eq()
}
},
{
label: "Sign Out",
visible: true,
icon: "",
style: {
color: "var(--systemRed)"
},
onclick: () => {
MusicKit.getInstance().unauthorize()
}
}
]
},
methods: {
close() {
modal.close()
}
}
})
var calc = {
width: 185,
left: e.clientX - (185 / 2)
}
// calculate the position of the menu based on the mouse position and the width of the menu itself
if (calc.left + calc.width > window.innerWidth) {
calc.left = (window.innerWidth - calc.width)
}
if (calc.left < 0) {
calc.left = 0
}
var modal = new AMEModal({
content: content,
CloseButton: false,
ModalClasses: ["ameUserMenu"],
BackdropStyle: {
background: "transparent"
},
Style: {
height: "auto",
width: `${calc["width"]}px`,
position: "absolute",
// top: "46px",
// right: "142px"
top: `46px`,
left: `${calc.left}px`
},
OnCreate() {
vm.$mount("#usermenu-vue")
if(typeof _plugins != "undefined") {
_plugins.menuitems.forEach((menuitem)=>{
vm.menuitems.unshift({
label: menuitem["Text"],
onclick: menuitem["OnClick"],
icon: "",
visible: true
})
})
}
},
OnClose() {
_vues.destroy(vm)
}
})
})
},
eq() {
AMJavaScript.getRequest("ameres://html/eq.html", (content) => {
var vm = new Vue({
data: {
manualEntry: false,
bass: bassFilter.gain.value.toFixed(2),
treble: trebleFilter.gain.value.toFixed(2),
gain: AMEx.result.gain.gain.value.toFixed(2)
},
methods: {
reset() {
this.bass = 0
bassFilter.gain.value = 0
this.treble = 0
trebleFilter.gain.value = 0
this.gain = 0
AMEx.result.gain.gain.value = 0
},
close() {
modal.close()
}
}
})
var modal = new AMEModal({
content: content,
CloseButton: false,
ModalClasses: ["ameUserMenu"],
BackdropStyle: {
background: "transparent"
},
Style: {
animation: "ameEQIn .10s var(--appleEase)",
width: "306px",
height: "254px",
position: "absolute",
top: "46px",
right: "42px"
},
OnCreate() {
vm.$mount("#eq-vm")
}
})
})
},
zoo() {
AMJavaScript.getRequest("ameres://html/zoo.html", (content) => {
var modal = new AMEModal({
content: content
})
})
},
outputDevice() {
AMJavaScript.getRequest("ameres://html/outputdevice.html", (content) => {
var vm = new Vue({
data: {
selected: "",
audio: document.querySelector("#apple-music-player"),
devices: []
},
methods: {
setOutputDevice(id) {
if (this.audio) {
selected = id
sessionStorage.setItem("outputDevice", id)
this.audio.setSinkId(id)
}
}
}
})
var modal = new AMEModal({
content: content,
Style: {
width: "30%",
minWidth: "500px"
},
OnCreate() {
vm.$mount("#outputdevices-vue")
if (vm.audio) {
vm.selected = audio.sinkId
} else {
vm.selected = "default"
}
navigator.mediaDevices.enumerateDevices()
.then(function (devices) {
vm.devices = devices.filter((device) => {
if (device.kind == "audiooutput") {
return device
}
})
})
.catch(function (err) {
console.log(err.name + ": " + err.message)
})
},
OnClose() {
_vues.destroy(vm)
}
})
})
},
stats() {
var container = document.createElement("div")
var frameRate = document.createElement("div")
var listeners = document.createElement("div")
Object.assign(container.style,
{
textAlign: "center",
position: "absolute",
fontSize: "18px",
bottom: "16px",
right: "16px",
pointerEvents: "none",
zIndex: 99991,
color: "white",
webkitTextStroke: "0.2px black"
})
document.body.appendChild(container)
container.appendChild(frameRate)
container.appendChild(listeners)
const times = [];
let fps;
function refreshLoop() {
window.requestAnimationFrame(() => {
const now = performance.now();
while (times.length > 0 && times[0] <= now - 1000) {
times.shift();
}
times.push(now);
fps = times.length;
frameRate.innerText = `${fps} FPS`
refreshLoop();
});
}
refreshLoop();
},
oobe(skipIntro = false, closeBtn = false) {
// MOVE ME ONCE IMPLEMENTED!
AMJavaScript.getRequest("ameres://html/oobe.html", (content) => {
var vm = new Vue({
data: {
prefs: {
general: {
storefront: "us",
discordRPC: "",
analyticsEnabled: true
},
visual: {
theme: "",
transparencyEffect: "",
useOperatingSystemAccent: false,
scaling: 1,
mxmon: false,
yton: false,
mxmlanguage: "en",
removeScrollbars: true
},
audio: {
audioQuality: "auto",
seamlessAudioTransitions: true,
castingBitDepth: '16',
enableDLNA: false,
},
window: {
closeButtonMinimize: true
}
},
page: "intro",
},
methods: {
btn() {
console.info("Button clicked")
},
getPrefs() {
let self = this
ipcRenderer.invoke("getStoreValue", "audio.audioQuality").then((result) => {
self.prefs.audio.audioQuality = result
})
ipcRenderer.invoke("getStoreValue", "audio.seamlessAudioTransitions").then((result) => {
self.prefs.audio.seamlessAudioTransitions = result
})
ipcRenderer.invoke("getStoreValue", "audio.castingBitDepth").then((result) => {
self.prefs.audio.castingBitDepth = result
})
ipcRenderer.invoke("getStoreValue", "audio.enableDLNA").then((result) => {
self.prefs.audio.enableDLNA = result
})
ipcRenderer.invoke("getStoreValue", "general.storefront").then((result) => {
self.prefs.general.storefront = result
})
ipcRenderer.invoke("getStoreValue", "general.discordRPC").then((result) => {
self.prefs.general.discordRPC = result
})
ipcRenderer.invoke("getStoreValue", "general.analyticsEnabled").then((result) => {
self.prefs.general.analyticsEnabled = result
})
ipcRenderer.invoke("getStoreValue", "window.closeButtonMinimize").then((result) => {
self.prefs.window.closeButtonMinimize = result
})
ipcRenderer.invoke("getStoreValue", "visual.theme").then((result) => {
self.prefs.visual.theme = result
})
ipcRenderer.invoke("getStoreValue", "visual.transparencyEffect").then((result) => {
self.prefs.visual.transparencyEffect = result
})
ipcRenderer.invoke("getStoreValue", "visual.useOperatingSystemAccent").then((result) => {
self.prefs.visual.useOperatingSystemAccent = result
})
ipcRenderer.invoke("getStoreValue", "visual.mxmon").then((result) => {
self.prefs.visual.mxmon = result
})
ipcRenderer.invoke("getStoreValue", "visual.yton").then((result) => {
self.prefs.visual.yton = result
})
ipcRenderer.invoke("getStoreValue", "visual.mxmlanguage").then((result) => {
self.prefs.visual.mxmlanguage = result
})
ipcRenderer.invoke("getStoreValue", "visual.removeScrollbars").then((result) => {
self.prefs.visual.removeScrollbars = result
})
},
setPrefs() {
let self = this
ipcRenderer.invoke("setStoreValue", "audio.audioQuality", self.prefs.audio.audioQuality)
ipcRenderer.invoke("setStoreValue", "audio.seamlessAudioTransitions", self.prefs.audio.seamlessAudioTransitions)
ipcRenderer.invoke("setStoreValue", "audio.castingBitDepth", self.prefs.audio.castingBitDepth)
ipcRenderer.invoke("setStoreValue", "audio.enableDLNA", self.prefs.audio.enableDLNA)
ipcRenderer.invoke("setStoreValue", "general.storefront", self.prefs.general.storefront)
ipcRenderer.invoke("setStoreValue", "general.discordRPC", self.prefs.general.discordRPC)
ipcRenderer.invoke("setStoreValue", "general.analyticsEnabled", self.prefs.general.analyticsEnabled)
ipcRenderer.invoke("setStoreValue", "window.closeButtonMinimize", self.prefs.window.closeButtonMinimize)
ipcRenderer.invoke("setStoreValue", "visual.theme", self.prefs.visual.theme)
ipcRenderer.invoke("setStoreValue", "visual.transparencyEffect", self.prefs.visual.transparencyEffect)
ipcRenderer.invoke("setStoreValue", "visual.useOperatingSystemAccent", self.prefs.visual.useOperatingSystemAccent)
ipcRenderer.invoke("setStoreValue", "visual.mxmon", self.prefs.visual.mxmon)
ipcRenderer.invoke("setStoreValue", "visual.yton", self.prefs.visual.yton)
ipcRenderer.invoke("setStoreValue", "visual.mxmlanguage", self.prefs.visual.mxmlanguage)
ipcRenderer.invoke("setStoreValue", "visual.removeScrollbars", self.prefs.visual.removeScrollbars)
},
promptRelaunch() {
var relaunch = confirm(
"Relaunch Required\nA relaunch is required in order for the settings you have changed to apply."
)
if (relaunch) {
ipcRenderer.send("relaunchApp")
}
},
close() {
this.setPrefs()
// this.promptRelaunch()
modal.close()
},
init() {
let self = this
document.getElementById('introVideo').addEventListener('ended', () => {
self.page = "welcome"
}, false);
this.getPrefs()
},
enableBlur() {
modal.setStyle("backdrop", {
backdropFilter: "blur(16px) saturate(180%)"
})
},
disableBlur() {
modal.setStyle("backdrop", {
backdropFilter: "blur(0px)"
})
}
}
})
var modal = new AMEModal({
content: content,
CloseButton: closeBtn,
Dismissible: closeBtn,
OnCreate() {
vm.$mount("#oobe-vue")
if (skipIntro) {
vm.page = "welcome"
} else {
vm.init()
}
},
OnClose() {
_vues.destroy(vm);
if (!MusicKit.getInstance().isAuthorized) {
MusicKit.getInstance().authorize();
}
}
})
})
}
};
if (ipcRenderer.sendSync('showOOBE')) {
setTimeout(() => {
_tests.oobe();
}, 200)
}

147
resources/js/utils.js Normal file
View file

@ -0,0 +1,147 @@
var _vues = {
instances: [],
killVue(id) {
let self = this
this.instances = this.instances.filter((instance) => {
console.warn(`Requested destroy: ${id}`)
if (instance["_amID"] != id) {
console.warn("Found vue")
console.warn("Destroying Vue")
instance.$destroy()
} else {
return instance
}
})
},
gc() {
var needsGC = this.instances.every((val, i, arr) => val == undefined)
if (needsGC) {
this.instances = []
}
},
destroy(vue) {
vue.$destroy()
vue = undefined
console.info("Destroyed Vue instance")
},
killAll() {
// Kill all Vue instances
this.instances.forEach((instance) => {
instance.$destroy()
instance = null
})
this.instances = []
}
};
class AMEModal {
constructor({
content = "",
OnCreate = () => {
},
OnClose = () => {
},
CloseButton = true,
Style = {},
ModalClasses = [],
BackdropStyle = {},
Dismissible = true
}) {
this.Style = Style
this.BackdropStyle = BackdropStyle
this.ModalClasses = ModalClasses
this.closeButton = CloseButton
this.content = content
this.OnClose = OnClose
this.OnCreate = OnCreate
this.Dismissible = Dismissible
this.modal = {}
this.create()
}
create() {
let self = this
var backdrop = document.createElement("div")
var dismissArea = document.createElement("div")
var modalWin = document.createElement("div")
var modalCloseBtn = document.createElement("button")
var modalContent = document.createElement("div")
backdrop.classList.add("ameModal-Backdrop")
modalWin.classList.add("ameModal")
modalCloseBtn.classList.add("ameModal-Close")
modalCloseBtn.innerHTML = ("Close")
modalCloseBtn.addEventListener("click", () => {
self.close()
}, {once: true})
Object.assign(dismissArea.style, {
width: "100%",
height: "100%",
position: "absolute",
top: "0px",
left: "0px",
cursor: "pointer"
})
modalContent.style.height = "100%"
setInnerHTML(modalContent, this.content)
if(this.closeButton) {
modalWin.appendChild(modalCloseBtn)
}
if(this.Dismissible) {
dismissArea.addEventListener("click",()=>{
self.close()
}, {once: true})
document.addEventListener("keyup", (e)=>{
if(e.key == "Escape") {
self.close()
}
}, {once: true})
backdrop.appendChild(dismissArea)
}
modalWin.appendChild(modalContent)
Object.assign(backdrop.style, this.BackdropStyle)
this.ModalClasses.forEach((cssClass)=>{
modalWin.classList.add(cssClass)
})
if(document.querySelectorAll(".ameModal").length == 0) {
document.body.classList.add("no-acrylic")
}
Object.assign(modalWin.style, this.Style)
backdrop.appendChild(modalWin)
document.body.appendChild(backdrop)
this.modal = {
backdrop: backdrop,
window: modalWin,
content: modalContent,
closeBtn: modalCloseBtn
}
this.OnCreate()
}
close() {
this.OnClose()
this.modal.backdrop.style.background = "transparent"
this.modal.window.classList.add("ameModal-closing")
setTimeout(()=>{
this.modal.backdrop.remove()
if(document.querySelectorAll(".ameModal").length == 0) {
document.body.classList.remove("no-acrylic")
}
}, 100)
}
setStyle (element, style = {}) {
if(this.modal[element]) {
Object.assign(this.modal[element].style, style)
}else{
console.warn(`Undefined modal element "${element}", available modals are: "backdrop", "window", "closeBtn"`)
}
}
OnCreate() {
}
OnClose() {
}
};

6
resources/js/vue.js Normal file

File diff suppressed because one or more lines are too long