Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion open-api/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -2323,6 +2323,16 @@
}
},
"description": "The mapping of each RaidHub activityId to its splash image URLs"
},
"versionSplashUrls": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ImageContentData"
}
},
"description": "The mapping of each RaidHub versionId to its splash image URLs"
}
},
"required": [
Expand All @@ -2342,7 +2352,8 @@
"versionsForActivity",
"rankingTiers",
"feats",
"splashUrls"
"splashUrls",
"versionSplashUrls"
],
"additionalProperties": false
},
Expand Down
28 changes: 19 additions & 9 deletions src/routes/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
sortPantheonActivityIds
} from "@/services/manifest/pantheon"
import { TierBreaks } from "@/services/manifest/tiers"
import { generateSplashUrls } from "@/services/manifest/urls"
import { generateSplashUrls, generateVersionSplashUrls } from "@/services/manifest/urls"
import { z } from "zod"

export const manifestRoute = new RaidHubRoute({
Expand Down Expand Up @@ -116,20 +116,29 @@ export const manifestRoute = new RaidHubRoute({
.openapi({
description:
"The mapping of each RaidHub activityId to its splash image URLs"
}),
versionSplashUrls: z
.record(zNumericalRecordKey(), z.array(zImageContentData))
.openapi({
description:
"The mapping of each RaidHub versionId to its splash image URLs"
})
})
.strict()
}
},
handler: async () => {
const activitiesPromise = listActivityDefinitions()
const [activities, versions, hashes, feats, splashUrls] = await Promise.all([
activitiesPromise,
listVersionDefinitions(),
listHashes(),
listFeatDefinitions(),
activitiesPromise.then(generateSplashUrls)
])
const versionsPromise = listVersionDefinitions()
const [activities, versions, hashes, feats, splashUrls, versionSplashUrls] =
await Promise.all([
activitiesPromise,
versionsPromise,
listHashes(),
listFeatDefinitions(),
activitiesPromise.then(generateSplashUrls),
versionsPromise.then(generateVersionSplashUrls)
])
const raids = activities.filter(a => a.isRaid)
const pantheonIds = sortPantheonActivityIds(activities, getPantheonActivityIds(activities))
const { pantheonVersionIds, pantheonSunsetVersionIds } = getPantheonVersionIds(
Expand Down Expand Up @@ -188,7 +197,8 @@ export const manifestRoute = new RaidHubRoute({
versionsForActivity: versionsForActivity,
rankingTiers: TierBreaks,
feats: feats,
splashUrls: splashUrls
splashUrls: splashUrls,
versionSplashUrls: versionSplashUrls
})
}
})
45 changes: 38 additions & 7 deletions src/services/manifest/urls.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,59 @@
import { streamR2BucketContents } from "@/integrations/r2"
import { ActivityDefinition } from "@/schema/components/ActivityDefinition"
import { ImageContentData, ImageSize } from "@/schema/components/ImageContentData"
import { VersionDefinition } from "@/schema/components/VersionDefinition"

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

export const generateSplashUrls = async (defs: ActivityDefinition[]) => {
const allValidSlugs = new Map<string, number>(defs.map(def => [def.splashSlug, def.id]))
const indexSplashObjects = async (
slugToIds: Map<string, number[]>
): Promise<Record<number, ImageContentData[]>> => {
const allIds = [...new Set([...slugToIds.values()].flat())]
const result: Record<number, ImageContentData[]> = Object.fromEntries(
defs.filter(def => allValidSlugs.has(def.splashSlug)).map(def => [def.id, []])
allIds.map(id => [id, []])
)

for await (const item of streamR2BucketContents({
prefix: "content/splash/",
useCache: true
})) {
const processed = processContentUrl(item)
if (allValidSlugs.has(processed.slug)) {
const activityId = allValidSlugs.get(processed.slug)!
const bucket = result[activityId]
bucket.push(processed)
const ids = slugToIds.get(processed.slug)
if (!ids) {
continue
}
for (const id of ids) {
result[id].push(processed)
}
}

return result
}

const groupIdsBySlug = (entries: [string, number][]): Map<string, number[]> => {
const slugToIds = new Map<string, number[]>()
for (const [slug, id] of entries) {
const ids = slugToIds.get(slug) ?? []
ids.push(id)
slugToIds.set(slug, ids)
}
return slugToIds
}

export const generateSplashUrls = async (defs: ActivityDefinition[]) => {
return indexSplashObjects(groupIdsBySlug(defs.map(def => [def.splashSlug, def.id])))
}

export const generateVersionSplashUrls = async (versions: VersionDefinition[]) => {
return indexSplashObjects(
groupIdsBySlug(
versions
.filter(version => version.associatedActivityId !== null)
.map(version => [version.path, version.id])
)
)
}

const processContentUrl = (path: string): ImageContentData => {
const components = path.split("/")
const slug = components[2]
Expand Down
Loading