Add support for live events, kind:30311

This commit is contained in:
Daniele Tonon
2023-11-06 23:28:20 +01:00
parent 6d6defa419
commit b5a4cfdfdb
5 changed files with 224 additions and 0 deletions

40
data.go
View File

@@ -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 {

View File

@@ -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

View File

@@ -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
View 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>

View File

@@ -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
} }