diff --git a/musikr/src/main/jni/build.rs b/musikr/src/main/jni/build.rs index de1055e88..0b4c6537e 100644 --- a/musikr/src/main/jni/build.rs +++ b/musikr/src/main/jni/build.rs @@ -110,6 +110,7 @@ fn main() { cxx_build::bridge("src/taglib/ffi.rs") .file("shim/iostream_shim.cpp") .file("shim/file_shim.cpp") + .file("shim/tk_shim.cpp") .include(format!("taglib/pkg/{}/include", arch)) .include("shim") .include(".") // Add the current directory to include path @@ -122,5 +123,7 @@ fn main() { println!("cargo:rerun-if-changed=shim/iostream_shim.cpp"); println!("cargo:rerun-if-changed=shim/file_shim.hpp"); println!("cargo:rerun-if-changed=shim/file_shim.cpp"); + println!("cargo:rerun-if-changed=shim/tk_shim.hpp"); + println!("cargo:rerun-if-changed=shim/tk_shim.cpp"); println!("cargo:rerun-if-changed=src/taglib/ffi.rs"); } diff --git a/musikr/src/main/jni/shim/tk_shim.cpp b/musikr/src/main/jni/shim/tk_shim.cpp new file mode 100644 index 000000000..171394cd1 --- /dev/null +++ b/musikr/src/main/jni/shim/tk_shim.cpp @@ -0,0 +1,30 @@ +#include "tk_shim.hpp" + + +namespace taglib_shim { +Property::Property(TagLib::String key, TagLib::StringList value) : key_(key), value_(value) {} + +const TagLib::String &Property::key() const { + return key_; +} + +const TagLib::StringList &Property::value() const { + return value_; +} + std::unique_ptr> SimplePropertyMap_to_vector(const TagLib::SimplePropertyMap &map) { + std::unique_ptr> result = std::make_unique>(); + for (const auto &pair : map) { + result->push_back(Property(pair.first, pair.second)); + } + return result; + } + + std::unique_ptr> StringList_to_vector(const TagLib::StringList &list) { + std::unique_ptr> result = std::make_unique>(); + for (const auto &str : list) { + result->push_back(str); + } + return result; + } + +} diff --git a/musikr/src/main/jni/shim/tk_shim.hpp b/musikr/src/main/jni/shim/tk_shim.hpp new file mode 100644 index 000000000..ae84faae1 --- /dev/null +++ b/musikr/src/main/jni/shim/tk_shim.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "taglib/tpropertymap.h" +#include "taglib/xiphcomment.h" +#include "taglib/tstring.h" +#include "taglib/tstringlist.h" +#include +#include + +namespace taglib_shim { + +struct Property { + Property(TagLib::String key, TagLib::StringList value); + const TagLib::String &key() const; + const TagLib::StringList &value() const; + +private: + TagLib::String key_; + TagLib::StringList value_; +}; + + std::unique_ptr> SimplePropertyMap_to_vector(const TagLib::SimplePropertyMap &map); + std::unique_ptr> StringList_to_vector(const TagLib::StringList &list); +} \ No newline at end of file diff --git a/musikr/src/main/jni/src/taglib/ffi.rs b/musikr/src/main/jni/src/taglib/ffi.rs index 7471b3a2b..d306006ee 100644 --- a/musikr/src/main/jni/src/taglib/ffi.rs +++ b/musikr/src/main/jni/src/taglib/ffi.rs @@ -3,10 +3,12 @@ pub(crate) mod bindings { unsafe extern "C++" { include!("taglib/taglib.h"); include!("taglib/tstring.h"); + include!("taglib/tstringlist.h"); include!("taglib/vorbisfile.h"); include!("taglib/xiphcomment.h"); include!("shim/iostream_shim.hpp"); include!("shim/file_shim.hpp"); + include!("shim/tk_shim.hpp"); #[namespace = "TagLib"] type FileRef; @@ -47,13 +49,11 @@ pub(crate) mod bindings { #[namespace = "TagLib::Ogg::Opus"] #[cxx_name = "File"] type OpusFile; + unsafe fn tag(self: Pin<&OpusFile>) -> *mut XiphComment; #[namespace = "TagLib::Ogg"] type XiphComment; - unsafe fn fieldListMap(self: Pin<&XiphComment>) -> &FieldListMap; - - #[namespace = "TagLib::Ogg"] - type FieldListMap; + unsafe fn fieldListMap(self: Pin<&XiphComment>) -> &SimplePropertyMap; #[namespace = "TagLib::MPEG"] #[cxx_name = "File"] @@ -94,6 +94,21 @@ pub(crate) mod bindings { #[namespace = "taglib_shim"] unsafe fn File_asAPE(file: *mut File) -> *mut APEFile; + #[namespace = "TagLib"] + type SimplePropertyMap; + #[namespace = "taglib_shim"] + fn SimplePropertyMap_to_vector(field_list_map: Pin<&SimplePropertyMap>) -> UniquePtr>; + + #[namespace = "taglib_shim"] + type Property; + fn key(self: Pin<&Property>) -> &TagString; + unsafe fn value(self: Pin<&Property>) -> &StringList; + + #[namespace = "TagLib"] + type StringList; + #[namespace = "taglib_shim"] + fn StringList_to_vector(string_list: Pin<&StringList>) -> UniquePtr>; + #[namespace = "TagLib"] #[cxx_name = "String"] type TagString; diff --git a/musikr/src/main/jni/src/taglib/mod.rs b/musikr/src/main/jni/src/taglib/mod.rs index 265b37756..31efec3de 100644 --- a/musikr/src/main/jni/src/taglib/mod.rs +++ b/musikr/src/main/jni/src/taglib/mod.rs @@ -2,9 +2,13 @@ mod ffi; mod stream; use ffi::bindings; -use std::pin::Pin; +use std::ffi::CStr; +use std::pin::{pin, Pin}; +use std::collections::HashMap; pub use stream::{RustStream, TagLibStream}; +type XiphComments = HashMap>; + pub enum File { Unknown { audio_properties: Option, @@ -14,15 +18,18 @@ pub enum File { }, FLAC { audio_properties: Option, + xiph_comments: Option, }, MP4 { audio_properties: Option, }, OGG { audio_properties: Option, + xiph_comments: Option, }, Opus { audio_properties: Option, + xiph_comments: Option, }, WAV { audio_properties: Option, @@ -100,7 +107,7 @@ impl FileRef { let flac_file = ffi::bindings::File_asFLAC(file_ptr); if !flac_file.is_null() { return Some(FileRef { - file: File::FLAC { audio_properties } + file: File::FLAC { audio_properties, xiph_comments: None } }); } @@ -134,15 +141,56 @@ impl FileRef { let vorbis_file = ffi::bindings::File_asVorbis(file_ptr); if !vorbis_file.is_null() { + let pinned_vorbis_file = Pin::new_unchecked(&*vorbis_file); + let xiph_comments = pinned_vorbis_file.tag(); + let pinned_xiph_comments = Pin::new_unchecked(&*xiph_comments); + let xiph_map = pinned_xiph_comments.fieldListMap(); + let pinned_xiph_map = Pin::new_unchecked(xiph_map); + let xiph_comments = ffi::bindings::SimplePropertyMap_to_vector(pinned_xiph_map); + + let mut xiph_safe_map = XiphComments::new(); + for property in xiph_comments.iter() { + let pinned_property = Pin::new_unchecked(property); + let tag_key = pinned_property.key(); + let pinned_key = Pin::new_unchecked(&*tag_key); + let c_str = pinned_key.toCString(true); + let key = CStr::from_ptr(c_str).to_string_lossy().to_string(); + + let tag_values = pinned_property.value(); + let pinned_values = Pin::new_unchecked(&*tag_values); + let cxx_vec_values = ffi::bindings::StringList_to_vector(pinned_values); + let values = cxx_vec_values.iter().map(|value| { + let pinned_value = Pin::new_unchecked(value); + let c_str = pinned_value.toCString(true); + CStr::from_ptr(c_str).to_string_lossy().to_string() + }).collect(); + + xiph_safe_map.insert(key, values); + } + return Some(FileRef { - file: File::OGG { audio_properties } + file: File::OGG { audio_properties, xiph_comments: Some(xiph_safe_map) } }); } let opus_file = ffi::bindings::File_asOpus(file_ptr); if !opus_file.is_null() { + let pinned_opus_file = Pin::new_unchecked(&*opus_file); + let xiph_comments = pinned_opus_file.tag(); + let pinned_xiph_comments = Pin::new_unchecked(&*xiph_comments); + let xiph_map = pinned_xiph_comments.fieldListMap(); + let pinned_xiph_map = Pin::new_unchecked(xiph_map); + let xiph_comments = ffi::bindings::SimplePropertyMap_to_vector(pinned_xiph_map); + + let mut xiph_safe_map = XiphComments::new(); + for property in xiph_comments.iter() { + let pinned_property = Pin::new_unchecked(property); + let tag_key = pinned_property.key(); + let pinned_key = Pin::new_unchecked(&*tag_key); + } + return Some(FileRef { - file: File::Opus { audio_properties } + file: File::Opus { audio_properties, xiph_comments: Some(xiph_safe_map) } }); }