all: format

This commit is contained in:
Alexander Capehart 2024-12-16 13:34:49 -05:00
parent 18c5b3618c
commit ed102d3414
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
13 changed files with 474 additions and 345 deletions

View file

@ -22,12 +22,10 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.yield
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.detail.list.DiscDivider import org.oxycblt.auxio.detail.list.DiscDivider
import org.oxycblt.auxio.detail.list.DiscHeader import org.oxycblt.auxio.detail.list.DiscHeader
@ -302,8 +300,8 @@ constructor(
} }
/** /**
* Set a new [currentSong] from it's [Music.UID]. [currentSong] will * Set a new [currentSong] from it's [Music.UID]. [currentSong] will be updated to align with
* be updated to align with the new [Song]. * the new [Song].
* *
* @param uid The UID of the [Song] to load. Must be valid. * @param uid The UID of the [Song] to load. Must be valid.
*/ */
@ -504,9 +502,7 @@ constructor(
}) })
} }
private fun refreshAudioInfo(song: Song) { private fun refreshAudioInfo(song: Song) {}
}
private inline fun <T : MusicParent> refreshDetail( private inline fun <T : MusicParent> refreshDetail(
detail: Detail<T>?, detail: Detail<T>?,

View file

@ -20,7 +20,6 @@ package org.oxycblt.auxio.detail
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.text.format.Formatter
import android.view.LayoutInflater import android.view.LayoutInflater
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
@ -29,13 +28,8 @@ import androidx.navigation.fragment.navArgs
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogSongDetailBinding import org.oxycblt.auxio.databinding.DialogSongDetailBinding
import org.oxycblt.auxio.detail.list.SongProperty
import org.oxycblt.auxio.detail.list.SongPropertyAdapter import org.oxycblt.auxio.detail.list.SongPropertyAdapter
import org.oxycblt.auxio.list.adapter.UpdateInstructions
import org.oxycblt.auxio.music.resolve import org.oxycblt.auxio.music.resolve
import org.oxycblt.auxio.music.resolveNames
import org.oxycblt.auxio.playback.formatDurationMs
import org.oxycblt.auxio.playback.replaygain.formatDb
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.concatLocalized import org.oxycblt.auxio.util.concatLocalized
@ -87,17 +81,22 @@ class SongDetailDialog : ViewBindingMaterialDialogFragment<DialogSongDetailBindi
// buildList { // buildList {
// add(SongProperty(R.string.lbl_name, song.zipName(context))) // add(SongProperty(R.string.lbl_name, song.zipName(context)))
// add(SongProperty(R.string.lbl_album, song.album.zipName(context))) // add(SongProperty(R.string.lbl_album, song.album.zipName(context)))
// add(SongProperty(R.string.lbl_artists, song.artists.zipNames(context))) // add(SongProperty(R.string.lbl_artists,
// add(SongProperty(R.string.lbl_genres, song.genres.resolveNames(context))) // song.artists.zipNames(context)))
// song.date?.let { add(SongProperty(R.string.lbl_date, it.resolve(context))) } // add(SongProperty(R.string.lbl_genres,
// song.genres.resolveNames(context)))
// song.date?.let { add(SongProperty(R.string.lbl_date,
// it.resolve(context))) }
// song.track?.let { // song.track?.let {
// add(SongProperty(R.string.lbl_track, getString(R.string.fmt_number, it))) // add(SongProperty(R.string.lbl_track,
// getString(R.string.fmt_number, it)))
// } // }
// song.disc?.let { // song.disc?.let {
// val formattedNumber = getString(R.string.fmt_number, it.number) // val formattedNumber = getString(R.string.fmt_number, it.number)
// val zipped = // val zipped =
// if (it.name != null) { // if (it.name != null) {
// getString(R.string.fmt_zipped_names, formattedNumber, it.name) // getString(R.string.fmt_zipped_names, formattedNumber,
// it.name)
// } else { // } else {
// formattedNumber // formattedNumber
// } // }
@ -109,21 +108,27 @@ class SongDetailDialog : ViewBindingMaterialDialogFragment<DialogSongDetailBindi
// // } // // }
// add( // add(
// SongProperty( // SongProperty(
// R.string.lbl_size, Formatter.formatFileSize(context, song.size))) // R.string.lbl_size, Formatter.formatFileSize(context,
// add(SongProperty(R.string.lbl_duration, song.durationMs.formatDurationMs(true))) // song.size)))
// add(SongProperty(R.string.lbl_duration,
// song.durationMs.formatDurationMs(true)))
// info.bitrateKbps?.let { // info.bitrateKbps?.let {
// add(SongProperty(R.string.lbl_bitrate, getString(R.string.fmt_bitrate, it))) // add(SongProperty(R.string.lbl_bitrate,
// getString(R.string.fmt_bitrate, it)))
// } // }
// info.sampleRateHz?.let { // info.sampleRateHz?.let {
// add( // add(
// SongProperty( // SongProperty(
// R.string.lbl_sample_rate, getString(R.string.fmt_sample_rate, it))) // R.string.lbl_sample_rate,
// getString(R.string.fmt_sample_rate, it)))
// } // }
// song.replayGainAdjustment.track?.let { // song.replayGainAdjustment.track?.let {
// add(SongProperty(R.string.lbl_replaygain_track, it.formatDb(context))) // add(SongProperty(R.string.lbl_replaygain_track,
// it.formatDb(context)))
// } // }
// song.replayGainAdjustment.album?.let { // song.replayGainAdjustment.album?.let {
// add(SongProperty(R.string.lbl_replaygain_album, it.formatDb(context))) // add(SongProperty(R.string.lbl_replaygain_album,
// it.formatDb(context)))
// } // }
// }, // },
// UpdateInstructions.Replace(0)) // UpdateInstructions.Replace(0))

View file

@ -1,6 +1,20 @@
// /*
// Created by oxycblt on 12/12/24. * Copyright (c) 2024 Auxio Project
// * JVMInputStream.cpp is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "JVMInputStream.h" #include "JVMInputStream.h"
@ -8,20 +22,26 @@
// TODO: Handle stream exceptions // TODO: Handle stream exceptions
JVMInputStream::JVMInputStream(JNIEnv *env, jobject inputStream) : JVMInputStream::JVMInputStream(JNIEnv *env, jobject inputStream)
env(env), inputStream(inputStream) { : env(env), inputStream(inputStream) {
if (!env->IsInstanceOf(inputStream, env->FindClass("org/oxycblt/ktaglib/NativeInputStream"))) { if (!env->IsInstanceOf(
inputStream,
env->FindClass("org/oxycblt/ktaglib/NativeInputStream"))) {
throw std::runtime_error("oStream is not an instance of TagLibOStream"); throw std::runtime_error("oStream is not an instance of TagLibOStream");
} }
jclass inputStreamClass = env->FindClass("org/oxycblt/ktaglib/NativeInputStream"); jclass inputStreamClass =
inputStreamNameMethod = env->GetMethodID(inputStreamClass, "name", "()Ljava/lang/String;"); env->FindClass("org/oxycblt/ktaglib/NativeInputStream");
inputStreamReadBlockMethod = env->GetMethodID(inputStreamClass, "readBlock", "(J)[B"); inputStreamNameMethod =
env->GetMethodID(inputStreamClass, "name", "()Ljava/lang/String;");
inputStreamReadBlockMethod =
env->GetMethodID(inputStreamClass, "readBlock", "(J)[B");
inputStreamIsOpenMethod = env->GetMethodID(inputStreamClass, "isOpen", "()Z"); inputStreamIsOpenMethod = env->GetMethodID(inputStreamClass, "isOpen", "()Z");
inputStreamSeekFromBeginningMethod = env->GetMethodID(inputStreamClass, "seekFromBeginning", inputStreamSeekFromBeginningMethod =
"(J)V"); env->GetMethodID(inputStreamClass, "seekFromBeginning", "(J)V");
inputStreamSeekFromCurrentMethod = env->GetMethodID(inputStreamClass, "seekFromCurrent", inputStreamSeekFromCurrentMethod =
"(J)V"); env->GetMethodID(inputStreamClass, "seekFromCurrent", "(J)V");
inputStreamSeekFromEndMethod = env->GetMethodID(inputStreamClass, "seekFromEnd", "(J)V"); inputStreamSeekFromEndMethod =
env->GetMethodID(inputStreamClass, "seekFromEnd", "(J)V");
inputStreamClearMethod = env->GetMethodID(inputStreamClass, "clear", "()V"); inputStreamClearMethod = env->GetMethodID(inputStreamClass, "clear", "()V");
inputStreamTellMethod = env->GetMethodID(inputStreamClass, "tell", "()J"); inputStreamTellMethod = env->GetMethodID(inputStreamClass, "tell", "()J");
inputStreamLengthMethod = env->GetMethodID(inputStreamClass, "length", "()J"); inputStreamLengthMethod = env->GetMethodID(inputStreamClass, "length", "()J");
@ -34,7 +54,8 @@ JVMInputStream::~JVMInputStream() {
} }
TagLib::FileName JVMInputStream::name() const { TagLib::FileName JVMInputStream::name() const {
auto name = (jstring) env->CallObjectMethod(inputStream, inputStreamNameMethod); auto name =
(jstring)env->CallObjectMethod(inputStream, inputStreamNameMethod);
const char *nameChars = env->GetStringUTFChars(name, nullptr); const char *nameChars = env->GetStringUTFChars(name, nullptr);
auto fileName = TagLib::FileName(nameChars); auto fileName = TagLib::FileName(nameChars);
env->ReleaseStringUTFChars(name, nameChars); env->ReleaseStringUTFChars(name, nameChars);
@ -42,10 +63,12 @@ TagLib::FileName JVMInputStream::name() const {
} }
TagLib::ByteVector JVMInputStream::readBlock(size_t length) { TagLib::ByteVector JVMInputStream::readBlock(size_t length) {
auto data = (jbyteArray) env->CallObjectMethod(inputStream, inputStreamReadBlockMethod, length); auto data = (jbyteArray)env->CallObjectMethod(
inputStream, inputStreamReadBlockMethod, length);
jsize dataLength = env->GetArrayLength(data); jsize dataLength = env->GetArrayLength(data);
auto dataBytes = env->GetByteArrayElements(data, nullptr); auto dataBytes = env->GetByteArrayElements(data, nullptr);
TagLib::ByteVector byteVector(reinterpret_cast<const char *>(dataBytes), dataLength); TagLib::ByteVector byteVector(reinterpret_cast<const char *>(dataBytes),
dataLength);
env->ReleaseByteArrayElements(data, dataBytes, JNI_ABORT); env->ReleaseByteArrayElements(data, dataBytes, JNI_ABORT);
return byteVector; return byteVector;
} }
@ -63,9 +86,7 @@ void JVMInputStream::removeBlock(TagLib::offset_t start, size_t length) {
throw std::runtime_error("Not implemented"); throw std::runtime_error("Not implemented");
} }
bool JVMInputStream::readOnly() const { bool JVMInputStream::readOnly() const { return true; }
return true;
}
bool JVMInputStream::isOpen() const { bool JVMInputStream::isOpen() const {
return env->CallBooleanMethod(inputStream, inputStreamIsOpenMethod); return env->CallBooleanMethod(inputStream, inputStreamIsOpenMethod);
@ -75,7 +96,8 @@ void JVMInputStream::seek(TagLib::offset_t offset, Position p) {
auto joffset = static_cast<jlong>(std::llround(offset)); auto joffset = static_cast<jlong>(std::llround(offset));
switch (p) { switch (p) {
case Beginning: case Beginning:
env->CallVoidMethod(inputStream, inputStreamSeekFromBeginningMethod, joffset); env->CallVoidMethod(inputStream, inputStreamSeekFromBeginningMethod,
joffset);
break; break;
case Current: case Current:
env->CallVoidMethod(inputStream, inputStreamSeekFromCurrentMethod, joffset); env->CallVoidMethod(inputStream, inputStreamSeekFromCurrentMethod, joffset);

View file

@ -1,14 +1,28 @@
// /*
// Created by oxycblt on 12/12/24. * Copyright (c) 2024 Auxio Project
// * JVMMetadataBuilder.cpp is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "JVMMetadataBuilder.h" #include "JVMMetadataBuilder.h"
#include <taglib/mp4tag.h> #include <taglib/mp4tag.h>
#include <taglib/textidentificationframe.h> #include <taglib/textidentificationframe.h>
JVMMetadataBuilder::JVMMetadataBuilder(JNIEnv *env) : env(env), id3v2(env), xiph(env), mp4(env), JVMMetadataBuilder::JVMMetadataBuilder(JNIEnv *env)
cover(), properties(nullptr) {} : env(env), id3v2(env), xiph(env), mp4(env), cover(), properties(nullptr) {}
void JVMMetadataBuilder::setMimeType(const std::string_view type) { void JVMMetadataBuilder::setMimeType(const std::string_view type) {
this->mimeType = type; this->mimeType = type;
@ -16,14 +30,18 @@ void JVMMetadataBuilder::setMimeType(const std::string_view type) {
void JVMMetadataBuilder::setId3v2(const TagLib::ID3v2::Tag &tag) { void JVMMetadataBuilder::setId3v2(const TagLib::ID3v2::Tag &tag) {
for (auto frame : tag.frameList()) { for (auto frame : tag.frameList()) {
if (auto txxxFrame = dynamic_cast<TagLib::ID3v2::UserTextIdentificationFrame *>(frame)) { if (auto txxxFrame =
dynamic_cast<TagLib::ID3v2::UserTextIdentificationFrame *>(frame)) {
TagLib::StringList frameText = txxxFrame->fieldList(); TagLib::StringList frameText = txxxFrame->fieldList();
// Frame text starts with the description then the remaining values // Frame text starts with the description then the remaining values
auto begin = frameText.begin(); auto begin = frameText.begin();
TagLib::String key = TagLib::String(frame->frameID()) + ":" + begin->upper(); TagLib::String key =
TagLib::String(frame->frameID()) + ":" + begin->upper();
frameText.erase(begin); frameText.erase(begin);
id3v2.add(key, frameText); id3v2.add(key, frameText);
} else if (auto textFrame = dynamic_cast<TagLib::ID3v2::TextIdentificationFrame *>(frame)) { } else if (auto textFrame =
dynamic_cast<TagLib::ID3v2::TextIdentificationFrame *>(
frame)) {
TagLib::String key = frame->frameID(); TagLib::String key = frame->frameID();
TagLib::StringList frameText = textFrame->fieldList(); TagLib::StringList frameText = textFrame->fieldList();
id3v2.add(key, frameText); id3v2.add(key, frameText);
@ -87,7 +105,8 @@ void JVMMetadataBuilder::setMp4(const TagLib::MP4::Tag &tag) {
} }
} }
void JVMMetadataBuilder::setCover(const TagLib::List<TagLib::VariantMap> covers) { void JVMMetadataBuilder::setCover(
const TagLib::List<TagLib::VariantMap> covers) {
if (covers.isEmpty()) { if (covers.isEmpty()) {
return; return;
} }
@ -111,14 +130,19 @@ void JVMMetadataBuilder::setProperties(TagLib::AudioProperties *properties) {
jobject JVMMetadataBuilder::build() { jobject JVMMetadataBuilder::build() {
jclass propertiesClass = env->FindClass("org/oxycblt/ktaglib/Properties"); jclass propertiesClass = env->FindClass("org/oxycblt/ktaglib/Properties");
jmethodID propertiesInit = env->GetMethodID(propertiesClass, "<init>", "(Ljava/lang/String;JII)V"); jmethodID propertiesInit =
jobject propertiesObj = env->NewObject(propertiesClass, propertiesInit, env->GetMethodID(propertiesClass, "<init>", "(Ljava/lang/String;JII)V");
env->NewStringUTF(mimeType.data()), (jlong) properties->lengthInMilliseconds(), jobject propertiesObj = env->NewObject(
properties->bitrate(), properties->sampleRate()); propertiesClass, propertiesInit, env->NewStringUTF(mimeType.data()),
(jlong)properties->lengthInMilliseconds(), properties->bitrate(),
properties->sampleRate());
env->DeleteLocalRef(propertiesClass); env->DeleteLocalRef(propertiesClass);
jclass metadataClass = env->FindClass("org/oxycblt/ktaglib/Metadata"); jclass metadataClass = env->FindClass("org/oxycblt/ktaglib/Metadata");
jmethodID metadataInit = env->GetMethodID(metadataClass, "<init>", "(Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;[BLorg/oxycblt/ktaglib/Properties;)V"); jmethodID metadataInit =
env->GetMethodID(metadataClass, "<init>",
"(Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;[BLorg/"
"oxycblt/ktaglib/Properties;)V");
jobject id3v2Map = id3v2.getObject(); jobject id3v2Map = id3v2.getObject();
jobject xiphMap = xiph.getObject(); jobject xiphMap = xiph.getObject();
jobject mp4Map = mp4.getObject(); jobject mp4Map = mp4.getObject();
@ -126,9 +150,12 @@ jobject JVMMetadataBuilder::build() {
if (cover.has_value()) { if (cover.has_value()) {
auto coverSize = static_cast<jsize>(cover->size()); auto coverSize = static_cast<jsize>(cover->size());
coverArray = env->NewByteArray(coverSize); coverArray = env->NewByteArray(coverSize);
env->SetByteArrayRegion(coverArray, 0, coverSize, reinterpret_cast<const jbyte *>(cover->data())); env->SetByteArrayRegion(coverArray, 0, coverSize,
reinterpret_cast<const jbyte *>(cover->data()));
} }
jobject metadataObj = env->NewObject(metadataClass, metadataInit, id3v2Map, xiphMap, mp4Map, coverArray, propertiesObj); jobject metadataObj =
env->NewObject(metadataClass, metadataInit, id3v2Map, xiphMap, mp4Map,
coverArray, propertiesObj);
env->DeleteLocalRef(metadataClass); env->DeleteLocalRef(metadataClass);
return metadataObj; return metadataObj;
} }

View file

@ -1,6 +1,20 @@
// /*
// Created by oxycblt on 12/12/24. * Copyright (c) 2024 Auxio Project
// * JVMTagMap.cpp is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "JVMTagMap.h" #include "JVMTagMap.h"
@ -8,26 +22,29 @@ JVMTagMap::JVMTagMap(JNIEnv *env) : env(env) {
jclass hashMapClass = env->FindClass("java/util/HashMap"); jclass hashMapClass = env->FindClass("java/util/HashMap");
jmethodID init = env->GetMethodID(hashMapClass, "<init>", "()V"); jmethodID init = env->GetMethodID(hashMapClass, "<init>", "()V");
hashMap = env->NewObject(hashMapClass, init); hashMap = env->NewObject(hashMapClass, init);
hashMapGetMethod = env->GetMethodID(hashMapClass, "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); hashMapGetMethod = env->GetMethodID(hashMapClass, "get",
hashMapPutMethod = env->GetMethodID(hashMapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); "(Ljava/lang/Object;)Ljava/lang/Object;");
hashMapPutMethod = env->GetMethodID(
hashMapClass, "put",
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
env->DeleteLocalRef(hashMapClass); env->DeleteLocalRef(hashMapClass);
jclass arrayListClass = env->FindClass("java/util/ArrayList"); jclass arrayListClass = env->FindClass("java/util/ArrayList");
arrayListInitMethod = env->GetMethodID(arrayListClass, "<init>", "()V"); arrayListInitMethod = env->GetMethodID(arrayListClass, "<init>", "()V");
arrayListAddMethod = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z"); arrayListAddMethod =
env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
env->DeleteLocalRef(arrayListClass); env->DeleteLocalRef(arrayListClass);
} }
JVMTagMap::~JVMTagMap() { JVMTagMap::~JVMTagMap() { env->DeleteLocalRef(hashMap); }
env->DeleteLocalRef(hashMap);
}
void JVMTagMap::add(TagLib::String &key, std::string_view value) { void JVMTagMap::add(TagLib::String &key, std::string_view value) {
jstring jKey = env->NewStringUTF(key.toCString(true)); jstring jKey = env->NewStringUTF(key.toCString(true));
jstring jValue = env->NewStringUTF(value.data()); jstring jValue = env->NewStringUTF(value.data());
// check if theres already a value arraylist in the map // check if theres already a value arraylist in the map
jobject existingValue = env->CallObjectMethod(hashMap, hashMapGetMethod, jKey); jobject existingValue =
env->CallObjectMethod(hashMap, hashMapGetMethod, jKey);
// if there is, add to the value to the existing arraylist // if there is, add to the value to the existing arraylist
if (existingValue != nullptr) { if (existingValue != nullptr) {
env->CallBooleanMethod(existingValue, arrayListAddMethod, jValue); env->CallBooleanMethod(existingValue, arrayListAddMethod, jValue);
@ -45,7 +62,8 @@ void JVMTagMap::add(TagLib::String &key, TagLib::StringList &value) {
jstring jKey = env->NewStringUTF(key.toCString(true)); jstring jKey = env->NewStringUTF(key.toCString(true));
// check if theres already a value arraylist in the map // check if theres already a value arraylist in the map
jobject existingValue = env->CallObjectMethod(hashMap, hashMapGetMethod, jKey); jobject existingValue =
env->CallObjectMethod(hashMap, hashMapGetMethod, jKey);
// if there is, add to the value to the existing arraylist // if there is, add to the value to the existing arraylist
if (existingValue != nullptr) { if (existingValue != nullptr) {
for (auto &val : value) { for (auto &val : value) {
@ -65,6 +83,4 @@ void JVMTagMap::add(TagLib::String &key, TagLib::StringList &value) {
} }
} }
jobject JVMTagMap::getObject() { jobject JVMTagMap::getObject() { return hashMap; }
return hashMap;
}

View file

@ -1,3 +1,21 @@
/*
* Copyright (c) 2024 Auxio Project
* taglib_jni.cpp is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <jni.h> #include <jni.h>
#include <string> #include <string>
@ -5,16 +23,15 @@
#include "JVMMetadataBuilder.h" #include "JVMMetadataBuilder.h"
#include "taglib/fileref.h" #include "taglib/fileref.h"
#include "taglib/mpegfile.h"
#include "taglib/mp4file.h"
#include "taglib/flacfile.h" #include "taglib/flacfile.h"
#include "taglib/mp4file.h"
#include "taglib/mpegfile.h"
#include "taglib/opusfile.h" #include "taglib/opusfile.h"
#include "taglib/vorbisfile.h" #include "taglib/vorbisfile.h"
#include "taglib/wavfile.h" #include "taglib/wavfile.h"
extern "C" JNIEXPORT jobject JNICALL extern "C" JNIEXPORT jobject JNICALL
Java_org_oxycblt_musikr_metadata_TagLibJNI_openNative( Java_org_oxycblt_musikr_metadata_TagLibJNI_openNative(JNIEnv *env,
JNIEnv* env,
jobject /* this */, jobject /* this */,
jobject inputStream) { jobject inputStream) {
JVMInputStream stream{env, inputStream}; JVMInputStream stream{env, inputStream};
@ -38,7 +55,8 @@ Java_org_oxycblt_musikr_metadata_TagLibJNI_openNative(
} else if (auto *opusFile = dynamic_cast<TagLib::Ogg::Opus::File *>(file)) { } else if (auto *opusFile = dynamic_cast<TagLib::Ogg::Opus::File *>(file)) {
builder.setMimeType("audio/opus"); builder.setMimeType("audio/opus");
builder.setXiph(*opusFile->tag()); builder.setXiph(*opusFile->tag());
} else if (auto *vorbisFile = dynamic_cast<TagLib::Ogg::Vorbis::File *>(file)) { } else if (auto *vorbisFile =
dynamic_cast<TagLib::Ogg::Vorbis::File *>(file)) {
builder.setMimeType("audio/vorbis"); builder.setMimeType("audio/vorbis");
builder.setXiph(*vorbisFile->tag()); builder.setXiph(*vorbisFile->tag());
} else if (auto *wavFile = dynamic_cast<TagLib::RIFF::WAV::File *>(file)) { } else if (auto *wavFile = dynamic_cast<TagLib::RIFF::WAV::File *>(file)) {

View file

@ -30,9 +30,9 @@ import androidx.room.Room
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import androidx.room.TypeConverter import androidx.room.TypeConverter
import androidx.room.TypeConverters import androidx.room.TypeConverters
import org.oxycblt.musikr.metadata.Properties
import org.oxycblt.musikr.cover.Cover import org.oxycblt.musikr.cover.Cover
import org.oxycblt.musikr.fs.query.DeviceFile import org.oxycblt.musikr.fs.query.DeviceFile
import org.oxycblt.musikr.metadata.Properties
import org.oxycblt.musikr.pipeline.RawSong import org.oxycblt.musikr.pipeline.RawSong
import org.oxycblt.musikr.tag.Date import org.oxycblt.musikr.tag.Date
import org.oxycblt.musikr.tag.parse.ParsedTags import org.oxycblt.musikr.tag.parse.ParsedTags

View file

@ -1,15 +1,31 @@
/*
* Copyright (c) 2024 Auxio Project
* AndroidInputStream.kt is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.musikr.metadata package org.oxycblt.musikr.metadata
import android.content.Context import android.content.Context
import java.io.FileInputStream import java.io.FileInputStream
import java.nio.ByteBuffer import java.nio.ByteBuffer
class AndroidInputStream( class AndroidInputStream(context: Context, fileRef: FileRef) : NativeInputStream {
context: Context,
fileRef: FileRef
) : NativeInputStream {
private val fileName = fileRef.fileName private val fileName = fileRef.fileName
private val fd = requireNotNull(context.contentResolver.openFileDescriptor(fileRef.uri, "r")) { private val fd =
requireNotNull(context.contentResolver.openFileDescriptor(fileRef.uri, "r")) {
"Failed to open file descriptor for ${fileRef.fileName}" "Failed to open file descriptor for ${fileRef.fileName}"
} }
private val fis = FileInputStream(fd.fileDescriptor) private val fis = FileInputStream(fd.fileDescriptor)

View file

@ -1,10 +1,27 @@
/*
* Copyright (c) 2024 Auxio Project
* NativeInputStream.kt is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.musikr.metadata package org.oxycblt.musikr.metadata
/** /**
* Java interface for the read-only methods in TagLib's IOStream API. * Java interface for the read-only methods in TagLib's IOStream API.
* *
* The vast majority of IO shim between Taglib/KTaglib should occur here * The vast majority of IO shim between Taglib/KTaglib should occur here to minimize JNI calls.
* to minimize JNI calls.
*/ */
interface NativeInputStream { interface NativeInputStream {
fun name(): String fun name(): String

View file

@ -1,3 +1,21 @@
/*
* Copyright (c) 2024 Auxio Project
* TagLibJNI.kt is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.musikr.metadata package org.oxycblt.musikr.metadata
import android.content.Context import android.content.Context
@ -11,8 +29,7 @@ object TagLibJNI {
/** /**
* Open a file and extract a tag. * Open a file and extract a tag.
* *
* Note: This method is blocking and should be handled as such if * Note: This method is blocking and should be handled as such if calling from a coroutine.
* calling from a coroutine.
*/ */
fun open(context: Context, ref: FileRef): Metadata? { fun open(context: Context, ref: FileRef): Metadata? {
val inputStream = AndroidInputStream(context, ref) val inputStream = AndroidInputStream(context, ref)
@ -24,10 +41,7 @@ object TagLibJNI {
private external fun openNative(ioStream: AndroidInputStream): Metadata? private external fun openNative(ioStream: AndroidInputStream): Metadata?
} }
data class FileRef( data class FileRef(val fileName: String, val uri: Uri)
val fileName: String,
val uri: Uri
)
data class Metadata( data class Metadata(
val id3v2: Map<String, List<String>>, val id3v2: Map<String, List<String>>,

View file

@ -28,12 +28,12 @@ import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.merge
import org.oxycblt.musikr.metadata.Properties
import org.oxycblt.musikr.Storage import org.oxycblt.musikr.Storage
import org.oxycblt.musikr.cache.CacheResult import org.oxycblt.musikr.cache.CacheResult
import org.oxycblt.musikr.cover.Cover import org.oxycblt.musikr.cover.Cover
import org.oxycblt.musikr.fs.query.DeviceFile import org.oxycblt.musikr.fs.query.DeviceFile
import org.oxycblt.musikr.metadata.MetadataExtractor import org.oxycblt.musikr.metadata.MetadataExtractor
import org.oxycblt.musikr.metadata.Properties
import org.oxycblt.musikr.tag.parse.ParsedTags import org.oxycblt.musikr.tag.parse.ParsedTags
import org.oxycblt.musikr.tag.parse.TagParser import org.oxycblt.musikr.tag.parse.TagParser

View file

@ -77,9 +77,7 @@ interface M3U {
} }
} }
private class M3UImpl( private class M3UImpl(private val volumeManager: VolumeManager) : M3U {
private val volumeManager: VolumeManager
) : M3U {
override fun read(stream: InputStream, workingDirectory: Path): ImportedPlaylist? { override fun read(stream: InputStream, workingDirectory: Path): ImportedPlaylist? {
val volumes = volumeManager.getVolumes() val volumes = volumeManager.getVolumes()
val reader = BufferedReader(InputStreamReader(stream)) val reader = BufferedReader(InputStreamReader(stream))

View file

@ -18,8 +18,8 @@
package org.oxycblt.musikr.tag.parse package org.oxycblt.musikr.tag.parse
import org.oxycblt.musikr.metadata.Metadata
import org.oxycblt.musikr.fs.query.DeviceFile import org.oxycblt.musikr.fs.query.DeviceFile
import org.oxycblt.musikr.metadata.Metadata
interface TagParser { interface TagParser {
fun parse(file: DeviceFile, metadata: Metadata): ParsedTags fun parse(file: DeviceFile, metadata: Metadata): ParsedTags