File size: 3,017 Bytes
43a06dc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import { resolveRedirectingURL } from "../url.js";
import { genericUserAgent } from "../../config.js";
import { createStream } from "../../stream/manage.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 patternMatch = await resolveRedirectingURL(
            `https://xhslink.com/a/${shareId}`,
            dispatcher
        );

        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('<script>window.__INITIAL_STATE__=')[1]
            .split('</script>')[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 };
}