diff --git a/go.mod b/go.mod
index 147824dd..d98b3625 100644
--- a/go.mod
+++ b/go.mod
@@ -63,9 +63,9 @@ require (
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/slok/go-http-metrics v0.13.0
github.com/starttoaster/prometheus-exporter-scraper v0.0.1
- github.com/streamplace/atproto-oauth-golang v0.0.0-20260413212710-98956064d06c
- github.com/streamplace/muxl/go v0.0.0-20260528173527-91f80c95ca45
- github.com/streamplace/oatproxy v0.0.0-20260602023256-6c7e423d20a0
+ github.com/streamplace/atproto-oauth-golang v0.0.0-20250619231223-a9c04fb888ac
+ github.com/streamplace/muxl/go v0.2.1
+ github.com/streamplace/oatproxy v0.0.0-20260508220721-f8852e8dbf44
github.com/stretchr/testify v1.11.1
github.com/tdewolff/canvas v0.0.0-20250728095813-50d4cb1eee71
github.com/tus/tusd/v2 v2.8.0
@@ -496,7 +496,7 @@ require (
github.com/tdewolff/minify/v2 v2.23.4 // indirect
github.com/tdewolff/parse/v2 v2.8.0 // indirect
github.com/tetafro/godot v1.5.1 // indirect
- github.com/tetratelabs/wazero v1.11.0 // indirect
+ github.com/tetratelabs/wazero v1.12.0 // indirect
github.com/thales-e-security/pool v0.0.2 // indirect
github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 // indirect
github.com/timonwong/loggercheck v0.11.0 // indirect
diff --git a/go.sum b/go.sum
index 29464487..05aa64ea 100644
--- a/go.sum
+++ b/go.sum
@@ -1369,16 +1369,16 @@ github.com/starttoaster/prometheus-exporter-scraper v0.0.1 h1:E3Zl3ho3v3Yt2Yk7i9
github.com/starttoaster/prometheus-exporter-scraper v0.0.1/go.mod h1:qNccVOSo0np2jYi6DolRyUICFcMkhH5k1Gs7CYxav1w=
github.com/stbenjam/no-sprintf-host-port v0.2.0 h1:i8pxvGrt1+4G0czLr/WnmyH7zbZ8Bg8etvARQ1rpyl4=
github.com/stbenjam/no-sprintf-host-port v0.2.0/go.mod h1:eL0bQ9PasS0hsyTyfTjjG+E80QIyPnBVQbYZyv20Jfk=
-github.com/streamplace/atproto-oauth-golang v0.0.0-20260413212710-98956064d06c h1:IzEPU2O4iL58Nb7aw+7lB9ttnesEwOVVE5oV9NEXemM=
-github.com/streamplace/atproto-oauth-golang v0.0.0-20260413212710-98956064d06c/go.mod h1:9LlKkqciiO5lRfbX0n4Wn5KNY9nvFb4R3by8FdW2TWc=
+github.com/streamplace/atproto-oauth-golang v0.0.0-20250619231223-a9c04fb888ac h1:heVM4CGox3kfJclSmagsI3hvKk7W52EegnFqqs9CSYk=
+github.com/streamplace/atproto-oauth-golang v0.0.0-20250619231223-a9c04fb888ac/go.mod h1:9LlKkqciiO5lRfbX0n4Wn5KNY9nvFb4R3by8FdW2TWc=
github.com/streamplace/go-dpop v0.0.0-20250510031900-c897158a8ad4 h1:L1fS4HJSaAyNnkwfuZubgfeZy8rkWmA0cMtH5Z0HqNc=
github.com/streamplace/go-dpop v0.0.0-20250510031900-c897158a8ad4/go.mod h1:bGUXY9Wd4mnd+XUrOYZr358J2f6z9QO/dLhL1SsiD+0=
github.com/streamplace/indigo v0.0.0-20260218231908-939cdaf0c507 h1:e8M3qPLr37NxEjlr18TaAwGP+OVyherVjgUG5VVmgWI=
github.com/streamplace/indigo v0.0.0-20260218231908-939cdaf0c507/go.mod h1:Pm2I1+iDXn/hLbF7XCg/DsZi6uDCiOo7hZGWprSM7k0=
-github.com/streamplace/muxl/go v0.0.0-20260528173527-91f80c95ca45 h1:v9KQ+tQnKwbSvS5orbszrd49ucySYi4F9npxoU2sLFU=
-github.com/streamplace/muxl/go v0.0.0-20260528173527-91f80c95ca45/go.mod h1:aCyYTW3o6c1Kush9UJ/Yv6EYMUbj8l8GTD7cHKcSxw8=
-github.com/streamplace/oatproxy v0.0.0-20260602023256-6c7e423d20a0 h1:FtJV63T621Dg5rQh049uTCx6kGQiiAXCReICz3hJSRQ=
-github.com/streamplace/oatproxy v0.0.0-20260602023256-6c7e423d20a0/go.mod h1:wpY+T/wE00jrUhgh2dKXbbE91D36u86KGlENK/hWFkE=
+github.com/streamplace/muxl/go v0.2.1 h1:ITjmjP3OREBgLowafspw7R5kGjmKX3uE6/ATAUIAHPk=
+github.com/streamplace/muxl/go v0.2.1/go.mod h1:aCyYTW3o6c1Kush9UJ/Yv6EYMUbj8l8GTD7cHKcSxw8=
+github.com/streamplace/oatproxy v0.0.0-20260508220721-f8852e8dbf44 h1:b38ToXNQCvKqBlx0SeQYUOhx6uqqnrF5AL6B0Z3zzdk=
+github.com/streamplace/oatproxy v0.0.0-20260508220721-f8852e8dbf44/go.mod h1:j1+zdhe1IC0+PTE2rIXk2LqYn4Lz2x9SJaV3eHkQyfs=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
@@ -1424,8 +1424,8 @@ github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpR
github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY=
github.com/tetafro/godot v1.5.1 h1:PZnjCol4+FqaEzvZg5+O8IY2P3hfY9JzRBNPv1pEDS4=
github.com/tetafro/godot v1.5.1/go.mod h1:cCdPtEndkmqqrhiCfkmxDodMQJ/f3L1BCNskCUZdTwk=
-github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA=
-github.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU=
+github.com/tetratelabs/wazero v1.12.0 h1:DuWcpNu/FzgEXgGBDp8J1Spc+CWOvvtvVyjKlaZopYU=
+github.com/tetratelabs/wazero v1.12.0/go.mod h1:LvKtzl2RqO4gyF27BiXU+nKAjcV8f38U+kP/q2vgxh0=
github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg=
github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU=
github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 h1:9LPGD+jzxMlnk5r6+hJnar67cgpDIz/iyD+rfl5r2Vk=
diff --git a/js/app/components/mobile/ui.tsx b/js/app/components/mobile/ui.tsx
index a9c25be0..2c40879c 100644
--- a/js/app/components/mobile/ui.tsx
+++ b/js/app/components/mobile/ui.tsx
@@ -160,7 +160,18 @@ export function MobileUi({
const hover = Gesture.Hover().onChange(onPlayerHover);
const pan = Gesture.Pan().onChange(onPlayerHover);
const tap = Gesture.Tap().onEnd(onPlayerHover);
-
+ let chatSection: React.ReactNode = null;
+ if (
+ mode !== "vod" &&
+ !hideMobileChat &&
+ !isSelfAndNotLive &&
+ playerIsReady &&
+ isPortrait
+ ) {
+ chatSection = (
+
+ );
+ }
const combined = Gesture.Race(hover, pan, tap);
return (
<>
@@ -301,10 +312,7 @@ export function MobileUi({
- {hideMobileChat ||
- (isPortrait && !isSelfAndNotLive && playerIsReady && (
-
- ))}
+ {chatSection}
>
);
}
diff --git a/js/components/src/components/mobile-player/video-async.native.tsx b/js/components/src/components/mobile-player/video-async.native.tsx
index 8ad93783..da0700f6 100644
--- a/js/components/src/components/mobile-player/video-async.native.tsx
+++ b/js/components/src/components/mobile-player/video-async.native.tsx
@@ -223,7 +223,7 @@ export function NativeVideo(props?: {
{
setFullscreen(true);
}}
diff --git a/js/components/src/hooks/useSegmentDimensions.tsx b/js/components/src/hooks/useSegmentDimensions.tsx
index bb95718e..f61b6835 100644
--- a/js/components/src/hooks/useSegmentDimensions.tsx
+++ b/js/components/src/hooks/useSegmentDimensions.tsx
@@ -1,18 +1,52 @@
-import { useLivestreamStore } from "../livestream-store";
+import {
+ PlaceStreamMediaTrack,
+ PlaceStreamSegment,
+ VideoViewHydrated,
+} from "streamplace";
+import { useLivestreamStoreOptional } from "../livestream-store";
+import { useVideoStoreOptional } from "../video-store";
export function useSegmentDimensions() {
- const latestSegment = useLivestreamStore((x) => x.segment);
+ const video = useVideoStoreOptional((x) => x.video);
+ const latestSegment = useLivestreamStoreOptional((x) => x.segment);
- let seg = latestSegment?.video && latestSegment.video[0];
+ let height = 0;
+ let width = 0;
+ if (video) {
+ const meta = getVideoMetadata(video);
+ if (meta) {
+ height = meta.height || 0;
+ width = meta.width || 0;
+ }
+ } else {
+ let seg = latestSegment?.video && latestSegment.video[0];
- let ratio = {
- height: seg?.height || 0,
- width: seg?.width || 0,
- };
+ height = seg?.height || 0;
+ width = seg?.width || 0;
+ }
return {
- isPlayerRatioGreater: ratio.width > ratio.height,
- height: ratio.height,
- width: ratio.width,
+ isPlayerRatioGreater: width > height,
+ height: height,
+ width: width,
};
}
+
+function getVideoMetadata(
+ view: VideoViewHydrated,
+): PlaceStreamSegment.Video | null {
+ if (!view.tracks) {
+ return null;
+ }
+ for (const track of view.tracks) {
+ const meta = track.record.metadata;
+ if (!PlaceStreamMediaTrack.isCommonMetadata(meta)) {
+ continue;
+ }
+ if (!meta.video) {
+ continue;
+ }
+ return meta.video;
+ }
+ return null;
+}
diff --git a/js/docs/src/content/docs/lex-reference/media/place-stream-media-getvideo.md b/js/docs/src/content/docs/lex-reference/media/place-stream-media-getvideo.md
index ee6c54c8..ecbd9f61 100644
--- a/js/docs/src/content/docs/lex-reference/media/place-stream-media-getvideo.md
+++ b/js/docs/src/content/docs/lex-reference/media/place-stream-media-getvideo.md
@@ -49,6 +49,7 @@ Get a hydrated view of a place.stream.video record — the record itself plus au
| `author` | [`app.bsky.actor.defs#profileViewBasic`](https://github.com/bluesky-social/atproto/tree/main/lexicons/app/bsky/actor/defs.json#profileViewBasic) | ✅ | | |
| `record` | `unknown` | ✅ | | |
| `viewCounts` | [`#viewCountSummary`](#viewcountsummary) | ✅ | Aggregated view counts across every indexed reporter. Always present; zero-valued (count=0, reporters=0) when no place.stream.media.viewCount records have been observed yet, so consumers can render a count unconditionally. | |
+| `tracks` | Array of [`place.stream.media.track#trackView`](/lex-reference/place-stream-media-track#trackview) | ❌ | | |
---
@@ -129,6 +130,13 @@ Sums across every place.stream.media.viewCount record indexed for this video, re
"type": "ref",
"ref": "#viewCountSummary",
"description": "Aggregated view counts across every indexed reporter. Always present; zero-valued (count=0, reporters=0) when no place.stream.media.viewCount records have been observed yet, so consumers can render a count unconditionally."
+ },
+ "tracks": {
+ "type": "array",
+ "items": {
+ "type": "ref",
+ "ref": "place.stream.media.track#trackView"
+ }
}
}
},
diff --git a/js/docs/src/content/docs/lex-reference/media/place-stream-media-track.md b/js/docs/src/content/docs/lex-reference/media/place-stream-media-track.md
index 74f8bafe..b948bd80 100644
--- a/js/docs/src/content/docs/lex-reference/media/place-stream-media-track.md
+++ b/js/docs/src/content/docs/lex-reference/media/place-stream-media-track.md
@@ -47,6 +47,23 @@ Metadata common to all media types. Contains subobjects for other media types.
---
+
+
+### `trackView`
+
+**Type:** `object`
+
+**Properties:**
+
+| Name | Type | Req'd | Description | Constraints |
+| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ----- | ----------- | ---------------- |
+| `uri` | `string` | ✅ | | Format: `at-uri` |
+| `cid` | `string` | ✅ | | Format: `cid` |
+| `author` | [`app.bsky.actor.defs#profileViewBasic`](https://github.com/bluesky-social/atproto/tree/main/lexicons/app/bsky/actor/defs.json#profileViewBasic) | ✅ | | |
+| `record` | `unknown` | ✅ | | |
+
+---
+
## Lexicon Source
```json
@@ -105,6 +122,27 @@ Metadata common to all media types. Contains subobjects for other media types.
"ref": "place.stream.segment#audio"
}
}
+ },
+ "trackView": {
+ "type": "object",
+ "required": ["uri", "cid", "author", "record"],
+ "properties": {
+ "uri": {
+ "type": "string",
+ "format": "at-uri"
+ },
+ "cid": {
+ "type": "string",
+ "format": "cid"
+ },
+ "author": {
+ "type": "ref",
+ "ref": "app.bsky.actor.defs#profileViewBasic"
+ },
+ "record": {
+ "type": "unknown"
+ }
+ }
}
}
}
diff --git a/js/docs/src/content/docs/lex-reference/openapi.json b/js/docs/src/content/docs/lex-reference/openapi.json
index ef53b659..9a5e5efd 100644
--- a/js/docs/src/content/docs/lex-reference/openapi.json
+++ b/js/docs/src/content/docs/lex-reference/openapi.json
@@ -4732,6 +4732,12 @@
"viewCounts": {
"$ref": "#/components/schemas/place.stream.media.getVideo_viewCountSummary",
"description": "Aggregated view counts across every indexed reporter. Always present; zero-valued (count=0, reporters=0) when no place.stream.media.viewCount records have been observed yet, so consumers can render a count unconditionally."
+ },
+ "tracks": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/place.stream.media.track_trackView"
+ }
}
},
"required": ["uri", "cid", "author", "record", "viewCounts"]
@@ -5100,6 +5106,24 @@
},
"required": ["count", "bytes", "durationMs", "reporters"]
},
+ "place.stream.media.track_trackView": {
+ "type": "object",
+ "properties": {
+ "uri": {
+ "type": "string",
+ "format": "uri"
+ },
+ "cid": {
+ "type": "string",
+ "format": "cid"
+ },
+ "author": {
+ "$ref": "#/components/schemas/app.bsky.actor.defs_profileViewBasic"
+ },
+ "record": {}
+ },
+ "required": ["uri", "cid", "author", "record"]
+ },
"place.stream.video": {
"description": "Unknown type"
},
diff --git a/js/streamplace/src/useful-types.ts b/js/streamplace/src/useful-types.ts
index 230bf200..263d4f8e 100644
--- a/js/streamplace/src/useful-types.ts
+++ b/js/streamplace/src/useful-types.ts
@@ -4,6 +4,7 @@ import {
PlaceStreamChatPinnedRecord,
PlaceStreamLivestream,
PlaceStreamMediaGetVideo,
+ PlaceStreamMediaTrack,
} from "./lexicons";
export interface LivestreamViewHydrated
@@ -23,4 +24,9 @@ export interface PinnedRecordViewHydrated
export interface VideoViewHydrated extends PlaceStreamMediaGetVideo.VideoView {
record: PlaceStreamLivestream.Record;
+ tracks: TrackViewHydrated[];
+}
+
+export interface TrackViewHydrated extends PlaceStreamMediaTrack.TrackView {
+ record: PlaceStreamMediaTrack.Record;
}
diff --git a/lexicons/place/stream/media/getVideo.json b/lexicons/place/stream/media/getVideo.json
index d210cd52..86f5ae1e 100644
--- a/lexicons/place/stream/media/getVideo.json
+++ b/lexicons/place/stream/media/getVideo.json
@@ -45,6 +45,13 @@
"type": "ref",
"ref": "#viewCountSummary",
"description": "Aggregated view counts across every indexed reporter. Always present; zero-valued (count=0, reporters=0) when no place.stream.media.viewCount records have been observed yet, so consumers can render a count unconditionally."
+ },
+ "tracks": {
+ "type": "array",
+ "items": {
+ "type": "ref",
+ "ref": "place.stream.media.track#trackView"
+ }
}
}
},
diff --git a/lexicons/place/stream/media/track.json b/lexicons/place/stream/media/track.json
index e2ea1fe3..8e0af8ab 100644
--- a/lexicons/place/stream/media/track.json
+++ b/lexicons/place/stream/media/track.json
@@ -53,6 +53,19 @@
"ref": "place.stream.segment#audio"
}
}
+ },
+ "trackView": {
+ "type": "object",
+ "required": ["uri", "cid", "author", "record"],
+ "properties": {
+ "uri": { "type": "string", "format": "at-uri" },
+ "cid": { "type": "string", "format": "cid" },
+ "author": {
+ "type": "ref",
+ "ref": "app.bsky.actor.defs#profileViewBasic"
+ },
+ "record": { "type": "unknown" }
+ }
}
}
}
diff --git a/pkg/linking/linking.go b/pkg/linking/linking.go
index 74899838..aafda0cb 100644
--- a/pkg/linking/linking.go
+++ b/pkg/linking/linking.go
@@ -214,24 +214,19 @@ func (l *Linker) GenerateVideoCard(ctx context.Context, u *url.URL, vv *streampl
imageURL = cardURL.String()
}
- titleStr := fmt.Sprintf("@%s's video on ", authorHandle)
-
- // Define all meta tags
+ // Define all meta tags. The title/description pair is appended after we
+ // resolve the node's branding title below: the card headline is the
+ // video's own title, and the author + node go in the description.
metaTags := []MetaTag{
- // Basic meta
- {Type: "name", Key: "description", Content: video.Title},
-
// Facebook Meta Tags
{Type: "property", Key: "og:url", Content: outURL},
{Type: "property", Key: "og:type", Content: "video.other"},
- {Type: "property", Key: "og:description", Content: video.Title},
{Type: "property", Key: "og:image", Content: imageURL},
// Twitter Meta Tags
{Type: "name", Key: "twitter:card", Content: "summary_large_image"},
{Type: "property", Key: "twitter:domain", Content: u.Host},
{Type: "property", Key: "twitter:url", Content: outURL},
- {Type: "name", Key: "twitter:description", Content: video.Title},
{Type: "name", Key: "twitter:image", Content: imageURL},
}
@@ -261,20 +256,21 @@ func (l *Linker) GenerateVideoCard(ctx context.Context, u *url.URL, vv *streampl
}
}
- // do twitter/og title after
- metaTags = append(metaTags, MetaTag{
- Type: "property",
- Key: "og:title",
- Content: fmt.Sprintf("%s%s", titleStr, brandingTitle),
- })
- metaTags = append(metaTags, MetaTag{
- Type: "name",
- Key: "twitter:title",
- Content: fmt.Sprintf("%s%s", titleStr, brandingTitle),
- })
+ // The card headline is the video's own title; the author + node name go
+ // in the description. Appended after the branding loop so the description
+ // can reference the resolved branding title.
+ title := video.Title
+ description := fmt.Sprintf("@%s's video on %s", authorHandle, brandingTitle)
+ metaTags = append(metaTags,
+ MetaTag{Type: "name", Key: "description", Content: description},
+ MetaTag{Type: "property", Key: "og:description", Content: description},
+ MetaTag{Type: "property", Key: "og:title", Content: title},
+ MetaTag{Type: "name", Key: "twitter:description", Content: description},
+ MetaTag{Type: "name", Key: "twitter:title", Content: title},
+ )
return l.GenerateHTML(ctx, &PageConfig{
- Title: fmt.Sprintf("%s%s", titleStr, brandingTitle),
+ Title: title,
Metas: metaTags,
SentryDSN: sentryDSN,
})
diff --git a/pkg/linking/linking_test.go b/pkg/linking/linking_test.go
index e6b23de5..2b9c4132 100644
--- a/pkg/linking/linking_test.go
+++ b/pkg/linking/linking_test.go
@@ -105,7 +105,10 @@ func TestGenerateVideoCard(t *testing.T) {
require.NoError(t, err)
linkStr := string(linkCard)
require.True(t, strings.Contains(linkStr, "iame.li"), "should contain the author handle")
- require.True(t, strings.Contains(linkStr, video.Title), "should contain the video title")
+ require.True(t, strings.Contains(linkStr, ""+video.Title+""),
+ "page title should be the video's own title")
+ require.True(t, strings.Contains(linkStr, `content="`+video.Title+`"`),
+ "og:title/twitter:title should be the video's own title")
require.True(t, strings.Contains(linkStr,
"https://cdn.bsky.app/img/feed_thumbnail/plain/did:plc:2zmxikig2sj7gqaezl5gntae/"+thumbCID+"@jpeg"),
"og:image should be the video thumbnail served via the bsky CDN")
diff --git a/pkg/model/video.go b/pkg/model/video.go
index 5ac3de68..ae4f8436 100644
--- a/pkg/model/video.go
+++ b/pkg/model/video.go
@@ -114,12 +114,36 @@ func (m *DBModel) GetVideoView(ctx context.Context, uri string) (*streamplace.Me
return nil, err
}
+ tracks := []*streamplace.MediaTrack_TrackView{}
+ if rec.Source.MediaDefs_SourceTracks != nil {
+ for _, track := range rec.Source.MediaDefs_SourceTracks.Tracks {
+ t, err := m.GetMediaTrackByURI(ctx, track.Uri)
+ if err != nil {
+ return nil, fmt.Errorf("get media track by uri: %w", err)
+ }
+ if t == nil {
+ continue
+ }
+ cid, err := spid.GetCID(t)
+ if err != nil {
+ return nil, fmt.Errorf("get cid: %w", err)
+ }
+ tracks = append(tracks, &streamplace.MediaTrack_TrackView{
+ Record: &lexutil.LexiconTypeDecoder{Val: t},
+ Uri: track.Uri,
+ Cid: cid.String(),
+ Author: author,
+ })
+ }
+ }
+
return &streamplace.MediaGetVideo_VideoView{
Uri: row.URI,
Cid: row.CID,
Author: author,
Record: &lexutil.LexiconTypeDecoder{Val: rec},
ViewCounts: summary,
+ Tracks: tracks,
}, nil
}
diff --git a/pkg/streamplace/mediagetVideo.go b/pkg/streamplace/mediagetVideo.go
index 7f365c97..028b80e1 100644
--- a/pkg/streamplace/mediagetVideo.go
+++ b/pkg/streamplace/mediagetVideo.go
@@ -16,6 +16,7 @@ type MediaGetVideo_VideoView struct {
Author *appbsky.ActorDefs_ProfileViewBasic `json:"author" cborgen:"author"`
Cid string `json:"cid" cborgen:"cid"`
Record *lexutil.LexiconTypeDecoder `json:"record" cborgen:"record"`
+ Tracks []*MediaTrack_TrackView `json:"tracks,omitempty" cborgen:"tracks,omitempty"`
Uri string `json:"uri" cborgen:"uri"`
// viewCounts: Aggregated view counts across every indexed reporter. Always present; zero-valued (count=0, reporters=0) when no place.stream.media.viewCount records have been observed yet, so consumers can render a count unconditionally.
ViewCounts *MediaGetVideo_ViewCountSummary `json:"viewCounts" cborgen:"viewCounts"`
diff --git a/pkg/streamplace/mediatrack.go b/pkg/streamplace/mediatrack.go
index 35d00403..38558888 100644
--- a/pkg/streamplace/mediatrack.go
+++ b/pkg/streamplace/mediatrack.go
@@ -11,6 +11,7 @@ import (
"io"
comatproto "github.com/bluesky-social/indigo/api/atproto"
+ appbsky "github.com/bluesky-social/indigo/api/bsky"
lexutil "github.com/bluesky-social/indigo/lex/util"
cbg "github.com/whyrusleeping/cbor-gen"
)
@@ -149,3 +150,11 @@ func (t *MediaTrack_Track) UnmarshalCBOR(r io.Reader) error {
return nil
}
}
+
+// MediaTrack_TrackView is a "trackView" in the place.stream.media.track schema.
+type MediaTrack_TrackView struct {
+ Author *appbsky.ActorDefs_ProfileViewBasic `json:"author" cborgen:"author"`
+ Cid string `json:"cid" cborgen:"cid"`
+ Record *lexutil.LexiconTypeDecoder `json:"record" cborgen:"record"`
+ Uri string `json:"uri" cborgen:"uri"`
+}