MusicBee UPnP Plugin Ajuda

Novidades

2.0.9 - 2026-08-23

O aviso de atualização abre as suas páginas no seu idioma

O quê: os links Novidades e Transferir no aviso de atualização agora abrem as páginas do plugin no idioma do MusicBee, em vez de em inglês.

Porquê: esses dois links restringiam o idioma a um de quatro — inglês, francês, espanhol ou alemão — antes de o enviar para o site, então todos os outros recebiam a página em inglês, mesmo quando existia uma tradução. Agora, eles passam o idioma do MusicBee inalterado e deixam o site decidir o que servir, que é o que o botão Ajuda ao lado deles sempre fez.

2.0.8 - 2026-08-22

O plugin agora apresenta-se corretamente às aplicações e dispositivos que o descobrem na sua rede, e as definições de Perfis de Dispositivo estão novamente alinhadas.

Os seus dispositivos mostram o fabricante, modelo e versão corretos

O quê: quando uma aplicação de controlo, um telemóvel ou uma TV encontra o MusicBee na sua rede, agora apresenta este plugin como sendo da yaiol, aponta para o site do próprio plugin, descreve-o como cobrindo os três papéis — servidor, leitor e renderizador — e reporta a versão que tem realmente instalada.

Porquê: cada dispositivo UPnP anuncia quem o fez e o que é, e as aplicações de controlo mostram isso como a identidade do dispositivo. Este plugin ainda estava a anunciar os detalhes do plugin original de onde foi bifurcado: o nome de outro autor, o site do MusicBee em vez do seu próprio, e um número de modelo fixado em "1.0" desde o primeiro lançamento. Do seu telemóvel não havia como saber com qual plugin estava a falar, muito menos qual a sua versão. Esses detalhes agora vêm do próprio plugin, para que a versão mostrada ao lado do dispositivo permaneça correta a cada atualização.

As definições de Perfis de Dispositivo estão novamente alinhadas

O quê: no separador Perfis de Dispositivo, os rótulos e as suas caixas partilham uma margem esquerda e estão com espaçamento uniforme, e o intervalo de taxa de amostragem é lido como uma única linha.

Porquê: os campos tinham-se afastado à medida que as opções eram adicionadas ao separador ao longo do tempo, e o rótulo "até" do intervalo de taxa de amostragem tinha acabado por ficar em cima da caixa ao lado — legível depois de saber o que dizia, intrigante na primeira vez que olhava.

2.0.7 - 2026-08-08

Faixas maiores mantêm o título e o controlo deslizante de posição

O quê: uma faixa maior enviada de um telemóvel — um ficheiro FLAC longo, de alta resolução ou DSD — agora mostra o seu título correto e pode ser percorrida, como uma pequena. Anteriormente, algumas delas eram reproduzidas pela rede, com um endereço web em vez do título e um controlo deslizante que não fazia nada.

Porquê: o plugin espera pela sua cópia local antes de começar, mas costumava decidir antecipadamente se valia a pena esperar por um ficheiro, com base no seu tamanho. Isso era realmente um palpite sobre a velocidade da sua rede, que não tem como saber: uma faixa de 65 MB era considerada muito grande, e depois terminava o download um segundo depois — confortavelmente dentro da espera que já tinha desistido. Agora, simplesmente observa o download. Enquanto ainda está a chegar, o plugin continua a esperar, independentemente do tempo que demore; desiste apenas quando a transferência realmente para, o que agora nota mais rapidamente do que o antigo atraso fixo.

2.0.6 - 2026-08-03

Álbuns enviados de um telemóvel agora são reproduzidos sem interrupções entre faixas, e a rádio da internet é reconhecida como rádio em vez de ser tratada como uma música invulgarmente longa.

Álbuns são reproduzidos sem interrupções

O quê: quando envia um álbum inteiro do seu telemóvel, o MusicBee agora passa de uma faixa para a seguinte sem pausa — assim, gravações ao vivo, DJ sets e obras clássicas contínuas mantêm a sua fluidez.

Porquê: o padrão permite que uma aplicação de controlo diga "isto é o que vem a seguir", o que torna possível uma transição perfeita. Essa instrução não era aceite de todo, então a aplicação não tinha onde colocar a faixa seguinte e anunciava-a como se fosse a atual — a causa do problema do álbum corrigido na versão anterior. Agora é aceite corretamente: a próxima faixa é obtida enquanto a atual ainda está a ser reproduzida, e o próprio leitor do MusicBee faz a transição.

Rádio da internet é reconhecida como rádio

O quê: uma estação ao vivo enviada para o MusicBee é reproduzida como um stream e nunca descarregada.

Porquê: descarregar uma transmissão não faz sentido — não tem fim, e não há nada para saltar — mas o plugin anteriormente não tinha como distinguir uma de um ficheiro de música, então começava a obter e parava assim que o download ultrapassava um tamanho fixo. A aplicação de controlo indica qual dos dois está a enviar, e isso agora é lido diretamente. Sem configurações, sem adivinhações.

Faixas longas de alta resolução mantêm a sua cópia

O quê: ficheiros DSD e gravações longas de 24 bits agora podem ser navegados como qualquer outra faixa.

Porquê: eram apanhados pelo limite de tamanho acima — um movimento de alta resolução de 20 minutos ou uma faixa DSD de 10 minutos excediam-no — então a sua cópia era abandonada e o controlo de posição parava de funcionar exatamente para o material que mais provavelmente valeria a pena "escrutinar". Com a rádio identificada corretamente, não é necessário limite de tamanho.

2.0.5 - 2026-08-03

Duas correções para controlar o MusicBee a partir de um telemóvel: o volume agora significa o mesmo em ambas as extremidades, e a transmissão de um álbum inteiro continua a funcionar após a primeira faixa.

O volume no seu telemóvel corresponde ao volume no MusicBee

O quê: colocar o volume no máximo no seu telemóvel agora atinge o máximo no MusicBee, e a própria definição do MusicBee é lida corretamente no telemóvel.

Porquê: o plugin nunca informou a aplicação de controlo qual era o seu volume máximo, então cada aplicação tinha que adivinhar. Uma delas definiu 69, o que significava que o seu 100% só atingia 69% no MusicBee, enquanto o 100% do MusicBee voltava como 144% no telemóvel — e os botões de volume do telemóvel nunca conseguiam atingir o máximo. O renderizador agora indica claramente o intervalo, para que ambas as extremidades estejam a falar sobre a mesma escala.

A transmissão de um álbum mantém os seus títulos e o seu controlo deslizante de posição

O quê: cada faixa de um álbum enviado de um telemóvel agora mostra o seu título correto e pode ser percorrida, não apenas a primeira.

Porquê: uma aplicação de controlo anuncia a próxima faixa uma fração de segundo após a atual, e esse anúncio estava a cancelar a cópia que estava a ser obtida para a faixa prestes a tocar — então a maioria das faixas voltava silenciosamente a tocar pela rede, o que perde tanto o título quanto a capacidade de saltar. Cópias para várias faixas são agora mantidas lado a lado, para que um anúncio não possa mais cancelar a que está em uso.

2.0.4 - 2026-08-02

A música enviada para o MusicBee a partir de um telemóvel ou outro servidor agora comporta-se como uma faixa real: pode avançar e retroceder nela, e mostra o seu título correto imediatamente. Além disso, uma correção para streamers de alta-fidelidade que se apresentam como um dispositivo combinado.

Avançar e retroceder numa faixa enviada de outro local

O quê: arrastar o controlo deslizante de posição agora funciona para uma faixa enviada do seu telemóvel, de um NAS ou de outro servidor de multimédia. Para tornar isso possível, o MusicBee descarrega uma cópia da faixa para uma pasta temporária enquanto começa a reproduzir, e reproduz essa cópia. Leva cerca de um segundo numa rede doméstica, a cópia é eliminada assim que envia outra faixa, e quaisquer restos são limpos na próxima vez que o MusicBee iniciar.

Porquê: o MusicBee pode iniciar e parar algo que está a ouvir pela rede, mas não pode avançar e retroceder nele — então o controlo deslizante parecia saltar e depois deslizava de volta para onde estava, sem explicação. Reproduzir um ficheiro comum no seu próprio disco remove a limitação inteiramente em vez de contorná-la.

Título e duração corretos desde a primeira nota

O quê: uma faixa enviada por uma aplicação que não dá aos seus ficheiros uma extensão de ficheiro comum agora mostra o seu título e duração reais assim que começa, em vez de aparecer como um longo endereço web.

Porquê: o MusicBee identifica uma faixa — e encontra as suas etiquetas — a partir da extensão do ficheiro, e alguns leitores fornecem endereços sem extensão alguma. A cópia local sempre tem a correta, então a faixa é reconhecida independentemente do que a aplicação de envio lhe chama.

Um salto que não pode ser feito agora é indicado

O quê: se o renderizador realmente não conseguir mover-se para o ponto solicitado, a aplicação de controlo é informada e relata-o.

Porquê: anteriormente respondia "feito" independentemente, então o controlo deslizante deslizava de volta um segundo depois sem nada para explicar o porquê. Uma recusa honesta é mais fácil de agir do que uma silenciosa.

Streamers de alta-fidelidade combinados são lidos corretamente

O quê: quando o MusicBee reproduz para um dispositivo que se apresenta como uma unidade combinada — um streamer Marantz ou Denon, onde o leitor está dentro de um invólucro do fabricante juntamente com um servidor de multimédia — o plugin agora lê os detalhes do próprio leitor em vez dos do servidor de multimédia.

Porquê: tinha estado a perguntar à parte errada do dispositivo quais os formatos de áudio que podia manusear, não obtendo nenhuma resposta utilizável, e continuando sem nunca verificar — exatamente o hardware onde o manuseio de formatos mais precisa de estar correto. A descrição do modelo de um dispositivo também é agora tida em conta ao combiná-lo com um perfil de dispositivo; estava a ser lida do local errado e descartada.

2.0.3 - 2026-08-02

O papel de "reproduzir para" cresce: o MusicBee agora pode receber músicas que não estão na sua biblioteca — um ficheiro no seu telemóvel, num NAS, noutro servidor — em vez de apenas faixas que já possui.

Reproduza músicas enviadas do seu telemóvel, não apenas da sua própria biblioteca

O quê: quando utiliza uma aplicação de controlo como o Symfonium ou o BubbleUPnP para enviar música para o MusicBee, a faixa já não tem de vir da própria biblioteca do MusicBee. Um ficheiro armazenado no próprio telemóvel, num NAS ou noutro servidor de multimédia agora é reproduzido. O título e a duração vêm da aplicação que o enviou, para que a faixa apareça corretamente, mesmo que o MusicBee nunca tenha visto o ficheiro.

Porquê: a função de "reproduzir para" foi criada para o caso em que navega na biblioteca deste PC a partir do seu telemóvel e toca numa música — a faixa já estava no PC, então o MusicBee simplesmente reproduzia o seu próprio ficheiro. Qualquer coisa que chegasse de outro lugar era silenciosamente descartada, o que tornava a funcionalidade inútil para o caso igualmente natural de enviar música do telemóvel para as boas colunas.

Uma faixa que não pode ser reproduzida informa

O quê: se o renderizador realmente não conseguir reproduzir o que lhe foi enviado, agora reporta isso à aplicação que o enviou.

Porquê: anteriormente, respondia "recebido" a qualquer coisa, então a aplicação de controlo continuava e premia reproduzir. Com nada realmente carregado, o MusicBee reiniciava qualquer faixa que tivesse sido deixada de antes — e se esse ficheiro tivesse desaparecido, reclamava que a sua fonte não podia ser encontrada. O erro nomeava uma faixa não relacionada e não apontava para o problema real.

Enviar uma nova faixa enquanto em pausa agora reproduz essa faixa

O quê: se o MusicBee estiver em pausa e a sua aplicação de controlo lhe enviar algo novo, a nova faixa começa.

