···1818 disc_total INTEGER NOT NULL,
1919 file_state INTEGER NOT NULL,
2020 file_path TEXT NOT NULL,
2121- extension TEXT NOT NULL
2121+ extension TEXT NOT NULL,
2222+ artwork_path TEXT
2223);
23242425CREATE VIEW IF NOT EXISTS albums (
+116-204
src/gpod/gpod.rs
···66use anyhow::{Result, anyhow};
77use glib::gobject_ffi::g_type_init;
8899-pub struct IPodHandle {
1010- db_ptr: *mut Itdb_iTunesDB,
1111- device_ptr: *mut Itdb_Device,
1212-}
1313-1414-// impl Drop for IPodHandle {
1515-// fn drop(&mut self) {
1616-// unsafe {
1717-// itdb_free(self.db_ptr);
1818-// itdb_device_free(self.device_ptr);
1919-// }
2020-// }
2121-// }
2222-2323-pub struct CopyProcess {
99+pub struct Manager {
2410 db_ptr: *mut Itdb_iTunesDB,
2511}
26122727-impl Drop for CopyProcess {
1313+impl Drop for Manager {
2814 fn drop(&mut self) {
2915 unsafe {
3016 let mut error: *mut GError = ptr::null_mut();
···4935 }
5036}
51375252-impl CopyProcess {
5353- pub fn copy_file(
3838+impl Manager {
3939+ pub fn new(mountpoint: String) -> Result<Self> {
4040+ unsafe {
4141+ g_type_init();
4242+ let mountpoint = std::ffi::CString::new(mountpoint.clone()).unwrap();
4343+4444+ let mut error: *mut GError = ptr::null_mut();
4545+ let db_ptr = itdb_parse(mountpoint.as_ptr(), &mut error);
4646+ if !error.is_null() {
4747+ let error = *error;
4848+ let err_msg = std::ffi::CString::from_raw(error.message);
4949+ return Err(anyhow!(
5050+ "read ipod database, code {}: {}",
5151+ error.code,
5252+ err_msg.to_string_lossy()
5353+ ));
5454+ }
5555+5656+ match db_ptr.is_null() {
5757+ true => Err(anyhow!("no database found")),
5858+ false => {
5959+ itdb_start_sync(db_ptr);
6060+6161+ Ok(Self { db_ptr })
6262+ }
6363+ }
6464+ }
6565+ }
6666+6767+ pub fn copy(
5468 &self,
5569 track: &Track,
5670 file: PathBuf,
···76907791 let mut error: *mut GError = ptr::null_mut();
7892 let path = CString::new(file.display().to_string()).unwrap();
9393+ if let Some(art) = track.artwork_path.clone() {
9494+ log::info!("adding album art: {}", art);
9595+ let art = CString::new(art).unwrap();
9696+ if itdb_track_set_thumbnails(ipod_track, art.as_ptr()) != 1 {
9797+ log::warn!("could not add album art to track")
9898+ }
9999+ }
79100 itdb_track_add(self.db_ptr, ipod_track, -1);
80101 itdb_playlist_add_track(itdb_playlist_mpl(self.db_ptr), ipod_track, -1);
81102 let copy_success = itdb_cp_track_to_ipod(ipod_track, path.as_ptr(), &mut error);
···100121 }
101122}
102123103103-impl IPodHandle {
104104- pub fn initialize(path: String, model: String) -> Result<()> {
105105- unsafe {
106106- g_type_init();
107107- let mountpoint = std::ffi::CString::new(path).unwrap();
108108- let model = std::ffi::CString::new(model).unwrap();
124124+unsafe fn c_strdup(s: &str) -> *mut std::ffi::c_char {
125125+ unsafe {
126126+ let c_str = std::ffi::CString::new(s).unwrap();
127127+ // libc::strdup allocates memory (malloc) that libgpod can safely read and eventually free.
128128+ libc::strdup(c_str.as_ptr())
129129+ }
130130+}
109131110110- let mut error: *mut GError = ptr::null_mut();
111111- if itdb_init_ipod(mountpoint.as_ptr(), model.as_ptr(), ptr::null(), &mut error) != 1 {
112112- if error.is_null() {
113113- return Err(anyhow!("could not initialize ipod, unknown error"));
114114- }
132132+pub fn initialize(path: String, model: String) -> Result<()> {
133133+ unsafe {
134134+ g_type_init();
135135+ let mountpoint = std::ffi::CString::new(path).unwrap();
136136+ let model = std::ffi::CString::new(model).unwrap();
115137116116- let error = *error;
117117- let err_msg = std::ffi::CString::from_raw(error.message);
118118- return Err(anyhow!(
119119- "initialization error, code {}: {}",
120120- error.code,
121121- err_msg.to_string_lossy()
122122- ));
138138+ let mut error: *mut GError = ptr::null_mut();
139139+ if itdb_init_ipod(mountpoint.as_ptr(), model.as_ptr(), ptr::null(), &mut error) != 1 {
140140+ if error.is_null() {
141141+ return Err(anyhow!("could not initialize ipod, unknown error"));
123142 }
124143125125- let serials = rusb::devices()?
126126- .iter()
127127- .filter(|d| {
128128- let dd = d.device_descriptor();
129129- let d = d.open();
130130- if d.is_err() {
131131- return false;
132132- }
133133- let d = d.unwrap();
134134-135135- if let Ok(dd) = dd {
136136- match dd.serial_number_string_index() {
137137- Some(_) => (),
138138- None => return false,
139139- };
140140-141141- let product_str = d.read_product_string_ascii(&dd).unwrap();
142142- let serial_str = d.read_serial_number_string_ascii(&dd).unwrap();
143143-144144- return dd.vendor_id() == 0x05ac
145145- && product_str == "iPod"
146146- && !serial_str.is_empty();
147147- } else {
148148- return false;
149149- }
150150- })
151151- .map(|d| {
152152- let d = d.open().unwrap();
153153- let dd = d.device().device_descriptor().unwrap();
154154-155155- d.read_serial_number_string_ascii(&dd).unwrap()
156156- })
157157- .collect::<Vec<String>>();
158158-159159- match serials.len() {
160160- 0 => return Err(anyhow!("no iPod found")),
161161- 1 => {
162162- let device_ptr = itdb_device_new();
163163- itdb_device_set_mountpoint(device_ptr, mountpoint.as_ptr());
164164- let fw_guid_field = CString::new("FirewireGuid").unwrap();
165165- let serial =
166166- CString::new(format!("0x{}", &serials.first().unwrap()[..16])).unwrap();
167167-168168- // FirewireGuid
169169- itdb_device_set_sysinfo(device_ptr, fw_guid_field.as_ptr(), serial.as_ptr());
170170- let mut error: *mut GError = ptr::null_mut();
171171- if itdb_device_write_sysinfo(device_ptr, &mut error) != 1 {
172172- if error.is_null() {
173173- return Err(anyhow!(
174174- "could not initialize write sysinfo, unknown error"
175175- ));
176176- }
144144+ let error = *error;
145145+ let err_msg = std::ffi::CString::from_raw(error.message);
146146+ return Err(anyhow!(
147147+ "initialization error, code {}: {}",
148148+ error.code,
149149+ err_msg.to_string_lossy()
150150+ ));
151151+ }
177152178178- let error = *error;
179179- let err_msg = std::ffi::CString::from_raw(error.message);
180180- return Err(anyhow!(
181181- "write sysinfo error, code {}: {}",
182182- error.code,
183183- err_msg.to_string_lossy()
184184- ));
185185- }
153153+ let serials = rusb::devices()?
154154+ .iter()
155155+ .filter(|d| {
156156+ let dd = d.device_descriptor();
157157+ let d = d.open();
158158+ if d.is_err() {
159159+ return false;
186160 }
187187- _ => return Err(anyhow!("more than 1 iPod found, please connect just one")),
188188- }
189189- }
161161+ let d = d.unwrap();
190162191191- Ok(())
192192- }
193193- pub fn new(path: String) -> Result<Self> {
194194- unsafe {
195195- g_type_init();
196196- let mountpoint = std::ffi::CString::new(path).unwrap();
163163+ if let Ok(dd) = dd {
164164+ match dd.serial_number_string_index() {
165165+ Some(_) => (),
166166+ None => return false,
167167+ };
197168198198- let device_ptr = itdb_device_new();
199199- itdb_device_set_mountpoint(device_ptr, mountpoint.as_ptr());
200200- if itdb_device_read_sysinfo(device_ptr) != 1 {
201201- return Err(anyhow!("could not read sysinfo from iPod"));
202202- };
169169+ let product_str = d.read_product_string_ascii(&dd).unwrap();
170170+ let serial_str = d.read_serial_number_string_ascii(&dd).unwrap();
203171204204- let mut error: *mut GError = ptr::null_mut();
205205- let db_ptr = itdb_parse(mountpoint.as_ptr(), &mut error);
206206- if !error.is_null() {
207207- let error = *error;
208208- let err_msg = std::ffi::CString::from_raw(error.message);
209209- return Err(anyhow!(
210210- "read ipod database, code {}: {}",
211211- error.code,
212212- err_msg.to_string_lossy()
213213- ));
214214- }
172172+ return dd.vendor_id() == 0x05ac
173173+ && product_str == "iPod"
174174+ && !serial_str.is_empty();
175175+ } else {
176176+ return false;
177177+ }
178178+ })
179179+ .map(|d| {
180180+ let d = d.open().unwrap();
181181+ let dd = d.device().device_descriptor().unwrap();
215182216216- match db_ptr.is_null() {
217217- true => Err(anyhow!("no database found")),
218218- false => Ok(Self { db_ptr, device_ptr }),
219219- }
220220- }
221221- }
222222- pub fn info(&self) {
223223- unsafe {
224224- let pi = itdb_device_get_sysinfo(
225225- self.device_ptr,
226226- CString::new("ModelNumStr").unwrap().as_ptr(),
227227- );
183183+ d.read_serial_number_string_ascii(&dd).unwrap()
184184+ })
185185+ .collect::<Vec<String>>();
228186229229- let asd = CString::from_raw(pi);
230230- println!("numstr: {}", asd.to_string_lossy());
187187+ match serials.len() {
188188+ 0 => return Err(anyhow!("no iPod found")),
189189+ 1 => {
190190+ let device_ptr = itdb_device_new();
191191+ itdb_device_set_mountpoint(device_ptr, mountpoint.as_ptr());
192192+ let fw_guid_field = CString::new("FirewireGuid").unwrap();
193193+ let serial =
194194+ CString::new(format!("0x{}", &serials.first().unwrap()[..16])).unwrap();
231195232232- let pi = itdb_device_get_sysinfo(
233233- self.device_ptr,
234234- CString::new("FirewireGuid").unwrap().as_ptr(),
235235- );
196196+ // FirewireGuid
197197+ itdb_device_set_sysinfo(device_ptr, fw_guid_field.as_ptr(), serial.as_ptr());
198198+ let mut error: *mut GError = ptr::null_mut();
199199+ if itdb_device_write_sysinfo(device_ptr, &mut error) != 1 {
200200+ if error.is_null() {
201201+ return Err(anyhow!("could not initialize write sysinfo, unknown error"));
202202+ }
236203237237- if pi.is_null() {
238238- println!("could not read FirewireGuid");
239239- return;
204204+ let error = *error;
205205+ let err_msg = std::ffi::CString::from_raw(error.message);
206206+ return Err(anyhow!(
207207+ "write sysinfo error, code {}: {}",
208208+ error.code,
209209+ err_msg.to_string_lossy()
210210+ ));
211211+ }
240212 }
241241-242242- let asd = CString::from_raw(pi);
243243- println!("numstr: {}", asd.to_string_lossy());
244244- }
245245- }
246246-247247- pub fn copy_process(&self) -> CopyProcess {
248248- unsafe {
249249- itdb_start_sync(self.db_ptr);
250250- }
251251- CopyProcess {
252252- db_ptr: self.db_ptr,
213213+ _ => return Err(anyhow!("more than 1 iPod found, please connect just one")),
253214 }
254215 }
255255-}
256216257257-unsafe fn c_strdup(s: &str) -> *mut std::ffi::c_char {
258258- unsafe {
259259- let c_str = std::ffi::CString::new(s).unwrap();
260260- // libc::strdup allocates memory (malloc) that libgpod can safely read and eventually free.
261261- libc::strdup(c_str.as_ptr())
262262- }
263263-}
264264-265265-#[cfg(test)]
266266-mod tests {
267267- use std::str::FromStr;
268268-269269- use super::*;
270270-271271- #[test]
272272- fn initialize() {
273273- IPodHandle::initialize("/run/media/geesawra/IPOD".to_owned(), "MA003FB".to_owned())
274274- .unwrap();
275275- let ih = IPodHandle::new("/run/media/geesawra/IPOD".to_owned()).unwrap();
276276- ih.info();
277277- }
278278-279279- #[test]
280280- fn copy() {
281281- let ih = IPodHandle::new("/run/media/geesawra/IPOD".to_owned()).unwrap();
282282- let p = ih.copy_process();
283283-284284- let t = Track {
285285- title: "Marinmba".to_owned(),
286286- album: "Hit It!".to_owned(),
287287- artist: "Vianova".to_owned(),
288288- number: 42,
289289- ..Default::default()
290290- };
291291-292292- p.copy_file(
293293- &t,
294294- PathBuf::from_str(
295295- "/home/geesawra/Code/tracksync/dest/Vianova/Hit It!/1/Marimba.m4a.m4a",
296296- )
297297- .unwrap(),
298298- "aac".to_owned(),
299299- 44100,
300300- 128,
301301- )
302302- .unwrap();
303303-304304- drop(p);
305305- }
217217+ Ok(())
306218}
+3-1
src/gpod/mod.rs
···88 include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
99}
10101111-pub mod gpod;
1111+mod gpod;
1212+pub use gpod::Manager;
1313+pub use gpod::initialize;