diff --git a/src/clis/xiaohongshu/user-helpers.test.ts b/src/clis/xiaohongshu/user-helpers.test.ts index f990110d..7050d4fe 100644 --- a/src/clis/xiaohongshu/user-helpers.test.ts +++ b/src/clis/xiaohongshu/user-helpers.test.ts @@ -75,6 +75,7 @@ describe('extractXhsUserNotes', () => { title: 'First note', type: 'video', likes: '4.6δΈ‡', + cover: '', url: 'https://www.xiaohongshu.com/user/profile/user-1/note-1?xsec_token=abc&xsec_source=pc_user', }, { @@ -82,11 +83,33 @@ describe('extractXhsUserNotes', () => { title: 'Second note', type: 'normal', likes: '42', + cover: '', url: 'https://www.xiaohongshu.com/user/profile/fallback-user/note-2', }, ]); }); + it('extracts cover urls with fallback priority urlDefault -> urlPre -> url', () => { + const rows = extractXhsUserNotes( + { + noteGroups: [ + [ + { noteCard: { noteId: 'cover-1', cover: { urlDefault: 'https://img.example/default.jpg', urlPre: 'https://img.example/pre.jpg', url: 'https://img.example/raw.jpg' } } }, + { noteCard: { noteId: 'cover-2', cover: { urlPre: 'https://img.example/pre-only.jpg', url: 'https://img.example/raw-only.jpg' } } }, + { noteCard: { noteId: 'cover-3', cover: { url: 'https://img.example/raw-fallback.jpg' } } }, + ], + ], + }, + 'fallback-user' + ); + + expect(rows.map(row => row.cover)).toEqual([ + 'https://img.example/default.jpg', + 'https://img.example/pre-only.jpg', + 'https://img.example/raw-fallback.jpg', + ]); + }); + it('deduplicates repeated notes by note id', () => { const rows = extractXhsUserNotes( { diff --git a/src/clis/xiaohongshu/user-helpers.ts b/src/clis/xiaohongshu/user-helpers.ts index b5ddb9c0..9bf1ac5c 100644 --- a/src/clis/xiaohongshu/user-helpers.ts +++ b/src/clis/xiaohongshu/user-helpers.ts @@ -8,6 +8,7 @@ export interface XhsUserNoteRow { title: string; type: string; likes: string; + cover: string; url: string; } @@ -72,11 +73,14 @@ export function extractXhsUserNotes(snapshot: XhsUserPageSnapshot, fallbackUserI const xsecToken = toCleanString(entry?.xsecToken ?? entry?.xsec_token ?? noteCard.xsecToken ?? noteCard.xsec_token); const likes = toCleanString(noteCard.interactInfo?.likedCount ?? noteCard.interact_info?.liked_count ?? 0) || '0'; + const cover = toCleanString(noteCard.cover?.urlDefault ?? noteCard.cover?.urlPre ?? noteCard.cover?.url ?? ''); + rows.push({ id: noteId, title: toCleanString(noteCard.displayTitle ?? noteCard.display_title ?? noteCard.title), type: toCleanString(noteCard.type), likes, + cover, url: buildXhsNoteUrl(userId || fallbackUserId, noteId, xsecToken), }); }