mirror of
https://github.com/iDescriptor/iDescriptor.git
synced 2026-06-21 19:35:49 +08:00
fix read_albums and query statements for various iOS versions
This commit is contained in:
@@ -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 = ?
|
||||
"#;
|
||||
|
||||
+103
-77
@@ -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<Arc<Mutex<Connection>>>,
|
||||
assets_table_name: Option<String>,
|
||||
assets_table_album_column: Option<String>,
|
||||
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<String> = None;
|
||||
let mut assets_table_album_column: Option<String> = 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<QStringList, Box<dyn std::error::Error + Send + Sync>> = (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 = {}
|
||||
|
||||
// ")
|
||||
|
||||
// }
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user