diff --git a/lib/routes/youtube/api/google.ts b/lib/routes/youtube/api/google.ts
index 32fbcf3be..8f794895c 100644
--- a/lib/routes/youtube/api/google.ts
+++ b/lib/routes/youtube/api/google.ts
@@ -8,6 +8,9 @@ import ofetch from '@/utils/ofetch';
import * as cheerio from 'cheerio';
import NotFoundError from '@/errors/types/not-found';
import { Data } from '@/types';
+import dayjs from 'dayjs';
+import duration from 'dayjs/plugin/duration';
+dayjs.extend(duration);
let count = 0;
const youtube = {};
@@ -102,6 +105,8 @@ export const getDataByUsername = async ({ username, embed, filterShorts }: { use
if (!playlistItems) {
throw new NotFoundError("This channel doesn't have any content.");
}
+ const videoIds = playlistItems.data.items.map((item) => item.snippet.resourceId.videoId).join(',');
+ const videoDetails = await utils.getVideos(videoIds, 'contentDetails', cache);
return {
title: `${userHandleData?.channelName || username} - YouTube`,
@@ -114,6 +119,7 @@ export const getDataByUsername = async ({ username, embed, filterShorts }: { use
const snippet = item.snippet;
const videoId = snippet.resourceId.videoId;
const img = utils.getThumbnail(snippet.thumbnails);
+ const detail = videoDetails?.data.items.find((d) => d.id === videoId);
return {
title: snippet.title,
description: utils.renderDescription(embed, videoId, img, utils.formatDescription(snippet.description)),
@@ -125,6 +131,7 @@ export const getDataByUsername = async ({ username, embed, filterShorts }: { use
{
url: getVideoUrl(videoId),
mime_type: 'text/html',
+ duration_in_seconds: detail?.contentDetails.duration ? dayjs.duration(detail.contentDetails.duration).asSeconds() : undefined,
},
],
};
@@ -140,6 +147,8 @@ export const getDataByChannelId = async ({ channelId, embed, filterShorts }: { c
const playlistId = filterShorts ? utils.getPlaylistWithShortsFilter(channelId) : originalPlaylistId;
const data = (await utils.getPlaylistItems(playlistId, 'snippet', cache)).data.items;
+ const videoIds = data.map((item) => item.snippet.resourceId.videoId).join(',');
+ const videoDetails = await utils.getVideos(videoIds, 'contentDetails', cache);
return {
title: `${data[0].snippet.channelTitle} - YouTube`,
@@ -151,6 +160,7 @@ export const getDataByChannelId = async ({ channelId, embed, filterShorts }: { c
const snippet = item.snippet;
const videoId = snippet.resourceId.videoId;
const img = utils.getThumbnail(snippet.thumbnails);
+ const detail = videoDetails?.data.items.find((d) => d.id === videoId);
return {
title: snippet.title,
description: utils.renderDescription(embed, videoId, img, utils.formatDescription(snippet.description)),
@@ -158,6 +168,13 @@ export const getDataByChannelId = async ({ channelId, embed, filterShorts }: { c
link: `https://www.youtube.com/watch?v=${videoId}`,
author: snippet.videoOwnerChannelTitle,
image: img.url,
+ attachments: [
+ {
+ url: getVideoUrl(videoId),
+ mime_type: 'text/html',
+ duration_in_seconds: detail?.contentDetails.duration ? dayjs.duration(detail.contentDetails.duration).asSeconds() : undefined,
+ },
+ ],
};
}),
};
@@ -167,6 +184,8 @@ export const getDataByPlaylistId = async ({ playlistId, embed }: { playlistId: s
const playlistTitle = (await utils.getPlaylist(playlistId, 'snippet', cache)).data.items[0].snippet.title;
const data = (await utils.getPlaylistItems(playlistId, 'snippet', cache)).data.items.filter((d) => d.snippet.title !== 'Private video' && d.snippet.title !== 'Deleted video');
+ const videoIds = data.map((item) => item.snippet.resourceId.videoId).join(',');
+ const videoDetails = await utils.getVideos(videoIds, 'contentDetails', cache);
return {
title: `${playlistTitle} by ${data[0].snippet.channelTitle} - YouTube`,
@@ -176,6 +195,7 @@ export const getDataByPlaylistId = async ({ playlistId, embed }: { playlistId: s
const snippet = item.snippet;
const videoId = snippet.resourceId.videoId;
const img = utils.getThumbnail(snippet.thumbnails);
+ const detail = videoDetails?.data.items.find((d) => d.id === videoId);
return {
title: snippet.title,
description: utils.renderDescription(embed, videoId, img, utils.formatDescription(snippet.description)),
@@ -183,6 +203,13 @@ export const getDataByPlaylistId = async ({ playlistId, embed }: { playlistId: s
link: `https://www.youtube.com/watch?v=${videoId}`,
author: snippet.videoOwnerChannelTitle,
image: img.url,
+ attachments: [
+ {
+ url: getVideoUrl(videoId),
+ mime_type: 'text/html',
+ duration_in_seconds: detail?.contentDetails.duration ? dayjs.duration(detail.contentDetails.duration).asSeconds() : undefined,
+ },
+ ],
};
}),
};
diff --git a/lib/routes/youtube/utils.ts b/lib/routes/youtube/utils.ts
index ac1c37713..e9365048d 100644
--- a/lib/routes/youtube/utils.ts
+++ b/lib/routes/youtube/utils.ts
@@ -50,16 +50,16 @@ export const getChannelWithUsername = (username, part, cache) =>
);
return res;
});
-// not in use
-// export const getVideoAuthor = async (id, part) => {
-// const res = await exec((youtube) =>
-// youtube.videos.list({
-// part,
-// id,
-// })
-// );
-// return res;
-// }
+export const getVideos = (id, part, cache) =>
+ cache.tryGet(`youtube:getVideos:${id}`, async () => {
+ const res = await exec((youtube) =>
+ youtube.videos.list({
+ part,
+ id,
+ })
+ );
+ return res;
+ });
export const getThumbnail = (thumbnails) => thumbnails.maxres || thumbnails.standard || thumbnails.high || thumbnails.medium || thumbnails.default;
export const formatDescription = (description) => description.replaceAll(/\r\n|\r|\n/g, '
');
export const renderDescription = (embed, videoId, img, description) =>
@@ -93,7 +93,9 @@ export async function getSubscriptionsRecusive(part, nextPageToken?) {
// recursively get next page
if (res.data.nextPageToken) {
const next = await getSubscriptionsRecusive(part, res.data.nextPageToken);
- res.data.items = [...res.data.items, ...next.data.items];
+ if (next.data.items) {
+ res.data.items = [...(res.data.items || []), ...next.data.items];
+ }
}
return res;
}
@@ -140,11 +142,12 @@ export const callApi = async ({ googleApi, youtubeiApi, params }: { googleApi
return await youtubeiApi(params);
};
-const youtubeUtils = {
+export default {
getPlaylistItems,
getPlaylist,
getChannelWithId,
getChannelWithUsername,
+ getVideos,
getThumbnail,
formatDescription,
renderDescription,
@@ -155,4 +158,3 @@ const youtubeUtils = {
getVideoUrl,
getPlaylistWithShortsFilter,
};
-export default youtubeUtils;