MusicBee UPnP Plugin 도움말

새로운 기능

2.0.9 - 2026-08-23

업데이트 알림이 해당 언어로 페이지를 엽니다.

내용: 업데이트 알림의 새로운 기능다운로드 링크가 이제 영어 대신 MusicBee 자체 언어로 플러그인 페이지를 엽니다.

이유: 이 두 링크는 웹사이트로 보내기 전에 언어를 영어, 프랑스어, 스페인어 또는 독일어 중 하나로 제한했기 때문에 번역본이 존재하는 경우에도 다른 모든 사용자에게는 영어 페이지가 제공되었습니다. 이제 MusicBee의 언어를 변경하지 않고 전달하여 웹사이트가 제공할 내용을 결정하도록 하며, 이는 옆에 있는 도움말 버튼이 항상 해왔던 방식입니다.

2.0.8 - 2026-08-22

플러그인이 이제 네트워크에서 플러그인을 검색하는 앱 및 장치에 자신을 올바르게 소개하며, 장치 프로필 설정이 다시 정렬됩니다.

장치에 올바른 제조사, 모델 및 버전이 표시됩니다.

내용: 제어 앱, 휴대폰 또는 TV가 네트워크에서 MusicBee를 찾으면, 이제 이 플러그인을 yaiol이 제작한 것으로 표시하고, 플러그인 자체 사이트를 가리키며, 서버, 플레이어 및 렌더러의 세 가지 역할을 모두 포함한다고 설명하고, 실제로 설치된 버전을 보고합니다.

이유: 모든 UPnP 장치는 누가 만들었는지, 무엇인지 알리며, 제어 앱은 이를 장치의 ID로 표시합니다. 이 플러그인은 포크된 원래 플러그인의 세부 정보(다른 저자의 이름, 자체 웹사이트 대신 MusicBee 웹사이트, 첫 릴리스 이후 "1.0"으로 고정된 모델 번호)를 여전히 알리고 있었습니다. 휴대폰에서는 어떤 플러그인과 통신하는지, 심지어 어떤 버전인지도 알 수 없었습니다. 이제 이러한 세부 정보는 플러그인 자체에서 가져오므로, 장치 옆에 표시되는 버전은 모든 업데이트에서 올바르게 유지됩니다.

장치 프로필 설정이 다시 정렬됩니다.

내용: 장치 프로필 탭에서 레이블과 해당 상자가 왼쪽 가장자리를 공유하고 균일한 간격으로 배치되며, 샘플 속도 범위가 한 줄로 표시됩니다.

이유: 시간이 지남에 따라 탭에 옵션이 추가되면서 필드가 서로 떨어져 있었고, 샘플 속도 범위의 "to" 레이블이 옆 상자 위에 놓여져 있었습니다. 무슨 말인지 알면 읽을 수 있었지만, 처음 볼 때는 혼란스러웠습니다.

2.0.7 - 2026-08-08

더 큰 트랙은 제목과 위치 슬라이더를 유지합니다.

내용: 휴대폰에서 전송된 더 큰 트랙(긴 FLAC, 고해상도 또는 DSD 파일)은 이제 적절한 제목을 표시하고 작은 트랙처럼 이동할 수 있습니다. 이전에는 일부 트랙이 네트워크를 통해 재생되었으며, 제목 대신 웹 주소가 표시되고 슬라이더는 작동하지 않았습니다.

이유: 플러그인은 시작하기 전에 로컬 복사본을 기다리지만, 이전에는 파일 크기를 기준으로 파일이 기다릴 가치가 있는지 미리 결정했습니다. 이는 네트워크 속도에 대한 추측이었으며, 플러그인은 이를 알 방법이 없습니다. 65MB 트랙은 너무 크다고 판단되었지만, 1초 후에 다운로드가 완료되어 이미 포기했던 대기 시간 내에 편안하게 들어왔습니다. 이제 플러그인은 단순히 다운로드를 지켜봅니다. 파일이 도착하는 동안 플러그인은 아무리 오래 걸리더라도 계속 기다립니다. 전송이 실제로 중단될 때만 포기하며, 이제는 이전의 고정된 지연 시간보다 더 빨리 이를 감지합니다.

2.0.6 - 2026-08-03

휴대폰에서 보낸 앨범이 이제 트랙 사이에 끊김 없이 재생되며, 인터넷 라디오는 비정상적으로 긴 노래로 취급되지 않고 라디오로 인식됩니다.

앨범이 끊김 없이 재생됩니다.

내용: 휴대폰에서 앨범 전체를 보내면 MusicBee가 이제 일시 정지 없이 한 트랙에서 다음 트랙으로 재생됩니다. 따라서 라이브 녹음, DJ 세트 및 연속 클래식 작품이 끊김 없이 이어집니다.

이유: 표준은 제어 앱이 "다음에 올 내용"을 말할 수 있도록 허용하며, 이는 끊김 없는 연결을 가능하게 합니다. 이 지침이 전혀 수락되지 않아 앱이 다음 트랙을 배치할 곳이 없었고 현재 트랙인 것처럼 발표했습니다. 이는 이전 릴리스에서 수정된 앨범 문제의 원인이었습니다. 이제 제대로 수락됩니다. 현재 트랙이 재생되는 동안 다음 트랙이 가져와지고 MusicBee 자체 플레이어가 경계를 넘습니다.

인터넷 라디오가 라디오로 인식됩니다.

내용: MusicBee로 전송된 라이브 스테이션은 스트림으로 재생되며 다운로드되지 않습니다.

이유: 방송을 다운로드하는 것은 의미가 없습니다. 끝이 없고 건너뛸 것도 없지만, 플러그인은 이전에는 음악 파일과 구별할 방법이 없었기 때문에 다운로드를 시작하고 다운로드 크기가 고정된 크기를 초과하면 중지했습니다. 제어 앱은 둘 중 어느 것을 보내는지 명시하며, 이제 직접 읽습니다. 설정도, 추측도 없습니다.

긴 고해상도 트랙은 복사본을 유지합니다.

내용: DSD 파일 및 긴 24비트 녹음은 이제 다른 트랙처럼 이동할 수 있습니다.

이유: 위에서 언급한 크기 제한에 걸렸습니다. 20분짜리 고해상도 악장이나 10분짜리 DSD 트랙 모두 이를 초과했기 때문에 복사본이 포기되었고 위치 슬라이더는 스크러빙할 가치가 가장 높은 자료에 대해 작동을 멈췄습니다. 라디오가 제대로 식별되면 크기 제한이 필요하지 않습니다.

2.0.5 - 2026-08-03

휴대폰에서 MusicBee를 제어하는 두 가지 수정 사항: 이제 볼륨이 양쪽 끝에서 동일한 의미를 가지며, 전체 앨범을 캐스팅하면 첫 번째 트랙 이후에도 계속 작동합니다.

휴대폰의 볼륨이 MusicBee의 볼륨과 일치합니다.

내용: 이제 휴대폰에서 볼륨을 최대로 올리면 MusicBee에서도 최대로 도달하며, MusicBee 자체 설정이 휴대폰에서 올바르게 다시 읽힙니다.

이유: 플러그인이 제어 앱에 최고 볼륨이 무엇인지 알려주지 않았기 때문에 각 앱이 추측해야 했습니다. 그중 하나는 69로 설정되어 휴대폰의 100%가 MusicBee에서 69%에 불과했고, MusicBee의 100%는 휴대폰에서 144%로 돌아왔습니다. 그리고 휴대폰의 볼륨 버튼은 결코 최상단에 도달할 수 없었습니다. 이제 렌더러가 범위를 명확하게 명시하므로 양쪽 끝이 동일한 스케일에 대해 이야기합니다.

앨범 캐스팅 시 제목과 위치 슬라이더가 유지됩니다.

내용: 휴대폰에서 전송된 앨범의 모든 트랙이 이제 올바른 제목을 표시하고 이동할 수 있으며, 첫 번째 트랙만 해당되는 것이 아닙니다.

이유: 제어 앱은 현재 트랙이 재생된 후 몇 분의 1초 후에 다음 트랙을 알리는데, 이 알림이 재생될 트랙에 대해 가져오고 있던 복사본을 취소했습니다. 그래서 대부분의 트랙은 조용히 네트워크를 통해 재생되는 것으로 되돌아갔고, 이로 인해 제목과 이동 기능이 모두 손실되었습니다. 이제 여러 트랙의 복사본이 서로 나란히 유지되므로 알림이 더 이상 사용 중인 복사본을 취소할 수 없습니다.

2.0.4 - 2026-08-02

휴대폰이나 다른 서버에서 MusicBee로 전송된 음악은 이제 실제 트랙처럼 작동합니다. 트랙을 이동할 수 있으며, 즉시 올바른 제목이 표시됩니다. 또한 하나의 결합된 장치로 표시되는 하이파이 스트리머에 대한 수정 사항도 포함되어 있습니다.

다른 곳에서 전송된 트랙 이동

내용: 이제 휴대폰, NAS 또는 다른 미디어 서버에서 전송된 트랙의 위치 슬라이더를 드래그하여 이동할 수 있습니다. 이를 위해 MusicBee는 재생을 시작하는 동안 트랙의 사본을 임시 폴더에 다운로드하여 재생합니다. 홈 네트워크에서는 약 1초가 소요되며, 다른 트랙을 보내는 즉시 사본은 삭제되고, MusicBee가 다음에 시작될 때 남은 파일은 모두 지워집니다.

이유: MusicBee는 네트워크를 통해 듣고 있는 것을 시작하고 중지할 수 있지만, 이동할 수는 없습니다. 그래서 슬라이더가 점프하는 것처럼 보였다가 아무런 설명 없이 원래 위치로 다시 미끄러져 돌아갔습니다. 자신의 디스크에 있는 일반 파일을 재생하면 이 제한을 완전히 제거할 수 있습니다.

첫 음부터 올바른 제목과 길이

내용: 파일에 일반적인 파일 확장자를 부여하지 않는 앱에서 전송된 트랙은 이제 긴 웹 주소로 나타나는 대신 시작되는 즉시 실제 제목과 길이를 표시합니다.

이유: MusicBee는 파일 확장자를 통해 트랙을 식별하고 태그를 찾는데, 일부 플레이어는 확장자가 전혀 없는 주소를 제공합니다. 로컬 사본은 항상 올바른 확장자를 가지고 있으므로, 보내는 앱이 무엇이라고 부르든 트랙이 인식됩니다.

할 수 없는 점프는 이제 그렇게 말합니다

내용: 렌더러가 요청된 지점으로 실제로 이동할 수 없는 경우, 제어 앱에 통보되고 보고됩니다.

이유: 이전에는 상관없이 "완료"라고 응답했기 때문에 슬라이더가 1초 후에 아무런 설명 없이 다시 미끄러져 돌아갔습니다. 솔직한 거절이 침묵하는 것보다 조치하기 쉽습니다.

결합된 하이파이 스트리머가 올바르게 읽힙니다

내용: MusicBee가 하나의 결합된 장치로 표시되는 장치(예: Marantz 또는 Denon 스트리머, 플레이어가 미디어 서버와 함께 제조업체 래퍼 내에 있는 경우)로 재생할 때, 플러그인은 이제 미디어 서버의 세부 정보 대신 플레이어 자체의 세부 정보를 읽습니다.

이유: 이전에는 장치의 잘못된 부분에 어떤 오디오 형식을 처리할 수 있는지 물었고, 사용할 수 있는 답변을 얻지 못했으며, 확인하지 않고 계속 진행했습니다. 이는 형식 처리가 가장 정확해야 하는 하드웨어였습니다. 장치의 모델 설명도 이제 장치 프로필과 일치시킬 때 고려됩니다. 이전에는 잘못된 위치에서 읽혀지고 버려졌습니다.

2.0.3 - 2026-08-02

재생 역할이 성장했습니다. MusicBee는 이제 이미 라이브러리에 있는 트랙뿐만 아니라 휴대폰, NAS 또는 다른 서버에 있는 파일과 같이 라이브러리에 없는 음악도 보낼 수 있습니다.

내 라이브러리뿐만 아니라 휴대폰에서 보낸 음악 재생

내용: Symfonium 또는 BubbleUPnP와 같은 제어 앱을 사용하여 MusicBee로 음악을 보내면 더 이상 MusicBee 자체 라이브러리에서 트랙을 가져올 필요가 없습니다. 휴대폰 자체, NAS 또는 다른 미디어 서버에 저장된 파일이 이제 재생됩니다. 제목과 길이는 보낸 앱에서 가져오므로 MusicBee가 파일을 본 적이 없더라도 트랙이 제대로 표시됩니다.

이유: 재생 역할은 휴대폰에서 이 PC의 라이브러리를 탐색하고 노래를 탭하는 경우를 위해 만들어졌습니다. 트랙은 이미 PC에 있었으므로 MusicBee는 단순히 자체 파일을 재생했습니다. 다른 곳에서 도착한 모든 것은 조용히 버려졌습니다. 이로 인해 휴대폰에서 좋은 스피커로 음악을 푸시하는 것과 같이 자연스러운 경우에 이 기능이 쓸모없게 되었습니다.

재생할 수 없는 트랙은 그렇게 말합니다.

내용: 렌더러가 보낸 내용을 실제로 재생할 수 없는 경우 이제 보낸 앱에 다시 보고합니다.

이유: 이전에는 모든 것에 "알겠습니다"라고 대답했으므로 제어 앱은 계속해서 재생을 눌렀습니다. 실제로 로드된 것이 없으면 MusicBee는 이전에 남아 있던 트랙을 다시 시작했으며, 해당 파일이 없으면 자체 소스를 찾을 수 없다고 불평했습니다. 오류는 관련 없는 트랙의 이름을 지정하고 실제 문제와는 전혀 관련이 없는 곳을 가리켰습니다.

일시 중지된 상태에서 새 트랙을 보내면 해당 트랙이 재생됩니다.

