mirror of
https://github.com/storytold/lightcraft.git
synced 2026-11-13 09:20:07 -06:00
LR-EDIT-OPTICS-PROFILE: the lens database is offered on first launch and downloads on request (#3073)
Nothing is fetched without the user's click. The first desktop start without a database (and without LIGHTCRAFT_LENSFUN_DIR) asks once, "Download lens profiles?", with the size added up from the pinned files, and Download / Not Now. Download starts the existing pinned download (activity row, cancel); Not Now (or Esc) fetches nothing and the app doesn't ask again. Settings > General > Lens profiles has a Download Lens Database button and a switch that turns the offer back on. The answer is kept in ui.json (lensDatabase: ask / accepted / declined; an unreadable value asks again), and lensfun_download::fetch_on_start is the one place that decides. The CLI and headless MCP never ask and never fetch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
ef2b209030
commit
2cd071c722
@@ -255,7 +255,7 @@ pub(crate) fn open_session(in_memory: bool, launch: &Launch, seed_demo: bool) ->
|
||||
Launch::Ask => (Session::new().with_fs().with_default_denoise_models().with_system_clock(), None),
|
||||
Launch::Open { dir, .. } => open_library_session(in_memory, dir.clone(), seed_demo),
|
||||
};
|
||||
// lens profiles from the app's own lensfun copy, fetched by itself on first run (not for a throwaway session)
|
||||
// lens profiles from the app's own lensfun copy, offered on the first launch (not for a throwaway session)
|
||||
if !in_memory {
|
||||
s = s.with_default_lens_database().with_lens_auto_download();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Lens profiles looked up again when the lens database appears or changes. A photo's lens data is
|
||||
//! stored when it is imported ([`Photo::embedded_lens`]), so photos imported before the database was there say "no
|
||||
//! profile" until something reads their file again. When the database arrives (the first-run download) or is updated,
|
||||
//! profile" until something reads their file again. When the database arrives (downloaded on the user's request) or is updated,
|
||||
//! and when "Enable Lens Corrections" is ticked on a photo without a profile, every library raw that has no profile
|
||||
//! but whose lens the database knows by name is probed again and gets what Reload from Disk would give it: the
|
||||
//! profile, and the correction switched on where its settings are still what import gave it (an edited photo keeps
|
||||
@@ -144,7 +144,7 @@ fn download(s: &mut Session, _: &Value) -> Result<Value> {
|
||||
/// Fetch the database into `dir` and select it, so this session reads it once it is in place (a session that
|
||||
/// selected no database, e.g. a bare headless one, would otherwise download it and go on without it; the user's
|
||||
/// `LIGHTCRAFT_LENSFUN_DIR` still wins). Until the download is whole the folder has no marker and isn't read.
|
||||
pub(crate) fn start_download(
|
||||
pub fn start_download(
|
||||
s: &mut Session,
|
||||
dir: &std::path::Path,
|
||||
mirrors: Vec<String>,
|
||||
@@ -188,7 +188,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Download the Lens Database",
|
||||
[],
|
||||
None,
|
||||
"{} — fetch lensfun's lens database (CC BY-SA 3.0, pinned to one commit, checked file by file) into the app's data folder in the background, with an activity row, and read lens profiles from it once it is in place (unless LIGHTCRAFT_LENSFUN_DIR names another folder); the desktop app does this by itself on first run. LIGHTCRAFT_LENSFUN_URL lists other places to fetch the same files from → {started, dir}",
|
||||
"{} — fetch lensfun's lens database (CC BY-SA 3.0, pinned to one commit, checked file by file) into the app's data folder in the background, with an activity row, and read lens profiles from it once it is in place (unless LIGHTCRAFT_LENSFUN_DIR names another folder); the desktop app offers it on its first launch and in Settings. LIGHTCRAFT_LENSFUN_URL lists other places to fetch the same files from → {started, dir}",
|
||||
always,
|
||||
download
|
||||
),
|
||||
|
||||
@@ -159,8 +159,8 @@ impl Session {
|
||||
self
|
||||
}
|
||||
|
||||
/// Let the host fetch the lens database by itself when it is missing ([`crate::lensfun_download::fetch_on_start`]
|
||||
/// decides when). Separate from selecting a database that is already there ([`Self::with_default_lens_database`]).
|
||||
/// Let the host offer the lens database at its start, and fetch it there once the user said yes
|
||||
/// ([`crate::lensfun_download::fetch_on_start`] decides). Separate from selecting a database that is already there ([`Self::with_default_lens_database`]).
|
||||
pub fn with_lens_auto_download(mut self) -> Self {
|
||||
self.lens_auto_download = true;
|
||||
self
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//! When a raw carries no lens correction of its own (DNG opcodes, or a camera correction a raw reader converts,
|
||||
//! [`crate::files::embedded_lens`]), the lens named in its EXIF is looked up in a lensfun-format database. The
|
||||
//! database is lensfun's `data/db` (CC BY-SA 3.0): never bundled with the app, it is downloaded at run time into the
|
||||
//! app's data folder ([`crate::lensfun_download`], which the desktop app starts by itself on first run), or it is the
|
||||
//! app's data folder ([`crate::lensfun_download`], which the desktop app offers on its first launch and Settings can start), or it is the
|
||||
//! directory in `LIGHTCRAFT_LENSFUN_DIR` (which wins). The profile is converted to the same
|
||||
//! [`lightcraft_develop::EmbeddedLens`] form (marked [`LensSource::Database`]), so "Enable Profile Corrections", its
|
||||
//! amount sliders, the GPU path and DNG export treat both alike. Without a database nothing changes.
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
//! The lensfun lens database, downloaded by itself on first run (zero setup).
|
||||
//! The lensfun lens database, downloaded on request: the desktop app offers it once on the first launch
|
||||
//! ([`fetch_on_start`]: nothing is fetched without the user's click).
|
||||
//!
|
||||
//! lensfun's `data/db` (CC BY-SA 3.0, <https://github.com/lensfun/lensfun>) is data, never bundled with LightCraft and
|
||||
//! never committed to its repository: the desktop app fetches it at run time into its data folder
|
||||
//! never committed to its repository: the desktop app fetches it at run time, when the user chooses Download, into its data folder
|
||||
//! ([`default_dir`]), with a row in the activity stack, and [`crate::lens_profiles`] reads it from there. The files are
|
||||
//! pinned to one lensfun commit ([`COMMIT`]) by name, size and SHA-256 ([`FILES`]), so whatever mirror serves them
|
||||
//! ([`URL_ENV`] lists others, e.g. a copy on LightCraft's own server), what arrives is exactly that commit's data or
|
||||
@@ -138,16 +139,64 @@ pub fn recover(dir: &Path) {
|
||||
}
|
||||
}
|
||||
|
||||
/// The first-start policy, in one place: the folder to fetch the database into without being asked, or `None`. The
|
||||
/// host must allow it (`auto`, [`crate::Session::lens_auto_download`]); a folder of the user's own
|
||||
/// (`LIGHTCRAFT_LENSFUN_DIR`) is never replaced; and the app's folder must lack the pinned data.
|
||||
pub fn fetch_on_start(auto: bool) -> Option<PathBuf> {
|
||||
if !auto || std::env::var_os(crate::lens_profiles::ENV).is_some_and(|v| !v.is_empty()) {
|
||||
return None;
|
||||
/// The user's answer to the first-launch offer of the database (the desktop app keeps it in its settings).
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum Offer {
|
||||
/// Not asked yet: the first start asks.
|
||||
#[default]
|
||||
Ask,
|
||||
/// Download was clicked (in the offer or in Settings): a start downloads the pinned database when it is missing
|
||||
/// or from an older pin (an interrupted or failed download is tried again).
|
||||
Accepted,
|
||||
/// Not Now: nothing is downloaded and the start doesn't ask again (Settings can download it, or turn the offer
|
||||
/// back on).
|
||||
Declined,
|
||||
}
|
||||
|
||||
/// What a start does about the database ([`fetch_on_start`]).
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Start {
|
||||
Nothing,
|
||||
/// Ask the user (once): download into this folder, or not now.
|
||||
Ask(PathBuf),
|
||||
/// Download into this folder (the user said yes before).
|
||||
Fetch(PathBuf),
|
||||
}
|
||||
|
||||
/// The first-start policy, in one place: what a start does about the database. Nothing touches the network
|
||||
/// without the user's click: the host must allow the question at all (`auto`, [`crate::Session::lens_auto_download`],
|
||||
/// the desktop app only), a folder of the user's own (`LIGHTCRAFT_LENSFUN_DIR`) is never replaced, an installed
|
||||
/// database needs nothing, and otherwise the user's answer (`offer`) decides: ask, download, or nothing.
|
||||
pub fn fetch_on_start(auto: bool, offer: Offer) -> Start {
|
||||
let own = std::env::var_os(crate::lens_profiles::ENV).is_some_and(|v| !v.is_empty());
|
||||
let dir = default_dir();
|
||||
if let Some(d) = dir.as_deref().filter(|_| auto && !own) {
|
||||
recover(d);
|
||||
}
|
||||
let dir = default_dir()?;
|
||||
recover(&dir);
|
||||
(!installed(&dir)).then_some(dir)
|
||||
let installed = dir.as_deref().is_some_and(installed);
|
||||
decide(auto, own, dir, installed, offer)
|
||||
}
|
||||
|
||||
/// [`fetch_on_start`] without the file system: `own` (`LIGHTCRAFT_LENSFUN_DIR` is set), the app's folder `dir` and
|
||||
/// whether it holds the pinned data.
|
||||
pub fn decide(auto: bool, own: bool, dir: Option<PathBuf>, installed: bool, offer: Offer) -> Start {
|
||||
let Some(dir) = dir.filter(|_| auto && !own && !installed) else { return Start::Nothing };
|
||||
match offer {
|
||||
Offer::Ask => Start::Ask(dir),
|
||||
Offer::Accepted => Start::Fetch(dir),
|
||||
Offer::Declined => Start::Nothing,
|
||||
}
|
||||
}
|
||||
|
||||
/// The download's size in bytes: the pinned files' sizes added up (what the offer says).
|
||||
pub fn download_size() -> u64 {
|
||||
FILES.iter().map(|f| f.1).fold(0u64, u64::saturating_add)
|
||||
}
|
||||
|
||||
/// [`download_size`] in whole megabytes for people ("about 5 MB"), never 0.
|
||||
pub fn download_megabytes() -> u64 {
|
||||
(download_size().saturating_add(500_000) / 1_000_000).max(1)
|
||||
}
|
||||
|
||||
/// The mirrors to try, in order: [`URL_ENV`]'s, then lensfun's own ([`DEFAULT_MIRROR`]).
|
||||
@@ -566,6 +615,26 @@ mod tests {
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
/// The first start asks before anything is downloaded; Download (or Settings) means a start fetches a missing
|
||||
/// database; Not Now means nothing, ever, on its own; an installed database, a folder of the user's own or a host
|
||||
/// that may not ask (CLI, MCP) mean nothing whatever the answer.
|
||||
#[test]
|
||||
fn the_start_asks_once_and_downloads_only_after_a_yes() {
|
||||
let dir = || Some(PathBuf::from("lensfun"));
|
||||
assert_eq!(decide(true, false, dir(), false, Offer::Ask), Start::Ask(PathBuf::from("lensfun")), "the first start asks");
|
||||
assert_eq!(decide(true, false, dir(), false, Offer::Accepted), Start::Fetch(PathBuf::from("lensfun")));
|
||||
assert_eq!(decide(true, false, dir(), false, Offer::Declined), Start::Nothing, "Not Now: never asked again");
|
||||
for offer in [Offer::Ask, Offer::Accepted, Offer::Declined] {
|
||||
assert_eq!(decide(true, false, dir(), true, offer), Start::Nothing, "installed: no question, no download");
|
||||
assert_eq!(decide(false, false, dir(), false, offer), Start::Nothing, "a headless host never asks or fetches");
|
||||
assert_eq!(decide(true, true, dir(), false, offer), Start::Nothing, "the user's own folder");
|
||||
assert_eq!(decide(true, false, None, false, offer), Start::Nothing, "no data folder");
|
||||
}
|
||||
// the size the offer names comes from the pins
|
||||
assert_eq!(download_size(), FILES.iter().map(|f| f.1).sum::<u64>());
|
||||
assert!((4..=8).contains(&download_megabytes()), "{}", download_megabytes());
|
||||
}
|
||||
|
||||
/// The pinned list is lensfun's whole `data/db` at the commit: unique XML names, sane sizes, hex hashes.
|
||||
#[test]
|
||||
fn the_pins_are_well_formed() {
|
||||
|
||||
@@ -287,8 +287,8 @@ pub struct Session {
|
||||
pub face_downloads: face_download::Downloads,
|
||||
/// The lens database download ([`lensfun_download`]).
|
||||
pub lensfun: lensfun_download::Download,
|
||||
/// The host may fetch the lens database by itself when it is missing ([`Session::with_lens_auto_download`];
|
||||
/// when: [`lensfun_download::fetch_on_start`]).
|
||||
/// The host may offer the lens database at its start, and fetch it after the user's yes
|
||||
/// ([`Session::with_lens_auto_download`]; when: [`lensfun_download::fetch_on_start`]).
|
||||
pub lens_auto_download: bool,
|
||||
/// Reverse geocoding: the parsed place names and their download (`metadata.*PlaceNames`).
|
||||
pub place_names: cmd::geocode::PlaceNames,
|
||||
|
||||
@@ -2415,5 +2415,17 @@
|
||||
"1 photo got its lens profile": "1 fotka získala profil objektivu",
|
||||
"Looking up lens profiles": "Hledání profilů objektivů",
|
||||
"Downloading the lens database": "Stahování databáze objektivů",
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "Databázi objektivů se nepodařilo stáhnout. LightCraft to zkusí znovu při příštím spuštění."
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "Databázi objektivů se nepodařilo stáhnout. LightCraft to zkusí znovu při příštím spuštění.",
|
||||
"Lens Profiles": "Profily objektivů",
|
||||
"Download lens profiles?": "Stáhnout profily objektivů?",
|
||||
"LightCraft can correct distortion and vignetting for many lenses using the open lens database (lensfun, about {size} MB).": "LightCraft umí opravit zkreslení a vinětaci mnoha objektivů pomocí otevřené databáze objektivů (lensfun, asi {size} MB).",
|
||||
"Nothing is downloaded until you choose Download. You can download it later from Settings.": "Nic se nestáhne, dokud nezvolíte Stáhnout. Později ji můžete stáhnout v Nastavení.",
|
||||
"Lens profiles": "Profily objektivů",
|
||||
"Lens profiles are read from the folder LIGHTCRAFT_LENSFUN_DIR names.": "Profily objektivů se čtou ze složky, kterou určuje LIGHTCRAFT_LENSFUN_DIR.",
|
||||
"The lens database is installed.": "Databáze objektivů je nainstalována.",
|
||||
"The lens database is not installed (about {size} MB).": "Databáze objektivů není nainstalována (asi {size} MB).",
|
||||
"Lens database": "Databáze objektivů",
|
||||
"Download Lens Database": "Stáhnout databázi objektivů",
|
||||
"Offer the lens database download when LightCraft starts": "Nabídnout stažení databáze objektivů při spuštění LightCraftu",
|
||||
"lensfun's open lens database (CC BY-SA 3.0) corrects distortion and vignetting for many lenses. It is only downloaded when you choose to.": "Otevřená databáze objektivů lensfun (CC BY-SA 3.0) opravuje zkreslení a vinětaci mnoha objektivů. Stáhne se jen, když to zvolíte."
|
||||
}
|
||||
|
||||
@@ -3447,5 +3447,17 @@
|
||||
"1 photo got its lens profile": "1 Foto hat sein Objektivprofil erhalten",
|
||||
"Looking up lens profiles": "Objektivprofile werden gesucht",
|
||||
"Downloading the lens database": "Objektivdatenbank wird geladen",
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "Die Objektivdatenbank konnte nicht geladen werden. LightCraft versucht es beim nächsten Start erneut."
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "Die Objektivdatenbank konnte nicht geladen werden. LightCraft versucht es beim nächsten Start erneut.",
|
||||
"Lens Profiles": "Objektivprofile",
|
||||
"Download lens profiles?": "Objektivprofile laden?",
|
||||
"LightCraft can correct distortion and vignetting for many lenses using the open lens database (lensfun, about {size} MB).": "LightCraft kann Verzeichnung und Vignettierung für viele Objektive mit der offenen Objektivdatenbank korrigieren (lensfun, etwa {size} MB).",
|
||||
"Nothing is downloaded until you choose Download. You can download it later from Settings.": "Nichts wird geladen, bevor Sie „Laden“ wählen. Sie können sie später in den Einstellungen laden.",
|
||||
"Lens profiles": "Objektivprofile",
|
||||
"Lens profiles are read from the folder LIGHTCRAFT_LENSFUN_DIR names.": "Objektivprofile werden aus dem Ordner gelesen, den LIGHTCRAFT_LENSFUN_DIR nennt.",
|
||||
"The lens database is installed.": "Die Objektivdatenbank ist installiert.",
|
||||
"The lens database is not installed (about {size} MB).": "Die Objektivdatenbank ist nicht installiert (etwa {size} MB).",
|
||||
"Lens database": "Objektivdatenbank",
|
||||
"Download Lens Database": "Objektivdatenbank laden",
|
||||
"Offer the lens database download when LightCraft starts": "Beim Start von LightCraft anbieten, die Objektivdatenbank zu laden",
|
||||
"lensfun's open lens database (CC BY-SA 3.0) corrects distortion and vignetting for many lenses. It is only downloaded when you choose to.": "Die offene Objektivdatenbank von lensfun (CC BY-SA 3.0) korrigiert Verzeichnung und Vignettierung für viele Objektive. Sie wird nur geladen, wenn Sie es wählen."
|
||||
}
|
||||
|
||||
@@ -2365,5 +2365,17 @@
|
||||
"1 photo got its lens profile": "1 foto recibió su perfil de objetivo",
|
||||
"Looking up lens profiles": "Buscando perfiles de objetivo",
|
||||
"Downloading the lens database": "Descargando la base de datos de objetivos",
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "No se pudo descargar la base de datos de objetivos. LightCraft lo intentará de nuevo la próxima vez que se inicie."
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "No se pudo descargar la base de datos de objetivos. LightCraft lo intentará de nuevo la próxima vez que se inicie.",
|
||||
"Lens Profiles": "Perfiles de objetivo",
|
||||
"Download lens profiles?": "¿Descargar perfiles de objetivo?",
|
||||
"LightCraft can correct distortion and vignetting for many lenses using the open lens database (lensfun, about {size} MB).": "LightCraft puede corregir la distorsión y el viñeteado de muchos objetivos con la base de datos abierta de objetivos (lensfun, unos {size} MB).",
|
||||
"Nothing is downloaded until you choose Download. You can download it later from Settings.": "No se descarga nada hasta que elija Descargar. Puede descargarla más tarde desde Ajustes.",
|
||||
"Lens profiles": "Perfiles de objetivo",
|
||||
"Lens profiles are read from the folder LIGHTCRAFT_LENSFUN_DIR names.": "Los perfiles de objetivo se leen de la carpeta que indica LIGHTCRAFT_LENSFUN_DIR.",
|
||||
"The lens database is installed.": "La base de datos de objetivos está instalada.",
|
||||
"The lens database is not installed (about {size} MB).": "La base de datos de objetivos no está instalada (unos {size} MB).",
|
||||
"Lens database": "Base de datos de objetivos",
|
||||
"Download Lens Database": "Descargar base de datos de objetivos",
|
||||
"Offer the lens database download when LightCraft starts": "Ofrecer la descarga de la base de datos de objetivos al iniciar LightCraft",
|
||||
"lensfun's open lens database (CC BY-SA 3.0) corrects distortion and vignetting for many lenses. It is only downloaded when you choose to.": "La base de datos abierta de objetivos de lensfun (CC BY-SA 3.0) corrige la distorsión y el viñeteado de muchos objetivos. Solo se descarga si usted lo elige."
|
||||
}
|
||||
|
||||
@@ -2327,5 +2327,17 @@
|
||||
"Drop to open the face model": "Pudota avataksesi kasvomallin",
|
||||
"Dropped Files": "Pudotetut tiedostot",
|
||||
"Finish Lightroom catalog import before adding photos": "Viimeistele Lightroom-luettelon tuonti ennen kuvien lisäämistä",
|
||||
"Import dropped files without review": "Tuo pudotetut tiedostot ilman tarkistusta"
|
||||
"Import dropped files without review": "Tuo pudotetut tiedostot ilman tarkistusta",
|
||||
"Lens Profiles": "Objektiiviprofiilit",
|
||||
"Download lens profiles?": "Ladataanko objektiiviprofiilit?",
|
||||
"LightCraft can correct distortion and vignetting for many lenses using the open lens database (lensfun, about {size} MB).": "LightCraft voi korjata monien objektiivien vääristymän ja vinjetoinnin avoimen objektiivitietokannan avulla (lensfun, noin {size} Mt).",
|
||||
"Nothing is downloaded until you choose Download. You can download it later from Settings.": "Mitään ei ladata, ennen kuin valitset Lataa. Voit ladata sen myöhemmin asetuksista.",
|
||||
"Lens profiles": "Objektiiviprofiilit",
|
||||
"Lens profiles are read from the folder LIGHTCRAFT_LENSFUN_DIR names.": "Objektiiviprofiilit luetaan kansiosta, jonka LIGHTCRAFT_LENSFUN_DIR nimeää.",
|
||||
"The lens database is installed.": "Objektiivitietokanta on asennettu.",
|
||||
"The lens database is not installed (about {size} MB).": "Objektiivitietokantaa ei ole asennettu (noin {size} Mt).",
|
||||
"Lens database": "Objektiivitietokanta",
|
||||
"Download Lens Database": "Lataa objektiivitietokanta",
|
||||
"Offer the lens database download when LightCraft starts": "Tarjoa objektiivitietokannan latausta, kun LightCraft käynnistyy",
|
||||
"lensfun's open lens database (CC BY-SA 3.0) corrects distortion and vignetting for many lenses. It is only downloaded when you choose to.": "lensfunin avoin objektiivitietokanta (CC BY-SA 3.0) korjaa monien objektiivien vääristymän ja vinjetoinnin. Se ladataan vain, jos valitset niin."
|
||||
}
|
||||
|
||||
@@ -2397,5 +2397,17 @@
|
||||
"1 photo got its lens profile": "1 photo a reçu son profil d'objectif",
|
||||
"Looking up lens profiles": "Recherche des profils d'objectif",
|
||||
"Downloading the lens database": "Téléchargement de la base de données d'objectifs",
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "La base de données d’objectifs n’a pas pu être téléchargée. LightCraft réessaiera au prochain démarrage."
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "La base de données d’objectifs n’a pas pu être téléchargée. LightCraft réessaiera au prochain démarrage.",
|
||||
"Lens Profiles": "Profils d’objectif",
|
||||
"Download lens profiles?": "Télécharger les profils d’objectif ?",
|
||||
"LightCraft can correct distortion and vignetting for many lenses using the open lens database (lensfun, about {size} MB).": "LightCraft peut corriger la distorsion et le vignetage de nombreux objectifs grâce à la base d’objectifs ouverte (lensfun, environ {size} Mo).",
|
||||
"Nothing is downloaded until you choose Download. You can download it later from Settings.": "Rien n’est téléchargé tant que vous ne choisissez pas Télécharger. Vous pourrez la télécharger plus tard dans les Réglages.",
|
||||
"Lens profiles": "Profils d’objectif",
|
||||
"Lens profiles are read from the folder LIGHTCRAFT_LENSFUN_DIR names.": "Les profils d’objectif sont lus dans le dossier indiqué par LIGHTCRAFT_LENSFUN_DIR.",
|
||||
"The lens database is installed.": "La base d’objectifs est installée.",
|
||||
"The lens database is not installed (about {size} MB).": "La base d’objectifs n’est pas installée (environ {size} Mo).",
|
||||
"Lens database": "Base d’objectifs",
|
||||
"Download Lens Database": "Télécharger la base d’objectifs",
|
||||
"Offer the lens database download when LightCraft starts": "Proposer le téléchargement de la base d’objectifs au démarrage de LightCraft",
|
||||
"lensfun's open lens database (CC BY-SA 3.0) corrects distortion and vignetting for many lenses. It is only downloaded when you choose to.": "La base d’objectifs ouverte de lensfun (CC BY-SA 3.0) corrige la distorsion et le vignetage de nombreux objectifs. Elle n’est téléchargée que si vous le choisissez."
|
||||
}
|
||||
|
||||
@@ -2339,5 +2339,17 @@
|
||||
"Drop to open the face model": "Jatuhkan untuk membuka model wajah",
|
||||
"Dropped Files": "File yang dijatuhkan",
|
||||
"Finish Lightroom catalog import before adding photos": "Selesaikan impor katalog Lightroom sebelum menambahkan foto",
|
||||
"Import dropped files without review": "Impor file yang dijatuhkan tanpa tinjauan"
|
||||
"Import dropped files without review": "Impor file yang dijatuhkan tanpa tinjauan",
|
||||
"Lens Profiles": "Profil lensa",
|
||||
"Download lens profiles?": "Unduh profil lensa?",
|
||||
"LightCraft can correct distortion and vignetting for many lenses using the open lens database (lensfun, about {size} MB).": "LightCraft dapat mengoreksi distorsi dan vinyet untuk banyak lensa menggunakan basis data lensa terbuka (lensfun, sekitar {size} MB).",
|
||||
"Nothing is downloaded until you choose Download. You can download it later from Settings.": "Tidak ada yang diunduh sampai Anda memilih Unduh. Anda dapat mengunduhnya nanti dari Pengaturan.",
|
||||
"Lens profiles": "Profil lensa",
|
||||
"Lens profiles are read from the folder LIGHTCRAFT_LENSFUN_DIR names.": "Profil lensa dibaca dari folder yang disebut LIGHTCRAFT_LENSFUN_DIR.",
|
||||
"The lens database is installed.": "Basis data lensa sudah terpasang.",
|
||||
"The lens database is not installed (about {size} MB).": "Basis data lensa belum terpasang (sekitar {size} MB).",
|
||||
"Lens database": "Basis data lensa",
|
||||
"Download Lens Database": "Unduh Basis Data Lensa",
|
||||
"Offer the lens database download when LightCraft starts": "Tawarkan unduhan basis data lensa saat LightCraft dimulai",
|
||||
"lensfun's open lens database (CC BY-SA 3.0) corrects distortion and vignetting for many lenses. It is only downloaded when you choose to.": "Basis data lensa terbuka lensfun (CC BY-SA 3.0) mengoreksi distorsi dan vinyet untuk banyak lensa. Basis data ini hanya diunduh jika Anda memilihnya."
|
||||
}
|
||||
|
||||
@@ -2368,5 +2368,17 @@
|
||||
"1 photo got its lens profile": "1 foto ha ricevuto il profilo dell'obiettivo",
|
||||
"Looking up lens profiles": "Ricerca dei profili degli obiettivi",
|
||||
"Downloading the lens database": "Download del database degli obiettivi",
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "Impossibile scaricare il database degli obiettivi. LightCraft riproverà al prossimo avvio."
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "Impossibile scaricare il database degli obiettivi. LightCraft riproverà al prossimo avvio.",
|
||||
"Lens Profiles": "Profili obiettivo",
|
||||
"Download lens profiles?": "Scaricare i profili degli obiettivi?",
|
||||
"LightCraft can correct distortion and vignetting for many lenses using the open lens database (lensfun, about {size} MB).": "LightCraft può correggere distorsione e vignettatura per molti obiettivi usando il database aperto degli obiettivi (lensfun, circa {size} MB).",
|
||||
"Nothing is downloaded until you choose Download. You can download it later from Settings.": "Non viene scaricato nulla finché non scegli Scarica. Puoi scaricarlo più tardi dalle Impostazioni.",
|
||||
"Lens profiles": "Profili obiettivo",
|
||||
"Lens profiles are read from the folder LIGHTCRAFT_LENSFUN_DIR names.": "I profili obiettivo vengono letti dalla cartella indicata da LIGHTCRAFT_LENSFUN_DIR.",
|
||||
"The lens database is installed.": "Il database degli obiettivi è installato.",
|
||||
"The lens database is not installed (about {size} MB).": "Il database degli obiettivi non è installato (circa {size} MB).",
|
||||
"Lens database": "Database obiettivi",
|
||||
"Download Lens Database": "Scarica database obiettivi",
|
||||
"Offer the lens database download when LightCraft starts": "Offri il download del database obiettivi all’avvio di LightCraft",
|
||||
"lensfun's open lens database (CC BY-SA 3.0) corrects distortion and vignetting for many lenses. It is only downloaded when you choose to.": "Il database aperto degli obiettivi di lensfun (CC BY-SA 3.0) corregge distorsione e vignettatura per molti obiettivi. Viene scaricato solo se lo scegli."
|
||||
}
|
||||
|
||||
@@ -2363,5 +2363,17 @@
|
||||
"1 photo got its lens profile": "1 枚の写真にレンズプロファイルが適用されました",
|
||||
"Looking up lens profiles": "レンズプロファイルを検索中",
|
||||
"Downloading the lens database": "レンズデータベースをダウンロード中",
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "レンズデータベースをダウンロードできませんでした。LightCraft は次回の起動時に再試行します。"
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "レンズデータベースをダウンロードできませんでした。LightCraft は次回の起動時に再試行します。",
|
||||
"Lens Profiles": "レンズプロファイル",
|
||||
"Download lens profiles?": "レンズプロファイルをダウンロードしますか?",
|
||||
"LightCraft can correct distortion and vignetting for many lenses using the open lens database (lensfun, about {size} MB).": "LightCraft はオープンなレンズデータベース(lensfun、約 {size} MB)を使って、多くのレンズの歪曲収差と周辺減光を補正できます。",
|
||||
"Nothing is downloaded until you choose Download. You can download it later from Settings.": "「ダウンロード」を選ぶまで何もダウンロードしません。あとで設定からダウンロードすることもできます。",
|
||||
"Lens profiles": "レンズプロファイル",
|
||||
"Lens profiles are read from the folder LIGHTCRAFT_LENSFUN_DIR names.": "レンズプロファイルは LIGHTCRAFT_LENSFUN_DIR が指定するフォルダーから読み込まれます。",
|
||||
"The lens database is installed.": "レンズデータベースはインストール済みです。",
|
||||
"The lens database is not installed (about {size} MB).": "レンズデータベースはインストールされていません(約 {size} MB)。",
|
||||
"Lens database": "レンズデータベース",
|
||||
"Download Lens Database": "レンズデータベースをダウンロード",
|
||||
"Offer the lens database download when LightCraft starts": "LightCraft の起動時にレンズデータベースのダウンロードを提案する",
|
||||
"lensfun's open lens database (CC BY-SA 3.0) corrects distortion and vignetting for many lenses. It is only downloaded when you choose to.": "lensfun のオープンなレンズデータベース(CC BY-SA 3.0)は多くのレンズの歪曲収差と周辺減光を補正します。選んだときにだけダウンロードされます。"
|
||||
}
|
||||
|
||||
@@ -2741,5 +2741,17 @@
|
||||
"1 photo got its lens profile": "1 ფოტომ მიიღო ობიექტივის პროფილი",
|
||||
"Looking up lens profiles": "ობიექტივის პროფილების ძიება",
|
||||
"Downloading the lens database": "ობიექტივების ბაზის ჩამოტვირთვა",
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "ობიექტივების მონაცემთა ბაზის ჩამოტვირთვა ვერ მოხერხდა. LightCraft კვლავ სცდის შემდეგ გაშვებისას."
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "ობიექტივების მონაცემთა ბაზის ჩამოტვირთვა ვერ მოხერხდა. LightCraft კვლავ სცდის შემდეგ გაშვებისას.",
|
||||
"Lens Profiles": "ობიექტივის პროფილები",
|
||||
"Download lens profiles?": "ჩამოვტვირთო ობიექტივის პროფილები?",
|
||||
"LightCraft can correct distortion and vignetting for many lenses using the open lens database (lensfun, about {size} MB).": "LightCraft-ს შეუძლია მრავალი ობიექტივის დისტორსიისა და ვინეტირების გასწორება ობიექტივების ღია ბაზით (lensfun, დაახლოებით {size} მბ).",
|
||||
"Nothing is downloaded until you choose Download. You can download it later from Settings.": "არაფერი ჩამოიტვირთება, სანამ „ჩამოტვირთვას“ არ აირჩევთ. მოგვიანებით მისი ჩამოტვირთვა პარამეტრებიდანაც შეგიძლიათ.",
|
||||
"Lens profiles": "ობიექტივის პროფილები",
|
||||
"Lens profiles are read from the folder LIGHTCRAFT_LENSFUN_DIR names.": "ობიექტივის პროფილები იკითხება საქაღალდიდან, რომელსაც LIGHTCRAFT_LENSFUN_DIR ასახელებს.",
|
||||
"The lens database is installed.": "ობიექტივების ბაზა დაყენებულია.",
|
||||
"The lens database is not installed (about {size} MB).": "ობიექტივების ბაზა არ არის დაყენებული (დაახლოებით {size} მბ).",
|
||||
"Lens database": "ობიექტივების ბაზა",
|
||||
"Download Lens Database": "ობიექტივების ბაზის ჩამოტვირთვა",
|
||||
"Offer the lens database download when LightCraft starts": "LightCraft-ის გაშვებისას ობიექტივების ბაზის ჩამოტვირთვის შეთავაზება",
|
||||
"lensfun's open lens database (CC BY-SA 3.0) corrects distortion and vignetting for many lenses. It is only downloaded when you choose to.": "lensfun-ის ობიექტივების ღია ბაზა (CC BY-SA 3.0) ასწორებს მრავალი ობიექტივის დისტორსიასა და ვინეტირებას. ის მხოლოდ თქვენი არჩევით ჩამოიტვირთება."
|
||||
}
|
||||
|
||||
@@ -2290,5 +2290,17 @@
|
||||
"1 photo got its lens profile": "1 zdjęcie otrzymało profil obiektywu",
|
||||
"Looking up lens profiles": "Wyszukiwanie profili obiektywów",
|
||||
"Downloading the lens database": "Pobieranie bazy obiektywów",
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "Nie udało się pobrać bazy obiektywów. LightCraft spróbuje ponownie przy następnym uruchomieniu."
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "Nie udało się pobrać bazy obiektywów. LightCraft spróbuje ponownie przy następnym uruchomieniu.",
|
||||
"Lens Profiles": "Profile obiektywów",
|
||||
"Download lens profiles?": "Pobrać profile obiektywów?",
|
||||
"LightCraft can correct distortion and vignetting for many lenses using the open lens database (lensfun, about {size} MB).": "LightCraft może korygować dystorsję i winietowanie wielu obiektywów, korzystając z otwartej bazy obiektywów (lensfun, około {size} MB).",
|
||||
"Nothing is downloaded until you choose Download. You can download it later from Settings.": "Nic nie zostanie pobrane, dopóki nie wybierzesz Pobierz. Możesz ją pobrać później w Ustawieniach.",
|
||||
"Lens profiles": "Profile obiektywów",
|
||||
"Lens profiles are read from the folder LIGHTCRAFT_LENSFUN_DIR names.": "Profile obiektywów są odczytywane z folderu wskazanego przez LIGHTCRAFT_LENSFUN_DIR.",
|
||||
"The lens database is installed.": "Baza obiektywów jest zainstalowana.",
|
||||
"The lens database is not installed (about {size} MB).": "Baza obiektywów nie jest zainstalowana (około {size} MB).",
|
||||
"Lens database": "Baza obiektywów",
|
||||
"Download Lens Database": "Pobierz bazę obiektywów",
|
||||
"Offer the lens database download when LightCraft starts": "Proponuj pobranie bazy obiektywów przy uruchomieniu LightCraft",
|
||||
"lensfun's open lens database (CC BY-SA 3.0) corrects distortion and vignetting for many lenses. It is only downloaded when you choose to.": "Otwarta baza obiektywów lensfun (CC BY-SA 3.0) koryguje dystorsję i winietowanie wielu obiektywów. Jest pobierana tylko wtedy, gdy to wybierzesz."
|
||||
}
|
||||
|
||||
@@ -2209,5 +2209,17 @@
|
||||
"1 photo got its lens profile": "1 foto recebeu o perfil da lente",
|
||||
"Looking up lens profiles": "Procurando perfis de lente",
|
||||
"Downloading the lens database": "Baixando o banco de dados de lentes",
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "Não foi possível baixar o banco de dados de lentes. O LightCraft tentará novamente na próxima vez que for aberto."
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "Não foi possível baixar o banco de dados de lentes. O LightCraft tentará novamente na próxima vez que for aberto.",
|
||||
"Lens Profiles": "Perfis de lente",
|
||||
"Download lens profiles?": "Baixar perfis de lente?",
|
||||
"LightCraft can correct distortion and vignetting for many lenses using the open lens database (lensfun, about {size} MB).": "O LightCraft pode corrigir distorção e vinheta de muitas lentes usando o banco de dados aberto de lentes (lensfun, cerca de {size} MB).",
|
||||
"Nothing is downloaded until you choose Download. You can download it later from Settings.": "Nada é baixado até você escolher Baixar. Você pode baixá-lo depois em Configurações.",
|
||||
"Lens profiles": "Perfis de lente",
|
||||
"Lens profiles are read from the folder LIGHTCRAFT_LENSFUN_DIR names.": "Os perfis de lente são lidos da pasta indicada por LIGHTCRAFT_LENSFUN_DIR.",
|
||||
"The lens database is installed.": "O banco de dados de lentes está instalado.",
|
||||
"The lens database is not installed (about {size} MB).": "O banco de dados de lentes não está instalado (cerca de {size} MB).",
|
||||
"Lens database": "Banco de dados de lentes",
|
||||
"Download Lens Database": "Baixar banco de dados de lentes",
|
||||
"Offer the lens database download when LightCraft starts": "Oferecer o download do banco de dados de lentes ao iniciar o LightCraft",
|
||||
"lensfun's open lens database (CC BY-SA 3.0) corrects distortion and vignetting for many lenses. It is only downloaded when you choose to.": "O banco de dados aberto de lentes do lensfun (CC BY-SA 3.0) corrige distorção e vinheta de muitas lentes. Ele só é baixado quando você escolhe."
|
||||
}
|
||||
|
||||
@@ -2356,5 +2356,17 @@
|
||||
"1 photo got its lens profile": "1 фото получило профиль объектива",
|
||||
"Looking up lens profiles": "Поиск профилей объективов",
|
||||
"Downloading the lens database": "Загрузка базы объективов",
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "Не удалось загрузить базу данных объективов. LightCraft попробует снова при следующем запуске."
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "Не удалось загрузить базу данных объективов. LightCraft попробует снова при следующем запуске.",
|
||||
"Lens Profiles": "Профили объективов",
|
||||
"Download lens profiles?": "Загрузить профили объективов?",
|
||||
"LightCraft can correct distortion and vignetting for many lenses using the open lens database (lensfun, about {size} MB).": "LightCraft может исправлять дисторсию и виньетирование для многих объективов с помощью открытой базы объективов (lensfun, около {size} МБ).",
|
||||
"Nothing is downloaded until you choose Download. You can download it later from Settings.": "Ничего не загружается, пока вы не выберете «Загрузить». Загрузить её можно и позже в настройках.",
|
||||
"Lens profiles": "Профили объективов",
|
||||
"Lens profiles are read from the folder LIGHTCRAFT_LENSFUN_DIR names.": "Профили объективов читаются из папки, указанной в LIGHTCRAFT_LENSFUN_DIR.",
|
||||
"The lens database is installed.": "База объективов установлена.",
|
||||
"The lens database is not installed (about {size} MB).": "База объективов не установлена (около {size} МБ).",
|
||||
"Lens database": "База объективов",
|
||||
"Download Lens Database": "Загрузить базу объективов",
|
||||
"Offer the lens database download when LightCraft starts": "Предлагать загрузку базы объективов при запуске LightCraft",
|
||||
"lensfun's open lens database (CC BY-SA 3.0) corrects distortion and vignetting for many lenses. It is only downloaded when you choose to.": "Открытая база объективов lensfun (CC BY-SA 3.0) исправляет дисторсию и виньетирование для многих объективов. Она загружается, только если вы это выберете."
|
||||
}
|
||||
|
||||
@@ -2429,5 +2429,17 @@
|
||||
"Drop to open the face model": "Släpp för att öppna ansiktsmodellen",
|
||||
"Dropped Files": "Släppta filer",
|
||||
"Finish Lightroom catalog import before adding photos": "Slutför importen av Lightroom-katalogen innan du lägger till foton",
|
||||
"Import dropped files without review": "Importera släppta filer utan granskning"
|
||||
"Import dropped files without review": "Importera släppta filer utan granskning",
|
||||
"Lens Profiles": "Objektivprofiler",
|
||||
"Download lens profiles?": "Hämta objektivprofiler?",
|
||||
"LightCraft can correct distortion and vignetting for many lenses using the open lens database (lensfun, about {size} MB).": "LightCraft kan korrigera distorsion och vinjettering för många objektiv med den öppna objektivdatabasen (lensfun, cirka {size} MB).",
|
||||
"Nothing is downloaded until you choose Download. You can download it later from Settings.": "Inget hämtas förrän du väljer Hämta. Du kan hämta den senare i Inställningar.",
|
||||
"Lens profiles": "Objektivprofiler",
|
||||
"Lens profiles are read from the folder LIGHTCRAFT_LENSFUN_DIR names.": "Objektivprofiler läses från mappen som LIGHTCRAFT_LENSFUN_DIR anger.",
|
||||
"The lens database is installed.": "Objektivdatabasen är installerad.",
|
||||
"The lens database is not installed (about {size} MB).": "Objektivdatabasen är inte installerad (cirka {size} MB).",
|
||||
"Lens database": "Objektivdatabas",
|
||||
"Download Lens Database": "Hämta objektivdatabas",
|
||||
"Offer the lens database download when LightCraft starts": "Erbjud att hämta objektivdatabasen när LightCraft startar",
|
||||
"lensfun's open lens database (CC BY-SA 3.0) corrects distortion and vignetting for many lenses. It is only downloaded when you choose to.": "lensfuns öppna objektivdatabas (CC BY-SA 3.0) korrigerar distorsion och vinjettering för många objektiv. Den hämtas bara när du väljer det."
|
||||
}
|
||||
|
||||
@@ -2361,5 +2361,17 @@
|
||||
"1 photo got its lens profile": "1 фото отримало профіль об'єктива",
|
||||
"Looking up lens profiles": "Пошук профілів об'єктивів",
|
||||
"Downloading the lens database": "Завантаження бази об'єктивів",
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "Не вдалося завантажити базу даних об’єктивів. LightCraft спробує знову під час наступного запуску."
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "Не вдалося завантажити базу даних об’єктивів. LightCraft спробує знову під час наступного запуску.",
|
||||
"Lens Profiles": "Профілі об’єктивів",
|
||||
"Download lens profiles?": "Завантажити профілі об’єктивів?",
|
||||
"LightCraft can correct distortion and vignetting for many lenses using the open lens database (lensfun, about {size} MB).": "LightCraft може виправляти дисторсію та віньєтування для багатьох об’єктивів за допомогою відкритої бази об’єктивів (lensfun, близько {size} МБ).",
|
||||
"Nothing is downloaded until you choose Download. You can download it later from Settings.": "Нічого не завантажується, доки ви не виберете «Завантажити». Завантажити її можна й пізніше в налаштуваннях.",
|
||||
"Lens profiles": "Профілі об’єктивів",
|
||||
"Lens profiles are read from the folder LIGHTCRAFT_LENSFUN_DIR names.": "Профілі об’єктивів читаються з теки, яку вказує LIGHTCRAFT_LENSFUN_DIR.",
|
||||
"The lens database is installed.": "Базу об’єктивів установлено.",
|
||||
"The lens database is not installed (about {size} MB).": "Базу об’єктивів не встановлено (близько {size} МБ).",
|
||||
"Lens database": "База об’єктивів",
|
||||
"Download Lens Database": "Завантажити базу об’єктивів",
|
||||
"Offer the lens database download when LightCraft starts": "Пропонувати завантажити базу об’єктивів під час запуску LightCraft",
|
||||
"lensfun's open lens database (CC BY-SA 3.0) corrects distortion and vignetting for many lenses. It is only downloaded when you choose to.": "Відкрита база об’єктивів lensfun (CC BY-SA 3.0) виправляє дисторсію та віньєтування для багатьох об’єктивів. Вона завантажується, лише якщо ви це виберете."
|
||||
}
|
||||
|
||||
@@ -2235,5 +2235,17 @@
|
||||
"Drop to open the face model": "Thả để mở mô hình khuôn mặt",
|
||||
"Dropped Files": "Tệp đã thả",
|
||||
"Finish Lightroom catalog import before adding photos": "Hoàn tất nhập danh mục Lightroom trước khi thêm ảnh",
|
||||
"Import dropped files without review": "Nhập các tệp đã thả mà không xem lại"
|
||||
"Import dropped files without review": "Nhập các tệp đã thả mà không xem lại",
|
||||
"Lens Profiles": "Hồ sơ ống kính",
|
||||
"Download lens profiles?": "Tải hồ sơ ống kính?",
|
||||
"LightCraft can correct distortion and vignetting for many lenses using the open lens database (lensfun, about {size} MB).": "LightCraft có thể sửa méo hình và tối góc cho nhiều ống kính bằng cơ sở dữ liệu ống kính mở (lensfun, khoảng {size} MB).",
|
||||
"Nothing is downloaded until you choose Download. You can download it later from Settings.": "Không có gì được tải xuống cho đến khi bạn chọn Tải xuống. Bạn có thể tải sau trong Cài đặt.",
|
||||
"Lens profiles": "Hồ sơ ống kính",
|
||||
"Lens profiles are read from the folder LIGHTCRAFT_LENSFUN_DIR names.": "Hồ sơ ống kính được đọc từ thư mục mà LIGHTCRAFT_LENSFUN_DIR chỉ định.",
|
||||
"The lens database is installed.": "Cơ sở dữ liệu ống kính đã được cài đặt.",
|
||||
"The lens database is not installed (about {size} MB).": "Cơ sở dữ liệu ống kính chưa được cài đặt (khoảng {size} MB).",
|
||||
"Lens database": "Cơ sở dữ liệu ống kính",
|
||||
"Download Lens Database": "Tải cơ sở dữ liệu ống kính",
|
||||
"Offer the lens database download when LightCraft starts": "Đề nghị tải cơ sở dữ liệu ống kính khi LightCraft khởi động",
|
||||
"lensfun's open lens database (CC BY-SA 3.0) corrects distortion and vignetting for many lenses. It is only downloaded when you choose to.": "Cơ sở dữ liệu ống kính mở của lensfun (CC BY-SA 3.0) sửa méo hình và tối góc cho nhiều ống kính. Nó chỉ được tải khi bạn chọn."
|
||||
}
|
||||
|
||||
@@ -2207,5 +2207,17 @@
|
||||
"1 photo got its lens profile": "1 张照片获得了镜头配置文件",
|
||||
"Looking up lens profiles": "正在查找镜头配置文件",
|
||||
"Downloading the lens database": "正在下载镜头数据库",
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "无法下载镜头数据库。LightCraft 会在下次启动时重试。"
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "无法下载镜头数据库。LightCraft 会在下次启动时重试。",
|
||||
"Lens Profiles": "镜头配置文件",
|
||||
"Download lens profiles?": "下载镜头配置文件?",
|
||||
"LightCraft can correct distortion and vignetting for many lenses using the open lens database (lensfun, about {size} MB).": "LightCraft 可以使用开放的镜头数据库(lensfun,约 {size} MB)校正许多镜头的畸变和暗角。",
|
||||
"Nothing is downloaded until you choose Download. You can download it later from Settings.": "在您选择“下载”之前不会下载任何内容。您也可以稍后在设置中下载。",
|
||||
"Lens profiles": "镜头配置文件",
|
||||
"Lens profiles are read from the folder LIGHTCRAFT_LENSFUN_DIR names.": "镜头配置文件从 LIGHTCRAFT_LENSFUN_DIR 指定的文件夹读取。",
|
||||
"The lens database is installed.": "镜头数据库已安装。",
|
||||
"The lens database is not installed (about {size} MB).": "镜头数据库未安装(约 {size} MB)。",
|
||||
"Lens database": "镜头数据库",
|
||||
"Download Lens Database": "下载镜头数据库",
|
||||
"Offer the lens database download when LightCraft starts": "LightCraft 启动时提示下载镜头数据库",
|
||||
"lensfun's open lens database (CC BY-SA 3.0) corrects distortion and vignetting for many lenses. It is only downloaded when you choose to.": "lensfun 的开放镜头数据库(CC BY-SA 3.0)可校正许多镜头的畸变和暗角。只有在您选择时才会下载。"
|
||||
}
|
||||
|
||||
@@ -2164,5 +2164,17 @@
|
||||
"1 photo got its lens profile": "1 張照片取得了鏡頭設定檔",
|
||||
"Looking up lens profiles": "正在查找鏡頭設定檔",
|
||||
"Downloading the lens database": "正在下載鏡頭資料庫",
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "無法下載鏡頭資料庫。LightCraft 會在下次啟動時重試。"
|
||||
"The lens database could not be downloaded. LightCraft tries again the next time it starts.": "無法下載鏡頭資料庫。LightCraft 會在下次啟動時重試。",
|
||||
"Lens Profiles": "鏡頭描述檔",
|
||||
"Download lens profiles?": "下載鏡頭描述檔?",
|
||||
"LightCraft can correct distortion and vignetting for many lenses using the open lens database (lensfun, about {size} MB).": "LightCraft 可以使用開放的鏡頭資料庫(lensfun,約 {size} MB)校正許多鏡頭的變形與暗角。",
|
||||
"Nothing is downloaded until you choose Download. You can download it later from Settings.": "在您選擇「下載」之前不會下載任何內容。您也可以稍後在設定中下載。",
|
||||
"Lens profiles": "鏡頭描述檔",
|
||||
"Lens profiles are read from the folder LIGHTCRAFT_LENSFUN_DIR names.": "鏡頭描述檔從 LIGHTCRAFT_LENSFUN_DIR 指定的資料夾讀取。",
|
||||
"The lens database is installed.": "鏡頭資料庫已安裝。",
|
||||
"The lens database is not installed (about {size} MB).": "鏡頭資料庫未安裝(約 {size} MB)。",
|
||||
"Lens database": "鏡頭資料庫",
|
||||
"Download Lens Database": "下載鏡頭資料庫",
|
||||
"Offer the lens database download when LightCraft starts": "LightCraft 啟動時提示下載鏡頭資料庫",
|
||||
"lensfun's open lens database (CC BY-SA 3.0) corrects distortion and vignetting for many lenses. It is only downloaded when you choose to.": "lensfun 的開放鏡頭資料庫(CC BY-SA 3.0)可校正許多鏡頭的變形與暗角。只有在您選擇時才會下載。"
|
||||
}
|
||||
|
||||
@@ -151,6 +151,7 @@ pub fn show(app: &mut LightcraftApp, ctx: &egui::Context, blocked: bool) {
|
||||
Dialog::MovePhotos { .. } => "Move Photos",
|
||||
Dialog::LightroomCatalog { .. } => "Lightroom Classic Catalog",
|
||||
Dialog::LightroomDestination { .. } => "Import Lightroom Catalog",
|
||||
Dialog::LensDatabase { .. } => "Lens Profiles",
|
||||
Dialog::SaveEffect { .. } => "New Effect Preset",
|
||||
Dialog::ManageEffects { .. } => "Manage Effect Presets",
|
||||
Dialog::DeleteEffects { names } if names.len() == 1 => "Delete Effect Preset",
|
||||
@@ -208,6 +209,7 @@ pub fn show(app: &mut LightcraftApp, ctx: &egui::Context, blocked: bool) {
|
||||
Dialog::FaceModel { .. } => 460.0,
|
||||
Dialog::LightroomCatalog { .. } => 440.0,
|
||||
Dialog::LightroomDestination { .. } => 480.0,
|
||||
Dialog::LensDatabase { .. } => 440.0,
|
||||
Dialog::ManageEffects { .. } => 560.0,
|
||||
_ => 380.0,
|
||||
})
|
||||
@@ -1195,6 +1197,7 @@ pub fn show(app: &mut LightcraftApp, ctx: &egui::Context, blocked: bool) {
|
||||
ui.label(egui::RichText::new(crate::i18n::tr(what)).color(t.text_dim));
|
||||
}
|
||||
d @ Dialog::LightroomDestination { .. } => crate::lightroom_destination::body(app, ui, d),
|
||||
Dialog::LensDatabase { .. } => crate::lens_db::offer(ui),
|
||||
Dialog::RemoveFolder { name, count, path, disk } => {
|
||||
ui.label(crate::i18n::tr_format!(
|
||||
"Remove “{name}” and its {count} photo{} from the library?",
|
||||
@@ -1281,7 +1284,7 @@ pub fn show(app: &mut LightcraftApp, ctx: &egui::Context, blocked: bool) {
|
||||
let sam_nowhere = sam_by_hand(sam);
|
||||
let cancel = match &dlg {
|
||||
Dialog::SamModel { .. } if sam_running || sam_installed || sam_nowhere => "Close",
|
||||
Dialog::SamModel { .. } => "Not Now",
|
||||
Dialog::SamModel { .. } | Dialog::LensDatabase { .. } => "Not Now",
|
||||
Dialog::LightroomResult { .. } => "Close",
|
||||
_ => "Cancel",
|
||||
};
|
||||
@@ -1313,6 +1316,7 @@ pub fn show(app: &mut LightcraftApp, ctx: &egui::Context, blocked: bool) {
|
||||
Dialog::RemoveFolder { .. } => "Remove",
|
||||
Dialog::LightroomCatalog { .. } => "Import Lightroom Catalog…",
|
||||
d @ Dialog::LightroomDestination { .. } => crate::lightroom_destination::ok_label(d),
|
||||
Dialog::LensDatabase { .. } => "Download",
|
||||
Dialog::SynchronizeFolder { .. } if informational => "Close",
|
||||
Dialog::SynchronizeFolder { .. } => "Synchronize",
|
||||
Dialog::SamModel { then: Some(_), .. } if sam_installed => "Continue",
|
||||
|
||||
@@ -222,6 +222,9 @@ pub fn confirm_dialog(app: &mut LightcraftApp, dlg: &Dialog) -> Result<serde_jso
|
||||
app.run("file.importLightroom", catalog.as_ref().map_or_else(|| json!({}), |c| json!({"path": c})))
|
||||
}
|
||||
d @ Dialog::LightroomDestination { .. } => crate::lightroom_destination::confirm(app, d),
|
||||
Dialog::LensDatabase { dir, mirrors } => {
|
||||
crate::lens_db::download(app, std::path::Path::new(dir), mirrors.clone()).map(|()| json!({"downloading": true}))
|
||||
}
|
||||
Dialog::SaveEffect { name, group, fields, amount, brush } => {
|
||||
crate::panels::mask_effects::save_confirm(app, name, group, fields, *amount, *brush)
|
||||
}
|
||||
|
||||
@@ -105,6 +105,50 @@ pub(super) fn general_tab(app: &mut LightcraftApp, ui: &mut egui::Ui, t: &Tokens
|
||||
"To hand this editor the original file itself instead (the raw, without a copy: DxO PhotoLab, for example), right-click a photo → Edit In → Open Original in Other Editor (RAW, no copy).",
|
||||
),
|
||||
);
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
lens_profiles(app, ui, t);
|
||||
}
|
||||
|
||||
/// Lens profiles: whether the lens database is here, a button that downloads it (nothing is fetched without a
|
||||
/// click: the first launch offers it once, [`crate::lens_db`]), and the startup offer turned back on or off.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn lens_profiles(app: &mut LightcraftApp, ui: &mut egui::Ui, t: &Tokens) {
|
||||
use lightcraft_engine::lensfun_download::{self, Offer};
|
||||
heading(ui, t, crate::i18n::tr("Lens profiles"));
|
||||
let running = app.session.lensfun.running();
|
||||
// (the user's own folder, LIGHTCRAFT_LENSFUN_DIR, is read instead and never replaced)
|
||||
let own = std::env::var_os(lightcraft_engine::lens_profiles::ENV).is_some_and(|v| !v.is_empty());
|
||||
let installed = lensfun_download::default_dir().is_some_and(|d| lensfun_download::installed(&d));
|
||||
let status = if own {
|
||||
crate::i18n::tr("Lens profiles are read from the folder LIGHTCRAFT_LENSFUN_DIR names.").to_string()
|
||||
} else if running {
|
||||
crate::i18n::tr("Downloading the lens database…").to_string()
|
||||
} else if installed {
|
||||
crate::i18n::tr("The lens database is installed.").to_string()
|
||||
} else {
|
||||
crate::i18n::tr_format!("The lens database is not installed (about {size} MB).", size = lensfun_download::download_megabytes())
|
||||
};
|
||||
row(ui, t, crate::i18n::tr("Lens database"), |ui| {
|
||||
ui.label(RichText::new(status).color(t.text));
|
||||
let r = ui.add_enabled(!own && !running && !installed, egui::Button::new(crate::i18n::tr("Download Lens Database")));
|
||||
register(ui, "button:settingsLensDatabase", r.rect);
|
||||
if r.clicked()
|
||||
&& let Err(e) = crate::lens_db::download_default(app)
|
||||
{
|
||||
app.toast(ui.ctx(), e);
|
||||
}
|
||||
});
|
||||
let mut offer = app.ui.settings.lens_database != Offer::Declined;
|
||||
if check(ui, "settings.lensDatabaseOffer", &mut offer, "Offer the lens database download when LightCraft starts") {
|
||||
app.ui.settings.lens_database = if offer { Offer::Ask } else { Offer::Declined };
|
||||
}
|
||||
hint(
|
||||
ui,
|
||||
t,
|
||||
crate::i18n::tr(
|
||||
"lensfun's open lens database (CC BY-SA 3.0) corrects distortion and vignetting for many lenses. It is only downloaded when you choose to.",
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// The file Edit In hands an editor: format, colour space and bit depth (`button:{id}Format-…`).
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
//! The lens database without setup: on first run the app fetches lensfun's database by itself
|
||||
//! (`lightcraft_engine::lensfun_download`, an activity row while it runs), and whenever the database in use changes
|
||||
//! The lens database on request: the first launch offers lensfun's database once (its size, Download / Not Now,
|
||||
//! [`offer`]), Settings ▸ General can download it any time ([`download_default`]), and nothing is fetched without
|
||||
//! that click (`lightcraft_engine::lensfun_download::fetch_on_start` decides; an activity row while it runs).
|
||||
//! Whenever the database in use changes
|
||||
//! (it arrived, a newer pin replaced it) or Enable Lens Corrections is ticked on a photo without a profile, the
|
||||
//! library's raws that have none are looked up again on a worker thread (`lightcraft_engine::cmd::lens_refresh`);
|
||||
//! when its files were replaced, so are those whose profile came from the database and those that named no lens.
|
||||
@@ -18,18 +20,75 @@ pub fn tick(app: &mut LightcraftApp) {
|
||||
refresh(app);
|
||||
}
|
||||
|
||||
/// Once per run, when the start policy says so ([`lensfun_download::fetch_on_start`]: the host allows it and the
|
||||
/// pinned database is missing or damaged).
|
||||
/// Once per run, what the start policy says ([`lensfun_download::fetch_on_start`]): offer the database, fetch it
|
||||
/// (the user said yes before), or nothing. The offer waits for the window to be free (another dialog, the library
|
||||
/// chooser).
|
||||
fn start_download(app: &mut LightcraftApp) {
|
||||
if app.ui.lens_download_checked {
|
||||
return;
|
||||
}
|
||||
app.ui.lens_download_checked = true;
|
||||
let Some(dir) = lensfun_download::fetch_on_start(app.session.lens_auto_download) else { return };
|
||||
let activity = app.session.activity.clone();
|
||||
if let Err(e) = app.session.lensfun.start(&dir, lensfun_download::mirrors(), lensfun_download::FILES, &activity) {
|
||||
log::warn!("lens database: {e}");
|
||||
if !app.session.lens_auto_download {
|
||||
app.ui.lens_download_checked = true;
|
||||
return;
|
||||
}
|
||||
if app.ui.dialog.is_some() || crate::panels::library_problem::blocks_dialog_input(app) {
|
||||
return;
|
||||
}
|
||||
app.ui.lens_download_checked = true;
|
||||
let start = lensfun_download::fetch_on_start(app.session.lens_auto_download, app.ui.settings.lens_database);
|
||||
begin(app, start, lensfun_download::mirrors());
|
||||
}
|
||||
|
||||
/// Act on the start policy's answer, fetching from `mirrors`. The offer counts as answered once it is shown: closing
|
||||
/// it any way but Download (Not Now, Esc) is "not now", and the next start doesn't ask again.
|
||||
pub(crate) fn begin(app: &mut LightcraftApp, start: lensfun_download::Start, mirrors: Vec<String>) {
|
||||
match start {
|
||||
lensfun_download::Start::Nothing => {}
|
||||
lensfun_download::Start::Ask(dir) => {
|
||||
app.ui.settings.lens_database = lensfun_download::Offer::Declined;
|
||||
app.ui.dialog = Some(crate::state::Dialog::LensDatabase { dir: dir.to_string_lossy().into_owned(), mirrors });
|
||||
}
|
||||
lensfun_download::Start::Fetch(dir) => {
|
||||
if let Err(e) = download(app, &dir, mirrors) {
|
||||
log::warn!("lens database: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Download the database into `dir` from `mirrors` (the offer's Download, Settings' button): remembered as the
|
||||
/// user's yes, shown in the activity stack, and read once it is in place.
|
||||
pub(crate) fn download(app: &mut LightcraftApp, dir: &std::path::Path, mirrors: Vec<String>) -> Result<(), String> {
|
||||
app.ui.settings.lens_database = lensfun_download::Offer::Accepted;
|
||||
lens_refresh::start_download(&mut app.session, dir, mirrors, lensfun_download::FILES)
|
||||
}
|
||||
|
||||
/// Settings' Download Lens Database: into the app's own folder, from the usual mirrors.
|
||||
pub fn download_default(app: &mut LightcraftApp) -> Result<(), String> {
|
||||
let dir = lensfun_download::default_dir().ok_or("this computer has no folder for LightCraft's data")?;
|
||||
download(app, &dir, lensfun_download::mirrors())
|
||||
}
|
||||
|
||||
/// The offer's window: what the database does, its size, and that Settings has it later.
|
||||
pub fn offer(ui: &mut egui::Ui) {
|
||||
let t = crate::theme::Tokens::get(ui.ctx());
|
||||
ui.label(egui::RichText::new(crate::i18n::tr("Download lens profiles?")).strong());
|
||||
ui.add_space(4.0);
|
||||
ui.add(
|
||||
egui::Label::new(crate::i18n::tr_format!(
|
||||
"LightCraft can correct distortion and vignetting for many lenses using the open lens database (lensfun, about {size} MB).",
|
||||
size = lensfun_download::download_megabytes()
|
||||
))
|
||||
.wrap(),
|
||||
);
|
||||
ui.add_space(4.0);
|
||||
ui.add(
|
||||
egui::Label::new(
|
||||
egui::RichText::new(crate::i18n::tr("Nothing is downloaded until you choose Download. You can download it later from Settings."))
|
||||
.color(t.text_dim),
|
||||
)
|
||||
.wrap(),
|
||||
);
|
||||
}
|
||||
|
||||
/// After Enable Lens Corrections was ticked on photo `id`: the photos to look up again, those of `id` and the
|
||||
|
||||
@@ -246,6 +246,12 @@ pub enum Dialog {
|
||||
#[serde(default)]
|
||||
error: Option<String>,
|
||||
},
|
||||
/// The first launch offers the lens database (`crate::lens_db`): its size, Download or Not Now. `dir`: where it
|
||||
/// goes; `mirrors`: where it comes from.
|
||||
LensDatabase {
|
||||
dir: String,
|
||||
mirrors: Vec<String>,
|
||||
},
|
||||
/// Open Library was given a Lightroom Classic catalog (issue #891): offer File ▸ Import
|
||||
/// Lightroom Catalog… with `catalog` (the `.lrcat` found; `None`: ask for it) instead.
|
||||
/// `folder`: what was chosen.
|
||||
|
||||
@@ -193,6 +193,10 @@ pub struct AppSettings {
|
||||
/// gets a new one, or always merge (issue #2672; the window's "Don't ask again" picks that).
|
||||
#[serde(deserialize_with = "lenient")]
|
||||
pub lightroom_import: LightroomImport,
|
||||
/// The answer to the first-launch offer of the lens database (Download / Not Now; Settings ▸ General ▸ Lens
|
||||
/// profiles changes it). A value this version doesn't read asks again.
|
||||
#[serde(deserialize_with = "lenient")]
|
||||
pub lens_database: lightcraft_engine::lensfun_download::Offer,
|
||||
}
|
||||
|
||||
/// Where File ▸ Import Lightroom Catalog… puts a catalog (Settings ▸ General ▸ Library). Saved as
|
||||
@@ -242,6 +246,7 @@ impl Default for AppSettings {
|
||||
zoom_to_focus: true,
|
||||
loupe_info: LoupeInfo::default(),
|
||||
lightroom_import: LightroomImport::Ask,
|
||||
lens_database: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ mod labels;
|
||||
mod launch_library;
|
||||
mod layout_image;
|
||||
mod lens_lookup;
|
||||
mod lens_offer;
|
||||
mod library_problem;
|
||||
mod lifecycle_037;
|
||||
mod lightroom_destination;
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
//! The lens database is offered once, on the first launch, and downloaded only after a click: Download in the offer
|
||||
//! or in Settings. Not Now downloads nothing and the next start doesn't ask; an installed database and headless
|
||||
//! hosts (CLI, MCP) never ask (`crate::lens_db`, `lightcraft_engine::lensfun_download::fetch_on_start`).
|
||||
|
||||
use lightcraft_engine::lensfun_download::{self, Offer, Start};
|
||||
use lightcraft_engine::{Session, lens_profiles};
|
||||
|
||||
use crate::state::Dialog;
|
||||
use crate::{LightcraftApp, Services};
|
||||
|
||||
/// A desktop-like app (allowed to ask) and a folder of this test's own for the database.
|
||||
fn desktop(name: &str) -> (LightcraftApp, std::path::PathBuf) {
|
||||
let mut session = Session::with_demo();
|
||||
session.lens_auto_download = true;
|
||||
let app = LightcraftApp::new(session, Services { png: None, ..Default::default() });
|
||||
let unique = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).map_or(0, |d| d.as_nanos());
|
||||
let root = std::env::temp_dir().join(format!("lc-lens-offer-{name}-{}-{unique}", std::process::id()));
|
||||
(app, root)
|
||||
}
|
||||
|
||||
/// Nothing listens on port 1: a download started in a test fails at once, without the network.
|
||||
fn offline() -> Vec<String> {
|
||||
vec!["http://127.0.0.1:1/db".into()]
|
||||
}
|
||||
|
||||
fn wait_idle(app: &LightcraftApp) {
|
||||
let t0 = std::time::Instant::now();
|
||||
while app.session.lensfun.running() && t0.elapsed() < std::time::Duration::from_secs(60) {
|
||||
std::thread::sleep(std::time::Duration::from_millis(20));
|
||||
}
|
||||
}
|
||||
|
||||
/// The first start shows the offer and fetches nothing; Download starts the download (an activity row) and is
|
||||
/// remembered as the user's yes.
|
||||
#[test]
|
||||
fn the_first_start_offers_the_database_and_fetches_nothing_until_download() {
|
||||
let (mut app, root) = desktop("first");
|
||||
let dir = root.join("lensfun");
|
||||
let before = lens_profiles::host_dir();
|
||||
crate::lens_db::begin(&mut app, lensfun_download::decide(true, false, Some(dir.clone()), false, Offer::Ask), offline());
|
||||
let Some(dlg @ Dialog::LensDatabase { .. }) = app.ui.dialog.clone() else { panic!("no offer: {:?}", app.ui.dialog) };
|
||||
assert_eq!(app.session.lensfun.state(), lensfun_download::State::Idle, "nothing fetched while it asks");
|
||||
assert!(app.session.activity.list().is_empty());
|
||||
crate::panels::dialogs::confirm_dialog(&mut app, &dlg).expect("Download");
|
||||
assert_ne!(app.session.lensfun.state(), lensfun_download::State::Idle, "Download starts it");
|
||||
assert_eq!(app.ui.settings.lens_database, Offer::Accepted);
|
||||
wait_idle(&app);
|
||||
lens_profiles::use_dir(before);
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
/// Not Now (or Esc: closing the offer any way but Download) downloads nothing, is saved with the settings, and the
|
||||
/// next start doesn't ask.
|
||||
#[test]
|
||||
fn not_now_is_remembered_and_the_next_start_does_not_ask() {
|
||||
let (mut app, root) = desktop("notnow");
|
||||
crate::lens_db::begin(&mut app, Start::Ask(root.join("lensfun")), offline());
|
||||
assert!(matches!(app.ui.dialog, Some(Dialog::LensDatabase { .. })));
|
||||
app.ui.dialog = None; // Not Now
|
||||
assert_eq!(app.session.lensfun.state(), lensfun_download::State::Idle, "nothing fetched");
|
||||
assert_eq!(app.ui.settings.lens_database, Offer::Declined);
|
||||
// saved and read back with the app's settings
|
||||
let saved = serde_json::to_value(&app.ui.settings).expect("settings save");
|
||||
let read: crate::state::AppSettings = serde_json::from_value(saved).expect("settings read");
|
||||
assert_eq!(read.lens_database, Offer::Declined);
|
||||
assert_eq!(lensfun_download::decide(true, false, Some(root.join("lensfun")), false, read.lens_database), Start::Nothing, "not asked again");
|
||||
// a damaged value asks again instead of failing to read the settings
|
||||
let mut damaged = serde_json::to_value(&app.ui.settings).expect("settings save");
|
||||
damaged["lensDatabase"] = serde_json::json!(42);
|
||||
let read: crate::state::AppSettings = serde_json::from_value(damaged).expect("a bad value doesn't spoil the settings");
|
||||
assert_eq!(read.lens_database, Offer::Ask);
|
||||
}
|
||||
|
||||
/// Settings' Download Lens Database downloads it (after a Not Now, too) and remembers the yes.
|
||||
#[test]
|
||||
fn the_settings_button_downloads_the_database() {
|
||||
let (mut app, root) = desktop("settings");
|
||||
let before = lens_profiles::host_dir();
|
||||
app.ui.settings.lens_database = Offer::Declined;
|
||||
crate::lens_db::download(&mut app, &root.join("lensfun"), offline()).expect("started");
|
||||
assert_ne!(app.session.lensfun.state(), lensfun_download::State::Idle, "the download ran");
|
||||
assert_eq!(app.ui.settings.lens_database, Offer::Accepted);
|
||||
assert_eq!(lens_profiles::host_dir(), Some(root.join("lensfun")), "and is read once it is in place");
|
||||
wait_idle(&app);
|
||||
lens_profiles::use_dir(before);
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
/// An installed database means no offer and no download.
|
||||
#[test]
|
||||
fn an_installed_database_is_not_offered() {
|
||||
let (mut app, root) = desktop("installed");
|
||||
for offer in [Offer::Ask, Offer::Accepted, Offer::Declined] {
|
||||
crate::lens_db::begin(&mut app, lensfun_download::decide(true, false, Some(root.join("lensfun")), true, offer), offline());
|
||||
assert!(app.ui.dialog.is_none(), "{offer:?}");
|
||||
assert_eq!(app.session.lensfun.state(), lensfun_download::State::Idle);
|
||||
}
|
||||
}
|
||||
|
||||
/// A headless app (CLI snapshot, MCP) never shows the offer and never fetches, whatever the settings say.
|
||||
#[test]
|
||||
fn a_headless_app_never_offers_or_fetches() {
|
||||
let mut app = LightcraftApp::new(Session::with_demo(), Services { png: None, ..Default::default() });
|
||||
assert!(!app.session.lens_auto_download);
|
||||
for offer in [Offer::Ask, Offer::Accepted] {
|
||||
app.ui.settings.lens_database = offer;
|
||||
app.ui.lens_download_checked = false;
|
||||
crate::lens_db::tick(&mut app);
|
||||
assert!(app.ui.dialog.is_none());
|
||||
assert_eq!(app.session.lensfun.state(), lensfun_download::State::Idle);
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
# Gaps: where LightCraft falls short of Lightroom
|
||||
|
||||
> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-27 · **Change:** minor (gap 8: lensfun's lens database downloaded on first run, looked up for the library; gap 30: the Info panel shows the selected photos' shared metadata and marks mixed fields; gap 28: ⌘⌥⇧C / ⌘⌥⇧V copy and paste metadata; gap 13: before a Lightroom catalog import the app asks merge or new library, #2672; gap 28: F8 shows / hides the right panel; import review album hierarchy, #2362; gap 20: Minolta MRW decodes; gap 10: 551 models verified; gap 15: Print Page Options — page numbers, page info, crop marks; gap 28: `\` shows / hides the Filter Bar in the grids; gap 21: Indonesian interface shipped, every message but What's New; gap 27: the Windows x64 installer verified on Windows 11; the Settings → Apps uninstall confirmation remains; gap 28: ⌘L Enable Filters switches the filter off and on; gap 15: Japanese photo and postcard paper sizes in the Print page setup and PDF)
|
||||
> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-28 · **Change:** minor (gap 8: lensfun's lens database offered on the first launch and downloaded on request, looked up for the library; gap 30: the Info panel shows the selected photos' shared metadata and marks mixed fields; gap 28: ⌘⌥⇧C / ⌘⌥⇧V copy and paste metadata; gap 13: before a Lightroom catalog import the app asks merge or new library, #2672; gap 28: F8 shows / hides the right panel; import review album hierarchy, #2362; gap 20: Minolta MRW decodes; gap 10: 551 models verified; gap 15: Print Page Options — page numbers, page info, crop marks; gap 28: `\` shows / hides the Filter Bar in the grids; gap 21: Indonesian interface shipped, every message but What's New; gap 27: the Windows x64 installer verified on Windows 11; the Settings → Apps uninstall confirmation remains; gap 28: ⌘L Enable Filters switches the filter off and on; gap 15: Japanese photo and postcard paper sizes in the Print page setup and PDF)
|
||||
|
||||
Every known shortfall, one entry each, ranked by user impact (how many Lightroom users hit it, and
|
||||
how hard it blocks them), then by effort. **Agents pick work from the top** (the first entry nobody is
|
||||
@@ -22,7 +22,7 @@ Hours are Opus 5.5 agent wall-clock hours (see [target-app-parity.md](target-app
|
||||
| 5 B | **AI masks: Subject, Sky, People, Background, Landscape** are heuristics; Object / Describe run SAM 3 but its download has no mirror configured | `crates/segment`; ai-masks.md; MASK rows 67% | masking is how most Lightroom users edit today | 40–70 + model decision | [target-app-parity](target-app-parity.md#ai) |
|
||||
| 6 B | **Compressed ORF** (every Olympus / OM body since ~2008) | measured: 66 of 78 Olympus / OM models preview-only; `orf.rs` refuses; OM bucket 3% of photographers, 3% of them fully work | +2.6 points; best gain per hour of any raw gap (~0.2) | 8–15 | [raw-parity](raw-parity.md#compressed-orf-a-clean-room-starting-point) (the two Olympus patents that describe the method) |
|
||||
| 7 | **Nikon High Efficiency NEF** (HE / HE★ on Z 8, Z 9, Z 6III, Z f, Z50II, Z5II when set to HE) | named and preview only (#193); needs a pure-Rust JPEG XS decoder | +1.1 points (only shots taken in HE; lossless works on the same bodies); low gain per hour (~0.04), so not a beta blocker | 20–40 | [raw-parity](raw-parity.md) |
|
||||
| 8 B | **Lens profiles: lensfun only, no picker, no own calibration** (LR-EDIT-OPTICS-PROFILE): the desktop app downloads lensfun's database (CC BY-SA 3.0, pinned, never bundled) by itself on first run and looks every raw up by its lens ([lens-profiles.md](lens-profiles.md)), but those profiles are not validated against Lightroom, there is no profile picker (in the file / lens database / own calibration) and no calibration wizard; in-file corrections: DNG opcodes, RW2 in-file distortion, Sony ILCE-7RM4A, full-frame ILCE-7CR distortion (Auto only) and ILCE-7RM6 distortion (`docs/sony-ilce7rm6-lens-corrections.md`) and Nikon Z 6II / Z 8 / Z f distortion (`docs/nikon-lens-corrections.md`) | Adobe ships 3,635 lens profiles; we never use them | distortion / vignetting correction is a default step for many | 40–80 + calibration shots | [raw-parity](raw-parity.md#lens-profiles) |
|
||||
| 8 B | **Lens profiles: lensfun only, no picker, no own calibration** (LR-EDIT-OPTICS-PROFILE): the desktop app offers lensfun's database (CC BY-SA 3.0, pinned, never bundled) once on the first launch and downloads it on that click or from Settings, then looks every raw up by its lens ([lens-profiles.md](lens-profiles.md)), but those profiles are not validated against Lightroom, there is no profile picker (in the file / lens database / own calibration) and no calibration wizard; in-file corrections: DNG opcodes, RW2 in-file distortion, Sony ILCE-7RM4A, full-frame ILCE-7CR distortion (Auto only) and ILCE-7RM6 distortion (`docs/sony-ilce7rm6-lens-corrections.md`) and Nikon Z 6II / Z 8 / Z f distortion (`docs/nikon-lens-corrections.md`) | Adobe ships 3,635 lens profiles; we never use them | distortion / vignetting correction is a default step for many | 40–80 + calibration shots | [raw-parity](raw-parity.md#lens-profiles) |
|
||||
| 9 B | **Tests don't run in CI on macOS or Windows**: Linux x64 runs `cargo xtask ci` on every PR and push to main (`.github/workflows/ci.yml`) and FreeBSD runs `cargo test`; the others build packages | `.github/workflows/` (ci, freebsd, packaging-lint, release, windows-arm64); the linux-riscv64 release job added in 0.6.0 (#844) runs only `lightcraft-cli --version` under QEMU; macOS / Windows jobs: #1148 | regressions in `cfg(target_os)` code reach macOS and Windows users | 6–12 | [target-app-parity](target-app-parity.md#by-dimension) |
|
||||
| 10 B | **Per-model verification thin, plus cheap per-body decode fixes**: 551 of 1,446 Adobe models verified (38%), ~85% of photographers on a verified body; Nikon 5 bodies verified, Canon 34, Leica 14 (the Q3 by a recorded check of 38 private DNGs). Cheap fixes: Z 6II packed 14-bit NEF (#769, PR #671), Sony A100 regression (#702); F828 SRF now discovered and imported as preview-only, sensor decoding still missing, DSLR-A200…A390 / A850 12-bit ARW (#535) | measured: [raw/coverage.tsv](raw/coverage.tsv) | per-body bugs (levels, crops, WB) surface only from users | 25–45 | [raw-parity](raw-parity.md#by-maker-measured) |
|
||||
| 11 | **Enhance: Raw Details, Super Resolution; AI denoise without shippable weights or X-Trans** | ENH rows 0%; denoise Bayer only, RawNIND weights GPL and opt-in | Fujifilm users get no AI denoise; nobody gets it out of the box | 30–60 + model decision | [raw-parity](raw-parity.md#demosaic-levels-crops-and-lens-metadata) |
|
||||
@@ -84,7 +84,7 @@ Beta-blocking gaps (B) total **~230–430 h**; with the stability and platform w
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-27 | minor | Gap 8 shrinks: the desktop app downloads lensfun's lens database (CC BY-SA, pinned) by itself on first run and looks the library's raws up when it arrives; validation, a profile picker and own calibration remain |
|
||||
| 2026-10-28 | minor | Gap 8 shrinks: the desktop app offers lensfun's lens database (CC BY-SA, pinned) once on the first launch and downloads it on request and looks the library's raws up when it arrives; validation, a profile picker and own calibration remain |
|
||||
| 2026-10-24 | minor | Gap 28: ⌘⌥⇧C / ⌘⌥⇧V (Ctrl+Alt+Shift+C / V on Windows and Linux) copy and paste metadata; no key leaves the entry; hours unchanged |
|
||||
| 2026-10-24 | minor | Gap 30 shrinks: the Info panel's Show Metadata For (Target Photo / Selected Photos, `view.infoSelectedPhotos`) shows the selected photos' shared values and marks fields that differ as Mixed; the All Metadata dialog remains; 1–3 h (was 4–8) |
|
||||
| 2026-10-23 | minor | Gap 13: before a Lightroom catalog import the app asks whether it merges into the open library or goes into a new one (opt-out in Settings, #2672); hours unchanged |
|
||||
|
||||
+14
-6
@@ -5,22 +5,30 @@ see [sony-lens-corrections.md](sony-lens-corrections.md) and [nikon-lens-correct
|
||||
XML format**. The app does not ship any lens database: lensfun's data is licensed CC BY-SA 3.0 and is downloaded at
|
||||
run time, never bundled and never committed. Adobe LCP profiles are never used.
|
||||
|
||||
## Setup: none
|
||||
## Setup: one click
|
||||
|
||||
1. On first run the desktop app downloads lensfun's `data/db` (56 XML files, about 5 MB) into its data folder
|
||||
1. Nothing is downloaded without the user's click. On the first launch the desktop app asks once: "Download lens
|
||||
profiles? LightCraft can correct distortion and vignetting for many lenses using the open lens database
|
||||
(lensfun, about N MB)", with **Download** and **Not Now** (N is the pinned files' sizes added up). Not Now (or
|
||||
Esc) downloads nothing and the app doesn't ask again; Settings ▸ General ▸ Lens profiles has **Download Lens
|
||||
Database** and a switch that turns the offer back on. The answer is kept with the app's settings (`ui.json`,
|
||||
`lensDatabase`: `ask` / `accepted` / `declined`; a value it can't read asks again), and
|
||||
`lensfun_download::fetch_on_start` is the one place that decides. After a yes, a start that finds the database
|
||||
missing or from an older pin downloads it again (an interrupted download resumes).
|
||||
Download fetches lensfun's `data/db` (56 XML files, about 5 MB) into the app's data folder
|
||||
(`<config dir>/lensfun`, beside the face and denoise models; see `crates/engine/src/config.rs`), with a row in
|
||||
the activity stack (its ✕ stops it; the next start resumes). The files are pinned to one lensfun commit by name,
|
||||
the activity stack (its ✕ stops it). The files are pinned to one lensfun commit by name,
|
||||
size and SHA-256 (`crates/engine/src/lensfun_download.rs`); a file that differs is thrown away. A later build with
|
||||
a newer pin replaces them once. The files are staged in `<config dir>/lensfun.download`, `SOURCE.txt` (the commit
|
||||
and the licence) is written there last, and the staged folder then replaces the old one as a whole: until it is
|
||||
in place the previous complete database is used, and if the swap fails it is put back. The app's folder is read
|
||||
only when it has `SOURCE.txt`, and a folder whose pinned files are missing or cut short is downloaded again.
|
||||
The CLI (`render`, `merge`, `snapshot`) and the headless MCP server read this folder too, but never start the
|
||||
first-run download; `lens.downloadDatabase` fetches it on request from any of them.
|
||||
The CLI (`render`, `merge`, `snapshot`) and the headless MCP server read this folder too, but never ask and never
|
||||
fetch on their own; `lens.downloadDatabase` fetches it on request from any of them.
|
||||
2. By default the files come from lensfun's GitHub repository at the pinned commit. `LIGHTCRAFT_LENSFUN_URL` lists
|
||||
other places serving the same files (base URLs, tried first), e.g. a copy on LightCraft's own server.
|
||||
3. `LIGHTCRAFT_LENSFUN_DIR` points at a database of your own instead (lensfun's `data/db`, or a Linux
|
||||
distribution's `/usr/share/lensfun/version_1/`); then the first-run download doesn't start, and that folder is read
|
||||
distribution's `/usr/share/lensfun/version_1/`); then the app doesn't offer the download, and that folder is read
|
||||
even after `lens.downloadDatabase` fetches the app's own copy.
|
||||
|
||||
When the database arrives (or changes), every library raw without a profile whose lens it knows is read again in the
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user