diff --git a/api/src/processing/match.js b/api/src/processing/match.js index 1265297c..508ccdd9 100644 --- a/api/src/processing/match.js +++ b/api/src/processing/match.js @@ -263,6 +263,7 @@ export default async function({ host, patternMatch, params, authType }) { case "xiaohongshu": r = await xiaohongshu({ ...patternMatch, + shareType: url.pathname.split("/")[1], h265: params.allowH265, isAudioOnly, dispatcher, diff --git a/api/src/processing/service-config.js b/api/src/processing/service-config.js index 906c23da..1bf5e020 100644 --- a/api/src/processing/service-config.js +++ b/api/src/processing/service-config.js @@ -207,7 +207,8 @@ export const services = { patterns: [ "explore/:id?xsec_token=:token", "discovery/item/:id?xsec_token=:token", - "a/:shareId" + "a/:shareId", + "m/:shareId" ], altDomains: ["xhslink.com"], }, diff --git a/api/src/processing/services/xiaohongshu.js b/api/src/processing/services/xiaohongshu.js index 06de21aa..a4f5cb0f 100644 --- a/api/src/processing/services/xiaohongshu.js +++ b/api/src/processing/services/xiaohongshu.js @@ -1,21 +1,35 @@ -import { resolveRedirectingURL } from "../url.js"; +import { resolveRedirectingURL, extract, normalizeURL } from "../url.js"; import { genericUserAgent } from "../../config.js"; import { createStream } from "../../stream/manage.js"; +import { request } from "undici"; const https = (url) => { return url.replace(/^http:/i, 'https:'); } -export default async function ({ id, token, shareId, h265, isAudioOnly, dispatcher }) { +export default async function ({ id, token, shareType, shareId, h265, isAudioOnly, dispatcher }) { let noteId = id; let xsecToken = token; - if (!noteId) { + if (!noteId && shareType === "a") { const patternMatch = await resolveRedirectingURL( `https://xhslink.com/a/${shareId}`, dispatcher ); + noteId = patternMatch?.id; + xsecToken = patternMatch?.token; + } else if (!noteId && shareType === "m") { + const location = await request(`https://xhslink.com/m/${shareId}`, { + dispatcher, + redirect: 'manual' + }).then(r => { + if (r.statusCode === 302 && r.headers['location']) { + return r.headers['location']; + } + }).catch(() => null); + + const { patternMatch } = extract(normalizeURL(location)); noteId = patternMatch?.id; xsecToken = patternMatch?.token; } diff --git a/api/src/processing/url.js b/api/src/processing/url.js index dbbda1cd..5f93f1d0 100644 --- a/api/src/processing/url.js +++ b/api/src/processing/url.js @@ -99,7 +99,7 @@ function aliasURL(url) { case "xhslink": if (url.hostname === 'xhslink.com' && parts.length === 3) { - url = new URL(`https://www.xiaohongshu.com/a/${parts[2]}`); + url = new URL(`https://www.xiaohongshu.com/${parts[1]}/${parts[2]}`); } break; diff --git a/api/src/util/tests/xiaohongshu.json b/api/src/util/tests/xiaohongshu.json index a169cc23..ed523856 100644 --- a/api/src/util/tests/xiaohongshu.json +++ b/api/src/util/tests/xiaohongshu.json @@ -1,7 +1,7 @@ [ { "name": "video (might have expired)", - "url": "https://www.xiaohongshu.com/explore/67cc17a3000000000e00726a?xsec_token=CBSFRtbF57so920elY1kbIX4fE1nhrwlpGZs9m6pIFpwo=", + "url": "https://www.xiaohongshu.com/explore/685e63e1000000000b02ee3b?xsec_token=ABN8EQJCDMPcFX9RRggeIPSHLIJ8zkGceFDyBewLGUz30=", "canFail": true, "params": {}, "expected": { @@ -11,7 +11,7 @@ }, { "name": "picker with multiple live photos (might have expired)", - "url": "https://www.xiaohongshu.com/explore/67c691b4000000000d0159cc?xsec_token=CB8p1eyB5DiFkwlUpy1BTeVsI9oOve6ppNjuDzo8V8p5w=", + "url": "https://www.xiaohongshu.com/explore/687128a2000000001203d94c?xsec_token=CBlDi5QDXDWZu2uUmbUrpKwg8lEL3uC10mc59lGf43r9w=", "canFail": true, "params": {}, "expected": { @@ -21,7 +21,7 @@ }, { "name": "one photo (might have expired)", - "url": "https://www.xiaohongshu.com/explore/676e132d000000000b016f68?xsec_token=ABRv6LKzizOFeSaf2HnnBkdBqniB5Ak1fI8tMAHzO31jA", + "url": "https://www.xiaohongshu.com/explore/64726b99000000000800e115?xsec_token=ABoD3qPHqVZolCfS-J8UP9QQaPXZ6Z6PVyODrhaiUg27U=", "canFail": true, "params": {}, "expected": { @@ -31,7 +31,7 @@ }, { "name": "short link (might have expired)", - "url": "https://xhslink.com/a/czn4z6c1tic4", + "url": "https://xhslink.com/m/2wAnaTkLRc1", "canFail": true, "params": {}, "expected": {