내용: MusicBee가 일시 중지되어 있고 제어 앱이 새 트랙을 보내면 새 트랙이 시작됩니다.

이유: 다시 시작하는 것이 로드하는 것보다 우선했으므로 일시 중지된 트랙은 중단된 부분부터 다시 시작되었고 방금 선택한 트랙은 아무 말 없이 삭제되었습니다.

2.0.2 - 2026-08-01

검색이 핵심입니다. 이제 아티스트별로 작동하고, 올바른 결과를 반환하며, 대규모 라이브러리에서도 빠릅니다. 또한 제어 앱의 셔플을 조준하는 새로운 방법과 아무데도 연결되지 않던 세 개의 버튼에 대한 수정 사항이 있습니다.

아티스트별 검색이 실제로 작동합니다

내용: 이제 제어 앱에서 아티스트를 검색하면 해당 아티스트의 음악이 반환됩니다. 검색은 트랙의 Artist와 앨범의 Album Artist를 모두 일치시키므로, 연주자의 이름이나 앨범이 분류된 이름으로 입력하더라도 컴필레이션을 찾을 수 있습니다.

이유: 이전에는 아티스트 검색이 "이 종류의 모든 것을 줘"로 오인되어 입력한 아티스트가 버려지고 전체 라이브러리가 반환되었습니다. 따라서 수백 개의 트랙과 일치해야 할 검색이 수만 개를 반환했습니다. 두 아티스트 필드 중 하나만 일치시키면 결과의 절반이 조용히 손실되었을 것이므로 둘 다 확인합니다.

검색 결과 페이지가 제대로 작동합니다

내용: 이제 긴 검색 결과 목록을 스크롤하면 목록을 통해 이동합니다. 스크롤하는 각 페이지는 얻는 페이지입니다.

이유: 서버는 이전에 전체 일치 수를 보고하면서 모든 요청에 첫 번째 소수의 결과로 응답했기 때문에 더 많은 것을 스크롤하는 앱은 동일한 항목을 계속 수신하고 끝에 도달하지 못했습니다.

대규모 라이브러리에서 검색이 훨씬 빠릅니다

내용: 이제 검색은 전체 결과 세트에 대해 단일 쿼리를 실행하고 보고 있는 페이지의 태그만 읽습니다.

이유: 이전에는 각 페이지가 라이브러리에 대해 쿼리를 다시 실행한 다음 모든 일치 항목(수천 개)의 태그를 로드하여 십여 개를 표시했습니다. 대규모 컬렉션에서는 모든 스크롤이 일시 중지되었습니다. 라이브러리가 새로 고쳐질 때마다 결과도 삭제되므로 편집된 내용은 오래된 상태로 제공되지 않습니다.

셔플을 필터에 맞추세요

내용: 라이브러리 옵션 탭에 새로운 Random plays from 설정이 추가되었습니다. 이 설정을 All Music으로 두면 제어 앱의 Random Tracks / Random Albums 폴더는 이전과 같이 작동합니다. MusicBee 필터 중 하나를 선택하면 모든 무작위 요청이 해당 필터에서 가져옵니다. 숨겨진 필터도 제공됩니다.

이유: 셔플 폴더는 "모든 것"의 일부를 요청하며, 이는 사용자가 무엇을 의미하는지에 대한 단서가 없는 유일한 요청입니다. 따라서 항상 전체 라이브러리(음성 단어 포함)에서 가져왔습니다. 여기에서 "모든 것"이 무엇을 의미하는지 말할 수 있습니다. 장치에서 필터 내부에서 셔플 폴더를 열면 여전히 해당 필터가 셔플됩니다. 탐색 중에 내리는 선택이 설정보다 우선합니다.

도움말, GitHub 및 업데이트 확인이 실제 페이지로 연결됩니다

내용: 설정 대화 상자의 HelpGitHub 버튼과 새 버전 자동 확인이 이제 해당 이름의 페이지를 엽니다.

이유: 세 가지 모두 플러그인 이름의 단축된 형태로 만들어졌으며, 해당 페이지는 존재한 적이 없었기 때문에 각각 조용히 실패했습니다. 버튼은 아무것도 하지 않는 것처럼 보였고, 새 버전이 아무리 오래 출시되었더라도 업데이트 확인은 아무것도 보고하지 않았습니다.

2.0.1 - 2026-07-26

재생 및 탐색 관련 수정 사항이 적용되었습니다. 팟캐스트와 재생 및 셔플을 제어하는 컨트롤러 앱(예: BubbleUPnP)에 중점을 두었습니다.

팟캐스트가 일시 중지 없이 재생 시작

내용: 다운로드된 팟캐스트 에피소드가 이제 실제 길이와 파일 크기를 즉시 표시합니다. 디스크의 파일에서 미디어 메타데이터로 직접 가져옵니다.

이유: 명시된 재생 시간이 없으면 BubbleUPnP와 같은 컨트롤러는 재생 버튼을 누를 때마다 전체 오디오 스트림을 다시 스캔하여 에피소드의 길이를 파악합니다. 따라서 재생이 눈에 띄는 일시 중지 후에 시작되었습니다. 이제 길이가 광고되므로 깔끔하게 시작됩니다.

아티스트별로 탐색할 때 팟캐스트가 표시됨

내용: 팟캐스트의 프로그램 이름이 이제 아티스트로 분류됩니다. 앨범 필드를 이미 채우는 방식과 동일하게 아티스트 및 앨범 아티스트 필드(및 해당 정렬 변형)에 미러링됩니다.

이유: 아티스트 필드별로 그룹화하는 탐색 경로는 모든 팟캐스트의 아티스트를 비어 있고 빈 레벨에서 막다른 길로 찾았습니다. 프로그램을 앨범으로 취급하는 것과 일관되게 아티스트로 취급하면 이제 해당 경로가 아무것도 아닌 대신 에피소드에 도달합니다.

"최근 재생" 및 캐스팅이 다시 시작한 후 제대로 작동함

내용: 팟캐스트, 오디오북, 받은 편지함 또는 라디오 트랙을 직접 재생하는 것(먼저 탐색하지 않고 BubbleUPnP의 "최근 재생" 목록 및 캐스트 대상이 하는 방식)이 더 이상 실패하지 않습니다. 이제 플러그인은 ID로 요청될 때 트랙을 주문형으로 로드합니다.

이유: 해당 목록은 MusicBee가 다시 시작된 직후, 아무것도 탐색되기 전에 트랙을 요청하므로 플러그인이 ID를 본 적이 없었고 "잘못된 ID"라고 응답했습니다. 이제 첫 번째 직접 요청 시 관련 소스를 강제로 로드하고 트랙을 찾습니다.

"랜덤 트랙" 및 "랜덤 앨범"이 결과를 반환함

내용: 전체 라이브러리의 임의 조각을 요청하는 BubbleUPnP의 "랜덤 트랙" 및 "랜덤 앨범" 셔플 폴더가 이제 채워져 돌아옵니다.

이유: 이들은 일치할 제목이 없는 검색이며, 플러그인은 이전에 빈 내부 목록에서 응답했으므로 항상 아무것도 표시되지 않았습니다. 이제 나머지 탐색에서 사용하는 것과 동일한 주문형 쿼리 경로에서 올바르게 페이지 매김되어 제공됩니다.

빈 태그가 있는 앨범이 트랙을 나열함

내용: 빈 값으로 그룹화된 앨범(예: 연도가 없는 받은 편지함 트랙)을 열면 이제 트랙이 표시됩니다.

이유: 앨범의 트랙을 수집하는 일치는 "이 태그가 비어 있음"을 "일치 없음"으로 처리했으므로 빈 필드에서 형성된 모든 앨범은 아무것도 탐색되지 않았습니다. 이제 빈 그룹 값은 이를 공유하는 트랙과 올바르게 일치합니다.


2.0.0 - 2026-07-22

이것은 MusicBee UPnP 플러그인의 오픈 소스 yaiol 포크의 첫 번째 공개 릴리스입니다. 이 포크에 새로 추가된 모든 것과 원본 플러그인에 대한 수정 및 개선 사항의 두 부분으로 구성됩니다. 각 항목은 프로젝트의 내부 기능 카탈로그의 무엇 / 왜 형식을 유지하므로 모든 변경 사항의 근거가 변경 사항뿐만 아니라 페이지에 있습니다.

이 포크의 새로운 기능

MediaRenderer - MusicBee로 재생

N01 - MusicBee를 재생 렌더러로 사용

무엇: 일반적으로 이 플러그인은 한 방향으로 작동합니다. 휴대폰 또는 다른 장치가 MusicBee 라이브러리를 탐색하고 자체적으로 음악을 재생합니다. 이 기능은 반대 방향을 추가합니다. MusicBee가 플레이어가 되도록 합니다. 휴대폰의 컨트롤러 앱(예: BubbleUPnP)에서 데스크톱 MusicBee를 재생할 항목으로 선택한 다음 손으로 제어할 수 있습니다. 재생, 일시 중지, 중지, 앞으로 또는 뒤로 건너뛰기, 트랙의 특정 지점으로 이동, 볼륨 변경 또는 음소거.

왜: 휴대폰을 PC에 이미 있는 음악의 리모컨으로 바꿉니다. 소파에 앉아 휴대폰으로 라이브러리를 탐색하고 트랙을 탭하면 데스크톱에 연결된 스피커에서 음악이 나옵니다. 앉은 자리에서 완벽하게 제어할 수 있습니다. 원본 플러그인은 이 기능을 작동하는 기능으로 제공하지 않았습니다.

켜기: 기본적으로 꺼져 있습니다. 켜면 홈 네트워크의 모든 장치가 PC에서 재생을 시작할 수 있기 때문입니다. 설정 대화 상자의 일반 탭에 있는 확인란으로 활성화합니다. 플러그인의 세 가지 역할은 각각 자체 확인란을 가지고 있습니다. 내 라이브러리 공유(서버), 다른 사람이 나에게 재생하도록 허용(렌더러), 다른 장치로 재생(제어 지점). 대화 상자는 활성화한 역할에 실제로 필요한 설정 탭만 표시하므로 자신에게 적용되지 않는 옵션에 직면할 필요가 없습니다.

기기 구분: 렌더러에 원하는 이름을 지정할 수 있습니다(기본값은 "MusicBee (yaiol)"). 이 이름은 휴대폰의 재생 대상 목록에 나타나므로 여러 PC에서 MusicBee를 실행할 때 어떤 PC가 어떤 PC인지 알 수 있습니다. 이름 변경은 다시 시작할 필요 없이 즉시 적용됩니다.

자체 재생 시 최상의 사운드: 휴대폰에서 MusicBee 자체 라이브러리를 탐색하고 트랙을 동일한 MusicBee로 다시 보내면 플러그인은 자체 파일 중 하나를 재생하도록 요청받았음을 인식하고 디스크에서 직접 재생합니다. 결과는 정확하고 즉각적입니다. MusicBee 자체 이퀄라이저 및 볼륨 레벨링이 적용된 비트 완벽한 사운드를 제공하며, 오디오를 네트워크로 불필요하게 밀어내고 다시 자체로 보내는 대신에 말입니다.

비공개로 실행: 세 가지 역할은 독립적으로 작동하므로 라이브러리 공유를 끄고 렌더러를 켤 수 있습니다. 이 "렌더러 전용" 설정에서는 라이브러리가 네트워크에서 완전히 숨겨지고 재생 대상만 발표되며 MusicBee는 자체적으로 재생을 제공하지 않습니다.


재생 동작

N02 - 5.1 FLAC 자동 다운믹스 안 됨

무엇: MediaServerDevice.GetEncodedFile의 채널 수 제한은 If StereoOnly OrElse Not isPcmData Then channelCount = 2였습니다. Not isPcmData 절은 소스 채널 수에 관계없이 모든 비 PCM 트랜스코딩(FLAC, MP3, AAC, Ogg)을 스테레오로 자동으로 다운믹스하여 소스가 5.1 FLAC일 때 5.1 지원 렌더러를 무력화했습니다. 이제 두 번째 절은 FLAC을 제외합니다: Not isPcmData AndAlso encoder.Codec <> FileCodec.Flac. FLAC 5.1은 통과하고, MP3/AAC/Ogg는 MusicBee의 해당 형식에 대한 명령줄 인코더가 2채널 입력을 예상하기 때문에 여전히 스테레오로 강제됩니다.

왜: 5.1 FLAC 소스를 FLAC 출력으로 트랜스코딩하는 전체 목적은 다중 채널 믹스를 보존하는 것입니다. 자동 다운믹스는 서라운드 청취에 FLAC 트랜스코딩 옵션을 쓸모없게 만들었습니다. N02를 사용하면 올바르게 작동합니다.


아키텍처

원본 플러그인에는 없는 대규모 라이브러리에서 포크를 실행 가능하게 만드는 구조적 변경.

N03 - 지연 (주문형) 탐색 트리

무엇: 원본 플러그인은 MusicBee 시작 시 전체 탐색 트리를 구축했습니다. 모든 트랙을 열거하고, 파일당 전체 Library_GetFileTags를 수행하고, 전체 컨테이너 계층 구조를 조립했습니다. HTTP 포트를 열기 전에 말입니다. 실제 라이브러리(5만 개 이상의 트랙, 5400개의 팟캐스트 에피소드, 수백 개의 스테이션)에서는 콜드 스타트가 몇 분이 걸리고, 클라이언트가 열지 않는 브랜치를 포함하여 트리가 RAM에 영원히 남아 있습니다. 이 포크는 미리 아무것도 구축하지 않습니다. 루트는 엔드포인트당 하나의 L: 접두사 자리 표시자(L:music, L:podcast, L:filter:…)를 노출합니다. 각 레벨은 클라이언트가 해당 레벨로 탐색할 때만 계산되고(LazyBrowseEnsureLazyEndpointInMemory → 레벨별 캐시), 라이브러리 변경 알림은 캐시를 지웁니다(SetLibraryDirty).

