22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 1 | import React, { useEffect, useState } from 'react'; |
| 2 | import axios from 'axios'; |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 3 | import { useParams } from 'wouter'; |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 4 | import Header from '../../../components/Header'; |
| 5 | import './SeedDetail.css'; |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 6 | import { useUser } from '../../../context/UserContext'; |
Krishya | 2283d88 | 2025-05-27 22:25:19 +0800 | [diff] [blame] | 7 | |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 8 | |
| 9 | const SeedDetail = () => { |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 10 | const params = useParams(); |
| 11 | const seed_id = params.id; |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 12 | |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 13 | 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 | |
| 21 | |
| 22 | // 格式化图片 URL |
| 23 | const formatImageUrl = (url) => { |
| 24 | if (!url) return ''; |
| 25 | const filename = url.split('/').pop(); |
22301009 | 4158f3a | 2025-06-06 19:59:10 +0800 | [diff] [blame] | 26 | return `http://localhost:5011/uploads/torrents/${filename}`; |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 27 | }; |
| 28 | |
| 29 | useEffect(() => { |
| 30 | if (!seed_id) { |
| 31 | setError('无效的种子ID'); |
| 32 | return; |
| 33 | } |
| 34 | |
| 35 | const fetchSeedDetail = async () => { |
| 36 | try { |
| 37 | const res = await axios.post(`/seeds/info/${seed_id}`); |
| 38 | if (res.data.code === 0) { |
| 39 | const seedData = res.data.data; |
| 40 | |
| 41 | // 处理封面图 |
| 42 | let cover = seedData.imageUrl; |
| 43 | if (!cover && seedData.imgUrl) { |
| 44 | const imgs = seedData.imgUrl |
| 45 | .split(',') |
| 46 | .map((i) => i.trim()) |
| 47 | .filter(Boolean); |
| 48 | cover = imgs.length > 0 ? formatImageUrl(imgs[0]) : null; |
| 49 | } |
| 50 | setCoverImage(cover); |
| 51 | setSeed(seedData); |
| 52 | setError(null); |
| 53 | } else { |
| 54 | setError('未能获取种子信息'); |
| 55 | } |
| 56 | } catch (err) { |
| 57 | console.error('请求种子详情出错:', err); |
| 58 | setError('获取种子详情失败'); |
| 59 | } |
| 60 | }; |
| 61 | |
| 62 | const fetchComments = async () => { |
| 63 | try { |
| 64 | const res = await axios.get(`/seeds/${seed_id}/comments`); |
| 65 | if (res.data.code === 0) { |
| 66 | setComments(res.data.data || []); |
| 67 | } else { |
| 68 | setComments([]); |
| 69 | } |
| 70 | } catch { |
| 71 | setComments([]); |
| 72 | } |
| 73 | }; |
| 74 | |
| 75 | fetchSeedDetail(); |
| 76 | fetchComments(); |
| 77 | }, [seed_id]); |
| 78 | |
| 79 | const handleDownload = async (seedId) => { |
| 80 | if (!user || !user.userId) { |
| 81 | alert('请先登录再下载种子文件'); |
| 82 | return; |
| 83 | } |
| 84 | |
| 85 | try { |
| 86 | const response = await axios.get(`/seeds/${seedId}/download`, { |
| 87 | params: { |
| 88 | passkey: user.userId, |
| 89 | }, |
| 90 | responseType: 'blob' |
Krishya | c0f7e9b | 2025-04-22 15:28:28 +0800 | [diff] [blame] | 91 | }); |
| 92 | |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 93 | const blob = new Blob([response.data], { type: 'application/x-bittorrent' }); |
| 94 | const downloadUrl = URL.createObjectURL(blob); |
| 95 | const a = document.createElement('a'); |
| 96 | a.href = downloadUrl; |
| 97 | a.download = `${seedId}.torrent`; |
| 98 | a.click(); |
| 99 | URL.revokeObjectURL(downloadUrl); |
| 100 | } catch (error) { |
| 101 | console.error('下载失败:', error); |
| 102 | alert('下载失败,请稍后再试。'); |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 103 | } |
Krishya | c0f7e9b | 2025-04-22 15:28:28 +0800 | [diff] [blame] | 104 | }; |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 105 | |
Krishya | c0f7e9b | 2025-04-22 15:28:28 +0800 | [diff] [blame] | 106 | |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 107 | const handleCollect = () => { |
| 108 | alert('已收藏'); |
| 109 | }; |
Krishya | c0f7e9b | 2025-04-22 15:28:28 +0800 | [diff] [blame] | 110 | |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 111 | const handleAddComment = () => { |
| 112 | if (newComment.trim()) { |
| 113 | setComments([...comments, { content: newComment, user: '用户' }]); |
| 114 | setNewComment(''); |
Krishya | c0f7e9b | 2025-04-22 15:28:28 +0800 | [diff] [blame] | 115 | } |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 116 | }; |
Krishya | c0f7e9b | 2025-04-22 15:28:28 +0800 | [diff] [blame] | 117 | |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 118 | if (error) { |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 119 | return ( |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 120 | <div className="seed-detail-page"> |
| 121 | <Header /> |
| 122 | <div className="seed-detail"> |
| 123 | <p className="error-text">{error}</p> |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 124 | </div> |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 125 | </div> |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 126 | ); |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 127 | } |
| 128 | |
| 129 | if (!seed) { |
| 130 | return ( |
| 131 | <div className="seed-detail-page"> |
| 132 | <Header /> |
| 133 | <div className="seed-detail"> |
| 134 | <p>加载中...</p> |
| 135 | </div> |
| 136 | </div> |
| 137 | ); |
| 138 | } |
| 139 | |
| 140 | const tags = seed.tags |
| 141 | ? Array.isArray(seed.tags) |
| 142 | ? seed.tags |
| 143 | : typeof seed.tags === 'string' |
| 144 | ? seed.tags.split(',').map((t) => t.trim()) |
| 145 | : [] |
| 146 | : []; |
| 147 | |
| 148 | const actors = seed.actors |
| 149 | ? Array.isArray(seed.actors) |
| 150 | ? seed.actors |
| 151 | : typeof seed.actors === 'string' |
| 152 | ? seed.actors.split(',').map((a) => a.trim()) |
| 153 | : [] |
| 154 | : []; |
| 155 | |
| 156 | return ( |
| 157 | <div className="seed-detail-page"> |
| 158 | <Header /> |
| 159 | <div className="seed-detail"> |
| 160 | <h1>{seed.title}</h1> |
| 161 | <div className="seed-header-container"> |
| 162 | <div className="seed-info"> |
| 163 | <div className="seed-basic-info"> |
| 164 | <p><strong>分类:</strong>{seed.category || '未知'}</p> |
| 165 | <p><strong>发布时间:</strong>{seed.upload_time ? new Date(seed.upload_time).toLocaleString() : '未知'}</p> |
| 166 | <p><strong>标签:</strong>{tags.join(' / ')}</p> |
| 167 | <p><strong>简介:</strong>{seed.description || '无'}</p> |
| 168 | <p><strong>大小:</strong>{seed.size || '未知'}</p> |
| 169 | <p><strong>分辨率:</strong>{seed.resolution || '未知'}</p> |
| 170 | <p><strong>片长:</strong>{seed.duration || '未知'}</p> |
| 171 | <p><strong>地区:</strong>{seed.region || '未知'}</p> |
| 172 | <p><strong>下载次数:</strong>{seed.downloads ?? 0}</p> |
| 173 | </div> |
| 174 | {(seed.category === '电影' || seed.category === '电视剧') && ( |
| 175 | <div className="seed-media-info"> |
| 176 | <p><strong>导演:</strong>{seed.director || '未知'}</p> |
| 177 | <p><strong>编剧:</strong>{seed.writer || '未知'}</p> |
| 178 | <p><strong>主演:</strong>{actors.join(' / ')}</p> |
| 179 | </div> |
| 180 | )} |
| 181 | </div> |
| 182 | <img |
| 183 | src={coverImage || '/default-cover.png'} |
| 184 | alt={seed.title} |
| 185 | className="cover-image" |
| 186 | /> |
| 187 | </div> |
| 188 | <div className="action-buttons"> |
| 189 | <button className="btn" onClick={() => handleDownload(seed.id)}>下载</button> |
| 190 | <button className="btn" onClick={handleCollect}>收藏</button> |
| 191 | </div> |
| 192 | <hr className="divider" /> |
| 193 | <h3>评论区</h3> |
| 194 | <div className="comments-section"> |
| 195 | <div className="comments-list"> |
| 196 | {comments.map((comment, index) => ( |
| 197 | <div key={index} className="comment"> |
| 198 | <p className="comment-user">{comment.user}</p> |
| 199 | <p className="comment-content">{comment.content}</p> |
| 200 | </div> |
| 201 | ))} |
| 202 | </div> |
| 203 | <div className="add-comment-form"> |
| 204 | <textarea |
| 205 | placeholder="输入你的评论..." |
| 206 | value={newComment} |
| 207 | onChange={(e) => setNewComment(e.target.value)} |
| 208 | /> |
| 209 | <div className="comment-options"> |
| 210 | <button className="btn" onClick={handleAddComment}>发布评论</button> |
| 211 | </div> |
| 212 | </div> |
| 213 | </div> |
| 214 | </div> |
| 215 | </div> |
| 216 | ); |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 217 | }; |
| 218 | |
| 219 | export default SeedDetail; |