feat: add docs

This commit is contained in:
Soph :3 2025-08-14 11:19:49 +03:00
parent 55840b54e5
commit 5ee0962917
3 changed files with 189 additions and 9 deletions

24
index.html Normal file
View file

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<title>Redoc</title>
<!-- needed for adaptive design -->
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
<!--
Redoc doesn't change outer page styles
-->
<style>
body {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<redoc spec-url='/openapi.json'></redoc>
<script src="https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"> </script>
</body>
</html>

View file

@ -3,13 +3,25 @@ const app = new Hono()
const joypixels = require("emoji-toolkit");
import { mkdirSync, writeFileSync, existsSync, rmSync, readFileSync, readFile, writeFile } from 'fs'
import { $ } from 'bun';
import { cors } from 'hono/cors'
import ytSearch from 'yt-search';
import { writeMDFPWMv3 } from './mdfpwmWriter';
let ytdlpPath = "yt-dlp"
if(existsSync("./yt-dlp")) {
ytdlpPath = "./yt-dlp"
}
const openApi = JSON.parse(readFileSync("openapi.json").toString("utf8"))
const index = readFileSync("index.html").toString("utf8")
app.use("*", cors())
app.get("/", async (c) => {
return c.html(index)
})
app.get("/openapi.json", async (c) => {
return c.json(openApi);
})
app.post("/youtube", async (c) => {
const auth = c.req.header('Authorization')
if (auth !== process.env.PASSWORD) {
@ -62,14 +74,14 @@ app.post("/youtube", async (c) => {
rmSync("test.opus")
}
}
return c.body(readFileSync(filename), {
headers: {
'Content-Type': `audio/vnd.${body.mdfpwm ? "m" : ""}dfpwm`,
'Content-Disposition': `attachment; filename="${filename}"`,
},
});
})
app.post('/render', async (c) => {
const auth = c.req.header('Authorization')
@ -100,7 +112,7 @@ app.post('/render', async (c) => {
size = { width: w, height: h };
extraArgs += ` -W ${w} -H ${h}`;
}
}
if(body?.extraArgs) {
if (typeof body.extraArgs !== 'string') {
return c.text('extraArgs must be a string', 400)
@ -159,17 +171,17 @@ app.post('/render', async (c) => {
if (!existsSync('cache')) {
mkdirSync('cache');
}
writeFileSync(filename, buffer);
}
} else {
return c.text('Please provide either emoji or imageUrl', 400);
}
if(!existsSync(filename+".bimg")) {
let sanjuuniPath = "sanjuuni";
if(existsSync("./sanjuuni")) {
sanjuuniPath = "./sanjuuni";
}
@ -182,8 +194,8 @@ app.post('/render', async (c) => {
return c.text(bimg)
})
export default {
export default {
port: 7427,
host: "127.0.0.1",
fetch: app.fetch,
}
fetch: app.fetch,
}

144
openapi.json Normal file
View file

@ -0,0 +1,144 @@
{
"openapi": "3.1.0",
"info": {
"title": "Sophie's convert API",
"version": "1.0.0",
"description": "API for use in Computercraft to download, convert and render media. Supports emojis, image rendering, and youtube audio."
},
"servers": [
{
"url": "http://127.0.0.1:7427"
}
],
"paths": {
"/youtube": {
"post": {
"summary": "Download and convert YouTube audio",
"description": "Downloads a YouTube video matching the given `title` and `artist` and converts it to either DFPWM or MDFPWM format.",
"security": [
{ "bearerAuth": [] }
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["title", "artist"],
"properties": {
"title": {
"type": "string",
"description": "Song title",
"example": "Never Gonna Give You Up"
},
"artist": {
"type": "string",
"description": "Artist name",
"example": "Rick Astley"
},
"mdfpwm": {
"type": "boolean",
"description": "If true, produces MDFPWM instead of DFPWM",
"example": true
}
}
}
}
}
},
"responses": {
"200": {
"description": "Audio file generated successfully",
"content": {
"audio/vnd.dfpwm": {
"schema": {
"type": "string",
"format": "binary"
}
},
"audio/vnd.mdfpwm": {
"schema": {
"type": "string",
"format": "binary"
}
}
}
},
"400": { "description": "Missing or invalid parameters" },
"401": { "description": "Unauthorized" },
"500": { "description": "Internal server error" }
}
}
},
"/render": {
"post": {
"summary": "Render an emoji or image to bimg format",
"description": "Renders an emoji (via JoyPixels) or arbitrary image to `.bimg` format using the `sanjuuni` tool. Optionally resizes the image before conversion. You cannot use both emojis and image URLs at the same time.",
"security": [
{ "bearerAuth": [] }
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"size": {
"type": "string",
"pattern": "^\\d{1,4}x\\d{1,4}$",
"description": "Desired image size in format `<width>x<height>` (max 1000x1000)",
"example": "64x64"
},
"extraArgs": {
"type": "string",
"description": "Extra command-line arguments to pass to sanjuuni",
"example": "-b -W 128 -H 128"
},
"emoji": {
"type": "string",
"description": "Unicode emoji to render",
"example": "🔥"
},
"imageUrl": {
"type": "string",
"format": "uri",
"description": "URL of an image to render",
"example": "https://example.com/image.png"
}
},
"oneOf": [
{ "required": ["emoji"] },
{ "required": ["imageUrl"] }
]
}
}
}
},
"responses": {
"200": {
"description": "Rendered `.bimg` output",
"content": {
"text/plain": {
"schema": { "type": "string" }
}
}
},
"400": { "description": "Missing parameters or invalid size format" },
"401": { "description": "Unauthorized" },
"500": { "description": "Internal server error" }
}
}
}
},
"components": {
"securitySchemes": {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "token",
"description": "Use the `Authorization` header with your API password:\n```\nAuthorization: <PASSWORD>\n```"
}
}
}
}