Porquê: retomar tinha prioridade sobre o carregamento, então a faixa em pausa continuava de onde parou e a faixa que tinha acabado de escolher era descartada sem uma palavra.

2.0.2 - 2026-08-01

A pesquisa é o tema: agora funciona por artista, devolve os resultados certos e é rápida numa biblioteca grande. Além disso, uma nova forma de direcionar a reprodução aleatória da sua aplicação de controlo e uma correção para três botões que não levavam a lado nenhum.

A pesquisa por artista funciona mesmo

O quê: pesquisar por um artista a partir da sua aplicação de controlo agora devolve a música desse artista. A pesquisa corresponde tanto ao Artist da faixa quanto ao Album Artist do álbum, então uma compilação é encontrada quer digite o nome do intérprete ou o nome sob o qual o álbum está arquivado.

Porquê: uma pesquisa de artista era anteriormente confundida com "dá-me tudo deste tipo" — o artista que digitou era descartado e a biblioteca inteira era devolvida, então uma pesquisa que deveria ter correspondido a algumas centenas de faixas devolvia dezenas de milhares. Corresponder apenas a um dos dois campos de artista teria silenciosamente perdido metade dos resultados, então ambos são verificados.

A página de resultados da pesquisa funciona corretamente

O quê: rolar por uma longa lista de resultados de pesquisa agora move-se através dela. Cada página para a qual rola é a página que obtém.

Porquê: o servidor costumava responder a cada pedido com o primeiro punhado de resultados, enquanto reportava a contagem total de correspondências, então uma aplicação que rolava para mais continuava a receber os mesmos itens e nunca chegava ao fim.

A pesquisa é muito mais rápida numa biblioteca grande

O quê: uma pesquisa agora executa uma única consulta para todo o conjunto de resultados e lê apenas as tags da página que está a ver.

Porquê: cada página anteriormente reexecutava a consulta contra a biblioteca e depois carregava as tags de cada correspondência — milhares delas — para mostrar uma dúzia. Numa coleção grande, isso fazia com que cada rolagem pausasse. Os resultados também são descartados sempre que a biblioteca é atualizada, então uma edição nunca é servida desatualizada.

Direcione a sua reprodução aleatória para um filtro

O quê: uma nova definição Random plays from no separador Library Options. Deixe-a em All Music e as pastas Random Tracks / Random Albums da sua aplicação de controlo comportam-se como antes; escolha um dos seus filtros MusicBee e cada pedido aleatório será retirado desse filtro. Filtros ocultos também são oferecidos.

Porquê: uma pasta de reprodução aleatória pede uma fatia de "tudo", e é o único pedido que não contém nenhuma pista sobre o que quis dizer — então sempre retirava da biblioteca inteira, palavra falada e tudo. É aqui que diz o que "tudo" significa. Abrir uma pasta de reprodução aleatória de dentro de um filtro no dispositivo ainda reproduz aleatoriamente esse filtro: uma escolha que faz enquanto navega prevalece sobre a definição.

Ajuda, GitHub e verificação de atualização alcançam páginas reais

O quê: os botões Help e GitHub na caixa de diálogo de definições, e a verificação automática de uma nova versão, agora abrem as páginas que nomeiam.

Porquê: todos os três foram construídos a partir de uma forma abreviada do nome do plugin que nunca existiu em nenhuma página, então cada um falhava silenciosamente — os botões pareciam não fazer nada e a verificação de atualização nunca reportava nada, por mais tempo que uma nova versão estivesse disponível.

2.0.1 - 2026-07-26

Uma ronda de correções de reprodução e navegação, focada em podcasts e nas aplicações de controlo (como o BubbleUPnP) que gerem a reprodução e a ordem aleatória.

Podcasts começam a reproduzir sem pausa

O quê: um episódio de podcast descarregado agora indica o seu comprimento real e tamanho de ficheiro de imediato, transportado diretamente do ficheiro em disco para os metadados de multimédia.

Porquê: sem uma duração declarada, um controlador como o BubbleUPnP volta a analisar todo o fluxo de áudio cada vez que se prime reproduzir, apenas para determinar a duração do episódio — assim, a reprodução só começava após uma pausa notável. Com a duração agora anunciada, começa de forma limpa.

Podcasts aparecem quando se navega por artista

O quê: o nome do programa de um podcast é agora arquivado como o seu artista — espelhado nos campos Artista e Artista do Álbum (e nas suas variantes de ordenação), exatamente como já preenche o campo Álbum.

Porquê: um caminho de navegação que agrupa por um campo de artista costumava encontrar o artista de cada podcast em branco e sem saída num nível vazio. Tratar o próprio programa como o artista — consistente com tratá-lo como o álbum — significa que esses caminhos agora alcançam os episódios em vez de nada.

"Reproduzidos Recentemente" e transmissão funcionam logo após um reinício

O quê: reproduzir um podcast, audiolivro, caixa de entrada ou faixa de rádio diretamente — sem primeiro navegar até ele, como fazem a lista "Reproduzidos Recentemente" do BubbleUPnP e os alvos de transmissão — já não falha. O plugin agora carrega a faixa a pedido quando é solicitada por id.

Porquê: essas listas solicitam uma faixa logo após o MusicBee reiniciar, antes de qualquer coisa ter sido navegada, então o plugin nunca tinha visto o id e respondia "Id inválido". Agora força o carregamento das fontes relevantes nessa primeira solicitação direta e encontra a faixa.

"Faixas Aleatórias" e "Álbuns Aleatórios" devolvem resultados

O quê: as pastas de ordem aleatória "Faixas Aleatórias" e "Álbuns Aleatórios" do BubbleUPnP, que pedem uma fatia aleatória de toda a biblioteca, agora voltam preenchidas.

Porquê: estas são pesquisas sem título para corresponder, e o plugin anteriormente respondia-as a partir de uma lista interna vazia, então sempre mostravam nada. Agora são servidas — corretamente paginadas — a partir do mesmo caminho de consulta a pedido que o resto da navegação usa.

Álbuns com uma etiqueta em branco listam as suas faixas

O quê: abrir um álbum que foi agrupado num valor vazio — por exemplo, faixas da caixa de entrada que não têm ano — agora mostra as suas faixas.

Porquê: a correspondência que reúne as faixas de um álbum tratava "esta etiqueta está vazia" como "nenhuma correspondência", então qualquer álbum formado a partir de um campo em branco navegava para nada. Um valor de grupo vazio agora corresponde corretamente às faixas que o partilham.


2.0.0 - 2026-07-22

Esta é a primeira versão pública do fork de código aberto yaiol do plugin MusicBee UPnP. É apresentada em duas partes: tudo o que é novo neste fork, e depois as correções e melhorias feitas ao plugin original. Cada item mantém o formato O Quê / Porquê do catálogo de funcionalidades internas do projeto, para que o raciocínio por trás de cada alteração esteja na página, e não apenas a alteração.

Novidades neste fork

MediaRenderer - reproduzir para o MusicBee

N01 - MusicBee como um renderizador de reprodução

O Quê: normalmente, este plugin funciona de uma forma: um telefone ou outro dispositivo navega na biblioteca do MusicBee e reproduz a música nele mesmo. Esta funcionalidade adiciona a direção oposta - permite que o MusicBee seja o reprodutor. A partir de uma aplicação de controlo no seu telefone (como o BubbleUPnP), pode escolher o seu MusicBee de desktop como o dispositivo que reproduz, e depois controlá-lo a partir da sua mão: reproduzir, pausar, parar, avançar ou retroceder, saltar para um ponto na faixa, e alterar o volume ou silenciar.

Porquê: transforma o seu telefone num controlo remoto para a música que já está no seu PC. Sente-se no sofá, navegue na sua biblioteca no telefone, toque numa faixa, e ela sai dos altifalantes ligados ao seu desktop - com controlo total de onde está sentado. O plugin original nunca lançou isto como uma funcionalidade em funcionamento.

Ativar: está desativado por predefinição, porque ativá-lo permite que qualquer coisa na sua rede doméstica inicie a reprodução no seu PC. Ativa-o com uma caixa de seleção no separador Geral da caixa de diálogo de configurações. Os três papéis do plugin têm cada um a sua própria caixa de seleção lá - partilhar a minha biblioteca (Servidor), permitir que outros reproduzam para mim (Renderizador), e reproduzir para outros dispositivos (Ponto de Controlo) - e a caixa de diálogo só mostra os separadores de configurações que os papéis que ativou realmente precisam, para que nunca se depare com opções que não se aplicam a si.

Distinguir as suas máquinas: pode dar ao renderizador o nome que quiser (começa como "MusicBee (yaiol)"). Esse nome é o que aparece na lista de destinos de reprodução do seu telefone, então quando mais de um PC está a executar o MusicBee, pode saber qual é qual. Uma alteração de nome entra em vigor imediatamente, sem reiniciar.

Melhor som possível quando reproduz para si mesmo: quando navega na própria biblioteca do MusicBee a partir do seu telefone e envia uma faixa de volta para esse mesmo MusicBee, o plugin reconhece que lhe está a ser pedido para reproduzir um dos seus próprios ficheiros e simplesmente o reproduz diretamente do seu disco. O resultado é exato e instantâneo - bit-perfect, com o próprio equalizador e nivelamento de volume do MusicBee aplicados - em vez de, inutilmente, enviar o áudio para a rede e de volta para si mesmo.

Executá-lo de forma privada: os três papéis funcionam independentemente, então pode ativar o renderizador enquanto mantém a partilha de biblioteca desativada. Nessa configuração "apenas renderizador", a sua biblioteca permanece completamente oculta da rede - apenas o destino de reprodução é anunciado - e o MusicBee nunca se oferecerá para reproduzir para si mesmo.


Comportamento de reprodução

N02 - FLAC 5.1 não é automaticamente misturado para estéreo

O Quê: o limite de contagem de canais em MediaServerDevice.GetEncodedFile era If StereoOnly OrElse Not isPcmData Then channelCount = 2. A cláusula Not isPcmData misturava silenciosamente todas as transcodificações não-PCM (FLAC, MP3, AAC, Ogg) para estéreo, independentemente da contagem de canais da fonte, desativando renderizadores capazes de 5.1 quando a fonte era FLAC 5.1. Agora a segunda cláusula exclui FLAC: Not isPcmData AndAlso encoder.Codec <> FileCodec.Flac. FLAC 5.1 passa; MP3/AAC/Ogg ainda forçam estéreo porque os codificadores de linha de comando do MusicBee para esses formatos esperam entrada de 2 canais.

Porquê: o objetivo de transcodificar uma fonte FLAC 5.1 para saída FLAC é preservar a mistura multicanal. A mistura silenciosa tornava a opção de transcodificação FLAC inútil para audição surround. Com N02, faz a coisa certa.


Arquitetura

A mudança estrutural que torna o fork viável numa grande biblioteca - ausente do plugin original.

N03 - Árvore de navegação preguiçosa (sob demanda)

O Quê: o plugin original construía toda a árvore de navegação no arranque do MusicBee - enumerava cada faixa, Library_GetFileTags completo por ficheiro, montava toda a hierarquia de contentores - antes de abrir a porta HTTP. Numa biblioteca real (mais de 50 mil faixas, 5400 episódios de podcast, centenas de estações), isso são minutos de arranque a frio, e a árvore permanece na RAM para sempre, incluindo ramos que nenhum cliente nunca abre. Este fork não constrói nada antecipadamente: a raiz expõe um placeholder prefixado com L: por endpoint (L:music, L:podcast, L:filter:…); cada nível é calculado apenas quando um cliente navega nele (LazyBrowseEnsureLazyEndpointInMemory → caches por nível), e as notificações de alteração da biblioteca limpam as caches (SetLibraryDirty).