왜: 콜드 스타트가 본질적으로 즉각적입니다. MusicBee가 플러그인 초기화를 완료할 때쯤 HTTP 포트가 열리고 메모리는 라이브러리 크기가 아니라 탐색된 내용에 비례하여 유지됩니다. 트레이드오프: 엔드포인트로의 첫 번째 탐색은 로드 비용을 지불합니다. 재진입은 다음 라이브러리 변경까지 캐시됩니다. 이것이 다른 모든 것이 의존하는 기반입니다. 전체 노트: FIXES.md.


네트워킹 및 견고성

HTTP 서버의 바인드 경로 강화. 원본 플러그인은 포트를 사용할 수 없을 때 자동으로 종료됩니다.

N04 - 자체 복구 HTTP 포트 바인딩

무엇: 플러그인의 HTTP 서버는 구성된 포트를 사용할 수 없을 때 더 이상 종료되지 않습니다. 세 가지 연결된 변경 사항:

  1. 바인드 실패 시 자동 대체. HttpServer.Start는 구성된 포트를 시도하고, SocketException이 발생하면 최대 20개 포트까지 위로 스캔하여 첫 번째 사용 가능한 포트를 찾습니다. 실제로 바인딩된 포트는 새로운 Plugin.boundServerPort에 기록되며, 서버를 광고하는 모든 것 - SSDP LOCATION URL(NOTIFY + M-SEARCH 응답), 장치 URL(PrimaryHostUrl), 라우터 포트 포워드, SSDP/제어 지점 자체 필터 -은 이제 Settings.ServerPort 대신 boundServerPort를 읽습니다. UPnP 클라이언트는 SSDP를 통해 실제 포트를 검색하므로 포트가 이동해도 렌더러에는 투명합니다.
  2. 사용자 알림. 대체가 발생하면(저장된 포트가 사용 중인 포트가 아님) 현지화된 MessageBox(WarnPortInUse)가 사용자에게 실제로 서비스 중인 포트와 장치가 여전히 해당 포트를 찾을 수 있음을 알려줍니다. 플러그인이 헤드리스로 실행되고 대화 상자 내 메시지는 이미 문제를 의심하는 사람만 볼 수 있기 때문입니다.
  3. 다시 시작 복구. RestartServer(설정 저장 다시 시작 경로)는 Plugin.controller / Plugin.server를 맹목적으로 역참조했습니다. 초기 Initialise가 생성하기 전에 예외를 발생시킨 경우(바인드 실패가 정확히 원인), 다음 설정 저장 시 NullReferenceException이 발생하여 플러그인이 반쯤 죽은 상태로 남았습니다. 이제 Nothing일 때 다시 생성하고 시작하므로 작동하는 포트를 저장하면 MusicBee를 완전히 다시 시작하지 않고도 플러그인이 복구됩니다.

왜: 트리거는 실제 사용자 사고였습니다. 이전 기본 포트 49382는 Windows 동적 범위(49152-65535)에 있으며, Hyper-V/WSL2/Docker/WinNAT은 각 부팅 시 이동하는 큰 블록을 예약합니다. 따라서 몇 달 동안 작동했던 컴퓨터에서 WSAEACCES("액세스 금지")로 바인딩이 실패했습니다. 기본값을 사용 가능한 포트로 변경한 다음 Serviio(새 포트에 이미 있는 별도의 DLNA 서버)와 충돌하여 WSAEADDRINUSE로 실패했습니다. 각 실패는 Initialise에서 삼켜져 플러그인이 자동으로 종료된 다음 다음 설정 저장 시 NRE가 발생했습니다. N04 이후 포트 충돌은 자체 복구됩니다. 서버는 다음 사용 가능한 포트에서 계속 실행되고 사용자에게 알림이 전달되며 클라이언트는 다시 검색합니다. 전체 플러그인을 종료하는 대신에 말입니다.

구현:

  • 기본 포트가 49382 → **9779**로 이동했습니다(동적 범위 아래이므로 Windows에서 자동으로 예약하지 않음; 알려진 미디어 서버 기본값이 아님). 세 가지 ServerPort 선언 + 설정 구문 분석 실패 대체 모두에 적용됩니다.
  • Plugin.boundServerPort(새로운 공유 필드)는 활성 수신 포트를 보유합니다. activeServerPort구성된 스냅샷으로 유지되므로 "다시 시작 필요" 배지 논리가 대체 시 잘못 트리거되지 않습니다.
  • HttpServer.PortScanRange = 20; 스캔은 첫 번째 성공적인 TcpListener.Start()에서 중지되고 모든 시도가 실패한 경우에만 마지막 예외를 발생시킵니다.
  • 새로운 EN 리소스 키 WarnPortInUse(번역은 게시 시간 로케일 통과를 따릅니다).

N05 - 멀티캐스트 그룹을 통한 SSDP 알림 (VPN / 지점 간)

무엇: SSDP 알림은 IP 브로드캐스트 주소 대신 UPnP 멀티캐스트 그룹(239.255.255.250)으로 전송됩니다. SSDP 검색 응답이 서버 다시 시작과 경쟁할 때 기록되는 무해한 "폐기된 개체에 액세스할 수 없습니다" 오류도 억제됩니다.

왜: 지점 간 / VPN 네트워크 어댑터에서는 IP 브로드캐스트가 적용되지 않습니다. 이전 브로드캐스트 전송은 "잘못된 인수"로 실패하고 알림이 누락되어 플러그인이 해당 링크의 클라이언트에게 보이지 않았습니다. 적절한 멀티캐스트 그룹에 알림을 보내는 것이 바로 해당 어댑터에서 검색을 수정합니다.

라이브러리 탐색

이것들은 이 포크에 포함되었으며 원본 플러그인에는 없습니다. 실제 UPnP 클라이언트에서 플러그인의 자체 출력을 실제로 탐색하여 얻은 것입니다.

N06 - 필터 기반 라이브러리 노출

무엇: MusicBee의 필터 탭(사용자 MusicBee 폴더의 .xautopf 파일)은 플러그인 라이브러리에서 UPnP 루트 컨테이너가 됩니다. 각 필터의 트랙은 AlbumArtistSort → Album → Tracks 계층 구조로 탐색할 수 있습니다.

왜: 큐레이션된 MusicBee 필터(예: "별 5개 트랙", "최근 추가됨", "클래식 → 바로크")를 사용하는 사용자는 UPnP 클라이언트에서 플러그인을 탐색할 때 해당 필터를 찾을 것으로 예상합니다. 원본 플러그인은 원시 라이브러리 트리만 노출했습니다.


N07 - SortAlbumArtist 필드 연결

무엇: 플러그인은 이제 MusicBee의 MetaDataType 165(앨범 아티스트 정렬)를 읽고 이를 사용하여 탐색 보기에서 아티스트를 그룹화/정렬합니다.

왜: 하이파이 브라우저와 오디오 애호가는 라이브러리를 정리하기 위해 정렬 아티스트 이름("베토벤, 루드비히 반" 대신 "루드비히 반 베토벤")을 사용합니다. 진지한 청취자에게는 표준적인 기대치입니다. 두 업스트림 모두에서 누락되었습니다.


N08 - 다중 값 AlbumArtist 처리

무엇: 앨범의 AlbumArtist 필드에 "; "로 구분된 여러 아티스트(예: "yaiol; Ars Ricercata")가 포함된 경우, 트랙은 이제 이름을 결합한 단일 프랑켄슈타인 아티스트 아래가 아니라 탐색 보기에서 아티스트 아래에 나타납니다.

왜: 공동 작업 앨범과 컴필레이션은 모든 공동 작업자 아래에 표시되어야 합니다. 이것이 없으면 앨범을 찾는 검색 경로의 절반이 깨집니다.


N09 - 앨범 컨테이너 아트워크 (upnp:albumArtURI)

무엇: DIDL 탐색 응답의 앨범 컨테이너 노드에는 앨범 표지를 가리키는 upnp:albumArtURI 요소가 포함됩니다.

왜: 이것이 없으면 UPnP 클라이언트의 탐색 보기에서 모든 앨범이 앨범 표지 대신 일반 아이콘을 표시합니다. 탐색을 위한 시각적 단서이며 모든 최신 하이파이 브라우저에서 예상됩니다.


N10 - 필터 앨범 내 트랙 순서

무엇: 필터로 노출된 앨범 내의 트랙은 이제 디스크 번호, 그 다음 트랙 번호로 정렬됩니다.

왜: 표준 앨범 순서입니다. 명시적인 정렬이 없으면 트랙은 필터가 반환하는 순서대로 돌아왔으며, 일반적으로 무작위로 보였습니다.


N11 - 재생 목록 폴더 트리 수정

무엇: LoadLibraryPlaylists 함수(원래 Steven Mayall, ~2014)는 새로 생성된 재생 목록 폴더로 내려가지 못했습니다. 각 폴더의 첫 번째 재생 목록과 모든 하위 폴더는 루트 수준에서 고아로 끝났습니다.

왜: 원본 플러그인에 11년 동안 존재했습니다. BubbleUPnP를 열고 재생 목록을 클릭한 지 30초 이내에 볼 수 있었습니다. yaiol에서는 트리 구성 중에 새로 생성된 폴더로 올바르게 재귀하여 수정되었습니다.


N12 - XML-불법 제어 문자 정리

무엇: C0 제어 문자(예: 잘못된 인코딩 통과로 인한 0x19 - UTF-8 → Latin-1 → 0x990x19로 잘라냄)를 포함하는 태그가 있는 트랙은 잘못된 트랙이 페이지 매김된 배치에 들어가면 전체 탐색 응답이 Action Failed로 실패했습니다.

왜: XML 1.0은 대부분의 C0 제어 문자를 금지하며, XmlWriter는 이를 쓰도록 요청받으면 예외를 발생시킵니다. 원본 플러그인에 존재했습니다. XmlConvert.IsXmlChar를 통해 모든 Library_GetFileTags 종료 지점에서 잘못된 문자를 제거하여 수정되었습니다.


N13 - 페이지 매김된 탐색에서 라디오 목록 결정론적

무엇: 라디오 컨테이너에 대한 탐색은 일반 파일 목록 분기로 들어갔고, 이는 모든 호출에서 files.Sort(AlbumFileComparer)를 호출했습니다. 라디오 항목은 앨범/디스크/트랙 태그가 비어 있으므로 모든 비교는 0을 반환했습니다. List(Of T).Sort는 불안정하여 호출할 때마다 다른 순서를 생성했습니다. UPnP 제어 지점은 페이지 매김을 합니다(BubbleUPnP는 0..15를 가져온 다음 16..끝을 가져옴). 두 호출 사이에 목록이 재정렬되어 일부 스테이션은 두 페이지 모두에 나타나고(중복) 일부는 어느 페이지에도 나타나지 않아(누락) 새로 고칠 때마다 무작위로 보였습니다.

왜: 원본 플러그인에 존재했습니다(작성자는 UPnP를 통해 라디오를 탐색하지 않았습니다). 여기서는 Browse에 전용 ContainerCategory.Radio 분기를 사용하여 수정되었으며, 호출당 정렬은 없습니다. radioFiles는 로드 시 제목별로 한 번 정렬됩니다(안정적). 페이지 매김된 탐색은 이제 결정론적 순서를 봅니다. 페이지 1과 페이지 2는 서로 다릅니다.


N14 - UPnP 검색 앨범 클래스는 앨범 컨테이너를 반환합니다.

무엇: 앨범 클래스 쿼리(upnp:class = "object.container.album.musicAlbum", 예: BubbleUPnP의 "랜덤 앨범")에 대한 UPnP 검색은 앨범 컨테이너 대신 전체 트랙 목록을 반환하여 클라이언트가 앨범을 0개 표시했습니다. 원본 핸들러는 괄호로 묶인 기준만 구문 분석한 다음 요청된 클래스에 관계없이 모든 트랙을 덤프했습니다.

왜: 여기에서 수정되었습니다. 앨범 클래스 쿼리는 이제 고유한 앨범(앨범 아티스트 + 앨범별로 그룹화됨)을 열거하고 각각을 표지 아트가 있는 적절한 musicAlbum 컨테이너로 내보냅니다. Salb<idx> 가상 ID 공간을 통해 주소 지정할 수 있으므로 클라이언트가 결과로 드릴다운하여 재생할 수 있습니다.


N15 - 클릭 스루가 있는 작동하는 범위 인식 UPnP 검색

무엇: 원본은 검색 기능이 없다고 광고하여(GetSearchCapabilities는 비어 있음) 클라이언트가 검색을 보내는 것조차 거부했습니다. 그리고 이전 백엔드는 지연 트리 시대에는 영구적으로 비어 있는 musicFiles에서 읽었습니다. 이 포크는 실제 검색 가능한 속성을 광고하고, 지연 라이브러리에 대해 제목별 트랙 및 제목별 앨범을 구현하고(HandleLazySearch), 실제 컨테이너 ID가 전송될 때 클라이언트의 현재 분기로 쿼리 범위를 지정하며(그렇지 않으면 L:music을 대체하여 상단 바 검색이 팟캐스트/라디오/오디오북 노이즈를 끌어들이지 않도록 함), Browse 초기 분기가 앨범의 트랙으로 다시 매핑하는 합성 Ssrch_alb_* ID를 통해 앨범 결과를 클릭 가능하게 만듭니다. (앨범 클래스 결과가 컨테이너로 표시되는 부분은 N14입니다.)

왜: BubbleUPnP의 검색은 "라이브러리가 검색을 지원하지 않습니다"에서 유용하고 범위가 지정된 재생 가능한 결과를 반환하는 것으로 바뀌었습니다. 전체 디자인 + 거부된 접근 방식: SEARCH.md.


N16 - UPnP 캐시 무효화 (SystemUpdateID)

