musikr: add basic mp4 tag

This commit is contained in:
Alexander Capehart 2025-02-17 09:25:17 -07:00
parent 6ef79a9aa5
commit 65d8959bcf
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
6 changed files with 126 additions and 13 deletions

View file

@ -123,6 +123,7 @@ fn main() {
.file("shim/xiph_shim.cpp") .file("shim/xiph_shim.cpp")
.file("shim/id3v1_shim.cpp") .file("shim/id3v1_shim.cpp")
.file("shim/id3v2_shim.cpp") .file("shim/id3v2_shim.cpp")
.file("shim/mp4_shim.cpp")
.include(format!("taglib/pkg/{}/include", target)) .include(format!("taglib/pkg/{}/include", target))
.include(".") // Add the current directory to include path .include(".") // Add the current directory to include path
.flag_if_supported("-std=c++14"); .flag_if_supported("-std=c++14");

View file

@ -0,0 +1,27 @@
#include "mp4_shim.hpp"
#include <taglib/tstring.h>
#include <memory>
#include <vector>
namespace taglib_shim {
ItemMapEntry::ItemMapEntry(TagLib::String key, TagLib::MP4::Item value)
: key_(std::move(key)), value_(std::move(value)) {}
const TagLib::String& ItemMapEntry::key() const {
return key_;
}
const TagLib::MP4::Item& ItemMapEntry::value() const {
return value_;
}
std::unique_ptr<std::vector<ItemMapEntry>> ItemMap_to_entries(const TagLib::MP4::ItemMap& map) {
auto entries = std::make_unique<std::vector<ItemMapEntry>>();
for (auto it = map.begin(); it != map.end(); ++it) {
entries->emplace_back(it->first, it->second);
}
return entries;
}
}

View file

@ -0,0 +1,22 @@
#pragma once
#include <memory>
#include <taglib/mp4tag.h>
#include <taglib/mp4item.h>
#include <taglib/tstring.h>
#include "rust/cxx.h"
namespace taglib_shim {
class ItemMapEntry {
public:
ItemMapEntry(TagLib::String key, TagLib::MP4::Item value);
const TagLib::String& key() const;
const TagLib::MP4::Item& value() const;
private:
TagLib::String key_;
TagLib::MP4::Item value_;
};
std::unique_ptr<std::vector<ItemMapEntry>> ItemMap_to_entries(const TagLib::MP4::ItemMap& map);
}

View file

@ -34,6 +34,7 @@ mod bridge_impl {
include!("shim/xiph_shim.hpp"); include!("shim/xiph_shim.hpp");
include!("shim/id3v2_shim.hpp"); include!("shim/id3v2_shim.hpp");
include!("shim/id3v1_shim.hpp"); include!("shim/id3v1_shim.hpp");
include!("shim/mp4_shim.hpp");
include!("taglib/mpegfile.h"); include!("taglib/mpegfile.h");
#[namespace = "TagLib"] #[namespace = "TagLib"]
@ -45,15 +46,12 @@ mod bridge_impl {
#[cxx_name = "FileRef"] #[cxx_name = "FileRef"]
type CPPFileRef; type CPPFileRef;
unsafe fn new_FileRef(stream: *mut CPPIOStream) -> UniquePtr<CPPFileRef>; unsafe fn new_FileRef(stream: *mut CPPIOStream) -> UniquePtr<CPPFileRef>;
#[cxx_name = "isNull"]
fn isNull(self: Pin<&CPPFileRef>) -> bool; fn isNull(self: Pin<&CPPFileRef>) -> bool;
#[cxx_name = "file"]
fn file(self: Pin<&CPPFileRef>) -> *mut CPPFile; fn file(self: Pin<&CPPFileRef>) -> *mut CPPFile;
#[namespace = "TagLib"] #[namespace = "TagLib"]
#[cxx_name = "File"] #[cxx_name = "File"]
type CPPFile; type CPPFile;
#[cxx_name = "audioProperties"]
fn audioProperties(self: Pin<&CPPFile>) -> *mut CppAudioProperties; fn audioProperties(self: Pin<&CPPFile>) -> *mut CppAudioProperties;
#[namespace = "taglib_shim"] #[namespace = "taglib_shim"]
unsafe fn File_asVorbis(file: *mut CPPFile) -> *mut CPPVorbisFile; unsafe fn File_asVorbis(file: *mut CPPFile) -> *mut CPPVorbisFile;
@ -71,13 +69,9 @@ mod bridge_impl {
#[namespace = "TagLib"] #[namespace = "TagLib"]
#[cxx_name = "AudioProperties"] #[cxx_name = "AudioProperties"]
type CppAudioProperties; type CppAudioProperties;
#[cxx_name = "lengthInMilliseconds"]
fn lengthInMilliseconds(self: Pin<&CppAudioProperties>) -> i32; fn lengthInMilliseconds(self: Pin<&CppAudioProperties>) -> i32;
#[cxx_name = "bitrate"]
fn bitrate(self: Pin<&CppAudioProperties>) -> i32; fn bitrate(self: Pin<&CppAudioProperties>) -> i32;
#[cxx_name = "sampleRate"]
fn sampleRate(self: Pin<&CppAudioProperties>) -> i32; fn sampleRate(self: Pin<&CppAudioProperties>) -> i32;
#[cxx_name = "channels"]
fn channels(self: Pin<&CppAudioProperties>) -> i32; fn channels(self: Pin<&CppAudioProperties>) -> i32;
#[namespace = "TagLib::Ogg::Vorbis"] #[namespace = "TagLib::Ogg::Vorbis"]
@ -97,7 +91,6 @@ mod bridge_impl {
#[namespace = "TagLib::FLAC"] #[namespace = "TagLib::FLAC"]
#[cxx_name = "File"] #[cxx_name = "File"]
type CPPFLACFile; type CPPFLACFile;
#[cxx_name = "xiphComment"]
fn xiphComment(self: Pin<&mut CPPFLACFile>, create: bool) -> *mut CPPXiphComment; fn xiphComment(self: Pin<&mut CPPFLACFile>, create: bool) -> *mut CPPXiphComment;
#[namespace = "taglib_shim"] #[namespace = "taglib_shim"]
fn FLACFile_pictureList(file: Pin<&mut CPPFLACFile>) -> UniquePtr<CPPPictureList>; fn FLACFile_pictureList(file: Pin<&mut CPPFLACFile>) -> UniquePtr<CPPPictureList>;
@ -118,7 +111,6 @@ mod bridge_impl {
#[namespace = "TagLib::MPEG"] #[namespace = "TagLib::MPEG"]
#[cxx_name = "File"] #[cxx_name = "File"]
type CPPMPEGFile; type CPPMPEGFile;
#[cxx_name = "ID3v2Tag"]
fn ID3v2Tag(self: Pin<&mut CPPMPEGFile>, create: bool) -> *mut CPPID3v2Tag; fn ID3v2Tag(self: Pin<&mut CPPMPEGFile>, create: bool) -> *mut CPPID3v2Tag;
#[namespace = "TagLib::MP4"] #[namespace = "TagLib::MP4"]
@ -140,7 +132,6 @@ mod bridge_impl {
type CPPXiphComment; type CPPXiphComment;
// Explicit lifecycle definition to state while the Pin is temporary, the CPPFieldListMap // Explicit lifecycle definition to state while the Pin is temporary, the CPPFieldListMap
// ref returned actually has the same lifetime as the CPPXiphComment. // ref returned actually has the same lifetime as the CPPXiphComment.
#[cxx_name = "fieldListMap"]
fn fieldListMap<'slf, 'file_ref>(self: Pin<&'slf CPPXiphComment>) -> &'file_ref CPPFieldListMap; fn fieldListMap<'slf, 'file_ref>(self: Pin<&'slf CPPXiphComment>) -> &'file_ref CPPFieldListMap;
#[namespace = "TagLib"] #[namespace = "TagLib"]
@ -154,9 +145,7 @@ mod bridge_impl {
#[namespace = "taglib_shim"] #[namespace = "taglib_shim"]
#[cxx_name = "FieldListEntry"] #[cxx_name = "FieldListEntry"]
type CPPFieldListEntry; type CPPFieldListEntry;
#[cxx_name = "key"]
fn key(self: Pin<&CPPFieldListEntry>) -> &CPPString; fn key(self: Pin<&CPPFieldListEntry>) -> &CPPString;
#[cxx_name = "value"]
fn value(self: Pin<&CPPFieldListEntry>) -> &CPPStringList; fn value(self: Pin<&CPPFieldListEntry>) -> &CPPStringList;
#[namespace = "TagLib::ID3v2"] #[namespace = "TagLib::ID3v2"]
@ -221,7 +210,6 @@ mod bridge_impl {
#[namespace = "TagLib"] #[namespace = "TagLib"]
#[cxx_name = "String"] #[cxx_name = "String"]
type CPPString; type CPPString;
#[cxx_name = "toCString"]
fn toCString(self: Pin<&CPPString>, unicode: bool) -> *const c_char; fn toCString(self: Pin<&CPPString>, unicode: bool) -> *const c_char;
#[namespace = "TagLib"] #[namespace = "TagLib"]
@ -249,6 +237,27 @@ mod bridge_impl {
fn ID3v1Tag_genreIndex(tag: Pin<&CPPID3v1Tag>) -> u32; fn ID3v1Tag_genreIndex(tag: Pin<&CPPID3v1Tag>) -> u32;
fn ID3v1Tag_year(tag: Pin<&CPPID3v1Tag>) -> u32; fn ID3v1Tag_year(tag: Pin<&CPPID3v1Tag>) -> u32;
fn ID3v1Tag_track(tag: Pin<&CPPID3v1Tag>) -> u32; fn ID3v1Tag_track(tag: Pin<&CPPID3v1Tag>) -> u32;
#[namespace = "TagLib::MP4"]
#[cxx_name = "Tag"]
type CPPMP4Tag;
#[namespace = "TagLib::MP4"]
#[cxx_name = "ItemMap"]
type CPPItemMap;
fn itemMap<'slf, 'file_ref>(self: Pin<&'slf CPPMP4Tag>) -> &'file_ref CPPItemMap;
fn ItemMap_to_entries(map: Pin<&CPPItemMap>) -> UniquePtr<CxxVector<CPPItemMapEntry>>;
#[namespace = "taglib_shim"]
#[cxx_name = "ItemMapEntry"]
type CPPItemMapEntry;
fn key(self: Pin<&CPPItemMapEntry>) -> &CPPString;
fn value(self: Pin<&CPPItemMapEntry>) -> &CPPMP4Item;
#[namespace = "TagLib::MP4"]
#[cxx_name = "Item"]
type CPPMP4Item;
} }
} }

View file

@ -11,3 +11,4 @@ pub mod xiph;
pub mod tk; pub mod tk;
pub mod id3v2; pub mod id3v2;
pub mod id3v1; pub mod id3v1;
pub mod mp4;

View file

@ -0,0 +1,53 @@
pub use super::bridge::CPPMP4Tag;
use super::bridge::{CPPItemMap, ItemMap_to_entries, CPPItemMapEntry};
use super::tk;
use super::this::{OwnedThis, RefThis, RefThisMut, ThisMut, This};
use std::collections::HashMap;
use std::pin::Pin;
pub struct MP4Tag<'file_ref> {
this: RefThisMut<'file_ref, CPPMP4Tag>,
}
impl<'file_ref> MP4Tag<'file_ref> {
pub fn new(this: RefThisMut<'file_ref, CPPMP4Tag>) -> Self {
Self { this }
}
pub fn item_map<'slf>(&'slf self) -> ItemMap<'file_ref> {
let map: &'file_ref CPPItemMap = self.this.pin().itemMap();
let map_this = unsafe { RefThis::new(map) };
ItemMap::new(map_this)
}
}
pub struct ItemMap<'file_ref> {
this: RefThis<'file_ref, CPPItemMap>,
}
impl<'file_ref> ItemMap<'file_ref> {
pub fn new(this: RefThis<'file_ref, CPPItemMap>) -> Self {
Self { this }
}
pub fn to_hashmap(&self) -> HashMap<String, ()> {
let cxx_vec = ItemMap_to_entries(self.this.pin());
cxx_vec
.iter()
.map(|property| {
// SAFETY:
// - This pin is only used in this unsafe scope.
// - The pin is used as a C++ this pointer in the ffi call, which does
// not change address by C++ semantics.
// - The values returned are copied and thus not dependent on the address
// of self.
let property_pin = unsafe { Pin::new_unchecked(property) };
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, ())
})
.collect()
}
}