Capture back-face printing images for double-faced cards

Add a back_image_uri column to printings, populated during ingest from
a card's second card_faces image (only present for transform/MDFC/
reversible cards — split/adventure cards share one combined image and
correctly get ""). Plumb it through CardRef across every query path
(search, library, filters, deck contents) via the same printing row,
using a shared deterministic ORDER BY so front/back always come from
one printing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-07 20:54:55 -05:00
parent 342b8da797
commit 2ea3c56511
8 changed files with 114 additions and 18 deletions

View File

@@ -10,8 +10,8 @@ use crate::model::{CardRef, ColorSet, LibraryCard};
use crate::Catalog;
/// Map a row shaped `(oracle_id, quantity_owned, location, name, mana_value,
/// colors, color_identity, type_line, image_uri)` into its library parts. Shared
/// by [`Catalog::list_library`] and the criteria search.
/// colors, color_identity, type_line, image_uri, back_image_uri)` into its
/// library parts. Shared by [`Catalog::list_library`] and the criteria search.
pub(crate) fn row_to_library_parts(
r: &rusqlite::Row,
) -> rusqlite::Result<(String, i64, String, CardRef)> {
@@ -28,6 +28,7 @@ pub(crate) fn row_to_library_parts(
color_identity: ColorSet(r.get::<_, i64>(6)? as u8),
type_line: r.get(7)?,
image_uri: r.get::<_, Option<String>>(8)?.unwrap_or_default(),
back_image_uri: r.get::<_, Option<String>>(9)?.unwrap_or_default(),
},
))
}
@@ -69,12 +70,13 @@ impl Catalog {
let mut stmt = self.conn.prepare(&format!(
"SELECT lc.oracle_id, lc.quantity_owned, lc.location,
oc.name, oc.mana_value, oc.colors, oc.color_identity, oc.type_line, {image}
oc.name, oc.mana_value, oc.colors, oc.color_identity, oc.type_line, {image}, {back}
FROM library_cards lc
JOIN {REF_DB}.oracle_cards oc ON oc.oracle_id = lc.oracle_id
WHERE lc.quantity_owned > 0
ORDER BY oc.name COLLATE NOCASE",
image = crate::db::image_subquery()
image = crate::db::image_subquery("image_uri"),
back = crate::db::image_subquery("back_image_uri")
))?;
let rows = stmt
.query_map([], row_to_library_parts)?
@@ -155,6 +157,29 @@ mod tests {
assert_eq!(sol.card.image_uri, "http://img/sol-ring");
}
#[test]
fn back_image_uri_is_plumbed_through() {
let fx = test_support::make();
let cat = &fx.catalog;
cat.connection()
.execute(
"UPDATE ref.printings SET back_image_uri = 'http://img/back' \
WHERE oracle_id = 'sol-ring'",
[],
)
.unwrap();
cat.add_owned("sol-ring", 1).unwrap();
cat.add_owned("llanowar-elves", 1).unwrap();
let lib = cat.list_library().unwrap();
let sol = lib.iter().find(|c| c.card.oracle_id == "sol-ring").unwrap();
assert_eq!(sol.card.back_image_uri, "http://img/back");
// Single-faced cards have no back image.
let elves = lib.iter().find(|c| c.card.oracle_id == "llanowar-elves").unwrap();
assert_eq!(elves.card.back_image_uri, "");
}
#[test]
fn location_is_stored_and_listed() {
let fx = test_support::make();