Update index.html

This commit is contained in:
VlastikYoutubeKo 2025-08-15 16:31:17 +02:00 committed by GitHub
parent 78d5df5962
commit ace0250e71
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -13,6 +13,7 @@
--border-color: #dee2e6; --border-color: #dee2e6;
--accent-color: #0d6efd; --accent-color: #0d6efd;
--header-bg: #ffffff; --header-bg: #ffffff;
--danger-color: #dc3545;
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
@ -23,6 +24,7 @@
--border-color: #343a40; --border-color: #343a40;
--accent-color: #4dabf7; --accent-color: #4dabf7;
--header-bg: #1e1e1e; --header-bg: #1e1e1e;
--danger-color: #f06571;
} }
} }
@ -77,6 +79,21 @@
.playlist-links p { .playlist-links p {
margin: 0.5rem 0; margin: 0.5rem 0;
} }
.search-container {
margin-bottom: 1.5rem;
}
#search-input {
width: 100%;
padding: 0.75rem 1rem;
font-size: 1rem;
border-radius: 8px;
border: 1px solid var(--border-color);
background-color: var(--header-bg);
color: var(--text-color);
box-sizing: border-box;
}
.category-folder { .category-folder {
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
@ -104,6 +121,7 @@
padding: 0.75rem 1.5rem; padding: 0.75rem 1.5rem;
border-top: 1px solid var(--border-color); border-top: 1px solid var(--border-color);
text-align: left; text-align: left;
vertical-align: middle;
} }
.channel-table th { .channel-table th {
@ -130,6 +148,18 @@
cursor: help; cursor: help;
font-size: 1.2rem; font-size: 1.2rem;
} }
.report-button {
font-size: 0.8rem;
padding: 4px 8px;
border-radius: 5px;
border: 1px solid var(--danger-color);
background: transparent;
color: var(--danger-color);
cursor: pointer;
text-decoration: none;
display: inline-block;
}
</style> </style>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script> <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
@ -143,7 +173,6 @@
<script type="text/babel"> <script type="text/babel">
const { useState, useEffect } = React; const { useState, useEffect } = React;
// --- Funkce pro parsování .m3u souborů ---
const parseM3U = (m3uContent, isStable) => { const parseM3U = (m3uContent, isStable) => {
const lines = m3uContent.split('\n'); const lines = m3uContent.split('\n');
const channels = {}; const channels = {};
@ -163,24 +192,31 @@
const channelName = line.split(',').pop().trim(); const channelName = line.split(',').pop().trim();
const streamUrl = nextLine.trim(); const streamUrl = nextLine.trim();
if (!channels[country]) { if (!channels[country]) channels[country] = {};
channels[country] = {};
}
if (!channels[country][channelName]) { if (!channels[country][channelName]) {
channels[country][channelName] = { name: channelName, stable: isStable, url: streamUrl }; channels[country][channelName] = { name: channelName, stable: isStable, url: streamUrl };
} }
} catch (e) { } catch (e) { console.error("Error parsing line:", line, e); }
console.error("Chyba při parsování řádku:", line, e);
}
} }
} }
return channels; return channels;
}; };
// --- Komponenty --- const Category = ({ country, channels, forceOpen }) => {
const Category = ({ country, channels }) => {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
useEffect(() => {
setIsOpen(forceOpen);
}, [forceOpen]);
const createReportURL = (channelName) => {
const repoUrl = "https://github.com/theariatv/theariatv.github.io/issues/new";
const title = `Broken Stream: ${channelName}`;
const body = `**Channel Name:** ${channelName}\n\n**Problem:** (Please describe the issue, e.g., 'Stream does not load', 'Shows a black screen', 'Wrong content', etc.)`;
return `${repoUrl}?title=${encodeURIComponent(title)}&body=${encodeURIComponent(body)}`;
};
return ( return (
<div className="category-folder"> <div className="category-folder">
<div className="category-header" onClick={() => setIsOpen(!isOpen)}> <div className="category-header" onClick={() => setIsOpen(!isOpen)}>
@ -193,6 +229,7 @@
<tr> <tr>
<th>Channel</th> <th>Channel</th>
<th>Stream Link</th> <th>Stream Link</th>
<th>Report</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -209,6 +246,9 @@
<td> <td>
<a href={channel.url} target="_blank" rel="noopener noreferrer">Link</a> <a href={channel.url} target="_blank" rel="noopener noreferrer">Link</a>
</td> </td>
<td>
<a href={createReportURL(channel.name)} target="_blank" rel="noopener noreferrer" className="report-button">Report</a>
</td>
</tr> </tr>
))} ))}
</tbody> </tbody>
@ -219,19 +259,16 @@
}; };
const App = () => { const App = () => {
const [channelData, setChannelData] = useState([]); const [allChannelData, setAllChannelData] = useState([]);
const [filteredData, setFilteredData] = useState([]);
const [searchTerm, setSearchTerm] = useState('');
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
useEffect(() => { useEffect(() => {
const fetchAndParseData = async () => { const fetchAndParseData = async () => {
try { try {
const [ariaRes, ariaPlusRes] = await Promise.all([ const [ariaRes, ariaPlusRes] = await Promise.all([ fetch('aria.m3u'), fetch('aria+.m3u') ]);
fetch('aria.m3u'), const [ariaText, ariaPlusText] = await Promise.all([ ariaRes.text(), ariaPlusRes.text() ]);
fetch('aria+.m3u')
]);
const ariaText = await ariaRes.text();
const ariaPlusText = await ariaPlusRes.text();
const stableChannels = parseM3U(ariaText, true); const stableChannels = parseM3U(ariaText, true);
const unstableChannels = parseM3U(ariaPlusText, false); const unstableChannels = parseM3U(ariaPlusText, false);
@ -251,16 +288,29 @@
channels: Object.values(allChannels[country]).sort((a, b) => a.name.localeCompare(b.name)) channels: Object.values(allChannels[country]).sort((a, b) => a.name.localeCompare(b.name))
})); }));
setChannelData(formattedData); setAllChannelData(formattedData);
} catch (error) { setFilteredData(formattedData);
console.error("Nepodařilo se načíst data kanálů:", error); } catch (error) { console.error("Could not load channel data:", error); }
} finally { finally { setLoading(false); }
setLoading(false);
}
}; };
fetchAndParseData(); fetchAndParseData();
}, []); }, []);
useEffect(() => {
if (searchTerm === '') {
setFilteredData(allChannelData);
return;
}
const lowercasedFilter = searchTerm.toLowerCase();
const filtered = allChannelData
.map(category => ({
...category,
channels: category.channels.filter(channel => channel.name.toLowerCase().includes(lowercasedFilter))
}))
.filter(category => category.channels.length > 0);
setFilteredData(filtered);
}, [searchTerm, allChannelData]);
return ( return (
<div className="container"> <div className="container">
@ -281,19 +331,29 @@
</div> </div>
<h3>Want to help us?</h3> <h3>Want to help us?</h3>
<p> <p>
You can go to the <a href="https://github.com/theariatv/theariatv.github.io/issues" target="_blank" rel="noopener noreferrer">Issues tab</a> on GitHub to request a channel-specific action. You can go to the <a href="https://github.com/theariatv/theariatv.github.io/issues" target="_blank" rel="noopener noreferrer">Issues tab</a> on GitHub to request a channel-specific action, or use the "Report" button next to a channel.
</p> </p>
</div> </div>
<h2>Channel List</h2> <h2>Channel List</h2>
<div className="search-container">
<input
id="search-input"
type="search"
placeholder="Search for a channel..."
value={searchTerm}
onChange={e => setSearchTerm(e.target.value)}
/>
</div>
{loading ? ( {loading ? (
<p>Loading channels...</p> <p>Loading channels...</p>
) : ( ) : (
channelData.map(category => ( filteredData.map(category => (
<Category <Category
key={category.country} key={category.country}
country={category.country} country={category.country}
channels={category.channels} channels={category.channels}
forceOpen={searchTerm.length > 0}
/> />
)) ))
)} )}