import { extract, normalizeURL } from "../url.js"; import { genericUserAgent } from "../../config.js"; import { createStream } from "../../stream/manage.js"; import { getRedirectingURL } from "../../misc/utils.js"; const https = (url) => { return url.replace(/^http:/i, 'https:'); } export default async function ({ id, token, shareId, h265, isAudioOnly, dispatcher }) { let noteId = id; let xsecToken = token; if (!noteId) { const extractedURL = await getRedirectingURL( `https://xhslink.com/a/${shareId}`, dispatcher ); if (extractedURL) { const { patternMatch } = extract(normalizeURL(extractedURL)); if (patternMatch) { noteId = patternMatch.id; xsecToken = patternMatch.token; } } } if (!noteId || !xsecToken) return { error: "fetch.short_link" }; const res = await fetch(`https://www.xiaohongshu.com/explore/${noteId}?xsec_token=${xsecToken}`, { headers: { "user-agent": genericUserAgent, }, dispatcher, }); const html = await res.text(); let note; try { const initialState = html .split('')[0] .replace(/:\s*undefined/g, ":null"); const data = JSON.parse(initialState); const noteInfo = data?.note?.noteDetailMap; if (!noteInfo) throw "no note detail map"; const currentNote = noteInfo[noteId]; if (!currentNote) throw "no current note in detail map"; note = currentNote.note; } catch {} if (!note) return { error: "fetch.empty" }; const video = note.video; const images = note.imageList; const filenameBase = `xiaohongshu_${noteId}`; if (video) { const videoFilename = `${filenameBase}.mp4`; const audioFilename = `${filenameBase}_audio`; let videoURL; if (h265 && !isAudioOnly && video.consumer?.originVideoKey) { videoURL = `https://sns-video-bd.xhscdn.com/${video.consumer.originVideoKey}`; } else { const h264Streams = video.media?.stream?.h264; if (h264Streams?.length) { videoURL = h264Streams.reduce((a, b) => Number(a?.videoBitrate) > Number(b?.videoBitrate) ? a : b).masterUrl; } } if (!videoURL) return { error: "fetch.empty" }; return { urls: https(videoURL), filename: videoFilename, audioFilename: audioFilename, } } if (!images || images.length === 0) { return { error: "fetch.empty" }; } if (images.length === 1) { return { isPhoto: true, urls: https(images[0].urlDefault), filename: `${filenameBase}.jpg`, } } const picker = images.map((image, i) => { return { type: "photo", url: createStream({ service: "xiaohongshu", type: "proxy", url: https(image.urlDefault), filename: `${filenameBase}_${i + 1}.jpg`, }) } }); return { picker }; }