musikr: add mp4 item bindings

This commit is contained in:
Alexander Capehart 2025-02-17 11:35:15 -07:00
parent 608082a49f
commit 2c03cf8fed
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
8 changed files with 361 additions and 11 deletions

View file

@ -24,4 +24,62 @@ namespace taglib_shim {
return entries;
}
IntPair::IntPair(int first, int second)
: first_(first), second_(second) {}
int IntPair::first() const {
return first_;
}
int IntPair::second() const {
return second_;
}
CoverArt::CoverArt(TagLib::MP4::CoverArt::Format format, const TagLib::ByteVector& data)
: art_(format, data) {}
uint32_t CoverArt::format() const {
return static_cast<uint32_t>(art_.format());
}
std::unique_ptr<TagLib::ByteVector> CoverArt::data() const {
return std::make_unique<TagLib::ByteVector>(art_.data());
}
CoverArtList::CoverArtList(const TagLib::MP4::CoverArtList& list)
: list_(list) {}
std::unique_ptr<std::vector<CoverArt>> CoverArtList::to_vector() const {
auto vec = std::make_unique<std::vector<CoverArt>>();
for (const auto& item : list_) {
vec->emplace_back(item.format(), item.data());
}
return vec;
}
unsigned int Item_type(const TagLib::MP4::Item& item) {
return static_cast<unsigned int>(item.type());
}
std::unique_ptr<IntPair> Item_toIntPair(const TagLib::MP4::Item& item) {
auto pair = item.toIntPair();
return std::make_unique<IntPair>(pair.first, pair.second);
}
std::unique_ptr<TagLib::StringList> Item_toStringList(const TagLib::MP4::Item& item) {
return std::make_unique<TagLib::StringList>(item.toStringList());
}
std::unique_ptr<TagLib::ByteVectorList> Item_toByteVectorList(const TagLib::MP4::Item& item) {
return std::make_unique<TagLib::ByteVectorList>(item.toByteVectorList());
}
std::unique_ptr<CoverArtList> Item_toCoverArtList(const TagLib::MP4::Item& item) {
return std::make_unique<CoverArtList>(item.toCoverArtList());
}
int64_t Item_toLongLong(const TagLib::MP4::Item& item) {
return item.toLongLong();
}
}

View file

@ -19,4 +19,41 @@ namespace taglib_shim {
};
std::unique_ptr<std::vector<ItemMapEntry>> ItemMap_to_entries(const TagLib::MP4::ItemMap& map);
class IntPair {
public:
IntPair(int first, int second);
int first() const;
int second() const;
private:
int first_;
int second_;
};
class CoverArt {
public:
CoverArt(TagLib::MP4::CoverArt::Format format, const TagLib::ByteVector& data);
uint32_t format() const;
std::unique_ptr<TagLib::ByteVector> data() const;
private:
TagLib::MP4::CoverArt art_;
};
class CoverArtList {
public:
CoverArtList(const TagLib::MP4::CoverArtList& list);
std::unique_ptr<std::vector<CoverArt>> to_vector() const;
private:
TagLib::MP4::CoverArtList list_;
};
unsigned int Item_type(const TagLib::MP4::Item& item);
std::unique_ptr<IntPair> Item_toIntPair(const TagLib::MP4::Item& item);
std::unique_ptr<TagLib::StringList> Item_toStringList(const TagLib::MP4::Item& item);
std::unique_ptr<TagLib::ByteVectorList> Item_toByteVectorList(const TagLib::MP4::Item& item);
std::unique_ptr<CoverArtList> Item_toCoverArtList(const TagLib::MP4::Item& item);
int64_t Item_toLongLong(const TagLib::MP4::Item& item);
}

View file

@ -32,4 +32,14 @@ namespace taglib_shim
}
return result;
}
std::unique_ptr<std::vector<TagLib::ByteVector>> ByteVectorList_to_vector(const TagLib::ByteVectorList &list)
{
std::unique_ptr<std::vector<TagLib::ByteVector>> result = std::make_unique<std::vector<TagLib::ByteVector>>();
for (const auto &vec : list)
{
result->push_back(vec);
}
return result;
}
}

View file

@ -30,4 +30,6 @@ namespace taglib_shim
std::unique_ptr<std::vector<Property>> SimplePropertyMap_to_vector(const TagLib::SimplePropertyMap &map);
std::unique_ptr<std::vector<TagLib::String>> StringList_to_vector(const TagLib::StringList &list);
std::unique_ptr<std::vector<TagLib::ByteVector>> ByteVectorList_to_vector(const TagLib::ByteVectorList &list);
}

