music: add support for id3v2.4 multi value tags
Add support for ID3v2.4-style multi-value tags separated by a null terminator. This temporarily removes most other forms of separator parsing in the app. My plan is to reunify it under a new separator setting that allows the user to select how multi-value tags are separated in their library. Separator parsing tends to be too destructive by default, so this tends to be a good option overall. This commit does require ExoPlayer to be forked once again to add ID3v2.4 separator support.
This commit is contained in:
parent
2690e8343a
commit
2033e2cb1f
5 changed files with 68 additions and 103 deletions
|
@ -56,9 +56,6 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
afterEvaluate {
|
|
||||||
preDebugBuild.dependsOn spotlessApply
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Kotlin
|
// Kotlin
|
||||||
|
@ -103,8 +100,11 @@ dependencies {
|
||||||
// Exoplayer
|
// Exoplayer
|
||||||
// WARNING: THE EXOPLAYER VERSION MUST BE KEPT IN LOCK-STEP WITH THE PRE-BUILD SCRIPT.
|
// WARNING: THE EXOPLAYER VERSION MUST BE KEPT IN LOCK-STEP WITH THE PRE-BUILD SCRIPT.
|
||||||
// IF NOT, VERY UNFRIENDLY BUILD FAILURES AND CRASHES MAY ENSUE.
|
// IF NOT, VERY UNFRIENDLY BUILD FAILURES AND CRASHES MAY ENSUE.
|
||||||
implementation "com.google.android.exoplayer:exoplayer-core:2.18.1"
|
implementation("com.google.android.exoplayer:exoplayer-core:2.18.1") {
|
||||||
|
exclude group: "com.google.android.exoplayer", module: "exoplayer-extractor"
|
||||||
|
}
|
||||||
|
|
||||||
|
implementation fileTree(dir: "libs", include: ["library-*.aar"])
|
||||||
implementation fileTree(dir: "libs", include: ["extension-*.aar"])
|
implementation fileTree(dir: "libs", include: ["extension-*.aar"])
|
||||||
|
|
||||||
// Image loading
|
// Image loading
|
||||||
|
|
|
@ -34,8 +34,6 @@ import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.ui.Sort
|
import org.oxycblt.auxio.ui.Sort
|
||||||
import org.oxycblt.auxio.ui.recycler.Item
|
import org.oxycblt.auxio.ui.recycler.Item
|
||||||
import org.oxycblt.auxio.util.inRangeOrNull
|
import org.oxycblt.auxio.util.inRangeOrNull
|
||||||
import org.oxycblt.auxio.util.logE
|
|
||||||
import org.oxycblt.auxio.util.msToSecs
|
|
||||||
import org.oxycblt.auxio.util.nonZeroOrNull
|
import org.oxycblt.auxio.util.nonZeroOrNull
|
||||||
import org.oxycblt.auxio.util.unlikelyToBeNull
|
import org.oxycblt.auxio.util.unlikelyToBeNull
|
||||||
|
|
||||||
|
@ -86,75 +84,31 @@ sealed class Music : Item {
|
||||||
* @author OxygenCobalt
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
@Parcelize
|
@Parcelize
|
||||||
class UID
|
class UID private constructor(private val tag: String, private val uuid: UUID) : Parcelable {
|
||||||
private constructor(private val datatype: String, private val isMusicBrainz: Boolean, private val uuid: UUID) :
|
|
||||||
Parcelable {
|
|
||||||
// TODO: Formalize datatype and isMusicBrainz more
|
|
||||||
|
|
||||||
// Cache the hashCode for speed
|
// Cache the hashCode for speed
|
||||||
@IgnoredOnParcel private val hashCode: Int
|
@IgnoredOnParcel private val hashCode = 31 * tag.hashCode() + uuid.hashCode()
|
||||||
|
|
||||||
init {
|
|
||||||
var result = datatype.hashCode()
|
|
||||||
result = 31 * result + isMusicBrainz.hashCode()
|
|
||||||
result = 31 * result + uuid.hashCode()
|
|
||||||
hashCode = result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode() = hashCode
|
override fun hashCode() = hashCode
|
||||||
|
|
||||||
override fun equals(other: Any?) =
|
override fun equals(other: Any?) = other is UID && tag == other.tag && uuid == other.uuid
|
||||||
other is UID &&
|
|
||||||
datatype == other.datatype &&
|
|
||||||
isMusicBrainz == other.isMusicBrainz &&
|
|
||||||
uuid == other.uuid
|
|
||||||
|
|
||||||
override fun toString() =
|
override fun toString() = "$tag:$uuid"
|
||||||
"$datatype/${if (isMusicBrainz) FORMAT_MUSICBRAINZ else FORMAT_AUXIO}:$uuid"
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val FORMAT_AUXIO = "auxio"
|
|
||||||
const val FORMAT_MUSICBRAINZ = "musicbrainz"
|
|
||||||
|
|
||||||
/** Parse a [UID] from the string [uid]. Returns null if not valid. */
|
/** Parse a [UID] from the string [uid]. Returns null if not valid. */
|
||||||
fun fromString(uid: String): UID? {
|
fun fromString(uid: String): UID? {
|
||||||
val split = uid.split(':', limit = 2)
|
val split = uid.split(':', limit = 2)
|
||||||
if (split.size != 2) {
|
if (split.size != 2) {
|
||||||
logE("Invalid uid: Malformed structure")
|
|
||||||
}
|
|
||||||
|
|
||||||
val namespace = split[0].split('/', limit = 2)
|
|
||||||
if (namespace.size != 2) {
|
|
||||||
logE("Invalid uid: Malformed namespace")
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
val datatype = namespace[0]
|
return UID(tag = split[0], split[1].toUuid() ?: return null)
|
||||||
val isMusicBrainz =
|
|
||||||
when (namespace[1]) {
|
|
||||||
FORMAT_AUXIO -> false
|
|
||||||
FORMAT_MUSICBRAINZ -> true
|
|
||||||
else -> {
|
|
||||||
logE("Invalid uid: Malformed uuid format")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val uuid =
|
|
||||||
try {
|
|
||||||
UUID.fromString(split[1])
|
|
||||||
} catch (e: IllegalArgumentException) {
|
|
||||||
logE("Invalid uid: Malformed uuid")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return UID(datatype, isMusicBrainz, uuid)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a UUID derived from the MD5 hash of the data digested in [updates].
|
* Make a UUID derived from the MD5 hash of the data digested in [updates].
|
||||||
*
|
*
|
||||||
* This is considered the "auxio" uuid format.
|
* This is Auxio's UID format.
|
||||||
*/
|
*/
|
||||||
fun hashed(clazz: KClass<*>, updates: MessageDigest.() -> Unit): UID {
|
fun hashed(clazz: KClass<*>, updates: MessageDigest.() -> Unit): UID {
|
||||||
// Auxio hashes consist of the MD5 hash of the non-subjective, consistent
|
// Auxio hashes consist of the MD5 hash of the non-subjective, consistent
|
||||||
|
@ -162,7 +116,8 @@ sealed class Music : Item {
|
||||||
val digest = MessageDigest.getInstance("MD5")
|
val digest = MessageDigest.getInstance("MD5")
|
||||||
updates(digest)
|
updates(digest)
|
||||||
val uuid = digest.digest().toUuid()
|
val uuid = digest.digest().toUuid()
|
||||||
return UID(unlikelyToBeNull(clazz.simpleName).lowercase(), false, uuid)
|
val tag = "auxio.${unlikelyToBeNull(clazz.simpleName).lowercase()}"
|
||||||
|
return UID(tag, uuid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -298,9 +253,6 @@ class Song constructor(private val raw: Raw) : Music() {
|
||||||
|
|
||||||
update(track)
|
update(track)
|
||||||
update(disc)
|
update(disc)
|
||||||
|
|
||||||
// Hashing by seconds makes the song more resilient to trimming
|
|
||||||
update(durationMs.msToSecs())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -580,9 +532,9 @@ fun ByteArray.toUuid(): UUID {
|
||||||
* nature of tag formats. Thus, it's better to use an analogous data structure that will not mangle
|
* nature of tag formats. Thus, it's better to use an analogous data structure that will not mangle
|
||||||
* or reject valid-ish dates.
|
* or reject valid-ish dates.
|
||||||
*
|
*
|
||||||
* Date instances are immutable and their implementation is hidden. To instantiate one, use
|
* Date instances are immutable and their implementation is hidden. To instantiate one, use [from].
|
||||||
* [from]. The string representation of a Date is RFC 3339, with granular position depending on the
|
* The string representation of a Date is RFC 3339, with granular position depending on the presence
|
||||||
* presence of particular tokens.
|
* of particular tokens.
|
||||||
*
|
*
|
||||||
* Please, **Do not use this for anything important related to time.** I cannot stress this enough.
|
* Please, **Do not use this for anything important related to time.** I cannot stress this enough.
|
||||||
* This code will blow up if you try to do that.
|
* This code will blow up if you try to do that.
|
||||||
|
@ -776,8 +728,6 @@ sealed class ReleaseType {
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun parse(type: String) = parse(type.split('+'))
|
|
||||||
|
|
||||||
fun parse(types: List<String>): ReleaseType {
|
fun parse(types: List<String>): ReleaseType {
|
||||||
val primary = types[0].trim()
|
val primary = types[0].trim()
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import android.provider.MediaStore
|
||||||
import androidx.core.text.isDigitsOnly
|
import androidx.core.text.isDigitsOnly
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.util.nonZeroOrNull
|
import org.oxycblt.auxio.util.nonZeroOrNull
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
/** Shortcut for making a [ContentResolver] query with less superfluous arguments. */
|
/** Shortcut for making a [ContentResolver] query with less superfluous arguments. */
|
||||||
fun ContentResolver.queryCursor(
|
fun ContentResolver.queryCursor(
|
||||||
|
@ -58,6 +59,8 @@ val Long.audioUri: Uri
|
||||||
val Long.albumCoverUri: Uri
|
val Long.albumCoverUri: Uri
|
||||||
get() = ContentUris.withAppendedId(EXTERNAL_ALBUM_ART_URI, this)
|
get() = ContentUris.withAppendedId(EXTERNAL_ALBUM_ART_URI, this)
|
||||||
|
|
||||||
|
fun String.toUuid() = try { UUID.fromString(this) } catch (e: IllegalArgumentException) { null }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse out the track number field as if the given Int is formatted as DTTT, where D Is the disc
|
* Parse out the track number field as if the given Int is formatted as DTTT, where D Is the disc
|
||||||
* and T is the track number. Values of zero will be ignored under the assumption that they are
|
* and T is the track number. Values of zero will be ignored under the assumption that they are
|
||||||
|
@ -100,9 +103,6 @@ fun String.parseSortName() =
|
||||||
else -> this
|
else -> this
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Shortcut to parse an [ReleaseType] from a string */
|
|
||||||
fun String.parseReleaseType() = ReleaseType.parse(this)
|
|
||||||
|
|
||||||
/** Shortcut to parse a [ReleaseType] from a list of strings */
|
/** Shortcut to parse a [ReleaseType] from a list of strings */
|
||||||
fun List<String>.parseReleaseType() = ReleaseType.parse(this)
|
fun List<String>.parseReleaseType() = ReleaseType.parse(this)
|
||||||
|
|
||||||
|
@ -110,8 +110,14 @@ fun List<String>.parseReleaseType() = ReleaseType.parse(this)
|
||||||
* Decodes the genre name from an ID3(v2) constant. See [GENRE_TABLE] for the genre constant map
|
* Decodes the genre name from an ID3(v2) constant. See [GENRE_TABLE] for the genre constant map
|
||||||
* that Auxio uses.
|
* that Auxio uses.
|
||||||
*/
|
*/
|
||||||
fun String.parseId3GenreName() =
|
fun String.parseId3GenreName() = parseId3v1Genre()?.let { listOf(it) } ?: parseId3v2Genre() ?: listOf(this)
|
||||||
parseId3v1Genre()?.let { listOf(it) } ?: parseId3v2Genre() ?: listOf(this)
|
|
||||||
|
/**
|
||||||
|
* Decodes the genre names from an ID3(v2) constant. See [GENRE_TABLE] for the genre constant map
|
||||||
|
* that Auxio uses.
|
||||||
|
*/
|
||||||
|
fun List<String>.parseId3GenreName() = flatMap { it.parseId3GenreName() }
|
||||||
|
|
||||||
|
|
||||||
private fun String.parseId3v1Genre(): String? =
|
private fun String.parseId3v1Genre(): String? =
|
||||||
when {
|
when {
|
||||||
|
|
|
@ -169,7 +169,7 @@ class Task(context: Context, private val raw: Song.Raw) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun completeAudio(metadata: Metadata) {
|
private fun completeAudio(metadata: Metadata) {
|
||||||
val id3v2Tags = mutableMapOf<String, String>()
|
val id3v2Tags = mutableMapOf<String, List<String>>()
|
||||||
val vorbisTags = mutableMapOf<String, MutableList<String>>()
|
val vorbisTags = mutableMapOf<String, MutableList<String>>()
|
||||||
|
|
||||||
// ExoPlayer only exposes ID3v2 and Vorbis metadata, which constitutes the vast majority
|
// ExoPlayer only exposes ID3v2 and Vorbis metadata, which constitutes the vast majority
|
||||||
|
@ -179,9 +179,9 @@ class Task(context: Context, private val raw: Song.Raw) {
|
||||||
when (val tag = metadata[i]) {
|
when (val tag = metadata[i]) {
|
||||||
is TextInformationFrame -> {
|
is TextInformationFrame -> {
|
||||||
val id = tag.description?.let { "TXXX:${it.sanitize()}" } ?: tag.id.sanitize()
|
val id = tag.description?.let { "TXXX:${it.sanitize()}" } ?: tag.id.sanitize()
|
||||||
val value = tag.value.sanitize()
|
val values = tag.values.map { it.sanitize() }
|
||||||
if (value.isNotEmpty()) {
|
if (values.isNotEmpty() && values.all { it.isNotEmpty() }) {
|
||||||
id3v2Tags[id] = value
|
id3v2Tags[id] = values
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is VorbisComment -> {
|
is VorbisComment -> {
|
||||||
|
@ -207,16 +207,16 @@ class Task(context: Context, private val raw: Song.Raw) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun populateId3v2(tags: Map<String, String>) {
|
private fun populateId3v2(tags: Map<String, List<String>>) {
|
||||||
// (Sort) Title
|
// (Sort) Title
|
||||||
tags["TIT2"]?.let { raw.name = it }
|
tags["TIT2"]?.let { raw.name = it[0] }
|
||||||
tags["TSOT"]?.let { raw.sortName = it }
|
tags["TSOT"]?.let { raw.sortName = it[0] }
|
||||||
|
|
||||||
// Track, as NN/TT
|
// Track, as NN/TT
|
||||||
tags["TRCK"]?.parsePositionNum()?.let { raw.track = it }
|
tags["TRCK"]?.run { get(0).parsePositionNum() }?.let { raw.track = it }
|
||||||
|
|
||||||
// Disc, as NN/TT
|
// Disc, as NN/TT
|
||||||
tags["TPOS"]?.parsePositionNum()?.let { raw.disc = it }
|
tags["TPOS"]?.run { get(0).parsePositionNum() } ?.let { raw.disc = it }
|
||||||
|
|
||||||
// Dates are somewhat complicated, as not only did their semantics change from a flat year
|
// Dates are somewhat complicated, as not only did their semantics change from a flat year
|
||||||
// value in ID3v2.3 to a full ISO-8601 date in ID3v2.4, but there are also a variety of
|
// value in ID3v2.3 to a full ISO-8601 date in ID3v2.4, but there are also a variety of
|
||||||
|
@ -227,22 +227,23 @@ class Task(context: Context, private val raw: Song.Raw) {
|
||||||
// 3. ID3v2.4 Release Date, as it is the second most common date type
|
// 3. ID3v2.4 Release Date, as it is the second most common date type
|
||||||
// 4. ID3v2.3 Original Date, as it is like #1
|
// 4. ID3v2.3 Original Date, as it is like #1
|
||||||
// 5. ID3v2.3 Release Year, as it is the most common date type
|
// 5. ID3v2.3 Release Year, as it is the most common date type
|
||||||
(tags["TDOR"]?.parseTimestamp()
|
(tags["TDOR"]?.run { get(0).parseTimestamp() }
|
||||||
?: tags["TDRC"]?.parseTimestamp() ?: tags["TDRL"]?.parseTimestamp()
|
?: tags["TDRC"]?.run { get(0).parseTimestamp() }
|
||||||
|
?: tags["TDRL"]?.run { get(0).parseTimestamp() }
|
||||||
?: parseId3v23Date(tags))
|
?: parseId3v23Date(tags))
|
||||||
?.let { raw.date = it }
|
?.let { raw.date = it }
|
||||||
|
|
||||||
// (Sort) Album
|
// (Sort) Album
|
||||||
tags["TALB"]?.let { raw.albumName = it }
|
tags["TALB"]?.let { raw.albumName = it[0] }
|
||||||
tags["TSOA"]?.let { raw.albumSortName = it }
|
tags["TSOA"]?.let { raw.albumSortName = it[0] }
|
||||||
|
|
||||||
// (Sort) Artist
|
// (Sort) Artist
|
||||||
tags["TPE1"]?.let { raw.artistName = it }
|
tags["TPE1"]?.let { raw.artistName = it.joinToString() }
|
||||||
tags["TSOP"]?.let { raw.artistSortName = it }
|
tags["TSOP"]?.let { raw.artistSortName = it.joinToString() }
|
||||||
|
|
||||||
// (Sort) Album artist
|
// (Sort) Album artist
|
||||||
tags["TPE2"]?.let { raw.albumArtistName = it }
|
tags["TPE2"]?.let { raw.albumArtistName = it.joinToString() }
|
||||||
tags["TSO2"]?.let { raw.albumArtistSortName = it }
|
tags["TSO2"]?.let { raw.albumArtistSortName = it.joinToString() }
|
||||||
|
|
||||||
// Genre, with the weird ID3 rules.
|
// Genre, with the weird ID3 rules.
|
||||||
tags["TCON"]?.let { raw.genreNames = it.parseId3GenreName() }
|
tags["TCON"]?.let { raw.genreNames = it.parseId3GenreName() }
|
||||||
|
@ -253,18 +254,18 @@ class Task(context: Context, private val raw: Song.Raw) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseId3v23Date(tags: Map<String, String>): Date? {
|
private fun parseId3v23Date(tags: Map<String, List<String>>): Date? {
|
||||||
val year = tags["TORY"]?.toIntOrNull() ?: tags["TYER"]?.toIntOrNull() ?: return null
|
val year = tags["TORY"]?.run { get(0).toIntOrNull() } ?: tags["TYER"]?.run { get(0).toIntOrNull() } ?: return null
|
||||||
|
|
||||||
val mmdd = tags["TDAT"]
|
val tdat = tags["TDAT"]
|
||||||
return if (mmdd != null && mmdd.length == 4 && mmdd.isDigitsOnly()) {
|
return if (tdat != null && tdat[0].length == 4 && tdat[0].isDigitsOnly()) {
|
||||||
val mm = mmdd.substring(0..1).toInt()
|
val mm = tdat[0].substring(0..1).toInt()
|
||||||
val dd = mmdd.substring(2..3).toInt()
|
val dd = tdat[0].substring(2..3).toInt()
|
||||||
|
|
||||||
val hhmi = tags["TIME"]
|
val time = tags["TIME"]
|
||||||
if (hhmi != null && hhmi.length == 4 && hhmi.isDigitsOnly()) {
|
if (time != null && time[0].length == 4 && time[0].isDigitsOnly()) {
|
||||||
val hh = hhmi.substring(0..1).toInt()
|
val hh = time[0].substring(0..1).toInt()
|
||||||
val mi = hhmi.substring(2..3).toInt()
|
val mi = time[0].substring(2..3).toInt()
|
||||||
Date.from(year, mm, dd, hh, mi)
|
Date.from(year, mm, dd, hh, mi)
|
||||||
} else {
|
} else {
|
||||||
Date.from(year, mm, dd)
|
Date.from(year, mm, dd)
|
||||||
|
@ -297,8 +298,8 @@ class Task(context: Context, private val raw: Song.Raw) {
|
||||||
?.let { raw.date = it }
|
?.let { raw.date = it }
|
||||||
|
|
||||||
// (Sort) Album
|
// (Sort) Album
|
||||||
tags["ALBUM"]?.let { raw.albumName = it.joinToString() }
|
tags["ALBUM"]?.let { raw.albumName = it[0] }
|
||||||
tags["ALBUMSORT"]?.let { raw.albumSortName = it.joinToString() }
|
tags["ALBUMSORT"]?.let { raw.albumSortName = it[0] }
|
||||||
|
|
||||||
// (Sort) Artist
|
// (Sort) Artist
|
||||||
tags["ARTIST"]?.let { raw.artistName = it.joinToString() }
|
tags["ARTIST"]?.let { raw.artistName = it.joinToString() }
|
||||||
|
|
16
prebuild.py
16
prebuild.py
|
@ -19,7 +19,7 @@ import re
|
||||||
|
|
||||||
# WARNING: THE EXOPLAYER VERSION MUST BE KEPT IN LOCK-STEP WITH THE FLAC EXTENSION AND
|
# WARNING: THE EXOPLAYER VERSION MUST BE KEPT IN LOCK-STEP WITH THE FLAC EXTENSION AND
|
||||||
# THE GRADLE DEPENDENCY. IF NOT, VERY UNFRIENDLY BUILD FAILURES AND CRASHES MAY ENSUE.
|
# THE GRADLE DEPENDENCY. IF NOT, VERY UNFRIENDLY BUILD FAILURES AND CRASHES MAY ENSUE.
|
||||||
EXO_VERSION = "2.18.1"
|
# EXO_VERSION = "2.18.1"
|
||||||
FLAC_VERSION = "1.3.2"
|
FLAC_VERSION = "1.3.2"
|
||||||
|
|
||||||
FATAL="\033[1;31m"
|
FATAL="\033[1;31m"
|
||||||
|
@ -95,9 +95,9 @@ sh("rm -rf " + exoplayer_path)
|
||||||
sh("rm -rf " + libs_path)
|
sh("rm -rf " + libs_path)
|
||||||
|
|
||||||
print(INFO + "info:" + NC + " cloning exoplayer...")
|
print(INFO + "info:" + NC + " cloning exoplayer...")
|
||||||
sh("git clone https://github.com/google/ExoPlayer.git " + exoplayer_path)
|
sh("git clone https://github.com/OxygenCobalt/ExoPlayer.git " + exoplayer_path)
|
||||||
os.chdir(exoplayer_path)
|
os.chdir(exoplayer_path)
|
||||||
sh("git checkout r" + EXO_VERSION)
|
sh("git checkout auxio")
|
||||||
|
|
||||||
print(INFO + "info:" + NC + " assembling flac extension...")
|
print(INFO + "info:" + NC + " assembling flac extension...")
|
||||||
flac_ext_aar_path = os.path.join(exoplayer_path, "extensions", "flac",
|
flac_ext_aar_path = os.path.join(exoplayer_path, "extensions", "flac",
|
||||||
|
@ -111,9 +111,17 @@ sh(ndk_build_path + " APP_ABI=all -j4")
|
||||||
|
|
||||||
os.chdir(exoplayer_path)
|
os.chdir(exoplayer_path)
|
||||||
sh("./gradlew extension-flac:bundleReleaseAar")
|
sh("./gradlew extension-flac:bundleReleaseAar")
|
||||||
|
|
||||||
|
print(INFO + "info:" + NC + " assembling extractor component...")
|
||||||
|
|
||||||
|
extractor_aar_path = os.path.join(exoplayer_path, "library", "extractor",
|
||||||
|
"buildout", "outputs", "aar", "library-extractor-release.aar")
|
||||||
|
|
||||||
|
sh("./gradlew library-extractor:bundleReleaseAar")
|
||||||
|
|
||||||
os.chdir(start_path)
|
os.chdir(start_path)
|
||||||
sh("mkdir " + libs_path)
|
sh("mkdir " + libs_path)
|
||||||
sh("cp " + flac_ext_aar_path + " " + libs_path)
|
sh("cp " + flac_ext_aar_path + " " + libs_path)
|
||||||
|
sh("cp " + extractor_aar_path + " " + libs_path)
|
||||||
|
|
||||||
print(OK + "success:" + NC + " completed pre-build")
|
print(OK + "success:" + NC + " completed pre-build")
|
||||||
|
|
Loading…
Reference in a new issue