feat: add discogs

This commit is contained in:
2025-09-27 12:51:34 +02:00
parent 1960e7226f
commit 175a26630a
5 changed files with 183 additions and 24 deletions

View File

@@ -14,6 +14,7 @@ import { Image } from 'astro:assets'
import type { CollectionEntry } from 'astro:content'
import Link from './Link.astro'
import { Ratings } from './ui/rating'
import { getReleaseData } from '@/lib/discogs'
interface Props {
entry: CollectionEntry<'blog'>
@@ -26,6 +27,7 @@ const authors = await parseAuthors(entry.data.authors ?? [])
const subpostCount = !isSubpost(entry.id) ? await getSubpostCount(entry.id) : 0
const rating = entry.data.tags?.find((tag) => tag.startsWith('rating-'))
const discogs = entry.data.discogs ? await getReleaseData(entry.data.discogs) : null;
---
<div
@@ -36,14 +38,14 @@ const rating = entry.data.tags?.find((tag) => tag.startsWith('rating-'))
class="flex flex-col gap-4 sm:flex-row"
>
{
entry.data.image && (
<div class="max-w-3xs sm:shrink-0">
entry.data.image || (discogs && discogs.images[0]?.resource_url) && (
<div class="w-48 max-w-48 sm:shrink-0 h-30">
<Image
src={entry.data.image}
src={entry.data.image || discogs.images[0].resource_url}
alt={entry.data.title}
width={1200}
height={630}
class="object-cover"
height={400}
class="w-full h-full object-cover rounded-lg"
/>
</div>
)

View File

@@ -12,6 +12,7 @@ const blog = defineCollection({
tags: z.array(z.string()).optional(),
authors: z.array(z.string()).optional(),
draft: z.boolean().optional(),
discogs: z.string().optional(),
}),
})

View File

@@ -4,6 +4,7 @@ description: 'Random notes I took on a listening session'
date: 2025-09-26
tags: ['rating-4', 'music', 'radiohead']
#image: './banner.png'
discogs: '1174296'
authors: ['srizan']
---

140
src/lib/discogs.ts Normal file
View File

@@ -0,0 +1,140 @@
export async function getReleaseData(discogsId: string) {
const res = await fetch(`https://api.discogs.com/releases/${discogsId}`, {
headers: {
'User-Agent': 'srizan.dev/1.0 +https://srizan.dev',
},
});
if (!res.ok) {
throw new Error(`Failed to fetch data from Discogs API: ${res.statusText}`);
}
const data = await res.json() as DiscogsRelease;
return data;
}
export interface DiscogsRelease {
title: string;
id: number;
artists: Artist[];
data_quality: string;
thumb: string;
community: Community;
companies: Company[];
country: string;
date_added: string;
date_changed: string;
estimated_weight: number;
extraartists: ExtraArtist[];
format_quantity: number;
formats: Format[];
genres: string[];
identifiers: Identifier[];
images: Image[];
labels: Label[];
lowest_price: number;
master_id: number;
master_url: string;
notes: string;
num_for_sale: number;
released: string;
released_formatted: string;
resource_url: string;
series: any[];
status: string;
styles: string[];
tracklist: Track[];
uri: string;
videos: Video[];
year: number;
}
export interface Artist {
anv: string;
id: number;
join: string;
name: string;
resource_url: string;
role: string;
tracks: string;
}
export interface Community {
contributors: Contributor[];
data_quality: string;
have: number;
rating: {
average: number;
count: number;
};
status: string;
submitter: Contributor;
want: number;
}
export interface Contributor {
resource_url: string;
username: string;
}
export interface Company {
catno: string;
entity_type: string;
entity_type_name: string;
id: number;
name: string;
resource_url: string;
}
export interface ExtraArtist {
anv: string;
id: number;
join: string;
name: string;
resource_url: string;
role: string;
tracks: string;
}
export interface Format {
descriptions: string[];
name: string;
qty: string;
}
export interface Identifier {
type: string;
value: string;
}
export interface Image {
height: number;
resource_url: string;
type: string;
uri: string;
uri150: string;
width: number;
}
export interface Label {
catno: string;
entity_type: string;
id: number;
name: string;
resource_url: string;
}
export interface Track {
duration: string;
position: string;
title: string;
type_: string;
}
export interface Video {
description: string;
duration: number;
embed: boolean;
title: string;
uri: string;
}

View File

@@ -9,6 +9,7 @@ import TOCHeader from '@/components/TOCHeader.astro'
import TOCSidebar from '@/components/TOCSidebar.astro'
import { badgeVariants } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Ratings } from '@/components/ui/rating'
import { Separator } from '@/components/ui/separator'
import Layout from '@/layouts/Layout.astro'
import {
@@ -22,6 +23,7 @@ import {
isSubpost,
parseAuthors,
} from '@/lib/data-utils'
import { getReleaseData } from '@/lib/discogs'
import { formatDate } from '@/lib/utils'
import { Icon } from 'astro-icon/components'
import { Image } from 'astro:assets'
@@ -51,6 +53,10 @@ const subpostCount = !isCurrentSubpost
? await getSubpostCount(currentPostId)
: 0
const postReadingTime = await getPostReadingTime(currentPostId)
const rating = post.data.tags?.find((tag) => tag.startsWith('rating-'))
const discogs = post.data.discogs
? await getReleaseData(post.data.discogs)
: null
---
<Layout>
@@ -101,15 +107,16 @@ const postReadingTime = await getPostReadingTime(currentPostId)
</div>
{
post.data.image && (
<Image
src={post.data.image}
alt={post.data.title}
width={1200}
height={630}
class="col-span-full mx-auto w-full max-w-5xl object-cover"
/>
)
post.data.image ||
(discogs && discogs.images[0]?.resource_url && (
<Image
src={post.data.image || discogs.images[0].resource_url}
alt={post.data.title}
width={1200}
height={630}
class="col-span-full mx-auto max-h-96 w-full max-w-4xl rounded-lg object-contain"
/>
))
}
<section class="col-start-2 flex flex-col gap-y-6 text-center">
@@ -176,17 +183,25 @@ const postReadingTime = await getPostReadingTime(currentPostId)
</div>
<div class="flex flex-wrap justify-center gap-2">
{rating && (
<Ratings rating={parseFloat(rating.replace('rating-', ''))} size={15} />
)}
{
post.data.tags && post.data.tags.length > 0 ? (
post.data.tags.map((tag) => (
<a
href={`/tags/${tag}`}
class={badgeVariants({ variant: 'secondary' })}
>
<Icon name="lucide:hash" class="size-3" />
{tag}
</a>
))
post.data.tags.map((tag) => {
if (tag.startsWith('rating-')) return null;
return (
<div>
<a
href={`/tags/${tag}`}
class={badgeVariants({ variant: 'secondary' })}
>
<Icon name="lucide:hash" class="size-3" />
{tag}
</a>
</div>
)
})
) : (
<span class="text-muted-foreground text-sm">
No tags available
@@ -257,7 +272,7 @@ const postReadingTime = await getPostReadingTime(currentPostId)
scrollToTopButton.classList.toggle(
'hidden',
window.scrollY <= 300 || isFooterVisible,
window.scrollY <= 300 || isFooterVisible
)
})
}