blob: 705368a827b810173f39087f25df30bcef16f2cf [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';
Krishyadbfadaa2025-06-09 20:33:15 +08008import AuthButton from '../../../components/AuthButton';
Krishya2283d882025-05-27 22:25:19 +08009
223010095b28c672025-04-10 20:12:45 +080010const SeedDetail = () => {
2230100980aaf0d2025-06-05 23:20:05 +080011 const params = useParams();
12 const seed_id = params.id;
223010095b28c672025-04-10 20:12:45 +080013
2230100980aaf0d2025-06-05 23:20:05 +080014 const [seed, setSeed] = useState(null);
15 const [coverImage, setCoverImage] = useState(null);
16 const [error, setError] = useState(null);
17 const [comments, setComments] = useState([]);
18 const [newComment, setNewComment] = useState('');
19
20 const { user } = useUser();
21
2230100980aaf0d2025-06-05 23:20:05 +080022 const formatImageUrl = (url) => {
23 if (!url) return '';
24 const filename = url.split('/').pop();
223010094158f3a2025-06-06 19:59:10 +080025 return `http://localhost:5011/uploads/torrents/${filename}`;
2230100980aaf0d2025-06-05 23:20:05 +080026 };
27
28 useEffect(() => {
29 if (!seed_id) {
30 setError('无效的种子ID');
31 return;
32 }
33
34 const fetchSeedDetail = async () => {
35 try {
36 const res = await axios.post(`/seeds/info/${seed_id}`);
37 if (res.data.code === 0) {
38 const seedData = res.data.data;
2230100980aaf0d2025-06-05 23:20:05 +080039 let cover = seedData.imageUrl;
40 if (!cover && seedData.imgUrl) {
41 const imgs = seedData.imgUrl
42 .split(',')
43 .map((i) => i.trim())
44 .filter(Boolean);
45 cover = imgs.length > 0 ? formatImageUrl(imgs[0]) : null;
46 }
47 setCoverImage(cover);
48 setSeed(seedData);
49 setError(null);
50 } else {
51 setError('未能获取种子信息');
52 }
53 } catch (err) {
54 console.error('请求种子详情出错:', err);
55 setError('获取种子详情失败');
56 }
57 };
58
59 const fetchComments = async () => {
60 try {
61 const res = await axios.get(`/seeds/${seed_id}/comments`);
62 if (res.data.code === 0) {
63 setComments(res.data.data || []);
64 } else {
65 setComments([]);
66 }
67 } catch {
68 setComments([]);
69 }
70 };
71
72 fetchSeedDetail();
73 fetchComments();
74 }, [seed_id]);
75
223010094952a0f2025-06-07 18:58:16 +080076 const handleDownload = async (seedId) => {
77 if (!user || !user.userId) {
78 alert('请先登录再下载种子文件');
79 return;
80 }
2230100980aaf0d2025-06-05 23:20:05 +080081
223010094952a0f2025-06-07 18:58:16 +080082 try {
83 const response = await axios.get(`/seeds/${seedId}/download`, {
84 params: { passkey: user.userId },
85 responseType: 'blob',
86 });
Krishyac0f7e9b2025-04-22 15:28:28 +080087
223010094952a0f2025-06-07 18:58:16 +080088 const blob = new Blob([response.data], { type: 'application/x-bittorrent' });
89 const downloadUrl = URL.createObjectURL(blob);
90 const a = document.createElement('a');
91 a.href = downloadUrl;
92 a.download = `${seedId}.torrent`;
93 a.click();
94 URL.revokeObjectURL(downloadUrl);
95 } catch (error) {
96 console.error('下载失败:', error);
97 alert('下载失败,请稍后再试。');
98 }
2230100980aaf0d2025-06-05 23:20:05 +080099 };
Krishyac0f7e9b2025-04-22 15:28:28 +0800100
223010094952a0f2025-06-07 18:58:16 +0800101 const handleCollect = async () => {
102 if (!user || !user.userId) {
103 alert('请先登录再收藏');
104 return;
105 }
106
107 try {
108 const res = await axios.post(`/seeds/${seed.id}/favorite-toggle`, null, {
109 params: { user_id: user.userId },
110 });
111
112 if (res.data.code === 0) {
113 alert('操作成功');
114 } else {
115 alert(res.data.msg || '操作失败');
116 }
117 } catch (err) {
118 console.error('收藏失败:', err);
119 alert('收藏失败,请稍后再试。');
120 }
121 };
122
123 const handleAddComment = async () => {
124 if (!user || !user.userId) {
125 alert('请登录后发表评论');
126 return;
127 }
128
2230100980aaf0d2025-06-05 23:20:05 +0800129 if (newComment.trim()) {
223010094952a0f2025-06-07 18:58:16 +0800130 try {
131 const res = await axios.post(`/seeds/${seed_id}/comments`, {
132 user_id: user.userId,
133 content: newComment,
134 });
135
136 if (res.data.code === 0) {
137 setComments([...comments, { content: newComment, user: user.username || '匿名用户' }]);
138 setNewComment('');
139 } else {
140 alert(res.data.msg || '评论失败');
141 }
142 } catch (err) {
143 console.error('评论提交失败:', err);
144 alert('评论失败,请稍后重试');
145 }
Krishyac0f7e9b2025-04-22 15:28:28 +0800146 }
2230100980aaf0d2025-06-05 23:20:05 +0800147 };
Krishyac0f7e9b2025-04-22 15:28:28 +0800148
2230100980aaf0d2025-06-05 23:20:05 +0800149 if (error) {
223010095b28c672025-04-10 20:12:45 +0800150 return (
2230100980aaf0d2025-06-05 23:20:05 +0800151 <div className="seed-detail-page">
152 <Header />
153 <div className="seed-detail">
154 <p className="error-text">{error}</p>
223010095b28c672025-04-10 20:12:45 +0800155 </div>
2230100980aaf0d2025-06-05 23:20:05 +0800156 </div>
223010095b28c672025-04-10 20:12:45 +0800157 );
2230100980aaf0d2025-06-05 23:20:05 +0800158 }
159
160 if (!seed) {
161 return (
162 <div className="seed-detail-page">
163 <Header />
164 <div className="seed-detail">
165 <p>加载中...</p>
166 </div>
167 </div>
168 );
169 }
170
223010094952a0f2025-06-07 18:58:16 +0800171 const tags = Array.isArray(seed.tags)
172 ? seed.tags
173 : typeof seed.tags === 'string'
174 ? seed.tags.split(',').map((t) => t.trim())
2230100980aaf0d2025-06-05 23:20:05 +0800175 : [];
176
223010094952a0f2025-06-07 18:58:16 +0800177 const actors = Array.isArray(seed.actors)
178 ? seed.actors
179 : typeof seed.actors === 'string'
180 ? seed.actors.split(',').map((a) => a.trim())
2230100980aaf0d2025-06-05 23:20:05 +0800181 : [];
182
183 return (
184 <div className="seed-detail-page">
185 <Header />
186 <div className="seed-detail">
187 <h1>{seed.title}</h1>
188 <div className="seed-header-container">
189 <div className="seed-info">
190 <div className="seed-basic-info">
191 <p><strong>分类:</strong>{seed.category || '未知'}</p>
192 <p><strong>发布时间:</strong>{seed.upload_time ? new Date(seed.upload_time).toLocaleString() : '未知'}</p>
193 <p><strong>标签:</strong>{tags.join(' / ')}</p>
194 <p><strong>简介:</strong>{seed.description || ''}</p>
195 <p><strong>大小:</strong>{seed.size || '未知'}</p>
196 <p><strong>分辨率:</strong>{seed.resolution || '未知'}</p>
197 <p><strong>片长:</strong>{seed.duration || '未知'}</p>
198 <p><strong>地区:</strong>{seed.region || '未知'}</p>
199 <p><strong>下载次数:</strong>{seed.downloads ?? 0}</p>
200 </div>
201 {(seed.category === '电影' || seed.category === '电视剧') && (
202 <div className="seed-media-info">
203 <p><strong>导演:</strong>{seed.director || '未知'}</p>
204 <p><strong>编剧:</strong>{seed.writer || '未知'}</p>
205 <p><strong>主演:</strong>{actors.join(' / ')}</p>
206 </div>
207 )}
208 </div>
209 <img
210 src={coverImage || '/default-cover.png'}
211 alt={seed.title}
212 className="cover-image"
213 />
214 </div>
223010094952a0f2025-06-07 18:58:16 +0800215
2230100980aaf0d2025-06-05 23:20:05 +0800216 <div className="action-buttons">
Krishyadbfadaa2025-06-09 20:33:15 +0800217 <AuthButton roles={["cookie", "chocolate", "ice-cream"]} onClick={() => handleDownload(seed.id)}>
218 下载
219 </AuthButton>
220 <AuthButton roles={["cookie", "chocolate", "ice-cream"]} onClick={handleCollect}>
221 收藏
222 </AuthButton>
Krishya3dc6b352025-06-07 19:02:25 +0800223 <SeedRating seedId={seed.id} />
2230100980aaf0d2025-06-05 23:20:05 +0800224 </div>
223010094952a0f2025-06-07 18:58:16 +0800225
Krishyadbfadaa2025-06-09 20:33:15 +0800226
2230100980aaf0d2025-06-05 23:20:05 +0800227 <hr className="divider" />
228 <h3>评论区</h3>
229 <div className="comments-section">
230 <div className="comments-list">
223010094952a0f2025-06-07 18:58:16 +0800231 {comments.length === 0 ? (
232 <p className="no-comments">暂无评论</p>
233 ) : (
234 comments.map((comment, index) => (
235 <div key={index} className="comment">
236 <p className="comment-user">{comment.user}</p>
237 <p className="comment-content">{comment.content}</p>
238 </div>
239 ))
240 )}
2230100980aaf0d2025-06-05 23:20:05 +0800241 </div>
242 <div className="add-comment-form">
243 <textarea
244 placeholder="输入你的评论..."
245 value={newComment}
246 onChange={(e) => setNewComment(e.target.value)}
247 />
248 <div className="comment-options">
249 <button className="btn" onClick={handleAddComment}>发布评论</button>
250 </div>
251 </div>
252 </div>
253 </div>
254 </div>
255 );
223010095b28c672025-04-10 20:12:45 +0800256};
257
258export default SeedDetail;