feat: fallback to unsplash cover when article contains no image
This commit is contained in:
102
lib/helpers.ts
102
lib/helpers.ts
@@ -31,19 +31,54 @@ export const fixRenderedMarkdown = (content: string) => {
|
||||
});
|
||||
};
|
||||
|
||||
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));
|
||||
}
|
||||
type StreamMessage = {
|
||||
type: "info";
|
||||
message: string;
|
||||
} | {
|
||||
type: "error";
|
||||
message: string;
|
||||
} | {
|
||||
type: "warning";
|
||||
message: string;
|
||||
} | {
|
||||
type: "finished";
|
||||
url: string;
|
||||
};
|
||||
|
||||
export async function fetchStream(
|
||||
url: string,
|
||||
cb: (chunk: StreamMessage) => void,
|
||||
init?: RequestInit,
|
||||
) {
|
||||
const res = await fetch(url, init);
|
||||
if (!res.body) return;
|
||||
|
||||
let buffer = "";
|
||||
const reader = res.body
|
||||
.pipeThrough(new TextDecoderStream())
|
||||
.pipeThrough(
|
||||
new TransformStream<string, string>({
|
||||
transform(chunk, controller) {
|
||||
buffer += chunk;
|
||||
let idx;
|
||||
while ((idx = buffer.indexOf("\n")) >= 0) {
|
||||
const line = buffer.slice(0, idx).trim();
|
||||
buffer = buffer.slice(idx + 1);
|
||||
if (line) controller.enqueue(line);
|
||||
}
|
||||
},
|
||||
flush(controller) {
|
||||
const line = buffer.trim();
|
||||
if (line) controller.enqueue(line);
|
||||
},
|
||||
}),
|
||||
)
|
||||
.getReader();
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
cb(JSON.parse(value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,32 +93,53 @@ export function hashString(message: string) {
|
||||
}
|
||||
|
||||
export const createStreamResponse = () => {
|
||||
let controller: ReadableStreamController<ArrayBufferView>;
|
||||
const body = new ReadableStream({
|
||||
start(cont) {
|
||||
controller = cont;
|
||||
const encoder = new TextEncoder();
|
||||
let controller: ReadableStreamDefaultController<Uint8Array>;
|
||||
|
||||
const body = new ReadableStream<Uint8Array>({
|
||||
start(c) {
|
||||
controller = c;
|
||||
},
|
||||
});
|
||||
|
||||
const response = new Response(body, {
|
||||
headers: {
|
||||
"content-type": "text/plain",
|
||||
// newline-delimited JSON
|
||||
"content-type": "application/x-ndjson; charset=utf-8",
|
||||
// prevent intermediaries from buffering/transforming
|
||||
"cache-control": "no-cache, no-transform",
|
||||
"x-content-type-options": "nosniff",
|
||||
// nginx hint to disable proxy buffering
|
||||
"x-accel-buffering": "no",
|
||||
// if you control compression, keep it off for streams
|
||||
// "content-encoding": "identity",
|
||||
},
|
||||
});
|
||||
|
||||
function cancel() {
|
||||
controller.close();
|
||||
const send = (obj: unknown) => {
|
||||
controller.enqueue(encoder.encode(JSON.stringify(obj) + "\n")); // ← delimiter
|
||||
};
|
||||
const cancel = () => controller.close();
|
||||
|
||||
function info(message: string) {
|
||||
return send({ type: "info", message });
|
||||
}
|
||||
|
||||
function enqueue(chunk: string) {
|
||||
controller?.enqueue(new TextEncoder().encode("$" + chunk));
|
||||
function error(message: string) {
|
||||
return send({ type: "error", message });
|
||||
}
|
||||
|
||||
function warning(message: string) {
|
||||
return send({ type: "warning", message });
|
||||
}
|
||||
|
||||
return {
|
||||
response,
|
||||
cancel,
|
||||
enqueue,
|
||||
send,
|
||||
info,
|
||||
error,
|
||||
warning,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user