// core/torrent-handler/components/TorrentDashboard.js import React, { useState, useEffect } from 'react'; export const TorrentDashboard = ({ core }) => { const [torrents, setTorrents] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const loadTorrents = async () => { setLoading(true); try { const torrentList = await core.getTorrents(); setTorrents(torrentList); setError(null); } catch (err) { setError('Failed to load torrents: ' + err.message); } finally { setLoading(false); } }; // Load torrents on component mount useEffect(() => { loadTorrents(); // Set up refresh interval const interval = setInterval(loadTorrents, 3000); // Clean up interval on unmount return () => clearInterval(interval); }, []); const handleStart = async (torrentId) => { try { await core.startTorrent(torrentId); loadTorrents(); } catch (err) { setError('Failed to start torrent: ' + err.message); } }; const handleStop = async (torrentId) => { try { await core.stopTorrent(torrentId); loadTorrents(); } catch (err) { setError('Failed to stop torrent: ' + err.message); } }; const handleRemove = async (torrentId, deleteData) => { if (confirm(`Are you sure you want to remove this torrent${deleteData ? ' and its data' : ''}?`)) { try { await core.removeTorrent(torrentId, deleteData); loadTorrents(); } catch (err) { setError('Failed to remove torrent: ' + err.message); } } }; // Helper function to format size const formatSize = (bytes) => { if (bytes === 0) return '0 B'; const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(1024)); return (bytes / Math.pow(1024, i)).toFixed(2) + ' ' + sizes[i]; }; // Helper function to format status const getStatusText = (status) => { const statusMap = { 0: 'Stopped', 1: 'Queued to check', 2: 'Checking', 3: 'Queued to download', 4: 'Downloading', 5: 'Queued to seed', 6: 'Seeding' }; return statusMap[status] || 'Unknown'; }; return (

Torrent Dashboard

{error && (
); }; // core/torrent-handler/components/index.js // Export all components for easier imports export { TorrentDashboard } from './TorrentDashboard'; export { AddTorrentForm } from './AddTorrentForm'; export { TorrentSettings } from './TorrentSettings'; error-message"> {error} )}
Add Torrent
{loading && torrents.length === 0 ? (
Loading torrents...
) : torrents.length === 0 ? (

No torrents found.

Add a torrent
) : ( {torrents.map(torrent => ( ))}
Name Status Progress Size Down Up Ratio Actions
{torrent.name} {getStatusText(torrent.status)}

Set to 0 for unlimited

Set to 0 for unlimited

Leave empty if authentication is not required

Default Torrent Settings
{Math.round(torrent.percentDone * 100)}%
{formatSize(torrent.totalSize)} {formatSize(torrent.rateDownload)}/s {formatSize(torrent.rateUpload)}/s {torrent.uploadRatio.toFixed(2)} {[0, 3, 5].includes(torrent.status) ? ( ) : ( )}
)} ); }; // core/torrent-handler/components/AddTorrentForm.js import React, { useState } from 'react'; export const AddTorrentForm = ({ core, navigate }) => { const [torrentFile, setTorrentFile] = useState(null); const [seedRatio, setSeedRatio] = useState(core.config.default_seed_ratio); const [seedTime, setSeedTime] = useState(core.config.default_seed_time); const [autoStart, setAutoStart] = useState(core.config.auto_start_torrents); const [downloadDir, setDownloadDir] = useState(''); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const handleFileChange = (e) => { const file = e.target.files[0]; if (file && (file.name.endsWith('.torrent') || file.type === 'application/x-bittorrent')) { setTorrentFile(file); setError(null); } else { setTorrentFile(null); setError('Please select a valid .torrent file'); } }; const handleSubmit = async (e) => { e.preventDefault(); if (!torrentFile) { setError('Please select a torrent file'); return; } setLoading(true); setError(null); try { await core.addTorrent(torrentFile, { seedRatio: parseFloat(seedRatio), seedTime: parseInt(seedTime, 10), autoStart: autoStart, downloadDirectory: downloadDir || undefined }); // Redirect to dashboard navigate('/torrents'); } catch (err) { setError('Failed to add torrent: ' + err.message); setLoading(false); } }; return (

Add New Torrent

{error && (
{error}
)}
setSeedRatio(e.target.value)} min="0" step="0.1" />

Set to 0 for unlimited

setSeedTime(e.target.value)} min="0" />

Set to 0 for unlimited

setDownloadDir(e.target.value)} placeholder="Leave empty for default" />
); }; // core/torrent-handler/components/TorrentSettings.js import React, { useState } from 'react'; export const TorrentSettings = ({ core, navigate }) => { const [settings, setSettings] = useState({ default_seed_ratio: core.config.default_seed_ratio, default_seed_time: core.config.default_seed_time, auto_start_torrents: core.config.auto_start_torrents, transmission_url: core.config.transmission_url, transmission_username: core.config.transmission_username, transmission_password: core.config.transmission_password }); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [success, setSuccess] = useState(false); const handleChange = (e) => { const { name, value, type, checked } = e.target; setSettings({ ...settings, [name]: type === 'checkbox' ? checked : value }); }; const handleSubmit = async (e) => { e.preventDefault(); setLoading(true); setError(null); setSuccess(false); try { // Update core configuration core.config = { ...core.config, ...settings }; // Save configuration to framework persistence await core.framework.saveConfiguration(core.id, core.config); setSuccess(true); } catch (err) { setError('Failed to save settings: ' + err.message); } finally { setLoading(false); } }; const handleTestConnection = async () => { setLoading(true); setError(null); setSuccess(false); try { // Temporarily update connection settings const originalConfig = { ...core.config }; core.config = { ...core.config, transmission_url: settings.transmission_url, transmission_username: settings.transmission_username, transmission_password: settings.transmission_password }; // Test connection await core.connectToTransmission(); setSuccess('Connection successful!'); } catch (err) { setError('Connection test failed: ' + err.message); } finally { setLoading(false); } }; return (

Torrent Settings

{error && (
{error}
)} {success && (
{typeof success === 'string' ? success : 'Settings saved successfully!'}
)}
Connection Settings

URL to Transmission RPC interface (e.g. http://localhost:9091/transmission/rpc)