Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@ Returns the specified metadata of the provided uri based on the `options` argume

> **Note:** The "complicated" typing is to make the resulting promise type-safe and be based off the provided `options`.

### getR128Gain

```ts
function getR128Gain(uri: string): Promise<number | null>;
```

Attempts to return the track replay gain.

### saveArtwork

```ts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import com.cyanchill.missingcore.metadataretriever.models.ArtworkOptions
import com.cyanchill.missingcore.metadataretriever.models.BridgeReturnables.*
import com.cyanchill.missingcore.metadataretriever.utils.MapUtils
import com.cyanchill.missingcore.metadataretriever.utils.Normalization
import com.cyanchill.missingcore.metadataretriever.utils.ReplayGainParser
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
Expand Down Expand Up @@ -250,7 +251,7 @@ class MetadataRetrieverModule(reactContext: ReactApplicationContext) :
metadataEntry.id.uppercase() in ID3v2_LYRIC_TAGS
) {
lyricsStr = when (metadataEntry) {
is TextInformationFrame -> metadataEntry.values[0]
is TextInformationFrame -> metadataEntry.values.firstOrNull()
is BinaryFrame -> {
val byteArr = metadataEntry.data
// The 1st byte in the array determines the encoding in ID3.
Expand Down Expand Up @@ -282,6 +283,8 @@ class MetadataRetrieverModule(reactContext: ReactApplicationContext) :

if (isLyricsSync) break
}

if (isLyricsSync) break
}

if (lyricsStr !== null) lyricsStr = lyricsStr.replace("\u0000", "")
Expand All @@ -300,6 +303,36 @@ class MetadataRetrieverModule(reactContext: ReactApplicationContext) :
}
}

override fun getR128Gain(uri: String, promise: Promise) {
try {
val metadataList = getMetadataList(getFormatList(uri))
var gain: Float? = null

for (metadata in metadataList) {
val numEntries = metadata.length()
// Manually iterate over metadata entries to find a supported key.
for (i in 0 until numEntries) {
gain = ReplayGainParser(metadata[i]).gain
if (gain != null) break
}

if (gain != null) break
}

promise.resolve(gain)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
} catch (e: ExecutionException) {
val isWantedException =
e.message?.contains("androidx.media3.datasource.FileDataSource\$FileDataSourceException")
?: false
when (isWantedException) {
true -> promise.reject("ENOENT", "ENOENT: No such file or directory (${uri})", e)
false -> promise.reject("ERR_REPLAY_GAIN", e.message, e)
}
} catch (e: Exception) {
promise.reject("ERR_REPLAY_GAIN", e.message, e)
}
}

/** Expose to the user the ability to update internal configuration options. */
override fun updateConfigs(options: ReadableMap, promise: Promise) {
reader.updateConfigs(Arguments.toBundle(options) as Bundle)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.cyanchill.missingcore.metadataretriever.utils

import androidx.annotation.OptIn
import androidx.media3.common.Metadata
import androidx.media3.common.util.UnstableApi
import androidx.media3.extractor.metadata.id3.TextInformationFrame
import androidx.media3.extractor.metadata.vorbis.VorbisComment
import androidx.media3.extractor.mp3.Mp3InfoReplayGain

@OptIn(UnstableApi::class)
class ReplayGainParser(entry: Metadata.Entry) {
var gain: Float? = null

init {
// Extract track gain based on the class.
gain = when (entry) {
is TextInformationFrame -> handleTextInformationFrame(entry)
is Mp3InfoReplayGain -> handleMp3InfoReplayGain(entry)
is VorbisComment -> handleVorbisComment(entry)
else -> null
}
}

private fun handleTextInformationFrame(frame: TextInformationFrame): Float? =
when (frame.description?.uppercase()) {
"REPLAYGAIN_TRACK_GAIN" -> frame.values.firstOrNull()?.parseReplayGainAdjustment()
//? R128 gain needs to be divided by `256f`.
"R128_TRACK_GAIN" -> frame.values.firstOrNull()?.parseReplayGainAdjustment()?.div(256f)
else -> null
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

/** For when we see `ReplayGain Xing/Info`. */
private fun handleMp3InfoReplayGain(frame: Mp3InfoReplayGain): Float? {
return frame.field1?.gain
}

private fun handleVorbisComment(comment: VorbisComment): Float? =
when (comment.key.uppercase()) {
"REPLAYGAIN_TRACK_GAIN" -> comment.value.parseReplayGainAdjustment()
//? R128 gain needs to be divided by `256f`.
"R128_TRACK_GAIN" -> comment.value.parseReplayGainAdjustment()?.div(256f)
else -> null
}

/** Some replay gain tags include "dB" in the string. */
private fun String.parseReplayGainAdjustment() =
replace(Regex("[^\\d.-]"), "").toFloatOrNull()
}
2 changes: 2 additions & 0 deletions src/NativeMetadataRetriever.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ export interface Spec extends TurboModule {

getLyric(uri: string): Promise<string | null>;

getR128Gain(uri: string): Promise<number | null>;

updateConfigs(options: ConfigOptions): Promise<void>;

/**
Expand Down
7 changes: 7 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ export async function getLyric(uri: string): Promise<string | null> {
}
//#endregion

//#region Replay Gain
/** Returns the replay gain for the track. */
export async function getR128Gain(uri: string): Promise<number | null> {
return MetadataRetriever.getR128Gain(uri);
}
//#endregion

//#region Configuration
/** Update internal configuration options. */
export async function updateConfigs(options: ConfigOptions): Promise<void> {
Expand Down
Loading