Skip to content

Commit 5ed4d79

Browse files
committed
Adicionar detecção de live para YouTube e ajustar controles de reprodução com base na largura do container
1 parent ea607de commit 5ed4d79

1 file changed

Lines changed: 93 additions & 31 deletions

File tree

src/components/StreamPanel.tsx

Lines changed: 93 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ export const StreamPanel = memo(function StreamPanel({
9595
const [isSeeking, setIsSeeking] = useState(false);
9696
const [willWrap, setWillWrap] = useState(false);
9797
const [animatedDisplayVolume, setAnimatedDisplayVolume] = useState(stream.volume);
98+
const [containerWidth, setContainerWidth] = useState<number>(999); // Inicializa com valor alto para mostrar controles até calcular
9899
const containerRef = useRef<HTMLDivElement>(null);
99100
const iframeRef = useRef<HTMLIFrameElement>(null);
100101
const twitchEmbedRef = useRef<HTMLDivElement>(null);
@@ -121,7 +122,13 @@ export const StreamPanel = memo(function StreamPanel({
121122
setCurrentTime(0);
122123
lastDurationRef.current = 0;
123124
durationIncreaseCountRef.current = 0;
124-
}, [stream.id, stream.channelId]);
125+
126+
// Se for um canal do YouTube (isChannel), assume que é live por padrão
127+
// A URL nesse caso usa /live_stream que sempre aponta para live ativa
128+
if (stream.platform === 'youtube' && stream.isChannel) {
129+
setIsLive(true);
130+
}
131+
}, [stream.id, stream.channelId, stream.platform, stream.isChannel]);
125132

126133
// Fecha chat automaticamente se for YouTube e não for live
127134
useEffect(() => {
@@ -245,6 +252,27 @@ export const StreamPanel = memo(function StreamPanel({
245252
};
246253
}, []);
247254

255+
// Monitora a largura do container baseado na porcentagem
256+
useEffect(() => {
257+
const updateWidth = () => {
258+
// Calcula largura real em pixels baseado na porcentagem do stream
259+
const viewportWidth = window.innerWidth;
260+
const calculatedWidth = (viewportWidth * stream.width) / 100;
261+
console.log('Container width:', calculatedWidth, 'Stream:', stream.title, 'Percentage:', stream.width);
262+
setContainerWidth(calculatedWidth);
263+
};
264+
265+
// Atualiza largura inicial
266+
updateWidth();
267+
268+
// Atualiza na mudança de tamanho da janela
269+
window.addEventListener('resize', updateWidth);
270+
271+
return () => {
272+
window.removeEventListener('resize', updateWidth);
273+
};
274+
}, [stream.width, stream.title]);
275+
248276
// Inicializa Twitch Embed quando plataforma for Twitch
249277
useEffect(() => {
250278
if (stream.platform !== 'twitch' || !twitchEmbedRef.current) return;
@@ -466,19 +494,26 @@ export const StreamPanel = memo(function StreamPanel({
466494

467495
// Detecta se é uma transmissão ao vivo
468496
// Lives do YouTube têm duração que aumenta continuamente
469-
if (newDuration > lastDurationRef.current && newDuration > 60) {
497+
if (newDuration > lastDurationRef.current && newDuration > 0) {
470498
// Incrementa contador de aumentos consecutivos
471499
durationIncreaseCountRef.current += 1;
472500

473-
// Só considera live se a duração aumentou múltiplas vezes (3+)
474-
// Isso evita falsos positivos com vídeos carregando
475-
if (durationIncreaseCountRef.current >= 3) {
501+
// Detecção mais agressiva de live:
502+
// 1. Se a duração aumentou pelo menos 2 vezes (mais rápido que antes)
503+
// 2. OU se a diferença entre currentTime e duration é pequena (indica live)
504+
// 3. OU se a duração é muito alta (>= 12h pode indicar live longa ou VOD de live)
505+
const timeDiff = newDuration - (data.info.currentTime || 0);
506+
const isProbablyLive = durationIncreaseCountRef.current >= 2 ||
507+
(timeDiff > 0 && timeDiff < 30 && newDuration > 60) ||
508+
newDuration >= 43200; // 12 horas
509+
510+
if (isProbablyLive) {
476511
setIsLive(true);
477512
}
478-
} else if (newDuration === lastDurationRef.current) {
479-
// Duração parou de crescer - provavelmente não é live
480-
// Ou a live acabou
481-
if (durationIncreaseCountRef.current < 3) {
513+
} else if (newDuration === lastDurationRef.current && newDuration > 0) {
514+
// Duração parou de crescer
515+
// Só marca como não-live se teve poucas aumentos E não é uma duração muito alta
516+
if (durationIncreaseCountRef.current < 2 && newDuration < 43200) {
482517
setIsLive(false);
483518
}
484519
}
@@ -498,6 +533,14 @@ export const StreamPanel = memo(function StreamPanel({
498533
}
499534
}
500535

536+
// Resposta de getVideoData para detectar live
537+
if (typeof data === 'object' && data.method === 'getVideoData' && data.value) {
538+
// YouTube retorna isLive no videoData quando disponível
539+
if (data.value.isLive !== undefined) {
540+
setIsLive(data.value.isLive);
541+
}
542+
}
543+
501544
// Resposta direta de getCurrentTime - só atualiza se não estiver fazendo seek e se o valor for maior ou igual ao atual
502545
if (typeof data === 'object' && data.method === 'getCurrentTime' && !isSeeking && (data.value || 0) >= currentTime) {
503546
setCurrentTime(data.value || 0);
@@ -558,6 +601,14 @@ export const StreamPanel = memo(function StreamPanel({
558601
args: []
559602
};
560603
iframeRef.current.contentWindow?.postMessage(JSON.stringify(durationMessage), '*');
604+
605+
// Requisita videoData para detectar live de forma mais precisa
606+
const videoDataMessage = {
607+
event: 'command',
608+
func: 'getVideoData',
609+
args: []
610+
};
611+
iframeRef.current.contentWindow?.postMessage(JSON.stringify(videoDataMessage), '*');
561612
}
562613
}, 500); // Atualiza a cada 500ms para resposta mais rápida
563614

@@ -1141,15 +1192,18 @@ export const StreamPanel = memo(function StreamPanel({
11411192
{/* Controles de reprodução (apenas para YouTube) */}
11421193
{stream.platform === 'youtube' && (
11431194
<>
1144-
<Button
1145-
variant="ghost"
1146-
size="icon"
1147-
className="h-8 w-8"
1148-
onClick={skipBackward}
1149-
title="Retroceder 10s (←)"
1150-
>
1151-
<SkipBack className="h-4 w-4" />
1152-
</Button>
1195+
{/* Botões de skip - ocultos em largura < 300px */}
1196+
{containerWidth >= 400 && (
1197+
<Button
1198+
variant="ghost"
1199+
size="icon"
1200+
className="h-8 w-8"
1201+
onClick={skipBackward}
1202+
title="Retroceder 10s (←)"
1203+
>
1204+
<SkipBack className="h-4 w-4" />
1205+
</Button>
1206+
)}
11531207
<Button
11541208
variant="ghost"
11551209
size="icon"
@@ -1163,27 +1217,35 @@ export const StreamPanel = memo(function StreamPanel({
11631217
<Play className="h-4 w-4" />
11641218
)}
11651219
</Button>
1166-
<Button
1167-
variant="ghost"
1168-
size="icon"
1169-
className="h-8 w-8"
1170-
onClick={skipForward}
1171-
title="Avançar 10s (→)"
1172-
>
1173-
<SkipForward className="h-4 w-4" />
1174-
</Button>
1220+
{/* Botões de skip - ocultos em largura < 400px */}
1221+
{containerWidth >= 400 && (
1222+
<Button
1223+
variant="ghost"
1224+
size="icon"
1225+
className="h-8 w-8"
1226+
onClick={skipForward}
1227+
title="Avançar 10s (→)"
1228+
>
1229+
<SkipForward className="h-4 w-4" />
1230+
</Button>
1231+
)}
11751232

11761233
{/* Botão "Ao Vivo" quando estiver atrasado */}
11771234
{isLive && isBehindLive && (
11781235
<Button
11791236
variant="default"
1180-
size="sm"
1181-
className="h-7 px-2 ml-1 bg-red-600 hover:bg-red-700 text-white"
1237+
size={containerWidth < 400 ? "icon" : "sm"}
1238+
className={cn(
1239+
"ml-1 bg-red-600 hover:bg-red-700 text-white",
1240+
containerWidth < 400 ? "h-7 w-7 px-0" : "h-7 px-2"
1241+
)}
11821242
onClick={goToLive}
11831243
title="Voltar ao ao vivo"
11841244
>
1185-
<Radio className="h-3 w-3 mr-1" />
1186-
<span className="text-xs font-bold">AO VIVO</span>
1245+
<Radio className={containerWidth < 400 ? "h-3 w-3" : "h-3 w-3 mr-1"} />
1246+
{containerWidth >= 400 && (
1247+
<span className="text-xs font-bold">AO VIVO</span>
1248+
)}
11871249
</Button>
11881250
)}
11891251
</>

0 commit comments

Comments
 (0)