fix: video upload
feat: use new video/short kinds
This commit is contained in:
parent
4bade72dd6
commit
de88158b3a
@ -8,11 +8,11 @@
|
|||||||
"@noble/hashes": "^1.4.0",
|
"@noble/hashes": "^1.4.0",
|
||||||
"@scure/base": "^1.1.6",
|
"@scure/base": "^1.1.6",
|
||||||
"@snort/shared": "^1.0.17",
|
"@snort/shared": "^1.0.17",
|
||||||
"@snort/system": "^1.5.6",
|
"@snort/system": "^1.6.1",
|
||||||
"@snort/system-react": "^1.5.6",
|
"@snort/system-react": "^1.6.1",
|
||||||
"@snort/system-wasm": "^1.0.5",
|
"@snort/system-wasm": "^1.0.5",
|
||||||
"@snort/wallet": "^0.2.1",
|
"@snort/wallet": "^0.2.4",
|
||||||
"@snort/worker-relay": "^1.3.0",
|
"@snort/worker-relay": "^1.3.1",
|
||||||
"@szhsin/react-menu": "^4.1.0",
|
"@szhsin/react-menu": "^4.1.0",
|
||||||
"@types/webscopeio__react-textarea-autocomplete": "^4.7.5",
|
"@types/webscopeio__react-textarea-autocomplete": "^4.7.5",
|
||||||
"@webscopeio/react-textarea-autocomplete": "^4.9.2",
|
"@webscopeio/react-textarea-autocomplete": "^4.9.2",
|
||||||
|
@ -8,8 +8,10 @@ export const GOAL = 9041 as EventKind;
|
|||||||
export const USER_CARDS = 17_777 as EventKind;
|
export const USER_CARDS = 17_777 as EventKind;
|
||||||
export const CARD = 37_777 as EventKind;
|
export const CARD = 37_777 as EventKind;
|
||||||
|
|
||||||
export const VIDEO_KIND = 34_235 as EventKind;
|
export const VIDEO_KIND = 21 as EventKind;
|
||||||
export const SHORTS_KIND = 34_236 as EventKind;
|
export const SHORTS_KIND = 22 as EventKind;
|
||||||
|
export const OLD_VIDEO_KIND = 34_235 as EventKind;
|
||||||
|
export const OLD_SHORTS_KIND = 34_236 as EventKind;
|
||||||
|
|
||||||
export const MINUTE = 60;
|
export const MINUTE = 60;
|
||||||
export const HOUR = 60 * MINUTE;
|
export const HOUR = 60 * MINUTE;
|
||||||
|
@ -5,13 +5,15 @@ import { Goal } from "./goal";
|
|||||||
import { Note } from "./note";
|
import { Note } from "./note";
|
||||||
import { EmojiPack } from "./emoji-pack";
|
import { EmojiPack } from "./emoji-pack";
|
||||||
import { BadgeInfo } from "./badge";
|
import { BadgeInfo } from "./badge";
|
||||||
import { GOAL, LIVE_STREAM_CLIP, StreamState } from "@/const";
|
import { GOAL, LIVE_STREAM_CLIP, OLD_SHORTS_KIND, OLD_VIDEO_KIND, SHORTS_KIND, StreamState, VIDEO_KIND } from "@/const";
|
||||||
import { useEventFeed } from "@snort/system-react";
|
import { useEventFeed } from "@snort/system-react";
|
||||||
import LiveStreamClip from "./stream/clip";
|
import LiveStreamClip from "./stream/clip";
|
||||||
import { ExternalLink } from "./external-link";
|
import { ExternalLink } from "./external-link";
|
||||||
import { extractStreamInfo } from "@/utils";
|
import { extractStreamInfo } from "@/utils";
|
||||||
import LiveVideoPlayer from "./stream/live-video-player";
|
import LiveVideoPlayer from "./stream/live-video-player";
|
||||||
import { HTMLProps } from "react";
|
import { HTMLProps, ReactNode } from "react";
|
||||||
|
import { ShortPage } from "@/pages/short";
|
||||||
|
import { VideoPage } from "@/pages/video";
|
||||||
|
|
||||||
interface EventProps {
|
interface EventProps {
|
||||||
link: NostrLink;
|
link: NostrLink;
|
||||||
@ -31,36 +33,49 @@ export function EventIcon({ kind }: { kind?: EventKind }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function NostrEvent({ ev }: { ev: TaggedNostrEvent }) {
|
export function NostrEvent({ ev }: { ev: TaggedNostrEvent }) {
|
||||||
|
const link = NostrLink.fromEvent(ev);
|
||||||
|
function modalPage(inner: ReactNode) {
|
||||||
|
return <div className="rounded-2xl px-4 py-3 md:w-[700px] mx-auto w-full">{inner}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
switch (ev.kind) {
|
switch (ev.kind) {
|
||||||
case GOAL: {
|
case GOAL: {
|
||||||
return <Goal ev={ev} />;
|
return modalPage(<Goal ev={ev} />);
|
||||||
}
|
}
|
||||||
case EventKind.EmojiSet: {
|
case EventKind.EmojiSet: {
|
||||||
return <EmojiPack ev={ev} />;
|
return modalPage(<EmojiPack ev={ev} />);
|
||||||
}
|
}
|
||||||
case EventKind.Badge: {
|
case EventKind.Badge: {
|
||||||
return <BadgeInfo ev={ev} />;
|
return modalPage(<BadgeInfo ev={ev} />);
|
||||||
}
|
}
|
||||||
case EventKind.TextNote: {
|
case EventKind.TextNote: {
|
||||||
return <Note ev={ev} />;
|
return modalPage(<Note ev={ev} />);
|
||||||
}
|
}
|
||||||
case LIVE_STREAM_CLIP: {
|
case LIVE_STREAM_CLIP: {
|
||||||
return <LiveStreamClip ev={ev} />;
|
return modalPage(<LiveStreamClip ev={ev} />);
|
||||||
|
}
|
||||||
|
case OLD_SHORTS_KIND:
|
||||||
|
case SHORTS_KIND: {
|
||||||
|
return <ShortPage link={link} evPreload={ev} />;
|
||||||
|
}
|
||||||
|
case OLD_VIDEO_KIND:
|
||||||
|
case VIDEO_KIND: {
|
||||||
|
return <VideoPage link={link} evPreload={ev} />;
|
||||||
}
|
}
|
||||||
case EventKind.LiveEvent: {
|
case EventKind.LiveEvent: {
|
||||||
const info = extractStreamInfo(ev);
|
const info = extractStreamInfo(ev);
|
||||||
return (
|
return modalPage(
|
||||||
<LiveVideoPlayer
|
<LiveVideoPlayer
|
||||||
|
link={link}
|
||||||
title={info.title}
|
title={info.title}
|
||||||
status={info.status}
|
status={info.status}
|
||||||
stream={info.status === StreamState.Live ? info.stream : info.recording}
|
stream={info.status === StreamState.Live ? info.stream : info.recording}
|
||||||
poster={info.image}
|
poster={info.image}
|
||||||
/>
|
/>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
const link = NostrLink.fromEvent(ev);
|
return modalPage(<ExternalLink href={`https://snort.social/${link.encode()}`}>{link.encode()}</ExternalLink>);
|
||||||
return <ExternalLink href={`https://snort.social/${link.encode()}`}>{link.encode()}</ExternalLink>;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,7 +145,7 @@ export function StreamEditor({ ev, onFinish, options }: StreamEditorProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const startsTimestamp = parseInt(start ?? (new Date() / 1000));
|
const startsTimestamp = Number(start ?? (new Date().getTime() / 1000));
|
||||||
const startsDate = new Date(startsTimestamp * 1000);
|
const startsDate = new Date(startsTimestamp * 1000);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
import { unwrap } from "@snort/shared";
|
|
||||||
import { NostrLink, NostrPrefix, RequestBuilder, TaggedNostrEvent } from "@snort/system";
|
import { NostrLink, NostrPrefix, RequestBuilder, TaggedNostrEvent } from "@snort/system";
|
||||||
import { useRequestBuilder } from "@snort/system-react";
|
import { useRequestBuilder } from "@snort/system-react";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|
||||||
import { LIVE_STREAM } from "@/const";
|
import { LIVE_STREAM } from "@/const";
|
||||||
import { getHost } from "@/utils";
|
|
||||||
|
|
||||||
export function useCurrentStreamFeed(link: NostrLink, leaveOpen = false, evPreload?: TaggedNostrEvent) {
|
export function useCurrentStreamFeed(link: NostrLink, leaveOpen = false, evPreload?: TaggedNostrEvent) {
|
||||||
const author = link.type === NostrPrefix.Address ? unwrap(link.author) : link.id;
|
|
||||||
const sub = useMemo(() => {
|
const sub = useMemo(() => {
|
||||||
const b = new RequestBuilder(`current-event:${link.id}`);
|
const b = new RequestBuilder(`current-event:${link.id}`);
|
||||||
b.withOptions({
|
b.withOptions({
|
||||||
@ -16,14 +13,8 @@ export function useCurrentStreamFeed(link: NostrLink, leaveOpen = false, evPrelo
|
|||||||
if (link.type === NostrPrefix.PublicKey || link.type === NostrPrefix.Profile) {
|
if (link.type === NostrPrefix.PublicKey || link.type === NostrPrefix.Profile) {
|
||||||
b.withFilter().authors([link.id]).kinds([LIVE_STREAM]);
|
b.withFilter().authors([link.id]).kinds([LIVE_STREAM]);
|
||||||
b.withFilter().tag("p", [link.id]).kinds([LIVE_STREAM]);
|
b.withFilter().tag("p", [link.id]).kinds([LIVE_STREAM]);
|
||||||
} else if (link.type === NostrPrefix.Address) {
|
} else {
|
||||||
const f = b.withFilter().tag("d", [link.id]);
|
b.withFilter().link(link);
|
||||||
if (link.author) {
|
|
||||||
f.authors([link.author]);
|
|
||||||
}
|
|
||||||
if (link.kind) {
|
|
||||||
f.kinds([link.kind]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return b;
|
return b;
|
||||||
}, [link.id, leaveOpen]);
|
}, [link.id, leaveOpen]);
|
||||||
@ -31,9 +22,7 @@ export function useCurrentStreamFeed(link: NostrLink, leaveOpen = false, evPrelo
|
|||||||
const q = useRequestBuilder(sub);
|
const q = useRequestBuilder(sub);
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const hosting = [...q, ...(evPreload ? [evPreload] : [])]
|
const hosting = [...q, ...(evPreload ? [evPreload] : [])].sort((a, b) => (b.created_at > a.created_at ? 1 : -1));
|
||||||
.filter(a => getHost(a) === author || a.pubkey === author)
|
|
||||||
.sort((a, b) => (b.created_at > a.created_at ? 1 : -1));
|
|
||||||
return hosting.at(0);
|
return hosting.at(0);
|
||||||
}, [q]);
|
}, [q]);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { SHORTS_KIND, VIDEO_KIND } from "@/const";
|
import { OLD_SHORTS_KIND, OLD_VIDEO_KIND, SHORTS_KIND, VIDEO_KIND } from "@/const";
|
||||||
import { MediaPayload, VideoInfo } from "@/service/video/info";
|
import { MediaPayload, VideoInfo } from "@/service/video/info";
|
||||||
import { findTag } from "@/utils";
|
import { findTag } from "@/utils";
|
||||||
import { NostrEvent, TaggedNostrEvent } from "@snort/system";
|
import { NostrEvent, TaggedNostrEvent } from "@snort/system";
|
||||||
@ -58,7 +58,7 @@ export function useDeadLink(ev: TaggedNostrEvent | NostrEvent) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const links =
|
const links =
|
||||||
ev.kind === VIDEO_KIND || ev.kind === SHORTS_KIND
|
ev.kind === VIDEO_KIND || ev.kind === SHORTS_KIND || ev.kind == OLD_SHORTS_KIND || ev.kind == OLD_VIDEO_KIND
|
||||||
? VideoInfo.parse(ev)?.sources()
|
? VideoInfo.parse(ev)?.sources()
|
||||||
: [
|
: [
|
||||||
{
|
{
|
||||||
|
@ -4,12 +4,7 @@ import { removeUndefined, sanitizeRelayUrl } from "@snort/shared";
|
|||||||
import { Nip96Server } from "@/service/upload/nip96";
|
import { Nip96Server } from "@/service/upload/nip96";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|
||||||
export const DefaultMediaServers = [
|
export const DefaultMediaServers = [new UnknownTag(["server", "https://nostr.download/"])];
|
||||||
//"https://media.zap.stream",
|
|
||||||
new UnknownTag(["server", "https://nostr.build/"]),
|
|
||||||
new UnknownTag(["server", "https://nostrcheck.me/"]),
|
|
||||||
new UnknownTag(["server", "https://files.v0l.io/"]),
|
|
||||||
];
|
|
||||||
|
|
||||||
export function useMediaServerList() {
|
export function useMediaServerList() {
|
||||||
const login = useLogin();
|
const login = useLogin();
|
||||||
|
@ -221,6 +221,9 @@
|
|||||||
"BD0vyn": {
|
"BD0vyn": {
|
||||||
"defaultMessage": "{name} created a clip"
|
"defaultMessage": "{name} created a clip"
|
||||||
},
|
},
|
||||||
|
"BHNL+v": {
|
||||||
|
"defaultMessage": "Raw Data:"
|
||||||
|
},
|
||||||
"Bd1yEX": {
|
"Bd1yEX": {
|
||||||
"defaultMessage": "New Stream Goal"
|
"defaultMessage": "New Stream Goal"
|
||||||
},
|
},
|
||||||
@ -641,6 +644,9 @@
|
|||||||
"ZcgtZo": {
|
"ZcgtZo": {
|
||||||
"defaultMessage": "We've put together a list of easy-to-use exchanges that will allow you to buy a small amount of bitcoin (sats) and to transfer them to your wallet of choice."
|
"defaultMessage": "We've put together a list of easy-to-use exchanges that will allow you to buy a small amount of bitcoin (sats) and to transfer them to your wallet of choice."
|
||||||
},
|
},
|
||||||
|
"Zfr//4": {
|
||||||
|
"defaultMessage": "No duration provided, please try another upload server."
|
||||||
|
},
|
||||||
"ZmqxZs": {
|
"ZmqxZs": {
|
||||||
"defaultMessage": "You can change this later"
|
"defaultMessage": "You can change this later"
|
||||||
},
|
},
|
||||||
@ -705,6 +711,9 @@
|
|||||||
"dkUMIH": {
|
"dkUMIH": {
|
||||||
"defaultMessage": "Clip by {name}"
|
"defaultMessage": "Clip by {name}"
|
||||||
},
|
},
|
||||||
|
"dqGkI+": {
|
||||||
|
"defaultMessage": "Video durations vary too much, are you sure each variant is the same video?"
|
||||||
|
},
|
||||||
"e011kf": {
|
"e011kf": {
|
||||||
"defaultMessage": "FAQ",
|
"defaultMessage": "FAQ",
|
||||||
"description": "Title: FAQ page"
|
"description": "Title: FAQ page"
|
||||||
@ -945,6 +954,9 @@
|
|||||||
"vP4dFa": {
|
"vP4dFa": {
|
||||||
"defaultMessage": "Visit {link} to get some sweet zap.stream merch!"
|
"defaultMessage": "Visit {link} to get some sweet zap.stream merch!"
|
||||||
},
|
},
|
||||||
|
"vaZKTn": {
|
||||||
|
"defaultMessage": "Add more content"
|
||||||
|
},
|
||||||
"vrTOHJ": {
|
"vrTOHJ": {
|
||||||
"defaultMessage": "{amount} sats"
|
"defaultMessage": "{amount} sats"
|
||||||
},
|
},
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { LIVE_STREAM, SHORTS_KIND, VIDEO_KIND } from "@/const";
|
import { LIVE_STREAM, OLD_SHORTS_KIND, OLD_VIDEO_KIND, SHORTS_KIND, VIDEO_KIND } from "@/const";
|
||||||
import { useStreamLink } from "@/hooks/stream-link";
|
import { useStreamLink } from "@/hooks/stream-link";
|
||||||
import { getEventFromLocationState } from "@/utils";
|
import { getEventFromLocationState } from "@/utils";
|
||||||
import { NostrPrefix } from "@snort/system";
|
import { NostrPrefix } from "@snort/system";
|
||||||
@ -20,20 +20,16 @@ export function LinkHandler() {
|
|||||||
if (!link) return;
|
if (!link) return;
|
||||||
|
|
||||||
if (link.type === NostrPrefix.Event) {
|
if (link.type === NostrPrefix.Event) {
|
||||||
return (
|
return <NostrEventElement link={link} />;
|
||||||
<div className="rounded-2xl px-4 py-3 md:w-[700px] mx-auto w-full">
|
|
||||||
<NostrEventElement link={link} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else if (link.kind === LIVE_STREAM || link.type === NostrPrefix.PublicKey) {
|
} else if (link.kind === LIVE_STREAM || link.type === NostrPrefix.PublicKey) {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(layoutContext.showHeader ? "h-[calc(100dvh-44px)]" : "h-[calc(100dvh)]", "w-full")}>
|
<div className={classNames(layoutContext.showHeader ? "h-[calc(100dvh-44px)]" : "h-[calc(100dvh)]", "w-full")}>
|
||||||
<StreamPage link={link} evPreload={evPreload} />
|
<StreamPage link={link} evPreload={evPreload} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else if (link.kind === VIDEO_KIND) {
|
} else if (link.kind === VIDEO_KIND || link.kind === OLD_VIDEO_KIND) {
|
||||||
return <VideoPage link={link} evPreload={evPreload} />;
|
return <VideoPage link={link} evPreload={evPreload} />;
|
||||||
} else if (link.kind === SHORTS_KIND) {
|
} else if (link.kind === SHORTS_KIND || link.kind === OLD_SHORTS_KIND) {
|
||||||
return <ShortPage link={link} evPreload={evPreload} />;
|
return <ShortPage link={link} evPreload={evPreload} />;
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { SHORTS_KIND } from "@/const";
|
import { OLD_SHORTS_KIND, SHORTS_KIND } from "@/const";
|
||||||
import VideoGrid from "@/element/video-grid";
|
import VideoGrid from "@/element/video-grid";
|
||||||
import { VideoTile } from "@/element/video/video-tile";
|
import { VideoTile } from "@/element/video/video-tile";
|
||||||
import { findTag } from "@/utils";
|
import { findTag } from "@/utils";
|
||||||
@ -8,13 +8,12 @@ import { FormattedMessage } from "react-intl";
|
|||||||
|
|
||||||
export function ShortsPage() {
|
export function ShortsPage() {
|
||||||
const rb = new RequestBuilder("shorts");
|
const rb = new RequestBuilder("shorts");
|
||||||
rb.withFilter().kinds([SHORTS_KIND]);
|
rb.withFilter().kinds([SHORTS_KIND, OLD_SHORTS_KIND]);
|
||||||
|
|
||||||
const videos = useRequestBuilder(rb);
|
const videos = useRequestBuilder(rb);
|
||||||
|
|
||||||
const sorted = videos.sort((a, b) => {
|
const sorted = videos.sort((a, b) => {
|
||||||
const pubA = findTag(a, "published_at");
|
const pubA = findTag(a, "published_at") ?? a.created_at;
|
||||||
const pubB = findTag(b, "published_at");
|
const pubB = findTag(b, "published_at") ?? b.created_at;
|
||||||
return Number(pubA) > Number(pubB) ? -1 : 1;
|
return Number(pubA) > Number(pubB) ? -1 : 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { VIDEO_KIND } from "@/const";
|
import { SHORTS_KIND, VIDEO_KIND } from "@/const";
|
||||||
import { DefaultButton, IconButton, Layer3Button, PrimaryButton, WarningButton } from "@/element/buttons";
|
import { DefaultButton, IconButton, Layer3Button, PrimaryButton, WarningButton } from "@/element/buttons";
|
||||||
import { Icon } from "@/element/icon";
|
import { Icon } from "@/element/icon";
|
||||||
import Modal from "@/element/modal";
|
import Modal from "@/element/modal";
|
||||||
@ -9,11 +9,11 @@ import { ServerList } from "@/element/upload/server-list";
|
|||||||
import useImgProxy from "@/hooks/img-proxy";
|
import useImgProxy from "@/hooks/img-proxy";
|
||||||
import { useLogin } from "@/hooks/login";
|
import { useLogin } from "@/hooks/login";
|
||||||
import { useMediaServerList } from "@/hooks/media-servers";
|
import { useMediaServerList } from "@/hooks/media-servers";
|
||||||
import { Nip94Tags, UploadResult, nip94TagsToIMeta } from "@/service/upload";
|
import { Nip94Tags, UploadResult, nip94TagsToIMeta, readNip94Tags } from "@/service/upload";
|
||||||
import { Nip96Server } from "@/service/upload/nip96";
|
import { Nip96Server } from "@/service/upload/nip96";
|
||||||
import { openFile } from "@/utils";
|
import { openFile } from "@/utils";
|
||||||
import { ExternalStore, removeUndefined, unixNow, unwrap } from "@snort/shared";
|
import { ExternalStore, removeUndefined, unwrap } from "@snort/shared";
|
||||||
import { EventPublisher, NostrLink } from "@snort/system";
|
import { EventBuilder, EventPublisher, NostrEvent, NostrLink } from "@snort/system";
|
||||||
import { SnortContext } from "@snort/system-react";
|
import { SnortContext } from "@snort/system-react";
|
||||||
import { useContext, useEffect, useState, useSyncExternalStore } from "react";
|
import { useContext, useEffect, useState, useSyncExternalStore } from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
@ -69,6 +69,23 @@ class UploadManager extends ExternalStore<Array<UploadStatus>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addUpload(server: string, file: NostrEvent, meta: Nip94Tags, type: UploadStatus["type"]) {
|
||||||
|
const name = file.content ?? meta.summary ?? meta.alt ?? "";
|
||||||
|
const uploadKey = `${name}:${server}:${type}`;
|
||||||
|
this.#uploads.set(uploadKey, {
|
||||||
|
type,
|
||||||
|
name,
|
||||||
|
size: meta.size ?? 0,
|
||||||
|
server,
|
||||||
|
result: {
|
||||||
|
url: meta.url,
|
||||||
|
header: file,
|
||||||
|
metadata: meta,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.notifyChange();
|
||||||
|
}
|
||||||
|
|
||||||
async uploadTo(server: string, file: File, pub: EventPublisher, type: UploadStatus["type"]) {
|
async uploadTo(server: string, file: File, pub: EventPublisher, type: UploadStatus["type"]) {
|
||||||
let uploader = this.#uploaders.get(server);
|
let uploader = this.#uploaders.get(server);
|
||||||
if (!uploader) {
|
if (!uploader) {
|
||||||
@ -130,6 +147,27 @@ class UploadManager extends ExternalStore<Array<UploadStatus>> {
|
|||||||
return resGroup;
|
return resGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the [min, max] duration from all variants
|
||||||
|
*/
|
||||||
|
duration() {
|
||||||
|
const uploads = this.snapshot();
|
||||||
|
return uploads.reduce(
|
||||||
|
(acc, v) => {
|
||||||
|
if (v.result?.metadata?.duration) {
|
||||||
|
if (acc[1] < v.result.metadata.duration) {
|
||||||
|
acc[1] = v.result.metadata.duration;
|
||||||
|
}
|
||||||
|
if (acc[0] > v.result.metadata.duration) {
|
||||||
|
acc[0] = v.result.metadata.duration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
[1_000_000, 0],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the `imeta` tag for this upload
|
* Create the `imeta` tag for this upload
|
||||||
*/
|
*/
|
||||||
@ -187,21 +225,24 @@ export function UploadPage() {
|
|||||||
return error.length == 0 && uploads.length > 0 && uploads.every(a => a.result !== undefined);
|
return error.length == 0 && uploads.length > 0 && uploads.every(a => a.result !== undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function publish() {
|
function makeEvent() {
|
||||||
const pub = login?.publisher();
|
const duration = manager.duration();
|
||||||
if (!pub) return;
|
const eb = new EventBuilder()
|
||||||
const ev = await pub.generic(eb => {
|
.pubKey(login?.pubkey ?? "00".repeat(31))
|
||||||
eb.kind(VIDEO_KIND);
|
.kind(duration[1] <= 60 ? SHORTS_KIND : VIDEO_KIND)
|
||||||
eb.tag(["d", manager.id]);
|
.tag(["title", title])
|
||||||
eb.tag(["title", title]);
|
.content(summary);
|
||||||
eb.tag(["published_at", unixNow().toString()]);
|
|
||||||
eb.content(summary);
|
|
||||||
|
|
||||||
const imeta = manager.makeIMeta();
|
const imeta = manager.makeIMeta();
|
||||||
imeta.forEach(a => eb.tag(a));
|
imeta.forEach(a => eb.tag(a));
|
||||||
|
|
||||||
return eb;
|
return eb;
|
||||||
});
|
}
|
||||||
|
|
||||||
|
async function publish() {
|
||||||
|
const pub = login?.publisher();
|
||||||
|
if (!pub) return;
|
||||||
|
const ev = await makeEvent().buildAndSign(pub.signer);
|
||||||
console.debug(ev);
|
console.debug(ev);
|
||||||
await system.BroadcastEvent(ev);
|
await system.BroadcastEvent(ev);
|
||||||
navigate(`/${NostrLink.fromEvent(ev).encode()}`);
|
navigate(`/${NostrLink.fromEvent(ev).encode()}`);
|
||||||
@ -229,7 +270,16 @@ export function UploadPage() {
|
|||||||
const data = await rsp.blob();
|
const data = await rsp.blob();
|
||||||
const pub = login?.publisher();
|
const pub = login?.publisher();
|
||||||
if (pub) {
|
if (pub) {
|
||||||
servers.servers.forEach(b => manager.uploadTo(b, new File([data], "thumb.jpg"), pub, "thumb"));
|
servers.servers.forEach(b =>
|
||||||
|
manager.uploadTo(
|
||||||
|
b,
|
||||||
|
new File([data], "thumb.jpg", {
|
||||||
|
type: "image/jpeg",
|
||||||
|
}),
|
||||||
|
pub,
|
||||||
|
"thumb",
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -291,6 +341,17 @@ export function UploadPage() {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
const d = manager.duration();
|
||||||
|
if (d[0] === 0 || d[1] === 0) {
|
||||||
|
err.push(formatMessage({ defaultMessage: "No duration provided, please try another upload server." }));
|
||||||
|
}
|
||||||
|
if (Math.abs(d[0] - d[1]) >= 0.5) {
|
||||||
|
err.push(
|
||||||
|
formatMessage({
|
||||||
|
defaultMessage: "Video durations vary too much, are you sure each variant is the same video?",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
setError(err);
|
setError(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,6 +373,7 @@ export function UploadPage() {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-xl:w-full xl:w-[1200px] xl:mx-auto grid gap-6 xl:grid-cols-[auto_350px] max-xl:px-4">
|
<div className="max-xl:w-full xl:w-[1200px] xl:mx-auto grid gap-6 xl:grid-cols-[auto_350px] max-xl:px-4">
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6">
|
||||||
@ -359,8 +421,11 @@ export function UploadPage() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{videos > 0 && (
|
{videos > 0 && (
|
||||||
<div onClick={() => uploadFile()} className="cursor-pointer">
|
<div className="flex flex-col gap-2">
|
||||||
{uploadButton()}
|
<div className="text-xl">
|
||||||
|
<FormattedMessage defaultMessage="Add more content" />
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-4 items-center">{uploadButton()}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{uploads.length > 0 && (
|
{uploads.length > 0 && (
|
||||||
@ -377,7 +442,7 @@ export function UploadPage() {
|
|||||||
<FormattedMessage defaultMessage="Thumbnail" />
|
<FormattedMessage defaultMessage="Thumbnail" />
|
||||||
</div>
|
</div>
|
||||||
<div className="border border-layer-3 border-dashed border-2 rounded-xl aspect-video overflow-hidden">
|
<div className="border border-layer-3 border-dashed border-2 rounded-xl aspect-video overflow-hidden">
|
||||||
{thumb && <img src={proxy(thumb)} className="w-full h-full" />}
|
{thumb && <img src={proxy(thumb)} className="w-full h-full object-contain" />}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-4">
|
<div className="flex gap-4">
|
||||||
<DefaultButton onClick={() => uploadThumb()}>
|
<DefaultButton onClick={() => uploadThumb()}>
|
||||||
@ -405,9 +470,13 @@ export function UploadPage() {
|
|||||||
</WarningButton>
|
</WarningButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<FormattedMessage defaultMessage="Raw Data:" />
|
||||||
<pre className="text-xs font-mono overflow-wrap text-pretty">
|
<pre className="text-xs font-mono overflow-wrap text-pretty">
|
||||||
{JSON.stringify(manager.makeIMeta(), undefined, 2)}
|
{JSON.stringify(makeEvent().build(), undefined, 2)}
|
||||||
</pre>
|
</pre>
|
||||||
|
</div>
|
||||||
{editServers && (
|
{editServers && (
|
||||||
<Modal id="server-list" onClose={() => setEditServers(false)}>
|
<Modal id="server-list" onClose={() => setEditServers(false)}>
|
||||||
<ServerList />
|
<ServerList />
|
||||||
@ -416,7 +485,19 @@ export function UploadPage() {
|
|||||||
{mediaPicker && (
|
{mediaPicker && (
|
||||||
<Modal id="media-picker" onClose={() => setMediaPicker(false)} largeModal={true}>
|
<Modal id="media-picker" onClose={() => setMediaPicker(false)} largeModal={true}>
|
||||||
<MediaServerFileList
|
<MediaServerFileList
|
||||||
onPicked={() => {
|
onPicked={files => {
|
||||||
|
files.forEach(f => {
|
||||||
|
const meta = readNip94Tags(f.tags);
|
||||||
|
if (meta.url) {
|
||||||
|
const url = new URL(meta.url);
|
||||||
|
manager.addUpload(
|
||||||
|
`${url.protocol}//${url.host}/`,
|
||||||
|
f,
|
||||||
|
meta,
|
||||||
|
meta.mimeType?.startsWith("image/") ?? false ? "thumb" : "video",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
setMediaPicker(false);
|
setMediaPicker(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { VIDEO_KIND } from "@/const";
|
import { OLD_VIDEO_KIND, VIDEO_KIND } from "@/const";
|
||||||
import VideoGrid from "@/element/video-grid";
|
import VideoGrid from "@/element/video-grid";
|
||||||
import { findTag, getHost } from "@/utils";
|
import { findTag, getHost } from "@/utils";
|
||||||
import { NostrLink, RequestBuilder } from "@snort/system";
|
import { NostrLink, RequestBuilder } from "@snort/system";
|
||||||
@ -11,7 +11,7 @@ export function VideosPage() {
|
|||||||
const login = useLogin();
|
const login = useLogin();
|
||||||
|
|
||||||
const rb = new RequestBuilder("videos");
|
const rb = new RequestBuilder("videos");
|
||||||
rb.withFilter().kinds([VIDEO_KIND]);
|
rb.withFilter().kinds([VIDEO_KIND, OLD_VIDEO_KIND]);
|
||||||
|
|
||||||
const videos = useRequestBuilder(rb);
|
const videos = useRequestBuilder(rb);
|
||||||
|
|
||||||
@ -22,8 +22,8 @@ export function VideosPage() {
|
|||||||
return (login?.state?.muted.length ?? 0) === 0 || !login?.state?.muted.some(a => a.equals(link));
|
return (login?.state?.muted.length ?? 0) === 0 || !login?.state?.muted.some(a => a.equals(link));
|
||||||
})
|
})
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
const pubA = findTag(a, "published_at");
|
const pubA = findTag(a, "published_at") ?? a.created_at;
|
||||||
const pubB = findTag(b, "published_at");
|
const pubB = findTag(b, "published_at") ?? b.created_at;
|
||||||
return Number(pubA) > Number(pubB) ? -1 : 1;
|
return Number(pubA) > Number(pubB) ? -1 : 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -16,6 +16,8 @@ export interface Nip94Tags {
|
|||||||
summary?: string;
|
summary?: string;
|
||||||
alt?: string;
|
alt?: string;
|
||||||
fallback?: Array<string>;
|
fallback?: Array<string>;
|
||||||
|
duration?: number;
|
||||||
|
bitrate?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UploadResult {
|
export interface UploadResult {
|
||||||
@ -103,6 +105,14 @@ export function readNip94Tags(tags: Array<Array<string>>) {
|
|||||||
res.fallback.push(v);
|
res.fallback.push(v);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "duration": {
|
||||||
|
res.duration = Number(v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "bitrate": {
|
||||||
|
res.bitrate = Number(v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
@ -126,6 +136,8 @@ export function nip94TagsToIMeta(meta: Nip94Tags) {
|
|||||||
ifPush("thumb", meta.thumb);
|
ifPush("thumb", meta.thumb);
|
||||||
ifPush("summary", meta.summary);
|
ifPush("summary", meta.summary);
|
||||||
ifPush("alt", meta.alt);
|
ifPush("alt", meta.alt);
|
||||||
|
ifPush("duration", meta.duration);
|
||||||
|
ifPush("bitrate", meta.bitrate);
|
||||||
if (meta.image) {
|
if (meta.image) {
|
||||||
meta.image.forEach(a => ifPush("image", a));
|
meta.image.forEach(a => ifPush("image", a));
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ export class Nip96Server {
|
|||||||
const fd = new FormData();
|
const fd = new FormData();
|
||||||
fd.append("size", file.size.toString());
|
fd.append("size", file.size.toString());
|
||||||
fd.append("caption", filename);
|
fd.append("caption", filename);
|
||||||
fd.append("media_type", file.type);
|
fd.append("content_type", file.type);
|
||||||
fd.append("file", file);
|
fd.append("file", file);
|
||||||
|
|
||||||
const rsp = await this.#req("", "POST", fd);
|
const rsp = await this.#req("", "POST", fd);
|
||||||
|
@ -2,7 +2,6 @@ import { NostrEvent } from "@snort/system";
|
|||||||
import { GameInfo } from "../game-database";
|
import { GameInfo } from "../game-database";
|
||||||
import { Nip94Tags, readNip94Tags, readNip94TagsFromIMeta } from "../upload";
|
import { Nip94Tags, readNip94Tags, readNip94TagsFromIMeta } from "../upload";
|
||||||
import { getHost, sortStreamTags, extractGameTag, findTag } from "@/utils";
|
import { getHost, sortStreamTags, extractGameTag, findTag } from "@/utils";
|
||||||
import { unwrap } from "@snort/shared";
|
|
||||||
|
|
||||||
export interface MediaPayload {
|
export interface MediaPayload {
|
||||||
url: string;
|
url: string;
|
||||||
@ -19,9 +18,12 @@ export class VideoInfo {
|
|||||||
goal?: string;
|
goal?: string;
|
||||||
gameId?: string;
|
gameId?: string;
|
||||||
gameInfo?: GameInfo;
|
gameInfo?: GameInfo;
|
||||||
duration?: number;
|
|
||||||
publishedAt?: number;
|
publishedAt?: number;
|
||||||
|
|
||||||
|
get duration() {
|
||||||
|
return this.media.find(m => m.duration)?.duration;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly host: string,
|
readonly host: string,
|
||||||
readonly id: string,
|
readonly id: string,
|
||||||
@ -31,28 +33,13 @@ export class VideoInfo {
|
|||||||
|
|
||||||
static parse(ev: NostrEvent) {
|
static parse(ev: NostrEvent) {
|
||||||
const { regularTags, prefixedTags } = sortStreamTags(ev.tags);
|
const { regularTags, prefixedTags } = sortStreamTags(ev.tags);
|
||||||
const ret = new VideoInfo(getHost(ev), unwrap(findTag(ev, "d")), regularTags, VideoInfo.#parseMediaTags(ev.tags));
|
const ret = new VideoInfo(getHost(ev), findTag(ev, "d") ?? ev.id, regularTags, VideoInfo.#parseMediaTags(ev.tags));
|
||||||
|
|
||||||
const matchInto = <K extends keyof VideoInfo>(
|
ret.title = findTag(ev, "title");
|
||||||
tag: Array<string>,
|
ret.summary = findTag(ev, "summary") ?? ev.content;
|
||||||
key: string,
|
ret.contentWarning = findTag(ev, "content-warning");
|
||||||
into: K,
|
ret.goal = findTag(ev, "goal");
|
||||||
fn?: (v: string) => never,
|
ret.publishedAt = Number(findTag(ev, "published_at") ?? ev.created_at);
|
||||||
) => {
|
|
||||||
if (tag[0] === key) {
|
|
||||||
ret[into] = fn ? fn(tag[1]) : (tag[1] as never);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const t of ev.tags) {
|
|
||||||
matchInto(t, "d", "id");
|
|
||||||
matchInto(t, "title", "title");
|
|
||||||
matchInto(t, "summary", "summary");
|
|
||||||
matchInto(t, "content-warning", "contentWarning");
|
|
||||||
matchInto(t, "goal", "goal");
|
|
||||||
matchInto(t, "duration", "duration");
|
|
||||||
matchInto(t, "published_at", "publishedAt");
|
|
||||||
}
|
|
||||||
|
|
||||||
const { gameInfo, gameId } = extractGameTag(prefixedTags);
|
const { gameInfo, gameId } = extractGameTag(prefixedTags);
|
||||||
ret.gameId = gameId;
|
ret.gameId = gameId;
|
||||||
|
@ -73,6 +73,7 @@
|
|||||||
"Axo/o5": "Science & Technology",
|
"Axo/o5": "Science & Technology",
|
||||||
"AyGauy": "Login",
|
"AyGauy": "Login",
|
||||||
"BD0vyn": "{name} created a clip",
|
"BD0vyn": "{name} created a clip",
|
||||||
|
"BHNL+v": "Raw Data:",
|
||||||
"Bd1yEX": "New Stream Goal",
|
"Bd1yEX": "New Stream Goal",
|
||||||
"Bep/gA": "Private key",
|
"Bep/gA": "Private key",
|
||||||
"BzQPM+": "Destination",
|
"BzQPM+": "Destination",
|
||||||
@ -211,6 +212,7 @@
|
|||||||
"ZXp0z1": "Features",
|
"ZXp0z1": "Features",
|
||||||
"ZaNcK4": "No goals yet",
|
"ZaNcK4": "No goals yet",
|
||||||
"ZcgtZo": "We've put together a list of easy-to-use exchanges that will allow you to buy a small amount of bitcoin (sats) and to transfer them to your wallet of choice.",
|
"ZcgtZo": "We've put together a list of easy-to-use exchanges that will allow you to buy a small amount of bitcoin (sats) and to transfer them to your wallet of choice.",
|
||||||
|
"Zfr//4": "No duration provided, please try another upload server.",
|
||||||
"ZmqxZs": "You can change this later",
|
"ZmqxZs": "You can change this later",
|
||||||
"ZsYhvh": "Zaps are lightning payments, which are published on nostr as receipts.",
|
"ZsYhvh": "Zaps are lightning payments, which are published on nostr as receipts.",
|
||||||
"Zse7yG": "Raid target",
|
"Zse7yG": "Raid target",
|
||||||
@ -232,6 +234,7 @@
|
|||||||
"dOQCL8": "Display name",
|
"dOQCL8": "Display name",
|
||||||
"dVD/AR": "Top Zappers",
|
"dVD/AR": "Top Zappers",
|
||||||
"dkUMIH": "Clip by {name}",
|
"dkUMIH": "Clip by {name}",
|
||||||
|
"dqGkI+": "Video durations vary too much, are you sure each variant is the same video?",
|
||||||
"e011kf": "FAQ",
|
"e011kf": "FAQ",
|
||||||
"ebmhes": "Nostr Extension",
|
"ebmhes": "Nostr Extension",
|
||||||
"f6biFA": "Oh, and you have {n} sats of free streaming on us! 💜",
|
"f6biFA": "Oh, and you have {n} sats of free streaming on us! 💜",
|
||||||
@ -311,6 +314,7 @@
|
|||||||
"ug01Mk": "Time",
|
"ug01Mk": "Time",
|
||||||
"uksRSi": "Latest Videos",
|
"uksRSi": "Latest Videos",
|
||||||
"vP4dFa": "Visit {link} to get some sweet zap.stream merch!",
|
"vP4dFa": "Visit {link} to get some sweet zap.stream merch!",
|
||||||
|
"vaZKTn": "Add more content",
|
||||||
"vrTOHJ": "{amount} sats",
|
"vrTOHJ": "{amount} sats",
|
||||||
"w+2Vw7": "Shorts",
|
"w+2Vw7": "Shorts",
|
||||||
"w0Xm2F": "Start typing",
|
"w0Xm2F": "Start typing",
|
||||||
|
44
yarn.lock
44
yarn.lock
@ -2573,14 +2573,14 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@snort/system-react@npm:^1.5.6":
|
"@snort/system-react@npm:^1.6.1":
|
||||||
version: 1.5.6
|
version: 1.6.1
|
||||||
resolution: "@snort/system-react@npm:1.5.6"
|
resolution: "@snort/system-react@npm:1.6.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@snort/shared": "npm:^1.0.17"
|
"@snort/shared": "npm:^1.0.17"
|
||||||
"@snort/system": "npm:^1.5.6"
|
"@snort/system": "npm:^1.6.1"
|
||||||
react: "npm:^18.2.0"
|
react: "npm:^18.2.0"
|
||||||
checksum: 10c0/b7fb8dbc87328603b202dad9ce57dbff2cb8231829128b48346ba10925ee9fa5103652941b6a0456d6221fce8379a7148dc6507ebe8e1d0d35f7efd92738d08a
|
checksum: 10c0/fe3180c1f4341df4fd585c4031ef632a7d59c7637c6aed16ea254a1c22a66e288516d96939c0080efd0fb73940531855a59a24fae5f82c326f6544362eb0394c
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -2591,9 +2591,9 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@snort/system@npm:^1.5.6":
|
"@snort/system@npm:^1.6.1":
|
||||||
version: 1.5.6
|
version: 1.6.1
|
||||||
resolution: "@snort/system@npm:1.5.6"
|
resolution: "@snort/system@npm:1.6.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@noble/ciphers": "npm:^0.6.0"
|
"@noble/ciphers": "npm:^0.6.0"
|
||||||
"@noble/curves": "npm:^1.4.0"
|
"@noble/curves": "npm:^1.4.0"
|
||||||
@ -2608,33 +2608,33 @@ __metadata:
|
|||||||
nostr-social-graph: "npm:^1.0.3"
|
nostr-social-graph: "npm:^1.0.3"
|
||||||
uuid: "npm:^9.0.0"
|
uuid: "npm:^9.0.0"
|
||||||
ws: "npm:^8.14.0"
|
ws: "npm:^8.14.0"
|
||||||
checksum: 10c0/38fee2d55240f91a5e6ea0684a4bd94e6f4c56fab9b6a20d9ebef26dcdd17ed6c9a42bf1804c7bcf135a89c6882659baecede6d885dcead4a167d5e3337c9764
|
checksum: 10c0/5da01450970f4a51df98369c4cdd2b8886add3480e7962d11ed17498cf7db421bcd5e585abe532ff754efc0bf26b5734b9b24005f958802357ec390ccb74d281
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@snort/wallet@npm:^0.2.1":
|
"@snort/wallet@npm:^0.2.4":
|
||||||
version: 0.2.1
|
version: 0.2.4
|
||||||
resolution: "@snort/wallet@npm:0.2.1"
|
resolution: "@snort/wallet@npm:0.2.4"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@cashu/cashu-ts": "npm:^1.0.0-rc.3"
|
"@cashu/cashu-ts": "npm:^1.0.0-rc.3"
|
||||||
"@lightninglabs/lnc-web": "npm:^0.3.1-alpha"
|
"@lightninglabs/lnc-web": "npm:^0.3.1-alpha"
|
||||||
"@scure/base": "npm:^1.1.6"
|
"@scure/base": "npm:^1.1.6"
|
||||||
"@snort/shared": "npm:^1.0.17"
|
"@snort/shared": "npm:^1.0.17"
|
||||||
"@snort/system": "npm:^1.5.6"
|
"@snort/system": "npm:^1.6.1"
|
||||||
debug: "npm:^4.3.4"
|
debug: "npm:^4.3.4"
|
||||||
eventemitter3: "npm:^5.0.1"
|
eventemitter3: "npm:^5.0.1"
|
||||||
checksum: 10c0/0dcf4b0336029e336bd6abcd7b79cf60d6fed08b2ab2847a8e791bb2399646e86c95aefc4dcfea08365c3dea417c362cdfa93939d7c36f037762de601936b331
|
checksum: 10c0/7d3e23d1d79595ee99e041b816cec5e7bfc6f34bfce6f0864a556de82cfcf29200f6978744b0717b5f6873c5c498e2dd9005571171f1f533f235cfbadf205a44
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@snort/worker-relay@npm:^1.3.0":
|
"@snort/worker-relay@npm:^1.3.1":
|
||||||
version: 1.3.0
|
version: 1.3.1
|
||||||
resolution: "@snort/worker-relay@npm:1.3.0"
|
resolution: "@snort/worker-relay@npm:1.3.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@sqlite.org/sqlite-wasm": "npm:^3.46.1-build3"
|
"@sqlite.org/sqlite-wasm": "npm:^3.46.1-build3"
|
||||||
eventemitter3: "npm:^5.0.1"
|
eventemitter3: "npm:^5.0.1"
|
||||||
uuid: "npm:^9.0.1"
|
uuid: "npm:^9.0.1"
|
||||||
checksum: 10c0/1a0eb175f50787bbcaa585641bf710347b59f3d3426cbf0f83182a5574bf7a63beb3e5d66bb41506e2d50c3ee904d55670c85c7f1542018936dd5a4ce06726e8
|
checksum: 10c0/19b89e4f96df425d2d73e87fda1f82844bf7f3a1ba114073d0bf4052c9d5fe3eac9e6ca6d88ad8e36b65bae6dfcf69db5cb47828ef1c195419a94bd87ae2ff53
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -7354,11 +7354,11 @@ __metadata:
|
|||||||
"@noble/hashes": "npm:^1.4.0"
|
"@noble/hashes": "npm:^1.4.0"
|
||||||
"@scure/base": "npm:^1.1.6"
|
"@scure/base": "npm:^1.1.6"
|
||||||
"@snort/shared": "npm:^1.0.17"
|
"@snort/shared": "npm:^1.0.17"
|
||||||
"@snort/system": "npm:^1.5.6"
|
"@snort/system": "npm:^1.6.1"
|
||||||
"@snort/system-react": "npm:^1.5.6"
|
"@snort/system-react": "npm:^1.6.1"
|
||||||
"@snort/system-wasm": "npm:^1.0.5"
|
"@snort/system-wasm": "npm:^1.0.5"
|
||||||
"@snort/wallet": "npm:^0.2.1"
|
"@snort/wallet": "npm:^0.2.4"
|
||||||
"@snort/worker-relay": "npm:^1.3.0"
|
"@snort/worker-relay": "npm:^1.3.1"
|
||||||
"@szhsin/react-menu": "npm:^4.1.0"
|
"@szhsin/react-menu": "npm:^4.1.0"
|
||||||
"@testing-library/dom": "npm:^9.3.1"
|
"@testing-library/dom": "npm:^9.3.1"
|
||||||
"@types/node": "npm:^20.12.12"
|
"@types/node": "npm:^20.12.12"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user