Releases: openwave-player/openwave
OpenWave 0.1.5
Esta versão traz melhorias significativas na estabilidade, fluidez da interface (UI) e desempenho geral do OpenWave. O foco principal foi eliminar travamentos ao carregar grandes bibliotecas musicais e otimizar o consumo de recursos do sistema.
O que há de novo?
Escaneamento de Biblioteca em Background (Não-Bloqueante)
- Antes: Ao selecionar uma pasta com milhares de músicas, a interface do OpenWave congelava até que o Mutagen terminasse de ler os metadados de todos os arquivos.
- Agora: O processo de varredura e indexação de arquivos foi movido para uma thread secundária assíncrona (
threading.Thread). A interface permanece 100% responsiva, exibindo um estado visual de carregamento inteligente enquanto processa as faixas em segundo plano.
Busca Otimizada com Debounce (Redução de CPU)
- Implementado um mecanismo de debounce (atraso controlado de 300ms) usando
GLib.timeout_addtanto na busca global de faixas quanto no filtro de artistas. - A interface agora aguarda o usuário terminar de digitar antes de disparar a filtragem na listagem, evitando renderizações consecutivas desnecessárias e reduzindo drasticamente o uso de CPU.
Desacoplamento do Motor de Áudio (Player)
- A classe
Playerfoi completamente isolada da lógica da interface gráfica (Gtk.Window). - Toda a comunicação interna do barramento do GStreamer (mensagens de TAG, Fim de Faixa/EOS, Erros e Progresso) agora utiliza um padrão baseado em callbacks puros definidos externamente (
on_tag_found_cb,on_eos_cb, etc.), facilitando a manutenção e futuros testes automatizados.
Polimento na Interface e Gerenciamento de Estado
- Sincronização de Seleção: Correção de concorrências visuais (travamentos em loops de eventos) ao alternar entre as visões de Álbum, Artista e Playlists através da sidebar.
- Proteção contra Falhas: Adicionada maior resiliência com blocos
try/exceptrefinados em consultas críticas de tempo/duração no pipeline do GStreamer, prevenindo crashes inesperados no fechamento do app.
Detalhes Técnicos das Alterações
openwave/player.py: Remoção de referências diretas à janela principal; encapsulamento estrito do loop de progresso (_tick) e timer.openwave/window.py: Implementação do_scan_worker, adoção de flags de controle comoself.is_scanninge timeouts para entradas de texto.openwave/ui_builder.py: Melhorias na consistência visual dos seletores.
Full Changelog: 0.1.4...0.1.5
OpenWave 0.1.4
Esta atualização traz um dos recursos mais essenciais para um player de música de desktop: a integração completa com o menu de volume e notificações do sistema!
Focada em oferecer uma experiência fluida no Linux Mint, essa atualização torna o OpenWave um "cidadão de primeira classe" no seu ambiente gráfico, sendo também totalmente compatível com GNOME, KDE Plasma e outros painéis modernos.
Novidades e Melhorias
- Integração com Applet de Som: O OpenWave agora tem um reprodutor embutido direto no menu de volume/notificações do painel do sistema.
- Controles Multimídia Nativos: Agora você pode pausar, reproduzir, avançar ou voltar faixas usando os botões do painel do sistema ou as teclas de mídia nativas do seu teclado.
- Metadados Completos: O sistema agora reconhece e exibe perfeitamente o título da música, o artista e o álbum da faixa em reprodução.
- Capa do Álbum no Painel: A arte do álbum agora é renderizada e enviada em tempo real para o applet de som, deixando o visual muito mais rico.
- Sincronia de Progresso: O tempo de reprodução da música agora é sincronizado com o sistema, permitindo que o painel mostre a minutagem correta.
O que mudou por baixo do capô (Technical Details)
- Novo Suporte a MPRIS2: Adicionado o módulo
mpris.py, construído diretamente sobre odbuspadrão do Python. Isso garante comunicação perfeita com o sistema sem inflar o aplicativo com dependências externas complexas. - Refatoração no Player: O módulo
player.py(GStreamer) recebeu o métodoget_position_us()para reportar a posição exata do áudio em microssegundos para o barramento D-Bus. - Gerenciamento de Capas: O
window.pyagora extrai os bytes da capa do álbum na memória e os salva de forma segura e temporária (current_cover.jpg) no diretório de configuração do usuário para que a interface gráfica do sistema operacional consiga acessá-la via URL (file://).
Full Changelog: 0.1.3...0.1.4
OpenWave 0.1.3
Nesta versão, o núcleo do projeto foi completamente reestruturado para melhorar a manutenibilidade, testabilidade e separação de conceitos. O antigo arquivo monolítico app.py foi dividido em um pacote estruturado chamado openwave/, composto por 7 novos módulos especializados.
Nova Estrutura de Arquivos e Responsabilidades
Abaixo está a divisão da arquitetura do aplicativo e o papel de cada novo módulo:
| Arquivo / Módulo | Responsabilidade |
|---|---|
app.py |
Ponto de entrada: Apenas inicializa o GTK e abre a janela principal. |
openwave/constants.py |
Configurações: Centraliza versão, URLs e extensões de áudio suportadas. |
openwave/utils.py |
Funções puras: normalize_text, read_audio_metadata, ensure_dir, parse_version. |
openwave/updater.py |
Atualizações: Verificação de updates e download/restart em background. |
openwave/dialogs.py |
Componentes visuais: Diálogos reutilizáveis como PlaylistDialog e show_about_dialog. |
openwave/player.py |
Core de Áudio: Classe Player que encapsula o pipeline GStreamer com callbacks desacoplados. |
openwave/ui_builder.py |
Design/Estilo: Gerenciamento de CSS e funções puras de UI (build_header, sidebar_section, add_sidebar_row). |
openwave/window.py |
Orquestrador: Classe OpenWave, agora muito menor, focada apenas em coordenar os módulos. |
Benefícios Práticos desta Refatoração
- Testabilidade Isolada: A classe
Playeragora pode ser testada separadamente e seu backend pode ser substituído (ex: por outro motor de áudio) sem a necessidade de tocar na janela do app. - Execução em Background Isolada da UI: O módulo
updater.pypode ser importado e testado de forma independente, sem precisar renderizar a interface GTK. - Facilidade em Ajustes Visuais: O CSS e os builders da barra lateral (sidebar) ficaram centralizados em
ui_builder.py, tornando as modificações estéticas muito mais fáceis de ajustar visualmente. - Manutenção Segura: Adicionar um novo diálogo em
dialogs.pyficou mais seguro, eliminando o risco de quebrar a lógica principal de reprodução de áudio.
Full Changelog: 0.1.2...0.1.3
OpenWave 0.1.2
Atualizações automáticas
- Verificação em segundo plano ao iniciar o app
- Comparação de versão com a última Release publicada no GitHub
- Download e substituição automática do
app.pycom reinício do processo ao aceitar a atualização
Full Changelog: 0.1.1...0.1.2
OpenWave 0.1.1
Refactoring realizado sobre
app.py(versão 0.1).
Bugs corrigidos
1. Gst.init(None) chamado duas vezes
Arquivo: app.py — bloco if __name__ == "__main__"
O GStreamer era inicializado em _init_gstreamer() e novamente no ponto de entrada. A segunda chamada é um no-op silencioso, mas pode causar avisos em certas versões do GStreamer e indica descuido na ordem de inicialização.
Fix: removida a chamada redundante do bloco __main__.
2. stop_playback deixava o pipeline GStreamer em estado inconsistente
Arquivo: app.py — método stop_playback
O método usava Gst.State.PAUSED para "parar" a reprodução. Isso mantinha o pipeline alocado (decodificadores, buffers, handle de arquivo), consumindo recursos desnecessariamente e causando comportamento imprevisível ao retomar outra faixa.
Fix: trocado para Gst.State.NULL, que libera o pipeline completamente. Adicionados reset visual da barra de progresso e do tempo atual.
3. Timer de progresso acumulava instâncias a cada faixa
Arquivo: app.py — método play_track
O timer criado com GLib.timeout_add(1000, update_progress) nunca era cancelado antes de iniciar uma nova faixa. Após tocar N faixas, N timers rodavam em paralelo, todos chamando update_progress a cada segundo — causando contenção, glitches de UI e uso de CPU crescente.
Fix: adicionado helper _cancel_progress_timer() chamado no início de play_track, em stop_playback e em on_error.
4. on_error não limpava o estado de reprodução
Arquivo: app.py — método on_error
Ao receber um erro do GStreamer, o método atualizava o ícone play/pause mas não cancelava o timer de progresso, não resetava current_track_path e não resetava a barra de progresso. A UI ficava mostrando a última posição conhecida e novos cliques em "próxima" podiam tentar retomar a faixa com erro.
Fix: on_error agora chama _cancel_progress_timer(), zera current_track_path, reseta progresso e tempo para 00:00.
5. empty_label sempre visível após inicialização com biblioteca carregada
Arquivo: app.py — método __init__
A sequência era: _apply_track_view() (que define empty_label.set_visible(False) quando há faixas) → show_all() (que força todos os widgets para visíveis) → empty_label.set_visible(True) (hardcoded). O resultado: o label "Abra uma pasta para ver suas músicas" aparecia sobreposto à lista mesmo com a biblioteca carregada.
Fix: a última linha foi substituída por set_visible(not has_tracks), baseado no estado real da biblioteca.
6. on_artists_tree_selection_changed referenciada mas nunca definida
Arquivo: app.py — método _sidebar_tree_section
O método conectava o sinal changed da seleção de um TreeView ao handler self.on_artists_tree_selection_changed, que não existe em lugar nenhum na classe. Qualquer código que chegasse a instanciar esse widget resultaria em AttributeError em runtime.
Fix: _sidebar_tree_section (e _build_artist_tree_model, que a alimentava) eram código completamente morto — nunca chamados. Ambos foram removidos.
Redundâncias eliminadas
| Elemento removido | Motivo |
|---|---|
| pretty_album_name() | Sempre retornava o argumento album sem transformação real; inlinado em read_audio_metadata |
| format_time(ns) | Wrapper de 2 linhas sobre format_time_from_seconds; callers atualizados para pos / Gst.SECOND |
| _set_cover_from_tag_bytes() | Wrapper de 2 linhas sobre _load_cover_from_bytes, nunca chamada em lugar nenhum |
| _build_artist_tree_model() | 50 linhas de código morto (nunca chamada) |
| _sidebar_tree_section() | 45 linhas de código morto (nunca chamada) |
| show_all() em main | Redundante; OpenWave.init já chama self.show_all() |
| Chamada dupla a _refresh_album_browser em _apply_track_view | Já era feita por _select_sidebar_row_for_view na mesma sequência de chamadas |
| 4× hasattr(self, "album_selector") | Widget sempre existe após _build_ui; guards mascaravam bugs em vez de proteger |
| Linhas em branco duplicadas em vários métodos | Estilo |