Skip to content

Commit 0bc54f6

Browse files
author
Frieder Hannenheim
authored
refactor: use lofty-rs for metadata instead of audiotags (#21)
* Use lofty-rs for metadata instead of audiotags * Add support for album artist tag * Add my lofty-rs fork
1 parent 52379c7 commit 0bc54f6

File tree

9 files changed

+70
-38
lines changed

9 files changed

+70
-38
lines changed

packages/metadata_god/example/lib/main.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,9 @@ class _MyAppState extends State<MyApp> {
9494
ElevatedButton(
9595
onPressed: () async {
9696
final result = await FilePicker.platform.pickFiles(
97+
type: FileType.audio,
9798
allowCompression: false,
9899
allowMultiple: false,
99-
allowedExtensions: ["m4a", "mp3", 'flac', 'ogg'],
100100
withData: false,
101101
withReadStream: false,
102102
);

packages/metadata_god/lib/src/bridge_generated.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// AUTO GENERATED FILE, DO NOT EDIT.
2-
// Generated by `flutter_rust_bridge`@ 1.72.1.
2+
// Generated by `flutter_rust_bridge`@ 1.72.2.
33
// ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member, prefer_is_empty, unnecessary_const
44

55
import 'dart:convert';

packages/metadata_god/lib/src/bridge_generated.io.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// AUTO GENERATED FILE, DO NOT EDIT.
2-
// Generated by `flutter_rust_bridge`@ 1.72.1.
2+
// Generated by `flutter_rust_bridge`@ 1.72.2.
33
// ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member, prefer_is_empty, unnecessary_const
44

55
import 'dart:convert';
@@ -421,4 +421,4 @@ class wire_Metadata extends ffi.Struct {
421421

422422
typedef DartPostCObjectFnType = ffi.Pointer<
423423
ffi.NativeFunction<ffi.Bool Function(DartPort, ffi.Pointer<ffi.Void>)>>;
424-
typedef DartPort = ffi.Int64;
424+
typedef DartPort = ffi.Int64;

packages/metadata_god/lib/src/bridge_generated.web.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// AUTO GENERATED FILE, DO NOT EDIT.
2-
// Generated by `flutter_rust_bridge`@ 1.72.1.
2+
// Generated by `flutter_rust_bridge`@ 1.72.2.
33
// ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member, prefer_is_empty, unnecessary_const
44

55
import 'dart:convert';

packages/metadata_god/native/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ flutter_rust_bridge_codegen = "1.72.*"
1212
[dependencies]
1313
flutter_rust_bridge = "1.72.*"
1414
anyhow = "1"
15-
audiotags = "0.4.1"
15+
lofty = { git = "https://github.com/FriederHannenheim/lofty-rs.git" }
+61-29
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use anyhow::Result;
2-
use audiotags::{MimeType, Tag};
2+
use lofty::{Accessor, AudioFile, PictureType, Tag, TaggedFile, TaggedFileExt, MimeType, TagExt, ItemKey, ItemValue, TagItem};
33

44
#[derive(Debug)]
55
pub struct Picture {
@@ -27,24 +27,32 @@ pub struct Metadata {
2727
}
2828

2929
pub fn read_metadata(file: String) -> Result<Metadata> {
30-
let tag = Tag::default().read_from_path(&file)?;
31-
let cover = tag.album_cover();
30+
let (tagged_file, tag) = get_tag_for_file(&file)?;
31+
let cover = tag
32+
.get_picture_type(PictureType::CoverFront)
33+
.or(tag.pictures().first());
3234
Ok(Metadata {
3335
title: tag.title().and_then(|s| Some(s.to_string())),
34-
duration_ms: tag.duration(),
35-
album: tag.album_title().and_then(|s| Some(s.to_string())),
36-
album_artist: tag.album_artist().and_then(|s| Some(s.to_string())),
36+
duration_ms: Some(tagged_file.properties().duration().as_millis() as f64),
37+
album: tag.album().and_then(|s| Some(s.to_string())),
38+
album_artist: tag.get(&ItemKey::AlbumArtist).and_then(|s| {
39+
match s.value() {
40+
ItemValue::Text(t) => Some(t.to_string()),
41+
_ => None
42+
}
43+
}
44+
),
3745
artist: tag.artist().and_then(|s| Some(s.to_string())),
38-
track_number: tag.track_number(),
39-
track_total: tag.total_tracks(),
40-
disc_number: tag.disc_number(),
41-
disc_total: tag.total_discs(),
42-
year: tag.year(),
46+
track_number: tag.track().map(|f| f as u16),
47+
track_total: tag.track_total().map(|f| f as u16),
48+
disc_number: tag.disk().map(|f| f as u16),
49+
disc_total: tag.disk_total().map(|f| f as u16),
50+
year: tag.year().map(|f| f as i32),
4351
genre: tag.genre().and_then(|s| Some(s.to_string())),
4452
picture: (match cover {
4553
Some(cover) => Some(Picture {
46-
mime_type: String::from(cover.mime_type),
47-
data: cover.data.to_vec(),
54+
mime_type: cover.mime_type().to_string(),
55+
data: cover.data().to_vec(),
4856
}),
4957
None => None,
5058
}),
@@ -53,46 +61,70 @@ pub fn read_metadata(file: String) -> Result<Metadata> {
5361
}
5462

5563
pub fn write_metadata(file: String, metadata: Metadata) -> Result<()> {
56-
let mut tag = Tag::default().read_from_path(&file)?;
64+
let (_tagged_file, mut tag) = get_tag_for_file(&file)?;
5765

5866
if metadata.title.is_some() {
59-
tag.set_title(metadata.title.unwrap().as_str());
67+
tag.set_title(metadata.title.unwrap());
6068
}
6169
if metadata.album.is_some() {
62-
tag.set_album_title(metadata.album.unwrap().as_str());
70+
tag.set_album(metadata.album.unwrap());
6371
}
6472
if metadata.album_artist.is_some() {
65-
tag.set_album_artist(metadata.album_artist.unwrap().as_str());
73+
tag.insert(TagItem::new(ItemKey::AlbumArtist, ItemValue::Text(metadata.album_artist.unwrap())));
6674
}
6775
if metadata.artist.is_some() {
68-
tag.set_artist(metadata.artist.unwrap().as_str());
76+
tag.set_artist(metadata.artist.unwrap());
6977
}
7078
if metadata.track_number.is_some() {
71-
tag.set_track_number(metadata.track_number.unwrap());
79+
tag.set_track(metadata.track_number.unwrap() as u32);
7280
}
7381
if metadata.track_total.is_some() {
74-
tag.set_total_tracks(metadata.track_total.unwrap());
82+
tag.set_track_total(metadata.track_total.unwrap() as u32);
7583
}
7684
if metadata.disc_number.is_some() {
77-
tag.set_disc_number(metadata.disc_number.unwrap());
85+
tag.set_disk(metadata.disc_number.unwrap() as u32);
7886
}
7987
if metadata.disc_total.is_some() {
80-
tag.set_total_discs(metadata.disc_total.unwrap());
88+
tag.set_disk_total(metadata.disc_total.unwrap() as u32);
8189
}
8290
if metadata.year.is_some() {
83-
tag.set_year(metadata.year.unwrap());
91+
tag.set_year(metadata.year.unwrap() as u32);
8492
}
8593
if metadata.genre.is_some() {
86-
tag.set_genre(metadata.genre.unwrap().as_str());
94+
tag.set_genre(metadata.genre.unwrap());
8795
}
8896
if metadata.picture.is_some() {
8997
let image = metadata.picture.unwrap();
90-
tag.set_album_cover(audiotags::Picture {
91-
mime_type: MimeType::try_from(image.mime_type.as_str()).unwrap(),
92-
data: &image.data,
93-
});
98+
tag.push_picture(lofty::Picture::new_unchecked(
99+
PictureType::CoverFront,
100+
MimeType::from_str(&image.mime_type),
101+
None,
102+
image.data,
103+
));
94104
}
95105

96-
tag.write_to_path(&file)?;
106+
tag.save_to_path(&file)?;
97107
Ok(())
98108
}
109+
110+
fn get_tag_for_file(file: &str) -> Result<(TaggedFile, Tag)> {
111+
let mut tagged_file = lofty::read_from_path(file)?;
112+
113+
let tag = match tagged_file.primary_tag_mut() {
114+
Some(primary_tag) => primary_tag,
115+
None => {
116+
if let Some(first_tag) = tagged_file.first_tag_mut() {
117+
first_tag
118+
} else {
119+
let tag_type = tagged_file.primary_tag_type();
120+
121+
eprintln!("WARN: No tags found, creating a new tag of type `{tag_type:?}`");
122+
tagged_file.insert_tag(Tag::new(tag_type));
123+
124+
tagged_file.primary_tag_mut().unwrap()
125+
}
126+
}
127+
}
128+
.to_owned();
129+
Ok((tagged_file, tag))
130+
}

packages/metadata_god/native/src/bridge_generated.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
clippy::too_many_arguments
1010
)]
1111
// AUTO GENERATED FILE, DO NOT EDIT.
12-
// Generated by `flutter_rust_bridge`@ 1.72.1.
12+
// Generated by `flutter_rust_bridge`@ 1.72.2.
1313

1414
use crate::api::*;
1515
use core::panic::UnwindSafe;
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
mod bridge_generated; /* AUTO INJECTED BY flutter_rust_bridge. This line may not be accurate, and you can change it according to your needs. */
21
mod api;
2+
mod bridge_generated; /* AUTO INJECTED BY flutter_rust_bridge. This line may not be accurate, and you can change it according to your needs. */

packages/metadata_god/pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,4 @@ flutter:
3636
macos:
3737
ffiPlugin: true
3838
windows:
39-
ffiPlugin: true
39+
ffiPlugin: true

0 commit comments

Comments
 (0)