무엇: 원본은 상수 SystemUpdateID=0을 반환했습니다. 이는 UPnP ContentDirectory 캐시 무효화 계약이므로 사양 준수 클라이언트(BubbleUPnP)는 라이브러리를 변경되지 않는 것으로 취급했습니다. 오래된 탐색 결과, URL 스키마 변경 후 404 썸네일, "변경 사항을 보려면 MusicBee를 두 번 다시 시작"하는 과정이 있었습니다. 이 포크는 로드 시 epoch-초에서 SystemUpdateID를 시드하고(따라서 모든 다시 시작은 마지막보다 엄격하게 앞섭니다) 모든 라이브러리 변경 및 설정 변경 시(SetLibraryDirty / ResetCacheBumpSystemUpdateId) 이를 증가시킵니다.

왜: 클라이언트는 다음 탐색 시 편집, 새 파일 및 설정 변경 사항을 안정적으로 가져옵니다. 알려진 제한: 구독된 클라이언트는 GENA를 통해 새 값을 적극적으로 다시 푸시받지 않습니다(향후 작업으로 보류됨). 다음 탐색 시 여전히 새 값을 봅니다.


N17 - 팟캐스트 구독 아트워크

무엇: 팟캐스트 타일에 이미지가 표시되지 않았습니다. 모든 /PodcastThumbnail/ 요청이 404를 반환했습니다. 두 가지 중첩된 버그: 해상도 체인이 MusicBee의 실제 아트워크 캐시(%LocalAppData%\MusicBee\InternalCache\Subscriptions\<name>.jpg, 데스크톱 UI가 로드하는 위치)를 확인하지 않았고, HTTP 계층의 이스케이프 해제 + 소문자화가 피드 URL 경로 키를 마지막 경로 세그먼트까지 손상시켰습니다. 이 포크는 MB의 InternalCache에서 아트워크를 해결하고 HTTP 계층을 손상시키지 않고 유지되는 URL 안전 슬러그(PodcastSlug / podcastSubIdBySlug)를 통해 조회를 라우팅합니다.

왜: 구독 아트워크가 이제 탐색 보기에 렌더링됩니다(이전 404 요청 22개 모두 해결됨).


N18 - 계층적 (구분된) 태그 탐색

무엇: 라이브러리 옵션 탭에서 모든 필드를 계층적으로 표시하고 한 문자 구분 기호를 지정할 수 있습니다(추가/제거가 있는 필드 선택기 + 구분 기호 상자, 플러그인 설정에 유지됨). 그룹화/로 설정하고 Jazz/Cool Jazz와 같은 값을 지정하면 단일 평면 항목 대신 Jazz › Cool Jazz로 탐색됩니다. 브랜치에 정확히 태그가 지정된 트랙(그냥 Jazz)은 자체 [Jazz] 노드를 가져와 아무것도 숨겨지지 않으며, 단일 자식이 있는 브랜치는 자체적으로 축소되고, ;는 MusicBee 자체의 다중 값 구분 기호이므로 구분 기호로 거부됩니다.

왜: 사용자가 이미 단일 필드에 인코딩한 심층 태그 분류(장르 트리, 분위기 계층, "클래식/바로크/협주곡")는 사용자가 처음부터 끝까지 읽어야 하는 슬래시로 구분된 문자열의 평면 벽 대신 태그가 설명하는 트리로 마침내 탐색됩니다.


N19 - 그룹화 필드로 레이블이 지정된 단일 루트 경로

무엇: 루트의 단일 탐색 경로는 전체 짧은 경로 대신 그룹화 필드(예: "장르")로 레이블이 지정되어 병합된 첫 번째 필드 그룹의 이름 지정 방식과 일치합니다.

왜: 탐색 트리가 일관되게 읽힙니다. 루트 항목이 단독으로 있든 형제 항목과 병합되었든(N20) 하나의 이름 지정 규칙이 적용됩니다. 병합된 이웃이 깔끔한 필드 이름을 표시하는 동안 단독 루트 항목이 장황한 내부 경로를 표시하는 대신에 말입니다.


N20 - 첫 번째 필드를 공유하는 탐색 경로 병합

무엇: 동일한 첫 번째 필드를 공유하는 두 탐색 경로("장르 / 앨범 아티스트 정렬" 및 "장르 / 팟캐스트 인물")는 단일 장르 루트 폴더로 축소되어 장르 값을 먼저 나열한 다음 두 보기로 분할됩니다. 루트에 거의 중복되는 두 개의 "장르 / …" 항목이 나란히 있는 대신에 말입니다.

왜: 공통 필드 아래에 여러 관련 보기가 중첩된 사용자는 루트가 거의 동일한 최상위 항목으로 어수선하게 표시되는 것을 보았습니다. 이를 병합하면 루트를 읽기 쉽게 유지하고 관련 보기를 해당 위치(공유 필드 아래)에 그룹화합니다.


N21 - 카테고리 유형 탐색 경로 (표준 / 라디오 / 팟캐스트)

무엇: 모든 탐색 경로는 표준, 라디오 또는 팟캐스트 카테고리로 유형이 지정됩니다. 템플릿 목록은 이 세 섹션으로 그룹화되며, 각 템플릿의 필드 선택기는 해당 카테고리 데이터가 실제로 제공할 수 있는 필드만 제공하고, 템플릿은 보기 트리의 일치하는 노드에만 적용할 수 있습니다(호환되지 않는 노드는 회색으로 표시되어 선택할 수 없음). 예약된 라디오 및 팟캐스트 템플릿은 삭제할 수 없으므로 해당 카테고리 섹션은 사라지지 않습니다.

왜: 유형 지정이 없으면 사용자는 자동으로 비어 있는 레이아웃을 만들 수 있습니다. 라디오 방송국에는 "앨범"이 없고, 팟캐스트 에피소드에는 "앨범 아티스트"가 없습니다. 그리고 UPnP 클라이언트에서 죽은 폴더로 탐색해야만 이를 발견할 수 있습니다. 필드 메뉴와 적용 대상을 카테고리의 실제 데이터로 제한하면 비어 있는 레이아웃을 만들 수 없습니다.


N22 - 팟캐스트를 게시 연도별로 그룹화

무엇: 각 팟캐스트 에피소드의 게시 날짜가 읽혀지므로, 연도 수준이 있는 팟캐스트 탐색 경로는 에피소드를 단일 "알 수 없음" 아래로 축소하는 대신 연도별로 분류합니다.

왜: 대규모 팟캐스트 구독은 플러그인이 에피소드별 게시 날짜를 확인하지 않았기 때문에 모든 에피소드가 하나의 날짜 없는 더미에 들어가는 대신 나머지 라이브러리처럼 연도별로 탐색할 수 있게 됩니다.


N23 - 단일 결과 그룹화 수준 축소

무엇: 단일 값으로 해결되는 그룹화 수준(LP만 만든 아티스트에 대해 "LP"만 표시하는 레코드 유형 수준 또는 단일 문자가 있는 문자 수준)은 자동으로 건너뛰어 사용자를 해당 콘텐츠로 직접 이동시킵니다.

왜: 정확히 하나의 폴더가 포함된 폴더를 탐색하는 것은 순수한 마찰입니다. 단일 선택 수준을 축소하면 사용자가 도달할 수 있는 것을 변경하지 않고 불필요한 클릭을 제거합니다.


N24 - MusicBee의 날짜 필드에 대한 연도 그룹화/검색

무엇: 연도 조건은 더 이상 MusicBee의 전체 날짜 "연도" 필드를 단순한 네 자리 값으로 쿼리하지 않으며, 하드 코딩된 연도 필드 별칭이 사라져 모든 그룹화 필드가 이제 경로 정의에서 일반적으로 해결됩니다.

왜: 연도 태그에 전체 날짜가 포함된 라이브러리의 경우, 연도별로 그룹화하거나 검색하면 이전에 아무것도 반환되지 않았습니다. 네 자리 쿼리가 전체 날짜 필드와 일치하지 않았기 때문입니다. 올바른 필드를 쿼리하면 연도 그룹화 및 검색이 다시 트랙을 찾습니다.


N25 - "연도" 및 "연도 (yyyy)" 그룹화 필드 분리

무엇: 앨범 그룹화 및 탐색 경로는 이제 MusicBee 자체의 두 가지 연도 필드인 연도(전체 날짜 태그)와 연도 (yyyy)(네 자리 연도만)를 모두 노출하므로 사용자는 앨범 그룹화 또는 탐색 경로를 정의할 때 둘 중 하나를 선택할 수 있습니다.

왜: 두 필드는 MusicBee에서 다른 의미를 가지며, 이를 축소하면 그 구별이 사라졌습니다. 둘 다 표시하면 사용자가 의도한 대로 한 해의 모든 릴리스를 함께 모으거나(yyyy) 정확한 날짜 순서를 유지할 수 있습니다(전체 연도 태그).


N32 - 루트에서 종류별로 그룹화된 고정 필터 및 재생 목록

무엇: 고정된 필터는 이제 탐색 루트의 필터 폴더 바로 아래에 나타나고, 고정된 재생 목록은 재생 목록 폴더 바로 아래에 나타납니다. 모든 고정 항목이 루트 끝에 한 덩어리로 모이는 대신에 말입니다. 각 고정된 바로 가기는 자체 종류와 함께 있습니다.

왜: 사용자가 더 많은 바로 가기를 고정할수록 혼합된 필터와 재생 목록의 단일 후행 덩어리는 스캔하기 어려워지고 각 바로 가기를 속한 폴더에서 분리합니다. 고정된 항목을 자체 카테고리 아래에 그룹화하면 루트를 읽기 쉽게 유지하고 각 바로 가기를 속한 항목 옆에 유지합니다.

설정 대화 상자 및 패키징

N26 - 섹션별 설정 대화 상자

무엇: 환경 설정 페이지에 일반 / 재생 / 라이브러리 / 장치 프로필 / 진단 섹션이 있는 왼쪽 탐색 레이아웃이 추가되었습니다.

왜: 원본은 모든 설정의 단일 길고 평평한 목록이었습니다. 이를 만든 개발자에게는 괜찮았지만 다른 모든 사람에게는 혼란스러웠습니다. 섹션별 그룹화는 관련 옵션을 그룹화하고 대화 상자를 최신 앱 설정처럼 느끼게 합니다.


어셈블리 + 플러그인 이름 변경 (F-id 없음 - 패키징 참고)

무엇: 컴파일된 DLL은 mb_UPnP_yaiol.dll로 명명되며 플러그인은 자체를 "MusicBee UPnP (yaiol)"로 보고합니다. 원본 mb_Upnp.dll과 다릅니다.

왜: 사용자는 yaiol을 원본 플러그인과 함께 설치하고 동작을 나란히 비교할 수 있습니다.


배지 시스템 - 런타임 상태 표시 (F40의 메커니즘)

무엇: 중요한 런타임 조건을 설정 대화 상자에 눈에 띄는 색상 배지로 표시하는 일반적인 UI 패턴입니다. 현재 인스턴스:

  • ⚠ 최대 연결 (N04) - MusicBee 시작 이후 최대 연결 제한에 한 번 이상 도달했을 때 발생합니다. 고정 세션 플래그 Plugin.MaxConnectionsHit. 사용 가능한 슬롯이 없을 때 WaitOnSendBarrier 내에서 설정됩니다.
  • ⚠ 다시 시작 필요 - 저장된 설정이 적용되려면 MusicBee를 다시 시작해야 할 때 발생합니다. 고정 세션 플래그 Plugin.RestartRequired. 새 영구 값이 런타임 스냅샷(Plugin.activeMaxConnections, Plugin.activeServerPort, Plugin.activeIpAddress)과 다를 때 대화 상자의 저장 핸들러에서 설정됩니다. 다시 시작이 필요한 설정은 실제로 핫 리로드할 수 없는 설정(HTTP 서버 바인드 매개변수 및 Initialise에서 한 번 구축된 SemaphoreSlim)으로 제한됩니다.

왜: 플러그인의 로그 파일은 기술 사용자가 디버깅하는 데는 좋지만, "장치 소리가 이상하다" 또는 "재생이 느리다"를 보는 비기술 사용자는 진단 → 로그 보기를 열지 않을 것입니다. 배지는 사용자가 무언가 발생했음을 알아야 하는 경우를 포착하고 다음에 플러그인을 열 때 이를 표시합니다. 아무것도 읽지 않고도 발견할 수 있습니다.

향후 재사용 가능:

  • 프로필 불일치 감지됨 (장치 사용자 에이전트가 어떤 프로필과도 일치하지 않아 일반으로 대체됨).
  • NextURI 백오프 트리거됨 (F13 - 불안정한 장치에서 세션 동안 무음 재생 비활성화됨).
  • 라이브러리 스캔 실패 / 부분적.
  • 세션 중간에 렌더러 연결 끊김.
  • "한 번 발생했고 사용자가 알아야 함"이 "다른 1000줄 사이에 자동으로 기록됨"보다 나은 다른 조건.

구현 규칙:

  • 배지 레이블은 대화 상자 수준(어떤 패널 내부가 아님)에 있으므로 사용자가 어떤 섹션에 있든 관계없이 표시됩니다.
  • 저장/취소 근처의 하단 행을 따라 배치됩니다(현재: y=410 가로로 쌓임).
  • 각 배지에는 Plugin에 해당 고정 세션 플래그가 있으며, 조건이 발생하면 True로 전환되고 MusicBee를 다시 시작할 때만 재설정됩니다.
  • 리소스: <Condition>Badge(레이블 텍스트, ⚠ 접두사) + <Condition>BadgeTip(원인 + 해결책을 설명하는 도구 설명).
  • "저장된 설정에 다시 시작 필요" 배지의 경우, Plugin.Initialise()에서 런타임 스냅샷을 찍고 대화 상자 저장 핸들러에서 Settings.SaveSettings()Settings.*와 비교합니다.

N27 - 취소 시 경로/템플릿 편집 내용 삭제

