diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 3ff7670739..278d3a7f93 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -5192,9 +5192,9 @@ } }, "node_modules/oidc-provider": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-9.3.0.tgz", - "integrity": "sha512-JVocwYM+Fs76nOCED2hMf3iMfrzhN4jISmCYVuFhBEnZiFk3QlODzQXkO1XS/Spw8VwRlKxwIl3otkiintFIjw==", + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-9.4.0.tgz", + "integrity": "sha512-1mEUejJq7cQV/b6cw2nitqOyIlOJTfQ6RNwGFcA7/Pp+vKIWBn8p48ylFtogP3Hbvrkf9s9W5HUeFe+v1KpcEQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/e2e/test-assets b/e2e/test-assets index 18736fc27a..37f60ea537 160000 --- a/e2e/test-assets +++ b/e2e/test-assets @@ -1 +1 @@ -Subproject commit 18736fc27a80c99c68e856cdb4f842bc81ed3445 +Subproject commit 37f60ea537c0228f5f92e4f42dc42f0bb39a6d7f diff --git a/i18n/cs.json b/i18n/cs.json index c14a4b6d0c..80ea9674c0 100644 --- a/i18n/cs.json +++ b/i18n/cs.json @@ -573,6 +573,8 @@ "backup_options_page_title": "Nastavení záloh", "backup_setting_subtitle": "Správa nastavení zálohování na pozadí a na popředí", "backward": "Pozpátku", + "beta_sync": "Stav synchronizace beta verze", + "beta_sync_subtitle": "Správa nového systému synchronizace", "biometric_auth_enabled": "Biometrické ověřování je povoleno", "biometric_locked_out": "Jste vyloučeni z biometrického ověřování", "biometric_no_options": "Biometrické možnosti nejsou k dispozici", @@ -590,7 +592,7 @@ "cache_settings_clear_cache_button": "Vymazat vyrovnávací paměť", "cache_settings_clear_cache_button_title": "Vymaže vyrovnávací paměť aplikace. To výrazně ovlivní výkon aplikace, dokud se vyrovnávací paměť neobnoví.", "cache_settings_duplicated_assets_clear_button": "VYMAZAT", - "cache_settings_duplicated_assets_subtitle": "Fotografie a videa, které aplikace zařadila na černou listinu", + "cache_settings_duplicated_assets_subtitle": "Fotografie a videa, které aplikace ignoruje", "cache_settings_duplicated_assets_title": "Duplicitní položky ({count})", "cache_settings_statistics_album": "Knihovna náhledů", "cache_settings_statistics_full": "Kompletní fotografie", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "Povolit dotykovou zpětnou vazbu", "haptic_feedback_title": "Dotyková zpětná vazba", "has_quota": "Má kvótu", + "hash_asset": "Hash položky", + "hashed_assets": "Hashované položky", + "hashing": "Hashování", "header_settings_add_header_tip": "Přidat hlavičku", "header_settings_field_validator_msg": "Hodnota nemůže být prázdná", "header_settings_header_name_input": "Název hlavičky", @@ -1083,6 +1088,7 @@ "host": "Hostitel", "hour": "Hodina", "id": "ID", + "idle": "Nečinnost", "ignore_icloud_photos": "Ignorovat fotografie na iCloudu", "ignore_icloud_photos_description": "Fotografie uložené na iCloudu se nebudou nahrávat na Immich server", "image": "Obrázek", @@ -1165,7 +1171,9 @@ "list": "Seznam", "loading": "Načítání", "loading_search_results_failed": "Načítání výsledků vyhledávání se nezdařilo", + "local": "Místní", "local_asset_cast_failed": "Nelze odeslat položku, která není nahraná na serveru", + "local_assets": "Místní položky", "local_network": "Místní síť", "local_network_sheet_info": "Aplikace se při použití zadané sítě Wi-Fi připojí k serveru prostřednictvím tohoto URL", "location_permission": "Oprávnění polohy", @@ -1322,6 +1330,7 @@ "no_results": "Žádné výsledky", "no_results_description": "Zkuste použít synonymum nebo obecnější klíčové slovo", "no_shared_albums_message": "Vytvořte si album a sdílejte fotografie a videa s lidmi ve své síti", + "no_uploads_in_progress": "Neprobíhá žádné nahrávání", "not_in_any_album": "Bez alba", "not_selected": "Není vybráno", "note_apply_storage_label_to_previously_uploaded assets": "Upozornění: Chcete-li použít štítek úložiště na dříve nahrané položky, spusťte příkaz", @@ -1359,6 +1368,7 @@ "original": "originál", "other": "Ostatní", "other_devices": "Ostatní zařízení", + "other_entities": "Ostatní entity", "other_variables": "Další proměnné", "owned": "Vlastní", "owner": "Vlastník", @@ -1519,6 +1529,8 @@ "refreshing_faces": "Obnovování obličejů", "refreshing_metadata": "Obnovování metadat", "regenerating_thumbnails": "Regenerace miniatur", + "remote": "Vzdálený", + "remote_assets": "Vzdálené položky", "remove": "Odstranit", "remove_assets_album_confirmation": "Opravdu chcete z alba odstranit {count, plural, one {# položku} few {# položky} other {# položek}}?", "remove_assets_shared_link_confirmation": "Opravdu chcete ze sdíleného odkazu odstranit {count, plural, one {# položku} few {# položky} other {# položek}}?", @@ -1556,11 +1568,15 @@ "reset_password": "Obnovit heslo", "reset_people_visibility": "Obnovit viditelnost lidí", "reset_pin_code": "Resetovat PIN kód", + "reset_sqlite": "Obnovit SQLite databázi", + "reset_sqlite_confirmation": "Jste si jisti, že chcete obnovit SQLite databázi? Pro opětovnou synchronizaci dat se budete muset odhlásit a znovu přihlásit", + "reset_sqlite_success": "Obnovení SQLite databáze proběhlo úspěšně", "reset_to_default": "Obnovit výchozí nastavení", "resolve_duplicates": "Vyřešit duplicity", "resolved_all_duplicates": "Vyřešeny všechny duplicity", "restore": "Obnovit", "restore_all": "Obnovit vše", + "restore_trash_action_prompt": "{count} obnoveno z koše", "restore_user": "Obnovit uživatele", "restored_asset": "Položka obnovena", "resume": "Pokračovat", @@ -1569,6 +1585,7 @@ "role": "Role", "role_editor": "Editor", "role_viewer": "Divák", + "running": "Probíhá", "save": "Uložit", "save_to_gallery": "Uložit do galerie", "saved_api_key": "API klíč uložen", @@ -1822,6 +1839,7 @@ "storage_quota": "Kvóta úložiště", "storage_usage": "Využito {used} z {available}", "submit": "Odeslat", + "success": "Úspěch", "suggestions": "Návrhy", "sunrise_on_the_beach": "Východ slunce na pláži", "support": "Podpora", @@ -1831,6 +1849,8 @@ "sync": "Synchronizovat", "sync_albums": "Synchronizovat alba", "sync_albums_manual_subtitle": "Synchronizovat všechna nahraná videa a fotografie do vybraných záložních alb", + "sync_local": "Synchronizovat místní", + "sync_remote": "Synchronizovat vzdálené", "sync_upload_album_setting_subtitle": "Vytvořit a nahrát fotografie a videa do vybraných alb na Immich", "tag": "Značka", "tag_assets": "Přiřadit značku", @@ -1841,6 +1861,7 @@ "tag_updated": "Aktualizována značka: {tag}", "tagged_assets": "Přiřazena značka {count, plural, one {# položce} other {# položkám}}", "tags": "Značky", + "tap_to_run_job": "Klepnutím na spustíte úlohu", "template": "Šablona", "theme": "Motiv", "theme_selection": "Výběr motivu", diff --git a/i18n/de.json b/i18n/de.json index 79ba1251d5..45245b182b 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -573,6 +573,8 @@ "backup_options_page_title": "Sicherungsoptionen", "backup_setting_subtitle": "Verwaltung der Upload-Einstellungen im Hintergrund und im Vordergrund", "backward": "Rückwärts", + "beta_sync": "Status des Beta Sync", + "beta_sync_subtitle": "Verwalte das neue Synchronisierungssystem", "biometric_auth_enabled": "Biometrische Authentifizierung aktiviert", "biometric_locked_out": "Du bist von der biometrischen Authentifizierung ausgeschlossen", "biometric_no_options": "Keine biometrischen Optionen verfügbar", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "Haptisches Feedback aktivieren", "haptic_feedback_title": "Haptisches Feedback", "has_quota": "Kontingent", + "hash_asset": "Dateihash", + "hashed_assets": "Gehashte Dateien", + "hashing": "Hashen", "header_settings_add_header_tip": "Header hinzufügen", "header_settings_field_validator_msg": "Der Wert darf nicht leer sein", "header_settings_header_name_input": "Header-Name", @@ -1083,6 +1088,7 @@ "host": "Host", "hour": "Stunde", "id": "ID", + "idle": "Untätig", "ignore_icloud_photos": "iCloud Fotos ignorieren", "ignore_icloud_photos_description": "Fotos, die in der iCloud gespeichert sind, werden nicht auf den immich Server hochgeladen", "image": "Bild", @@ -1165,7 +1171,9 @@ "list": "Liste", "loading": "Laden", "loading_search_results_failed": "Laden von Suchergebnissen fehlgeschlagen", + "local": "Lokal", "local_asset_cast_failed": "Eine Datei, die nicht auf den Server hochgeladen wurde, kann nicht gecastet werden", + "local_assets": "Lokale Dateien", "local_network": "Lokales Netzwerk", "local_network_sheet_info": "Die App stellt über diese URL eine Verbindung zum Server her, wenn sie das angegebene WLAN-Netzwerk verwendet", "location_permission": "Standort Genehmigung", @@ -1322,6 +1330,7 @@ "no_results": "Keine Ergebnisse", "no_results_description": "Versuche es mit einem Synonym oder einem allgemeineren Stichwort", "no_shared_albums_message": "Erstelle ein Album, um Fotos und Videos mit Personen in deinem Netzwerk zu teilen", + "no_uploads_in_progress": "Kein Upload in Bearbeitung", "not_in_any_album": "In keinem Album", "not_selected": "Nicht ausgewählt", "note_apply_storage_label_to_previously_uploaded assets": "Hinweis: Um eine Speicherpfadbezeichnung anzuwenden, starte den", @@ -1359,6 +1368,7 @@ "original": "Original", "other": "Sonstiges", "other_devices": "Andere Geräte", + "other_entities": "Andere Entitäten", "other_variables": "Sonstige Variablen", "owned": "Eigenes", "owner": "Besitzer", @@ -1519,6 +1529,8 @@ "refreshing_faces": "Gesichter werden aktualisiert", "refreshing_metadata": "Metadaten werden aktualisiert", "regenerating_thumbnails": "Miniaturansichten werden neu erstellt", + "remote": "Entfernt", + "remote_assets": "Entfernte Dateien", "remove": "Entfernen", "remove_assets_album_confirmation": "Bist du sicher, dass du {count, plural, one {# Datei} other {# Dateien}} aus dem Album entfernen willst?", "remove_assets_shared_link_confirmation": "Bist du sicher, dass du {count, plural, one {# Datei} other {# Dateien}} von diesem geteilten Link entfernen willst?", @@ -1556,11 +1568,15 @@ "reset_password": "Passwort zurücksetzen", "reset_people_visibility": "Sichtbarkeit von Personen zurücksetzen", "reset_pin_code": "PIN Code zurücksetzen", + "reset_sqlite": "SQLite Datenbank zurücksetzen", + "reset_sqlite_confirmation": "Bist du sicher, dass du die SQLite-Datenbank zurücksetzen willst? Du musst dich ab- und wieder anmelden, um die Daten neu zu synchronisieren", + "reset_sqlite_success": "SQLite Datenbank erfolgreich zurückgesetzt", "reset_to_default": "Auf Standard zurücksetzen", "resolve_duplicates": "Duplikate entfernen", "resolved_all_duplicates": "Alle Duplikate aufgelöst", "restore": "Wiederherstellen", "restore_all": "Alle wiederherstellen", + "restore_trash_action_prompt": "{count} aus dem Papierkorb wiederhergestellt", "restore_user": "Nutzer wiederherstellen", "restored_asset": "Datei wiederhergestellt", "resume": "Fortsetzen", @@ -1569,6 +1585,7 @@ "role": "Rolle", "role_editor": "Bearbeiter", "role_viewer": "Betrachter", + "running": "Läuft", "save": "Speichern", "save_to_gallery": "In Galerie speichern", "saved_api_key": "API-Schlüssel wurde gespeichert", @@ -1822,6 +1839,7 @@ "storage_quota": "Speicherplatz-Kontingent", "storage_usage": "{used} von {available} verwendet", "submit": "Bestätigen", + "success": "Erfolgreich", "suggestions": "Vorschläge", "sunrise_on_the_beach": "Sonnenaufgang am Strand", "support": "Unterstützung", @@ -1831,6 +1849,8 @@ "sync": "Synchronisieren", "sync_albums": "Alben synchronisieren", "sync_albums_manual_subtitle": "Synchronisiere alle hochgeladenen Videos und Fotos in die ausgewählten Backup-Alben", + "sync_local": "Lokal synchronisieren", + "sync_remote": "Entfernt synchronisieren", "sync_upload_album_setting_subtitle": "Erstelle deine ausgewählten Alben in Immich und lade die Fotos und Videos dort hoch", "tag": "Tag", "tag_assets": "Dateien taggen", @@ -1841,6 +1861,7 @@ "tag_updated": "Tag aktualisiert: {tag}", "tagged_assets": "{count, plural, one {# Datei} other {# Dateien}} getagged", "tags": "Tags", + "tap_to_run_job": "Tippen um den Job zu starten", "template": "Vorlage", "theme": "Theme", "theme_selection": "Themenauswahl", diff --git a/i18n/en.json b/i18n/en.json index 59cb5be6ae..59c7affa8d 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1003,6 +1003,8 @@ "explorer": "Explorer", "export": "Export", "export_as_json": "Export as JSON", + "export_database": "Export Database", + "export_database_description": "Export the SQLite database", "extension": "Extension", "external": "External", "external_libraries": "External Libraries", @@ -1942,11 +1944,13 @@ "updated_at": "Updated", "updated_password": "Updated password", "upload": "Upload", + "upload_action_prompt": "{count} queued for upload", "upload_concurrency": "Upload concurrency", "upload_details": "Upload Details", "upload_dialog_info": "Do you want to backup the selected Asset(s) to the server?", "upload_dialog_title": "Upload Asset", "upload_errors": "Upload completed with {count, plural, one {# error} other {# errors}}, refresh the page to see new upload assets.", + "upload_finished": "Upload finished", "upload_progress": "Remaining {remaining, number} - Processed {processed, number}/{total, number}", "upload_skipped_duplicates": "Skipped {count, plural, one {# duplicate asset} other {# duplicate assets}}", "upload_status_duplicates": "Duplicates", @@ -1955,6 +1959,7 @@ "upload_success": "Upload success, refresh the page to see new upload assets.", "upload_to_immich": "Upload to Immich ({count})", "uploading": "Uploading", + "uploading_media": "Uploading media", "url": "URL", "usage": "Usage", "use_biometric": "Use biometric", diff --git a/i18n/et.json b/i18n/et.json index 804d0c6c46..1d293a3bdf 100644 --- a/i18n/et.json +++ b/i18n/et.json @@ -573,6 +573,8 @@ "backup_options_page_title": "Varundamise valikud", "backup_setting_subtitle": "Halda taustal ja esiplaanil üleslaadimise seadeid", "backward": "Tagasi", + "beta_sync": "Beeta sünkroonimise staatus", + "beta_sync_subtitle": "Halda uut sünkroonimissüsteemi", "biometric_auth_enabled": "Biomeetriline autentimine lubatud", "biometric_locked_out": "Biomeetriline autentimine on blokeeritud", "biometric_no_options": "Biomeetrilisi valikuid ei ole", @@ -590,7 +592,7 @@ "cache_settings_clear_cache_button": "Tühjenda puhver", "cache_settings_clear_cache_button_title": "Tühjendab rakenduse puhvri. See mõjutab oluliselt rakenduse jõudlust, kuni puhver uuesti täidetakse.", "cache_settings_duplicated_assets_clear_button": "TÜHJENDA", - "cache_settings_duplicated_assets_subtitle": "Fotod ja videod, mis on rakenduse poolt mustfiltreeritud", + "cache_settings_duplicated_assets_subtitle": "Fotod ja videod, mis on rakenduse poolt ignoreeritud", "cache_settings_duplicated_assets_title": "Dubleeritud üksused ({count})", "cache_settings_statistics_album": "Kogu pisipildid", "cache_settings_statistics_full": "Täismõõdus pildid", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "Luba haptiline tagasiside", "haptic_feedback_title": "Haptiline tagasiside", "has_quota": "On kvoot", + "hash_asset": "Arvuta üksuse räsi", + "hashed_assets": "Räsiga üksused", + "hashing": "Räsi arvutamine", "header_settings_add_header_tip": "Lisa päis", "header_settings_field_validator_msg": "Väärtus ei saa olla tühi", "header_settings_header_name_input": "Päise nimi", @@ -1083,6 +1088,7 @@ "host": "Host", "hour": "Tund", "id": "ID", + "idle": "Jõude", "ignore_icloud_photos": "Ignoreeri iCloud fotosid", "ignore_icloud_photos_description": "Fotosid, mis on iCloud'is, ei laadita üles Immich'i serverisse", "image": "Pilt", @@ -1165,7 +1171,9 @@ "list": "Loend", "loading": "Laadimine", "loading_search_results_failed": "Otsitulemuste laadimine ebaõnnestus", + "local": "Lokaalne üksus", "local_asset_cast_failed": "Ei saa edastada üksust, mis pole serverisse üles laaditud", + "local_assets": "Lokaalsed üksused", "local_network": "Kohalik võrk", "local_network_sheet_info": "Rakendus ühendub valitud Wi-Fi võrgus olles serveriga selle URL-i kaudu", "location_permission": "Asukoha luba", @@ -1322,6 +1330,7 @@ "no_results": "Vasteid pole", "no_results_description": "Proovi sünonüümi või üldisemat märksõna", "no_shared_albums_message": "Lisa album, et fotosid ja videosid teistega jagada", + "no_uploads_in_progress": "Üleslaadimisi käimas ei ole", "not_in_any_album": "Pole üheski albumis", "not_selected": "Ei ole valitud", "note_apply_storage_label_to_previously_uploaded assets": "Märkus: Et rakendada talletussilt varem üleslaaditud üksustele, käivita", @@ -1359,6 +1368,7 @@ "original": "originaal", "other": "Muud", "other_devices": "Muud seadmed", + "other_entities": "Muud objektid", "other_variables": "Muud muutujad", "owned": "Minu omad", "owner": "Omanik", @@ -1519,6 +1529,8 @@ "refreshing_faces": "Nägude värskendamine", "refreshing_metadata": "Metaandmete värskendamine", "regenerating_thumbnails": "Pisipiltide uuesti genereerimine", + "remote": "Kaugüksus", + "remote_assets": "Kaugüksused", "remove": "Eemalda", "remove_assets_album_confirmation": "Kas oled kindel, et soovid {count, plural, one {# üksuse} other {# üksust}} albumist eemaldada?", "remove_assets_shared_link_confirmation": "Kas oled kindel, et soovid eemaldada {count, plural, one {# üksuse} other {# üksust}} sellelt jagatud lingilt?", @@ -1556,11 +1568,15 @@ "reset_password": "Lähtesta parool", "reset_people_visibility": "Lähtesta isikute nähtavus", "reset_pin_code": "Lähtesta PIN-kood", + "reset_sqlite": "Lähtesta SQLite andmebaas", + "reset_sqlite_confirmation": "Kas oled kindel, et soovid SQLite andmebaasi lähtestada? Andmete uuesti sünkroonimiseks pead välja ja jälle sisse logima", + "reset_sqlite_success": "SQLite andmebaas edukalt lähtestatud", "reset_to_default": "Lähtesta", "resolve_duplicates": "Lahenda duplikaadid", "resolved_all_duplicates": "Kõik duplikaadid lahendatud", "restore": "Taasta", "restore_all": "Taasta kõik", + "restore_trash_action_prompt": "{count} prügikastust taastatud", "restore_user": "Taasta kasutaja", "restored_asset": "Üksus taastatud", "resume": "Jätka", @@ -1569,6 +1585,7 @@ "role": "Roll", "role_editor": "Muutja", "role_viewer": "Vaataja", + "running": "Käimas", "save": "Salvesta", "save_to_gallery": "Salvesta galeriisse", "saved_api_key": "API võti salvestatud", @@ -1822,6 +1839,7 @@ "storage_quota": "Talletuskvoot", "storage_usage": "{used}/{available} kasutatud", "submit": "Saada", + "success": "Õnnestus", "suggestions": "Soovitused", "sunrise_on_the_beach": "Päikesetõus rannal", "support": "Tugi", @@ -1831,6 +1849,8 @@ "sync": "Sünkrooni", "sync_albums": "Sünkrooni albumid", "sync_albums_manual_subtitle": "Sünkrooni kõik üleslaaditud videod ja fotod valitud varundusalbumitesse", + "sync_local": "Sünkrooni lokaalsed üksused", + "sync_remote": "Sünkrooni kaugüksused", "sync_upload_album_setting_subtitle": "Loo ja laadi oma pildid ja videod üles Immich'isse valitud albumitesse", "tag": "Silt", "tag_assets": "Sildista üksuseid", @@ -1841,6 +1861,7 @@ "tag_updated": "Muudetud silt: {tag}", "tagged_assets": "{count, plural, one {# üksus} other {# üksust}} sildistatud", "tags": "Sildid", + "tap_to_run_job": "Puuduta tööte käivitamiseks", "template": "Mall", "theme": "Teema", "theme_selection": "Teema valik", diff --git a/i18n/fr.json b/i18n/fr.json index cebd4b694f..ff9f783771 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -406,6 +406,7 @@ "album_options": "Options de l'album", "album_remove_user": "Supprimer l'utilisateur ?", "album_remove_user_confirmation": "Êtes-vous sûr de vouloir supprimer {user} ?", + "album_search_not_found": "Aucun album trouvé ne correspond à votre recherche", "album_share_no_users": "Il semble que vous ayez partagé cet album avec tous les utilisateurs ou que vous n'ayez aucun utilisateur avec lequel le partager.", "album_updated": "Album mis à jour", "album_updated_setting_description": "Recevoir une notification par courriel lorsqu'un album partagé a de nouveaux médias", @@ -425,6 +426,7 @@ "albums_default_sort_order": "Ordre de tri par défaut des albums", "albums_default_sort_order_description": "Ordre de tri des médias pour les nouveaux albums créés.", "albums_feature_description": "Bibliothèques de médias pouvant être partagés avec d'autres utilisateurs.", + "albums_on_device_count": "Album sur l'appareil ({count})", "all": "Tout", "all_albums": "Tous les albums", "all_people": "Toutes les personnes", @@ -571,6 +573,8 @@ "backup_options_page_title": "Options de sauvegarde", "backup_setting_subtitle": "Ajuster les paramètres d'envoi au premier et en arrière-plan", "backward": "Arrière", + "beta_sync": "Statut de la synchronisation béta", + "beta_sync_subtitle": "Gérer le nouveau système de synchronisation", "biometric_auth_enabled": "Authentification biométrique activée", "biometric_locked_out": "L'authentification biométrique est verrouillé", "biometric_no_options": "Aucune option biométrique disponible", @@ -605,6 +609,7 @@ "cancel": "Annuler", "cancel_search": "Annuler la recherche", "canceled": "Annulé", + "canceling": "Annulation", "cannot_merge_people": "Impossible de fusionner les personnes", "cannot_undo_this_action": "Vous ne pouvez pas annuler cette action !", "cannot_update_the_description": "Impossible de mettre à jour la description", @@ -765,6 +770,7 @@ "description": "Description", "description_input_hint_text": "Ajouter une description...", "description_input_submit_error": "Erreur de mise à jour de la description, vérifier le journal pour plus de détails", + "deselect_all": "Tout désélectionner", "details": "Détails", "direction": "Ordre", "disabled": "Désactivé", @@ -839,6 +845,7 @@ "empty_trash": "Vider la corbeille", "empty_trash_confirmation": "Êtes-vous sûr de vouloir vider la corbeille ? Cela supprimera définitivement de Immich tous les médias qu'elle contient.\nVous ne pouvez pas annuler cette action !", "enable": "Active", + "enable_backup": "Activer Backup", "enable_biometric_auth_description": "Entrez votre code PIN pour activer l'authentification biométrique", "enabled": "Activé", "end_date": "Date de fin", @@ -1046,6 +1053,9 @@ "haptic_feedback_switch": "Activer le retour haptique", "haptic_feedback_title": "Retour haptique", "has_quota": "Quota", + "hash_asset": "Hasher le média", + "hashed_assets": "Média hashés", + "hashing": "Hash", "header_settings_add_header_tip": "Ajouter un en-tête", "header_settings_field_validator_msg": "Cette valeur ne peut pas être vide", "header_settings_header_name_input": "Nom de l'en-tête", @@ -1160,7 +1170,9 @@ "list": "Liste", "loading": "Chargement", "loading_search_results_failed": "Chargement des résultats échoué", + "local": "Local", "local_asset_cast_failed": "Impossible de caster un média qui n'a pas envoyé vers le serveur", + "local_assets": "Média locaux", "local_network": "Réseau local", "local_network_sheet_info": "L'application va se connecter au serveur via cette URL quand l'appareil est connecté à ce réseau Wi-Fi", "location_permission": "Autorisation de localisation", @@ -1317,6 +1329,7 @@ "no_results": "Aucun résultat", "no_results_description": "Essayez un synonyme ou un mot-clé plus général", "no_shared_albums_message": "Créer un album pour partager vos photos et vidéos avec les personnes de votre réseau", + "no_uploads_in_progress": "Pas d'envoi en cours", "not_in_any_album": "Dans aucun album", "not_selected": "Non sélectionné", "note_apply_storage_label_to_previously_uploaded assets": "Note : Pour appliquer l'étiquette de stockage aux médias précédemment envoyés, exécutez", @@ -1354,6 +1367,7 @@ "original": "original", "other": "Autre", "other_devices": "Autres appareils", + "other_entities": "Autres entités", "other_variables": "Autres variables", "owned": "Possédé", "owner": "Propriétaire", @@ -1485,6 +1499,7 @@ "purchase_server_description_2": "Statut de contributeur", "purchase_server_title": "Serveur", "purchase_settings_server_activated": "La clé du produit pour le Serveur est gérée par l'administrateur", + "queue_status": "File d'attente {count}/{total}", "rating": "Étoile d'évaluation", "rating_clear": "Effacer l'évaluation", "rating_count": "{count, plural, one {# étoile} other {# étoiles}}", @@ -1513,6 +1528,8 @@ "refreshing_faces": "Actualisation des visages", "refreshing_metadata": "Actualisation des métadonnées", "regenerating_thumbnails": "Regénération des miniatures", + "remote": "A distance", + "remote_assets": "Média à distance", "remove": "Supprimer", "remove_assets_album_confirmation": "Êtes-vous sûr de vouloir supprimer {count, plural, one {# média} other {# médias}} de l'album ?", "remove_assets_shared_link_confirmation": "Êtes-vous sûr de vouloir supprimer {count, plural, one {# média} other {# médias}} de ce lien partagé ?", @@ -1550,11 +1567,15 @@ "reset_password": "Réinitialiser le mot de passe", "reset_people_visibility": "Réinitialiser la visibilité des personnes", "reset_pin_code": "Réinitialiser le code PIN", + "reset_sqlite": "Réinitialiser la base de données SQLite", + "reset_sqlite_confirmation": "Êtes vous sur que vous voulez réinitialiser la base de données SQLite ? Vous devrez vous déconnecter and vous reconnecter à nouveau pour re-synchroniser les données", + "reset_sqlite_success": "La base de données SQLite à été réinitialisé avec succès", "reset_to_default": "Rétablir les valeurs par défaut", "resolve_duplicates": "Résoudre les doublons", "resolved_all_duplicates": "Résolution de tous les doublons", "restore": "Restaurer", "restore_all": "Tout restaurer", + "restore_trash_action_prompt": "{count} restauré de la corbeille", "restore_user": "Restaurer l'utilisateur", "restored_asset": "Média restauré", "resume": "Reprendre", @@ -1563,6 +1584,7 @@ "role": "Rôle", "role_editor": "Éditeur", "role_viewer": "Visionneuse", + "running": "En marche", "save": "Sauvegarder", "save_to_gallery": "Enregistrer", "saved_api_key": "Clé API sauvegardée", @@ -1816,6 +1838,7 @@ "storage_quota": "Quota de stockage", "storage_usage": "{used} sur {available} utilisé", "submit": "Soumettre", + "success": "Réussi", "suggestions": "Suggestions", "sunrise_on_the_beach": "Lever de soleil sur la plage", "support": "Soutenir", @@ -1825,6 +1848,8 @@ "sync": "Synchroniser", "sync_albums": "Synchroniser dans des albums", "sync_albums_manual_subtitle": "Synchroniser toutes les vidéos et photos envoyées dans les albums sélectionnés", + "sync_local": "Synchronisation locale", + "sync_remote": "Synchronisation à distance", "sync_upload_album_setting_subtitle": "Créez et envoyez vos photos et vidéos dans les albums sélectionnés sur Immich", "tag": "Étiquette", "tag_assets": "Étiqueter les médias", @@ -1835,6 +1860,7 @@ "tag_updated": "Étiquette mise à jour : {tag}", "tagged_assets": "Étiquette ajoutée à {count, plural, one {# média} other {# médias}}", "tags": "Étiquettes", + "tap_to_run_job": "Appuyez pour démarrer la tâche", "template": "Modèle", "theme": "Thème", "theme_selection": "Sélection du thème", @@ -1915,6 +1941,7 @@ "updated_password": "Mot de passe mis à jour", "upload": "Envoyer", "upload_concurrency": "Envois simultanés", + "upload_details": "Uploader les details", "upload_dialog_info": "Voulez-vous sauvegarder la sélection vers le serveur ?", "upload_dialog_title": "Envoyer le média", "upload_errors": "L'envoi s'est complété avec {count, plural, one {# erreur} other {# erreurs}}. Rafraîchissez la page pour voir les nouveaux médias envoyés.", @@ -1965,6 +1992,7 @@ "view_album": "Afficher l'album", "view_all": "Voir tout", "view_all_users": "Voir tous les utilisateurs", + "view_details": "Voir les détails", "view_in_timeline": "Voir dans la vue chronologique", "view_link": "Voir le lien", "view_links": "Voir les liens", diff --git a/i18n/hu.json b/i18n/hu.json index c00fb37224..df995c0d6c 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -140,7 +140,7 @@ "machine_learning_smart_search_description": "Képek szemantikai keresése CLIP beágyazások segítségével", "machine_learning_smart_search_enabled": "Okos keresés engedélyezése", "machine_learning_smart_search_enabled_description": "Ha ki van kapcsolva, a képek nem lesznek átalakítva okos kereséshez.", - "machine_learning_url_description": "Gépi tanulás szerver URL címe. Ha többi, mint egy URL van megadva, mindegyik szervert egyenként próbálja meg, amíg az egyik sikeresen nem válaszol, sorrendben az elsőtől az utólsóig. A nem válaszoló szervereket átmenetileg figyelmen kívül hagyja, amíg újra online nem lesznek.", + "machine_learning_url_description": "Gépi tanulás szerver URL címe. Ha többi, mint egy URL van megadva, mindegyik szervert egyenként próbálja meg, amíg az egyik sikeresen nem válaszol, sorrendben az elsőtől az utólsóig. A nem elérhető szervereket átmenetileg figyelmen kívül lesznek hagyva, amíg újra online nem lesznek.", "manage_concurrency": "Párhuzamos Feladatok Kezelése", "manage_log_settings": "Naplózási beállítások kezelése", "map_dark_style": "Sötét stílus", @@ -166,6 +166,12 @@ "metadata_settings_description": "Metaadat beállítások kezelése", "migration_job": "Migrálás", "migration_job_description": "Az elemek és arcok bélyegképeinek migrálása a legújabb mappastruktúrába", + "nightly_tasks_cluster_faces_setting_description": "Arcfelismerés futtatása az újonnan érzékelt arcokon", + "nightly_tasks_database_cleanup_setting": "Adatbázis-tisztítási feladatok", + "nightly_tasks_database_cleanup_setting_description": "A régi, lejárt adatok törlése az adatbázisból", + "nightly_tasks_generate_memories_setting": "Emlékek generálása", + "nightly_tasks_generate_memories_setting_description": "Új emlékek létrehozása elemekből", + "nightly_tasks_missing_thumbnails_setting": "Hiányzó indexképek generálása", "no_paths_added": "Nincs megadva elérési útvonal", "no_pattern_added": "Nincs megadva minta (pattern)", "note_apply_storage_label_previous_assets": "Megjegyzés: Ha a korábban feltöltött elemekhez is szeretne Tárhely Címkéket társítani, akkor futtassa ezt", @@ -360,7 +366,7 @@ "advanced_settings_enable_alternate_media_filter_subtitle": "Ezzel a beállítással a szinkronizálás során alternatív kritériumok alapján szűrheted a fájlokat. Csak akkor próbáld ki, ha problémáid vannak azzal, hogy az alkalmazás nem ismeri fel az összes albumot.", "advanced_settings_enable_alternate_media_filter_title": "[KÍSÉRLETI] Alternatív eszköz album szinkronizálási szűrő használata", "advanced_settings_log_level_title": "Naplózás szintje: {level}", - "advanced_settings_prefer_remote_subtitle": "Néhány eszköz fájdalmasan lassan tölti be az eszközön lévő bélyegképeket. Ez a beállítás inkább a távoli képeket tölti be helyettük.", + "advanced_settings_prefer_remote_subtitle": "Néhány eszköz fájdalmasan lassan tölti be az eszközön lévő indexképeket. Ez a beállítás inkább a távoli képeket (a szerverről) tölti be helyettük.", "advanced_settings_prefer_remote_title": "Távoli képek előnyben részesítése", "advanced_settings_proxy_headers_subtitle": "Add meg azokat a proxy fejléceket, amiket az app elküldjön minden hálózati kérésnél", "advanced_settings_proxy_headers_title": "Proxy Fejlécek", @@ -517,7 +523,7 @@ "backup_controller_page_background_is_on": "Automatikus mentés a háttérben be van kapcsolva", "backup_controller_page_background_turn_off": "Háttérszolgáltatás kikapcsolása", "backup_controller_page_background_turn_on": "Háttérszolgáltatás bekapcsolása", - "backup_controller_page_background_wifi": "Csak WiFi-n", + "backup_controller_page_background_wifi": "Csak Wi-Fi-n", "backup_controller_page_backup": "Mentés", "backup_controller_page_backup_selected": "Kiválasztva: ", "backup_controller_page_backup_sub": "Mentett fotók és videók", @@ -551,8 +557,8 @@ "backup_setting_subtitle": "A háttérben és előtérben mentés beállításainak kezelése", "backward": "Visszafele", "biometric_auth_enabled": "Biometrikus azonosítás engedélyezve", - "biometric_locked_out": "A biometrikus azonosításból kizárva", - "biometric_no_options": "A biometrikus azonosítás nem elérhető", + "biometric_locked_out": "Ki vagy zárva a biometrikus azonosításból", + "biometric_no_options": "Nincsen elérhető biometrikus azonosítás", "biometric_not_available": "Biometrikus azonosítás ezen az eszközön nem elérhető", "birthdate_saved": "Születésnap elmentve", "birthdate_set_description": "A születés napját a rendszer arra használja, hogy kiírja, hogy a fénykép készítésekor a személy hány éves volt.", @@ -642,7 +648,7 @@ "confirm_keep_this_delete_others": "Minden más elem a készletben törlésre kerül, kivéve ezt az elemet. Biztosan folytatni szeretnéd?", "confirm_new_pin_code": "Új PIN kód megerősítése", "confirm_password": "Jelszó megerősítése", - "confirm_tag_face": "Szeretnéd ezt az arcot {name}-ként megjelölni?", + "confirm_tag_face": "Szeretnéd ezt az arcot {name}-nak/nek megjelölni?", "confirm_tag_face_unnamed": "Szeretnéd ezt az arcot megjelölni?", "connected_device": "Kapcsolt eszköz", "connected_to": "Kapcsolódva", @@ -770,7 +776,7 @@ "download_started": "Letöltés megkezdve", "download_sucess": "Sikeres letöltés", "download_sucess_android": "Média letöltve a DCIM/Immich mappába", - "download_waiting_to_retry": "Várakozás újrapróbálkozáshoz", + "download_waiting_to_retry": "Várás az újrapróbálkozásra", "downloading": "Letöltés", "downloading_asset_filename": "{filename} elem letöltése", "downloading_media": "Média letöltése", @@ -816,7 +822,7 @@ "enqueued": "Sorba állítva", "enter_wifi_name": "Add meg a Wi-Fi hálózat nevét", "enter_your_pin_code": "Add meg a jelszavad", - "enter_your_pin_code_subtitle": "Add meg a jelszavad a zárolt mappa megnyitásához", + "enter_your_pin_code_subtitle": "Add meg a PIN kódodat a zárolt mappa megnyitásához", "error": "Hiba", "error_change_sort_album": "Album sorbarendezésének megváltoztatása sikertelen", "error_delete_face": "Hiba az arc törlése során", @@ -916,7 +922,7 @@ "unable_to_remove_partner": "Partner eltávolítása sikertelen", "unable_to_remove_reaction": "Reakció eltávolítása sikertelen", "unable_to_reset_password": "Jelszó visszaállítása sikertelen", - "unable_to_reset_pin_code": "Jelszó visszaállítása sikertelen", + "unable_to_reset_pin_code": "PIN kód visszaállítása sikertelen", "unable_to_resolve_duplicate": "Duplikátum feloldása sikertelen", "unable_to_restore_assets": "Elemek visszaállítása sikertelen", "unable_to_restore_trash": "Az összes elem visszaállítása sikertelen", @@ -988,7 +994,7 @@ "filetype": "Fájltípus", "filter": "Szűrő", "filter_people": "Személyek szűrése", - "filter_places": "Helyek szűrése", + "filter_places": "Helyszínek szűrése", "find_them_fast": "Név alapján kereséssel gyorsan megtalálhatóak", "fix_incorrect_match": "Hibás találat javítása", "folder": "Mappa", @@ -1029,9 +1035,9 @@ "hide_person": "Személy elrejtése", "hide_unnamed_people": "Név nélküli személyek elrejtése", "home_page_add_to_album_conflicts": "{added} elem hozzáadva a(z) \"{album}\" albumhoz. {failed} elem már eleve az albumban volt.", - "home_page_add_to_album_err_local": "Helyi elemeket még nem lehet albumba tenni, kihagyás", + "home_page_add_to_album_err_local": "Helyi elemeket még nem lehet albumba tenni, ki lesznek hagyva", "home_page_add_to_album_success": "{added} elem hozzáadva a(z) \"{album}\" albumhoz.", - "home_page_album_err_partner": "Még nem lehet a partner elemeit albumokhoz adni, kihagyás", + "home_page_album_err_partner": "Még nem lehet a partner elemeit albumokhoz adni, ki lesznek hagyva", "home_page_archive_err_local": "Helyi elemek archiválása még nem támogatott, úgyhogy kihagyjuk", "home_page_archive_err_partner": "Partner elemeit nem lehet archiválni, úgyhogy kihagyjuk", "home_page_building_timeline": "Idővonal összeállítása", @@ -1040,7 +1046,7 @@ "home_page_favorite_err_local": "Helyi elemeket még nem lehet a kedvencek közé tenni, úgyhogy ezeket kihagyjuk", "home_page_favorite_err_partner": "Partner elemeit még nem lehet a kedvencek közé tenni, úgyhogy ezeket kihagyjuk", "home_page_first_time_notice": "Ha most használod először az alkalmazást, a fotók és videók megjelenítéséhez az idővonaladon, állítsd be, hogy melyik albumaidról készüljön biztonsági mentés", - "home_page_locked_error_local": "Helyi elemek nem mozgathatóak a zárolt mappába, átugorva", + "home_page_locked_error_local": "A Helyi elemek nem mozgathatóak a zárolt mappába, ki lesznek hagyva", "home_page_locked_error_partner": "Partner elemek nem mozgathatóak a zárolt mappába, átugorva", "home_page_share_err_local": "Helyi elemekről nem lehet megosztott linket készíteni, úgyhogy kihagyjuk", "home_page_upload_err_limit": "Csak 30 elemet tudsz egyszerre feltölteni, úgyhogy kihagyjuk", @@ -1087,10 +1093,10 @@ "invite_people": "Személyek Meghívása", "invite_to_album": "Meghívás az albumba", "ios_debug_info_last_sync_at": "Utoljára szinkronizálva {dateTime}", - "ios_debug_info_no_processes_queued": "Nincs sorba állított hátterfolyamat", + "ios_debug_info_no_processes_queued": "Nincs a sorban háttérfolyamat jelenleg", "ios_debug_info_no_sync_yet": "Még nem futott szinkronizáló háttérfolyamat", "ios_debug_info_processes_queued": "{count, plural, one {{count} háttérfolyamat előkészítve} other {{count} háttérfolyamat előkészítve}}", - "ios_debug_info_processing_ran_at": "Feldolgozás futott {dateTime}", + "ios_debug_info_processing_ran_at": "A feldolgozás ekkor futott: {dateTime}", "items_count": "{count, plural, other {# elem}}", "jobs": "Feladatok", "keep": "Megtart", @@ -1099,7 +1105,7 @@ "kept_this_deleted_others": "Ez az elem és a töröltek meg lettek hagyva {count, plural, one {# asset} other {# assets}}", "keyboard_shortcuts": "Billentyűparancsok", "language": "Nyelv", - "language_no_results_subtitle": "Próbáld a keresésed módosítását", + "language_no_results_subtitle": "Próbáld módosítani a szavaidat a keresésnél", "language_search_hint": "Nyelvek keresése...", "language_setting_description": "Válaszd ki preferált nyelvet", "last_seen": "Utoljára láttuk", @@ -1129,7 +1135,7 @@ "local_network": "Helyi hálózat", "local_network_sheet_info": "Az alkalmazés ezen az URL címen fogja elérni a szervert, ha a megadott WiFi hálózathoz van csatlankozva", "location_permission": "Helymeghatározási engedély", - "location_permission_content": "Hálózatok automatikus váltásához az Immich-nek szüksége van a pontos helymeghatározásra, hogy az alkalmazás le tudja kérni a Wi-Fi hálózat nevét", + "location_permission_content": "A Hálózatok automatikus váltásához az Immich-nek szüksége van a pontos helymeghatározásra, hogy az alkalmazás le tudja kérni a Wi-Fi hálózat nevét", "location_picker_choose_on_map": "Válassz a térképen", "location_picker_latitude_error": "Érvényes szélességi kört írj be", "location_picker_latitude_hint": "Ide írd a szélességi kört", @@ -1139,7 +1145,7 @@ "locked_folder": "Zárolt mappa", "log_out": "Kijelentkezés", "log_out_all_devices": "Kijelentkezés Minden Eszközön", - "logged_in_as": "{user}-ként belépve", + "logged_in_as": "Belépve: {user} néven", "logged_out_all_devices": "Minden eszköz kijelentkeztetve", "logged_out_device": "Eszköz kijelentkeztetve", "login": "Bejelentkezés", @@ -1234,9 +1240,9 @@ "monthly_title_text_date_format": "y MMMM", "more": "Továbbiak", "move": "Áthelyezés", - "move_off_locked_folder": "Zárolt mappából kivonás", + "move_off_locked_folder": "Átmozgatás a zárolt mappából", "move_to_locked_folder": "Áthelyezés a zárolt mappába", - "move_to_locked_folder_confirmation": "Ezek a képek és videók az összes albumból kikerülnek, és csak a zárolt mappából lesznek elérhetőek", + "move_to_locked_folder_confirmation": "Ezek a képek és videók az összes albumból kikerülnek, és csak a zárolt mappában lesznek elérhetőek", "moved_to_archive": "{count, plural, one {# Elem} other {# Elemek}} archiválva", "moved_to_library": "{count, plural, one {# Elem} other {# Elemek}} másik könyvtárba költöztetve", "moved_to_trash": "Áthelyezve a lomtárba", @@ -1254,7 +1260,7 @@ "new_password": "Új jelszó", "new_person": "Új személy", "new_pin_code": "Új PIN kód", - "new_pin_code_subtitle": "Ez az első alkalom hogy megnyitod a zárolt mappát. Hozz létre egy jelszót az oldal biztosítására", + "new_pin_code_subtitle": "Ez az első alkalom hogy megnyitod a zárolt mappát. Hozz létre egy jelszót a mappa biztonságos eléréséhez", "new_user_created": "Új felhasználó létrehozva", "new_version_available": "ÚJ VERZIÓ ÉRHETŐ EL", "newest_first": "Legújabb először", @@ -1283,7 +1289,7 @@ "not_selected": "Nincs kiválasztva", "note_apply_storage_label_to_previously_uploaded assets": "Megjegyzés: a korábban feltöltött elemek Tárhely Címkézéséhez futtasd a(z)", "notes": "Megjegyzések", - "nothing_here_yet": "Itt még nincs semmi", + "nothing_here_yet": "Még semmi sincs itt", "notification_permission_dialog_content": "Az értesítések bekapcsolásához a Beállítások menüben válaszd ki az Engedélyezés-t.", "notification_permission_list_tile_content": "Értesítések engedélyezése.", "notification_permission_list_tile_enable_button": "Értesítések Bekapcsolása", @@ -1293,7 +1299,7 @@ "notifications_setting_description": "Értesítések kezelése", "oauth": "OAuth", "official_immich_resources": "Hivatalos Immich Források", - "offline": "Offline", + "offline": "Nem elérhető (offline)", "ok": "Rendben", "oldest_first": "Legrégebbi először", "on_this_device": "Ezen az eszközön", @@ -1303,7 +1309,7 @@ "onboarding_theme_description": "Válassz egy színtémát. Ezt bármikor megváltoztathatod a beállításokban.", "onboarding_user_welcome_description": "Kezdjünk bele!", "onboarding_welcome_user": "Üdvözöllek {user}", - "online": "Online", + "online": "Online (elérhető)", "only_favorites": "Csak kedvencek", "open": "Nyitva", "open_in_map_view": "Megnyitás térkép nézetben", @@ -1754,7 +1760,7 @@ "stack_select_one_photo": "Válassz egy fő képet a csoportból", "stack_selected_photos": "Kiválasztott fényképek csoportosítása", "stacked_assets_count": "{count, plural, other {# elem}} csoportosítva", - "stacktrace": "Hibaleírás", + "stacktrace": "Hiba leírása", "start": "Elindít", "start_date": "Kezdő dátum", "state": "Megye/Állam", @@ -1882,7 +1888,7 @@ "user_liked": "{user} felhasználónak {type, select, photo {ez a fénykép} video {ez a videó} asset {ez az elem} other {ez}} tetszik", "user_pin_code_settings": "PIN kód", "user_pin_code_settings_description": "PIN kód kezelése", - "user_privacy": "Felhasználói biztonság", + "user_privacy": "Felhasználói adatvédelem", "user_purchase_settings": "Megvásárlás", "user_purchase_settings_description": "Vásárlás kezelése", "user_role_set": "{user} felhasználónak {role} jogkör biztosítása", diff --git a/i18n/id.json b/i18n/id.json index ff5e2524e2..92cb07ff30 100644 --- a/i18n/id.json +++ b/i18n/id.json @@ -169,15 +169,23 @@ "nightly_tasks_cluster_faces_setting_description": "Mulai pengenalan wajah pada semua wajah yang baru saja terdeteksi", "nightly_tasks_cluster_new_faces_setting": "Kelompokkan semua wajah baru", "nightly_tasks_database_cleanup_setting": "Tugas pembersihan basis data", + "nightly_tasks_database_cleanup_setting_description": "Membersihkan data lama, kadaluarsa dari database", "nightly_tasks_generate_memories_setting": "Buat kenang-kenangan", "nightly_tasks_generate_memories_setting_description": "Buat kenang-kenangan baru dari berbagai aset", + "nightly_tasks_missing_thumbnails_setting": "Membuat thumbnail yang hilang", + "nightly_tasks_missing_thumbnails_setting_description": "Mengantrikan aset tanpa thumbnail untuk pembuatan thumbnail", + "nightly_tasks_settings": "Pengaturan Tugas Malam", + "nightly_tasks_settings_description": "Atur tugas malam", "nightly_tasks_start_time_setting": "Waktu mulai", + "nightly_tasks_start_time_setting_description": "Waktu saat server mulai menjalankan tugas malam", + "nightly_tasks_sync_quota_usage_setting": "Sinkronisasi penggunaan kuota", + "nightly_tasks_sync_quota_usage_setting_description": "Pembaruan kuota penyimpanan pengguna, berdasarkan penggunaan sekarang", "no_paths_added": "Tidak ada jalur yang ditambahkan", "no_pattern_added": "Tidak ada pola yang ditambahkan", "note_apply_storage_label_previous_assets": "Catatan: Untuk menerapkan Label Penyimpanan untuk aset yang telah diunggah sebelumnya, jalankan", "note_cannot_be_changed_later": "CATATAN: Ini tidak akan dapat diubah lagi!", "notification_email_from_address": "Dari alamat", - "notification_email_from_address_description": "Alamat surel pengirim, misalnya: \"Server Foto Immich \". Pastikan untuk menggunakan alamat yang diizinkan untuk mengirim email", + "notification_email_from_address_description": "Alamat surel pengirim, misalnya: \"Server Foto Immich \". Pastikan untuk menggunakan alamat yang diizinkan untuk mengirim email.", "notification_email_host_description": "Hos server surel (mis. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Abaikan eror sertifikat", "notification_email_ignore_certificate_errors_description": "Abaikan eror validasi sertifikat TLS (tidak disarankan)", diff --git a/i18n/it.json b/i18n/it.json index 5e12bdfc97..0aec538a85 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -406,6 +406,7 @@ "album_options": "Impostazioni Album", "album_remove_user": "Rimuovi l'utente?", "album_remove_user_confirmation": "Sicuro di voler rimuovere l'utente {user}?", + "album_search_not_found": "Nessun album trovato corrispondente alla tua ricerca", "album_share_no_users": "Sembra che tu abbia condiviso questo album con tutti gli utenti oppure non hai nessun utente con cui condividere.", "album_updated": "Album aggiornato", "album_updated_setting_description": "Ricevi una notifica email quando un album condiviso ha nuovi media", @@ -425,6 +426,7 @@ "albums_default_sort_order": "Ordinamento predefinito degli album", "albums_default_sort_order_description": "Ordine iniziale degli elementi alla creazione di nuovi album.", "albums_feature_description": "Raggruppamento di elementi che possono essere condivisi con altri utenti.", + "albums_on_device_count": "Album sul dispositivo ({count})", "all": "Tutti", "all_albums": "Tutti gli album", "all_people": "Tutte le persone", @@ -605,6 +607,7 @@ "cancel": "Annulla", "cancel_search": "Annulla ricerca", "canceled": "Annullato", + "canceling": "Annullamento", "cannot_merge_people": "Impossibile unire le persone", "cannot_undo_this_action": "Non puoi annullare questa azione!", "cannot_update_the_description": "Impossibile aggiornare la descrizione", @@ -751,6 +754,7 @@ "delete_key": "Elimina chiave", "delete_library": "Elimina libreria", "delete_link": "Elimina link", + "delete_local_action_prompt": "{count} elementi rimossi in locale", "delete_local_dialog_ok_backed_up_only": "Elimina solo con backup", "delete_local_dialog_ok_force": "Elimina comunque", "delete_others": "Elimina gli altri", @@ -764,6 +768,7 @@ "description": "Descrizione", "description_input_hint_text": "Aggiungi descrizione...", "description_input_submit_error": "Errore modificare descrizione, controlli I log per maggiori dettagli", + "deselect_all": "Deseleziona Tutto", "details": "Dettagli", "direction": "Direzione", "disabled": "Disabilitato", @@ -781,6 +786,7 @@ "documentation": "Documentazione", "done": "Fatto", "download": "Scarica", + "download_action_prompt": "Scaricando {count} elementi", "download_canceled": "Download annullato", "download_complete": "Download completato", "download_enqueue": "Download in coda", @@ -837,6 +843,7 @@ "empty_trash": "Svuota cestino", "empty_trash_confirmation": "Sei sicuro di volere svuotare il cestino? Questo rimuoverà tutte le risorse nel cestino in modo permanente da Immich.\nNon puoi annullare questa azione!", "enable": "Abilita", + "enable_backup": "Abilita Backup", "enable_biometric_auth_description": "Inserire il codice PIN per abilitare l'autenticazione biometrica", "enabled": "Abilitato", "end_date": "Data Fine", @@ -1483,6 +1490,7 @@ "purchase_server_description_2": "Stato di Contributore", "purchase_server_title": "Server", "purchase_settings_server_activated": "La chiave del prodotto del server è gestita dall'amministratore", + "queue_status": "Messi in coda {count}/{total}", "rating": "Valutazione a stelle", "rating_clear": "Crea valutazione", "rating_count": "{count, plural, one {# stella} other {# stelle}}", @@ -1518,6 +1526,7 @@ "remove_custom_date_range": "Rimuovi intervallo data personalizzato", "remove_deleted_assets": "Rimuovi file offline", "remove_from_album": "Rimuovere dall'album", + "remove_from_album_action_prompt": "{count} elementi rimossi dall'album", "remove_from_favorites": "Rimuovi dai preferiti", "remove_from_lock_folder_action_prompt": "{count} elementi rimossi dalla cartella sicura", "remove_from_locked_folder": "Rimuovi dalla cartella privata", @@ -1691,6 +1700,7 @@ "settings_saved": "Impostazioni salvate", "setup_pin_code": "Configura un codice PIN", "share": "Condivisione", + "share_action_prompt": "Condivisi {count} elementi", "share_add_photos": "Aggiungi foto", "share_assets_selected": "{count} selezionati", "share_dialog_preparing": "Preparo…", @@ -1792,6 +1802,7 @@ "sort_title": "Titolo", "source": "Fonte", "stack": "Raggruppa", + "stack_action_prompt": "{count} elementi raggruppati", "stack_duplicates": "Raggruppa i duplicati", "stack_select_one_photo": "Seleziona una foto principale per il gruppo", "stack_selected_photos": "Impila foto selezionate", @@ -1880,6 +1891,7 @@ "unable_to_change_pin_code": "Impossibile cambiare il codice PIN", "unable_to_setup_pin_code": "Impossibile configurare il codice PIN", "unarchive": "Annulla l'archiviazione", + "unarchive_action_prompt": "{count} elementi rimossi dall'Archivio", "unarchived_count": "{count, plural, other {Non archiviati #}}", "undo": "Annulla", "unfavorite": "Rimuovi preferito", diff --git a/i18n/lv.json b/i18n/lv.json index 5af3e0511c..2e6b702e22 100644 --- a/i18n/lv.json +++ b/i18n/lv.json @@ -707,6 +707,9 @@ "next_memory": "Nākamā atmiņa", "no": "Nē", "no_albums_message": "Izveido albumu, lai organizētu savas fotogrāfijas un video", + "no_albums_with_name_yet": "Izskatās, ka tev vēl nav albumu ar šādu nosaukumu.", + "no_albums_yet": "Izskatās, ka tev vēl nav neviena albuma.", + "no_archived_assets_message": "Arhivē fotoattēlus un videoklipus, lai paslēptu tos no Fotoattēli skata", "no_assets_message": "NOKLIKŠĶINIET, LAI AUGŠUPIELĀDĒTU SAVU PIRMO FOTOATTĒLU", "no_assets_to_show": "Nav uzrādāmo aktīvu", "no_duplicates_found": "Dublikāti netika atrasti.", @@ -730,6 +733,10 @@ "official_immich_resources": "Oficiālie Immich resursi", "offline": "Bezsaistē", "ok": "Labi", + "onboarding": "Uzņemšana", + "onboarding_locale_description": "Izvēlies vēlamo valodu. To vēlāk var mainīt iestatījumos.", + "onboarding_theme_description": "Izvēlies savas instances krāsu motīvu. To vēlāk var mainīt iestatījumos.", + "onboarding_user_welcome_description": "Sāksim darbu!", "online": "Tiešsaistē", "only_favorites": "Tikai izlase", "open_in_map_view": "Atvērt kartes skatā", @@ -737,13 +744,16 @@ "open_the_search_filters": "Atvērt meklēšanas filtrus", "options": "Iestatījumi", "or": "vai", + "organize_your_library": "Bibliotēkas organizēšana", "original": "oriģināls", "other": "Citi", "other_devices": "Citas ierīces", "other_variables": "Citi mainīgie", "owned": "Īpašumā", "owner": "Īpašnieks", + "partner": "Partneris", "partner_can_access": "{partner} var piekļūt", + "partner_can_access_location": "Fotogrāfiju uzņemšanas vieta", "partner_list_user_photos": "{user} fotoattēli", "partner_list_view_all": "Apskatīt visu", "partner_page_empty_message": "Jūsu fotogrāfijas pagaidām nav kopīgotas ar nevienu partneri.", @@ -757,6 +767,7 @@ "password_does_not_match": "Parole nesakrīt", "path": "Ceļš", "pause": "Pauzēt", + "pause_memories": "Pauzēt atmiņas", "paused": "Nopauzēts", "people": "Cilvēki", "permission_onboarding_back": "Atpakaļ", diff --git a/i18n/nb_NO.json b/i18n/nb_NO.json index 89b5e298bc..2abc08e7f2 100644 --- a/i18n/nb_NO.json +++ b/i18n/nb_NO.json @@ -573,6 +573,8 @@ "backup_options_page_title": "Backupinnstillinger", "backup_setting_subtitle": "Administrer opplastingsinnstillinger for bakgrunn og forgrunn", "backward": "Bakover", + "beta_sync": "Beta synkroniseringsstatus", + "beta_sync_subtitle": "Håndter det nye synkroniseringssystemet", "biometric_auth_enabled": "Biometrisk autentisering aktivert", "biometric_locked_out": "Du er låst ute av biometrisk verifisering", "biometric_no_options": "Ingen biometriske valg tilgjengelige", @@ -590,7 +592,7 @@ "cache_settings_clear_cache_button": "Tøm buffer", "cache_settings_clear_cache_button_title": "Tømmer app-ens buffer. Dette vil ha betydelig innvirkning på appens ytelse inntil bufferen er gjenoppbygd.", "cache_settings_duplicated_assets_clear_button": "TØM", - "cache_settings_duplicated_assets_subtitle": "Bilder og videoer som er svartelistet av app'en", + "cache_settings_duplicated_assets_subtitle": "Bilder og videoer som er ignorert av app'en", "cache_settings_duplicated_assets_title": "Dupliserte objekter ({count})", "cache_settings_statistics_album": "Bibliotekminiatyrbilder", "cache_settings_statistics_full": "Originalbilder", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "Aktivert haptisk tilbakemelding", "haptic_feedback_title": "Haptisk tilbakemelding", "has_quota": "Kvote", + "hash_asset": "Hash objekter", + "hashed_assets": "Hashede objekter", + "hashing": "Hasher", "header_settings_add_header_tip": "Legg til header", "header_settings_field_validator_msg": "Verdi kan ikke være null", "header_settings_header_name_input": "Header navn", @@ -1083,6 +1088,7 @@ "host": "Vert", "hour": "Time", "id": "ID", + "idle": "Uvirksom", "ignore_icloud_photos": "Ignorer iCloud bilder", "ignore_icloud_photos_description": "Bilder som er lagret på iCloud vil ikke lastes opp til Immich", "image": "Bilde", @@ -1165,7 +1171,9 @@ "list": "Liste", "loading": "Laster", "loading_search_results_failed": "Klarte ikke å laste inn søkeresultater", + "local": "Lokal", "local_asset_cast_failed": "Kan ikke caste et bilde som ikke er lastet opp til serveren", + "local_assets": "Lokale objekter", "local_network": "Lokalt nettverk", "local_network_sheet_info": "Appen vil koble til serveren via denne URL-en når du bruker det angitte Wi-Fi-nettverket", "location_permission": "Stedstillatelse", @@ -1322,6 +1330,7 @@ "no_results": "Ingen resultater", "no_results_description": "Prøv et synonym eller mer generelt søkeord", "no_shared_albums_message": "Opprett et album for å dele bilder og videoer med personer i nettverket ditt", + "no_uploads_in_progress": "Ingen opplasting pågår", "not_in_any_album": "Ikke i noe album", "not_selected": "Ikke valgt", "note_apply_storage_label_to_previously_uploaded assets": "Merk: For å bruke lagringsetiketten på tidligere opplastede filer, kjør", @@ -1359,6 +1368,7 @@ "original": "original", "other": "Annet", "other_devices": "Andre enheter", + "other_entities": "Andre objekter", "other_variables": "Andre variabler", "owned": "Dine", "owner": "Eier", @@ -1519,6 +1529,8 @@ "refreshing_faces": "Oppdaterer ansikter", "refreshing_metadata": "Oppdaterer matadata", "regenerating_thumbnails": "Regenererer miniatyrbilder", + "remote": "Eksternt", + "remote_assets": "Eksterne objekter", "remove": "Fjern", "remove_assets_album_confirmation": "Er du sikker på at du fil slette {count, plural, one {# asset} other {# assets}} fra albumet?", "remove_assets_shared_link_confirmation": "Er du sikker på at du vil slette {count, plural, one {# asset} other {# assets}} fra den delte lenken?", @@ -1556,11 +1568,15 @@ "reset_password": "Tilbakestill passord", "reset_people_visibility": "Tilbakestill personsynlighet", "reset_pin_code": "Resett PINkode", + "reset_sqlite": "Reset SQLite Databasen", + "reset_sqlite_confirmation": "Er du sikker på at du vil resette SQLite databasen? Du blir nødt til å logge ut og inn igjen for å resynkronisere data", + "reset_sqlite_success": "Vellykket resetting av SQLite databasen", "reset_to_default": "Tilbakestill til standard", "resolve_duplicates": "Løs duplikater", "resolved_all_duplicates": "Løste alle duplikater", "restore": "Gjenopprett", "restore_all": "Gjenopprett alle", + "restore_trash_action_prompt": "{count} gjenopprettet fra søppelbøtten", "restore_user": "Gjenopprett bruker", "restored_asset": "Gjenopprettet ressurs", "resume": "Fortsett", @@ -1569,6 +1585,7 @@ "role": "Rolle", "role_editor": "Redigerer", "role_viewer": "Visning", + "running": "Kjører", "save": "Lagre", "save_to_gallery": "Lagre til galleriet", "saved_api_key": "Lagret API-nøkkel", @@ -1822,6 +1839,7 @@ "storage_quota": "Lagringsplass", "storage_usage": "{used} av {available} brukt", "submit": "Send inn", + "success": "Vellykket", "suggestions": "Forslag", "sunrise_on_the_beach": "Soloppgang på stranden", "support": "Støtte", @@ -1831,6 +1849,8 @@ "sync": "Synkroniser", "sync_albums": "Synkroniser albumer", "sync_albums_manual_subtitle": "Synkroniser alle opplastede videoer og bilder til det valgte backupalbumet", + "sync_local": "Synkroniser lokalt", + "sync_remote": "Synkroniser eksternt", "sync_upload_album_setting_subtitle": "Opprett og last opp dine bilder og videoer til det valgte albumet på Immich", "tag": "Tagg", "tag_assets": "Merk ressurser", @@ -1841,6 +1861,7 @@ "tag_updated": "Oppdater merke: {tag}", "tagged_assets": "Merket {count, plural, one {# asset} other {# assets}}", "tags": "Merker", + "tap_to_run_job": "Trykk for å kjøre jobben", "template": "Mal", "theme": "Tema", "theme_selection": "Temavalg", diff --git a/i18n/nl.json b/i18n/nl.json index 2cedd63b15..86cb0fba6e 100644 --- a/i18n/nl.json +++ b/i18n/nl.json @@ -426,6 +426,7 @@ "albums_default_sort_order": "Standaard sorteervolgorde album", "albums_default_sort_order_description": "Initiële sorteervolgorde bij het maken van nieuwe albums.", "albums_feature_description": "Collectie van assets die je kan delen met andere gebruikers.", + "albums_on_device_count": "Albums op apparaat ({count})", "all": "Alle", "all_albums": "Alle albums", "all_people": "Alle mensen", @@ -572,6 +573,8 @@ "backup_options_page_title": "Back-up instellingen", "backup_setting_subtitle": "Beheer achtergrond en voorgrond uploadinstellingen", "backward": "Achteruit", + "beta_sync": "Beta Sync Status", + "beta_sync_subtitle": "Beheer het nieuwe synchronisatiesysteem", "biometric_auth_enabled": "Biometrische authenticatie ingeschakeld", "biometric_locked_out": "Biometrische authenticatie is vergrendeld", "biometric_no_options": "Geen biometrische opties beschikbaar", @@ -589,7 +592,7 @@ "cache_settings_clear_cache_button": "Cache wissen", "cache_settings_clear_cache_button_title": "Wist de cache van de app. Dit zal de presentaties van de app aanzienlijk beïnvloeden totdat de cache opnieuw is opgebouwd.", "cache_settings_duplicated_assets_clear_button": "MAAK VRIJ", - "cache_settings_duplicated_assets_subtitle": "Foto's en video's op de zwarte lijst van de app", + "cache_settings_duplicated_assets_subtitle": "Foto’s en video's die de app negeert", "cache_settings_duplicated_assets_title": "Gedupliceerde assets ({count})", "cache_settings_statistics_album": "Bibliotheekthumbnails", "cache_settings_statistics_full": "Volledige afbeeldingen", @@ -1050,6 +1053,9 @@ "haptic_feedback_switch": "Aanraaktrillingen inschakelen", "haptic_feedback_title": "Aanraaktrillingen", "has_quota": "Heeft limiet", + "hash_asset": "Hash asset", + "hashed_assets": "Gehashte assets", + "hashing": "Hashen", "header_settings_add_header_tip": "Header toevoegen", "header_settings_field_validator_msg": "Waarde kan niet leeg zijn", "header_settings_header_name_input": "Header naam", @@ -1082,6 +1088,7 @@ "host": "Host", "hour": "Uur", "id": "ID", + "idle": "Idle", "ignore_icloud_photos": "Negeer iCloud foto's", "ignore_icloud_photos_description": "Foto's die op iCloud zijn opgeslagen, worden niet geüpload naar de Immich server", "image": "Afbeelding", @@ -1164,7 +1171,9 @@ "list": "Lijst", "loading": "Laden", "loading_search_results_failed": "Laden van zoekresultaten mislukt", + "local": "Lokaal", "local_asset_cast_failed": "Kan geen asset casten die nog niet geüpload is naar de server", + "local_assets": "Lokale Assets", "local_network": "Lokaal netwerk", "local_network_sheet_info": "De app maakt verbinding met de server via deze URL wanneer het opgegeven WiFi-netwerk wordt gebruikt", "location_permission": "Locatietoestemming", @@ -1321,6 +1330,7 @@ "no_results": "Geen resultaten", "no_results_description": "Probeer een synoniem of een algemener zoekwoord", "no_shared_albums_message": "Maak een album om foto's en video's te delen met mensen in je netwerk", + "no_uploads_in_progress": "Geen uploads bezig", "not_in_any_album": "Niet in een album", "not_selected": "Niet geselecteerd", "note_apply_storage_label_to_previously_uploaded assets": "Opmerking: om het opslaglabel toe te passen op eerder geüploade assets, voer de volgende taak uit", @@ -1358,6 +1368,7 @@ "original": "origineel", "other": "Overige", "other_devices": "Andere apparaten", + "other_entities": "Andere entities", "other_variables": "Andere variabelen", "owned": "Eigenaar", "owner": "Eigenaar", @@ -1518,6 +1529,8 @@ "refreshing_faces": "Gezichten aan het vernieuwen", "refreshing_metadata": "Metadata aan het vernieuwen", "regenerating_thumbnails": "Thumbnails opnieuw aan het genereren", + "remote": "Remote", + "remote_assets": "Remote Assets", "remove": "Verwijderen", "remove_assets_album_confirmation": "Weet je zeker dat je {count, plural, one {# asset} other {# assets}} uit het album wilt verwijderen?", "remove_assets_shared_link_confirmation": "Weet je zeker dat je {count, plural, one {# asset} other {# assets}} uit deze gedeelde link wilt verwijderen?", @@ -1555,11 +1568,15 @@ "reset_password": "Wachtwoord resetten", "reset_people_visibility": "Zichtbaarheid mensen resetten", "reset_pin_code": "Reset PIN code", + "reset_sqlite": "Reset SQLite Database", + "reset_sqlite_confirmation": "Ben je zeker dat je de SQLite database wilt resetten? Je zal moetenn uitloggen om de data opnieuw te synchroniseren.", + "reset_sqlite_success": "De SQLite database is succesvol gereset", "reset_to_default": "Resetten naar standaard", "resolve_duplicates": "Duplicaten oplossen", "resolved_all_duplicates": "Alle duplicaten opgelost", "restore": "Herstellen", "restore_all": "Herstel alle", + "restore_trash_action_prompt": "{count} teruggezet uit prullenbak", "restore_user": "Gebruiker herstellen", "restored_asset": "Asset hersteld", "resume": "Hervatten", @@ -1568,6 +1585,7 @@ "role": "Rol", "role_editor": "Bewerker", "role_viewer": "Bekijker", + "running": "Actief", "save": "Opslaan", "save_to_gallery": "Opslaan in galerij", "saved_api_key": "API-sleutel opgeslagen", @@ -1821,6 +1839,7 @@ "storage_quota": "Opslaglimiet", "storage_usage": "{used} van {available} gebruikt", "submit": "Verzenden", + "success": "Succes", "suggestions": "Suggesties", "sunrise_on_the_beach": "Zonsopkomst op het strand", "support": "Ondersteuning", @@ -1830,6 +1849,8 @@ "sync": "Sync", "sync_albums": "Albums synchroniseren", "sync_albums_manual_subtitle": "Synchroniseer alle geüploade video’s en foto’s naar de geselecteerde back-up albums", + "sync_local": "Lokaal synchroniseren", + "sync_remote": "Op afstand synchroniseren", "sync_upload_album_setting_subtitle": "Maak en upload je foto's en video's naar de geselecteerde albums op Immich", "tag": "Tag", "tag_assets": "Assets taggen", @@ -1840,6 +1861,7 @@ "tag_updated": "Tag bijgewerkt: {tag}", "tagged_assets": "{count, plural, one {# asset} other {# assets}} getagd", "tags": "Tags", + "tap_to_run_job": "Klik om job te starten", "template": "Template", "theme": "Thema", "theme_selection": "Thema selectie", diff --git a/i18n/ru.json b/i18n/ru.json index 84db991027..c0a01b0be2 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -228,7 +228,7 @@ "password_settings_description": "Управление настройками входа по паролю", "paths_validated_successfully": "Все пути успешно прошли проверку", "person_cleanup_job": "Очистка персоны", - "quota_size_gib": "Размер квоты (ГБ)", + "quota_size_gib": "Размер квоты (GiB)", "refreshing_all_libraries": "Обновление всех библиотек", "registration": "Регистрация администратора", "registration_description": "Первый зарегистрированный пользователь будет назначен администратором. В дальнейшем этой учетной записи будет доступно создание дополнительных пользователей и управление сервером.", @@ -241,7 +241,7 @@ "server_external_domain_settings": "Внешний домен", "server_external_domain_settings_description": "Домен для публичных ссылок, включая http(s)://", "server_public_users": "Публичные пользователи", - "server_public_users_description": "Отображать всех пользователей (имена и email) для добавления в общие альбомы. Когда отключено, список пользователей будет доступен только администраторам.", + "server_public_users_description": "Выводить список пользователей (имена и email) в общих альбомах. Когда отключено, список доступен только администраторам, пользователи смогут делиться только ссылкой.", "server_settings": "Настройки сервера", "server_settings_description": "Управление настройками сервера", "server_welcome_message": "Приветственное сообщение", @@ -407,7 +407,7 @@ "album_remove_user": "Удалить пользователя?", "album_remove_user_confirmation": "Вы уверены, что хотите удалить пользователя {user}?", "album_search_not_found": "Не найдено альбомов по вашему запросу", - "album_share_no_users": "Похоже, вы поделились этим альбомом со всеми пользователями или у вас нет пользователей, с которыми можно поделиться.", + "album_share_no_users": "Нет доступных пользователей, с которыми можно поделиться альбомом.", "album_updated": "Альбом обновлён", "album_updated_setting_description": "Получать уведомление по электронной почте при добавлении новых ресурсов в общий альбом", "album_user_left": "Вы покинули {album}", @@ -433,8 +433,8 @@ "all_videos": "Все видео", "allow_dark_mode": "Разрешить темный режим", "allow_edits": "Разрешить редактирование", - "allow_public_user_to_download": "Разрешить скачивание публичным пользователям", - "allow_public_user_to_upload": "Разрешить публичным пользователям загружать файлы", + "allow_public_user_to_download": "Разрешить скачивание", + "allow_public_user_to_upload": "Разрешить добавление файлов", "alt_text_qr_code": "QR-код", "anti_clockwise": "Против часовой", "api_key": "API ключ", @@ -573,6 +573,8 @@ "backup_options_page_title": "Резервное копирование", "backup_setting_subtitle": "Настройка активного и фонового резервного копирования", "backward": "Назад", + "beta_sync": "Статус бета-синхронизации", + "beta_sync_subtitle": "Управление новой системой синхронизации", "biometric_auth_enabled": "Биометрическая аутентификация включена", "biometric_locked_out": "Вам закрыт доступ к биометрической аутентификации", "biometric_no_options": "Биометрическая аутентификация недоступна", @@ -590,7 +592,7 @@ "cache_settings_clear_cache_button": "Очистить кэш", "cache_settings_clear_cache_button_title": "Очищает кэш приложения. Это негативно повлияет на производительность, пока кэш не будет создан заново.", "cache_settings_duplicated_assets_clear_button": "ОЧИСТИТЬ", - "cache_settings_duplicated_assets_subtitle": "Фото и видео, занесенные приложением в черный список", + "cache_settings_duplicated_assets_subtitle": "Фото и видео, пропускаемые приложением", "cache_settings_duplicated_assets_title": "Дублирующиеся объекты ({count})", "cache_settings_statistics_album": "Миниатюры библиотеки", "cache_settings_statistics_full": "Полные изображения", @@ -616,7 +618,7 @@ "change_date": "Изменить дату", "change_description": "Изменить описание", "change_display_order": "Изменить порядок отображения", - "change_expiration_time": "Изменить время окончания", + "change_expiration_time": "Изменить срок действия", "change_location": "Изменить местоположение", "change_name": "Изменить имя", "change_name_successfully": "Имя успешно изменено", @@ -692,7 +694,7 @@ "copy_link": "Копировать ссылку", "copy_link_to_clipboard": "Скопировать ссылку в буфер обмена", "copy_password": "Скопировать пароль", - "copy_to_clipboard": "Скопировать в буфер обмена", + "copy_to_clipboard": "Скопировать настройки в буфер обмена", "country": "Страна", "cover": "Обложка", "covers": "Обложки", @@ -830,7 +832,7 @@ "edit_people": "Редактировать людей", "edit_tag": "Изменить тег", "edit_title": "Редактировать Заголовок", - "edit_user": "Редактирование пользователя", + "edit_user": "Изменить пользователя", "edited": "Отредактировано", "editor": "Редактор", "editor_close_without_save_prompt": "Изменения не будут сохранены", @@ -994,7 +996,7 @@ "experimental_settings_subtitle": "Используйте на свой страх и риск!", "experimental_settings_title": "Экспериментальные функции", "expire_after": "Истекает через", - "expired": "Срок действия истек", + "expired": "Срок действия истёк", "expires_date": "Срок действия до {date}", "explore": "Поиск", "explorer": "Проводник", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "Включить тактильную отдачу", "haptic_feedback_title": "Тактильная отдача", "has_quota": "Квота", + "hash_asset": "Хешированный объект", + "hashed_assets": "Хешированные объекты", + "hashing": "Хеширование", "header_settings_add_header_tip": "Добавить заголовок", "header_settings_field_validator_msg": "Значение не может быть пустым", "header_settings_header_name_input": "Имя заголовка", @@ -1083,6 +1088,7 @@ "host": "Хост", "hour": "Час", "id": "ID", + "idle": "В ожидании", "ignore_icloud_photos": "Пропускать файлы из iCloud", "ignore_icloud_photos_description": "Не загружать файлы в Immich, если они хранятся в iCloud", "image": "Изображения", @@ -1109,8 +1115,8 @@ "include_archived": "Отображать архив", "include_shared_albums": "Включать общие альбомы", "include_shared_partner_assets": "Включать общие ресурсы партнера", - "individual_share": "Персональный доступ", - "individual_shares": "Индивидуальный доступ", + "individual_share": "Индивидуальная подборка", + "individual_shares": "Подборки", "info": "Информация", "interval": { "day_at_onepm": "Каждый день в 13:00", @@ -1165,7 +1171,9 @@ "list": "Список", "loading": "Загрузка", "loading_search_results_failed": "Загрузка результатов поиска не удалась", + "local": "На устройстве", "local_asset_cast_failed": "Невозможно транслировать объект, который ещё не загружен на сервер", + "local_assets": "Объекты на устройстве", "local_network": "Локальная сеть", "local_network_sheet_info": "Приложение будет подключаться к серверу по этому адресу, когда устройство подключено к выбранной Wi-Fi сети", "location_permission": "Доступ к местоположению", @@ -1322,6 +1330,7 @@ "no_results": "Нет результатов", "no_results_description": "Попробуйте использовать синоним или более общее ключевое слово", "no_shared_albums_message": "Создайте альбом для обмена фотографиями и видеозаписями с людьми в вашей сети", + "no_uploads_in_progress": "Нет активных загрузок", "not_in_any_album": "Ни в одном альбоме", "not_selected": "Не выбрано", "note_apply_storage_label_to_previously_uploaded assets": "Примечание: Чтобы применить метку хранилища к ранее загруженным ресурсам, запустите", @@ -1359,6 +1368,7 @@ "original": "оригинал", "other": "Другое", "other_devices": "Другие устройства", + "other_entities": "Другие объекты", "other_variables": "Другие переменные", "owned": "Мои", "owner": "Владелец", @@ -1456,7 +1466,7 @@ "profile_drawer_server_out_of_date_minor": "Версия сервера устарела. Пожалуйста, обновите его.", "profile_image_of_user": "Изображение профиля {user}", "profile_picture_set": "Фото профиля установлено.", - "public_album": "Публичный альбом", + "public_album": "Общий альбом", "public_share": "Публичный доступ", "purchase_account_info": "Поддержка", "purchase_activated_subtitle": "Благодарим вас за поддержку Immich и программного обеспечения с открытым исходным кодом", @@ -1519,6 +1529,8 @@ "refreshing_faces": "Обновление лиц", "refreshing_metadata": "Обновление метаданных", "regenerating_thumbnails": "Восстановление миниатюр", + "remote": "На сервере", + "remote_assets": "Объекты на сервере", "remove": "Удалить", "remove_assets_album_confirmation": "Вы действительно хотите удалить {count, plural, one {# объект} many {# объектов} other {# объекта}} из альбома?", "remove_assets_shared_link_confirmation": "Вы действительно хотите удалить {count, plural, one {# объект} many {# объектов} other {# объекта}} из публичного доступа по этой ссылке?", @@ -1556,11 +1568,15 @@ "reset_password": "Сброс пароля", "reset_people_visibility": "Восстановить видимость людей", "reset_pin_code": "Сбросить PIN-код", + "reset_sqlite": "Очистить базу данных SQLite", + "reset_sqlite_confirmation": "Вы уверены, что хотите очистить базу данных SQLite? Вам потребуется выйти из системы и снова войти для повторной синхронизации данных.", + "reset_sqlite_success": "База данных SQLite успешно очищена", "reset_to_default": "Восстановление значений по умолчанию", "resolve_duplicates": "Устранить дубликаты", "resolved_all_duplicates": "Все дубликаты устранены", "restore": "Восстановить", "restore_all": "Восстановить все", + "restore_trash_action_prompt": "{count} восстановлено из корзины", "restore_user": "Восстановить пользователя", "restored_asset": "Восстановленный объект", "resume": "Продолжить", @@ -1569,6 +1585,7 @@ "role": "Роль", "role_editor": "Редактор", "role_viewer": "Зритель", + "running": "Выполняется", "save": "Сохранить", "save_to_gallery": "Сохранить в галерею", "saved_api_key": "API ключ изменён", @@ -1822,6 +1839,7 @@ "storage_quota": "Квота хранилища", "storage_usage": "{used} из {available}", "submit": "Подтвердить", + "success": "Успешно", "suggestions": "Предложения", "sunrise_on_the_beach": "Восход солнца на пляже", "support": "Поддержка", @@ -1831,6 +1849,8 @@ "sync": "Синхр.", "sync_albums": "Синхронизировать альбомы", "sync_albums_manual_subtitle": "Синхронизировать все загруженные фото и видео в выбранные альбомы для резервного копирования", + "sync_local": "Синхронизировать локально", + "sync_remote": "Синхронизация с сервером", "sync_upload_album_setting_subtitle": "Создавайте и загружайте свои фотографии и видео в выбранные альбомы на сервер Immich", "tag": "Тег", "tag_assets": "Добавить теги", @@ -1841,6 +1861,7 @@ "tag_updated": "Тег {tag} изменен", "tagged_assets": "Тег назначен для {count, plural, one {# объекта} other {# объектов}}", "tags": "Теги", + "tap_to_run_job": "Нажмите для запуска задачи", "template": "Шаблон", "theme": "Тема", "theme_selection": "Выбор темы", diff --git a/i18n/sk.json b/i18n/sk.json index c8c1cb5303..a29262a6e8 100644 --- a/i18n/sk.json +++ b/i18n/sk.json @@ -45,14 +45,14 @@ "backup_database_enable_description": "Povoliť výpisy z databázy", "backup_keep_last_amount": "Množstvo predchádzajúcich výpisov, ktoré sa majú zachovať", "backup_settings": "Nastavenia výpisu databázy", - "backup_settings_description": "Správa nastavení výpisu databázy.", + "backup_settings_description": "Spravovať nastavenia výpisu databázy.", "cleared_jobs": "Hotové úlohy pre: {job}", "config_set_by_file": "Konfigurácia je v súčasnosti nastavená konfiguračným súborom", "confirm_delete_library": "Naozaj chcete vymazať knižnicu {library}?", "confirm_delete_library_assets": "Ste si istí, že chcete vymazať túto knižnicu? Tato operácia nenávratne odstráni {count, plural, one {# zahrnutú položku} few {# zahrnuté položky} other {všetkých # zahrnutých položiek}} z aplikácie Immich. Súbory budú ponechané na disku.", "confirm_email_below": "Pre potvrdenie zadajte \"{email}\" nižšie", "confirm_reprocess_all_faces": "Naozaj chcete spracovať všetky tváre znova? Tento proces vymaže pomenovaných ľudí.", - "confirm_user_password_reset": "Naozaj chcete resetovať heslo pre {user}?", + "confirm_user_password_reset": "Naozaj chcete obnoviť heslo pre {user}?", "confirm_user_pin_code_reset": "Ste si istí, že chcete opätovne nastaviť PIN kód používateľa {user}?", "create_job": "Vytvoriť úlohu", "cron_expression": "Výraz cron", @@ -61,10 +61,10 @@ "disable_login": "Zakázať prihlásenie", "duplicate_detection_job_description": "Spustite strojové učenie na položkách pre detekciu podobných obrázkov. Spolieha sa na inteligentné vyhľadávanie", "exclusion_pattern_description": "Vylučovacie vzory Vám umožňujú ignorovať súbory a priečinky pri skenovaní Vašej knižnice. Toto je užitočné, ak máte priečinky obsahujúce súbory, ktoré nechcete importovať, napríklad RAW súbory.", - "external_library_management": "Správa Externej Knižnice", + "external_library_management": "Spravovanie externej knižnice", "face_detection": "Detekcia tvárí", - "face_detection_description": "Rozpoznajte tváre v položkách pomocou strojového učenia. V prípade videí sa berie do úvahy len náhľad. „Obnoviť“ (znovu) spracuje všetky položky. „Resetovať“ dodatočne vymaže všetky aktuálne údaje o tvárach. „Chýbajúce“ zaradí do poradia médiá, ktoré ešte neboli spracované. Zistené tváre sa po dokončení rozpoznávania tvárí zaradia do poradia na rozpoznávanie tvárí, pričom sa zoskupia do existujúcich alebo nových osôb.", - "facial_recognition_job_description": "Zoskupte rozpoznané tváre do osôb. Tento krok sa vykoná po dokončení rozpoznávania tvárí. „Resetovať“ (znovu) zoskupí všetky tváre. „Chýbajúce“ zaradí tváre, ktoré nemajú pridelenú osobu.", + "face_detection_description": "Rozpoznajte tváre v položkách pomocou strojového učenia. V prípade videí sa berie do úvahy len náhľad. „Aktualizovať“ (znovu) spracuje všetky položky. „Obnoviť“ dodatočne vymaže všetky aktuálne údaje o tvárach. „Chýbajúce“ zaradí do poradia médiá, ktoré ešte neboli spracované. Zistené tváre sa po dokončení rozpoznávania tvárí zaradia do poradia na rozpoznávanie tvárí, pričom sa zoskupia do existujúcich alebo nových osôb.", + "facial_recognition_job_description": "Zoskupte rozpoznané tváre do osôb. Tento krok sa vykoná po dokončení rozpoznávania tvárí. „Obnoviť“ (znovu) zoskupí všetky tváre. „Chýbajúce“ zaradí tváre, ktoré nemajú pridelenú osobu.", "failed_job_command": "Príkaz {command} zlyhal pre úlohu: {job}", "force_delete_user_warning": "VAROVANIE: Toto okamžite odstráni používateľa a všetky položky. Tento krok nie je možné vrátiť späť a súbory nebude možné obnoviť.", "image_format": "Formát", @@ -94,7 +94,7 @@ "job_not_concurrency_safe": "Táto úloha nie je bezpečná pre súbežné spracovanie.", "job_settings": "Úlohy", "job_settings_description": "Spravovať súbežnosť úloh", - "job_status": "Stav Úloh", + "job_status": "Stav úloh", "jobs_delayed": "{jobCount, plural, one {# oneskorený} few {# oneskorené} other {# oneskorených}}", "jobs_failed": "{jobCount, plural, one {# neúspešný} few {# neúspešné} other {# neúspešných}}", "library_created": "Vytvorená knižnica: {library}", @@ -141,15 +141,15 @@ "machine_learning_smart_search_enabled": "Povoliť inteligentné vyhľadávanie", "machine_learning_smart_search_enabled_description": "Ak je vypnuté, obrázky nebudú spracované pre inteligentné vyhľadávanie.", "machine_learning_url_description": "URL adresa servera strojového učenia. Ak je zadaných viacero adries URL, každý server bude testovaný postupne, kým jeden z nich neodpovie úspešne, v poradí od prvého po posledný. Servery, ktoré neodpovedajú, budú dočasne ignorované, kým nebudú opäť online.", - "manage_concurrency": "Správa súbežnosti", + "manage_concurrency": "Spravovať súbežnosť", "manage_log_settings": "Spravovať nastavenia ukladania záznamov", "map_dark_style": "Tmavý štýl", "map_enable_description": "Povoliť funkcie mapy", "map_gps_settings": "Mapa a nastavenia GPS", - "map_gps_settings_description": "Spravujte nastavenia mapy a GPS (reverzné geokódovanie)", + "map_gps_settings_description": "Spravovať nastavenia mapy a GPS (reverzné geokódovanie)", "map_implications": "Táto funkčnosť sa spolieha na externý servis spracovania mapových dlaždíc (tiles.immich.cloud)", "map_light_style": "Svetlý štýl", - "map_manage_reverse_geocoding_settings": "Správa nastavení Reverzného geokódovania", + "map_manage_reverse_geocoding_settings": "Spravovať nastavenia reverzného geokódovania", "map_reverse_geocoding": "Reverzné Geokódovanie", "map_reverse_geocoding_enable_description": "Povoliť reverzné geokódovanie", "map_reverse_geocoding_settings": "Reverzné geokódovanie", @@ -214,7 +214,7 @@ "oauth_role_claim_description": "Automaticky udeliť prístup správcu na základe prítomnosti tejto požiadavky. Požiadavka môže mať príznak „user“ alebo „admin“.", "oauth_settings": "OAuth", "oauth_settings_description": "Spravovať nastavenia prihlásenia OAuth", - "oauth_settings_more_details": "Pre viac informácii o tejto funkcii, prejdite na docs.", + "oauth_settings_more_details": "Pre viac informácii o tejto funkcii, prejdite na dokumentáciu.", "oauth_storage_label_claim": "Nárokovať Štítok úložiska", "oauth_storage_label_claim_description": "Automaticky nastaviť štítok úložiska používateľa na hodnotu tohto nároku.", "oauth_storage_quota_claim": "Deklarácia kvóty úložiska", @@ -234,7 +234,7 @@ "registration_description": "Keďže ste prvým používateľom v systéme, budú vám pridelené správcovské práva na vykonávanie všetkých úloh a vrátane tvorby nových používateľov.", "require_password_change_on_login": "Vyžadovať od používateľa zmenu hesla pri prvom prihlásení", "reset_settings_to_default": "Obnoviť pôvodné nastavenia", - "reset_settings_to_recent_saved": "Obnoviť naposledy uložené nastavenia", + "reset_settings_to_recent_saved": "Nastavenia boli obnovené na posledné uložené nastavenia", "scanning_library": "Knižnica sa skenuje", "search_jobs": "Vyhľadať úlohy…", "send_welcome_email": "Odoslať uvítací e-mail", @@ -259,7 +259,7 @@ "storage_template_migration_description": "Použite aktuálnu {template} na predtým nahrané médiá", "storage_template_migration_info": "Šablóna úložiska skonvertuje všetky prípony na malé písmená. Zmeny šablón sa budú vzťahovať iba na nové diela. Ak chcete šablónu spätne použiť na predtým nahrané médiá, spustite {job}.", "storage_template_migration_job": "Úloha migrácie šablóny úložiska", - "storage_template_more_details": "Ďalšie podrobnosti o tejto funkcii nájdete v Šablóna úložiska a jej dôsledky", + "storage_template_more_details": "Podrobnejšie informácie o tejto funkcii nájdete v časti šablóna úložiska a jej následky", "storage_template_onboarding_description_v2": "Ak je táto funkcia zapnutá, automaticky usporiada súbory na základe šablóny definovanej používateľom. Ďalšie informácie nájdete v dokumentácii.", "storage_template_path_length": "Približný limit dĺžky cesty: {length, number}/{limit, number}", "storage_template_settings": "Šablóna úložiska", @@ -278,9 +278,9 @@ "template_settings_description": "Spravovanie vlastných šablón upozornení", "theme_custom_css_settings": "Vlastné CSS", "theme_custom_css_settings_description": "CSS štýly umožňujú prispôsobiť dizajn Immich.", - "theme_settings": "Motívy", + "theme_settings": "Nastavenia témy", "theme_settings_description": "Spravovať prispôsobenie webového rozhrania Immich", - "thumbnail_generation_job": "Generovať Miniatúry", + "thumbnail_generation_job": "Generovať miniatúry", "thumbnail_generation_job_description": "Generujte veľké, malé a rozmazané miniatúry pre každú položku, ako aj miniatúry pre každú osobu", "transcoding_acceleration_api": "API pre akceleráciu", "transcoding_acceleration_api_description": "Rozhranie API, ktoré bude spolupracovať s vaším zariadením s cieľom urýchliť prekódovanie. Toto nastavenie je „najlepšie úsilie“: pri zlyhaní sa vráti k softvérovému prekódovaniu. VP9 môže alebo nemusí fungovať v závislosti od vášho hardvéru.", @@ -300,7 +300,7 @@ "transcoding_bitrate_description": "Videá presahujúce maximálnu bitovú rýchlosť alebo videá, ktoré nie sú v akceptovanom formáte", "transcoding_codecs_learn_more": "Ak sa chcete dozvedieť viac o tu použitej terminológii, pozrite si dokumentáciu FFmpeg pre kodek H.264, kodek HEVC a VP9 kodek.", "transcoding_constant_quality_mode": "Režim konštantnej kvality", - "transcoding_constant_quality_mode_description": "ICQ je lepšie ako CQP, ale niektoré zariadenia na hardvérovú akceleráciu tento režim nepodporujú. Nastavenie tejto možnosti uprednostní špecifikovaný režim pri použití kódovania založeného na kvalite. Ignorované spoločnosťou NVENC, pretože nepodporuje ICQ.", + "transcoding_constant_quality_mode_description": "ICQ je lepšie ako CQP, ale niektoré zariadenia na hardvérovú akceleráciu tento režim nepodporujú. Nastavenie tejto možnosti uprednostní špecifikovaný režim pri použití kódovania založeného na kvalite. Ignorované funkciou NVENC, pretože nepodporuje ICQ.", "transcoding_constant_rate_factor": "Faktor konštantnej rýchlosti (-crf)", "transcoding_constant_rate_factor_description": "Úroveň kvality videa. Typické hodnoty sú 23 pre H.264, 28 pre HEVC, 31 pre VP9 a 35 pre AV1. Nižšie je lepšie, ale vytvára väčšie súbory.", "transcoding_disabled_description": "Neprekódovať žiadne videá, na niektorých klientoch môže prerušiť prehrávanie", @@ -321,13 +321,13 @@ "transcoding_policy_description": "Nastavte, kedy bude video prekódované", "transcoding_preferred_hardware_device": "Uprednostňované hardvérové zariadenie", "transcoding_preferred_hardware_device_description": "Platí len pre VAAPI a QSV. Nastavuje uzol dri, ktorý sa používa na hardvérové prekódovanie.", - "transcoding_preset_preset": "Prednastavenie (-preset)", + "transcoding_preset_preset": "Predvoľba (-preset)", "transcoding_preset_preset_description": "Rýchlosť kompresie. Pomalšie predvoľby vytvárajú menšie súbory a zvyšujú kvalitu, keď sa zameriavajú na určitý dátový tok. VP9 ignoruje rýchlosti vyššie ako „rýchlejšie“.", "transcoding_reference_frames": "Referenčné snímky", "transcoding_reference_frames_description": "Počet snímok, na ktoré sa má odkazovať pri kompresii daného snímku. Vyššie hodnoty zvyšujú účinnosť kompresie, ale spomaľujú kódovanie. Hodnota 0 sa nastavuje automaticky.", "transcoding_required_description": "Iba videá, ktoré nie sú v prijatom formáte", "transcoding_settings": "Nastavenia prekódovania videa", - "transcoding_settings_description": "Spravujte, ktoré videá sa majú prekódovať a ako ich spracovať", + "transcoding_settings_description": "Spravovať, ktoré videá sa majú prekódovať a ako sa majú spracovať", "transcoding_target_resolution": "Cieľové rozlíšenie", "transcoding_target_resolution_description": "Vyššie rozlíšenia môžu zachovať viac detailov, ale ich kódovanie trvá dlhšie, majú väčšiu veľkosť súborov a môžu znížiť odozvu aplikácie.", "transcoding_temporal_aq": "Časové AQ", @@ -349,13 +349,13 @@ "trash_settings_description": "Spravovať nastavenia koša", "user_cleanup_job": "Premazanie používateľov", "user_delete_delay": "Konto {user} a jeho médiá budú podľa plánu natrvalo vymazané za {delay, plural, one {# deň} few {# dni} other {# dní}}.", - "user_delete_delay_settings": "Odstrániť oneskorenie", - "user_delete_delay_settings_description": "Počet dní po odstránení na trvalé vymazanie účtu a médií používateľa. Úloha odstraňovania používateľov sa spúšťa o polnoci, aby sa skontrolovali používatelia, ktorí sú pripravení na odstránenie. Zmeny tohto nastavenia sa vyhodnotia pri ďalšom spustení.", + "user_delete_delay_settings": "Oneskorenie vymazania", + "user_delete_delay_settings_description": "Počet dní, po ktorých sa po odstránení používateľa natrvalo odstráni jeho účet a položky. Úloha odstraňovania používateľov sa spúšťa o polnoci, aby sa skontrolovali používatelia, ktorí sú pripravení na odstránenie. Zmeny tohto nastavenia sa vyhodnotia pri ďalšom spustení.", "user_delete_immediately": "Konto a médiá používateľa {user} budú zaradené do poradia na trvalé vymazanie okamžite.", "user_delete_immediately_checkbox": "Používateľ a médiá budú zaradení do frontu na okamžité vymazanie", "user_details": "Podrobnosti o používateľovi", - "user_management": "Správa používateľov", - "user_password_has_been_reset": "Heslo používateľa bolo resetované:", + "user_management": "Spravovanie používateľov", + "user_password_has_been_reset": "Heslo používateľa bolo obnovené:", "user_password_reset_description": "Poskytnite používateľovi dočasné heslo a informujte ho, že si ho bude musieť zmeniť pri ďalšom prihlásení.", "user_restore_description": "{user} bude účet obnovený.", "user_restore_scheduled_removal": "Obnoviť používateľa - plánované odstránenie na {date, date, long}", @@ -393,7 +393,7 @@ "age_year_months": "Vek 1 rok, {months, plural, one {# month} other {# months}}", "age_years": "{years, plural, other {Vek #}}", "album_added": "Album bol pridaný", - "album_added_notification_setting_description": "Obdržať upozornenie emailom, keď ste pridaní do zdieľaného albumu", + "album_added_notification_setting_description": "Obdržať upozornenie emailom, keď vás pridajú do zdieľaného albumu", "album_cover_updated": "Obal albumu aktualizovaný", "album_delete_confirmation": "Ste si istý, že chcete odstrániť album {album}?", "album_delete_confirmation_description": "Ak je tento album zdieľaný, ostatní používatelia k nemu už nebudú mať prístup.", @@ -573,6 +573,8 @@ "backup_options_page_title": "Možnosti zálohovania", "backup_setting_subtitle": "Spravovať nastavenia odosielania na pozadí a v popredí", "backward": "Dozadu", + "beta_sync": "Stav synchronizácie verzie Beta", + "beta_sync_subtitle": "Spravovať nový systém synchronizácie", "biometric_auth_enabled": "Biometrické overovanie je povolené", "biometric_locked_out": "Ste vymknutí z biometrického overovania", "biometric_no_options": "Nie sú k dispozícii žiadne biometrické možnosti", @@ -590,7 +592,7 @@ "cache_settings_clear_cache_button": "Vymazať vyrovnávaciu pamäť", "cache_settings_clear_cache_button_title": "Vymaže vyrovnávaciu pamäť aplikácie. To výrazne ovplyvní výkon aplikácie, kým sa vyrovnávacia pamäť neobnoví.", "cache_settings_duplicated_assets_clear_button": "VYČISTIŤ", - "cache_settings_duplicated_assets_subtitle": "Fotky a videá ktoré sú na čiernej listine zvolené aplikáciou", + "cache_settings_duplicated_assets_subtitle": "Fotografie a videá, ktoré aplikácia ignoruje podľa zoznamu", "cache_settings_duplicated_assets_title": "Duplicitné položky ({count})", "cache_settings_statistics_album": "Knižnica náhľadov", "cache_settings_statistics_full": "Kompletné fotografie", @@ -628,7 +630,7 @@ "change_password_form_password_mismatch": "Heslá sa nezhodujú", "change_password_form_reenter_new_password": "Znova zadajte nové heslo", "change_pin_code": "Zmeniť PIN kód", - "change_your_password": "Zmeňte si heslo", + "change_your_password": "Zmeniť heslo", "changed_visibility_successfully": "Viditeľnosť bola úspešne zmenená", "check_corrupt_asset_backup": "Skontrolovať, či nie sú poškodené zálohy položiek", "check_corrupt_asset_backup_button": "Vykonať kontrolu", @@ -723,7 +725,7 @@ "custom_locale_description": "Formátovanie dátumov a čísel podľa jazyka a regiónu", "daily_title_text_date": "EEEE, d. MMMM", "daily_title_text_date_year": "EEEE, d. MMMM y", - "dark": "Tmavý", + "dark": "Tmavá", "dark_theme": "Prepnúť tmavú tému", "date_after": "Dátum po", "date_and_time": "Dátum a Čas", @@ -949,7 +951,7 @@ "unable_to_remove_library": "Nie je možné odstrániť knižnicu", "unable_to_remove_partner": "Nie je možné odstrániť partnera", "unable_to_remove_reaction": "Nie je možné odstrániť reakciu", - "unable_to_reset_password": "Nie je možné resetovať heslo", + "unable_to_reset_password": "Nie je možné obnoviť heslo", "unable_to_reset_pin_code": "Nie je možné obnoviť PIN kód", "unable_to_resolve_duplicate": "Nie je možné vyriešiť duplikát", "unable_to_restore_assets": "Nie je možné obnoviť položky", @@ -987,7 +989,7 @@ "exif_bottom_sheet_person_age_months": "Vek {months} mesiacov", "exif_bottom_sheet_person_age_year_months": "Vek 1 rok, {months} mesiacov", "exif_bottom_sheet_person_age_years": "Vek {years}", - "exit_slideshow": "Opustiť Slideshow", + "exit_slideshow": "Opustiť prezentáciu", "expand_all": "Rozbaliť všetko", "experimental_settings_new_asset_list_subtitle": "Prebiehajúca práca", "experimental_settings_new_asset_list_title": "Povolenie experimentálnej mriežky fotografií", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "Povoliť hmatovú odozvu", "haptic_feedback_title": "Hmatová odozva", "has_quota": "Má kvótu", + "hash_asset": "Hashovať položku", + "hashed_assets": "Hashované položky", + "hashing": "Hashovanie", "header_settings_add_header_tip": "Pridať hlavičku", "header_settings_field_validator_msg": "Hodnota nemôže byť prázdna", "header_settings_header_name_input": "Názov hlavičky", @@ -1083,6 +1088,7 @@ "host": "Hostiteľ", "hour": "Hodina", "id": "ID", + "idle": "Nečinné", "ignore_icloud_photos": "Ignorovať fotky v službe iCloud", "ignore_icloud_photos_description": "Fotografie uložené v službe iCloud sa nebudú odosielať na server Immich", "image": "Obrázok", @@ -1139,7 +1145,7 @@ "language_no_results_subtitle": "Skúste upraviť hľadaný výraz", "language_no_results_title": "Neboli nájdené žiadne jazyky", "language_search_hint": "Vyhľadať jazyky...", - "language_setting_description": "Vyberte preferovaný jazyk", + "language_setting_description": "Vyberte požadovaný jazyk", "last_seen": "Naposledy videné", "latest_version": "Najnovšia verzia", "latitude": "Zemepisná šírka", @@ -1156,7 +1162,7 @@ "library_page_sort_last_modified": "Naposledy upravené", "library_page_sort_title": "Podľa názvu albumu", "licenses": "Licencie", - "light": "Svetlý", + "light": "Svetlá", "like_deleted": "Like odstránený", "link_motion_video": "Pripojiť pohyblivé video", "link_options": "Možnosti odkazu", @@ -1165,7 +1171,9 @@ "list": "Zoznam", "loading": "Načítavanie", "loading_search_results_failed": "Načítanie výsledkov hľadania sa nepodarilo", + "local": "Lokálne", "local_asset_cast_failed": "Nie je možné preniesť médium, ktoré nie je nahrané na serveri", + "local_assets": "Lokálne položky", "local_network": "Miestna sieť", "local_network_sheet_info": "Pri použití zadanej siete Wi-Fi sa aplikácia pripojí k serveru prostredníctvom tejto URL adresy", "location_permission": "Povolenie na určenie polohy", @@ -1277,7 +1285,7 @@ "move_off_locked_folder": "Presunúť zo zamknutého priečinka", "move_to_lock_folder_action_prompt": "{count} pridaných do zamknutého priečinka", "move_to_locked_folder": "Presunúť do zamknutého priečinka", - "move_to_locked_folder_confirmation": "Tieto fotografie a videá budú odstránené zo všetkých albumov a bude ich možné zobraziť len v zamknutom priečinku", + "move_to_locked_folder_confirmation": "Tieto fotografie a videá budú odobrané zo všetkých albumov a bude ich možné zobraziť len v zamknutom priečinku", "moved_to_archive": "{count, plural, one {Presunutá # položka} few {Presunuté # položky} other {Presunutých # položiek}} do archívu", "moved_to_library": "{count, plural, one {Presunutá # položka} few {Presunuté # položky} other {Presunutých # položiek}} do knižnice", "moved_to_trash": "Presunuté do koša", @@ -1322,6 +1330,7 @@ "no_results": "Žiadne výsledky", "no_results_description": "Skúste synonymum alebo všeobecnejší výraz", "no_shared_albums_message": "Vytvorí album na zdieľanie fotiek a videí s ľuďmi vo vašej sieti", + "no_uploads_in_progress": "Žiadne prebiehajúce nahrávanie", "not_in_any_album": "Nie je v žiadnom albume", "not_selected": "Nevybrané", "note_apply_storage_label_to_previously_uploaded assets": "Poznámka: Ak chcete použiť Štítok úložiska na predtým nahrané médiá, spustite príkaz", @@ -1343,7 +1352,7 @@ "onboarding": "Na palube", "onboarding_locale_description": "Vyberte požadovaný jazyk. Neskôr ho môžete zmeniť v nastaveniach.", "onboarding_privacy_description": "Nasledujúce (voliteľné) funkcie závisia na externých službách a kedykoľvek ich môžete vypnúť nastaveniach.", - "onboarding_server_welcome_description": "Nastavme vašu inštanciu pomocou bežných nastavení.", + "onboarding_server_welcome_description": "Poďme si nastaviť vašu inštanciu s niekoľkými bežnými nastaveniami.", "onboarding_theme_description": "Vyberte farbu témy pre váš server. Môžete to aj neskôr zmeniť vo vašich nastaveniach.", "onboarding_user_welcome_description": "Začnime!", "onboarding_welcome_user": "Vitaj, {user}", @@ -1359,6 +1368,7 @@ "original": "originál", "other": "Ostatné", "other_devices": "Ďalšie zariadenia", + "other_entities": "Ostatné subjekty", "other_variables": "Ostatné premenné", "owned": "Vlastnené", "owner": "Vlastník", @@ -1434,9 +1444,9 @@ "play_or_pause_video": "Pustí alebo pozastaví video", "please_auth_to_access": "Prosím, potvrďte overenie pre prístup", "port": "Port", - "preferences_settings_subtitle": "Spravujte predvoľby aplikácie", + "preferences_settings_subtitle": "Spravovať predvoľby aplikácie", "preferences_settings_title": "Predvoľby", - "preset": "Prednastavenie", + "preset": "Predvoľba", "preview": "Náhľad", "previous": "Predošlé", "previous_memory": "Predošlá spomienka", @@ -1508,7 +1518,7 @@ "recently_added_page_title": "Nedávno pridané", "recently_taken": "Nedávno nasnímané", "recently_taken_page_title": "Nedávno zhotovené", - "refresh": "Obnoviť", + "refresh": "Aktualizovať", "refresh_encoded_videos": "Obnoviť enkódované videá", "refresh_faces": "Obnoviť tváre", "refresh_metadata": "Obnoviť metadáta", @@ -1519,6 +1529,8 @@ "refreshing_faces": "Obnovovanie tvárí", "refreshing_metadata": "Obnovovanie metadát", "regenerating_thumbnails": "Pregenerovanie náhľadov", + "remote": "Vzdialené", + "remote_assets": "Vzdialené položky", "remove": "Odstrániť", "remove_assets_album_confirmation": "Naozaj chcete odstrániť {count, plural, one {# položku} few {# položky} other {# položiek}} z albumu?", "remove_assets_shared_link_confirmation": "Naozaj chcete odstrániť {count, plural, one {# položku} few {# položky} other {# položiek}} z tohoto zdieľaného odkazu?", @@ -1529,8 +1541,8 @@ "remove_from_album_action_prompt": "{count} odstránené z albumu", "remove_from_favorites": "Odstrániť z obľúbených", "remove_from_lock_folder_action_prompt": "{count} odobrané zo zamknutého priečinka", - "remove_from_locked_folder": "Odstrániť zo zamknutého priečinka", - "remove_from_locked_folder_confirmation": "Ste si istí, že chcete tieto fotografie a videá presunúť zo zamknutého priečinka? Budú viditeľné vo vašej knižnici.", + "remove_from_locked_folder": "Odobrať zo zamknutého priečinka", + "remove_from_locked_folder_confirmation": "Ste si istí, že chcete tieto fotografie a videá odobrať zo zamknutého priečinka? Budú viditeľné vo vašej knižnici.", "remove_from_shared_link": "Odstrániť zo zdieľaného odkazu", "remove_memory": "Odstrániť spomienku", "remove_photo_from_memory": "Odstrániť fotografiu z tejto spomienky", @@ -1552,28 +1564,33 @@ "require_password": "Vyžadovať heslo", "require_user_to_change_password_on_first_login": "Vyžadovať zmenu hesla po prvom prihlásení", "rescan": "Opätovné vyhľadávanie", - "reset": "Resetovať", + "reset": "Obnoviť", "reset_password": "Obnoviť heslo", - "reset_people_visibility": "Resetovať viditeľnosť ľudí", + "reset_people_visibility": "Obnoviť viditeľnosť ľudí", "reset_pin_code": "Obnoviť PIN kód", - "reset_to_default": "Resetovať na predvolené", + "reset_sqlite": "Obnoviť SQLite databázu", + "reset_sqlite_confirmation": "Ste si istí, že chcete obnoviť SQLite databázu? Na opätovnú synchronizáciu údajov sa budete musieť odhlásiť a znova prihlásiť", + "reset_sqlite_success": "Úspešné obnovenie databázy SQLite", + "reset_to_default": "Obnoviť na predvolené", "resolve_duplicates": "Vyriešiť duplicity", "resolved_all_duplicates": "Vyriešené všetky duplicity", "restore": "Navrátiť", "restore_all": "Navrátit všetko", + "restore_trash_action_prompt": "{count} obnovených z koša", "restore_user": "Navrátiť používateľa", "restored_asset": "Navrátené položky", "resume": "Pokračovať", "retry_upload": "Zopakovať nahrávanie", - "review_duplicates": "Prezrieť duplikáty", + "review_duplicates": "Preskúmať duplikáty", "role": "Rola", "role_editor": "Editor", "role_viewer": "Divák", + "running": "Spustené", "save": "Uložiť", "save_to_gallery": "Uložiť do galérie", "saved_api_key": "Uložený API Kľúč", "saved_profile": "Uložený profil", - "saved_settings": "Uložené nastavenia", + "saved_settings": "Nastavenia boli uložené", "say_something": "Napíšte niečo", "scaffold_body_error_occurred": "Vyskytla sa chyba", "scan_all_libraries": "Preskenovať všetky knižnice", @@ -1585,7 +1602,7 @@ "search_by_context": "Hľadať s kontextom", "search_by_description": "Vyhľadávanie podľa popisu", "search_by_description_example": "Pešia turistika v Sape", - "search_by_filename": "Hľadať s názvom alebo príponou súboru", + "search_by_filename": "Hľadať podľa názvu alebo prípony súboru", "search_by_filename_example": "napr. IMG_1234.JPG alebo PNG", "search_camera_make": "Hľadať značku fotoaparátu...", "search_camera_model": "Hľadať model fotoaparátu...", @@ -1809,7 +1826,7 @@ "stacked_assets_count": "{count, plural, one {Zoskupená # položka} few {Zoskupené # položky} other {Zoskupených # položiek}}", "stacktrace": "Výpis zásobníku", "start": "Štart", - "start_date": "Začiatočný dátum", + "start_date": "Počiatočný dátum", "state": "Štát", "status": "Stav", "stop_casting": "Zastaviť prenos", @@ -1822,6 +1839,7 @@ "storage_quota": "Úložný limit", "storage_usage": "Využitých {used} z {available}", "submit": "Odoslať", + "success": "Úspech", "suggestions": "Návrhy", "sunrise_on_the_beach": "Východ slnka na pláži", "support": "Podpora", @@ -1831,9 +1849,11 @@ "sync": "Synchronizovať", "sync_albums": "Synchronizovať albumy", "sync_albums_manual_subtitle": "Synchronizujte všetky nahrané videá a fotografie s vybranými záložnými albumami", + "sync_local": "Synchronizovať lokálne", + "sync_remote": "Synchronizovať vzdialené", "sync_upload_album_setting_subtitle": "Vytvárajte a nahrávajte svoje fotografie a videá do vybraných albumov na Immich", "tag": "Štítok", - "tag_assets": "Označiť položky", + "tag_assets": "Pridať štítky", "tag_created": "Vytvorený štítok: {tag}", "tag_feature_description": "Prehliadanie fotiek a videá zoskupených podľa tematických štítkov", "tag_not_found_question": "Neviete nájsť štítok? Vytvorte nový štítok.", @@ -1841,6 +1861,7 @@ "tag_updated": "Upravený štítok: {tag}", "tagged_assets": "Štítok priradený {count, plural, one {# položke} other {# položkám}}", "tags": "Štítky", + "tap_to_run_job": "Ťuknutím na položku spustíte úlohu", "template": "Šablóna", "theme": "Téma", "theme_selection": "Výber témy", @@ -1863,7 +1884,7 @@ "time_based_memories": "Časové spomienky", "timeline": "Časová os", "timezone": "Časové pásmo", - "to_archive": "Archív", + "to_archive": "Archivovať", "to_change_password": "Zmeniť heslo", "to_favorite": "Obľúbiť", "to_login": "Prihlásiť", @@ -1898,7 +1919,7 @@ "unfavorite_action_prompt": "{count} odstránené z Obľúbených", "unhide_person": "Odkryť osobu", "unknown": "Neznáme", - "unknown_country": "Neznámy štát", + "unknown_country": "Neznáma krajina", "unknown_year": "Neznámy rok", "unlimited": "Neobmedzené", "unlink_motion_video": "Odpojiť pohyblivé video", @@ -1986,7 +2007,7 @@ "viewer_stack_use_as_main_asset": "Použiť ako hlavnú fotku", "viewer_unstack": "Odskupiť", "visibility_changed": "Viditeľnosť zmenená pre {count, plural, one {# osobu} few {# osoby} other {# osôb}}", - "waiting": "Čaká", + "waiting": "Čakajúce", "warning": "Varovanie", "week": "Týždeň", "welcome": "Vitajte", @@ -1996,7 +2017,7 @@ "year": "Rok", "years_ago": "pred {years, plural, one {# rokom} other {# rokmi}}", "yes": "Áno", - "you_dont_have_any_shared_links": "Nemáte žiadne zdielané linky", + "you_dont_have_any_shared_links": "Nemáte žiadne zdielané odkazy", "your_wifi_name": "Váš názov siete Wi-Fi", "zoom_image": "Priblížiť obrázok" } diff --git a/i18n/sl.json b/i18n/sl.json index c4409c717e..e08a9dd506 100644 --- a/i18n/sl.json +++ b/i18n/sl.json @@ -573,6 +573,8 @@ "backup_options_page_title": "Možnosti varnostne kopije", "backup_setting_subtitle": "Upravljaj nastavitve nalaganja v ozadju in ospredju", "backward": "Nazaj", + "beta_sync": "Stanje sinhronizacije beta različice", + "beta_sync_subtitle": "Upravljanje novega sistema sinhronizacije", "biometric_auth_enabled": "Biometrična avtentikacija omogočena", "biometric_locked_out": "Biometrična avtentikacija vam je onemogočena", "biometric_no_options": "Biometrične možnosti niso na voljo", @@ -590,7 +592,7 @@ "cache_settings_clear_cache_button": "Počisti predpomnilnik", "cache_settings_clear_cache_button_title": "Počisti predpomnilnik aplikacije. To bo znatno vplivalo na delovanje aplikacije, dokler se predpomnilnik ne obnovi.", "cache_settings_duplicated_assets_clear_button": "POČISTI", - "cache_settings_duplicated_assets_subtitle": "Fotografije in videoposnetki, ki jih je aplikacija uvrstila na črni seznam", + "cache_settings_duplicated_assets_subtitle": "Fotografije in videoposnetki, ki so prezrti s strani aplikacije", "cache_settings_duplicated_assets_title": "Podvojena sredstva ({count})", "cache_settings_statistics_album": "Sličice knjižnice", "cache_settings_statistics_full": "Izvirne slike", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "Uporabi haptičen odziv", "haptic_feedback_title": "Haptičen odziv", "has_quota": "Ima kvoto", + "hash_asset": "Zgoščeno sredstvo", + "hashed_assets": "Zgoščena sredstva", + "hashing": "Zgoščevanje", "header_settings_add_header_tip": "Dodaj glavo", "header_settings_field_validator_msg": "Vrednost ne sme biti prazna", "header_settings_header_name_input": "Ime glave", @@ -1083,6 +1088,7 @@ "host": "Gostitelj", "hour": "Ura", "id": "ID", + "idle": "Nedejavnost", "ignore_icloud_photos": "Ignoriraj fotografije iCloud", "ignore_icloud_photos_description": "Fotografije, shranjene v iCloud, ne bodo naložene na strežnik Immich", "image": "Slika", @@ -1165,7 +1171,9 @@ "list": "Seznam", "loading": "Nalaganje", "loading_search_results_failed": "Nalaganje rezultatov iskanja ni uspelo", + "local": "Lokalno", "local_asset_cast_failed": "Sredstva, ki niso naložena na strežnik, ni mogoče predvajati", + "local_assets": "Lokalna sredstva", "local_network": "Lokalno omrežje", "local_network_sheet_info": "Aplikacija se bo povezala s strežnikom prek tega URL-ja, ko bo uporabljala navedeno omrežje Wi-Fi", "location_permission": "Dovoljenje za lokacijo", @@ -1322,6 +1330,7 @@ "no_results": "Brez rezultatov", "no_results_description": "Poskusite s sinonimom ali bolj splošno ključno besedo", "no_shared_albums_message": "Ustvarite album za skupno rabo fotografij in videoposnetkov z osebami v vašem omrežju", + "no_uploads_in_progress": "Ni nalaganj v teku", "not_in_any_album": "Ni v nobenem albumu", "not_selected": "Ni izbrano", "note_apply_storage_label_to_previously_uploaded assets": "Opomba: Če želite oznako za shranjevanje uporabiti za predhodno naložena sredstva, zaženite", @@ -1359,6 +1368,7 @@ "original": "izvirnik", "other": "drugo", "other_devices": "Druge naprave", + "other_entities": "Drugi subjekti", "other_variables": "Druge spremenljivke", "owned": "V lasti", "owner": "Lastnik", @@ -1390,7 +1400,7 @@ "pause": "Premor", "pause_memories": "Zaustavi spomine", "paused": "Zaustavljeno", - "pending": "V teku", + "pending": "Čakanje", "people": "Osebe", "people_edits_count": "{count, plural, one {Urejena # oseba} two {Urejeni # osebi} few {Urejene # osebe} other {Urejenih # oseb}}", "people_feature_description": "Brskanje po fotografijah in videoposnetkih, razvrščenih po osebah", @@ -1519,6 +1529,8 @@ "refreshing_faces": "Osveževanje obrazev", "refreshing_metadata": "Osveževanje metapodatkov", "regenerating_thumbnails": "Obnavljanje sličic", + "remote": "Oddaljeno", + "remote_assets": "Oddaljena sredstva", "remove": "Odstrani", "remove_assets_album_confirmation": "Ali ste prepričani, da želite odstraniti {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} iz albuma?", "remove_assets_shared_link_confirmation": "Ali ste prepričani, da želite odstraniti {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} iz te skupne povezave?", @@ -1556,11 +1568,15 @@ "reset_password": "Ponastavi geslo", "reset_people_visibility": "Ponastavi vidnost ljudi", "reset_pin_code": "Ponastavi PIN kodo", + "reset_sqlite": "Ponastavi bazo podatkov SQLite", + "reset_sqlite_confirmation": "Ali ste prepričani, da želite ponastaviti bazo podatkov SQLite? Za ponovno sinhronizacijo podatkov se boste morali odjaviti in znova prijaviti", + "reset_sqlite_success": "Uspešno ponastavljena baza podatkov SQLite", "reset_to_default": "Ponastavi na privzeto", "resolve_duplicates": "Razreši dvojnike", "resolved_all_duplicates": "Razrešeni vsi dvojniki", "restore": "Obnovi", "restore_all": "Obnovi vse", + "restore_trash_action_prompt": "{count} obnovljenih iz koša", "restore_user": "Obnovi uporabnika", "restored_asset": "Obnovljeno sredstvo", "resume": "Nadaljuj", @@ -1569,6 +1585,7 @@ "role": "Vloga", "role_editor": "Urejevalec", "role_viewer": "Gledalec", + "running": "V teku", "save": "Shrani", "save_to_gallery": "Shrani v galerijo", "saved_api_key": "Shranjen API ključ", @@ -1822,6 +1839,7 @@ "storage_quota": "Kvota shranjevanja", "storage_usage": "uporabljeno {used} od {available}", "submit": "Predloži", + "success": "Uspeh", "suggestions": "Predlogi", "sunrise_on_the_beach": "Sončni vzhod na plaži", "support": "Podpora", @@ -1831,6 +1849,8 @@ "sync": "Sinhronizacija", "sync_albums": "Sinhronizacija albumov", "sync_albums_manual_subtitle": "Sinhronizirajte vse naložene videoposnetke in fotografije v izbrane varnostne albume", + "sync_local": "Sinhroniziraj lokalno", + "sync_remote": "Sinhroniziraj oddaljeno", "sync_upload_album_setting_subtitle": "Ustvarite in naložite svoje fotografije in videoposnetke v izbrane albume na Immich", "tag": "Oznaka", "tag_assets": "Označi sredstva", @@ -1841,6 +1861,7 @@ "tag_updated": "Posodobljena oznaka: {tag}", "tagged_assets": "Označeno {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", "tags": "Oznake", + "tap_to_run_job": "Dotaknite se za zagon opravila", "template": "Predloga", "theme": "Tema", "theme_selection": "Izbira teme", diff --git a/i18n/zh_SIMPLIFIED.json b/i18n/zh_SIMPLIFIED.json index 28d6a5c0d5..bcbdcc5d6b 100644 --- a/i18n/zh_SIMPLIFIED.json +++ b/i18n/zh_SIMPLIFIED.json @@ -573,6 +573,8 @@ "backup_options_page_title": "备份选项", "backup_setting_subtitle": "管理后台和前台上传设置", "backward": "后退", + "beta_sync": "测试版同步状态", + "beta_sync_subtitle": "管理新的同步系统", "biometric_auth_enabled": "生物识别身份验证已启用", "biometric_locked_out": "您被锁定在生物识别身份验证之外", "biometric_no_options": "没有可用的生物识别选项", @@ -590,7 +592,7 @@ "cache_settings_clear_cache_button": "清除缓存", "cache_settings_clear_cache_button_title": "清除应用缓存。在重新生成缓存之前,将显著影响应用的性能。", "cache_settings_duplicated_assets_clear_button": "清除", - "cache_settings_duplicated_assets_subtitle": "已加入黑名单的照片和视频", + "cache_settings_duplicated_assets_subtitle": "应用程序忽略的照片和视频", "cache_settings_duplicated_assets_title": "重复项目({count})", "cache_settings_statistics_album": "图库缩略图", "cache_settings_statistics_full": "完整图像", @@ -1051,6 +1053,9 @@ "haptic_feedback_switch": "启用振动反馈", "haptic_feedback_title": "振动反馈", "has_quota": "配额大小", + "hash_asset": "哈希项目", + "hashed_assets": "已哈希的项目", + "hashing": "正在哈希", "header_settings_add_header_tip": "添加标头", "header_settings_field_validator_msg": "设置不可为空", "header_settings_header_name_input": "标头名称", @@ -1083,6 +1088,7 @@ "host": "服务器", "hour": "时", "id": "ID", + "idle": "空闲", "ignore_icloud_photos": "忽略 iCloud 照片", "ignore_icloud_photos_description": "存储在 iCloud 中的照片不会上传至 Immich 服务器", "image": "图片", @@ -1165,7 +1171,9 @@ "list": "列表", "loading": "加载中", "loading_search_results_failed": "加载搜索结果失败", + "local": "本地", "local_asset_cast_failed": "无法投放未上传至服务器的项目", + "local_assets": "本地项目", "local_network": "本地网络", "local_network_sheet_info": "当使用指定的 Wi-Fi 网络时,应用程序将通过此 URL 访问服务器", "location_permission": "定位权限", @@ -1322,6 +1330,7 @@ "no_results": "无结果", "no_results_description": "尝试使用同义词或更通用的关键词", "no_shared_albums_message": "创建相册以共享照片和视频", + "no_uploads_in_progress": "没有正在进行的上传", "not_in_any_album": "不在任何相册中", "not_selected": "未选择", "note_apply_storage_label_to_previously_uploaded assets": "提示:要将存储标签应用于之前上传的项目,需要运行", @@ -1359,6 +1368,7 @@ "original": "原图", "other": "其它", "other_devices": "其它设备", + "other_entities": "其他实体", "other_variables": "其它变量", "owned": "我的", "owner": "所有者", @@ -1519,6 +1529,8 @@ "refreshing_faces": "正在面部重新识别", "refreshing_metadata": "正在刷新元数据", "regenerating_thumbnails": "正在重新生成缩略图", + "remote": "远程", + "remote_assets": "远程项目", "remove": "移除", "remove_assets_album_confirmation": "确定要从图库中移除{count, plural, one {#个项目} other {#个项目}}?", "remove_assets_shared_link_confirmation": "确定要从共享链接中移除{count, plural, one {#个项目} other {#个项目}}?", @@ -1556,11 +1568,15 @@ "reset_password": "重置密码", "reset_people_visibility": "重置人物识别", "reset_pin_code": "重置PIN码", + "reset_sqlite": "重置 SQLite 数据库", + "reset_sqlite_confirmation": "您确定要重置 SQLite 数据库吗?您需要注销并重新登录才能重新同步数据", + "reset_sqlite_success": "已成功重置 SQLite 数据库", "reset_to_default": "恢复默认值", "resolve_duplicates": "处理重复项", "resolved_all_duplicates": "处理所有重复项", "restore": "恢复", "restore_all": "恢复全部", + "restore_trash_action_prompt": "从回收站中恢复了 {count} 项", "restore_user": "恢复用户", "restored_asset": "已恢复项目", "resume": "继续", @@ -1569,6 +1585,7 @@ "role": "选择用户权限", "role_editor": "可编辑", "role_viewer": "仅查看", + "running": "正在运行", "save": "保存", "save_to_gallery": "保存到图库", "saved_api_key": "已保存的 API 密钥", @@ -1822,6 +1839,7 @@ "storage_quota": "存储配额", "storage_usage": "已用:{used}/{available}", "submit": "提交", + "success": "成功", "suggestions": "建议", "sunrise_on_the_beach": "海滩上的日出", "support": "支持", @@ -1831,6 +1849,8 @@ "sync": "同步", "sync_albums": "同步相册", "sync_albums_manual_subtitle": "将所有上传的视频和照片同步到选定的备份相册", + "sync_local": "同步本地", + "sync_remote": "同步远程", "sync_upload_album_setting_subtitle": "创建照片和视频并上传到 Immich 上的选定相册中", "tag": "标签", "tag_assets": "标记项目", @@ -1841,6 +1861,7 @@ "tag_updated": "已更新标签:{tag}", "tagged_assets": "{count, plural, one {# 个项目} other {# 个项目}}被加上标签", "tags": "标签", + "tap_to_run_job": "点击运行作业", "template": "模版", "theme": "主题", "theme_selection": "主题选项", diff --git a/mobile/drift_schemas/main/drift_schema_v4.json b/mobile/drift_schemas/main/drift_schema_v4.json index 82ef30adae..2488319a5e 100644 --- a/mobile/drift_schemas/main/drift_schema_v4.json +++ b/mobile/drift_schemas/main/drift_schema_v4.json @@ -1 +1 @@ -{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[2],"type":"index","data":{"on":2,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":5,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":6,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":8,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":9,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":10,"references":[2,9],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":5,"references":[3,4],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":6,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":9,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":10,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/mobile/lib/domain/models/asset/remote_asset.model.dart b/mobile/lib/domain/models/asset/remote_asset.model.dart index 4e4ce8f27f..df7e236ac0 100644 --- a/mobile/lib/domain/models/asset/remote_asset.model.dart +++ b/mobile/lib/domain/models/asset/remote_asset.model.dart @@ -16,7 +16,6 @@ class RemoteAsset extends BaseAsset { final DateTime? localDateTime; final String ownerId; final String? stackId; - final int stackCount; const RemoteAsset({ required this.id, @@ -36,7 +35,6 @@ class RemoteAsset extends BaseAsset { this.localDateTime, super.livePhotoVideoId, this.stackId, - this.stackCount = 0, }); @override @@ -64,7 +62,6 @@ class RemoteAsset extends BaseAsset { visibility: $visibility, localDateTime: ${localDateTime ?? ""}, stackId: ${stackId ?? ""}, - stackCount: $stackCount, checksum: $checksum, livePhotoVideoId: ${livePhotoVideoId ?? ""}, }'''; @@ -81,8 +78,7 @@ class RemoteAsset extends BaseAsset { thumbHash == other.thumbHash && visibility == other.visibility && localDateTime == other.localDateTime && - stackId == other.stackId && - stackCount == other.stackCount; + stackId == other.stackId; } @override @@ -94,8 +90,7 @@ class RemoteAsset extends BaseAsset { thumbHash.hashCode ^ visibility.hashCode ^ localDateTime.hashCode ^ - stackId.hashCode ^ - stackCount.hashCode; + stackId.hashCode; RemoteAsset copyWith({ String? id, @@ -115,7 +110,6 @@ class RemoteAsset extends BaseAsset { DateTime? localDateTime, String? livePhotoVideoId, String? stackId, - int? stackCount, }) { return RemoteAsset( id: id ?? this.id, @@ -135,7 +129,6 @@ class RemoteAsset extends BaseAsset { localDateTime: localDateTime ?? this.localDateTime, livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, stackId: stackId ?? this.stackId, - stackCount: stackCount ?? this.stackCount, ); } } diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift b/mobile/lib/infrastructure/entities/merged_asset.drift index 3dc7221c15..9778ba723b 100644 --- a/mobile/lib/infrastructure/entities/merged_asset.drift +++ b/mobile/lib/infrastructure/entities/merged_asset.drift @@ -1,76 +1,68 @@ import 'remote_asset.entity.dart'; -import 'local_asset.entity.dart'; import 'stack.entity.dart'; +import 'local_asset.entity.dart'; +import 'local_album.entity.dart'; +import 'local_album_asset.entity.dart'; -mergedAsset: SELECT * FROM -( - SELECT - rae.id as remote_id, - lae.id as local_id, - rae.name, - rae."type", - rae.created_at, - rae.updated_at, - rae.width, - rae.height, - rae.duration_in_seconds, - rae.is_favorite, - rae.thumb_hash, - rae.checksum, - rae.owner_id, - rae.live_photo_video_id, - 0 as orientation, - rae.stack_id, - COALESCE(stack_count.total_count, 0) AS stack_count - FROM - remote_asset_entity rae - LEFT JOIN - local_asset_entity lae ON rae.checksum = lae.checksum - LEFT JOIN - stack_entity se ON rae.stack_id = se.id - LEFT JOIN - (SELECT - stack_id, - COUNT(*) AS total_count - FROM remote_asset_entity - WHERE deleted_at IS NULL - AND visibility = 0 - AND stack_id IS NOT NULL - GROUP BY stack_id - ) AS stack_count ON rae.stack_id = stack_count.stack_id - WHERE - rae.deleted_at IS NULL - AND rae.visibility = 0 - AND rae.owner_id in ? - AND ( - rae.stack_id IS NULL - OR rae.id = se.primary_asset_id - ) - UNION ALL - SELECT - NULL as remote_id, - lae.id as local_id, - lae.name, - lae."type", - lae.created_at, - lae.updated_at, - lae.width, - lae.height, - lae.duration_in_seconds, - lae.is_favorite, - NULL as thumb_hash, - lae.checksum, - NULL as owner_id, - NULL as live_photo_video_id, - lae.orientation, - NULL as stack_id, - 0 AS stack_count - FROM - local_asset_entity lae - LEFT JOIN - remote_asset_entity rae ON rae.checksum = lae.checksum - WHERE - rae.id IS NULL +mergedAsset: +SELECT + rae.id as remote_id, + (SELECT lae.id FROM local_asset_entity lae WHERE lae.checksum = rae.checksum LIMIT 1) as local_id, + rae.name, + rae."type", + rae.created_at as created_at, + rae.updated_at, + rae.width, + rae.height, + rae.duration_in_seconds, + rae.is_favorite, + rae.thumb_hash, + rae.checksum, + rae.owner_id, + rae.live_photo_video_id, + 0 as orientation, + rae.stack_id +FROM + remote_asset_entity rae +LEFT JOIN + stack_entity se ON rae.stack_id = se.id +WHERE + rae.deleted_at IS NULL + AND rae.visibility = 0 -- timeline visibility + AND rae.owner_id in ? + AND ( + rae.stack_id IS NULL + OR rae.id = se.primary_asset_id + ) + +UNION ALL + +SELECT + NULL as remote_id, + lae.id as local_id, + lae.name, + lae."type", + lae.created_at as created_at, + lae.updated_at, + lae.width, + lae.height, + lae.duration_in_seconds, + lae.is_favorite, + NULL as thumb_hash, + lae.checksum, + NULL as owner_id, + NULL as live_photo_video_id, + lae.orientation, + NULL as stack_id +FROM + local_asset_entity lae +WHERE NOT EXISTS ( + SELECT 1 FROM remote_asset_entity rae WHERE rae.checksum = lae.checksum +) +AND EXISTS ( + SELECT 1 FROM local_album_asset_entity laa + INNER JOIN local_album_entity la on laa.album_id = la.id + WHERE laa.asset_id = lae.id AND la.backup_selection = 0 -- selected ) ORDER BY created_at DESC LIMIT $limit; @@ -85,17 +77,14 @@ SELECT FROM ( SELECT - rae.name, rae.created_at FROM remote_asset_entity rae - LEFT JOIN - local_asset_entity lae ON rae.checksum = lae.checksum LEFT JOIN stack_entity se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL - AND rae.visibility = 0 + AND rae.visibility = 0 -- timeline visibility AND rae.owner_id in ? AND ( rae.stack_id IS NULL @@ -103,14 +92,18 @@ FROM ) UNION ALL SELECT - lae.name, lae.created_at FROM local_asset_entity lae LEFT JOIN remote_asset_entity rae ON rae.checksum = lae.checksum + LEFT JOIN + local_album_asset_entity laa ON laa.asset_id = lae.id + LEFT JOIN + local_album_entity la ON la.id = laa.album_id WHERE rae.id IS NULL + AND la.backup_selection = 0 -- selected ) GROUP BY bucket_date ORDER BY bucket_date DESC; diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift.dart b/mobile/lib/infrastructure/entities/merged_asset.drift.dart index ac3db868e1..7f56b25d4e 100644 --- a/mobile/lib/infrastructure/entities/merged_asset.drift.dart +++ b/mobile/lib/infrastructure/entities/merged_asset.drift.dart @@ -3,24 +3,29 @@ import 'package:drift/drift.dart' as i0; import 'package:drift/internal/modular.dart' as i1; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart' as i2; -import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' - as i3; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart' + as i3; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' as i4; import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart' as i5; +import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart' + as i6; +import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart' + as i7; class MergedAssetDrift extends i1.ModularAccessor { MergedAssetDrift(i0.GeneratedDatabase db) : super(db); i0.Selectable mergedAsset(List var1, - {required i0.Limit limit}) { + {required MergedAsset$limit limit}) { var $arrayStartIndex = 1; final expandedvar1 = $expandVar($arrayStartIndex, var1.length); $arrayStartIndex += var1.length; - final generatedlimit = $write(limit, startIndex: $arrayStartIndex); + final generatedlimit = $write(limit(alias(this.localAssetEntity, 'lae')), + startIndex: $arrayStartIndex); $arrayStartIndex += generatedlimit.amountOfVariables; return customSelect( - 'SELECT * FROM (SELECT rae.id AS remote_id, lae.id AS local_id, rae.name, rae.type, rae.created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id, COALESCE(stack_count.total_count, 0) AS stack_count FROM remote_asset_entity AS rae LEFT JOIN local_asset_entity AS lae ON rae.checksum = lae.checksum LEFT JOIN stack_entity AS se ON rae.stack_id = se.id LEFT JOIN (SELECT stack_id, COUNT(*) AS total_count FROM remote_asset_entity WHERE deleted_at IS NULL AND visibility = 0 AND stack_id IS NOT NULL GROUP BY stack_id) AS stack_count ON rae.stack_id = stack_count.stack_id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar1) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id, 0 AS stack_count FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum WHERE rae.id IS NULL) ORDER BY created_at DESC ${generatedlimit.sql}', + 'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar1) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) ORDER BY created_at DESC ${generatedlimit.sql}', variables: [ for (var $ in var1) i0.Variable($), ...generatedlimit.introducedVariables @@ -29,12 +34,14 @@ class MergedAssetDrift extends i1.ModularAccessor { remoteAssetEntity, localAssetEntity, stackEntity, + localAlbumAssetEntity, + localAlbumEntity, ...generatedlimit.watchedTables, }).map((i0.QueryRow row) => MergedAssetResult( remoteId: row.readNullable('remote_id'), localId: row.readNullable('local_id'), name: row.read('name'), - type: i3.$RemoteAssetEntityTable.$convertertype + type: i4.$RemoteAssetEntityTable.$convertertype .fromSql(row.read('type')), createdAt: row.read('created_at'), updatedAt: row.read('updated_at'), @@ -48,7 +55,6 @@ class MergedAssetDrift extends i1.ModularAccessor { livePhotoVideoId: row.readNullable('live_photo_video_id'), orientation: row.read('orientation'), stackId: row.readNullable('stack_id'), - stackCount: row.read('stack_count'), )); } @@ -58,30 +64,39 @@ class MergedAssetDrift extends i1.ModularAccessor { final expandedvar2 = $expandVar($arrayStartIndex, var2.length); $arrayStartIndex += var2.length; return customSelect( - 'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.name, rae.created_at FROM remote_asset_entity AS rae LEFT JOIN local_asset_entity AS lae ON rae.checksum = lae.checksum LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar2) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.name, lae.created_at FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum WHERE rae.id IS NULL) GROUP BY bucket_date ORDER BY bucket_date DESC', + 'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.created_at FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar2) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.created_at FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum LEFT JOIN local_album_asset_entity AS laa ON laa.asset_id = lae.id LEFT JOIN local_album_entity AS la ON la.id = laa.album_id WHERE rae.id IS NULL AND la.backup_selection = 0) GROUP BY bucket_date ORDER BY bucket_date DESC', variables: [ i0.Variable(groupBy), for (var $ in var2) i0.Variable($) ], readsFrom: { remoteAssetEntity, - localAssetEntity, stackEntity, + localAssetEntity, + localAlbumAssetEntity, + localAlbumEntity, }).map((i0.QueryRow row) => MergedBucketResult( assetCount: row.read('asset_count'), bucketDate: row.read('bucket_date'), )); } - i3.$RemoteAssetEntityTable get remoteAssetEntity => + i4.$RemoteAssetEntityTable get remoteAssetEntity => i1.ReadDatabaseContainer(attachedDatabase) - .resultSet('remote_asset_entity'); - i4.$LocalAssetEntityTable get localAssetEntity => - i1.ReadDatabaseContainer(attachedDatabase) - .resultSet('local_asset_entity'); + .resultSet('remote_asset_entity'); i5.$StackEntityTable get stackEntity => i1.ReadDatabaseContainer(attachedDatabase) .resultSet('stack_entity'); + i3.$LocalAssetEntityTable get localAssetEntity => + i1.ReadDatabaseContainer(attachedDatabase) + .resultSet('local_asset_entity'); + i6.$LocalAlbumAssetEntityTable get localAlbumAssetEntity => + i1.ReadDatabaseContainer(attachedDatabase) + .resultSet( + 'local_album_asset_entity'); + i7.$LocalAlbumEntityTable get localAlbumEntity => + i1.ReadDatabaseContainer(attachedDatabase) + .resultSet('local_album_entity'); } class MergedAssetResult { @@ -101,7 +116,6 @@ class MergedAssetResult { final String? livePhotoVideoId; final int orientation; final String? stackId; - final int stackCount; MergedAssetResult({ this.remoteId, this.localId, @@ -119,10 +133,11 @@ class MergedAssetResult { this.livePhotoVideoId, required this.orientation, this.stackId, - required this.stackCount, }); } +typedef MergedAsset$limit = i0.Limit Function(i3.$LocalAssetEntityTable lae); + class MergedBucketResult { final int assetCount; final String bucketDate; diff --git a/mobile/lib/infrastructure/repositories/backup.repository.dart b/mobile/lib/infrastructure/repositories/backup.repository.dart index 99df206db5..aed7118e5a 100644 --- a/mobile/lib/infrastructure/repositories/backup.repository.dart +++ b/mobile/lib/infrastructure/repositories/backup.repository.dart @@ -50,7 +50,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { return query.get().then((rows) => rows.length); } - Future getRemainderCount() async { + Future getRemainderCount(String userId) async { final query = _db.localAlbumAssetEntity.selectOnly(distinct: true) ..addColumns([_db.localAlbumAssetEntity.assetId]) ..join([ @@ -74,7 +74,8 @@ class DriftBackupRepository extends DriftDatabaseRepository { ..where( _db.localAlbumEntity.backupSelection .equalsValue(BackupSelection.selected) & - _db.remoteAssetEntity.id.isNull() & + (_db.remoteAssetEntity.id.isNull() | + _db.remoteAssetEntity.ownerId.equals(userId).not()) & _db.localAlbumAssetEntity.assetId .isNotInQuery(_getExcludedSubquery()), ); @@ -82,7 +83,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { return query.get().then((rows) => rows.length); } - Future getBackupCount() async { + Future getBackupCount(String userId) async { final query = _db.localAlbumAssetEntity.selectOnly(distinct: true) ..addColumns( [_db.localAlbumAssetEntity.assetId], @@ -109,6 +110,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { _db.localAlbumEntity.backupSelection .equalsValue(BackupSelection.selected) & _db.remoteAssetEntity.id.isNotNull() & + _db.remoteAssetEntity.ownerId.equals(userId) & _db.localAlbumAssetEntity.assetId .isNotInQuery(_getExcludedSubquery()), ); @@ -116,7 +118,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { return query.get().then((rows) => rows.length); } - Future> getCandidates() async { + Future> getCandidates(String userId) async { final selectedAlbumIds = _db.localAlbumEntity.selectOnly(distinct: true) ..addColumns([_db.localAlbumEntity.id]) ..where( @@ -141,6 +143,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { ..addColumns([_db.remoteAssetEntity.checksum]) ..where( _db.remoteAssetEntity.checksum.equalsExp(lae.checksum) & + _db.remoteAssetEntity.ownerId.equals(userId) & lae.checksum.isNotNull(), ), ) & diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index ced148f855..5cbbb3b4f4 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -4,8 +4,8 @@ import 'package:drift/drift.dart'; import 'package:drift_flutter/drift_flutter.dart'; import 'package:flutter/foundation.dart'; import 'package:immich_mobile/domain/interfaces/db.interface.dart'; -import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; import 'package:immich_mobile/infrastructure/entities/asset_face.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; @@ -97,7 +97,9 @@ class Drift extends $Drift implements IDatabaseRepository { await m.alterTable(TableMigration(v3.stackEntity)); }, from3To4: (m, v4) async { + // Thumbnail path column got removed from person_entity await m.alterTable(TableMigration(v4.personEntity)); + // asset_face_entity is added await m.create(v4.assetFaceEntity); }, ), diff --git a/mobile/lib/infrastructure/repositories/db.repository.drift.dart b/mobile/lib/infrastructure/repositories/db.repository.drift.dart index 7b722dfff6..f5962f09ab 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.drift.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.drift.dart @@ -5,17 +5,17 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i1; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' as i2; -import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart' - as i3; import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart' + as i3; +import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart' as i4; -import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart' - as i5; -import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart' - as i6; import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart' - as i7; + as i5; import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart' + as i6; +import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart' + as i7; +import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart' as i8; import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart' as i9; @@ -43,17 +43,17 @@ abstract class $Drift extends i0.GeneratedDatabase { late final i1.$UserEntityTable userEntity = i1.$UserEntityTable(this); late final i2.$RemoteAssetEntityTable remoteAssetEntity = i2.$RemoteAssetEntityTable(this); - late final i3.$LocalAssetEntityTable localAssetEntity = - i3.$LocalAssetEntityTable(this); - late final i4.$StackEntityTable stackEntity = i4.$StackEntityTable(this); - late final i5.$UserMetadataEntityTable userMetadataEntity = - i5.$UserMetadataEntityTable(this); - late final i6.$PartnerEntityTable partnerEntity = - i6.$PartnerEntityTable(this); - late final i7.$LocalAlbumEntityTable localAlbumEntity = - i7.$LocalAlbumEntityTable(this); - late final i8.$LocalAlbumAssetEntityTable localAlbumAssetEntity = - i8.$LocalAlbumAssetEntityTable(this); + late final i3.$StackEntityTable stackEntity = i3.$StackEntityTable(this); + late final i4.$LocalAssetEntityTable localAssetEntity = + i4.$LocalAssetEntityTable(this); + late final i5.$LocalAlbumEntityTable localAlbumEntity = + i5.$LocalAlbumEntityTable(this); + late final i6.$LocalAlbumAssetEntityTable localAlbumAssetEntity = + i6.$LocalAlbumAssetEntityTable(this); + late final i7.$UserMetadataEntityTable userMetadataEntity = + i7.$UserMetadataEntityTable(this); + late final i8.$PartnerEntityTable partnerEntity = + i8.$PartnerEntityTable(this); late final i9.$RemoteExifEntityTable remoteExifEntity = i9.$RemoteExifEntityTable(this); late final i10.$RemoteAlbumEntityTable remoteAlbumEntity = @@ -77,15 +77,15 @@ abstract class $Drift extends i0.GeneratedDatabase { List get allSchemaEntities => [ userEntity, remoteAssetEntity, - localAssetEntity, stackEntity, - i3.idxLocalAssetChecksum, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + i4.idxLocalAssetChecksum, i2.uQRemoteAssetOwnerChecksum, i2.idxRemoteAssetChecksum, userMetadataEntity, partnerEntity, - localAlbumEntity, - localAlbumAssetEntity, remoteExifEntity, remoteAlbumEntity, remoteAlbumAssetEntity, @@ -113,6 +113,22 @@ abstract class $Drift extends i0.GeneratedDatabase { i0.TableUpdate('stack_entity', kind: i0.UpdateKind.delete), ], ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName('local_asset_entity', + limitUpdateKind: i0.UpdateKind.delete), + result: [ + i0.TableUpdate('local_album_asset_entity', + kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName('local_album_entity', + limitUpdateKind: i0.UpdateKind.delete), + result: [ + i0.TableUpdate('local_album_asset_entity', + kind: i0.UpdateKind.delete), + ], + ), i0.WritePropagation( on: i0.TableUpdateQuery.onTableName('user_entity', limitUpdateKind: i0.UpdateKind.delete), @@ -135,22 +151,6 @@ abstract class $Drift extends i0.GeneratedDatabase { i0.TableUpdate('partner_entity', kind: i0.UpdateKind.delete), ], ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('local_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('local_album_asset_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('local_album_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('local_album_asset_entity', - kind: i0.UpdateKind.delete), - ], - ), i0.WritePropagation( on: i0.TableUpdateQuery.onTableName('remote_asset_entity', limitUpdateKind: i0.UpdateKind.delete), @@ -260,18 +260,18 @@ class $DriftManager { i1.$$UserEntityTableTableManager(_db, _db.userEntity); i2.$$RemoteAssetEntityTableTableManager get remoteAssetEntity => i2.$$RemoteAssetEntityTableTableManager(_db, _db.remoteAssetEntity); - i3.$$LocalAssetEntityTableTableManager get localAssetEntity => - i3.$$LocalAssetEntityTableTableManager(_db, _db.localAssetEntity); - i4.$$StackEntityTableTableManager get stackEntity => - i4.$$StackEntityTableTableManager(_db, _db.stackEntity); - i5.$$UserMetadataEntityTableTableManager get userMetadataEntity => - i5.$$UserMetadataEntityTableTableManager(_db, _db.userMetadataEntity); - i6.$$PartnerEntityTableTableManager get partnerEntity => - i6.$$PartnerEntityTableTableManager(_db, _db.partnerEntity); - i7.$$LocalAlbumEntityTableTableManager get localAlbumEntity => - i7.$$LocalAlbumEntityTableTableManager(_db, _db.localAlbumEntity); - i8.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i8 + i3.$$StackEntityTableTableManager get stackEntity => + i3.$$StackEntityTableTableManager(_db, _db.stackEntity); + i4.$$LocalAssetEntityTableTableManager get localAssetEntity => + i4.$$LocalAssetEntityTableTableManager(_db, _db.localAssetEntity); + i5.$$LocalAlbumEntityTableTableManager get localAlbumEntity => + i5.$$LocalAlbumEntityTableTableManager(_db, _db.localAlbumEntity); + i6.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i6 .$$LocalAlbumAssetEntityTableTableManager(_db, _db.localAlbumAssetEntity); + i7.$$UserMetadataEntityTableTableManager get userMetadataEntity => + i7.$$UserMetadataEntityTableTableManager(_db, _db.userMetadataEntity); + i8.$$PartnerEntityTableTableManager get partnerEntity => + i8.$$PartnerEntityTableTableManager(_db, _db.partnerEntity); i9.$$RemoteExifEntityTableTableManager get remoteExifEntity => i9.$$RemoteExifEntityTableTableManager(_db, _db.remoteExifEntity); i10.$$RemoteAlbumEntityTableTableManager get remoteAlbumEntity => diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index d8c35707ed..7f179fb5f0 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.steps.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.steps.dart @@ -1273,15 +1273,15 @@ final class Schema4 extends i0.VersionedSchema { late final List entities = [ userEntity, remoteAssetEntity, - localAssetEntity, stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, idxLocalAssetChecksum, uQRemoteAssetOwnerChecksum, idxRemoteAssetChecksum, userMetadataEntity, partnerEntity, - localAlbumEntity, - localAlbumAssetEntity, remoteExifEntity, remoteAlbumEntity, remoteAlbumAssetEntity, @@ -1342,6 +1342,24 @@ final class Schema4 extends i0.VersionedSchema { attachedDatabase: database, ), alias: null); + late final Shape3 stackEntity = Shape3( + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(id)', + ], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_75, + ], + attachedDatabase: database, + ), + alias: null); late final Shape2 localAssetEntity = Shape2( source: i0.VersionedTable( entityName: 'local_asset_entity', @@ -1366,9 +1384,9 @@ final class Schema4 extends i0.VersionedSchema { attachedDatabase: database, ), alias: null); - late final Shape3 stackEntity = Shape3( + late final Shape6 localAlbumEntity = Shape6( source: i0.VersionedTable( - entityName: 'stack_entity', + entityName: 'local_album_entity', withoutRowId: true, isStrict: true, tableConstraints: [ @@ -1376,10 +1394,26 @@ final class Schema4 extends i0.VersionedSchema { ], columns: [ _column_0, - _column_9, + _column_1, _column_5, - _column_15, - _column_75, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null); + late final Shape7 localAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: [ + 'PRIMARY KEY(asset_id, album_id)', + ], + columns: [ + _column_34, + _column_35, ], attachedDatabase: database, ), @@ -1423,40 +1457,6 @@ final class Schema4 extends i0.VersionedSchema { attachedDatabase: database, ), alias: null); - late final Shape6 localAlbumEntity = Shape6( - source: i0.VersionedTable( - entityName: 'local_album_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_5, - _column_31, - _column_32, - _column_33, - ], - attachedDatabase: database, - ), - alias: null); - late final Shape7 localAlbumAssetEntity = Shape7( - source: i0.VersionedTable( - entityName: 'local_album_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, album_id)', - ], - columns: [ - _column_34, - _column_35, - ], - attachedDatabase: database, - ), - alias: null); late final Shape8 remoteExifEntity = Shape8( source: i0.VersionedTable( entityName: 'remote_exif_entity', diff --git a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart index 231fbc16bb..e7c5379303 100644 --- a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart @@ -32,49 +32,21 @@ class RemoteAssetRepository extends DriftDatabaseRepository { } Stream watchAsset(String id) { - final stackCountRef = _db.stackEntity.id.count(); - final query = _db.remoteAssetEntity.select().addColumns([ _db.localAssetEntity.id, - _db.stackEntity.primaryAssetId, - stackCountRef, ]).join([ leftOuterJoin( _db.localAssetEntity, _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), useColumns: false, ), - leftOuterJoin( - _db.stackEntity, - _db.stackEntity.primaryAssetId.equalsExp(_db.remoteAssetEntity.id), - useColumns: false, - ), - leftOuterJoin( - _db.remoteAssetEntity.createAlias('stacked_assets'), - _db.stackEntity.id.equalsExp( - _db.remoteAssetEntity.createAlias('stacked_assets').stackId, - ), - useColumns: false, - ), ]) ..where(_db.remoteAssetEntity.id.equals(id)) - ..groupBy([ - _db.remoteAssetEntity.id, - _db.localAssetEntity.id, - _db.stackEntity.primaryAssetId, - ]) ..limit(1); return query.map((row) { final asset = row.readTable(_db.remoteAssetEntity).toDto(); - final primaryAssetId = row.read(_db.stackEntity.primaryAssetId); - final stackCount = - primaryAssetId == id ? (row.read(stackCountRef) ?? 0) : 0; - - return asset.copyWith( - localId: row.read(_db.localAssetEntity.id), - stackCount: stackCount, - ); + return asset.copyWith(localId: row.read(_db.localAssetEntity.id)); }).watchSingleOrNull(); } diff --git a/mobile/lib/infrastructure/repositories/timeline.repository.dart b/mobile/lib/infrastructure/repositories/timeline.repository.dart index 7bbae9a80a..2916993a9b 100644 --- a/mobile/lib/infrastructure/repositories/timeline.repository.dart +++ b/mobile/lib/infrastructure/repositories/timeline.repository.dart @@ -71,7 +71,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { required int count, }) { return _db.mergedAssetDrift - .mergedAsset(userIds, limit: Limit(count, offset)) + .mergedAsset(userIds, limit: (_) => Limit(count, offset)) .map( (row) => row.remoteId != null && row.ownerId != null ? RemoteAsset( @@ -90,7 +90,6 @@ class DriftTimelineRepository extends DriftDatabaseRepository { durationInSeconds: row.durationInSeconds, livePhotoVideoId: row.livePhotoVideoId, stackId: row.stackId, - stackCount: row.stackCount, ) : LocalAsset( id: row.localId!, @@ -142,6 +141,11 @@ class DriftTimelineRepository extends DriftDatabaseRepository { _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), useColumns: false, ), + leftOuterJoin( + _db.remoteAssetEntity, + _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum), + useColumns: false, + ), ]) ..addColumns([assetCountExp, dateExp]) ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 9f39a89b33..b62fb180e5 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -203,6 +203,19 @@ class ImmichAppState extends ConsumerState ), progressBar: true, ); + + FileDownloader().configureNotificationForGroup( + kManualUploadGroup, + running: TaskNotification( + 'uploading_media'.tr(), + '${'file_name'.tr()}: {displayName}', + ), + complete: TaskNotification( + 'upload_finished'.tr(), + '${'file_name'.tr()}: {displayName}', + ), + progressBar: true, + ); } Future _deepLinkBuilder(PlatformDeepLink deepLink) async { diff --git a/mobile/lib/pages/backup/drift_backup.page.dart b/mobile/lib/pages/backup/drift_backup.page.dart index 7780413399..08b09250e1 100644 --- a/mobile/lib/pages/backup/drift_backup.page.dart +++ b/mobile/lib/pages/backup/drift_backup.page.dart @@ -9,6 +9,7 @@ import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/backup/backup_toggle_button.widget.dart'; import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/backup/backup_info_card.dart'; @@ -24,12 +25,24 @@ class _DriftBackupPageState extends ConsumerState { @override void initState() { super.initState(); - ref.read(driftBackupProvider.notifier).getBackupStatus(); + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id); } Future startBackup() async { - await ref.read(driftBackupProvider.notifier).getBackupStatus(); - await ref.read(driftBackupProvider.notifier).backup(); + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await ref + .read(driftBackupProvider.notifier) + .getBackupStatus(currentUser.id); + await ref.read(driftBackupProvider.notifier).backup(currentUser.id); } Future stopBackup() async { @@ -207,7 +220,13 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { trailing: ElevatedButton( onPressed: () async { await context.pushRoute(const DriftBackupAlbumSelectionRoute()); - ref.read(driftBackupProvider.notifier).getBackupStatus(); + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + ref + .read(driftBackupProvider.notifier) + .getBackupStatus(currentUser.id); }, child: const Text( "select", diff --git a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart index 18d3ee1156..f0f3bedaab 100644 --- a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart +++ b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart @@ -4,17 +4,17 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart'; - import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; -import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart'; -import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/widgets/common/search_field.dart'; +import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; @RoutePage() class DriftBackupAlbumSelectionPage extends ConsumerStatefulWidget { @@ -92,7 +92,15 @@ class _DriftBackupAlbumSelectionPageState if (didPop && !_hasPopped) { _hasPopped = true; - await ref.read(driftBackupProvider.notifier).getBackupStatus(); + super.initState(); + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await ref + .read(driftBackupProvider.notifier) + .getBackupStatus(currentUser.id); final currentTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount)); @@ -107,7 +115,7 @@ class _DriftBackupAlbumSelectionPageState final backupNotifier = ref.read(driftBackupProvider.notifier); backupNotifier.cancel().then((_) { - backupNotifier.backup(); + backupNotifier.backup(currentUser.id); }); } } diff --git a/mobile/lib/pages/common/change_experience.page.dart b/mobile/lib/pages/common/change_experience.page.dart index a8569b25a0..3cd545ea33 100644 --- a/mobile/lib/pages/common/change_experience.page.dart +++ b/mobile/lib/pages/common/change_experience.page.dart @@ -2,10 +2,14 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/providers/background_sync.provider.dart'; +import 'package:immich_mobile/providers/backup/backup.provider.dart'; +import 'package:immich_mobile/providers/backup/manual_upload.provider.dart'; import 'package:immich_mobile/providers/gallery_permission.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; @@ -43,6 +47,17 @@ class _ChangeExperiencePageState extends ConsumerState { albumNotifier.dispose(); } + // Cancel uploads + await Store.put(StoreKey.backgroundBackup, false); + ref.read(backupProvider.notifier).configureBackgroundBackup( + enabled: false, + onBatteryInfo: () {}, + onError: (_) {}, + ); + ref.read(backupProvider.notifier).setAutoBackup(false); + ref.read(backupProvider.notifier).cancelBackup(); + ref.read(manualUploadProvider.notifier).cancelBackup(); + // Start listening to new websocket events ref.read(websocketProvider.notifier).stopListenToOldEvents(); ref.read(websocketProvider.notifier).startListeningToBetaEvents(); diff --git a/mobile/lib/pages/common/tab_shell.page.dart b/mobile/lib/pages/common/tab_shell.page.dart index b0be136a15..418c8b9e39 100644 --- a/mobile/lib/pages/common/tab_shell.page.dart +++ b/mobile/lib/pages/common/tab_shell.page.dart @@ -11,6 +11,7 @@ import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/search/search_input_focus.provider.dart'; import 'package:immich_mobile/providers/tab.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; @@ -38,7 +39,14 @@ class _TabShellPageState extends ConsumerState { await runNewSync(ref, full: true).then((_) async { if (isEnableBackup) { - await ref.read(driftBackupProvider.notifier).handleBackupResume(); + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await ref + .read(driftBackupProvider.notifier) + .handleBackupResume(currentUser.id); } }); }); diff --git a/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart index 66467e44a3..d67c952b1a 100644 --- a/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart @@ -1,16 +1,50 @@ import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; class UploadActionButton extends ConsumerWidget { - const UploadActionButton({super.key}); + final ActionSource source; + + const UploadActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).upload(source); + + final successMessage = 'upload_action_prompt'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success + ? successMessage + : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + + ref.read(multiSelectProvider.notifier).reset(); + } + } @override Widget build(BuildContext context, WidgetRef ref) { return BaseActionButton( iconData: Icons.backup_outlined, label: "upload".t(context: context), + onPressed: () => _onTap(context, ref), ); } } diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart index cb4e02b56c..5b86258e72 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart @@ -6,11 +6,7 @@ class StackChildrenNotifier extends AutoDisposeFamilyAsyncNotifier, BaseAsset?> { @override Future> build(BaseAsset? asset) async { - if (asset == null || - asset is! RemoteAsset || - asset.stackId == null || - // The stackCount check is to ensure we only fetch stacks for timelines that have stacks - asset.stackCount == 0) { + if (asset == null || asset is! RemoteAsset || asset.stackId == null) { return const []; } diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart index d35a315f48..65bab5acb8 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart @@ -5,6 +5,7 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; @@ -37,6 +38,8 @@ class ViewerBottomBar extends ConsumerWidget { final actions = [ const ShareActionButton(source: ActionSource.viewer), + if (asset.isLocalOnly) + const UploadActionButton(source: ActionSource.viewer), if (asset.hasRemote && isOwner) const ArchiveActionButton(source: ActionSource.viewer), ]; @@ -50,31 +53,30 @@ class ViewerBottomBar extends ConsumerWidget { duration: Durations.short4, child: isSheetOpen ? const SizedBox.shrink() - : SafeArea( - child: Theme( - data: context.themeData.copyWith( - iconTheme: - const IconThemeData(size: 22, color: Colors.white), - textTheme: context.themeData.textTheme.copyWith( - labelLarge: - context.themeData.textTheme.labelLarge?.copyWith( - color: Colors.white, - ), + : Theme( + data: context.themeData.copyWith( + iconTheme: + const IconThemeData(size: 22, color: Colors.white), + textTheme: context.themeData.textTheme.copyWith( + labelLarge: + context.themeData.textTheme.labelLarge?.copyWith( + color: Colors.white, ), ), - child: Container( - height: asset.isVideo ? 160 : 80, - color: Colors.black.withAlpha(125), - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - if (asset.isVideo) const VideoControls(), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: actions, - ), - ], - ), + ), + child: Container( + height: context.padding.bottom + (asset.isVideo ? 160 : 80), + color: Colors.black.withAlpha(125), + padding: EdgeInsets.only(bottom: context.padding.bottom), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (asset.isVideo) const VideoControls(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: actions, + ), + ], ), ), ), diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart index 0e426fde6d..9111469629 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -63,7 +63,7 @@ class AssetDetailBottomSheet extends ConsumerWidget { ], if (asset.storage == AssetState.local) ...[ const DeleteLocalActionButton(source: ActionSource.viewer), - const UploadActionButton(), + const UploadActionButton(source: ActionSource.timeline), ], ]; diff --git a/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart index 17880da3e7..2accf465ed 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart @@ -27,6 +27,26 @@ import 'package:logging/logging.dart'; import 'package:native_video_player/native_video_player.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; +bool _isCurrentAsset( + BaseAsset asset, + BaseAsset? currentAsset, +) { + if (asset is RemoteAsset) { + return switch (currentAsset) { + RemoteAsset remoteAsset => remoteAsset.id == asset.id, + LocalAsset localAsset => localAsset.remoteId == asset.id, + _ => false, + }; + } else if (asset is LocalAsset) { + return switch (currentAsset) { + RemoteAsset remoteAsset => remoteAsset.localId == asset.id, + LocalAsset localAsset => localAsset.id == asset.id, + _ => false, + }; + } + return false; +} + class NativeVideoViewer extends HookConsumerWidget { final BaseAsset asset; final bool showControls; @@ -56,7 +76,7 @@ class NativeVideoViewer extends HookConsumerWidget { // If the swipe is completed, `isCurrent` will be true for video B after a delay. // If the swipe is canceled, `currentAsset` will not have changed and video A will continue to play. final currentAsset = useState(ref.read(currentAssetNotifier)); - final isCurrent = currentAsset.value == asset; + final isCurrent = _isCurrentAsset(asset, currentAsset.value); // Used to show the placeholder during hero animations for remote videos to avoid a stutter final isVisible = useState(Platform.isIOS && asset.hasLocal); diff --git a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart index d2c690e125..709da83d96 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart @@ -53,7 +53,7 @@ class ArchiveBottomSheet extends ConsumerWidget { ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), - const UploadActionButton(), + const UploadActionButton(source: ActionSource.timeline), ], ], ); diff --git a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart index d54b71e071..85b8dc959b 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart @@ -53,7 +53,7 @@ class FavoriteBottomSheet extends ConsumerWidget { ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), - const UploadActionButton(), + const UploadActionButton(source: ActionSource.timeline), ], ], ); diff --git a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart index 5250d55449..9b98e269f2 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart @@ -56,7 +56,7 @@ class GeneralBottomSheet extends ConsumerWidget { ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), - const UploadActionButton(), + const UploadActionButton(source: ActionSource.timeline), ], ], ); diff --git a/mobile/lib/presentation/widgets/bottom_sheet/local_album_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/local_album_bottom_sheet.widget.dart index 2ad0fe7485..b1e87dfaea 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/local_album_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/local_album_bottom_sheet.widget.dart @@ -18,7 +18,7 @@ class LocalAlbumBottomSheet extends ConsumerWidget { actions: [ ShareActionButton(source: ActionSource.timeline), DeleteLocalActionButton(source: ActionSource.timeline), - UploadActionButton(), + UploadActionButton(source: ActionSource.timeline), ], ); } diff --git a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart index 3fb882a296..3fbca04967 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart @@ -56,7 +56,7 @@ class RemoteAlbumBottomSheet extends ConsumerWidget { ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), - const UploadActionButton(), + const UploadActionButton(source: ActionSource.timeline), ], RemoveFromAlbumActionButton( source: ActionSource.timeline, diff --git a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart index ce3d39629f..b059f5feaa 100644 --- a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart +++ b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart @@ -54,7 +54,7 @@ class ThumbnailTile extends ConsumerWidget { : const BoxDecoration(); final hasStack = - asset is RemoteAsset && (asset as RemoteAsset).stackCount > 0; + asset is RemoteAsset && (asset as RemoteAsset).stackId != null; return Stack( children: [ @@ -86,9 +86,7 @@ class ThumbnailTile extends ConsumerWidget { right: 10.0, top: asset.isVideo ? 24.0 : 6.0, ), - child: _StackIndicator( - stackCount: (asset as RemoteAsset).stackCount, - ), + child: const _TileOverlayIcon(Icons.burst_mode_rounded), ), ), if (asset.isVideo) @@ -198,40 +196,6 @@ class _SelectionIndicator extends StatelessWidget { } } -class _StackIndicator extends StatelessWidget { - final int stackCount; - - const _StackIndicator({required this.stackCount}); - - @override - Widget build(BuildContext context) { - return Row( - spacing: 3, - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.end, - // CrossAxisAlignment.start looks more centered vertically than CrossAxisAlignment.center - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - stackCount.toString(), - style: const TextStyle( - color: Colors.white, - fontSize: 12, - fontWeight: FontWeight.bold, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - ), - ], - ), - ), - const _TileOverlayIcon(Icons.burst_mode_rounded), - ], - ); - } -} - class _VideoIndicator extends StatelessWidget { final Duration duration; const _VideoIndicator(this.duration); diff --git a/mobile/lib/providers/app_life_cycle.provider.dart b/mobile/lib/providers/app_life_cycle.provider.dart index 3be46d2fbd..647ada4a64 100644 --- a/mobile/lib/providers/app_life_cycle.provider.dart +++ b/mobile/lib/providers/app_life_cycle.provider.dart @@ -19,6 +19,7 @@ import 'package:immich_mobile/providers/memory.provider.dart'; import 'package:immich_mobile/providers/notification_permission.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/tab.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/services/background.service.dart'; @@ -110,7 +111,14 @@ class AppLifeCycleNotifier extends StateNotifier { .getSetting(AppSettingsEnum.enableBackup); if (isEnableBackup) { - await _ref.read(driftBackupProvider.notifier).handleBackupResume(); + final currentUser = _ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await _ref + .read(driftBackupProvider.notifier) + .handleBackupResume(currentUser.id); } }); } catch (e, stackTrace) { diff --git a/mobile/lib/providers/backup/drift_backup.provider.dart b/mobile/lib/providers/backup/drift_backup.provider.dart index 3a2c7fd9ce..45941639a0 100644 --- a/mobile/lib/providers/backup/drift_backup.provider.dart +++ b/mobile/lib/providers/backup/drift_backup.provider.dart @@ -329,11 +329,11 @@ class ExpBackupNotifier extends StateNotifier { ); } - Future getBackupStatus() async { + Future getBackupStatus(String userId) async { final [totalCount, backupCount, remainderCount] = await Future.wait([ _backupService.getTotalCount(), - _backupService.getBackupCount(), - _backupService.getRemainderCount(), + _backupService.getBackupCount(userId), + _backupService.getRemainderCount(userId), ]); state = state.copyWith( @@ -343,8 +343,8 @@ class ExpBackupNotifier extends StateNotifier { ); } - Future backup() { - return _backupService.backup(_updateEnqueueCount); + Future backup(String userId) { + return _backupService.backup(userId, _updateEnqueueCount); } void _updateEnqueueCount(EnqueueStatus status) { @@ -379,11 +379,11 @@ class ExpBackupNotifier extends StateNotifier { } } - Future handleBackupResume() async { + Future handleBackupResume(String userId) async { final tasks = await FileDownloader().allTasks(group: kBackupGroup); if (tasks.isEmpty) { // Start a new backup queue - await backup(); + await backup(userId); } debugPrint("Tasks to resume: ${tasks.length}"); diff --git a/mobile/lib/providers/infrastructure/action.provider.dart b/mobile/lib/providers/infrastructure/action.provider.dart index 90a5e6da36..7321cd2480 100644 --- a/mobile/lib/providers/infrastructure/action.provider.dart +++ b/mobile/lib/providers/infrastructure/action.provider.dart @@ -5,6 +5,7 @@ import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asse import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/action.service.dart'; +import 'package:immich_mobile/services/drift_backup.service.dart'; import 'package:immich_mobile/services/timeline.service.dart'; import 'package:logging/logging.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -32,12 +33,14 @@ class ActionResult { class ActionNotifier extends Notifier { final Logger _logger = Logger('ActionNotifier'); late ActionService _service; + late DriftBackupService _backupService; ActionNotifier() : super(); @override void build() { _service = ref.watch(actionServiceProvider); + _backupService = ref.watch(driftBackupServiceProvider); } List _getRemoteIdsForSource(ActionSource source) { @@ -390,6 +393,21 @@ class ActionNotifier extends Notifier { ); } } + + Future upload(ActionSource source) async { + final assets = _getAssets(source).whereType().toList(); + try { + await _backupService.manualBackup(assets); + return ActionResult(count: assets.length, success: true); + } catch (error, stack) { + _logger.severe('Failed manually upload assets', error, stack); + return ActionResult( + count: assets.length, + success: false, + error: error.toString(), + ); + } + } } extension on Iterable { diff --git a/mobile/lib/repositories/upload.repository.dart b/mobile/lib/repositories/upload.repository.dart index b98eece656..6d75313a24 100644 --- a/mobile/lib/repositories/upload.repository.dart +++ b/mobile/lib/repositories/upload.repository.dart @@ -20,6 +20,11 @@ class UploadRepository { taskStatusCallback: (update) => onUploadStatus?.call(update), taskProgressCallback: (update) => onTaskProgress?.call(update), ); + FileDownloader().registerCallbacks( + group: kManualUploadGroup, + taskStatusCallback: (_) => {}, + taskProgressCallback: (_) => {}, + ); } void enqueueAll(List tasks) { diff --git a/mobile/lib/services/drift_backup.service.dart b/mobile/lib/services/drift_backup.service.dart index 2f51c261fb..ab878969a0 100644 --- a/mobile/lib/services/drift_backup.service.dart +++ b/mobile/lib/services/drift_backup.service.dart @@ -26,6 +26,7 @@ final driftBackupServiceProvider = Provider( ), ); +// TODO: Rename to UploadService after removing Isar class DriftBackupService { DriftBackupService( this._backupRepository, @@ -48,20 +49,39 @@ class DriftBackupService { return _backupRepository.getTotalCount(); } - Future getRemainderCount() { - return _backupRepository.getRemainderCount(); + Future getRemainderCount(String userId) { + return _backupRepository.getRemainderCount(userId); } - Future getBackupCount() { - return _backupRepository.getBackupCount(); + Future getBackupCount(String userId) { + return _backupRepository.getBackupCount(userId); + } + + Future manualBackup(List localAssets) async { + List tasks = []; + for (final asset in localAssets) { + final task = await _getUploadTask( + asset, + group: kManualUploadGroup, + priority: 1, // High priority after upload motion photo part + ); + if (task != null) { + tasks.add(task); + } + } + + if (tasks.isNotEmpty) { + _uploadService.enqueueTasks(tasks); + } } Future backup( + String userId, void Function(EnqueueStatus status) onEnqueueTasks, ) async { shouldCancel = false; - final candidates = await _backupRepository.getCandidates(); + final candidates = await _backupRepository.getCandidates(userId); if (candidates.isEmpty) { return; } @@ -146,7 +166,11 @@ class DriftBackupService { } } - Future _getUploadTask(LocalAsset asset) async { + Future _getUploadTask( + LocalAsset asset, { + String group = kBackupGroup, + int? priority, + }) async { final entity = await _storageRepository.getAssetEntityForAsset(asset); if (entity == null) { return null; @@ -192,7 +216,8 @@ class DriftBackupService { originalFileName: originalFileName, deviceAssetId: asset.id, metadata: metadata, - group: kBackupGroup, + group: group, + priority: priority, ); } diff --git a/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart b/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart index 3d37fb102b..e69717d3a4 100644 --- a/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart +++ b/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:drift/drift.dart' as drift_db; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -10,6 +12,9 @@ import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/memory.provider.dart'; import 'package:immich_mobile/providers/sync_status.provider.dart'; import 'package:immich_mobile/widgets/settings/beta_sync_settings/entity_count_tile.dart'; +import 'package:path/path.dart' as path; +import 'package:path_provider/path_provider.dart'; +import 'package:share_plus/share_plus.dart'; class BetaSyncSettings extends HookConsumerWidget { const BetaSyncSettings({ @@ -69,6 +74,67 @@ class BetaSyncSettings extends HookConsumerWidget { }); } + Future exportDatabase() async { + try { + // WAL Checkpoint to ensure all changes are written to the database + await ref + .read(driftProvider) + .customStatement("pragma wal_checkpoint(truncate)"); + final documentsDir = await getApplicationDocumentsDirectory(); + final dbFile = File(path.join(documentsDir.path, 'immich.sqlite')); + + if (!await dbFile.exists()) { + if (context.mounted) { + context.scaffoldMessenger.showSnackBar( + SnackBar( + content: Text("Database file not found".t(context: context)), + ), + ); + } + return; + } + + final timestamp = DateTime.now().millisecondsSinceEpoch; + final exportFile = File( + path.join( + documentsDir.path, + 'immich_export_$timestamp.sqlite', + ), + ); + + await dbFile.copy(exportFile.path); + + await Share.shareXFiles( + [XFile(exportFile.path)], + text: 'Immich Database Export', + ); + + Future.delayed(const Duration(seconds: 30), () async { + if (await exportFile.exists()) { + await exportFile.delete(); + } + }); + + if (context.mounted) { + context.scaffoldMessenger.showSnackBar( + SnackBar( + content: + Text("Database exported successfully".t(context: context)), + ), + ); + } + } catch (e) { + if (context.mounted) { + context.scaffoldMessenger.showSnackBar( + SnackBar( + content: + Text("Failed to export database: $e".t(context: context)), + ), + ); + } + } + } + return FutureBuilder>( future: loadCounts(), builder: (context, snapshot) { @@ -232,6 +298,19 @@ class BetaSyncSettings extends HookConsumerWidget { ), const SizedBox(height: 24), _SectionHeaderText(text: "actions".t(context: context)), + ListTile( + title: Text( + "export_database".t(context: context), + style: const TextStyle( + fontWeight: FontWeight.w500, + ), + ), + subtitle: Text( + "export_database_description".t(context: context), + ), + leading: const Icon(Icons.download), + onTap: exportDatabase, + ), ListTile( title: Text( "reset_sqlite".t(context: context), diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index b20a0694c5..2d764aa153 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -479,6 +479,7 @@ Class | Method | HTTP request | Description - [SyncAssetFaceDeleteV1](doc//SyncAssetFaceDeleteV1.md) - [SyncAssetFaceV1](doc//SyncAssetFaceV1.md) - [SyncAssetV1](doc//SyncAssetV1.md) + - [SyncAuthUserV1](doc//SyncAuthUserV1.md) - [SyncEntityType](doc//SyncEntityType.md) - [SyncMemoryAssetDeleteV1](doc//SyncMemoryAssetDeleteV1.md) - [SyncMemoryAssetV1](doc//SyncMemoryAssetV1.md) diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index 545955a184..8b8acc0042 100644 --- a/mobile/openapi/lib/api.dart +++ b/mobile/openapi/lib/api.dart @@ -260,6 +260,7 @@ part 'model/sync_asset_exif_v1.dart'; part 'model/sync_asset_face_delete_v1.dart'; part 'model/sync_asset_face_v1.dart'; part 'model/sync_asset_v1.dart'; +part 'model/sync_auth_user_v1.dart'; part 'model/sync_entity_type.dart'; part 'model/sync_memory_asset_delete_v1.dart'; part 'model/sync_memory_asset_v1.dart'; diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart index 55d6f4108b..d9cae66dd3 100644 --- a/mobile/openapi/lib/api_client.dart +++ b/mobile/openapi/lib/api_client.dart @@ -576,6 +576,8 @@ class ApiClient { return SyncAssetFaceV1.fromJson(value); case 'SyncAssetV1': return SyncAssetV1.fromJson(value); + case 'SyncAuthUserV1': + return SyncAuthUserV1.fromJson(value); case 'SyncEntityType': return SyncEntityTypeTypeTransformer().decode(value); case 'SyncMemoryAssetDeleteV1': diff --git a/mobile/openapi/lib/model/sync_auth_user_v1.dart b/mobile/openapi/lib/model/sync_auth_user_v1.dart new file mode 100644 index 0000000000..1dab7f47e3 --- /dev/null +++ b/mobile/openapi/lib/model/sync_auth_user_v1.dart @@ -0,0 +1,215 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class SyncAuthUserV1 { + /// Returns a new [SyncAuthUserV1] instance. + SyncAuthUserV1({ + required this.avatarColor, + required this.deletedAt, + required this.email, + required this.hasProfileImage, + required this.id, + required this.isAdmin, + required this.name, + required this.oauthId, + required this.pinCode, + required this.profileChangedAt, + required this.quotaSizeInBytes, + required this.quotaUsageInBytes, + required this.storageLabel, + }); + + UserAvatarColor? avatarColor; + + DateTime? deletedAt; + + String email; + + bool hasProfileImage; + + String id; + + bool isAdmin; + + String name; + + String oauthId; + + String? pinCode; + + DateTime profileChangedAt; + + int? quotaSizeInBytes; + + int quotaUsageInBytes; + + String? storageLabel; + + @override + bool operator ==(Object other) => identical(this, other) || other is SyncAuthUserV1 && + other.avatarColor == avatarColor && + other.deletedAt == deletedAt && + other.email == email && + other.hasProfileImage == hasProfileImage && + other.id == id && + other.isAdmin == isAdmin && + other.name == name && + other.oauthId == oauthId && + other.pinCode == pinCode && + other.profileChangedAt == profileChangedAt && + other.quotaSizeInBytes == quotaSizeInBytes && + other.quotaUsageInBytes == quotaUsageInBytes && + other.storageLabel == storageLabel; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (avatarColor == null ? 0 : avatarColor!.hashCode) + + (deletedAt == null ? 0 : deletedAt!.hashCode) + + (email.hashCode) + + (hasProfileImage.hashCode) + + (id.hashCode) + + (isAdmin.hashCode) + + (name.hashCode) + + (oauthId.hashCode) + + (pinCode == null ? 0 : pinCode!.hashCode) + + (profileChangedAt.hashCode) + + (quotaSizeInBytes == null ? 0 : quotaSizeInBytes!.hashCode) + + (quotaUsageInBytes.hashCode) + + (storageLabel == null ? 0 : storageLabel!.hashCode); + + @override + String toString() => 'SyncAuthUserV1[avatarColor=$avatarColor, deletedAt=$deletedAt, email=$email, hasProfileImage=$hasProfileImage, id=$id, isAdmin=$isAdmin, name=$name, oauthId=$oauthId, pinCode=$pinCode, profileChangedAt=$profileChangedAt, quotaSizeInBytes=$quotaSizeInBytes, quotaUsageInBytes=$quotaUsageInBytes, storageLabel=$storageLabel]'; + + Map toJson() { + final json = {}; + if (this.avatarColor != null) { + json[r'avatarColor'] = this.avatarColor; + } else { + // json[r'avatarColor'] = null; + } + if (this.deletedAt != null) { + json[r'deletedAt'] = this.deletedAt!.toUtc().toIso8601String(); + } else { + // json[r'deletedAt'] = null; + } + json[r'email'] = this.email; + json[r'hasProfileImage'] = this.hasProfileImage; + json[r'id'] = this.id; + json[r'isAdmin'] = this.isAdmin; + json[r'name'] = this.name; + json[r'oauthId'] = this.oauthId; + if (this.pinCode != null) { + json[r'pinCode'] = this.pinCode; + } else { + // json[r'pinCode'] = null; + } + json[r'profileChangedAt'] = this.profileChangedAt.toUtc().toIso8601String(); + if (this.quotaSizeInBytes != null) { + json[r'quotaSizeInBytes'] = this.quotaSizeInBytes; + } else { + // json[r'quotaSizeInBytes'] = null; + } + json[r'quotaUsageInBytes'] = this.quotaUsageInBytes; + if (this.storageLabel != null) { + json[r'storageLabel'] = this.storageLabel; + } else { + // json[r'storageLabel'] = null; + } + return json; + } + + /// Returns a new [SyncAuthUserV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SyncAuthUserV1? fromJson(dynamic value) { + upgradeDto(value, "SyncAuthUserV1"); + if (value is Map) { + final json = value.cast(); + + return SyncAuthUserV1( + avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']), + deletedAt: mapDateTime(json, r'deletedAt', r''), + email: mapValueOfType(json, r'email')!, + hasProfileImage: mapValueOfType(json, r'hasProfileImage')!, + id: mapValueOfType(json, r'id')!, + isAdmin: mapValueOfType(json, r'isAdmin')!, + name: mapValueOfType(json, r'name')!, + oauthId: mapValueOfType(json, r'oauthId')!, + pinCode: mapValueOfType(json, r'pinCode'), + profileChangedAt: mapDateTime(json, r'profileChangedAt', r'')!, + quotaSizeInBytes: mapValueOfType(json, r'quotaSizeInBytes'), + quotaUsageInBytes: mapValueOfType(json, r'quotaUsageInBytes')!, + storageLabel: mapValueOfType(json, r'storageLabel'), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = SyncAuthUserV1.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = SyncAuthUserV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SyncAuthUserV1-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = SyncAuthUserV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'avatarColor', + 'deletedAt', + 'email', + 'hasProfileImage', + 'id', + 'isAdmin', + 'name', + 'oauthId', + 'pinCode', + 'profileChangedAt', + 'quotaSizeInBytes', + 'quotaUsageInBytes', + 'storageLabel', + }; +} + diff --git a/mobile/openapi/lib/model/sync_entity_type.dart b/mobile/openapi/lib/model/sync_entity_type.dart index 65ed78105c..5368126923 100644 --- a/mobile/openapi/lib/model/sync_entity_type.dart +++ b/mobile/openapi/lib/model/sync_entity_type.dart @@ -23,6 +23,7 @@ class SyncEntityType { String toJson() => value; + static const authUserV1 = SyncEntityType._(r'AuthUserV1'); static const userV1 = SyncEntityType._(r'UserV1'); static const userDeleteV1 = SyncEntityType._(r'UserDeleteV1'); static const assetV1 = SyncEntityType._(r'AssetV1'); @@ -67,6 +68,7 @@ class SyncEntityType { /// List of all possible values in this [enum][SyncEntityType]. static const values = [ + authUserV1, userV1, userDeleteV1, assetV1, @@ -146,6 +148,7 @@ class SyncEntityTypeTypeTransformer { SyncEntityType? decode(dynamic data, {bool allowNull = true}) { if (data != null) { switch (data) { + case r'AuthUserV1': return SyncEntityType.authUserV1; case r'UserV1': return SyncEntityType.userV1; case r'UserDeleteV1': return SyncEntityType.userDeleteV1; case r'AssetV1': return SyncEntityType.assetV1; diff --git a/mobile/openapi/lib/model/sync_request_type.dart b/mobile/openapi/lib/model/sync_request_type.dart index 800b3f4485..8a1857366e 100644 --- a/mobile/openapi/lib/model/sync_request_type.dart +++ b/mobile/openapi/lib/model/sync_request_type.dart @@ -30,6 +30,7 @@ class SyncRequestType { static const albumAssetExifsV1 = SyncRequestType._(r'AlbumAssetExifsV1'); static const assetsV1 = SyncRequestType._(r'AssetsV1'); static const assetExifsV1 = SyncRequestType._(r'AssetExifsV1'); + static const authUsersV1 = SyncRequestType._(r'AuthUsersV1'); static const memoriesV1 = SyncRequestType._(r'MemoriesV1'); static const memoryToAssetsV1 = SyncRequestType._(r'MemoryToAssetsV1'); static const partnersV1 = SyncRequestType._(r'PartnersV1'); @@ -51,6 +52,7 @@ class SyncRequestType { albumAssetExifsV1, assetsV1, assetExifsV1, + authUsersV1, memoriesV1, memoryToAssetsV1, partnersV1, @@ -107,6 +109,7 @@ class SyncRequestTypeTypeTransformer { case r'AlbumAssetExifsV1': return SyncRequestType.albumAssetExifsV1; case r'AssetsV1': return SyncRequestType.assetsV1; case r'AssetExifsV1': return SyncRequestType.assetExifsV1; + case r'AuthUsersV1': return SyncRequestType.authUsersV1; case r'MemoriesV1': return SyncRequestType.memoriesV1; case r'MemoryToAssetsV1': return SyncRequestType.memoryToAssetsV1; case r'PartnersV1': return SyncRequestType.partnersV1; diff --git a/mobile/openapi/lib/model/sync_user_v1.dart b/mobile/openapi/lib/model/sync_user_v1.dart index b9b41bb723..c01ddcc9fc 100644 --- a/mobile/openapi/lib/model/sync_user_v1.dart +++ b/mobile/openapi/lib/model/sync_user_v1.dart @@ -13,12 +13,15 @@ part of openapi.api; class SyncUserV1 { /// Returns a new [SyncUserV1] instance. SyncUserV1({ + required this.avatarColor, required this.deletedAt, required this.email, required this.id, required this.name, }); + UserAvatarColor? avatarColor; + DateTime? deletedAt; String email; @@ -29,6 +32,7 @@ class SyncUserV1 { @override bool operator ==(Object other) => identical(this, other) || other is SyncUserV1 && + other.avatarColor == avatarColor && other.deletedAt == deletedAt && other.email == email && other.id == id && @@ -37,16 +41,22 @@ class SyncUserV1 { @override int get hashCode => // ignore: unnecessary_parenthesis + (avatarColor == null ? 0 : avatarColor!.hashCode) + (deletedAt == null ? 0 : deletedAt!.hashCode) + (email.hashCode) + (id.hashCode) + (name.hashCode); @override - String toString() => 'SyncUserV1[deletedAt=$deletedAt, email=$email, id=$id, name=$name]'; + String toString() => 'SyncUserV1[avatarColor=$avatarColor, deletedAt=$deletedAt, email=$email, id=$id, name=$name]'; Map toJson() { final json = {}; + if (this.avatarColor != null) { + json[r'avatarColor'] = this.avatarColor; + } else { + // json[r'avatarColor'] = null; + } if (this.deletedAt != null) { json[r'deletedAt'] = this.deletedAt!.toUtc().toIso8601String(); } else { @@ -67,6 +77,7 @@ class SyncUserV1 { final json = value.cast(); return SyncUserV1( + avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']), deletedAt: mapDateTime(json, r'deletedAt', r''), email: mapValueOfType(json, r'email')!, id: mapValueOfType(json, r'id')!, @@ -118,6 +129,7 @@ class SyncUserV1 { /// The list of required keys that must be present in a JSON. static const requiredKeys = { + 'avatarColor', 'deletedAt', 'email', 'id', diff --git a/mobile/test/drift/main/generated/schema_v4.dart b/mobile/test/drift/main/generated/schema_v4.dart index d02e2ff9c4..9cf72a098b 100644 --- a/mobile/test/drift/main/generated/schema_v4.dart +++ b/mobile/test/drift/main/generated/schema_v4.dart @@ -982,6 +982,255 @@ class RemoteAssetEntityCompanion } } +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + @override + List get $columns => + [id, createdAt, updatedAt, ownerId, primaryAssetId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id'])!, + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + updatedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + ownerId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData( + {required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith( + {String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId}) => + StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith( + {Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId}) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + class LocalAssetEntity extends Table with TableInfo { @override @@ -1415,640 +1664,6 @@ class LocalAssetEntityCompanion extends UpdateCompanion { } } -class StackEntity extends Table with TableInfo { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - StackEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); - late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn primaryAssetId = GeneratedColumn( - 'primary_asset_id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - @override - List get $columns => - [id, createdAt, updatedAt, ownerId, primaryAssetId]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'stack_entity'; - @override - Set get $primaryKey => {id}; - @override - StackEntityData map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return StackEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - primaryAssetId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, - ); - } - - @override - StackEntity createAlias(String alias) { - return StackEntity(attachedDatabase, alias); - } - - @override - bool get withoutRowId => true; - @override - bool get isStrict => true; -} - -class StackEntityData extends DataClass implements Insertable { - final String id; - final DateTime createdAt; - final DateTime updatedAt; - final String ownerId; - final String primaryAssetId; - const StackEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.primaryAssetId}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['id'] = Variable(id); - map['created_at'] = Variable(createdAt); - map['updated_at'] = Variable(updatedAt); - map['owner_id'] = Variable(ownerId); - map['primary_asset_id'] = Variable(primaryAssetId); - return map; - } - - factory StackEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return StackEntityData( - id: serializer.fromJson(json['id']), - createdAt: serializer.fromJson(json['createdAt']), - updatedAt: serializer.fromJson(json['updatedAt']), - ownerId: serializer.fromJson(json['ownerId']), - primaryAssetId: serializer.fromJson(json['primaryAssetId']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'id': serializer.toJson(id), - 'createdAt': serializer.toJson(createdAt), - 'updatedAt': serializer.toJson(updatedAt), - 'ownerId': serializer.toJson(ownerId), - 'primaryAssetId': serializer.toJson(primaryAssetId), - }; - } - - StackEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? primaryAssetId}) => - StackEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - primaryAssetId: primaryAssetId ?? this.primaryAssetId, - ); - StackEntityData copyWithCompanion(StackEntityCompanion data) { - return StackEntityData( - id: data.id.present ? data.id.value : this.id, - createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, - updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, - primaryAssetId: data.primaryAssetId.present - ? data.primaryAssetId.value - : this.primaryAssetId, - ); - } - - @override - String toString() { - return (StringBuffer('StackEntityData(') - ..write('id: $id, ') - ..write('createdAt: $createdAt, ') - ..write('updatedAt: $updatedAt, ') - ..write('ownerId: $ownerId, ') - ..write('primaryAssetId: $primaryAssetId') - ..write(')')) - .toString(); - } - - @override - int get hashCode => - Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is StackEntityData && - other.id == this.id && - other.createdAt == this.createdAt && - other.updatedAt == this.updatedAt && - other.ownerId == this.ownerId && - other.primaryAssetId == this.primaryAssetId); -} - -class StackEntityCompanion extends UpdateCompanion { - final Value id; - final Value createdAt; - final Value updatedAt; - final Value ownerId; - final Value primaryAssetId; - const StackEntityCompanion({ - this.id = const Value.absent(), - this.createdAt = const Value.absent(), - this.updatedAt = const Value.absent(), - this.ownerId = const Value.absent(), - this.primaryAssetId = const Value.absent(), - }); - StackEntityCompanion.insert({ - required String id, - this.createdAt = const Value.absent(), - this.updatedAt = const Value.absent(), - required String ownerId, - required String primaryAssetId, - }) : id = Value(id), - ownerId = Value(ownerId), - primaryAssetId = Value(primaryAssetId); - static Insertable custom({ - Expression? id, - Expression? createdAt, - Expression? updatedAt, - Expression? ownerId, - Expression? primaryAssetId, - }) { - return RawValuesInsertable({ - if (id != null) 'id': id, - if (createdAt != null) 'created_at': createdAt, - if (updatedAt != null) 'updated_at': updatedAt, - if (ownerId != null) 'owner_id': ownerId, - if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, - }); - } - - StackEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? primaryAssetId}) { - return StackEntityCompanion( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - primaryAssetId: primaryAssetId ?? this.primaryAssetId, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (id.present) { - map['id'] = Variable(id.value); - } - if (createdAt.present) { - map['created_at'] = Variable(createdAt.value); - } - if (updatedAt.present) { - map['updated_at'] = Variable(updatedAt.value); - } - if (ownerId.present) { - map['owner_id'] = Variable(ownerId.value); - } - if (primaryAssetId.present) { - map['primary_asset_id'] = Variable(primaryAssetId.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('StackEntityCompanion(') - ..write('id: $id, ') - ..write('createdAt: $createdAt, ') - ..write('updatedAt: $updatedAt, ') - ..write('ownerId: $ownerId, ') - ..write('primaryAssetId: $primaryAssetId') - ..write(')')) - .toString(); - } -} - -class UserMetadataEntity extends Table - with TableInfo { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - UserMetadataEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn userId = GeneratedColumn( - 'user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn key = GeneratedColumn( - 'key', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); - late final GeneratedColumn value = GeneratedColumn( - 'value', aliasedName, false, - type: DriftSqlType.blob, requiredDuringInsert: true); - @override - List get $columns => [userId, key, value]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'user_metadata_entity'; - @override - Set get $primaryKey => {userId, key}; - @override - UserMetadataEntityData map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return UserMetadataEntityData( - userId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - key: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}key'])!, - value: attachedDatabase.typeMapping - .read(DriftSqlType.blob, data['${effectivePrefix}value'])!, - ); - } - - @override - UserMetadataEntity createAlias(String alias) { - return UserMetadataEntity(attachedDatabase, alias); - } - - @override - bool get withoutRowId => true; - @override - bool get isStrict => true; -} - -class UserMetadataEntityData extends DataClass - implements Insertable { - final String userId; - final int key; - final Uint8List value; - const UserMetadataEntityData( - {required this.userId, required this.key, required this.value}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['user_id'] = Variable(userId); - map['key'] = Variable(key); - map['value'] = Variable(value); - return map; - } - - factory UserMetadataEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return UserMetadataEntityData( - userId: serializer.fromJson(json['userId']), - key: serializer.fromJson(json['key']), - value: serializer.fromJson(json['value']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'userId': serializer.toJson(userId), - 'key': serializer.toJson(key), - 'value': serializer.toJson(value), - }; - } - - UserMetadataEntityData copyWith( - {String? userId, int? key, Uint8List? value}) => - UserMetadataEntityData( - userId: userId ?? this.userId, - key: key ?? this.key, - value: value ?? this.value, - ); - UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { - return UserMetadataEntityData( - userId: data.userId.present ? data.userId.value : this.userId, - key: data.key.present ? data.key.value : this.key, - value: data.value.present ? data.value.value : this.value, - ); - } - - @override - String toString() { - return (StringBuffer('UserMetadataEntityData(') - ..write('userId: $userId, ') - ..write('key: $key, ') - ..write('value: $value') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is UserMetadataEntityData && - other.userId == this.userId && - other.key == this.key && - $driftBlobEquality.equals(other.value, this.value)); -} - -class UserMetadataEntityCompanion - extends UpdateCompanion { - final Value userId; - final Value key; - final Value value; - const UserMetadataEntityCompanion({ - this.userId = const Value.absent(), - this.key = const Value.absent(), - this.value = const Value.absent(), - }); - UserMetadataEntityCompanion.insert({ - required String userId, - required int key, - required Uint8List value, - }) : userId = Value(userId), - key = Value(key), - value = Value(value); - static Insertable custom({ - Expression? userId, - Expression? key, - Expression? value, - }) { - return RawValuesInsertable({ - if (userId != null) 'user_id': userId, - if (key != null) 'key': key, - if (value != null) 'value': value, - }); - } - - UserMetadataEntityCompanion copyWith( - {Value? userId, Value? key, Value? value}) { - return UserMetadataEntityCompanion( - userId: userId ?? this.userId, - key: key ?? this.key, - value: value ?? this.value, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (userId.present) { - map['user_id'] = Variable(userId.value); - } - if (key.present) { - map['key'] = Variable(key.value); - } - if (value.present) { - map['value'] = Variable(value.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('UserMetadataEntityCompanion(') - ..write('userId: $userId, ') - ..write('key: $key, ') - ..write('value: $value') - ..write(')')) - .toString(); - } -} - -class PartnerEntity extends Table - with TableInfo { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - PartnerEntity(this.attachedDatabase, [this._alias]); - late final GeneratedColumn sharedById = GeneratedColumn( - 'shared_by_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn sharedWithId = GeneratedColumn( - 'shared_with_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); - late final GeneratedColumn inTimeline = GeneratedColumn( - 'in_timeline', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const CustomExpression('0')); - @override - List get $columns => [sharedById, sharedWithId, inTimeline]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'partner_entity'; - @override - Set get $primaryKey => {sharedById, sharedWithId}; - @override - PartnerEntityData map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return PartnerEntityData( - sharedById: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, - sharedWithId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, - inTimeline: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, - ); - } - - @override - PartnerEntity createAlias(String alias) { - return PartnerEntity(attachedDatabase, alias); - } - - @override - bool get withoutRowId => true; - @override - bool get isStrict => true; -} - -class PartnerEntityData extends DataClass - implements Insertable { - final String sharedById; - final String sharedWithId; - final bool inTimeline; - const PartnerEntityData( - {required this.sharedById, - required this.sharedWithId, - required this.inTimeline}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['shared_by_id'] = Variable(sharedById); - map['shared_with_id'] = Variable(sharedWithId); - map['in_timeline'] = Variable(inTimeline); - return map; - } - - factory PartnerEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return PartnerEntityData( - sharedById: serializer.fromJson(json['sharedById']), - sharedWithId: serializer.fromJson(json['sharedWithId']), - inTimeline: serializer.fromJson(json['inTimeline']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'sharedById': serializer.toJson(sharedById), - 'sharedWithId': serializer.toJson(sharedWithId), - 'inTimeline': serializer.toJson(inTimeline), - }; - } - - PartnerEntityData copyWith( - {String? sharedById, String? sharedWithId, bool? inTimeline}) => - PartnerEntityData( - sharedById: sharedById ?? this.sharedById, - sharedWithId: sharedWithId ?? this.sharedWithId, - inTimeline: inTimeline ?? this.inTimeline, - ); - PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { - return PartnerEntityData( - sharedById: - data.sharedById.present ? data.sharedById.value : this.sharedById, - sharedWithId: data.sharedWithId.present - ? data.sharedWithId.value - : this.sharedWithId, - inTimeline: - data.inTimeline.present ? data.inTimeline.value : this.inTimeline, - ); - } - - @override - String toString() { - return (StringBuffer('PartnerEntityData(') - ..write('sharedById: $sharedById, ') - ..write('sharedWithId: $sharedWithId, ') - ..write('inTimeline: $inTimeline') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is PartnerEntityData && - other.sharedById == this.sharedById && - other.sharedWithId == this.sharedWithId && - other.inTimeline == this.inTimeline); -} - -class PartnerEntityCompanion extends UpdateCompanion { - final Value sharedById; - final Value sharedWithId; - final Value inTimeline; - const PartnerEntityCompanion({ - this.sharedById = const Value.absent(), - this.sharedWithId = const Value.absent(), - this.inTimeline = const Value.absent(), - }); - PartnerEntityCompanion.insert({ - required String sharedById, - required String sharedWithId, - this.inTimeline = const Value.absent(), - }) : sharedById = Value(sharedById), - sharedWithId = Value(sharedWithId); - static Insertable custom({ - Expression? sharedById, - Expression? sharedWithId, - Expression? inTimeline, - }) { - return RawValuesInsertable({ - if (sharedById != null) 'shared_by_id': sharedById, - if (sharedWithId != null) 'shared_with_id': sharedWithId, - if (inTimeline != null) 'in_timeline': inTimeline, - }); - } - - PartnerEntityCompanion copyWith( - {Value? sharedById, - Value? sharedWithId, - Value? inTimeline}) { - return PartnerEntityCompanion( - sharedById: sharedById ?? this.sharedById, - sharedWithId: sharedWithId ?? this.sharedWithId, - inTimeline: inTimeline ?? this.inTimeline, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (sharedById.present) { - map['shared_by_id'] = Variable(sharedById.value); - } - if (sharedWithId.present) { - map['shared_with_id'] = Variable(sharedWithId.value); - } - if (inTimeline.present) { - map['in_timeline'] = Variable(inTimeline.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('PartnerEntityCompanion(') - ..write('sharedById: $sharedById, ') - ..write('sharedWithId: $sharedWithId, ') - ..write('inTimeline: $inTimeline') - ..write(')')) - .toString(); - } -} - class LocalAlbumEntity extends Table with TableInfo { @override @@ -2498,6 +2113,391 @@ class LocalAlbumAssetEntityCompanion } } +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn key = GeneratedColumn( + 'key', aliasedName, false, + type: DriftSqlType.int, requiredDuringInsert: true); + late final GeneratedColumn value = GeneratedColumn( + 'value', aliasedName, false, + type: DriftSqlType.blob, requiredDuringInsert: true); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, + key: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}key'])!, + value: attachedDatabase.typeMapping + .read(DriftSqlType.blob, data['${effectivePrefix}value'])!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData( + {required this.userId, required this.key, required this.value}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith( + {String? userId, int? key, Uint8List? value}) => + UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith( + {Value? userId, Value? key, Value? value}) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), + defaultValue: const CustomExpression('0')); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, + sharedWithId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, + inTimeline: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData( + {required this.sharedById, + required this.sharedWithId, + required this.inTimeline}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith( + {String? sharedById, String? sharedWithId, bool? inTimeline}) => + PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: + data.sharedById.present ? data.sharedById.value : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: + data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith( + {Value? sharedById, + Value? sharedWithId, + Value? inTimeline}) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + class RemoteExifEntity extends Table with TableInfo { @override @@ -5467,8 +5467,11 @@ class DatabaseAtV4 extends GeneratedDatabase { DatabaseAtV4(QueryExecutor e) : super(e); late final UserEntity userEntity = UserEntity(this); late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); - late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); late final StackEntity stackEntity = StackEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); late final Index idxLocalAssetChecksum = Index('idx_local_asset_checksum', 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); late final Index uQRemoteAssetOwnerChecksum = Index( @@ -5478,9 +5481,6 @@ class DatabaseAtV4 extends GeneratedDatabase { 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); late final PartnerEntity partnerEntity = PartnerEntity(this); - late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); - late final LocalAlbumAssetEntity localAlbumAssetEntity = - LocalAlbumAssetEntity(this); late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = @@ -5498,15 +5498,15 @@ class DatabaseAtV4 extends GeneratedDatabase { List get allSchemaEntities => [ userEntity, remoteAssetEntity, - localAssetEntity, stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, idxLocalAssetChecksum, uQRemoteAssetOwnerChecksum, idxRemoteAssetChecksum, userMetadataEntity, partnerEntity, - localAlbumEntity, - localAlbumAssetEntity, remoteExifEntity, remoteAlbumEntity, remoteAlbumAssetEntity, diff --git a/mobile/test/fixtures/sync_stream.stub.dart b/mobile/test/fixtures/sync_stream.stub.dart index de2d58bc9d..6c47b9172e 100644 --- a/mobile/test/fixtures/sync_stream.stub.dart +++ b/mobile/test/fixtures/sync_stream.stub.dart @@ -9,6 +9,7 @@ abstract final class SyncStreamStub { email: "admin@admin", id: "1", name: "Admin", + avatarColor: null, ), ack: "1", ); @@ -19,6 +20,7 @@ abstract final class SyncStreamStub { email: "user@user", id: "5", name: "User", + avatarColor: null, ), ack: "5", ); diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index cd61f3e004..270a560fb0 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -13978,8 +13978,79 @@ ], "type": "object" }, + "SyncAuthUserV1": { + "properties": { + "avatarColor": { + "allOf": [ + { + "$ref": "#/components/schemas/UserAvatarColor" + } + ], + "nullable": true + }, + "deletedAt": { + "format": "date-time", + "nullable": true, + "type": "string" + }, + "email": { + "type": "string" + }, + "hasProfileImage": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "isAdmin": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "oauthId": { + "type": "string" + }, + "pinCode": { + "nullable": true, + "type": "string" + }, + "profileChangedAt": { + "format": "date-time", + "type": "string" + }, + "quotaSizeInBytes": { + "nullable": true, + "type": "integer" + }, + "quotaUsageInBytes": { + "type": "integer" + }, + "storageLabel": { + "nullable": true, + "type": "string" + } + }, + "required": [ + "avatarColor", + "deletedAt", + "email", + "hasProfileImage", + "id", + "isAdmin", + "name", + "oauthId", + "pinCode", + "profileChangedAt", + "quotaSizeInBytes", + "quotaUsageInBytes", + "storageLabel" + ], + "type": "object" + }, "SyncEntityType": { "enum": [ + "AuthUserV1", "UserV1", "UserDeleteV1", "AssetV1", @@ -14242,6 +14313,7 @@ "AlbumAssetExifsV1", "AssetsV1", "AssetExifsV1", + "AuthUsersV1", "MemoriesV1", "MemoryToAssetsV1", "PartnersV1", @@ -14364,6 +14436,14 @@ }, "SyncUserV1": { "properties": { + "avatarColor": { + "allOf": [ + { + "$ref": "#/components/schemas/UserAvatarColor" + } + ], + "nullable": true + }, "deletedAt": { "format": "date-time", "nullable": true, @@ -14380,6 +14460,7 @@ } }, "required": [ + "avatarColor", "deletedAt", "email", "id", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 81d279407c..bcbe757066 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -4099,6 +4099,7 @@ export enum Error2 { NotFound = "not_found" } export enum SyncEntityType { + AuthUserV1 = "AuthUserV1", UserV1 = "UserV1", UserDeleteV1 = "UserDeleteV1", AssetV1 = "AssetV1", @@ -4149,6 +4150,7 @@ export enum SyncRequestType { AlbumAssetExifsV1 = "AlbumAssetExifsV1", AssetsV1 = "AssetsV1", AssetExifsV1 = "AssetExifsV1", + AuthUsersV1 = "AuthUsersV1", MemoriesV1 = "MemoriesV1", MemoryToAssetsV1 = "MemoryToAssetsV1", PartnersV1 = "PartnersV1", diff --git a/server/bin/immich-dev b/server/bin/immich-dev index 533c10ef9d..e861c0ee06 100755 --- a/server/bin/immich-dev +++ b/server/bin/immich-dev @@ -6,4 +6,4 @@ if [ "$IMMICH_ENV" != "development" ]; then fi cd /usr/src/app/server || exit 1 -npm exec nest start --debug "0.0.0.0:9230" --watch -- "$@" +npm exec nest -- start --debug "0.0.0.0:9230" --watch -- "$@" diff --git a/server/package-lock.json b/server/package-lock.json index 207776873e..c0c048c554 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -54,7 +54,7 @@ "i18n-iso-countries": "^7.6.0", "ioredis": "^5.3.2", "js-yaml": "^4.1.0", - "kysely": "^0.28.0", + "kysely": "^0.28.2", "kysely-postgres-js": "^2.0.0", "lodash": "^4.17.21", "luxon": "^3.4.2", @@ -297,6 +297,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@angular-devkit/schematics-cli/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@angular-devkit/schematics-cli/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -369,6 +382,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@angular-devkit/schematics/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@angular-devkit/schematics/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -1159,9 +1185,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", - "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1209,9 +1235,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", - "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", + "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", "dev": true, "license": "MIT", "engines": { @@ -1245,19 +1271,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.0.tgz", - "integrity": "sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@golevelup/nestjs-discovery": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/@golevelup/nestjs-discovery/-/nestjs-discovery-4.0.3.tgz", @@ -2736,6 +2749,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@nestjs/cli/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@nestjs/cli/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -2757,9 +2783,9 @@ } }, "node_modules/@nestjs/common": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.3.tgz", - "integrity": "sha512-ogEK+GriWodIwCw6buQ1rpcH4Kx+G7YQ9EwuPySI3rS05pSdtQ++UhucjusSI9apNidv+QURBztJkRecwwJQXg==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.5.tgz", + "integrity": "sha512-DQpWdr3ShO0BHWkHl3I4W/jR6R3pDtxyBlmrpTuZF+PXxQyBXNvsUne0Wyo6QHPEDi+pAz9XchBFoKbqOhcdTg==", "license": "MIT", "dependencies": { "file-type": "21.0.0", @@ -2788,9 +2814,9 @@ } }, "node_modules/@nestjs/core": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.3.tgz", - "integrity": "sha512-5lTni0TCh8x7bXETRD57pQFnKnEg1T6M+VLE7wAmyQRIecKQU+2inRGZD+A4v2DC1I04eA0WffP0GKLxjOKlzw==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.5.tgz", + "integrity": "sha512-Qr25MEY9t8VsMETy7eXQ0cNXqu0lzuFrrTr+f+1G57ABCtV5Pogm7n9bF71OU2bnkDD32Bi4hQLeFR90cku3Tw==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -2862,14 +2888,14 @@ } }, "node_modules/@nestjs/platform-express": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.3.tgz", - "integrity": "sha512-hEDNMlaPiBO72fxxX/CuRQL3MEhKRc/sIYGVoXjrnw6hTxZdezvvM6A95UaLsYknfmcZZa/CdG1SMBZOu9agHQ==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.5.tgz", + "integrity": "sha512-OsoiUBY9Shs5IG3uvDIt9/IDfY5OlvWBESuB/K4Eun8xILw1EK5d5qMfC3d2sIJ+kA3l+kBR1d/RuzH7VprLIg==", "license": "MIT", "dependencies": { "cors": "2.8.5", "express": "5.1.0", - "multer": "2.0.1", + "multer": "2.0.2", "path-to-regexp": "8.2.0", "tslib": "2.8.1" }, @@ -2882,71 +2908,10 @@ "@nestjs/core": "^11.0.0" } }, - "node_modules/@nestjs/platform-express/node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@nestjs/platform-express/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@nestjs/platform-express/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@nestjs/platform-express/node_modules/multer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.1.tgz", - "integrity": "sha512-Ug8bXeTIUlxurg8xLTEskKShvcKDZALo1THEX5E41pYCD2sCVub5/kIRIGqWNoqV6szyLyQKV6mD4QUrWE5GCQ==", - "license": "MIT", - "dependencies": { - "append-field": "^1.0.0", - "busboy": "^1.6.0", - "concat-stream": "^2.0.0", - "mkdirp": "^0.5.6", - "object-assign": "^4.1.1", - "type-is": "^1.6.18", - "xtend": "^4.0.2" - }, - "engines": { - "node": ">= 10.16.0" - } - }, - "node_modules/@nestjs/platform-express/node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/@nestjs/platform-socket.io": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.1.3.tgz", - "integrity": "sha512-jQ+ccprmh3kKolBp+bb97zoaS3vKaiyeNqyctGqV4CSG8P6mXSaaUObWxAsw6Jdgn5YQAVEBWJ6FhvF4s6QZbg==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.1.5.tgz", + "integrity": "sha512-DY3zNY+BjbrYpV/t8HL8ptrusrWK8J0cfkfY1iZsfCd+0/1+j8IKno+QMLkerNQAZ7/Frh5tkaKHVwWk18TkMw==", "license": "MIT", "dependencies": { "socket.io": "4.8.1", @@ -3063,6 +3028,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@nestjs/schematics/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@nestjs/schematics/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -3117,9 +3095,9 @@ } }, "node_modules/@nestjs/testing": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.3.tgz", - "integrity": "sha512-CeXG6/eEqgFIkPkmU00y18Dd3DLOIDFhPItzJK1SWckKo6IhcnfoRJzGx75bmuvUMjb51j6An96S/+MJ2ty9jA==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.5.tgz", + "integrity": "sha512-ZYRYF750SefmuIo7ZqPlHDcin1OHh6My0OkOfGEFjrD9mJ0vMVIpwMTOOkpzCfCcpqUuxeHBuecpiIn+NLrQbQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3145,9 +3123,9 @@ } }, "node_modules/@nestjs/websockets": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.1.3.tgz", - "integrity": "sha512-IjhWKfRf0D247JxYIEs8USblJJbcxUsKJpzbCPaZ7TrVy4LrpG3IRQDlSTOw599TRIYP5ixyH9C0+v5DyaI9uA==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.1.5.tgz", + "integrity": "sha512-mAM11HwyS7aeSUbXdOqHbNCRoHwB0OOb+cmx5sgxvszhdG0Y6bwR60nKA4+EXL9xUEeWoxmbfLmSHlTSIJ9GKA==", "license": "MIT", "dependencies": { "iterare": "1.2.1", @@ -5838,9 +5816,9 @@ } }, "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.34.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.34.0.tgz", - "integrity": "sha512-aKcOkyrorBGlajjRdVoJWHTxfxO1vCNHLJVlSDaRHDIdjU+pX8IYQPvPDkYiujKLbRnWU+1TBwEt0QRgSm4SGA==", + "version": "1.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.36.0.tgz", + "integrity": "sha512-TtxJSRD8Ohxp6bKkhrm27JRHAxPczQA7idtcTOMYI+wQRRrfgqxHv1cFbCApcSnNjtXkmzFozn6jQtFrOmbjPQ==", "license": "Apache-2.0", "engines": { "node": ">=14" @@ -6627,9 +6605,9 @@ "license": "MIT" }, "node_modules/@swc/core": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.12.11.tgz", - "integrity": "sha512-P3GM+0lqjFctcp5HhR9mOcvLSX3SptI9L1aux0Fuvgt8oH4f92rCUrkodAa0U2ktmdjcyIiG37xg2mb/dSCYSA==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.0.tgz", + "integrity": "sha512-7Fh16ZH/Rj3Di720if+sw9BictD4N5kbTpsyDC+URXhvsZ7qRt1lH7PaeIQYyJJQHwFhoKpwwGxfGU9SHgPLdw==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -6645,16 +6623,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.12.11", - "@swc/core-darwin-x64": "1.12.11", - "@swc/core-linux-arm-gnueabihf": "1.12.11", - "@swc/core-linux-arm64-gnu": "1.12.11", - "@swc/core-linux-arm64-musl": "1.12.11", - "@swc/core-linux-x64-gnu": "1.12.11", - "@swc/core-linux-x64-musl": "1.12.11", - "@swc/core-win32-arm64-msvc": "1.12.11", - "@swc/core-win32-ia32-msvc": "1.12.11", - "@swc/core-win32-x64-msvc": "1.12.11" + "@swc/core-darwin-arm64": "1.13.0", + "@swc/core-darwin-x64": "1.13.0", + "@swc/core-linux-arm-gnueabihf": "1.13.0", + "@swc/core-linux-arm64-gnu": "1.13.0", + "@swc/core-linux-arm64-musl": "1.13.0", + "@swc/core-linux-x64-gnu": "1.13.0", + "@swc/core-linux-x64-musl": "1.13.0", + "@swc/core-win32-arm64-msvc": "1.13.0", + "@swc/core-win32-ia32-msvc": "1.13.0", + "@swc/core-win32-x64-msvc": "1.13.0" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" @@ -6666,9 +6644,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.12.11.tgz", - "integrity": "sha512-J19Jj9Y5x/N0loExH7W0OI9OwwoVyxutDdkyq1o/kgXyBqmmzV7Y/Q9QekI2Fm/qc5mNeAdP7aj4boY4AY/JPw==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.0.tgz", + "integrity": "sha512-SkmR9u7MHDu2X8hf7SjZTmsAfQTmel0mi+TJ7AGtufLwGySv6pwQfJ/CIJpcPxYENVqDJAFnDrHaKV8mgA6kxQ==", "cpu": [ "arm64" ], @@ -6683,9 +6661,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.12.11.tgz", - "integrity": "sha512-PTuUQrfStQ6cjW+uprGO2lpQHy84/l0v+GqRqq8s/jdK55rFRjMfCeyf6FAR0l6saO5oNOQl+zWR1aNpj8pMQw==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.0.tgz", + "integrity": "sha512-15/SyDjXRtFJ09fYHBXUXrj4tpiSpCkjgsF1z3/sSpHH1POWpQUQzxmFyomPQVZ/SsDqP18WGH09Vph4Qriuiw==", "cpu": [ "x64" ], @@ -6700,9 +6678,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.12.11.tgz", - "integrity": "sha512-poxBq152HsupOtnZilenvHmxZ9a8SRj4LtfxUnkMDNOGrZR9oxbQNwEzNKfi3RXEcXz+P8c0Rai1ubBazXv8oQ==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.0.tgz", + "integrity": "sha512-AHauVHZQEJI/dCZQg6VYNNQ6HROz8dSOnCSheXzzBw1DGWo77BlcxRP0fF0jaAXM9WNqtCUOY1HiJ9ohkAE61Q==", "cpu": [ "arm" ], @@ -6717,9 +6695,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.12.11.tgz", - "integrity": "sha512-y1HNamR/D0Hc8xIE910ysyLe269UYiGaQPoLjQS0phzWFfWdMj9bHM++oydVXZ4RSWycO7KyJ3uvw4NilvyMKQ==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.0.tgz", + "integrity": "sha512-qyZmBZF7asF6954/x7yn6R7Bzd45KRG05rK2atIF9J3MTa8az7vubP1Q3BWmmss1j8699DELpbuoJucGuhsNXw==", "cpu": [ "arm64" ], @@ -6734,9 +6712,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.12.11.tgz", - "integrity": "sha512-LlBxPh/32pyQsu2emMEOFRm7poEFLsw12Y1mPY7FWZiZeptomKSOSHRzKDz9EolMiV4qhK1caP1lvW4vminYgQ==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.0.tgz", + "integrity": "sha512-whskQCOUlLQT7MjnronpHmyHegBka5ig9JkQvecbqhWzRfdwN+c2xTJs3kQsWy2Vc2f1hcL3D8hGIwY5TwPxMQ==", "cpu": [ "arm64" ], @@ -6751,9 +6729,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.12.11.tgz", - "integrity": "sha512-bOjiZB8O/1AzHkzjge1jqX62HGRIpOHqFUrGPfAln/NC6NR+Z2A78u3ixV7k5KesWZFhCV0YVGJL+qToL27myA==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.0.tgz", + "integrity": "sha512-51n4P4nv6rblXyH3zCEktvmR9uSAZ7+zbfeby0sxbj8LS/IKuVd7iCwD5dwMj4CxG9Fs+HgjN73dLQF/OerHhg==", "cpu": [ "x64" ], @@ -6768,9 +6746,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.12.11.tgz", - "integrity": "sha512-4dzAtbT/m3/UjF045+33gLiHd8aSXJDoqof7gTtu4q0ZyAf7XJ3HHspz+/AvOJLVo4FHHdFcdXhmo/zi1nFn8A==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.0.tgz", + "integrity": "sha512-VMqelgvnXs27eQyhDf1S2O2MxSdchIH7c1tkxODRtu9eotcAeniNNgqqLjZ5ML0MGeRk/WpbsAY/GWi7eSpiHw==", "cpu": [ "x64" ], @@ -6785,9 +6763,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.12.11.tgz", - "integrity": "sha512-h8HiwBZErKvCAmjW92JvQp0iOqm6bncU4ac5jxBGkRApabpUenNJcj3h2g5O6GL5K6T9/WhnXE5gyq/s1fhPQg==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.0.tgz", + "integrity": "sha512-NLJmseWJngWeENgat+O/WB4ptNxtx2X4OfPnSG5a/A4sxcn2E4jq91OPvbeUQwDkH+ZQWKXmbXFzt7Nn661QYA==", "cpu": [ "arm64" ], @@ -6802,9 +6780,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.12.11.tgz", - "integrity": "sha512-1pwr325mXRNUhxTtXmx1IokV5SiRL+6iDvnt3FRXj+X5UvXXKtg2zeyftk+03u8v8v8WUr5I32hIypVJPTNxNg==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.0.tgz", + "integrity": "sha512-UBfwrp0xW37KQGTA08mwrCLIm1ZKy6pXK8IVwou7BvhMgrItRNweTGyUrCnvDLUfyYFuJCmzcEaJ3NudtctD6g==", "cpu": [ "ia32" ], @@ -6819,9 +6797,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.12.11.tgz", - "integrity": "sha512-5gggWo690Gvs7XiPxAmb5tHwzB9RTVXUV7AWoGb6bmyUd1OXYaebQF0HAOtade5jIoNhfQMQJ7QReRgt/d2jAA==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.0.tgz", + "integrity": "sha512-BAB1P7Z/y2EENsfsPytPnjIyBVRZN2WULY+s3ozW4QkGmYHde6XXG28n0ABTHhcIOmmR2VzM+uaW1x48laSimw==", "cpu": [ "x64" ], @@ -7500,17 +7478,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz", - "integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", + "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/type-utils": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/type-utils": "8.37.0", + "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -7524,7 +7502,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.36.0", + "@typescript-eslint/parser": "^8.37.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -7540,16 +7518,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz", - "integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", + "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4" }, "engines": { @@ -7565,14 +7543,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", + "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", + "@typescript-eslint/tsconfig-utils": "^8.37.0", + "@typescript-eslint/types": "^8.37.0", "debug": "^4.3.4" }, "engines": { @@ -7587,14 +7565,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", + "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7605,9 +7583,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", + "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", "dev": true, "license": "MIT", "engines": { @@ -7622,14 +7600,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz", - "integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", + "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/utils": "8.36.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -7646,9 +7625,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", + "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", "dev": true, "license": "MIT", "engines": { @@ -7660,16 +7639,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", + "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/project-service": "8.37.0", + "@typescript-eslint/tsconfig-utils": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -7715,16 +7694,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", + "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7739,13 +7718,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", + "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/types": "8.37.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -8905,9 +8884,9 @@ } }, "node_modules/bullmq": { - "version": "5.56.2", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.56.2.tgz", - "integrity": "sha512-bq0PSxPCWeNlFBc5yjBs3eR+e6GxIEIeHY0xxq6WELzG65GPjL+A2ni1NS7NroKsur0C3UJdabw51IswiSTSYw==", + "version": "5.56.4", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.56.4.tgz", + "integrity": "sha512-5wSHd0oXs2jS6P+6tay/01Iz0cWRK8iYcscKtpS/GewEq0bJZwbkMZc77GJVOT9SP+UQuXA2y+pQTdCQJel7kQ==", "license": "MIT", "dependencies": { "cron-parser": "^4.9.0", @@ -9556,16 +9535,16 @@ } }, "node_modules/compression": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz", - "integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", "license": "MIT", "dependencies": { "bytes": "3.1.2", "compressible": "~2.0.18", "debug": "2.6.9", "negotiator": "~0.6.4", - "on-headers": "~1.0.2", + "on-headers": "~1.1.0", "safe-buffer": "5.2.1", "vary": "~1.1.2" }, @@ -9588,6 +9567,15 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/compression/node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -10685,9 +10673,9 @@ } }, "node_modules/eslint": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", - "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", + "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10695,9 +10683,9 @@ "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.14.0", + "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.30.1", + "@eslint/js": "9.31.0", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -13823,9 +13811,9 @@ "license": "MIT" }, "node_modules/nest-commander": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/nest-commander/-/nest-commander-3.17.0.tgz", - "integrity": "sha512-1R9vppZT2j/9njKiG0zYTDLAyQOj14KdGWdNuhluveK8VXoQepXNb0t09dRNWy4KCWrI7wDZ2tQTEwb43JyHOw==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/nest-commander/-/nest-commander-3.18.0.tgz", + "integrity": "sha512-NWtodOl2aStnApWp9oajCoJW71lqN0CCjf9ygOWxpXnG3o4nQ8ZO5CgrExfVw2+0CVC877hr0rFR7FSu2rypGg==", "license": "MIT", "dependencies": { "@fig/complete-commander": "^3.0.0", @@ -13926,9 +13914,9 @@ "license": "MIT" }, "node_modules/node-addon-api": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.4.0.tgz", - "integrity": "sha512-D9DI/gXHvVmjHS08SVch0Em8G5S1P+QWtU31appcKT/8wFSPRcdHadIFSAntdMMVM5zz+/DL+bL/gz3UDppqtg==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", "license": "MIT", "engines": { "node": "^18 || ^20 || >= 21" @@ -14687,9 +14675,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", "engines": { "node": ">=12" @@ -15397,9 +15385,9 @@ } }, "node_modules/react-email": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.1.0.tgz", - "integrity": "sha512-UvG5z1/gNOsLNwKPO87vgMoF7tdzUGd0kIy4fozzdBBsyLUju7hNVLBRm9j+Li/CwP5CXFT8Y5jZBtIFvSyr0w==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.2.3.tgz", + "integrity": "sha512-LUKyk9nNVFuTqAyp4yCEQFQjBe+s8nl3VauMWuOhBZ4VhGnimbrnv01U8yD2YwzaHKtytS0U659x5dc/0+xu+Q==", "license": "MIT", "dependencies": { "@babel/parser": "^7.27.0", @@ -18447,15 +18435,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.36.0.tgz", - "integrity": "sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz", + "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.36.0", - "@typescript-eslint/parser": "8.36.0", - "@typescript-eslint/utils": "8.36.0" + "@typescript-eslint/eslint-plugin": "8.37.0", + "@typescript-eslint/parser": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/server/package.json b/server/package.json index d461f1d767..5dbe83381a 100644 --- a/server/package.json +++ b/server/package.json @@ -79,7 +79,7 @@ "i18n-iso-countries": "^7.6.0", "ioredis": "^5.3.2", "js-yaml": "^4.1.0", - "kysely": "^0.28.0", + "kysely": "^0.28.2", "kysely-postgres-js": "^2.0.0", "lodash": "^4.17.21", "luxon": "^3.4.2", @@ -113,7 +113,6 @@ "validator": "^13.12.0" }, "devDependencies": { - "canvas": "^3.1.0", "@eslint/eslintrc": "^3.1.0", "@eslint/js": "^9.8.0", "@nestjs/cli": "^11.0.2", @@ -146,6 +145,7 @@ "@types/ua-parser-js": "^0.7.36", "@types/validator": "^13.15.2", "@vitest/coverage-v8": "^3.0.0", + "canvas": "^3.1.0", "eslint": "^9.14.0", "eslint-config-prettier": "^10.0.0", "eslint-plugin-prettier": "^5.1.3", diff --git a/server/src/database.ts b/server/src/database.ts index dc99fc5b31..53c39b7383 100644 --- a/server/src/database.ts +++ b/server/src/database.ts @@ -356,6 +356,7 @@ export const columns = { ], syncAlbumUser: ['album_user.albumsId as albumId', 'album_user.usersId as userId', 'album_user.role'], syncStack: ['stack.id', 'stack.createdAt', 'stack.updatedAt', 'stack.primaryAssetId', 'stack.ownerId'], + syncUser: ['id', 'name', 'email', 'avatarColor', 'deletedAt', 'updateId'], stack: ['stack.id', 'stack.primaryAssetId', 'ownerId'], syncAssetExif: [ 'asset_exif.assetId', diff --git a/server/src/dtos/sync.dto.ts b/server/src/dtos/sync.dto.ts index c8b1a7dde9..8614bd5776 100644 --- a/server/src/dtos/sync.dto.ts +++ b/server/src/dtos/sync.dto.ts @@ -10,6 +10,7 @@ import { MemoryType, SyncEntityType, SyncRequestType, + UserAvatarColor, UserMetadataKey, } from 'src/enum'; import { UserMetadata } from 'src/types'; @@ -58,9 +59,25 @@ export class SyncUserV1 { id!: string; name!: string; email!: string; + @ValidateEnum({ enum: UserAvatarColor, name: 'UserAvatarColor', nullable: true }) + avatarColor!: UserAvatarColor | null; deletedAt!: Date | null; } +@ExtraModel() +export class SyncAuthUserV1 extends SyncUserV1 { + isAdmin!: boolean; + pinCode!: string | null; + oauthId!: string; + storageLabel!: string | null; + @ApiProperty({ type: 'integer' }) + quotaSizeInBytes!: number | null; + @ApiProperty({ type: 'integer' }) + quotaUsageInBytes!: number; + hasProfileImage!: boolean; + profileChangedAt!: Date; +} + @ExtraModel() export class SyncUserDeleteV1 { userId!: string; @@ -301,6 +318,7 @@ export class SyncAckV1 {} export class SyncResetV1 {} export type SyncItem = { + [SyncEntityType.AuthUserV1]: SyncAuthUserV1; [SyncEntityType.UserV1]: SyncUserV1; [SyncEntityType.UserDeleteV1]: SyncUserDeleteV1; [SyncEntityType.PartnerV1]: SyncPartnerV1; diff --git a/server/src/enum.ts b/server/src/enum.ts index f2eae615ab..75a96dfe67 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -559,6 +559,7 @@ export enum SyncRequestType { AlbumAssetExifsV1 = 'AlbumAssetExifsV1', AssetsV1 = 'AssetsV1', AssetExifsV1 = 'AssetExifsV1', + AuthUsersV1 = 'AuthUsersV1', MemoriesV1 = 'MemoriesV1', MemoryToAssetsV1 = 'MemoryToAssetsV1', PartnersV1 = 'PartnersV1', @@ -573,6 +574,8 @@ export enum SyncRequestType { } export enum SyncEntityType { + AuthUserV1 = 'AuthUserV1', + UserV1 = 'UserV1', UserDeleteV1 = 'UserDeleteV1', diff --git a/server/src/queries/sync.repository.sql b/server/src/queries/sync.repository.sql index 7502b79f57..7c7774a020 100644 --- a/server/src/queries/sync.repository.sql +++ b/server/src/queries/sync.repository.sql @@ -444,6 +444,29 @@ where order by "asset_face"."updateId" asc +-- SyncRepository.authUser.getUpserts +select + "id", + "name", + "email", + "avatarColor", + "deletedAt", + "updateId", + "isAdmin", + "pinCode", + "oauthId", + "storageLabel", + "quotaSizeInBytes", + "quotaUsageInBytes", + "profileImagePath", + "profileChangedAt" +from + "user" +where + "updatedAt" < now() - interval '1 millisecond' +order by + "updateId" asc + -- SyncRepository.memory.getDeletes select "id", @@ -871,6 +894,7 @@ select "id", "name", "email", + "avatarColor", "deletedAt", "updateId" from diff --git a/server/src/repositories/sync.repository.ts b/server/src/repositories/sync.repository.ts index dba52d25a0..486984118d 100644 --- a/server/src/repositories/sync.repository.ts +++ b/server/src/repositories/sync.repository.ts @@ -43,6 +43,7 @@ export class SyncRepository { asset: AssetSync; assetExif: AssetExifSync; assetFace: AssetFaceSync; + authUser: AuthUserSync; memory: MemorySync; memoryToAsset: MemoryToAssetSync; partner: PartnerSync; @@ -63,6 +64,7 @@ export class SyncRepository { this.asset = new AssetSync(this.db); this.assetExif = new AssetExifSync(this.db); this.assetFace = new AssetFaceSync(this.db); + this.authUser = new AuthUserSync(this.db); this.memory = new MemorySync(this.db); this.memoryToAsset = new MemoryToAssetSync(this.db); this.partner = new PartnerSync(this.db); @@ -367,6 +369,27 @@ class AssetSync extends BaseSync { } } +class AuthUserSync extends BaseSync { + @GenerateSql({ params: [], stream: true }) + getUpserts(ack?: SyncAck) { + return this.db + .selectFrom('user') + .select(columns.syncUser) + .select([ + 'isAdmin', + 'pinCode', + 'oauthId', + 'storageLabel', + 'quotaSizeInBytes', + 'quotaUsageInBytes', + 'profileImagePath', + 'profileChangedAt', + ]) + .$call(this.upsertTableFilters(ack)) + .stream(); + } +} + class PersonSync extends BaseSync { @GenerateSql({ params: [DummyValue.UUID], stream: true }) getDeletes(userId: string, ack?: SyncAck) { @@ -693,11 +716,7 @@ class UserSync extends BaseSync { @GenerateSql({ params: [], stream: true }) getUpserts(ack?: SyncAck) { - return this.db - .selectFrom('user') - .select(['id', 'name', 'email', 'deletedAt', 'updateId']) - .$call(this.upsertTableFilters(ack)) - .stream(); + return this.db.selectFrom('user').select(columns.syncUser).$call(this.upsertTableFilters(ack)).stream(); } } diff --git a/server/src/services/sync.service.ts b/server/src/services/sync.service.ts index fb582ab038..57b953f12e 100644 --- a/server/src/services/sync.service.ts +++ b/server/src/services/sync.service.ts @@ -54,6 +54,7 @@ const sendEntityBackfillCompleteAck = (response: Writable, ackType: SyncEntityTy const FULL_SYNC = { needsFullSync: true, deleted: [], upserted: [] }; export const SYNC_TYPES_ORDER = [ + SyncRequestType.AuthUsersV1, SyncRequestType.UsersV1, SyncRequestType.PartnersV1, SyncRequestType.AssetsV1, @@ -140,6 +141,7 @@ export class SyncService extends BaseService { const checkpointMap: CheckpointMap = Object.fromEntries(checkpoints.map(({ type, ack }) => [type, fromAck(ack)])); const handlers: Record Promise> = { + [SyncRequestType.AuthUsersV1]: () => this.syncAuthUsersV1(response, checkpointMap), [SyncRequestType.UsersV1]: () => this.syncUsersV1(response, checkpointMap), [SyncRequestType.PartnersV1]: () => this.syncPartnersV1(response, checkpointMap, auth), [SyncRequestType.AssetsV1]: () => this.syncAssetsV1(response, checkpointMap, auth), @@ -169,6 +171,14 @@ export class SyncService extends BaseService { response.end(); } + private async syncAuthUsersV1(response: Writable, checkpointMap: CheckpointMap) { + const upsertType = SyncEntityType.AuthUserV1; + const upserts = this.syncRepository.authUser.getUpserts(checkpointMap[upsertType]); + for await (const { updateId, profileImagePath, ...data } of upserts) { + send(response, { type: upsertType, ids: [updateId], data: { ...data, hasProfileImage: !!profileImagePath } }); + } + } + private async syncUsersV1(response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.UserDeleteV1; const deletes = this.syncRepository.user.getDeletes(checkpointMap[deleteType]); diff --git a/server/test/medium.factory.ts b/server/test/medium.factory.ts index d6038b6b84..1b669e83e4 100644 --- a/server/test/medium.factory.ts +++ b/server/test/medium.factory.ts @@ -507,7 +507,14 @@ const userInsert = (user: Partial> = {}) => { deletedAt: null, isAdmin: false, profileImagePath: '', + profileChangedAt: newDate(), shouldChangePassword: true, + storageLabel: null, + pinCode: null, + oauthId: '', + avatarColor: null, + quotaSizeInBytes: null, + quotaUsageInBytes: 0, }; return { ...defaults, ...user, id }; diff --git a/server/test/medium/specs/sync/sync-auth-user.spec.ts b/server/test/medium/specs/sync/sync-auth-user.spec.ts new file mode 100644 index 0000000000..80ce8b37fa --- /dev/null +++ b/server/test/medium/specs/sync/sync-auth-user.spec.ts @@ -0,0 +1,87 @@ +import { Kysely } from 'kysely'; +import { SyncEntityType, SyncRequestType } from 'src/enum'; +import { UserRepository } from 'src/repositories/user.repository'; +import { DB } from 'src/schema'; +import { SyncTestContext } from 'test/medium.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = async (db?: Kysely) => { + const ctx = new SyncTestContext(db || defaultDatabase); + const { auth, user, session } = await ctx.newSyncAuthUser(); + return { auth, user, session, ctx }; +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(SyncEntityType.AuthUserV1, () => { + it('should detect and sync the first user', async () => { + const { auth, user, ctx } = await setup(await getKyselyDB()); + + const response = await ctx.syncStream(auth, [SyncRequestType.AuthUsersV1]); + expect(response).toHaveLength(1); + expect(response).toEqual([ + { + ack: expect.any(String), + data: { + id: user.id, + isAdmin: user.isAdmin, + deletedAt: user.deletedAt, + name: user.name, + avatarColor: user.avatarColor, + email: user.email, + pinCode: user.pinCode, + hasProfileImage: false, + profileChangedAt: (user.profileChangedAt as Date).toISOString(), + oauthId: user.oauthId, + quotaSizeInBytes: user.quotaSizeInBytes, + quotaUsageInBytes: user.quotaUsageInBytes, + storageLabel: user.storageLabel, + }, + type: 'AuthUserV1', + }, + ]); + + await ctx.syncAckAll(auth, response); + await expect(ctx.syncStream(auth, [SyncRequestType.AuthUsersV1])).resolves.toEqual([]); + }); + + it('should sync a change and then another change to that same user', async () => { + const { auth, user, ctx } = await setup(await getKyselyDB()); + + const userRepo = ctx.get(UserRepository); + + const response = await ctx.syncStream(auth, [SyncRequestType.AuthUsersV1]); + expect(response).toHaveLength(1); + expect(response).toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + id: user.id, + isAdmin: false, + }), + type: 'AuthUserV1', + }, + ]); + + await ctx.syncAckAll(auth, response); + + await userRepo.update(user.id, { isAdmin: true }); + + const newResponse = await ctx.syncStream(auth, [SyncRequestType.AuthUsersV1]); + expect(newResponse).toHaveLength(1); + expect(newResponse).toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + id: user.id, + isAdmin: true, + }), + type: 'AuthUserV1', + }, + ]); + }); +}); diff --git a/server/test/medium/specs/sync/sync-user.spec.ts b/server/test/medium/specs/sync/sync-user.spec.ts index 24137e3aea..72661e119c 100644 --- a/server/test/medium/specs/sync/sync-user.spec.ts +++ b/server/test/medium/specs/sync/sync-user.spec.ts @@ -37,6 +37,7 @@ describe(SyncEntityType.UserV1, () => { email: user.email, id: user.id, name: user.name, + avatarColor: user.avatarColor, }, type: 'UserV1', }, @@ -49,8 +50,7 @@ describe(SyncEntityType.UserV1, () => { it('should detect and sync a soft deleted user', async () => { const { auth, ctx } = await setup(await getKyselyDB()); - const deletedAt = new Date().toISOString(); - const { user: deleted } = await ctx.newUser({ deletedAt }); + const { user: deleted } = await ctx.newUser({ deletedAt: new Date().toISOString() }); const response = await ctx.syncStream(auth, [SyncRequestType.UsersV1]); @@ -59,22 +59,12 @@ describe(SyncEntityType.UserV1, () => { expect.arrayContaining([ { ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: auth.user.name, - }, + data: expect.objectContaining({ id: auth.user.id }), type: 'UserV1', }, { ack: expect.any(String), - data: { - deletedAt, - email: deleted.email, - id: deleted.id, - name: deleted.name, - }, + data: expect.objectContaining({ id: deleted.id }), type: 'UserV1', }, ]), @@ -85,7 +75,7 @@ describe(SyncEntityType.UserV1, () => { }); it('should detect and sync a deleted user', async () => { - const { auth, ctx } = await setup(await getKyselyDB()); + const { auth, user: authUser, ctx } = await setup(await getKyselyDB()); const userRepo = ctx.get(UserRepository); @@ -104,12 +94,7 @@ describe(SyncEntityType.UserV1, () => { }, { ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: auth.user.name, - }, + data: expect.objectContaining({ id: authUser.id }), type: 'UserV1', }, ]); @@ -119,7 +104,7 @@ describe(SyncEntityType.UserV1, () => { }); it('should sync a user and then an update to that same user', async () => { - const { auth, ctx } = await setup(await getKyselyDB()); + const { auth, user, ctx } = await setup(await getKyselyDB()); const userRepo = ctx.get(UserRepository); @@ -128,12 +113,7 @@ describe(SyncEntityType.UserV1, () => { expect(response).toEqual([ { ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: auth.user.name, - }, + data: expect.objectContaining({ id: user.id }), type: 'UserV1', }, ]); @@ -147,12 +127,7 @@ describe(SyncEntityType.UserV1, () => { expect(newResponse).toEqual([ { ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: updated.name, - }, + data: expect.objectContaining({ id: user.id, name: updated.name }), type: 'UserV1', }, ]); diff --git a/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte b/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte index c120cb3750..acc920981b 100644 --- a/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte +++ b/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte @@ -183,7 +183,7 @@ label={$t('admin.machine_learning_min_detection_score')} description={$t('admin.machine_learning_min_detection_score_description')} bind:value={config.machineLearning.facialRecognition.minScore} - step="0.1" + step="0.01" min={0.1} max={1} disabled={disabled || !config.machineLearning.enabled || !config.machineLearning.facialRecognition.enabled} @@ -196,7 +196,7 @@ label={$t('admin.machine_learning_max_recognition_distance')} description={$t('admin.machine_learning_max_recognition_distance_description')} bind:value={config.machineLearning.facialRecognition.maxDistance} - step="0.1" + step="0.01" min={0.1} max={2} disabled={disabled || !config.machineLearning.enabled || !config.machineLearning.facialRecognition.enabled}