A tool to sync music with your favorite devices
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

*: use embedded album art if on-disk one doesn't exist, store lyrics/description if any

Gee Sawra 46c66b6f 44d57b19

+110 -21
-12
.sqlx/query-7df5b4b79676a84928fe29acafd880afa71213b84515916aae4e3eb373bf134a.json
··· 1 - { 2 - "db_name": "SQLite", 3 - "query": "\n INSERT OR REPLACE INTO tracks (\n kind,\n track_id,\n title,\n artist,\n album,\n genre,\n track_len,\n year,\n number,\n file_path,\n disc_number,\n disc_total,\n file_state,\n extension,\n artwork_path\n ) VALUES (\n ?1,\n ?2,\n ?3,\n ?4,\n ?5,\n ?6,\n ?7,\n ?8,\n ?9,\n ?10,\n ?11,\n ?12,\n ?13,\n ?14,\n ?15\n );\n ", 4 - "describe": { 5 - "columns": [], 6 - "parameters": { 7 - "Right": 15 8 - }, 9 - "nullable": [] 10 - }, 11 - "hash": "7df5b4b79676a84928fe29acafd880afa71213b84515916aae4e3eb373bf134a" 12 - }
+13 -1
.sqlx/query-86245ea0c3119d94e08fe0404482b4f8de3241bc067136c0181ff4e0aa2bcc67.json
··· 82 82 "name": "kind", 83 83 "ordinal": 15, 84 84 "type_info": "Int64" 85 + }, 86 + { 87 + "name": "description", 88 + "ordinal": 16, 89 + "type_info": "Text" 90 + }, 91 + { 92 + "name": "artwork_bytes", 93 + "ordinal": 17, 94 + "type_info": "Blob" 85 95 } 86 96 ], 87 97 "parameters": { ··· 103 113 false, 104 114 false, 105 115 true, 106 - false 116 + false, 117 + true, 118 + true 107 119 ] 108 120 }, 109 121 "hash": "86245ea0c3119d94e08fe0404482b4f8de3241bc067136c0181ff4e0aa2bcc67"
+13 -1
.sqlx/query-90d659e4ebb83559ac94ba4618a065c5d9966e99d5c318c5b8f9dcaa670ae5b2.json
··· 82 82 "name": "kind", 83 83 "ordinal": 15, 84 84 "type_info": "Int64" 85 + }, 86 + { 87 + "name": "description", 88 + "ordinal": 16, 89 + "type_info": "Text" 90 + }, 91 + { 92 + "name": "artwork_bytes", 93 + "ordinal": 17, 94 + "type_info": "Blob" 85 95 } 86 96 ], 87 97 "parameters": { ··· 103 113 false, 104 114 false, 105 115 true, 106 - false 116 + false, 117 + true, 118 + true 107 119 ] 108 120 }, 109 121 "hash": "90d659e4ebb83559ac94ba4618a065c5d9966e99d5c318c5b8f9dcaa670ae5b2"
+13 -1
.sqlx/query-f1349b72ca08058d2503c7348ee66ef638dfc1c3ce09cfd2167c01237ed25840.json
··· 82 82 "name": "kind", 83 83 "ordinal": 15, 84 84 "type_info": "Int64" 85 + }, 86 + { 87 + "name": "description", 88 + "ordinal": 16, 89 + "type_info": "Text" 90 + }, 91 + { 92 + "name": "artwork_bytes", 93 + "ordinal": 17, 94 + "type_info": "Blob" 85 95 } 86 96 ], 87 97 "parameters": { ··· 103 113 false, 104 114 false, 105 115 true, 106 - false 116 + false, 117 + true, 118 + true 107 119 ] 108 120 }, 109 121 "hash": "f1349b72ca08058d2503c7348ee66ef638dfc1c3ce09cfd2167c01237ed25840"
+12
.sqlx/query-fb1b6f8f014dffd156eb3239412bf4b17580774e340eef77d21653168bf267fe.json
··· 1 + { 2 + "db_name": "SQLite", 3 + "query": "\n INSERT OR REPLACE INTO tracks (\n kind,\n track_id,\n title,\n artist,\n album,\n genre,\n track_len,\n year,\n number,\n file_path,\n disc_number,\n disc_total,\n file_state,\n extension,\n artwork_path,\n artwork_bytes,\n description\n ) VALUES (\n ?1,\n ?2,\n ?3,\n ?4,\n ?5,\n ?6,\n ?7,\n ?8,\n ?9,\n ?10,\n ?11,\n ?12,\n ?13,\n ?14,\n ?15,\n ?16,\n ?17\n );\n ", 4 + "describe": { 5 + "columns": [], 6 + "parameters": { 7 + "Right": 17 8 + }, 9 + "nullable": [] 10 + }, 11 + "hash": "fb1b6f8f014dffd156eb3239412bf4b17580774e340eef77d21653168bf267fe" 12 + }
+1
Justfile
··· 6 6 DATABASE_URL=sqlite://.temp.db cargo sqlx prepare 7 7 rm .temp.db 8 8 9 + # Create a new named migration. 9 10 new_migration name: 10 11 cargo sqlx migrate add --source src/db/migrations {{ name }}
+15 -3
src/db/instance.rs
··· 235 235 disc_total, 236 236 file_state, 237 237 extension, 238 - artwork_path 238 + artwork_path, 239 + artwork_bytes, 240 + description 239 241 ) VALUES ( 240 242 ?1, 241 243 ?2, ··· 251 253 ?12, 252 254 ?13, 253 255 ?14, 254 - ?15 256 + ?15, 257 + ?16, 258 + ?17 255 259 ); 256 260 "#, 257 261 track.kind, ··· 268 272 track.disc_total, 269 273 track.file_state, 270 274 track.extension, 271 - track.artwork_path 275 + track.artwork_path, 276 + track.artwork_bytes, 277 + track.description 272 278 ) 273 279 .execute(&mut *conn) 274 280 .await?; ··· 315 321 year: year as usize, 316 322 artwork_path: r.get("artwork_path"), 317 323 kind: r.get("kind"), 324 + artwork_bytes: r.get("artwork_bytes"), 325 + description: r.get("description"), 318 326 } 319 327 }) 320 328 .collect()) ··· 352 360 track_len: r.track_len, 353 361 year: r.year as usize, 354 362 artwork_path: r.artwork_path, 363 + artwork_bytes: r.artwork_bytes, 364 + description: r.description, 355 365 }) 356 366 .collect::<Vec<model::Track>>()) 357 367 } ··· 543 553 track_len: track.track_len, 544 554 year: track.year as usize, 545 555 artwork_path: track.artwork_path, 556 + artwork_bytes: track.artwork_bytes, 557 + description: track.description, 546 558 })) 547 559 .await 548 560 .unwrap(),
+2
src/db/migrations/0007_track_descr_embedded_albumart.sql
··· 1 + ALTER TABLE tracks ADD description TEXT; 2 + ALTER TABLE tracks ADD artwork_bytes BLOB;
+15
src/gpod/gpod.rs
··· 134 134 (*ipod_track).soundcheck = sc; 135 135 } 136 136 137 + if let Some(d) = track.description.clone() { 138 + (*ipod_track).description = c_strdup(&d); 139 + } 140 + 137 141 let mut error: *mut GError = ptr::null_mut(); 138 142 let path = CString::new(file.display().to_string()).unwrap(); 139 143 if let Some(art) = track.artwork_path.clone() { 140 144 let art = CString::new(art).unwrap(); 141 145 if itdb_track_set_thumbnails(ipod_track, art.as_ptr()) != 1 { 142 146 log::warn!("could not add album art to track") 147 + } 148 + } else if let Some(ab) = track.artwork_bytes.clone() { 149 + if itdb_track_set_thumbnails_from_data( 150 + ipod_track, 151 + ab.as_ptr(), 152 + ab.len().try_into().unwrap(), 153 + ) != 1 154 + { 155 + log::warn!("could not add album art bytes (embedded in track) to ipod") 143 156 } 144 157 } 145 158 itdb_track_add(self.db_ptr, ipod_track, -1); ··· 265 278 extension: file_type, 266 279 artwork_path: None, 267 280 kind: self.kind, 281 + artwork_bytes: None, 282 + description: Some(string((*it_track).description)), 268 283 }; 269 284 270 285 track.track_id = model::track_hash(&track);
+26 -3
src/model.rs
··· 1 1 use anyhow::{Context, anyhow}; 2 2 use lofty::{ 3 3 file::{AudioFile, TaggedFile, TaggedFileExt}, 4 - tag::Accessor, 4 + tag::{Accessor, ItemKey}, 5 5 }; 6 6 use once_cell::sync::Lazy; 7 7 use rhai::{CustomType, TypeBuilder}; ··· 106 106 pub file_state: FileState, 107 107 pub extension: String, 108 108 pub artwork_path: Option<String>, 109 + pub artwork_bytes: Option<Vec<u8>>, 110 + pub description: Option<String>, 109 111 } 110 112 111 113 impl std::fmt::Display for Track { ··· 153 155 anyhow!("could not read track tags") 154 156 .context(format!("file path: {}", track.path.clone())), 155 157 )?; 158 + 159 + let lyrics = tags.get_string(&ItemKey::Lyrics); 160 + let description = tags.get_string(&ItemKey::Description); 161 + 162 + let description = if let Some(d) = description { 163 + Some(d.to_owned()) 164 + } else if let Some(l) = lyrics { 165 + Some(l.to_owned()) 166 + } else { 167 + None 168 + }; 169 + 156 170 let length = track.tags.properties().duration(); 157 - let artist = tags.artist().unwrap_or("Uknown Artist".into()).to_string(); 171 + let artist = tags 172 + .artist() 173 + .unwrap_or(match track.kind { 174 + TrackKind::Unknown => panic!("impossible!"), 175 + TrackKind::Music => "Unknown Artist".into(), 176 + TrackKind::Podcast => tags.album().unwrap_or("Unknown Artist".into()), 177 + }) 178 + .to_string(); 158 179 let title = tags.title().unwrap_or("Unknown Title".into()).to_string(); 159 180 let album = tags.album().unwrap_or("Unknown Album".into()).to_string(); 160 181 let genre = tags.genre().unwrap_or("Unknown Genre".into()).to_string(); 161 - tags.pictures() 182 + let artwork_bytes = tags.pictures().get(0).map(|p| p.data().to_vec()); 162 183 163 184 let mut t = Self { 164 185 id: 0, ··· 177 198 track_len: length.as_millis() as i64, 178 199 year: tags.year().unwrap_or(1970) as usize, 179 200 artwork_path: track.artwork_path, 201 + artwork_bytes: artwork_bytes, 202 + description: description, 180 203 }; 181 204 182 205 t.track_id = track_hash(&t);