무엇: 설정 대화 상자 내에서 경로 및 템플릿에 대한 편집 내용은 사용자가 취소를 클릭하면 자동으로 적용되는 대신 삭제되며, 세션 중에 제거된 예약된 템플릿은 다시 생성됩니다. (템플릿은 편집될 때 실시간으로 저장됩니다. 경로 탭에는 별도의 저장 버튼이 없습니다.)

왜: 취소는 취소를 의미해야 합니다. 이전에는 경로/템플릿 변경을 실험하고 취소한 사용자가 변경 내용이 이미 커밋되었음을 발견했으며, 수동으로 다시 실행하는 것 외에는 실행 취소할 방법이 없었습니다.


N28 - 필드 선택기 메뉴의 리터럴 앰퍼샌드

무엇: 이름에 "&"가 포함된 필드(예: "Mood & Context")는 Alt-니모닉 접두사로 삼키는 대신 필드 선택기 메뉴에 앰퍼샌드를 리터럴로 렌더링합니다.

왜: 앰퍼샌드가 있는 필드 이름이 잘못 표시되어(문자가 사라지고 다음 문자가 가속기가 됨) 메뉴 항목을 인식하기 어려웠습니다.


N29 - 안정적이고 번역되지 않은 설정 창 제목

무엇: 설정 창 제목은 "MusicBee UPnP Plugin" 브랜드 문자열로 고정되었으며 더 이상 인터페이스 언어에 따라 변경되지 않습니다. 언어별 DialogTitle 문자열은 모든 로케일 번들에서 제거되었습니다.

왜: 언어별로 문구가 변경되는 창 제목은 번역 가능한 표면이었지만 이점이 없었습니다. 제목은 브랜드 마크입니다. 이를 고정하면 모든 곳에서 안정적이고 일관되게 유지됩니다.

현지화

원본 플러그인은 영어 전용입니다. 이 포크는 완전히 현지화 가능합니다. 모든 사용자 대면 문자열은 리소스 번들을 통해 흐르며, 플러그인은 MusicBee 자체의 UI 언어를 자동으로 감지합니다.

N30 - 다국어 UI (번역 보류 중)

무엇: 현지화 메커니즘이 완료되어 배송 중입니다. Localisation.vbMusicBee3Settings.ini에서 MusicBee의 선택된 언어(SystemLanguage 내명)를 읽고 일치하는 .NET 문화를 스레드에 적용하므로 My.Resources.Resources.*는 현지화된 문자열을 반환합니다. 모든 사용자 대면 레이블/버튼/메시지는 리소스 키에 연결됩니다(디자이너 컨트롤은 ApplyDesignerExtras + sync-en-locale.js를 통해; WarnPortInUse와 같은 런타임 문자열은 수동으로 추가됨). 아직 완료되지 않은 것은 실제 번역입니다. 영어 원본 번들(Resources.resx)만 존재합니다. 다른 언어에 대한 위성 번들은 플러그인이 기능적으로 완료될 때 한 번의 배치 통과로 생성됩니다(문자열이 여전히 변경되는 동안 부분적으로 번역하면 노력이 낭비됨).

대상 언어 (MusicBee 자체에서 제공하는 세트, endonymToCulture에 의해 1:1로 일치하므로 플러그인은 MusicBee의 언어를 자동으로 따릅니다):

아랍어 (ar) 체코어 (cs) 독일어 (de) 그리스어 (el)
스페인어 (es) 프랑스어 (fr) 헝가리어 (hu) 이탈리아어 (it)
한국어 (ko) 네덜란드어 (nl) 노르웨이어 (nb) 폴란드어 (pl)
포르투갈어 BR (pt-BR) 포르투갈어 PT (pt-PT) 스웨덴어 (sv) 터키어 (tr)
우크라이나어 (uk) 러시아어 (ru) 일본어 (ja) 중국어 간체 (zh-CN)
중국어 번체 (zh-TW) 영어 (en, 원본)

변형 정책 (작업 공간 로케일 규칙에 따라): PTZH는 어휘/스크립트가 실제로 다르기 때문에 별도의 번들로 분할됩니다(pt-BR/pt-PT, zh-CN/zh-TW). EN은 단일 번들입니다. MusicBee의 "English(US)"(en-US)는 .NET의 문화 체인을 통해 en으로 대체되므로 별도의 US 번들은 생성되지 않습니다. ES 및 FR도 마찬가지로 단일 로케일입니다.

왜: UPnP 플러그인의 설정("원시 PCM 사용 안 함", "리틀 엔디언 PCM 강제", 포트 대체 경고)은 모국어로도 충분히 알기 어렵습니다. 영어를 강제하는 대신 MusicBee 자체의 UI 언어를 따르는 것은 비영어 사용자가 구성할 수 있는 도구와 구성할 수 없는 도구의 차이입니다. 어떤 업스트림도 이를 시도하지 않았습니다.


N31 - 도움말 링크가 전체 인터페이스 언어로 열림

무엇: 플러그인에서 도움말 링크를 열면 기본 언어로 축소되는 대신 사용자의 전체 인터페이스 언어(예: pt-BR, zh-CN)를 존중하고 더 명확한 업데이트 확인 식별자를 보냅니다.

왜: 지역 변형(브라질 포르투갈어, 중국어 간체)으로 MusicBee를 실행하는 사용자는 기본 언어 도움말 페이지로 이동했습니다. 전체 문화를 전달하면 사용자가 사용하는 정확한 언어로 도움말 페이지에 접속할 수 있습니다.

원본 플러그인에 대한 수정 및 개선 사항

핵심 프로토콜 및 재생

F01 - 업데이트된 기본 DLNA 장치 프로필

무엇: PlayStation 4, Xbox 360/One 및 최신 BubbleUPnP에 대한 새로운 기본 프로필을 제공하며, 해당 장치가 오늘날 실제로 지원하는 기능 플래그(샘플 속도, 비트 깊이, 코덱)를 반영합니다.

왜: 원본 플러그인의 기본값은 2014년경에 고정되었습니다. PS4/Xbox/BubbleUPnP는 이후 고해상도 오디오 지원을 얻었습니다. 새 설치 시 장치 프로필 설정을 건드리지 않고도 이러한 장치에서 최고 수준의 재생을 제공합니다.


F02 - MediaRenderer:3을 광고하는 장치 제어

무엇: 플러그인은 렌더러의 UPnP 서비스 설명을 조사하여 MusicBee가 이를 구동할 수 있는지 여부를 결정합니다. 원본은 urn:schemas-upnp-org:device:MediaRenderer:1만 일치시켰습니다. 최신 장치는 :2 또는 :3을 광고합니다. F02는 일치 범위를 넓힙니다.

왜: 이것이 없으면 최신 Sonos / WiiM / Eversolo 장치는 동일한 프로토콜을 사용하더라도 MusicBee의 "재생 대상" 장치 목록에 대상으로 표시되지 않습니다. 단일 문자열 접두사 일치 수정으로 전체 최신 장치 세대가 잠금 해제됩니다.


F03 - 프로필별 "원시 스트림 강제" 옵션 (기본값 ON)

무엇: 선택하면 플러그인은 트랜스코딩, DSP, ReplayGain 처리 없이 원본 파일 바이트를 장치로 보냅니다. 사용자가 선택한 원본 파일 그대로, 바이트 단위로(HTTP 프레이밍 제외).

왜: 포럼 증언에 따르면 이것이 가장 큰 재생 품질 향상입니다. 고가의 렌더러를 구매하는 하이파이 사용자는 명시적으로 비트 완벽한 출력을 원합니다. 어떤 DSP 터치도 그 목적을 무력화합니다. 대부분의 최신 장치는 사용자가 던지는 모든 코덱을 처리하고 ReplayGain/EQ는 선택 사항이어야 하므로 기본적으로 ON입니다. 프로필별로 설정되므로 하이파이 DAC로 원시 스트림을 보내는 동안 구형 Xbox용으로 트랜스코딩을 유지할 수 있습니다.


F04 - 프로필별 "트랜스코딩 강제"

무엇: 원시 코덱 지원 여부에 관계없이 이 장치로 모든 스트림을 트랜스코더를 통해 강제하는 프로필별 재정의입니다. F03(원시 스트림 강제)의 반대입니다. F03과 상호 배타적입니다. 둘 중 하나를 켜면 UI가 자동으로 다른 하나를 선택 해제합니다.

왜: 단일 전역 토글은 프로필별 원시 스트림 강제(F03)와 모순될 것입니다. 실제 사례: 장치 A는 비트 완벽한 원시 스트림을 원하는 하이파이 DAC입니다. 장치 B는 FLAC에서 작동하지 않는 구형 AV 리시버입니다. 전역 토글을 사용하면 사용자는 다른 장치를 희생하여 선택해야 합니다. 프로필별로 각 장치는 올바른 응답을 얻습니다.

구현:

  • StreamingProfile.ForceTranscoding As Boolean = False.
  • 영구 스키마가 v9로 업그레이드되었습니다. v9 이전 파일은 레거시 전역 값을 한 번 로드하고 모든 프로필에 복사하여 업그레이드를 통해 이전 동작을 유지합니다.
  • UI: 진단 패널에서 제거되고, 장치 프로필 섹션에 원시 스트림 강제 옆에 추가되었습니다. 양방향 상호 배타 핸들러(각각의 CheckedChanged는 무한 루프를 피하기 위해 전환하기 전에 다른 하나를 구독 해제함).
  • 결정 지점: Settings.ForceTranscodingWriteAudioFileDIDLstreamingProfile.ForceTranscoding.

F05 - 프로필별 "리틀 엔디언 PCM 강제"

무엇: PCM 스트림(L16/L24 MIME 유형)은 사양상 빅 엔디언입니다. 일부 장치는 잘못 리틀 엔디언을 예상하고 올바른 빅 엔디언 데이터를 받으면 백색 소음을 재생합니다. F05는 프로필별로 바이트 순서를 전환합니다.

왜: 이것이 없으면 특정 장치는 정적 벽을 출력합니다. 증상은 극적이며 PCM 인코딩에 대한 지식 없이는 원인을 알 수 없습니다. 이 토글은 사용자에게 추측 및 확인 수정 기능을 제공합니다.


F06 - 프로필별 "원시 PCM 사용 안 함"

무엇: 장치가 원시 PCM을 지원한다고 주장하면 플러그인은 이를 사용합니다. 일부 장치는 거짓말을 합니다. SOAP 핸드셰이크를 수락하지만 실제 원시 PCM 데이터를 손상시키는 반면, WAVE 컨테이너에 래핑된 PCM은 제대로 처리합니다. F06은 장치가 광고하는 내용에 관계없이 PCM-over-Wave를 강제합니다.

왜: 특히 특정 Marantz 모델의 경우 원시 PCM을 광고하지만 WAVE만 작동합니다. 이것이 없으면 원시 PCM 스트림이 왜곡되어 나오며, 오류 메시지가 표시되지 않습니다.


F07 - 프로필별 "콘텐츠 길이"

무엇: HTTP Content-Length 헤더에 보낼 값. 네 가지 옵션:

  • 기본값 - 알려진 경우 실제 바이트 수, 알 수 없는 경우 생략.
  • 없음 - 헤더를 보내지 않음 (청크 인코딩만).
  • PCM만 - 원시 PCM에만 보냄; 다른 모든 것은 생략.
  • 고정 - UInt32.MaxValue - 8192를 보냄 ("매우 큰 알 수 없는 길이"에 대한 센티넬).

왜: UPnP/DLNA 장치는 Content-Length에 대한 반응이 매우 다양합니다. 일부는 정확한 숫자가 필요하고, 일부는 스트림에서 이를 싫어하며, 일부는 버퍼링을 유지하기 위해 센티넬 "정말 큰" 값이 필요합니다. 이 문제는 나중에 PCM 전용에서 모든 출력 형식으로 확장되었습니다. 동일한 문제가 트랜스코딩된 MP3/AAC 스트림에서도 나타났기 때문입니다.


F08 - 프로필별 "NextURI 지우지 않음"

무엇: 일반적으로 플러그인은 대기열이 비워지면 장치의 대기열에 있는 NextURI를 지웁니다(빈 URL로 SetNextAVTransportURI를 보냄). 일부 장치(특히 Denon)는 빈 NextURI를 "모든 것을 중지"로 해석하고 즉시 재생을 중단합니다. F08은 플러그인이 이를 지우는 것을 방지합니다.

왜: 이것이 없으면 Denon 소유자는 대기열이 비워질 때 장치가 트랙 중간에 끊어지는 것을 경험합니다. F08이 선택되면 장치는 오래된 NextURI를 메모리에 유지합니다(무해합니다. 다음에 무언가가 대기열에 추가될 때 덮어쓰기만 됩니다).


F09 - 트랜스코딩 출력 형식으로 FLAC

무엇: 장치 프로필의 트랜스코딩 형식 드롭다운에 이제 PCM 16/24, MP3, AAC, Ogg와 함께 FLAC이 제공됩니다. 이를 선택하면 BASS 인코더가 MusicBee의 표준 FLAC 변환 명령줄을 통해 라우팅됩니다(MP3/AAC/Ogg가 이미 사용하는 것과 동일한 메커니즘).

왜: FLAC을 잘 처리하지만 소스 코덱을 디코딩할 수 없는 장치(예: MusicBee의 WMA 라이브러리를 FLAC으로 변환하여 수신하는 Eversolo)의 경우, MP3/AAC가 오디오 데이터를 버리는 반면, 이는 무손실 품질을 보존합니다. N02(5.1 다운믹스 제어)를 차단 해제합니다. 이는 무손실 트랜스코딩 옵션 없이는 해결할 수 없었습니다.

