your personal website on atproto - mirror
blento.app
1import { user, listRecords, getCDNImageBlobUrl } from '$lib/atproto';
2import type { CardDefinition } from '../../types';
3import LivestreamCard from './LivestreamCard.svelte';
4import LivestreamEmbedCard from './LivestreamEmbedCard.svelte';
5
6export const LivestreamCardDefitition = {
7 type: 'latestLivestream',
8 contentComponent: LivestreamCard,
9 createNew: (card) => {
10 card.w = 4;
11 card.h = 4;
12 card.mobileH = 8;
13 card.mobileW = 8;
14
15 card.cardType = 'latestLivestream';
16 },
17 loadData: async (items, { did }) => {
18 const records = await listRecords({ did, collection: 'place.stream.livestream', limit: 3 });
19
20 let latestLivestream:
21 | {
22 createdAt: string;
23 title: string;
24 thumb?: string;
25 href: string;
26 online?: boolean;
27 }
28 | undefined;
29
30 if (records?.length) {
31 const latest = JSON.parse(JSON.stringify(records?.[0]));
32
33 latestLivestream = {
34 createdAt: latest.value.createdAt,
35 title: latest.value?.title as string,
36 thumb: latest.value?.thumb?.ref?.$link
37 ? getCDNImageBlobUrl({ blob: latest.value.thumb, did })
38 : undefined,
39 href: latest.value?.canonicalUrl || latest.value.url,
40 online: undefined
41 };
42 }
43
44 if (latestLivestream) {
45 try {
46 const segmentsResponse = await fetch(
47 'https://stream.place/xrpc/place.stream.live.getSegments?userDID=' +
48 encodeURIComponent(did)
49 );
50 const segments = await segmentsResponse.json();
51
52 const lastSegment = segments.segments[0];
53 const startTime = new Date(lastSegment.record.startTime).getTime();
54
55 const FIVE_MINUTES = 5 * 60 * 1000;
56 const now = Date.now();
57
58 latestLivestream.online = now - startTime <= FIVE_MINUTES;
59 } catch (error) {
60 console.error(error);
61 }
62 }
63
64 return latestLivestream;
65 },
66
67 onUrlHandler: (url, item) => {
68 if (url === 'https://stream.place/' + user.profile?.handle) {
69 item.w = 4;
70 item.h = 4;
71 item.mobileH = 8;
72 item.mobileW = 8;
73 item.cardData.href = 'https://stream.place/' + user.profile?.handle;
74 return item;
75 }
76 },
77
78 canChange: (item) => item.cardData.href === 'https://stream.place/' + user.profile?.handle,
79
80 urlHandlerPriority: 5,
81
82 name: 'Latest Livestream (stream.place)',
83 keywords: ['stream', 'live', 'broadcast', 'video'],
84 groups: ['Media'],
85 icon: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="size-4"><path stroke-linecap="round" stroke-linejoin="round" d="m15.75 10.5 4.72-4.72a.75.75 0 0 1 1.28.53v11.38a.75.75 0 0 1-1.28.53l-4.72-4.72M4.5 18.75h9a2.25 2.25 0 0 0 2.25-2.25v-9a2.25 2.25 0 0 0-2.25-2.25h-9A2.25 2.25 0 0 0 2.25 7.5v9a2.25 2.25 0 0 0 2.25 2.25Z" /></svg>`
86} as CardDefinition & { type: 'latestLivestream' };
87
88export const LivestreamEmbedCardDefitition = {
89 type: 'livestreamEmbed',
90 contentComponent: LivestreamEmbedCard,
91 createNew: (card) => {
92 card.w = 4;
93 card.h = 2;
94 card.mobileW = 8;
95 card.mobileH = 4;
96
97 card.cardData = {
98 href: 'https://stream.place/' + user.profile?.handle,
99 embed: 'https://stream.place/embed/' + user.profile?.handle
100 };
101 }
102 // canChange: (item) => item.cardData.href === 'https://stream.place/' + client.profile?.handle,
103
104 // change: (item) => {
105 // item.cardData.embed = 'https://stream.place/embed/' + client.profile?.handle;
106 // },
107} as CardDefinition & { type: 'livestreamEmbed' };