Porquê: o arranque a frio é essencialmente instantâneo - a porta HTTP está aberta quando o MusicBee termina a inicialização do plugin - e a memória permanece proporcional ao que foi navegado, não ao tamanho da biblioteca. Compromisso: a primeira navegação num endpoint paga o seu custo de carregamento; a reentrada é armazenada em cache até à próxima alteração da biblioteca. Esta é a base de tudo o resto. Notas completas: FIXES.md.


Rede e robustez

Reforço do caminho de ligação do servidor HTTP. O plugin original morre silenciosamente quando a sua porta está indisponível.

N04 - Ligação da porta HTTP com auto-reparação

O Quê: o servidor HTTP do plugin já não morre quando a sua porta configurada está indisponível. Três alterações ligadas:

  1. Auto-fallback em caso de falha de ligação. HttpServer.Start tenta a porta configurada e, em caso de SocketException, procura até 20 portas para a primeira livre. A porta realmente ligada é registada num novo Plugin.boundServerPort, e tudo o que anuncia o servidor - URLs SSDP LOCATION (NOTIFY + resposta M-SEARCH), o URL do dispositivo (PrimaryHostUrl), o encaminhamento de porta do router e os auto-filtros SSDP/ponto de controlo - agora lê boundServerPort em vez de Settings.ServerPort. Os clientes UPnP descobrem a porta real via SSDP, pelo que uma porta movida é transparente para os renderizadores.
  2. Notificação ao utilizador. Quando ocorre um fallback (a porta guardada não é a que está em uso), uma MessageBox localizada (WarnPortInUse) informa o utilizador qual a porta que está realmente a servir e que os dispositivos ainda a encontrarão - porque o plugin é executado sem interface gráfica e uma mensagem na caixa de diálogo só seria vista por alguém que já suspeitasse de um problema.
  3. Recuperação de reinício. RestartServer (o caminho de reinício de guardar configurações) costumava desreferenciar Plugin.controller / Plugin.server cegamente. Se a Initialise inicial lançasse uma exceção antes de os criar (exatamente o que uma ligação falhada causava), a próxima gravação de configurações atingia uma NullReferenceException - deixando um plugin meio morto. Agora recria-os e inicia-os quando Nothing, de modo que guardar uma porta funcional revive o plugin sem um reinício completo do MusicBee.

Porquê: o gatilho foi um incidente real de utilizador. A antiga porta predefinida 49382 reside no intervalo dinâmico do Windows (49152-65535), onde Hyper-V/WSL2/Docker/WinNAT reservam grandes blocos que mudam a cada arranque - então a ligação falhava com WSAEACCES ("acesso proibido") numa máquina onde tinha funcionado durante meses. Mudar a predefinição para uma porta livre colidia então com o Serviio (um servidor DLNA separado já na nova porta), falhando com WSAEADDRINUSE. Cada falha era engolida em Initialise, deixando o plugin silenciosamente morto e depois NRE-ing na próxima gravação de configurações. Após N04, uma colisão de porta auto-cura - o servidor continua a funcionar na próxima porta livre, o utilizador é informado, e os clientes redescobrem-no - em vez de derrubar todo o plugin.

Implementação:

  • A porta predefinida mudou de 49382 para 9779 (abaixo do intervalo dinâmico, para que o Windows nunca a reserve automaticamente; não é uma predefinição conhecida de servidor de mídia) em todas as três declarações de ServerPort + o fallback de falha de análise de configurações.
  • Plugin.boundServerPort (novo campo partilhado) contém a porta de escuta ativa; activeServerPort mantém o instantâneo configurado para que a lógica do distintivo "Reinício Necessário" não dispare falsamente num fallback.
  • HttpServer.PortScanRange = 20; a pesquisa para na primeira TcpListener.Start() bem-sucedida e lança a última exceção apenas se todas as tentativas falharem.
  • Nova chave de recurso EN WarnPortInUse (as traduções seguem a passagem de localidade no momento da publicação).

N05 - Anúncios SSDP através do grupo multicast (VPN / ponto a ponto)

O Quê: os anúncios SSDP são enviados para o grupo multicast UPnP (239.255.255.250) em vez de um endereço de transmissão IP. O erro inofensivo "não é possível aceder a um objeto descartado" registado quando uma resposta de pesquisa SSDP compete com um reinício do servidor também é suprimido.

Porquê: em adaptadores de rede ponto a ponto / VPN, a transmissão IP não se aplica - o antigo envio de transmissão falhava com "argumento inválido" e os anúncios eram perdidos, então o plugin era invisível para os clientes nessas ligações. Anunciar para o grupo multicast correto corrige a descoberta exatamente nesses adaptadores.

Navegação na biblioteca

Estas foram incluídas neste fork e não estão no plugin original. Resultaram da navegação real da saída do próprio plugin a partir de clientes UPnP reais.

N06 - Exposição da biblioteca baseada em filtros

O Quê: os separadores de filtro do MusicBee (ficheiros .xautopf na pasta do utilizador do MusicBee) tornam-se contentores raiz UPnP na biblioteca do plugin. As faixas de cada filtro são então navegáveis numa hierarquia de AlbumArtistSort → Album → Tracks.

Porquê: os utilizadores com filtros MusicBee curados (por exemplo, "Faixas 5 estrelas", "Adicionadas recentemente", "Clássica → Barroca") esperam encontrá-los ao navegar no plugin a partir de um cliente UPnP. O plugin original apenas expunha a árvore de biblioteca bruta.


N07 - Ligação do campo SortAlbumArtist

O Quê: o plugin agora lê o MetaDataType 165 (Sort Album Artist) do MusicBee e usa-o para agrupar/ordenar artistas nas visualizações de navegação.

Porquê: navegadores de alta fidelidade e audiófilos usam nomes de artistas de ordenação ("Beethoven, Ludwig van" em vez de "Ludwig van Beethoven") para organizar bibliotecas. Expectativa padrão para ouvintes sérios. Ausente em ambos os upstreams.


N08 - Tratamento de AlbumArtist com múltiplos valores

O Quê: quando o campo AlbumArtist de um álbum contém vários artistas separados por "; " (por exemplo, "yaiol; Ars Ricercata"), a faixa agora aparece sob cada artista nas visualizações de navegação, e não sob um único artista Frankenstein que combina os nomes.

Porquê: álbuns colaborativos e compilações precisam de aparecer sob cada colaborador. Sem isto, metade dos caminhos de pesquisa para encontrar o álbum estão quebrados.


N09 - Arte de capa do contentor do álbum (upnp:albumArtURI)

O Quê: os nós do contentor do álbum nas respostas de Navegação DIDL agora incluem um elemento upnp:albumArtURI que aponta para a capa do álbum.

Porquê: sem isto, cada álbum na vista de navegação de um cliente UPnP mostra um ícone genérico em vez da capa do álbum. Pista visual para navegação; esperado por todos os navegadores de alta fidelidade modernos.


N10 - Ordenação de faixas dentro de álbuns filtrados

O Quê: as faixas dentro de um álbum exposto por filtro são agora ordenadas por Número de Disco, e depois por Número de Faixa.

Porquê: ordem padrão do álbum. Sem ordenação explícita, as faixas eram devolvidas na ordem em que o filtro as retornava - geralmente parecendo aleatórias.


N11 - Correção da árvore de pastas de listas de reprodução

O Quê: a função LoadLibraryPlaylists (originalmente de Steven Mayall, ~2014) falhava ao descer para pastas de listas de reprodução recém-criadas. A primeira lista de reprodução em cada pasta, mais quaisquer subpastas, acabava órfã no nível raiz.

Porquê: presente no plugin original por onze anos. Visível em 30 segundos após abrir o BubbleUPnP e clicar em Listas de Reprodução. Corrigido no yaiol ao recursar corretamente em pastas recém-criadas durante a construção da árvore.


N12 - Sanitização de caracteres de controlo XML-ilegais

O Quê: qualquer faixa com uma etiqueta contendo um caractere de controlo C0 (por exemplo, 0x19 de uma má passagem de codificação - UTF-8 → Latin-1 → truncagem de 0x99 para 0x19) fazia com que toda a resposta de Navegação falhasse com Action Failed assim que a faixa defeituosa entrava num lote paginado.

Porquê: o XML 1.0 proíbe a maioria dos caracteres de controlo C0, e o XmlWriter lança uma exceção quando solicitado a escrever qualquer um deles. Presente no plugin original. Corrigido pela remoção de caracteres inválidos em cada ponto de saída de Library_GetFileTags via XmlConvert.IsXmlChar.


N13 - Listagem de rádio determinística em navegação paginada

O Quê: a navegação para o contentor de Rádio caía no ramo genérico de lista de ficheiros, que chamava files.Sort(AlbumFileComparer) em cada chamada. As entradas de rádio têm etiquetas de Álbum/Disco/Faixa vazias, então cada comparação retornava 0 - List(Of T).Sort é instável, produzindo uma ordem diferente a cada invocação. Os pontos de controlo UPnP paginam (BubbleUPnP busca 0..15 e depois 16..fim); entre as duas chamadas a lista era reordenada, então algumas estações apareciam em ambas as páginas (duplicados) e algumas em nenhuma (em falta) - parecendo aleatórias a cada atualização.

Porquê: presente no plugin original (o seu autor nunca navega rádio via UPnP). Corrigido aqui com um ramo ContainerCategory.Radio dedicado em Browse, sem ordenação por chamada; radioFiles é ordenado uma vez no momento do carregamento por Título (estável). A navegação paginada agora vê uma ordem determinística; a página 1 e a página 2 são disjuntas.


N14 - Pesquisa UPnP de classe de álbum retorna contentores de álbum

O Quê: a pesquisa UPnP para consultas de classe de álbum (upnp:class = "object.container.album.musicAlbum", por exemplo, "Álbuns Aleatórios" do BubbleUPnP) retornava a lista completa de faixas em vez de contentores de álbum, então o cliente mostrava zero álbuns. O manipulador original apenas analisava critérios entre parênteses, e depois descartava todas as faixas, independentemente da classe solicitada.

Porquê: corrigido aqui - as consultas de classe de álbum agora enumeram álbuns distintos (agrupados por Artista do Álbum + Álbum) e emitem cada um como um contentor musicAlbum adequado com arte de capa, endereçável através do espaço de ID virtual Salb<idx> para que o cliente possa aprofundar um resultado e reproduzi-lo.


N15 - Pesquisa UPnP funcional e com reconhecimento de escopo, com clique para navegar

O Quê: o original não anunciava capacidades de pesquisa (GetSearchCapabilities retornava vazio), então os clientes recusavam-se a sequer enviar uma Pesquisa; e o antigo backend lia de musicFiles, permanentemente vazio na era da árvore preguiçosa. Este fork anuncia as propriedades pesquisáveis reais, implementa faixas por título e álbuns por título contra a biblioteca preguiçosa (HandleLazySearch), delimita a consulta ao ramo atual do cliente quando um ID de contentor real é enviado (caso contrário, substitui L:music para que as pesquisas na barra superior não arrastem ruído de podcast/rádio/audiolivro), e torna os resultados de álbum clicáveis via IDs sintéticos Ssrch_alb_* que um ramo inicial de Browse mapeia de volta para as faixas do álbum. (A parte dos resultados de classe de álbum como contentores é N14.)

Porquê: a pesquisa no BubbleUPnP passou de "A biblioteca não suporta pesquisa" para retornar resultados úteis, delimitados e reproduzíveis. Design completo + abordagens rejeitadas: SEARCH.md.


N16 - Invalidação da cache UPnP (SystemUpdateID)

O Quê: o original retornava um SystemUpdateID=0 constante - o contrato de invalidação de cache do ContentDirectory UPnP - então clientes compatíveis com a especificação (BubbleUPnP) tratavam a biblioteca como nunca mudando: resultados de navegação desatualizados, miniaturas 404 após uma alteração de esquema de URL, e a dança de "reiniciar o MusicBee duas vezes para ver as alterações". Este fork inicializa SystemUpdateID a partir de segundos de época no carregamento (para que cada reinício esteja estritamente à frente do último) e o incrementa em cada mutação da biblioteca e alteração de configurações (SetLibraryDirty / ResetCacheBumpSystemUpdateId).

