fix: 收紧Bilibili链接解析边界
This commit is contained in:
parent
7522b4c2b2
commit
2d0d8dc4cd
@ -2,10 +2,10 @@ import type { BilibiliVideoReference } from './bilibili-card.types';
|
||||
|
||||
const BILIBILI_HOST = 'bilibili.com';
|
||||
const B23_HOST = 'b23.tv';
|
||||
const TRAILING_WRAPPERS = /[\s"'<>,。!?、;:))\]}]+$/u;
|
||||
const LEADING_WRAPPERS = /^[\s"'<>(([{]+/u;
|
||||
const BVID_PATTERN = /(?:^|\/)(BV[0-9A-Za-z]{10,})/;
|
||||
const AID_PATTERN = /(?:^|\/)(?:av|AV)(\d+)(?:$|[/?#])/;
|
||||
const TRAILING_WRAPPERS = /[\s"'<>,。!?、;:))\]}】]+$/u;
|
||||
const LEADING_WRAPPERS = /^[\s"'<>(([{【]+/u;
|
||||
const BVID_PATTERN = /^BV[0-9A-Za-z]{10}$/;
|
||||
const AID_PATTERN = /^(?:av|AV)(\d+)$/;
|
||||
|
||||
/**
|
||||
* Removes QQ card wrappers and punctuation that often stick to copied URLs.
|
||||
@ -17,6 +17,10 @@ export function cleanBilibiliUrlCandidate(candidate: string) {
|
||||
.replaceAll('&', '&')
|
||||
.replaceAll('"', '"')
|
||||
.replaceAll('"', '"')
|
||||
.replaceAll('<', '<')
|
||||
.replaceAll('<', '<')
|
||||
.replaceAll('>', '>')
|
||||
.replaceAll('>', '>')
|
||||
.replace(LEADING_WRAPPERS, '')
|
||||
.replace(TRAILING_WRAPPERS, '')
|
||||
.trim();
|
||||
@ -54,18 +58,23 @@ export function parseBilibiliVideoReference(
|
||||
if (!isAllowedBilibiliUrl(cleaned)) return null;
|
||||
|
||||
const url = new URL(cleaned);
|
||||
const probe = `${url.pathname}${url.search}${url.hash}`;
|
||||
const bvid = probe.match(BVID_PATTERN)?.[1];
|
||||
if (bvid) {
|
||||
const pathSegments = url.pathname.split('/').filter(Boolean);
|
||||
const videoSegmentIndex = pathSegments.findIndex(
|
||||
(segment) => segment.toLowerCase() === 'video',
|
||||
);
|
||||
if (videoSegmentIndex < 0) return null;
|
||||
|
||||
const videoIdSegment = pathSegments[videoSegmentIndex + 1];
|
||||
if (videoIdSegment && BVID_PATTERN.test(videoIdSegment)) {
|
||||
return {
|
||||
canonicalVideoId: bvid,
|
||||
canonicalVideoId: videoIdSegment,
|
||||
kind: 'bvid',
|
||||
sourceUrl: cleaned,
|
||||
value: bvid,
|
||||
value: videoIdSegment,
|
||||
};
|
||||
}
|
||||
|
||||
const aid = `${url.pathname}/`.match(AID_PATTERN)?.[1];
|
||||
const aid = videoIdSegment?.match(AID_PATTERN)?.[1];
|
||||
if (aid) {
|
||||
return {
|
||||
canonicalVideoId: `av${aid}`,
|
||||
|
||||
@ -44,4 +44,33 @@ describe('Bilibili URL parser', () => {
|
||||
),
|
||||
).toBe('https://www.bilibili.com/video/BV1xx411c7mD?p=1');
|
||||
});
|
||||
|
||||
it('does not parse video ids from query or hash on non-video Bilibili pages', () => {
|
||||
expect(
|
||||
parseBilibiliVideoReference(
|
||||
'https://space.bilibili.com/1?from=/video/BV1xx411c7mD',
|
||||
),
|
||||
).toBeNull();
|
||||
expect(
|
||||
parseBilibiliVideoReference(
|
||||
'https://www.bilibili.com/search?keyword=BV1xx411c7mD',
|
||||
),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('rejects malformed BV path segments with extra characters', () => {
|
||||
expect(
|
||||
parseBilibiliVideoReference(
|
||||
'https://www.bilibili.com/video/BV1xx411c7mDextra',
|
||||
),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('cleans xml html entities and full-width brackets', () => {
|
||||
expect(
|
||||
cleanBilibiliUrlCandidate(
|
||||
'<【https://www.bilibili.com/video/BV1xx411c7mD】>',
|
||||
),
|
||||
).toBe('https://www.bilibili.com/video/BV1xx411c7mD');
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user