fix(now-playing): Орфей — чиним кодировку (двойная мойибейк cp1251) + режем хекс-хвосты
status-json смешанной кодировки: часть тайтлов нормальный UTF-8, часть — cp1251- байты, прочитанные как latin1 и завёрнутые в UTF-8 (мойибейк «Íèêîëà»→«Никола»). fixEncoding: реальную кириллицу не трогаем, мойибейк (À-ÿ) восстанавливаем latin1→windows-1251. Срезаем приклеенный служебный id трека (- f0098627), фильтр hex/числовых плейсхолдеров усилен. Каналы без реального произведения — без подписи. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -89,22 +89,52 @@ export class OrpheusNowPlayingService {
|
||||
return m ? m[1] : null;
|
||||
}
|
||||
|
||||
/** Разбирает title «Артист — Произведение», отсекая мусор. */
|
||||
/**
|
||||
* Чинит кодировку: реальную кириллицу (U+0400-04FF) не трогаем; «двойную
|
||||
* мойибейк» (cp1251-байты, прочитанные как latin1 и завёрнутые в UTF-8 —
|
||||
* признак латиницы-1 À-ÿ) восстанавливаем latin1→windows-1251.
|
||||
*/
|
||||
private fixEncoding(s: string): string {
|
||||
if (/[Ѐ-ӿ]/.test(s)) return s; // уже корректная кириллица
|
||||
if (/[À-ÿ]/.test(s)) {
|
||||
try {
|
||||
return new TextDecoder('windows-1251').decode(Buffer.from(s, 'latin1'));
|
||||
} catch {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/** Разбирает title «Артист — Произведение», чиня кодировку и отсекая мусор/служебные id. */
|
||||
private parseTitle(title?: string): { artist: string; song: string } | null {
|
||||
if (!title) return null;
|
||||
const t = title.trim();
|
||||
if (!t || t === 'undefined') return null;
|
||||
// hex-плейсхолдеры (0003b6d2), URL-источники (http://fonotron.ru), JSON
|
||||
if (/^[0-9a-f]{6,}$/i.test(t) || t.startsWith('http') || t.startsWith('{')) {
|
||||
let t = this.fixEncoding(title).replace(/\s+/g, ' ').trim();
|
||||
if (!t || t.toLowerCase() === 'undefined') return null;
|
||||
// Срезаем хвостовой служебный id трека: « - f0098627», « - 147-1-10», « - 263-2-01»
|
||||
t = t
|
||||
.replace(/\s*[-—]\s*[0-9a-f]{6,}\s*$/i, '')
|
||||
.replace(/\s*[-—]\s*\d+-\d+(?:-\d+)?\s*$/, '')
|
||||
.trim();
|
||||
if (!t) return null;
|
||||
// Целиком мусор: hex-плейсхолдер, числовой код, URL, JSON
|
||||
if (
|
||||
/^[0-9a-f]{4,}$/i.test(t) ||
|
||||
/^\d+-\d+/.test(t) ||
|
||||
t.startsWith('http') ||
|
||||
t.startsWith('{')
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
// Разделитель — длинное тире или дефис с пробелами
|
||||
const idx = t.indexOf(' — ') >= 0 ? t.indexOf(' — ') : t.indexOf(' - ');
|
||||
// Разделитель — длинное тире или дефис с пробелами (оба длиной 3 символа: « X »)
|
||||
const emIdx = t.indexOf(' — ');
|
||||
const idx = emIdx >= 0 ? emIdx : t.indexOf(' - ');
|
||||
if (idx < 0) return null;
|
||||
const sepLen = t.indexOf(' — ') >= 0 ? 3 : 3;
|
||||
const artist = t.slice(0, idx).trim();
|
||||
const song = t.slice(idx + sepLen).trim();
|
||||
const song = t.slice(idx + 3).trim();
|
||||
if (!artist || !song) return null;
|
||||
// Часть после чистки всё ещё мусорная
|
||||
if (/^[0-9a-f]{4,}$/i.test(song) || /^[0-9a-f]{4,}$/i.test(artist)) return null;
|
||||
return { artist, song };
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user