fix(player): multi-source balancers, remove broken embess, add server switcher & unreleased trailer handling to 0.0.4
This commit is contained in:
parent
a4fc19885f
commit
6750d7d437
5 changed files with 361 additions and 78 deletions
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
if (anilibriaStreamUrl) {
|
||||||
|
setSelectedMirror("anilibria");
|
||||||
|
} else if (kodikResults.length > 0) {
|
||||||
setSelectedMirror("kodik_ru");
|
setSelectedMirror("kodik_ru");
|
||||||
showTvToast("🌸 Русская озвучка аниме", "🎙️", 1800);
|
|
||||||
} else {
|
} else {
|
||||||
setSelectedMirror("embess_ru");
|
setSelectedMirror("vidlink_pro");
|
||||||
showTvToast("🇷🇺 Включен полный русский дубляж (1080p)", "🎙️", 1800);
|
}
|
||||||
|
showTvToast("🌸 Сервер: Kodik (Аниме / Озвучка)", "🎙️", 1800);
|
||||||
|
} else {
|
||||||
|
// 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
|
// 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>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue