From acffe78750021f1ab1c56ed5d407bd8f2e8b6e9a Mon Sep 17 00:00:00 2001 From: kxkm Date: Sat, 21 Mar 2026 14:40:54 +0100 Subject: [PATCH] =?UTF-8?q?feat:=203615=20Jai=20pete=20fork=20=E2=80=94=20?= =?UTF-8?q?patched=20API=20URLs,=20bypassed=20features,=20local=20mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All api.opendaw.studio → /api/opendaw (local proxy) Feature checks bypassed (crossOriginIsolated via Express headers) DemoProjects, PublishMusic, UserCounter, ErrorsPage all local --- ai-bridge.js | 73 ++++++++++++++ cert.pem | 19 ++++ key.pem | 28 ++++++ .../app/studio/src/errors/ErrorHandler.ts | 2 +- packages/app/studio/src/features.ts | 16 +--- packages/app/studio/src/ui/App.tsx | 2 - packages/app/studio/src/ui/Footer.tsx | 2 +- .../app/studio/src/ui/ai-panel/AIPanel.sass | 68 ------------- .../app/studio/src/ui/ai-panel/AIPanel.tsx | 95 ------------------- .../studio/src/ui/dashboard/DemoProject.tsx | 2 +- .../studio/src/ui/dashboard/DemoProjects.tsx | 4 +- .../studio/src/ui/info-panel/PublishMusic.ts | 4 +- .../app/studio/src/ui/pages/ErrorsPage.tsx | 4 +- .../studio/src/ui/pages/OpenBundlePage.tsx | 2 +- .../app/studio/src/ui/pages/UsersPage.tsx | 2 +- .../studio/core/src/samples/OpenSampleAPI.ts | 2 +- .../core/src/soundfont/OpenSoundfontAPI.ts | 2 +- serve-studio.js | 36 +++++++ 18 files changed, 172 insertions(+), 191 deletions(-) create mode 100644 ai-bridge.js create mode 100644 cert.pem create mode 100644 key.pem delete mode 100644 packages/app/studio/src/ui/ai-panel/AIPanel.sass delete mode 100644 packages/app/studio/src/ui/ai-panel/AIPanel.tsx create mode 100644 serve-studio.js diff --git a/ai-bridge.js b/ai-bridge.js new file mode 100644 index 00000000..fba6a535 --- /dev/null +++ b/ai-bridge.js @@ -0,0 +1,73 @@ +const http = require("http"); +const { execFileSync } = require("child_process"); +const fs = require("fs"); +const PORT = 8301; +const TTS_URL = "http://127.0.0.1:9100"; +const OLLAMA_URL = "http://127.0.0.1:11434"; + +function readBody(req) { + return new Promise(r => { const c = []; req.on("data", d => c.push(d)); req.on("end", () => r(Buffer.concat(c).toString())); }); +} + +const server = http.createServer(async (req, res) => { + res.setHeader("Access-Control-Allow-Origin", "*"); + res.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS"); + res.setHeader("Access-Control-Allow-Headers", "Content-Type"); + if (req.method === "OPTIONS") { res.writeHead(200); res.end(); return; } + const url = new URL(req.url, `http://localhost:${PORT}`); + + if (url.pathname === "/health") { + res.writeHead(200, {"Content-Type":"application/json"}); + res.end(JSON.stringify({ok:true,service:"ai-bridge",backends:["tts","musicgen","noise","ollama"]})); + return; + } + if (url.pathname === "/generate/music" && req.method === "POST") { + const {prompt="ambient",duration=30,style="experimental"} = JSON.parse(await readBody(req)); + try { + const r = await fetch(`${TTS_URL}/compose`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({prompt:`${prompt}, ${style}`,duration}),signal:AbortSignal.timeout(300000)}); + if (!r.ok) throw new Error(`TTS ${r.status}`); + const buf = Buffer.from(await r.arrayBuffer()); + res.writeHead(200,{"Content-Type":"audio/wav","Content-Length":buf.length}); res.end(buf); + } catch(e) { res.writeHead(500,{"Content-Type":"application/json"}); res.end(JSON.stringify({error:e.message})); } + return; + } + if (url.pathname === "/generate/voice" && req.method === "POST") { + const {text="Bonjour",persona="pharmacius"} = JSON.parse(await readBody(req)); + try { + const r = await fetch(`${TTS_URL}/synthesize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({text,persona}),signal:AbortSignal.timeout(30000)}); + if (!r.ok) throw new Error(`TTS ${r.status}`); + const buf = Buffer.from(await r.arrayBuffer()); + res.writeHead(200,{"Content-Type":"audio/wav","Content-Length":buf.length}); res.end(buf); + } catch(e) { res.writeHead(500,{"Content-Type":"application/json"}); res.end(JSON.stringify({error:e.message})); } + return; + } + if (url.pathname === "/generate/noise" && req.method === "POST") { + const {type="pink",duration=10} = JSON.parse(await readBody(req)); + const types = {white:"anoisesrc=d=DUR:c=white",pink:"anoisesrc=d=DUR:c=pink",brown:"anoisesrc=d=DUR:c=brown",sine:"sine=frequency=220:duration=DUR",drone:"sine=frequency=55:duration=DUR,tremolo=f=0.1:d=0.7"}; + const filter = (types[type]||types.pink).replace(/DUR/g,String(duration)); + const tmp = `/tmp/ai-noise-${Date.now()}.wav`; + try { + execFileSync("ffmpeg",["-f","lavfi","-i",filter,"-t",String(duration),"-ar","44100","-ac","2","-y",tmp],{timeout:15000}); + const buf = fs.readFileSync(tmp); fs.unlinkSync(tmp); + res.writeHead(200,{"Content-Type":"audio/wav","Content-Length":buf.length}); res.end(buf); + } catch(e) { res.writeHead(500,{"Content-Type":"application/json"}); res.end(JSON.stringify({error:e.message})); } + return; + } + if (url.pathname === "/generate/suggest" && req.method === "POST") { + const {tracks=[]} = JSON.parse(await readBody(req)); + const list = tracks.map((t,i)=>`#${i+1} [${t.type}] ${t.name} (${t.duration}s)`).join("\n"); + try { + const r = await fetch(`${OLLAMA_URL}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:"qwen3.5:9b",messages:[{role:"user",content:`Compositeur. Pistes:\n${list}\nSuggere en JSON: {"type":"music|voice|noise","prompt":"...","duration":30}`}],stream:false,options:{num_predict:100},keep_alive:"30m",think:false}),signal:AbortSignal.timeout(15000)}); + const d = await r.json(); + res.writeHead(200,{"Content-Type":"application/json"}); res.end(d.message?.content||'{"type":"noise","prompt":"pink","duration":10}'); + } catch { res.writeHead(200,{"Content-Type":"application/json"}); res.end('{"type":"noise","prompt":"pink","duration":10}'); } + return; + } + if (url.pathname === "/personas") { + try { const r = await fetch("http://localhost:3333/api/personas"); const d = await r.json(); res.writeHead(200,{"Content-Type":"application/json"}); res.end(JSON.stringify(d)); } + catch { res.writeHead(200,{"Content-Type":"application/json"}); res.end('{"data":[]}'); } + return; + } + res.writeHead(404); res.end("Not found"); +}); +server.listen(PORT, () => console.log(`[ai-bridge] :${PORT}`)); diff --git a/cert.pem b/cert.pem new file mode 100644 index 00000000..d9bc21bc --- /dev/null +++ b/cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDCTCCAfGgAwIBAgIUVin7hraN4CoyLlfzFCwtKgQiLZEwDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI2MDMyMTEyMTI0NFoXDTI3MDMy +MTEyMTI0NFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAnzO332ld3+3A7lGiX29bryZAJDzmEJFVdlH93/2AJ4eQ +iTSgeYVg4Wn5ynTm34tCFORNEERF5sgZ+YYWzYITM/cXySF4vFErs3aROGGBGl8C +jqDuL1FYs9UL9IUjVD5eNGVnrAKu34fi+OAkopN+l3jSr7SL9Y6eJ+Dx358PvQnJ +dDVCOa/vEj38z1f+QLJ2KiEtdMiLhlKOWcX6Mvzx0VQcf0rMK3R8IporBBQGY3s1 +8lYgLJ1cBuR8zKR1cdHhvltz0xG06UvvvoTA6RJDHJeYmoE2qpwfdtg01+sl1ZKG +YtGQn+nP5wLmLWTiIuoaje5Aq7MJTWYDRG55p2hl9QIDAQABo1MwUTAdBgNVHQ4E +FgQUG+YDZltxNukp8Xv1y+c9P58fCKEwHwYDVR0jBBgwFoAUG+YDZltxNukp8Xv1 +y+c9P58fCKEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEASprF +lqq2v84HBTTJtjrk4Wj9dhEWOlQ/uN03kcW3/kfv+cTAy0oDS1WsBVD7V+Z0TtPc +H40APr/nVwpOwvFgkJSYzEmIdTJGm6XO6DZc9vxzScUWernbJiRrrq6trpSjHtHA +7mVCHDDBy0QZusyF3G2qKpMtprpE9bgts5ctnCtPmHw0KS8OANA5s+tdhFVQ5Cl8 +KMqlw9Yk2gN61ASj0Kdy8U6RdCruTWcU7yO3dCnA2v5NtMVxaZx5Qwgt7lW8CFWD +VNmgoQVPYvTNeSh6RmCSlnFI07Frl2yIWzpVK0ak/izuy54LtPt+lhjEQBnKVzMT +L6G6H6UmUlV9KI+JCQ== +-----END CERTIFICATE----- diff --git a/key.pem b/key.pem new file mode 100644 index 00000000..88379596 --- /dev/null +++ b/key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCfM7ffaV3f7cDu +UaJfb1uvJkAkPOYQkVV2Uf3f/YAnh5CJNKB5hWDhafnKdObfi0IU5E0QREXmyBn5 +hhbNghMz9xfJIXi8USuzdpE4YYEaXwKOoO4vUViz1Qv0hSNUPl40ZWesAq7fh+L4 +4CSik36XeNKvtIv1jp4n4PHfnw+9Ccl0NUI5r+8SPfzPV/5AsnYqIS10yIuGUo5Z +xfoy/PHRVBx/SswrdHwimisEFAZjezXyViAsnVwG5HzMpHVx0eG+W3PTEbTpS+++ +hMDpEkMcl5iagTaqnB922DTX6yXVkoZi0ZCf6c/nAuYtZOIi6hqN7kCrswlNZgNE +bnmnaGX1AgMBAAECggEAGqrtKA1cXkBTSxh9nILo1sQPhoJ0/1149WLypeyu5PA+ +c91O7Ew9WEUKRLGkYtnO7fT4O7tUAj6Oq0+czjv1v4SqhVSIBvOJ4HwCA3HB0IsN +wJqQi3iS5TzQzdrnehsgaKJ75UJlzKfUi1/wgxM152Wj1S7nKvEC6SczHQRl/II9 +gIpUbWK4EqB/3n2wuqjKRK8t+wOHAWE4aR+2bNgMF4OeOcnVZzANrxIgcrLXb1VX +rzJxPq3GzgyrONtOJR83eC8strrtY6HHx4h5U3zHqFeQZaOZZxNNuZ4dWbtUU6YR +Ui1RH/iD1oZwmrN1Kvj+16pscVlD5h4YaUlEqWkigQKBgQDRcuGymYjemM9Vm0PS +2beEo5/Yan2Qm5nQoBlxfTt8R7n33dC+Rq8OjszGIQ1pzmEMnCUTjppAhpo8Orh/ +8tYenRKfDnReB88mdFwRdtRT9QpoxlUnhgefr9Nt0pceJKFW5mf3rItPaoZbWDDi +VTr382T7jsqrfkltaLwB8we70QKBgQDClevcTmowq5onz1lyPQQ/5AdbPRgM2gUM +BhC5eyqbjuz6w1XJxvtSdDt5A/gmiSUBKww9s6Dx50fYEEzlxNEHANsFyZ0Cl3HL +E/V0up9t2IRa2SmcEcSohKfNyXmSptvlRy7NY6MQtzK7xuIyrSBg2deS6WDx8rJ4 +Tbx/AtQk5QKBgGvbojiL8jzXKmYmR74MGgJ3FHipFjJivsn9uDyGRhzQ/waSts1V +rw5rIS6ilLq46adJvZodpHe4GTMM9jgFgJhRQOah1fEaSmW+JC9mJx95H7wm7nEl +B0YHkHSMrv9504/hpQaZV0A1Vi0x9DKJu402TPlyZhH8PanW9oJ2ebQRAoGBAI13 +LXiHUdB8kaBrsN3K2nIQuABvFDMTtiOdrT8L+lgJuuQ8avw9O64gbrR2XEy33D1T +LJ0XIOV9fc/pWd6aDlWHG7TDdPDTZZl8szDgFNRpWyApTYT3rf/OIakM1uP1h1zT +OYV88KpQZgP6fazWDPocpgtavG5qQbIQ4f988Pq5AoGAbxyCVTH//OrqfDNSlNTM +ta4TT1A4sDtSizCFHvfEkAoPB8PeMTMG2PC59JYQ7b6bL+ABabyTG1WAMh9EmrxZ +LH3TzVWmFXO/lxPAe1BT7mR/Z+EWxn+EWbzgAEdsXfhL3thMfYSu9de+zVZONfCO +KLPp3E3Yd+RI+6ssiQSBbA0= +-----END PRIVATE KEY----- diff --git a/packages/app/studio/src/errors/ErrorHandler.ts b/packages/app/studio/src/errors/ErrorHandler.ts index c144f1ca..0433ef1d 100644 --- a/packages/app/studio/src/errors/ErrorHandler.ts +++ b/packages/app/studio/src/errors/ErrorHandler.ts @@ -178,7 +178,7 @@ export class ErrorHandler { error: {...error, stack: error.stack?.slice(0, maxStackSize)}, logs: LogBuffer.get() } satisfies ErrorLog) - fetch("https://logs.opendaw.studio/log.php", { + fetch("/api/opendaw-logs/log.php", { method: "POST", headers: {"Content-Type": "application/json"}, body diff --git a/packages/app/studio/src/features.ts b/packages/app/studio/src/features.ts index 7714495a..2c1706be 100644 --- a/packages/app/studio/src/features.ts +++ b/packages/app/studio/src/features.ts @@ -1,14 +1,4 @@ -import {requireProperty} from "@opendaw/lib-std" - export const testFeatures = async (): Promise => { - requireProperty(Promise, "withResolvers") - requireProperty(Array.prototype, "toSorted") - requireProperty(window, "indexedDB") - requireProperty(window, "AudioWorkletNode") - requireProperty(window, "SharedArrayBuffer") - requireProperty(navigator, "storage") - requireProperty(navigator.storage, "getDirectory") - requireProperty(crypto, "randomUUID") - requireProperty(crypto, "subtle") - requireProperty(crypto.subtle, "digest") -} \ No newline at end of file + // Feature checks bypassed for 3615 J'ai pete + console.log("[3615] Feature checks bypassed") +} diff --git a/packages/app/studio/src/ui/App.tsx b/packages/app/studio/src/ui/App.tsx index 192a219c..16c1691d 100644 --- a/packages/app/studio/src/ui/App.tsx +++ b/packages/app/studio/src/ui/App.tsx @@ -19,7 +19,6 @@ import {UsersPage} from "@/ui/pages/UsersPage" import {PrivacyPage} from "@/ui/pages/PrivacyPage" import {PreferencesPage} from "@/ui/pages/PreferencesPage" import {TestPage} from "@/ui/pages/TestPage" -import {AIPanel} from "@/ui/ai-panel/AIPanel" export const App = (service: StudioService) => { const terminator = new Terminator() @@ -55,7 +54,6 @@ export const App = (service: StudioService) => { ]} />