View file

@ -220,6 +220,12 @@ mod bridge_impl {
string_list: Pin<&CPPStringList>,
) -> UniquePtr<CxxVector<CPPString>>;
#[namespace = "TagLib"]
#[cxx_name = "ByteVectorList"]
type CPPByteVectorList;
#[namespace = "taglib_shim"]
fn ByteVectorList_to_vector(list: Pin<&CPPByteVectorList>) -> UniquePtr<CxxVector<CPPByteVector>>;
#[namespace = "TagLib"]
#[cxx_name = "ByteVector"]
type CPPByteVector;
@ -251,13 +257,78 @@ mod bridge_impl {
#[namespace = "taglib_shim"]
#[cxx_name = "ItemMapEntry"]
type CPPItemMapEntry;
fn key(self: Pin<&CPPItemMapEntry>) -> &CPPString;
fn value(self: Pin<&CPPItemMapEntry>) -> &CPPMP4Item;
fn key<'slf, 'file_ref>(self: Pin<&'slf CPPItemMapEntry>) -> &'file_ref CPPString;
fn value<'slf, 'file_ref>(self: Pin<&'slf CPPItemMapEntry>) -> &'file_ref CPPMP4Item;
#[namespace = "TagLib::MP4"]
#[cxx_name = "Item"]
type CPPMP4Item;
fn isValid(self: Pin<&CPPMP4Item>) -> bool;
fn toBool(self: Pin<&CPPMP4Item>) -> bool;
fn toInt(self: Pin<&CPPMP4Item>) -> i32;
fn toByte(self: Pin<&CPPMP4Item>) -> u8;
fn toUInt(self: Pin<&CPPMP4Item>) -> u32;
fn Item_type(item: Pin<&CPPMP4Item>) -> u32;
#[namespace = "taglib_shim"]
fn Item_toIntPair(item: Pin<&CPPMP4Item>) -> UniquePtr<CPPIntPair>;
#[namespace = "taglib_shim"]
fn Item_toStringList(item: Pin<&CPPMP4Item>) -> UniquePtr<CPPStringList>;
#[namespace = "taglib_shim"]
fn Item_toByteVectorList(item: Pin<&CPPMP4Item>) -> UniquePtr<CPPByteVectorList>;
#[namespace = "taglib_shim"]
fn Item_toCoverArtList(item: Pin<&CPPMP4Item>) -> UniquePtr<CPPCoverArtList>;
#[namespace = "taglib_shim"]
fn Item_toLongLong(item: Pin<&CPPMP4Item>) -> i64;
#[namespace = "taglib_shim"]
#[cxx_name = "IntPair"]
type CPPIntPair;
fn first(self: Pin<&CPPIntPair>) -> i32;
fn second(self: Pin<&CPPIntPair>) -> i32;
#[namespace = "taglib_shim"]
#[cxx_name = "CoverArtList"]
type CPPCoverArtList;
fn to_vector(self: Pin<&CPPCoverArtList>) -> UniquePtr<CxxVector<CPPCoverArt>>;
#[namespace = "taglib_shim"]
#[cxx_name = "CoverArt"]
type CPPCoverArt;
fn format(self: Pin<&CPPCoverArt>) -> u32;
fn data(self: Pin<&CPPCoverArt>) -> UniquePtr<CPPByteVector>;
}
}
#[repr(u8)]
pub enum MP4ItemType {
Void,
Bool,
Int,
IntPair,
Byte,
UInt,
LongLong,
StringList,
ByteVectorList,
CoverArtList,
}
impl MP4ItemType {
pub fn from_u32(value: u32) -> Option<Self> {
match value {
0 => Some(Self::Void),
1 => Some(Self::Bool),
2 => Some(Self::Int),
3 => Some(Self::IntPair),
4 => Some(Self::Byte),
5 => Some(Self::UInt),
6 => Some(Self::LongLong),
7 => Some(Self::StringList),
8 => Some(Self::ByteVectorList),
9 => Some(Self::CoverArtList),
_ => None,
}
}
}

View file

@ -54,7 +54,6 @@ impl<'io_stream> DynIOStream<'io_stream> {
pub fn write(&mut self, data: &[u8]) {
self.0.write_all(data).unwrap();
}
pub fn seek(&mut self, offset: i64, whence: i32) {
let pos = match whence {
0 => SeekFrom::Start(offset as u64),

View file

@ -1,5 +1,5 @@
pub use super::bridge::CPPMP4Tag;
use super::bridge::{CPPItemMap, ItemMap_to_entries, CPPItemMapEntry};
use super::bridge::{CPPItemMap, ItemMap_to_entries, CPPItemMapEntry, CPPMP4Item, MP4ItemType, CPPIntPair};
use super::tk;
use super::this::{OwnedThis, RefThis, RefThisMut, ThisMut, This};
use std::collections::HashMap;
@ -30,10 +30,10 @@ impl<'file_ref> ItemMap<'file_ref> {
Self { this }
}
pub fn to_hashmap(&self) -> HashMap<String, ()> {
pub fn to_hashmap(&self) -> HashMap<String, MP4Item<'file_ref>> {
let cxx_vec = ItemMap_to_entries(self.this.pin());
cxx_vec
.iter()
let vec: Vec<(String, MP4Item<'file_ref>)> =
cxx_vec.iter()
.map(|property| {
// SAFETY:
// - This pin is only used in this unsafe scope.
@ -45,9 +45,158 @@ impl<'file_ref> ItemMap<'file_ref> {
let key_ref = property_pin.key();
let key_this = unsafe { RefThis::new(key_ref) };
let key = tk::String::new(key_this).to_string();
// For now, we're just returning () as a placeholder for MP4::Item
(key, ())
let value_ref = property_pin.value();
let value_this = unsafe { RefThis::new(value_ref) };
let value = MP4Item::new(value_this);
(key, value)
})
.collect();
HashMap::from_iter(vec)
}
}
pub struct MP4Item<'file_ref> {
this: RefThis<'file_ref, CPPMP4Item>,
}
impl<'file_ref> MP4Item<'file_ref> {
pub fn new(this: RefThis<'file_ref, CPPMP4Item>) -> Self {
Self { this }
}
pub fn data(&self) -> Option<MP4Data<'file_ref>> {
if !self.this.pin().isValid() {
return None;
}
let item_type = MP4ItemType::from_u32(super::bridge::Item_type(self.this.pin()));
item_type.and_then(|item_type| match item_type {
MP4ItemType::Void => Some(MP4Data::Void),
MP4ItemType::Bool => Some(MP4Data::Bool(self.this.pin().toBool())),
MP4ItemType::Int => Some(MP4Data::Int(self.this.pin().toInt())),
MP4ItemType::IntPair => {
let pair = super::bridge::Item_toIntPair(self.this.pin());
let pair_this = unsafe { OwnedThis::new(pair) };
pair_this.map(|this| MP4Data::IntPair(IntPair::new(this)))
},
MP4ItemType::Byte => Some(MP4Data::Byte(self.this.pin().toByte())),
MP4ItemType::UInt => Some(MP4Data::UInt(self.this.pin().toUInt())),
MP4ItemType::LongLong => Some(MP4Data::LongLong(super::bridge::Item_toLongLong(self.this.pin()))),
MP4ItemType::StringList => {
let string_list = super::bridge::Item_toStringList(self.this.pin());
let string_list_this = unsafe { OwnedThis::new(string_list) };
string_list_this.map(|this| MP4Data::StringList(tk::StringList::new(this)))
},
MP4ItemType::ByteVectorList => {
let byte_vector_list = super::bridge::Item_toByteVectorList(self.this.pin());
let byte_vector_list_this = unsafe { OwnedThis::new(byte_vector_list) };
byte_vector_list_this.map(|this| MP4Data::ByteVectorList(tk::ByteVectorList::new(this)))
},
MP4ItemType::CoverArtList => {
let cover_art_list = super::bridge::Item_toCoverArtList(self.this.pin());
let cover_art_list_this = unsafe { OwnedThis::new(cover_art_list) };
cover_art_list_this.map(|this| MP4Data::CoverArtList(CoverArtList::new(this)))
}
})
}
}
pub struct CoverArtList<'file_ref> {
this: OwnedThis<'file_ref, super::bridge::CPPCoverArtList>,
}
impl<'file_ref> CoverArtList<'file_ref> {
pub fn new(this: OwnedThis<'file_ref, super::bridge::CPPCoverArtList>) -> Self {
Self { this }
}
pub fn to_vec(&self) -> Vec<CoverArt> {
let cover_arts = self.this.pin().to_vector();
cover_arts
.iter()
.map(|ca| {
let ca_pin = unsafe { Pin::new_unchecked(ca) };
let format = CoverArtFormat::from_u32(ca_pin.format());
let data = ca_pin.data();
let data_this = unsafe { RefThis::new(&*data) };
let data = tk::ByteVector::new(data_this).to_vec();
CoverArt { format, data }
})
.collect()
}
}
}
pub struct IntPair<'file_ref> {
this: OwnedThis<'file_ref, CPPIntPair>,
}
impl<'file_ref> IntPair<'file_ref> {
pub fn new(this: OwnedThis<'file_ref, CPPIntPair>) -> Self {
Self { this }
}
pub fn to_tuple(&self) -> Option<(i32, i32)> {
let this = self.this.pin();
let first = this.first();
let second = this.second();
Some((first, second))
}
}
pub enum MP4Data<'file_ref> {
Void,
Bool(bool),
Int(i32),
Byte(u8),
UInt(u32),
LongLong(i64),
IntPair(IntPair<'file_ref>),
StringList(tk::OwnedStringList<'file_ref>),
ByteVectorList(tk::OwnedByteVectorList<'file_ref>),
CoverArtList(CoverArtList<'file_ref>),
}
#[derive(Debug, Clone, PartialEq)]
pub struct CoverArt {
format: CoverArtFormat,
data: Vec<u8>,
}
impl CoverArt {
pub fn new(format: CoverArtFormat, data: Vec<u8>) -> Self {
Self { format, data }
}
pub fn format(&self) -> CoverArtFormat {
self.format
}
pub fn data(&self) -> &[u8] {
&self.data
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum CoverArtFormat {
Unknown,
BMP,
JPEG,
GIF,
PNG,
}
impl CoverArtFormat {
fn from_u32(value: u32) -> Self {
match value {
0 => CoverArtFormat::Unknown,
1 => CoverArtFormat::BMP,
2 => CoverArtFormat::JPEG,
3 => CoverArtFormat::GIF,
4 => CoverArtFormat::PNG,
_ => CoverArtFormat::Unknown,
}
}
}

View file

@ -1,4 +1,4 @@
use super::bridge::{self, CPPByteVector, CPPString, CPPStringList};
use super::bridge::{self, CPPByteVector, CPPByteVectorList, CPPString, CPPStringList};
use super::this::{RefThis, RefThisMut, This, OwnedThis};
use cxx::{memory::UniquePtrTarget, UniquePtr};
use std::marker::PhantomData;
@ -94,3 +94,27 @@ impl<'file_ref, T: This<'file_ref, CPPByteVector>> ByteVector<'file_ref, T> {
pub type OwnedByteVector<'file_ref> = ByteVector<'file_ref, OwnedThis<'file_ref, CPPByteVector>>;
pub type RefByteVector<'file_ref> = ByteVector<'file_ref, RefThis<'file_ref, CPPByteVector>>;
pub type RefByteVectorMut<'file_ref> = ByteVector<'file_ref, RefThisMut<'file_ref, CPPByteVector>>;
pub struct ByteVectorList<'file_ref, T: This<'file_ref, CPPByteVectorList>> {
_data: PhantomData<&'file_ref CPPByteVectorList>,
this: T,
}
impl<'file_ref, T: This<'file_ref, CPPByteVectorList>> ByteVectorList<'file_ref, T> {
pub(super) fn new(this: T) -> Self {
Self { _data: PhantomData, this }
}
pub fn to_vec(&self) -> Vec<Vec<u8>> {
let cxx_values = bridge::ByteVectorList_to_vector(self.this.pin());
cxx_values
.iter()
.map(|value| ByteVector::new(unsafe { RefThis::new(value) }).to_vec())
.collect()
}
}
pub type OwnedByteVectorList<'file_ref> = ByteVectorList<'file_ref, OwnedThis<'file_ref, CPPByteVectorList>>;
pub type RefByteVectorList<'file_ref> = ByteVectorList<'file_ref, RefThis<'file_ref, CPPByteVectorList>>;
pub type RefByteVectorListMut<'file_ref> = ByteVectorList<'file_ref, RefThisMut<'file_ref, CPPByteVectorList>>;