131 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			131 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| export function json(content: unknown) {
 | |
|   const headers = new Headers();
 | |
|   headers.append("Content-Type", "application/json");
 | |
|   return new Response(JSON.stringify(content), {
 | |
|     headers,
 | |
|   });
 | |
| }
 | |
| 
 | |
| export const isValidUrl = (urlString: string) => {
 | |
|   try {
 | |
|     return Boolean(new URL(urlString));
 | |
|   } catch (_e) {
 | |
|     return false;
 | |
|   }
 | |
| };
 | |
| 
 | |
| export const fixRenderedMarkdown = (content: string) => {
 | |
|   return content.replace("***\n", "---")
 | |
|     .replace("----------------", "---")
 | |
|     .replace("\n---", "---")
 | |
|     .replace(/^(date:[^'\n]*)'|'/gm, (match, p1, p2) => {
 | |
|       if (p1) {
 | |
|         // This is a line starting with date: followed by single quotes
 | |
|         return p1.replace(/'/gm, "");
 | |
|       } else if (p2) {
 | |
|         return "";
 | |
|       } else {
 | |
|         // This is a line with single quotes, but not starting with date:
 | |
|         return match;
 | |
|       }
 | |
|     });
 | |
| };
 | |
| 
 | |
| export async function fetchStream(url: string, cb: (chunk: string) => void) {
 | |
|   const response = await fetch(url);
 | |
|   const reader = response?.body?.getReader();
 | |
|   if (reader) {
 | |
|     while (true) {
 | |
|       const { done, value } = await reader.read();
 | |
|       if (done) return;
 | |
|       const data = new TextDecoder().decode(value);
 | |
|       data
 | |
|         .split("$")
 | |
|         .filter((d) => d && d.length)
 | |
|         .map((d) => cb(Array.isArray(d) ? d[0] : d));
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| export function hashString(message: string) {
 | |
|   let hash = 0;
 | |
|   for (let i = 0; i < message.length; i++) {
 | |
|     const char = message.charCodeAt(i);
 | |
|     hash = ((hash << 5) - hash) + char;
 | |
|     hash = hash & hash; // Convert to 32bit integer
 | |
|   }
 | |
|   return hash;
 | |
| }
 | |
| 
 | |
| export const createStreamResponse = () => {
 | |
|   let controller: ReadableStreamController<ArrayBufferView>;
 | |
|   const body = new ReadableStream({
 | |
|     start(cont) {
 | |
|       controller = cont;
 | |
|     },
 | |
|   });
 | |
| 
 | |
|   const response = new Response(body, {
 | |
|     headers: {
 | |
|       "content-type": "text/plain",
 | |
|       "x-content-type-options": "nosniff",
 | |
|     },
 | |
|   });
 | |
| 
 | |
|   function cancel() {
 | |
|     controller.close();
 | |
|   }
 | |
| 
 | |
|   function enqueue(chunk: string) {
 | |
|     controller?.enqueue(new TextEncoder().encode("$" + chunk));
 | |
|   }
 | |
| 
 | |
|   return {
 | |
|     response,
 | |
|     cancel,
 | |
|     enqueue,
 | |
|   };
 | |
| };
 | |
| 
 | |
| export type StreamResponse = ReturnType<typeof createStreamResponse>;
 | |
| 
 | |
| export function debounce<T extends (...args: Parameters<T>) => void>(
 | |
|   this: ThisParameterType<T>,
 | |
|   fn: T,
 | |
|   delay = 300,
 | |
| ) {
 | |
|   let timer: ReturnType<typeof setTimeout> | undefined;
 | |
|   return (...args: Parameters<T>) => {
 | |
|     clearTimeout(timer);
 | |
|     timer = setTimeout(() => fn.apply(this, args), delay);
 | |
|   };
 | |
| }
 | |
| 
 | |
| export function parseRating(rating: string | number) {
 | |
|   if (typeof rating === "string") {
 | |
|     return [...rating.matchAll(/⭐/g)].length;
 | |
|   }
 | |
|   return rating;
 | |
| }
 | |
| 
 | |
| export async function convertOggToMp3(
 | |
|   oggData: ArrayBuffer,
 | |
| ): Promise<Uint8Array> {
 | |
|   const ffmpeg = new Deno.Command("ffmpeg", {
 | |
|     args: ["-f", "ogg", "-i", "pipe:0", "-f", "mp3", "pipe:1"],
 | |
|     stdin: "piped",
 | |
|     stdout: "piped",
 | |
|     stderr: "null",
 | |
|   });
 | |
| 
 | |
|   const process = ffmpeg.spawn();
 | |
|   const writer = process.stdin.getWriter();
 | |
|   await writer.write(new Uint8Array(oggData));
 | |
|   await writer.close();
 | |
| 
 | |
|   const output = await process.output();
 | |
|   const { code } = await process.status;
 | |
|   if (code !== 0) throw new Error(`FFmpeg exited with code ${code}`);
 | |
|   return output.stdout;
 | |
| }
 |