Merge branch 'develop'

This commit is contained in:
child_duckling 2022-03-08 18:07:59 -08:00
commit df2f7b7216
195 changed files with 62285 additions and 12895 deletions

30
src/renderer/main/app.js Normal file
View file

@ -0,0 +1,30 @@
import { app } from "./vueapp.js"
import {CiderCache} from './cidercache.js'
import {CiderFrontAPI} from './ciderfrontapi.js'
import {simulateGamepad} from './gamepad.js'
import {CiderAudio} from '../audio/audio.js'
import {Events} from './events.js'
import { wsapi } from "./wsapi_interop.js"
import { MusicKitTools } from "./musickittools.js"
// Define window objects
window.app = app
window.MusicKitTools = MusicKitTools
window.CiderAudio = CiderAudio
window.CiderCache = CiderCache
window.CiderFrontAPI = CiderFrontAPI
window.wsapi = wsapi
// Mount Vue to #app
app.$mount("#app")
// Init CiderAudio
if (app.cfg.advanced.AudioContext){
CiderAudio.init()
}
// Import gamepad support
app.simulateGamepad = simulateGamepad
Events.InitEvents()

View file

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

View file

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

View file

@ -0,0 +1,74 @@
const Events = {
InitEvents() {
const app = window.app
// Key binds
document.addEventListener('keydown', function (e) {
if (e.keyCode === 70 && e.ctrlKey) {
app.$refs.searchInput.focus()
app.$refs.searchInput.select()
}
});
// add event listener for when window.location.hash changes
window.addEventListener("hashchange", function () {
app.appRoute(window.location.hash)
});
// Key bind to unjam MusicKit in case it fails: CTRL+F10
document.addEventListener('keydown', function (event) {
if (event.ctrlKey && event.keyCode == 121) {
try {
app.mk._services.mediaItemPlayback._currentPlayer.stop()
} catch (e) {
}
try {
app.mk._services.mediaItemPlayback._currentPlayer.destroy()
} catch (e) {
}
}
});
window.addEventListener("mouseup", (e) => {
if (e.button === 3) {
e.preventDefault()
app.navigateBack()
} else if (e.button === 4) {
e.preventDefault()
app.navigateForward()
}
});
document.addEventListener('keydown', function (event) {
if (event.ctrlKey && event.keyCode == 122) {
try {
ipcRenderer.send('detachDT', '')
} catch (e) {
}
}
});
// Hang Timer
app.hangtimer = setTimeout(() => {
if (confirm("Cider is not responding. Reload the app?")) {
window.location.reload()
}
}, 10000)
// Refresh Focus
function refreshFocus() {
if (document.hasFocus() == false) {
app.windowFocus(false)
} else {
app.windowFocus(true)
}
setTimeout(refreshFocus, 200);
}
app.getHTMLStyle()
refreshFocus();
}
}
export {Events}

View file

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

View file

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

4006
src/renderer/main/vueapp.js Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,20 @@
const store = new Vuex.Store({
state: {
library: {
// songs: ipcRenderer.sendSync("get-library-songs"),
// albums: ipcRenderer.sendSync("get-library-albums"),
// recentlyAdded: ipcRenderer.sendSync("get-library-recentlyAdded"),
// playlists: ipcRenderer.sendSync("get-library-playlists")
},
artwork: {
playerLCD: ""
}
},
mutations: {
setLCDArtwork(state, artwork) {
state.artwork.playerLCD = artwork
}
}
})
export {store}

View file

@ -0,0 +1,113 @@
const wsapi = {
cache: {playParams: {id: 0}, status: null, remainingTime: 0},
playbackCache: {status: null, time: Date.now()},
search(term, limit) {
MusicKit.getInstance().api.search(term, {limit: limit, types: 'songs,artists,albums,playlists'}).then((results)=>{
ipcRenderer.send('wsapi-returnSearch', JSON.stringify(results))
})
},
searchLibrary(term, limit) {
MusicKit.getInstance().api.library.search(term, {limit: limit, types: 'library-songs,library-artists,library-albums,library-playlists'}).then((results)=>{
ipcRenderer.send('wsapi-returnSearchLibrary', JSON.stringify(results))
})
},
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 = attributes.endTime ? attributes.endTime : Date.now();
attributes.volume = mk.volume;
attributes.shuffleMode = mk.shuffleMode;
attributes.repeatMode = mk.repeatMode;
attributes.autoplayEnabled = mk.autoplayEnabled;
return attributes
},
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
},
returnDynamic(data, type) {
ipcRenderer.send('wsapi-returnDynamic', JSON.stringify(data), type)
},
musickitApi(method, id, params, library = false) {
if (library) {
MusicKit.getInstance().api.library[method](id, params).then((results)=>{
ipcRenderer.send('wsapi-returnMusicKitApi', JSON.stringify(results), method)
})
} else {
MusicKit.getInstance().api[method](id, params).then((results)=>{
ipcRenderer.send('wsapi-returnMusicKitApi', JSON.stringify(results), method)
})
}
},
getPlaybackState () {
ipcRenderer.send('wsapi-updatePlaybackState', MusicKitInterop.getAttributes());
},
getLyrics() {
ipcRenderer.send('wsapi-returnLyrics',JSON.stringify(app.lyrics));
},
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, kind = "song") {
MusicKit.getInstance().setQueue({ [kind]: id , parameters : {l : app.mklang}}).then(function (queue) {
MusicKit.getInstance().play()
})
},
quickPlay(term) {
// Quick play by song name
MusicKit.getInstance().api.search(term, { limit: 2, types: 'songs' }).then(function (data) {
MusicKit.getInstance().setQueue({ song: data["songs"][0]["id"],parameters : {l : app.mklang} }).then(function (queue) {
MusicKit.getInstance().play()
})
})
},
toggleShuffle() {
MusicKit.getInstance().shuffleMode = MusicKit.getInstance().shuffleMode === 0 ? 1 : 0
},
toggleRepeat() {
if(MusicKit.getInstance().repeatMode == 0) {
MusicKit.getInstance().repeatMode = 1
}else if(MusicKit.getInstance().repeatMode == 1){
MusicKit.getInstance().repeatMode = 2
}else{
MusicKit.getInstance().repeatMode = 0
}
},
getmaxVolume() {
ipcRenderer.send('wsapi-returnvolumeMax',JSON.stringify(app.cfg.audio.maxVolume));
}
}
export {wsapi}