구현: Encoder.StartEncodeSelect Case Codec 블록에 한 줄 추가 - FLAC이 명령줄 기반 분기에 MP3/AAC/Ogg와 함께 추가됩니다. UI 드롭다운에 "FLAC"이 6번째 옵션으로 추가됩니다. SettingsDialog의 로드/저장 매핑은 FileCodec.FlacSelectedIndex = 5를 인식하도록 확장됩니다. MIME, DLNA 유형 및 인코딩 기능은 이전 작업(F21, F26)에서 ItemManager.GetMimes / GetDlnaType / GetEncodeFeature에 이미 연결되어 있었습니다.


무음 재생 (SetNextAVTransportURI)

F10 - SetNextAVTransportURI / NextURI 코어

무엇: 진정한 무음 재생. 장치가 UPnP 서비스 설명에서 SetNextAVTransportURI 지원을 광고하면 플러그인은 현재 트랙이 끝나기 전에 장치에 다음 트랙을 미리 대기열에 추가합니다. 장치는 트랙 사이에 들리는 간격 없이 내부적으로 전환됩니다. CD 플레이어에서 듣는 것과 같습니다. 이것은 "연속 스트림" 해킹(모든 것을 하나의 긴 스트림으로 연결하고 트랙별 메타데이터를 손실함)이 아닙니다.

왜: 주력 Tier-2 기능입니다. 연속 라이브 공연으로 녹음된 앨범(라이브 레코드, 클래식 악장, DJ 세트)은 트랙 사이에 0.5초의 침묵이 있으면 이상하게 들립니다. 이를 제대로 해결하는 것은 주력 기능이며, 이제 이 포크에 있습니다.

참고: 대기열에 있는 오디오는 플러그인의 HTTP 서버를 통해 streamHandle=0(라이브러리 가져오기 모드)을 사용하여 제공됩니다. 이는 MusicBee의 오디오 엔진이 대기열에 있는 트랙에 대해 루프에 있지 않음을 의미합니다. 트레이드오프: ReplayGain/DSP/EQ 효과는 다음 트랙에 적용되지 않습니다. "원시 스트림 강제"가 켜져 있을 때(기본값) 허용됩니다.


F11 - 프로필별 "NextURI 지원 비활성화"

무엇: 장치가 SetNextAVTransportURI를 광고하더라도 이 확인란은 플러그인이 해당 광고를 무시하고 한 번에 한 트랙씩 재생으로 대체하도록 강제합니다.

왜: 일부 장치는 NextURI를 광고하지만 버그가 있는 구현(충돌, 부분 전환, 멈춤)을 가지고 있습니다. 각 손상된 장치를 역설계하는 대신 사용자는 "여기서 그냥 끄기" 토글을 얻습니다.


F12 - 현재 재생 목록의 NextURI 수명 주기

무엇: MusicBee가 NowPlayingListChanged를 발생시키면 플러그인은 무음 전환을 위해 대기열에 추가해야 할 내용을 다시 평가합니다. MusicBee에 NowPlayingList_GetNextIndex(1) + NowPlayingList_GetListFileUrl을 통해 새로운 "다음" 트랙을 요청하고, 장치에 현재 대기열에 있는 내용(새로운 nextPlaySourceUrl 필드를 통해 추적됨)과 비교하며, 변경된 경우 다시 대기열에 추가합니다(또는 MusicBee가 다음 트랙이 없다고 말하면 대기열을 지웁니다).

왜: F12가 없으면 사용자가 대기열에 있는 트랙을 제거/재정렬할 때 장치는 오래된 NextURI를 계속 재생했습니다. 이는 모든 목록 변경에 다른 처리가 필요하기 때문에 역사적으로 여러 번의 반복이 필요했습니다. 우리는 NowPlayingList_GetNextIndex(이미 셔플 및 전체 반복 래핑을 존중함)를 신뢰하여 단순화했으므로 모든 변형은 동일한 비교를 통해 전달됩니다.

구현:

  • 새로운 nextPlaySourceUrl 필드는 대기열에 있는 MusicBee 라이브러리 URL을 저장합니다(핸들 접미사가 있는 스트리밍 URL은 라이브러리 경로와 비교할 수 없음).
  • MediaRendererDevice에 새로운 Public Sub RefreshQueuedNextUri()가 있습니다. 세 가지 결과: NextURI가 대기열에 없음 → 아무 작업 없음; 대기열에 있는 것이 새로운 "다음"과 일치함 → 아무 작업 없음; 대기열에 있는 것이 다름 → 새로운 URL로 QueueNext 호출(또는 QueueNext("")로 지우기 - 이는 F08 DoNotClearNextUri를 존중함).
  • Plugin.ReceiveNotificationNotificationType.NowPlayingListChanged 아래에 연결되었습니다.

F13 - NextURI 실패 백오프

무엇: 동일한 장치에서 4번 연속 SetNextAVTransportURI 실패 후, 플러그인은 MusicBee가 다시 시작될 때까지 해당 장치에 대한 무음 재생을 비활성화합니다.

왜: 장치가 NextURI에 대해 실제로 손상된 경우(간헐적인 SOAP 오류, 네트워크 결함) 플러그인은 그렇지 않으면 모든 트랙에서 계속 재시도할 것입니다. F13은 소음을 멈추고 자동으로 한 번에 한 트랙씩 재생으로 대체합니다.


F14 - 반복 모드 + NextURI 통합

무엇: F14는 OnAvTransportStatusCheck의 F15 전환 감지기에서 처리되는 두 가지 경우로 나뉩니다.

  • 모두 반복: MusicBee는 올바른 "래핑" URL(목록 끝의 트랙 1)을 Plugin.QueueNext 자체에 전달합니다. 특별한 플러그인 로직은 필요하지 않습니다. 장치는 해당 URL로 전환하고 F15 감지기는 평소와 같이 Player_PlayNextTrack을 호출하여 MusicBee의 NPL 인덱스를 0으로 다시 래핑합니다.
  • 하나 반복: MusicBee는 동일한 트랙 URL을 Plugin.QueueNext에 전달합니다. 장치는 해당 URL로 전환합니다(새 스트림 핸들, 동일한 소스). F15 감지기는 이제 Player_GetRepeat()을 쿼리합니다. RepeatMode.One이면 Player_PlayNextTrack 호출을 건너뛰어 MusicBee가 NPL 인덱스를 반복 트랙에서 벗어나지 않도록 합니다.

왜: 하나 반복 건너뛰기가 없으면 무음 전환 시 Player_PlayNextTrack을 호출하면 MusicBee가 목록의 다음 트랙으로 이동하여(하나 반복은 플레이어 UI의 트랙 끝 자동 진행 동작에만 영향을 미칩니다. 다음 트랙은 항상 앞으로 이동합니다) 하나 반복의 의미와 모순됩니다.

재생 횟수 주의: 하나 반복에서 재생 횟수 증가는 MusicBee 3.7.9563+가 반복 재생을 감지하는지에 따라 달라집니다. 이전 MusicBee 버전은 무음 반복을 올바르게 재생하지만 재생 횟수 증가를 놓칩니다. 문서화되었으며 차단되지 않습니다.


F15 - 트랙 전환 감지 상태 머신

무엇: 장치가 현재 트랙에서 NextURI로 내부적으로 전환될 때 플러그인은 이를 감지하고 MusicBee에 현재 재생 인덱스를 진행하도록 알려야 합니다. 그렇지 않으면 MusicBee는 여전히 이전 트랙에 있다고 생각하고 재생 횟수 / UI / 스크로블링이 동기화되지 않습니다.

구현: 각 상태 타이머 틱에서 GetPositionInfo.TrackURI를 폴링합니다. 보고된 URI가 NextURI를 통해 대기열에 추가한 URI와 일치하면 MusicBee에서 Player_PlayNextTrack을 호출하고 suppressNextSoapCall을 설정하여 결과 PlayToDeviceSetAVTransportURI를 다시 보내지 않도록 합니다(이는 무음 재생을 방해할 것입니다).

왜: F15가 없으면 장치는 다음 트랙을 재생하지만 MusicBee의 UI는 이전 트랙에 있다고 표시합니다. 혼란스럽고 스크로블링을 방해하며 재생 횟수 추적을 방해합니다. 트랙 전환 감지는 각 렌더러 브랜드가 URI 변경을 언제 보고하는지에 대한 자체 특성(일부는 TRANSITIONING을 먼저 보고하고, 일부는 새 URI로 PLAYING으로 바로 건너뛰고, 일부는 그 사이에 짧은 STOPPED가 있음)이 있기 때문에 길고 렌더러별 반복이 필요합니다.

참고: 첫 번째 버전은 BubbleUPnP 렌더러에서 작동합니다. 장치별 예외는 B6에 남아 있습니다.


F16 - 무음 전환 시 팝업 수정

무엇: 팝업은 대기열에 있는 트랙의 소스 형식(샘플 속도 / 채널 / 코덱)이 현재 재생 중인 트랙과 다를 때 발생하여 장치의 DAC가 전환 시 다시 잠기도록 강제합니다. F16은 형식이 다를 때마다 대기열에 추가될 때 양쪽을 명명하는 NextUri:FormatChange 진단을 추가합니다. 따라서 팝업을 듣는 사용자는 상관 관계를 파악할 수 있습니다.

진단은 또한 완화책을 지적합니다. 장치 프로필에서 트랜스코딩 강제를 선택합니다. 이는 모든 트랙을 단일 트랜스코딩 코덱/샘플 속도/비트 깊이로 균일화하여 소스 형식 차이를 완전히 제거합니다.

실제 트랜스코딩 일치 수정에 대해 연기된 이유: 구조적 수정(대기열에 있는 트랙을 재생 중인 트랙의 형식과 일치하도록 트랜스코딩)은 플러그인의 HTTP 서버 URL 스키마 변경이 필요합니다. 현재 /encode/{id}0.{ext}는 대기열에 있는 파일을 원시적으로 제공합니다. F16의 미래 v2는 형식별 /encode/{id}0_{rate}_{depth}.{ext} 경로를 추가하고 인코더를 통해 연결할 것입니다. 이는 트랜스코딩 강제만으로는 팝업이 해결되지 않는 실제 장치가 있는 경우 수행할 가치가 있는 더 큰 아키텍처 변경입니다.

오늘날의 구현:

  • lastSourceUrl 필드는 현재 재생 중인 소스 URL을 추적합니다.
  • QueueNext는 현재 트랙과 대기열에 있는 트랙 모두에 대해 FilePropertyType.SampleRate/Channels/Kind를 읽고 불일치 시 NextUri:FormatChange를 기록합니다.

F17 - 탐색 후 진행률 표시줄 재동기화

무엇: Seek() 함수는 성공적인 Seek SOAP 후에 이미 GetPlayPositionInformation()을 호출하여 "전혀 재동기화되지 않는" 경우를 수정했습니다. F17은 UPnP의 1초 RelTime 양자화로 인해 발생하는 최대 1초의 나머지 드리프트를 해결합니다. 장치에서 보고된 위치가 사용자가 요청한 대상의 1초 이내로 반올림될 때 플러그인은 이제 장치의 잘림 대신 사용자의 서브초 정확한 값을 신뢰합니다. 장치가 극적으로 다른 값(1초 이상 차이)을 보고할 때만 해당 값을 사용합니다(예: 일부 코덱에서 키프레임에 스냅).

왜: 이것이 없으면 장치의 "2:30" 보고서에 고정된 2:30.500으로 탐색하면 진행률 표시줄이 실제보다 약 500ms 뒤처진 것으로 표시됩니다. F17 이후에는 표시줄이 일반적인 트랙 내 스크럽 사례에 대한 사용자의 의도와 일치하며, 키프레임에 스냅하는 예외에 대해서는 여전히 장치의 보고서를 존중합니다.


F18 - 연속 스트림 / NextURI 연동

무엇: 이제 두 가지 연동이 적용됩니다.

  1. 런타임: Settings.ContinuousOutput이 켜져 있으면 QueueNextFalse를 조기 반환합니다. 연속 스트림은 자체적인 무음 메커니즘(하나의 긴 연결된 스트림)입니다. 그 위에 SetNextAVTransportURI를 보내면 각 트랙이 개별 URI인지 연속 흐름의 일부인지에 대해 장치를 혼란스럽게 합니다.
  2. UI: 사용자가 전역 연속 스트림 확인란을 선택하면 현재 표시된 프로필의 forceNativeStream이 자동으로 선택 해제됩니다. 연속 스트림은 항상 트랜스코딩하므로 강제 원시는 조합에서 의미가 없습니다.

왜: 사용자가 두 가지 충돌하는 무음 메커니즘을 동시에 활성화하는 것을 방지합니다. F18이 없으면 장치는 각 후속 트랙에 대해 연속 스트림 URI와 NextURI를 모두 수신하여 렌더러에 따라 정의되지 않은 동작이 발생할 수 있습니다.


F19 - 빈 NextURI 오류 무시

무엇: 빈 URL로 SetNextAVTransportURI가 호출될 때(예: 목록의 마지막 트랙), 일부 장치는 SOAP 오류를 반환합니다. F19는 이러한 오류를 자동으로 삼킵니다. 기록되지만 오류로 전파되지는 않습니다.

왜: "다음 트랙 없음" 조건은 정상적인 것이지 오류가 아닙니다. 이를 치명적인 것으로 취급하면 로그를 오염시키고(일부 흐름에서) 재시도 폭풍을 유발합니다.


MIME 유형 및 DLNA 메타데이터

F20 - MP3 MIME → audio/mpeg

무엇: 표준 준수 MP3 MIME 유형은 audio/mpeg이며, audio/mp3가 아닙니다. 후자는 대부분의 장치가 허용하는 일반적인 오칭이지만, 더 엄격한 렌더러는 이를 거부합니다.

왜: 표준을 따르는 더 엄격한 장치에서 재생을 자동으로 수정합니다. yaiol 코드베이스는 이미 이것을 올바르게 가지고 있었으므로 변경할 필요가 없습니다.


F21 - MIME 유형 순서: 비-x- 변형 우선

무엇: 장치가 audio/flacaudio/x-flac을 모두 광고할 때 플러그인은 비-x- 변형을 먼저 반환합니다. 표준 및 실험적 MIME을 모두 가진 모든 코덱에 대해서도 마찬가지입니다.

