Releases: jandersonss/xstream-player
Release list
v1.12.0
Estado do Modo TV em SQLite 📺
O Modo TV guardava as transmissões ativas e o sync de reprodução em dois JSONs (live-sessions.json e live-sync.json), reescritos por inteiro a cada heartbeat e sem nenhum lock. Na prática isso dava dois problemas: dois dispositivos batendo heartbeat ao mesmo tempo liam a mesma lista e um sobrescrevia o outro — o dispositivo sumia da tela do Modo TV até o próximo heartbeat; e, como a escrita não é atômica, quem lesse o arquivo no meio de uma gravação podia pegar um JSON truncado.
Agora esse estado vive em data/tv-mode.sqlite, com upsert por linha e transações: cada heartbeat mexe só na própria linha, e as leituras nunca enxergam um estado pela metade.
Por que um banco separado
Já existiam dois bancos com ciclos de vida opostos — o cache de catálogo (xstream-player.sqlite, descartável) e os dados do usuário (user-data.sqlite, insubstituível). O estado do Modo TV não pertence a nenhum dos dois: ele é efêmero (toda linha expira em segundos sem heartbeat) e recebe escrita o tempo todo. Colocá-lo no banco de usuário incharia justamente o arquivo que importa no backup; colocá-lo no cache faria a limpeza do catálogo — uma operação normal — derrubar transmissões em andamento.
Com um terceiro banco, apagar o tv-mode.sqlite a qualquer momento não custa nada: os dispositivos se re-anunciam no heartbeat seguinte.
O que fazer
Nada. Não há migração: os JSONs antigos são apagados sozinhos na primeira execução, e todo o conteúdo deles expiraria em segundos de qualquer forma. Quem roda várias instâncias compartilhando a pasta data/ continua funcionando igual — o estado compartilhado só mudou de arquivo.
v1.11.1
Correções na imagem Docker 🐳
Credenciais podiam vazar para dentro da imagem (só em build local)
O file tracing do Next varria a pasta data/ para dentro de .next/standalone, e o estágio final do Dockerfile copia o standalone inteiro. Ou seja: o que estivesse em data/ no momento do build entrava na imagem — inclusive o config.json com as credenciais Xtream em texto puro e o banco de usuário (user-data.sqlite).
Na prática isso só afeta quem builda localmente, numa cópia do projeto que já rodou e portanto tem data/ populado. Um build em pipeline parte de um checkout limpo, sem data/, então não havia o que vazar — nenhuma imagem publicada foi afetada.
A pasta data/ é estado de runtime, montada como volume, e agora está excluída tanto do tracing (next.config.ts) quanto do contexto de build (.dockerignore).
O que fazer: se você construiu a imagem localmente e a compartilhou ou publicou em algum registry, trate as credenciais como expostas e troque a senha do provedor Xtream. Se as imagens locais nunca saíram da sua máquina, basta reconstruir com esta versão.
Script de migração não chegava na imagem
O estágio final também nunca copiava a pasta scripts/, então o comando documentado no próprio script — docker exec <container> node scripts/migrate-subtitles.mjs — falhava com "module not found". Quem atualizou para a v1.11.0 via Docker não conseguiu migrar as legendas antigas para o novo layout por idioma. Agora a pasta é copiada explicitamente.
O que fazer: se você atualizou para a v1.11.0 em Docker e tinha legendas baixadas, rode a migração após subir esta versão:
docker exec <container> node scripts/migrate-subtitles.mjs --dry-run # confere
docker exec <container> node scripts/migrate-subtitles.mjs # move
Sem isso, as legendas antigas ficam invisíveis para o player e seriam baixadas de novo, consumindo a cota da OpenSubtitles (5 por dia).
Full Changelog: v1.11.0...v1.11.1
v1.11.0
Perfis de usuário 👤
Favoritos e "continuar assistindo" eram globais — todas as TVs da casa dividiam a mesma lista. Agora cada pessoa tem o seu perfil, escolhido uma vez por dispositivo.
Novidades
- Perfis: crie, edite e troque de perfil pelo seletor no topo da sidebar. Favoritos e progresso de reprodução são escopados por perfil; o perfil ativo viaja no cookie
xstream_profile, então as rotas resolvem sozinhas quem está assistindo. - Dados do usuário em SQLite: perfis, favoritos e progresso saíram dos JSONs e passaram a viver em
data/user-data.sqlite, separado do cache de catálogo. Apagar o cache para forçar um re-sync continua sendo uma operação inofensiva — não leva os seus dados junto. - Legendas por idioma: o cache de legendas agora é chaveado por idioma (
subtitles/<idioma>/). Antes, um download em um idioma sobrescrevia silenciosamente a legenda de outro idioma para o mesmo conteúdo. - Busca instantânea: a busca deixou de baixar o catálogo inteiro (20 mil itens, com sinopse, elenco e imagens) para o navegador e filtrar a cada tecla. A consulta vai para o SQLite e volta já ranqueada — e agora ignora acentos e pontuação ("obsessao" acha "Obsessão", "spider man" acha "Spider-Man"). Título exato vem antes de prefixo, que vem antes de palavra, então o filme original fica na frente das continuações.
Interface
- Sidebar redesenhada no estilo Netflix: compacta, com o perfil no topo.
- Logo movida para o header da home, com o nome de usuário no badge de status.
- Badges de conta unificados, mostrando o uso de conexões ao vivo (quantas das suas conexões estão em uso agora).
- Configurações de legenda saíram da sidebar e foram para o header da home.
Correções
- TMDb pegava o filme errado: um remake casava com o original ("Obsessão" 2026 → "Obsessão (2019)") e uma sequência casava com a irmã ("Todo Mundo em Pânico 6" → "Todo Mundo em Pânico 2"), o que também fazia as legendas serem baixadas para o filme errado. Ano e número de sequência agora são filtros rígidos, aplicados antes da pontuação de similaridade.
- Progresso corrompido: o arquivo de resumo era escrito por duas rotas ao mesmo tempo, sem lock nem escrita atômica, e já havia chegado a corromper o
watch-progress.json. O resumo agora é derivado das linhas de episódio, não é mais uma segunda cópia mantida à mão. - Segredos na resposta da API: as credenciais Xtream deixaram de aparecer na resposta da rota de conta, e passam a ser lidas do formato aninhado do config.
Projeto
- Licença AGPL-3.0 e CLA para contribuidores, com check de CI que barra PRs sem a frase de aceite. O ffmpeg embutido na imagem Docker (GPL/LGPL) está documentado no README.
CLAUDE.md, padrões de projeto e comentários de código traduzidos para inglês (textos de UI seguem em pt-BR).
Atualizando
⚠️ Use a v1.11.1 no lugar desta. Na imagem Docker da v1.11.0 falta a pastascripts/, então a migração de legendas não roda em container — e um build local (comdata/já populado) embutia odata/config.jsonna imagem. Build em pipeline, a partir de checkout limpo, não é afetado.
Nada a fazer: favoritos e progresso são importados dos JSONs no primeiro boot e as legendas existentes são movidas por scripts/migrate-subtitles.mjs. O cache do TMDb é invalidado nesta versão (ele guardava os casamentos errados) e será refeito sob demanda.
Full Changelog: v1.10.0...v1.11.0
v1.10.0
Legendas para séries 🎬
A funcionalidade de legendas (antes exclusiva de filmes) agora funciona para séries.
Novidades
- Buscar/baixar em lote: fluxo em duas etapas — primeiro buscar (não consome cota) mostra por episódio se há legenda disponível, já baixada ou sem legenda; depois baixar pega apenas as encontradas.
- Download sob demanda: quando a série já tem legendas, ao abrir um episódio sem legenda ela é buscada e baixada automaticamente antes de reproduzir e anexada ao player — transparente para o usuário.
Correções
- Detecção correta da cota diária da OpenSubtitles (HTTP 406 com
remaining:0— antes só tratava 407). O lote agora para de forma limpa ao esgotar os 5 downloads/24h e exibe o saldo restante.
Sob o capô
- Novo endpoint
GET /api/subtitles/user/listmarca de uma vez os episódios já legendados. - Rate limit respeitado com pausa entre requisições e backoff exponencial no servidor.
Full Changelog: v1.9.0...v1.10.0
v1.9.0
Modo TV — compartilhamento de conexão 📺
Novidades
- Modo TV: compartilhamento de conexão para transmitir live e VOD via ffmpeg.
- Transmitir tudo com sincronização entre players, título no player e correção do auto-skip.
Correções
- Estado de sincronização agora é persistido em
data/, funcionando corretamente em cenários multi-instância.
Documentação
- Documentado o Modo TV (compartilhamento de conexão) e screenshot atualizado.
Full Changelog: v1.8.0...v1.9.0
v1.8.0
Auto-skip de episódios + otimizações ⏭️
Novidades
- Pular para o próximo episódio: no último minuto de um episódio de série aparece um prompt com dois botões — pular agora ou adiar (para quando os créditos são curtos e o episódio ainda está rodando). O botão primário em foco avança automaticamente após uma contagem regressiva de 10s.
- A contagem é ancorada ao tempo do vídeo: congela ao pausar e retrocede ao voltar (seek). A duração é atualizada em
durationchange, evitando que streams progressivos disparem o prompt cedo demais.
Performance
- Menor uso de memória na home em TVs de baixo desempenho.
Sob o capô
- Migração do armazenamento de cache para SQLite (catálogo, sync do Xtream e sincronização passam pelo fluxo SQLite no servidor).
- Middleware para redirecionamento de WebOS legado e novas rotas de API.
- Ajustes de tipagem no acesso a dados de sync e na fachada de cache.
Full Changelog: v1.7.0...v1.8.0
v1.7.0
What's Changed
- Add legacy React app for old WebOS TVs by @jandersonss in #4
Full Changelog: v1.6.2...v1.7.0
v1.6.2
What's Changed
- Fix WebOS playback loading and add debug diagnostics by @jandersonss in #3
Full Changelog: v1.6.1...v1.6.2
v1.6.1
What's Changed
- Improve VOD playback on constrained devices by @jandersonss in #2
Full Changelog: v1.6.0...v1.6.1
v1.6.0
What's Changed
- Add remote access PIN protection by @jandersonss in #1
New Contributors
- @jandersonss made their first contribution in #1
Full Changelog: v1.5.1...v1.6.0