Skip to content

Instantly share code, notes, and snippets.

@kdankov
Created October 22, 2024 08:14
Show Gist options
  • Save kdankov/5b7d1b2d0c65c09bbd278b9c239f9122 to your computer and use it in GitHub Desktop.
Save kdankov/5b7d1b2d0c65c09bbd278b9c239f9122 to your computer and use it in GitHub Desktop.
import type { Loader } from 'astro/loaders';
import { z } from 'astro:content'
import type { YouTubeLoaderOptions } from "./types/youtube-loader-options.type.ts";
export function youtubeLoader(options: YouTubeLoaderOptions): Loader {
const url = new URL('https://www.googleapis.com/youtube/v3/search');
url.searchParams.append('key', options.apiKey);
url.searchParams.append('channelId', options.channelId);
url.searchParams.append('part', 'snippet, id');
url.searchParams.append('order', 'date');
url.searchParams.append('maxResults', '24');
return {
name: "youtube-loader",
schema: youtubeSearchSchema,
load: async ({ store, parseData }) => {
await fetchVideos();
async function fetchVideos() {
const response = await fetch(url.href).then((res) => res.json());
for (const item of response.items) {
const id = item.id.videoId;
try {
const data = await parseData({ id, data: item });
store.set({ id, data });
} catch (e) {
console.warn(`Skipped video ${id}.`, e);
}
}
}
}
}
}
const youtubeThumbnailSchema = z.object({
url: z.string(),
width: z.number(),
height: z.number(),
});
const youtubeSearchSchema = z
.object({
kind: z.string(),
etag: z.string(),
id: z.object({
kind: z.string(),
videoId: z.string(),
}),
snippet: z.object({
publishedAt: z.coerce.date(),
channelId: z.string(),
title: z.string().optional(),
description: z.string().optional(),
thumbnails: z.object({
default: youtubeThumbnailSchema,
medium: youtubeThumbnailSchema.optional(),
high: youtubeThumbnailSchema.optional(),
}).optional(),
channelTitle: z.string(),
liveBroadcastContent: z.string(),
publishTime: z.coerce.date(),
}),
})
.transform((item) => ({
title: item.snippet.title,
description: item.snippet.description,
permalink: `https://www.youtube.com/watch?v=${item.id.videoId}`,
publishedAt: item.snippet.publishedAt,
thumbnail: item.snippet.thumbnails?.high?.url,
}));
const youtubeApiResponseSchema = z.object({
kind: z.string(),
etag: z.string(),
nextPageToken: z.string().optional(),
regionCode: z.string(),
pageInfo: z.object({
totalResults: z.number(),
resultsPerPage: z.number(),
}),
items: z.array(
z
.object({
id: z
.object({
videoId: z.string(),
})
.passthrough(),
})
.passthrough(),
),
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment