fix(player): tell the user when the server cannot read the media file

A 404 on the media stream means the server resolved the item but could not
open the file behind it — moved, deleted, or on storage that went away.
Jellyfin maps the resulting FileNotFoundException to 404, and PlaybackInfo
never stats the file, so negotiation succeeds and only the stream request
fails. Playback then died with a snackbar reading "Failed to open
[REDACTED_URL]" before popping the route, which tells the user nothing and
leaves nothing useful in a bug report.

Generalize the HTTP-500 log probe into PlayerError.httpStatusFromLog and
latch every status in fatalPlaybackHttpStatuses. Each latches on its own so
the 503 that stream-lavf-o deliberately retries cannot mask the fatal status
behind it. A 404 now raises a dedicated modal naming the cause and the fix.

On Android a 404 previously failed the "Response code: 500" string test and
fell through to the ExoPlayer→MPV fallback, showing "switching to compatible
player" before failing again on the same request. Read the real status off
HttpDataSource.InvalidResponseCodeException instead and skip the fallback:
an HTTP status is not a codec problem.
This commit is contained in:
edde746
2026-07-31 21:45:33 +02:00
parent 6f9edd3e93
commit 86c8011b72
35 changed files with 290 additions and 45 deletions
+2
View File
@@ -695,6 +695,8 @@
"switchingToCompatiblePlayer": "Uyğun oynadıcıya keçilir...",
"serverLimitTitle": "Oynatma uğursuz oldu",
"serverLimitBody": "Server xətası (HTTP 500). Məhdudiyyət bu seansı rədd etdi.",
"mediaUnreadableTitle": "",
"mediaUnreadableBody": "",
"logsUploaded": "Jurnallar yükləndi",
"logsUploadFailed": "Jurnallar yüklənə bilmədi",
"logId": "Jurnal ID-si"
+2
View File
@@ -695,6 +695,8 @@
"switchingToCompatiblePlayer": "Превключване към съвместим плейър...",
"serverLimitTitle": "Възпроизвеждането е неуспешно",
"serverLimitBody": "Грешка на сървъра (HTTP 500). Вероятно лимит за пропускателна способност/транскодиране е отхвърлил тази сесия. Помолете собственика да го коригира.",
"mediaUnreadableTitle": "",
"mediaUnreadableBody": "",
"logsUploaded": "Логовете са качени",
"logsUploadFailed": "Неуспешно качване на логовете",
"logId": "ID на лога"
+2
View File
@@ -695,6 +695,8 @@
"switchingToCompatiblePlayer": "Skifter til kompatibel afspiller...",
"serverLimitTitle": "Afspilning mislykkedes",
"serverLimitBody": "Serverfejl (HTTP 500). En båndbredde- eller transkodningsgrænse afviste sandsynligvis sessionen. Bed ejeren om at justere den.",
"mediaUnreadableTitle": "",
"mediaUnreadableBody": "",
"logsUploaded": "Logfilerne blev uploadet",
"logsUploadFailed": "Logfilerne kunne ikke uploades",
"logId": "Log-ID"
+2
View File
@@ -695,6 +695,8 @@
"switchingToCompatiblePlayer": "Wechsel zu einem kompatiblen Player …",
"serverLimitTitle": "Wiedergabe fehlgeschlagen",
"serverLimitBody": "Serverfehler (HTTP 500). Vermutlich hat ein Bandbreiten- oder Transkodierungslimit diese Sitzung abgelehnt. Bitte den Besitzer, das Limit anzupassen.",
"mediaUnreadableTitle": "",
"mediaUnreadableBody": "",
"logsUploaded": "Protokolle hochgeladen",
"logsUploadFailed": "Protokolle konnten nicht hochgeladen werden",
"logId": "Protokoll-ID"
+2
View File
@@ -695,6 +695,8 @@
"switchingToCompatiblePlayer": "Switching to compatible player...",
"serverLimitTitle": "Playback failed",
"serverLimitBody": "Server error (HTTP 500). A bandwidth/transcoding limit likely rejected this session. Ask the owner to adjust it.",
"mediaUnreadableTitle": "File unavailable",
"mediaUnreadableBody": "The server found this item but could not read its file (HTTP 404). The file was probably moved, deleted, or its storage is offline. Ask the server owner to check the file and rescan the library.",
"logsUploaded": "Logs uploaded",
"logsUploadFailed": "Failed to upload logs",
"logId": "Log ID"
+2
View File
@@ -695,6 +695,8 @@
"switchingToCompatiblePlayer": "Cambiando a reproductor compatible...",
"serverLimitTitle": "Error de reproducción",
"serverLimitBody": "Error del servidor (HTTP 500). Un límite de ancho de banda/transcodificación probablemente rechazó esta sesión. Pide al propietario que lo ajuste.",
"mediaUnreadableTitle": "",
"mediaUnreadableBody": "",
"logsUploaded": "Registros subidos",
"logsUploadFailed": "Error al subir registros",
"logId": "ID de registro"
+2
View File
@@ -695,6 +695,8 @@
"switchingToCompatiblePlayer": "Passage au lecteur compatible...",
"serverLimitTitle": "Échec de la lecture",
"serverLimitBody": "Erreur serveur (HTTP 500). Une limite de bande passante/transcodage a probablement rejeté cette session. Demandez au propriétaire de l'ajuster.",
"mediaUnreadableTitle": "",
"mediaUnreadableBody": "",
"logsUploaded": "Journaux envoyés",
"logsUploadFailed": "Échec de lenvoi des journaux",
"logId": "Identifiant du journal"
+2
View File
@@ -695,6 +695,8 @@
"switchingToCompatiblePlayer": "Váltás kompatibilis lejátszóra...",
"serverLimitTitle": "A lejátszás nem sikerült",
"serverLimitBody": "Szerverhiba (HTTP 500). A munkamenetet valószínűleg egy sávszélességi vagy átkódolási korlát utasította el. Kérd meg a tulajdonost a korlát módosítására.",
"mediaUnreadableTitle": "",
"mediaUnreadableBody": "",
"logsUploaded": "Naplók feltöltve",
"logsUploadFailed": "Nem sikerült a naplók feltöltése",
"logId": "Naplóazonosító"
+2
View File
@@ -695,6 +695,8 @@
"switchingToCompatiblePlayer": "Passaggio al lettore compatibile...",
"serverLimitTitle": "Riproduzione non riuscita",
"serverLimitBody": "Errore del server (HTTP 500). È probabile che un limite di banda o transcodifica abbia impedito questa sessione. Chiedi al proprietario di modificare il limite.",
"mediaUnreadableTitle": "",
"mediaUnreadableBody": "",
"logsUploaded": "Log caricati",
"logsUploadFailed": "Impossibile caricare i log",
"logId": "ID log"
+2
View File
@@ -694,6 +694,8 @@
"switchingToCompatiblePlayer": "互換性のあるプレーヤーに切り替え中…",
"serverLimitTitle": "再生に失敗しました",
"serverLimitBody": "サーバーエラー(HTTP 500)。帯域幅/トランスコード制限により拒否された可能性があります。所有者に調整を依頼してください。",
"mediaUnreadableTitle": "",
"mediaUnreadableBody": "",
"logsUploaded": "ログをアップロードしました",
"logsUploadFailed": "ログのアップロードに失敗しました",
"logId": "ログID"
+2
View File
@@ -695,6 +695,8 @@
"switchingToCompatiblePlayer": "Үйлесімді ойнатқышқа ауысуда...",
"serverLimitTitle": "Ойнату қатесі",
"serverLimitBody": "Сервер қатесі (HTTP 500). Шектеу бұл сеансты қабылдамады.",
"mediaUnreadableTitle": "",
"mediaUnreadableBody": "",
"logsUploaded": "Журналдар жүктелді",
"logsUploadFailed": "Журналдарды жүктеу мүмкін болмады",
"logId": "Журнал ID-сі"
+2
View File
@@ -694,6 +694,8 @@
"switchingToCompatiblePlayer": "호환되는 플레이어로 전환 중...",
"serverLimitTitle": "재생 실패",
"serverLimitBody": "서버 오류(HTTP 500). 대역폭/트랜스코딩 제한으로 세션이 거부된 것 같습니다. 소유자에게 조정을 요청하세요.",
"mediaUnreadableTitle": "",
"mediaUnreadableBody": "",
"logsUploaded": "로그 업로드 완료",
"logsUploadFailed": "로그 업로드 실패",
"logId": "로그 ID"
+2
View File
@@ -695,6 +695,8 @@
"switchingToCompatiblePlayer": "Bytter til kompatibel spiller...",
"serverLimitTitle": "Avspilling mislyktes",
"serverLimitBody": "Serverfeil (HTTP 500). En båndbredde-/transkodingsgrense avviste trolig økten. Be eieren justere den.",
"mediaUnreadableTitle": "",
"mediaUnreadableBody": "",
"logsUploaded": "Logger lastet opp",
"logsUploadFailed": "Kunne ikke laste opp logger",
"logId": "Logg-ID"
+2
View File
@@ -695,6 +695,8 @@
"switchingToCompatiblePlayer": "Overschakelen naar compatibele speler...",
"serverLimitTitle": "Afspelen mislukt",
"serverLimitBody": "Serverfout (HTTP 500). Waarschijnlijk weigerde een bandbreedte-/transcodeerlimiet deze sessie. Vraag de eigenaar dit aan te passen.",
"mediaUnreadableTitle": "",
"mediaUnreadableBody": "",
"logsUploaded": "Logbestanden geüpload",
"logsUploadFailed": "Uploaden van logbestanden mislukt",
"logId": "Logboek-ID"
+2
View File
@@ -697,6 +697,8 @@
"switchingToCompatiblePlayer": "Przełączanie na kompatybilny odtwarzacz...",
"serverLimitTitle": "Odtwarzanie nie powiodło się",
"serverLimitBody": "Błąd serwera (HTTP 500). Limit przepustowości/transkodowania prawdopodobnie odrzucił tę sesję. Poproś właściciela o zmianę.",
"mediaUnreadableTitle": "",
"mediaUnreadableBody": "",
"logsUploaded": "Logi przesłane",
"logsUploadFailed": "Nie udało się przesłać logów",
"logId": "ID logu"
+2
View File
@@ -695,6 +695,8 @@
"switchingToCompatiblePlayer": "Alternando para um reprodutor compatível...",
"serverLimitTitle": "Falha na reprodução",
"serverLimitBody": "Erro do servidor (HTTP 500). Um limite de largura de banda ou transcodificação provavelmente rejeitou esta sessão. Peça ao proprietário do servidor para ajustá-lo.",
"mediaUnreadableTitle": "",
"mediaUnreadableBody": "",
"logsUploaded": "Logs enviados",
"logsUploadFailed": "Falha ao enviar logs",
"logId": "ID do log"
+2
View File
@@ -697,6 +697,8 @@
"switchingToCompatiblePlayer": "Переключение на совместимый плеер...",
"serverLimitTitle": "Ошибка воспроизведения",
"serverLimitBody": "Ошибка сервера (HTTP 500). Лимит пропускной способности/транскодирования, вероятно, отклонил сессию. Попросите владельца изменить настройки.",
"mediaUnreadableTitle": "",
"mediaUnreadableBody": "",
"logsUploaded": "Логи загружены",
"logsUploadFailed": "Не удалось загрузить логи",
"logId": "ID лога"
+1 -1
View File
@@ -4,7 +4,7 @@
/// To regenerate, run: `dart run slang`
///
/// Locales: 22
/// Strings: 32938 (1497 per locale)
/// Strings: 32940 (1497 per locale)
// coverage:ignore-file
// ignore_for_file: type=lint, unused_import
+12 -4
View File
@@ -2117,6 +2117,12 @@ class Translations$messages$en {
/// en: 'Server error (HTTP 500). A bandwidth/transcoding limit likely rejected this session. Ask the owner to adjust it.'
String get serverLimitBody => 'Server error (HTTP 500). A bandwidth/transcoding limit likely rejected this session. Ask the owner to adjust it.';
/// en: 'File unavailable'
String get mediaUnreadableTitle => 'File unavailable';
/// en: 'The server found this item but could not read its file (HTTP 404). The file was probably moved, deleted, or its storage is offline. Ask the server owner to check the file and rescan the library.'
String get mediaUnreadableBody => 'The server found this item but could not read its file (HTTP 404). The file was probably moved, deleted, or its storage is offline. Ask the server owner to check the file and rescan the library.';
/// en: 'Logs uploaded'
String get logsUploaded => 'Logs uploaded';
@@ -6669,6 +6675,8 @@ extension on Translations {
'messages.switchingToCompatiblePlayer' => 'Switching to compatible player...',
'messages.serverLimitTitle' => 'Playback failed',
'messages.serverLimitBody' => 'Server error (HTTP 500). A bandwidth/transcoding limit likely rejected this session. Ask the owner to adjust it.',
'messages.mediaUnreadableTitle' => 'File unavailable',
'messages.mediaUnreadableBody' => 'The server found this item but could not read its file (HTTP 404). The file was probably moved, deleted, or its storage is offline. Ask the server owner to check the file and rescan the library.',
'messages.logsUploaded' => 'Logs uploaded',
'messages.logsUploadFailed' => 'Failed to upload logs',
'messages.logId' => 'Log ID',
@@ -7034,10 +7042,10 @@ extension on Translations {
'explore.sourceMaterial.novel' => 'Novel',
'explore.sourceMaterial.visualNovel' => 'Visual novel',
'explore.sourceMaterial.game' => 'Game',
'explore.sourceMaterial.webComic' => 'Web comic',
'explore.sourceMaterial.musicRelease' => 'Music',
_ => null,
} ?? switch (path) {
'explore.sourceMaterial.webComic' => 'Web comic',
'explore.sourceMaterial.musicRelease' => 'Music',
'explore.sourceMaterial.otherMedia' => 'Other',
'explore.creditRole.director' => 'Director',
'explore.creditRole.writer' => 'Writer',
@@ -7548,10 +7556,10 @@ extension on Translations {
'metadataEdit.screenTitle' => 'Edit Metadata',
'metadataEdit.basicInfo' => 'Basic Info',
'metadataEdit.artwork' => 'Artwork',
'metadataEdit.advancedSettings' => 'Advanced Settings',
'metadataEdit.title' => 'Title',
_ => null,
} ?? switch (path) {
'metadataEdit.advancedSettings' => 'Advanced Settings',
'metadataEdit.title' => 'Title',
'metadataEdit.sortTitle' => 'Sort Title',
'metadataEdit.originalTitle' => 'Original Title',
'metadataEdit.releaseDate' => 'Release Date',
+2
View File
@@ -695,6 +695,8 @@
"switchingToCompatiblePlayer": "Byter till kompatibel spelare...",
"serverLimitTitle": "Uppspelningen misslyckades",
"serverLimitBody": "Serverfel (HTTP 500). En bandbredds-/transkodningsgräns avvisade troligen sessionen. Be ägaren justera den.",
"mediaUnreadableTitle": "",
"mediaUnreadableBody": "",
"logsUploaded": "Loggarna har laddats upp",
"logsUploadFailed": "Det gick inte att ladda upp loggarna",
"logId": "Logg-ID"
+2
View File
@@ -695,6 +695,8 @@
"switchingToCompatiblePlayer": "Uyumlu oynatıcıya geçiliyor...",
"serverLimitTitle": "Oynatma başarısız oldu",
"serverLimitBody": "Sunucu hatası (HTTP 500). Muhtemelen bir bant genişliği/kod dönüştürme sınırı bu oturumu reddetti. Sunucu sahibinden bunu ayarlamasını isteyin.",
"mediaUnreadableTitle": "",
"mediaUnreadableBody": "",
"logsUploaded": "Günlükler yüklendi",
"logsUploadFailed": "Günlükler yüklenemedi",
"logId": "Günlük Kimliği (ID)"
+2
View File
@@ -695,6 +695,8 @@
"switchingToCompatiblePlayer": "Mos keluvchi pleyerga oʻtilmoqda...",
"serverLimitTitle": "Ijro etishda xatolik",
"serverLimitBody": "Server xatoligi (HTTP 500). Cheklov ushbu seansni rad etdi.",
"mediaUnreadableTitle": "",
"mediaUnreadableBody": "",
"logsUploaded": "Jurnallar yuklandi",
"logsUploadFailed": "Jurnallarni yuklab boʻlmadi",
"logId": "Jurnal ID-si"
+2
View File
@@ -694,6 +694,8 @@
"switchingToCompatiblePlayer": "正在切換至相容的播放器…",
"serverLimitTitle": "播放失敗",
"serverLimitBody": "伺服器錯誤(HTTP 500)。伺服器的頻寬或轉碼限制可能拒絕此播放要求。請聯絡伺服器擁有者調整設定。",
"mediaUnreadableTitle": "",
"mediaUnreadableBody": "",
"logsUploaded": "日誌已上傳",
"logsUploadFailed": "上傳日誌失敗",
"logId": "日誌 ID"
+2
View File
@@ -694,6 +694,8 @@
"switchingToCompatiblePlayer": "正在切换到兼容的播放器…",
"serverLimitTitle": "播放失败",
"serverLimitBody": "服务器错误(HTTP 500)。此次会话可能因带宽或转码限制而被拒绝。请联系服务器所有者调整限制。",
"mediaUnreadableTitle": "",
"mediaUnreadableBody": "",
"logsUploaded": "日志已上传",
"logsUploadFailed": "上传日志失败",
"logId": "日志 ID"