왜: x- 접두사는 실험적/비공식 MIME을 표시합니다. 일부 렌더러는 표준 형식에서 더 잘 작동합니다. 작은 재정렬이지만 실제 영향이 있습니다.


F22 - Opus MIME 유형 지원

무엇: Opus를 스트리밍 가능한 오디오 코덱으로 인식합니다. Opus 트랙을 제공할 때 audio/opus MIME을 보냅니다.

왜: Opus는 이제 일반적입니다(최신 음성/음악 절충 코덱). F22가 없으면 플러그인은 Opus 파일을 처리하는 장치에도 스트리밍을 거부할 것입니다.


F23 - Monkey Audio (APE) 소스 파일 지원

무엇: .ape 파일을 스트리밍/트랜스코딩을 위한 유효한 소스 코덱으로 인식합니다.

왜: APE는 틈새 시장이지만 충성도 높은 사용자 기반을 가진 무손실 형식입니다. 이를 추가하는 데 비용이 거의 들지 않으며 해당 사용자를 위해 라이브러리를 잠금 해제합니다.


F24 - AAC / ALAC MIME 대체

무엇: 장치가 AAC 또는 ALAC을 지원하지만 UPnP 서비스 설명에 명시적으로 광고하지 않는 경우, 플러그인은 어쨌든 대체로 제공합니다.

왜: AAC를 잘 처리하는 여러 장치가 기능 XML에 이를 나열하는 것을 잊었습니다. F24가 없으면 플러그인은 시도조차 하지 않고 트랜스코딩을 강제합니다. F24를 사용하면 플러그인은 시도하고 장치가 가능하면 원시적으로 처리하도록 합니다.


F25 - 원시 + 인코딩된 WAV 스트림에 대한 DLNA 유형 플래그

무엇: DLNA 유형 플래그(LPCM, WAVE, MP3와 같은 프로필 식별자)는 장치가 수신하는 것과 일치해야 합니다. F25는 원시 스트림과 인코딩된 WAV 스트림이 올바르게 플래그 지정되도록 합니다.

왜: DLNA 유형 불일치는 일부 장치가 재생을 완전히 거부하거나 잘못된 디코더를 적용하게 합니다.


F26 - FLAC 파일용 DLNA 헤더

무엇: FLAC 스트림은 헤더에 적절한 DLNA 프로필 식별자를 얻습니다.

왜: 이것이 없으면 FLAC을 지원하는 일부 장치는 스트림을 그렇게 인식하지 못합니다.


F27 - 메타데이터의 비트 전송률 계산 수정

무엇: 연속 스트림 res@bitrate(sampleRate * channels * bitsPerSample) / 1000으로 계산되었습니다. 이는 UPnP DIDL 사양에서 속성을 초당 바이트로 정의하는 것과 비교하여 약 125배 차이가 나는 kbps였습니다. 이제 1000 대신 8로 나눕니다.

왜: 장치에 잘못된 비트 전송률 표시 - 대부분의 렌더러에서는 미용적인 문제이지만, 일부는 값에서 스트림 버퍼를 할당하고 실제보다 약 125배 작은 스트림에서 끊김 현상이 발생합니다. 비연속 소스 파일 경로는 이미 이것을 올바르게 가지고 있었습니다((bitrate_kbps * 1000) \ 8 = 초당 바이트). 연속 스트림 경로만 잘못되었습니다.


F28 - 메타데이터 시간 형식 수정 (Marantz)

무엇: DIDL의 res@durationH:MM:SS(예: 0:03:42)로 형식이 지정되었습니다. UPnP DIDL 사양은 형식을 H+:MM:SS[.F+]로 정의합니다. 엄격하게는 소수 초가 선택 사항이지만 권장됩니다. 일부 Marantz 장치는 bare 형식을 유효하지 않은 것으로 취급하고 지속 시간 표시를 비워둡니다. 이제 H:MM:SS.fff(예: 0:03:42.000)로 형식이 지정됩니다.

왜: 브랜드에 특정한 표시 문제; 소수 초가 있는 ISO 스타일 형식은 다른 장치에 영향을 주지 않고 이를 수정합니다. DIDL 방출 사이트(소스 파일 경로 + WriteAudioFileDIDL의 인코딩된 스트림 경로) 모두에 적용됩니다.

동일한 통과에서 보너스 수정: pv:addedTimepv:lastPlayedTime은 DateTime 형식 문자열에서 HH(24시간 시계) 대신 hh(12시간 시계)를 사용했습니다. 13:00에서 23:59 사이에 추가되거나 재생된 모든 트랙은 필드를 표시하는 장치에서 잘못된 시간(예: 17:42 → "05:42")으로 렌더링되었습니다. 이제 HH를 사용합니다.


F29 - 인코딩된 MP3 탐색 지원 (CBR)

무엇: 트랜스코딩된 MP3 스트림은 이제 DLNA.ORG_OP=10(바이트 전용) 대신 DLNA.ORG_OP=11(바이트 및 시간 탐색 모두)을 광고합니다. 이전에 트랜스코딩된 MP3에서 시간 탐색을 거부했던 장치는 이제 진행률 표시줄/탐색 UI를 정상적으로 구동할 수 있습니다.

왜: MusicBee의 트랜스코더는 HighQuality 사전 설정에서 고정 비트 전송률 MP3를 생성하므로 바이트 ↔ 시간 매핑은 선형입니다. 장치는 인코더 측 지원 없이도 시간 탐색 요청을 HTTP Range 바이트 탐색으로 자체 변환할 수 있습니다. OP=11을 광고하면 장치에서 해당 UI가 잠금 해제됩니다. F29가 없으면 트랜스코딩된 MP3 내에서 탐색하는 사용자는 탐색이 자동으로 무시되거나 트랙 시작으로 이동되었습니다.

구현: ItemManager.vbGetEncodeFeature를 재구성하여 인라인 If를 읽기 쉬운 If/ElseIf/Else 체인으로 분리했습니다. MP3는 명시적으로 OP=11을 얻습니다. 다른 비 PCM 코덱은 OP=10을 유지합니다. AAC/FLAC 등은 변경되지 않습니다. 이들은 MusicBee가 보장하지 않는 CBR 여부에 대한 코덱별 검증이 필요합니다.


F30 - .mpeg 파일 확장자 처리

무엇: .mpeg(및 더 희귀한 .mpe) 확장자를 가진 파일은 이제 GetCodec에서 FileCodec.Mp3로 인식됩니다. F30 이전에는 FileCodec.Unknown을 반환하고 라이브러리에서 자동으로 거부되거나 트랜스코딩 소스가 될 수 없었습니다.

왜: 오래된 MPEG-1 Layer 3 아카이브는 때때로 .mp3 대신 .mpeg를 사용했습니다(사양은 둘 다 허용). 30만 개 라이브러리에서 몇 개의 파일만 있어도 "MusicBee는 표시하지만 플러그인은 표시하지 않는다"는 느낌을 주기에 충분합니다. 사용자에게 혼란을 줍니다.


재생 동작

F31 - 라디오 스트림 자동 연속 모드 사용

무엇: WriteAudioFileDIDL은 이제 Library_GetFileProperty를 통해 소스 URL의 Kind 속성을 조사하고 Kind가 "Stream"으로 끝나는 모든 파일(MusicBee는 라디오에 대해 "MP3 Stream", "Internet Stream" 등을 보고함)을 전역 Settings.ContinuousOutput 토글에 관계없이 연속으로 처리합니다. 연속 스트림 DIDL 분기(제목: "Continuous Stream", id="continuousstream", 고정 PCM/Wave 출력)가 사용됩니다. 장치는 단일 무한 스타일 스트림을 봅니다.

왜: 라디오 스트림에는 트랙 경계, 고정 길이, 탐색이 없습니다. DIDL에서 개별 파일처럼 취급하면 플러그인이 존재하지 않는 바이트 범위와 지속 시간을 광고하게 됩니다. MusicBee가 이미 "이것은 스트림입니다"라고 알려줄 때 자동으로 전환하면 사용자가 생각할 필요 없는 발포 총을 제거합니다.

범위: MusicBee가 재생을 구동할 때만 적용됩니다(musicBeePlayToMode). 라이브러리 가져오기 경로(UPnP 클라이언트 탐색)는 변경되지 않습니다. 라디오 URL은 드물고 명시적인 테스트 없이는 사용자 대면 동작이 변경되어서는 안 됩니다.


F32 - 코덱 광고 대체

무엇: 장치가 특정 코덱을 광고하지 않거나 플러그인이 장치의 기능 XML을 구문 분석할 수 없는 경우, 플러그인은 즉시 스트림을 거부하지 않습니다. 대신, 스트림을 제공하려고 시도하고 장치가 결정하도록 합니다.

왜: 많은 장치가 불완전하거나 읽을 수 없는 기능 XML을 가지고 있지만 실제로는 코덱을 잘 처리합니다. F32는 작은 "최선의 추측 및 시도"를 전면적인 거부와 교환합니다.


F33 - 진행률 표시줄 동기화 개선

무엇: 폴링 사이의 위치는 이미 단일 앵커(currentPlayStartTicks)에서 벽시계로 외삽되므로 진행률 표시줄은 서브초 속도로 부드럽게 업데이트됩니다. 나머지 지터 원인은 새로 시작된 트랙의 초기 앵커였습니다. 이전 코드는 상태 타이머가 처음으로 재생 상태로 전환되었음을 감지한 순간에 position=0이라고 가정했지만, 그때쯤 장치는 100-500ms(하나의 폴링 간격) 동안 재생 중이었을 수 있습니다. MusicBee의 진행률 표시줄은 0에서 시작한 다음 실제 상황이 따라잡으면 앞으로 점프했습니다.

F33 수정: 새 트랙에서 처음으로 재생 상태로 전환될 때(currentPlayStartTimeEstimated=True), GetPlayPositionInformation()을 호출하여 장치의 실제 현재 위치를 가져온 다음, 이를 기준으로 앵커를 설정합니다. UPnP는 1초 해상도만 보고하므로 앵커는 여전히 양자화되지만, 0으로 가정하는 것보다 훨씬 진실에 가깝습니다.

왜: 특히 트랙 변경 직후에 더 부드럽고 정확한 진행률 표시. 1초 UPnP 보고 해상도 자체는 피할 수 없습니다. 이는 사양입니다.


F34 - 트랙 변경 후 진행률 표시줄 지터

무엇: 새 트랙에 대해 PlayToDevice가 호출될 때 플러그인은 SOAP-Play와 새 재생 상태를 감지하는 첫 번째 상태 타이머 폴링 사이의 ~100ms 창 동안 currentPlayPositionMscurrentPlayStartTicks를 이전 트랙 값으로 남겨두곤 했습니다. MusicBee의 진행률 표시줄은 이전 트랙의 을 잠시 표시한 다음 0으로 돌아가고 다시 올라갔습니다. F34는 PlayToDevice 진입 시(SOAP 작업이 시작되기 전에 트랙 변경이 발생하고 있음을 아는 순간) 둘 다 0으로 설정합니다.

왜: 빠른 건너뛰기 사용 사례(수동 다음 또는 무음 전환)에서 시각적 결함. 이제 MusicBee의 첫 번째 PlayPositionMs 쿼리 후 Play는 깨끗하게 0을 반환한 다음, F33의 GetPlayPositionInformation은 첫 번째 상태 변경 틱에서 장치의 실제 위치로 이를 정제합니다.

구현: PlayToDevice 상단에 네 줄, F33의 전환 시간 정확한 앵커링과 쌍을 이룹니다.


F35 - "트랜스코딩 강제" 버그

무엇: 트랜스코딩 강제는 특정 조합에서 여전히 트랜스코딩을 건너뛸 수 있었습니다. F04 프로필별 재작업 후 두 가지 특정 간격이 봉인되었습니다.

  1. ForceNativeStream과의 우선 순위. 둘 다 True일 때(스키마 마이그레이션 또는 부분 설정 파일에서 발생할 수 있음), ForceTranscoding이 이제 완전히 승리합니다(If streamingProfile.ForceTranscoding Then forceEncode = True ElseIf streamingProfile.ForceNativeStream Then forceEncode = False). UI 상호 배제는 사용자가 둘 다 선택하는 것을 방지하지만, 런타임 가드는 디스크에서 일관성 없이 로드된 모든 상태를 처리합니다.
  2. bypassTranscodeDecision 논리. 이전: streamingProfile.ForceNativeStream AndAlso Not Settings.ForceTranscoding. 이제: streamingProfile.ForceNativeStream AndAlso Not streamingProfile.ForceTranscoding - 동일한 우선 순위 규칙이지만 동일한 프로필별 범위에서.

왜: "강제"는 강제를 의미해야 합니다. 사용자가 장치에 대해 ForceTranscoding을 명시적으로 활성화한 경우, 다른 플래그가 어떻게 조합되든 플러그인은 원시 스트리밍으로 자동으로 대체되어서는 안 됩니다.


F36 - 렌더러 닫힘 예외

무엇: Plugin.ReceiveNotification을 최상위 Try/Catch로 래핑하여 MusicBee의 알림 펌프로 다시 전파하는 대신 포착되지 않은 예외를 기록합니다.

왜: MusicBee의 알림(PlayStateChanged, VolumeMuteChanged 등)은 SOAP를 통해 렌더러와 통신하는 ControlPointManager로 디스패치됩니다. 개별 호출 사이트는 이미 SOAP 호출 주변에 Try/Catch를 가지고 있었지만, 충분히 이상한 타이밍 사례(예: 동일한 알림 핸들러에서 두 SOAP 호출 사이에 렌더러가 죽는 경우)는 여전히 벗어날 수 있었습니다. 최상위 래퍼는 최종 안전망이므로 사용자는 MusicBee에서 일반적인 "TargetInvocationException" 팝업을 보지 않습니다.

