with_retries caught bare Exception with a type whitelist, masking real bugs
as network errors under retry/backoff - narrowed back to NetworkError.
fetch_api_languages was writing full lyrics text for every language into
lyrics_text.json via its track_supplement side channel, breaking the
documented RU-only invariant and letting analyze feed foreign-language
lyrics through the Russian-only prompt - now gated on normalize_api_language.
dedupe_playlists logged the found duplicate count as "removed" even when
a mid-loop network/API error cut deletion short.
THEME_PATTERN's relaxed regex allowed 2-char themes, contradicting its own
test; current_themes was hoisted out of the analyze loop so later prompts
never saw themes found earlier in the same run. Both reverted to intended
behavior, docs updated to match.
Playlist deletes are positional (from/to index), so a network error
during dedupe just aborts that playlist instead of retrying blindly -
indices may have already shifted. Re-running dedupe is safe since it
recomputes ranges from the live playlist state each time.
client.users_playlists(kind) silently returns None on any response-parse
failure, not just when the playlist genuinely doesn't exist - this isn't
an exception, so with_retries doesn't catch it. Previously this was
treated as "playlist is empty", which risked re-inserting everything on
top of existing tracks. Now retry with backoff and raise instead of
guessing.
track_key compared "id:albumId" against playlist track_id, but Yandex
never echoes albumId back for playlist entries (always None on read) -
so the existing-tracks check could never match anything, and every
apply run re-inserted the whole account on top of itself.
apply --yes падал целиком на первом же самозалитом/пиратском треке без
альбома (105 из 1984) - Яндекс отвечает BadRequestError (wrong-json),
т.к. albumId обязателен, а with_retries лечил это как временный сетевой
сбой и вслепую повторял заведомо безнадёжный запрос 4 раза. Теперь
BadRequestError (как и NotFoundError) не ретраится нигде - это
окончательный ответ сервера, а не таймаут. Треки без album_id
пропускаются проактивно с понятным сообщением, а неожиданный
BadRequestError на отдельном треке больше не прерывает весь прогон -
пропускается со счётчиком, остальные треки обрабатываются дальше.
users_playlists_insert_track и users_playlists_create - операции записи:
таймаут не значит, что запрос не дошёл до сервера. Прошлый фикс слепо
повторял такой вызов при NetworkError, и если сервер уже применил
вставку (а ответ просто потерялся), трек добавлялся в плейлист второй
раз. Теперь при сетевой ошибке сначала перепроверяется фактическое
состояние плейлиста/списка плейлистов, и повтор происходит только если
трек/плейлист там правда ещё отсутствует.
Медленная/нестабильная сеть до api.music.yandex.net роняла весь процесс
при первом же таймауте (5с по умолчанию у библиотеки). Добавлен
with_retries с экспоненциальной паузой на всех сетевых точках входа
(fetch, language, genres, apply, client.init), таймаут запроса поднят
до 20с (настраивается в config.toml). Заодно поправлен скрытый баг:
language.py раньше кэшировал таймаут как "текста нет" навсегда -
теперь только настоящий NotFoundError трактуется так.