fix(player): multi-source balancers, remove broken embess, add server switcher & unreleased trailer handling to 0.0.4

This commit is contained in:
Nikita 2026-10-27 11:13:25 +05:00
parent a4fc19885f
commit 6750d7d437
5 changed files with 361 additions and 78 deletions

View file

@ -1,7 +1,7 @@
{ {
"name": "has", "name": "has",
"private": true, "private": true,
"version": "0.0.3", "version": "0.0.4",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "has" name = "has"
version = "0.0.3" version = "0.0.4"
description = "has" description = "has"
authors = ["autli"] authors = ["autli"]
edition = "2024" edition = "2024"

View file

@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "has", "productName": "has",
"version": "0.0.3", "version": "0.0.4",
"identifier": "com.has.tauri-app", "identifier": "com.has.tauri-app",
"build": { "build": {
"beforeDevCommand": "npm run dev", "beforeDevCommand": "npm run dev",

View file

@ -769,6 +769,172 @@
color: #ffffff; color: #ffffff;
} }
/* Server selector pills in top HUD */
.has-cinema-player-hud-servers {
display: flex;
align-items: center;
gap: 6px;
background: rgba(15, 23, 42, 0.75);
padding: 3px 6px;
border-radius: 9999px;
border: 1px solid rgba(255, 255, 255, 0.14);
backdrop-filter: blur(12px);
}
.has-cinema-player-server-pill {
background: transparent;
border: 1px solid transparent;
color: #94a3b8;
padding: 5px 11px;
border-radius: 9999px;
font-size: 11.5px;
font-weight: 700;
cursor: pointer;
transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
white-space: nowrap;
}
.has-cinema-player-server-pill:hover {
color: #ffffff;
background: rgba(255, 255, 255, 0.1);
border-color: rgba(255, 255, 255, 0.2);
}
.has-cinema-player-server-pill.active {
background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);
color: #ffffff;
border-color: #818cf8;
box-shadow: 0 0 14px rgba(99, 102, 241, 0.5);
}
.has-cinema-player-server-pill:focus-visible,
.has-cinema-player-server-pill[data-tv-focused="true"] {
outline: none;
background: #6366f1;
color: #ffffff;
border-color: #ffffff;
box-shadow: 0 0 0 3px #818cf8;
transform: scale(1.06);
}
/* Bottom failover floating helper */
.has-cinema-player-bottom-failover {
position: absolute;
bottom: 24px;
left: 50%;
transform: translateX(-50%);
z-index: 999;
display: flex;
align-items: center;
gap: 12px;
background: rgba(15, 23, 42, 0.88);
border: 1px solid rgba(255, 255, 255, 0.16);
backdrop-filter: blur(16px);
padding: 8px 18px;
border-radius: 9999px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.6);
transition: opacity 0.3s ease, transform 0.3s ease;
pointer-events: auto;
}
.has-cinema-player-bottom-failover.hud-hidden {
opacity: 0;
transform: translateX(-50%) translateY(20px);
pointer-events: none;
}
.failover-hint {
font-size: 12.5px;
color: #94a3b8;
font-weight: 500;
}
.failover-btn {
background: linear-gradient(135deg, #3b82f6 0%, #6366f1 100%);
border: none;
color: #ffffff;
padding: 6px 14px;
border-radius: 9999px;
font-size: 12px;
font-weight: 700;
cursor: pointer;
transition: transform 0.18s ease, box-shadow 0.18s ease;
}
.failover-btn:hover {
transform: scale(1.05);
box-shadow: 0 0 16px rgba(59, 130, 246, 0.6);
}
.failover-btn:focus-visible,
.failover-btn[data-tv-focused="true"] {
outline: none;
box-shadow: 0 0 0 3px #ffffff, 0 0 20px #3b82f6;
transform: scale(1.08);
}
/* Unreleased movie player card */
.has-cinema-unreleased-player-view {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
padding: 24px;
background: radial-gradient(circle at center, rgba(30, 41, 59, 0.6) 0%, #030712 100%);
z-index: 10;
}
.has-cinema-unreleased-card {
max-width: 680px;
width: 100%;
background: rgba(15, 23, 42, 0.9);
border: 1px solid rgba(255, 255, 255, 0.14);
backdrop-filter: blur(20px);
border-radius: 20px;
padding: 32px;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.8);
}
.has-cinema-unreleased-card .unreleased-icon {
font-size: 42px;
margin-bottom: 12px;
}
.has-cinema-unreleased-card h3 {
font-size: 22px;
font-weight: 800;
color: #ffffff;
margin: 0 0 8px 0;
}
.has-cinema-unreleased-card p {
font-size: 14px;
color: #94a3b8;
line-height: 1.5;
margin: 0 0 20px 0;
}
.unreleased-trailer-box {
width: 100%;
aspect-ratio: 16 / 9;
border-radius: 14px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.7);
margin-bottom: 20px;
background: #000000;
}
.unreleased-trailer-frame {
width: 100%;
height: 100%;
border: none;
}
/* 100% Immersive Video Frame */ /* 100% Immersive Video Frame */
.has-cinema-video-frame { .has-cinema-video-frame {
width: 100vw; width: 100vw;
@ -2807,6 +2973,10 @@
flex-shrink: 0; flex-shrink: 0;
} }
.has-cinema-player-hud-servers {
display: none !important;
}
.has-cinema-player-hud-btn { .has-cinema-player-hud-btn {
padding: 6px 10px !important; padding: 6px 10px !important;
font-size: 12px !important; font-size: 12px !important;

View file

@ -464,14 +464,16 @@ interface RussianMediaInfo {
} }
type BalancerMirror = type BalancerMirror =
| "smotrim_ru"
| "embess_ru"
| "alloha_ru"
| "kodik_ru"
| "anilibria"
| "vidlink_pro" | "vidlink_pro"
| "kodik_ru"
| "autoembed"
| "vidsrc_pm"
| "multiembed"
| "vidsrc_to" | "vidsrc_to"
| "autoembed"; | "alloha_ru"
| "anilibria"
| "embess_ru"
| "smotrim_ru";
@ -612,7 +614,7 @@ export default function Cinema({
const [playingEpisode, setPlayingEpisode] = useState<CinemaEpisode | null>(null); const [playingEpisode, setPlayingEpisode] = useState<CinemaEpisode | null>(null);
const [playingSeasonNumber, setPlayingSeasonNumber] = useState<number>(1); const [playingSeasonNumber, setPlayingSeasonNumber] = useState<number>(1);
const playerMode: "balancer" | "direct" = "balancer"; const playerMode: "balancer" | "direct" = "balancer";
const [selectedMirror, setSelectedMirror] = useState<BalancerMirror>("embess_ru"); const [selectedMirror, setSelectedMirror] = useState<BalancerMirror>("vidlink_pro");
const [currentKpId, setCurrentKpId] = useState<number | null>(null); const [currentKpId, setCurrentKpId] = useState<number | null>(null);
const [currentImdbId, setCurrentImdbId] = useState<string | null>(null); const [currentImdbId, setCurrentImdbId] = useState<string | null>(null);
const [currentAllohaUrl, setCurrentAllohaUrl] = useState<string | null>(null); const [currentAllohaUrl, setCurrentAllohaUrl] = useState<string | null>(null);
@ -1280,13 +1282,20 @@ export default function Cinema({
setCurrentImdbId(null); setCurrentImdbId(null);
setCurrentAllohaUrl(null); setCurrentAllohaUrl(null);
// Default to genuine Russian spoken voiceover (direct Embess Full HD stream): // Default to verified reliable high-speed 1080p stream:
if (isAnime) { if (isAnime) {
setSelectedMirror("kodik_ru"); if (anilibriaStreamUrl) {
showTvToast("🌸 Русская озвучка аниме", "🎙️", 1800); setSelectedMirror("anilibria");
} else if (kodikResults.length > 0) {
setSelectedMirror("kodik_ru");
} else {
setSelectedMirror("vidlink_pro");
}
showTvToast("🌸 Сервер: Kodik (Аниме / Озвучка)", "🎙️", 1800);
} else { } else {
setSelectedMirror("embess_ru"); // Default to VidLink Pro (ultra reliable, resolves all TMDB movies/series in 1080p)
showTvToast("🇷🇺 Включен полный русский дубляж (1080p)", "🎙️", 1800); setSelectedMirror("vidlink_pro");
showTvToast("⚡ Запущен Сервер 1 (Full HD 1080p)", "🎬", 1800);
} }
// Resolve Kinopoisk ID & Russian media data asynchronously via Rust backend or web fetch // Resolve Kinopoisk ID & Russian media data asynchronously via Rust backend or web fetch
@ -1464,11 +1473,18 @@ export default function Cinema({
} }
}; };
// Quick 1-click mirror switcher if a CDN balancer fails or is blocked // Quick 1-click multi-source switcher with automatic fallback
const handleCycleMirror = () => { const handleCycleMirror = () => {
const mirrors: BalancerMirror[] = ["embess_ru", "vidlink_pro", "vidsrc_to", "autoembed"]; const mirrors: BalancerMirror[] = [
if (kodikResults.length > 0) mirrors.push("kodik_ru"); "vidlink_pro",
"kodik_ru",
"autoembed",
"vidsrc_pm",
"multiembed",
"vidsrc_to",
];
if (currentAllohaUrl) mirrors.push("alloha_ru"); if (currentAllohaUrl) mirrors.push("alloha_ru");
if (anilibriaStreamUrl) mirrors.push("anilibria");
const currentIndex = mirrors.indexOf(selectedMirror); const currentIndex = mirrors.indexOf(selectedMirror);
const nextIndex = (currentIndex + 1) % mirrors.length; const nextIndex = (currentIndex + 1) % mirrors.length;
@ -1476,16 +1492,18 @@ export default function Cinema({
setSelectedMirror(nextMirror); setSelectedMirror(nextMirror);
const labels: Record<BalancerMirror, string> = { const labels: Record<BalancerMirror, string> = {
embess_ru: "Embess (Дубляж 1080p)", vidlink_pro: "Сервер 1: VidLink Pro (1080p)",
kodik_ru: "Сервер 2: Kodik (Студии дубляжа)",
autoembed: "Сервер 3: AutoEmbed",
vidsrc_pm: "Сервер 4: VidSrc Pro",
multiembed: "Сервер 5: MultiStream VIP",
vidsrc_to: "Сервер 6: VidSrc To",
alloha_ru: "Сервер 7: Alloha",
anilibria: "Сервер 8: AniLibria HLS",
embess_ru: "Embess",
smotrim_ru: "Smotrim", smotrim_ru: "Smotrim",
vidlink_pro: "VidLink Pro (Full HD)",
vidsrc_to: "VidSrc Pro",
autoembed: "AutoEmbed",
alloha_ru: "Alloha",
kodik_ru: "Kodik",
anilibria: "AniLibria",
}; };
showTvToast(`Сервер: ${labels[nextMirror] || nextMirror}`, "🔄", 2000); showTvToast(`Переключено на: ${labels[nextMirror] || nextMirror}`, "🔄", 2200);
resetHudTimer(); resetHudTimer();
}; };
@ -2173,26 +2191,7 @@ export default function Cinema({
const imdbId = currentImdbId; const imdbId = currentImdbId;
switch (selectedMirror) { switch (selectedMirror) {
case "smotrim_ru": case "vidlink_pro":
case "embess_ru":
if (kpId) {
return `https://api.embess.ws/embed/kp/${kpId}${isTv ? `?season=${sNum}&episode=${epNum}` : ""}`;
}
if (imdbId) {
return `https://api.embess.ws/embed/imdb/${imdbId}${isTv ? `?season=${sNum}&episode=${epNum}` : ""}`;
}
// If neither KP nor IMDb is known for Embess, route to VidLink Pro which natively resolves all TMDB IDs
return isTv
? `https://vidlink.pro/tv/${tmdb || 693134}/${sNum}/${epNum}?primaryColor=6366f1`
: `https://vidlink.pro/movie/${tmdb || 693134}?primaryColor=6366f1`;
case "alloha_ru":
if (currentAllohaUrl) {
return currentAllohaUrl;
}
if (kpId) {
return `https://api.delivembd.ws/embed/kp/${kpId}${isTv ? `?season=${sNum}&episode=${epNum}` : ""}`;
}
return isTv return isTv
? `https://vidlink.pro/tv/${tmdb || 693134}/${sNum}/${epNum}?primaryColor=6366f1` ? `https://vidlink.pro/tv/${tmdb || 693134}/${sNum}/${epNum}?primaryColor=6366f1`
: `https://vidlink.pro/movie/${tmdb || 693134}?primaryColor=6366f1`; : `https://vidlink.pro/movie/${tmdb || 693134}?primaryColor=6366f1`;
@ -2201,32 +2200,49 @@ export default function Cinema({
if (selectedKodikLink) { if (selectedKodikLink) {
return selectedKodikLink.startsWith("//") ? `https:${selectedKodikLink}` : selectedKodikLink; return selectedKodikLink.startsWith("//") ? `https:${selectedKodikLink}` : selectedKodikLink;
} }
if (kpId) { if (kodikResults.length > 0 && kodikResults[0]?.link) {
return `https://api.embess.ws/embed/kp/${kpId}${isTv ? `?season=${sNum}&episode=${epNum}` : ""}`; const l = kodikResults[0].link;
return l.startsWith("//") ? `https:${l}` : l;
} }
return isTv return isTv
? `https://vidlink.pro/tv/${tmdb || 693134}/${sNum}/${epNum}?primaryColor=6366f1` ? `https://vidlink.pro/tv/${tmdb || 693134}/${sNum}/${epNum}?primaryColor=6366f1`
: `https://vidlink.pro/movie/${tmdb || 693134}?primaryColor=6366f1`; : `https://vidlink.pro/movie/${tmdb || 693134}?primaryColor=6366f1`;
case "vidlink_pro":
return isTv
? `https://vidlink.pro/tv/${tmdb || 693134}/${sNum}/${epNum}?primaryColor=6366f1`
: `https://vidlink.pro/movie/${tmdb || 693134}?primaryColor=6366f1`;
case "vidsrc_to":
return isTv
? `https://vidsrc.to/embed/tv/${tmdb || 693134}/${sNum}/${epNum}`
: `https://vidsrc.to/embed/movie/${tmdb || 693134}`;
case "autoembed": case "autoembed":
return isTv return isTv
? `https://player.autoembed.co/embed/tv/${tmdb || 693134}/${sNum}/${epNum}` ? `https://player.autoembed.co/embed/tv/${tmdb || 693134}/${sNum}/${epNum}`
: `https://player.autoembed.co/embed/movie/${tmdb || 693134}`; : `https://player.autoembed.co/embed/movie/${tmdb || 693134}`;
default: case "vidsrc_pm":
if (kpId) { return isTv
return `https://api.embess.ws/embed/kp/${kpId}${isTv ? `?season=${sNum}&episode=${epNum}` : ""}`; ? `https://vidsrc.pm/embed/tv/${tmdb || 693134}/${sNum}/${epNum}`
: `https://vidsrc.pm/embed/movie/${tmdb || 693134}`;
case "multiembed":
return isTv
? `https://multiembed.mov/?video_id=${tmdb || 693134}&tmdb=1&s=${sNum}&e=${epNum}`
: `https://multiembed.mov/?video_id=${tmdb || 693134}&tmdb=1`;
case "vidsrc_to":
return isTv
? `https://vidsrc.to/embed/tv/${tmdb || 693134}/${sNum}/${epNum}`
: `https://vidsrc.to/embed/movie/${tmdb || 693134}`;
case "alloha_ru":
if (currentAllohaUrl) {
return currentAllohaUrl.startsWith("//") ? `https:${currentAllohaUrl}` : currentAllohaUrl;
} }
if (kpId) {
return `https://theatre.stravers.live/?kp=${kpId}`;
}
if (imdbId) {
return `https://theatre.stravers.live/?imdb=${imdbId}`;
}
return isTv
? `https://vidlink.pro/tv/${tmdb || 693134}/${sNum}/${epNum}?primaryColor=6366f1`
: `https://vidlink.pro/movie/${tmdb || 693134}?primaryColor=6366f1`;
default:
return isTv return isTv
? `https://vidlink.pro/tv/${tmdb || 693134}/${sNum}/${epNum}?primaryColor=6366f1` ? `https://vidlink.pro/tv/${tmdb || 693134}/${sNum}/${epNum}?primaryColor=6366f1`
: `https://vidlink.pro/movie/${tmdb || 693134}?primaryColor=6366f1`; : `https://vidlink.pro/movie/${tmdb || 693134}?primaryColor=6366f1`;
@ -3582,15 +3598,64 @@ export default function Cinema({
</button> </button>
)} )}
{/* Server selector buttons right in the HUD */}
<div className="has-cinema-player-hud-servers">
<button
type="button"
className={`has-cinema-player-server-pill ${selectedMirror === "vidlink_pro" ? "active" : ""}`}
onClick={() => handleSelectMirror("vidlink_pro")}
title="Сервер 1: Скоростной Full HD 1080p"
data-tv-focusable="true"
>
⚡ Сервер 1 (1080p)
</button>
<button
type="button"
className={`has-cinema-player-server-pill ${selectedMirror === "kodik_ru" ? "active" : ""}`}
onClick={() => handleSelectMirror("kodik_ru")}
title="Сервер 2: Студии дубляжа (Kodik)"
data-tv-focusable="true"
>
🎙️ Сервер 2 (Дубляж)
</button>
<button
type="button"
className={`has-cinema-player-server-pill ${selectedMirror === "autoembed" ? "active" : ""}`}
onClick={() => handleSelectMirror("autoembed")}
title="Сервер 3: AutoEmbed"
data-tv-focusable="true"
>
🌐 Сервер 3
</button>
<button
type="button"
className={`has-cinema-player-server-pill ${selectedMirror === "vidsrc_pm" ? "active" : ""}`}
onClick={() => handleSelectMirror("vidsrc_pm")}
title="Сервер 4: VidSrc"
data-tv-focusable="true"
>
🎬 Сервер 4
</button>
<button
type="button"
className={`has-cinema-player-server-pill ${selectedMirror === "multiembed" ? "active" : ""}`}
onClick={() => handleSelectMirror("multiembed")}
title="Сервер 5: MultiStream"
data-tv-focusable="true"
>
🚀 Сервер 5
</button>
</div>
{/* Quick mirror switcher */} {/* Quick mirror switcher */}
<button <button
type="button" type="button"
className="has-cinema-player-hud-btn" className="has-cinema-player-hud-btn"
onClick={handleCycleMirror} onClick={handleCycleMirror}
title="Переключить на другой сервер/плеер (если текущий не загружается)" title="Переключить на следующий сервер (если текущий заблокирован или не загружается)"
data-tv-focusable="true" data-tv-focusable="true"
> >
🔄 Сменить плеер 🔄 Сменить источник
</button> </button>
{/* Single Settings button (Contains voiceover studios & mirrors) */} {/* Single Settings button (Contains voiceover studios & mirrors) */}
@ -3639,9 +3704,57 @@ export default function Cinema({
</div> </div>
</div> </div>
{/* Quick Fallback Switcher (Always accessible if video fails to load) */}
<div className={`has-cinema-player-bottom-failover ${!isHudVisible ? "hud-hidden" : ""}`}>
<span className="failover-hint">Не работает плеер?</span>
<button
type="button"
className="failover-btn"
onClick={handleCycleMirror}
data-tv-focusable="true"
title="Переключить на следующий доступный сервер"
>
🔄 Сменить сервер
</button>
</div>
{/* Clean 100% Video Canvas */} {/* Clean 100% Video Canvas */}
<div className="has-cinema-video-frame"> <div className="has-cinema-video-frame">
{selectedMirror === "anilibria" ? ( {(playingMedia || selectedMedia).is_unreleased ? (
<div className="has-cinema-unreleased-player-view">
<div className="has-cinema-unreleased-card">
<span className="unreleased-icon">⏳</span>
<h3>Фильм ещё не вышел в цифровой прокат</h3>
<p>
«{(playingMedia || selectedMedia).title}» ещё находится в производстве или кинотеатральном прокате (релиз: {(playingMedia || selectedMedia).release_date_full || (playingMedia || selectedMedia).release_year || "2026"}).
</p>
{(playingMedia || selectedMedia).trailer_youtube_key ? (
<div className="unreleased-trailer-box">
<iframe
src={`https://www.youtube-nocookie.com/embed/${(playingMedia || selectedMedia).trailer_youtube_key}?autoplay=1&rel=0`}
title="Официальный трейлер"
className="unreleased-trailer-frame"
allow="autoplay; fullscreen"
allowFullScreen
/>
</div>
) : (
<p style={{ color: "#64748b", fontSize: "13px" }}>
Цифровой плеер подключится автоматически, как только фильм выйдет в мировой релиз.
</p>
)}
<button
type="button"
className="has-cinema-btn-play"
onClick={handleClosePlayer}
style={{ marginTop: 12 }}
data-tv-focusable="true"
>
← Вернуться в каталог
</button>
</div>
</div>
) : selectedMirror === "anilibria" ? (
anilibriaStreamUrl ? ( anilibriaStreamUrl ? (
<video <video
ref={videoRef} ref={videoRef}
@ -3664,7 +3777,7 @@ export default function Cinema({
title={(playingMedia || selectedMedia).title} title={(playingMedia || selectedMedia).title}
className="has-cinema-iframe-elem" className="has-cinema-iframe-elem"
allow="autoplay *; fullscreen *; encrypted-media *; picture-in-picture *; clipboard-write *" allow="autoplay *; fullscreen *; encrypted-media *; picture-in-picture *; clipboard-write *"
sandbox="allow-scripts allow-same-origin allow-forms allow-presentation" referrerPolicy="no-referrer-when-downgrade"
allowFullScreen allowFullScreen
{...{ webkitallowfullscreen: "true", mozallowfullscreen: "true" }} {...{ webkitallowfullscreen: "true", mozallowfullscreen: "true" }}
/> />
@ -3734,31 +3847,31 @@ export default function Cinema({
<button <button
type="button" type="button"
className={`has-cinema-drawer-option-btn ${ className={`has-cinema-drawer-option-btn ${
selectedMirror === "embess_ru" || selectedMirror === "smotrim_ru" ? "active" : "" selectedMirror === "vidlink_pro" ? "active" : ""
}`} }`}
onClick={() => { onClick={() => {
handleSelectMirror("embess_ru"); handleSelectMirror("vidlink_pro");
showTvToast("Включен русский дубляж (1080p)", "🇷🇺", 1500); showTvToast("Включен Сервер 1: Full HD (1080p)", "⚡", 1500);
}} }}
data-tv-focusable="true" data-tv-focusable="true"
> >
<span className="option-name">🇷🇺 Полный русский дубляж (1080p)</span> <span className="option-name">⚡ Сервер 1: VidLink Pro (1080p)</span>
<span className="option-desc">Профессиональный многоголосый перевод высокой четкости</span> <span className="option-desc">Высокая скорость, Full HD качество, стабильный поток</span>
</button> </button>
<button <button
type="button" type="button"
className={`has-cinema-drawer-option-btn ${ className={`has-cinema-drawer-option-btn ${
selectedMirror === "embess_ru" ? "active" : "" selectedMirror === "kodik_ru" ? "active" : ""
}`} }`}
onClick={() => { onClick={() => {
handleSelectMirror("embess_ru"); handleSelectMirror("kodik_ru");
showTvToast("Включен дубляж Embess 1080p", "🎙️", 1500); showTvToast("Включен Сервер 2: Kodik (Дубляж)", "🎙️", 1500);
}} }}
data-tv-focusable="true" data-tv-focusable="true"
> >
<span className="option-name">🎙️ Embess Full HD</span> <span className="option-name">🎙️ Сервер 2: Kodik (Студии дубляжа)</span>
<span className="option-desc">Высокая четкость 1080p с русской дорожкой</span> <span className="option-desc">Профессиональный русский дубляж (LostFilm, Red Head Sound, HDRezka)</span>
</button> </button>
{currentAllohaUrl && ( {currentAllohaUrl && (
@ -3769,12 +3882,12 @@ export default function Cinema({
}`} }`}
onClick={() => { onClick={() => {
handleSelectMirror("alloha_ru"); handleSelectMirror("alloha_ru");
showTvToast("Включен плеер Alloha", "🔊", 1500); showTvToast("Включен Сервер 3: Alloha", "🔊", 1500);
}} }}
data-tv-focusable="true" data-tv-focusable="true"
> >
<span className="option-name">🔊 Alloha Player</span> <span className="option-name">🔊 Сервер 3: Alloha Player</span>
<span className="option-desc">Студии: Red Head Sound, Flarrow Films, LostFilm</span> <span className="option-desc">Российский балансер кинолент</span>
</button> </button>
)} )}