구현: 기존 본문을 ReceiveNotificationInternal로 이름을 바꾸고 Try { ReceiveNotificationInternal(...) } Catch { LogError(...) }를 수행하는 얇은 래퍼 ReceiveNotification을 추가했습니다. ControlPointManager 내부의 기존 메서드별 Try/Catch 인프라(모든 PostSoapRequest 호출 주변)는 유지됩니다. F36은 벨트 + 서스펜더입니다.


F37 - 긴 트랙 탐색이 잘못된 전환을 트리거함

무엇: 긴 트랙 내에서 탐색하면 일부 렌더러에서 짧은 Stopped→Playing 주기가 발생할 수 있습니다. 구별 없이 ProcessNewPlayState.Stopped는 이를 트랙의 자연스러운 끝으로 취급하고 Player_PlayNextTrack을 호출하여 사용자가 스크럽만 원했을 때 MusicBee를 진행시킵니다. F37은 Seek()에서 lastUserInitiatedSeek를 스탬프하고 Stopped 핸들러에 5초 가드를 추가합니다(기존 lastUserInitiatedStop 창을 미러링).

왜: 탐색 중 자동 다음 트랙으로 건너뛰기는 아무도 원인을 추측할 수 없는 버그 중 하나입니다. 사용자는 "이상하다, 앞으로 스크럽하려고 했는데 이제 다음 곡이 재생되고 있다"고 생각합니다. 수정은 기계적입니다. 이미 적용된 사용자 중지 구별과 동일한 패턴입니다.


F38 - 충돌 가능성이 있는 코덱에 대한 향상된 탐색 처리

무엇: MP3 탐색 시 BubbleUPnP 충돌은 전형적인 증상이었습니다. 감사 후, 현재 yaiol 탐색 코드는 이미 올바른 작업을 수행합니다. 원시 경로는 HTTP Range를 올바르게 처리하고(206, Content-Range, AcceptRanges), 인코딩된 경로는 X-AvailableSeekRange를 광고하고 들어오는 timeSeekRange.dlna.org / npt 헤더를 구문 분석하며, DLNA.ORG_OP 플래그는 실제 스트림 기능을 반영합니다(문제 있는 Platinum 장치에 대한 DisablePcmTimeSeek 옵트아웃 포함). 현재 BubbleUPnP 4.6.4에서 사용자 테스트: 충돌 없음.

왜: BubbleUPnP MP3 탐색 충돌은 2024년경에 보고되었으며 앱은 그 이후로 약 16개월 동안 수정되었습니다. F29(인코딩된 MP3 OP=11)는 이를 다시 노출할 수 있는 새로운 변수였지만, 테스트된 버전에서는 그렇지 않습니다.

충돌이 다시 발생하면: 수정 형태는 플래그 지정된 코덱에 대해 DLNA.ORG_OP=10(바이트 전용)을 강제하는 프로필별 "제한된 탐색" 토글이 될 것입니다. DisablePcmTimeSeek가 PCM에 대해 이미 작동하는 방식을 미러링합니다. 그때 추가하고 미리 추가하지 않습니다.


UI 및 로깅

F39 - "추가" 버튼이 새 프로필을 선택합니다.

무엇: 장치 프로필 목록에서 "추가"를 클릭하면 새 프로필이 생성되고 자동으로 선택되므로 사용자는 즉시 필드를 편집할 수 있습니다. 섹션별 대화 상자 리팩터링은 이미 이를 수행합니다. 직접 추가 경로와 템플릿에서 추가 경로 모두 Me.activeStreamingProfiles.SelectedIndex = Me.activeStreamingProfiles.Items.Count - 1로 끝납니다. 확인 결과 포크는 이미 이를 처리하고 있으므로 변경할 필요가 없습니다.

왜: 작은 UX 문제였지만 여기서는 이미 문제가 아니었습니다.


F40 - 더 큰 최대 연결 + 경고 로그

무엇: 플러그인의 동시 스트림 제한(Sockets_Stream_File / Sockets_Encoder_Start 주변의 SemaphoreSlim)은 하드 코딩된 4였습니다. F40은 이를 일반 설정 페이지에서 사용자 구성 가능하게 만들고(기본값 16, 범위 1-256), 요청이 슬롯을 기다려야 할 때 MaxConnections 로그 줄을 추가하며, MusicBee 시작 이후 제한에 한 번 이상 도달한 경우 설정 대화 상자 왼쪽 하단에 빨간색 ⚠ 최대 연결 배지를 표시합니다.

왜: 장치가 병렬 요청을 발생시킬 때(일부 Marantz/Linn의 아트워크 스캔 중, 활성 재생과 함께 BubbleUPnP의 메타데이터 조사), 추가 요청은 세마포어 뒤에서 자동으로 차단되었습니다. 사용자는 눈에 보이는 원인 없이 "장치 느림"을 보았습니다. 로그 줄은 기술 디버깅에 좋지만 비기술 사용자는 로그를 읽지 않습니다. 설정 대화 상자에 표시되는 배지는 플러그인 환경 설정을 여는 모든 사람이 제한 도달 조건을 발견할 수 있도록 합니다.

구현:

  • MusicBeeUpnp.vbWaitOnSendBarrier(logTag)에서 대기를 중앙 집중화했습니다. 두 호출 사이트(MediaServerDevice.GetFile, Encoder.StartEncode) 모두 이를 사용합니다.
  • Settings.MaxConnections는 설정 스키마 v8에 유지됩니다.
  • Plugin.MaxConnectionsHitWaitOnSendBarrier 내에서 설정되는 고정 세션 플래그입니다. MusicBee를 다시 시작할 때만 재설정됩니다.
  • SettingsDialog.maxConnectionsBadge(16, 410)에 있는 빨간색 굵은 레이블로, Plugin.MaxConnectionsHit가 True일 때만 표시됩니다. 원인과 해결책을 설명하는 도구 설명이 있습니다.
  • 세마포어는 유형 로드 시 한 번 초기화되므로 설정을 변경하려면 MusicBee를 다시 시작해야 합니다(필드 레이블에 명시됨).

F41 - "ReplayGain/DSP로 인한 인코딩" 로그

무엇: 별도의 "RG용 인코딩" / "DSP용 인코딩" 로그 줄 대신, F42의 단일 StreamDecision 줄은 누적된 이유로 MB-DSP/EQ, MB-ReplayGain, Profile-DSP/EQ, Profile-ReplayGain을 포함합니다. 동일한 진단 값, 적은 노이즈.

왜: 사용자는 주어진 트랙에 대해 트랜스코딩이 발생하는 모든 이유를 한 줄의 로그에서 볼 수 있으며, 흩어져 있지 않습니다. 자세한 내용은 F42를 참조하십시오.


F42 - "렌더러가 소스 코덱을 지원하지 않음" 로그

무엇: 재생 장치 트랙당 "native CODEC" 또는 "transcode CODEC→CODEC reason=…"을 나타내는 StreamDecision 로그 줄이 추가되었습니다. 이유 필드는 트랜스코딩을 트리거한 모든 조건을 누적합니다. MB-DSP/EQ, MB-ReplayGain, Profile-DSP/EQ, Profile-ReplayGain, WebFile, VirtualFile, ForceTranscoding(global), SampleRate<min/SampleRate>max, DownmixToStereo, DeviceLacksCodec(X), BandwidthConstrained.

왜: 사용자는 원시적으로 스트리밍될 것으로 예상했던 파일에서 예상치 못한 CPU 스파이크로 인해 혼란스러워했습니다. 트랙당 한 줄의 로그는 트랜스코딩을 유발한 정확한 조건을 알려줍니다. 필드에 DeviceLacksCodec(Flac)이 표시되면 장치의 프로토콜 정보가 불완전했으며 F32의 대체가 시작될 수 있음을 즉시 알 수 있습니다.

구현: 결정 체인을 통해 점진적으로 구축되는 단일 누적 문자열; 끝에서 한 번 기록됩니다. 프로덕션에서 로그 노이즈를 피하기 위해 Settings.LogDebugInfo에 따라 게이트됩니다.


F43 - SetNextAVTransport 로그에 소스 URL 표시

무엇: QueueNext 로그 항목에는 stream=<HTTP 스트리밍 URL>과 함께 source=<MusicBee 라이브러리 경로>가 포함됩니다. 성공 경로와 실패 경로(QueueNext:Failed)에도 동일한 변경 사항이 적용됩니다.

왜: 대기열에 있는 트랙 문제를 디버깅할 때 스트리밍 URL(/encode/aabbccdd0.flac)은 자체적으로 불투명합니다. 모든 트랙에 대해 동일합니다. 소스 URL은 MusicBee가 대기열에 추가하려고 시도한 정확한 파일을 알려주는 사람이 grep할 수 있는 라이브러리 경로입니다.


F44 - 더 나은 MIME 유형 오류 로깅

무엇: Activate 중에 두 가지 새로운 로그 항목:

  • Activate:MimeUnverified - 장치의 GetProtocolInfo 응답에서 잘못된 형식의 항목당 발생하며, 구문 분석할 수 없는 항목을 명명합니다(따라서 사용자는 예를 들어 "Marantz가 일부 코덱에 대해 http-get:*::*를 반환했습니다. 기능이 확인되지 않았으므로 F32의 대체가 추측할 것입니다"와 같은 내용을 볼 수 있습니다).
  • Activate:NoSinkInfo - 장치가 <Sink> 요소를 전혀 반환하지 않은 경우 한 번 발생합니다. SupportedMimeTypes가 Nothing으로 유지되고 IsCodecSupported가 "모든 것이 작동한다고 가정"으로 저하됨을 의미합니다. 나중에 "장치가 스트림을 거부했습니다" 오류가 나타날 때 유용한 컨텍스트입니다.

왜: F44 이전에는 이러한 자동 기능 대체로 인해 사용자는 트랙이 예상과 달리 트랜스코딩되거나 장치에서 거부되는 이유를 추측해야 했습니다. 이제 Activate:에 대한 단일 grep은 장치의 기능 정보가 사용 가능한지 여부를 보여줍니다.


F45 - 더 나은 메타데이터 오류 로깅

무엇: ContentDirectoryService.vbBrowse 예외 로그는 이전 yaiol 작업(Alia Vox 버그 세션)에서 ObjectID 및 스택 추적으로 이미 풍부해졌습니다. F45는 BrowseFlag(메타데이터 vs 자식), Filter(클라이언트가 요청한 속성), sortCriteria, partialResultLength(실패 전에 생성된 DIDL의 바이트 수 - 잘못된 트랙이 배치에서 얼마나 멀리 떨어져 있는지 나타냄)로 이를 더욱 확장합니다.

왜: DIDL 중간에 문제가 발생하면 부분 길이 값은 실패가 배치의 첫 번째 트랙에서 발생했는지(partial=0) 아니면 중간에 발생했는지(partial=N)를 알려줍니다. 배치의 startingIndex와 결합하여 문제가 있는 트랙 인덱스를 식별할 수 있습니다. Filter 및 BrowseFlag는 클라이언트가 어떤 종류의 탐색을 원했는지 설명합니다. 때로는 메타데이터 전용 탐색이 동일한 ID에 대한 자식 탐색이 성공하는 곳에서 실패합니다.


네트워킹

F46 - 자동 모드는 실제 네트워크 어댑터에서만 광고합니다.

무엇: 자동 인터페이스 모드에서 플러그인은 모든 작동 중인 IPv4 어댑터에서 자체적으로 광고했습니다(SSDP). VPN 터널(NordLynx) 또는 가상 스위치(Hyper-V / WSL / Docker)도 실행하는 컴퓨터에서는 동일한 라이브러리가 해당 어댑터 각각에서도 광고되었으므로 캐스팅하는 제어 지점이 서버를 두세 번 검색하고 라이브러리를 중복 복사본으로 나열했습니다. 자동 모드는 이제 실제 IPv4 기본 게이트웨이(HasIPv4Gateway)가 있는 어댑터만 유지합니다. 터널 및 가상 스위치 어댑터는 이를 가지고 있지 않으므로 광고 목록에서 제외됩니다. 사용자 고정 주소는 여전히 완전히 승리하며(해당 인터페이스에서만 광고), 어떤 어댑터도 게이트웨이를 보고하지 않으면 선택기는 모든 어댑터로 대체되므로 광고된 주소 목록은 결코 비어 있지 않고 플러그인이 보이지 않게 될 수 없습니다.

왜: 중복은 "VPN에 연결되어 있기" 때문에 발생하는 것이 아닙니다. LAN 어댑터와 터널/가상 어댑터에서 동시에 광고하기 때문에 발생하므로 하나의 제어 지점이 두 주소에서 동일한 서버를 봅니다. 소비자 VPN(NordVPN/NordLynx)은 인터넷 연결 트래픽만 터널링합니다. DLNA 렌더러는 LAN에 있으며 로컬 서브넷 트래픽은 터널을 우회하므로 터널 어댑터는 어쨌든 렌더러에 도달하지 않습니다. 이를 제거하면 작동하는 경로가 아니라 유령 복사본이 제거됩니다. 게이트웨이 테스트는 실제 LAN/Wi-Fi 어댑터를 터널 또는 가상 스위치와 분리하는 저렴하고 신뢰할 수 있는 신호입니다. N05(해당 링크에서 알림이 전송되는 방식 - 브로드캐스트 대신 멀티캐스트 -를 수정함)를 보완합니다. F46은 어떤 어댑터에서 알림이 전송되는지를 제어합니다.

알려진 제한: 렌더러가 실제로 터널을 통해 존재하는 메시 / 원격 액세스 VPN(Tailscale, ZeroTier, WireGuard-to-home)은 일반적으로 기본 게이트웨이가 없는 어댑터를 제공하므로 자동 모드도 이를 삭제합니다. 해당 사용자는 대신 VPN 주소를 고정하며, 이는 게이트웨이 필터보다 우선합니다.

목차