Porquê: os clientes detetam de forma fiável edições, novos ficheiros e alterações de configuração na sua próxima navegação. Limite conhecido: os clientes subscritos não recebem ativamente o novo valor via GENA (estacionado como trabalho futuro); eles ainda o veem na sua próxima navegação.


N17 - Arte de capa de subscrição de podcast

O Quê: os mosaicos de podcast não mostravam imagens - cada pedido /PodcastThumbnail/ resultava em 404. Dois bugs empilhados: a cadeia de resolução nunca verificava a cache de arte de capa real do MusicBee (%LocalAppData%\MusicBee\InternalCache\Subscriptions\<name>.jpg, de onde a UI de desktop carrega), e a camada HTTP de unescape+lowercase corrompia a chave de rota do URL do feed até ao seu último segmento de caminho. Este fork resolve a arte de capa da InternalCache do MB e encaminha as pesquisas via um slug seguro para URL que sobrevive intacto à camada HTTP (PodcastSlug / podcastSubIdBySlug).

Porquê: a arte de capa da subscrição agora é renderizada nas visualizações de navegação (todos os 22 pedidos que anteriormente resultavam em 404 são resolvidos).


N18 - Navegação hierárquica (delimitada) por tags

O Quê: qualquer campo pode ser marcado como hierárquico no separador Opções da Biblioteca e receber um delimitador de um caractere (um seletor de campo + caixa de delimitador com adicionar/remover, persistido nas configurações do plugin). Defina Agrupamento para / e um valor como Jazz/Cool Jazz então navega como Jazz › Cool Jazz em vez de uma entrada plana. As faixas marcadas exatamente num ramo (apenas Jazz) obtêm o seu próprio nó [Jazz] para que nada fique oculto, um ramo com um único filho colapsa por si só, e ; é recusado como delimitador porque é o próprio separador de múltiplos valores do MusicBee.

Porquê: taxonomias de tags profundas que um utilizador já codificou num único campo (árvores de géneros, hierarquias de humor, "Clássica/Barroca/Concerto") finalmente navegam como a árvore que a tag descreve, em vez de uma parede plana de strings separadas por barras que o utilizador tem de ler de ponta a ponta.


N19 - Caminho raiz único rotulado pelo seu campo de agrupamento

O Quê: um único caminho de navegação na raiz é rotulado pelo seu campo de agrupamento (por exemplo, "Género") em vez do seu caminho curto completo, correspondendo à forma como os grupos do primeiro campo mesclados são nomeados.

Porquê: a árvore de navegação é lida de forma consistente - uma regra de nomenclatura, quer uma entrada raiz esteja sozinha ou tenha sido mesclada com irmãos (N20) - em vez de uma entrada raiz solitária mostrar um caminho interno verboso enquanto os seus vizinhos mesclados mostram um nome de campo limpo.


N20 - Mesclar caminhos de navegação que partilham um primeiro campo

O Quê: dois caminhos de navegação que partilham o mesmo primeiro campo - "Género / Artista do Álbum de Ordenação" e "Género / Pessoas do Podcast" - colapsam numa única pasta raiz Género que lista os valores de género primeiro e depois se divide nas duas vistas, em vez de duas entradas "Género / ..." quase duplicadas lado a lado na raiz.

Porquê: um utilizador com várias vistas relacionadas aninhadas sob um campo comum via a raiz desordenada com entradas de nível superior quase idênticas. A fusão delas mantém a raiz legível e agrupa as vistas relacionadas onde elas pertencem - sob o seu campo partilhado.


N21 - Caminhos de navegação categorizados (Padrão / Rádio / Podcast)

O Quê: cada caminho de navegação é tipado por categoria - Padrão, Rádio ou Podcast. A lista de modelos é agrupada nessas três secções, o seletor de campo de cada modelo oferece apenas os campos que os dados dessa categoria podem realmente fornecer, e um modelo só pode ser aplicado a nós correspondentes na árvore de Visualização (nós incompatíveis ficam cinzentos e não podem ser selecionados). Os modelos reservados de Rádio e Podcasts não podem ser eliminados, então a sua secção de categoria nunca desaparece.

Porquê: sem a tipagem, um utilizador poderia construir um layout que silenciosamente aparecesse vazio - uma estação de rádio não tem "álbum", um episódio de podcast não tem "artista do álbum" - e só o descobriria navegando para uma pasta morta a partir de um cliente UPnP. Restringir o menu de campos e os alvos de aplicação aos dados reais da categoria torna os layouts vazios impossíveis de construir.


N22 - Agrupar podcasts por ano de publicação

O Quê: a data de publicação de cada episódio de podcast é lida, de modo que um caminho de navegação de podcast com um nível de Ano agrupa os episódios por ano em vez de os colapsar sob um único "Desconhecido".

Porquê: grandes subscrições de podcast tornam-se navegáveis por ano como o resto da biblioteca, em vez de cada episódio cair numa única pilha sem data porque o plugin nunca olhou para a data de publicação por episódio.


N23 - Colapsar níveis de agrupamento de resultado único

O Quê: um nível de agrupamento que resolve para um único valor - um nível de Tipo de Registo mostrando apenas "LP" para um artista que só fez LPs, ou um nível de letra com uma única letra - é automaticamente ignorado, levando o utilizador diretamente para o seu conteúdo.

Porquê: navegar por uma pasta que contém exatamente uma pasta é pura fricção. Colapsar o nível de escolha única remove o clique morto sem alterar o que o utilizador pode alcançar.


N24 - Agrupamento/pesquisa por ano em relação ao campo de data do MusicBee

O Quê: a condição de ano já não consulta o campo "Ano" de data completa do MusicBee com um valor de quatro dígitos simples, e o aliasing de campo de ano codificado está ausente, de modo que cada campo de agrupamento agora resolve genericamente a partir da definição do caminho.

Porquê: para bibliotecas cuja tag Ano contém uma data completa, agrupar ou pesquisar por ano anteriormente não retornava nada - a consulta de quatro dígitos nunca correspondia ao campo de data completa. Consultar o campo correto faz com que o agrupamento e a pesquisa por ano encontrem as faixas novamente.


N25 - Campos de agrupamento separados "Ano" e "Ano (aaaa)"

O Quê: os caminhos de agrupamento e navegação de álbuns agora expõem ambos os campos de ano do próprio MusicBee - Ano (a tag de data completa) e Ano (aaaa) (apenas o ano de quatro dígitos) - para que o utilizador possa escolher qualquer um ao definir um agrupamento de álbum ou um caminho de navegação.

Porquê: os dois campos significam coisas diferentes no MusicBee, e colapsá-los perdia essa distinção. Expor ambos permite que um utilizador reúna todos os lançamentos de um ano (aaaa) ou mantenha a ordenação por data exata (tag de Ano completa), conforme pretendido.


N32 - Filtros e listas de reprodução fixados agrupados por tipo na raiz

O Quê: um filtro fixado agora aparece diretamente abaixo da pasta Filtros na raiz de navegação, e uma lista de reprodução fixada diretamente abaixo da pasta Listas de Reprodução, em vez de todos os fixados se agruparem num único aglomerado no final da raiz. Cada atalho fixado fica com o seu próprio tipo.

Porquê: à medida que um utilizador fixa mais atalhos, um único aglomerado final de filtros e listas de reprodução mistos torna-se mais difícil de digitalizar e separa cada atalho da pasta a que pertence. Agrupar itens fixados sob a sua própria categoria mantém a raiz legível e mantém cada atalho próximo das coisas a que pertence.

Caixa de diálogo de configurações e empacotamento

N26 - Caixa de diálogo de configurações seccionada

O Quê: a página de Preferências recebeu um layout de navegação à esquerda com secções: Geral / Reprodução / Biblioteca / Perfis de Dispositivo / Diagnóstico.

Porquê: o original era uma única lista alta e plana de todas as configurações - bom para o desenvolvedor que a construiu, confuso para todos os outros. O seccionamento agrupa opções relacionadas e faz com que a caixa de diálogo pareça mais com as configurações de aplicações modernas.


Renomeação do Assembly + plugin (sem F-id - nota de empacotamento)

O Quê: a DLL compilada é nomeada mb_UPnP_yaiol.dll e o plugin reporta-se como "MusicBee UPnP (yaiol)". Distinto do original mb_Upnp.dll.

Porquê: os utilizadores podem instalar o yaiol juntamente com o plugin original e comparar o comportamento lado a lado.


Sistema de distintivos - exibindo o estado de execução (mecanismo por trás de F40)

O Quê: um padrão de UI genérico para exibir condições importantes de tempo de execução como distintivos coloridos visíveis na caixa de diálogo de Configurações. Instâncias atuais:

  • ⚠ Max Conn (N04) - dispara quando o limite máximo de conexões foi atingido pelo menos uma vez desde que o MusicBee iniciou. Flag de sessão persistente Plugin.MaxConnectionsHit. Definido dentro de WaitOnSendBarrier quando não há slot livre.
  • ⚠ Reinício Necessário - dispara quando uma configuração guardada precisa de um reinício do MusicBee para ter efeito. Flag de sessão persistente Plugin.RestartRequired. Definido no manipulador de Guardar da caixa de diálogo quando o novo valor persistido difere do instantâneo de tempo de execução (Plugin.activeMaxConnections, Plugin.activeServerPort, Plugin.activeIpAddress). As configurações que exigem reinício são limitadas àquelas que genuinamente não podem ser recarregadas a quente - parâmetros de ligação do servidor HTTP e o SemaphoreSlim construído uma vez na Inicialização.

Porquê: o ficheiro de registo do plugin é bom para utilizadores técnicos que depuram, mas um utilizador não técnico a olhar para "o dispositivo soa mal" ou "a reprodução está lenta" nunca abrirá Diagnóstico → Ver registo. Os distintivos capturam os casos em que o utilizador precisa de saber que algo aconteceu e os exibem na próxima vez que abrirem o plugin - detetáveis sem ler nada.

Reutilizável para o futuro:

  • Incompatibilidade de perfil detetada (o user-agent do dispositivo nunca correspondeu a nenhum perfil, voltou para Genérico).
  • Recuo de NextURI acionado (F13 - sem interrupções desativado para a sessão num dispositivo instável).
  • Análise da biblioteca falhou / parcial.
  • Conexão do renderizador perdida a meio da sessão.
  • Qualquer outra condição em que "aconteceu uma vez, o utilizador deve saber" supera "registado silenciosamente entre 1000 outras linhas".

Convenções de implementação:

  • Os rótulos dos distintivos vivem ao nível da caixa de diálogo (não dentro de nenhum painel) para que sejam visíveis independentemente da secção em que o utilizador se encontra.
  • Posicionados na linha inferior perto de Guardar/Cancelar (atual: y=410 empilhados horizontalmente).
  • Cada distintivo tem uma flag de sessão persistente correspondente em Plugin que muda para Verdadeiro quando a condição ocorre e só é reiniciada no reinício do MusicBee.
  • Recursos: <Condition>Badge (texto do rótulo, prefixado com ⚠) + <Condition>BadgeTip (tooltip explicando a causa + remédio).
  • Para distintivos "configuração guardada precisa de reinício", tire um instantâneo de tempo de execução em Plugin.Initialise() e compare com Settings.* após Settings.SaveSettings() no manipulador de Guardar da caixa de diálogo.

N27 - Cancelar descarta edições de caminho/modelo

O Quê: as edições feitas em caminhos e modelos dentro da caixa de diálogo de configurações são agora descartadas quando o utilizador clica em Cancelar, em vez de permanecerem silenciosamente aplicadas, e qualquer modelo reservado removido durante a sessão é recriado. (Os modelos, de outra forma, são guardados em tempo real à medida que são editados - não há um botão Guardar separado no separador Caminhos.)

