Initial commit: Indiekit Eleventy theme
This commit is contained in:
206
_data/youtubeChannel.js
Normal file
206
_data/youtubeChannel.js
Normal file
@@ -0,0 +1,206 @@
|
||||
/**
|
||||
* YouTube Channel Data
|
||||
* Fetches from Indiekit's endpoint-youtube public API
|
||||
* Supports single or multiple channels
|
||||
*/
|
||||
|
||||
import EleventyFetch from "@11ty/eleventy-fetch";
|
||||
|
||||
const INDIEKIT_URL = process.env.SITE_URL || "https://example.com";
|
||||
|
||||
/**
|
||||
* Fetch from Indiekit's public YouTube API endpoint
|
||||
*/
|
||||
async function fetchFromIndiekit(endpoint) {
|
||||
try {
|
||||
const url = `${INDIEKIT_URL}/youtubeapi/api/${endpoint}`;
|
||||
console.log(`[youtubeChannel] Fetching from Indiekit: ${url}`);
|
||||
const data = await EleventyFetch(url, {
|
||||
duration: "5m",
|
||||
type: "json",
|
||||
});
|
||||
console.log(`[youtubeChannel] Indiekit ${endpoint} success`);
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.log(
|
||||
`[youtubeChannel] Indiekit API unavailable for ${endpoint}: ${error.message}`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format large numbers with locale separators
|
||||
*/
|
||||
function formatNumber(num) {
|
||||
if (!num) return "0";
|
||||
return new Intl.NumberFormat().format(num);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format view count with K/M suffix for compact display
|
||||
*/
|
||||
function formatViewCount(num) {
|
||||
if (!num) return "0";
|
||||
if (num >= 1000000) {
|
||||
return (num / 1000000).toFixed(1).replace(/\.0$/, "") + "M";
|
||||
}
|
||||
if (num >= 1000) {
|
||||
return (num / 1000).toFixed(1).replace(/\\.0$/, "") + "K";
|
||||
}
|
||||
return num.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format relative time from ISO date string
|
||||
*/
|
||||
function formatRelativeTime(dateString) {
|
||||
if (!dateString) return "";
|
||||
const date = new Date(dateString);
|
||||
const now = new Date();
|
||||
const diffMs = now - date;
|
||||
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
||||
|
||||
if (diffDays === 0) return "Today";
|
||||
if (diffDays === 1) return "Yesterday";
|
||||
if (diffDays < 7) return `${diffDays} days ago`;
|
||||
if (diffDays < 30) return `${Math.floor(diffDays / 7)} weeks ago`;
|
||||
if (diffDays < 365) return `${Math.floor(diffDays / 30)} months ago`;
|
||||
return `${Math.floor(diffDays / 365)} years ago`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format channel data with computed fields
|
||||
*/
|
||||
function formatChannel(channel) {
|
||||
if (!channel) return null;
|
||||
return {
|
||||
...channel,
|
||||
subscriberCountFormatted: formatNumber(channel.subscriberCount),
|
||||
videoCountFormatted: formatNumber(channel.videoCount),
|
||||
viewCountFormatted: formatNumber(channel.viewCount),
|
||||
url: `https://www.youtube.com/channel/${channel.id}`,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Format video data with computed fields
|
||||
*/
|
||||
function formatVideo(video) {
|
||||
return {
|
||||
...video,
|
||||
viewCountFormatted: formatViewCount(video.viewCount),
|
||||
relativeTime: formatRelativeTime(video.publishedAt),
|
||||
};
|
||||
}
|
||||
|
||||
export default async function () {
|
||||
try {
|
||||
console.log("[youtubeChannel] Fetching YouTube data...");
|
||||
|
||||
// Fetch all data from Indiekit API
|
||||
const [channelData, videosData, liveData] = await Promise.all([
|
||||
fetchFromIndiekit("channel"),
|
||||
fetchFromIndiekit("videos"),
|
||||
fetchFromIndiekit("live"),
|
||||
]);
|
||||
|
||||
// Check if we got data
|
||||
const hasData =
|
||||
channelData?.channel ||
|
||||
channelData?.channels?.length ||
|
||||
videosData?.videos?.length;
|
||||
|
||||
if (!hasData) {
|
||||
console.log("[youtubeChannel] No data available from Indiekit");
|
||||
return {
|
||||
channel: null,
|
||||
channels: [],
|
||||
videos: [],
|
||||
videosByChannel: {},
|
||||
liveStatus: null,
|
||||
liveStatuses: [],
|
||||
isMultiChannel: false,
|
||||
source: "unavailable",
|
||||
};
|
||||
}
|
||||
|
||||
console.log("[youtubeChannel] Using Indiekit API data");
|
||||
|
||||
// Determine if multi-channel mode
|
||||
const isMultiChannel = !!(channelData?.channels && channelData.channels.length > 1);
|
||||
|
||||
// Format channels
|
||||
let channels = [];
|
||||
let channel = null;
|
||||
|
||||
if (isMultiChannel) {
|
||||
channels = (channelData.channels || []).map(formatChannel).filter(Boolean);
|
||||
channel = channels[0] || null;
|
||||
} else {
|
||||
channel = formatChannel(channelData?.channel);
|
||||
channels = channel ? [channel] : [];
|
||||
}
|
||||
|
||||
// Format videos
|
||||
const videos = (videosData?.videos || []).map(formatVideo);
|
||||
|
||||
// Group videos by channel if multi-channel
|
||||
let videosByChannel = {};
|
||||
if (isMultiChannel && videosData?.videosByChannel) {
|
||||
for (const [channelName, channelVideos] of Object.entries(videosData.videosByChannel)) {
|
||||
videosByChannel[channelName] = (channelVideos || []).map(formatVideo);
|
||||
}
|
||||
} else if (channel) {
|
||||
videosByChannel[channel.configName || channel.title] = videos;
|
||||
}
|
||||
|
||||
// Format live status
|
||||
let liveStatus = null;
|
||||
let liveStatuses = [];
|
||||
|
||||
if (liveData) {
|
||||
if (isMultiChannel && liveData.liveStatuses) {
|
||||
liveStatuses = liveData.liveStatuses;
|
||||
// Find first live or upcoming
|
||||
const live = liveStatuses.find((s) => s.isLive);
|
||||
const upcoming = liveStatuses.find((s) => s.isUpcoming && !s.isLive);
|
||||
liveStatus = {
|
||||
isLive: !!live,
|
||||
isUpcoming: !live && !!upcoming,
|
||||
stream: live?.stream || upcoming?.stream || null,
|
||||
};
|
||||
} else {
|
||||
liveStatus = {
|
||||
isLive: liveData.isLive || false,
|
||||
isUpcoming: liveData.isUpcoming || false,
|
||||
stream: liveData.stream || null,
|
||||
};
|
||||
liveStatuses = [{ ...liveStatus, channelConfigName: channel?.configName }];
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
channel,
|
||||
channels,
|
||||
videos,
|
||||
videosByChannel,
|
||||
liveStatus,
|
||||
liveStatuses,
|
||||
isMultiChannel,
|
||||
source: "indiekit",
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("[youtubeChannel] Error:", error.message);
|
||||
return {
|
||||
channel: null,
|
||||
channels: [],
|
||||
videos: [],
|
||||
videosByChannel: {},
|
||||
liveStatus: null,
|
||||
liveStatuses: [],
|
||||
isMultiChannel: false,
|
||||
source: "error",
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user