import React, { useState, useEffect } from 'react'; import styles from './FormatterPreview.module.css'; import { ParsedStream } from '@aiostreams/types'; import { gdriveFormat, torrentioFormat, torboxFormat, customFormat, } from '@aiostreams/formatters'; import { parseFilename } from '@aiostreams/parser'; import { serviceDetails } from '@aiostreams/utils'; interface FormatterPreviewProps { formatter: string; } const FormatterPreview: React.FC = ({ formatter }) => { const [isExpanded, setIsExpanded] = useState(false); const DEFAULT_FILENAME = 'Movie.Title.2023.2160p.BluRay.HEVC.DV.TrueHD.Atmos.7.1.iTA.ENG-GROUP.mkv'; const DEFAULT_FOLDERNAME = 'Movie.Title.2023.2160p.BluRay.HEVC.DV.TrueHD.Atmos.7.1.iTA.ENG-GROUP'; // Create a sample stream for preview const [filename, setFilename] = React.useState(DEFAULT_FILENAME); const [foldername, setFoldername] = React.useState(DEFAULT_FOLDERNAME); const [indexers, setIndexers] = React.useState('RARBG'); const [seeders, setSeeders] = React.useState(125); const [usenetAge, setUsenetAge] = React.useState('10d'); // Days const [addonName, setAddonName] = React.useState('Torrentio'); const [providerId, setProviderId] = React.useState('realdebrid'); const [isCached, setIsCached] = React.useState(true); const [isP2P, setIsP2P] = React.useState(false); const [isPersonal, setIsPersonal] = React.useState(false); const [duration, setDuration] = React.useState(9120000); // 2h 32m const [fileSize, setFileSize] = React.useState(62500000000); // 58.2 GB const [proxied, setProxied] = React.useState(false); // Proxied or not const parsedInfo = parseFilename(filename); console.log(`Formatter: ${formatter}`); const sampleStream: ParsedStream = { ...parsedInfo, addon: { id: 'test-addon', name: addonName, }, filename: filename, folderName: foldername !== filename ? foldername : undefined, size: fileSize, duration: duration, // 2h 32m provider: providerId === 'None' ? undefined : { id: providerId, cached: isCached }, torrent: { seeders: seeders, infoHash: isP2P ? 'infoHash' : undefined, }, indexers: indexers, usenet: { age: usenetAge, }, type: providerId === 'usenet' ? 'usenet' : 'debrid', personal: isPersonal, proxied: proxied, }; const getFormatterExample = () => { switch (formatter) { case 'gdrive': return gdriveFormat(sampleStream, false); case 'minimalistic-gdrive': return gdriveFormat(sampleStream, true); case 'torrentio': return torrentioFormat(sampleStream); case 'torbox': return torboxFormat(sampleStream); case 'imposter': return { name: 'ಞ AIOStreams 4K ಞ', description: 'ಞ Sus: Very ಞ\nಞ Vented: Yes ಞ\nಞ Tasks: None ಞ\nಞ Crewmates: 0 ಞ', }; default: if (formatter.startsWith('custom') && formatter.length > 7) { const jsonString = formatter.slice(7); const data = JSON.parse(jsonString); return customFormat(sampleStream, data); } return gdriveFormat(sampleStream, false); } }; const example = getFormatterExample(); const resetFilename = () => { setFilename(DEFAULT_FILENAME); }; const resetFoldername = () => { setFoldername(DEFAULT_FOLDERNAME); }; // Toggle switch component with animation fix const ToggleSwitch = ({ label, isChecked, setChecked, }: { label: string; isChecked: boolean; setChecked: (checked: boolean) => void; }) => { const [visualState, setVisualState] = useState(isChecked); const [isAnimating, setIsAnimating] = useState(false); // Sync visual state with actual state useEffect(() => { if (!isAnimating) { setVisualState(isChecked); } }, [isChecked, isAnimating]); const handleChange = (e: React.ChangeEvent) => { const newState = e.target.checked; setIsAnimating(true); setVisualState(newState); // Allow animation to play before updating the actual state setTimeout(() => { setChecked(newState); // Reset animating state after animation completes setTimeout(() => { setIsAnimating(false); }, 50); }, 250); // Slightly shorter than the CSS transition duration }; return (
); }; return (

setIsExpanded(!isExpanded)} > Preview {isExpanded ? '▼' : '►'}

{isExpanded && (

This is a preview of how the formatter will look like. You can change the filename and other parameters to see how it affects the output.

Note: The options here do not affect your configuration and are only for testing purposes.

{/* Formatter example display */}
{example.name}
{example.description}
{/* File name input with reset button */}
setFilename(e.target.value)} className={styles.filenameInput} />
{/* Folder name input with reset button */}
setFoldername(e.target.value)} className={styles.filenameInput} />
{/* Small inputs in one row */}
setIndexers(e.target.value)} className={styles.smallInput} />
setSeeders(Number(e.target.value))} className={styles.smallInput} min="0" />
setUsenetAge(e.target.value)} className={styles.smallInput} min="0" />
setDuration(Number(e.target.value) * 1000)} className={styles.smallInput} min="0" />
setFileSize(Number(e.target.value))} className={styles.smallInput} min="0" />
{/* Provider selection and toggle switches */}
setAddonName(e.target.value)} className={styles.smallInput} />
{/* Toggle switches in a more compact layout */}
)}
); }; export default FormatterPreview;