Skip to content

Commit 589b5d0

Browse files
committed
Adicionar funcionalidade de compartilhamento de streams via URL e otimizar carregamento de streams da URL
1 parent 5ed4d79 commit 589b5d0

4 files changed

Lines changed: 150 additions & 3 deletions

File tree

src/components/StreamPanel.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,6 @@ export const StreamPanel = memo(function StreamPanel({
258258
// Calcula largura real em pixels baseado na porcentagem do stream
259259
const viewportWidth = window.innerWidth;
260260
const calculatedWidth = (viewportWidth * stream.width) / 100;
261-
console.log('Container width:', calculatedWidth, 'Stream:', stream.title, 'Percentage:', stream.width);
262261
setContainerWidth(calculatedWidth);
263262
};
264263

src/hooks/useStreams.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useState, useCallback, useEffect } from "react";
22
import { Stream, Platform, AppSettings } from "@/types/stream";
33
import { toast } from "sonner";
4+
import { getStreamsFromCurrentUrl } from "@/lib/shareUtils";
45

56
const STORAGE_KEY_STREAMS = "multistream-streams";
67
const STORAGE_KEY_SETTINGS = "multistream-settings";
@@ -14,9 +15,21 @@ function generateUUID() {
1415
}));
1516
}
1617

17-
// Carrega streams do localStorage
18+
// Carrega streams do localStorage ou da URL compartilhada
1819
function loadStreams(): Stream[] {
1920
try {
21+
// Primeiro, verifica se há streams na URL (prioridade)
22+
const urlStreams = getStreamsFromCurrentUrl();
23+
if (urlStreams && urlStreams.length > 0) {
24+
console.log('Carregando streams da URL compartilhada:', urlStreams.length);
25+
// Limpa o hash da URL após carregar
26+
if (window.history.replaceState) {
27+
window.history.replaceState(null, '', window.location.pathname + window.location.search);
28+
}
29+
return urlStreams;
30+
}
31+
32+
// Se não há streams na URL, carrega do localStorage
2033
const saved = localStorage.getItem(STORAGE_KEY_STREAMS);
2134
if (!saved) return [];
2235

src/lib/shareUtils.ts

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { Stream } from "@/types/stream";
2+
3+
// Dados mínimos para compartilhamento
4+
interface ShareableStream {
5+
p: string; // platform
6+
c: string; // channelId
7+
t: string; // title
8+
m?: boolean; // isMain
9+
ch?: boolean; // showChat
10+
w?: number; // width
11+
h?: number; // height
12+
v?: number; // volume
13+
ic?: boolean; // isChannel
14+
}
15+
16+
/**
17+
* Codifica as streams em uma string compacta para compartilhamento via URL
18+
*/
19+
export function encodeStreamsToUrl(streams: Stream[]): string {
20+
if (streams.length === 0) return '';
21+
22+
const shareableStreams: ShareableStream[] = streams.map(stream => {
23+
const shareable: ShareableStream = {
24+
p: stream.platform,
25+
c: stream.channelId,
26+
t: stream.title,
27+
};
28+
29+
// Só adiciona propriedades não-padrão para economizar espaço
30+
if (stream.isMain) shareable.m = true;
31+
if (stream.showChat) shareable.ch = true;
32+
if (stream.width !== 50) shareable.w = Math.round(stream.width * 100) / 100; // 2 decimais
33+
if (stream.height !== 400) shareable.h = stream.height;
34+
if (stream.volume !== 100) shareable.v = stream.volume;
35+
if (stream.isChannel) shareable.ic = true;
36+
37+
return shareable;
38+
});
39+
40+
// Converte para JSON e comprime com base64
41+
const json = JSON.stringify(shareableStreams);
42+
const compressed = btoa(encodeURIComponent(json));
43+
44+
return compressed;
45+
}
46+
47+
/**
48+
* Decodifica streams de uma URL compartilhada
49+
*/
50+
export function decodeStreamsFromUrl(encoded: string): Stream[] {
51+
if (!encoded) return [];
52+
53+
try {
54+
// Decodifica de base64
55+
const json = decodeURIComponent(atob(encoded));
56+
const shareableStreams: ShareableStream[] = JSON.parse(json);
57+
58+
// Converte de volta para formato completo
59+
const streams: Stream[] = shareableStreams.map((shareable, index) => ({
60+
id: `stream-${Date.now()}-${index}`,
61+
platform: shareable.p as any,
62+
channelId: shareable.c,
63+
title: shareable.t,
64+
isMain: shareable.m || false,
65+
showChat: shareable.ch || false,
66+
width: shareable.w ?? 50,
67+
height: shareable.h ?? 400,
68+
volume: shareable.v ?? 100,
69+
isChannel: shareable.ic || false,
70+
}));
71+
72+
return streams;
73+
} catch (error) {
74+
console.error('Erro ao decodificar streams da URL:', error);
75+
return [];
76+
}
77+
}
78+
79+
/**
80+
* Gera URL completa para compartilhamento
81+
*/
82+
export function generateShareUrl(streams: Stream[]): string {
83+
const encoded = encodeStreamsToUrl(streams);
84+
if (!encoded) return window.location.origin;
85+
86+
const url = new URL(window.location.origin);
87+
url.hash = `streams=${encoded}`;
88+
89+
return url.toString();
90+
}
91+
92+
/**
93+
* Extrai streams da URL atual
94+
*/
95+
export function getStreamsFromCurrentUrl(): Stream[] | null {
96+
const hash = window.location.hash;
97+
if (!hash) return null;
98+
99+
// Remove o # e extrai o parâmetro streams
100+
const params = hash.substring(1);
101+
const match = params.match(/streams=([^&]+)/);
102+
103+
if (!match) return null;
104+
105+
return decodeStreamsFromUrl(match[1]);
106+
}

src/pages/Index.tsx

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import { SettingsTooltip } from "@/components/SettingsTooltip";
66
import { KeyboardShortcutsDialog } from "@/components/KeyboardShortcutsDialog";
77
import { Button } from "@/components/ui/button";
88
import { Badge } from "@/components/ui/badge";
9-
import { Monitor, Maximize, Minimize, Headphones } from "lucide-react";
9+
import { Monitor, Maximize, Minimize, Headphones, Share2 } from "lucide-react";
10+
import { generateShareUrl } from "@/lib/shareUtils";
11+
import { toast } from "sonner";
1012

1113
const Index = () => {
1214
const {
@@ -70,6 +72,24 @@ const Index = () => {
7072
}
7173
}, [isFullscreen]);
7274

75+
const handleShare = useCallback(async () => {
76+
if (streams.length === 0) {
77+
toast.error('Adicione pelo menos uma stream para compartilhar');
78+
return;
79+
}
80+
81+
try {
82+
const shareUrl = generateShareUrl(streams);
83+
84+
// Copia para clipboard
85+
await navigator.clipboard.writeText(shareUrl);
86+
toast.success('Link copiado para a área de transferência!');
87+
} catch (error) {
88+
console.error('Erro ao copiar link:', error);
89+
toast.error('Erro ao copiar link');
90+
}
91+
}, [streams]);
92+
7393
// Detectar quando o usuário sai da tela cheia usando ESC
7494
useEffect(() => {
7595
const handleFullscreenChange = () => {
@@ -233,6 +253,15 @@ const Index = () => {
233253
</div>
234254
<div className="flex items-center gap-2">
235255
<KeyboardShortcutsDialog open={showShortcuts} onOpenChange={setShowShortcuts} />
256+
<Button
257+
variant="ghost"
258+
size="icon"
259+
onClick={handleShare}
260+
disabled={streams.length === 0}
261+
title="Compartilhar configuração de streams"
262+
>
263+
<Share2 className="h-4 w-4" />
264+
</Button>
236265
<Button
237266
variant="ghost"
238267
size="icon"

0 commit comments

Comments
 (0)