From 7445d5befefd029e1ef764075293e10c920788a1 Mon Sep 17 00:00:00 2001 From: Huang Xin Date: Sat, 21 Mar 2026 02:32:09 +0800 Subject: [PATCH] feat(library): add batch refresh metadata option, closes #3294 (#3573) Add a "Refresh Metadata" option under Advanced Settings that re-parses book files to update metadata (series info, language, etc.) for books imported before these fields were supported. - Add refreshBookMetadata() in bookService that opens a book file, re-parses with DocumentLoader, and updates metadata/metaHash/series without touching updatedAt or user-edited fields - Add menu item in SettingsMenu with progress display - Add unit tests for single-book metadata refresh - Translate new i18n strings across all 29 locales Co-authored-by: Claude Opus 4.6 (1M context) --- .../public/locales/ar/translation.json | 11 ++++- .../public/locales/bn/translation.json | 7 ++- .../public/locales/bo/translation.json | 6 ++- .../public/locales/de/translation.json | 7 ++- .../public/locales/el/translation.json | 7 ++- .../public/locales/es/translation.json | 8 +++- .../public/locales/fa/translation.json | 7 ++- .../public/locales/fr/translation.json | 8 +++- .../public/locales/he/translation.json | 8 +++- .../public/locales/hi/translation.json | 7 ++- .../public/locales/id/translation.json | 6 ++- .../public/locales/it/translation.json | 8 +++- .../public/locales/ja/translation.json | 6 ++- .../public/locales/ko/translation.json | 6 ++- .../public/locales/ms/translation.json | 6 ++- .../public/locales/nl/translation.json | 7 ++- .../public/locales/pl/translation.json | 9 +++- .../public/locales/pt/translation.json | 8 +++- .../public/locales/ru/translation.json | 9 +++- .../public/locales/si/translation.json | 7 ++- .../public/locales/sl/translation.json | 9 +++- .../public/locales/sv/translation.json | 7 ++- .../public/locales/ta/translation.json | 7 ++- .../public/locales/th/translation.json | 6 ++- .../public/locales/tr/translation.json | 7 ++- .../public/locales/uk/translation.json | 9 +++- .../public/locales/vi/translation.json | 6 ++- .../public/locales/zh-CN/translation.json | 6 ++- .../public/locales/zh-TW/translation.json | 6 ++- .../__tests__/services/suites/book-tests.ts | 28 +++++++++++ .../app/library/components/SettingsMenu.tsx | 46 ++++++++++++++++++- apps/readest-app/src/services/appService.ts | 4 ++ apps/readest-app/src/services/bookService.ts | 31 +++++++++++++ apps/readest-app/src/types/system.ts | 1 + 34 files changed, 291 insertions(+), 30 deletions(-) diff --git a/apps/readest-app/public/locales/ar/translation.json b/apps/readest-app/public/locales/ar/translation.json index 33e66148..416f00b1 100644 --- a/apps/readest-app/public/locales/ar/translation.json +++ b/apps/readest-app/public/locales/ar/translation.json @@ -1145,5 +1145,14 @@ "Restore completed successfully!": "تمت الاستعادة بنجاح!", "Your library has been saved to the selected location.": "تم حفظ مكتبتك في الموقع المحدد.", "{{added}} books added, {{updated}} books updated.": "تمت إضافة {{added}} كتب، وتحديث {{updated}} كتب.", - "Operation failed": "فشلت العملية" + "Operation failed": "فشلت العملية", + "Loading library...": "جارٍ تحميل المكتبة...", + "{{count}} books refreshed_zero": "لم يتم تحديث أي كتاب", + "{{count}} books refreshed_one": "تم تحديث كتاب واحد", + "{{count}} books refreshed_two": "تم تحديث كتابين", + "{{count}} books refreshed_few": "تم تحديث {{count}} كتب", + "{{count}} books refreshed_many": "تم تحديث {{count}} كتابًا", + "{{count}} books refreshed_other": "تم تحديث {{count}} كتاب", + "Failed to refresh metadata": "فشل تحديث البيانات الوصفية", + "Refresh Metadata": "تحديث البيانات الوصفية" } diff --git a/apps/readest-app/public/locales/bn/translation.json b/apps/readest-app/public/locales/bn/translation.json index 8d7023e9..148faf84 100644 --- a/apps/readest-app/public/locales/bn/translation.json +++ b/apps/readest-app/public/locales/bn/translation.json @@ -1097,5 +1097,10 @@ "Restore completed successfully!": "পুনরুদ্ধার সফলভাবে সম্পন্ন!", "Your library has been saved to the selected location.": "আপনার লাইব্রেরি নির্বাচিত স্থানে সংরক্ষিত হয়েছে।", "{{added}} books added, {{updated}} books updated.": "{{added}}টি বই যোগ হয়েছে, {{updated}}টি বই আপডেট হয়েছে।", - "Operation failed": "অপারেশন ব্যর্থ" + "Operation failed": "অপারেশন ব্যর্থ", + "Loading library...": "লাইব্রেরি লোড হচ্ছে...", + "{{count}} books refreshed_one": "{{count}}টি বইয়ের মেটাডেটা রিফ্রেশ হয়েছে", + "{{count}} books refreshed_other": "{{count}}টি বইয়ের মেটাডেটা রিফ্রেশ হয়েছে", + "Failed to refresh metadata": "মেটাডেটা রিফ্রেশ ব্যর্থ", + "Refresh Metadata": "মেটাডেটা রিফ্রেশ" } diff --git a/apps/readest-app/public/locales/bo/translation.json b/apps/readest-app/public/locales/bo/translation.json index 3ce70c10..f952ae15 100644 --- a/apps/readest-app/public/locales/bo/translation.json +++ b/apps/readest-app/public/locales/bo/translation.json @@ -1085,5 +1085,9 @@ "Restore completed successfully!": "སླར་གསོ་ལེགས་གྲུབ་བྱུང་!", "Your library has been saved to the selected location.": "ཁྱོད་ཀྱི་དཔེ་མཛོད་འདེམས་ས་དེར་ཉར་ཟིན།", "{{added}} books added, {{updated}} books updated.": "དཔེ་དེབ {{added}} བསྣན་ཟིན, {{updated}} གསར་བསྒྱུར་བྱས་ཟིན།", - "Operation failed": "བཀོལ་སྤྱོད་བྱེད་མ་ཐུབ" + "Operation failed": "བཀོལ་སྤྱོད་བྱེད་མ་ཐུབ", + "Loading library...": "དཔེ་མཛོད་འཇུག་བཞིན་པ...", + "{{count}} books refreshed_other": "تم تحديث {{count}} كتب", + "Failed to refresh metadata": "མེ་ཊ་ཌེ་ཊ་གསར་བསྒྱུར་བྱེད་མ་ཐུབ", + "Refresh Metadata": "མེ་ཊ་ཌེ་ཊ་གསར་བསྒྱུར" } diff --git a/apps/readest-app/public/locales/de/translation.json b/apps/readest-app/public/locales/de/translation.json index 02d94536..6faddf71 100644 --- a/apps/readest-app/public/locales/de/translation.json +++ b/apps/readest-app/public/locales/de/translation.json @@ -1097,5 +1097,10 @@ "Restore completed successfully!": "Wiederherstellung erfolgreich abgeschlossen!", "Your library has been saved to the selected location.": "Ihre Bibliothek wurde am ausgewählten Ort gespeichert.", "{{added}} books added, {{updated}} books updated.": "{{added}} Bücher hinzugefügt, {{updated}} Bücher aktualisiert.", - "Operation failed": "Vorgang fehlgeschlagen" + "Operation failed": "Vorgang fehlgeschlagen", + "Loading library...": "Bibliothek wird geladen...", + "{{count}} books refreshed_one": "{{count}} Buch aktualisiert", + "{{count}} books refreshed_other": "{{count}} Bücher aktualisiert", + "Failed to refresh metadata": "Metadaten-Aktualisierung fehlgeschlagen", + "Refresh Metadata": "Metadaten aktualisieren" } diff --git a/apps/readest-app/public/locales/el/translation.json b/apps/readest-app/public/locales/el/translation.json index b2e4a9e3..da4edb28 100644 --- a/apps/readest-app/public/locales/el/translation.json +++ b/apps/readest-app/public/locales/el/translation.json @@ -1097,5 +1097,10 @@ "Restore completed successfully!": "Η επαναφορά ολοκληρώθηκε!", "Your library has been saved to the selected location.": "Η βιβλιοθήκη σας αποθηκεύτηκε στην επιλεγμένη τοποθεσία.", "{{added}} books added, {{updated}} books updated.": "{{added}} βιβλία προστέθηκαν, {{updated}} βιβλία ενημερώθηκαν.", - "Operation failed": "Η λειτουργία απέτυχε" + "Operation failed": "Η λειτουργία απέτυχε", + "Loading library...": "Φόρτωση βιβλιοθήκης...", + "{{count}} books refreshed_one": "{{count}} βιβλίο ανανεώθηκε", + "{{count}} books refreshed_other": "{{count}} βιβλία ανανεώθηκαν", + "Failed to refresh metadata": "Αποτυχία ανανέωσης μεταδεδομένων", + "Refresh Metadata": "Ανανέωση μεταδεδομένων" } diff --git a/apps/readest-app/public/locales/es/translation.json b/apps/readest-app/public/locales/es/translation.json index 78eae30a..1fb04eac 100644 --- a/apps/readest-app/public/locales/es/translation.json +++ b/apps/readest-app/public/locales/es/translation.json @@ -1109,5 +1109,11 @@ "Restore completed successfully!": "¡Restauración completada!", "Your library has been saved to the selected location.": "Tu biblioteca se ha guardado en la ubicación seleccionada.", "{{added}} books added, {{updated}} books updated.": "{{added}} libros añadidos, {{updated}} libros actualizados.", - "Operation failed": "La operación falló" + "Operation failed": "La operación falló", + "Loading library...": "Cargando biblioteca...", + "{{count}} books refreshed_one": "{{count}} libro actualizado", + "{{count}} books refreshed_many": "{{count}} libros actualizados", + "{{count}} books refreshed_other": "{{count}} libros actualizados", + "Failed to refresh metadata": "Error al actualizar metadatos", + "Refresh Metadata": "Actualizar metadatos" } diff --git a/apps/readest-app/public/locales/fa/translation.json b/apps/readest-app/public/locales/fa/translation.json index b116b2dd..ce58be67 100644 --- a/apps/readest-app/public/locales/fa/translation.json +++ b/apps/readest-app/public/locales/fa/translation.json @@ -1097,5 +1097,10 @@ "Restore completed successfully!": "بازیابی با موفقیت انجام شد!", "Your library has been saved to the selected location.": "کتابخانه شما در مکان انتخاب‌شده ذخیره شد.", "{{added}} books added, {{updated}} books updated.": "{{added}} کتاب اضافه شد، {{updated}} کتاب به‌روزرسانی شد.", - "Operation failed": "عملیات ناموفق" + "Operation failed": "عملیات ناموفق", + "Loading library...": "در حال بارگذاری کتابخانه...", + "{{count}} books refreshed_one": "{{count}} کتاب به‌روزرسانی شد", + "{{count}} books refreshed_other": "{{count}} کتاب به‌روزرسانی شد", + "Failed to refresh metadata": "به‌روزرسانی فراداده ناموفق", + "Refresh Metadata": "به‌روزرسانی فراداده" } diff --git a/apps/readest-app/public/locales/fr/translation.json b/apps/readest-app/public/locales/fr/translation.json index 137bdc3b..bb0de22e 100644 --- a/apps/readest-app/public/locales/fr/translation.json +++ b/apps/readest-app/public/locales/fr/translation.json @@ -1109,5 +1109,11 @@ "Restore completed successfully!": "Restauration terminée avec succès !", "Your library has been saved to the selected location.": "Votre bibliothèque a été enregistrée à l'emplacement sélectionné.", "{{added}} books added, {{updated}} books updated.": "{{added}} livres ajoutés, {{updated}} livres mis à jour.", - "Operation failed": "L'opération a échoué" + "Operation failed": "L'opération a échoué", + "Loading library...": "Chargement de la bibliothèque...", + "{{count}} books refreshed_one": "{{count}} livre rafraîchi", + "{{count}} books refreshed_many": "{{count}} livres rafraîchis", + "{{count}} books refreshed_other": "{{count}} livres rafraîchis", + "Failed to refresh metadata": "Échec du rafraîchissement des métadonnées", + "Refresh Metadata": "Rafraîchir les métadonnées" } diff --git a/apps/readest-app/public/locales/he/translation.json b/apps/readest-app/public/locales/he/translation.json index 67fd069f..08ac2277 100644 --- a/apps/readest-app/public/locales/he/translation.json +++ b/apps/readest-app/public/locales/he/translation.json @@ -1109,5 +1109,11 @@ "Restore completed successfully!": "השחזור הושלם בהצלחה!", "Your library has been saved to the selected location.": "הספרייה שלך נשמרה במיקום שנבחר.", "{{added}} books added, {{updated}} books updated.": "{{added}} ספרים נוספו, {{updated}} ספרים עודכנו.", - "Operation failed": "הפעולה נכשלה" + "Operation failed": "הפעולה נכשלה", + "Loading library...": "טוען ספרייה...", + "{{count}} books refreshed_one": "ספר {{count}} רוענן", + "{{count}} books refreshed_two": "{{count}} ספרים רועננו", + "{{count}} books refreshed_other": "{{count}} ספרים רועננו", + "Failed to refresh metadata": "רענון המטא-נתונים נכשל", + "Refresh Metadata": "רענון מטא-נתונים" } diff --git a/apps/readest-app/public/locales/hi/translation.json b/apps/readest-app/public/locales/hi/translation.json index 486e8e7e..323d52a3 100644 --- a/apps/readest-app/public/locales/hi/translation.json +++ b/apps/readest-app/public/locales/hi/translation.json @@ -1097,5 +1097,10 @@ "Restore completed successfully!": "पुनर्स्थापना सफलतापूर्वक पूर्ण!", "Your library has been saved to the selected location.": "आपकी लाइब्रेरी चयनित स्थान पर सहेजी गई है।", "{{added}} books added, {{updated}} books updated.": "{{added}} पुस्तकें जोड़ी गईं, {{updated}} पुस्तकें अपडेट की गईं।", - "Operation failed": "ऑपरेशन विफल" + "Operation failed": "ऑपरेशन विफल", + "Loading library...": "लाइब्रेरी लोड हो रही है...", + "{{count}} books refreshed_one": "{{count}} पुस्तक रिफ्रेश हुई", + "{{count}} books refreshed_other": "{{count}} पुस्तकें रिफ्रेश हुईं", + "Failed to refresh metadata": "मेटाडेटा रिफ्रेश विफल", + "Refresh Metadata": "मेटाडेटा रिफ्रेश करें" } diff --git a/apps/readest-app/public/locales/id/translation.json b/apps/readest-app/public/locales/id/translation.json index 709d6ddf..0222aa16 100644 --- a/apps/readest-app/public/locales/id/translation.json +++ b/apps/readest-app/public/locales/id/translation.json @@ -1085,5 +1085,9 @@ "Restore completed successfully!": "Pemulihan berhasil!", "Your library has been saved to the selected location.": "Perpustakaan Anda telah disimpan di lokasi yang dipilih.", "{{added}} books added, {{updated}} books updated.": "{{added}} buku ditambahkan, {{updated}} buku diperbarui.", - "Operation failed": "Operasi gagal" + "Operation failed": "Operasi gagal", + "Loading library...": "Memuat perpustakaan...", + "{{count}} books refreshed_other": "{{count}} buku diperbarui", + "Failed to refresh metadata": "Gagal memperbarui metadata", + "Refresh Metadata": "Perbarui Metadata" } diff --git a/apps/readest-app/public/locales/it/translation.json b/apps/readest-app/public/locales/it/translation.json index e11f1374..6cf1dad1 100644 --- a/apps/readest-app/public/locales/it/translation.json +++ b/apps/readest-app/public/locales/it/translation.json @@ -1109,5 +1109,11 @@ "Restore completed successfully!": "Ripristino completato con successo!", "Your library has been saved to the selected location.": "La tua libreria è stata salvata nella posizione selezionata.", "{{added}} books added, {{updated}} books updated.": "{{added}} libri aggiunti, {{updated}} libri aggiornati.", - "Operation failed": "Operazione fallita" + "Operation failed": "Operazione fallita", + "Loading library...": "Caricamento libreria...", + "{{count}} books refreshed_one": "{{count}} libro aggiornato", + "{{count}} books refreshed_many": "{{count}} libri aggiornati", + "{{count}} books refreshed_other": "{{count}} libri aggiornati", + "Failed to refresh metadata": "Aggiornamento metadati fallito", + "Refresh Metadata": "Aggiorna metadati" } diff --git a/apps/readest-app/public/locales/ja/translation.json b/apps/readest-app/public/locales/ja/translation.json index e237d3bc..b2e570eb 100644 --- a/apps/readest-app/public/locales/ja/translation.json +++ b/apps/readest-app/public/locales/ja/translation.json @@ -1085,5 +1085,9 @@ "Restore completed successfully!": "復元が完了しました!", "Your library has been saved to the selected location.": "ライブラリが選択した場所に保存されました。", "{{added}} books added, {{updated}} books updated.": "{{added}}冊追加、{{updated}}冊更新されました。", - "Operation failed": "操作に失敗しました" + "Operation failed": "操作に失敗しました", + "Loading library...": "ライブラリを読み込み中...", + "{{count}} books refreshed_other": "{{count}}冊のメタデータを更新しました", + "Failed to refresh metadata": "メタデータの更新に失敗しました", + "Refresh Metadata": "メタデータを更新" } diff --git a/apps/readest-app/public/locales/ko/translation.json b/apps/readest-app/public/locales/ko/translation.json index 92833540..2b63eb39 100644 --- a/apps/readest-app/public/locales/ko/translation.json +++ b/apps/readest-app/public/locales/ko/translation.json @@ -1085,5 +1085,9 @@ "Restore completed successfully!": "복원이 완료되었습니다!", "Your library has been saved to the selected location.": "라이브러리가 선택한 위치에 저장되었습니다.", "{{added}} books added, {{updated}} books updated.": "{{added}}권 추가, {{updated}}권 업데이트되었습니다.", - "Operation failed": "작업 실패" + "Operation failed": "작업 실패", + "Loading library...": "라이브러리 로딩 중...", + "{{count}} books refreshed_other": "{{count}}권 새로고침됨", + "Failed to refresh metadata": "메타데이터 새로고침 실패", + "Refresh Metadata": "메타데이터 새로고침" } diff --git a/apps/readest-app/public/locales/ms/translation.json b/apps/readest-app/public/locales/ms/translation.json index 529c7365..d161cd91 100644 --- a/apps/readest-app/public/locales/ms/translation.json +++ b/apps/readest-app/public/locales/ms/translation.json @@ -1085,5 +1085,9 @@ "Restore completed successfully!": "Pemulihan berjaya!", "Your library has been saved to the selected location.": "Perpustakaan anda telah disimpan di lokasi yang dipilih.", "{{added}} books added, {{updated}} books updated.": "{{added}} buku ditambah, {{updated}} buku dikemas kini.", - "Operation failed": "Operasi gagal" + "Operation failed": "Operasi gagal", + "Loading library...": "Memuatkan perpustakaan...", + "{{count}} books refreshed_other": "{{count}} buku dimuat semula", + "Failed to refresh metadata": "Gagal memuat semula metadata", + "Refresh Metadata": "Muat Semula Metadata" } diff --git a/apps/readest-app/public/locales/nl/translation.json b/apps/readest-app/public/locales/nl/translation.json index bbfefa04..0695a8e9 100644 --- a/apps/readest-app/public/locales/nl/translation.json +++ b/apps/readest-app/public/locales/nl/translation.json @@ -1097,5 +1097,10 @@ "Restore completed successfully!": "Herstel succesvol voltooid!", "Your library has been saved to the selected location.": "Uw bibliotheek is opgeslagen op de geselecteerde locatie.", "{{added}} books added, {{updated}} books updated.": "{{added}} boeken toegevoegd, {{updated}} boeken bijgewerkt.", - "Operation failed": "Bewerking mislukt" + "Operation failed": "Bewerking mislukt", + "Loading library...": "Bibliotheek laden...", + "{{count}} books refreshed_one": "{{count}} boek vernieuwd", + "{{count}} books refreshed_other": "{{count}} boeken vernieuwd", + "Failed to refresh metadata": "Metadata vernieuwen mislukt", + "Refresh Metadata": "Metadata vernieuwen" } diff --git a/apps/readest-app/public/locales/pl/translation.json b/apps/readest-app/public/locales/pl/translation.json index 51e1baf9..db09e9bb 100644 --- a/apps/readest-app/public/locales/pl/translation.json +++ b/apps/readest-app/public/locales/pl/translation.json @@ -1121,5 +1121,12 @@ "Restore completed successfully!": "Przywracanie ukończone!", "Your library has been saved to the selected location.": "Biblioteka została zapisana w wybranej lokalizacji.", "{{added}} books added, {{updated}} books updated.": "Dodano {{added}} książek, zaktualizowano {{updated}} książek.", - "Operation failed": "Operacja nie powiodła się" + "Operation failed": "Operacja nie powiodła się", + "Loading library...": "Wczytywanie biblioteki...", + "{{count}} books refreshed_one": "Odświeżono {{count}} książkę", + "{{count}} books refreshed_few": "Odświeżono {{count}} książki", + "{{count}} books refreshed_many": "Odświeżono {{count}} książek", + "{{count}} books refreshed_other": "Odświeżono {{count}} książek", + "Failed to refresh metadata": "Nie udało się odświeżyć metadanych", + "Refresh Metadata": "Odśwież metadane" } diff --git a/apps/readest-app/public/locales/pt/translation.json b/apps/readest-app/public/locales/pt/translation.json index 77a02e17..d92b6a06 100644 --- a/apps/readest-app/public/locales/pt/translation.json +++ b/apps/readest-app/public/locales/pt/translation.json @@ -1109,5 +1109,11 @@ "Restore completed successfully!": "Restauração concluída com sucesso!", "Your library has been saved to the selected location.": "Sua biblioteca foi salva no local selecionado.", "{{added}} books added, {{updated}} books updated.": "{{added}} livros adicionados, {{updated}} livros atualizados.", - "Operation failed": "Operação falhou" + "Operation failed": "Operação falhou", + "Loading library...": "Carregando biblioteca...", + "{{count}} books refreshed_one": "{{count}} livro atualizado", + "{{count}} books refreshed_many": "{{count}} livros atualizados", + "{{count}} books refreshed_other": "{{count}} livros atualizados", + "Failed to refresh metadata": "Falha ao atualizar metadados", + "Refresh Metadata": "Atualizar metadados" } diff --git a/apps/readest-app/public/locales/ru/translation.json b/apps/readest-app/public/locales/ru/translation.json index d5c47a3f..4650ebb8 100644 --- a/apps/readest-app/public/locales/ru/translation.json +++ b/apps/readest-app/public/locales/ru/translation.json @@ -1121,5 +1121,12 @@ "Restore completed successfully!": "Восстановление завершено!", "Your library has been saved to the selected location.": "Ваша библиотека сохранена в выбранном месте.", "{{added}} books added, {{updated}} books updated.": "Добавлено {{added}} книг, обновлено {{updated}} книг.", - "Operation failed": "Операция не удалась" + "Operation failed": "Операция не удалась", + "Loading library...": "Загрузка библиотеки...", + "{{count}} books refreshed_one": "Обновлена {{count}} книга", + "{{count}} books refreshed_few": "Обновлено {{count}} книги", + "{{count}} books refreshed_many": "Обновлено {{count}} книг", + "{{count}} books refreshed_other": "Обновлено {{count}} книг", + "Failed to refresh metadata": "Не удалось обновить метаданные", + "Refresh Metadata": "Обновить метаданные" } diff --git a/apps/readest-app/public/locales/si/translation.json b/apps/readest-app/public/locales/si/translation.json index 46eb93a0..c76721b9 100644 --- a/apps/readest-app/public/locales/si/translation.json +++ b/apps/readest-app/public/locales/si/translation.json @@ -1097,5 +1097,10 @@ "Restore completed successfully!": "ප්‍රතිසාධනය සාර්ථකව සම්පූර්ණයි!", "Your library has been saved to the selected location.": "ඔබේ පුස්තකාලය තෝරාගත් ස්ථානයේ සුරැකිණි.", "{{added}} books added, {{updated}} books updated.": "පොත් {{added}}ක් එකතු විය, {{updated}}ක් යාවත්කාලීන විය.", - "Operation failed": "මෙහෙයුම අසාර්ථකයි" + "Operation failed": "මෙහෙයුම අසාර්ථකයි", + "Loading library...": "පුස්තකාලය පූරණය වෙමින්...", + "{{count}} books refreshed_one": "පොත් {{count}}ක් යාවත්කාලීන විය", + "{{count}} books refreshed_other": "පොත් {{count}}ක් යාවත්කාලීන විය", + "Failed to refresh metadata": "පාරදත්ත යාවත්කාලීන කිරීම අසාර්ථකයි", + "Refresh Metadata": "පාරදත්ත යාවත්කාලීන කරන්න" } diff --git a/apps/readest-app/public/locales/sl/translation.json b/apps/readest-app/public/locales/sl/translation.json index 16e047f4..64eb1d30 100644 --- a/apps/readest-app/public/locales/sl/translation.json +++ b/apps/readest-app/public/locales/sl/translation.json @@ -1121,5 +1121,12 @@ "Restore completed successfully!": "Obnovitev uspešno zaključena!", "Your library has been saved to the selected location.": "Vaša knjižnica je shranjena na izbrani lokaciji.", "{{added}} books added, {{updated}} books updated.": "{{added}} knjig dodanih, {{updated}} knjig posodobljenih.", - "Operation failed": "Operacija ni uspela" + "Operation failed": "Operacija ni uspela", + "Loading library...": "Nalaganje knjižnice...", + "{{count}} books refreshed_one": "Osvežena {{count}} knjiga", + "{{count}} books refreshed_two": "Osveženi {{count}} knjigi", + "{{count}} books refreshed_few": "Osvežene {{count}} knjige", + "{{count}} books refreshed_other": "Osveženih {{count}} knjig", + "Failed to refresh metadata": "Osveževanje metapodatkov ni uspelo", + "Refresh Metadata": "Osveži metapodatke" } diff --git a/apps/readest-app/public/locales/sv/translation.json b/apps/readest-app/public/locales/sv/translation.json index 292d0e56..f04e3b63 100644 --- a/apps/readest-app/public/locales/sv/translation.json +++ b/apps/readest-app/public/locales/sv/translation.json @@ -1097,5 +1097,10 @@ "Restore completed successfully!": "Återställningen slutförd!", "Your library has been saved to the selected location.": "Ditt bibliotek har sparats på den valda platsen.", "{{added}} books added, {{updated}} books updated.": "{{added}} böcker tillagda, {{updated}} böcker uppdaterade.", - "Operation failed": "Åtgärden misslyckades" + "Operation failed": "Åtgärden misslyckades", + "Loading library...": "Laddar bibliotek...", + "{{count}} books refreshed_one": "{{count}} bok uppdaterad", + "{{count}} books refreshed_other": "{{count}} böcker uppdaterade", + "Failed to refresh metadata": "Kunde inte uppdatera metadata", + "Refresh Metadata": "Uppdatera metadata" } diff --git a/apps/readest-app/public/locales/ta/translation.json b/apps/readest-app/public/locales/ta/translation.json index ab2168a7..61b05fb9 100644 --- a/apps/readest-app/public/locales/ta/translation.json +++ b/apps/readest-app/public/locales/ta/translation.json @@ -1097,5 +1097,10 @@ "Restore completed successfully!": "மீட்டமைப்பு வெற்றிகரமாக முடிந்தது!", "Your library has been saved to the selected location.": "உங்கள் நூலகம் தேர்ந்தெடுக்கப்பட்ட இடத்தில் சேமிக்கப்பட்டது.", "{{added}} books added, {{updated}} books updated.": "{{added}} புத்தகங்கள் சேர்க்கப்பட்டன, {{updated}} புத்தகங்கள் புதுப்பிக்கப்பட்டன.", - "Operation failed": "செயல்பாடு தோல்வி" + "Operation failed": "செயல்பாடு தோல்வி", + "Loading library...": "நூலகம் ஏற்றப்படுகிறது...", + "{{count}} books refreshed_one": "{{count}} புத்தகம் புதுப்பிக்கப்பட்டது", + "{{count}} books refreshed_other": "{{count}} புத்தகங்கள் புதுப்பிக்கப்பட்டன", + "Failed to refresh metadata": "மெட்டாடேட்டா புதுப்பிப்பு தோல்வி", + "Refresh Metadata": "மெட்டாடேட்டா புதுப்பிக்கவும்" } diff --git a/apps/readest-app/public/locales/th/translation.json b/apps/readest-app/public/locales/th/translation.json index d3abec72..58ec3c44 100644 --- a/apps/readest-app/public/locales/th/translation.json +++ b/apps/readest-app/public/locales/th/translation.json @@ -1085,5 +1085,9 @@ "Restore completed successfully!": "กู้คืนสำเร็จ!", "Your library has been saved to the selected location.": "ห้องสมุดของคุณถูกบันทึกไปยังตำแหน่งที่เลือก", "{{added}} books added, {{updated}} books updated.": "เพิ่ม {{added}} เล่ม, อัปเดต {{updated}} เล่ม", - "Operation failed": "การดำเนินการล้มเหลว" + "Operation failed": "การดำเนินการล้มเหลว", + "Loading library...": "กำลังโหลดห้องสมุด...", + "{{count}} books refreshed_other": "รีเฟรชแล้ว {{count}} เล่ม", + "Failed to refresh metadata": "รีเฟรชข้อมูลเมตาล้มเหลว", + "Refresh Metadata": "รีเฟรชข้อมูลเมตา" } diff --git a/apps/readest-app/public/locales/tr/translation.json b/apps/readest-app/public/locales/tr/translation.json index ee0c44e3..f030f0cd 100644 --- a/apps/readest-app/public/locales/tr/translation.json +++ b/apps/readest-app/public/locales/tr/translation.json @@ -1097,5 +1097,10 @@ "Restore completed successfully!": "Geri yükleme başarıyla tamamlandı!", "Your library has been saved to the selected location.": "Kütüphaneniz seçilen konuma kaydedildi.", "{{added}} books added, {{updated}} books updated.": "{{added}} kitap eklendi, {{updated}} kitap güncellendi.", - "Operation failed": "İşlem başarısız" + "Operation failed": "İşlem başarısız", + "Loading library...": "Kütüphane yükleniyor...", + "{{count}} books refreshed_one": "{{count}} kitap yenilendi", + "{{count}} books refreshed_other": "{{count}} kitap yenilendi", + "Failed to refresh metadata": "Meta veri yenileme başarısız", + "Refresh Metadata": "Meta Verileri Yenile" } diff --git a/apps/readest-app/public/locales/uk/translation.json b/apps/readest-app/public/locales/uk/translation.json index f48626d1..6e0cda65 100644 --- a/apps/readest-app/public/locales/uk/translation.json +++ b/apps/readest-app/public/locales/uk/translation.json @@ -1121,5 +1121,12 @@ "Restore completed successfully!": "Відновлення завершено!", "Your library has been saved to the selected location.": "Вашу бібліотеку збережено у вибраному місці.", "{{added}} books added, {{updated}} books updated.": "Додано {{added}} книг, оновлено {{updated}} книг.", - "Operation failed": "Операція не вдалася" + "Operation failed": "Операція не вдалася", + "Loading library...": "Завантаження бібліотеки...", + "{{count}} books refreshed_one": "Оновлено {{count}} книгу", + "{{count}} books refreshed_few": "Оновлено {{count}} книги", + "{{count}} books refreshed_many": "Оновлено {{count}} книг", + "{{count}} books refreshed_other": "Оновлено {{count}} книг", + "Failed to refresh metadata": "Не вдалося оновити метадані", + "Refresh Metadata": "Оновити метадані" } diff --git a/apps/readest-app/public/locales/vi/translation.json b/apps/readest-app/public/locales/vi/translation.json index 330df31c..0282e4f0 100644 --- a/apps/readest-app/public/locales/vi/translation.json +++ b/apps/readest-app/public/locales/vi/translation.json @@ -1085,5 +1085,9 @@ "Restore completed successfully!": "Khôi phục thành công!", "Your library has been saved to the selected location.": "Thư viện của bạn đã được lưu tại vị trí đã chọn.", "{{added}} books added, {{updated}} books updated.": "Đã thêm {{added}} sách, cập nhật {{updated}} sách.", - "Operation failed": "Thao tác thất bại" + "Operation failed": "Thao tác thất bại", + "Loading library...": "Đang tải thư viện...", + "{{count}} books refreshed_other": "Đã làm mới {{count}} sách", + "Failed to refresh metadata": "Không thể làm mới siêu dữ liệu", + "Refresh Metadata": "Làm mới siêu dữ liệu" } diff --git a/apps/readest-app/public/locales/zh-CN/translation.json b/apps/readest-app/public/locales/zh-CN/translation.json index 6abcf0df..edc1e7df 100644 --- a/apps/readest-app/public/locales/zh-CN/translation.json +++ b/apps/readest-app/public/locales/zh-CN/translation.json @@ -1085,5 +1085,9 @@ "Restore completed successfully!": "恢复成功!", "Your library has been saved to the selected location.": "您的书库已保存到所选位置。", "{{added}} books added, {{updated}} books updated.": "新增 {{added}} 本书,更新 {{updated}} 本书。", - "Operation failed": "操作失败" + "Operation failed": "操作失败", + "Loading library...": "正在加载书库...", + "{{count}} books refreshed_other": "已刷新 {{count}} 本书", + "Failed to refresh metadata": "刷新元数据失败", + "Refresh Metadata": "刷新元数据" } diff --git a/apps/readest-app/public/locales/zh-TW/translation.json b/apps/readest-app/public/locales/zh-TW/translation.json index 5f93fead..e061fcfd 100644 --- a/apps/readest-app/public/locales/zh-TW/translation.json +++ b/apps/readest-app/public/locales/zh-TW/translation.json @@ -1085,5 +1085,9 @@ "Restore completed successfully!": "還原成功!", "Your library has been saved to the selected location.": "您的書庫已儲存至所選位置。", "{{added}} books added, {{updated}} books updated.": "新增 {{added}} 本書,更新 {{updated}} 本書。", - "Operation failed": "操作失敗" + "Operation failed": "操作失敗", + "Loading library...": "正在載入書庫...", + "{{count}} books refreshed_other": "已重新整理 {{count}} 本書", + "Failed to refresh metadata": "重新整理中繼資料失敗", + "Refresh Metadata": "重新整理中繼資料" } diff --git a/apps/readest-app/src/__tests__/services/suites/book-tests.ts b/apps/readest-app/src/__tests__/services/suites/book-tests.ts index 10dbcfb2..1c312f09 100644 --- a/apps/readest-app/src/__tests__/services/suites/book-tests.ts +++ b/apps/readest-app/src/__tests__/services/suites/book-tests.ts @@ -213,4 +213,32 @@ export function bookTests( expect(loaded.progress).toEqual([10, 200]); }); }); + + describe('Refresh metadata', () => { + it('should refresh metadata for a single book', async () => { + const service = getService(); + const books: Book[] = []; + const book = await service.importBook(await getBookFile('sample-alice.epub'), books); + expect(book).not.toBeNull(); + + // Clear metadata to simulate a book imported before metadata parsing was added + book!.metadata = undefined; + book!.primaryLanguage = undefined; + + const result = await service.refreshBookMetadata(book!); + expect(result).toBe(true); + expect(book!.metadata).toBeDefined(); + }); + + it('should not update updatedAt', async () => { + const service = getService(); + const books: Book[] = []; + const book = await service.importBook(await getBookFile('sample-alice.epub'), books); + expect(book).not.toBeNull(); + + const oldUpdatedAt = book!.updatedAt; + await service.refreshBookMetadata(book!); + expect(book!.updatedAt).toBe(oldUpdatedAt); + }); + }); } diff --git a/apps/readest-app/src/app/library/components/SettingsMenu.tsx b/apps/readest-app/src/app/library/components/SettingsMenu.tsx index 71e6b68c..6f7b5359 100644 --- a/apps/readest-app/src/app/library/components/SettingsMenu.tsx +++ b/apps/readest-app/src/app/library/components/SettingsMenu.tsx @@ -67,7 +67,9 @@ const SettingsMenu: React.FC = ({ onPullLibrary, setIsDropdow ); const iconSize = useResponsiveSize(16); - const { isSyncing } = useLibraryStore(); + const [isRefreshingMetadata, setIsRefreshingMetadata] = useState(false); + const [refreshMetadataProgress, setRefreshMetadataProgress] = useState(''); + const { isSyncing, setLibrary } = useLibraryStore(); const { stats, hasActiveTransfers, setIsTransferQueueOpen } = useTransferQueue(); const openTransferQueue = () => { @@ -189,6 +191,42 @@ const SettingsMenu: React.FC = ({ onPullLibrary, setIsDropdow setBackupDialogVisible(true); }; + const handleRefreshMetadata = async () => { + if (!appService || isRefreshingMetadata) return; + setIsRefreshingMetadata(true); + setRefreshMetadataProgress(_('Loading library...')); + try { + const books = await appService.loadLibraryBooks(); + const activeBooks = books.filter((b) => !b.deletedAt); + let refreshed = 0; + for (let i = 0; i < activeBooks.length; i++) { + setRefreshMetadataProgress(`${i + 1} / ${activeBooks.length}`); + try { + if (await appService.refreshBookMetadata(activeBooks[i]!)) { + refreshed++; + } + } catch { + // Skip books whose files can't be opened + } + } + setLibrary(books); + await appService.saveLibraryBooks(books); + setRefreshMetadataProgress(_('{{count}} books refreshed', { count: refreshed })); + onPullLibrary(true); + setTimeout(() => { + setIsRefreshingMetadata(false); + setRefreshMetadataProgress(''); + }, 2000); + } catch (error) { + console.error('Failed to refresh metadata:', error); + setRefreshMetadataProgress(_('Failed to refresh metadata')); + setTimeout(() => { + setIsRefreshingMetadata(false); + setRefreshMetadataProgress(''); + }, 2000); + } + }; + const openSettingsDialog = () => { setIsDropdownOpen?.(false); setSettingsDialogOpen(true); @@ -381,6 +419,12 @@ const SettingsMenu: React.FC = ({ onPullLibrary, setIsDropdow )} + {appService?.isAndroidApp && appService?.distChannel !== 'playstore' && ( { + return BookSvc.refreshBookMetadata(this.fs, book); + } + async isBookAvailable(book: Book): Promise { return BookSvc.isBookAvailable(this.fs, book); } diff --git a/apps/readest-app/src/services/bookService.ts b/apps/readest-app/src/services/bookService.ts index 2160b20d..2baca3bf 100644 --- a/apps/readest-app/src/services/bookService.ts +++ b/apps/readest-app/src/services/bookService.ts @@ -527,6 +527,37 @@ export async function fetchBookDetails( return bookDoc.metadata; } +/** + * Refresh metadata for a single book by re-opening and re-parsing its file. + * Updates series info, language, and other metadata fields without modifying + * user-edited titles or reading progress. + * Returns true if the metadata was successfully refreshed. + */ +export async function refreshBookMetadata(fs: FileSystem, book: Book): Promise { + const { file } = await loadBookContent(fs, book); + const { book: bookDoc } = await new DocumentLoader(file).open(); + if (!bookDoc) return false; + + book.metadata = bookDoc.metadata; + book.metaHash = getMetadataHash(bookDoc.metadata); + const primaryLanguage = getPrimaryLanguage(bookDoc.metadata.language); + if (primaryLanguage) { + book.primaryLanguage = primaryLanguage; + } + + // Update series info from metadata + if (book.metadata?.belongsTo?.series) { + const belongsTo = book.metadata.belongsTo.series; + const series = Array.isArray(belongsTo) ? belongsTo[0] : belongsTo; + if (series) { + book.metadata.series = formatTitle(series.name); + book.metadata.seriesIndex = parseFloat(series.position || '0'); + } + } + + return true; +} + export async function exportBook( fs: FileSystem, book: Book, diff --git a/apps/readest-app/src/types/system.ts b/apps/readest-app/src/types/system.ts index cb144a30..3eb7fbc1 100644 --- a/apps/readest-app/src/types/system.ts +++ b/apps/readest-app/src/types/system.ts @@ -133,6 +133,7 @@ export interface AppService { overwrite?: boolean, transient?: boolean, ): Promise; + refreshBookMetadata(book: Book): Promise; deleteBook(book: Book, deleteAction: DeleteAction): Promise; uploadBook(book: Book, onProgress?: ProgressHandler): Promise; downloadBook(