refactor: live streams
This commit is contained in:
parent
9049f337b0
commit
c74af0159c
@ -25,7 +25,7 @@ async function tryUseCacheRelay(url: string) {
|
||||
localStorage.setItem("cache-relay", url);
|
||||
return conn;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
console.warn(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ export async function initRelayWorker() {
|
||||
}
|
||||
|
||||
try {
|
||||
await workerRelay.debug("");
|
||||
await workerRelay.debug("*");
|
||||
await workerRelay.init({
|
||||
databasePath: "relay.db",
|
||||
insertBatchSize: 100,
|
||||
|
@ -5,7 +5,6 @@ import { CSSProperties, useMemo } from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import useFollowsControls from "@/Hooks/useFollowControls";
|
||||
import useImgProxy from "@/Hooks/useImgProxy";
|
||||
import { findTag } from "@/Utils";
|
||||
import { Hour } from "@/Utils/Const";
|
||||
@ -13,21 +12,16 @@ import { Hour } from "@/Utils/Const";
|
||||
import Avatar from "../User/Avatar";
|
||||
|
||||
export function LiveStreams() {
|
||||
const { followList } = useFollowsControls();
|
||||
const sub = useMemo(() => {
|
||||
const rb = new RequestBuilder("follows:streams");
|
||||
if (followList.length > 0) {
|
||||
rb.withFilter()
|
||||
.kinds([EventKind.LiveEvent])
|
||||
.authors(followList)
|
||||
.since(unixNow() - Hour);
|
||||
rb.withFilter()
|
||||
.kinds([EventKind.LiveEvent])
|
||||
.tag("p", followList)
|
||||
.since(unixNow() - Hour);
|
||||
}
|
||||
const rb = new RequestBuilder("streams");
|
||||
rb.withFilter()
|
||||
.kinds([EventKind.LiveEvent])
|
||||
.since(unixNow() - Hour);
|
||||
rb.withFilter()
|
||||
.kinds([EventKind.LiveEvent])
|
||||
.since(unixNow() - Hour);
|
||||
return rb;
|
||||
}, [followList.length]);
|
||||
}, []);
|
||||
|
||||
const streams = useRequestBuilder(sub);
|
||||
if (streams.length === 0) return null;
|
||||
@ -72,16 +66,22 @@ function LiveStreamEvent({ ev }: { ev: NostrEvent }) {
|
||||
backgroundImage: `url(${imageProxy})`,
|
||||
} as CSSProperties
|
||||
}></div>
|
||||
<div className="absolute left-0 top-7 w-full overflow-hidden">
|
||||
<div className="whitespace-nowrap px-2 text-ellipsis overflow-hidden text-xs">{title}</div>
|
||||
<div className="absolute left-0 top-0 w-full overflow-hidden">
|
||||
<div
|
||||
className="whitespace-nowrap px-1 text-ellipsis overflow-hidden text-xs font-medium bg-background opacity-70 text-center"
|
||||
title={title}>
|
||||
{title}
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute top-1 left-1 bg-heart rounded-md px-2 uppercase font-bold">{status}</div>
|
||||
<div className="absolute right-1 top-1">
|
||||
<div className="absolute bottom-1 left-1 bg-heart rounded-md px-2 uppercase font-bold">{status}</div>
|
||||
<div className="absolute right-1 bottom-1">
|
||||
<Avatar pubkey={host} user={hostProfile} size={25} className="outline outline-2 outline-highlight" />
|
||||
</div>
|
||||
<div className="absolute left-1 bottom-1 rounded-md px-2 py-1 text-xs bg-gray font-medium">
|
||||
<FormattedMessage defaultMessage="{n} viewers" values={{ n: viewers }} />
|
||||
</div>
|
||||
{viewers && (
|
||||
<div className="absolute left-1 bottom-7 rounded-md px-2 py-1 text-xs bg-gray font-medium">
|
||||
<FormattedMessage defaultMessage="{n} viewers" values={{ n: viewers }} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
|
@ -28,11 +28,11 @@ export default function useLoginFeed() {
|
||||
}, [login, publisher, system]);
|
||||
|
||||
const subLogin = useMemo(() => {
|
||||
const b = new RequestBuilder(`login:sub`);
|
||||
b.withOptions({
|
||||
leaveOpen: true,
|
||||
});
|
||||
if (CONFIG.features.subscriptions && !login.readonly) {
|
||||
const b = new RequestBuilder(`login`);
|
||||
b.withOptions({
|
||||
leaveOpen: true,
|
||||
});
|
||||
if (pubKey) {
|
||||
b.withFilter()
|
||||
.relay("wss://relay.snort.social/")
|
||||
@ -41,8 +41,8 @@ export default function useLoginFeed() {
|
||||
.tag("p", [pubKey])
|
||||
.limit(10);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
return b;
|
||||
}, [pubKey, login]);
|
||||
|
||||
const loginFeed = useRequestBuilder(subLogin);
|
||||
|
@ -18,7 +18,7 @@
|
||||
"dist"
|
||||
],
|
||||
"dependencies": {
|
||||
"@sqlite.org/sqlite-wasm": "^3.45.3-build3",
|
||||
"@sqlite.org/sqlite-wasm": "^3.46.1-build3",
|
||||
"eventemitter3": "^5.0.1",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
|
@ -64,7 +64,6 @@ export class WorkerRelayInterface {
|
||||
}
|
||||
|
||||
async delete(req: ReqCommand) {
|
||||
console.debug("DELETE", req);
|
||||
return await this.#workerRpc<ReqCommand, Array<string>>("delete", req);
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ export interface WorkQueueItem {
|
||||
reject(e: unknown): void;
|
||||
}
|
||||
|
||||
export async function processWorkQueue(queue?: Array<WorkQueueItem>, queueDelay = 200) {
|
||||
export async function processWorkQueue(queue?: Array<WorkQueueItem>) {
|
||||
while (queue && queue.length > 0) {
|
||||
const v = queue.shift();
|
||||
if (v) {
|
||||
@ -16,7 +16,6 @@ export async function processWorkQueue(queue?: Array<WorkQueueItem>, queueDelay
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(() => processWorkQueue(queue, queueDelay), queueDelay);
|
||||
}
|
||||
|
||||
export const barrierQueue = async <T>(queue: Array<WorkQueueItem>, then: () => Promise<T>): Promise<T> => {
|
||||
|
Binary file not shown.
@ -24,22 +24,20 @@ let eventWriteQueue: Array<NostrEvent> = [];
|
||||
async function insertBatch() {
|
||||
// Only insert event batches when the command queue is empty
|
||||
// This is to make req's execute first and not block them
|
||||
if (eventWriteQueue.length > 0 && cmdQueue.length === 0) {
|
||||
await barrierQueue(cmdQueue, async () => {
|
||||
const start = unixNowMs();
|
||||
const timeLimit = 1000;
|
||||
if (relay) {
|
||||
while (eventWriteQueue.length > 0) {
|
||||
if (unixNowMs() - start >= timeLimit) {
|
||||
//console.debug("Yield insert, queue length: ", eventWriteQueue.length, ", cmds: ", cmdQueue.length);
|
||||
break;
|
||||
}
|
||||
const batch = eventWriteQueue.splice(0, insertBatchSize);
|
||||
eventWriteQueue = eventWriteQueue.slice(batch.length);
|
||||
relay.eventBatch(batch);
|
||||
if (eventWriteQueue.length > 0) {
|
||||
const start = unixNowMs();
|
||||
const timeLimit = 1000;
|
||||
if (relay) {
|
||||
while (eventWriteQueue.length > 0) {
|
||||
if (unixNowMs() - start >= timeLimit) {
|
||||
//console.debug("Yield insert, queue length: ", eventWriteQueue.length, ", cmds: ", cmdQueue.length);
|
||||
break;
|
||||
}
|
||||
const batch = eventWriteQueue.splice(0, insertBatchSize);
|
||||
eventWriteQueue = eventWriteQueue.slice(batch.length);
|
||||
relay.eventBatch(batch);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
setTimeout(() => insertBatch(), 100);
|
||||
}
|
||||
@ -47,7 +45,6 @@ async function insertBatch() {
|
||||
const cmdQueue: Array<WorkQueueItem> = [];
|
||||
try {
|
||||
setTimeout(() => insertBatch(), 100);
|
||||
processWorkQueue(cmdQueue, 50);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
@ -75,23 +72,21 @@ const handleMsg = async (port: MessagePort | DedicatedWorkerGlobalScope, ev: Mes
|
||||
break;
|
||||
}
|
||||
case "init": {
|
||||
await barrierQueue(cmdQueue, async () => {
|
||||
const args = msg.args as InitAargs;
|
||||
insertBatchSize = args.insertBatchSize ?? 10;
|
||||
try {
|
||||
if ("WebAssembly" in self) {
|
||||
relay = new SqliteRelay();
|
||||
} else {
|
||||
relay = new InMemoryRelay();
|
||||
}
|
||||
await relay.init(args.databasePath);
|
||||
} catch (e) {
|
||||
console.error("Fallback to InMemoryRelay", e);
|
||||
const args = msg.args as InitAargs;
|
||||
insertBatchSize = args.insertBatchSize ?? 10;
|
||||
try {
|
||||
if ("WebAssembly" in self) {
|
||||
relay = new SqliteRelay();
|
||||
} else {
|
||||
relay = new InMemoryRelay();
|
||||
await relay.init(args.databasePath);
|
||||
}
|
||||
reply(msg.id, true);
|
||||
});
|
||||
await relay.init(args.databasePath);
|
||||
} catch (e) {
|
||||
console.error("Fallback to InMemoryRelay", e);
|
||||
relay = new InMemoryRelay();
|
||||
await relay.init(args.databasePath);
|
||||
}
|
||||
reply(msg.id, true);
|
||||
break;
|
||||
}
|
||||
case "event": {
|
||||
@ -105,85 +100,68 @@ const handleMsg = async (port: MessagePort | DedicatedWorkerGlobalScope, ev: Mes
|
||||
break;
|
||||
}
|
||||
case "close": {
|
||||
await barrierQueue(cmdQueue, async () => {
|
||||
const res = relay!.close();
|
||||
reply(msg.id, res);
|
||||
});
|
||||
const res = relay!.close();
|
||||
reply(msg.id, res);
|
||||
break;
|
||||
}
|
||||
case "req": {
|
||||
await barrierQueue(cmdQueue, async () => {
|
||||
const req = msg.args as ReqCommand;
|
||||
const filters = req.slice(2) as Array<ReqFilter>;
|
||||
const results: Array<string | NostrEvent> = [];
|
||||
const ids = new Set<string>();
|
||||
for (const r of filters) {
|
||||
const rx = relay!.req(req[1], r);
|
||||
for (const x of rx) {
|
||||
if ((typeof x === "string" && ids.has(x)) || ids.has((x as NostrEvent).id)) {
|
||||
continue;
|
||||
}
|
||||
ids.add(typeof x === "string" ? x : (x as NostrEvent).id);
|
||||
results.push(x);
|
||||
const req = msg.args as ReqCommand;
|
||||
const filters = req.slice(2) as Array<ReqFilter>;
|
||||
const results: Array<string | NostrEvent> = [];
|
||||
const ids = new Set<string>();
|
||||
for (const r of filters) {
|
||||
const rx = relay!.req(req[1], r);
|
||||
for (const x of rx) {
|
||||
if ((typeof x === "string" && ids.has(x)) || ids.has((x as NostrEvent).id)) {
|
||||
continue;
|
||||
}
|
||||
ids.add(typeof x === "string" ? x : (x as NostrEvent).id);
|
||||
results.push(x);
|
||||
}
|
||||
reply(msg.id, results);
|
||||
});
|
||||
}
|
||||
reply(msg.id, results);
|
||||
break;
|
||||
}
|
||||
case "count": {
|
||||
await barrierQueue(cmdQueue, async () => {
|
||||
const req = msg.args as ReqCommand;
|
||||
let results = 0;
|
||||
const filters = req.slice(2) as Array<ReqFilter>;
|
||||
for (const r of filters) {
|
||||
const c = relay!.count(r);
|
||||
results += c;
|
||||
}
|
||||
reply(msg.id, results);
|
||||
});
|
||||
const req = msg.args as ReqCommand;
|
||||
let results = 0;
|
||||
const filters = req.slice(2) as Array<ReqFilter>;
|
||||
for (const r of filters) {
|
||||
const c = relay!.count(r);
|
||||
results += c;
|
||||
}
|
||||
reply(msg.id, results);
|
||||
break;
|
||||
}
|
||||
case "delete": {
|
||||
console.debug("DELETE", msg.args);
|
||||
await barrierQueue(cmdQueue, async () => {
|
||||
const req = msg.args as ReqCommand;
|
||||
let results = [];
|
||||
const filters = req.slice(2) as Array<ReqFilter>;
|
||||
for (const r of filters) {
|
||||
const c = relay!.delete(r);
|
||||
results.push(...c);
|
||||
}
|
||||
reply(msg.id, results);
|
||||
});
|
||||
const req = msg.args as ReqCommand;
|
||||
let results = [];
|
||||
const filters = req.slice(2) as Array<ReqFilter>;
|
||||
for (const r of filters) {
|
||||
const c = relay!.delete(r);
|
||||
results.push(...c);
|
||||
}
|
||||
reply(msg.id, results);
|
||||
break;
|
||||
}
|
||||
case "summary": {
|
||||
await barrierQueue(cmdQueue, async () => {
|
||||
const res = relay!.summary();
|
||||
reply(msg.id, res);
|
||||
});
|
||||
const res = relay!.summary();
|
||||
reply(msg.id, res);
|
||||
break;
|
||||
}
|
||||
case "dumpDb": {
|
||||
await barrierQueue(cmdQueue, async () => {
|
||||
const res = await relay!.dump();
|
||||
reply(msg.id, res);
|
||||
});
|
||||
const res = await relay!.dump();
|
||||
reply(msg.id, res);
|
||||
break;
|
||||
}
|
||||
case "forYouFeed": {
|
||||
await barrierQueue(cmdQueue, async () => {
|
||||
const res = await getForYouFeed(relay!, msg.args as string);
|
||||
reply(msg.id, res);
|
||||
});
|
||||
const res = await getForYouFeed(relay!, msg.args as string);
|
||||
reply(msg.id, res);
|
||||
break;
|
||||
}
|
||||
case "setEventMetadata": {
|
||||
await barrierQueue(cmdQueue, async () => {
|
||||
const [id, metadata] = msg.args as [string, EventMetadata];
|
||||
relay!.setEventMetadata(id, metadata);
|
||||
});
|
||||
const [id, metadata] = msg.args as [string, EventMetadata];
|
||||
relay!.setEventMetadata(id, metadata);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
10
yarn.lock
10
yarn.lock
@ -4842,7 +4842,7 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@snort/worker-relay@workspace:packages/worker-relay"
|
||||
dependencies:
|
||||
"@sqlite.org/sqlite-wasm": "npm:^3.45.3-build3"
|
||||
"@sqlite.org/sqlite-wasm": "npm:^3.46.1-build3"
|
||||
"@types/debug": "npm:^4.1.12"
|
||||
"@types/sharedworker": "npm:^0.0.112"
|
||||
"@types/uuid": "npm:^9.0.7"
|
||||
@ -4860,12 +4860,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@sqlite.org/sqlite-wasm@npm:^3.45.3-build3":
|
||||
version: 3.45.3-build3
|
||||
resolution: "@sqlite.org/sqlite-wasm@npm:3.45.3-build3"
|
||||
"@sqlite.org/sqlite-wasm@npm:^3.46.1-build3":
|
||||
version: 3.46.1-build3
|
||||
resolution: "@sqlite.org/sqlite-wasm@npm:3.46.1-build3"
|
||||
bin:
|
||||
sqlite-wasm: bin/index.js
|
||||
checksum: 10/58dd96936973c8bb3989a7d23d6d6021b05e4b35ae997d617d28b5faf5f370196aaf71d1a060eb1822fed3636acb4b5e0d444807d86687bff2a6efa637f9f1dc
|
||||
checksum: 10/a64225fd784ed2ee8c8bf82f042d05b567b4707fba22a9508fbe3ac42cf1cf7e722d2ede0e1d91556bfec66b140aeb3cf3967546249693f39cc81475d7be90ff
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user