From 6750d7d43779d9df50ad4aa6bb8afe091fd7fec6 Mon Sep 17 00:00:00 2001 From: Autli Date: Tue, 27 Oct 2026 11:13:25 +0500 Subject: [PATCH] fix(player): multi-source balancers, remove broken embess, add server switcher & unreleased trailer handling to 0.0.4 --- package.json | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/tauri.conf.json | 2 +- src/pages/cinema/Cinema.css | 170 +++++++++++++++++++++++ src/pages/cinema/Cinema.tsx | 263 ++++++++++++++++++++++++++---------- 5 files changed, 361 insertions(+), 78 deletions(-) diff --git a/package.json b/package.json index f2cf7b2..c4dca4e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "has", "private": true, - "version": "0.0.3", + "version": "0.0.4", "type": "module", "scripts": { "dev": "vite", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index b78e28d..851b682 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "has" -version = "0.0.3" +version = "0.0.4" description = "has" authors = ["autli"] edition = "2024" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index d182384..0b79f81 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "has", - "version": "0.0.3", + "version": "0.0.4", "identifier": "com.has.tauri-app", "build": { "beforeDevCommand": "npm run dev", diff --git a/src/pages/cinema/Cinema.css b/src/pages/cinema/Cinema.css index 6d8a8a7..a12e84a 100644 --- a/src/pages/cinema/Cinema.css +++ b/src/pages/cinema/Cinema.css @@ -769,6 +769,172 @@ 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 */ .has-cinema-video-frame { width: 100vw; @@ -2807,6 +2973,10 @@ flex-shrink: 0; } + .has-cinema-player-hud-servers { + display: none !important; + } + .has-cinema-player-hud-btn { padding: 6px 10px !important; font-size: 12px !important; diff --git a/src/pages/cinema/Cinema.tsx b/src/pages/cinema/Cinema.tsx index e16ee86..3087657 100644 --- a/src/pages/cinema/Cinema.tsx +++ b/src/pages/cinema/Cinema.tsx @@ -464,14 +464,16 @@ interface RussianMediaInfo { } type BalancerMirror = - | "smotrim_ru" - | "embess_ru" - | "alloha_ru" - | "kodik_ru" - | "anilibria" | "vidlink_pro" + | "kodik_ru" + | "autoembed" + | "vidsrc_pm" + | "multiembed" | "vidsrc_to" - | "autoembed"; + | "alloha_ru" + | "anilibria" + | "embess_ru" + | "smotrim_ru"; @@ -612,7 +614,7 @@ export default function Cinema({ const [playingEpisode, setPlayingEpisode] = useState(null); const [playingSeasonNumber, setPlayingSeasonNumber] = useState(1); const playerMode: "balancer" | "direct" = "balancer"; - const [selectedMirror, setSelectedMirror] = useState("embess_ru"); + const [selectedMirror, setSelectedMirror] = useState("vidlink_pro"); const [currentKpId, setCurrentKpId] = useState(null); const [currentImdbId, setCurrentImdbId] = useState(null); const [currentAllohaUrl, setCurrentAllohaUrl] = useState(null); @@ -1280,13 +1282,20 @@ export default function Cinema({ setCurrentImdbId(null); setCurrentAllohaUrl(null); - // Default to genuine Russian spoken voiceover (direct Embess Full HD stream): + // Default to verified reliable high-speed 1080p stream: if (isAnime) { - setSelectedMirror("kodik_ru"); - showTvToast("🌸 Русская озвучка аниме", "🎙️", 1800); + if (anilibriaStreamUrl) { + setSelectedMirror("anilibria"); + } else if (kodikResults.length > 0) { + setSelectedMirror("kodik_ru"); + } else { + setSelectedMirror("vidlink_pro"); + } + showTvToast("🌸 Сервер: Kodik (Аниме / Озвучка)", "🎙️", 1800); } else { - setSelectedMirror("embess_ru"); - showTvToast("🇷🇺 Включен полный русский дубляж (1080p)", "🎙️", 1800); + // Default to VidLink Pro (ultra reliable, resolves all TMDB movies/series in 1080p) + setSelectedMirror("vidlink_pro"); + showTvToast("⚡ Запущен Сервер 1 (Full HD 1080p)", "🎬", 1800); } // 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 mirrors: BalancerMirror[] = ["embess_ru", "vidlink_pro", "vidsrc_to", "autoembed"]; - if (kodikResults.length > 0) mirrors.push("kodik_ru"); + const mirrors: BalancerMirror[] = [ + "vidlink_pro", + "kodik_ru", + "autoembed", + "vidsrc_pm", + "multiembed", + "vidsrc_to", + ]; if (currentAllohaUrl) mirrors.push("alloha_ru"); + if (anilibriaStreamUrl) mirrors.push("anilibria"); const currentIndex = mirrors.indexOf(selectedMirror); const nextIndex = (currentIndex + 1) % mirrors.length; @@ -1476,16 +1492,18 @@ export default function Cinema({ setSelectedMirror(nextMirror); const labels: Record = { - 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", - 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(); }; @@ -2173,26 +2191,7 @@ export default function Cinema({ const imdbId = currentImdbId; switch (selectedMirror) { - case "smotrim_ru": - 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}` : ""}`; - } + case "vidlink_pro": return isTv ? `https://vidlink.pro/tv/${tmdb || 693134}/${sNum}/${epNum}?primaryColor=6366f1` : `https://vidlink.pro/movie/${tmdb || 693134}?primaryColor=6366f1`; @@ -2201,32 +2200,49 @@ export default function Cinema({ if (selectedKodikLink) { return selectedKodikLink.startsWith("//") ? `https:${selectedKodikLink}` : selectedKodikLink; } - if (kpId) { - return `https://api.embess.ws/embed/kp/${kpId}${isTv ? `?season=${sNum}&episode=${epNum}` : ""}`; + if (kodikResults.length > 0 && kodikResults[0]?.link) { + const l = kodikResults[0].link; + return l.startsWith("//") ? `https:${l}` : l; } return isTv ? `https://vidlink.pro/tv/${tmdb || 693134}/${sNum}/${epNum}?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": return isTv ? `https://player.autoembed.co/embed/tv/${tmdb || 693134}/${sNum}/${epNum}` : `https://player.autoembed.co/embed/movie/${tmdb || 693134}`; - default: - if (kpId) { - return `https://api.embess.ws/embed/kp/${kpId}${isTv ? `?season=${sNum}&episode=${epNum}` : ""}`; + case "vidsrc_pm": + return isTv + ? `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 ? `https://vidlink.pro/tv/${tmdb || 693134}/${sNum}/${epNum}?primaryColor=6366f1` : `https://vidlink.pro/movie/${tmdb || 693134}?primaryColor=6366f1`; @@ -3582,15 +3598,64 @@ export default function Cinema({ )} + {/* Server selector buttons right in the HUD */} +
+ + + + + +
+ {/* Quick mirror switcher */} {/* Single Settings button (Contains voiceover studios & mirrors) */} @@ -3639,9 +3704,57 @@ export default function Cinema({ + {/* Quick Fallback Switcher (Always accessible if video fails to load) */} +
+ Не работает плеер? + +
+ {/* Clean 100% Video Canvas */}
- {selectedMirror === "anilibria" ? ( + {(playingMedia || selectedMedia).is_unreleased ? ( +
+
+ ⏳ +

Фильм ещё не вышел в цифровой прокат

+

+ «{(playingMedia || selectedMedia).title}» ещё находится в производстве или кинотеатральном прокате (релиз: {(playingMedia || selectedMedia).release_date_full || (playingMedia || selectedMedia).release_year || "2026"}). +

+ {(playingMedia || selectedMedia).trailer_youtube_key ? ( +
+