musikr: cleanup and cover stuff

This commit is contained in:
Alexander Capehart 2025-02-17 21:33:10 -07:00
parent 8415db30ff
commit 57eb30701d
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
16 changed files with 138 additions and 121 deletions

View file

@ -33,6 +33,10 @@ namespace taglib_shim {
return std::make_unique<TagLib::ByteVector>(frame.picture());
}
uint32_t AttachedPictureFrame_type(const TagLib::ID3v2::AttachedPictureFrame& frame) {
return static_cast<uint32_t>(frame.type());
}
std::unique_ptr<TagLib::StringList> TextIdentificationFrame_fieldList(const TagLib::ID3v2::TextIdentificationFrame& frame) {
return std::make_unique<TagLib::StringList>(frame.fieldList());
}

View file

@ -27,6 +27,7 @@ namespace taglib_shim {
// Frame data access
std::unique_ptr<TagLib::ByteVector> AttachedPictureFrame_picture(const TagLib::ID3v2::AttachedPictureFrame& frame);
uint32_t AttachedPictureFrame_type(const TagLib::ID3v2::AttachedPictureFrame& frame);
std::unique_ptr<TagLib::StringList> TextIdentificationFrame_fieldList(const TagLib::ID3v2::TextIdentificationFrame& frame);
std::unique_ptr<TagLib::StringList> UserTextIdentificationFrame_fieldList(const TagLib::ID3v2::UserTextIdentificationFrame& frame);

View file

@ -29,4 +29,8 @@ namespace taglib_shim {
std::unique_ptr<TagLib::ByteVector> Picture_data(const TagLib::FLAC::Picture& picture) {
return std::make_unique<TagLib::ByteVector>(picture.data());
}
uint32_t Picture_type(const TagLib::FLAC::Picture& picture) {
return static_cast<uint32_t>(picture.type());
}
}

View file

@ -3,6 +3,7 @@
#include "taglib/flacpicture.h"
#include "taglib/tstring.h"
#include "taglib/tbytevector.h"
#include "taglib/tpicturetype.h"
#include "tk_shim.hpp"
#include <memory>
#include <vector>
@ -22,5 +23,6 @@ namespace taglib_shim {
std::unique_ptr<std::vector<PicturePointer>> PictureList_to_vector(const PictureList& list);
uint32_t Picture_type(const TagLib::FLAC::Picture& picture);
std::unique_ptr<TagLib::ByteVector> Picture_data(const TagLib::FLAC::Picture& picture);
}

View file

@ -6,7 +6,12 @@ use jni::{
use std::cell::RefCell;
use std::rc::Rc;
use crate::taglib::{audioproperties, id3v1, id3v2, mp4, xiph};
use crate::taglib::{
audioproperties,
flac::PictureType,
id3v1, id3v2, mp4,
xiph::{self, FLACPictureList},
};
use crate::tagmap::JTagMap;
@ -84,7 +89,9 @@ impl<'local, 'file_ref> JMetadataBuilder<'local, 'file_ref> {
first_pic = picture_frame.picture().map(|p| p.to_vec());
}
// TODO: Check for front cover type when bindings are available
if front_cover_pic.is_none() {
if let (Some(PictureType::FrontCover), None) =
(picture_frame.picture_type(), &front_cover_pic)
{
front_cover_pic = picture_frame.picture().map(|p| p.to_vec());
}
}
@ -95,13 +102,12 @@ impl<'local, 'file_ref> JMetadataBuilder<'local, 'file_ref> {
self.cover = front_cover_pic.or(first_pic);
}
pub fn set_xiph(&mut self, tag: &xiph::XiphComment) {
pub fn set_xiph(&mut self, tag: &mut xiph::XiphComment<'file_ref>) {
for (key, values) in tag.field_list_map().to_hashmap() {
let values: Vec<String> = values.to_vec().into_iter().map(|s| s.to_string()).collect();
self.xiph.add_id_list(key.to_uppercase(), values);
}
// TODO: Handle FLAC pictures when bindings are available
self.set_flac_pictures(&tag.picture_list());
}
pub fn set_mp4(&mut self, tag: &mp4::MP4Tag) {
@ -159,6 +165,14 @@ impl<'local, 'file_ref> JMetadataBuilder<'local, 'file_ref> {
}
}
pub fn set_flac_pictures(&mut self, pictures: &FLACPictureList<'file_ref>) {
for picture in pictures.to_vec().into_iter() {
if let Some(PictureType::FrontCover) = picture.picture_type() {
self.cover = Some(picture.data().to_vec());
}
}
}
pub fn set_properties(&mut self, properties: audioproperties::AudioProperties<'file_ref>) {
self.properties = Some(properties);
}

View file

@ -34,21 +34,28 @@ pub extern "C" fn Java_org_oxycblt_musikr_metadata_MetadataJNI_openFile<'local>(
}
if let Some(vorbis) = file.as_vorbis() {
jbuilder.set_mime_type("audio/ogg");
if let Some(tag) = vorbis.xiph_comments() {
jbuilder.set_xiph(&tag);
if let Some(mut tag) = vorbis.xiph_comments() {
jbuilder.set_xiph(&mut tag);
}
}
if let Some(opus) = file.as_opus() {
jbuilder.set_mime_type("audio/opus");
if let Some(tag) = opus.xiph_comments() {
jbuilder.set_xiph(&tag);
if let Some(mut tag) = opus.xiph_comments() {
jbuilder.set_xiph(&mut tag);
}
}
if let Some(mut flac) = file.as_flac() {
jbuilder.set_mime_type("audio/flac");
if let Some(tag) = flac.xiph_comments() {
jbuilder.set_xiph(&tag);
if let Some(tag) = flac.id3v1_tag() {
jbuilder.set_id3v1(&tag);
}
if let Some(tag) = flac.id3v2_tag() {
jbuilder.set_id3v2(&tag);
}
if let Some(mut tag) = flac.xiph_comments() {
jbuilder.set_xiph(&mut tag);
}
jbuilder.set_flac_pictures(&flac.picture_list());
}
if let Some(mut mpeg) = file.as_mpeg() {
jbuilder.set_mime_type("audio/mpeg");

View file

@ -129,8 +129,9 @@ mod bridge_impl {
#[cxx_name = "Picture"]
type CPPFLACPicture;
#[namespace = "taglib_shim"]
fn Picture_type(picture: &CPPFLACPicture) -> u32;
#[namespace = "taglib_shim"]
fn Picture_data(picture: &CPPFLACPicture) -> UniquePtr<CPPByteVector>;
// XIPHComment
#[namespace = "TagLib::Ogg"]
@ -305,6 +306,8 @@ mod bridge_impl {
#[cxx_name = "AttachedPictureFrame"]
type CPPID3v2AttachedPictureFrame;
#[namespace = "taglib_shim"]
fn AttachedPictureFrame_type(frame: &CPPID3v2AttachedPictureFrame) -> u32;
#[namespace = "taglib_shim"]
fn AttachedPictureFrame_picture(
frame: &CPPID3v2AttachedPictureFrame,
) -> UniquePtr<CPPByteVector>;
@ -336,6 +339,60 @@ mod bridge_impl {
}
}
#[repr(u8)]
pub enum PictureType {
Other,
FileIcon,
OtherFileIcon,
FrontCover,
BackCover,
LeafletPage,
Media,
LeadArtist,
Artist,
Conductor,
Band,
Composer,
Lyricist,
RecordingLocation,
DuringRecording,
DuringPerformance,
MovieScreenCapture,
ColoredFish,
Illustration,
BandLogo,
PublisherLogo,
}
impl PictureType {
pub fn from_u32(value: u32) -> Option<Self> {
match value {
0 => Some(Self::Other),
1 => Some(Self::FileIcon),
2 => Some(Self::OtherFileIcon),
3 => Some(Self::FrontCover),
4 => Some(Self::BackCover),
5 => Some(Self::LeafletPage),
6 => Some(Self::Media),
7 => Some(Self::LeadArtist),
8 => Some(Self::Artist),
9 => Some(Self::Conductor),
10 => Some(Self::Band),
11 => Some(Self::Composer),
12 => Some(Self::Lyricist),
13 => Some(Self::RecordingLocation),
14 => Some(Self::DuringRecording),
15 => Some(Self::DuringPerformance),
16 => Some(Self::MovieScreenCapture),
17 => Some(Self::ColoredFish),
18 => Some(Self::Illustration),
19 => Some(Self::BandLogo),
20 => Some(Self::PublisherLogo),
_ => None,
}
}
}
#[repr(u8)]
pub enum MP4ItemType {
Void,

View file

@ -1,13 +1,14 @@
pub use super::bridge::CPPFLACFile;
pub use super::bridge::CPPFLACPicture;
pub use super::bridge::PictureType;
use super::bridge::{
CPPPictureList, FLACFile_pictureList, PictureList_to_vector, Picture_data,
CPPPictureList, FLACFile_pictureList, PictureList_to_vector, Picture_data, Picture_type,
};
use super::id3v1::ID3v1Tag;
use super::id3v2::ID3v2Tag;
use super::this::{OwnedThis, RefThis, RefThisMut, ThisMut};
use super::tk::{ByteVector, OwnedByteVector};
pub use super::xiph::XiphComment;
use super::xiph::XiphComment;
pub struct FLACFile<'file_ref> {
this: RefThisMut<'file_ref, CPPFLACFile>,
@ -29,10 +30,10 @@ impl<'file_ref> FLACFile<'file_ref> {
tag_this.map(|this| XiphComment::new(this))
}
pub fn picture_list(&mut self) -> PictureList<'file_ref> {
pub fn picture_list(&mut self) -> FLACPictureList<'file_ref> {
let pictures = FLACFile_pictureList(self.this.pin_mut());
let this = OwnedThis::new(pictures).unwrap();
PictureList::new(this)
FLACPictureList::new(this)
}
pub fn id3v1_tag(&mut self) -> Option<ID3v1Tag<'file_ref>> {
@ -50,16 +51,16 @@ impl<'file_ref> FLACFile<'file_ref> {
}
}
pub struct PictureList<'file_ref> {
pub struct FLACPictureList<'file_ref> {
this: OwnedThis<'file_ref, CPPPictureList>,
}
impl<'file_ref> PictureList<'file_ref> {
impl<'file_ref> FLACPictureList<'file_ref> {
pub(super) fn new(this: OwnedThis<'file_ref, CPPPictureList>) -> Self {
Self { this }
}
pub fn to_vec(&self) -> Vec<Picture<'file_ref>> {
pub fn to_vec(&self) -> Vec<FLACPicture<'file_ref>> {
let pictures = PictureList_to_vector(self.this.as_ref());
let mut result = Vec::new();
for picture_ptr in pictures.iter() {
@ -70,19 +71,20 @@ impl<'file_ref> PictureList<'file_ref> {
picture_ptr.as_ref().unwrap()
};
let picture_this = RefThis::new(picture_ref);
result.push(Picture::new(picture_this));
result.push(FLACPicture { this: picture_this });
}
result
}
}
pub struct Picture<'file_ref> {
pub struct FLACPicture<'file_ref> {
this: RefThis<'file_ref, CPPFLACPicture>,
}
impl<'file_ref> Picture<'file_ref> {
pub(super) fn new(this: RefThis<'file_ref, CPPFLACPicture>) -> Self {
Self { this }
impl<'file_ref> FLACPicture<'file_ref> {
pub fn picture_type(&self) -> Option<PictureType> {
let picture_type = Picture_type(self.this.as_ref());
PictureType::from_u32(picture_type)
}
pub fn data(&self) -> OwnedByteVector<'file_ref> {

View file

@ -1,10 +1,12 @@
use super::bridge::{
self, CPPID3v2AttachedPictureFrame, CPPID3v2Frame, CPPID3v2FrameList,
CPPID3v2Tag, CPPID3v2TextIdentificationFrame, CPPID3v2UserTextIdentificationFrame,
self, CPPID3v2AttachedPictureFrame, CPPID3v2Frame, CPPID3v2FrameList, CPPID3v2Tag,
CPPID3v2TextIdentificationFrame, CPPID3v2UserTextIdentificationFrame,
};
use super::this::{OwnedThis, RefThis, RefThisMut};
use super::tk::{self, ByteVector, OwnedByteVector, OwnedStringList, StringList};
pub use super::bridge::PictureType;
pub struct ID3v2Tag<'file_ref> {
this: RefThisMut<'file_ref, CPPID3v2Tag>,
}
@ -16,7 +18,7 @@ impl<'file_ref> ID3v2Tag<'file_ref> {
pub fn frames(&self) -> Option<FrameList<'file_ref>> {
let frames = bridge::Tag_frameList(self.this.as_ref());
let this = unsafe { OwnedThis::new(frames) };
let this = OwnedThis::new(frames);
this.map(|this| FrameList::new(this))
}
}
@ -37,7 +39,7 @@ impl<'file_ref> FrameList<'file_ref> {
.map(|frame| {
let frame_ptr = frame.get();
let frame_ref = unsafe { frame_ptr.as_ref().unwrap() };
let frame_this = unsafe { RefThis::new(frame_ref) };
let frame_this = RefThis::new(frame_ref);
Frame::new(frame_this)
})
.collect()
@ -94,7 +96,7 @@ impl<'file_ref> TextIdentificationFrame<'file_ref> {
pub fn field_list(&self) -> Option<OwnedStringList<'file_ref>> {
let field_list = bridge::TextIdentificationFrame_fieldList(self.this.as_ref());
let this = unsafe { OwnedThis::new(field_list) };
let this = OwnedThis::new(field_list);
this.map(|this| StringList::new(this))
}
}
@ -110,7 +112,7 @@ impl<'file_ref> UserTextIdentificationFrame<'file_ref> {
pub fn values(&self) -> Option<OwnedStringList<'file_ref>> {
let values = bridge::UserTextIdentificationFrame_fieldList(self.this.as_ref());
let this = unsafe { OwnedThis::new(values) };
let this = OwnedThis::new(values);
this.map(|this| StringList::new(this))
}
}
@ -124,9 +126,14 @@ impl<'file_ref> AttachedPictureFrame<'file_ref> {
Self { this }
}
pub fn picture_type(&self) -> Option<PictureType> {
let picture_type = bridge::AttachedPictureFrame_type(self.this.as_ref());
PictureType::from_u32(picture_type)
}
pub fn picture(&self) -> Option<OwnedByteVector<'file_ref>> {
let picture = bridge::AttachedPictureFrame_picture(self.this.as_ref());
let this = unsafe { OwnedThis::new(picture) };
let this = OwnedThis::new(picture);
this.map(|this| ByteVector::new(this))
}
}

View file

@ -1,6 +1,6 @@
use super::bridge::{self, CPPIOStream};
use cxx::UniquePtr;
use std::io::{Seek, SeekFrom};
use std::io::SeekFrom;
pub trait IOStream {
fn read_block(&mut self, buffer: &mut [u8]) -> usize;

View file

@ -1,7 +1,6 @@
pub use super::bridge::CPPMP4Tag;
use super::bridge::{
CPPIntPair, CPPItemMap, CPPMP4File, CPPMP4Item, ItemMap_to_entries,
MP4ItemType,
CPPIntPair, CPPItemMap, CPPMP4File, CPPMP4Item, ItemMap_to_entries, MP4ItemType,
};
use super::this::{OwnedThis, RefThis, RefThisMut};
use super::tk;
@ -148,7 +147,7 @@ impl<'file_ref> CoverArtList<'file_ref> {
.map(|ca| {
let format = CoverArtFormat::from_u32(ca.format());
let data = ca.data();
let data_this = unsafe { RefThis::new(&*data) };
let data_this = RefThis::new(&*data);
let data = tk::ByteVector::new(data_this).to_vec();
CoverArt { format, data }
})
@ -193,10 +192,6 @@ pub struct CoverArt {
}
impl CoverArt {
pub fn new(format: CoverArtFormat, data: Vec<u8>) -> Self {
Self { format, data }
}
pub fn format(&self) -> CoverArtFormat {
self.format
}

View file

@ -1,4 +1,4 @@
use super::bridge::{CPPMPEGFile};
use super::bridge::CPPMPEGFile;
use super::id3v1::ID3v1Tag;
use super::id3v2::ID3v2Tag;
use super::this::{RefThisMut, ThisMut};

View file

@ -1,4 +1,4 @@
use super::bridge::{CPPWAVFile};
use super::bridge::CPPWAVFile;
use super::id3v2::ID3v2Tag;
use super::this::RefThisMut;

View file

@ -3,53 +3,21 @@ use cxx::{memory::UniquePtrTarget, UniquePtr};
use std::marker::PhantomData;
use std::pin::Pin;
/// A taglib-FFI-specific trait representing a C++ object returned by the library.
///
/// `This` instances must hold the following contract:
/// - This object will remain valid as long as TagLib's FileRef object is valid,
/// and will be dropped when the FileRef is dropped.
/// - This object will not move or be mutated over the FileRef's lifetime, this way
/// it can be temporarily pinned for use as a `this` pointer.
pub trait This<'file_ref, T: TagLibAllocated>: AsRef<T> {}
/// A taglib-FFI-specific trait representing a C++ object returned by the library.
///
/// This trait is used to provide a temporary pin of the object for use in C++
/// member function calls.
///
/// `ThisMut` instances must hold the following contract:
/// - This object will remain valid as long as TagLib's FileRef object is valid,
/// and will be dropped when the FileRef is dropped.
/// - This object will not move over the FileRef's lifetime, this way it can be
/// temporarily pinned for use as a `this` pointer.
pub trait ThisMut<'file_ref, T: TagLibAllocated>: This<'file_ref, T> {
fn pin_mut(&mut self) -> Pin<&mut T>;
}
/// A [This] instance that is a reference to a C++ object.
pub struct RefThis<'file_ref, T: TagLibRef> {
this: &'file_ref T,
}
impl<'file_ref, T: TagLibRef> RefThis<'file_ref, T> {
/// Create a new [RefThis] from a reference to a C++ object.
///
/// This is safe to call assuming the contract of [This] is upheld. Since this
/// contract cannot be enforced by the Rust compiler, it is the caller's
/// responsibility to ensure that the reference is valid for the lifetime of
/// the `'file_ref` parameter. More or less, if it comes from the TagLib FFI
/// interface, it is safe to use this.
pub fn new(this: &'file_ref T) -> Self {
// Rough informal contact is that the reference points to a C++ object
// that will live and not move for as long as 'file_ref.
Self { this }
}
/// Get a pointer to the C++ object.
///
/// This can be used to pass the object to FFI functions that take a pointer.
///
/// This is safe to call assuming the contract of [This] is upheld.
pub fn ptr(&self) -> *const T {
self.this as *const T
}
@ -63,39 +31,19 @@ impl<'file_ref, T: TagLibRef> AsRef<T> for RefThis<'file_ref, T> {
impl<'file_ref, T: TagLibRef> This<'file_ref, T> for RefThis<'file_ref, T> {}
/// A [ThisMut] instance that is a reference to a C++ object.
///
/// This is similar to [RefThis], but allows mutating the object.
pub struct RefThisMut<'file_ref, T: TagLibRef> {
this: &'file_ref mut T,
}
impl<'file_ref, T: TagLibRef> RefThisMut<'file_ref, T> {
/// Create a new [RefThisMut] from a reference to a C++ object.
///
/// This is safe to call assuming the contract of [ThisMut] is upheld. Since
/// this contract cannot be enforced by the Rust compiler, it is the caller's
/// responsibility to ensure that the reference is valid for the lifetime of
/// the `'file_ref` parameter. More or less, if it comes from the TagLib FFI
/// interface, it is safe to use this.
pub fn new(this: &'file_ref mut T) -> Self {
Self { this }
}
/// Get a pointer to the C++ object.
///
/// This can be used to pass the object to FFI functions that take a pointer.
///
/// This is safe to call assuming the contract of [ThisMut] is upheld.
pub fn ptr(&self) -> *const T {
self.this as *const T
}
/// Get a pointer to the C++ object.
///
/// This can be used to pass the object to FFI functions that take a pointer.
///
/// This is safe to call assuming the contract of [ThisMut] is upheld.
pub fn ptr_mut(&mut self) -> *mut T {
self.this as *mut T
}
@ -115,26 +63,12 @@ impl<'file_ref, T: TagLibRef> ThisMut<'file_ref, T> for RefThisMut<'file_ref, T>
}
}
/// A [This] instance that is "owned" by the caller.
///
/// "Owned" in this context only really means that the object is not a rust reference.
/// In practice, all "owned" taglib objects are actually shared references, and are
/// thus tied to the lifetime of the `'file_ref` parameter.
pub struct OwnedThis<'file_ref, T: TagLibShared + UniquePtrTarget> {
_data: PhantomData<&'file_ref ()>,
this: UniquePtr<T>,
}
impl<'file_ref, T: TagLibShared + UniquePtrTarget> OwnedThis<'file_ref, T> {
/// Create a new [OwnedThis] from a [UniquePtr].
///
/// This is safe to call assuming the contract of [This] is upheld. Since this
/// contract cannot be enforced by the Rust compiler, it is the caller's
/// responsibility to ensure that the `UniquePtr` is valid for the lifetime of
/// the `'file_ref` parameter. More or less, if it comes from the TagLib FFI
/// interface, it is safe to use this.
///
/// This will return `None` if the `UniquePtr` is `null`.
pub fn new(this: UniquePtr<T>) -> Option<Self> {
if !this.is_null() {
Some(Self {

View file

@ -71,9 +71,6 @@ impl<'file_ref, T: This<'file_ref, InnerStringList>> StringList<'file_ref, T> {
}
pub type OwnedStringList<'file_ref> = StringList<'file_ref, OwnedThis<'file_ref, InnerStringList>>;
pub type RefStringList<'file_ref> = StringList<'file_ref, RefThis<'file_ref, InnerStringList>>;
pub type RefStringListMut<'file_ref> =
StringList<'file_ref, RefThisMut<'file_ref, InnerStringList>>;
pub struct ByteVector<'file_ref, T: This<'file_ref, InnerByteVector>> {
_data: PhantomData<&'file_ref InnerByteVector>,
@ -114,9 +111,6 @@ impl<'file_ref, T: This<'file_ref, InnerByteVector>> ByteVector<'file_ref, T> {
}
pub type OwnedByteVector<'file_ref> = ByteVector<'file_ref, OwnedThis<'file_ref, InnerByteVector>>;
pub type RefByteVector<'file_ref> = ByteVector<'file_ref, RefThis<'file_ref, InnerByteVector>>;
pub type RefByteVectorMut<'file_ref> =
ByteVector<'file_ref, RefThisMut<'file_ref, InnerByteVector>>;
pub struct ByteVectorList<'file_ref, T: This<'file_ref, InnerByteVectorList>> {
_data: PhantomData<&'file_ref InnerByteVectorList>,
@ -135,14 +129,10 @@ impl<'file_ref, T: This<'file_ref, InnerByteVectorList>> ByteVectorList<'file_re
let cxx_values = bridge::ByteVectorList_to_vector(self.this.as_ref());
cxx_values
.iter()
.map(|value| ByteVector::new(unsafe { RefThis::new(value) }).to_vec())
.map(|value| ByteVector::new(RefThis::new(value)).to_vec())
.collect()
}
}
pub type OwnedByteVectorList<'file_ref> =
ByteVectorList<'file_ref, OwnedThis<'file_ref, InnerByteVectorList>>;
pub type RefByteVectorList<'file_ref> =
ByteVectorList<'file_ref, RefThis<'file_ref, InnerByteVectorList>>;
pub type RefByteVectorListMut<'file_ref> =
ByteVectorList<'file_ref, RefThisMut<'file_ref, InnerByteVectorList>>;

View file

@ -1,6 +1,6 @@
pub use super::bridge::CPPXiphComment;
use super::bridge::{CPPFieldListMap, FieldListMap_to_entries, XiphComment_pictureList};
pub use super::flac::PictureList;
pub use super::flac::FLACPictureList;
use super::this::{OwnedThis, RefThis, RefThisMut, ThisMut};
use super::tk;
use std::collections::HashMap;
@ -20,10 +20,10 @@ impl<'file_ref> XiphComment<'file_ref> {
FieldListMap::new(map_this)
}
pub fn picture_list(&mut self) -> PictureList<'file_ref> {
pub fn picture_list(&mut self) -> FLACPictureList<'file_ref> {
let pictures = XiphComment_pictureList(self.this.pin_mut());
let pictures_this = OwnedThis::new(pictures).unwrap();
PictureList::new(pictures_this)
FLACPictureList::new(pictures_this)
}
}