Porquê: Cancelar deve significar cancelar. Anteriormente, um utilizador que experimentava alterações de caminho/modelo e voltava atrás descobria que as alterações já estavam confirmadas, sem forma de as desfazer a não ser refazendo cada uma manualmente.


N28 - E comercial literal no menu do seletor de campo

O Quê: um campo cujo nome contém "&" - por exemplo, "Humor & Contexto" - renderiza o e comercial literalmente no menu do seletor de campo em vez de o engolir como um prefixo mnemónico Alt.

Porquê: nomes de campos com um e comercial eram exibidos incorretamente (o caractere desaparecia e a próxima letra tornava-se um acelerador), tornando a entrada do menu difícil de reconhecer.


N29 - Título da janela de configurações estável e não traduzido

O Quê: o título da janela de configurações é fixado à string da marca "MusicBee UPnP Plugin" e já não varia com o idioma da interface; a string DialogTitle por idioma foi removida de todos os pacotes de localização.

Porquê: um título de janela que mudava a redação por idioma era uma superfície traduzível sem benefício - o título é uma marca da marca. Fixá-lo mantém-no estável e consistente em todo o lado.

Localização

O plugin original é apenas em inglês. Este fork é totalmente localizável - cada string voltada para o utilizador flui através de um pacote de recursos, e o plugin deteta automaticamente o idioma da UI do próprio MusicBee.

N30 - UI multi-idioma (traduções pendentes)

O Quê: o mecanismo de localização está completo e a ser lançado. Localisation.vb lê o idioma selecionado do MusicBee de MusicBee3Settings.ini (endónimo <SystemLanguage>) e aplica a cultura .NET correspondente ao thread, para que My.Resources.Resources.* retorne a string localizada. Cada rótulo/botão/mensagem voltado para o utilizador está ligado a uma chave de recurso (controles do designer via ApplyDesignerExtras + sync-en-locale.js; strings de tempo de execução como WarnPortInUse adicionadas manualmente). O que não está feito ainda é a tradução real: apenas o pacote de origem em inglês (Resources.resx) existe - os pacotes satélite para os outros idiomas são produzidos num único lote quando o plugin estiver completo em termos de funcionalidades (traduzir por partes enquanto as strings ainda estão a mudar desperdiça esforço).

Idiomas alvo (o conjunto que o próprio MusicBee oferece, correspondido 1:1 por endonymToCulture para que o plugin siga o idioma do MusicBee automaticamente):

Árabe (ar) Checo (cs) Alemão (de) Grego (el)
Espanhol (es) Francês (fr) Húngaro (hu) Italiano (it)
Coreano (ko) Holandês (nl) Norueguês (nb) Polaco (pl)
Português BR (pt-BR) Português PT (pt-PT) Sueco (sv) Turco (tr)
Ucraniano (uk) Russo (ru) Japonês (ja) Chinês Simplificado (zh-CN)
Chinês Tradicional (zh-TW) Inglês (en, fonte)

Política de variantes (de acordo com a regra de localidade do espaço de trabalho): PT e ZH são divididos em pacotes distintos porque o vocabulário/script realmente diverge (pt-BR/pt-PT, zh-CN/zh-TW). EN é um único pacote - "English(US)" (en-US) do MusicBee volta para en através da cadeia de cultura do .NET, então nenhum pacote US separado é produzido. ES e FR são igualmente de localidade única.

Porquê: as configurações de um plugin UPnP ("não usar PCM bruto", "forçar PCM little-endian", avisos de fallback de porta) são suficientemente crípticas na língua nativa. Seguir o idioma da própria UI do MusicBee - em vez de forçar o inglês - é a diferença entre uma ferramenta que um utilizador não-inglês pode configurar e uma que não pode. Nenhuma das versões anteriores tentou isto.


N31 - O link de ajuda abre na linguagem de interface completa

O Quê: abrir o link de Ajuda do plugin respeita a linguagem de interface completa do utilizador (por exemplo, pt-BR, zh-CN) em vez de colapsar para a linguagem base, e envia um identificador de verificação de atualização mais claro.

Porquê: um utilizador a executar o MusicBee numa variante regional (Português do Brasil, Chinês Simplificado) era enviado para a página de ajuda na linguagem base. Levar a cultura completa leva-o à página de ajuda na linguagem exata que utiliza.

Correções e melhorias ao plugin original

Protocolo principal e reprodução

F01 - Perfis de dispositivo DLNA predefinidos atualizados

O Quê: inclui novos perfis predefinidos para PlayStation 4, Xbox 360/One e BubbleUPnP moderno, com flags de capacidade (taxas de amostragem, profundidades de bits, codecs) que refletem o que esses dispositivos realmente suportam hoje.

Porquê: os padrões do plugin original estavam congelados por volta de 2014. PS4/Xbox/BubbleUPnP ganharam desde então suporte a áudio de alta resolução. Fora da caixa, uma nova instalação reproduz o melhor da classe nestes dispositivos sem que o utilizador precise de tocar nas configurações do perfil do dispositivo.


F02 - Controlar dispositivos que anunciam MediaRenderer:3

O Quê: o plugin sonda a descrição do serviço UPnP de um renderizador para decidir se o MusicBee pode controlá-lo. O original apenas correspondia a urn:schemas-upnp-org:device:MediaRenderer:1. Dispositivos modernos anunciam :2 ou :3. F02 alarga a correspondência.

Porquê: sem isto, unidades Sonos / WiiM / Eversolo recentes simplesmente não aparecem como alvos na lista de dispositivos "Reproduzir Para" do MusicBee - mesmo que falem o mesmo protocolo. Uma única correção de correspondência de prefixo de string desbloqueia toda a geração de dispositivos modernos.


F03 - Opção "Forçar fluxo nativo" por perfil (predefinição LIGADO)

O Quê: quando selecionado, o plugin envia os bytes do ficheiro original para o dispositivo sem transcodificação, DSP, ou processamento ReplayGain aplicados. Apenas o ficheiro bruto que o utilizador escolheu, byte a byte (módulo de enquadramento HTTP).

Porquê: por testemunho do fórum, esta é a maior vitória em qualidade de reprodução. Utilizadores de alta fidelidade que compram renderizadores caros querem explicitamente uma saída bit-perfect; qualquer toque de DSP anula o objetivo. Predefinição LIGADO porque a maioria dos dispositivos modernos lida com qualquer codec que o utilizador lhes envie, e ReplayGain/EQ deve ser opcional. É por perfil para que possa manter a transcodificação para uma Xbox antiga enquanto envia nativo para um DAC de alta fidelidade.


F04 - "Forçar transcodificação" por perfil

O Quê: substituição por perfil que força cada fluxo para este dispositivo através do transcodificador, independentemente do suporte de codec nativo. O inverso de F03 (ForceNativeStream). Mutuamente exclusivo com F03 - a UI desmarca automaticamente o outro quando um é ativado.

Porquê: um único interruptor global seria contraditório com o ForceNativeStream por perfil (F03). Caso real: o dispositivo A é um DAC de alta fidelidade que quer fluxos nativos bit-perfect; o dispositivo B é um recetor AV antigo que engasga com FLAC. Com um interruptor global, o utilizador tem de escolher - à custa do outro dispositivo. Com por perfil, cada dispositivo obtém a resposta certa.

Implementação:

  • StreamingProfile.ForceTranscoding As Boolean = False.
  • Esquema de persistência atualizado para v9. Ficheiros pré-v9 carregam o valor global legado uma vez e copiam-no para todos os perfis, preservando o comportamento antigo através da atualização.
  • UI: removido do painel de Diagnóstico, adicionado à secção Perfis de Dispositivo ao lado de ForceNativeStream. Manipuladores de exclusão mútua bidirecionais (CheckedChanged em cada um desinscreve o outro antes de alternar, para evitar um loop infinito).
  • Local de decisão: Settings.ForceTranscodingstreamingProfile.ForceTranscoding em WriteAudioFileDIDL.

F05 - "Forçar PCM little-endian" por perfil

O Quê: os fluxos PCM (tipos mime L16/L24) são big-endian por especificação. Alguns dispositivos esperam erroneamente little-endian e reproduzem ruído branco quando recebem dados big-endian corretos. F05 alterna a ordem dos bytes por perfil.

Porquê: sem isto, certos dispositivos produzem uma parede de estática. O sintoma é dramático e a causa invisível sem conhecimento da codificação PCM - o interruptor dá aos utilizadores uma correção por tentativa e erro.


F06 - "Não usar PCM Bruto" por perfil

O Quê: quando o dispositivo afirma que suporta PCM bruto, o plugin usa isso. Alguns dispositivos mentem - aceitam o handshake SOAP mas corrompem os dados PCM brutos reais, enquanto lidam corretamente com PCM encapsulado num contentor WAVE. F06 força PCM-over-Wave independentemente do que o dispositivo anuncia.

Porquê: certos modelos Marantz especificamente - eles anunciam PCM bruto mas apenas WAVE funciona. Sem isto, os fluxos PCM brutos saem distorcidos, sem nenhuma mensagem de erro para apontar.


F07 - "Comprimento do conteúdo" por perfil

O Quê: qual valor enviar no cabeçalho HTTP Content-Length. Quatro opções:

  • Predefinição - contagem real de bytes quando conhecida, omitir quando desconhecida.
  • Nenhum - nunca enviar o cabeçalho (apenas codificação em blocos).
  • Apenas PCM - enviar apenas para PCM bruto; omitir para tudo o resto.
  • Fixo - enviar UInt32.MaxValue - 8192 (um sentinela para "comprimento desconhecido enorme").

Porquê: os dispositivos UPnP/DLNA variam muito na forma como reagem ao Content-Length. Alguns precisam de um número exato, alguns odeiam-no em fluxos, alguns precisam de um valor sentinela "muito grande" para manter o buffering. Isto foi posteriormente alargado de apenas PCM para todos os formatos de saída porque os mesmos problemas apareceram em fluxos MP3/AAC transcodificados.


F08 - "Não limpar NextURI" por perfil

O Quê: normalmente o plugin limpa o NextURI em fila do dispositivo quando a fila esvazia (enviando SetNextAVTransportURI com um URL em branco). Alguns dispositivos (notavelmente Denon) interpretam um NextURI em branco como "parar tudo" e interrompem a reprodução imediatamente. F08 impede que o plugin o limpe.

Porquê: sem isto, os proprietários de Denon experimentam o dispositivo a cortar a meio da faixa quando a fila esvazia. Com F08 selecionado, o dispositivo mantém o NextURI obsoleto na memória (inofensivo - apenas é sobrescrito na próxima vez que algo é colocado em fila).


F09 - FLAC como formato de saída de transcodificação

O Quê: o menu pendente de formato de transcodificação em Perfis de Dispositivo agora oferece FLAC juntamente com PCM 16/24, MP3, AAC, Ogg. Escolhê-lo encaminha o codificador BASS através da linha de comando de conversão FLAC padrão do MusicBee (o mesmo mecanismo que MP3/AAC/Ogg já usam).

Porquê: para dispositivos que lidam bem com FLAC mas não conseguem descodificar o codec de origem (por exemplo, um Eversolo a receber a biblioteca WMA do MusicBee convertida para FLAC), isto preserva a qualidade sem perdas onde MP3/AAC descartariam dados de áudio. Desbloqueia N02 (controlo de downmix 5.1), que não podia ser abordado sem uma opção de transcodificação sem perdas.

Implementação: adição de uma linha ao bloco Select Case Codec de Encoder.StartEncode - FLAC junta-se a MP3/AAC/Ogg no ramo acionado por linha de comando. O menu pendente da UI recebe "FLAC" como 6ª opção. O mapeamento de carregamento/gravação em SettingsDialog estende-se para reconhecer FileCodec.FlacSelectedIndex = 5. Mime, tipo DLNA e funcionalidade de codificação já estavam ligados em ItemManager.GetMimes / GetDlnaType / GetEncodeFeature de trabalho anterior (F21, F26).


