+37
-16
src/components/LookUp.vue
+37
-16
src/components/LookUp.vue
···
10
10
} from "@atcute/identity-resolver";
11
11
import {AtpAgent} from "@atproto/api";
12
12
13
+
// Types for fm.teal.alpha.feed.play
14
+
interface PlayArtist {
15
+
artistMbId?: string;
16
+
artistName: string;
17
+
}
18
+
interface PlayValue {
19
+
$type?: "fm.teal.alpha.feed.play";
20
+
artists?: PlayArtist[];
21
+
// legacy support: earlier records may use artistNames
22
+
artistNames?: string[];
23
+
trackName: string;
24
+
playedTime?: string; // ISO string
25
+
releaseMbId?: string;
26
+
releaseName?: string;
27
+
recordingMbId?: string;
28
+
submissionClientAgent?: string;
29
+
}
30
+
interface ListRecord<T> {
31
+
uri: string;
32
+
cid: string;
33
+
value: T;
34
+
}
35
+
13
36
// handle resolution
14
37
const handleResolver = new CompositeHandleResolver({
15
38
strategy: "race",
···
64
87
const doc = await docResolver.resolve(did);
65
88
console.log(doc);
66
89
67
-
// const handler = simpleFetchHandler({ service: });
90
+
const endpoint = doc.service?.[0]?.serviceEndpoint as string | undefined;
91
+
if (!endpoint) {
92
+
throw new Error("could not resolve service endpoint for DID");
93
+
}
68
94
const agent = new AtpAgent({
69
-
service: doc.service[0].serviceEndpoint as string,
95
+
service: endpoint,
70
96
});
71
-
let cursor = "";
97
+
let cursor: string | undefined = undefined;
72
98
let totalCount = 0;
73
99
let inner_tracks: { name: string; artist: string; plays: number }[] = [];
74
100
let inner_artists: { name: string; plays: number }[] = [];
···
78
104
repo: did,
79
105
collection: "fm.teal.alpha.feed.play",
80
106
limit: 100,
81
-
cursor: cursor,
107
+
...(cursor ? { cursor } : {}),
82
108
});
83
109
84
110
// Process pages incrementally while fetching them
85
111
while (true) {
86
-
const records = response.data.records ?? [];
112
+
const records = (response.data.records as unknown as ListRecord<PlayValue>[]) ?? [];
87
113
totalCount += records.length;
88
114
89
115
for (const play of records) {
90
116
// spot-check if play is valid
91
-
if (
92
-
play.value == undefined ||
93
-
play.value.artistName ||
94
-
play.value.trackName == undefined
95
-
) {
117
+
if (!play.value || !play.value.trackName) {
96
118
continue;
97
119
}
98
120
// Aggregate by artist(s)
···
129
151
(a) => a.name === play.value.trackName,
130
152
);
131
153
if (!alreadyPlayed && play?.value) {
154
+
const primaryArtist = play.value.artists?.[0]?.artistName ?? play.value.artistNames?.[0] ?? "Unknown Artist";
132
155
inner_tracks.push({
133
156
name: play.value.trackName,
134
-
artist: play.value?.artists
135
-
? play.value.artists[0].artistName
136
-
: play.value.artistNames[0],
157
+
artist: primaryArtist,
137
158
plays: 1,
138
159
});
139
160
} else if (alreadyPlayed) {
···
169
190
repo: did,
170
191
collection: "fm.teal.alpha.feed.play",
171
192
limit: 100,
172
-
cursor: cursor,
193
+
...(cursor ? { cursor } : {}),
173
194
});
174
195
}
175
-
} catch (error) {
176
-
errorMessage.value = error.message;
196
+
} catch (error: unknown) {
197
+
errorMessage.value = error instanceof Error ? error.message : String(error);
177
198
throw error;
178
199
} finally {
179
200
loading.value = false;