mirror of
https://github.com/aljazceru/njump.git
synced 2025-12-17 14:24:27 +01:00
Add support for live events, kind:30311
This commit is contained in:
40
data.go
40
data.go
@@ -87,6 +87,7 @@ type Data struct {
|
|||||||
content string
|
content string
|
||||||
alt string
|
alt string
|
||||||
kind1063Metadata *Kind1063Metadata
|
kind1063Metadata *Kind1063Metadata
|
||||||
|
kind30311Metadata *Kind30311Metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
type Kind1063Metadata struct {
|
type Kind1063Metadata struct {
|
||||||
@@ -104,6 +105,16 @@ type Kind1063Metadata struct {
|
|||||||
Thumb string
|
Thumb string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Kind30311Metadata struct {
|
||||||
|
Title string
|
||||||
|
Summary string
|
||||||
|
Image string
|
||||||
|
Status string
|
||||||
|
Host sdk.ProfileMetadata
|
||||||
|
HostNpub string
|
||||||
|
Tags []string
|
||||||
|
}
|
||||||
|
|
||||||
func (fm Kind1063Metadata) IsVideo() bool { return strings.Split(fm.M, "/")[0] == "video" }
|
func (fm Kind1063Metadata) IsVideo() bool { return strings.Split(fm.M, "/")[0] == "video" }
|
||||||
func (fm Kind1063Metadata) IsImage() bool { return strings.Split(fm.M, "/")[0] == "image" }
|
func (fm Kind1063Metadata) IsImage() bool { return strings.Split(fm.M, "/")[0] == "image" }
|
||||||
func (fm Kind1063Metadata) DisplayImage() string {
|
func (fm Kind1063Metadata) DisplayImage() string {
|
||||||
@@ -246,6 +257,35 @@ func grabData(ctx context.Context, code string, isProfileSitemap bool) (*Data, e
|
|||||||
if tag := event.Tags.GetFirst([]string{"summary", ""}); tag != nil {
|
if tag := event.Tags.GetFirst([]string{"summary", ""}); tag != nil {
|
||||||
data.kind1063Metadata.Summary = (*tag)[1]
|
data.kind1063Metadata.Summary = (*tag)[1]
|
||||||
}
|
}
|
||||||
|
case 30311:
|
||||||
|
data.templateId = LiveEvent
|
||||||
|
d := event.Tags.GetFirst([]string{"d", ""})
|
||||||
|
data.naddr, _ = nip19.EncodeEntity(event.PubKey, event.Kind, d.Value(), data.relays)
|
||||||
|
data.kind30311Metadata = &Kind30311Metadata{}
|
||||||
|
|
||||||
|
if tag := event.Tags.GetFirst([]string{"title", ""}); tag != nil {
|
||||||
|
data.kind30311Metadata.Title = (*tag)[1]
|
||||||
|
}
|
||||||
|
if tag := event.Tags.GetFirst([]string{"summary", ""}); tag != nil {
|
||||||
|
data.kind30311Metadata.Summary = (*tag)[1]
|
||||||
|
}
|
||||||
|
if tag := event.Tags.GetFirst([]string{"image", ""}); tag != nil {
|
||||||
|
data.kind30311Metadata.Image = (*tag)[1]
|
||||||
|
}
|
||||||
|
if tag := event.Tags.GetFirst([]string{"status", ""}); tag != nil {
|
||||||
|
data.kind30311Metadata.Status = (*tag)[1]
|
||||||
|
}
|
||||||
|
pTags := event.Tags.GetAll([]string{"p", ""})
|
||||||
|
for _, p := range pTags {
|
||||||
|
if p[3] == "host" {
|
||||||
|
data.kind30311Metadata.Host = sdk.FetchProfileMetadata(ctx, pool, p[1], data.relays...)
|
||||||
|
data.kind30311Metadata.HostNpub = data.kind30311Metadata.Host.Npub()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tTags := event.Tags.GetAll([]string{"t", ""})
|
||||||
|
for _, t := range tTags {
|
||||||
|
data.kind30311Metadata.Tags = append(data.kind30311Metadata.Tags, t[1])
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
data.templateId = Other
|
data.templateId = Other
|
||||||
if event.Kind >= 30000 && event.Kind < 40000 {
|
if event.Kind >= 30000 && event.Kind < 40000 {
|
||||||
|
|||||||
33
pages.go
33
pages.go
@@ -18,6 +18,7 @@ const (
|
|||||||
LongForm
|
LongForm
|
||||||
TelegramInstantView
|
TelegramInstantView
|
||||||
FileMetadata
|
FileMetadata
|
||||||
|
LiveEvent
|
||||||
Other
|
Other
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -96,6 +97,7 @@ type DetailsPartial struct {
|
|||||||
|
|
||||||
// kind-specific stuff
|
// kind-specific stuff
|
||||||
FileMetadata *Kind1063Metadata
|
FileMetadata *Kind1063Metadata
|
||||||
|
LiveEvent *Kind30311Metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*DetailsPartial) TemplateText() string { return tmplDetails }
|
func (*DetailsPartial) TemplateText() string { return tmplDetails }
|
||||||
@@ -295,6 +297,37 @@ type FileMetadataPage struct {
|
|||||||
|
|
||||||
func (*FileMetadataPage) TemplateText() string { return tmplFileMetadata }
|
func (*FileMetadataPage) TemplateText() string { return tmplFileMetadata }
|
||||||
|
|
||||||
|
var (
|
||||||
|
//go:embed templates/live_event.html
|
||||||
|
tmplLiveEvent string
|
||||||
|
LiveEventTemplate = tmpl.MustCompile(&LiveEventPage{})
|
||||||
|
)
|
||||||
|
|
||||||
|
type LiveEventPage struct {
|
||||||
|
OpenGraphPartial `tmpl:"opengraph"`
|
||||||
|
HeadCommonPartial `tmpl:"head_common"`
|
||||||
|
TopPartial `tmpl:"top"`
|
||||||
|
DetailsPartial `tmpl:"details"`
|
||||||
|
ClientsPartial `tmpl:"clients"`
|
||||||
|
FooterPartial `tmpl:"footer"`
|
||||||
|
|
||||||
|
Content template.HTML
|
||||||
|
CreatedAt string
|
||||||
|
Metadata *sdk.ProfileMetadata
|
||||||
|
Npub string
|
||||||
|
NpubShort string
|
||||||
|
ParentLink template.HTML
|
||||||
|
SeenOn []string
|
||||||
|
Style Style
|
||||||
|
Subject string
|
||||||
|
TitleizedContent string
|
||||||
|
Alt string
|
||||||
|
|
||||||
|
LiveEvent Kind30311Metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*LiveEventPage) TemplateText() string { return tmplLiveEvent }
|
||||||
|
|
||||||
var (
|
var (
|
||||||
//go:embed templates/relay.html
|
//go:embed templates/relay.html
|
||||||
tmplRelay string
|
tmplRelay string
|
||||||
|
|||||||
@@ -245,6 +245,7 @@ func renderEvent(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// kind-specific stuff
|
// kind-specific stuff
|
||||||
FileMetadata: data.kind1063Metadata,
|
FileMetadata: data.kind1063Metadata,
|
||||||
|
LiveEvent: data.kind30311Metadata,
|
||||||
}
|
}
|
||||||
|
|
||||||
opengraph := OpenGraphPartial{
|
opengraph := OpenGraphPartial{
|
||||||
@@ -330,6 +331,33 @@ func renderEvent(w http.ResponseWriter, r *http.Request) {
|
|||||||
IsImage: data.kind1063Metadata.IsImage(),
|
IsImage: data.kind1063Metadata.IsImage(),
|
||||||
IsVideo: data.kind1063Metadata.IsVideo(),
|
IsVideo: data.kind1063Metadata.IsVideo(),
|
||||||
})
|
})
|
||||||
|
case LiveEvent:
|
||||||
|
opengraph.Image = data.kind30311Metadata.Image
|
||||||
|
|
||||||
|
err = LiveEventTemplate.Render(w, &LiveEventPage{
|
||||||
|
OpenGraphPartial: opengraph,
|
||||||
|
HeadCommonPartial: HeadCommonPartial{
|
||||||
|
IsProfile: false,
|
||||||
|
TailwindDebugStuff: tailwindDebugStuff,
|
||||||
|
NaddrNaked: data.naddrNaked,
|
||||||
|
NeventNaked: data.neventNaked,
|
||||||
|
},
|
||||||
|
DetailsPartial: detailsData,
|
||||||
|
ClientsPartial: ClientsPartial{
|
||||||
|
Clients: generateClientList(style, data.naddr, data.event),
|
||||||
|
},
|
||||||
|
|
||||||
|
CreatedAt: data.createdAt,
|
||||||
|
Metadata: data.metadata,
|
||||||
|
Npub: data.npub,
|
||||||
|
NpubShort: data.npubShort,
|
||||||
|
Style: style,
|
||||||
|
Subject: subject,
|
||||||
|
TitleizedContent: titleizedContent,
|
||||||
|
Alt: data.alt,
|
||||||
|
|
||||||
|
LiveEvent: *data.kind30311Metadata,
|
||||||
|
})
|
||||||
case Other:
|
case Other:
|
||||||
detailsData.HideDetails = false // always open this since we know nothing else about the event
|
detailsData.HideDetails = false // always open this since we know nothing else about the event
|
||||||
|
|
||||||
|
|||||||
117
templates/live_event.html
Normal file
117
templates/live_event.html
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html class="theme--default text-lg font-light print:text-base sm:text-xl">
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<head>
|
||||||
|
<title>Stream: {{.LiveEvent.Title}} by {{ .LiveEvent.Host.Name }}</title>
|
||||||
|
{{template "opengraph" .OpenGraphPartial}}
|
||||||
|
<!---->
|
||||||
|
{{template "head_common" .HeadCommonPartial}}
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body
|
||||||
|
class="mb-16 bg-white text-gray-600 dark:bg-neutral-900 dark:text-neutral-50 print:text-black"
|
||||||
|
>
|
||||||
|
{{template "top" .}}
|
||||||
|
|
||||||
|
<div class="mx-auto px-4 sm:flex sm:items-center sm:justify-center sm:px-0">
|
||||||
|
<div
|
||||||
|
class="w-full max-w-screen-2xl justify-between gap-10 overflow-visible print:w-full sm:flex sm:w-11/12 sm:px-4 md:w-10/12 lg:w-9/12 lg:gap-48vw"
|
||||||
|
>
|
||||||
|
<div class="w-full break-words print:w-full">
|
||||||
|
<header class="mb-4 max-w-full">
|
||||||
|
<a class="flex flex-wrap items-center" href="/{{.Npub}}">
|
||||||
|
<div
|
||||||
|
class="print:basis-1-12 imgclip mr-2 max-w-full basis-1/6 overflow-hidden sm:mr-4"
|
||||||
|
>
|
||||||
|
<img class="block h-auto w-full" src="{{.Metadata.Picture}}" />
|
||||||
|
</div>
|
||||||
|
<div class="block print:text-base sm:grow">
|
||||||
|
<div class="text-sm leading-4 sm:text-2xl">
|
||||||
|
{{.Metadata.Name}}
|
||||||
|
<!---->
|
||||||
|
{{if not (eq .Metadata.Name .Metadata.DisplayName)}}
|
||||||
|
<span class="text-sm text-stone-400 sm:text-xl"
|
||||||
|
>{{.Metadata.DisplayName}}</span
|
||||||
|
>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
<div class="text-sm leading-4 text-stone-400 sm:text-base">
|
||||||
|
{{.NpubShort}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</header>
|
||||||
|
<div class="w-full text-right text-sm text-stone-400">
|
||||||
|
{{.CreatedAt}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-full text-right text-sm text-stone-400">
|
||||||
|
{{ if not (eq "" .ParentLink) }} in reply to
|
||||||
|
<span class="text-strongpink">{{ .ParentLink }}</span> {{ end }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="-ml-4 mb-6 h-1.5 w-1/3 bg-zinc-100 dark:bg-zinc-700 sm:-ml-2.5"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<article
|
||||||
|
class="prose-cite:text-sm prose mb-6 max-w-full leading-5 dark:prose-invert prose-headings:font-light prose-p:m-0 prose-p:mb-2 prose-blockquote:mx-0 prose-blockquote:my-8 prose-blockquote:border-l-05rem prose-blockquote:border-solid prose-blockquote:border-l-gray-100 prose-blockquote:py-2 prose-blockquote:pl-4 prose-blockquote:pr-0 prose-ol:m-0 prose-ol:p-0 prose-ol:pl-4 prose-ul:m-0 prose-ul:p-0 prose-ul:pl-4 prose-li:mb-2 dark:prose-blockquote:border-l-zinc-800 sm:prose-a:text-justify"
|
||||||
|
>
|
||||||
|
<h1 class="text-2xl">
|
||||||
|
{{.LiveEvent.Title}} {{ if (eq "ended" .LiveEvent.Status)}}
|
||||||
|
<span
|
||||||
|
class="ml-4 rounded bg-neutral-400 px-4 py-1 text-base text-white dark:bg-neutral-700"
|
||||||
|
>Ended</span
|
||||||
|
>
|
||||||
|
{{ else if (eq "live" .LiveEvent.Status)}}
|
||||||
|
<span
|
||||||
|
class="ml-4 rounded bg-strongpink px-4 py-1 text-base text-white"
|
||||||
|
>Live now!</span
|
||||||
|
>
|
||||||
|
{{ end }}
|
||||||
|
</h1>
|
||||||
|
<div class="mb-4">
|
||||||
|
Streaming hosted by
|
||||||
|
<a href="/{{ .LiveEvent.HostNpub }}"
|
||||||
|
>{{ .LiveEvent.Host.Name }}</a
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<!-- main content -->
|
||||||
|
<div class="mb-4">
|
||||||
|
{{ range .LiveEvent.Tags }}
|
||||||
|
<span
|
||||||
|
class="mr-2 rounded bg-neutral-200 px-2 dark:bg-neutral-700 dark:text-white"
|
||||||
|
>{{ . }}</span
|
||||||
|
>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
{{ if (not (eq "" .LiveEvent.Summary))}}
|
||||||
|
<div>{{ .LiveEvent.Summary }}</div>
|
||||||
|
{{ end }} {{ if (not (eq "" .LiveEvent.Image))}}
|
||||||
|
<img
|
||||||
|
src="{{ .LiveEvent.Image }}"
|
||||||
|
alt="{{ .Alt }}"
|
||||||
|
_="on load
|
||||||
|
repeat until '{{ .LiveEvent.Status }}' == 'ended'
|
||||||
|
set @src to '{{ .LiveEvent.Image }}'
|
||||||
|
wait 5s
|
||||||
|
end
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
{{ end }}
|
||||||
|
</article>
|
||||||
|
|
||||||
|
{{template "details" .DetailsPartial}}
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="-ml-4 mb-6 h-1.5 w-1/3 bg-zinc-100 dark:bg-zinc-700 sm:-ml-2.5"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{template "clients" .ClientsPartial}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{template "footer" .}}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
6
utils.go
6
utils.go
@@ -163,6 +163,12 @@ func generateClientList(style Style, code string, event *nostr.Event) []ClientRe
|
|||||||
{ID: "snort", Name: "Snort", URL: template.URL("https://snort.social/p/" + code)},
|
{ID: "snort", Name: "Snort", URL: template.URL("https://snort.social/p/" + code)},
|
||||||
{ID: "coracle", Name: "Coracle", URL: template.URL("https://coracle.social/" + code)},
|
{ID: "coracle", Name: "Coracle", URL: template.URL("https://coracle.social/" + code)},
|
||||||
}
|
}
|
||||||
|
} else if event.Kind == 30311 {
|
||||||
|
return []ClientReference{
|
||||||
|
{ID: "native", Name: "your native client", URL: template.URL("nostr:" + code)},
|
||||||
|
{ID: "zap.stream", Name: "zap.stream", URL: template.URL("https://zap.stream/" + code)},
|
||||||
|
{ID: "nostrudel", Name: "Nostrudel", URL: template.URL("https://nostrudel.ninja/#/streams/" + code)},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user