Sem interrupções (SetNextAVTransportURI)

F10 - SetNextAVTransportURI / NextURI core

O Quê: reprodução verdadeiramente sem interrupções. Quando o dispositivo anuncia suporte para SetNextAVTransportURI na sua descrição de serviço UPnP, o plugin pré-coloca a próxima faixa na fila do dispositivo antes que a atual termine. O dispositivo faz a transição internamente sem interrupção audível entre as faixas - o que se ouve num leitor de CD. Isto não é o hack de "fluxo contínuo" (que concatena tudo num longo fluxo e perde metadados por faixa).

Porquê: a funcionalidade Tier-2 principal. Álbuns gravados como uma performance ao vivo contínua (discos ao vivo, movimentos clássicos, sets de DJ) soam errados quando há um silêncio de meio segundo entre as faixas. Resolver isto corretamente é uma funcionalidade principal, agora neste fork.

Notas: o áudio em fila é servido através do servidor HTTP do plugin usando streamHandle=0 (modo de busca de biblioteca), o que significa que o motor de áudio do MusicBee não está envolvido na faixa em fila. Compromisso: os efeitos ReplayGain/DSP/EQ não se aplicam à próxima faixa. Aceitável quando "forçar fluxo nativo" está ativado (o padrão).


F11 - "Desativar suporte NextURI" por perfil

O Quê: mesmo que um dispositivo anuncie SetNextAVTransportURI, esta caixa de seleção força o plugin a ignorar esse anúncio e a voltar para a reprodução de uma faixa de cada vez.

Porquê: alguns dispositivos anunciam NextURI mas têm uma implementação com bugs (falhas, transições incompletas, bloqueios). Em vez de fazer engenharia inversa de cada dispositivo avariado, o utilizador obtém um interruptor "basta desligá-lo aqui".


F12 - Ciclo de vida do NextURI na lista de reprodução atual

O Quê: quando o MusicBee dispara NowPlayingListChanged, o plugin reavalia o que deve ser colocado em fila para transição sem interrupções. Ele pede ao MusicBee a nova faixa "seguinte" via NowPlayingList_GetNextIndex(1) + NowPlayingList_GetListFileUrl, compara com o que está atualmente em fila no dispositivo (rastreado via o novo campo nextPlaySourceUrl), e coloca novamente em fila se tiver mudado (ou limpa a fila se o MusicBee disser que não há próxima faixa).

Porquê: sem F12, o dispositivo continuava a reproduzir um NextURI obsoleto quando o utilizador removia/reordenava a faixa em fila. Isto historicamente levou várias iterações porque cada mutação de lista precisa de tratamento diferente - nós simplificamos confiando em NowPlayingList_GetNextIndex (que já respeita a reprodução aleatória e a repetição de tudo), então todas as variantes são canalizadas através da mesma comparação.

Implementação:

  • Novo campo nextPlaySourceUrl armazena o URL da biblioteca MusicBee do que está em fila (o URL de streaming com sufixo de identificador não é comparável a um caminho de biblioteca).
  • Nova Public Sub RefreshQueuedNextUri() em MediaRendererDevice. Três resultados: nenhum NextURI em fila → sem operação; em fila corresponde ao novo "próximo" → sem operação; em fila difere → chamar QueueNext com o novo URL (ou QueueNext("") para limpar - o que respeita F08 DoNotClearNextUri).
  • Ligado em Plugin.ReceiveNotification sob NotificationType.NowPlayingListChanged.

F13 - Recuo em caso de falha do NextURI

O Quê: após 4 falhas consecutivas de SetNextAVTransportURI no mesmo dispositivo, o plugin desativa a reprodução sem interrupções para esse dispositivo até que o MusicBee seja reiniciado.

Porquê: se um dispositivo estiver realmente avariado para NextURI (falhas SOAP intermitentes, falhas de rede), o plugin continuaria a tentar em cada faixa. F13 para o ruído e volta para a reprodução de uma faixa de cada vez silenciosamente.


F14 - Modo de repetição + integração NextURI

O Quê: F14 divide-se em dois casos tratados no detetor de transição F15 em OnAvTransportStatusCheck:

  • Repetir Tudo: O MusicBee passa o URL "wrap" correto (faixa 1 no fim da lista) para Plugin.QueueNext ele próprio. Não é necessária lógica especial do plugin - o dispositivo transita para ele e o detetor F15 chama Player_PlayNextTrack como de costume, que envolve o índice NPL do MusicBee de volta para 0.
  • Repetir Um: O MusicBee passa o MESMO URL da faixa para Plugin.QueueNext. O dispositivo transita para ele (novo identificador de fluxo, mesma fonte). O detetor F15 agora consulta Player_GetRepeat() - se for RepeatMode.One, ele ignora a chamada Player_PlayNextTrack para que o MusicBee não avance o índice NPL para longe da faixa em loop.

Porquê: sem o salto de Repetir Um, chamar Player_PlayNextTrack na transição sem interrupções avançaria o MusicBee para a próxima faixa na lista (Repetir Um afeta apenas o comportamento de avanço automático no final da faixa na UI do reprodutor - Próxima Faixa sempre avança), contradizendo o que Repetir Um significa.

Advertência de contagem de reproduções: em Repetir Um, o incremento da contagem de reproduções depende do MusicBee 3.7.9563+ notar a reprodução em loop. Versões mais antigas do MusicBee reproduzem a repetição sem interrupções corretamente, mas perdem o aumento da contagem de reproduções. Documentado; não bloqueador.


F15 - Máquina de estados de deteção de transição de faixa

O Quê: quando o dispositivo transita internamente da faixa atual para o NextURI, o plugin precisa de notar e dizer ao MusicBee para avançar o seu índice de reprodução atual. Caso contrário, o MusicBee pensa que ainda está na faixa anterior e as contagens de reprodução / UI / scrobbling ficam dessincronizadas.

Implementação: consulta GetPositionInfo.TrackURI a cada tick do temporizador de estado. Quando o URI reportado corresponde ao que colocamos em fila via NextURI, chamamos Player_PlayNextTrack no MusicBee e definimos suppressNextSoapCall para que o PlayToDevice resultante não reenvie SetAVTransportURI (o que interromperia a reprodução sem interrupções).

Porquê: sem F15, o dispositivo reproduz a próxima faixa, mas a UI do MusicBee diz que ainda está na anterior. Confuso, quebra o scrobbling, quebra o rastreamento da contagem de reproduções. A deteção de transição de faixa precisa de uma iteração longa, por renderizador, porque cada marca de renderizador tem as suas próprias peculiaridades em quando reporta a alteração do URI (alguns reportam TRANSITIONING primeiro, alguns saltam diretamente para PLAYING com novo URI, alguns têm um breve STOPPED entre eles).

Notas: o nosso primeiro corte funciona no renderizador BubbleUPnP. Os casos extremos por dispositivo permanecem em B6.


F16 - Correção de "pop" na transição sem interrupções

O Quê: o "pop" acontece quando o formato da fonte (taxa de amostragem / canais / codec) da faixa em fila difere da faixa atualmente em reprodução, forçando o DAC do dispositivo a re-sincronizar na transição. F16 adiciona um diagnóstico NextUri:FormatChange que dispara no momento da fila sempre que os formatos diferem, nomeando ambos os lados - para que os utilizadores que ouvem "pops" possam correlacionar.

O diagnóstico também aponta para a mitigação: selecione ForceTranscoding no perfil do dispositivo. Isso homogeneiza todas as faixas para um único codec/taxa de amostragem/profundidade de bits de transcodificação, eliminando completamente a diferença de formato da fonte.

Porquê adiado para a correção real de transcodificação para correspondência: a correção estrutural (transcodificar a faixa em fila para corresponder ao formato da faixa em reprodução) requer alterações no esquema de URL do servidor HTTP do plugin - atualmente /encode/{id}0.{ext} serve o ficheiro em fila nativamente. Uma futura v2 de F16 adicionaria rotas /encode/{id}0_{rate}_{depth}.{ext} por formato e as ligaria através do codificador. Essa é uma mudança arquitetónica maior que vale a pena fazer se um dispositivo real exibir o "pop" depois que o ForceTranscoding não for suficiente.

Implementação hoje:

  • O campo lastSourceUrl rastreia o URL da fonte atualmente em reprodução.
  • QueueNextFilePropertyType.SampleRate/Channels/Kind para as faixas atual e em fila e regista NextUri:FormatChange em caso de incompatibilidade.

F17 - Ressincronização da barra de progresso após busca

O Quê: a função Seek() já chamava GetPlayPositionInformation() após um SOAP de Busca bem-sucedido, o que corrige o caso de "nenhuma ressincronização". F17 fecha o desvio restante de até 1s causado pela quantização de 1 segundo de RelTime do UPnP: quando a posição reportada do dispositivo arredonda para dentro de 1 segundo do alvo solicitado pelo utilizador, o plugin agora confia no valor sub-segundo preciso do utilizador em vez do truncamento do dispositivo. Apenas quando o dispositivo reporta algo dramaticamente diferente (>1s de desvio) usamos o seu valor (a busca aterrou noutro local do que o pedido, por exemplo, snap-to-keyframe em alguns codecs).

Porquê: sem isto, procurar 2:30.500 ancorado contra o relatório "2:30" do dispositivo faria com que a barra de progresso mostrasse ~500ms atrás da realidade. Após F17, a barra corresponde à intenção do utilizador para o caso comum de arrasto na faixa, e ainda respeita o relatório do dispositivo para o caso atípico de snap-to-keyframe.


F18 - Interbloqueio de fluxo contínuo / NextURI

O Quê: dois interbloqueios agora em vigor:

  1. Tempo de execução: QueueNext retorna False antecipadamente quando Settings.ContinuousOutput está ativado. O fluxo contínuo é o seu próprio mecanismo sem interrupções (um longo fluxo concatenado); enviar SetNextAVTransportURI por cima dele confunde o dispositivo sobre se cada faixa é um URI discreto ou parte do fluxo contínuo.
  2. UI: quando o utilizador seleciona a caixa de seleção global de fluxo contínuo, o forceNativeStream do perfil atualmente exibido é automaticamente desmarcado. O fluxo contínuo sempre transcodifica, então o fluxo nativo forçado é sem sentido em combinação.

Porquê: impede que o utilizador ative dois mecanismos sem interrupções conflitantes simultaneamente. Sem F18, o dispositivo receberia um URI de fluxo contínuo E um NextURI para cada faixa subsequente, com comportamento indefinido dependendo do renderizador.


F19 - Erros de NextURI em branco ignorados

O Quê: quando SetNextAVTransportURI é chamado com um URL em branco (por exemplo, última faixa na lista), alguns dispositivos retornam uma falha SOAP. F19 engole-as silenciosamente - registadas mas não propagadas como erros.

Porquê: a condição "sem próxima faixa" é normal, não um erro. Tratá-la como fatal polui o registo e (em alguns fluxos) aciona tempestades de repetição.


Tipos MIME e metadados DLNA

F20 - Tipo MIME MP3 → audio/mpeg

O Quê: o tipo MIME MP3 em conformidade com os padrões é audio/mpeg, não audio/mp3. Este último é um nome incorreto comum que a maioria dos dispositivos tolera, mas renderizadores mais rigorosos rejeitam-no.

Porquê: corrige silenciosamente a reprodução em dispositivos mais rigorosos que seguem o padrão. A base de código yaiol já tinha isto correto; nenhuma alteração necessária.


F21 - Ordem dos tipos MIME: variante não-x- primeiro

O Quê: quando um dispositivo anuncia audio/flac e audio/x-flac, o plugin retorna a variante não-x- primeiro. O mesmo para qualquer codec com mimes padrão e experimentais.

