From 21b65c8c33d68390657619c896a0ba7e9a67708a Mon Sep 17 00:00:00 2001 From: uncor3 Date: Sat, 23 May 2026 13:09:55 +0000 Subject: [PATCH] fix read_albums and query statements for various iOS versions --- src/constants.rs | 23 ++++++ src/query_sqlite.rs | 180 +++++++++++++++++++++++------------------ src/service_factory.rs | 14 ++++ 3 files changed, 140 insertions(+), 77 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index 5771b1a..0c80ee6 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -12,6 +12,19 @@ pub static IOS_15_ALBUM_QUERY_STATEMENT: &str = "SELECT WHERE ZGENERICALBUM.ZKEYASSET IS NOT NULL "; +pub static IOS_26_ALBUM_QUERY_STATEMENT: &str = "SELECT + ZGENERICALBUM.Z_PK, + ZGENERICALBUM.ZTITLE, + ZGENERICALBUM.ZCACHEDCOUNT, + ZASSET.ZDIRECTORY, + ZASSET.ZFILENAME + FROM ZGENERICALBUM + LEFT JOIN ZASSET ON ZGENERICALBUM.Z_ENT = ZASSET.Z_PK + WHERE ZGENERICALBUM.Z_ENT IS NOT NULL + AND ZGENERICALBUM.ZTITLE IS NOT NULL + AND ZGENERICALBUM.ZCACHEDCOUNT IS NOT 0 + "; + pub static RECENTS_ALBUM_QUERY: &str = " SELECT ZASSET.ZFILENAME, @@ -48,3 +61,13 @@ pub static FAVS_QUERY: &str = " WHERE ZASSET.ZFAVORITE = 1 ORDER BY ZASSET.Z_PK DESC "; +//FIXME: is Z_3ASSETS consistent ? +pub static ALBUM_CONTENTS_QUERY_TEMPLATE: &str = r#" + SELECT + ZASSET.ZDIRECTORY, + ZASSET.ZFILENAME + FROM ZGENERICALBUM + LEFT JOIN {table} ON ZGENERICALBUM.Z_PK = {table}.{album} + LEFT JOIN ZASSET ON {table}.Z_3ASSETS = ZASSET.Z_PK + WHERE ZGENERICALBUM.Z_PK = ? +"#; diff --git a/src/query_sqlite.rs b/src/query_sqlite.rs index a06ea1b..8376e16 100644 --- a/src/query_sqlite.rs +++ b/src/query_sqlite.rs @@ -2,7 +2,8 @@ use qmetaobject::prelude::*; use qttypes::{QStringList, QVariantMap}; use crate::constants::{ - FAVS_ALBUM_ID, FAVS_ALBUM_QUERY, FAVS_QUERY, IOS_15_ALBUM_QUERY_STATEMENT, RECENTS_ALBUM_ID, + ALBUM_CONTENTS_QUERY_TEMPLATE, FAVS_ALBUM_ID, FAVS_ALBUM_QUERY, FAVS_QUERY, + IOS_15_ALBUM_QUERY_STATEMENT, IOS_26_ALBUM_QUERY_STATEMENT, RECENTS_ALBUM_ID, RECENTS_ALBUM_QUERY, RECENTS_QUERY, }; use crate::device_ctx; @@ -10,7 +11,7 @@ use crate::qt_threading::{QtThread, QtThreading}; use crate::utils::create_album_info; use crate::{RUNTIME, run_sync}; use idevice::afc::{AfcClient, opcode::AfcFopenMode}; -use rusqlite::{Connection, Rows}; +use rusqlite::{Connection, OptionalExtension, Rows}; use serde_json::json; use std::default; use std::fmt::format; @@ -23,17 +24,20 @@ use tokio::sync::Mutex; use tokio::sync::oneshot; -#[derive(QObject)] +#[derive(QObject, Default)] pub struct Query { base: qt_base_class!(trait QObject), - udid: QString, + udid: String, + ios_version: u32, albums: qt_property!(QVariantMap; NOTIFY albums_changed), albums_changed: qt_signal!(), connection: Option>>, + assets_table_name: Option, + assets_table_album_column: Option, state: qt_property!(QVariantMap; NOTIFY state_changed), state_changed: qt_signal!(), - init: qt_method!(fn(&mut self, udid: QString)), + init: qt_method!(fn(&mut self)), read_albums: qt_method!(fn(&mut self)), query_album: qt_method!(fn(&mut self, id: i32)), album_queried: qt_signal!(id: i32, items: QStringList), @@ -48,31 +52,21 @@ impl QtThreading for Query { } } -impl Default for Query { - fn default() -> Self { +impl Query { + pub fn with_device_attr(udid: QString, ios_version: u32) -> Self { let mut state = QVariantMap::default(); state.insert(QString::from("init"), QVariant::from(false)); state.insert(QString::from("err"), QVariant::from(QString::default())); - Self { - base: Default::default(), - udid: Default::default(), - albums: Default::default(), - albums_changed: Default::default(), - connection: None, state, - state_changed: Default::default(), - init: Default::default(), - read_albums: Default::default(), - query_album: Default::default(), - album_queried: Default::default(), + ios_version, + udid: udid.to_string(), + ..Default::default() } } -} -impl Query { - fn init(&mut self, udid: QString) { - let udid_clone = udid.clone(); + fn init(&mut self) { + let udid_clone = self.udid.clone(); let qt_thread = self.qt_thread(); RUNTIME.spawn(async move { @@ -108,15 +102,61 @@ impl Query { } }; - //FIXME:need to drop vec somewhere safe + //FIXME:need to drop the vec somewhere safe /* std::mem::forget is needed because vec is dropped but - sqlite still needs, we need to manually drop the vec + sqlite still needs it, we need to manually drop the vec */ std::mem::forget(gallery_db_bytes); + /* + we need to get the dynamic asset table name from the table + iOS seems to be bumping the version with every major iOS update + but not sure why + */ + let mut assets_table_name: Option = None; + let mut assets_table_album_column: Option = None; + { + let mut stm = + conn.prepare("SELECT name FROM sqlite_master WHERE type='table'")?; + let table_iter = stm.query_map([], |r| r.get::<_, String>(0))?; + + let re = regex::Regex::new(r"^Z_\d+ASSETS$")?; + + for table in table_iter { + let table_name = table?; + + if re.is_match(&table_name) { + println!("Found assets table: {}", table_name); + //extract the prefix from for example Z_27ASSETS + assets_table_album_column = match table_name.strip_suffix("ASSETS") { + // Z_27ALBUMS + Some(pre) => Some(String::from(format!("{}ALBUMS", pre))), + None => None, + }; + println!( + "Found assets_table_album_column: {}", + assets_table_album_column.clone().unwrap() + ); + + assets_table_name = Some(table_name); + break; + } + } + }; + + if assets_table_name.is_none() { + return Err("Couldn't find the assets table".into()); + } + + if assets_table_album_column.is_none() { + return Err("Couldn't find assets_table_album_column".into()); + } + qt_thread.queue(|s| { s.connection = Some(Arc::new(Mutex::new(conn))); + s.assets_table_name = assets_table_name; + s.assets_table_album_column = assets_table_album_column; }); Ok(()) @@ -159,6 +199,7 @@ impl Query { } }; + let ios_ver = self.ios_version; RUNTIME.spawn(async move { println!("Runtime spawn for read_albums"); let mut albums = QVariantMap::default(); @@ -167,12 +208,19 @@ impl Query { //recents album let mut recents_stmt = conn.prepare(RECENTS_ALBUM_QUERY)?; - let (fname, fdir, count) = recents_stmt.query_row([], |r| { - let fname: String = r.get(0)?; - let fdir: String = r.get(1)?; - let count: i32 = r.get(2)?; - Ok((fname, fdir, count)) - })?; + // FIXME: can this be better handled ? + let placeholder_or_empty = (String::default(), String::from("EMPTY"), 0); + + let recents_row = recents_stmt + .query_row([], |r| { + let fname: String = r.get(0)?; + let fdir: String = r.get(1)?; + let count: i32 = r.get(2)?; + Ok((fname, fdir, count)) + }) + .optional()?; + + let (fname, fdir, count) = recents_row.unwrap_or(placeholder_or_empty.clone()); let recents_album_data = create_album_info(RECENTS_ALBUM_ID, count, fdir, fname); @@ -184,12 +232,16 @@ impl Query { //favs let mut favs_stmt = conn.prepare(FAVS_ALBUM_QUERY)?; - let (fname, fdir, count) = favs_stmt.query_row([], |r| { - let fname: String = r.get(0)?; - let fdir: String = r.get(1)?; - let count: i32 = r.get(2)?; - Ok((fname, fdir, count)) - })?; + let favs_row = favs_stmt + .query_row([], |r| { + let fname: String = r.get(0)?; + let fdir: String = r.get(1)?; + let count: i32 = r.get(2)?; + Ok((fname, fdir, count)) + }) + .optional()?; + + let (fname, fdir, count) = favs_row.unwrap_or(placeholder_or_empty.clone()); let favs_album_data = create_album_info(FAVS_ALBUM_ID, count, fdir, fname); @@ -198,23 +250,13 @@ impl Query { QVariant::from(&QString::from(favs_album_data)), ); - // IOS 26 - // let mut stmt = conn.prepare( - // "SELECT - // ZGENERICALBUM.Z_PK, - // ZGENERICALBUM.ZTITLE, - // ZGENERICALBUM.ZCACHEDCOUNT, - // ZASSET.ZDIRECTORY, - // ZASSET.ZFILENAME - // FROM ZGENERICALBUM - // LEFT JOIN ZASSET ON ZGENERICALBUM.Z_ENT = ZASSET.Z_PK - // WHERE ZGENERICALBUM.Z_ENT IS NOT NULL - // AND ZGENERICALBUM.ZTITLE IS NOT NULL - // AND ZGENERICALBUM.ZCACHEDCOUNT IS NOT 0 - // ", - // )?; + let q_stm = if ios_ver > 15 { + IOS_26_ALBUM_QUERY_STATEMENT + } else { + IOS_15_ALBUM_QUERY_STATEMENT + }; - let mut stmt = conn.prepare(IOS_15_ALBUM_QUERY_STATEMENT)?; + let mut stmt = conn.prepare(q_stm)?; let rows_iter = stmt.query_map([], |row| { let album_id: i32 = row.get(0)?; @@ -243,6 +285,7 @@ impl Query { if let Err(e) = res { q_thread.queue(move |q_self| { + println!("Error reading albums {}", e.to_string()); q_self.state[QString::from("err")] = QVariant::from(QString::from(e.to_string())); q_self.albums = albums; @@ -280,22 +323,19 @@ impl Query { }; let q_thread = self.qt_thread(); - + let table_name = self.assets_table_name.clone().unwrap(); + let album_column = self.assets_table_album_column.clone().unwrap(); RUNTIME.spawn(async move { let res: Result> = (async { let con = con_arc.lock().await; let mut list: QStringList = QStringList::default(); - let mut stmt = con.prepare(&format!( - " - SELECT - ZASSET.ZDIRECTORY, - ZASSET.ZFILENAME - FROM ZASSET WHERE ZASSET.Z_OPT = {} - ", - id - ))?; + let mut stmt = con.prepare( + &ALBUM_CONTENTS_QUERY_TEMPLATE + .replace("{table}", &table_name) + .replace("{album}", &album_column), + )?; - let row_iter = stmt.query_map([], |r| { + let row_iter = stmt.query_map([id], |r| { let fdir: String = r.get(0)?; let fname: String = r.get(1)?; Ok((fdir, fname)) @@ -418,17 +458,3 @@ impl Query { }); } } - -// fn get_album_query_sql(ios_ver : i32, id : i32) { -// let assets_table = crate::utils::get_sqlite_assets_name(ios_ver); - -// format!(" -// SELECT -// {}.ZDIRECTORY, -// {}.ZFILENAME -// FROM {} WHERE -// {}.Z_OPT = {} - -// ") - -// } diff --git a/src/service_factory.rs b/src/service_factory.rs index 11a89a0..c89c6ac 100644 --- a/src/service_factory.rs +++ b/src/service_factory.rs @@ -1,4 +1,5 @@ use crate::device_ctx; +use crate::query_sqlite::Query; use crate::service_manager::ServiceManager; use crate::utils::{empty_qjsvalue, engine_ptr_new_object, vend_app_documents}; @@ -23,6 +24,7 @@ pub struct ServiceFactory { create_hause_arrest_afc_client: qt_method!(fn(&self, udid: QString, bundle_id: QString) -> QJSValue), create_service_manager: qt_method!(fn(&self, udid: QString, ios_version: u32) -> QJSValue), + create_sqlite_query_backend: qt_method!(fn(&self, udid: QString, ios_version: u32) -> QJSValue), } impl ServiceFactory { @@ -33,6 +35,7 @@ impl ServiceFactory { create_afc_client: Default::default(), create_hause_arrest_afc_client: Default::default(), create_service_manager: Default::default(), + create_sqlite_query_backend: Default::default(), } } @@ -140,4 +143,15 @@ impl ServiceFactory { } } } + + fn create_sqlite_query_backend(&self, udid: QString, ios_version: u32) -> QJSValue { + let engine_ptr: *mut c_void = self.engine_ptr; + if engine_ptr.is_null() { + eprintln!("ServiceFactory: engine_ptr is null"); + return empty_qjsvalue(); + } + let mng = Query::with_device_attr(udid, ios_version); + let obj_ptr = qmetaobject::into_leaked_cpp_ptr(mng); + engine_ptr_new_object(engine_ptr, obj_ptr) + } }