blob: 4ab9274e82cd0047947cce1f7ba6bef2d8f99f73 [file] [log] [blame]
223010095b28c672025-04-10 20:12:45 +08001import React, { useEffect, useState } from 'react';
2import axios from 'axios';
2230100980aaf0d2025-06-05 23:20:05 +08003import { useParams } from 'wouter';
223010095b28c672025-04-10 20:12:45 +08004import Header from '../../../components/Header';
5import './SeedDetail.css';
2230100980aaf0d2025-06-05 23:20:05 +08006import { useUser } from '../../../context/UserContext';
Krishya3dc6b352025-06-07 19:02:25 +08007import SeedRating from './SeedRating';
Krishya2283d882025-05-27 22:25:19 +08008
223010095b28c672025-04-10 20:12:45 +08009const SeedDetail = () => {
2230100980aaf0d2025-06-05 23:20:05 +080010 const params = useParams();
11 const seed_id = params.id;
223010095b28c672025-04-10 20:12:45 +080012
2230100980aaf0d2025-06-05 23:20:05 +080013 const [seed, setSeed] = useState(null);
14 const [coverImage, setCoverImage] = useState(null);
15 const [error, setError] = useState(null);
16 const [comments, setComments] = useState([]);
17 const [newComment, setNewComment] = useState('');
18
19 const { user } = useUser();
20
2230100980aaf0d2025-06-05 23:20:05 +080021 const formatImageUrl = (url) => {
22 if (!url) return '';
23 const filename = url.split('/').pop();
223010094158f3a2025-06-06 19:59:10 +080024 return `http://localhost:5011/uploads/torrents/${filename}`;
2230100980aaf0d2025-06-05 23:20:05 +080025 };
26
27 useEffect(() => {
28 if (!seed_id) {
29 setError('无效的种子ID');
30 return;
31 }
32
33 const fetchSeedDetail = async () => {
34 try {
35 const res = await axios.post(`/seeds/info/${seed_id}`);
36 if (res.data.code === 0) {
37 const seedData = res.data.data;
2230100980aaf0d2025-06-05 23:20:05 +080038 let cover = seedData.imageUrl;
39 if (!cover && seedData.imgUrl) {
40 const imgs = seedData.imgUrl
41 .split(',')
42 .map((i) => i.trim())
43 .filter(Boolean);
44 cover = imgs.length > 0 ? formatImageUrl(imgs[0]) : null;
45 }
46 setCoverImage(cover);
47 setSeed(seedData);
48 setError(null);
49 } else {
50 setError('未能获取种子信息');
51 }
52 } catch (err) {
53 console.error('请求种子详情出错:', err);
54 setError('获取种子详情失败');
55 }
56 };
57
58 const fetchComments = async () => {
59 try {
60 const res = await axios.get(`/seeds/${seed_id}/comments`);
61 if (res.data.code === 0) {
62 setComments(res.data.data || []);
63 } else {
64 setComments([]);
65 }
66 } catch {
67 setComments([]);
68 }
69 };
70
71 fetchSeedDetail();
72 fetchComments();
73 }, [seed_id]);
74
223010094952a0f2025-06-07 18:58:16 +080075 const handleDownload = async (seedId) => {
76 if (!user || !user.userId) {
77 alert('请先登录再下载种子文件');
78 return;
79 }
2230100980aaf0d2025-06-05 23:20:05 +080080
223010094952a0f2025-06-07 18:58:16 +080081 try {
82 const response = await axios.get(`/seeds/${seedId}/download`, {
83 params: { passkey: user.userId },
84 responseType: 'blob',
85 });
Krishyac0f7e9b2025-04-22 15:28:28 +080086
223010094952a0f2025-06-07 18:58:16 +080087 const blob = new Blob([response.data], { type: 'application/x-bittorrent' });
88 const downloadUrl = URL.createObjectURL(blob);
89 const a = document.createElement('a');
90 a.href = downloadUrl;
91 a.download = `${seedId}.torrent`;
92 a.click();
93 URL.revokeObjectURL(downloadUrl);
94 } catch (error) {
95 console.error('下载失败:', error);
96 alert('下载失败,请稍后再试。');
97 }
2230100980aaf0d2025-06-05 23:20:05 +080098 };
Krishyac0f7e9b2025-04-22 15:28:28 +080099
223010094952a0f2025-06-07 18:58:16 +0800100 const handleCollect = async () => {
101 if (!user || !user.userId) {
102 alert('请先登录再收藏');
103 return;
104 }
105
106 try {
107 const res = await axios.post(`/seeds/${seed.id}/favorite-toggle`, null, {
108 params: { user_id: user.userId },
109 });
110
111 if (res.data.code === 0) {
112 alert('操作成功');
113 } else {
114 alert(res.data.msg || '操作失败');
115 }
116 } catch (err) {
117 console.error('收藏失败:', err);
118 alert('收藏失败,请稍后再试。');
119 }
120 };
121
122 const handleAddComment = async () => {
123 if (!user || !user.userId) {
124 alert('请登录后发表评论');
125 return;
126 }
127
2230100980aaf0d2025-06-05 23:20:05 +0800128 if (newComment.trim()) {
223010094952a0f2025-06-07 18:58:16 +0800129 try {
130 const res = await axios.post(`/seeds/${seed_id}/comments`, {
131 user_id: user.userId,
132 content: newComment,
133 });
134
135 if (res.data.code === 0) {
136 setComments([...comments, { content: newComment, user: user.username || '匿名用户' }]);
137 setNewComment('');
138 } else {
139 alert(res.data.msg || '评论失败');
140 }
141 } catch (err) {
142 console.error('评论提交失败:', err);
143 alert('评论失败,请稍后重试');
144 }
Krishyac0f7e9b2025-04-22 15:28:28 +0800145 }
2230100980aaf0d2025-06-05 23:20:05 +0800146 };
Krishyac0f7e9b2025-04-22 15:28:28 +0800147
2230100980aaf0d2025-06-05 23:20:05 +0800148 if (error) {
223010095b28c672025-04-10 20:12:45 +0800149 return (
2230100980aaf0d2025-06-05 23:20:05 +0800150 <div className="seed-detail-page">
151 <Header />
152 <div className="seed-detail">
153 <p className="error-text">{error}</p>
223010095b28c672025-04-10 20:12:45 +0800154 </div>
2230100980aaf0d2025-06-05 23:20:05 +0800155 </div>
223010095b28c672025-04-10 20:12:45 +0800156 );
2230100980aaf0d2025-06-05 23:20:05 +0800157 }
158
159 if (!seed) {
160 return (
161 <div className="seed-detail-page">
162 <Header />
163 <div className="seed-detail">
164 <p>加载中...</p>
165 </div>
166 </div>
167 );
168 }
169
223010094952a0f2025-06-07 18:58:16 +0800170 const tags = Array.isArray(seed.tags)
171 ? seed.tags
172 : typeof seed.tags === 'string'
173 ? seed.tags.split(',').map((t) => t.trim())
2230100980aaf0d2025-06-05 23:20:05 +0800174 : [];
175
223010094952a0f2025-06-07 18:58:16 +0800176 const actors = Array.isArray(seed.actors)
177 ? seed.actors
178 : typeof seed.actors === 'string'
179 ? seed.actors.split(',').map((a) => a.trim())
2230100980aaf0d2025-06-05 23:20:05 +0800180 : [];
181
182 return (
183 <div className="seed-detail-page">
184 <Header />
185 <div className="seed-detail">
186 <h1>{seed.title}</h1>
187 <div className="seed-header-container">
188 <div className="seed-info">
189 <div className="seed-basic-info">
190 <p><strong>分类:</strong>{seed.category || '未知'}</p>
191 <p><strong>发布时间:</strong>{seed.upload_time ? new Date(seed.upload_time).toLocaleString() : '未知'}</p>
192 <p><strong>标签:</strong>{tags.join(' / ')}</p>
193 <p><strong>简介:</strong>{seed.description || ''}</p>
194 <p><strong>大小:</strong>{seed.size || '未知'}</p>
195 <p><strong>分辨率:</strong>{seed.resolution || '未知'}</p>
196 <p><strong>片长:</strong>{seed.duration || '未知'}</p>
197 <p><strong>地区:</strong>{seed.region || '未知'}</p>
198 <p><strong>下载次数:</strong>{seed.downloads ?? 0}</p>
199 </div>
200 {(seed.category === '电影' || seed.category === '电视剧') && (
201 <div className="seed-media-info">
202 <p><strong>导演:</strong>{seed.director || '未知'}</p>
203 <p><strong>编剧:</strong>{seed.writer || '未知'}</p>
204 <p><strong>主演:</strong>{actors.join(' / ')}</p>
205 </div>
206 )}
207 </div>
208 <img
209 src={coverImage || '/default-cover.png'}
210 alt={seed.title}
211 className="cover-image"
212 />
213 </div>
223010094952a0f2025-06-07 18:58:16 +0800214
2230100980aaf0d2025-06-05 23:20:05 +0800215 <div className="action-buttons">
216 <button className="btn" onClick={() => handleDownload(seed.id)}>下载</button>
223010094952a0f2025-06-07 18:58:16 +0800217 <button className="btn-outline" onClick={handleCollect}>收藏</button>
Krishya3dc6b352025-06-07 19:02:25 +0800218 {/* <button className="btn" onClick={handleCollect}>收藏</button> */}
219 <SeedRating seedId={seed.id} />
2230100980aaf0d2025-06-05 23:20:05 +0800220 </div>
223010094952a0f2025-06-07 18:58:16 +0800221
2230100980aaf0d2025-06-05 23:20:05 +0800222 <hr className="divider" />
223 <h3>评论区</h3>
224 <div className="comments-section">
225 <div className="comments-list">
223010094952a0f2025-06-07 18:58:16 +0800226 {comments.length === 0 ? (
227 <p className="no-comments">暂无评论</p>
228 ) : (
229 comments.map((comment, index) => (
230 <div key={index} className="comment">
231 <p className="comment-user">{comment.user}</p>
232 <p className="comment-content">{comment.content}</p>
233 </div>
234 ))
235 )}
2230100980aaf0d2025-06-05 23:20:05 +0800236 </div>
237 <div className="add-comment-form">
238 <textarea
239 placeholder="输入你的评论..."
240 value={newComment}
241 onChange={(e) => setNewComment(e.target.value)}
242 />
243 <div className="comment-options">
244 <button className="btn" onClick={handleAddComment}>发布评论</button>
245 </div>
246 </div>
247 </div>
248 </div>
249 </div>
250 );
223010095b28c672025-04-10 20:12:45 +0800251};
252
253export default SeedDetail;