Porquê: o prefixo x- marca mimes experimentais/não oficiais. Alguns renderizadores comportam-se melhor com o formato padrão. Pequena reordenação, impacto no mundo real.


F22 - Suporte para tipo MIME Opus

O Quê: reconhece Opus como um codec de áudio transmitível; envia o tipo MIME audio/opus ao servir faixas Opus.

Porquê: Opus é agora comum (codec de compromisso moderno para fala/música). Sem F22, o plugin recusaria transmitir ficheiros Opus mesmo para dispositivos que os suportam.


F23 - Suporte para ficheiros de origem Monkey Audio (APE)

O Quê: reconhece ficheiros .ape como um codec de origem válido para streaming/transcodificação.

Porquê: APE é um formato sem perdas com uma base de utilizadores de nicho, mas leal. Adicioná-lo custa pouco e desbloqueia a biblioteca para esses utilizadores.


F24 - Fallback de tipo MIME AAC / ALAC

O Quê: se um dispositivo suporta AAC ou ALAC mas não os anuncia explicitamente na sua descrição de serviço UPnP, o plugin oferece-os de qualquer forma como um fallback.

Porquê: vários dispositivos que lidam bem com AAC esqueceram-se de o listar no seu XML de capacidades. Sem F24, o plugin nem tentaria, forçando a transcodificação. Com F24, o plugin tenta e deixa o dispositivo lidar com ele nativamente se puder.


F25 - Flag de tipo DLNA para fluxos WAV nativos + codificados

O Quê: a flag de tipo DLNA (um identificador de perfil como LPCM, WAVE, MP3) precisa de corresponder ao que o dispositivo recebe. F25 garante que os fluxos nativos e os fluxos WAV codificados são sinalizados corretamente.

Porquê: o tipo DLNA incompatível faz com que alguns dispositivos recusem a reprodução completamente ou apliquem o descodificador errado.


F26 - Cabeçalho DLNA para ficheiros FLAC

O Quê: os fluxos FLAC recebem o identificador de perfil DLNA adequado nos seus cabeçalhos.

Porquê: sem ele, alguns dispositivos que suportam FLAC não reconhecem o fluxo como tal.


F27 - Correção do cálculo da taxa de bits nos metadados

O Quê: o res@bitrate do fluxo contínuo estava a ser calculado como (sampleRate * channels * bitsPerSample) / 1000 - kbps, com um erro de um fator de ~125 em relação à especificação UPnP DIDL que define o atributo como bytes por segundo. Agora divide por 8 em vez de 1000.

Porquê: exibição de taxa de bits errada no dispositivo - cosmético na maioria dos renderizadores, mas alguns alocam buffers de fluxo a partir do valor e gaguejam em fluxos que parecem ~125× menores do que são. O caminho do ficheiro de origem não contínuo já tinha isto correto ((bitrate_kbps * 1000) \ 8 = bytes/seg); apenas o caminho do fluxo contínuo estava errado.


F28 - Correção do formato de tempo dos metadados (Marantz)

O Quê: res@duration em DIDL era formatado como H:MM:SS (por exemplo, 0:03:42). A especificação UPnP DIDL define o formato como H+:MM:SS[.F+] - estritamente com segundos fracionários opcionais mas recomendados; alguns dispositivos Marantz tratam o formato simples como inválido e deixam a sua exibição de duração em branco. Agora formatado como H:MM:SS.fff (por exemplo, 0:03:42.000).

Porquê: problema de exibição específico de uma marca; o formato ISO-style conforme com segundos fracionários corrige-o sem afetar qualquer outro dispositivo. Aplicado em ambos os locais de emissão DIDL (caminho do ficheiro de origem + caminho do fluxo codificado em WriteAudioFileDIDL).

Correção bónus na mesma passagem: pv:addedTime e pv:lastPlayedTime estavam a usar hh (relógio de 12 horas) nas suas strings de formato DateTime em vez de HH (24 horas). Qualquer faixa adicionada ou reproduzida entre as 13:00 e as 23:59 seria renderizada com uma hora errada (por exemplo, 17:42 → "05:42") em dispositivos que exibem o campo. Agora usa HH.


F29 - Suporte a busca em MP3 codificado (CBR)

O Quê: os fluxos MP3 transcodificados agora anunciam DLNA.ORG_OP=11 (busca por byte e por tempo) em vez de DLNA.ORG_OP=10 (apenas busca por byte). Dispositivos que anteriormente recusavam a busca por tempo em MP3 transcodificado agora podem controlar a sua barra de progresso/UI de busca normalmente.

Porquê: o transcodificador do MusicBee produz MP3 de taxa de bits constante no preset HighQuality, então o mapeamento byte ↔ tempo é linear - o dispositivo pode converter um pedido de busca por tempo para uma busca por byte de HTTP Range sem qualquer suporte do lado do codificador. Anunciar OP=11 desbloqueia essa UI no dispositivo. Sem F29, os utilizadores que procuravam dentro de um MP3 transcodificado tinham a busca silenciosamente ignorada ou eram levados para o início da faixa.

Implementação: reestruturado GetEncodeFeature em ItemManager.vb para quebrar o If inline numa cadeia If/ElseIf/Else legível. MP3 obtém OP=11 explicitamente; outros codecs não-PCM mantêm OP=10. Nenhuma alteração para AAC/FLAC/etc. - esses precisariam de verificação específica do codec de CBR-ness que o MusicBee não garante.


F30 - Extensão de ficheiro .mpeg tratada

O Quê: ficheiros com extensão .mpeg (e o ainda mais raro .mpe) são agora reconhecidos como FileCodec.Mp3 em GetCodec. Antes de F30, retornavam FileCodec.Unknown e eram silenciosamente rejeitados da biblioteca / incapazes de serem fontes de transcodificação.

Porquê: arquivos antigos MPEG-1 Layer 3 às vezes usavam .mpeg em vez de .mp3 (a especificação permite ambos). Um punhado de ficheiros numa biblioteca de 300k é suficiente para sentir "o MusicBee mostra-os, mas o plugin não" - confuso para o utilizador.


Comportamento de reprodução

F31 - Streams de rádio usam automaticamente o modo contínuo

O Quê: WriteAudioFileDIDL agora sonda a propriedade Kind do URL de origem via Library_GetFileProperty e trata qualquer ficheiro cuja Kind termine em "Stream" (MusicBee reporta "MP3 Stream", "Internet Stream", etc. para rádio) como contínuo, independentemente do interruptor global Settings.ContinuousOutput. O ramo DIDL de fluxo contínuo (Título: "Fluxo Contínuo", id="continuousstream", saída PCM/Wave fixa) é usado; o dispositivo vê um único fluxo de estilo infinito.

Porquê: os streams de rádio não têm limites de faixa, comprimento fixo, nem busca. Tratá-los como ficheiros discretos no DIDL fazia com que o plugin anunciasse intervalos de bytes e durações que não existem. A mudança automática quando o MusicBee já nos disse "isto é um Stream" remove um problema que o utilizador não deveria ter de pensar.

Âmbito: aplica-se apenas quando o MusicBee está a controlar a reprodução (musicBeePlayToMode). O caminho de busca da biblioteca (navegação do cliente UPnP) permanece inalterado - os URLs de rádio são raros lá e o comportamento voltado para o utilizador não deve mudar sem testes explícitos.


F32 - Fallback de anúncio de codec

O Quê: se um dispositivo não anuncia certos codecs (ou o plugin não consegue analisar o XML de capacidade do dispositivo), o plugin não rejeita imediatamente o fluxo. Em vez disso, tenta servi-lo e deixa o dispositivo decidir.

Porquê: muitos dispositivos têm XML de capacidade incompleto ou ilegível, mas na verdade lidam bem com o codec. F32 troca um pequeno "tentar e adivinhar" por uma recusa total.


F33 - Melhoria da sincronização da barra de progresso

O Quê: a posição entre as sondagens já é extrapolada por relógio de parede a partir de uma única âncora (currentPlayStartTicks), de modo que a barra de progresso é atualizada suavemente a uma taxa sub-segundo. A fonte de jitter restante era a âncora inicial para uma faixa recém-iniciada: o código anterior assumia position=0 no momento em que o temporizador de estado notou pela primeira vez que o estado passou para Reprodução, mas nessa altura o dispositivo poderia estar a reproduzir há 100-500ms (um intervalo de sondagem). A barra de progresso do MusicBee começaria em 0, depois saltaria para a frente quando a realidade o alcançasse.

Correção F33: ao fazer a transição para Reprodução pela primeira vez numa nova faixa (currentPlayStartTimeEstimated=True), chame GetPlayPositionInformation() para obter a posição real atual do dispositivo, e depois ancore-se nisso. O UPnP apenas reporta resolução de 1 segundo, então a âncora ainda é quantizada, mas está muito mais próxima da verdade do que assumir 0.

Porquê: exibição de progresso mais suave + mais precisa, especialmente logo após a mudança de faixa. Não há como contornar a própria resolução de relatório de 1 segundo do UPnP - isso é especificação.


F34 - Jitter da barra de progresso após mudança de faixa

O Quê: quando PlayToDevice é chamado para uma nova faixa, o plugin costumava deixar currentPlayPositionMs e currentPlayStartTicks nos seus valores da faixa anterior durante a janela de ~100ms entre o SOAP-Play e a primeira sondagem do temporizador de estado que detetava o novo estado de Reprodução. A barra de progresso do MusicBee mostraria brevemente o fim da faixa anterior, depois voltaria para 0, e depois subiria. F34 zera ambos na entrada de PlayToDevice - no momento em que sabemos que uma mudança de faixa está a acontecer, antes de qualquer trabalho SOAP.

Porquê: falha visual em casos de uso de salto rápido (próximo manual ou transição sem interrupções). Agora, a primeira consulta de PlayPositionMs do MusicBee pós-Play retorna 0 limpa, e depois o GetPlayPositionInformation de F33 refina-a para a posição real do dispositivo no primeiro tick de mudança de estado.

Implementação: quatro linhas no topo de PlayToDevice, emparelhadas com a ancoragem precisa do tempo de transição de F33.


F35 - Bug de "Forçar transcodificação"

O Quê: forçar a transcodificação ainda podia ignorar a transcodificação em certas combinações. Após a reformulação por perfil F04, duas lacunas específicas foram seladas:

  1. Precedência com ForceNativeStream. Quando ambos eram Verdadeiros (o que pode acontecer numa migração de esquema ou num ficheiro de configurações parcial), ForceTranscoding agora vence (If streamingProfile.ForceTranscoding Then forceEncode = True ElseIf streamingProfile.ForceNativeStream Then forceEncode = False). A exclusão mútua da UI impede que o utilizador selecione ambos, mas a guarda de tempo de execução lida com qualquer estado que tenha carregado inconsistente do disco.
  2. Lógica bypassTranscodeDecision. Anteriormente: streamingProfile.ForceNativeStream AndAlso Not Settings.ForceTranscoding. Agora: streamingProfile.ForceNativeStream AndAlso Not streamingProfile.ForceTranscoding - mesma regra de precedência, mas no mesmo escopo por perfil.

Porquê: "forçar" deve significar forçar. Se o utilizador ativou explicitamente a Transcodificação Forçada para um dispositivo, o plugin nunca deve passar silenciosamente para o streaming nativo, independentemente de como outras flags se combinam.


F36 - Exceção de renderizador fechado

O Quê: envolveu Plugin.ReceiveNotification num Try/Catch de nível superior que regista qualquer exceção não capturada em vez de a deixar propagar de volta para o sistema de notificações do MusicBee.

Porquê: as notificações do MusicBee (PlayStateChanged, VolumeMuteChanged, etc.) são despachadas para ControlPointManager que comunica com o renderizador via SOAP. Os locais de chamada individuais já tinham Try/Catch em torno das suas chamadas SOAP, mas um caso de temporização suficientemente estranho (por exemplo, o renderizador a morrer entre duas chamadas SOAP no mesmo manipulador de notificação) ainda podia escapar. O wrapper de nível superior é a rede de segurança final para que o utilizador nunca veja um popup genérico de "TargetInvocationException" do MusicBee.

