Skip to content

Commit b3ecf1d

Browse files
Fix manifest splash URLs for shared splashSlug (#136)
* Fix manifest splash URLs when activities share a splashSlug. Pantheon activities 101/102 had empty splashUrls because only one activity per slug received CDN assets. Also expose versionSplashUrls on manifest. Co-authored-by: Cursor <cursoragent@cursor.com> * Regenerate OpenAPI spec for versionSplashUrls manifest field. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 2837956 commit b3ecf1d

3 files changed

Lines changed: 69 additions & 17 deletions

File tree

open-api/openapi.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2323,6 +2323,16 @@
23232323
}
23242324
},
23252325
"description": "The mapping of each RaidHub activityId to its splash image URLs"
2326+
},
2327+
"versionSplashUrls": {
2328+
"type": "object",
2329+
"additionalProperties": {
2330+
"type": "array",
2331+
"items": {
2332+
"$ref": "#/components/schemas/ImageContentData"
2333+
}
2334+
},
2335+
"description": "The mapping of each RaidHub versionId to its splash image URLs"
23262336
}
23272337
},
23282338
"required": [
@@ -2342,7 +2352,8 @@
23422352
"versionsForActivity",
23432353
"rankingTiers",
23442354
"feats",
2345-
"splashUrls"
2355+
"splashUrls",
2356+
"versionSplashUrls"
23462357
],
23472358
"additionalProperties": false
23482359
},

src/routes/manifest.ts

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
sortPantheonActivityIds
1818
} from "@/services/manifest/pantheon"
1919
import { TierBreaks } from "@/services/manifest/tiers"
20-
import { generateSplashUrls } from "@/services/manifest/urls"
20+
import { generateSplashUrls, generateVersionSplashUrls } from "@/services/manifest/urls"
2121
import { z } from "zod"
2222

2323
export const manifestRoute = new RaidHubRoute({
@@ -116,20 +116,29 @@ export const manifestRoute = new RaidHubRoute({
116116
.openapi({
117117
description:
118118
"The mapping of each RaidHub activityId to its splash image URLs"
119+
}),
120+
versionSplashUrls: z
121+
.record(zNumericalRecordKey(), z.array(zImageContentData))
122+
.openapi({
123+
description:
124+
"The mapping of each RaidHub versionId to its splash image URLs"
119125
})
120126
})
121127
.strict()
122128
}
123129
},
124130
handler: async () => {
125131
const activitiesPromise = listActivityDefinitions()
126-
const [activities, versions, hashes, feats, splashUrls] = await Promise.all([
127-
activitiesPromise,
128-
listVersionDefinitions(),
129-
listHashes(),
130-
listFeatDefinitions(),
131-
activitiesPromise.then(generateSplashUrls)
132-
])
132+
const versionsPromise = listVersionDefinitions()
133+
const [activities, versions, hashes, feats, splashUrls, versionSplashUrls] =
134+
await Promise.all([
135+
activitiesPromise,
136+
versionsPromise,
137+
listHashes(),
138+
listFeatDefinitions(),
139+
activitiesPromise.then(generateSplashUrls),
140+
versionsPromise.then(generateVersionSplashUrls)
141+
])
133142
const raids = activities.filter(a => a.isRaid)
134143
const pantheonIds = sortPantheonActivityIds(activities, getPantheonActivityIds(activities))
135144
const { pantheonVersionIds, pantheonSunsetVersionIds } = getPantheonVersionIds(
@@ -188,7 +197,8 @@ export const manifestRoute = new RaidHubRoute({
188197
versionsForActivity: versionsForActivity,
189198
rankingTiers: TierBreaks,
190199
feats: feats,
191-
splashUrls: splashUrls
200+
splashUrls: splashUrls,
201+
versionSplashUrls: versionSplashUrls
192202
})
193203
}
194204
})

src/services/manifest/urls.ts

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,59 @@
11
import { streamR2BucketContents } from "@/integrations/r2"
22
import { ActivityDefinition } from "@/schema/components/ActivityDefinition"
33
import { ImageContentData, ImageSize } from "@/schema/components/ImageContentData"
4+
import { VersionDefinition } from "@/schema/components/VersionDefinition"
45

56
const baseUrl = "https://cdn.raidhub.io"
67

7-
export const generateSplashUrls = async (defs: ActivityDefinition[]) => {
8-
const allValidSlugs = new Map<string, number>(defs.map(def => [def.splashSlug, def.id]))
8+
const indexSplashObjects = async (
9+
slugToIds: Map<string, number[]>
10+
): Promise<Record<number, ImageContentData[]>> => {
11+
const allIds = [...new Set([...slugToIds.values()].flat())]
912
const result: Record<number, ImageContentData[]> = Object.fromEntries(
10-
defs.filter(def => allValidSlugs.has(def.splashSlug)).map(def => [def.id, []])
13+
allIds.map(id => [id, []])
1114
)
15+
1216
for await (const item of streamR2BucketContents({
1317
prefix: "content/splash/",
1418
useCache: true
1519
})) {
1620
const processed = processContentUrl(item)
17-
if (allValidSlugs.has(processed.slug)) {
18-
const activityId = allValidSlugs.get(processed.slug)!
19-
const bucket = result[activityId]
20-
bucket.push(processed)
21+
const ids = slugToIds.get(processed.slug)
22+
if (!ids) {
23+
continue
24+
}
25+
for (const id of ids) {
26+
result[id].push(processed)
2127
}
2228
}
29+
2330
return result
2431
}
2532

33+
const groupIdsBySlug = (entries: [string, number][]): Map<string, number[]> => {
34+
const slugToIds = new Map<string, number[]>()
35+
for (const [slug, id] of entries) {
36+
const ids = slugToIds.get(slug) ?? []
37+
ids.push(id)
38+
slugToIds.set(slug, ids)
39+
}
40+
return slugToIds
41+
}
42+
43+
export const generateSplashUrls = async (defs: ActivityDefinition[]) => {
44+
return indexSplashObjects(groupIdsBySlug(defs.map(def => [def.splashSlug, def.id])))
45+
}
46+
47+
export const generateVersionSplashUrls = async (versions: VersionDefinition[]) => {
48+
return indexSplashObjects(
49+
groupIdsBySlug(
50+
versions
51+
.filter(version => version.associatedActivityId !== null)
52+
.map(version => [version.path, version.id])
53+
)
54+
)
55+
}
56+
2657
const processContentUrl = (path: string): ImageContentData => {
2758
const components = path.split("/")
2859
const slug = components[2]

0 commit comments

Comments
 (0)