use new runtime api

This commit is contained in:
SrGooglo 2025-03-06 03:51:05 +00:00
parent 3e24dfec7a
commit 0c49d7fcf8
4 changed files with 223 additions and 446 deletions

View File

@ -5,20 +5,17 @@ import NotificationFeedback from "./feedback"
export default class NotificationCore extends Core { export default class NotificationCore extends Core {
static namespace = "notifications" static namespace = "notifications"
static depenpencies = [ static depenpencies = ["api", "settings"]
"api",
"settings",
]
listenSockets = { listenSockets = {
"notifications": { notifications: {
"notification.new": (data) => { "notification.new": (data) => {
this.new(data) this.new(data)
}, },
"notification.broadcast": (data) => { "notification.broadcast": (data) => {
this.new(data) this.new(data)
}, },
} },
} }
public = { public = {
@ -27,7 +24,7 @@ export default class NotificationCore extends Core {
} }
async onInitialize() { async onInitialize() {
this.ctx.CORES.api.registerSocketListeners(this.listenSockets) this.ctx.cores.get("api").registerSocketListeners(this.listenSockets)
} }
async new(notification) { async new(notification) {

View File

@ -1,11 +1,7 @@
import AudioPlayerStorage from "../player.storage" import AudioPlayerStorage from "../player.storage"
export default class Presets { export default class Presets {
constructor({ constructor({ storage_key, defaultPresetValue, onApplyValues }) {
storage_key,
defaultPresetValue,
onApplyValues,
}) {
if (!storage_key) { if (!storage_key) {
throw new Error("storage_key is required") throw new Error("storage_key is required")
} }
@ -18,9 +14,11 @@ export default class Presets {
} }
get presets() { get presets() {
return AudioPlayerStorage.get(`${this.storage_key}_presets`) ?? { return (
default: this.defaultPresetValue AudioPlayerStorage.get(`${this.storage_key}_presets`) ?? {
default: this.defaultPresetValue,
} }
)
} }
set presets(presets) { set presets(presets) {
@ -36,7 +34,10 @@ export default class Presets {
} }
get currentPresetKey() { get currentPresetKey() {
return AudioPlayerStorage.get(`${this.storage_key}_current-key`) ?? "default" return (
AudioPlayerStorage.get(`${this.storage_key}_current-key`) ??
"default"
)
} }
get currentPresetValues() { get currentPresetValues() {
@ -126,14 +127,14 @@ export default class Presets {
async setCurrentPresetToDefault() { async setCurrentPresetToDefault() {
return await new Promise((resolve) => { return await new Promise((resolve) => {
app.layout.modal.confirm.confirm({ app.layout.modal.confirm({
title: "Reset to default values?", title: "Reset to default values?",
content: "Are you sure you want to reset to default values?", content: "Are you sure you want to reset to default values?",
onOk: () => { onConfirm: () => {
this.setToCurrent(this.defaultPresetValue) this.setToCurrent(this.defaultPresetValue)
resolve(this.currentPresetValues) resolve(this.currentPresetValues)
} },
}) })
}) })
} }

View File

@ -1,230 +0,0 @@
import { EventBus } from "@ragestudio/vessel"
export default class ChunkedUpload {
constructor(params) {
const {
endpoint,
file,
headers = {},
splitChunkSize = 1024 * 1024 * 10,
maxRetries = 3,
delayBeforeRetry = 5,
} = params
if (!endpoint) {
throw new Error("Missing endpoint")
}
if ((!file) instanceof File) {
throw new Error("Invalid or missing file")
}
if (typeof headers !== "object") {
throw new Error("Invalid headers")
}
if (splitChunkSize <= 0) {
throw new Error("Invalid splitChunkSize")
}
this.chunkCount = 0
this.retriesCount = 0
this.splitChunkSize = splitChunkSize
this.totalChunks = Math.ceil(file.size / splitChunkSize)
this.maxRetries = maxRetries
this.delayBeforeRetry = delayBeforeRetry
this.offline = this.paused = false
this.endpoint = endpoint
this.file = file
this.headers = {
...headers,
"uploader-original-name": encodeURIComponent(file.name),
"uploader-file-id": this.getFileUID(file),
"uploader-chunks-total": this.totalChunks,
"chunk-size": splitChunkSize,
Connection: "keep-alive",
"Cache-Control": "no-cache",
}
this.setupListeners()
this.nextSend()
console.debug("[Uploader] Created", {
splitChunkSize: splitChunkSize,
totalChunks: this.totalChunks,
totalSize: file.size,
})
}
_reader = new FileReader()
events = new EventBus()
setupListeners() {
window.addEventListener(
"online",
() =>
!this.offline &&
((this.offline = false),
this.events.emit("online"),
this.nextSend()),
)
window.addEventListener(
"offline",
() => ((this.offline = true), this.events.emit("offline")),
)
}
getFileUID(file) {
return (
Math.floor(Math.random() * 100000000) +
Date.now() +
file.size +
"_tmp"
)
}
loadChunk() {
return new Promise((resolve) => {
const start = this.chunkCount * this.splitChunkSize
const end = Math.min(start + this.splitChunkSize, this.file.size)
this._reader.onload = () =>
resolve(
new Blob([this._reader.result], {
type: "application/octet-stream",
}),
)
this._reader.readAsArrayBuffer(this.file.slice(start, end))
})
}
async sendChunk() {
const form = new FormData()
form.append("file", this.chunk)
this.headers["uploader-chunk-number"] = this.chunkCount
console.log(`[UPLOADER] Sending chunk ${this.chunkCount}`, {
currentChunk: this.chunkCount,
totalChunks: this.totalChunks,
})
try {
const res = await fetch(this.endpoint, {
method: "POST",
headers: this.headers,
body: form,
})
return res
} catch (error) {
this.manageRetries()
}
}
manageRetries() {
if (++this.retriesCount < this.maxRetries) {
setTimeout(() => this.nextSend(), this.delayBeforeRetry * 1000)
this.events.emit("fileRetry", {
message: `Retrying chunk ${this.chunkCount}`,
chunk: this.chunkCount,
retriesLeft: this.retries - this.retriesCount,
})
} else {
this.events.emit("error", {
message: `No more retries for chunk ${this.chunkCount}`,
})
}
}
async nextSend() {
if (this.paused || this.offline) {
return null
}
this.chunk = await this.loadChunk()
const res = await this.sendChunk()
const data = await res.json()
if ([200, 201, 204].includes(res.status)) {
console.log(`[UPLOADER] Chunk ${this.chunkCount} sent`)
this.chunkCount = this.chunkCount + 1
if (this.chunkCount < this.totalChunks) {
this.nextSend()
}
// check if is the last chunk, if so, handle sse events
if (this.chunkCount === this.totalChunks) {
if (data.eventChannelURL) {
console.log(
`[UPLOADER] Connecting to SSE channel >`,
data.eventChannelURL,
)
const eventSource = new EventSource(data.eventChannelURL)
eventSource.onerror = (error) => {
this.events.emit("error", error)
}
eventSource.onopen = () => {
console.log(`[UPLOADER] SSE channel opened`)
}
eventSource.onmessage = (event) => {
// parse json
const messageData = JSON.parse(event.data)
console.log(`[UPLOADER] SSE Event >`, messageData)
if (messageData.status === "done") {
this.events.emit("finish", messageData.result)
eventSource.close()
}
if (messageData.status === "error") {
this.events.emit("error", messageData.result)
}
if (messageData.status === "progress") {
this.events.emit("progress", {
percentProgress: messageData.progress,
})
}
}
} else {
this.events.emit("finish", data)
}
}
this.events.emit("progress", {
percentProgress: Math.round(
(100 / this.totalChunks) * this.chunkCount,
),
})
} else if ([408, 502, 503, 504].includes(res.status)) {
this.manageRetries()
} else {
this.events.emit("error", {
message: `[${res.status}] ${data.error ?? data.message}`,
})
}
}
togglePause() {
this.paused = !this.paused
if (!this.paused) {
return this.nextSend()
}
}
}

View File

@ -1,6 +1,6 @@
import { Core } from "@ragestudio/vessel" import { Core } from "@ragestudio/vessel"
import ChunkedUpload from "./chunkedUpload" import ChunkedUpload from "@classes/ChunkedUpload"
import SessionModel from "@models/session" import SessionModel from "@models/session"
export default class RemoteStorage extends Core { export default class RemoteStorage extends Core {
@ -31,7 +31,9 @@ export default class RemoteStorage extends Core {
const buffer = await file.arrayBuffer() const buffer = await file.arrayBuffer()
const hash = await crypto.subtle.digest("SHA-256", buffer) const hash = await crypto.subtle.digest("SHA-256", buffer)
const hashArray = Array.from(new Uint8Array(hash)) const hashArray = Array.from(new Uint8Array(hash))
const hashHex = hashArray.map(b => b.toString(16).padStart(2, "0")).join("") const hashHex = hashArray
.map((b) => b.toString(16).padStart(2, "0"))
.join("")
return hashHex return hashHex
} }
@ -39,15 +41,16 @@ export default class RemoteStorage extends Core {
async uploadFile( async uploadFile(
file, file,
{ {
onProgress = () => { }, onProgress = () => {},
onFinish = () => { }, onFinish = () => {},
onError = () => { }, onError = () => {},
service = "standard", service = "standard",
headers = {}, headers = {},
} = {}, } = {},
) { ) {
return await new Promise((_resolve, _reject) => { return await new Promise((_resolve, _reject) => {
const fn = async () => new Promise((resolve, reject) => { const fn = async () =>
new Promise((resolve, reject) => {
const uploader = new ChunkedUpload({ const uploader = new ChunkedUpload({
endpoint: `${app.cores.api.client().mainOrigin}/upload/chunk`, endpoint: `${app.cores.api.client().mainOrigin}/upload/chunk`,
splitChunkSize: 5 * 1024 * 1024, splitChunkSize: 5 * 1024 * 1024,
@ -56,19 +59,22 @@ export default class RemoteStorage extends Core {
headers: { headers: {
...headers, ...headers,
"provider-type": service, "provider-type": service,
"Authorization": `Bearer ${SessionModel.token}`, Authorization: `Bearer ${SessionModel.token}`,
}, },
}) })
uploader.events.on("error", ({ message }) => { uploader.events.on("error", ({ message }) => {
this.console.error("[Uploader] Error", message) this.console.error("[Uploader] Error", message)
app.cores.notifications.new({ app.cores.notifications.new(
{
title: "Could not upload file", title: "Could not upload file",
description: message description: message,
}, { },
type: "error" {
}) type: "error",
},
)
if (typeof onError === "function") { if (typeof onError === "function") {
onError(file, message) onError(file, message)
@ -87,11 +93,14 @@ export default class RemoteStorage extends Core {
uploader.events.on("finish", (data) => { uploader.events.on("finish", (data) => {
this.console.debug("[Uploader] Finish", data) this.console.debug("[Uploader] Finish", data)
app.cores.notifications.new({ app.cores.notifications.new(
{
title: "File uploaded", title: "File uploaded",
}, { },
type: "success" {
}) type: "success",
},
)
if (typeof onFinish === "function") { if (typeof onFinish === "function") {
onFinish(file, data) onFinish(file, data)