max / audiofiles
- Co-Authored-By
- Claude Opus 4.8 (1M context) <noreply@anthropic.com>
16 files changed,
+639 insertions,
-144 deletions
| @@ -270,6 +270,7 @@ | |||
| 270 | 270 | Some(Err(msg)) => return Err(CoreError::Internal(msg)), | |
| 271 | 271 | None => audiofiles_core::store::hash_file(path)?, | |
| 272 | 272 | }; | |
| 273 | + | let hash = audiofiles_core::SampleHash::from_trusted(hash); | |
| 273 | 274 | if loose_files { | |
| 274 | 275 | store.import_loose_files_hashed(path, &hash, file_size, db)?; | |
| 275 | 276 | } else { | |
| @@ -279,7 +280,7 @@ | |||
| 279 | 280 | let ext = audiofiles_core::util::get_extension(path); | |
| 280 | 281 | ||
| 281 | 282 | match vfs::create_sample_link(db, vfs_id, parent_id, &name, &hash) { | |
| 282 | - | Ok(_) => Ok(ImportFileResult::Imported(hash, ext)), | |
| 283 | + | Ok(_) => Ok(ImportFileResult::Imported(hash.into_inner(), ext)), | |
| 283 | 284 | Err(CoreError::NameConflict(_)) => Ok(ImportFileResult::Duplicate), | |
| 284 | 285 | Err(e) => { | |
| 285 | 286 | if let CoreError::Db(ref sqlite_err) = e |
| @@ -2426,10 +2426,12 @@ | |||
| 2426 | 2426 | ||
| 2427 | 2427 | // sample_extension reads via query_sample_field, which now filters | |
| 2428 | 2428 | // out tombstoned rows. | |
| 2429 | - | let live_ext = crate::store::sample_extension(&db, "live").unwrap(); | |
| 2429 | + | let live_ext = | |
| 2430 | + | crate::store::sample_extension(&db, &crate::SampleHash::from_trusted("live")).unwrap(); | |
| 2430 | 2431 | assert_eq!(live_ext, "wav"); | |
| 2431 | 2432 | ||
| 2432 | - | let tomb_ext = crate::store::sample_extension(&db, "tomb"); | |
| 2433 | + | let tomb_ext = | |
| 2434 | + | crate::store::sample_extension(&db, &crate::SampleHash::from_trusted("tomb")); | |
| 2433 | 2435 | assert!( | |
| 2434 | 2436 | matches!(tomb_ext, Err(crate::error::CoreError::SampleNotFound(_))), | |
| 2435 | 2437 | "tombstoned sample should be hidden from sample_extension; got {tomb_ext:?}" |
| @@ -409,8 +409,22 @@ | |||
| 409 | 409 | insert_fake_sample(&db, "hash2"); | |
| 410 | 410 | ||
| 411 | 411 | let vfs_id = vfs::create_vfs(&db, "Test").unwrap(); | |
| 412 | - | vfs::create_sample_link(&db, vfs_id, None, "kick.wav", "hash1").unwrap(); | |
| 413 | - | vfs::create_sample_link(&db, vfs_id, None, "snare.wav", "hash2").unwrap(); | |
| 412 | + | vfs::create_sample_link( | |
| 413 | + | &db, | |
| 414 | + | vfs_id, | |
| 415 | + | None, | |
| 416 | + | "kick.wav", | |
| 417 | + | &crate::SampleHash::from_trusted("hash1"), | |
| 418 | + | ) | |
| 419 | + | .unwrap(); | |
| 420 | + | vfs::create_sample_link( | |
| 421 | + | &db, | |
| 422 | + | vfs_id, | |
| 423 | + | None, | |
| 424 | + | "snare.wav", | |
| 425 | + | &crate::SampleHash::from_trusted("hash2"), | |
| 426 | + | ) | |
| 427 | + | .unwrap(); | |
| 414 | 428 | ||
| 415 | 429 | (db, vfs_id) | |
| 416 | 430 | } | |
| @@ -500,7 +514,14 @@ | |||
| 500 | 514 | for i in 0..(SEARCH_RESULT_LIMIT + 50) { | |
| 501 | 515 | let hash = format!("h{i}"); | |
| 502 | 516 | insert_fake_sample(&db, &hash); | |
| 503 | - | vfs::create_sample_link(&db, vfs_id, None, &format!("kick_{i}.wav"), &hash).unwrap(); | |
| 517 | + | vfs::create_sample_link( | |
| 518 | + | &db, | |
| 519 | + | vfs_id, | |
| 520 | + | None, | |
| 521 | + | &format!("kick_{i}.wav"), | |
| 522 | + | &crate::SampleHash::from_trusted(hash.clone()), | |
| 523 | + | ) | |
| 524 | + | .unwrap(); | |
| 504 | 525 | } | |
| 505 | 526 | let filter = SearchFilter { | |
| 506 | 527 | text_query: "kick".to_string(), |
| @@ -61,5 +61,12 @@ | |||
| 61 | 61 | }; | |
| 62 | 62 | crate::analysis::save_analysis_batch(db, std::slice::from_ref(&result)).unwrap(); | |
| 63 | 63 | ||
| 64 | - | crate::vfs::create_sample_link(db, vfs_id, None, name, hash).unwrap() | |
| 64 | + | crate::vfs::create_sample_link( | |
| 65 | + | db, | |
| 66 | + | vfs_id, | |
| 67 | + | None, | |
| 68 | + | name, | |
| 69 | + | &crate::SampleHash::from_trusted(hash), | |
| 70 | + | ) | |
| 71 | + | .unwrap() | |
| 65 | 72 | } |
| @@ -243,7 +243,7 @@ | |||
| 243 | 243 | vfs_id: VfsId, | |
| 244 | 244 | parent_id: Option<NodeId>, | |
| 245 | 245 | name: &str, | |
| 246 | - | sample_hash: &str, | |
| 246 | + | sample_hash: &SampleHash, | |
| 247 | 247 | ) -> Result<NodeId> { | |
| 248 | 248 | validate_node_name(name)?; | |
| 249 | 249 | check_root_name_conflict(db, vfs_id, parent_id, name)?; | |
| @@ -706,8 +706,22 @@ | |||
| 706 | 706 | let dir = create_directory(&db, vfs_id, None, "Drums").unwrap(); | |
| 707 | 707 | insert_fake_sample(&db, "live"); | |
| 708 | 708 | insert_fake_sample(&db, "dead"); | |
| 709 | - | create_sample_link(&db, vfs_id, Some(dir), "live.wav", "live").unwrap(); | |
| 710 | - | create_sample_link(&db, vfs_id, Some(dir), "dead.wav", "dead").unwrap(); | |
| 709 | + | create_sample_link( | |
| 710 | + | &db, | |
| 711 | + | vfs_id, | |
| 712 | + | Some(dir), | |
| 713 | + | "live.wav", | |
| 714 | + | &crate::SampleHash::from_trusted("live"), | |
| 715 | + | ) | |
| 716 | + | .unwrap(); | |
| 717 | + | create_sample_link( | |
| 718 | + | &db, | |
| 719 | + | vfs_id, | |
| 720 | + | Some(dir), | |
| 721 | + | "dead.wav", | |
| 722 | + | &crate::SampleHash::from_trusted("dead"), | |
| 723 | + | ) | |
| 724 | + | .unwrap(); | |
| 711 | 725 | ||
| 712 | 726 | let before = list_full_tree(&db).unwrap(); | |
| 713 | 727 | assert!(before.iter().any(|n| n.path.ends_with("dead.wav"))); | |
| @@ -773,7 +787,14 @@ | |||
| 773 | 787 | insert_fake_sample(&db, "abc123"); | |
| 774 | 788 | let vfs_id = create_vfs(&db, "Lib").unwrap(); | |
| 775 | 789 | ||
| 776 | - | let node_id = create_sample_link(&db, vfs_id, None, "kick.wav", "abc123").unwrap(); | |
| 790 | + | let node_id = create_sample_link( | |
| 791 | + | &db, | |
| 792 | + | vfs_id, | |
| 793 | + | None, | |
| 794 | + | "kick.wav", | |
| 795 | + | &crate::SampleHash::from_trusted("abc123"), | |
| 796 | + | ) | |
| 797 | + | .unwrap(); | |
| 777 | 798 | let node = get_node(&db, node_id).unwrap(); | |
| 778 | 799 | assert_eq!(node.node_type, NodeType::Sample); | |
| 779 | 800 | assert_eq!(node.sample_hash.as_deref(), Some("abc123")); | |
| @@ -870,7 +891,14 @@ | |||
| 870 | 891 | insert_fake_sample(&db, "sample1"); | |
| 871 | 892 | let vfs_id = create_vfs(&db, "Lib").unwrap(); | |
| 872 | 893 | ||
| 873 | - | create_sample_link(&db, vfs_id, None, "zzz.wav", "sample1").unwrap(); | |
| 894 | + | create_sample_link( | |
| 895 | + | &db, | |
| 896 | + | vfs_id, | |
| 897 | + | None, | |
| 898 | + | "zzz.wav", | |
| 899 | + | &crate::SampleHash::from_trusted("sample1"), | |
| 900 | + | ) | |
| 901 | + | .unwrap(); | |
| 874 | 902 | create_directory(&db, vfs_id, None, "AAA").unwrap(); | |
| 875 | 903 | ||
| 876 | 904 | let children = list_children(&db, vfs_id, None).unwrap(); | |
| @@ -884,7 +912,14 @@ | |||
| 884 | 912 | let vfs_id = create_vfs(&db, "Lib").unwrap(); | |
| 885 | 913 | insert_fake_sample(&db, "s1"); | |
| 886 | 914 | let dir = create_directory(&db, vfs_id, None, "Dir").unwrap(); | |
| 887 | - | create_sample_link(&db, vfs_id, Some(dir), "s1.wav", "s1").unwrap(); | |
| 915 | + | create_sample_link( | |
| 916 | + | &db, | |
| 917 | + | vfs_id, | |
| 918 | + | Some(dir), | |
| 919 | + | "s1.wav", | |
| 920 | + | &crate::SampleHash::from_trusted("s1"), | |
| 921 | + | ) | |
| 922 | + | .unwrap(); | |
| 888 | 923 | ||
| 889 | 924 | let subtree = collect_subtree(&db, dir).unwrap(); | |
| 890 | 925 | assert_eq!(subtree.len(), 2); // dir + sample | |
| @@ -949,7 +984,14 @@ | |||
| 949 | 984 | let db = setup(); | |
| 950 | 985 | let vfs_id = create_vfs(&db, "Lib").unwrap(); | |
| 951 | 986 | crate::test_helpers::insert_fake_sample(&db, "hash1"); | |
| 952 | - | create_sample_link(&db, vfs_id, None, "kick.wav", "hash1").unwrap(); | |
| 987 | + | create_sample_link( | |
| 988 | + | &db, | |
| 989 | + | vfs_id, | |
| 990 | + | None, | |
| 991 | + | "kick.wav", | |
| 992 | + | &crate::SampleHash::from_trusted("hash1"), | |
| 993 | + | ) | |
| 994 | + | .unwrap(); | |
| 953 | 995 | ||
| 954 | 996 | let nodes = list_children_enriched(&db, vfs_id, None).unwrap(); | |
| 955 | 997 | assert_eq!(nodes.len(), 1); | |
| @@ -961,7 +1003,14 @@ | |||
| 961 | 1003 | let db = setup(); | |
| 962 | 1004 | let vfs_id = create_vfs(&db, "Lib").unwrap(); | |
| 963 | 1005 | crate::test_helpers::insert_fake_sample(&db, "hash1"); | |
| 964 | - | create_sample_link(&db, vfs_id, None, "kick.wav", "hash1").unwrap(); | |
| 1006 | + | create_sample_link( | |
| 1007 | + | &db, | |
| 1008 | + | vfs_id, | |
| 1009 | + | None, | |
| 1010 | + | "kick.wav", | |
| 1011 | + | &crate::SampleHash::from_trusted("hash1"), | |
| 1012 | + | ) | |
| 1013 | + | .unwrap(); | |
| 965 | 1014 | ||
| 966 | 1015 | // Set cloud_only=1 directly | |
| 967 | 1016 | db.conn() | |
| @@ -992,9 +1041,30 @@ | |||
| 992 | 1041 | insert_fake_sample(&db, "hash_a"); | |
| 993 | 1042 | insert_fake_sample(&db, "hash_b"); | |
| 994 | 1043 | insert_fake_sample(&db, "hash_c"); | |
| 995 | - | create_sample_link(&db, vfs_id, None, "a.wav", "hash_a").unwrap(); | |
| 996 | - | create_sample_link(&db, vfs_id, None, "b.wav", "hash_b").unwrap(); | |
| 997 | - | create_sample_link(&db, vfs_id, None, "c.wav", "hash_c").unwrap(); | |
| 1044 | + | create_sample_link( | |
| 1045 | + | &db, | |
| 1046 | + | vfs_id, | |
| 1047 | + | None, | |
| 1048 | + | "a.wav", | |
| 1049 | + | &crate::SampleHash::from_trusted("hash_a"), | |
| 1050 | + | ) | |
| 1051 | + | .unwrap(); | |
| 1052 | + | create_sample_link( | |
| 1053 | + | &db, | |
| 1054 | + | vfs_id, | |
| 1055 | + | None, | |
| 1056 | + | "b.wav", | |
| 1057 | + | &crate::SampleHash::from_trusted("hash_b"), | |
| 1058 | + | ) | |
| 1059 | + | .unwrap(); | |
| 1060 | + | create_sample_link( | |
| 1061 | + | &db, | |
| 1062 | + | vfs_id, | |
| 1063 | + | None, | |
| 1064 | + | "c.wav", | |
| 1065 | + | &crate::SampleHash::from_trusted("hash_c"), | |
| 1066 | + | ) | |
| 1067 | + | .unwrap(); | |
| 998 | 1068 | ||
| 999 | 1069 | let results = find_nodes_by_hashes(&db, vfs_id, &["hash_b", "hash_a"]).unwrap(); | |
| 1000 | 1070 | ||
| @@ -1027,7 +1097,14 @@ | |||
| 1027 | 1097 | let db = setup(); | |
| 1028 | 1098 | let vfs_id = create_vfs(&db, "Lib").unwrap(); | |
| 1029 | 1099 | insert_fake_sample(&db, "hash_cloud"); | |
| 1030 | - | create_sample_link(&db, vfs_id, None, "cloud.wav", "hash_cloud").unwrap(); | |
| 1100 | + | create_sample_link( | |
| 1101 | + | &db, | |
| 1102 | + | vfs_id, | |
| 1103 | + | None, | |
| 1104 | + | "cloud.wav", | |
| 1105 | + | &crate::SampleHash::from_trusted("hash_cloud"), | |
| 1106 | + | ) | |
| 1107 | + | .unwrap(); | |
| 1031 | 1108 | ||
| 1032 | 1109 | db.conn() | |
| 1033 | 1110 | .execute( | |
| @@ -1162,7 +1239,13 @@ | |||
| 1162 | 1239 | let db = setup(); | |
| 1163 | 1240 | insert_fake_sample(&db, "hash1"); | |
| 1164 | 1241 | let vfs_id = create_vfs(&db, "Lib").unwrap(); | |
| 1165 | - | let result = create_sample_link(&db, vfs_id, None, "foo/bar.wav", "hash1"); | |
| 1242 | + | let result = create_sample_link( | |
| 1243 | + | &db, | |
| 1244 | + | vfs_id, | |
| 1245 | + | None, | |
| 1246 | + | "foo/bar.wav", | |
| 1247 | + | &crate::SampleHash::from_trusted("hash1"), | |
| 1248 | + | ); | |
| 1166 | 1249 | assert!(matches!(result, Err(CoreError::InvalidNodeName(_)))); | |
| 1167 | 1250 | } | |
| 1168 | 1251 | ||
| @@ -1192,7 +1275,14 @@ | |||
| 1192 | 1275 | let vfs_id = create_vfs(&db, "Library").unwrap(); | |
| 1193 | 1276 | let drums = create_directory(&db, vfs_id, None, "Drums").unwrap(); | |
| 1194 | 1277 | let kicks = create_directory(&db, vfs_id, Some(drums), "Kicks").unwrap(); | |
| 1195 | - | create_sample_link(&db, vfs_id, Some(kicks), "808.wav", "s1").unwrap(); | |
| 1278 | + | create_sample_link( | |
| 1279 | + | &db, | |
| 1280 | + | vfs_id, | |
| 1281 | + | Some(kicks), | |
| 1282 | + | "808.wav", | |
| 1283 | + | &crate::SampleHash::from_trusted("s1"), | |
| 1284 | + | ) | |
| 1285 | + | .unwrap(); | |
| 1196 | 1286 | ||
| 1197 | 1287 | let tree = list_full_tree(&db).unwrap(); | |
| 1198 | 1288 | let paths: Vec<&str> = tree.iter().map(|n| n.path.as_str()).collect(); | |
| @@ -1216,7 +1306,14 @@ | |||
| 1216 | 1306 | let v1 = create_vfs(&db, "Alpha").unwrap(); | |
| 1217 | 1307 | let v2 = create_vfs(&db, "Beta").unwrap(); | |
| 1218 | 1308 | create_directory(&db, v1, None, "Dir1").unwrap(); | |
| 1219 | - | create_sample_link(&db, v2, None, "sample.wav", "s1").unwrap(); | |
| 1309 | + | create_sample_link( | |
| 1310 | + | &db, | |
| 1311 | + | v2, | |
| 1312 | + | None, | |
| 1313 | + | "sample.wav", | |
| 1314 | + | &crate::SampleHash::from_trusted("s1"), | |
| 1315 | + | ) | |
| 1316 | + | .unwrap(); | |
| 1220 | 1317 | ||
| 1221 | 1318 | let tree = list_full_tree(&db).unwrap(); | |
| 1222 | 1319 | let paths: Vec<&str> = tree.iter().map(|n| n.path.as_str()).collect(); |
| @@ -11,6 +11,7 @@ | |||
| 11 | 11 | ||
| 12 | 12 | use tracing::{debug, instrument, warn}; | |
| 13 | 13 | ||
| 14 | + | use crate::SampleHash; | |
| 14 | 15 | use crate::db::Database; | |
| 15 | 16 | use crate::error::{Result, io_err}; | |
| 16 | 17 | use crate::store::{sample_extension, sample_location, sample_source_path}; | |
| @@ -246,7 +247,7 @@ | |||
| 246 | 247 | let mut seen = HashSet::new(); | |
| 247 | 248 | for &hash in hashes { | |
| 248 | 249 | if seen.insert(hash) | |
| 249 | - | && let Ok(ext) = sample_extension(db, hash) | |
| 250 | + | && let Ok(ext) = sample_extension(db, &SampleHash::from_trusted(hash)) | |
| 250 | 251 | { | |
| 251 | 252 | let source = sample_source_path(db, hash).ok().flatten(); | |
| 252 | 253 | result.push((hash.to_string(), ext, source)); | |
| @@ -378,7 +379,14 @@ | |||
| 378 | 379 | create_store_file(&store_dir, "abc123", "wav"); | |
| 379 | 380 | ||
| 380 | 381 | let vfs_id = create_vfs(&db, "Library").unwrap(); | |
| 381 | - | create_sample_link(&db, vfs_id, None, "kick.wav", "abc123").unwrap(); | |
| 382 | + | create_sample_link( | |
| 383 | + | &db, | |
| 384 | + | vfs_id, | |
| 385 | + | None, | |
| 386 | + | "kick.wav", | |
| 387 | + | &crate::SampleHash::from_trusted("abc123"), | |
| 388 | + | ) | |
| 389 | + | .unwrap(); | |
| 382 | 390 | ||
| 383 | 391 | let config = make_config(&mirror_dir, &store_dir); | |
| 384 | 392 | let stats = sync_mirror(&db, &config).unwrap(); | |
| @@ -414,7 +422,14 @@ | |||
| 414 | 422 | // Deliberately do NOT create a store blob, there isn't one for loose files. | |
| 415 | 423 | ||
| 416 | 424 | let vfs_id = create_vfs(&db, "Library").unwrap(); | |
| 417 | - | create_sample_link(&db, vfs_id, None, "kick.wav", &hash).unwrap(); | |
| 425 | + | create_sample_link( | |
| 426 | + | &db, | |
| 427 | + | vfs_id, | |
| 428 | + | None, | |
| 429 | + | "kick.wav", | |
| 430 | + | &crate::SampleHash::from_trusted(hash.clone()), | |
| 431 | + | ) | |
| 432 | + | .unwrap(); | |
| 418 | 433 | ||
| 419 | 434 | let config = make_config(&mirror_dir, &store_dir); | |
| 420 | 435 | let stats = sync_mirror(&db, &config).unwrap(); | |
| @@ -441,7 +456,14 @@ | |||
| 441 | 456 | create_store_file(&store_dir, "abc123", "wav"); | |
| 442 | 457 | ||
| 443 | 458 | let vfs_id = create_vfs(&db, "Library").unwrap(); | |
| 444 | - | let node_id = create_sample_link(&db, vfs_id, None, "kick.wav", "abc123").unwrap(); | |
| 459 | + | let node_id = create_sample_link( | |
| 460 | + | &db, | |
| 461 | + | vfs_id, | |
| 462 | + | None, | |
| 463 | + | "kick.wav", | |
| 464 | + | &crate::SampleHash::from_trusted("abc123"), | |
| 465 | + | ) | |
| 466 | + | .unwrap(); | |
| 445 | 467 | ||
| 446 | 468 | let config = make_config(&mirror_dir, &store_dir); | |
| 447 | 469 | sync_mirror(&db, &config).unwrap(); | |
| @@ -477,7 +499,14 @@ | |||
| 477 | 499 | create_store_file(&store_dir, "abc123", "wav"); | |
| 478 | 500 | ||
| 479 | 501 | let vfs_id = create_vfs(&db, "Library").unwrap(); | |
| 480 | - | create_sample_link(&db, vfs_id, None, "kick.wav", "abc123").unwrap(); | |
| 502 | + | create_sample_link( | |
| 503 | + | &db, | |
| 504 | + | vfs_id, | |
| 505 | + | None, | |
| 506 | + | "kick.wav", | |
| 507 | + | &crate::SampleHash::from_trusted("abc123"), | |
| 508 | + | ) | |
| 509 | + | .unwrap(); | |
| 481 | 510 | ||
| 482 | 511 | let config = make_config(&mirror_dir, &store_dir); | |
| 483 | 512 | sync_mirror(&db, &config).unwrap(); |
| @@ -89,7 +89,14 @@ | |||
| 89 | 89 | ); | |
| 90 | 90 | ||
| 91 | 91 | // Verify the file exists in the store. | |
| 92 | - | assert!(env.store.exists(&hash, "wav").unwrap()); | |
| 92 | + | assert!( | |
| 93 | + | env.store | |
| 94 | + | .exists( | |
| 95 | + | &audiofiles_core::SampleHash::from_trusted(hash.clone()), | |
| 96 | + | "wav" | |
| 97 | + | ) | |
| 98 | + | .unwrap() | |
| 99 | + | ); | |
| 93 | 100 | ||
| 94 | 101 | // Verify the sample row exists in the database. | |
| 95 | 102 | let row_count: i64 = env | |
| @@ -110,9 +117,14 @@ | |||
| 110 | 117 | // ── Step 2: Create VFS and link sample ────────────────────── | |
| 111 | 118 | let vfs_id = vfs::create_vfs(&env.db, "TestLibrary").unwrap(); | |
| 112 | 119 | let drums_dir = vfs::create_directory(&env.db, vfs_id, None, "Drums").unwrap(); | |
| 113 | - | let node_id = | |
| 114 | - | vfs::create_sample_link(&env.db, vfs_id, Some(drums_dir), "test_sine_440.wav", &hash) | |
| 115 | - | .unwrap(); | |
| 120 | + | let node_id = vfs::create_sample_link( | |
| 121 | + | &env.db, | |
| 122 | + | vfs_id, | |
| 123 | + | Some(drums_dir), | |
| 124 | + | "test_sine_440.wav", | |
| 125 | + | &audiofiles_core::SampleHash::from_trusted(hash.clone()), | |
| 126 | + | ) | |
| 127 | + | .unwrap(); | |
| 116 | 128 | ||
| 117 | 129 | // Verify the VFS node was created correctly. | |
| 118 | 130 | let node = vfs::get_node(&env.db, node_id).unwrap(); | |
| @@ -125,7 +137,13 @@ | |||
| 125 | 137 | assert_eq!(children[0].name, "test_sine_440.wav"); | |
| 126 | 138 | ||
| 127 | 139 | // ── Step 3: Run audio analysis ────────────────────────────── | |
| 128 | - | let store_path = env.store.sample_path(&hash, "wav").unwrap(); | |
| 140 | + | let store_path = env | |
| 141 | + | .store | |
| 142 | + | .sample_path( | |
| 143 | + | &audiofiles_core::SampleHash::from_trusted(hash.clone()), | |
| 144 | + | "wav", | |
| 145 | + | ) | |
| 146 | + | .unwrap(); | |
| 129 | 147 | let config = AnalysisConfig::default(); | |
| 130 | 148 | let result = analysis::analyze_sample(&hash, &store_path, &config).unwrap(); | |
| 131 | 149 | ||
| @@ -328,7 +346,15 @@ | |||
| 328 | 346 | ); | |
| 329 | 347 | ||
| 330 | 348 | // Verify the exported file content matches the store copy. | |
| 331 | - | let store_bytes = fs::read(env.store.sample_path(&hash, "wav").unwrap()).unwrap(); | |
| 349 | + | let store_bytes = fs::read( | |
| 350 | + | env.store | |
| 351 | + | .sample_path( | |
| 352 | + | &audiofiles_core::SampleHash::from_trusted(hash.clone()), | |
| 353 | + | "wav", | |
| 354 | + | ) | |
| 355 | + | .unwrap(), | |
| 356 | + | ) | |
| 357 | + | .unwrap(); | |
| 332 | 358 | let export_bytes = fs::read(&exported_file).unwrap(); | |
| 333 | 359 | assert_eq!( | |
| 334 | 360 | store_bytes, export_bytes, | |
| @@ -397,7 +423,13 @@ | |||
| 397 | 423 | let env = TestEnv::new(); | |
| 398 | 424 | ||
| 399 | 425 | let hash = env.store.import(&env.wav_path, &env.db).unwrap(); | |
| 400 | - | let store_path = env.store.sample_path(&hash, "wav").unwrap(); | |
| 426 | + | let store_path = env | |
| 427 | + | .store | |
| 428 | + | .sample_path( | |
| 429 | + | &audiofiles_core::SampleHash::from_trusted(hash.clone()), | |
| 430 | + | "wav", | |
| 431 | + | ) | |
| 432 | + | .unwrap(); | |
| 401 | 433 | ||
| 402 | 434 | let config = AnalysisConfig::default(); | |
| 403 | 435 | let result = analysis::analyze_sample(&hash, &store_path, &config).unwrap(); | |
| @@ -450,18 +482,44 @@ | |||
| 450 | 482 | ||
| 451 | 483 | // Analyze both. | |
| 452 | 484 | let config = AnalysisConfig::default(); | |
| 453 | - | let store_path1 = env.store.sample_path(&hash1, "wav").unwrap(); | |
| 485 | + | let store_path1 = env | |
| 486 | + | .store | |
| 487 | + | .sample_path( | |
| 488 | + | &audiofiles_core::SampleHash::from_trusted(hash1.clone()), | |
| 489 | + | "wav", | |
| 490 | + | ) | |
| 491 | + | .unwrap(); | |
| 454 | 492 | let result1 = analysis::analyze_sample(&hash1, &store_path1, &config).unwrap(); | |
| 455 | 493 | analysis::save_analysis_batch(&env.db, std::slice::from_ref(&result1)).unwrap(); | |
| 456 | 494 | ||
| 457 | - | let store_path2 = env.store.sample_path(&hash2, "wav").unwrap(); | |
| 495 | + | let store_path2 = env | |
| 496 | + | .store | |
| 497 | + | .sample_path( | |
| 498 | + | &audiofiles_core::SampleHash::from_trusted(hash2.clone()), | |
| 499 | + | "wav", | |
| 500 | + | ) | |
| 501 | + | .unwrap(); | |
| 458 | 502 | let result2 = analysis::analyze_sample(&hash2, &store_path2, &config).unwrap(); | |
| 459 | 503 | analysis::save_analysis_batch(&env.db, std::slice::from_ref(&result2)).unwrap(); | |
| 460 | 504 | ||
| 461 | 505 | // Create VFS with both samples. | |
| 462 | 506 | let vfs_id = vfs::create_vfs(&env.db, "MultiTest").unwrap(); | |
| 463 | - | vfs::create_sample_link(&env.db, vfs_id, None, "test_sine_440.wav", &hash1).unwrap(); | |
| 464 | - | vfs::create_sample_link(&env.db, vfs_id, None, "bass_note_80hz.wav", &hash2).unwrap(); | |
| 507 | + | vfs::create_sample_link( | |
| 508 | + | &env.db, | |
| 509 | + | vfs_id, | |
| 510 | + | None, | |
| 511 | + | "test_sine_440.wav", | |
| 512 | + | &audiofiles_core::SampleHash::from_trusted(hash1.clone()), | |
| 513 | + | ) | |
| 514 | + | .unwrap(); | |
| 515 | + | vfs::create_sample_link( | |
| 516 | + | &env.db, | |
| 517 | + | vfs_id, | |
| 518 | + | None, | |
| 519 | + | "bass_note_80hz.wav", | |
| 520 | + | &audiofiles_core::SampleHash::from_trusted(hash2.clone()), | |
| 521 | + | ) | |
| 522 | + | .unwrap(); | |
| 465 | 523 | ||
| 466 | 524 | // Tag differently. | |
| 467 | 525 | tags::add_tag(&env.db, &hash1, "instrument.synth").unwrap(); |
| @@ -260,7 +260,14 @@ | |||
| 260 | 260 | ||
| 261 | 261 | // Create directory structure in VFS | |
| 262 | 262 | let drums_id = vfs::create_directory(db, vfs_id, None, "Drums").unwrap(); | |
| 263 | - | vfs::create_sample_link(db, vfs_id, Some(drums_id), "kick.wav", &hash).unwrap(); | |
| 263 | + | vfs::create_sample_link( | |
| 264 | + | db, | |
| 265 | + | vfs_id, | |
| 266 | + | Some(drums_id), | |
| 267 | + | "kick.wav", | |
| 268 | + | &crate::SampleHash::from_trusted(hash.clone()), | |
| 269 | + | ) | |
| 270 | + | .unwrap(); | |
| 264 | 271 | ||
| 265 | 272 | vfs_id | |
| 266 | 273 | } | |
| @@ -716,7 +723,14 @@ | |||
| 716 | 723 | let wav_path = dir.path().join("KICK.wav"); | |
| 717 | 724 | write_test_wav(&wav_path, 1, 44100, &[0.1, -0.1]); | |
| 718 | 725 | let hash2 = store.import(&wav_path, &db).unwrap(); | |
| 719 | - | vfs::create_sample_link(&db, vfs_id, None, "KICK.wav", &hash2).unwrap(); | |
| 726 | + | vfs::create_sample_link( | |
| 727 | + | &db, | |
| 728 | + | vfs_id, | |
| 729 | + | None, | |
| 730 | + | "KICK.wav", | |
| 731 | + | &crate::SampleHash::from_trusted(hash2.clone()), | |
| 732 | + | ) | |
| 733 | + | .unwrap(); | |
| 720 | 734 | ||
| 721 | 735 | let items = collect_export_items(&db, vfs_id, None).unwrap(); | |
| 722 | 736 | assert_eq!(items.len(), 2); | |
| @@ -1024,7 +1038,14 @@ | |||
| 1024 | 1038 | let wav2 = dir.path().join("snare.wav"); | |
| 1025 | 1039 | write_test_wav(&wav2, 1, 44100, &[0.1, -0.1]); | |
| 1026 | 1040 | let hash2 = store.import(&wav2, &db).unwrap(); | |
| 1027 | - | vfs::create_sample_link(&db, vfs_id, None, "snare.wav", &hash2).unwrap(); | |
| 1041 | + | vfs::create_sample_link( | |
| 1042 | + | &db, | |
| 1043 | + | vfs_id, | |
| 1044 | + | None, | |
| 1045 | + | "snare.wav", | |
| 1046 | + | &crate::SampleHash::from_trusted(hash2.clone()), | |
| 1047 | + | ) | |
| 1048 | + | .unwrap(); | |
| 1028 | 1049 | ||
| 1029 | 1050 | let items = collect_export_items(&db, vfs_id, None).unwrap(); | |
| 1030 | 1051 | assert!(items.len() >= 2); | |
| @@ -1080,7 +1101,14 @@ | |||
| 1080 | 1101 | write_test_wav(&src, 1, 44100, &samples); | |
| 1081 | 1102 | let hash = store.import(&src, &db).unwrap(); | |
| 1082 | 1103 | let vfs_id = vfs::create_vfs(&db, "v").unwrap(); | |
| 1083 | - | vfs::create_sample_link(&db, vfs_id, None, "long.wav", &hash).unwrap(); | |
| 1104 | + | vfs::create_sample_link( | |
| 1105 | + | &db, | |
| 1106 | + | vfs_id, | |
| 1107 | + | None, | |
| 1108 | + | "long.wav", | |
| 1109 | + | &crate::SampleHash::from_trusted(hash.clone()), | |
| 1110 | + | ) | |
| 1111 | + | .unwrap(); | |
| 1084 | 1112 | let items = collect_export_items(&db, vfs_id, None).unwrap(); | |
| 1085 | 1113 | ||
| 1086 | 1114 | let dest_dir = dir.path().join("export_stream_cancel"); | |
| @@ -1133,7 +1161,14 @@ | |||
| 1133 | 1161 | write_test_wav(&src, 1, 44100, &samples); | |
| 1134 | 1162 | let hash = store.import(&src, &db).unwrap(); | |
| 1135 | 1163 | let vfs_id = vfs::create_vfs(&db, "v").unwrap(); | |
| 1136 | - | vfs::create_sample_link(&db, vfs_id, None, "long.wav", &hash).unwrap(); | |
| 1164 | + | vfs::create_sample_link( | |
| 1165 | + | &db, | |
| 1166 | + | vfs_id, | |
| 1167 | + | None, | |
| 1168 | + | "long.wav", | |
| 1169 | + | &crate::SampleHash::from_trusted(hash.clone()), | |
| 1170 | + | ) | |
| 1171 | + | .unwrap(); | |
| 1137 | 1172 | let items = collect_export_items(&db, vfs_id, None).unwrap(); | |
| 1138 | 1173 | ||
| 1139 | 1174 | let dest_dir = dir.path().join("export_stream_ok"); |
| @@ -203,9 +203,9 @@ | |||
| 203 | 203 | // Stage 1: copy every slice blob into the content store. Content-addressed | |
| 204 | 204 | // and idempotent, so a failure here touches no VFS state, there is nothing | |
| 205 | 205 | // to roll back, and a retry reuses any blobs already written. | |
| 206 | - | let mut imported: Vec<(&str, String)> = Vec::with_capacity(slices.len()); | |
| 206 | + | let mut imported: Vec<(&str, crate::SampleHash)> = Vec::with_capacity(slices.len()); | |
| 207 | 207 | for (slice_name, temp_path) in &slices { | |
| 208 | - | let hash = store.import(temp_path, db)?; | |
| 208 | + | let hash = crate::SampleHash::from_trusted(store.import(temp_path, db)?); | |
| 209 | 209 | imported.push((slice_name.as_str(), hash)); | |
| 210 | 210 | } | |
| 211 | 211 | ||
| @@ -329,10 +329,13 @@ | |||
| 329 | 329 | let run_dir = temp_path.parent().map(Path::to_path_buf); | |
| 330 | 330 | let import = store.import(&temp_path, db); | |
| 331 | 331 | let _ = std::fs::remove_file(&temp_path); | |
| 332 | - | let hash = import?; | |
| 332 | + | let hash = crate::SampleHash::from_trusted(import?); | |
| 333 | 333 | vfs::create_sample_link(db, vfs_id, parent_id, &out_name, &hash)?; | |
| 334 | 334 | cleanup_run_dir(run_dir.as_deref()); | |
| 335 | - | Ok(ConformResult { hash, overshoot }) | |
| 335 | + | Ok(ConformResult { | |
| 336 | + | hash: hash.into_inner(), | |
| 337 | + | overshoot, | |
| 338 | + | }) | |
| 336 | 339 | } | |
| 337 | 340 | ||
| 338 | 341 | /// Decode `source_path`, conform it to `target`, and write the result as a new | |
| @@ -574,8 +577,12 @@ | |||
| 574 | 577 | assert_eq!(children[0].name, "pad_22k_16bit.wav"); | |
| 575 | 578 | ||
| 576 | 579 | // The stored file decodes back at the conformed spec. | |
| 577 | - | let ext = crate::store::sample_extension(&db, &hash).unwrap(); | |
| 578 | - | let path = store.sample_path(&hash, &ext).unwrap(); | |
| 580 | + | let ext = | |
| 581 | + | crate::store::sample_extension(&db, &crate::SampleHash::from_trusted(hash.clone())) | |
| 582 | + | .unwrap(); | |
| 583 | + | let path = store | |
| 584 | + | .sample_path(&crate::SampleHash::from_trusted(hash.clone()), &ext) | |
| 585 | + | .unwrap(); | |
| 579 | 586 | let decoded = decode_multichannel(&path).unwrap(); | |
| 580 | 587 | assert_eq!(decoded.sample_rate, 22050); | |
| 581 | 588 | assert_eq!(decoded.channels, 1); |