feat: douban url

This commit is contained in:
shinya
2025-07-01 20:35:54 +08:00
parent dfc6e84475
commit 4cb26067fe
5 changed files with 28 additions and 7 deletions

View File

@@ -5,6 +5,7 @@ import { DoubanItem, DoubanResult } from '@/lib/types';
interface DoubanApiResponse {
subjects: Array<{
id: string;
title: string;
cover: string;
rate: string;
@@ -95,6 +96,7 @@ export async function GET(request: Request) {
// 转换数据格式
const list: DoubanItem[] = doubanData.subjects.map((item) => ({
id: item.id,
title: item.title,
poster: item.cover,
rate: item.rate,
@@ -149,21 +151,23 @@ function handleTop250(pageStart: number) {
// 获取 HTML 内容
const html = await fetchResponse.text();
// 使用正则表达式提取电影信息
// 通过正则同时捕获影片 id、标题、封面以及评分
const moviePattern =
/<div class="item">[\s\S]*?<img[^>]+alt="([^"]+)"[^>]*src="([^"]+)"[\s\S]*?<span class="rating_num"[^>]*>([^<]+)<\/span>[\s\S]*?<\/div>/g;
/<div class="item">[\s\S]*?<a[^>]+href="https?:\/\/movie\.douban\.com\/subject\/(\d+)\/"[\s\S]*?<img[^>]+alt="([^"]+)"[^>]*src="([^"]+)"[\s\S]*?<span class="rating_num"[^>]*>([^<]*)<\/span>[\s\S]*?<\/div>/g;
const movies: DoubanItem[] = [];
let match;
while ((match = moviePattern.exec(html)) !== null) {
const title = match[1];
const cover = match[2];
const rate = match[3] || '';
const id = match[1];
const title = match[2];
const cover = match[3];
const rate = match[4] || '';
// 处理图片 URL确保使用 HTTPS
const processedCover = cover.replace(/^http:/, 'https:');
movies.push({
id: id,
title: title,
poster: processedCover,
rate: rate,

View File

@@ -199,6 +199,7 @@ function DoubanPageClient() {
doubanData.map((item, index) => (
<div key={`${item.title}-${index}`} className='w-full'>
<DemoCard
id={item.id}
title={item.title}
poster={item.poster}
rate={item.rate}

View File

@@ -193,6 +193,7 @@ function HomeClient() {
className='min-w-[96px] w-24 sm:min-w-[180px] sm:w-44'
>
<DemoCard
id={movie.id}
title={movie.title}
poster={movie.poster}
rate={movie.rate}
@@ -237,6 +238,7 @@ function HomeClient() {
className='min-w-[96px] w-24 sm:min-w-[180px] sm:w-44'
>
<DemoCard
id={show.id}
title={show.title}
poster={show.poster}
rate={show.rate}

View File

@@ -1,9 +1,10 @@
import { Search } from 'lucide-react';
import { Link as LinkIcon, Search } from 'lucide-react';
import Image from 'next/image';
import { useRouter } from 'next/navigation';
import React, { useState } from 'react';
interface DemoCardProps {
id: string;
title: string;
poster: string;
rate?: string;
@@ -53,7 +54,7 @@ function SearchCircle({
);
}
const DemoCard = ({ title, poster, rate }: DemoCardProps) => {
const DemoCard = ({ id, title, poster, rate }: DemoCardProps) => {
const [hover, setHover] = useState(false);
const router = useRouter();
@@ -98,6 +99,18 @@ const DemoCard = ({ title, poster, rate }: DemoCardProps) => {
</div>
</div>
</div>
{/* 顶部左侧 🔗 链接按钮 */}
<a
href={`https://movie.douban.com/subject/${id}`}
target='_blank'
rel='noopener noreferrer'
onClick={(e) => e.stopPropagation()}
className='absolute top-2 left-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200'
>
<div className='w-8 h-8 sm:w-9 sm:h-9 rounded-full bg-gray-600/60 flex items-center justify-center transition-all duration-200 hover:bg-green-500 hover:scale-110'>
<LinkIcon className='w-4 h-4 text-white' strokeWidth={2} />
</div>
</a>
</div>
{/* 信息层 */}
<div className='absolute top-[calc(100%+0.2rem)] left-0 right-0'>

View File

@@ -32,6 +32,7 @@ export interface SearchResult {
}
export interface DoubanItem {
id: string;
title: string;
poster: string;
rate: string;