Implementação: renomeou o corpo existente para ReceiveNotificationInternal e adicionou um wrapper fino ReceiveNotification que faz Try { ReceiveNotificationInternal(...) } Catch { LogError(...) }. A infraestrutura Try/Catch pré-existente por método dentro de ControlPointManager (em torno de cada chamada PostSoapRequest) permanece - F36 é cinto + suspensórios.


F37 - Busca de faixa longa acionando falsa transição

O Quê: procurar dentro de uma faixa longa pode produzir um breve ciclo Parado→A Reproduzir em alguns renderizadores. Sem discriminação, ProcessNewPlayState.Stopped trata isso como fim natural da faixa e chama Player_PlayNextTrack, avançando o MusicBee quando o utilizador apenas queria avançar. F37 marca lastUserInitiatedSeek em Seek() e adiciona uma guarda de 5 segundos no manipulador de Parado (espelhando a janela lastUserInitiatedStop existente).

Porquê: o salto silencioso para a próxima faixa durante uma busca é um daqueles bugs cuja causa ninguém consegue adivinhar - o utilizador pensa "estranho, tentei avançar e agora está a reproduzir a próxima música". A correção é mecânica: o mesmo padrão da discriminação de paragem do utilizador já existente.


F38 - Manuseamento de busca melhorado para codecs propensos a falhas

O Quê: a falha do BubbleUPnP na busca de MP3 era o sintoma canónico. Após auditoria, o código de busca atual do yaiol já faz as coisas certas - o caminho nativo lida corretamente com HTTP Range (206, Content-Range, AcceptRanges), o caminho codificado anuncia X-AvailableSeekRange e analisa os cabeçalhos timeSeekRange.dlna.org / npt de entrada, as flags DLNA.ORG_OP refletem as capacidades reais do fluxo (com DisablePcmTimeSeek opt-out para dispositivos Platinum problemáticos). Testado pelo utilizador no BubbleUPnP 4.6.4 atual: não foram observadas falhas.

Porquê: a falha de busca de MP3 do BubbleUPnP foi relatada por volta de 2024 e a aplicação teve cerca de 16 meses de correções desde então. F29 (MP3 codificado OP=11) foi a nova variável que poderia tê-la reexposto; não o faz, nas versões testadas.

Se uma falha retornar: a forma da correção seria um interruptor "busca limitada" por perfil que força DLNA.ORG_OP=10 (apenas por byte) em codecs sinalizados - espelhando como DisablePcmTimeSeek já funciona para PCM. Adicionar então, não preventivamente.


UI e registo

F39 - O botão "Adicionar" seleciona o novo perfil

O Quê: clicar em "Adicionar" na lista de perfis de dispositivo cria um novo perfil E seleciona-o automaticamente para que o utilizador possa editar os campos imediatamente. A nossa refatorização da caixa de diálogo seccionada já faz isso - tanto o caminho de Adição direta quanto o caminho a partir do modelo terminam com Me.activeStreamingProfiles.SelectedIndex = Me.activeStreamingProfiles.Items.Count - 1. Uma verificação confirmou que o nosso fork já lida com isso - nada a alterar.

Porquê: pequena falha de UX que acabou por já não ser uma falha aqui.


F40 - Limite de conexões maior + registo de aviso

O Quê: o limite de streams concorrentes do plugin (SemaphoreSlim em torno de Sockets_Stream_File / Sockets_Encoder_Start) era um valor fixo de 4. F40 torna-o configurável pelo utilizador na página de configurações Gerais (predefinição 16, intervalo 1-256), adiciona uma linha de registo MaxConnections quando um pedido tem de esperar por um slot, E exibe um distintivo vermelho ⚠ Max Conn na parte inferior esquerda da caixa de diálogo de configurações se o limite foi atingido pelo menos uma vez desde que o MusicBee iniciou.

Porquê: quando um dispositivo dispara pedidos paralelos (alguns Marantz/Linn durante scans de arte de capa, sondagens de metadados do BubbleUPnP juntamente com reprodução ativa), pedidos adicionais eram bloqueados silenciosamente atrás do semáforo - o utilizador via "dispositivo lento" sem causa visível. A linha de registo é boa para depuração técnica, mas utilizadores não técnicos nunca leem registos. O distintivo visível na caixa de diálogo de configurações torna a condição de limite atingido detetável por qualquer pessoa que abra as preferências do plugin.

Implementação:

  • Centralizou a espera em WaitOnSendBarrier(logTag) em MusicBeeUpnp.vb; ambos os locais de chamada (MediaServerDevice.GetFile, Encoder.StartEncode) usam-no.
  • Settings.MaxConnections persistido na v8 do esquema de configurações.
  • Plugin.MaxConnectionsHit é uma flag de sessão persistente definida dentro de WaitOnSendBarrier; reinicia apenas no reinício do MusicBee.
  • SettingsDialog.maxConnectionsBadge é um rótulo vermelho em negrito em (16, 410) que só aparece quando Plugin.MaxConnectionsHit é Verdadeiro. Tem uma dica de ferramenta explicando a causa e o remédio.
  • O semáforo é inicializado uma vez no carregamento do tipo, então alterar a configuração requer um reinício do MusicBee (observado no rótulo do campo).

F41 - Registar "codificação devido a ReplayGain/DSP"

O Quê: em vez de linhas de registo separadas "codificação para RG" / "codificação para DSP", a única linha StreamDecision de F42 inclui MB-DSP/EQ, MB-ReplayGain, Profile-DSP/EQ, Profile-ReplayGain como razões acumuladas. Mesmo valor de diagnóstico, menos ruído.

Porquê: os utilizadores veem todas as razões pelas quais a transcodificação está a acontecer para uma determinada faixa numa única linha de registo, não dispersas. Consulte F42 para detalhes completos.


F42 - Registar "o renderizador não suporta o codec de origem"

O Quê: adicionada uma linha de registo StreamDecision por faixa reproduzida para dispositivo que diz "CODEC nativo" ou "transcodificar CODEC→CODEC razão=…". O campo de razão acumula todas as condições que acionaram a transcodificação: MB-DSP/EQ, MB-ReplayGain, Profile-DSP/EQ, Profile-ReplayGain, WebFile, VirtualFile, ForceTranscoding(global), SampleRate<min/SampleRate>max, DownmixToStereo, DeviceLacksCodec(X), BandwidthConstrained.

Porquê: os utilizadores estavam confusos com picos inesperados de CPU em ficheiros que esperavam transmitir nativamente. Uma linha de registo por faixa diz-lhes exatamente qual a condição que causou a transcodificação - e se o campo mostra DeviceLacksCodec(Flac), eles sabem imediatamente que a informação de protocolo do dispositivo estava incompleta e podem querer que o fallback de F32 seja ativado.

Implementação: string acumuladora única construída incrementalmente através da cadeia de decisão; registada uma vez no final. Controlada por Settings.LogDebugInfo para evitar ruído no registo em produção.


F43 - O registo SetNextAVTransport mostra o URL de origem

O Quê: as entradas de registo QueueNext agora incluem source=<caminho da biblioteca MusicBee> juntamente com stream=<URL de streaming HTTP>. A mesma alteração aplicada ao caminho de sucesso e ao caminho de falha (QueueNext:Failed).

Porquê: ao depurar um problema de faixa em fila, o URL de streaming (/encode/aabbccdd0.flac) é opaco por si só - o mesmo para cada faixa. O URL de origem é o caminho da biblioteca legível por humanos que lhe diz exatamente qual ficheiro o MusicBee tentou colocar em fila.


F44 - Melhor registo de erros de tipo MIME

O Quê: duas novas entradas de registo durante Activate:

  • Activate:MimeUnverified - dispara por entrada malformada na resposta GetProtocolInfo do dispositivo, nomeando qual entrada não pôde ser analisada (para que o utilizador possa ver, por exemplo, "o Marantz retornou http-get:*::* para algum codec - a capacidade não é verificada, o fallback de F32 adivinhará").
  • Activate:NoSinkInfo - dispara uma vez se o dispositivo não retornou nenhum elemento <Sink>. Significa que SupportedMimeTypes permanece Nothing e IsCodecSupported degrada para "assumir que tudo funciona" - contexto útil quando erros posteriores de "dispositivo recusou stream" aparecem.

Porquê: antes de F44, estas falhas silenciosas de capacidade deixavam os utilizadores a adivinhar porque as suas faixas eram transcodificadas contra as expectativas ou recusadas pelo dispositivo. Agora, uma única pesquisa por Activate: mostra se a informação de capacidade do dispositivo era utilizável.


F45 - Melhor registo de erros de metadados

O Quê: o registo de exceções Browse em ContentDirectoryService.vb já foi enriquecido em trabalhos anteriores do yaiol (a sessão de bugs do Alia Vox) com ObjectID e stack trace. F45 expande-o ainda mais com BrowseFlag (metadados vs filhos), Filter (quais atributos o cliente solicitou), sortCriteria e partialResultLength (quantos bytes de DIDL foram produzidos antes da falha - aponta para quão longe no lote a faixa defeituosa se encontra).

Porquê: quando algo corre mal a meio do DIDL, o valor do comprimento parcial diz se a falha ocorreu na primeira faixa do lote (parcial=0) ou a meio (parcial=N) - combinado com o startingIndex do lote, pode identificar o índice da faixa ofensora. Filter e BrowseFlag explicam que tipo de navegação o cliente queria; por vezes, uma navegação apenas de metadados falha onde uma navegação de filhos para o mesmo ID é bem-sucedida.


Rede

F46 - O modo automático anuncia apenas em adaptadores de rede reais

O Quê: no modo de interface Automático, o plugin costumava anunciar-se (SSDP) em todos os adaptadores IPv4 operacionais. Numa máquina que também executa um túnel VPN (NordLynx) ou um switch virtual (Hyper-V / WSL / Docker), a mesma biblioteca era anunciada em cada um desses adaptadores também, então o ponto de controlo de onde se transmitia descobria o servidor duas ou três vezes e listava a biblioteca como cópias duplicadas. O modo Automático agora mantém apenas os adaptadores que têm um gateway IPv4 predefinido real (HasIPv4Gateway) - o que os adaptadores de túnel e switch virtual não têm - então esses são removidos da lista de anúncios. Um endereço fixado pelo utilizador ainda prevalece (anunciar apenas nessa interface), e se nenhum adaptador reportar um gateway, o seletor volta para todos os adaptadores, então a lista de endereços anunciados nunca está vazia e o plugin não pode tornar-se invisível.

Porquê: a duplicação não é causada por "estar numa VPN" - é causada por anunciar no adaptador LAN e no adaptador de túnel/virtual ao mesmo tempo, então um ponto de controlo vê o mesmo servidor em dois endereços. Uma VPN de consumidor (NordVPN/NordLynx) apenas tunela o tráfego com destino à internet; o renderizador DLNA vive na LAN e o tráfego da sub-rede local ignora o túnel, então o adaptador de túnel nunca chega a um renderizador de qualquer forma - removê-lo remove uma cópia fantasma, nunca um caminho de trabalho. O teste do gateway é o sinal barato e fiável que separa um adaptador LAN/Wi-Fi real de um túnel ou switch virtual. Complementa N05 (que corrigiu como os anúncios são enviados nessas ligações - multicast em vez de broadcast); F46 governa quais adaptadores são anunciados.

Limite conhecido: uma VPN de malha / acesso remoto (Tailscale, ZeroTier, WireGuard-to-home) cujos renderizadores realmente vivem através do túnel geralmente apresenta um adaptador sem gateway predefinido, então o modo Automático também o remove. Esses utilizadores fixam o endereço VPN, que tem precedência sobre o filtro de gateway.

Conteúdo