UNPKG

media-scraper

Version:

TypeScript-first multi-platform social media scraper without API keys

82 lines (81 loc) 3.34 kB
import { $fetch } from "ofetch"; import { parseURL } from "ufo"; import { redditHeaders } from "../utils/helpers.mjs"; import { redditRegex } from "../utils/regex.mjs"; export default async (url) => { const match = url.match(redditRegex); if (!match) throw new Error("Invalid Reddit URL"); const { protocol, host, pathname } = parseURL(url); const jsonData = await $fetch(`${protocol}//${host}${pathname}/.json`, { headers: redditHeaders }).catch(() => null); const { data } = jsonData.find((item) => item?.data?.children?.[0]?.kind === "t3")?.data?.children?.[0]; const crosspostData = data?.crosspost_parent_list?.[0]; const buildVideoObject = async (videoData2) => { if (videoData2?.is_video || videoData2?.url?.includes(".gif")) { let finalURL; if (videoData2?.url?.includes(".gif")) { finalURL = videoData2?.media?.reddit_video?.fallback_url; } else { const dash = videoData2?.media?.reddit_video?.dash_url; const xmlString = await $fetch(dash, { responseType: "text" }).catch(() => null); const dashAudio = xmlString?.match(/<AdaptationSet[^>]+contentType="audio"[^>]*>[\s\S]+?<BaseURL>(.*?)<\/BaseURL>/)?.[1]; const fallback_audio = `${videoData2?.url}/${dashAudio}`; const fallback_video = videoData2?.media?.reddit_video?.fallback_url; const merge = await $fetch("https://redvid.io/download-link", { query: { token: { video_url: fallback_video, audio_url: fallback_audio, id: data?.id } } }).catch(() => null); finalURL = merge?.url ? `https://redvid.io${merge?.url}` : null; } if (!finalURL) return null; return { width: videoData2?.media?.reddit_video?.width, height: videoData2?.media?.reddit_video?.height, duration: videoData2?.media?.reddit_video?.duration ? videoData2?.media.reddit_video.duration * 1e3 : void 0, url: finalURL, type: videoData2?.url?.includes(".gif") ? "gif" : videoData2?.is_video ? "video" : void 0 }; } }; const videoData = crosspostData || data; const buildedData = await buildVideoObject(videoData); const authorData = await $fetch(`${protocol}//${host}/user/${videoData?.author}/about.json`, { headers: redditHeaders }).catch(() => null); return { id: data?.id, caption: data?.title?.trim(), permalink_url: `${protocol}//${host}${data?.permalink}`, thumbnail_url: data?.thumbnail?.replace(/&amp;/g, "&"), short_url: data?.url, author: { id: data?.author_fullname?.replace("t2_", ""), name: data?.author, username: data?.author, avatar_url: authorData?.data?.snoovatar_img, url: data?.author ? `https://www.reddit.com/user/${data?.author}/` : void 0 }, up_count: data?.ups, comment_count: data?.num_comments, created_at: data?.created_utc, ...buildedData && { video: buildedData }, ...videoData?.gallery_data?.items && { gallery: videoData?.gallery_data?.items?.map((item) => { const metaData = videoData?.media_metadata?.[item?.media_id]; return { url: metaData?.s?.u.replace(/&amp;/g, "&"), width: metaData?.s?.x, height: metaData?.s?.y }; }) } }; };