diff --git a/.github/ISSUE_TEMPLATE/bug-crash-report.yml b/.github/ISSUE_TEMPLATE/bug-crash-report.yml
index abc516401..094d55e76 100644
--- a/.github/ISSUE_TEMPLATE/bug-crash-report.yml
+++ b/.github/ISSUE_TEMPLATE/bug-crash-report.yml
@@ -34,6 +34,7 @@ body:
attributes:
label: What android version do you use?
options:
+ - Android 15
- Android 14
- Android 13
- Android 12L
diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
index 9b7aae1cd..59383ae5d 100644
--- a/.github/workflows/android.yml
+++ b/.github/workflows/android.yml
@@ -14,23 +14,25 @@ jobs:
- name: Install ninja-build
run: sudo apt-get install -y ninja-build
- name: Clone repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Clone submodules
run: git submodule update --init --recursive --remote
- name: Set up JDK 17
- uses: actions/setup-java@v3
+ uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: gradle
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- - name: Test app with Gradle
- run: ./gradlew app:testDebug
+ - name: Check formatting with spotless
+ run: ./gradlew spotlessCheck
+ - name: Test musikr with Gradle
+ run: ./gradlew musikr:testDebug
- name: Build debug APK with Gradle
run: ./gradlew app:packageDebug
- name: Upload debug APK artifact
- uses: actions/upload-artifact@v3.1.1
+ uses: actions/upload-artifact@v4
with:
name: Auxio_Canary
path: ./app/build/outputs/apk/debug/app-debug.apk
diff --git a/.gitignore b/.gitignore
index c03b3271b..d461864a6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,4 @@ captures/
.externalNativeBuild
*.iml
.cxx
+.kotlin
diff --git a/.gitmodules b/.gitmodules
index 552a758f6..1e85e13c2 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,8 @@
[submodule "media"]
path = media
url = https://github.com/OxygenCobalt/media.git
+
+[submodule "musikr/src/main/cpp/taglib"]
+ path = musikr/src/main/cpp/taglib
+ url = https://github.com/taglib/taglib.git
+ tag = ee1931b
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8901e55c6..8e08ae917 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,54 @@
# Changelog
+## 4.0.0
+
+#### What's New
+- A total user interface refresh based on the latest Material Design specs
+ - New theme palettes
+ - Improved designs for playback and detail views
+ - New app branding and icon
+ - Refreshed round mode
+ - Less intrusive music loading indicators
+- **Musikr**, a brand new music loading system
+ - Directly accesses user files rather than unreliable media database
+ - Uses faster and more capable native tag parsing
+ - Stores cover data on-device for fast and high-quality access
+ - New interpretation system with many quality-of-life improvements
+- Android 15 support
+
+#### What's Improved
+- Initial music loading is signifigantly faster and less resource intensive
+- Album grouping no longer done with artist
+- MusicBrainz IDs will no longer split albums/artists in less tagged libraries
+- M3U playlist file name is now proposed if one cannot be found within the file
+- Duration is now parsed from certain files that previously could not be parsed
+- ID3v2 tags are now parsed from WAV files
+- NN/TT tracks/discs are now handled in Vorbis
+- Music library will is less likely to fail to respond to updates
+- Hidden audio files can now be loaded
+- Sorting songs by date now uses songs date first, before the earliest album date
+- Added working layouts for small split-screen form factors
+- Added fast scrolling in detail views
+- Added ability to make issues and make feedback e-mails in-app
+
+#### What's Fixed
+- Fixed playback sheet flickering on warm start
+- No longer possible to save a sort with no direction specified
+- Fixed inconsistent corner radii in widget
+- Possibly fixed foreground start music loading failures
+- Fixed playlist view not exiting on deletion
+
+#### What's Changed
+- Date added is now local to when the app discovers the file and will not
+persist long-term
+- Songs with no album are now "Unknown album" rather than folder name
+- Tab layout no longer changes depending on device configuration
+- Round mode is now on by default
+
+#### Dev/Meta
+- No longer using custom logging setup
+- Music loading split off into separate musikr module
+
## 3.6.3
#### What's Fixed
diff --git a/app/NOTICE b/NOTICE
similarity index 100%
rename from app/NOTICE
rename to NOTICE
diff --git a/README.md b/README.md
index 3e99eeb61..94a1126cc 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,8 @@
Auxio
A simple, rational music player for android.
-
-
+
+
@@ -28,14 +28,12 @@ Auxio is a local music player with a fast, reliable UI/UX without the many usele
## Screenshots
-
-
-
-
-
-
-
-
+
+
+
+
+
+
@@ -61,7 +59,7 @@ precise/original dates, sort tags, and more
- Headset autoplay
- Stylish widgets that automatically adapt to their size
- Completely private and offline
-- No rounded album covers (by default)
+- No rounded album covers (if you want them)
## Permissions
@@ -73,23 +71,19 @@ precise/original dates, sort tags, and more
You can support Auxio's development through [my Github Sponsors page](https://github.com/sponsors/OxygenCobalt). Get the ability to prioritize features and have your profile added to the README, Release Changelogs, and even the app itself!
-$16/month supporters:
-
-
-
yrliet
-
-
$8/month supporters:
-
+
+
## Building
-Auxio relies on a custom version of Media3 that enables some extra features. This adds some caveats to the build process:
+Auxio relies on a patched version of Media3 that enables some extra playback features, alongside taglib for metadata
+parsing. This adds some caveats to the build process:
1. `cmake` and `ninja-build` must be installed before building the project.
2. The project uses submodules, so when cloning initially, use `git clone --recurse-submodules` to properly
download the external code.
diff --git a/app/build.gradle b/app/build.gradle
index 66642458e..a5c6bf437 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -2,7 +2,6 @@ plugins {
id "com.android.application"
id "kotlin-android"
id "androidx.navigation.safeargs.kotlin"
- id "com.diffplug.spotless"
id "kotlin-parcelize"
id "dagger.hilt.android.plugin"
id "kotlin-kapt"
@@ -11,21 +10,19 @@ plugins {
}
android {
- compileSdk 34
- // NDK is not used in Auxio explicitly (used in the ffmpeg extension), but we need to specify
- // it here so that binary stripping will work.
- // TODO: Eventually you might just want to start vendoring the FFMpeg extension so the
- // NDK use is unified
- ndkVersion "26.3.11579264"
+ compileSdk 35
+ // Auxio implicitly depends on the native modules, explicitly specify it
+ // here so the libraries are still stripped.
+ ndkVersion ndk_version
namespace "org.oxycblt.auxio"
defaultConfig {
applicationId namespace
- versionName "3.6.3"
- versionCode 53
+ versionName "4.0.0"
+ versionCode 59
- minSdk 24
- targetSdk 34
+ minSdk min_sdk
+ targetSdk target_sdk
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@@ -70,6 +67,7 @@ android {
buildFeatures {
viewBinding true
+ buildConfig true
}
}
@@ -79,16 +77,16 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
- def coroutines_version = '1.7.2'
- implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
- implementation "org.jetbrains.kotlinx:kotlinx-coroutines-guava:$coroutines_version"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-guava:$kotlin_coroutines_version"
// --- SUPPORT ---
// General
- implementation "androidx.core:core-ktx:1.12.0"
- implementation "androidx.appcompat:appcompat:1.6.1"
- implementation "androidx.activity:activity-ktx:1.8.2"
+ implementation "androidx.core:core-ktx:$core_version"
+ implementation "androidx.appcompat:appcompat:1.7.0"
+ implementation "androidx.activity:activity-ktx:1.9.3"
+ // noinspection GradleDependency
implementation "androidx.fragment:fragment-ktx:1.6.2"
// Components
@@ -97,11 +95,13 @@ dependencies {
// TODO: Report this issue and hope for a timely fix
// noinspection GradleDependency
implementation "androidx.recyclerview:recyclerview:1.2.1"
- implementation "androidx.constraintlayout:constraintlayout:2.1.4"
+ implementation "androidx.constraintlayout:constraintlayout:2.2.0"
+ // 1.1.0 upgrades recyclerview to 1.3.0, keep it on 1.0.0
+ //noinspection GradleDependency
implementation "androidx.viewpager2:viewpager2:1.0.0"
// Lifecycle
- def lifecycle_version = "2.7.0"
+ def lifecycle_version = "2.8.7"
implementation "androidx.lifecycle:lifecycle-common:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
@@ -121,25 +121,31 @@ dependencies {
implementation "androidx.preference:preference-ktx:1.2.1"
// Database
- def room_version = '2.6.1'
implementation "androidx.room:room-runtime:$room_version"
ksp "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-ktx:$room_version"
+ // Build
+ coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:$desugaring_version"
+
+ // --- SECOND PARTY ---
+
+ // Musikr
+ implementation project(":musikr")
+
// --- THIRD PARTY ---
// Exoplayer (Vendored)
implementation project(":media-lib-exoplayer")
implementation project(":media-lib-decoder-ffmpeg")
- coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.0.4"
// Image loading
- implementation 'io.coil-kt:coil-base:2.4.0'
+ implementation 'io.coil-kt.coil3:coil-core:3.0.2'
// Material
// TODO: Exactly figure out the conditions that the 1.7.0 ripple bug occurred so you can just
// PR a fix.
- implementation "com.google.android.material:material:1.10.0"
+ implementation "com.google.android.material:material:1.13.0-alpha07"
// Dependency Injection
implementation "com.google.dagger:dagger:$hilt_version"
@@ -158,25 +164,4 @@ dependencies {
// Fuzzy search
implementation 'org.apache.commons:commons-text:1.9'
-
- // Testing
- debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
- testImplementation "junit:junit:4.13.2"
- testImplementation "io.mockk:mockk:1.13.7"
- testImplementation "org.robolectric:robolectric:4.11"
- testImplementation 'androidx.test:core-ktx:1.5.0'
- androidTestImplementation 'androidx.test.ext:junit:1.1.5'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
-}
-
-spotless {
- kotlin {
- target "src/**/*.kt"
- ktfmt().dropboxStyle()
- licenseHeaderFile("NOTICE")
- }
-}
-
-afterEvaluate {
- preDebugBuild.dependsOn spotlessApply
}
diff --git a/app/src/debug/res/values/donottranslate.xml b/app/src/debug/res/values/donottranslate.xml
index dd28633f5..caaf4f429 100644
--- a/app/src/debug/res/values/donottranslate.xml
+++ b/app/src/debug/res/values/donottranslate.xml
@@ -1,4 +1,5 @@
Auxio Debug
+ org.oxycblt.auxio.debug.image.CoverProvider
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 308962b34..2d0499edd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -48,6 +48,7 @@
android:exported="true"
android:icon="@mipmap/ic_launcher"
android:launchMode="singleTask"
+ android:allowCrossUidActivitySwitchFromBelow="false"
android:roundIcon="@mipmap/ic_launcher"
android:windowSoftInputMode="adjustPan">
@@ -92,12 +93,22 @@
android:foregroundServiceType="mediaPlayback"
android:icon="@mipmap/ic_launcher"
android:exported="true"
- android:roundIcon="@mipmap/ic_launcher">
+ android:roundIcon="@mipmap/ic_launcher"
+ tools:ignore="ExportedService">
+
+
+
-
+
+ android:toYDelta="100%p"
+ tools:ignore="PrivateResource" />
diff --git a/app/src/main/res/color/fill_icon_bg.xml b/app/src/main/res/color/fill_icon_bg.xml
deleted file mode 100644
index 916db1e7d..000000000
--- a/app/src/main/res/color/fill_icon_bg.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/color/overlay_stroke.xml b/app/src/main/res/color/overlay_stroke.xml
deleted file mode 100644
index 21337acc3..000000000
--- a/app/src/main/res/color/overlay_stroke.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/color/sel_cover_bg.xml b/app/src/main/res/color/sel_cover_bg.xml
index a35d5a7c5..5858fbefa 100644
--- a/app/src/main/res/color/sel_cover_bg.xml
+++ b/app/src/main/res/color/sel_cover_bg.xml
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/res/color/sel_on_cover_bg.xml b/app/src/main/res/color/sel_on_cover_bg.xml
index dcc29354b..705c80c3d 100644
--- a/app/src/main/res/color/sel_on_cover_bg.xml
+++ b/app/src/main/res/color/sel_on_cover_bg.xml
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/res/color/sel_track.xml b/app/src/main/res/color/sel_track.xml
deleted file mode 100644
index e3a847564..000000000
--- a/app/src/main/res/color/sel_track.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_album_48.xml b/app/src/main/res/drawable/ic_album_48.xml
new file mode 100644
index 000000000..b06d7c0d1
--- /dev/null
+++ b/app/src/main/res/drawable/ic_album_48.xml
@@ -0,0 +1,11 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_artist_48.xml b/app/src/main/res/drawable/ic_artist_48.xml
new file mode 100644
index 000000000..7bd67d49a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_artist_48.xml
@@ -0,0 +1,11 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_email_24.xml b/app/src/main/res/drawable/ic_email_24.xml
new file mode 100644
index 000000000..a50fe012a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_email_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_feature_request_24.xml b/app/src/main/res/drawable/ic_feature_request_24.xml
new file mode 100644
index 000000000..1765191ed
--- /dev/null
+++ b/app/src/main/res/drawable/ic_feature_request_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_genre_48.xml b/app/src/main/res/drawable/ic_genre_48.xml
new file mode 100644
index 000000000..27851f420
--- /dev/null
+++ b/app/src/main/res/drawable/ic_genre_48.xml
@@ -0,0 +1,11 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
index c16ace719..2011ea038 100644
--- a/app/src/main/res/drawable/ic_launcher_background.xml
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -6,8 +6,35 @@
android:viewportHeight="108">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml
index 7a0184ca7..3a7b2746b 100644
--- a/app/src/main/res/drawable/ic_launcher_foreground.xml
+++ b/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -1,9 +1,9 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_pause_48.xml b/app/src/main/res/drawable/ic_pause_48.xml
new file mode 100644
index 000000000..76e9b9f6e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_pause_48.xml
@@ -0,0 +1,11 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_play_48.xml b/app/src/main/res/drawable/ic_play_48.xml
new file mode 100644
index 000000000..d163cd960
--- /dev/null
+++ b/app/src/main/res/drawable/ic_play_48.xml
@@ -0,0 +1,11 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_playlist_48.xml b/app/src/main/res/drawable/ic_playlist_48.xml
new file mode 100644
index 000000000..780039011
--- /dev/null
+++ b/app/src/main/res/drawable/ic_playlist_48.xml
@@ -0,0 +1,11 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_repeat_on_24.xml b/app/src/main/res/drawable/ic_repeat_on_24.xml
index 592f3d843..256407c6e 100644
--- a/app/src/main/res/drawable/ic_repeat_on_24.xml
+++ b/app/src/main/res/drawable/ic_repeat_on_24.xml
@@ -1,10 +1,10 @@
-
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorPrimary">
+
diff --git a/app/src/main/res/drawable/ic_repeat_one_24.xml b/app/src/main/res/drawable/ic_repeat_one_24.xml
index 307b27ecf..124138ff0 100644
--- a/app/src/main/res/drawable/ic_repeat_one_24.xml
+++ b/app/src/main/res/drawable/ic_repeat_one_24.xml
@@ -1,10 +1,10 @@
-
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorPrimary">
+
diff --git a/app/src/main/res/drawable/ic_shuffle_on_24.xml b/app/src/main/res/drawable/ic_shuffle_on_24.xml
index 52d82252f..a6dcd0ec4 100644
--- a/app/src/main/res/drawable/ic_shuffle_on_24.xml
+++ b/app/src/main/res/drawable/ic_shuffle_on_24.xml
@@ -1,11 +1,10 @@
-
-
+ android:viewportHeight="960"
+ android:tint="?attr/colorPrimary">
+
diff --git a/app/src/main/res/drawable/ic_skip_next_40.xml b/app/src/main/res/drawable/ic_skip_next_40.xml
new file mode 100644
index 000000000..28ca513fc
--- /dev/null
+++ b/app/src/main/res/drawable/ic_skip_next_40.xml
@@ -0,0 +1,11 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_skip_prev_40.xml b/app/src/main/res/drawable/ic_skip_prev_40.xml
new file mode 100644
index 000000000..d21330db7
--- /dev/null
+++ b/app/src/main/res/drawable/ic_skip_prev_40.xml
@@ -0,0 +1,11 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_song_48.xml b/app/src/main/res/drawable/ic_song_48.xml
new file mode 100644
index 000000000..0da40bce7
--- /dev/null
+++ b/app/src/main/res/drawable/ic_song_48.xml
@@ -0,0 +1,11 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_splash_anim.xml b/app/src/main/res/drawable/ic_splash_anim.xml
index d71c04457..bf2bb92d2 100644
--- a/app/src/main/res/drawable/ic_splash_anim.xml
+++ b/app/src/main/res/drawable/ic_splash_anim.xml
@@ -1,28 +1,97 @@
-
-
-
-
+ android:width="432dp"
+ android:height="432dp"
+ android:viewportWidth="432"
+ android:viewportHeight="432">
+ android:name="bg"
+ android:pivotX="56"
+ android:pivotY="56">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -30,37 +99,67 @@
-
-
-
-
+
-
+
+ android:valueType="floatType"
+ tools:ignore="PrivateResource" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/sel_playing_state_48.xml b/app/src/main/res/drawable/sel_playing_state_48.xml
new file mode 100644
index 000000000..d9b283a03
--- /dev/null
+++ b/app/src/main/res/drawable/sel_playing_state_48.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ui_popup.xml b/app/src/main/res/drawable/ui_popup.xml
new file mode 100644
index 000000000..14e0b82ad
--- /dev/null
+++ b/app/src/main/res/drawable/ui_popup.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ui_remote_fab_container_paused.xml b/app/src/main/res/drawable/ui_remote_fab_container_paused.xml
index 82362866f..7e9d09828 100644
--- a/app/src/main/res/drawable/ui_remote_fab_container_paused.xml
+++ b/app/src/main/res/drawable/ui_remote_fab_container_paused.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ui_remote_fab_container_playing.xml b/app/src/main/res/drawable/ui_remote_fab_container_playing.xml
index f5e00b30c..9090ea9b3 100644
--- a/app/src/main/res/drawable/ui_remote_fab_container_playing.xml
+++ b/app/src/main/res/drawable/ui_remote_fab_container_playing.xml
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ui_scroll_thumb.xml b/app/src/main/res/drawable/ui_scroll_thumb.xml
index 5802c85a9..19a93b84c 100644
--- a/app/src/main/res/drawable/ui_scroll_thumb.xml
+++ b/app/src/main/res/drawable/ui_scroll_thumb.xml
@@ -4,13 +4,7 @@
android:tint="?attr/colorSecondary">
-
+ android:width="4dp" />
\ No newline at end of file
diff --git a/app/src/main/res/layout-h360dp/fragment_detail.xml b/app/src/main/res/layout-h360dp/fragment_detail.xml
new file mode 100644
index 000000000..12bb1598b
--- /dev/null
+++ b/app/src/main/res/layout-h360dp/fragment_detail.xml
@@ -0,0 +1,229 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-sw600dp/fragment_playback_panel.xml b/app/src/main/res/layout-h360dp/fragment_playback_panel.xml
similarity index 64%
rename from app/src/main/res/layout-sw600dp/fragment_playback_panel.xml
rename to app/src/main/res/layout-h360dp/fragment_playback_panel.xml
index 21152be5a..b73de1fd9 100644
--- a/app/src/main/res/layout-sw600dp/fragment_playback_panel.xml
+++ b/app/src/main/res/layout-h360dp/fragment_playback_panel.xml
@@ -12,66 +12,82 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:menu="@menu/toolbar_playback"
+ app:titleCentered="true"
+ app:subtitleCentered="true"
+ app:titleTextAppearance="@style/TextAppearance.Auxio.LabelLarge"
+ app:subtitleTextAppearance="@style/TextAppearance.Auxio.BodySmall"
app:navigationIcon="@drawable/ic_down_24"
app:title="@string/lbl_playback"
tools:subtitle="@string/lbl_all_songs" />
+
-
-
-
-
-
-
+ app:layout_constraintTop_toBottomOf="@+id/playback_toolbar"
+ app:layout_constraintVertical_chainStyle="packed" />
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -79,10 +95,12 @@
android:id="@+id/playback_controls_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/spacing_medium"
- android:layout_marginEnd="@dimen/spacing_medium"
+ android:layout_marginStart="@dimen/spacing_mid_medium"
+ android:layout_marginEnd="@dimen/spacing_mid_medium"
android:layout_marginBottom="@dimen/spacing_medium"
- app:layout_constraintBottom_toBottomOf="parent">
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent">
+ tools:icon="@drawable/ic_play_24" />
+
+
diff --git a/app/src/main/res/layout-h480dp/fragment_detail.xml b/app/src/main/res/layout-h480dp/fragment_detail.xml
new file mode 100644
index 000000000..a14df91c8
--- /dev/null
+++ b/app/src/main/res/layout-h480dp/fragment_detail.xml
@@ -0,0 +1,212 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-h480dp/fragment_playback_panel.xml b/app/src/main/res/layout-h480dp/fragment_playback_panel.xml
index 4157231ac..bcf2e70a8 100644
--- a/app/src/main/res/layout-h480dp/fragment_playback_panel.xml
+++ b/app/src/main/res/layout-h480dp/fragment_playback_panel.xml
@@ -13,7 +13,11 @@
app:layout_constraintTop_toTopOf="parent"
app:menu="@menu/toolbar_playback"
app:navigationIcon="@drawable/ic_down_24"
+ app:subtitleCentered="true"
+ app:subtitleTextAppearance="@style/TextAppearance.Auxio.BodySmall"
app:title="@string/lbl_playback"
+ app:titleCentered="true"
+ app:titleTextAppearance="@style/TextAppearance.Auxio.LabelLarge"
tools:subtitle="@string/lbl_all_songs" />
+ app:layout_constraintTop_toBottomOf="@+id/playback_toolbar"
+ app:layout_constraintVertical_chainStyle="packed" />
-
-
-
-
-
+ app:layout_constraintTop_toBottomOf="@+id/playback_toolbar"
+ app:layout_constraintVertical_bias="1.0"
+ app:layout_constraintVertical_chainStyle="packed">
+
+
+
+
+
+
+
+
+
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/playback_info_container" />
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintWidth_max="400dp">
-
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout-h480dp/item_playback_song.xml b/app/src/main/res/layout-h480dp/item_playback_song.xml
deleted file mode 100644
index 9ce0bcf47..000000000
--- a/app/src/main/res/layout-h480dp/item_playback_song.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout-h600dp/item_detail_header.xml b/app/src/main/res/layout-h600dp/item_detail_header.xml
deleted file mode 100644
index cdaf0e484..000000000
--- a/app/src/main/res/layout-h600dp/item_detail_header.xml
+++ /dev/null
@@ -1,88 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout-land/item_detail_header.xml b/app/src/main/res/layout-land/item_detail_header.xml
deleted file mode 100644
index 432f6dc3e..000000000
--- a/app/src/main/res/layout-land/item_detail_header.xml
+++ /dev/null
@@ -1,103 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout-sw600dp/fragment_detail.xml b/app/src/main/res/layout-sw600dp/fragment_detail.xml
new file mode 100644
index 000000000..e2eab4bb5
--- /dev/null
+++ b/app/src/main/res/layout-sw600dp/fragment_detail.xml
@@ -0,0 +1,227 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-sw600dp/item_detail_header.xml b/app/src/main/res/layout-sw600dp/item_detail_header.xml
deleted file mode 100644
index 4b354bc58..000000000
--- a/app/src/main/res/layout-sw600dp/item_detail_header.xml
+++ /dev/null
@@ -1,105 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout-sw600dp/item_playback_song.xml b/app/src/main/res/layout-sw600dp/item_playback_song.xml
deleted file mode 100644
index 9ce0bcf47..000000000
--- a/app/src/main/res/layout-sw600dp/item_playback_song.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout-sw840dp/item_detail_header.xml b/app/src/main/res/layout-sw840dp/item_detail_header.xml
deleted file mode 100644
index d66af44bb..000000000
--- a/app/src/main/res/layout-sw840dp/item_detail_header.xml
+++ /dev/null
@@ -1,100 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout-w600dp/fragment_detail.xml b/app/src/main/res/layout-w600dp/fragment_detail.xml
new file mode 100644
index 000000000..12bb1598b
--- /dev/null
+++ b/app/src/main/res/layout-w600dp/fragment_detail.xml
@@ -0,0 +1,229 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-w600dp-land/fragment_main.xml b/app/src/main/res/layout-w720dp/fragment_main.xml
similarity index 53%
rename from app/src/main/res/layout-w600dp-land/fragment_main.xml
rename to app/src/main/res/layout-w720dp/fragment_main.xml
index 15beea699..c7cd805f2 100644
--- a/app/src/main/res/layout-w600dp-land/fragment_main.xml
+++ b/app/src/main/res/layout-w720dp/fragment_main.xml
@@ -7,22 +7,77 @@
android:layout_height="match_parent"
android:background="?attr/colorSurface">
-
+ app:layout_behavior="org.oxycblt.auxio.ui.BottomSheetContentBehavior">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -71,10 +127,12 @@
android:layout_weight="1" />
-
-
+
diff --git a/app/src/main/res/layout/dialog_error_details.xml b/app/src/main/res/layout/dialog_error_details.xml
index e19d930ab..bd5317a53 100644
--- a/app/src/main/res/layout/dialog_error_details.xml
+++ b/app/src/main/res/layout/dialog_error_details.xml
@@ -40,7 +40,7 @@
android:hyphenationFrequency="none"
android:paddingStart="@dimen/spacing_medium"
android:paddingTop="@dimen/spacing_medium"
- android:paddingEnd="@dimen/size_copy_button"
+ android:paddingEnd="@dimen/size_touchable_large"
android:paddingBottom="@dimen/spacing_medium"
android:typeface="monospace"
tools:text="Stack trace here" />
diff --git a/app/src/main/res/layout/dialog_menu.xml b/app/src/main/res/layout/dialog_menu.xml
index 1a5a7a605..922c6df18 100644
--- a/app/src/main/res/layout/dialog_menu.xml
+++ b/app/src/main/res/layout/dialog_menu.xml
@@ -24,7 +24,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_music_locations.xml b/app/src/main/res/layout/dialog_music_locations.xml
new file mode 100644
index 000000000..8f015dec3
--- /dev/null
+++ b/app/src/main/res/layout/dialog_music_locations.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_pre_amp.xml b/app/src/main/res/layout/dialog_pre_amp.xml
index 24ca0a85e..9ad5a99b9 100644
--- a/app/src/main/res/layout/dialog_pre_amp.xml
+++ b/app/src/main/res/layout/dialog_pre_amp.xml
@@ -40,7 +40,7 @@
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_large"
android:gravity="center"
- android:minWidth="@dimen/size_pre_amp_ticker"
+ android:minWidth="@dimen/size_touchable_medium"
android:textAppearance="@style/TextAppearance.Auxio.BodySmall"
app:layout_constraintBottom_toBottomOf="@+id/with_tags_slider"
app:layout_constraintEnd_toEndOf="parent"
@@ -77,7 +77,7 @@
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_large"
android:gravity="center"
- android:minWidth="@dimen/size_pre_amp_ticker"
+ android:minWidth="@dimen/size_touchable_medium"
android:textAppearance="@style/TextAppearance.Auxio.BodySmall"
app:layout_constraintBottom_toBottomOf="@+id/without_tags_slider"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/app/src/main/res/layout/fragment_about.xml b/app/src/main/res/layout/fragment_about.xml
index 787fcb546..d574f8e79 100644
--- a/app/src/main/res/layout/fragment_about.xml
+++ b/app/src/main/res/layout/fragment_about.xml
@@ -204,6 +204,8 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+ android:layout_height="match_parent"
+ app:expandedTitleMargin="0dp"
+ app:forceApplySystemWindowInsetTop="false"
+ app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
+ app:titleEnabled="false"
+ app:toolbarId="@+id/detail_toolbar">
-
+ android:layout_gravity="bottom"
+ android:paddingStart="@dimen/spacing_medium"
+ android:paddingTop="?attr/actionBarSize"
+ android:paddingEnd="@dimen/spacing_medium"
+ app:layout_collapseMode="parallax"
+ app:layout_collapseParallaxMultiplier="0.85">
-
+
+
+
+
+
+
+
+
+
+
+
+
+ android:layout_gravity="bottom"
+ app:layout_collapseMode="pin" />
-
+ app:layout_collapseMode="pin">
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ tools:listitem="@layout/item_song" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml
index c92bab632..314aaae15 100644
--- a/app/src/main/res/layout/fragment_home.xml
+++ b/app/src/main/res/layout/fragment_home.xml
@@ -65,119 +65,36 @@
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
tools:layout="@layout/fragment_home_list" />
-
+ android:layout_gravity="top|start"
+ android:transitionGroup="true"
+ android:visibility="invisible"
+ android:layout_margin="@dimen/spacing_medium">
-
+ android:layout_margin="@dimen/spacing_tiny" />
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/fragment_home_list.xml b/app/src/main/res/layout/fragment_home_list.xml
index 138fef24c..cb4102b30 100644
--- a/app/src/main/res/layout/fragment_home_list.xml
+++ b/app/src/main/res/layout/fragment_home_list.xml
@@ -1,8 +1,57 @@
-
+ android:layout_height="match_parent">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml
index a4c2804a9..9c4de0ad4 100644
--- a/app/src/main/res/layout/fragment_main.xml
+++ b/app/src/main/res/layout/fragment_main.xml
@@ -8,26 +8,75 @@
android:background="?attr/colorSurface"
android:transitionGroup="true">
-
+ app:layout_behavior="org.oxycblt.auxio.ui.BottomSheetContentBehavior">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:layout_height="match_parent"
+ android:alpha="0"
+ android:background="?attr/colorSurfaceContainerLow" />
+ android:layout_height="match_parent"
+ android:visibility="invisible" />
diff --git a/app/src/main/res/layout/fragment_playback_panel.xml b/app/src/main/res/layout/fragment_playback_panel.xml
index 050d2b86b..fa845fb33 100644
--- a/app/src/main/res/layout/fragment_playback_panel.xml
+++ b/app/src/main/res/layout/fragment_playback_panel.xml
@@ -13,22 +13,25 @@
app:layout_constraintTop_toTopOf="parent"
app:menu="@menu/toolbar_playback"
app:navigationIcon="@drawable/ic_down_24"
+ app:subtitleCentered="true"
+ app:subtitleTextAppearance="@style/TextAppearance.Auxio.BodySmall"
app:title="@string/lbl_playback"
+ app:titleCentered="true"
+ app:titleTextAppearance="@style/TextAppearance.Auxio.LabelLarge"
tools:subtitle="@string/lbl_all_songs" />
-
+ app:layout_constraintTop_toBottomOf="@+id/playback_toolbar" />
-
-
-
-
-
+ app:layout_constraintHorizontal_bias="0.5"
+ app:layout_constraintStart_toEndOf="@+id/playback_cover"
+ app:layout_constraintTop_toBottomOf="@+id/playback_info_container">
-
diff --git a/app/src/main/res/layout/item_detail_header.xml b/app/src/main/res/layout/item_detail_header.xml
deleted file mode 100644
index 0ea6a4111..000000000
--- a/app/src/main/res/layout/item_detail_header.xml
+++ /dev/null
@@ -1,90 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/item_disc_header.xml b/app/src/main/res/layout/item_disc_header.xml
index dbcc0e4f6..9559b913b 100644
--- a/app/src/main/res/layout/item_disc_header.xml
+++ b/app/src/main/res/layout/item_disc_header.xml
@@ -6,57 +6,49 @@
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="@dimen/spacing_medium"
- android:paddingTop="@dimen/spacing_mid_medium"
+ android:paddingTop="@dimen/spacing_small"
android:paddingEnd="@dimen/spacing_medium"
- android:paddingBottom="@dimen/spacing_mid_medium">
+ android:paddingBottom="@dimen/spacing_small">
-
-
-
-
-
+ app:tint="@color/sel_on_cover_bg"
+ tools:ignore="ContentDescription" />
+ tools:text="Part 1" />
diff --git a/app/src/main/res/layout/item_music_dir.xml b/app/src/main/res/layout/item_music_location.xml
similarity index 89%
rename from app/src/main/res/layout/item_music_dir.xml
rename to app/src/main/res/layout/item_music_location.xml
index ae1082de6..45d427155 100644
--- a/app/src/main/res/layout/item_music_dir.xml
+++ b/app/src/main/res/layout/item_music_location.xml
@@ -10,7 +10,7 @@
android:paddingBottom="@dimen/spacing_small">
diff --git a/app/src/main/res/layout/item_new_music_location.xml b/app/src/main/res/layout/item_new_music_location.xml
new file mode 100644
index 000000000..2fd5338ed
--- /dev/null
+++ b/app/src/main/res/layout/item_new_music_location.xml
@@ -0,0 +1,17 @@
+
+
diff --git a/app/src/main/res/layout/item_playback_song.xml b/app/src/main/res/layout/item_playback_song.xml
deleted file mode 100644
index 3e8c0c6a1..000000000
--- a/app/src/main/res/layout/item_playback_song.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/view_scroll_thumb.xml b/app/src/main/res/layout/view_scroll_thumb.xml
new file mode 100644
index 000000000..774195cab
--- /dev/null
+++ b/app/src/main/res/layout/view_scroll_thumb.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/widget_default.xml b/app/src/main/res/layout/widget_default.xml
index a51b36977..7c6bfe598 100644
--- a/app/src/main/res/layout/widget_default.xml
+++ b/app/src/main/res/layout/widget_default.xml
@@ -4,7 +4,8 @@
android:id="@android:id/background"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="?attr/colorSurface"
+ android:background="@drawable/ui_widget_bg_sharp"
+ android:backgroundTint="?attr/colorSurface"
android:theme="@style/Theme.Auxio.Widget"
tools:ignore="Overdraw">
diff --git a/app/src/main/res/layout/widget_docked_thin.xml b/app/src/main/res/layout/widget_docked_thin.xml
index 086013dcc..9347007d2 100644
--- a/app/src/main/res/layout/widget_docked_thin.xml
+++ b/app/src/main/res/layout/widget_docked_thin.xml
@@ -69,8 +69,8 @@
@@ -82,8 +82,8 @@
@@ -95,8 +95,8 @@
diff --git a/app/src/main/res/layout/widget_docked_wide.xml b/app/src/main/res/layout/widget_docked_wide.xml
index d344799f4..646348f70 100644
--- a/app/src/main/res/layout/widget_docked_wide.xml
+++ b/app/src/main/res/layout/widget_docked_wide.xml
@@ -56,8 +56,8 @@
@@ -69,8 +69,8 @@
@@ -82,8 +82,8 @@
@@ -95,8 +95,8 @@
@@ -108,8 +108,8 @@
diff --git a/app/src/main/res/layout/widget_pane_thin.xml b/app/src/main/res/layout/widget_pane_thin.xml
index 2b2361971..8b5be1f4e 100644
--- a/app/src/main/res/layout/widget_pane_thin.xml
+++ b/app/src/main/res/layout/widget_pane_thin.xml
@@ -80,8 +80,8 @@
@@ -93,8 +93,8 @@
@@ -106,8 +106,8 @@
diff --git a/app/src/main/res/layout/widget_pane_wide.xml b/app/src/main/res/layout/widget_pane_wide.xml
index 07c693564..14c671bf7 100644
--- a/app/src/main/res/layout/widget_pane_wide.xml
+++ b/app/src/main/res/layout/widget_pane_wide.xml
@@ -82,8 +82,8 @@
@@ -95,8 +95,8 @@
@@ -108,8 +108,8 @@
@@ -121,8 +121,8 @@
@@ -134,8 +134,8 @@
diff --git a/app/src/main/res/layout/widget_wafer_thin.xml b/app/src/main/res/layout/widget_wafer_thin.xml
index db12288cf..385a39fe7 100644
--- a/app/src/main/res/layout/widget_wafer_thin.xml
+++ b/app/src/main/res/layout/widget_wafer_thin.xml
@@ -40,8 +40,8 @@
@@ -55,8 +55,8 @@
@@ -73,8 +73,8 @@
diff --git a/app/src/main/res/layout/widget_wafer_wide.xml b/app/src/main/res/layout/widget_wafer_wide.xml
index d773fc5f0..bd922107a 100644
--- a/app/src/main/res/layout/widget_wafer_wide.xml
+++ b/app/src/main/res/layout/widget_wafer_wide.xml
@@ -39,8 +39,8 @@
@@ -60,8 +60,8 @@
@@ -75,8 +75,8 @@
@@ -93,8 +93,8 @@
@@ -114,8 +114,8 @@
diff --git a/app/src/main/res/menu/toolbar_playback.xml b/app/src/main/res/menu/toolbar_playback.xml
index 27791e20a..d1a524ba5 100644
--- a/app/src/main/res/menu/toolbar_playback.xml
+++ b/app/src/main/res/menu/toolbar_playback.xml
@@ -6,8 +6,4 @@
android:icon="@drawable/ic_config_24"
android:title="@string/lbl_equalizer"
app:showAsAction="always" />
-
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
index 6f3b755bf..50ec88623 100644
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -1,6 +1,6 @@
-
-
-
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp
index f22c5e0b4..c9676da47 100644
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp
index 2e4171a83..8c4f734a8 100644
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
index 04aa61f98..ba9db7c78 100644
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
index 0d56eed4f..9e519c6b3 100644
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
index 389bd1376..d21d9ff65 100644
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/navigation/inner.xml b/app/src/main/res/navigation/inner.xml
index ff6f18d10..aa10ee2f9 100644
--- a/app/src/main/res/navigation/inner.xml
+++ b/app/src/main/res/navigation/inner.xml
@@ -87,6 +87,9 @@
+
+ app:argType="org.oxycblt.musikr.Music$UID" />
@@ -256,7 +259,7 @@
tools:layout="@layout/fragment_detail">
+ app:argType="org.oxycblt.musikr.Music$UID" />
@@ -305,7 +308,7 @@
tools:layout="@layout/fragment_detail">
+ app:argType="org.oxycblt.musikr.Music$UID" />
@@ -354,7 +357,7 @@
tools:layout="@layout/fragment_detail">
+ app:argType="org.oxycblt.musikr.Music$UID" />
@@ -412,7 +415,7 @@
tools:layout="@layout/dialog_playlist_name">
+ app:argType="org.oxycblt.musikr.Music$UID[]" />
+ app:argType="org.oxycblt.musikr.Music$UID" />
+ app:argType="org.oxycblt.musikr.Music$UID[]" />
@@ -449,7 +452,7 @@
tools:layout="@layout/dialog_playlist_name">
+ app:argType="org.oxycblt.musikr.Music$UID" />
+
+
\ No newline at end of file
diff --git a/app/src/main/res/navigation/outer.xml b/app/src/main/res/navigation/outer.xml
index 63adfa466..0d15cbc8a 100644
--- a/app/src/main/res/navigation/outer.xml
+++ b/app/src/main/res/navigation/outer.xml
@@ -34,8 +34,8 @@
android:id="@+id/audio_peferences"
app:destination="@id/audio_preferences_fragment" />
+ android:id="@+id/music_locations_settings"
+ app:destination="@id/music_locations_dialog" />
+ android:id="@+id/music_locations_dialog"
+ android:name="org.oxycblt.auxio.music.locations.MusicSourcesDialog"
+ android:label="music_locations_dialog"
+ tools:layout="@layout/dialog_music_locations" />
تشغيل من البوم
تشغيل من فنان
طابور
- شغل الاغنية التالية
+ شغل التالي
أضف إلى الطابور
تمت الإضافة إلى الطابور
أذهب إلى الفنان
أذهب إلى الالبوم
- تم حفظ الحالة
أضف
حفظ
- لا مجلد
+ لا مجلد
حول
الإصدار
- عرض على الكود في Github
+ كود البرنامج
التراخيص
- تمت برمجة التطبيق من قبل الكساندر كابيهارت
+ الكساندر كابيهارت
الإعدادات
- المظهر
+ المظهر والاحساس
السمة
تلقائي
فاتح
مظلم
نظام الألوان
السمة السوداء
- استخدام اللون الاسود القاتم في الوضع المظلم
+ استخدام سمة اللون الاسود النقي
عرض
تبويتات المكتبة
تغيير ظهور وترتيب تبويتات المكتبة
- اغلفة البوم مدورة
- جعل اغلفة الابومات ذات زوايا مدورة
- استخدام نشاط بديل للإشعار
+ وضع دائري
+ تفعيل الزوايا المدورة لحدات الواجهه الاضافية (يتطلب اغلفة الالبوم ان تكون مدورة)
+ فعالية تنبيه مخصصة
صوتيات
صخب الصوت
تفضيل المقطع
تفضيل الالبوم
ديناميكي
- سلوك
- عند اختيار اغنية
+ تخصيص
+ عند التشغيل من المكتبة
تذكر الخلط
إبقاء وضع الخلط عند تشغيل اغنية جديدة
تشجيع قبل التخطي للخلف
@@ -71,16 +70,14 @@
ايقاف مؤقت عند التكرار
ايقاف مؤقت عند تكرار تشغيل اغنية
محتوى
- حفظ حالة التشغيل
- حفظ حالة التشغيل الحالية الآن
لم يتم ايجاد موسيقى
فشل تحميل الموسيقى
اوكسيو يحتاج إلى صلاحيات لقراءة للاطلاع على مكتبتك للموسيقى
لا يوجد تطبيق لفتح هذا الرابط
- هذا المجلد غير مدعوم
+ هذا المجلد غير مدعوم
- البحث في مكتبتك…
+ ابحث في مكتبتك…
المقطع %d
تشغيل او ايقاف مؤقت
@@ -93,7 +90,7 @@
نقل اغنية من الطابور
تحريك التبويت
إزالة كلمة البحث
- إزالة المجلد المستبعد
+ إزالة المجلد المستبعد
ايقونة اوكسيو
غلاف الالبوم
غلاف الالبوم لـ %s
@@ -139,46 +136,44 @@
- %d ألبومات
- %d ألبومات
- MixMix
+ مكس دي جي
الغاء
التنسيق
الحجم
إحصائيات المكتبة
معدل البت
تجميع مباشر
- تجميعات
+ تجميعات ريمكس
خصائص الاغنية
معدل العينة
عشوائي
- تشغيل كل الاغاني بشكل عشوائي
+ تشغيل عشوائي للكل
حسنا
- اعادة الحالة
تنازلي
عرض الخصائص
- مسح الحالة
مباشر
اعادة ضبط
- يتم تحمل مكتبتك …
+ يتم تحمل مكتبتك الموسيقية …
النوع
- مراقبة تغييرات في مكتبتك
+ مراقبة تغييرات في مكتبتك الموسيقية…
مراقبة مكتبة الموسيقة
تحميل الموسيقى
المعادل
- منفصل
+ فرديات
فردي
- EP
- EPs
- Mixtape
- تسجيل صوتي
- Mixtapes
+ أغنية مطولة
+ أغاني مطولة
+ مكس
+ موسيقى تصويرية
+ مكسات
RemixesRemixes
- الموسيقى التصويرية
+ موسيقات تصويرية
البوم مباشر
- ريمكس
- مؤاثرات مباشرة
- مؤاثرات ريمكس
+ البوم ريمكس
+ أغنية مطولة مباشرة
+ اغنية مطولة ريمكس
بث مباشر فردي
- ريمكس منفصل
+ ريمكس فردي
تجميعات
مدة
عدد الأغاني
@@ -186,9 +181,9 @@
مسار
تاريخ الاضافة
تحميل الموسيقى
- التحويل البرمجي
- مزيج
- Wiki
+ تجميع
+ مكسات دي جي
+ ويكي
أغنية
أتجاه
أختيار
@@ -209,8 +204,66 @@
تم حذف قائمة التشغيل
تقرير
قائمة تشغيل جديدة
- معلومات خاطئة
+ معلومات الخطأ
تم إعادة تسمية قائمة التشغيل
إعادة تسمية قائمة التشغيل
يظهر على
+ إختيار المجلد
+ مقاطع تجريبية
+ استيراد
+ تصدير
+ تصدير قائمة تشغيل
+ ملاحظات
+ سجل مشكلة على GitHub
+ أرسل ايميل
+ تبرع
+ تخطى الى التالي
+ عند التشغيل من خصائص العنصر
+ تشغيل من العناصر المعروظة
+ تشغيل من النوع
+ تحكم بكيفية تحميل الاموسيقى والصور
+ التحميل التلقائي
+ تجاوز الملفات الصوتية الغير موسيقية, مثل البودكاست
+ تعريف الفواصل التي تدل على علامات متعددة
+ فاصلة منقوطة(;)
+ نمط المسار
+ ألمزيد
+ مطلق
+ الداعمين
+ فعالية شريط تشغيل مخصصة
+ العطف(&)
+ قائمة تشغيل مستوردة
+ فواصل متعددة القيم
+ تغيير سمات والوان البرنامج
+ تجاوز غير الموسيقى
+ مقطع تجريبي
+ الاغاني الخاصة بك ستضهر هنا.
+ الفنانين الخاصين بك سيضهرون هنا.
+ تعديل تثبيت مستوى الصوت للاغنية
+ تعديل تثبيت مستوى الصوت للالبوم
+ تم استيراد قائمة التشغيل
+ تم تصدير قائمة التشغيل
+ تبرع للمشروع لتتم اضافة اسمك هنا!
+ وضع التكرار
+ تشغيل اغنية محددة
+ زائد(+)
+ استيراد قائمة تشغيل
+ قائمة تشغيل فارغة
+ تخصيص ادوات تحكم الواجهه و سلوكها
+ المسار
+ الترتيب الذكي
+ الشارحة(/)
+ بدء التشغيل
+ نسبي
+ إستخدام مسارات متوافقة مع نافذة
+ تشغيل Auxio بأستخدام الحالة المحفوظه مسبقا. اذا لم تتوفر حالة, كل الاغاني ستشغل بشكل عشوائي.\n\nتحذير:كن حذرا بالتحكم بهذة الخدمة, اذا اغلقتها وفتحتها مجددا,قد يتوقف البرنامج عن العمل.
+ المؤلف
+ الموسيقى
+ قوائم التشغيل الخاصة بك ستضهر هنا.
+ السلوك
+ فاصلة (,)
+ الالبومات الخاصة بك ستضهر هنا.
+ الفئات الخاصة بك ستضهر هنا.
+ اعادة تحميل مكتبة الموسيقى عند حصول تغيير(يتطلب تنبيه ثابت)
+ تحذير: استخدام هذا الاعداد قد ينتج عنه ان يتم تفسير بعض العلامات بشكل خاطئ مثل ان تحتوي على قيم متعددة. يمكن ان يتم حل هذا بتقديم الفواصل الغير مرغوبةبالشارحة الخلفية(\\).
\ No newline at end of file
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 51bb12461..9d6db7b73 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -1,6 +1,6 @@
- مشغّل موسيقى بسيط ومعقول للأندرويد
+ مشغّل موسيقى بسيط ومعقول للأندرويد.
مراقبة مكتبة الموسيقى
إعادة المحاولة
منح
@@ -19,8 +19,7 @@
إضافة لقائمة التشغيل
إعادة ضبط
إضافة مجلد
- تم حفظ الحالة
- يتم تحميل مكتبتك الموسيقية
+ جارِ تحميل مكتبتك الموسيقية…
أضيفت للطابور
تم إنشاء قائمة التشغيل
فنانون
@@ -39,7 +38,7 @@
تجميعة
تجميعة مباشرة
مباشر
- ظهر فيه
+ ظهر على
فنان
ريميكسات
نوع
@@ -71,12 +70,268 @@
الحجم
معدل البِت
موافق
- تم حذف الحالة
- تمت استعادة الحالة
حول
الإصدار
شفرة المصدر
الموسوعة
التراخيص
إحصائيات المكتبة
-
\ No newline at end of file
+ إيقاف التشغيل
+ فشل تحميل الموسيقى
+ انقل علامة التبويب هذه
+ مسح استعلام البحث
+ صورة قائمة التشغيل لـ %s
+ نوع غير معروف
+ لا قرص
+ صوت MPEG-4
+ أوغ الصوت
+ صوت ماتروسكا
+ برنامج ترميز الصوت المجاني بدون فقدان البيانات (FLAC)
+ نيلي
+ أزرق
+ أخضر عميق
+ جير
+ أصفر
+ بط نهري صغير
+ أخضر
+ البرتقالي
+ المدة الإجمالية: %s
+ رمادي
+ تم تحديد %d
+ -%.1f ديسيبل
+
+ - %d أغنية
+ - %d أغاني
+ - %d أغاني
+ - %d أغاني
+ - %d أغاني
+ - %d أغاني
+
+ إظهار فقط الفنانين المُعتمدين مباشرة على الألبوم (يعمل بشكل أفضل في المكتبات المعروفة بتوسيماتها الجيدة)
+ المحتوى
+ مجلدات
+ إخفاء المتعاونين
+ ضبط سلوك وصوت التشغيل
+ إعادة التشغيل قبل الانتقال للوراء
+ إعادة التشغيل قبل الانتقال إلى الأغنية السابقة
+ البقاء على التشغيل/الإيقاف عند الانتقال أو تعديل قائمة التشغيل
+ تذكر الإيقاف المؤقت
+ مسح ذاكرة التخزين المؤقت للعلامات وإعادة تحميل كامل مكتبة الموسيقى (أبطأ ولكن أكثر اكتمالًا)
+ يحتاج Auxio إلى إذن لقراءة مكتبتك الموسيقية
+ غير قادر على استيراد قائمة التشغيل من هذا الملف
+ لم يتم العثور على تطبيق يمكنه التعامل مع هذه المهمة
+ تشغيل أو إيقاف مؤقت
+ إيقاف التشغيل
+ صوت MPEG-1
+ أرجواني
+ الأرجواني العميق
+ تم استيراد قائمة التشغيل
+ إجراء الإشعارات المخصص
+ الانتقال إلى التالي
+ غلاف الألبوم
+ غير قادر على تصدير قائمة التشغيل إلى هذا الملف
+ إزالة المجلد
+ رمز مساعد
+ %1$s، %2$s
+ متحرك
+ البومات ما قبل الإطلاق
+ البوم قبل الإطلاق
+ تسجيل تجريبي
+ تسجيلات تجريبية
+ اغاني دي جي
+ اغنية دي جي
+ المزيد
+ صورة التحديد
+ إزالة هذه الأغنية
+ نقل هذه الأغنية
+ ألبوم ريميكس
+ تذكر خاصية الخلط
+ تم تصدير قائمة التشغيل
+ تم حذف قائمة التشغيل
+ تبرع إلى المشروع لإضافة اسمك هنا!
+ علامات تبويب المكتبة
+ تغيير مرئية وترتيب علامات تبويب المكتبة
+ إجراء شريط التشغيل المخصص
+ تشغيل من العنصر المعروض
+ تشغيل من الألبوم
+ تشغيل من الفنان
+ تشغيل من النوع
+ تشغيل الأغنية بمفردها
+ الموسيقى
+ تحذير: استخدام هذا الإعداد قد يؤدي إلى تفسير بعض العلامات بشكل غير صحيح بأن لديها قيم متعددة. يمكنك حل هذه المشكلة عن طريق إضافة شريط مائل مسبوق بشرطة مائلة عكسية (\\).
+ استعادة
+ ألبومات ممتدة
+ البوم مصغر
+ ألبوم موسيقي مباشر
+ جاري تحميل الموسيقى
+ جاري تحميل الموسيقى
+ أغنية فردية مباشرة
+ اغنية فردية ريميكس
+ الصور
+ إعادة تحميل مكتبة الموسيقى، باستخدام العلامات المخزنة مؤقتًا عند الإمكان
+ لا توجد مجلدات
+ هذا المجلد غير مدعوم
+ خلط جميع الأغاني
+ ازرق سماوي
+ تحرير %s
+ تلقائي
+ نظام الألوان
+ أغنية فردية
+ أغاني فردية
+ الرمز زائد (+)
+ الفرز الذكي
+ أحمر
+ ازرق غامق
+ إعادة التحميل التلقائي
+ ضبط الأحرف التي تشير إلى قيم علامات متعددة
+ البدء التلقائي في التشغيل عند توصيل سماعة الرأس (قد لا يعمل على جميع الأجهزة)
+ اصوات تصويرية
+ التحكم في كيفية تحميل الموسيقى والصور
+ حافظ على خاصية الخلط عند تشغيل أغنية جديدة
+ سماعة الرأس تشغيل تلقائي
+ توحيد مستوى الصوت
+ إيقاف مؤقت عند التكرار
+ إيقاف مؤقت عند تكرار الأغنية
+ المسار %d
+ البوم اغاني ريميكس
+ لا توجد ألبومات
+ إعادة فحص الموسيقى
+ غلاف الألبوم لـ %s
+ صورة الفنان لـ %s
+ صورة النوع لـ %s
+ فنان غير معروف
+ استراتيجية الريبلاي جين
+ ضبط بدون علامات
+ قائمة تشغيل فارغة
+ الوضع الدائري
+ عند التشغيل من المكتبة
+ تشغيل من جميع الأغاني
+ وضع التكرار
+ السلوك
+ عند التشغيل من تفاصيل العنصر
+ تجاهل ملفات الصوت التي ليست موسيقى، مثل البودكاست
+ فواصل القيم المتعددة
+ أعد تحميل مكتبة الموسيقى عندما تتغير (يتطلب إشعار دائم)
+ استثناء غير الموسيقى
+ الرمز التّعجبي (&)
+ الفاصلة (،)
+ شرطة مائلة (/)
+ أغطية الألبومات
+ سريع
+ عالية الجودة
+ فرض أغطية ألبومات مربعة
+ قص جميع أغطية الألبومات إلى نسبة جانب 1:1
+ صوت
+ تحذير: تغيير مكبر الصوت المسبق إلى قيمة إيجابية عالية قد يؤدي إلى ظهور ذروات صوتية على بعض المسارات الصوتية.
+ مكتبة
+ مجلدات الموسيقى
+ إدارة موقع تحميل الموسيقى
+ الانتقال إلى الأغنية الأخيرة
+ تغيير وضع التكرار
+ افتح قائمة الانتظار
+ الانتقال إلى الأغنية التالية
+ تشغيل العشوائية أو إيقافها
+ %d كيلو بايت في الثانية
+ ابحث في مكتبتك…
+ داكن
+ استخدم سمة داكنة بلون أسود نقي
+ المظهر
+ غيّر سمة وألوان التطبيق
+ السمة
+ فاتح
+ تفعيل الزوايا المستديرة على عناصر واجهة المستخدم الإضافية (يتطلب أن تكون أغلفة الألبومات مستديرة)
+ تخصيص
+ تفضيل الألبوم إذا كان أحد الألبومات يُشغّل
+ تفضيل المسار
+ مكبر الصوت المسبق لـ ReplayGain
+ تفضيل الألبوم
+ يتم تطبيق مكبر الصوت المسبق على التعديل الحالي أثناء التشغيل
+ ضبط مع العلامات
+ تحديث الموسيقى
+ لم يتم العثور على موسيقى
+ لا مسار
+ لا يوجد تشغيل الموسيقى
+ ترميز الصوت المتقدم (AAC)
+ لا يوجد تاريخ
+ لا أغاني
+ لون القرنفل
+ الفرز الصحيح للأسماء التي تبدأ بأرقام أو بكلمات مثل \"the\" (يعمل بشكل أفضل مع الموسيقى باللغة الإنجليزية)
+ إيقاف
+ بني
+ الأغاني التي تم تحميلها: %d
+ الألبومات المحملة: %d
+ الفنانون الذين تم تحميلهم: %d
+ قائمة التشغيل %d
+ جارٍ تحميل مكتبة الموسيقى الخاصة بك… (%1$d/%2$d)
+ هل تريد حذف %s؟ هذا لا يمكن التراجع عنها.
+ الأنواع المحملة: %d
+ القرص %d
+ +%.1f ديسيبل
+ %d هرتز
+ عرض
+ البوم موسيقي ريميكس
+ صوت تصويري
+ تم إعادة تسمية قائمة التشغيل
+ سمة اللون الأسود
+ تخصيص عناصر التحكم وسلوك واجهة المستخدم
+ تمت إضافته إلى قائمة التشغيل
+ إعدادات
+ الفاصلة المنقوطة (؛)
+ اختر المجلدات
+ استورد
+ صدّر
+ صدّر قائمة التشغيل
+ معدل العينة
+ نمط المسار
+ تحديد
+ الانطباعات
+ افتح مشكلة على GitHub
+ إرسل بريد إلكتروني
+ المؤيدون
+ المزيد
+ نسبي
+ مطلق
+ غير معروف
+ قائمة التشغيل استوردت
+ ألبوم غير معروف
+ افرز حسب
+ الاتجاه
+
+ - لا فنان
+ - فنان
+ - فنانان
+ - %d فنانين
+ - %d فنان
+ - %d فنان
+
+ ستظهر أغانيك هنا.
+ ستظهر ألبوماتك هنا.
+ سوف يظهر الفنانون الخاص بك هنا.
+ سوف تظهر قوائم تشغيلك هنا.
+ سوف تظهر أنواعك هنا.
+ تعديل الألبوم Replaygain
+ تعديل مسار ReplayGain
+ ألكساندر كيبهارت
+ تبرّع
+ المؤلف
+ استورد قائمة التشغيل
+ المسار
+ مجلد جديد
+ بدء التشغيل
+ معلومات الخطأ
+ نُسخت
+ إبلاغ
+ يبدأ Auxio باستخدام الحالة المحفوظة مسبقًا. إذا لم تتوفر حالة محفوظة، فسيتم خلط جميع الأغاني. سيبدأ التشغيل على الفور. \n\nتحذير: احرص على التحكم في هذه الخدمة، إذا قمت بإغلاقها ثم حاول استخدامها مرة أخرى، فمن المحتمل أن ينهار التطبيق.
+ استخدم مسارات متوافقة مع Windows
+ وفر المساحة
+
+ - لا ألبوم
+ - ألبوم
+ - ألبومان
+ - %d ألبومات
+ - %d ألبوم
+ - %d ألبوم
+
+ MPEG-4 تحتوي على %s
+ Apple Lossless Audio Codec (ALAC)
+
diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml
index 8a0023135..5f602a887 100644
--- a/app/src/main/res/values-az/strings.xml
+++ b/app/src/main/res/values-az/strings.xml
@@ -3,25 +3,133 @@
Musiqi yüklənir
Musiqi yüklənir
Təkrar cəhd et
- İcazə ver
+ Qəbul et
Mahnılar
Bütün mahnılar
Albomlar
Albom
Canlı albom
- Remiks albom
+ Qarışıq albom
Ep-lər
- Tək
- Tək
- Canlı tək
- Remiks tək
+ Seçmə
+ Seçmə
+ Canlı seçmə
+ Qarışıq seçmə
Kompilyasiya
Kompilyasiyalar
- Səs treki
- Android üçün asan, səmərəli musiqi oynadıcı.
+ Səs axını
+ Android üçün asan, səmərəli musiqi səsləndirici.
Musiqi kitabxanası yoxlanılır
Canlı EP
EP
Remiks EP
- Səs treklər
+ Səs axınları
+ Yeni pleylist
+ Remiks kompilyasiyası
+ DJ Mikslər
+ Canlı kompilyasiya
+ Demo
+ Demos
+ Canlı
+ Remikslər
+ Sənətkar
+ Sənətkarlar
+ Janr
+ Daha çox
+ Mahnı
+ Qarışıq lentlər
+ Qarışıq lent
+ DJ Miks
+ Janrlar
+ Pleylist
+ Pleylistlər
+ Boş pleylist
+ Üzərində Görünür
+ İdxal edilən pleylist
+ Sil
+ Axtarış
+ Disk
+ Adı dəyişdir
+ Düzəliş et
+ Hamısı
+ Müddət
+ Filtr
+ Tarix
+ Pleylisti idxal et
+ Pleylisti yenidən adlandır
+ Pleylist silinsin?
+ Ad
+ Mahnı sayı
+ İdxal et
+ İxrac et
+ Pleylist ixrac et
+ Pleylistə əlavə et
+ Xüsusiyyətlərə bax
+ Nümunə sürəti
+ Daha çox
+ Səsləndirməni başlat
+ İmtina et
+ Sıfırla
+ Növbə
+ Qarışdır
+ Sənətkara bax
+ Alboma keç
+ Baxış
+ Paylaş
+ Əlavə et
+ Səsləndir
+ Çeşidləmə üsulu
+ Göstəriş
+ Azalan
+ Axın
+ Əlavə edilən tarix
+ Artan
+ İndi səslənir
+ Format
+ Həcm
+ ReplayGain Axın Nizamlanması
+ ReplayGain Albom Nizamlanması
+ Oldu
+ Saxla
+ Çeşidlə
+ Tarazlayıcı
+ Qarışdır
+ Növbəti səsləndirmə
+ Növbəyə əlavə et
+ Mahnı detalları
+ Yol
+ Hamısın Qarışdır
+ Bit sürəti
+ Haqqında
+ Köçürüldü
+ Hesabat
+ Xəta məlumatı
+ Yaradıcı
+ Alexander Capehart
+ Versiya
+ Mənbə kodu
+ Viki
+ Kitabxana statistikası
+ Lisenziyalar
+ Seçim
+ Yol üsulu
+ Tam
+ Nisbi
+ Windows-a uyuşan yolları istifadə et
+ Musiqi səslənməsinə bax və idarə et
+ İanə ver
+ Dəstəkçilər
+ Musiqi kitabxananız yüklənir…
+ Musiqi kitabxananız dəyişikliklər üçün yoxlanılır…
+ Əks Əlaqə
+ E-poçt göndər
+ GitHub-da problem yarat
+ Qovluqları seç
+ Pleylist ixrac edildi
+ Pleylistə əlavə edildi
+ Pleylist adı dəyişdirildi
+ Pleylist idxal edildi
+ Pleylist silindi
+ Səsləndirmə siyahısı yaradıldı
+ Növbəyə əlavə edildi
\ No newline at end of file
diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml
index e84a07e3d..2e83e80c0 100644
--- a/app/src/main/res/values-be/strings.xml
+++ b/app/src/main/res/values-be/strings.xml
@@ -16,7 +16,6 @@
Коска (,)
Плюс (+)
Амперсанд (&)
- Ўключыць
Афармленне
Паўтарыць
Пошук у вашай бібліятэцы…
@@ -63,7 +62,6 @@
Жанры
Сартаваць
Дата дабаўлення
- Дзяржава адноўлена
Дыск
Дата
Працягласць
@@ -91,22 +89,19 @@
Частата дыскрэтызацыі
Скінуць
Дадаць
- Дзяржава захаваная
Вікі
Захаваць
- Дзяржава ачышчана
Версія
Выкарыстоўвайце чыста чорную цёмную тэму
- Падвышанай якасці (павольная загрузка)
+ Падвышанай якасці (павольная загрузка)
Аддайце перавагу альбому, калі ён гучыць
Карэкціроўка без тэгаў
- Тэчкі з музыкай
- Захаваць бягучы стан прайгравання
+ Тэчкі з музыкай
Аўтапрайграванне гарнітуры
Наладзьце гук і паводзіны прайгравання
Заўсёды пачынаць гульню, калі падключана гарнітура (можа працаваць не на ўсіх прыладах)
Паўза, калі песня паўтараецца
- Выраўноўванне гучнасці ReplayGain
+ Выраўноўванне гучнасці
Карэкціроўка з тэгамі
Пераматайце назад, перш чым перайсці назад
Перамотка назад, перш чым перайсці да папярэдняй песні
@@ -117,33 +112,21 @@
Аддайце перавагу альбому
Папярэдні ўзмацняльнік ReplayGain
Бібліятэка
- Кіруйце месцам загрузкі музыкі
- Тэчкі
- Рэжым
- Выключэнні
- Музыка не будзе загружана з выбраных тэчак.
- Музыка будзе загружана толькі з выбраных тэчак.
+ Кіруйце месцам загрузкі музыкі
+ Тэчкі
Перасканаваць музыку
Абнавіць музыку
Перазагрузіце музычную бібліятэку, выкарыстоўваючы па магчымасці кэшаваныя тэгі
Ачысціце кэш тэгаў і цалкам перазагрузіце музычную бібліятэку (павольней, але больш поўна)
- Ачысціць стан прайгравання
- Немагчыма ачысціць стан
- Стан прайгравання
- Захаваць стан прайгравання
- Аднавіць раней захаваны стан прайгравання (калі ёсць)
Auxio патрабуецца дазвол на чытанне вашай музычнай бібліятэкі
- Ачысціць раней захаваны стан прайгравання (калі ёсць)
Музыка не знойдзена
Памылка загрузкі музыкі
- Няма тэчак
- Гэтая папка не падтрымліваецца
- Немагчыма аднавіць стан
+ Няма тэчак
+ Гэтая папка не падтрымліваецца
Кампазіцыя %d
Перамясціць песню ў чаргу
Не знойдзена прыкладання, якое можа справіцца з гэтай задачай
Прайграванне або прыпыненне
- Немагчыма захаваць стан
Перайсці да наступнай песні
Перайсці да апошняй песні
Змяніць рэжым паўтору
@@ -154,7 +137,7 @@
Спыніць прайграванне
Адкрыйце чаргу
Ачысціць пошукавы запыт
- Выдаліць тэчку
+ Выдаліць тэчку
Вокладка альбома
Вокладка альбома %s
Выява выканаўцы для %s
@@ -249,7 +232,6 @@
Шматзначныя раздзяляльнікі
Папярэджанне: выкарыстанне гэтай налады можа прывесці да таго, што некаторыя тэгі будуць памылкова інтэрпрэтавацца як тыя, што маюць некалькі значэнняў. Вы можаце вырашыць гэта, дадаўшы да непажаданых сімвалаў-падзельнікаў зваротную касую рысу (\\).
Паўза пры паўторы
- Аднавіць стан прайгравання
Касая рыса (/)
Малюнкі
Кропка з коскай (;)
@@ -258,7 +240,7 @@
Вокладкі альбомаў
Перамясціць гэту ўкладку
Адключаны
- Зыходныя (хуткая загрузка)
+ Зыходныя (хуткая загрузка)
Аўдыё
Прайграванне
Канцэртны міні-альбом
@@ -270,7 +252,6 @@
Вокладка плэйліст для %s
Плэйліст
Плэйлісты
- Стварыце новы плэйліст
Плэйліст %d
Новы плэйліст
Дадаць у плэйліст
@@ -328,4 +309,25 @@
Ахвяруйце на праект, каб ваша імя было дададзена тут!
Запамінаць паўзу
Пакідаць прайграванне/паўзу падчас пропуску або рэдагаванні чаргі
+ Адкл.
+ Пачаць прайграванне
+ Запускаць auxio, выкарыстоўваючы раней захаваны стан. Калі захаваны стан недаступны, усе песні будуць ператасаваныя. Прайграванне пачнецца неадкладна.
+\n
+\nПапярэджанне: будзьце асцярожныя пры кіраванні гэтай службай, калі вы закрыеце яе, а затым паспрабуеце выкарыстоўваць зноў, вы, верагодна, прывядзеце да збою прыкладання.
+ Больш
+ Абярыце тэчкі
+ MPEG-4, які змяшчае %s
+ Невядомы альбом
+ Вашыя альбомы будуць адлюстроўвацца тут.
+ Тут будуць адлюстроўвацца вашыя жанры.
+ Новая тэчка
+ Apple Lossless Audio Codec (ALAC)
+ Невядомы
+ Адправіць электронны ліст
+ Водгукі
+ Вашыя песні будуць адлюстроўвацца тут.
+ Тут з\'явяцца вашыя выканаўцы.
+ Тут будуць адлюстроўвацца вашыя плэйлісты.
+ Эканомія месца
+ Паведаміць аб праблеме на GitHub
\ No newline at end of file
diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml
new file mode 100644
index 000000000..54296b712
--- /dev/null
+++ b/app/src/main/res/values-bg/strings.xml
@@ -0,0 +1,313 @@
+
+
+ Зареждане на музика
+ Мониторинг на музикална библиотека
+ Опитайте отново
+ Песни
+ Песен
+ Всички песни
+ Албум
+ Албум на живо
+ Ремикс албум
+ Ремикс EP
+ Сингли
+ Сингъл
+ Ремикс сингъл
+ Компилации
+ Компилация
+ Компилация на живо
+ Ремикс компилация
+ Саундтраци
+ Саундтрак
+ Миксове
+ Микс
+ Демонстрации
+ DJ миксове
+ На живо
+ Ремикси
+ Появява се на
+ Изпълнител
+ Изпълнители
+ Жанр
+ Жанрове
+ Плейлист
+ Нов плейлист
+ Празен плейлист
+ Изнасяне
+ Внасяне
+ Внасяне на плейлист
+ Изнасяне на плейлист
+ Преименувай
+ Изтрий
+ Изтрий плейлист?
+ Редактирай
+ Търсене
+ Филтър
+ Всички
+ Име
+ Дата
+ Продължителност
+ Диск
+ Изпълни
+ Разбъркано
+ Опашка
+ Изпълни следваща
+ Добави към опашката
+ Добави към плейлист
+ Отиди на изпълнител
+ Отиди на албум
+ Виж свойства
+ Преглед
+ Сподели
+ Свойства на песента
+ Път
+ Формат
+ Размер
+ Битрейт
+ Честота на дискретизация
+ ReplayGain Регулиране на албума
+ Разбъркано
+ Добре
+ Отказ
+ Запази
+ Нулирай
+ Стил на пътя
+ Относително
+ Използвай съвместими с Windows пътища
+ Прост, рационален музикален плейър за android.
+ Зарежда се музика
+ Още
+ Албуми
+ EP на живо
+ Сингъл наживо
+ Внесен плейлист
+ Преименувай плейлист
+ Брой песни
+ Датата е добавена
+ Вид
+ DJ микс
+ Писта
+ Сортирай по
+ Еквалайзер
+ Демонстрация
+ Посока
+ Плейлисти
+ Възходящ
+ Низходящ
+ Сега се изпълнява
+ ReplayGain Регулиране на песента
+ Разбъркай всички
+ Добави
+ Относно
+ Абсолютно
+ EPs
+ EP
+ Версия
+ Програмен код
+ Wiki
+ Лицензи
+ Библиотечна статистика
+ Избор
+ Информация за грешка
+ Копирано
+ Докладвай
+ Автор
+ Alexander Capehart
+ Дарете
+ Поддръжници
+ Преглед и контрол на възпроизвеждането на музика
+ Вашата музикална библиотека се зарежда…
+ Наблюдение на музикалната ви библиотека за промени…
+ Добави към опашката
+ Създаден плейлист
+ Внесен плейлист
+ Преименуван плейлист
+ Изнесен плейлист
+ Изтрит плейлист
+ Добави към плейлист
+ Настройки
+ Виж и Усети
+ Промени темата и цветовете на приложението
+ Тема
+ Автоматична
+ Светла
+ Тъмна
+ Цветова схема
+ Черна тема
+ Използвай чисто черна тъмна тема
+ Кръгъл режим
+ Активирай заоблените ъгли на допълнителните елементи на потребителския интерфейс (изисква обложките на албумите да са заоблени)
+ Персонализирайте
+ Персонализирайте контролите и поведението на потребителския интерфейс
+ Дисплей
+ Раздели на библиотеката
+ Промени видимостта и реда на разделите на библиотеката
+ Персонализирано действие за известие
+ Премини към следваща
+ Режим на повторение
+ Поведение
+ При изпълнение от детайли на елемента
+ При изпълнение от библиотеката
+ Изпълни от албум
+ Изпълни от изпълнител
+ Изпълни от жанр
+ Изпълни песен сама по себе си
+ Запомни разбъркване
+ Запази функцията разбъркване при изпълнение на нова песен
+ Съдържание
+ Контролирай как да се зареждат музика и изображение
+ Музика
+ Автоматично презареждане
+ Презаредете музикалната библиотека, когато се промени (изисква постоянно известие)
+ Изключи не-музика
+ Разделители с множество стойности
+ Конфигурирай знаци, които обозначават множество стойности на тагове
+ Запетая (,)
+ Точка и запетая (;)
+ Наклонена черта (/)
+ Плюс (+)
+ Амперсанд (&)
+ Интелигентно сортиране
+ Обложки на албум
+ Изключено
+ Бързо
+ Високо качество
+ Функция квадратни обложки на албум
+ Изрежи всички корици на албуми до съотношение 1:1
+ Аудио
+ Конфигурирай поведението на звука и възпроизвеждане
+ Възпроизвеждане
+ Автоматично пускане на слушалки
+ Винаги започва да възпроизвежда, когато са свързани слушалки (може да не работи на всички устройства)
+ Превъртете преди да преминете назад
+ Превъртете преди да преминете към предишната песен
+ Пауза при повторение
+ Запомни пауза
+ Нормализация на звука
+ ReplayGain стратегия
+ Изключено
+ Предоставяне
+ Дарете за проекта, за да бъде добавено името ви тук!
+ Търсене във вашата библиотека…
+ Персонализирано действие на лентата за възпроизвеждане
+ Изпълни от показания елемент
+ Изпълни от всички песни
+ Игнорирай аудио файлове, които не са музика, като подкасти
+ Предупреждение: Използването на тази настройка може да доведе до неправилно тълкуване на някои тагове като имащи множество стойности. Можете да разрешите това, като поставите пред нежеланите разделителни знаци обратна наклонена черта (\\).
+ Правилно сортиране на имена, които започват с цифри или думи като \"the\" (работи най-добре с музика на английски)
+ Скриване на сътрудниците
+ Показвай само изпълнители, които са директно посочени в албум (работи най-добре при добре маркирани библиотеки)
+ Изображения
+ Пауза когато песента се повтаря
+ Останете в режим на изпълнение/пауза, когато прескачате или редактирате опашка
+ Предпочитам песен
+ Предпочитам албум
+ Предпочитам албум, ако се пуска такъв
+ ReplayGain предусилвател
+ Предварителният усилвател се прилага към съществуващата настройка по време на възпроизвеждане
+ Регулиране с етикети
+ Корекция без етикети
+ Библиотека
+ Музикални папки
+ Управлявайте откъде да се зарежда музиката
+ Папки
+ Обновяване на музика
+ Няма намерена музика
+ Неуспешно зареждане на музика
+ Auxio се нуждае от разрешение, за да чете вашата музикална библиотека
+ Плейлиста не може да се изнесе в този файл
+ Няма намерено приложение, което да може да се справи с тази задача
+ Няма папки
+ Изпълни или пауза
+ Премини към следваща песен
+ Премини към последна песен
+ Промени режима на повторение
+ Включи или изключи разбъркване
+ Разбъркай всички песни
+ Спри възпроизвеждането
+ Премахни тази песен
+ Премести тази песен
+ Отвори опашката
+ Избери изображение
+ Премести този раздел
+ Предупреждение: Промяната на пред усилвателя на висока положителна стойност може да доведе до пикове на някои аудио записи.
+ Изчисти заявката за търсене
+ Премахни папка
+ Не може да се внесе плейлист от този файл
+ Тази папка не се поддържа
+ Auxio икона
+ Обложка на албум
+ Обложка на албум за %s
+ Изображение на изпълнител за %s
+ Презареди музикалната библиотека, като използвате кеширани етикети, когато е възможно
+ Жанрово изображение за %s
+ Повторно сканиране на музика
+ Песен %d
+ Изображение на плейлист за %s
+ Изчисти кеша на етикета и пълно презареждане на музикалната библиотека (по-бавно, но по-пълно)
+ Неизвестен изпълнител
+ Няма дата
+ Няма диск
+ Няма песен
+ Няма песни
+ Няма албуми
+ Няма музика за възпроизвеждане
+ MPEG-1 audio
+ MPEG-4 audio
+ Ogg audio
+ Matroska audio
+ Розово
+ Червено
+ Лилаво
+ Индиго
+ Free Lossless Audio Codec (FLAC)
+ Зелено
+ Наситено зелено
+ Лайм
+ Жълто
+ Синьо-зелено
+ Зеленикаво-синьо
+ Оранжево
+ Редактиране %s
+ Диск %d
+ %1$s, %2$s
+ Плейлист %d
+ +%.1f dB
+ Заредени песни: %d
+ -%.1f dB
+ %d kbps
+ Заредени албуми: %d
+ Заредени изпълнители: %d
+ Заредени жанрове: %d
+ Обща продължителност: %s
+ Наситено лилаво
+ Синьо
+ Наситено синьо
+ Кафяво
+ %d Избрано
+ Вашата музикална библиотека се зарежда… (%1$d/%2$d)
+ Изтрий %s? Това не може да бъде отменено.
+
+ - %d песен
+ - %d песени
+
+ Сиво
+
+ - %d изпълнител
+ - %d изпълнители
+
+ Динамичен
+
+ - %d албум
+ - %d албуми
+
+ Advanced Audio Coding (AAC)
+ %d Hz
+ Неизвестен жанр
+ Стартирай Auxio, използвайки предварително запазеното състояние. Ако няма налично запазено състояние, всички песни ще бъдат разбъркани. Възпроизвеждането ще започне веднага. \n \nПРЕДУПРЕЖДЕНИЕ: Бъдете внимателни, когато контролирате тази услуга, ако я затворите и след това опитате да я използвате отново, вероятно ще сринете приложението.
+ Стартирай възпроизвеждане
+ Още
+ Обратна връзка
+ Направете проблем в GitHub
+ Изпратете имейл
+
\ No newline at end of file
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 6d70c2fce..386ec94e2 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -38,7 +38,6 @@
Přidáno do fronty
Přejít na umělce
Přejít na album
- Stav uložen
OK
@@ -88,8 +87,6 @@
Pozastavit při opakování
Pozastavit při opakování skladby
Obsah
- Uložit stav přehrávání
- Uložit aktuální stav přehrávání
Obnovit hudbu
Znovu načíst hudební knihovnu, pokud možno s použitím značek uložených v mezipaměti
@@ -97,8 +94,8 @@
Načítání hudby selhalo
Auxio potřebuje oprávnění ke čtení vaší hudební knihovny
Nebyla nalezena žádná aplikace, která by dokázala vykonat tuto akci
- Žádné složky
- Tato složka není podporována
+ Žádné složky
+ Tato složka není podporována
Prohledat vaší knihovnu…
@@ -113,7 +110,7 @@
Přesunout tuto skladbu ve frontě
Přesunout tuto kartu
Vymazat hledání
- Odebrat složku
+ Odebrat složku
Ikona Auxio
Obal alba
Obal alba %s
@@ -167,11 +164,6 @@
Náhodně
Vše náhodně
- Režim
- Vyloučit
- Z přidaných složek nebude načtena hudba.
- Zahrnout
- Hudba bude načtena pouze z přidaných složek.
Zvuk MPEG-1
Zvuk MPEG-4
Zvuk Ogg
@@ -181,7 +173,7 @@
%d kbps
%d Hz
Při přehrávání z podrobností o položce
- Spravovat, odkud by měla být načítána hudba
+ Spravovat, odkud by měla být načítána hudba
Přehrát ze zobrazené položky
Zobrazit vlastnosti
Vlastnosti skladby
@@ -189,11 +181,7 @@
Velikost
Přenosová rychlost
Vzorkovací frekvence
- Složky s hudbou
- Obnovit stav přehrávání
- Stav obnoven
- Obnovit dříve uložený stav přehrávání (pokud existuje)
- Nepodařilo se obnovit stav
+ Složky s hudbou
Monitorování hudební knihovny
Automatické znovunačítání
Sledování změn v hudební knihovně…
@@ -218,9 +206,6 @@
Remixové album
Živé EP
Remixové EP
- Vymazat stav přehrávání
- Vymazat dříve uložený stav přehrávání (pokud existuje)
- Stav vymazán
Otevřít frontu
Žánr
Vlastní akce lišty přehrávání
@@ -243,19 +228,17 @@
Vyloučit nehudební obsah
Ignorovat zvukové soubory, které nejsou hudbou, například podcasty
Obaly alb
- Vysoká kvalita
+ Vysoká kvalita
Vypnuté
- Rychlé
+ Rychlé
Skrýt spoluautory
Zobrazit pouze umělce, kteří jsou přímo uvedeni na albu (funguje nejlépe u dobře označených knihoven)
- Nepodařilo se uložit stav
- %d umělec
- %d umělci
- %d umělců
- %d umělců
- Nepodařilo se vymazat stav
Znovu najít hudbu
Vymazat mezipaměť značek a znovu úplně znovu načíst hudební knihovnu (pomalejší, ale úplnější)
Vybráno %d
@@ -263,8 +246,8 @@
Wiki
%1$s, %2$s
Obnovit
- Složky
- ReplayGain
+ Složky
+ Normalizace hlasitosti
Chování
Změnit motiv a barvy aplikace
Přizpůsobit ovládání a chování rozhraní
@@ -274,14 +257,12 @@
Nastavit chování zvuku a přehrávání
Přehrávání
Knihovna
- Perzistence
Sestupně
Seznamy skladeb
Obrázek seznamu skladeb pro %s
Seznam skladeb
Při řazení ignorovat předložky
Ignorovat slova jako „the“ při řazení podle názvu (funguje nejlépe u hudby v angličtině)
- Vytvořit nový playlist
Přidat do seznamu skladeb
Přidáno do seznamu skladeb
Seznam skladeb vytvořen
@@ -339,4 +320,25 @@
Přispějte na projekt a uvidíte zde své jméno!
Zůstat ve stavu přehrávání/pozastavení při přeskakování nebo úpravě fronty
Zapamatovat pozastavení
+ Vypnuto
+ Spustit přehrávání
+ Spustí Auxio pomocí naposledy uloženého stavu. Pokud není dostupný žádný uložený stav, budou náhodně přehrány všechny skladby. Přehrávání se spustí ihned.
+\n
+\nVAROVÁNÍ: při ovládání této služby buďte opatrní, pokud ji zavřete a pak se ji znovu pokusíte použít, aplikace nejspíše spadne.
+ Více
+ Zpětná vazba
+ Vytvořit problém na GitHubu
+ Poslat e-mail
+ Vybrat složky
+ Neznámé album
+ MPEG-4 obsahující %s
+ Apple Lossless Audio Codec (ALAC)
+ Neznámý
+ Nová složka
+ Úspora místa
+ Vaše skladby se zobrazí zde.
+ Vaše alba se zobrazí zde.
+ Vaši umělci se zobrazí zde.
+ Vaše seznamy skladeb se zobrazí zde.
+ Vaše žánry se zobrazí zde.
\ No newline at end of file
diff --git a/app/src/main/res/values-cy/strings.xml b/app/src/main/res/values-cy/strings.xml
new file mode 100644
index 000000000..c2f6fb9f0
--- /dev/null
+++ b/app/src/main/res/values-cy/strings.xml
@@ -0,0 +1,332 @@
+
+
+ Yn llwytho cerddoriaeth
+ Llwytho cerddoriaeth
+ Yn monitro llyfrgell cerddoriaeth
+ Ceisio eto
+ Caniatáu
+ Caneuon
+ Cân
+ Holl ganeuon
+ Albymau
+ Albwm
+ Albwm byw
+ Albwm ail-gymysgiad
+ EPau
+ EP
+ EP byw
+ Detholiadau
+ Detholiad
+ Detholiad byw
+ Detholiad ail-gymysgiad
+ Traciau sain
+ Trac sain
+ Tapiau cymysgiad
+ Tâp cymysgiad
+ Chwaraewr cerddoriaeth syml a synhwyrol.
+ Mwy
+ EP ail-gymysgiad
+ Enw
+ Dyddiad
+ Hyd
+ Nifer gân
+ Disg
+ Trac
+ Didoli
+ Didoli gan
+ Ffolderi
+ Rhestri chwarae
+ Rhestr chwarae newydd
+ Rhestr chwarae gwag
+ Popeth
+ Dyddiad wedi\'i ychwanegu
+ Priodweddau cân
+ Fformat
+ Maint
+ Llawn
+ Cymharol
+ Ystadegau llyfrgell
+ Dewisiad
+ Manylion y gwall
+ Cadwyd
+ Adrodd
+ Awdur
+ Cyfrannu
+ Cyfranwyr
+ Gweld a rheoli chwarae cerddoriaeth
+ Yn llwytho eich llyfrgell cerddoriaeth…
+ Yn monitro eich llyfrgell cerddoriaeth am newidiadau…
+ Wedi\'i ychwanegu i\'r ciw
+ Rhestr chwarae wedi\'i greu
+ Rhestr chwarae wedi\'i fewnforio
+ Rhestr chwarae wedi\'i ailenwi
+ Rhestr chwarae wedi\'i allforio
+ Rhestr chwarae wedi\'i ddileu
+ Wedi\'i ychwanegu i\'r rhestr chwarae
+ Cyfrannwch i\'r prosiect er mwyn ychwanegu eich enw yma!
+ Chwilio eich llyfrgell…
+ Newid amlygrwydd a threfn tabiau llyfrgell
+ Wrth chwarae o fanylion yr eitem
+ Chwarae o\'r eitem wedi\'i dangos
+ Chwarae o holl ganeuon
+ Chwarae o\'r albwm
+ Chwarae o\'r artist
+ Chwarae o\'r genre
+ Chwarae\'r gân ar ei phen ei hunan
+ Cynnwys
+ Cerddoriaeth
+ Ail-lwytho awtomatig
+ Cuddio cyfranwyr
+ Lluniau
+ Cloriau albwm
+ I ffwrdd
+ Yn gyflym
+ Ansawdd uwch
+ Gorfodi cloriau albwm sgwâr
+ Sain
+ I ffwrdd
+ Llyfrgell
+ Ffolderi cerddoriaeth
+ Adnewyddu cerddoriaeth
+ Trac %d
+ Chwarae pob cân ar hap
+ Artist anhysbys
+ Genre anhysbys
+ Dim dyddiad
+ Dim disg
+ Dim trac
+ Dim caneuon
+ Dim albymau
+ Dim cerddoriaeth yn chwarae
+ Coch
+ Pinc
+ Porffor
+ Porffor dwfn
+ Glas
+ Glas dwfn
+ Gwyrddlas 1
+ Gwyrdd
+ Gwyrdd dwfn
+ Leim
+ Melyn
+ Oren
+ Llwyd
+ Brown
+ Dynamig
+ %d Wedi\'u dewis
+ Yn golygu %s
+ Disg %d
+ Rhestr chwarae %d
+ Cymysgiadau DJ
+ Cymysgiad DJ
+ Byw
+ Ail-gymysgiadau
+ Artistiaid
+ Rhestr chwarae
+ Rhestr chwarae wedi\'i mewnforio
+ Mewnforio
+ Mewnforio rhestr chwarae
+ Allforio
+ Allforio rhestr chwarae
+ Ailenwi
+ Ailenwi rhestr chwarae
+ Dileu
+ Dileu rhestr chwarae?
+ Golygu
+ Chwilio
+ Hidlo
+ Disgynnol
+ Yn chwarae nawr
+ Gosodiadau sain
+ Chwarae
+ Chwarae ar hap
+ Ciwio
+ Esgynnol
+ Chwarae nesaf
+ Ychwanegu i\'r ciw
+ Ychwanegu i\'r rhestr chwarae
+ Mynd i\'r artist
+ Mynd i\'r albwm
+ Gweld priodweddau
+ Gweld
+ Rhannu
+ Llwybr
+ Chwarae ar hap
+ Chwarae pob cân ar hap
+ Iawn
+ Diddymu
+ Cadw
+ Ailosod
+ Ychwanegu
+ Dull llwybr
+ Defnyddio llwybrau Windows
+ Ynghylch
+ Fersiwn
+ Cod ffynhonnell
+ Wici
+ Trwyddedau
+ Defnyddio thema ddu pur
+ Modd crwn
+ Gosodiadau
+ Ymddangosiad
+ Newid thema a lliwiau\'r ap
+ Thema
+ Awtomatig
+ Golau
+ Tywyll
+ Lliw
+ Thema ddu
+ Galluogi corneli crwn ar elfennau UI ychwanegol (angen cloriau albwm i fod yn grwn)
+ Personoleiddio
+ Arddangos
+ Tabiau\'r llyfrgell
+ Neidio i\'r nesaf
+ Modd ailadrodd
+ Ymarweddiad
+ Wrth chwarae o\'r llyfrgell
+ Clawr albwm
+ Yn llwytho eich llyfrgell cerddoriaeth… (%1$d/%2$d)
+ Caneuon wedi\'u llwytho: %d
+ Albymau wedi\'u llwytho: %d
+ Artistiaid wedi\'u llwytho: %d
+ Genres wedi\'u llwytho: %d
+ Cyfanswm hyd: %s
+ Addasiad heb dagiau
+ Ffafrio albwm os oes un yn chwarae
+ Cofio cyflwr oedi
+ Normaleiddio uchder sain
+ Strategaeth ReplayGain
+ Ffafrio trac
+ Ffafrio albwm
+ Addasiad gyda thagiau
+ Ni chanfuwyd cerddoriaeth
+ Methwyd llwytho cerddoriaeth
+ Mae ar Auxio angen caniatâd i ddarllen eich llyfrgell gerddoriaeth
+ Methwyd dod o hyd ap sydd yn gallu gwneud y tasg hon
+ Dim ffolderi
+ Neidio i\'r gân nesaf
+ Neidio i\'r gân ddiwethaf
+ Newid y modd ail-chwarae
+ Symud y gân hon
+ Llun rhestr chwarae %s
+ Llun y dewis
+ Sain MPEG-1
+ Sain Ogg
+ %d cilobeit yr eiliad
+
+ - %d artistiaid
+ - %d artist
+ - %d artist
+ - %d artist
+ - %d artist
+ - %d artist
+
+
+ - %d albymau
+ - %d albwm
+ - %d albwm
+ - %d albwm
+ - %d albwm
+ - %d albwm
+
+ Cofio y modd chwarae ar hap
+ Methwyd llwytho rhestr chwarae o\'r ffeil hon
+ Methwyd allforio\'r rhestr chwarae i\'r ffeil hon
+ Symud y tab hwn
+ Clirio\'r ymholiad chwilio
+ Tynnu ffolder
+ Eicon Auxio
+ Clawr albwm %s
+ Llun artist %s
+ Llun genre %s
+ Chwarae neu seibio
+ Troi\'r modd chwarae ar hap ymlaen neu i ffwrdd
+ Stopio\'r chwarae
+ Tynnu\'r gân hon
+ Agor y ciw
+ Sain MPEG-4
+ Sain Matroska
+ Dileu %s? Ni fydd yn bosib dadwneud hyn.
+
+ - %d caneuon
+ - %d gân
+ - %d gân
+ - %d cân
+ - %d chân
+ - %d cân
+
+ Senglau
+ Sengl
+ Demo
+ Demos
+ Ymddengys ar
+ Artist
+ Genre
+ Genres
+ Didradd
+ Plws (+)
+ Chwarae
+ Indigo
+ Gwyrddlas 2
+ +%.1f desibelau
+ -%.1f desibelau
+ %d Hz
+ Sengl byw
+ Sengl ail-gymysgiad
+ Cyfeiriad
+ Gradd samplu
+ Alexander Capehart
+ Allgáu pethau sydd ddim yn gerddoriaeth
+ Seibio os yn ailadrodd
+ Seibio pan fydd cân yn ailadrodd
+ pre-amp ReplayGain
+ Coma (,)
+ Gwahannod (;)
+ Blaenslaes (/)
+ Didoli\'n ddeallus
+ Didoli enwau sydd yn dechrau gyda rhifau neu enwau megis \"the\" yn gywir (mae\'n gweithio orau gyda Saesneg)
+ Chwarae\'n awtomatig gyda chlustffon
+ Ail-sganio cerddoriaeth
+ %1$s, %2$s
+ Rheoli o ble i lwytho cerddoriaeth
+ Free Lossless Audio Codec (FLAC)
+ Addasu rheolaethau\'r UI ac ymarweddiad
+ Gweithred addasedig ar y bar chwarae
+ Gweithred hysbysiad addasedig
+ Gwahanydd aml-werth
+ Tocio pob clawr albwm i gymhareb agwedd 1:1
+ Rhybudd: Gall newid y pre-amp i lefel bositif uchel yn achosi uchafbwyntiau ar rai traciau sain.
+ Ail-lwytho\'r llyfrgell cerddoriaeth, gan ddefnyddio tagiau wedi\'u cadw\'n barod pan fo modd
+ Dileu\'r tagiau wedi\'u cadw\'n barod ac ail-lwytho\'r llyfrgell gerddoriaeth yn llawn (yn arafach, ond yn fwy cyflawn)
+ Ni chefnogir y ffolder hon
+ Advanced Audio Coding (AAC)
+ Addasu Albwm ReplayGain
+ Addasu Trac ReplayGain
+ Mynd i ddechrau\'r gân cyn neidio\'n ôl
+ Mynd i ddechrau\'r gân cyn neidio i\'r gân flaenorol
+ Parhau i chwarae / oedi wrth neidio neu wrth golygu\'r ciw
+ Dechrau chwarae
+ Diystyru ffeiliau sain sydd ddim yn gerddoriaeth, megis podlediadau
+ Rhybudd: Gall ddefnyddio\'r gosodiad hwn achosi i rai tagiau i gael eu dehongli\'n anghywir fel bod â gwerthoedd lluosog. Gallwch ddatrys hyn trwy ragddodi llythrennau gwahanydd diangen ag adlach (\\).
+ Dangos dim ond artistiaid sy\'n cael eu cydnabyddiaeth ar albwm (mae\'n gweithio gorau ar llyfrgelloedd gyda thagiau da)
+ Dechrau chwarae bob amser pan fydd clustffon wedi\'i gysylltu (efallai na fydd yn gweithio gyda phob un dyfeisiau)
+ Mae\'r pre-amp yn cael ei gymhwyso i\'r addasiad presennol yn ystod chwarae
+ Yn dechrau Auxio yn y cyflwr wedi\'i gadw\'n barod. Os nad yw cyflwr sydd wedi\'i gadw ar gael, bydd pob cân yn cael eu chwarae ar hap. Bydd chwarae yn dechrau\'n syth.
+\n
+\nRHYBUDD: Byddwch yn ofalus wrth reoli\'r gwasanaeth hwn. Os ydych yn ei gau ac yna\'n ceisio ei ddefnyddio, mae\'n debygol y byddwch yn chwalu\'r ap.
+ Cadw y modd chwarae ar hap wrth chwarae cân newydd
+ Rheoli sut mae cerddoriaeth a lluniau\'n cael eu llwytho
+ Ail-lwytho\'r llyfrgell cerddoriaeth pryd bynnag y bydd yn ei newid (mae angen hysbysiad di-ball)
+ Ffurfweddu llythrennau sydd yn dynodi mwy nag un gwerth tag
+ Ampersand (&)
+ Ffurfweddu sain ac ymarweddiad chwarae
+ Dewis ffolderi
+ Adborth
+ Creu issue ar GitHub
+ Anfon ebost
+ Mwy
+ Albwm anhysbys
+ Anhysbys
+ MPEG-4 gan gynnwys %s
+ Apple Lossless Audio Codec (ALAC)
+
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 4f1f2bee1..6eaa0d219 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -24,10 +24,9 @@
Der Warteschlange hinzugefügt
Zum Künstler gehen
Zum Album gehen
- Wiedergabezustand gespeichert
Hinzufügen
Speichern
- Keine Ordner
+ Keine Ordner
Über
Version
Quellcode
@@ -62,8 +61,6 @@
Zurückspulen, bevor das Lied zurück geändert wird
Zurückspulen, bevor zum vorherigen Lied gewechselt wird
Inhalt
- Wiedergabezustand speichern
- Den aktuellen Wiedergabezustand speichern
Musik neu laden
Musikbibliothek neu laden, verwendet gecachte Tags wenn möglich
@@ -71,7 +68,7 @@
Laden der Musik fehlgeschlagen
Auxio benötigt die Berechtigung, um deine Musikbibliothek zu lesen
Es konnte keine App gefunden werden, die diese Aufgabe übernehmen kann
- Das Verzeichnis wird nicht unterstützt
+ Das Verzeichnis wird nicht unterstützt
Musikbibliothek durchsuchen…
@@ -126,7 +123,7 @@
Pausieren, wenn ein Song wiederholt wird
Zufällig an- oder ausschalten
Lied in der Warteschlange verschieben
- Verzechnis entfernen
+ Verzechnis entfernen
Albumcover
Keine Musik wird gespielt
Bibliotheks-Registerkarten
@@ -155,13 +152,8 @@
Warnung: Das Erhöhen der Vorverstärkung zu einem hohen positiven Wert könnte zu einer Übersteuerung bei einigen Audiospuren führen.
Wenn ein Lied aus den Elementdetails abgespielt wird
Vom dargestellten Element abspielen
- Musikordner
- Verwalten, von wo die Musik geladen werden soll
- Modus
- Ausschließen
- Musik wird nicht aus den von dir hinzugefügten Ordnern geladen.
- Einschließen
- Musik wird nur aus den von dir hinzugefügten Ordnern geladen.
+ Musikordner
+ Verwalten, von wo die Musik geladen werden soll
Kein Titel
Ogg-Audio
MPEG-4-Audio
@@ -188,9 +180,6 @@
Hinzugefügt am
Musikbibliothek neu laden, sobald es Änderungen gibt (erfordert persistente Benachrichtigung)
Automatisch neuladen
- Zustand wiederhergestellt
- Den vorher gespeicherten Wiedergabezustand wiederherstellen (wenn verfügbar)
- Zustand konnte nicht wiederhergestellt werden
EP
Mini-Alben
Single
@@ -199,7 +188,6 @@
Kompilation
Soundtrack
Soundtracks
- Wiedergabezustand wiederherstellen
Remix-Album
Mixtapes
Mixtape
@@ -210,10 +198,7 @@
Live-Single
Remix-Single
Live
- Den vorher gespeicherten Wiedergabezustand löschen (wenn vorhanden)
- Zustand gelöscht
Warteschlange öffnen
- Wiedergabezustand löschen
Genre
Equalizer
Angepasste Wiedergabeaktionstaste
@@ -237,16 +222,14 @@
Audio-Dateien, die keine Musik sind (wie Podcasts), ignorieren
Album-Cover
Aus
- Schnell
- Hohe Qualität
+ Schnell
+ Hohe Qualität
Mitarbeitende ausblenden
Nur Künstler anzeigen, die direkt auf einem Album erwähnt werden (funktioniert am besten mit gut getaggten Bibliotheken)
- %d Künstler
- %d Künstler
- Zustand konnte nicht gelöscht werden
- Zustand konnte nicht gespeichert werden
Music neu scannen
Tag-Cache leeren und die Musik-Bibliothek vollständig neu laden (langsamer, aber vollständiger)
%d ausgewählt
@@ -261,18 +244,16 @@
Musik
Bilder
Bibliothek
- Ordner
+ Ordner
Wiedergabe
Ton und Wiedergabeverhalten konfigurieren
- Persistenz
- Lautstärkeanpassung ReplayGain
+ Lautstärkenormalisierung
Absteigend
Wiedergabelistenbild für %s
Wiedergabeliste
Wiedergabelisten
- Artikel beim Sortieren ignorieren
- Wörter wie „the“ ignorieren (funktioniert am besten mit englischsprachiger Musik)
- Neue Wiedergabeliste erstellen
+ Intelligente Sortierung
+ Korrekte Sortierung von Namen, die mit Nummern oder Wörtern wie „the“ beginnen (funktioniert am besten mit englischsprachiger Musik)
Neue Wiedergabeliste
Zur Wiedergabeliste hinzugefügt
Zur Wiedergabeliste hinzufügen
@@ -320,7 +301,7 @@
Wiedergabeliste importieren
Wiedergabeliste exportieren
Pfadstil
- Abolut
+ Absolut
Relativ
Windows-kompatible Pfade verwenden
Exportieren
@@ -330,4 +311,25 @@
Wiedergabeliste exportiert
Pause merken
Wiedergabe/Pause beim Springen oder Bearbeiten der Warteschlange beibehalten
+ Aus
+ Wiedergabe starten
+ Startet Auxio mit dem vorher gespeicherten Zustand. Wenn kein gespeicherter Zustand vorhanden ist, werden alle Lieder zufällig abgespielt. Die Wiedergabe startet sofort.
+\n
+\nACHTUNG: Sei vorsichtig bei der Steuerung dieses Dienstes. Wenn du ihn schließt und dann erneut versuchst, ihn zu nutzen, stürzt die App wahrscheinlich ab.
+ Mehr
+ Ein Issue auf GitHub erstellen
+ Rückmeldung
+ Eine E-Mail senden
+ Ordner auswählen
+ MPEG-4, enthält %s
+ Apple Lossless Audio Codec (ALAC)
+ Unbekannt
+ Unbekanntes Album
+ Neuer Ordner
+ Speicherplatz sparen
+ Deine Alben werden hier angezeigt.
+ Deine Wiedergabelisten werden hier angezeigt.
+ Deine Lieder werden hier angezeigt.
+ Deine Genres werden hier angezeigt.
+ Deine Künstler werden hier angezeigt.
\ No newline at end of file
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index 039c864ec..6f379565b 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -69,7 +69,7 @@
Φόρτωση της συλλογής μουσικής σας… (%1$d/%2$d)
Είδη που φορτώθηκαν: %d
Αριθμός τραγουδιών
- Αυτός ο φάκελος δεν υποστηρίζεται
+ Αυτός ο φάκελος δεν υποστηρίζεται
Δίσκος %d
Album που φορτώθηκαν: %d
Καλλιτέχνες που φορτώθηκαν: %d
@@ -95,7 +95,7 @@
Όνομα
Διάρκεια
Συνολική διάρκεια: %s
- Καθόλου φάκελοι
+ Καθόλου φάκελοι
Μια απλή, λογική εφαρμογή αναπαραγωγής μουσικής για Android.
Φόρτωση μουσικής
Προβολή και έλεγχος αναπαραγωγής μουσικής
@@ -118,18 +118,10 @@
Pυθμός δειγματοληψίας
Φόρτωση της μουσικής συλλογής σας…
Παρακολούθηση της μουσικής συλλογής σας
- Η κατάσταση αναπαραγωγής αποθηκεύτηκε
- Εκκαθάριση κατάστασης αναπαραγωγής
- Εκκαθάριση της προηγούμενης αποθηκευμένης κατάστασης αναπαραγωγής (αν υπάρχει)
- Η κατάσταση αναπαραγωγής εκκαθαρίστηκε
- Η κατάσταση αναπαραγωγής επαναφέρθηκε
- Αποθήκευση κατάστασης αναπαραγωγής
- Αποθήκευση της τωρινής κατάστασης αναπαραγωγής τώρα
- Αποκατάσταση κατάσταση αναπαραγωγής
Επαναφόρτωση μουσικής
Σάουντρακ
Ζωντανά
- Φάκελοι μουσικής
+ Φάκελοι μουσικής
Μουσικο κομματι
Σύνθεση ζωντανών κομματιών
Σύνθεση ρεμίξ
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 924e8f202..99573f8c6 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -32,10 +32,9 @@
Agregado a la cola
Ir al artista
Ir al álbum
- Estado guardado
Agregar
Guardar
- Sin carpetas
+ Sin carpetas
Acerca de
Versión
Código fuente
@@ -71,8 +70,6 @@
Pausar al repetir
Pausar cuando se repite una canción
Contenido
- Guardar estado de reproducción
- Guardar el estado de reproducción ahora
Actualizar música
Recargar la biblioteca musical, utilizando las etiquetas en caché cuando sea posible
@@ -80,7 +77,7 @@
Falló la carga de música
Auxio necesita permiso para leer su biblioteca de música
No se encontró ninguna aplicación que pueda manejar esta tarea
- Directorio no soportado
+ Directorio no soportado
Buscar en la biblioteca…
@@ -95,7 +92,7 @@
Mover canción en la cola
Mover pestaña
Borrar búsqueda
- Quitar carpeta
+ Quitar carpeta
Icono de Auxio
Carátula de álbum
Carátula de álbum para %s
@@ -150,10 +147,6 @@
Ajuste sin etiquetas
Advertencia: Cambiar el pre-amp a un valor alto puede resultar en picos en algunas pistas de audio.
Reproducir desde el elemento que se muestra
- Modo
- Excluir
- La músicano se cargará de las carpetas que añadas.
- Incluir
Audio matroska
Free Losless Audio Codec (FLAC)
Advanced Audio Coding (AAC)
@@ -162,10 +155,8 @@
Géneros cargados: %d
Carga de música
Número de canciones
- Estado restaurado
Recarga automática
Recargar la biblioteca musical cada vez que cambie (requiere una notificación persistente)
- No es posible restaurar el estado
Cargando tu librería de música…
Cargando música
Monitorizando la librería de música
@@ -177,11 +168,8 @@
Frecuencia de muestreo
Cancelar
Reproducción automática con auriculares
- Restablecer el estado de reproducción
- Restablecer el estado de reproducción guardado previamente (si existe)
- Carpetas de música
- Gestionar de dónde se cargará la música
- La música solo se cargará de las carpetas que añadas.
+ Carpetas de música
+ Gestionar de dónde se cargará la música
Dinámico
Disco %d
Reproducción extendidas (EPs)
@@ -214,11 +202,8 @@
Single remix
Compilaciones
EP de remixes
- Eliminar el estado de reproducción guardado previamente (si existe)
Abrir la cola
Género
- Estado limpiado
- Limpiar el estado de reproducción
Separadores de varios valores
Excluye los archivos que no sean música
Configurar caracteres que denotan múltiples valores de la etiqueta
@@ -238,8 +223,8 @@
Detener la reproducción
Ignorar archivos de audio que no sean música, como podcasts
Advertencia: El uso de esta configuración puede dar lugar a que algunas etiquetas se interpreten incorrectamente como si tuvieran varios valores. Puede resolverlo anteponiendo a los caracteres separadores no deseados una barra invertida (\\).
- Alta calidad
- Rápido
+ Alta calidad
+ Rápido
Acción personalizada de la barra de reproducción
Saltar al siguiente
Mostrar solo artistas que estén acreditados directamente en un álbum (funciona mejor en bibliotecas bien etiquetadas)
@@ -249,8 +234,6 @@
- %d artistas
- %d artistas
- No se pudo guardar el estado
- No se puede borrar el estado
Borrar la caché de las etiquetas y recargar completamente la biblioteca musical (más lento, pero más completo)
Volver a escanear la música
%d seleccionado
@@ -259,14 +242,13 @@
%1$s, %2$s
Restablecer
Comportamiento
- Ganancia de la reproducción
+ Normalización del volumen
Controla cómo se cargan la música y las imágenes
Música
Imágenes
Configurar el comportamiento del sonido y la reproducción
Reproducción
- Carpetas
- Persistencia
+ Carpetas
Cambiar el tema y los colores de la aplicación
Personalizar los controles y el comportamiento de la interfaz de usuario
Biblioteca
@@ -276,7 +258,6 @@
Lista de reproducción
Ignorar artículos al ordenar
Ignorar palabras como \"the\" al ordenar por nombre (funciona mejor con música en inglés)
- Crear una nueva lista de reproducción
Nueva lista de reproducción
Lista de reproducción %d
Agregar a la lista de reproducción
@@ -334,4 +315,25 @@
¡Haga una donación al proyecto para que agreguen su nombre aquí!
Recordar la pausa
Permanecer en reproducción/pausa al saltar o editar la cola
-
\ No newline at end of file
+ Apagar
+ Iniciar reproducción
+ Inicia Auxio utilizando el estado previamente guardado. Si no hay ningún estado guardado, todas las canciones se reproducirán aleatoriamente. La reproducción comenzará inmediatamente.
+\n
+\nADVERTENCIA: Ten cuidado al controlar este servicio, si lo cierras y luego intentas usarlo de nuevo, probablemente se bloqueará la aplicación.
+ Más
+ Crear una incidencia en GitHub
+ Enviar un correo electrónico
+ Retroalimentación
+ Seleccionar carpetas
+ Álbum desconocido
+ Desconocido
+ El MPEG-4 contiene %s
+ Apple Lossless Audio Codec (ALAC)
+ Tus álbumes aparecerán aquí.
+ Tus artistas aparecerán aquí.
+ Tus géneros aparecerán aquí.
+ Nueva carpeta
+ Tus listas de reproducción aparecerán aquí.
+ Tus canciones aparecerán aquí.
+ Ahorra espacio
+
diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml
new file mode 100644
index 000000000..e6117970d
--- /dev/null
+++ b/app/src/main/res/values-et/strings.xml
@@ -0,0 +1,327 @@
+
+
+ Muusika on laadimisel
+ Jälgime muudatusi muusikakogus
+ Veel
+ Anna õigused
+ Pala
+ Albumid
+ Album elavas esituses muusikaga
+ EP
+ EP elavas esituses muusikaga
+ EP remiksidega
+ Singel
+ Singel elavas esituses muusikaga
+ Singel remiksidega
+ Kogumikud
+ Kogumik elavas esituses muusikaga
+ Kogumik remiksidega
+ Filmimuusika palad
+ Filmimuusika
+ Mixtape-kogumik
+ Mixtape
+ Demo
+ Elavas esituses
+ Esineb
+ Esitajaid
+ Žanr
+ Esitusloendid
+ Uus esitusloend
+ Imporditud esitusloend
+ Impordi esitusloend
+ Muuda nime
+ Kustuta
+ Otsi
+ Kõik
+ Nimi
+ Kuupäev
+ Palade arv
+ Plaat
+ Rada
+ Lisamise kuupäev
+ Suund
+ Kasvavalt
+ Esita järgmisena
+ Lisa esitusjärjekorda
+ Mine esitaja juurde
+ Mine albumi juurde
+ Pala teave
+ Asukoht
+ Vorming
+ Bitikiirus
+ Diskreetimissagedus
+ Lookohane esitusvaljuse tundlikkus
+ Albumikohane esitusvaljuse tundlikkus
+ Sega lood
+ Sega kõik lood
+ Alusta taasesitust
+ Katkesta
+ Salvesta
+ Lisa
+ Asukoha kuvamise viis
+ Absoluutne
+ Rakenduse teave
+ Versioon
+ Viki
+ Litsentsid
+ Muusikakogu statistika
+ Valik
+ Veateave
+ Kopeeritud lõikelauale
+ Teata veast
+ Autor
+ Arenduse eestvedaja Alexander Capehart
+ Toeta arendajat
+ Toetajad
+ Laadime sinu muusikakogu…
+ Jälgime muudatusi sinu muusikakogus…
+ Esitusloend on loodud
+ Esitusloend on imporditud
+ Esitusloendi nimi on muudetud
+ Esitusloend on eksporditud
+ Esitusloend on kustutatud
+ Lisasime esitusloendisse
+ Seadistused
+ Välimus ja tunnetus
+ Muuda rakenduse kujundust ja värve
+ Kujundus
+ Automaatne kujundus
+ Ümarad nurgad
+ Kasutajaliidese elementide puhul pruugi ümaraid nurki (eeldab, et ka kaanepiltide nurgad on ümarad)
+ Laadime muusikat
+ Lihtne ja ratsionaalne muusikamängija Androidi jaoks.
+ Album
+ Proovi uuesti
+ Palad
+ Kõik palad
+ Album remiksidega
+ EP-albumid
+ Singlid
+ Kogumik
+ Demo-kogumik
+ DJ-miksid
+ DJ-miks
+ Remiksid
+ Esitaja
+ Ekspordi esitusloend
+ Žanrid
+ Esitusloend
+ Tühi esitusloend
+ Muuda esitusloendi nime
+ Impordi
+ Ekspordi
+ Kas kustutame esitusloendi?
+ Muuda
+ Filtreeri
+ Kestus
+ Järjesta
+ Sega lood
+ Järjestuse alus
+ Kahanevalt
+ Hetkel esitamisel
+ Esita
+ Esitusjärjekord
+ Vaata teavet
+ Ekvalaiser
+ Lisa esitusloendisse
+ Vaata
+ Jaga
+ Suurus
+ Sobib
+ Suhteline
+ Kasuta Windowsiga ühilduvaid asukohti
+ Lähtekood
+ Vaata ja halda muusika esitamist
+ Lähtesta
+ Lisatud esitusjärjekorda
+ Kui soovid siin näha oma nime, siis toeta rahaliselt meie arendust!
+ Otsi oma muusikakogust…
+ Hele kujundus
+ Must kujundus
+ Tume kujundus
+ Tumeda kujunduse puhul kasuta päris musta kujundust
+ Värviteema
+ Purpurpunane
+ Sügav purpurpunane
+ Kollane
+ Rohekassinine
+ %d. esitusloend
+ Sinine
+ Sügavsinine
+ Free Lossless Audio Codec (FLAC)
+ Sinakasroheline
+ %d kbps
+ Punane
+ Indigosinine
+ Roheline
+ Sügavroheline
+ Pruun
+ Roosa
+ Hall
+ Laimiroheline
+ Oranž
+ Dünaamiline värv
+ %1$s, %2$s
+ -%.1f dB
+ %d valitud
+ Muudame esitusloendit %s
+ %d. plaat
+ +%.1f dB
+ %d Hz
+ Laadime sinu muusikakogu… (%1$d/%2$d)
+ Auxio käivitub varem salvestatud olekus. Kui sellist olekut ei leidu, siis segatakse kõikide lugude järjekord ning taasesitus algab koheselt.
+\n
+\nHOIATUS: Ole selle teenuse kasutamisel hoolikas - kui sa ta sulged ja siis proovid uuesti kasutada, siis rakendus ilmselt jookseb kokku.
+ Kohanda endale sobivaks
+ Seadista kasutajaliidese juhtnuppe ning käitumist
+ Ekraan
+ Muusikakogu kaardid
+ Muuda muusikakogu kaartide nähtavust ja järjekorda
+ Pildid
+ Kaanepildid
+ Pole kasutusel
+ Kuva kõrgekvaliteedilisena
+ Kuva kiirelt
+ Näita kaanepildid teravate nurkadega
+ Heli
+ Kadreeri kaanepildid 1:1 küljesuhte alusel
+ Seadista heli ja taasesitust
+ Taasesitus
+ Automaatne esitus kõrvaklappidest
+ Kõrvaklappide ühendamisel alusta koheselt meedia mängimist (ei pruugi toimida kõikide seadmetega)
+ Kordamisel tee paus
+ Enne tagasi hüppamist keri tagasi
+ Enne eelmise palani tagasi hüppamist keri jooksev pala tagasi
+ Pala kordamisel tee esituse paus
+ Jäta paus meelde
+ Esitusjärjekorras vahelejätmisel või selle muutmisel jää senisesse olekusse (mängimine või paus)
+ Helivaljuse normaliseerimine
+ Esitusvaljuse tundlikkuse seadistamise strateegia
+ Pole kasutusel
+ Eelista lookohast esitusvaljuse tundlikkust
+ Eelista albumikohast esitusvaljuse tundlikkust
+ Kui album juba on esitamisel, siis eelista albumikohast esitusvaljuse tundlikkust
+ Esitusvaljuse tundlikkuse eelvõimendus
+ Kohandatud toimingud teavitusel
+ Kohandatud toimingud taasesitusribal
+ Esitamisel muusikakogust
+ Esita näidatud palast
+ Esita žanrist
+ Halda muusika ja piltide laadimist
+ Muusika
+ Muusikakogu automaatne uuendamine
+ Välista failid, kus pole muusikat
+ Järjesta muusikat nii, et arvesse ei lähe numbrid ja artiklid nagu „the“ (toimib kõige paremini ingliskeelse muusika puhul)
+ Peida kaasautorid
+ Hüppa järgmise juurde
+ Taasesituse viis
+ Esita albumist
+ Käitumine
+ Esitamisel pala detailsest vaatest
+ Esita kõikidest paladest
+ Esita esitajast
+ Esita samast palast
+ Jäta meelde segatud loend
+ Uue pala esitamisel jäta meelde segatud loendi olek
+ Sisu
+ Eira selliseid helifaile, kus pole muusikat (näiteks taskuhäälingud)
+ Uuenda muusikakogu alati, kui seal midagi muutub (eeldab püsiteavituse olemasolu)
+ Mitme väärtuse eraldajad siltides
+ Koma (,)
+ Semikoolon (;)
+ Pluss (+)
+ Ampersand (&)
+ Seadista tähemärke, mis eraldavad siltides mitut väärtust
+ Kaldkriips (/)
+ Hoiatus: selle seadistuse kasutamisel ei pruugi mitu väärtust siltides olla alati korralikult tuvastatud; seda olukorda saad proovida lahendada täiendava prefiksi lisamisega kurakaldkriipsu näol (\\).
+ Nutikas järjestamine
+ Näita vaid esitajaid, kes on otsesõnu nimetatud albumi autoriteks (eeldab, et muusikakogu on korralikult sildistatud)
+ Eelvõimenduse väärtus lisatakse taasesituse ajal olemasolevale valjuse seadistusele
+ Kohandamine siltide alusel
+ Kohandamine ilma siltides leiduvate väärtusteta
+ Hoiatus: kui muudad eelvõimenduse väärtuse suureks positiivseks väärtuseks, siis mõnede lugude puhul võib see tähendada liiga kõrgeid toone.
+ Muusikakogu
+ Muusika kaustad
+ Halda kaustu, kust otsime ja laadime muusikat
+ Kaustad
+ Värskenda muusika andmed
+ Laadi muusikakogu uuesti ning kui võimalik, siis kasuta puhverdatud silte
+ Laadi muusikakogu uuesti
+ Kustuta puhverdatud siltide andmed ja laadi muusikakogu tervikuna uuesti (aeglasem, aga täpsem tulemus)
+ Muusikat ei leidu
+ Muusika laadimine ei õnnestunud
+ Auxio vajab muusikakogu töötlemiseks õigust lugeda faile ja kaustu sinu nutiseadmes
+ Sellest failist ei õnnestu esitusloendit importida
+ Sellesse faili ei õnnestu esitusloendit eksportida
+ Ei leidu selle ülesande täitmiseks sobilikku rakendust
+ Lugu %d
+ Kaustu pole määratud
+ See kaust pole toetatud
+ Esita või peata
+ Muuda kordamise režiimi
+ Hüppa järgmise pala juurde
+ Hüppa viimase pala juurde
+ Lülita segamisrežiim sisse või välja
+ Lõpeta taasesitus
+ Ava esitusjärjekord
+ Liiguta seda vahelehte
+ Sega kõik palad
+ Eemalda see pala
+ Tõsta see pala teise kohta
+ Tühjenda otsinguajalugu
+ Eemalda kaust
+ Auxio ikoon
+ Albumi kaanepilt
+ %s albumi kaanepilt
+ %s esitaja pilt
+ %s žanri pilt
+ %s esitusloendi pilt
+ Valiku pilt
+ Tundmatu esitaja
+ Lugu ei leidu
+ Tundmatu žanr
+ Kuupäeva pole
+ Plaati pole nimetatud
+ Palasid ei leidu
+ Albumeid ei leidu
+ Muusika pole esitamisel
+ Ogg Vorbis audio
+ MPEG-1 audio
+ MPEG-4 audio
+ Matroska audio
+ Advanced Audio Coding (AAC)
+ Laaditud palasid: %d
+ Kas kustutame %s? Seda tegevust ei saa tagasi pöörata.
+
+ - %d pala
+ - %d pala
+
+ Laaditud albumeid: %d
+ Laaditud esitajaid: %d
+ Laaditud žanre: %d
+ Kestus kokku: %s
+
+ - %d album
+ - %d albumit
+
+
+ - %d esitaja
+ - %d esitajat
+
+ Veel
+ Tagasiside
+ Koosta GitHubis veateade või ettepanek
+ Saada e-kiri
+ Vali kaustad
+ Tundmatu album
+ MPEG-4 %s koodekiga
+ Apple Lossless Audio Codec (ALAC)
+ Teadmata
+ Uus kaust
+ Säästa ruumi
+ Sinu muusikapalad saavad olema nähtavad siin.
+ Sinu esitajad saavad olema nähtavad siin.
+ Sinu žanrid saavad olema nähtavad siin.
+ Sinu albumid saavad olema nähtavad siin.
+ Sinu esitusloendid saavad olema nähtavad siin.
+
\ No newline at end of file
diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml
index 63f4df9b6..c10ce2e6c 100644
--- a/app/src/main/res/values-fi/strings.xml
+++ b/app/src/main/res/values-fi/strings.xml
@@ -52,9 +52,6 @@
Tallenna
Palauta oletus
Lisää
- Tila tallennettu
- Tila tyhjennetty
- Tila palautettu
Valvotaa musiikkikirjastoa muutosten varalta…
Sekoita
Sekoita kaikki
@@ -65,7 +62,7 @@
Muuta kirjastovälilehtien näkyvyyttä ja järjestystä
Siirry seuraavaan
Kertaustila
- Kirjastosta toistettaessa
+ Kun toistetaan kirjastosta
Kohteen tiedoista toistettaessa
Muista sekoitus
Toista kaikista kappaleista
@@ -73,44 +70,32 @@
Toista tyylilajista
Moniarvoerottimet
Ohita äänitiedostot, jotka eivät ole musiikkia, kuten podcastit
- Ja-merkki (&)
+ Ampersand (&)
Pilkku (,)
Plus (+)
Puolipiste (;)
Älykäs järjestys
Piilota avustajat
Pois
- Nopea
- Korkea laatu
+ Nopea
+ Korkea laatu
Ääni
Määritä äänen ja toiston toimintaa
Toisto
Suosi kappaletta
ReplayGainin esivahvistus
Kirjasto
- Musiikkikansiot
- Määritä mistä musiikki tulee ladata
+ Musiikkikansiot
+ Määritä mistä musiikki tulee ladata
Läpikäy musiikki uudelleen
- Tila
- Ohita
- Sisällytä
- Musiikkia ladataan vain lisäämistäsi kansioista.
- Tallenna toiston tila
- Pysyvyys
- Tyhjennä toiston tila
- Palauta toiston tila
- Tyhjennä aiemmin tallennettu toiston tila (jos olemassa)
Tähän tehtävään kykenevää sovellusta ei löytynyt
- Ei kansioita
- Tilaa ei voi palauttaa
- Tilaa ei voi tyhjentää
+ Ei kansioita
Raita %d
Siirry seuraavaan kappaleeseen
Muuta kertaustilaa
- Luo uusi soittolista
Pysäytä toisto
Avaa jono
- Poista kansio
+ Poista kansio
Auxion kuvake
Albumin %s kansi
Tyylilajin %s kuva
@@ -186,7 +171,7 @@
Musiikkia ei löytynyt
Wiki
Harmaa
- Muuta sovellukse teemaa ja värejä
+ Muuta sovelluksen teemaa ja värejä
Teema
Automaattinen
Vaalea
@@ -207,26 +192,24 @@
- %d esittäjää
Nousevasti
- Toista seuraava
+ Toista seuraavaksi
Siirry esittäjään
Sisältö
Musiikki
Kuvat
Albumikannet
- ReplayGain
+ Äänenvoimakkuuden normalisointi
Suosi albumia
ReplayGain-strategia
Automaattinen uudelleenlataus
Automaattitoisto kuulokkeilla
Aloita aina toisto, kun kuulokkeet yhdistetään (ei välttämättä toimi kaikilla laitteilla)
- Tallenna nykyinen toiston tila
Siirry viimeiseen kappaleeseen
- Kansiot
+ Kansiot
Toista tai keskeytä
- Tämä kansio ei ole tuettu
+ Tämä kansio ei ole tuettu
Sekoitus päällä/pois
Sekoita kaikki kappaleet
- Tilaa ei voi tallentaa
Siirry tätä välilehteä
Tyhjennä hakuehto
Esittäjän %s kuva
@@ -236,8 +219,6 @@
Musiikkia ei toisteta
Toista esittäjältä
Ohita muu kuin musiikki
- Palauta aiemmin tallennettu toiston tila (jos olemassa)
- Musiikkia ei ladata valitsemistasi kansioista.
Suosi albumia, jos sellaista toistetaan
Uusi soittolista
Soittolista %d
@@ -300,4 +281,45 @@
Soittolistan tuonti tästä tiedostosta ei onnistu
Soittolistan vienti tähän tiedostoon ei onnistu
Valintakuva
-
\ No newline at end of file
+ Pois
+ Mukautettu toistopalkin toiminto
+ Aloita toisto
+ Mixtapet
+ Mixtape
+ Näytä vain esittäjät, jotka on suoraan osoitettu jonkin albumin tekijäksi (toimii parhaiten hyvin tagatuissa kirjastoissa)
+ Kelaa ennen takaisinsiirtymistä
+ Toista kappale itsenään
+ Varoitus: Tämän asetuksen käyttäminen voi johtaa joidenkin tagien virheelliseen tulkintaan useina arvoina. Voit ratkaista tämän liittämällä kenoviivan (\\) ei-toivottujen erotinmerkkien eteen.
+ Järjestä nimet, jotka alkavat numerolla tai sanoilla kuten \"the\", oikein (toimii parhaiten englanninkielisellä musiikilla)
+ Keskeytä uudelleentoistettaessa
+ Keskeytä kun kappale toistetaan uudelleen
+ Toista näytetystä kohteesta
+ Kelaa takaisin ennen edelliseen kappaleeseen siirtymistä
+ Demo
+ Demot
+ Virhetiedot
+ Pidä sekoitus päällä kun toistetaan uutta kappaletta
+ Pysy toisto/keskeytystilassa ohittaessa kappaleita tai muokatessa jonoa
+ Valitse merkit, jotka erottavat tagiarvot toisistaan
+ Varoitus: Esivahvistuksen asettaminen korkeaan positiiviseen arvoon saattaa johtaa säröilyyn joissain kappaleissa.
+ Valitse kansiot
+ Ilmoita viasta GitHubissa
+ Lähetä sähköpostia
+ Lisää
+ Palaute
+ Käynnistää Auxion käyttämällä aiemmin tallennettua tilaa. Jos tallennettua tilaa ei ole saatavilla, kaikki kappaleet sekoitetaan. Toisto alkaa välittömästi.\n\nVaroitus: Ole varovainen tämän palvelun hallinnassa. Jos suljet sen ja yrität sitten käyttää sitä uudelleen, sovellus todennäköisesti kaatuu.
+ Säätö ilman tunnisteita
+ Esivahvistinta käytetään olemassa olevaan säätöön toiston aikana
+ Säätö tunnisteilla
+ Tuntematon albumi
+ Uusi kansio
+ MPEG-4 sisältäen %s
+ Tuntematon
+ Kappaleesi tulevat näkymään tässä.
+ Albumisi tulevat näkymään tässä.
+ Esittäjäsi tulevat näkymään tässä.
+ Soittolistasi tulevat näkymään tässä.
+ Tyylilajisi tulevat näkymään tässä.
+ Apple Lossless Audio Codec (ALAC)
+ Säätä tilaa
+
diff --git a/app/src/main/res/values-fil/strings.xml b/app/src/main/res/values-fil/strings.xml
index b65baaf8f..f7c9d50a7 100644
--- a/app/src/main/res/values-fil/strings.xml
+++ b/app/src/main/res/values-fil/strings.xml
@@ -50,8 +50,6 @@
Laki
Tulin ng mga bit
Tulin ng sample
- Naibalik ang kalagayan
- Na-save ang kalagayan
Haluin
Idagdag
I-save
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 839ea4dce..bff206f2b 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -72,7 +72,6 @@
- %d albums
Format
- État sauvegardé
Mélanger
OK
Statistiques de la bibliothèque
@@ -111,7 +110,7 @@
Compilation
Live
Chargement de la musique
- Suivre la librairie musicale
+ Suivre la bibliothèque musicale
EP
EP
Singles
@@ -132,14 +131,14 @@
Genre inconnu
Dynamique
Cyan
- Aucun dossier
- Supprimer le dossier
+ Aucun dossier
+ Supprimer le dossier
Artiste inconnu
Compilation en direct
Compilation de remix
Mix DJ
Mix DJ
- Ce dossier n\'est pas pris en charge
+ Ce dossier n\'est pas pris en charge
Réinitialiser
Ogg audio
Violet foncé
@@ -149,12 +148,10 @@
Audio MPEG-4
Pas de date
Couverture de l\'album pour %s
- État effacé
Surveillance de votre bibliothèque musicale pour les changements…
Couvertures arrondies
Activer les coins arrondis sur des éléments d\'interface utilisateur supplémentaires (nécessite que les couvertures d\'album soient arrondies)
Descendant
- Etat restauré
Personnaliser les commandes et le comportement de l\'interface utilisateur
Passer au suivant
Mode répétition
@@ -172,13 +169,13 @@
Contrôler le chargement de la musique et des images
Musique
Images
- Qualité améliorée (chargement lent)
+ Qualité améliorée (chargement lent)
Configurer les caractères qui indiquent plusieurs valeurs de balise
Pochettes d\'albums
Masquer les collaborateurs
Afficher uniquement les artistes qui sont directement crédités sur un album (fonctionne mieux sur les bibliothèques bien étiquetées)
Désactivé
- Couvertures originales (téléchargement rapide)
+ Couvertures originales (téléchargement rapide)
Configurer le son et le comportement de lecture
Listes de lecture
Lors de la lecture depuis la bibliothèque
@@ -195,50 +192,36 @@
Lire depuis l\'album
Barre oblique (/)
Plus (+)
- Vider l\'état de lecture précédemment enregistré (s\'il existe)
Ajustement avec étiquettes
- Dossiers de musique
- Gérer d\'où la musique doit être chargée
+ Dossiers de musique
+ Gérer d\'où la musique doit être chargée
Lecture
- Persistance
- Vider l\'état de lecture
Toujours commencer la lecture lorsqu\'un périphérique audio est connecté (pourrait ne pas fonctionner sur tous les appareils)
Stratégie de normalisation de volume
Par chanson
Par album
- Dossiers
+ Dossiers
Par album si un album est en lecture
Bibliothèque
- La musique sera uniquement chargée des dossiers ajoutés.
- Inclure
Actualiser la musique
Effacer le cache des étiquettes et recharger entièrement la bibliothèque musicale (lent, mais plus complet)
Aucune application trouvée qui puisse gérer cette tâche
- Impossible de restaurer l\'état
- Rétablir l\'état de lecture
Auxio a besoin de permissions pour lire votre bibliothèque musicale
Tri intelligent
Ignorer les nombres ou certains mots comme \"the\" en début de nom lors du tri (fonctionne au mieux avec de la musique en anglais)
- Les dossiers de musique ajoutés ne seront pas chargés.
Scanner à nouveau la musique
Ajustement sans étiquettes
- Enregistrer l\'état de lecture actuel maintenant
- Rétablir l\'état de lecture enregistré précédemment (s\'il existe)
Volume normalisé
Le préampli est appliqué à l\'ajustement actuel durant la lecture
- Enregistrer l\'état de lecture
Lecture automatique avec casque audio
Normalisation de volume par préampli
Recharger la bibliothèque musicale en utilisant si possible les étiquettes en cache
- Mode
- Exclure
Nouvelle liste de lecture
Passer à la chanson suivante
Activer ou désactiver la lecture aléatoire
%d Hz
Passer à la dernière chanson
Ajouter à la liste de lecture
- Créer une nouvelle liste de lecture
Audio Matroska
Artistes chargés : %d
Rembobiner avant de revenir en arrière
@@ -249,7 +232,6 @@
Pause quand une chanson se répète
Déplacer cet onglet
Renommer
- Impossible d\'effacer l\'état
Modifier le mode de répétition
Albums chargés : %d
Durée totale : %s
@@ -273,17 +255,16 @@
%d ko/s
+%.1f dB
-%.1f dB
- Supprimer %s \? Cette opération ne peut pas être annulée.
+ Supprimer %s ? Cette opération ne peut pas être annulée.
Avertissement : Le fait de régler le préamplificateur sur une valeur positive élevée peut entraîner l\'apparition des distortions sur certaines pistes audio.
Liste de lecture %d
Apparaît sur
Renommer la liste de lecture
- Supprimer la liste de lecture \?
+ Supprimer la liste de lecture ?
Partager
Retirer cette chanson
Déplacer cette chanson
Ouvrir la file d\'attente
- Impossible de sauvegarder l\'état
Aucune chanson
Modification de %s
Genres chargés : %d
@@ -332,4 +313,12 @@
Absolu
Relatif
Utiliser les chemins compatibles Windows
-
\ No newline at end of file
+ Désactivé
+ Démarrer la lecture
+ Démarre Auxio en utilisant le dernier état enregistré. S\'il n\'y en a aucun, tous les titres seront remélangés et la lecture commencera immédiatement.\n\nAVERTISSEMENT : Faites attention en contrôlant ce service, si vous le fermez puis essayez de l\'utiliser à nouveau, vous ferez probablement planter l\'appli.
+ Plus
+ Avis
+ Faire un ticket sur GitHub
+ Envoyer un courriel
+ Choisir des dossiers
+
diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml
index 50748355f..765e7879d 100644
--- a/app/src/main/res/values-gl/strings.xml
+++ b/app/src/main/res/values-gl/strings.xml
@@ -60,13 +60,10 @@
Tamaño
Tasa de bits
De acordo
- Estado restablecido
Cambiar o tema e as cores da aplicación
Cancelar
- Estado gardado
Buscando a túa biblioteca…
Automático
- Estado limpado
Código fonte
Wiki
Tema
@@ -109,10 +106,9 @@
- %d cancións
Son
- Alta calidade
- Cartafois de música
- Excluír
- Este cartafol non está soportado
+ Alta calidade
+ Cartafois de música
+ Este cartafol non está soportado
Reproducir ou pausar
Saltar á seguinte canción
Monitorizando a biblioteca de música
@@ -146,7 +142,7 @@
Imaxes
Portadas de álbums
Apagado
- Rápido
+ Rápido
Configurar o comportamento de son e reprodución
Reprodución
Reprodución automática con auriculares
@@ -159,27 +155,18 @@
Axuste con etiquetas
Axuste sen etiquetas
Advertencia: O cambio do pre-amp a un valor alto positivo pode resultar en picos en nalgunhas pistas.
- A música só se cargará dende os cartafois que engadas.
Biblioteca
- A música non se cargará dende os cartafois que engadas.
- Incluír
Actualizar música
Recargar a biblioteca de música, utilizando as etiquetas na caché cando sexa posible
Volver a escanear a música
Borrar a caché das etiquetas a recargar completamente a biblioteca de música (máis lento, pero máis completo)
- Persistencia
- Limpar o estado de reprodución
- Eliminar o estado de reprodución anterior (se existe)
- Imposible restaurar o estado
- Imposible borrar o estado
- Imposible gardar o estado
Cambiar o modo de repetición
Activar ou desactivar a mezcla
Mezclar todas as cancións
Deter a reprodución
Abrir a cola
Borrar o historial de busca
- Quitar cartafol
+ Quitar cartafol
Icona de Auxio
Portada de álbum
Portada de álbum para %s
@@ -194,7 +181,7 @@
Fallou a carga de música
Auxio necesita permiso para leer a túa biblioteca de música
Non se atopou ningunha aplicación que poda facer esta tarefa
- Sen cartafois
+ Sen cartafois
Audio ogg
Advanced Audio Coding (AAC)
Free Lossless Audio Codec (FLAC)
@@ -238,16 +225,11 @@
Máis (+)
Ignorar palabras como \"the\" ao ordenar por nome (funciona mellor con música en inglés)
- Modo
- Xestionar dende onde se carga a música
+ Xestionar dende onde se carga a música
Pausar cando se repite unha canción
- Cartafois
- Gardar o estado de reprodución
- Gardar o estado actual de reprodución agora
- Restablecer o estado de reprodución gardado previamente (se existe)
+ Cartafois
Ningunha pista
Saltar á última canción
- Restablecer o estado de reprodución
Sen música
Pista %d
Audio Matroska
diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml
index f40159028..bba044624 100644
--- a/app/src/main/res/values-hi/strings.xml
+++ b/app/src/main/res/values-hi/strings.xml
@@ -98,8 +98,6 @@
%s हटाएँ\? इसे पूर्ववत नहीं किया जा सकता।
लोड किए गए गाने: %d
अवरोही
- स्थिति साफ की गई
- स्थिति सहेजी गई
लायब्रेरी टैब की दृश्यता और क्रम बदलें
संगीत
UI नियंत्रण और व्यवहार अनुकूलित करें
@@ -175,7 +173,6 @@
दिखाई देता है
साझा करें
शफल करें
- स्थिति बहाल
प्लेलिस्ट का नाम बदला गया
अलेक्जेंडर कैपहार्ट द्वारा विकसित
एकाधिक टैग मानों को निरूपित करने वाले वर्ण कॉन्फ़िगर करें
@@ -185,20 +182,19 @@
संपादन %s
पलॅस (+)
ऐंपरसैंड (&)
- मोड
एल्बम कवर
%s के लिए एल्बम कवर
नीला हरा
कोई संगीत नहीं बज रहा
अंतिम गीत पर जाएँ
- रीप्ले गेन
+ वॉल्यूम नार्मलाईज़ेशन
रीप्ले गेन रणनीति
पिछले गाने को छोड़ने से पहले रिवाइंड करें
%s के लिए शैली छवि
उन्नत ऑडियो कोडिंग (AAC)
गहरा हरा
बैंगनी
- संगीत फ़ोल्डर
+ संगीत फ़ोल्डर
दोहराने पर विराम
बंद
सभी एल्बम को 1: 1 पहलू अनुपात में कवर करें
@@ -206,17 +202,11 @@
जब कोई गीत दोहराया जाता है तो रुक जाएं
रीप्लेगेन प्री-एम्प
टैग के साथ समायोजन
- फ़ोल्डर
- बाहर करें
- पर्सिस्टेंस
- वर्तमान प्लेबैक स्थिति को अभी सहेजें
- पहले से सहेजी गई प्लेबैक स्थिति को पुनर्स्थापित करें (यदि कोई हो)
+ फ़ोल्डर
संगीत लोड करना विफल रहा
- यह फ़ोल्डर समर्थित नहीं है
- स्थिति पुनर्स्थापित करने में असमर्थ
+ यह फ़ोल्डर समर्थित नहीं है
रिपीट मोड बदलें
शफ़ल चालू या बंद करें
- एक नई प्लेलिस्ट बनाएं
सभी गीत शफ़ल करें
प्लेबैक बंद करो
इस गीत को इस स्थानांतरित करें
@@ -233,9 +223,7 @@
स्लेटी
%s के लिए प्लेलिस्ट छवि
तिथि नहीं
- आपके द्वारा जोड़े गए फ़ोल्डरों से संगीत लोड नहीं किया जाएगा।
- संगीत केवल आपके द्वारा जोड़े गए फ़ोल्डरों से लोड किया जाएगा।
- प्रबंधित करें कि संगीत कहाँ से लोड किया जाना चाहिए
+ प्रबंधित करें कि संगीत कहाँ से लोड किया जाना चाहिए
%s के लिए कलाकार छवि
MPEG-4 ऑडियो
छवियां
@@ -252,11 +240,9 @@
देखें
चेतावनी: इस सेटिंग का उपयोग करने से कुछ टैग को गलत तरीके से एकाधिक मान के रूप में व्याख्या किया जा सकता है. आप बैकस्लैश (\\) के साथ अवांछित विभाजक वर्णों को उपसर्ग करके इसे हल कर सकते हैं।
केवल उन कलाकारों को दिखाएँ जिन्हें सीधे एल्बम पर श्रेय दिया जाता है (अच्छी तरह से टैग की गई लाइब्रेरी पर अच्छा काम करता है)
- तेज
- उच्च गुणवत्ता
- स्थिति साफ़ करने में असमर्थ
- फ़ोल्डर हटाएँ
- स्थिती को सहेजने में असमर्थ
+ तेज
+ उच्च गुणवत्ता
+ फ़ोल्डर हटाएँ
यह गीत हटाओ
डिस्क नहीं
निःशुल्क दोषरहित ऑडियो कोडेक (FLAC)
@@ -265,9 +251,7 @@
वर्गीकृत एल्बम कवर फोर्स करें
ध्वनि और प्लेबैक व्यवहार कॉन्फ़िगर करें
चेतावनी: प्री-एम्प को उच्च सकारात्मक मान में बदलने से कुछ ऑडियो ट्रैक पर आवाज फट सकती है।
- शामिल करें
जब संभव हो तो कैश्ड टैग का उपयोग करके संगीत लाइब्रेरी को पुनः लोड करें
- पहले से सहेजी गई प्लेबैक स्थिति साफ़ करें (यदि कोई हो)
कोई ऐप नहीं मिला जो इस कार्य को संभाल सके
अज्ञात कलाकार
इस टैब को स्थानांतरित करें
@@ -278,11 +262,8 @@
ट्रैक को प्राथमिकता दें
टैग कैश साफ़ करें और संगीत लाइब्रेरी को पूरी तरह पुनः लोड करें (धीमी, लेकिन अधिक पूर्ण)
Auxio को आपकी संगीत लाइब्रेरी पढ़ने के लिए अनुमति की आवश्यकता है
- कोई फ़ोल्डर नहीं
+ कोई फ़ोल्डर नहीं
भूरा
- प्लेबैक स्थिति सहेजें
- प्लेबैक स्थिति साफ़ करें
- प्लेबैक स्थिति पुनर्स्थापित करें
पीला
नींबू रंग
हेडसेट कनेक्ट होने पर हमेशा चलाना शुरू करें (सभी उपकरणों पर काम नहीं करेगा)
@@ -329,4 +310,9 @@
अपना नाम यहां जुड़वाने के लिए परियोजना में दान करें!
विराम याद रखें
कतार छोड़ते या संपादित करते समय चलता/रोका रखिए
+ बंद
+ प्लेबैक शुरू करें
+ पहले से सहेजे गए स्टेट का उपयोग करके Auxio को शुरू करता है। यदि कोई सहेजा हुआ स्टेट उपलब्ध नहीं है, तो सभी गाने शफल पर चलेंगे और प्लेबैक तुरंत शुरू हो जाएगा।
+\n
+\nचेतावनी: इस सेवा को नियंत्रित करते समय सावधान रहें, यदि आप इसे बंद करके फिर से उपयोग करने का प्रयास करते हैं, तो संभवतः ऐप क्रैश हो जाएगा।
\ No newline at end of file
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index 73c4b3fff..d5cea9257 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -9,14 +9,14 @@
Albumi
Album
Album uživo
- Album remixa
- Kompilacije za promidžbu
- Kompilacija za promidžbu
- Kompilacija za promidžbu uživo
- Remiks kompilacije za promidžbu
+ Album remiksa
+ Mini-albumi
+ Mini-album
+ Mini-album uživo
+ Mini-album remiksa
Singlovi
Kompilacije
- Kompliacija
+ Kompilacija
Zvučni zapis
Muzičke zbirke
Muzička zbirka
@@ -37,15 +37,13 @@
Trenutačno svira
Reproduciraj
Promiješaj
- Popis pjesama
+ Redoslijed
Reproduciraj sljedeću
Svojstva pjesme
Format
Veličina
Brzina prijenosa
Frekvencija
- Stanje spremljeno
- Stanje vraćeno
Izmiješaj
Izmiješaj sve
U redu
@@ -56,7 +54,7 @@
Inačica
Izvorni kod
Licencije
- Programer: Alexander Capehart
+ Alexander Capehart
Statistika zbirke
Izgled
Svjetla
@@ -65,18 +63,18 @@
Crna tema
Koristi potpuno tamnu temu
Prikaz
- Pločice zbirke
- Promijeni vidljivost i redoslijed pločica zbirke
+ Kartice zbirke
+ Promijeni vidljivost i redoslijed kartica zbirke
Zaobljena naslovnica
Zvuk
Slušalice: odmah reproduciraj
Uvijek pokreni reprodukciju kada su slušalice povezane (možda neće raditi na svim uređajima)
- ReplayGain strategija
+ Strategija normalizacije glasnoće
Preferiraj zvučni zapis
Preferiraj album
- Ako se reproducira album, preferiraj album
- ReplayGain pretpojačalo
- Pretpojačalo je tijekom reprodukcije primijenjeno postojećoj prilagodbi
+ Preferiraj album ako se jedan reproducira
+ Pretpojačalo normalizacije glasnoće
+ Pretpojačalo se tijekom reprodukcije primijenjue na postojeću podešavanje
Prilagođavanje s oznakama
Prilagođavanje bez oznaka
Upozorenje: Postavljanje pretpojačala na visoke razine može uzrokovati vrhunce tonova u nekim zvučnim zapisima.
@@ -85,31 +83,25 @@
Reproduciraj iz svih pjesama
Aktualiziraj glazbu
Ponovo učitaj glazbenu biblioteku, koristeći predmemorirane oznake kada je to moguće
- Mape glazbe
- Upravljaj odakle će se glazba učitati
- Način
- Isključi
- Glazba se neće učitati iz dodanih mapa.
- Uključi
- Glazba će se učitati samo iz dodanih mapa.
+ Mape glazbe
+ Upravljaj odakle će se glazba učitati
Automatsko ponovno učitavanje
Ponovo učitaj svoju zbirku glazbe čim se dogode promjene (zahtijeva stalno obavještavanje)
Nijedna glazba nije pronađena
Greška u učitvanju glazbe
Auxio treba dozvolu za čitanje tvoje zbirke glazbe
Nijedna aplikacija ne može obraditi ovaj zadatak
- Nema mapa
- Ova mapa nije podržana
- Nije moguće obnoviti stanje
+ Nema mapa
+ Ova mapa nije podržana
Pretraži svoju zbirku …
Zvučni zapis %d
Omogućite ili onemogućite miješanje
Izmiješaj sve pjesme
Ukoni ovu pjesmu iz popisa pjesama
Premjesti ovu pjesmu u popisu pjesama
- Pomakni ovu pločicu
+ Pomakni ovu karticu
Izbriši pretražene pojmove
- Ukloni mapu
+ Ukloni mapu
Auxio logotip
Omot albuma
Omot albuma za %s
@@ -124,7 +116,7 @@
MPEG-4 zvuk
Ogg zvuk
Napredno kodiranje zvuka (AAC)
- Slobodan audio kodek bez gubitaka (FLAC)
+ Slobodan kodek zvuka bez gubitaka (FLAC)
Crvena
Ružičasta
Tamnoljubičasta
@@ -146,7 +138,7 @@
−%.1f dB
%d kbps
%d Hz
- Učitavanje tvoje zbirke blazbe … (%1$d/%2$d)
+ Učitavanje tvoje zbirke glazbe … (%1$d/%2$d)
Broj učitanih pjesama: %d
Broj učitanih albuma: %d
Broj učitanih izvođača: %d
@@ -174,8 +166,8 @@
Zvučni zapisi
Traži
Sve
- Dodaj u popis pjesama
- Dodano u popis pjesama
+ Dodaj u redoslijed
+ Dodano u redoslijed izvođenja
Pogledaj svojstva
Idi na izvođača
Idi na album
@@ -185,29 +177,22 @@
Automatski
Koristi alternativnu radnju za obavijest
Omogući zaobljene rubove na dodatnim elementima korisničkog sučelja (zahtijeva zaobljene omote albuma)
- Ponašanje
+ Prilagodi
Premotaj prije preskakanja natrag
- Spremi trenutno stanje reprodukcije
Preskoči na sljedeću pjesmu
Reproduciraj iz prikazanog predmeta
Zapamti miješanje glazbe
- Vrati prethodno spremljeno stanje reprodukcije (ako postoji)
Reproduciraj iz albuma
Pauziraj pri ponavljanju pjesme
Premotaj prije vraćanja na prethodnu pjesmu
Reproduciraj ili pauziraj
Pauziraj pri ponavljanju
Sadržaj
- Spremi stanje reprodukcije
- Vrati stanje reprodukcije
Preskoči na prethodnu pjesmu
Promijeni način ponavljanja
Ljubičasto
- Matroska Zvuk
- Izbriši stanje reprodukcije
- Stanje izbrisano
- Izbriši prethodno stanje reprodukcije (ako postoji)
- Otvori popis pjesama
+ Matroska zvuk
+ Otvori redoslijed
Žanr
Zarez (,)
Znak i (&)
@@ -229,14 +214,12 @@
Isključeno
Isključi sve što nije glazba
Reproduciraj iz izvođača
- Visoka kvaliteta
- Brzo
+ Visoka kvaliteta
+ Srednja kvaliteta
Zanemari sve audio datoteke koje nisu glazba, npr. podcast datoteke
Upozorenje: Korištenje ove postavke može dovesti do pogrešnog tumačenja nekih oznaka kao da imaju više vrijednosti. To možeš riješiti postavljanjem obrnute kose crte (\\) ispred neželjenih znakova rastavljanja.
Omoti albuma
Prikaži samo izvođače koji su izravno navedeni na albumu
- Nije moguće očistiti stanje
- Nije moguće spremiti stanje
- %d izvođač
- %d izvođača
@@ -249,25 +232,23 @@
Wiki
%1$s, %2$s
Resetiraj
- ReplayGain
- Mape
+ Normalizacija glasnoće
+ Mape
Silazno
Promijenite temu i boje aplikacije
Prilagodite kontrole i ponašanje korisničkog sučelja
Upravljajte učitavanjem glazbe i slika
Slike
- Konfigurirajte ponašanje zvuka i reprodukcije
+ Konfiguriraj ponašanje zvuka i reprodukcije
Reprodukcija
Fonoteka
- Status reprodukcije
Popisi pjesama
Popis pjesama
Glazba
Slika popisa pjesama za %s
Ponašanje
Pametno razvrstavanje
- Ispravno razvrstaj imena koja počinju brojevima ili riječima poput „the” (najbolje radi s glazbom na engleskom jeziku)
- Stvori novi popis pjesama
+ Ispravno razvrstaj imena koja počinju s brojevima ili riječima poput „the” (najbolje radi s glazbom na engleskom jeziku)
Novi popis pjesama
Dodaj u popis pjesama
Nema pjesama
@@ -283,7 +264,7 @@
Uredi
Izbrisati %s\? To je nepovratna radnja.
Uređivanje popisa pjesama %s
- Sudjelovanja:
+ Sudjeluje u
Dijeli
Nema diska
Prisili kvadratične omote albuma
@@ -297,7 +278,7 @@
Odabir
Više
Podaci greške
- Prijavi grešku
+ Prijavi
Kopirano
Nema albuma
Uvezen popis pjesama
@@ -321,8 +302,29 @@
Koristi Windows kompatibilne putanje
Popis pjesama je uvezen
Popis pjesama je izvezen
- Podešavanje ReplayGain pjesme
- Podešavanje ReplayGain albuma
+ Podešavanje normalizacije glasnoće pjesme
+ Podešavanje normalizacije glasnoće albuma
Zapamti pauzu
- Nastavi reprodukciju/pauziranje prilikom preskakanja ili uređivanja slijeda
-
\ No newline at end of file
+ Nastavi reprodukciju/pauziranje prilikom preskakanja ili uređivanja redoslijeda
+ Isključeno
+ Pokreni reprodukciju
+ Pokreće Auxio koristeći prethodno spremljeno stanje. Ako nijedno spremljeno stanje nije dostupno, sve će se pjesme nasumično reproducirati. Reprodukcija će započeti odmah.
+\n
+\nUPOZORENJE: Oprez pri upravljanju ovom uslugom. Ako je zatvoriš i zatim je pokušaš ponovo koristiti, aplikacija će se vjerojatno zatvoriti.
+ Više
+ Povratne informacije
+ Prijavi problem na GitHubu
+ Pošalji e-mail
+ Odaberi mape
+ Appleov kodek zvuka bez gubitaka (ALAC)
+ Nepoznat album
+ Nepoznato
+ MPEG-4 sadrži %s
+ Niža kvaliteta
+ Nova mapa
+ Tvoje pjesme će se ovdje prikazati.
+ Tvoji albumi će se ovdje prikazati.
+ Tvoji izvođači će se ovdje prikazati.
+ Tvoji popisi pjesama će se ovdje prikazati.
+ Tvoji žanrovi će se ovdje prikazati.
+
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index a994adbda..ea06a90e6 100644
--- a/app/src/main/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -9,7 +9,7 @@
Dalok
Összes dal
Keresés
- Szűrő
+ Szűrés
Összes
Rendezés
Növekvő
@@ -45,7 +45,7 @@
Lejátszás/szünet
Vörös
- Pink
+ Rózsaszín
Bíbor
Mély bíbor
Indigókék
@@ -62,11 +62,11 @@
- %d dal
- - %d dal
+ - %d dalok
- %d album
- - %d album
+ - %d albumok
EP-k
EP
@@ -82,8 +82,8 @@
Ismétlő mód módosítása
Keverés be/ki kapcsolása
%s album borítója
- Visszajátszás
- Mappa eltávolítása
+ Hangerő normalizálás
+ Mappa eltávolítása
Lejátszólistához ad
Formátum
Wiki
@@ -95,10 +95,9 @@
Visszajátszás stratégia
Inkább album
Inkább hangsáv
- Ez a mappa nem támogatott
+ Ez a mappa nem támogatott
%s playlista képe
Nincs hangsáv
- Az aktuális lejátszási állapot mentése most
Nincs dal
MPEG-1 hang
MPEG-4 hang
@@ -113,14 +112,13 @@
DJ Mix
Műfaj
Dal tulajdonságai
- Nincs mappa
+ Nincs mappa
Tiszta fekete sötét téma használata
Dinamikus
Album borítók
Visszatekerés az előző dalra való ugrás előtt
Figyelem: Az előerősítő magas pozitív értékre módosítása egyes hangsávoknál csúcsosodást eredményezhet.
Könyvtár
- Állapot
Lejátszólista
Lejátszólisták
Töröl
@@ -134,7 +132,6 @@
Új dal lejátszásakor a keverési mód bekapcsolva tartása
A számokkal vagy \"the\" típusú szavakkal kezdődő nevek helyes rendezése (legjobban angol nyelvű zenékkel működik)
Az összes keverése
- A korábban elmentett lejátszási állapot visszaállítása (ha van ilyen)
Visszajátszás előerősítő
Az előerősítő a lejátszás során a létező beállítással kerül alkalmazásra
Helyezze át ezt a dalt
@@ -166,35 +163,28 @@
Lejátszólista átnevezés
Átnevez
Hozzáadás dátuma
- Mappák
+ Mappák
Ment
Alaphelyzet
- Állapot törölve
Fejlesztő Alexander Capehart
Lejátszás az összes dalból
Lejátszás műfajból
Tartalom
A zenei könyvtár újratöltése, ha változik (állandó értesítést igényel)
- Zene könyvtárak
- A zene nem töltődik be a hozzáadott mappákból.
- Kizárva
+ Zene könyvtárak
A zene betöltése sikertelen
Plusz (+)
Nem találtunk olyan alkalmazást, amely képes lenne kezelni ezt a feladatot
- Állapot helyreállítás nem lehetséges
Ismeretlen előadó
%1$s, %2$s
- Egy új playlista készítése
Várósor megnyitás
Mozgassa ezt a lapot
Keresési lekérdezés törlése
Nincs zenelejátszás
Egyéni értesítési művelet
Nincs dátum
- Gyors
+ Gyors
A nem zenei anyagok kizárása
- Állapot törlés nem lehetséges
- Állapot mentés nem lehetséges
Keverés minden dalból
Ogg audio
Megjelenítés
@@ -205,7 +195,6 @@
Fekete téma
Lekerekített sarkok engedélyezése további UI elemeken (az albumborítók lekerekítése szükséges)
Könyvtár fülek
- Mód
Free Lossless Audio Codec (FLAC)
Beállítás címkékkel
A könyvtárból történő lejátszáskor
@@ -215,7 +204,6 @@
Zene betöltés
Zene betöltése
Zene könyvtár figyelése
- Állapot mentve
Lejátszás megállítása
Egyszerű, praktikus zenelejátszó androidra.
Matroska hang
@@ -239,7 +227,7 @@
Per jel (/)
- %d előadó
- - %d előadó
+ - %d előadók
Ekvalizer
Könyvtár statisztika
@@ -257,17 +245,14 @@
Közreműködők elrejtése
Csak az albumon közvetlenül feltüntetett előadók megjelenítése (a jól címkézett könyvtárakban működik a legjobban)
Ki
- Magas minőség
+ Magas minőség
Hang és lejátszási viselkedés konfigurálása
Lejátszás
Fejhallgató auto. lejátszás
Beállítás címkék nélkül
- Kezelje, hogy honnan töltsön be zenét
+ Kezelje, hogy honnan töltsön be zenét
Zene újraolvasása
A címkék gyorsítótárának törlése és a zenei könyvtár teljes újratöltése (lassabb, de teljesebb)
- Lejátszási állapot mentése
- Lejátszási állapot törlése
- A zene csak az Ön által hozzáadott mappákból töltődik be.
A zenei könyvtár újratöltése, lehetőség szerint a gyorstárazott címkék használatával
%d kiválasztott
%d lemez
@@ -281,10 +266,6 @@
Megjelenik itt,
Megoszt
Lejátszólista törlése\?
- Állapot helyreállítva
- A korábban elmentett lejátszási állapot törlése (ha van ilyen)
- Lejátszási állapot visszaállítása
- Tartalmaz
Az Auxio engedélyt kér a zenei könyvtár olvasásához
Távolítsa el ezt a dalt
Auxio ikon
@@ -303,4 +284,31 @@
Másolva
Jelentés
Hiba információ
+ Ki
+ Importált lejátszási lista
+ Lejátszási lista exportálva
+ Demó
+ ReplayGain Track módosítása
+ ReplayGain Album módosítása
+ Szerző
+ Adományozás
+ Támogatók
+ Adományozzon a projektnek, hogy itt legyen a neved!
+ Szünet megjegyzése
+ A lejátszás/szünet megmarad a várólista kihagyásakor vagy szerkesztésekor
+ Cím
+ Nincsenek albumok
+ Lejátszási lista exportálása
+ Használja a Windows-kompatibilis útvonalakat
+ Path stílus
+ Export
+ Import
+ Lejátszási lista importálása
+ Demók
+ Üres lejátszási lista
+ Abszolút
+ Relatív
+ Lejátszási lista importálva
+ Nem sikerült importálni a lejátszási listát ebből a fájlból
+ Nem sikerült exportálni a lejátszási listát ebbe a fájlba
\ No newline at end of file
diff --git a/app/src/main/res/values-ia/strings.xml b/app/src/main/res/values-ia/strings.xml
index 79cd80ab4..693501bfe 100644
--- a/app/src/main/res/values-ia/strings.xml
+++ b/app/src/main/res/values-ia/strings.xml
@@ -74,7 +74,6 @@
Reinitialisar
Stylo de percurso
Usar percursos compatibile con Windows
- Stato salveguardate
A proposito de
Un reproductor de musica simple e rational pro Android.
Genere
@@ -126,32 +125,26 @@
Imagine de genere ab %s
Statisticas del bibliotheca
Bibliotheca
- Stato radite
Copiate
Modo de repetition
Reproducer ab tote le cantos
Contento
Musica
Audio
- Crear un nove lista de reproduction
Remover iste canto
Copertura de album
Coperrturas de album
Disactivate
- Rapide
+ Rapide
Reproduction
Rememorar le pausa
- Dossieres de musica
- Dossieres
- Modo
+ Dossieres de musica
+ Dossieres
Actualisar le musica
- Salveguardar stato de reproduction
- Restabilir le stato de reproduction
Nulle musica trovate
Falleva le carga del musica
- Necun dossieres
- Iste dossier non es supportate
- Non poteva salveguardar le stato
+ Necun dossieres
+ Iste dossier non es supportate
Tracia %d
Reproducer o pausar
Saltar al canto sequente
@@ -159,7 +152,7 @@
Cambiar modo de repetition
Stoppar le reproduction
Aperir le cauda
- Remover le dossier
+ Remover le dossier
Icone de Auxio
Copertura de album pro %s
Artista incognite
@@ -188,8 +181,6 @@
- %d canto
- %d cantos
- Non poteva rader le stato
- Non poteva restaurar le stato
Cantos cargate: %d
Albumes cargate: %d
Artistas cargate: %d
@@ -202,4 +193,27 @@
Lista de reproduction delite
Recargamento automatic
Imagines
+ Cargante tu bibliotheca de musica…
+ Action personalisate del barra de reproduction
+ Vader al artista
+ Plus (+)
+ Configurar le sono e le comportamento de reproduction
+ Deler %s? Isto non es pote disfacer.
+ +%.1f dB
+ -%.1f dB
+ %d Hz
+ %d kbps
+ Cargante le bibliotheca de musica… (%1$d/%2$d)
+ Importar lista de reproduction
+ Action de notification personalisate
+ Controlar le cargamento del musica e imagines
+ Adjustamento sin etiquettas
+ Rescannar le musica
+ Rader le requestas de recerca
+ Imagine de artista pro %s
+ Imagine del lista de reproduction pro %s
+ Necun musica in reproduction
+ Modificante %s
+ Lista de reproduction %d
+ Initiar le reproduction
\ No newline at end of file
diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml
index 707c86f24..d0c9761fd 100644
--- a/app/src/main/res/values-in/strings.xml
+++ b/app/src/main/res/values-in/strings.xml
@@ -43,13 +43,13 @@
Telusuri pustaka Anda…
Trek %d
- Putar/Jeda
+ Putar atau jeda
- - %d Lagu
+ - %d lagu
- - %d Album
+ - %d album
Properti lagu
Laju bit
@@ -84,7 +84,6 @@
Tindakan notifikasi khusus
Acak
Acak semua
- Status disimpan
Utamakan trek
Utamakan album
Pra-amp ReplayGain
@@ -99,26 +98,22 @@
Putar mundur sebelum melompat ke lagu sebelumnya
Jeda saat lagu diulang
Konten
- Simpan status pemutaran
- Simpan status pemutaran saat ini sekarang
Muat ulang musik
- Akan memulai ulang aplikasi
- Kelola dari mana musik dimuat
- Kecualikan
- Termasuk
- Tidak ada aplikasi yang bisa membuka tautan ini
- Folder ini tidak didukung
+ Muat ulang perpustakaan musik, menggunakan tag yang di-cache jika memungkinkan
+ Kelola dari mana musik dimuat
+ Tidak ada aplikasi yang bisa membuka tugas ini
+ Folder ini tidak didukung
Pindahkan tab ini
- Tidak Ada Nomor Trek
+ Tidak ada nomor trek
Audio MPEG-1
Pengkodean Audio Tingkat Lanjut (AAC)
Free Lossless Audio Codec (FLAC)
Merah
Ungu
- Deep Purple
+ Ungu tua
Nila
Biru
- Biru Tua
+ Biru tua
Lime
Dinamis
Disk %d
@@ -134,26 +129,23 @@
Saat diputar dari pustaka
Putar dari album
Ubah mode pengulangan
- Gambar Artis untuk %s
+ Gambar artis untuk %s
Saat diputar dari keterangan item
- Musik tidak akan dimuat dari folder yang Anda tambahkan.
Hapus lagu antrian ini
Hapus kueri pencarian
Penyesuaian tanpa tag
- Folder musik
+ Folder musik
Putar dari artis
- Mode
Auxio memerlukan izin untuk membaca perpustakaan musik Anda
Loncat ke lagu terakhir
Acak semua lagu
- Tidak Ada Tanggal
- Musik hanya akan dimuat dari folder yang Anda tambahkan.
+ Tidak ada tanggal
Pemuatan musik gagal
- Sampul Album untuk %s
- Artis Tidak Dikenal
- Tidak ada Folder
+ Sampul album untuk %s
+ Artis tidak dikenal
+ Tidak ada folder
Loncat ke lagu berikutnya
- Hapus direktori
+ Hapus folder
Ikon Auxio
Sampul album
Aktifkan atau nonaktifkan acak
@@ -163,21 +155,21 @@
Cokelat
Abu-abu
Total durasi: %s
- Gambar Genre untuk %s
- Genre Tidak Diketahui
+ Gambar genre untuk %s
+ Genre tidak diketahui
Audio Matroska
- Hijau Tua
+ Hijau tua
Kuning
Audio MPEG-4
- Cyan
- Teal
+ Sian
+ Hijau laut
Hijau
Jingga
Genre yang dimuat: %d
Jumlah lagu
Trek
Merah muda
- ReplayGain
+ Normalisasi volume
Muat ulang otomatis
Selalu muat ulang pustaka musik saat terjadi perubahan (membutuhkan notifikasi tetap)
Perilaku
@@ -186,16 +178,16 @@
Koma (,)
Tambah (+)
Tanggal ditambahkan
- Kualitas tinggi
+ Kualitas tinggi
Titik koma (;)
Wiki
Putar dari aliran
Aliran
Sampul album
Nonaktif
- Cepat
+ Cepat
Album remix
- Folder
+ Folder
Memuat musik
Memantau pustaka musik
Memantau perubahan pada pustaka musik Anda…
@@ -231,4 +223,88 @@
Kompilasi live
EP Remix
Single
+ Daftar putar dibuat
+ Ditambahkan ke daftar putar
+ Bersihkan cache tag dan muat ulang perpustakaan musik secara penuh (lebih lambat, tetapi lebih sempurna)
+ Daftar putar yang diimpor
+ Daftar putar diimpor
+ Daftar putar diekspor
+ Daftar putar dihapus
+ Tampilkan
+ Kecualikan item non-musik
+ Paksa sampul album kotak
+ Potong semua sampul album ke aspek rasio 1:1
+ %d terpilih
+ Ubah nama
+ Ubah nama daftar putar
+ Hapus daftar putar?
+ Gambar daftar putar untuk %s
+ Demo
+ Demo
+ Tambahkan ke daftar putar
+ Remix
+ Hentikan pemutaran
+ Hapus
+ Urutkan berdasarkan
+ Turun
+ Penyesuaian Trek ReplayGain
+ Penyesuaian Album ReplayGain
+ Disalin
+ Laporkan
+ Tidak ada lagu
+ Pencipta
+ Donasi
+ Pendukung
+ Donasi ke proyek ini agar nama Anda ditambahkan kesini!
+ Pemisah nilai ganda
+ Konfigurasikan suara dan perilaku pemutaran
+ Ingat jeda
+ Tetap memainkan/menjeda ketika melewati atau mengatur antrean
+ Tidak dapat mengekspor daftar putar ke berkas ini
+ Buka antrean
+ Pemilihan gambar
+ Tidak ada disk
+ Tidak ada album
+ %1$s, %2$s
+ Daftar putar baru
+ Arah
+ Gunakan jalur yang kompatibel dengan Windows
+ Langsung
+ DJ Campuran
+ DJ Campuran
+ Lewati ke lagu berikutnya
+ Mode perulangan
+ Peringatan: Menggunakan pengaturan ini mungkin akan menghasilkan beberapa tag salah diinterpretasikan sebagai nilai multipel. Anda dapat mengatasinya dengan mengawali karakter yang tidak diinginkan dengan garis miring terbalik (\\).
+ Nonaktif
+ Daftar putar kosong
+ Impor daftar putar
+ Atur
+ Ekualiser
+ Atur ulang
+ Pindai ulang musik
+ Bagikan
+ Daftar putar
+ Muncul di
+ Daftar putar
+ Jalur
+ Pilihan
+ Nama daftar putar diubah
+ Aksi bar playback kustom
+ Putar lagu dengan sendirinya
+ Konfigurasi karakter yang menunjukkan nilai tag multipel
+ Penyortiran cerdas
+ Hanya tampilkan artis yang secara langsung dikreditkan pada sebuah album (bekerja paling baik pada pustaka yang diberi tag dengan baik)
+ Sembunyikan kolaborator
+ Mengedit %s
+ Daftar putar %d
+ Hapus %s? Aksi ini tidak dapat dikembalikan.
+ Tidak dapat mengimpor daftar putar dari berkas ini
+ Gaya jalur
+ Absolut
+ Impor
+ Ekspor
+ Ekspor daftar putar
+ Relatif
+ Informasi kesalahan
+ Sortir nama yang dimulai dengan nomor atau kata seperti \"the\" dengan benar (bekerja paling baik dengan lagu berbahasa Inggris)
\ No newline at end of file
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 27372f238..a152d00e3 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -1,8 +1,8 @@
- Semplice e razionale lettore musicale per android.
- Vedi e gestisci la riproduzione musicale
+ Un lettore musicale semplice e razionale per android.
+ Visualizza e gestisci la riproduzione musicale
Riprova
Autorizza
@@ -10,7 +10,7 @@
Artisti
Album
Brani
- Tutte i brani
+ Tutti i brani
Cerca
Filtro
Tutto
@@ -18,90 +18,87 @@
Nome
Artista
Album
- Anno
- Ascendente
- Ora in riproduzione
+ Data
+ Crescente
+ In riproduzione
Riproduci
- Mescola
+ Casuale
Riproduci da tutti i brani
Riproduci dall\'album
Riproduci dall\'artista
Coda
- Riproduci successivo
- Accoda
- Accodato
+ Riproduci come successivo
+ Aggiungi alla coda
+ Aggiungi alla coda
Vai all\'artista
Vai all\'album
- Stato salvato
Aggiungi
Salva
- Nessuna cartella
+ Nessuna cartella
Informazioni
Versione
Codice sorgente
Licenze
- Sviluppato da Alexander Capehart
- Statistiche della raccolta
+ Alexander Capehart
+ Statistiche della libreria
Opzioni
Aspetto
Tema
- Automatico
+ Sistema
Chiaro
Scuro
Accento
Tema nero
- Usa tema nero puro
+ Usa un tema scuro AMOLED
Visualizzazione
Schede libreria
Cambia la visibilità e l\'ordine delle schede della libreria
- Copertine dischi arrotondate
- Abilita gli angoli arrotondati su ulteriori elementi dell\'interfaccia utente (richiede che le copertine degli album siano arrotondate)
- Usa azioni notifica alternative
+ Copertine arrotondate
+ Abilita gli angoli arrotondati su ulteriori elementi dell\'interfaccia (richiede che le copertine degli album siano arrotondate)
+ Personalizza azione notifica
Audio
- Autoplay cuffie
- Comincia la riproduzione ogni volta che le cuffie sono inserite (potrebbe non funzionare su tutti i dispositivi)
- Strategia ReplayGain
+ Riproduzione automatica
+ Inizia la riproduzione ogni volta che vengono connesse delle cuffie (potrebbe non funzionare su tutti i dispositivi)
+ Modalità ReplayGain
Preferisci traccia
Preferisci album
Preferisci l\'album se in riproduzione
- Comportamento
+ Personalizza
Quando in riproduzione dalla libreria
- Mantieni mescolamento
- Mantiene il mescolamento anche se un nuovo brano è selezionato
- Riavvolgi prima di saltare indietro
- Riavvolge prima di saltare al brano precedente
+ Ricorda riproduzione casuale
+ Mantiene la riproduzione casuale quando viene riprodotto un nuovo brano
+ Riavvolgi prima di tornare indietro
+ Riavvolge il brano in riproduzione prima di passare a quello precedente
Pausa alla ripetizione
- Pausa quando un brano si ripete
+ Mette in pausa quando un brano si ripete
Contenuti
- Salva stato riproduzione
- Salva lo stato di riproduzione corrente
- Aggiorna musica
+ Aggiorna libreria
Ricarica la libreria musicale, usando i tag nella cache quando possibile
Musica non trovata
Caricamento musica fallito
Auxio ha bisogno del permesso per leggere la tua libreria musicale
Nessuna app può completare questa azione
- Questa cartella non è supportata
+ Questa cartella non è supportata
Cerca nella libreria…
- Canzone %d
- Riproduci o pausa
- Salta a brano successivo
- Salta a ultimo brano
+ Traccia %d
+ Riproduci o metti in pausa
+ Passa al brano successivo
+ Passa all\'ultimo brano
Cambia modalità ripetizione
- Attiva o disattiva mescolamento
- Mescola tutti i brani
+ Attiva o disattiva la riproduzione casuale
+ Riproduce casualmente tutti i brani
Rimuovi questo brano
Sposta questo brano
- Muove questa scheda
- Cancella la query di ricerca
- Rimuovi cartella
+ Sposta questa scheda
+ Cancella la ricerca
+ Rimuovi cartella
Icona Auxio
Copertina album
- Copertina album per %s
+ Copertina album di %s
Immagine artista per %s
Immagine genere per %s
@@ -109,7 +106,7 @@
Genere sconosciuto
Data sconosciuta
Nessuna traccia
- Nessuna canzone in riproduzione
+ Nessun brano in riproduzione
Rosso
Rosa
@@ -124,7 +121,7 @@
Verde scuro
Lime
Giallo
- Arancio
+ Arancione
Marrone
Grigio
@@ -143,33 +140,28 @@
- %d album
- %d album
- Modo
- La musica non sarà caricata dalle cartelle che aggiungi.
- Includi
- La musica sarà caricata solo dalle cartelle che aggiungi.
MPEG-1 audio
MPEG-4 audio
Ogg audio
Dinamico
+%.1f dB
%d Hz
- Caricamento libreria musicale… (%1$d/%2$d)
- Quando in riproduzione dai dettagli dell\'elemento
- Attenzione: impostare valore positivi alti può provocare distorsioni su alcune tracce.
+ Caricamento della tua libreria musicale… (%1$d/%2$d)
+ Quando riproduci dai dettagli di un elemento
+ Attenzione: impostare alti valori positivi può provocare la distorsione di alcune tracce.
Regolazione senza tag
- Mescola
- Mescola tutto
- Regolazione con tag
+ Casuale
+ Tutto in casuale
+ Regolazione mediante tag
Il pre-amp è applicato alla regolazione esistente durante la riproduzione
Riproduci dall\'elemento mostrato
- Gestisci le cartelle da dove caricare la musica
- Cartelle musica
- Escludi
+ Gestisci le cartelle da dove caricare la musica
+ Cartelle musicali
Matroska audio
Free Lossless Audio Codec (FLAC)
Advanced Audio Coding (AAC)
Disco %d
- %d kB/s
+ %d kbps
-%.1f dB
Caricamento musica
Caricamento libreria musicale…
@@ -179,23 +171,19 @@
Traccia
OK
Frequenza di campionamento
- Vedi proprietà
+ Visualizza proprietà
Proprietà brano
Formato
Dimensione
- Bitrate
- Cancella
+ Velocità in bit
+ Annulla
Pre-amp ReplayGain
Sto monitorando i cambiamenti nella tua libreria musicale…
Ricarica la tua libreria musicale se subisce cambiamenti (richiede notifica persistente)
Caricamento musica
Monitoraggio libreria musicale
- Stato ripristinato
Data aggiunta
Ricaricamento automatico
- Ripristina stato riproduzione
- Ripristina lo stato di riproduzione precedentemente salvato (se disponibile)
- Impossibile ripristinare lo stato
EP
EP
Singoli
@@ -209,38 +197,35 @@
Singolo live
Album live
EP live
- Raccolta
+ Compilation
Colonne sonore
Singolo remix
- Raccolte
+ Compilation
Colonna sonora
- Ripristina lo stato di riproduzione precedentemente salvato (se presente)
- Ripristina stato riproduzione
Equalizzatore
Apri la coda
Genere
- Stato ripristinato
- Azione personalizzata barra di riproduzione
- Vai alla prossima
+ Personalizza azione barra di riproduzione
+ Passa al brano successivo
Modalità ripetizione
- Interrompi riproduzione
+ Interrompi la riproduzione
Nascondi collaboratori
- Mostra solo artisti che sono direttamente accreditati in un album (funziona meglio su librerie ben taggate)
+ Mostra solo gli artisti che appaiono esplicitamente nei riconoscimenti di un album (funziona meglio su librerie ben taggate)
Copertine album
- Off
- Veloce
- Escludi file non musicali
- Ignora file audio non musicali, come i podcast
+ Disattiva copertine
+ Media qualità
+ Escludi i file non musicali
+ Ignora i file audio non musicali, come i podcast
Separatori multi-valore
- Configura caratteri che indicano valori multipli di tag
+ Configura i caratteri che identificano tag con valori multipli
Barra (/)
- Attenzione: potrebbero verificarsi degli errori nella interpretazione di alcuni tag con valori multipli. Puoi risolvere aggiungendo come prefisso la barra rovesciata (\\) ai separatori indesiderati.
+ Attenzione: potrebbero verificarsi degli errori nell\'interpretazione di alcuni tag con valori multipli. Puoi risolvere aggiungendo come prefisso la barra rovesciata () ai separatori indesiderati.
E commerciale (&)
- Raccolte live
- Raccolta remix
+ Compilation live
+ Compilation remix
Mix DJ
Mix DJ
- Alta qualità
+ Alta qualità
Virgola (,)
Punto e virgola (;)
Più (+)
@@ -250,37 +235,33 @@
- %d artisti
Riscansiona musica
- Impossibile salvare
Svuota la cache dei tag e ricarica completamente la libreria musicale (più lento, ma più completo)
- Impossibile svuotare
%d selezionati
Riproduci dal genere
Wiki
%1$s, %2$s
Ripristina
Comportamento
- Cartelle
+ Cartelle
Musica
Immagini
- Collezione
+ Libreria
Cambia il tema e i colori dell\'app
Controlla come vengono caricate musica e immagini
- ReplayGain
+ Normalizzazione del volume
Riproduzione
- Persistenza
- Personalizza controlli e comportamento dell\'UI
- Configura comportamento di suono e riproduzione
- Discendente
+ Personalizza i controlli e il comportamento dell\'interfaccia
+ Suono e riproduzione
+ Decrescente
Playlist
Playlist
- Ordinazione intelligente
- Ordina correttamente i nomi che iniziano con numeri o parole come \"the\" (funziona meglio con i titoli in inglese)
- Crea una nuova playlist
+ Ordinamento intelligente
+ Ordina correttamente i titoli che iniziano con numeri o parole come \"the\" (funziona meglio con i titoli in inglese)
Immagine della playlist per %s
Nuova playlist
- Aggiungi a playlist
+ Aggiungi alla playlist
Playlist creata
- Aggiunto a playlist
+ Aggiunto alla playlist
Nessun brano
Playlist %d
Elimina
@@ -293,31 +274,31 @@
Playlist rinominata
Condividi
Nessun disco
- Appare su
- Modifica di %s
+ Appare in
+ Modificando %s
Forza copertine album quadrate
Adatta tutte le copertine degli album ad una visualizzazione 1:1
Brano
Visualizza
- Riproduci brano da solo
+ Riproduci solo il brano
Ordina per
- Playlist importata
+ Playlist esterna
Impossibile esportare la playlist in questo file
- Direzione
+ Ordine
Espandi
- Immagine di selezione
+ Selezione immagine
Selezione
Copiato
Segnala
Autore
Dona
- Supporti
+ Sostenitori
Informazioni sull\'errore
Nessun album
Percorso
Playlist vuota
Importa playlist
- Dona al progetto; il tuo nome sarà aggiunto qui!
+ Dona al progetto, il tuo nome sarà aggiunto qui!
Impossibile importare una playlist da questo file
Importa
Esporta
@@ -328,4 +309,29 @@
Usa percorsi compatibili con Windows
Playlist importata
Playlist esportata
+ Disattivato
+ Demo
+ Demo
+ Regolazione traccia ReplayGain
+ Regolazione album ReplayGain
+ Ricorda pausa
+ Mantiene riproduzione/pausa durante il cambio di brano e la modifica della coda di riproduzione
+ Avvia la riproduzione
+ Avvia Auxio utilizzando le ultime impostazioni di riproduzione salvate. Se non è disponibile alcuna impostazione di riproduzione, i brani verranno riprodotti in ordine casuale. La riproduzione inizierà immediatamente.\n\nAttenzione: fai attenzione a controllare questa impostazione; se lo chiudi e poi provi a usarlo di nuovo, è probabile che l\'app si blocchi.
+ Scegli le cartelle
+ Feedback
+ Invia un\'email
+ Apri un\'issue su GitHub
+ Album sconosciuto
+ I tuoi generi appariranno qui.
+ I tuoi brani appariranno qui.
+ I tuoi artisti appariranno qui.
+ I tuoi album appariranno qui.
+ Le tue playlist appariranno qui.
+ Espandi
+ Nuova cartella
+ Libera spazio
+ MPEG-4 contenente %s
+ Apple Lossless Audio Codec (ALAC)
+ Sconosciuto
\ No newline at end of file
diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml
index e39afd69e..8b9ad9827 100644
--- a/app/src/main/res/values-iw/strings.xml
+++ b/app/src/main/res/values-iw/strings.xml
@@ -56,7 +56,6 @@
שמירה
אתחול
הוספה
- המצב נשמר
גרסה
קוד מקור
ויקי
@@ -107,14 +106,14 @@
הצגת אומנים שמצויינים ישירות בקרדיטים של אלבום בלבד (עובד באופן מיטבי על ספריות מתויגות היטב)
עטיפות אלבום
כבוי
- מהיר
+ מהיר
שמע
ניגון
ניגון אוטומטי באוזניות
הרצה לאחור לפני דילוג אחורה
הרצה לאחור לפני דילוג לשיר הקודם
עצירה בעת חזרה
- ReplayGain
+ נורמליזציית עוצמת הקול
העדפת אלבום
מגבר ReplayGain
התאמה עם תגיות
@@ -132,7 +131,6 @@
תור
מעבר לאומן
ערבוב
- המצב שוחזר
אודות
הגדרות
אוטומטי
@@ -145,7 +143,7 @@
התאמת תווים המציינים ערכי תגית מרובים
קו נטוי (/)
אזהרה: השימוש בהגדרה זו עלול לגרום לחלק מהתגיות להיות מפורשות באופן שגוי כבעלות מספר ערכים. ניתן לפתור זאת על ידי הכנסת קו נטוי אחורי (\\) לפני תווים מפרידים לא רצויים.
- איכות גבוהה
+ איכות גבוהה
התעלמות ממספרים או מילים כמו \"The\" (\"ה׳ היידוע\") בעת סידור על פי שם (עובד באופן מיטבי עם מוזיקה בשפה האנגלית)
תמונות
הגדרת הצליל והניגון
@@ -166,13 +164,10 @@
שינוי שם רשימת השמעה
למחוק את רשימת ההשמעה\?
עריכה
- לא ניתן לנקות את המצב
כתום
- תיקיות מוזיקה
+ תיקיות מוזיקה
טעינה מחדש של ספריית המוזיקה, במידה וניתן ייעשה שימוש בתגיות מהמטמון
סריקת מוסיקה מחדש
- שמירת מצב הנגינה
- לא ניתן לשמור את המצב
Auxio צריך הרשאות על מנת לקרוא את ספריית המוזיקה שלך
פתיחת התור
משך כולל: %s
@@ -181,12 +176,10 @@
שירים טעונים: %d
אלבומים טעונים: %d
ז\'אנרים טעונים: %d
- המצב נוקה
ספריה
- שמירת מצב הנגינה הנוכחי כעת
לא נמצא יישום שיכול לטפל במשימה זו
- אין תיקיות
- תיקייה זו אינה נתמכת
+ אין תיקיות
+ תיקייה זו אינה נתמכת
דילוג לשיר האחרון
שינוי מצב חזרה
ניגון או השהיה
@@ -206,22 +199,20 @@
דינמי
המוזיקה שלך בטעינה… (%1$d/%2$d)
דיסק %d
- ניהול המקומות שמהם תיטען מוזיקה
+ ניהול המקומות שמהם תיטען מוזיקה
אין שירים
ורוד
נוצרה רשימת השמעה
- תיקיות
+ תיקיות
- אומן אחד
- שני אומנים
- %d אומנים
- לכלול
רענון מוזיקה
לא נמצאה מוזיקה
אירע כשל בטעינה מוזיקה
עטיפת אלבום
- ניקוי מצב הנגינה
- שיר אחד
- שני שירים
@@ -237,18 +228,14 @@
נוסף לרשימת השמעה
ערבוב כל השירים
סמל Auxio
- הסרת תיקייה
+ הסרת תיקייה
תמונת רשימת השמעה עבור %s
אדום
ירוק
- לא ניתן לשחזר את המצב
רצועה %d
- יצירת רשימת השמעה חדשה
עצירת הנגינה
הסרת שיר זה
שיתוף
- מצב
- החרגה
העברת שיר זה
העברת לשונית זו
ניקוי תור החיפוש
@@ -261,16 +248,12 @@
הצגה
הכרחת עטיפות אלבום מרובעות
ריקון מטמון התגיות וטעינת ספריית המוזיקה מחדש במלואה (איטי יותר, אך יותר שלם)
- ניקוי מצב הנגינה הקודם שנשמר (אם קיים)
מיון על פי
כיוון
חיתוך כל עטיפות האלבומים ליחס של 1:1
- מוזיקה לא תיטען מהתיקיות שנוספו.
- מוזיקה תיטען רק מהתיקיות שנוספו.
מופיע~ה ב-
ניגון השיר בלבד
אזהרה: שינוי המגבר לערך חיובי גבוה עלול לגרום לעיוות (דיסטורשן) בחלק מרצועות האודיו.
- שחזור מצב נגינה
אינדיגו
אודיו MPEG-1
אודיו MPEG-4
@@ -279,7 +262,6 @@
טורקיז
חום
%d נבחרו
- התמדה
עוד
בחירה
מידע על השגיאה
@@ -293,9 +275,8 @@
%d הרץ (Hz)
%d קילוביטים לשנייה (kbps)
מועתק
- שחזור מצב הנגינה שנשמר קודם (אם קיים)
אודיו Matroska
- קידוד אודיו מתקדם (AAC)
+ קודק אודיו מתקדם (AAC)
%1$s, %2$s
ליים
%s נערך
@@ -317,4 +298,25 @@
רשימת השמעה יוצאה
יצא רשימת השמעה
אין יכולת לייצא רשימת השמעה מהקובץ הנ”ל
+ בחר תיקיות
+ התחל השמעה
+ זכור השהיה
+ כבוי
+ עוד
+ תרומה
+ MPEG-4 המכיל %s
+ לא ידוע
+ אלבום לא ידוע
+ תיקון רצועות ReplayGain
+ תיקון אלבומים ReplayGain
+ יוצר
+ תיקיה חדשה
+ תרמו לפרויקט כדי להוסיף את שמכם כאן!
+ מפעיל Auxio במצב שנשמר קודם לכן. אם אין מצב שמור זמין, כל השירים יתערבבו. השמעה תתחיל מידית.\n\nאזהרה: היזהר בשליטה בשירות זה, אם תסגרו אותו ואחר כך תנסו להשתמש בו שוב, ייתכן שתקרסו את האפליקציה.
+ הישאר בניגון/בהשהיה בעת דילוג או עריכה של התור
+ קודק אודיו Apple ללא אובדן נתונים (ALAC)
+ משוב
+ תומכים
+ יצירת דוח ב-GitHub
+ שליחת מייל
\ No newline at end of file
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index fd4e11299..8004f19ff 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -6,7 +6,6 @@
アーティスト
ジャンル
曲の長さ
- 現在の再生状態を保存
このタブを移動
この再生待ちの曲を移動
日付けがありません
@@ -25,7 +24,7 @@
曲の繰り返し時にポーズ
検索クエリを解除
日付け
- 高クオリティ
+ 高クオリティ
ラウンドモード
音楽が見つかりません
音楽の読み込みに失敗
@@ -41,7 +40,7 @@
ライブラリタブの視度と順序をカスタマイズ
アルバムで直接クレジットされたアーティストのみを表示 (適正にタグ付けされたライブラリで最適に作動します)
前の曲にスキップ前に曲を巻き戻す
- 音楽フォルダ
+ 音楽フォルダ
プラス (+)
リミックスコンピレーション
DJミックス
@@ -53,7 +52,6 @@
再生中
カラースキーム
黒基調
- 再生状態を復元
表示されたアイテムから再生
再生停止
赤
@@ -78,19 +76,15 @@
アンパサンド (&)
アルバムを優先
トラックを優先
- 音楽の読み込み元を管理
- 除外
+ 音楽の読み込み元を管理
音楽の再読み込み
- 再生状態を保存
Auxio は音楽ライブラリを読む許可を必要とします
- 追加
- フォルダがありません
- このフォルダはサポートされていません
- 再生状態を復元できません
+ フォルダがありません
+ このフォルダはサポートされていません
トラック %d
再生またはポーズ
再生待ちの曲を除去
- フォルダを除去
+ フォルダを除去
Auxio アイコン
アルバムカバー
%s のアルバムカバー
@@ -138,11 +132,8 @@
読み込みが完了したジャンル数: %d
-%.1f デシベル
読み込みが完了したアルバム数: %d
- 再生状態の解除完了
- 再生状態の復元完了
リセット
Auxioについて
- 再生状態の保存完了
ソースコード
Wiki
バージョン
@@ -155,13 +146,9 @@
ライブラリからの再生時
アイテム詳細からの再生時
音楽以外を除外
- ここに追加したフォルダからのみ音楽が読み込まれます。
- 前回保存された再生状態がある場合、再生状態を復元
このタスクを実行できるアプリが見つかりません
コンテンツ
音楽の再スキャン
- 再生状態を解除
- 前回保存された再生状態を解除
Matroska オーディオ
高度なオーディオ コーデック (AAC)
品質を損なうことのない無料のオーディオ コーデック (FLAC)
@@ -187,16 +174,14 @@
オーディオ
再生
ポーズと繰り返し
- ここに追加したフォルダはからは音楽が読み込まれません。
ライブラリ
タグ無しで調整
- フォルダ
+ フォルダ
セミコロン (;)
スラッシュ (/)
繰り返しモードを変更
シャフルのオン・オフ
次の曲にスキップ
- 再生状態を保存できません
すべての曲をシャフル
%d ヘルツ
%d kbps
@@ -227,7 +212,6 @@
ジャンルから再生
新しい曲の再生時にシャフルを保持
ダイナミック
- 再生状態を解除できません
再生中の音楽がありません
MPEG-4 オーディオ
深緑
@@ -236,19 +220,17 @@
+%.1f デシベル
追加の UI 要素で角丸を有効にします (アルバム カバーを丸める必要があります)
外観
- モード
複数のタグ値を表す文字を構成する
カスタム再生バー アクション
ソート時に記事を無視する
名前で並べ替えるときに「the」などの単語を無視する (英語の音楽に最適)
- 初期 (高速読み込み)
+ 初期 (高速読み込み)
再生中の場合はアルバムを優先
複数値セパレータ
変更されるたびに音楽ライブラリをリロードします (永続的な通知が必要です)
サウンドと再生の動作を構成する
戻る前に巻き戻す
警告: プリアンプを高い正の値に変更すると、一部のオーディオ トラックでピーキングが発生する場合があります。
- 再生状況
キューを開く
音楽再生の表示と制御
ラウドネスイコライゼーション
@@ -267,7 +249,6 @@
プレイリストに追加されました
曲がありません
プレイリスト %d
- 新しいプレイリストを作成する
消去
名前の変更
プレイリストの名前を変更する
@@ -283,4 +264,27 @@
曲
フォーススクエアのアルバムカバー
すべてのアルバム カバーを 1:1 のアスペクト比にトリミングします
+ Windows互換パスを使用する
+ 寄付
+ サポーター
+ デモ
+ デモ
+ アルバムがありません
+ プレイリストをインポート
+ パス
+ 再生開始
+ エラー情報
+ コピーしました
+ パススタイル
+ インポート
+ エクスポート
+ プレイリストをエクスポート
+ フィードバック
+ メールを送信
+ プレイリストをインポートしました
+ プレイリストをエクスポートしました
+ プロジェクトに寄付して、ここにあなたの名前を追加します!
+ オフ
+ このファイルからプレイリストをインポートできません
+ このファイルにプレイリストをエクスポートできません
\ No newline at end of file
diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml
new file mode 100644
index 000000000..954c3d4a9
--- /dev/null
+++ b/app/src/main/res/values-ka/strings.xml
@@ -0,0 +1,22 @@
+
+
+ გამეორება
+ მეტი
+ EP
+ მარტივი და სასიამოვნო მუსიკის დამკვრელი Android-სთვის.
+ სიმღერა
+ EP-ის რემიქსი
+ მუსიკის ჩატვირთვა
+ სიმღერები
+ მუსიკის ბიბლიოთეკის მონიტორინგი
+ ავტორიზაცია
+ ალბომები
+ EP-ები
+ ყველა სიმღერა
+ ალბომი
+ შეარჩიე საქაღალდეები
+ მუსიკის ჩატვირთვა
+ პირდაპირი EP
+ სინგლები
+ სინგლი
+
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index bae32c1d8..bd47b3560 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -36,7 +36,6 @@
대기열에 추가했습니다.
아티스트로 이동
앨범으로 이동
- 상태 저장됨
확인
@@ -86,8 +85,6 @@
반복 재생 시 일시 중지
곡이 반복 재생될 때 일시 중지
내용
- 재생 상태 저장
- 현재 재생 상태를 지금 저장합니다.
음악 새로고침
캐시된 태그를 사용하여 음악 라이브러리를 다시 불러옵니다.
@@ -95,8 +92,8 @@
음악 불러오기 실패
앱에서 음악 라이브러리를 읽을 수 있는 권한이 필요합니다.
이 작업을 처리할 수 있는 앱을 찾을 수 없습니다.
- 폴더 없음
- 지원하지 않는 폴더입니다.
+ 폴더 없음
+ 지원하지 않는 폴더입니다
라이브러리에서 검색…
@@ -111,7 +108,7 @@
이 곡 이동
이 탭 이동
검색 기록 삭제
- 폴더 제거
+ 폴더 제거
Auxio 아이콘
앨범 커버
%s의 앨범 커버
@@ -158,9 +155,6 @@
MPEG-4 오디오
Free Lossless Audio Codec (FLAC)
- 이전에 저장된 재생 상태 초기화
- 제외
- 추가한 폴더에서만 음악을 불러옵니다.
곡 속성
속성 보기
샘플 속도
@@ -179,12 +173,8 @@
무작위 재생
표시된 항목에서 재생
음악 라이브러리 불러오는 중…
- 재생 상태 지우기
- 재생 상태 복원
- 음악 폴더
- 음악을 불러오는 위치 관리
- 추가한 폴더에서 음악을 불러오지 않습니다.
- 포함
+ 음악 폴더
+ 음악을 불러오는 위치 관리
다중 값 구분 기호
태그 값이 여러 개일 때 태그를 구분할 기호를 설정합니다.
콤마 (,)
@@ -211,16 +201,12 @@
Advanced Audio Coding (AAC)
앨범 커버
끔
- 빠름
- 고품질
- 이전에 저장된 재생 상태 복원
- 재생 상태를 복원할 수 없습니다.
+ 빠름
+ 고품질
음악 라이브러리가 변경될 때마다 새로고침 (고정 알림 필요)
- 상태 지워짐
음악 불러오는 중
음악 불러오는 중
음악 라이브러리 모니터링 중
- 상태 복원됨
EP 앨범
EP 앨범
싱글
@@ -232,7 +218,6 @@
믹스테이프
리믹스
자동 새로고침
- 모드
음악 라이브러리를 불러오는 중… (%1$d/%2$d)
장르
경고: 이 설정을 사용하면 몇몇 태그가 다중 값을 가진 것으로 잘못 나타날 수 있습니다. 태그에서 구분 기호 앞에 백슬래시(\\)를 붙이면 구분 기호로 인식하지 않습니다.
@@ -242,8 +227,6 @@
팟캐스트 등 음악이 아닌 오디오 파일 무시
공동 작업자 숨기기
앨범에 등장하는 아티스트만 표시 (자세히 태그된 라이브러리에 최적화)
- 재생 상태를 지울 수 없습니다.
- 재생 상태를 저장할 수 없습니다.
음악 재탐색
- %d 아티스트
@@ -254,16 +237,15 @@
위키
장르에서 재생
%1$s, %2$s
- ReplayGain
+ ReplayGain 볼륨 조정
사운드 및 재생 동작 구성
재생
- 폴더
+ 폴더
앱 테마 및 색상 변경
음악
라이브러리
이미지
음악 및 이미지 불러오기 방식 설정
- 지속
동작
UI 제어 및 동작 사용자 정의
내림차순
@@ -272,7 +254,6 @@
%s의 재생 목록 이미지
적응형 정렬
정렬할 때 숫자나 \"the\"와 같은 단어를 무시합니다. 태그가 영어로 되어 있을 때 가장 잘 작동합니다.
- 새 재생 목록 만들기
새 재생 목록
재생 목록에 추가
재생 목록을 만들었습니다.
@@ -330,4 +311,14 @@
ReplayGain 트랙 조절값
ReplayGain 앨범 조절값
일시 중지 기억
+ 끄기
+ 재생 시작
+ 이전에 저장된 상태에 따라 Auxio를 시작합니다. 저장된 상태가 없으면 모든 곡을 무작위 재생합니다. 재생은 즉시 시작됩니다.
+\n
+\n경고: 이 서비스는 주의하여 사용하세요. 서비스를 닫은 뒤 다시 사용하려고 할 경우 앱이 충돌할 수 있습니다.
+ 더 보기
+ 폴더 선택
+ 전자우편을 전송합니다
+ 깃허브에 문제를 제기합니다
+ 응답
\ No newline at end of file
diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml
index 15c4f41fd..c7e5d0f38 100644
--- a/app/src/main/res/values-lt/strings.xml
+++ b/app/src/main/res/values-lt/strings.xml
@@ -5,7 +5,7 @@
Paieška
Filtruoti
Visi
- Rūšiavimas
+ Rūšiuoti
Pavadinimas
Data
Trukmė
@@ -13,14 +13,14 @@
Diskas
Pridėta data
Didėjantis
- Groti kitą
+ Leisti kitą
Pridėti į eilę
Eilė
Eiti į atlikėją
Eiti į albumą
Peržiūrėti ypatybes
Dydis
- Bitų srautas
+ Bitų sparta
Skaitmeninimo dažnis
Automatinis
Šviesi
@@ -30,12 +30,12 @@
Atlikėjai
Albumai
Takelis
- Dabar groja
- Groti
+ Dabar leidžiama
+ Leisti
Licencijos
Maišyti
Pridėta į eilę
- Dainų ypatybės
+ Dainos ypatybės
Išsaugoti
Apie
Pridėti
@@ -44,15 +44,15 @@
Versija
Nustatymai
Tema
- Naudoti grynai juodą tamsią temą.
- Paprastas, racionalus Android muzikos grotuvas.
- Muzikos pakrovimas
- Peržiūrėk ir valdyk muzikos grojimą
+ Naudok grynai juodą tamsią temą.
+ Paprastas, racionalus „Android“ muzikos leistuvė.
+ Muzikos įkėlimas
+ Peržiūrėk ir valdyk muzikos įrašo perklausą
Žanrai
Pakartoti
Suteikti
- Kraunama muzika
- Kraunama tavo muzikos biblioteka…
+ Įkeliama muzika
+ Įkeliamas tavo muzikos biblioteka…
Bibliotekos statistika
Rožinis
Albumas
@@ -70,19 +70,17 @@
Nežinomas atlikėjas
Albumo viršelis
Giliai violetinė
- Stebėjimas muzikos biblioteka
+ Stebima muzikos biblioteka
Stebimas tavo muzikos biblioteka dėl pakeitimų…
Maišyti
Maišyti viską
- Atkurta būsena
- Išsaugota būsena
Atšaukti
Šaltinio kodas
Rodinys
- ReplayGain strategija
+ „ReplayGain“ strategija
Singlai
Gerai
- Įjungti suapvalintų kampų papildomiems UI elementams (reikia, kad albumo viršeliai būtų suapvalinti).
+ Įjunk suapvalintų kampų papildomiems naudotojo sąsajos elementams (reikia, kad albumo viršeliai būtų suapvalinti).
Garso takelis
Garso takeliai
Garsas
@@ -120,28 +118,28 @@
Gyvai albumas
Remikso albumas
Gyvai
- Visada pradėti groti, kai ausinės yra prijungtos (gali neveikti visuose įrenginiuose).
+ Visada pradėk leisti, kai ausinės yra prijungtos (gali neveikti visuose įrenginiuose).
Ogg garsas
Aleksandras Keiphartas (angl. Alexander Capehart)
Pageidauti takeliui
- Nėra aplankų
- Šis aplankas nepalaikomas.
- Groti arba pristabdyti
+ Nėra aplankų
+ Šis aplankas nepalaikomas.
+ Leisti arba pristabdyti
Praleisti į kitą dainą
Praleisti į paskutinę dainą
- Mikstapas
- Mikstapai
+ Mikso juosta
+ Misko juostos
Bibliotekos skirtukai
- Keisti bibliotekos skirtukų matomumą ir tvarką.
+ Keisk bibliotekos skirtukų matomumą ir tvarką.
Pageidauti albumui
Pageidauti albumui, jei vienas groja
Programėlę nerasta, kuri galėtų atlikti šią užduotį.
- Auxio piktograma
+ „Auxio“ piktograma
Perkelti šią dainą
Perkelti šį skirtuką
- Muzikos pakrovimas nepavyko.
- Auxio reikia leidimo skaityti tavo muzikos biblioteką.
- Diskas %d
+ Nepavyko įkelti muzikos.
+ „Auxio“ reikia leidimo skaityti tavo muzikos biblioteką.
+ %d diskas
+%.1f dB
-%.1f dB
Bendra trukmė: %s
@@ -150,61 +148,53 @@
Rinkiniai
Rinkinys
Prisiminti maišymą
- Palikti maišymą įjungtą, kai groja nauja daina.
- Persukti prieš praleistant atgal
- Persukti atgal prieš praleistant į ankstesnę dainą.
+ Palik maišymą įjungtą, kai groja nauja daina.
+ Persukti prieš praleidžiant atgal
+ Persuk atgal prieš praleidžiant į ankstesnę dainą.
Pauzė ant kartojimo
- Kai grojant iš bibliotekos
- Kai grojant iš elemento detalių
- Pašalinti aplanką
+ Kai leidžiant iš bibliotekos
+ Kai leidžiant iš elemento informacijos
+ Pašalinti aplanką
Žanras
Ieškok savo bibliotekoje…
Ekvalaizeris
- Režimas
- Automatinis perkrauvimas
- Muzikos nerasta.
- Sustabdyti grojimą
+ Automatinis perkėlimas
+ Muzika nerasta.
+ Sustabdyti įrašo perklausą
Nėra takelio
Praleisti į kitą
- Automatinis ausinių grojimas
+ Automatinis ausinių leidimas
Kartojimo režimas
Atidaryti eilę
Išvalyti paieškos užklausą
- Muzika nebus kraunama iš pridėtų aplankų, kurių tu pridėsi.
- Įtraukti
Pašalinti šią dainą
- Groti iš visų dainų
- Groti iš parodyto elemento
- Groti iš albumo
- Groti iš atlikėjo
- Išvalyta būsena
- Neįtraukti
- Muzika bus kraunama iš aplankų, kurių tu pridėsi.
+ Leisti iš visų dainų
+ Leisti iš parodyto elemento
+ Leisti iš albumo
+ Leisti iš atlikėjo
%d Hz
- Perkrauti muzikos biblioteką, kai ji pasikeičia (reikia nuolatinio pranešimo).
- Pakrautos dainos: %d
- Pakrautos žanros: %d
- Pakrauti albumai: %d
- Pakrauti atlikėjai: %d
- Kraunama tavo muzikos biblioteka… (%1$d/%2$d)
+ Perkelk muzikos biblioteką, kai ji pasikeičia (reikia nuolatinio pranešimo).
+ Įkeltos dainos: %d
+ Įkeltos žanros: %d
+ Įkelti albumai: %d
+ Įkelti atlikėjai: %d
+ Įkėliamas tavo muzikos biblioteka… (%1$d/%2$d)
Maišyti visas dainas
Suasmeninti
Įspėjimas: keičiant išankstinį stiprintuvą į didelę teigiamą reikšmę, kai kuriuose garso takeliuose gali atsirasti tarpų.
Albumo viršelis %s
Atlikėjo vaizdas %s
- Nėra grojančio muzikos
- Sustabdyti, kai daina kartojasi.
+ Nėra leidžiamos muzikos
+ Sustabdyk, kai daina kartojasi.
Turinys
- Muzikos aplankai
+ Muzikos aplankai
Atnaujinti muziką
- Perkrauti muzikos biblioteką, naudojant talpyklos žymes, kai įmanoma.
- Pasirinktinis grojimo juostos veiksmas
- Nepavyksta atkurti būsenos.
- ReplayGain išankstinis stiprintuvas
- Išsaugoti grojimo būseną
- Tvarkyti, kur muzika turėtų būti kraunama iš.
+ Perkrauk muzikos biblioteką, naudojant podėlio žymes, kai įmanoma.
+ Pasirinktinis įrašo perklausos juostos veiksmas
+ „ReplayGain“ išankstinis stiprintuvas
+ Tvarkyk, kur muzika turėtų būti įkeliama iš.
Žanro vaizdas %s
- Įjungti maišymą arba išjungti
+ Įjungti arba išjungti maišymą
%d takelis
Keisti kartojimo režimą
Indigos
@@ -213,32 +203,25 @@
DJ miksas
Gyvai rinkinys
Remikso rinkinys
- Išvalyti anksčiau išsaugotą grojimo būseną (jei yra).
- Daugiareikšmiai separatoriai
+ Daugiareikšmiai skirtukai
Pasvirasis brūkšnys (/)
Pliusas (+)
Ampersandas (&)
Albumų viršeliai
Išjungta
- Greitis
- Išsaugoti dabartinę grojimo būseną dabar.
- Išvalyti grojimo būseną
- Konfigūruoti simbolius, kurie nurodo kelias žymių reikšmes.
+ Greita
+ Konfigūruok simbolius, kurie nurodo kelias žymių reikšmes.
Kablelis (,)
- Koregavimas be žymių
+ Koreguoti be žymių
Įspėjimas: naudojant šį nustatymą, kai kurios žymes gali būti neteisingai interpretuojamos kaip turinčios kelias reikšmes. Tai galima išspręsti prieš nepageidaujamus skiriamuosius ženklus su agalinių brūkšniu (\\).
Kabliataškis (;)
- Aukštos kokybės
- Atkurti grojimo būseną
+ Aukštos kokybės
Neįtraukti nemuzikinių
- Ignoruoti garso failus, kurie nėra muzika, tokius kaip tinklalaides.
- Išankstinis stiprintuvas taikomas esamam koregavimui grojimo metu.
- Koregavimas su žymėmis
- Atkurti anksčiau išsaugotą grojimo būseną (jei yra).
+ Ignoruok garso failus, kurie nėra muzika, tokius kaip tinklalaides.
+ Išankstinis stiprintuvas taikomas esamam koregavimui įrašo perklausos metu.
+ Koreguoti su žymėmis
Slėpti bendradarbius
- Rodyti tik tuos atlikėjus, kurie yra tiesiogiai įtraukti į albumą (geriausiai veikia gerai pažymėtose bibliotekose).
- Nepavyksta išvalyti būsenos.
- Nepavyksta išsaugoti būsenos.
+ Rodyk tik tuos atlikėjus, kurie yra tiesiogiai įvardyti į albumą (geriausiai veikia gerai pažymėtose bibliotekose).
- %d atlikėjas
- %d atlikėjai
@@ -246,31 +229,29 @@
- %d atlikėjų
Perskenuoti muziką
- Išvalyti žymių talpyklą ir pilnai perkrauti muzikos biblioteką (lėčiau, bet labiau užbaigta).
+ Išvalyk žymių podėlį ir pilnai perkrauk muzikos biblioteką (lėčiau, bet labiau užbaigta).
%d pasirinkta
- Groti iš žanro
+ Leisti iš žanro
Viki
%1$s, %2$s
Atkurti
Biblioteka
Elgesys
Pakeisk programėlės temą ir spalvas.
- Valdyk, kaip muzika ir vaizdai kraunami.
- Konfigūruok garso ir grojimo elgesį.
+ Valdyk, kaip muzika ir vaizdai įkeliami.
+ Konfigūruok garso ir įrašo perklausos elgesį.
Pritaikyk naudotojo sąsajos valdiklius ir elgseną.
Muzika
Vaizdai
- Grojimas
- ReplayGain
- Aplankai
- Pastovumas
+ Įrašo perklausa
+ Garso normalizavimas
+ Aplankai
Mažėjantis
- Teisingai surūšiuoti pavadinimus, kurie prasideda skaičiais arba žodžiais, tokiais kaip „the“ (geriausiai veikia su anglų kalbos muzika).
+ Teisingai surūšiuok pavadinimus, kurie prasideda skaičiais arba žodžiais, tokiais kaip „the“ (geriausiai veikia su anglų kalbos muzika).
Išmanusis rūšiavimas
Grojaraštis
Grojaraščiai
Grojaraščio vaizdas %s
- Kurti naują grojaraštį
Naujas grojaraštis
Pridėti į grojaraštį
Pridėta į grojaraštį
@@ -287,13 +268,13 @@
Sukurtas grojaraštis
Ištrintas grojaraštis
Nėra disko
- Redaguojama %s
+ %s redaguojama
Pasirodo
Daina
Peržiūrėti
- Apkarpyti visus albumų viršelius iki 1:1 kraštinių koeficiento.
+ Apkarpyk visus albumų viršelius iki 1:1 kraštinių koeficiento.
Priversti kvadratinių albumų viršelius
- Groti dainą pačią
+ Leisti dainą pačią
Rūšiuoti pagal
Kryptis
Pasirinkimo vaizdas
@@ -303,14 +284,14 @@
Daugiau
Pranešti
Nėra albumų
- Demo
- Demos
+ Demo versija
+ Demo versijos
Importuotas grojaraštis
Importuotas grojaraštis
Eksportuotas grojaraštis
- Nepavyksta eksportuoti grojaraščio į šį failą
- ReplayGain takelio koregavimas
- ReplayGain albumo koregavimas
+ Nepavyksta eksportuoti grojaraščio į šį failą.
+ „ReplayGain“ takelio koregavimas
+ „ReplayGain“ albumo koregavimas
Autorius
Aukoti
Palaikytojai
@@ -325,7 +306,28 @@
Kelio stilius
Absoliutinis
Santykinis
- Naudoti Windows suderinamus kelius
+ Naudoti „Windows“ suderinamus kelius
Prisiminti pauzę
- Išlieka grojimas ir (arba) pristabdomas, kai praleidžiama arba redaguojama eilė.
+ Išlaikyk leidimą / pristabdymą, kai praleidžiama arba redaguojama eilė.
+ Išjungta
+ Paleidžia „Auxio“ naudojant anksčiau išsaugotą būseną. Jei nėra išsaugotos būsenos, visos dainos bus išmaišytos. Įrašo perklausa bus pradėta iš karto.
+\n
+\nĮSPĖJIMAS: būk atsargus (-i) valdant šią paslaugą, jei ją uždarysi ir vėl bandysi naudoti, tikriausiai sutriks programėlė.
+ Paleisti įrašo perklausą
+ Daugiau
+ Atsiliepimai
+ Sukurti problemą svetainėje „GitHub“
+ MPEG-4, kuriame yra %s
+ Nežinomas albumas
+ Čia bus rodomos jūsų dainos.
+ Čia bus rodomi jūsų albumai.
+ Čia bus rodomi jūsų atlikėjai.
+ Čia bus rodomi jūsų grojaraščiai.
+ Čia bus rodomi jūsų žanrai.
+ Pasirinkti aplankus
+ Siųsti el. laišką
+ Naujas aplankas
+ „Apple“ be nuostolių garso kodekas (ALAC)
+ Nežinomas
+ Išsaugoti vietos
\ No newline at end of file
diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml
new file mode 100644
index 000000000..a6b3daec9
--- /dev/null
+++ b/app/src/main/res/values-lv/strings.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml
index b93ec52f9..f363362d8 100644
--- a/app/src/main/res/values-ml/strings.xml
+++ b/app/src/main/res/values-ml/strings.xml
@@ -36,12 +36,10 @@
ക്രമീകരണങ്ങൾ
സംഗീതം
ചിത്രങ്ങൾ
- വേഗം
- ഉയർന്ന നിലവാരമുള്ളത്
+ വേഗം
+ ഉയർന്ന നിലവാരമുള്ളത്
അടുത്ത പാട്ടിലേക്ക് പോകുക
ഗ്രന്ഥശാല
- ഒഴിവാക്കുക
- സ്ഥിരോത്സാഹം
പച്ച
അവസാന പാട്ടിലേക്ക് പോകുക
കളിക്കുക അല്ലെങ്കിൽ താൽക്കാലികമായി നിർത്തുക
@@ -87,11 +85,8 @@
കലാകാരനിലേക്ക് പോകുക
സവിശേഷതകൾ കാണുക
- സ്ഥിതി സംരക്ഷിച്ചു
അവരോഹണം
- സ്ഥിതി പുനഃസ്ഥാപിച്ചു
വിക്കി
- സ്ഥിതി മായ്ച്ചു
തത്സമയം
തത്സമയ സമാഹാരം
ഗീതം
diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml
index 85c0607ee..cb76f29f3 100644
--- a/app/src/main/res/values-nb-rNO/strings.xml
+++ b/app/src/main/res/values-nb-rNO/strings.xml
@@ -1,18 +1,18 @@
Legg til i kø
- Personaliser
+ Personalisér
Artister innlastet: %d
Legg til i spilleliste
Kildekode
Lisenser
Lys
Automatisk
- ReplayGain
+ Volumnormalisering
Gjenoppfrisk musikk
Justering uten etiketter
Flytt denne fanen
- Tøm søk
+ Tøm søket
Auxio-ikon
Åpne køen
Ingen dato
@@ -29,14 +29,14 @@
Artist
Artister
Sjanger
- Sjangere
+ Sjangrer
Ny spilleliste
Søk
- Kompilasjoner
- Kompilasjon
- Live-kompilasjon
+ Sammenstillinger
+ Sammenstilling
+ Live-sammenstilling
Remiks-kompilasjon
- Bidrar på
+ Fremtrer på
Alle
Navn
Varighet
@@ -44,7 +44,6 @@
Kø
Spill neste
Bibliotek
- Kunne ikke lagre tilstand
- %d artist
- %d artister
@@ -53,87 +52,77 @@
- %d spor
- %d spor
- Filter
+ Filtrér
Spilleliste
Spillelister
Slett
- Disk
+ Platenummer
Gå til artist
Gå til album
Vis
Del
Størrelse
Bitrate
- Samplerate
- Omstokking
- Omstokk alt
+ Samplingsfrekvens
+ Omstokk
+ Omstokk alle
Avbryt
Om
Vis og kontroller musikkavspilling
Tillagt i kø
Gjentagelsesmodus
Tilpass grensesnittskontroller og adferd
- Utseende og adferd
+ Utseende
Avrundede hjørner i ytterligere grensesnittselementer (krever at albumsomslag er avrundet)
Behold omstokking ved avspilling av et nytt spor
Husk omstokking
Skråstrek (/)
Plusstegn (+)
- Skjul bidragsytere
+ Skjul medvirkende
Bilder
- Albumsomslag
- Rask
- Høy kvalitet
- Alltid start avspilling når hodetelefoner kobles til (trenger ikke å virke på alle enheter)
+ Albumomslag
+ Raskt
+ Høy kvalitet
+ Alltid start avspilling når hodetelefoner kobles til (virker kanskje ikke på alle enheter)
Sett opp lyd- og avspillingsadferd
Lyd
ReplayGain-strategi
- Håndter hvor musikk lastes inn fra
+ Håndter hvor musikk lastes inn fra
Forforsterkning brukes for eksisterende justering under avspilling
- Modus
- Lagre nåværende avspillingstilstand nå
- Lagre avspillingstilstand
Tøm etiketthurtiglager og last inn hele musikkbiblioteket igjen (tregere, men mer fullstendig)
- Vedvarende
Spor %d
- Albumsomslag
+ Albumomslag
Skru omstokking på eller av
Fjern dette sporet
Flytt dette sporet
- Ingen disk
- Ingen spor
+ Ingen plate
+ Intet spor
Avansert audio-koding (AAC)
Dynamisk
%d valgt
Laster inn musikkbiblioteket ditt … (%1$d/%2$d)
%d Hz
- Slett %s for godt\?
- Gjenopprett tidligere lagret avspillingstilstand (hvis noen)
+ Slett %s for godt? Dette kan ikke angres.
MPEG-4-lyd
Spilleliste %d
Cyanblå
Spor innlastet: %d
Dato
Endre drakten og programfargene
- Mapper
- Tøm avspillingstilstand
- Fjern tidligere lagret avspillingstilstand (hvis noen)
+ Mapper
Gul
Intelligent sortering
Gi nytt navn
Gi spillelisten nytt navn
Slett spilleliste\?
OK
- Tilstand lagret
Versjon
Wiki
Tilbakestill
Legg til
- Tilstand fjernet
- Tilstand gjenopprettet
Fargedrakt
Svart drakt
- Ifør helsvart drakt
+ Bruk en helsvart mørk drakt
Drakt
Mørk
Musikk
@@ -141,43 +130,39 @@
Komma (,)
Semikolon (;)
Automatisk gjeninnlasting
- Ignorer lydfiler som ikke er musikk, som f-eks. nettradioopptak
+ Ignorer lydfiler som ikke er musikk, som f.eks. podkaster
Multiverdi-inndelere
Sett opp tegn for inndeling av flere etikett-verdier
- Kun vis artister som er kreditert direkte på album (fungerer best med godt etikettmerkede bibliotek)
+ Vis kun artister som er kreditert direkte på album (fungerer best med velmerkede bibliotek)
Reskann musikk
- Musikk vil kun innlastes fra mappene du legger til.
Last inn musikkbiblioteket igjen, ved bruk av hurtiglagrede etiketter når mulig
- Ingen mapper
- Kunne ikke gjenopprette tilstand
- Kunne ikke fjerne tilstand
+ Ingen mapper
Album innlastet: %d
- Biblioteksstatistikk
+ Bibliotekstatistikk
Av
Avrundede hjørner
ReplayGain-forforsterkning
Justering med etiketter
Adferd
Innhold
- Musikkmapper
- Gjeninnlast musikkbibliotek når det endrer seg (krever vedvarende merknad)
- Kunne ikke laste inn musikk
- Denne mappen støttes ikke
+ Musikkmapper
+ Gjeninnlast musikkbibliotek når det endrer seg (krever vedvarende varsling)
+ Klarte ikke laste inn musikk
+ Denne mappen støttes ikke
Hopp til neste spor
Hopp til siste spor
Omstokk alle spor
- Fjern mappe
+ Fjern mappe
Ukjent sjanger
Sjangerbilde for %s
Ukjent artist
Sjangere innlastet: %d
Stopp avspilling
- Opprett en ny spilleliste
Grønn
Mørkegrønn
- Turkis
+ Blågrønn
Rediger
- Albumsomslag for %s
+ Albumomslag for %s
Lilla
Blå
Fritt tapsfritt lydkodek (FLAC)
@@ -187,21 +172,21 @@
DJ-miks
Live
Spilles nå
- Omstokking
+ Omstokk
Stigende
Format
Vis egenskaper
- Spor-egenskaper
+ Sporegenskaper
Pause ved gjentagelse
Rød
- %d album
- %d album
- Synkende
+ Fallende
Spor
Dato tillagt
- Sorter
+ Sortér
Utviklet av Alexander Capehart
Søk i biblioteket ditt …
Innstillinger
@@ -211,21 +196,18 @@
Spilleliste slettet
Lagt til i spilleliste
Hopp til neste
- Egendefinert merknadshandling
+ Egendefinert varslingshandling
Foretrekk spor
Pause når et spor gjentas
Spol tilbake før spor hoppes over
Spol tilbake før hopp til forrige spor
Advarsel: Endring av forforsterkning til høy positiv verdi kan resultere i forvrengning ved høyt lydtrykk på noen spor.
Avspilling
- Disk %d
+ Plate %d
Skjerm
- Biblioteksfaner
- Endre synlighet og rekkefølgen på biblioteksfaner
+ Bibliotekfaner
+ Endre synlighet og rekkefølgen på bibliotekfaner
Egendefinert avspillingsfelt-handling
- Utelat
- Inkluder
- Musikk vil ikke innlastes fra mappene du legger til.
Ingen spor
Ingen musikk spilles
Oransje
@@ -233,12 +215,11 @@
Spill fra album
Spill fra sjanger
Foretrekk album
- Gjenopprett avspillingstilstand
Ampersand (&)
Spill sporet for seg selv
- Påtving kvadratiske albumsomslag
+ Påtving kvadratiske albumomslag
Korrekt sortering av navn som begynner med tall eller ord som «the» (fungerer best med engelskspråklig musikk)
- Beskjær alle albumsomslag til 1:1-sideforhold
+ Beskjær alle albumomslag til 1:1-sideforhold
Spill av eller pause
Artistbilde for %s
Auxio trenger tilgang til å lese musikkbiblioteket ditt
@@ -254,11 +235,11 @@
Album
Live-EP
Spill fra alle spor
- Laster inn musikk …
+ Musikk lastes inn
Live-singel
- Laster inn musikk …
- Holder øye med musikkbiblioteket …
- Innvilg
+ Laster inn musikk
+ Holder øye med musikkbiblioteket
+ Bevilge
Singler
Spor
Album
@@ -279,13 +260,55 @@
Fant ikke noe musikk
Matroska-lyd
Spill fra vist element
- Ogg-lyd
+ OGG-lyd
Mørkeblå
- Foretrekk album hvis det avspilles
- Hodesett-autoavspilling
+ Foretrekk album hvis et album spilles av
+ Automatisk avspilling med headsett
Spillelistebilde for %s
Kontroller hvordan musikk og bilder innlastes
Installer et program som kan utføre denne handlingen først
- Advarsel: Kan forårsake feilaktig tolkning av etiketter som om de har flere verdier. Kan løses ved å innlede uønskede inndelertegn med omvendt skråstrek (\\).
+ Advarsel: Hvis denne innstillingen er påslått, kan enkelte etiketter feiltolkes som om de har flere verdier. Dette kan løses ved å legge en omvendt skråstrek (\\) foran skilletegn som ikke skal skille verdier.
%d kbps
+ Av
+ Bane
+ Utvalg
+ Eksportér spilleliste
+ ReplayGain-sporjustering
+ Absolutt
+ Bruk Windows-kompatible baner
+ Banestil
+ Relativt
+ Forbli spillende/pauset ved sporbytting eller køredigering
+ Spilleliste importert
+ Importert spilleliste
+ Spilleliste eksportert
+ Kan ikke importere en spilleliste fra denne filen
+ Kan ikke eksportere spillelisten til denne filen
+ Demo
+ Demoer
+ Vis mer
+ Feilopplysninger
+ Kopiert
+ ReplayGain-albumjustering
+ Forfatter
+ Donér
+ Støttespillere
+ Donér til prosjektet slik at navnet ditt kan legges til her!
+ Rapportér
+ Husk pause
+ Utvalgsbild
+ Ingen album
+ Tom spilleliste
+ Importér spilleliste
+ Sortér etter
+ Retning
+ Importér
+ Eksportér
+ Mer
+ Begynner Auxio med den forrige lagrede tilstanden. Dersom ingen lagrede tilstand er tilgjengelig, skal sangene omstokkes. Avspilling begynner med én gang.\n\nADVARSEL: Vær varsom med å kontrollere denne tjenesten, hvis du lukker den og så prøver å bruke den igjen, skal det nok få appen til å krasje.
+ Begynn avspilling
+ Opprett en sak på GitHub
+ Gi tilbakemelding
+ Send en e-post
+ Velg mapper
\ No newline at end of file
diff --git a/app/src/main/res/values-ne/strings.xml b/app/src/main/res/values-ne/strings.xml
new file mode 100644
index 000000000..65aac81c4
--- /dev/null
+++ b/app/src/main/res/values-ne/strings.xml
@@ -0,0 +1,67 @@
+
+
+ गीतहरु लोड हुँदै छन्
+ एन्डरोइडको लागि एक सहज, विवेकशील गीत बजाउने एप।
+ साउन्डट्रयाक
+ सिङ्गल
+ लाइभ ईपी
+ डेमोहरु
+ रीमिक्स सङ्कलन
+ डिस्क
+ प्लेलिस्ट एक्सपोर्ट गर्नुहोस्
+ सबै
+ मिति
+ प्लेलिस्टको नाम परिवर्तन गर्नुहोस्
+ प्लेलिस्टहरु
+ खाली प्लेलिस्ट
+ पुनः प्रयास गर्नुहोस्
+ प्लेलिस्ट इम्पोर्ट गर्नुहोस्
+ एल्बम
+ गीतहरु लोड गर्दै
+ सङ्गीत लाइब्रेरी निगरानी गर्दै
+ फोल्डर छान्नुहोस्
+ अझै
+ दिनुहोस्
+ गीतहरु
+ गीत
+ सबै गीत
+ लाइभ एल्बम
+ रीमिक्स एल्बम
+ ईपीहरु
+ ईपी
+ रीमिक्स ईपी
+ सिङ्गलहरु
+ लाइभ सिङ्गल
+ सङ्कलनहरु
+ लाइभ सङ्कलन
+ साउन्डट्रयाकहरु
+ मिक्सटेपहरु
+ मिक्सटेप
+ डेमो
+ डिजे मिक्सहरु
+ लाइभ
+ रीमिक्सहरु
+ यीमा देखिन्छन्
+ कलाकार
+ कलाकारहरु
+ विधा
+ विधाहरु
+ प्लेलिस्ट
+ नयाँ प्लेलिस्ट
+ इम्पोर्ट गरिएको प्लेलिस्ट
+ इम्पोर्ट गर्नुहोस्
+ एक्सपोर्ट गर्नुहोस्
+ नाम परिवर्तन गर्नुहोस्
+ हटाउनुहोस्
+ साँचै प्लेलिस्ट हटाउने?
+ सम्पादन गर्नुहोस्
+ खोज्नुहोस्
+ अवधि
+ गीतको सङ्ख्या
+ डिजे मिक्स
+ नाम
+ छान्नुहोस्
+ एल्बमहरु
+ रीमिक्स सिङ्गल
+ सङ्कलन
+
diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml
deleted file mode 100644
index 10dfde63d..000000000
--- a/app/src/main/res/values-night/colors.xml
+++ /dev/null
@@ -1,399 +0,0 @@
-
-
-
- #01000000
-
-
- @color/material_dynamic_secondary20
- @color/material_dynamic_neutral90
- @color/material_dynamic_neutral20
-
- #FFB4A8
- #680001
- #940002
- #FFDAD3
- #BC1714
- #E7BCB6
- #442A26
- #5D3F3B
- #FFDAD4
- #DFC38C
- #3F2E04
- #574419
- #FCDFA6
- #FFB4A9
- #680003
- #930006
- #FFDAD4
- #211A19
- #EDE0DE
- #534341
- #D8C2BF
- #EDE0DE
- #362F2E
-
- #FFB2C0
- #670024
- #900036
- #FFD9DF
- #BC0049
- #E5BDC2
- #43292D
- #5C3F43
- #FFD9DE
- #EBBF90
- #452B08
- #5F411C
- #FFDDB8
- #FFB4A9
- #680003
- #930006
- #FFDAD4
- #201A1B
- #ECE0E0
- #524345
- #D6C1C3
- #ECE0E0
- #352F2F
-
- #FBAAFF
- #570068
- #7B0091
- #FFD5FF
- #9A25AE
- #D7BFD5
- #3B2B3B
- #534153
- #F5DBF2
- #F6B8AE
- #4C251F
- #663B34
- #FFDAD2
- #FFB4A9
- #680003
- #930006
- #FFDAD4
- #1E1A1D
- #E9E0E5
- #4D444C
- #D0C3CC
- #E9E0E5
- #332F32
-
- #D4BAFF
- #3E008E
- #5727A7
- #ECDCFF
- #6F43BF
- #CDC2DB
- #342D41
- #4B4358
- #E9DEF7
- #F0B8C5
- #4A2530
- #643B46
- #FFD9E2
- #FFB4A9
- #680003
- #930006
- #FFDAD4
- #1D1B1F
- #E6E1E5
- #49454E
- #CBC4CF
- #E6E1E5
- #323033
-
- #B9C3FF
- #08218A
- #293CA0
- #DDE0FF
- #4355B9
- #C4C5DD
- #2D2F42
- #43465A
- #E0E1FA
- #E5BAD7
- #45263E
- #5D3C55
- #FFD7F3
- #FFB4A9
- #680003
- #930006
- #FFDAD4
- #1B1B1F
- #E4E1E6
- #46464F
- #C6C5D0
- #E4E1E6
- #303034
-
- #9CCAFF
- #00325A
- #00497F
- #D0E4FF
- #0061A6
- #BBC8DB
- #253140
- #3C4858
- #D6E3F7
- #D6BEE4
- #3B2948
- #523F5F
- #F3DAFF
- #FFB4A9
- #930006
- #680003
- #FFDAD4
- #1B1B1B
- #E2E2E6
- #42474E
- #C3C7D0
- #E2E2E6
- #2F3033
-
- #62D3FF
- #003546
- #004D64
- #BAE9FF
- #006684
- #B4CAD6
- #1F333C
- #354A53
- #D0E6F2
- #C5C2EA
- #2E2D4C
- #454365
- #E3DFFF
- #FFB4A9
- #680003
- #930006
- #FFDAD4
- #191C1E
- #E1E2E4
- #40484C
- #C0C8CD
- #E1E2E4
- #2F3132
-
- #44D8F1
- #00363F
- #004E5A
- #9CEFFF
- #006877
- #B1CBD1
- #1C3439
- #334A4F
- #CDE7ED
- #BCC5EA
- #262F4D
- #3D4665
- #DAE1FF
- #FFB4A9
- #680003
- #930006
- #FFDAD4
- #191C1D
- #E1E3E3
- #3F484A
- #BFC8CA
- #E1E3E3
- #2D3132
-
- #53DBC9
- #003730
- #005047
- #74F7E5
- #006A5F
- #B1CCC6
- #1C3531
- #334B47
- #CDE8E2
- #ADCAE6
- #153349
- #2D4960
- #CBE5FF
- #FFB4A9
- #680003
- #930006
- #FFDAD4
- #191C1B
- #E0E3E1
- #3F4947
- #BFC9C6
- #E0E3E1
- #2E3130
-
- #78DC77
- #003907
- #00530F
- #93F990
- #006E17
- #B9CCB3
- #253423
- #3B4B38
- #D5E8CE
- #A1CFD5
- #00363B
- #1E4D52
- #BCEBF0
- #FFB4A9
- #680003
- #930006
- #FFDAD4
- #1A1C19
- #E2E3DD
- #424840
- #C2C8BD
- #E2E3DD
- #2F312D
-
- #9ED75C
- #1C3700
- #2C5000
- #B9F475
- #3C6A00
- #BFCAAD
- #2A331E
- #404A34
- #DBE7C7
- #A0D0CC
- #003735
- #1E4E4B
- #BCECE8
- #FFB4A9
- #680003
- #930006
- #FFDAD4
- #1A1C17
- #E3E3DB
- #44483D
- #C4C8B9
- #E3E3DB
- #2F312C
-
- #C1D02C
- #2E3400
- #434B00
- #DEED49
- #5A6400
- #C7C9A6
- #30321A
- #46492E
- #E4E5C1
- #A3D0C1
- #06372C
- #234E43
- #BEECDC
- #FFB4A9
- #680003
- #930006
- #FFDAD4
- #1C1C17
- #E5E2DA
- #47473B
- #C8C7B7
- #E5E2DA
- #31312B
-
- #FABD00
- #402D00
- #5C4300
- #FFDF99
- #795900
- #D7C4A0
- #3B2F15
- #52452A
- #F4E0BB
- #B0CFA9
- #1D361C
- #334D31
- #CCEBC5
- #FFB4A9
- #680003
- #930006
- #FFDAD4
- #1E1B16
- #E9E1D8
- #4D4639
- #D0C5B4
- #E9E1D8
- #3C2E16
-
- #FFB86D
- #4B2800
- #6A3B00
- #FFDCBB
- #E2C1A4
- #402C18
- #8C5000
- #59422C
- #FEDCBE
- #C0CC9A
- #2B3410
- #414B25
- #DCE8B4
- #FFB4A9
- #680003
- #930006
- #FFDAD4
- #1F1B17
- #EBE0D9
- #51453A
- #D5C3B5
- #EBE0D9
- #352F2B
-
- #E7BEB0
- #442A20
- #5D4035
- #FFDBCD
- #77574C
- #FFB598
- #5C1A00
- #7B2E0D
- #FFDBCD
- #D5C78E
- #383005
- #50461A
- #F1E2A7
- #FFB4A9
- #680003
- #930006
- #FFDAD4
- #201A18
- #EDE0DC
- #52433E
- #D8C2BB
- #EDE0DC
- #362F2D
-
- #EEEEEE
- #424242
- #757575
- #616161
- #F5F5F5
- #B4B4B4
- #1F1F1F
- #353535
- #D0D0D0
- #C5C5C5
- #2E2E2E
- #454545
- #E3E3E3
- #FFB4B4
- #680000
- #930000
- #FFDADA
- #1F1F1F
- #E1E1E1
- #484848
- #C8C8C8
- #fafafa
- #2D3132
-
- @color/m3_ref_palette_dynamic_primary80
-
\ No newline at end of file
diff --git a/app/src/main/res/values-night/colors_android.xml b/app/src/main/res/values-night/colors_android.xml
new file mode 100644
index 000000000..36d82280f
--- /dev/null
+++ b/app/src/main/res/values-night/colors_android.xml
@@ -0,0 +1,15 @@
+
+
+
+ #01000000
+
+
+ @color/material_dynamic_secondary20
+ @color/material_dynamic_neutral90
+ @color/material_dynamic_neutral20
+
+ @color/m3_ref_palette_dynamic_primary80
+
\ No newline at end of file
diff --git a/app/src/main/res/values-night/colors_ui.xml b/app/src/main/res/values-night/colors_ui.xml
new file mode 100644
index 000000000..87bd21bc7
--- /dev/null
+++ b/app/src/main/res/values-night/colors_ui.xml
@@ -0,0 +1,2274 @@
+
+
+ #FFB4A9
+ #561E18
+ #73342C
+ #FFDAD5
+ #E7BDB7
+ #442926
+ #5D3F3B
+ #FFDAD5
+ #DFC38C
+ #3E2E04
+ #574419
+ #FCDFA6
+ #FFB4AB
+ #690005
+ #93000A
+ #FFDAD6
+ #1A1110
+ #F1DEDC
+ #1A1110
+ #F1DEDC
+ #534341
+ #D8C2BE
+ #A08C89
+ #534341
+ #000000
+ #F1DEDC
+ #392E2C
+ #904A42
+ #FFDAD5
+ #3B0906
+ #FFB4A9
+ #73342C
+ #FFDAD5
+ #2C1512
+ #E7BDB7
+ #5D3F3B
+ #FCDFA6
+ #261A00
+ #DFC38C
+ #574419
+ #1A1110
+ #423735
+ #140C0B
+ #231918
+ #271D1C
+ #322826
+ #3D3231
+ #FFBAB0
+ #330503
+ #CC7B70
+ #000000
+ #EBC1BB
+ #26100D
+ #AE8883
+ #000000
+ #E3C790
+ #1F1500
+ #A68E5B
+ #000000
+ #FFBAB1
+ #370001
+ #FF5449
+ #000000
+ #1A1110
+ #F1DEDC
+ #1A1110
+ #FFF9F8
+ #534341
+ #DCC6C3
+ #B39E9B
+ #927F7C
+ #000000
+ #F1DEDC
+ #322826
+ #74352D
+ #FFDAD5
+ #2C0101
+ #FFB4A9
+ #5E241D
+ #FFDAD5
+ #200B08
+ #E7BDB7
+ #4B2F2B
+ #FCDFA6
+ #191000
+ #DFC38C
+ #453409
+ #1A1110
+ #423735
+ #140C0B
+ #231918
+ #271D1C
+ #322826
+ #3D3231
+ #FFF9F8
+ #000000
+ #FFBAB0
+ #000000
+ #FFF9F8
+ #000000
+ #EBC1BB
+ #000000
+ #FFFAF7
+ #000000
+ #E3C790
+ #000000
+ #FFF9F9
+ #000000
+ #FFBAB1
+ #000000
+ #1A1110
+ #F1DEDC
+ #1A1110
+ #FFFFFF
+ #534341
+ #FFF9F8
+ #DCC6C3
+ #DCC6C3
+ #000000
+ #F1DEDC
+ #000000
+ #4E1812
+ #FFE0DB
+ #000000
+ #FFBAB0
+ #330503
+ #FFE0DB
+ #000000
+ #EBC1BB
+ #26100D
+ #FFE4AF
+ #000000
+ #E3C790
+ #1F1500
+ #1A1110
+ #423735
+ #140C0B
+ #231918
+ #271D1C
+ #322826
+ #3D3231
+
+ #FFB2BD
+ #561D29
+ #72333F
+ #FFD9DD
+ #E5BDC1
+ #43292D
+ #5C3F43
+ #FFD9DD
+ #EABF8F
+ #452B07
+ #5E411C
+ #FFDDB9
+ #FFB4AB
+ #690005
+ #93000A
+ #FFDAD6
+ #191112
+ #F0DEDF
+ #191112
+ #F0DEDF
+ #524345
+ #D6C2C3
+ #9F8C8E
+ #524345
+ #000000
+ #F0DEDF
+ #382E2F
+ #8E4956
+ #FFD9DD
+ #3B0715
+ #FFB2BD
+ #72333F
+ #FFD9DD
+ #2C1519
+ #E5BDC1
+ #5C3F43
+ #FFDDB9
+ #2B1700
+ #EABF8F
+ #5E411C
+ #191112
+ #413738
+ #140C0D
+ #22191A
+ #261D1E
+ #312829
+ #3D3233
+ #FFB8C2
+ #330310
+ #C97A87
+ #000000
+ #E9C1C5
+ #261014
+ #AC888C
+ #000000
+ #EFC393
+ #241200
+ #B08A5E
+ #000000
+ #FFBAB1
+ #370001
+ #FF5449
+ #000000
+ #191112
+ #F0DEDF
+ #191112
+ #FFF9F9
+ #524345
+ #DBC6C7
+ #B29EA0
+ #917F80
+ #000000
+ #F0DEDF
+ #312829
+ #733440
+ #FFD9DD
+ #2C000B
+ #FFB2BD
+ #5D222F
+ #FFD9DD
+ #200B0E
+ #E5BDC1
+ #4A2F33
+ #FFDDB9
+ #1D0E00
+ #EABF8F
+ #4C300D
+ #191112
+ #413738
+ #140C0D
+ #22191A
+ #261D1E
+ #312829
+ #3D3233
+ #FFF9F9
+ #000000
+ #FFB8C2
+ #000000
+ #FFF9F9
+ #000000
+ #E9C1C5
+ #000000
+ #FFFAF8
+ #000000
+ #EFC393
+ #000000
+ #FFF9F9
+ #000000
+ #FFBAB1
+ #000000
+ #191112
+ #F0DEDF
+ #191112
+ #FFFFFF
+ #524345
+ #FFF9F9
+ #DBC6C7
+ #DBC6C7
+ #000000
+ #F0DEDF
+ #000000
+ #4E1623
+ #FFDFE2
+ #000000
+ #FFB8C2
+ #330310
+ #FFDFE2
+ #000000
+ #E9C1C5
+ #261014
+ #FFE2C5
+ #000000
+ #EFC393
+ #241200
+ #191112
+ #413738
+ #140C0D
+ #22191A
+ #261D1E
+ #312829
+ #3D3233
+
+ #EBB5ED
+ #49204E
+ #613766
+ #FFD6FE
+ #D7BFD5
+ #3B2B3C
+ #534153
+ #F4DBF1
+ #F6B8AD
+ #4C251F
+ #673B34
+ #FFDAD4
+ #FFB4AB
+ #690005
+ #93000A
+ #FFDAD6
+ #171216
+ #EBDFE6
+ #171216
+ #EBDFE6
+ #4D444C
+ #D0C3CC
+ #998D96
+ #4D444C
+ #000000
+ #EBDFE6
+ #352F34
+ #7B4E7F
+ #FFD6FE
+ #310937
+ #EBB5ED
+ #613766
+ #F4DBF1
+ #251626
+ #D7BFD5
+ #534153
+ #FFDAD4
+ #33110C
+ #F6B8AD
+ #673B34
+ #171216
+ #3E373D
+ #110D11
+ #1F1A1F
+ #231E23
+ #2E282D
+ #393338
+ #F0B9F1
+ #2B0432
+ #B280B4
+ #000000
+ #DCC3D9
+ #1F1121
+ #A08A9E
+ #000000
+ #FABCB2
+ #2C0C07
+ #BA837A
+ #000000
+ #FFBAB1
+ #370001
+ #FF5449
+ #000000
+ #171216
+ #EBDFE6
+ #171216
+ #FFF9FA
+ #4D444C
+ #D4C7D1
+ #AB9FA9
+ #8B8089
+ #000000
+ #EBDFE6
+ #2E282D
+ #633867
+ #FFD6FE
+ #24002C
+ #EBB5ED
+ #4F2654
+ #F4DBF1
+ #1A0C1B
+ #D7BFD5
+ #413142
+ #FFDAD4
+ #250704
+ #F6B8AD
+ #532B25
+ #171216
+ #3E373D
+ #110D11
+ #1F1A1F
+ #231E23
+ #2E282D
+ #393338
+ #FFF9FA
+ #000000
+ #F0B9F1
+ #000000
+ #FFF9FA
+ #000000
+ #DCC3D9
+ #000000
+ #FFF9F8
+ #000000
+ #FABCB2
+ #000000
+ #FFF9F9
+ #000000
+ #FFBAB1
+ #000000
+ #171216
+ #EBDFE6
+ #171216
+ #FFFFFF
+ #4D444C
+ #FFF9FA
+ #D4C7D1
+ #D4C7D1
+ #000000
+ #EBDFE6
+ #000000
+ #411947
+ #FFDCFD
+ #000000
+ #F0B9F1
+ #2B0432
+ #F9DFF6
+ #000000
+ #DCC3D9
+ #1F1121
+ #FFE0DB
+ #000000
+ #FABCB2
+ #2C0C07
+ #171216
+ #3E373D
+ #110D11
+ #1F1A1F
+ #231E23
+ #2E282D
+ #393338
+
+ #D3BCFD
+ #38265C
+ #4F3D74
+ #EBDDFF
+ #CDC2DB
+ #342D40
+ #4B4358
+ #E9DEF8
+ #F0B7C5
+ #4A2530
+ #643B46
+ #FFD9E1
+ #FFB4AB
+ #690005
+ #93000A
+ #FFDAD6
+ #151218
+ #E7E0E8
+ #151218
+ #E7E0E8
+ #49454E
+ #CBC4CF
+ #948F99
+ #49454E
+ #000000
+ #E7E0E8
+ #322F35
+ #68548E
+ #EBDDFF
+ #230F46
+ #D3BCFD
+ #4F3D74
+ #E9DEF8
+ #1F182B
+ #CDC2DB
+ #4B4358
+ #FFD9E1
+ #31101B
+ #F0B7C5
+ #643B46
+ #151218
+ #3B383E
+ #0F0D13
+ #1D1B20
+ #211F24
+ #2C292F
+ #36343A
+ #D7C0FF
+ #1D0840
+ #9B86C4
+ #000000
+ #D1C6DF
+ #191325
+ #968DA4
+ #000000
+ #F5BCC9
+ #2B0B16
+ #B68390
+ #000000
+ #FFBAB1
+ #370001
+ #FF5449
+ #000000
+ #151218
+ #E7E0E8
+ #151218
+ #FFF9FF
+ #49454E
+ #CFC8D3
+ #A7A1AB
+ #86818B
+ #000000
+ #E7E0E8
+ #2C292F
+ #513E75
+ #EBDDFF
+ #18023B
+ #D3BCFD
+ #3E2C62
+ #E9DEF8
+ #140E20
+ #CDC2DB
+ #3A3346
+ #FFD9E1
+ #250611
+ #F0B7C5
+ #512A36
+ #151218
+ #3B383E
+ #0F0D13
+ #1D1B20
+ #211F24
+ #2C292F
+ #36343A
+ #FFF9FF
+ #000000
+ #D7C0FF
+ #000000
+ #FFF9FF
+ #000000
+ #D1C6DF
+ #000000
+ #FFF9F9
+ #000000
+ #F5BCC9
+ #000000
+ #FFF9F9
+ #000000
+ #FFBAB1
+ #000000
+ #151218
+ #E7E0E8
+ #151218
+ #FFFFFF
+ #49454E
+ #FFF9FF
+ #CFC8D3
+ #CFC8D3
+ #000000
+ #E7E0E8
+ #000000
+ #321F55
+ #EEE2FF
+ #000000
+ #D7C0FF
+ #1D0840
+ #EEE2FC
+ #000000
+ #D1C6DF
+ #191325
+ #FFDFE5
+ #000000
+ #F5BCC9
+ #2B0B16
+ #151218
+ #3B383E
+ #0F0D13
+ #1D1B20
+ #211F24
+ #2C292F
+ #36343A
+
+ #BAC3FF
+ #222C61
+ #394379
+ #DEE0FF
+ #C3C5DD
+ #2D2F42
+ #434659
+ #E0E1F9
+ #E6BAD7
+ #44263D
+ #5D3C55
+ #FFD7F1
+ #FFB4AB
+ #690005
+ #93000A
+ #FFDAD6
+ #121318
+ #E4E1E9
+ #121318
+ #E4E1E9
+ #46464F
+ #C7C5D0
+ #90909A
+ #46464F
+ #000000
+ #E4E1E9
+ #303036
+ #515B92
+ #DEE0FF
+ #0B154B
+ #BAC3FF
+ #394379
+ #E0E1F9
+ #181A2C
+ #C3C5DD
+ #434659
+ #FFD7F1
+ #2D1228
+ #E6BAD7
+ #5D3C55
+ #121318
+ #39393F
+ #0D0E13
+ #1B1B21
+ #1F1F25
+ #29292F
+ #34343A
+ #C0C7FF
+ #050F46
+ #848DC8
+ #000000
+ #C8C9E1
+ #121526
+ #8D8FA6
+ #000000
+ #EABEDC
+ #270C22
+ #AC85A1
+ #000000
+ #FFBAB1
+ #370001
+ #FF5449
+ #000000
+ #121318
+ #E4E1E9
+ #121318
+ #FDFAFF
+ #46464F
+ #CBC9D4
+ #A3A2AC
+ #83828C
+ #000000
+ #E4E1E9
+ #292A2F
+ #3B447A
+ #DEE0FF
+ #000841
+ #BAC3FF
+ #283267
+ #E0E1F9
+ #0D1021
+ #C3C5DD
+ #323548
+ #FFD7F1
+ #21071D
+ #E6BAD7
+ #4B2C43
+ #121318
+ #39393F
+ #0D0E13
+ #1B1B21
+ #1F1F25
+ #29292F
+ #34343A
+ #FDFAFF
+ #000000
+ #C0C7FF
+ #000000
+ #FDFAFF
+ #000000
+ #C8C9E1
+ #000000
+ #FFF9F9
+ #000000
+ #EABEDC
+ #000000
+ #FFF9F9
+ #000000
+ #FFBAB1
+ #000000
+ #121318
+ #E4E1E9
+ #121318
+ #FFFFFF
+ #46464F
+ #FDFAFF
+ #CBC9D4
+ #CBC9D4
+ #000000
+ #E4E1E9
+ #000000
+ #1C255A
+ #E4E5FF
+ #000000
+ #C0C7FF
+ #050F46
+ #E4E5FE
+ #000000
+ #C8C9E1
+ #121526
+ #FFDDF3
+ #000000
+ #EABEDC
+ #270C22
+ #121318
+ #39393F
+ #0D0E13
+ #1B1B21
+ #1F1F25
+ #29292F
+ #34343A
+
+ #A1C9FD
+ #003259
+ #1B4975
+ #D2E4FF
+ #BBC7DB
+ #253141
+ #3C4858
+ #D7E3F8
+ #D7BDE4
+ #3B2947
+ #533F5F
+ #F3DAFF
+ #FFB4AB
+ #690005
+ #93000A
+ #FFDAD6
+ #111418
+ #E1E2E8
+ #111418
+ #E1E2E8
+ #43474E
+ #C3C6CF
+ #8D9199
+ #43474E
+ #000000
+ #E1E2E8
+ #2E3135
+ #37618E
+ #D2E4FF
+ #001C37
+ #A1C9FD
+ #1B4975
+ #D7E3F8
+ #101C2B
+ #BBC7DB
+ #3C4858
+ #F3DAFF
+ #251431
+ #D7BDE4
+ #533F5F
+ #111418
+ #36393E
+ #0B0E13
+ #191C20
+ #1D2024
+ #272A2F
+ #32353A
+ #A8CEFF
+ #00172E
+ #6B93C4
+ #000000
+ #BFCCDF
+ #0A1725
+ #8592A4
+ #000000
+ #DCC2E8
+ #200F2B
+ #A088AC
+ #000000
+ #FFBAB1
+ #370001
+ #FF5449
+ #000000
+ #111418
+ #E1E2E8
+ #111418
+ #FAFAFF
+ #43474E
+ #C7CBD3
+ #9FA3AB
+ #7F838B
+ #000000
+ #E1E2E8
+ #272A2F
+ #1D4A76
+ #D2E4FF
+ #001225
+ #A1C9FD
+ #003863
+ #D7E3F8
+ #051220
+ #BBC7DB
+ #2B3747
+ #F3DAFF
+ #1A0926
+ #D7BDE4
+ #412F4D
+ #111418
+ #36393E
+ #0B0E13
+ #191C20
+ #1D2024
+ #272A2F
+ #32353A
+ #FAFAFF
+ #000000
+ #A8CEFF
+ #000000
+ #FAFAFF
+ #000000
+ #BFCCDF
+ #000000
+ #FFF9FB
+ #000000
+ #DCC2E8
+ #000000
+ #FFF9F9
+ #000000
+ #FFBAB1
+ #000000
+ #111418
+ #E1E2E8
+ #111418
+ #FFFFFF
+ #43474E
+ #FAFAFF
+ #C7CBD3
+ #C7CBD3
+ #000000
+ #E1E2E8
+ #000000
+ #002B4F
+ #D9E8FF
+ #000000
+ #A8CEFF
+ #00172E
+ #DBE8FC
+ #000000
+ #BFCCDF
+ #0A1725
+ #F6DFFF
+ #000000
+ #DCC2E8
+ #200F2B
+ #111418
+ #36393E
+ #0B0E13
+ #191C20
+ #1D2024
+ #272A2F
+ #32353A
+
+ #8BD0F0
+ #003546
+ #004D64
+ #BEE9FF
+ #B4CAD6
+ #1F333D
+ #354A54
+ #D0E6F2
+ #C6C2EA
+ #2F2D4D
+ #454364
+ #E3DFFF
+ #FFB4AB
+ #690005
+ #93000A
+ #FFDAD6
+ #0F1417
+ #DFE3E7
+ #0F1417
+ #DFE3E7
+ #40484C
+ #C0C8CD
+ #8A9297
+ #40484C
+ #000000
+ #DFE3E7
+ #2C3134
+ #136682
+ #BEE9FF
+ #001F2A
+ #8BD0F0
+ #004D64
+ #D0E6F2
+ #081E27
+ #B4CAD6
+ #354A54
+ #E3DFFF
+ #1A1836
+ #C6C2EA
+ #454364
+ #0F1417
+ #353A3D
+ #0A0F11
+ #171C1F
+ #1B2023
+ #262B2E
+ #303538
+ #90D4F4
+ #001923
+ #5499B7
+ #000000
+ #B8CEDA
+ #031922
+ #7F949F
+ #000000
+ #CBC6EF
+ #141231
+ #908DB2
+ #000000
+ #FFBAB1
+ #370001
+ #FF5449
+ #000000
+ #0F1417
+ #DFE3E7
+ #0F1417
+ #F7FBFF
+ #40484C
+ #C4CCD1
+ #9CA4A9
+ #7C8489
+ #000000
+ #DFE3E7
+ #262B2E
+ #004E66
+ #BEE9FF
+ #00131C
+ #8BD0F0
+ #003B4E
+ #D0E6F2
+ #00131C
+ #B4CAD6
+ #253942
+ #E3DFFF
+ #0F0D2B
+ #C6C2EA
+ #353353
+ #0F1417
+ #353A3D
+ #0A0F11
+ #171C1F
+ #1B2023
+ #262B2E
+ #303538
+ #F7FBFF
+ #000000
+ #90D4F4
+ #000000
+ #F7FBFF
+ #000000
+ #B8CEDA
+ #000000
+ #FEF9FF
+ #000000
+ #CBC6EF
+ #000000
+ #FFF9F9
+ #000000
+ #FFBAB1
+ #000000
+ #0F1417
+ #DFE3E7
+ #0F1417
+ #FFFFFF
+ #40484C
+ #F7FBFF
+ #C4CCD1
+ #C4CCD1
+ #000000
+ #DFE3E7
+ #000000
+ #002E3E
+ #C9ECFF
+ #000000
+ #90D4F4
+ #001923
+ #D4EAF7
+ #000000
+ #B8CEDA
+ #031922
+ #E8E4FF
+ #000000
+ #CBC6EF
+ #141231
+ #0F1417
+ #353A3D
+ #0A0F11
+ #171C1F
+ #1B2023
+ #262B2E
+ #303538
+
+ #83D2E4
+ #00363F
+ #004E5A
+ #A4EEFF
+ #B2CBD1
+ #1C3439
+ #334A50
+ #CDE7ED
+ #BDC5EB
+ #262F4D
+ #3D4565
+ #DCE1FF
+ #FFB4AB
+ #690005
+ #93000A
+ #FFDAD6
+ #0E1416
+ #DEE3E5
+ #0E1416
+ #DEE3E5
+ #3F484B
+ #BFC8CB
+ #899295
+ #3F484B
+ #000000
+ #DEE3E5
+ #2B3133
+ #006877
+ #A4EEFF
+ #001F25
+ #83D2E4
+ #004E5A
+ #CDE7ED
+ #051F24
+ #B2CBD1
+ #334A50
+ #DCE1FF
+ #111A37
+ #BDC5EB
+ #3D4565
+ #0E1416
+ #343A3C
+ #090F11
+ #171D1E
+ #1B2122
+ #252B2C
+ #303637
+ #87D7E8
+ #00191F
+ #4A9CAC
+ #000000
+ #B6CFD5
+ #01191E
+ #7C959B
+ #000000
+ #C1C9EF
+ #0B1531
+ #878FB3
+ #000000
+ #FFBAB1
+ #370001
+ #FF5449
+ #000000
+ #0E1416
+ #DEE3E5
+ #0E1416
+ #F6FCFD
+ #3F484B
+ #C3CCCF
+ #9BA4A7
+ #7B8587
+ #000000
+ #DEE3E5
+ #252B2C
+ #00505C
+ #A4EEFF
+ #001418
+ #83D2E4
+ #003C46
+ #CDE7ED
+ #001418
+ #B2CBD1
+ #223A3F
+ #DCE1FF
+ #060F2C
+ #BDC5EB
+ #2C3553
+ #0E1416
+ #343A3C
+ #090F11
+ #171D1E
+ #1B2122
+ #252B2C
+ #303637
+ #F3FCFF
+ #000000
+ #87D7E8
+ #000000
+ #F3FCFF
+ #000000
+ #B6CFD5
+ #000000
+ #FCFAFF
+ #000000
+ #C1C9EF
+ #000000
+ #FFF9F9
+ #000000
+ #FFBAB1
+ #000000
+ #0E1416
+ #DEE3E5
+ #0E1416
+ #FFFFFF
+ #3F484B
+ #F3FCFF
+ #C3CCCF
+ #C3CCCF
+ #000000
+ #DEE3E5
+ #000000
+ #002F37
+ #B4F1FF
+ #000000
+ #87D7E8
+ #00191F
+ #D2EBF2
+ #000000
+ #B6CFD5
+ #01191E
+ #E1E6FF
+ #000000
+ #C1C9EF
+ #0B1531
+ #0E1416
+ #343A3C
+ #090F11
+ #171D1E
+ #1B2122
+ #252B2C
+ #303637
+
+ #82D5C7
+ #003731
+ #005048
+ #9EF2E3
+ #B1CCC6
+ #1C3531
+ #334B47
+ #CCE8E2
+ #ADCAE5
+ #143349
+ #2D4960
+ #CCE5FF
+ #FFB4AB
+ #690005
+ #93000A
+ #FFDAD6
+ #0E1513
+ #DDE4E1
+ #0E1513
+ #DDE4E1
+ #3F4946
+ #BEC9C5
+ #899390
+ #3F4946
+ #000000
+ #DDE4E1
+ #2B3230
+ #006B5F
+ #9EF2E3
+ #00201C
+ #82D5C7
+ #005048
+ #CCE8E2
+ #06201C
+ #B1CCC6
+ #334B47
+ #CCE5FF
+ #001E31
+ #ADCAE5
+ #2D4960
+ #0E1513
+ #343B39
+ #090F0E
+ #161D1B
+ #1A211F
+ #252B2A
+ #303634
+ #87DACC
+ #001A17
+ #4A9E92
+ #000000
+ #B5D0CA
+ #011A17
+ #7C9690
+ #000000
+ #B1CEEA
+ #001829
+ #7794AE
+ #000000
+ #FFBAB1
+ #370001
+ #FF5449
+ #000000
+ #0E1513
+ #DDE4E1
+ #0E1513
+ #F6FCF9
+ #3F4946
+ #C3CDCA
+ #9BA5A2
+ #7B8582
+ #000000
+ #DDE4E1
+ #252B2A
+ #005249
+ #9EF2E3
+ #001511
+ #82D5C7
+ #003E37
+ #CCE8E2
+ #001511
+ #B1CCC6
+ #223B36
+ #CCE5FF
+ #001321
+ #ADCAE5
+ #1B394F
+ #0E1513
+ #343B39
+ #090F0E
+ #161D1B
+ #1A211F
+ #252B2A
+ #303634
+ #EBFFFA
+ #000000
+ #87DACC
+ #000000
+ #EBFFFA
+ #000000
+ #B5D0CA
+ #000000
+ #F9FBFF
+ #000000
+ #B1CEEA
+ #000000
+ #FFF9F9
+ #000000
+ #FFBAB1
+ #000000
+ #0E1513
+ #DDE4E1
+ #0E1513
+ #FFFFFF
+ #3F4946
+ #F3FDF9
+ #C3CDCA
+ #C3CDCA
+ #000000
+ #DDE4E1
+ #000000
+ #00302B
+ #A3F6E8
+ #000000
+ #87DACC
+ #001A17
+ #D1EDE6
+ #000000
+ #B5D0CA
+ #011A17
+ #D4E9FF
+ #000000
+ #B1CEEA
+ #001829
+ #0E1513
+ #343B39
+ #090F0E
+ #161D1B
+ #1A211F
+ #252B2A
+ #303634
+
+ #A2D399
+ #0C390E
+ #255023
+ #BDF0B3
+ #BACCB3
+ #253423
+ #3B4B38
+ #D6E8CE
+ #A0CFD4
+ #00363B
+ #1E4D52
+ #BCEBF0
+ #FFB4AB
+ #690005
+ #93000A
+ #FFDAD6
+ #10140F
+ #E0E4DA
+ #10140F
+ #E0E4DA
+ #424940
+ #C2C8BD
+ #8C9388
+ #424940
+ #000000
+ #E0E4DA
+ #2D322B
+ #3C6838
+ #BDF0B3
+ #002203
+ #A2D399
+ #255023
+ #D6E8CE
+ #111F0F
+ #BACCB3
+ #3B4B38
+ #BCEBF0
+ #002022
+ #A0CFD4
+ #1E4D52
+ #10140F
+ #363A34
+ #0B0F0A
+ #191D17
+ #1D211B
+ #272B25
+ #323630
+ #A6D89D
+ #001C02
+ #6E9C67
+ #000000
+ #BED0B7
+ #0C1A0A
+ #85957F
+ #000000
+ #A5D3D8
+ #001A1C
+ #6B989D
+ #000000
+ #FFBAB1
+ #370001
+ #FF5449
+ #000000
+ #10140F
+ #E0E4DA
+ #10140F
+ #F9FCF2
+ #424940
+ #C6CDC1
+ #9EA59A
+ #7F857A
+ #000000
+ #E0E4DA
+ #272B25
+ #265124
+ #BDF0B3
+ #001601
+ #A2D399
+ #133F14
+ #D6E8CE
+ #071406
+ #BACCB3
+ #2B3A28
+ #BCEBF0
+ #001416
+ #A0CFD4
+ #073C41
+ #10140F
+ #363A34
+ #0B0F0A
+ #191D17
+ #1D211B
+ #272B25
+ #323630
+ #F1FFE9
+ #000000
+ #A6D89D
+ #000000
+ #F1FFE9
+ #000000
+ #BED0B7
+ #000000
+ #EFFEFF
+ #000000
+ #A5D3D8
+ #000000
+ #FFF9F9
+ #000000
+ #FFBAB1
+ #000000
+ #10140F
+ #E0E4DA
+ #10140F
+ #FFFFFF
+ #424940
+ #F7FDF0
+ #C6CDC1
+ #C6CDC1
+ #000000
+ #E0E4DA
+ #000000
+ #043208
+ #C1F4B7
+ #000000
+ #A6D89D
+ #001C02
+ #DAECD2
+ #000000
+ #BED0B7
+ #0C1A0A
+ #C0EFF4
+ #000000
+ #A5D3D8
+ #001A1C
+ #10140F
+ #363A34
+ #0B0F0A
+ #191D17
+ #1D211B
+ #272B25
+ #323630
+
+ #B0D18B
+ #1E3702
+ #334E17
+ #CBEDA5
+ #BFCBAD
+ #2A331F
+ #404A34
+ #DBE7C8
+ #A0CFCC
+ #003735
+ #1F4E4C
+ #BBECE8
+ #FFB4AB
+ #690005
+ #93000A
+ #FFDAD6
+ #12140E
+ #E2E3D8
+ #12140E
+ #E2E3D8
+ #44483D
+ #C4C8BA
+ #8E9285
+ #44483D
+ #000000
+ #E2E3D8
+ #2F312A
+ #4A672D
+ #CBEDA5
+ #0E2000
+ #B0D18B
+ #334E17
+ #DBE7C8
+ #151E0B
+ #BFCBAD
+ #404A34
+ #BBECE8
+ #00201F
+ #A0CFCC
+ #1F4E4C
+ #12140E
+ #373A33
+ #0C0F09
+ #1A1C16
+ #1E211A
+ #282B24
+ #33362E
+ #B4D58F
+ #0B1A00
+ #7B9A5A
+ #000000
+ #C3CFB1
+ #101907
+ #89957A
+ #000000
+ #A4D4D0
+ #001A19
+ #6B9996
+ #000000
+ #FFBAB1
+ #370001
+ #FF5449
+ #000000
+ #12140E
+ #E2E3D8
+ #12140E
+ #FAFCF0
+ #44483D
+ #C9CCBE
+ #A1A497
+ #818578
+ #000000
+ #E2E3D8
+ #282B24
+ #344F18
+ #CBEDA5
+ #071400
+ #B0D18B
+ #233D07
+ #DBE7C8
+ #0B1404
+ #BFCBAD
+ #2F3924
+ #BBECE8
+ #001414
+ #A0CFCC
+ #083D3B
+ #12140E
+ #373A33
+ #0C0F09
+ #1A1C16
+ #1E211A
+ #282B24
+ #33362E
+ #F4FFE1
+ #000000
+ #B4D58F
+ #000000
+ #F4FFE1
+ #000000
+ #C3CFB1
+ #000000
+ #EAFFFD
+ #000000
+ #A4D4D0
+ #000000
+ #FFF9F9
+ #000000
+ #FFBAB1
+ #000000
+ #12140E
+ #E2E3D8
+ #12140E
+ #FFFFFF
+ #44483D
+ #F9FCED
+ #C9CCBE
+ #C9CCBE
+ #000000
+ #E2E3D8
+ #000000
+ #183000
+ #D0F2A9
+ #000000
+ #B4D58F
+ #0B1A00
+ #DFEBCC
+ #000000
+ #C3CFB1
+ #101907
+ #C0F0ED
+ #000000
+ #A4D4D0
+ #001A19
+ #12140E
+ #373A33
+ #0C0F09
+ #1A1C16
+ #1E211A
+ #282B24
+ #33362E
+
+ #C3CD7B
+ #2E3400
+ #434B05
+ #E0E995
+ #C7C9A7
+ #2F321A
+ #46492F
+ #E3E5C1
+ #A2D0C1
+ #06372D
+ #224E43
+ #BEECDD
+ #FFB4AB
+ #690005
+ #93000A
+ #FFDAD6
+ #13140D
+ #E5E3D6
+ #13140D
+ #E5E3D6
+ #47483B
+ #C8C7B7
+ #929283
+ #47483B
+ #000000
+ #E5E3D6
+ #313128
+ #5B631E
+ #E0E995
+ #1A1E00
+ #C3CD7B
+ #434B05
+ #E3E5C1
+ #1B1D07
+ #C7C9A7
+ #46492F
+ #BEECDD
+ #002019
+ #A2D0C1
+ #224E43
+ #13140D
+ #393A31
+ #0E0F08
+ #1B1C14
+ #202018
+ #2A2B22
+ #35352D
+ #C8D17F
+ #151800
+ #8D964B
+ #000000
+ #CBCDAB
+ #151804
+ #909374
+ #000000
+ #A6D4C6
+ #001B14
+ #6D9A8C
+ #000000
+ #FFBAB1
+ #370001
+ #FF5449
+ #000000
+ #13140D
+ #E5E3D6
+ #13140D
+ #FDFBEE
+ #47483B
+ #CCCCBB
+ #A4A494
+ #848475
+ #000000
+ #E5E3D6
+ #2A2B22
+ #454C06
+ #E0E995
+ #101300
+ #C3CD7B
+ #333A00
+ #E3E5C1
+ #101301
+ #C7C9A7
+ #35381F
+ #BEECDD
+ #00150F
+ #A2D0C1
+ #0F3D33
+ #13140D
+ #393A31
+ #0E0F08
+ #1B1C14
+ #202018
+ #2A2B22
+ #35352D
+ #FAFFC9
+ #000000
+ #C8D17F
+ #000000
+ #FBFDD9
+ #000000
+ #CBCDAB
+ #000000
+ #ECFFF7
+ #000000
+ #A6D4C6
+ #000000
+ #FFF9F9
+ #000000
+ #FFBAB1
+ #000000
+ #13140D
+ #E5E3D6
+ #13140D
+ #FFFFFF
+ #47483B
+ #FDFCEA
+ #CCCCBB
+ #CCCCBB
+ #000000
+ #E5E3D6
+ #000000
+ #282D00
+ #E4EE98
+ #000000
+ #C8D17F
+ #151800
+ #E7E9C6
+ #000000
+ #CBCDAB
+ #151804
+ #C2F1E1
+ #000000
+ #A6D4C6
+ #001B14
+ #13140D
+ #393A31
+ #0E0F08
+ #1B1C14
+ #202018
+ #2A2B22
+ #35352D
+
+ #F0BE6D
+ #432C00
+ #604100
+ #FFDEAC
+ #DBC3A1
+ #3D2E16
+ #55442A
+ #F8DFBB
+ #B5CEA4
+ #213618
+ #374C2C
+ #D1EABF
+ #FFB4AB
+ #690005
+ #93000A
+ #FFDAD6
+ #17130B
+ #ECE1D4
+ #17130B
+ #ECE1D4
+ #4E4539
+ #D2C4B4
+ #9B8F80
+ #4E4539
+ #000000
+ #ECE1D4
+ #362F27
+ #7D570D
+ #FFDEAC
+ #281900
+ #F0BE6D
+ #604100
+ #F8DFBB
+ #261904
+ #DBC3A1
+ #55442A
+ #D1EABF
+ #0D2005
+ #B5CEA4
+ #374C2C
+ #17130B
+ #3F382F
+ #120D07
+ #201B13
+ #241F17
+ #2F2921
+ #3A342B
+ #F5C371
+ #211400
+ #B5893E
+ #000000
+ #E0C7A5
+ #201402
+ #A38E6E
+ #000000
+ #B9D2A8
+ #081A02
+ #809871
+ #000000
+ #FFBAB1
+ #370001
+ #FF5449
+ #000000
+ #17130B
+ #ECE1D4
+ #17130B
+ #FFFAF7
+ #4E4539
+ #D6C9B8
+ #ADA191
+ #8D8173
+ #000000
+ #ECE1D4
+ #2F2921
+ #614200
+ #FFDEAC
+ #1A0F00
+ #F0BE6D
+ #4A3100
+ #F8DFBB
+ #1A0F00
+ #DBC3A1
+ #43341B
+ #D1EABF
+ #041500
+ #B5CEA4
+ #273B1D
+ #17130B
+ #3F382F
+ #120D07
+ #201B13
+ #241F17
+ #2F2921
+ #3A342B
+ #FFFAF7
+ #000000
+ #F5C371
+ #000000
+ #FFFAF7
+ #000000
+ #E0C7A5
+ #000000
+ #F2FFE5
+ #000000
+ #B9D2A8
+ #000000
+ #FFF9F9
+ #000000
+ #FFBAB1
+ #000000
+ #17130B
+ #ECE1D4
+ #17130B
+ #FFFFFF
+ #4E4539
+ #FFFAF7
+ #D6C9B8
+ #D6C9B8
+ #000000
+ #ECE1D4
+ #000000
+ #3A2600
+ #FFE3BB
+ #000000
+ #F5C371
+ #211400
+ #FDE3BF
+ #000000
+ #E0C7A5
+ #201402
+ #D5EFC3
+ #000000
+ #B9D2A8
+ #081A02
+ #17130B
+ #3F382F
+ #120D07
+ #201B13
+ #241F17
+ #2F2921
+ #3A342B
+
+ #FFB77B
+ #4C2700
+ #6B3A05
+ #FFDCC2
+ #E3C0A5
+ #412C19
+ #5A422E
+ #FFDCC2
+ #C4CB97
+ #2D330D
+ #444A22
+ #E0E7B1
+ #FFB4AB
+ #690005
+ #93000A
+ #FFDAD6
+ #19120C
+ #EFE0D6
+ #19120C
+ #EFE0D6
+ #51443B
+ #D6C3B6
+ #9E8E82
+ #51443B
+ #000000
+ #EFE0D6
+ #382F28
+ #88511D
+ #FFDCC2
+ #2E1500
+ #FFB77B
+ #6B3A05
+ #FFDCC2
+ #2A1707
+ #E3C0A5
+ #5A422E
+ #E0E7B1
+ #191E00
+ #C4CB97
+ #444A22
+ #19120C
+ #413731
+ #140D08
+ #221A14
+ #261E18
+ #312822
+ #3C332C
+ #FFBD87
+ #271100
+ #C3824A
+ #000000
+ #E7C4A9
+ #241203
+ #AA8B73
+ #000000
+ #C8CF9B
+ #141800
+ #8E9565
+ #000000
+ #FFBAB1
+ #370001
+ #FF5449
+ #000000
+ #19120C
+ #EFE0D6
+ #19120C
+ #FFFAF8
+ #51443B
+ #DAC7BA
+ #B1A094
+ #908075
+ #000000
+ #EFE0D6
+ #312822
+ #6D3C06
+ #FFDCC2
+ #1F0C00
+ #FFB77B
+ #552C00
+ #FFDCC2
+ #1E0D01
+ #E3C0A5
+ #48311F
+ #E0E7B1
+ #0F1300
+ #C4CB97
+ #333913
+ #19120C
+ #413731
+ #140D08
+ #221A14
+ #261E18
+ #312822
+ #3C332C
+ #FFFAF8
+ #000000
+ #FFBD87
+ #000000
+ #FFFAF8
+ #000000
+ #E7C4A9
+ #000000
+ #F9FFCD
+ #000000
+ #C8CF9B
+ #000000
+ #FFF9F9
+ #000000
+ #FFBAB1
+ #000000
+ #19120C
+ #EFE0D6
+ #19120C
+ #FFFFFF
+ #51443B
+ #FFFAF8
+ #DAC7BA
+ #DAC7BA
+ #000000
+ #EFE0D6
+ #000000
+ #432100
+ #FFE1CC
+ #000000
+ #FFBD87
+ #271100
+ #FFE1CC
+ #000000
+ #E7C4A9
+ #241203
+ #E4ECB5
+ #000000
+ #C8CF9B
+ #141800
+ #19120C
+ #413731
+ #140D08
+ #221A14
+ #261E18
+ #312822
+ #3C332C
+
+ #E7BDB0
+ #442A21
+ #6D4E43
+ #FFF7F5
+ #D8C2BB
+ #3B2D29
+ #483A35
+ #E2CCC5
+ #CFC7A1
+ #353116
+ #5B5537
+ #FFF8E3
+ #FFB4AB
+ #690005
+ #93000A
+ #FFDAD6
+ #161312
+ #E9E1DF
+ #161312
+ #E9E1DF
+ #504440
+ #D4C3BE
+ #9D8E89
+ #504440
+ #000000
+ #E9E1DF
+ #33302E
+ #77574C
+ #FFDBCF
+ #2C160D
+ #E7BDB0
+ #5D4036
+ #F5DED6
+ #251915
+ #D8C2BB
+ #53433E
+ #ECE3BB
+ #201C04
+ #CFC7A1
+ #4C472A
+ #161312
+ #3C3837
+ #100E0D
+ #1E1B1A
+ #221F1E
+ #2D2928
+ #383433
+ #EBC2B4
+ #261009
+ #AD887C
+ #000000
+ #DCC6BF
+ #1F1410
+ #A08D86
+ #000000
+ #D4CBA5
+ #1A1602
+ #98916E
+ #000000
+ #FFBAB1
+ #370001
+ #FF5449
+ #000000
+ #161312
+ #E9E1DF
+ #161312
+ #FFF9F8
+ #504440
+ #D8C7C2
+ #AFA09B
+ #8E807C
+ #000000
+ #E9E1DF
+ #2D2928
+ #5E4137
+ #FFDBCF
+ #200B05
+ #E7BDB0
+ #4A3026
+ #F5DED6
+ #190F0B
+ #D8C2BB
+ #41332E
+ #ECE3BB
+ #151100
+ #CFC7A1
+ #3B361B
+ #161312
+ #3C3837
+ #100E0D
+ #1E1B1A
+ #221F1E
+ #2D2928
+ #383433
+ #FFF9F8
+ #000000
+ #EBC2B4
+ #000000
+ #FFF9F8
+ #000000
+ #DCC6BF
+ #000000
+ #FFFAF4
+ #000000
+ #D4CBA5
+ #000000
+ #FFF9F9
+ #000000
+ #FFBAB1
+ #000000
+ #161312
+ #E9E1DF
+ #161312
+ #FFFFFF
+ #504440
+ #FFF9F8
+ #D8C7C2
+ #D8C7C2
+ #000000
+ #E9E1DF
+ #000000
+ #3D241B
+ #FFE0D7
+ #000000
+ #EBC2B4
+ #261009
+ #F9E2DB
+ #000000
+ #DCC6BF
+ #1F1410
+ #F1E7C0
+ #000000
+ #D4CBA5
+ #1A1602
+ #161312
+ #3C3837
+ #100E0D
+ #1E1B1A
+ #221F1E
+ #2D2928
+ #383433
+
+ #C7C6C6
+ #303031
+ #6C6C6C
+ #FFFFFF
+ #C8C6C5
+ #303030
+ #3E3D3D
+ #D3D0D0
+ #CBC5C7
+ #323031
+ #6F6B6D
+ #FFFFFF
+ #FFB4AB
+ #690005
+ #93000A
+ #FFDAD6
+ #141313
+ #E5E2E1
+ #141313
+ #E5E2E1
+ #444748
+ #C4C7C7
+ #8E9192
+ #444748
+ #000000
+ #E5E2E1
+ #313030
+ #5E5E5E
+ #E3E2E2
+ #1B1C1C
+ #C7C6C6
+ #464747
+ #E5E2E1
+ #1B1C1C
+ #C8C6C5
+ #474746
+ #E7E1E3
+ #1D1B1D
+ #CBC5C7
+ #494648
+ #141313
+ #3A3939
+ #0E0E0E
+ #1C1B1B
+ #201F1F
+ #2A2A2A
+ #353434
+ #CBCACA
+ #151617
+ #919090
+ #000000
+ #CCCACA
+ #161616
+ #929090
+ #000000
+ #CFC9CB
+ #181617
+ #948F91
+ #000000
+ #FFBAB1
+ #370001
+ #FF5449
+ #000000
+ #141313
+ #E5E2E1
+ #141313
+ #FEFAF9
+ #444748
+ #C8CBCC
+ #A0A3A4
+ #808484
+ #000000
+ #E5E2E1
+ #2B2A2A
+ #474848
+ #E3E2E2
+ #101111
+ #C7C6C6
+ #353636
+ #E5E2E1
+ #111111
+ #C8C6C5
+ #363636
+ #E7E1E3
+ #121112
+ #CBC5C7
+ #383537
+ #141313
+ #3A3939
+ #0E0E0E
+ #1C1B1B
+ #201F1F
+ #2A2A2A
+ #353434
+ #FCFAFA
+ #000000
+ #CBCACA
+ #000000
+ #FDFAF9
+ #000000
+ #CCCACA
+ #000000
+ #FFF9FA
+ #000000
+ #CFC9CB
+ #000000
+ #FFF9F9
+ #000000
+ #FFBAB1
+ #000000
+ #141313
+ #E5E2E1
+ #141313
+ #FFFFFF
+ #444748
+ #F9FBFC
+ #C8CBCC
+ #C8CBCC
+ #000000
+ #E5E2E1
+ #000000
+ #292A2A
+ #E8E6E6
+ #000000
+ #CBCACA
+ #151617
+ #E9E6E6
+ #000000
+ #CCCACA
+ #161616
+ #EBE5E7
+ #000000
+ #CFC9CB
+ #181617
+ #141313
+ #3A3939
+ #0E0E0E
+ #1C1B1B
+ #201F1F
+ #2A2A2A
+ #353434
+
\ No newline at end of file
diff --git a/app/src/main/res/values-night/styles_ui.xml b/app/src/main/res/values-night/styles_ui.xml
new file mode 100644
index 000000000..83b8900f6
--- /dev/null
+++ b/app/src/main/res/values-night/styles_ui.xml
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index 7f3ea75d0..6d668957c 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -3,79 +3,76 @@
Een eenvoudige, rationele muziekspeler voor Android.
- Opnieuw proberen
- Toestaan
+ Probeer opnieuw
+ Sta toe
Genres
Artiesten
Albums
Nummers
Alle nummers
- Zoeken
+ Zoek
Filter
Alles
- Sorteren
+ Sorteer
Oplopend
- Afspelen
+ Speel af
Shuffle
Speel van alle nummers
- Speel af van album
+ Speel vanaf album
Speel van artiest
Nu afspelen
Wachtrij
- Afspelen als volgende
- Toevoegen aan wachtrij
- Toegevoegd aan de wachtrij
+ Speel volgende
+ Voeg toe aan wachtrij
+ Toegevoegd aan wachtrij
Ga naar artiest
Ga naar album
- Staat gered
- Toevoegen
- Opslaan
- Geen mappen
+ Voeg toe
+ Bewaar
+ Geen mappen
Over
Versie
Broncode
Licenties
- Ontwikkeld door Alexander Capehart
+ Alexander Capehart
Instellingen
- Uiterlijk en gevoel
+ Uiterlijk
Thema
Automatisch
Licht
Donker
- Accent
+ Kleuren schema
Scherm
- Gebruikt een afternatief notification action
+ Aangepaste meldingsactie
Audio
- Gedrag
+ Personaliseer
Bij het afspelen vanuit de bibliotheek
Onthoud shuffle
Houd shuffle aan bij het afspelen van een nieuw nummer
- Terugspoelen voordat je terugspoelt
- Terugspoelen voordat u naar het vorige nummer gaat
+ Terugspoelen voor het overslaan
+ Terugspoelen voor het overslaan van het vorige nummer
Inhoud
- Afspeelstatus opslaan
- Sla de huidige afspeelstatus nu op
Geen muziek aangetroffen
Laden van muziek mislukt
Auxio heeft toestemming nodig om uw muziekbibliotheek te lezen
Geen app gevonden die deze taak kan uitvoeren
- Deze map wordt niet ondersteund
+ Deze map wordt niet ondersteund
Zoek in uw bibliotheek…
Nummer %d
Afspelen of pauzeren
- Naar volgend nummer gaan
- Naar het laatste nummer gaan
- Herhaalfunctie wijzigen
- Zoekopdracht wissen
- Map verwijderen
- Auxio pictogram
+ Ga naar volgend nummer
+ Ga naar laatste nummer
+ Wijzig herhaalmodus
+ Wis zoekopdracht
+ Verwijder map
+ Auxio icoon
Albumhoes voor %s
- Artiesten-afbeelding voor %s
- Genre-afbeelding voor %s
+ Artiest afbeelding voor %s
+ Genre afbeelding voor %s
Onbekend genre
Geen datum
@@ -83,24 +80,24 @@
Rood
Roze
Paars
- Dieppaars
+ Diep paars
Indigoblauw
Blauw
- Diepblauw
+ Diep blauw
Blauwgroen
Groen
- Diepgroen
+ Diep groen
Cyaan
Geelgroen
Geel
Oranje
Bruin
- Grijis
+ Grijs
Nummers geladen: %d
- - %d lied
- - %d liedjes
+ - %d nummer
+ - %d nummers
- %d album
@@ -108,95 +105,84 @@
Onbekende artiest
Zwart thema
- Gebruik een puur-zwart donker thema
+ Gebruik een puur zwart thema
Pauze op herhaling
Schijf %d
%d kbps
+%.1f dB
- Muziekweergave bekijken en regelen
+ Bekijk en regel muziekweergave
%d Hz
Bekijk eigenschappen
Naam
Artiest
- Annuleren
+ Annuleer
Bibliotheek tabbladen
- Jaar
- Lied eigenschappen
+ Datum
+ Nummer eigenschappen
Voorkeur album als er een speelt
- Voorkeur titel
+ Voorkeur track
Voorkeur album
De voorversterker wordt toegepast op de bestaande afstelling tijdens weergave
- Muziek mappen
+ Muziek mappen
ReplayGain voorversterker
- Modus
- Bepaal waar muziek vandaan moet worden geladen
+ Beheer waarvan muziek moet worden geladen
Totale duur: %s
- Alles schudden
- Oké
- Altijd beginnen met spelen als een headset is aangesloten (werkt mogelijk niet op alle apparaten)
+ Shuffle alles
+ OK
+ Start altijd met afspelen wanneer een headset is aangesloten (werkt mogelijk niet op alle apparaten)
Schakel shuffle aan of uit
- De muziekbibliotheek opnieuw laden, indien mogelijk met behulp van tags uit het cachegeheugen
+ Laad de muziekbibliotheek opnieuw, indien mogelijk met behulp van gecashte tags
Uw muziekbibliotheek wordt geladen… (%1$d/%2$d)
- Uitgezonderd
- Alle liedjes shuffelen
- Includeer
- Pauze wanneer een liedje wordt herhaald
- Muziek zal niet worden geladen vanuit de mappen die u toevoegt.
- Muziek verfrissen
- Muziek zal alleen worden geladen uit de mappen die u toevoegt.
+ Shuffle alle nummers
+ Pauze wanneer een nummer wordt herhaald
+ Muziek opnieuw laden
Aanpassing met tags
Aanpassing zonder tags
- Er speelt geen muziek
+ Geen muziek wordt afgespeeld
Bij het afspelen van item details
Ronde modus
- Afgeronde hoeken inschakelen voor extra UI-elementen (vereist dat albumhoezen zijn afgerond)
- Staat gerestaureerd
- Bibliotheekstatistieken
- Verander de zichtbaarheid en volgorde van bibliotheek-tabbladen
+ Schakel afgeronde hoeken in voor extra UI-elementen (vereist dat albumhoezen zijn afgerond)
+ Bibliotheek statistieken
+ Verander de zichtbaarheid en volgorde van bibliotheek tabbladen
Headset automatisch afspelen
- ReplayGain
+ ReplayGain strategie
Waarschuwing: Als u de voorversterker op een hoge positieve waarde zet, kan dit bij sommige audiotracks tot pieken leiden.
- Afspelen vanaf getoond item
- Afspeelstatus herstellen
- Herstel de eerder opgeslagen afspeelstatus (indien aanwezig)
- Kan status niet herstellen
- Verwijder dit wachtrij liedje
- Verplaats dit wachtrij liedje
- Verplaats deze tab
- Album cover
+ Speel vanaf getoond item
+ Verwijder dit nummer
+ Verplaats dit nummer
+ Verplaats dit tabblad
+ Albumhoes
Geen nummer
-%.1f dB
Dynamisch
MPEG-1 audio
- MPEG-4-audio
+ MPEG-4 audio
Ogg audio
- Matroska-audio
+ Matroska audio
Albums geladen: %d
Artiesten geladen: %d
Genres geladen: %d
- Muziek aan het laden
+ Muziek laden
Album
- Looptijd
- Aantal Liedjes
+ Duur
+ Aantal nummers
Disc
Titel
Formaat
Grootte
- Bitsnelheid
- Bemonsteringsfrequentie
+ Bit rate
+ Sample rate
Shuffle
- Geavanceerde audio codering (GAC)
- Gratis verliesvrije audiocodec (GVAC)
+ Geavanceerde audio codering (AAC)
+ Free Lossless Audio Codec (FLAC)
Nieuwe afspeellijst
- Afspeelstatus gewist
- Geluids- en afspeelgedrag configureren
- Een nieuwe afspeellijst maken
- %d Geselecteerd
- Toevoegen aan afspeellijst
+ Configureer geluid en afspeel gedrag
+ %d geselecteerd
+ Voeg toe aan afspeellijst
Afspeellijst gemaakt
Toegevoegd aan afspeellijst
- Het thema en de kleuren van de app wijzigen
- UI-besturingselementen en gedrag aanpassen
+ Wijzig het thema en de kleuren van de app
+ Pas UI elementen en gedrag aan
Bepaal hoe muziek en afbeeldingen worden geladen
Muziek
Automatisch herladen
@@ -206,8 +192,8 @@
Waarschuwing: Het gebruik van deze instelling kan ertoe leiden dat sommige tags verkeerd geïnterpreteerd worden als tags met meerdere waarden. U kunt dit oplossen door ongewenste scheidingstekens vooraf te laten gaan door een backslash (\\).
Toon alleen artiesten die rechtstreeks op een album worden genoemd (werkt het beste op goed getagde bibliotheken)
Sorteer namen die beginnen met cijfers of woorden zoals \"de\" correct (werkt het beste met Engelstalige muziek)
- Stop met afspelen
- Uw muziekbibliotheek wordt geladen…
+ Stop afspelen
+ Uw muziekbibliotheek laden…
Gedrag
Remix compilatie
Soundtrack
@@ -215,15 +201,12 @@
DJ-mix
Remixen
Schuine streep (/)
- WeergaveWinst
- Volharding
+ Volume normalisatie
Afspeellijst
Wiki
- Kan status niet opslaan
- Resetten
+ Reset
Afbeeldingen
- Afspeelstatus wissen
- Snel
+ Snel
Bibliotheek
Live EP
Remix EP
@@ -232,22 +215,21 @@
Compilaties
Compilatie
Live
- Afspeellijst hernoemen
- Afspeellijst verwijderen\?
+ Hernoem afspeellijst
+ Verwijder afspeellijst?
Herhaalmodus
- Mappen
- De wachtrij openen
+ Mappen
+ Open de wachtrij
%s verwijderen\? Dit kan niet ongedaan worden gemaakt.
Uit
- Hoge kwaliteit
+ Hoge kwaliteit
Genre
Ampersand (&)
- Bewerken
+ Bewerk
Aflopend
- Kan status niet wissen
- Afspeellijst-afbeelding voor %s
+ Afspeellijst afbeelding voor %s
Geen nummers
- Gelijkmaker
+ Equalizer
Singles
Single
EP\'s
@@ -262,11 +244,11 @@
DJ-mixen
Uw muziekbibliotheek controleren op wijzigingen…
Afspeellijst hernoemd
- Hernoemen
- Aangepaste afspeelbalkactie
- Tekens configureren die meerdere tagwaarden aanduiden
- Verwijderen
- Scheiders met meerdere waarden
+ Hernoem
+ Aangepaste afspeelbalk actie
+ Configureer tekens die meerdere tag waarden aanduiden
+ Verwijder
+ Meerdere waarden scheidingstekens
Verberg bijdragers
Speel vanuit genre
Datum toegevoegd
@@ -279,17 +261,56 @@
Intelligent sorteren
Verschijnt op
Afspeellijsten
- Delen
+ Deel
Afspeellijst verwijderd
Naar volgende
Laad de muziekbibliotheek opnieuw wanneer deze wordt gewijzigd (vereist permanente melding)
- Niet-muziek uitsluiten
+ Sluit niet-muziek uit
Negeer audiobestanden die geen muziek zijn, zoals podcasts
Albumhoezen
- Afspeel
- Muziek opnieuw scannen
- De tag-cache wissen en de muziekbibliotheek volledig opnieuw laden (langzamer, maar vollediger)
- De eerder opgeslagen afspeelstatus wissen (indien aanwezig)
+ Afspelen
+ Scan muziek opnieuw
+ Wis de tag-cache en laad de muziekbibliotheek opnieuw (langzamer, maar vollediger)
Geen schijf
- %s aan het bewerken
+ %s bewerken
+ Uit
+ Kan de afspeellijst niet naar dit bestand exporteren
+ Geen albums
+ Nummer
+ Geïmporteerde afspeellijst
+ Afbeelding selectie
+ Demo
+ Demo\'s
+ Sorteer op
+ Meer
+ Richting
+ ReplayGain spoor aanpassing
+ ReplayGain album aanpassing
+ Auteur
+ Doneer
+ Supporters
+ Doneer aan het project om uw naam hier te krijgen!
+ Forceer vierkante albumhoezen
+ Snijd alle albumhoezen bij tot een verhouding van 1:1
+ Fout informatie
+ Gekopieerd
+ Bekijk
+ Selectie
+ Onthoud pauze
+ Blijf afspelen/pauzeren tijdens het overslaan of bewerken van wachtrijen
+ Lege afspeellijst
+ Importeer afspeellijst
+ Importeer
+ Absoluut
+ Kan geen afspeellijst uit dit bestand importeren
+ Relatief
+ Afspeellijst geïmporteerd
+ Speel nummer zelf af
+ Exporteer afspeellijst
+ Exporteer
+ Pad
+ Pad stijl
+ Gebruik Windows-compatibele paden
+ Rapporteer
+ Afspeellijst geëxporteerd
\ No newline at end of file
diff --git a/app/src/main/res/values-nn/strings.xml b/app/src/main/res/values-nn/strings.xml
new file mode 100644
index 000000000..108229ec8
--- /dev/null
+++ b/app/src/main/res/values-nn/strings.xml
@@ -0,0 +1,314 @@
+
+
+ Spor
+ Sorter
+ Stigande
+ Legg til i speleliste
+ Retning
+ Legg til i kø
+ Bland
+ Syn album
+ Del
+ Bane
+ Lisensar
+ Kopiert
+ Bland alle
+ OK
+ Begynn avspeling
+ Legg til
+ Absolutt
+ Lagre
+ Kjeldekode
+ Wiki
+ Sporeigenskapar
+ Ein enkel, rasjonell musikkspillar for Android.
+ Musikk vert lasta inn
+ Held auge med musikkbiblioteket
+ Løyv
+ Spor
+ Spor
+ Alle spor
+ Album
+ Album
+ Remiks-album
+ EP
+ Live-EP
+ Remiks-EP
+ Singlar
+ Singel
+ Samanstilling
+ Live-samanstilling
+ Remiks-samanstilling
+ Ljodspor
+ Miksteipar
+ Miksteip
+ DJ-miksar
+ DJ-miks
+ Live
+ Remiksar
+ Opptrer på
+ Artist
+ Artistar
+ Sjanger
+ Sjangrar
+ Ny speleliste
+ Tom speleliste
+ Importert speleliste
+ Importer
+ Eksporter speleliste
+ Slett
+ Slett speleliste?
+ Rediger
+ Lengd
+ Sporantal
+ Platenummer
+ Dato tillagt
+ Spelar no
+ Tonekontroll
+ Spel
+ Kø
+ Spel neste
+ Syn artist
+ Syn eigenskapar
+ Syn
+ Bitrate
+ Samplingsfrekvens
+ ReplayGain-sporjustering
+ ReplayGain-albumjustering
+ Bland
+ Avbryt
+ Tilbakestill
+ Meir
+ Banestil
+ Relativt
+ Bruk Windows-kompatible baner
+ Om
+ Utgåve
+ Bibliotekstatistikk
+ Rapporter
+ Utvikla av Alexander Capehart
+ Lastar inn musikk
+ Prøv igjen
+ Syn meir
+ Live-singel
+ Ljodspor
+ Live-album
+ EP
+ Remiks-singel
+ Samanstillingar
+ Demoar
+ Demo
+ Format
+ Storleik
+ Spelelister
+ Bytt namn
+ Importer speleliste
+ Eksporter
+ Søk
+ Namn
+ Dato
+ Speleliste
+ Bytt namn på speleliste
+ Filtrer
+ Alle
+ Sorter etter
+ Fallande
+ Utviklar
+ Utval
+ Feilopplysningar
+ La til i speleliste
+ Albumomslag for %s
+ Opprett ei sak på GitHub
+ Send ein e-post
+ Donér
+ Støttespelarar
+ Syn og kontroller musikkavspeling
+ Held auge med endringar i musikkbiblioteket ditt…
+ La til i køen
+ Speleliste importert
+ Namn på speleliste endra
+ Speleliste eksportert
+ Spelelista vart sletta
+ Donér til prosjektet, så skal namnet ditt leggjast til her!
+ Søk igjennom biblioteket ditt…
+ Utsjånad
+ Drakt
+ Automatisk
+ Fargedrakt
+ Svart drakt
+ Bruk ein heilsvart mørk drakt
+ Avrunda hjørne
+ Slå på avrunda hjørne på fleire grensesnittselement (krev at albumomslag er òg avrunda)
+ Skjerm
+ Bibliotekfaner
+ Hopp til neste
+ Gjentakingsmodus
+ Åtferd
+ Ved avspeling frå biblioteket
+ Spel frå album
+ Spel frå sjanger
+ Spel sporet for seg sjølv
+ Hugs blandingsmodus
+ Innhald
+ Musikk
+ Automatisk gjeninnlasting
+ Utelat ikkje-musikk
+ Fleirverdi-inndelarar
+ Still inn teikn for inndeling av fleire verdiar på etiketten
+ Komma (,)
+ Intelligent sortering
+ Sorterer namn som byrjar med siffer eller ord som «the» korrekt (fungerer best med engelskspråkig musikk)
+ Døl medverkande
+ Syn kun artistar som er direkte kreditert på albumet (fungerer best med velmerka bibliotek)
+ Bilete
+ Albumomslag
+ Av
+ Høg kvalitet
+ Raskt
+ Påtving firkanta albumomslag
+ Beskjer alle albumomslag til 1:1-sideforhold
+ Still inn ljod- og avspelingsåtferd
+ Avspeling
+ Automatisk avspeling med headsett
+ Spol att før spor vert hoppa over
+ Spol att før Auxio hoppar til forrige spor
+ Pause ved gjentaking
+ Hugs pausemodus
+ Vert spelande/pausa ved sporbytting eller køredigering
+ Volumnormalisering
+ ReplayGain-strategi
+ Av
+ Føretrekk spor
+ Føretrekk album
+ Føretrekk album om eit album vert avspelt
+ ReplayGain-forforsterkning
+ Forforsterkning vert bruka på justeringa allereie teken i bruk under avspeling
+ Justering med etikettar
+ Justering utan etikettar
+ Bibliotek
+ Handsam kvar musikk skal lastast inn ifrå
+ Mapper
+ Gjenoppfrisk musikk
+ Kan ikkje importere ei speleliste frå denne fila
+ Hopp til neste spor
+ Endre gjentakingsmodus
+ Slå på/av blanding
+ Opne køen
+ Albumomslag
+ Spelelistebilete for %s
+ Inkje spor
+ Ingen spor
+ Ingen album
+ Fri tapsfri ljodkodek (FLAC)
+ Raud
+ Rosa
+ Lilla
+ Mørklilla
+ Mørkblå
+ Cyanblå
+ %1$s, %2$s
+ Plate %d
+ Speleliste %d
+ Lastar inn musikkbiblioteket ditt… (%1$d/%2$d)
+ Slett %s for godt? Dette kan ikkje angrast.
+ Innlasta spor: %d
+ Innlasta sjangrar: %d
+
+ - %d spor
+ - %d spor
+
+
+ - %d album
+ - %d album
+
+
+ - %d artist
+ - %d artistar
+
+ Gje tilbakemelding
+ Lastar inn musikkbiblioteket ditt…
+ Speleliste oppretta
+ Begynner Auxio med den forrige lagra tilstanden. Dersom ingen lagra tilstand er tilgjengeleg, skal alle spor blandast. Avspeling begynner med éin gong.\n\nÅTVARING: Ver varsom med å kontrollere denne tenesten, viss du lukkar han og så prøver å bruke han igjen, skal det nok få appen til å krasje.
+ Innstillingar
+ Endre drakten og programfargane
+ Ljos
+ Mørk
+ Tilpass
+ Tilpass grensesnittskontrollar og åtferd
+ Eigendefinert avspelingsfelthandling
+ Eigendefinert merknadshandling
+ Endre synlegdom og rekkjefylgje på bibliotekfaner
+ Spel frå synt element
+ Ved avspeling frå elementsdetaljar
+ Spel frå alle spor
+ Spel frå artist
+ Kontroller korleis musikk og bilete vert innlasta
+ Last inn musikkbiblioteket på nytt når det vert endra (krev vedvarande merknad)
+ Behald blanding når eit nytt spor vert spelt
+ Ignorer ljodfiler som ikkje er musikk, som t.d. podkastar
+ Semikolon (;)
+ Skråstrek (/)
+ Plussteikn (+)
+ Ampersand (&)
+ Åtvaring: Viss denne innstillinga er påslått, kan enkelte etikettar verte feiltolka som om dei har fleire verdiar. Dette kan løysast ved å leggje ein omvend skråstrek (\\) bak skiljeteiknet som ikkje skal skilja verdiar.
+ Ljod
+ Alltid byrj avspeling når hovudtelefonar vert tilkopla (verkar kanskje ikkje på alle einingar)
+ Pause når eit spor vert gjenteke
+ Åtvaring: Å endre forforsterkninga til høge positive verdier, kan forårsake forvrengning ved høge ljodtrykk på nokre spor.
+ Musikkmapper
+ Last inn musikkbiblioteket på nytt og bruk hurtiglagra etikettar når mogleg
+ Fann ikkje musikk
+ Skann musikk på nytt
+ Tøm etiketthurtiglager og last inn heile musikkbiblioteket på nytt (tregare, men meir fullstendig)
+ Auxio treng løyve til å lesa musikkbiblioteket ditt
+ Artistbilete for %s
+ Klarte ikkje å laste inn musikk
+ Kan ikkje eksportere spelelista til denne fila
+ Denne mappa er ikkje støtta
+ Ingen app funne som kan handsama denne oppgåva
+ Spel av eller pause
+ Auxio-ikon
+ Spor %d
+ Ingen mapper
+ Bland alle spor
+ Fjern dette sporet
+ Flytt dette sporet
+ Hopp til siste spor
+ Stopp avspeling
+ Flytt denne fana
+ Tøm søket
+ Ukjend sjanger
+ Fjern mappe
+ Utvalsbilete
+ Ingen dato
+ +%.1f dB
+ Sjangerbilete for %s
+ Ukjend artist
+ OGG-ljod
+ Limegrønn
+ Gul
+ Oransje
+ Inga plate
+ Matroska-ljod
+ Ingen musik vert spelt av
+ MPEG-4-ljod
+ Avansert audio-koding (AAC)
+ Blå
+ Grønn
+ MPEG-1-ljod
+ Indigo
+ Blågrønn
+ Mørkgrønn
+ Grå
+ Redigerer %s
+ Brun
+ Dynamisk
+ %d valde
+ %d kbps
+ Innlasta artistar: %d
+ -%.1f dB
+ %d Hz
+ Innlasta album: %d
+ Total lengd: %s
+ Vel mapper
+
diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml
index 9e23fb78b..87feed829 100644
--- a/app/src/main/res/values-pa/strings.xml
+++ b/app/src/main/res/values-pa/strings.xml
@@ -63,8 +63,6 @@
ਰੱਦ ਕਰੋ
ਸਾਂਭੋ
ਰੀਸੈਟ ਕਰੋ
- ਸਟੇਟ ਕਲੀਅਰ ਕੀਤੀ ਗਈ
- ਸਟੇਟ ਰੀਸਟੋਰ ਕੀਤੀ ਗਈ
ਦੇ ਬਾਰੇ
ਸੰਸਕਰਣ
ਸ੍ਰੋਤ ਕੋਡ
@@ -78,7 +76,6 @@
ਬਿੱਟ ਰੇਟ
ਸੈਂਪਲ ਰੇਟ
ਸ਼ਾਮਿਲ ਕਰੋ
- ਸਟੇਟ ਸਾਂਭੀ ਗਈ
ਤਬਦੀਲੀਆਂ ਲਈ ਤੁਹਾਡੀ ਸੰਗੀਤ ਲਾਇਬ੍ਰੇਰੀ ਦੀ ਨਿਗਰਾਨੀ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ…
ਲਾਈਸੈਂਸ
ਸੰਗੀਤ ਪਲੇਬੈਕ ਵੇਖੋ ਅਤੇ ਕੰਟਰੋਲ ਕਰੋ
@@ -117,26 +114,17 @@
ਸ਼ੈਲੀ ਤੋਂ ਖੇਡੋ
ਸ਼ਫਲ ਯਾਦ ਰੱਖੋ
ਗੀਤ ਦੁਹਰਾਉਣ ਤੇ ਰੋਕੋ
- ਰੀਪਲੇਅ-ਗੇਨ
+ ਵਾਲੀਅਮ ਨਾਰਮਲਾਈਜ਼ੇਸ਼ਨ
ਰੀਪਲੇਅ-ਗੇਨ ਰਣਨੀਤੀ
ਟਰੈਕ ਨੂੰ ਤਰਜੀਹ
ਐਲਬਮ ਨੂੰ ਤਰਜੀਹ
ਬਿਨਾਂ ਟੈਗਾਂ ਦੇ ਐਡਜਸਟਮੈਂਟ
- ਪ੍ਰਬੰਧਿਤ ਕਰੋ ਕਿ ਸੰਗੀਤ ਕਿੱਥੋਂ ਲੋਡ ਕੀਤਾ ਜਾਣਾ ਚਾਹੀਦਾ ਹੈ
- ਫੋਲਡਰ
- ਬਾਹਰ ਰੱਖੋ
- ਸ਼ਾਮਿਲ ਕਰੋ
+ ਪ੍ਰਬੰਧਿਤ ਕਰੋ ਕਿ ਸੰਗੀਤ ਕਿੱਥੋਂ ਲੋਡ ਕੀਤਾ ਜਾਣਾ ਚਾਹੀਦਾ ਹੈ
+ ਫੋਲਡਰ
ਸੰਗੀਤ ਤਾਜ਼ਾ-ਤਰੀਨ ਕਰੋ
- ਪਰਸਿਸਟੈਂਟ
- ਪਲੇਬੈਕ ਸਥਿਤੀ ਨੂੰ ਸੁਰੱਖਿਅਤ ਕਰੋ
- ਮੌਜੂਦਾ ਪਲੇਬੈਕ ਸਥਿਤੀ ਨੂੰ ਹੁਣੇ ਸੁਰੱਖਿਅਤ ਕਰੋ
- ਪਲੇਬੈਕ ਸਥਿਤੀ ਸਾਫ਼ ਕਰੋ
- ਪਲੇਬੈਕ ਸਥਿਤੀ ਨੂੰ ਰੀਸਟੋਰ ਕਰੋ
- ਪਹਿਲਾਂ ਸੁਰੱਖਿਅਤ ਕੀਤੀ ਪਲੇਬੈਕ ਸਥਿਤੀ ਨੂੰ ਰੀਸਟੋਰ ਕਰੋ (ਜੇ ਕੋਈ ਹੈ)
ਕੋਈ ਐਪ ਨਹੀਂ ਮਿਲੀ ਜੋ ਇਸ ਕਾਰਜ ਨੂੰ ਸੰਭਾਲ ਸਕਦੀ ਹੈ
- ਕੋਈ ਫੋਲਡਰ ਨਹੀਂ
- ਇਹ ਫੋਲਡਰ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ
- ਸਥਿਤੀ ਨੂੰ ਸੁਰੱਖਿਅਤ ਕਰਨ ਵਿੱਚ ਅਸਮਰੱਥ
+ ਕੋਈ ਫੋਲਡਰ ਨਹੀਂ
+ ਇਹ ਫੋਲਡਰ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ
ਚਲਾਓ ਜਾਂ ਰੋਕੋ
ਅਗਲੇ ਗੀਤ \'ਤੇ ਜਾਓ
ਆਖਰੀ ਗੀਤ \'ਤੇ ਜਾਓ
@@ -148,16 +136,10 @@
ਪ੍ਰੀ-ਐਂਪ ਨੂੰ ਪਲੇਬੈਕ ਦੌਰਾਨ ਮੌਜੂਦਾ ਵਿਵਸਥਾ \'ਤੇ ਲਾਗੂ ਕੀਤਾ ਜਾਂਦਾ ਹੈ
ਟੈਗਸ ਨਾਲ ਐਡਜਸਟਮੈਂਟ
ਚੇਤਾਵਨੀ: ਪ੍ਰੀ-ਐਂਪ ਨੂੰ ਉੱਚ ਸਕਾਰਾਤਮਕ ਮੁੱਲ ਵਿੱਚ ਬਦਲਣ ਦੇ ਨਤੀਜੇ ਵਜੋਂ ਕੁਝ ਆਡੀਓ ਟਰੈਕਾਂ \'ਤੇ ਸਿਖਰ ਹੋ ਸਕਦਾ ਹੈ।
- ਤੁਹਾਡੇ ਦੁਆਰਾ ਸ਼ਾਮਲ ਕੀਤੇ ਫੋਲਡਰਾਂ ਤੋਂ ਸੰਗੀਤ ਨੂੰ ਲੋਡ ਕੀਤਾ ਨਹੀਂ ਜਾਵੇਗਾ।
- ਤੁਹਾਡੇ ਦੁਆਰਾ ਸ਼ਾਮਲ ਕੀਤੇ ਫੋਲਡਰਾਂ ਤੋਂ ਸੰਗੀਤ ਸਿਰਫ਼ ਲੋਡ ਕੀਤਾ ਜਾਵੇਗਾ।
- ਮੋਡ
ਟੈਗ ਕੈਸ਼ ਨੂੰ ਸਾਫ਼ ਕਰੋ ਅਤੇ ਸੰਗੀਤ ਲਾਇਬ੍ਰੇਰੀ ਨੂੰ ਪੂਰੀ ਤਰ੍ਹਾਂ ਰੀਲੋਡ ਕਰੋ (ਹੌਲੀ, ਪਰ ਵਧੇਰੇ ਸੰਪੂਰਨ)
- ਪਹਿਲਾਂ ਸੁਰੱਖਿਅਤ ਕੀਤੀ ਪਲੇਬੈਕ ਸਥਿਤੀ ਨੂੰ ਸਾਫ਼ ਕਰੋ (ਜੇ ਕੋਈ ਹੈ)
ਕੋਈ ਸੰਗੀਤ ਨਹੀਂ ਮਿਲਿਆ
ਸੰਗੀਤ ਲੋਡ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ
Auxio ਨੂੰ ਤੁਹਾਡੀ ਸੰਗੀਤ ਲਾਇਬ੍ਰੇਰੀ ਨੂੰ ਪੜ੍ਹਨ ਲਈ ਇਜਾਜ਼ਤ ਦੀ ਲੋੜ ਹੈ
- ਸਥਿਤੀ ਨੂੰ ਰੀਸਟੋਰ ਕਰਨ ਵਿੱਚ ਅਸਮਰੱਥ
- ਸਥਿਤੀ ਨੂੰ ਸਾਫ਼ ਕਰਨ ਵਿੱਚ ਅਸਮਰੱਥ
%d ਨੂੰ ਟਰੈਕ ਕਰੋ
ਸਿਰਫ਼ ਉਹਨਾਂ ਕਲਾਕਾਰਾਂ ਨੂੰ ਦਿਖਾਓ ਜੋ ਕਿਸੇ ਐਲਬਮ \'ਤੇ ਸਿੱਧੇ ਤੌਰ \'ਤੇ ਕ੍ਰੈਡਿਟ ਕੀਤੇ ਜਾਂਦੇ ਹਨ (ਚੰਗੀ ਤਰ੍ਹਾਂ ਨਾਲ ਟੈਗ ਕੀਤੀਆਂ ਲਾਇਬ੍ਰੇਰੀਆਂ \'ਤੇ ਵਧੀਆ ਕੰਮ ਕਰਦਾ ਹੈ
ਉਹਨਾਂ ਆਡੀਓ ਫਾਈਲਾਂ ਨੂੰ ਅਣਡਿੱਠ ਕਰੋ ਜੋ ਸੰਗੀਤ ਨਹੀਂ ਹਨ, ਜਿਵੇਂ ਕਿ ਪੌਡਕਾਸਟ
@@ -184,13 +166,13 @@
ਚਿੱਤਰ
ਐਲਬਮ ਕਵਰ
ਬੰਦ
- ਤੇਜ
- ਉੱਚ ਕੁਆਲਿਟੀ
+ ਤੇਜ
+ ਉੱਚ ਕੁਆਲਿਟੀ
ਆਡੀਓ
ਪਿਛਲੇ ਗੀਤ \'ਤੇ ਜਾਣ ਤੋਂ ਪਹਿਲਾਂ ਰੀਵਾਈਂਡ ਕਰੋ
ਦੁਹਰਾਉਣ \'ਤੇ ਰੁਕੋ
ਲਾਇਬ੍ਰੇਰੀ
- ਸੰਗੀਤ ਫੋਲਡਰ
+ ਸੰਗੀਤ ਫੋਲਡਰ
ਕਤਾਰ ਖੋਲ੍ਹੋ
ਇਸ ਕਤਾਰ ਗੀਤ ਨੂੰ ਹਟਾਓ
ਇਸ ਕਤਾਰ ਗੀਤ ਨੂੰ ਮੂਵ ਕਰੋ
@@ -208,7 +190,6 @@
ਪਲੇਅ-ਲਿਸਟ ਦਾ ਨਾਂ ਬਦਲਿਆ
ਨੀਲਾ-ਹਰਾ
ਮਿਟਾਓ
- ਇੱਕ ਨਵੀਂ ਪਲੇਅ-ਲਿਸਟ ਬਣਾਓ
ਪਲੇਅ- ਲਿਸਟ ਵਿੱਚ ਸ਼ਾਮਿਲ ਕੀਤਾ
ਇਹ ਟੈਬ ਹਿਲਾਓ
ਕੋਈ ਗੀਤ ਨਹੀਂ
@@ -273,7 +254,7 @@
ਸਲੇਟੀ
ਕੁੱਲ ਮਿਆਦ: %s
- ਫੋਲਡਰ ਹਟਾਓ
+ ਫੋਲਡਰ ਹਟਾਓ
Auxio ਆਈਕਾਨ
ਉੱਤੇ ਵਿਖਾਈ ਦਿੰਦਾ ਹੈ
ਪਲੇਅ-ਲਿਸਟਾਂ
@@ -322,4 +303,9 @@
ਆਪਣਾ ਨਾਮ ਇੱਥੇ ਜੋੜਨ ਲਈ ਪ੍ਰੋਜੈਕਟ ਨੂੰ ਦਾਨ ਕਰੋ!
ਕਤਾਰ ਨੂੰ ਛੱਡਣ ਜਾਂ ਸੰਪਾਦਿਤ ਕਰਨ ਵੇਲੇ ਚਲਾਉਂਦੇ/ਰੋਕੇ ਰਹੋ
ਵਿਰਾਮ ਯਾਦ ਰੱਖੋ
+ ਬੰਦ
+ ਪਲੇਬੈਕ ਸ਼ੁਰੂ ਕਰੋ
+ ਪਹਿਲਾਂ ਸੁਰੱਖਿਅਤ ਕੀਤੀ ਸਥਿਤੀ ਦੀ ਵਰਤੋਂ ਕਰਕੇ Auxio ਨੂੰ ਸ਼ੁਰੂ ਕਰਦਾ ਹੈ। ਜੇਕਰ ਕੋਈ ਰੱਖਿਅਤ ਸਥਿਤੀ ਉਪਲਬਧ ਨਹੀਂ ਹੈ, ਤਾਂ ਸਾਰੇ ਗੀਤ ਸ਼ਫਲ ਤੇ ਚੱਲਣਗੇ ਅਤੇ ਪਲੇਬੈਕ ਤੁਰੰਤ ਸ਼ੁਰੂ ਹੋ ਜਾਵੇਗਾ।
+\n
+\nਚੇਤਾਵਨੀ: ਇਸ ਸੇਵਾ ਨੂੰ ਨਿਯੰਤਰਿਤ ਕਰਨ ਵਿੱਚ ਸਾਵਧਾਨ ਰਹੋ, ਜੇਕਰ ਤੁਸੀਂ ਇਸਨੂੰ ਬੰਦ ਕਰਦੇ ਹੋ ਅਤੇ ਫਿਰ ਇਸਨੂੰ ਦੁਬਾਰਾ ਵਰਤਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰਦੇ ਹੋ, ਤਾਂ ਤੁਸੀਂ ਸ਼ਾਇਦ ਐਪ ਨੂੰ ਕ੍ਰੈਸ਼ ਕਰ ਦਿਓਗੇ।
\ No newline at end of file
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 02c6dd410..dfd3baee3 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -129,7 +129,7 @@
Brak utworu
Korektor
Rozmiar
- Brak folderów
+ Brak folderów
Odtwórz wszystkie utwory
Odtwórz album
Automatycznie odtwórz muzykę po podłączeniu słuchawek (może nie działać na wszystkich urządzeniach)
@@ -149,14 +149,13 @@
Przejdź do następnego utworu
Autorstwa Alexandra Capeharta
Zaokrąglone krawędzie
- Włącz zaokrąglone rogi na dodatkowych elementach interfejsu (wymaga zaokrąglenia okładek albumów)
+ Włącz zaokrąglone narożniki na dodatkowych elementach interfejsu (wymaga zaokrąglenia okładek albumów)
Akcja na pasku odtwarzania
Następny utwór
Tryb powtarzania
Ustawienie ReplayGain
Preferuj album, jeśli jest odtwarzany
Odtwarzanie z widoku biblioteki
- Zapisz stan odtwarzania
Przecinek (,)
Średnik (;)
Ukośnik (/)
@@ -165,35 +164,24 @@
MPEG-4
Ogg
Prosty i praktyczny odtwarzacz muzyki na Androida.
- Usuń folder
+ Usuń folder
Pokaż kolejkę
AAC
Automatycznie odśwież bibliotekę po wykryciu zmian (wymaga stałego powiadomienia)
- Wyklucz
- Zawrzyj
Zmień pozycję utworu w kolejce
Przesuń kartę
Wizerunek wykonawcy dla %s
Ładuję bibliotekę muzyczną…
Preferuj utwór
- Przywrócono stan odtwarzania
- Wyczyszczono stan odtwarzania
- Zapisano stan odtwarzania
Karty w bibliotece
Zmień widoczność i kolejność kart w bibliotece
Użyj czarnego motywu
Elementy
Material You
%d kb/s
- Zapisz obecny stan odtwarzania
- Wyczyść stan odtwarzania
Matroska
- Wyczyść poprzedni stan odtwarzania (jeśli istnieje)
- Przywróć stan odtwarzania
- Przywróć poprzedni stan odtwarzania (jeśli istnieje)
- Foldery z muzyką
- Wybierz z których folderów importowane są utwory
- Tryb
+ Foldery z muzyką
+ Wybierz z których folderów importowane są utwory
Przewiń przed odtworzeniem poprzedniego utworu
Przewiń do początku obecnie odtwarzanego utworu zamiast odtworzenia poprzedniego
Preamplifier ReplayGain
@@ -207,7 +195,6 @@
Użyj alternatywnej akcji w powiadomieniu
Zatrzymaj odtwarzanie przy powtórzeniu
Wyczyść zapytanie wyszukiwania
- Nie można przywrócić stanu odtwarzania
Okładka gatunku %s
Podgląd i sterowanie odtwarzanianiem muzyki
Regulacja w oparciu o tagi
@@ -216,17 +203,15 @@
Odtwarzanie z widoku szczegółowego
Odtwórz tylko wybrane
Zatrzymaj odtwarzanie, kiedy utwór się powtórzy
- Muzyka będzie importowana tylko z wybranych folderów.
Znaki oddzielające wartości
Wybierz znaki oddzielające poszczególne wartości w metadanych
Auxio wymaga zgody na dostęp do twojej biblioteki muzycznej
- Ten folder nie jest wspierany
+ Ten folder nie jest wspierany
Utwory nie są odtwarzane
Importuję bibliotekę muzyczną… (%1$d/%2$d)
Zaimportowane albumy: %d
Zaimportowane gatunki: %d
Łączny czas trwania: %s
- Muzyka nie będzie importowana z wybranych folderów.
Zmień tryb powtarzania
Odtwórz losowo wszystkie utwory
Monitoruję zmiany w bibliotece muzycznej…
@@ -239,9 +224,9 @@
Wyklucz inne pliki dźwiękowe
Okładki albumów
Wyłączone
- Niska jakość
- Wysoka jakość
- Uwaga: To ustawienie może powodować nieprawidłowe przetwarzenie tagów (tak, jakby posiadały wiele wartości). Problem ten należy rozwiązać stawiając ukośnik wsteczny (\\) przed niepożądanymi znakami oddzielającymi.
+ Niska jakość
+ Wysoka jakość
+ Uwaga: To ustawienie może powodować nieprawidłowe przetwarzenie tagów (mogą być przetwarzane tak, jakby posiadały wiele wartości). Problem ten należy rozwiązać stawiając ukośnik wsteczny () przed niepożądanymi znakami oddzielającymi.
Dostosuj motyw i kolory aplikacji
Ukryj wykonawców uczestniczących
Zarządzaj importowaniem muzyki i obrazów
@@ -249,12 +234,11 @@
Dostosuj elementy i funkcje interfejsu
Pokaż tylko artystów bezpośrednio przypisanych do albumu (działa najlepiej w przypadku dobrze otagowanych bibliotek)
Odtwarzanie
- Foldery
- Stan odtwarzania
+ Foldery
Obrazy
Zarządzaj dźwiękiem i odtwarzaniem muzyki
Wybrano %d
- Wyrównanie głośności (ReplayGain)
+ Normalizacja głośności
Resetuj
Wiki
Funkcje
@@ -269,15 +253,12 @@
%1$s, %2$s
Muzyka
- Nie można wyczyścić stanu odtwarzania
- Nie można zapisać stanu odtwarzania
Malejąco
Playlisty
Playlista
Obraz playlisty %s
Inteligentne sortowanie
- Ignoruj słowa takie jak „the” oraz numery w tytule podczas sortowania (działa najlepiej z utworami w języku angielskim)
- Utwórz nową playlistę
+ Ignoruj słowa takie jak „the” oraz liczby w tytule podczas sortowania (działa najlepiej z utworami w języku angielskim)
Nowa playlista
Dodaj do playlisty
Utworzono playlistę
@@ -299,8 +280,59 @@
Przytnij okładki do formatu 1:1
Wymuś kwadratowe okładki
Piosenka
- Odtwarzanie utworu samodzielnie
+ Odtwórz tylko utwór
Widok
Sortuj według
Kierunek
+ Brak albumów
+ Zaimportowane playlisty
+ Importuj
+ Zgłoś
+ Relatywna
+ Wyłącz
+ Informacje o błędzie
+ Zaimportowano playlistę
+ Eksportuj
+ Styl ścieżki
+ Wybór obrazu
+ Więcej
+ Zaznaczenie
+ Skopiowano
+ Autor
+ Wspomóż finansowo
+ Wspierający
+ Wyeksportowano playlistę
+ Pusta playlista
+ Importuj playlistę
+ Ścieżka
+ Eksportuj playlistę
+ Absolutna
+ Demo
+ Dema
+ Więcej
+ Uruchamia Auxio z użyciem poprzednio zapisanego stanu. Jeśli nie zapisano poprzedniego stanu, wszystkie utwory będą odtwarzane w losowej kolejności. Odtwarzanie rozpocznie się natychmiast.\n\nUWAGA: Zachowaj ostrożność przy korzystaniu z tej opcji. Jeśli ją zamkniesz, a następnie spróbujesz ponownie z niej skorzystać, aplikacja może się zawiesić.
+ Nie udało się wyeksportować playlisty do tego pliku
+ Dopasowanie głośności utworu (ReplayGain)
+ Dopasowanie głośności albumu (ReplayGain)
+ Wesprzyj projekt darowizną, aby Twoje imię pojawiło się tutaj!
+ Pozostań w trakcie odtwarzania/zatrzymania podczas pomijania lub edytowania kolejki
+ Zapamiętaj pauzę
+ Zacznij odtwarzanie
+ Nie udało się zaimportować playlisty z tego pliku
+ Używaj ścieżek kompatybilnych z Windows
+ Wyślij e-maila
+ Informacja zwrotna
+ Zgłoś problem na GitHubie
+ Wybierz foldery
+ MPEG-4 zawierający %s
+ Nieznany
+ Nieznany album
+ Nowy folder
+ Apple Lossless Audio Codec (ALAC)
+ Oszczędzanie miejsca
+ Tutaj pojawią się dodane utwory.
+ Tutaj pojawią się dodane albumy.
+ Tutaj pojawią się dodani artyści.
+ Tutaj pojawią się dodane playlisty.
+ Tutaj pojawią się dodane gatunki.
\ No newline at end of file
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 4b659230c..4ba7c30a5 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -80,24 +80,19 @@
Usar player de notificação alternativo
A pré-amplificação é aplicada em cima da normalização de volume
Aviso: Alterar a pré-amplificação para um valor positivo muito alto pode resultar em picos de volume em algumas faixas.
- Pastas de música
- Gerencia de onde as músicas devem ser carregadas
- Incluir
+ Pastas de música
+ Gerencia de onde as músicas devem ser carregadas
Falha ao carregar músicas
Nenhum aplicativo encontrado que possa lidar com esta tarefa
- Sem pastas
- Esta pasta não é compatível
+ Sem pastas
+ Esta pasta não é compatível
Recarrega a biblioteca de músicas usando metadados salvos em cache quando possível
Retroceder antes de voltar
Recarregar música
Retroceder a música antes de voltar para a anterior
- As músicas serão carregadas somente das pastas que você adicionar.
- Excluir
- As músicas não serão carregadas das pastas que você adicionar.
- Modo
O Auxio precisa de permissão para ler sua biblioteca de músicas
Um reprodutor de música simples e racional para android.
- Carregando sua biblioteca de músicas…
+ Carregando a sua biblioteca de músicas…
Ano
Duração
Contagem de músicas
@@ -110,7 +105,6 @@
Tamanho
Taxa de bits
Taxa de amostragem
- Lista salva
Altere a ordem e visibilidade das abas da biblioteca
Tema Amoled
Use um tema Amoled
@@ -118,7 +112,6 @@
Deixa o modo aleatório ativo ao reproduzir uma nova música
Pausar ao repetir
Pausar quando uma música se repete
- Salva a lista de reprodução atual
Pular para a música anterior
Alterar o modo de repetição
Aleatorizar todas das músicas
@@ -129,7 +122,7 @@
Capa do álbum
Pular para a próxima música
Ativa ou desativa o modo aleatório
- Remover pasta
+ Remover pasta
Ciano
Roxo escuro
Índigo
@@ -182,28 +175,20 @@
Abas da biblioteca
Gênero
Reproduzir do artista
- Restaura a lista de reprodução salva anteriormente (se houver)
Ajuste em faixas com metadados
- Lista limpa
- Lista restaurada
Carregando música
Carregando música
Monitorando a biblioteca de músicas
Cantos arredondados
Pular para o próximo
Reproduzir do álbum
- Salvar lista de reprodução
- Limpar lista de reprodução
- Restaurar lista de reprodução
Visualize e controle a reprodução de música
Ação personalizada na barra de reprodução
Modo de repetição
- Limpa a lista de reprodução salva anteriormente (se houver)
EPs
EP
Singles
Single
- Nenhuma lista pode ser restaurada
Compilações
Compilação
Single remix
@@ -236,8 +221,8 @@
Ignorar arquivos que não sejam músicas
Capas de álbuns
Desligado
- Rápido
- Alta qualidade
+ Rápido
+ Alta qualidade
Mixagens de DJ
Mixagem de DJ
@@ -247,8 +232,6 @@
Re-escanear músicas
Limpa os metadados em cache e recarrega totalmente a biblioteca de música (lento, porém mais completo)
- Não foi possível limpar a lista
- Não foi possível salvar a lista
Ocultar artistas colaboradores
Mostrar apenas artistas que foram creditados diretamente no álbum (funciona melhor em músicas com metadados completos)
%d Selecionadas
@@ -263,12 +246,11 @@
Customize os controles da interface e o comportamento
Música
Controle como as músicas e imagens são carregadas
- Normalização de volume (ReplayGain)
+ Normalização de volume
Biblioteca
- Persistência
Comportamento
- Pastas
- Descendente
+ Pastas
+ Decrescente
Ignorar artigos ao classificar
Ignore palavras como \"the\" ao classificar por nome (funciona melhor com músicas em inglês)
Playlists
@@ -287,7 +269,6 @@
Organizar por
Música
Apagar playlist\?
- Criar uma nova playlist
Playlist deletada
Nova playlist
Playlist renomeada
@@ -314,7 +295,7 @@
Seleção
Exportar
Exportar playlist
- Direção
+ Ordem
Estilo de caminho
Absoluto
Relativo
@@ -329,4 +310,28 @@
Recorta todas as capas de álbuns para uma proporção de imagem 1:1
Sem músicas
Informar
+ Desligado
+ Imagem de playlist para %s
+ Reproduzir música sozinha
+ Imagem de seleção
+ Iniciar a reprodução
+ Inicia o Auxio usando o estado salvo anteriormente. Se nenhum estado salvo estiver disponível, todas as músicas serão reproduzidas aleatoriamente.
+\n
+\nAVISO: Tenha cuidado ao controlar este serviço. Se você fechá-lo e então tentar usá-lo novamente, provavelmente ocasionará um crash no aplicativo.
+ Mais
+ Envie um e-mail
+ Crie uma issue no GitHub
+ Escolher pastas
+ MPEG-4 contendo %s
+ Apple Lossless Audio Codec (ALAC)
+ Desconhecido
+ Álbum desconhecido
+ Feedback
+ As suas músicas aparecerão aqui.
+ Os seus álbuns aparecerão aqui.
+ Os seus artistas aparecerão aqui.
+ As suas playlists aparecerão aqui.
+ Os seus gêneros aparecerão aqui.
+ Salvar espaço
+ Nova pasta
\ No newline at end of file
diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml
index d6d307c19..a71a388f6 100644
--- a/app/src/main/res/values-pt-rPT/strings.xml
+++ b/app/src/main/res/values-pt-rPT/strings.xml
@@ -89,17 +89,17 @@
Estatísticas da biblioteca
Capa do álbum
Data
- Rápido
- Qualidade alta
+ Rápido
+ Qualidade alta
Personalizar a barra de reprodução
Modo de repetição
Reproduzir do artista
Pausar na repetição
O Auxio precisa de permissão para ler a sua biblioteca de músicas
- Sem pastas
- Esta pasta não é compatível
+ Sem pastas
+ Esta pasta não é compatível
Mover esta música da fila
- Remover pasta
+ Remover pasta
Mistura de compilações
Compilação ao vivo
Disco
@@ -120,8 +120,6 @@
Propriedades da música
OK
Adicionar
- Estado salvo
- Estado limpo
Tema preto
Limpar consulta de pesquisa
Imagem de gênero para %s
@@ -139,19 +137,14 @@
Duração
Cancelar
A carregar biblioteca de músicas…
- Configurar onde a música deve ser carregada
+ Configurar onde a música deve ser carregada
Gênero
Mantenha a reprodução aleatória ao reproduzir uma nova música
Pular para a próxima música
Aviso: Usar essa configuração pode resultar em algumas etiquetas serem interpretadas incorretamente como tendo múltiplos valores. Pode resolver isso pré-definindo caracteres de separador indesejados com uma barra invertida (\\).
Áudio MPEG-1
Recarregamento automático
- Pastas de música
- Modo
- Excluir
- A música não será carregada das pastas que adicionar.
- Incluir
- A música será somente carregada das pastas que adicionar.
+ Pastas de música
Excluir não-música
Ignorar ficheiros de áudio que não são música, tal como podcasts
Configurar caracteres que denotam múltiplos valores de etiqueta
@@ -174,7 +167,6 @@
A monitorizar a biblioteca de música
Equalizador
Um reprodutor de música simples e racional para Android.
- Estado restaurado
Mostrar
Abas da biblioteca
Altere a visibilidade e a ordem das abas da biblioteca
@@ -189,9 +181,6 @@
O pré-amplificador é aplicado ao ajuste existente durante a reprodução
Reproduzir de todas as músicas
Pausar quando uma música é repetida
- Limpar o estado de reprodução salvo anteriormente (se houver)
- Restaurar o estado de reprodução
- Restaurar o estado de reprodução salvo anteriormente (se houver)
Ativar ou desativar a reprodução aleatória
Embaralhar todas as músicas
Remover esta música de fila
@@ -206,9 +195,6 @@
Mixtape
Remixes
Artista
- Gravar estado da reprodução
- Salvar o estado de reprodução atual
- Limpar estado de reprodução
Álbum ao vivo
-%.1f dB
%d kbps
@@ -219,7 +205,7 @@
Ativar cantos arredondados em elementos adicionais da interface do utilizador (requer que as capas dos álbuns sejam arredondadas)
%d Selecionadas
Misturas DJ
- DJ Mix
+ Mixagem de DJ
Aleatório
Ocultar colaboradores
Limpa os metadados em cache e recarrega totalmente a biblioteca de música (lento, porém mais completo)
@@ -240,10 +226,7 @@
Retrocede a música antes de voltar para a anterior
Recarregar música
%1$s, %2$s
- Não foi possível limpar a lista
- Não foi possível gravar a lista
Procurar músicas novamente
- Nenhuma lista pode ser restaurada
Ícone do Auxio
Misturar tudo
Ao tocar da biblioteca
@@ -255,7 +238,7 @@
- %d artistas
- %d artistas
- Configurar ganho de repetição
+ Normalização de volume
Descendente
Mudar o tema e cores da app
Personalize os controlos e o comportamento do interface do utilizador
@@ -264,9 +247,8 @@
Imagens
Configurar o som e comportamento da reprodução
Reprodução
- Pastas
+ Pastas
Biblioteca
- Estado da reprodução
E comercial (&)
Comportamento
Classificação inteligente
@@ -295,8 +277,7 @@
Ordenar por
Visualizar
Música
- Eliminar lista de reprodução
- Criar nova lista de reprodução
+ Apagar lista de reprodução?
Lista de reprodução eliminada
Relatório
Nova lista de reprodução
@@ -306,4 +287,33 @@
Renomear lista de reprodução
Excluir %s\? Não pode ser desfeito.
Só aparecer
+ Desligado
+ Incapaz de exportar a lista de reprodução para este ficheiro
+ Lista de reprodução importada
+ Não foi possível importar uma lista de reprodução deste ficheiro
+ Demo
+ Demos
+ Ajuste de ReplayGain da faixa
+ Autor
+ Doar
+ Apoiadores
+ Lista de reprodução exportada
+ Doe para o projeto para ter o seu nome adicionado aqui!
+ Lembrar pausa
+ Continuar a reproduzir/pausado quando ao pular ou editar a fila
+ Lista de reprodução vazia
+ Importar lista de reprodução
+ Sem álbuns
+ Caminho
+ Importar
+ Exportar
+ Exportar lista de reprodução
+ Estilo de caminho
+ Absoluto
+ Lista de reprodução importada
+ Ajuste de ReplayGain do álbum
+ Relativo
+ Usar caminhos compatíveis com Windows
+ Inicia o Auxio utilizando o estado anteriormente guardado. Se não existir nenhum estado guardado, todas as músicas serão reproduzidas aleatoriamente. A reprodução começará imediatamente.\n\nAVISO: Tenha cuidado ao controlar este serviço. Se o fechar e tentar utilizá-lo novamente, provavelmente irá causar uma falha na aplicação.
+ Iniciar reprodução
\ No newline at end of file
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index 3a0906840..f193cf386 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -1,2 +1,328 @@
-
\ No newline at end of file
+
+ Lista de reprodução importada
+ A monitorizar a biblioteca de música
+ Tentar novamente
+ Mais
+ Um reprodutor de música simples e racional para Android.
+ Permitir
+ Música
+ Todas as músicas
+ Álbuns
+ Álbum
+ Compilações
+ Importar
+ Gênero
+ Lista de reprodução importada
+ Géneros
+ Reproduzir
+ Reproduzir a próxima
+ Adicionar à fila
+ Adicionar à lista de reprodução
+ Propriedades da música
+ Formato
+ Fila
+ Repor
+ Código fonte
+ Seleção
+ A carregar biblioteca de músicas…
+ Licenças
+ Estatísticas da biblioteca
+ Artistas
+ Lista de reprodução
+ Listas de reprodução
+ Adicionada à fila
+ Adicionado à lista de reprodução
+ Autor
+ Doar
+ A Monitorizar alterações na sua biblioteca de músicas…
+ Lista de reprodução exportada
+ Doe para o projeto para ter o seu nome adicionado aqui!
+ Definições
+ Abas da biblioteca
+ Altere a visibilidade e a ordem das abas da biblioteca
+ Personalizar notificações
+ Lista de reprodução vazia
+ Importar lista de reprodução
+ Procurar
+ Filtrar
+ Duração
+ Contagem de músicas
+ Faixa
+ Data adicionada
+ Organizar
+ Ordenar por
+ A tocar agora
+ Equalizador
+ Misturar
+ Ir para o artista
+ Ir para o álbum
+ Propriedades
+ Visualizar
+ Tudo
+ Data
+ A carregar música
+ A carregar música
+ Músicas
+ Álbum ao vivo
+ Álbum remix
+ Solteiro
+ Compilação
+ Artista
+ Renomear
+ Eliminar
+ Apagar lista de reprodução?
+ Nova lista de reprodução
+ Renomear lista de reprodução
+ Editar
+ Nome
+ Cancelar
+ Adicionar
+ Salvar
+ Mudar o tema e cores da app
+ Tema
+ Claro
+ Automático
+ Caminho
+ Informações de erro
+ Copiado
+ Relatório
+ Vêr e controlar a reprodução da música
+ Lista de reprodução criada
+ Aparência
+ Tema preto
+ Utilizar tema preto puro
+ Modo redondo
+ Ativar cantos arredondados em elementos adicionais da interface do utilizador (requer que as capas dos álbuns sejam arredondadas)
+ Personalizar
+ Personalize os controlos e o comportamento do interface do utilizador
+ Escuro
+ Esquema de cores
+ Avançar para o próximo
+ Modo de repetição
+ Comportamento
+ Exportar lista de reprodução
+ Partilhar
+ Tamanho
+ Aleatório
+ Misturar tudo
+ Exportar
+ Sobre
+ Versão
+ Lista de reprodução renomeada
+ Lista de reprodução eliminada
+ Procurar na biblioteca…
+ Desligado
+ Configurar onde a música deve ser carregada
+ Pastas
+ Ativar ou desativar a reprodução aleatória
+ Seleção de imagem
+ Verde
+ Disco %d
+ %d Hz
+ Músicas carregado: %d
+ Álbuns carregados: %d
+ Artistas carregados: %d
+ Preferir álbum
+
+ - %d Música
+ - %d Músicas
+ - %d Músicas
+
+ Demo
+ Normalização de volume
+ Ajuste de ReplayGain da faixa
+ Ajuste de ReplayGain do álbum
+ Desenvolvido por Alexander Capehart
+ Apoiadores
+ Mostrar
+ Personalizar a barra de reprodução
+ Forçar capas em formato quadrado
+ Recortar à capa dos álbuns numa proporção de 1:1
+ Configurar o som e comportamento da reprodução
+ Reprodução
+ Retroceder antes de voltar
+ Retrocede a música antes de voltar para a anterior
+ Pausar na repetição
+ Pausar quando uma música é repetida
+ Pastas de música
+ Lembrar pausa
+ Continuar a reproduzir/pausado quando ao pular ou editar a fila
+ Estratégia do ganho de repetição
+ Prefira o álbum se estiver a tocar
+ +%.1f dB
+ -%.1f dB
+ %d kbps
+ Gêneros carregados: %d
+ Direção
+ Ascendente
+ Descendente
+ Desligado
+ Recarregar música
+ Miniálbuns
+ EP
+ EP ao vivo
+ Álbum de Remix
+ Solteiros
+ Single ao vivo
+ Single remix
+ Compilação ao vivo
+ Mistura de compilações
+ Trilhas sonoras
+ Trilha sonora
+ Mixtapes
+ Mixagem de DJ
+ Ao vivo
+ Remixes
+ Disco
+ Wiki
+ Ignorar ficheiros de áudio que não são música, tal como podcasts
+ Classificação inteligente
+ Ignore palavras como \"the\" ao classificar por nome (funciona melhor com músicas em inglês)
+ Imagens
+ Áudio
+ Pré-amplificação da normalização de volume
+ O pré-amplificador é aplicado ao ajuste existente durante a reprodução
+ Ajustar com etiquetas
+ Ajustar sem etiquetas
+ Nenhuma música encontrada
+ Falha ao carregar música
+ O Auxio precisa de permissão para ler a sua biblioteca de músicas
+ Música %d
+ Reproduzir ou pausar
+ Pular para a próxima música
+ Pular para a última música
+ Embaralhar todas as músicas
+ Ícone do Auxio
+ Capa do álbum
+ Nenhum disco
+ Nenhuma faixa
+ Nenhuma música
+ Nenhuma música tocando
+ Vermelho
+ Rosa
+ Roxo
+ Roxo profundo
+ Índigo
+ Azul
+ Verde profundo
+ Verde-amarelo
+ Amarelo
+ Laranja
+ Moreno
+
+ - %d Álbum
+ - %d Álbuns
+ - %d Álbuns
+
+
+ - %d artista
+ - %d artistas
+ - %d artistas
+
+ Ao tocar da biblioteca
+ Ao tocar a partir dos detalhes do item
+ Reproduzir do artista
+ Tocar a partir do gênero
+ Tocar música sozinha
+ Memorizar música misturada
+ Mantenha a reprodução aleatória ao reproduzir uma nova música
+ Ponto-e-vírgula (;)
+ Barra (/)
+ Mais (+)
+ Aviso: Usar essa configuração pode resultar em algumas etiquetas serem interpretadas incorretamente como tendo múltiplos valores. Pode resolver isso pré-definindo caracteres de separador indesejados com uma barra invertida (\\).
+ Ocultar colaboradores
+ Mostrar apenas artistas que foram creditados diretamente no álbum (funciona melhor em músicas com metadados completos)
+ Capas de álbuns
+ Rápido
+ Gênero desconhecido
+ Sem data
+ Codec de Audio Gratuito Sem Perdas (FLAC)
+ Ciano
+ Lista de reprodução %d
+ A carregar a sua biblioteca de músicas… (%1$d/%2$d)
+ Excluir %s? Não pode ser desfeito.
+ Taxa de bits
+ Taxa de amostragem
+ OK
+ Estilo de caminho
+ Absoluto
+ Relativo
+ Usar caminhos compatíveis com Windows
+ Codificação de Audio Avançada (AAC)
+ A editar %s
+ Demos
+ Misturas DJ
+ Só aparecer
+ Mixtape
+ Reproduzir a partir do item mostrado
+ Reproduzir de todas as músicas
+ Reproduzir do álbum
+ Conteúdo
+ Controlar como a música e as imagens são carregadas
+ Música
+ Recarregamento automático
+ Recarrega a biblioteca de músicas sempre que ela mudar (requer notificação fixa)
+ Excluir não-música
+ Separadores multi-valor
+ Configurar caracteres que denotam múltiplos valores de etiqueta
+ Vírgula (,)
+ E comercial (&)
+ Qualidade alta
+ Reprodução automática dos auscultadores
+ Iniciar música quando os auscultadores forem conectados (pode não funcionar em todos os aparelhos)
+ Preferir faixa
+ Aviso: Alterar o pré-amplificador para um valor positivo alto pode resultar em picos em algumas faixas de áudio.
+ Biblioteca
+ Recarrega a biblioteca de músicas usando metadados salvos em cache quando possível
+ Procurar músicas novamente
+ Limpa os metadados em cache e recarrega totalmente a biblioteca de música (lento, porém mais completo)
+ Não foi possível importar uma lista de reprodução deste ficheiro
+ Incapaz de exportar a lista de reprodução para este ficheiro
+ Nenhuma aplicação encontrada que possa executar esta tarefa
+ Sem pastas
+ Esta pasta não é compatível
+ Alterar o modo de repetição
+ Parar reprodução
+ Remover esta música de fila
+ Mover esta música da fila
+ Abra a fila
+ Mover esta guia
+ Limpar consulta de pesquisa
+ Remover pasta
+ Capa do álbum para %s
+ Imagem do artista para %s
+ Imagem de gênero para %s
+ Imagem da lista de reprodução de %s
+ Artista desconhecido
+ Sem álbuns
+ Áudio MPEG-1
+ Áudio MPEG-4
+ Áudio Ogg
+ Áudio Matroska
+ Azul profundo
+ Azul-verde
+ Grisalho
+ Dinâmico
+ %1$s, %2$s
+ %d Selecionadas
+ Duração total: %s
+ Mais
+ Iniciar a reprodução
+ Inicia o Auxio usando o estado salvo anteriormente. Se nenhum estado salvo estiver disponível, todas as músicas serão embaralhadas. A reprodução começará imediatamente.\n\nAVISO: Tenha cuidado ao controlar este serviço, se você fechá-lo e tentar usá-lo novamente, provavelmente irá travar o aplicativo.
+ Opinião
+ Crie um problema no GitHub
+ Envie um e-mail
+ Escolha pastas
+ Os seus artistas aparecerão aqui.
+ Álbum desconhecido
+ As suas músicas aparecerão aqui.
+ Os seus álbuns aparecerão aqui.
+ As suas listas de reprodução aparecerão aqui.
+ Os seus gêneros aparecerão aqui.
+ Nova pasta
+ Salvar espaco
+ MPEG-4 contendo %s
+ Apple Lossless Audio Codec (ALAC)
+ Desconhecido
+
diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml
index 604fc6980..851e63946 100644
--- a/app/src/main/res/values-ro/strings.xml
+++ b/app/src/main/res/values-ro/strings.xml
@@ -21,7 +21,7 @@
Adăugați la lista de așteptare
A fost adăugat la lista de așteptare
Mergi la artist
- Accesaţi albumul
+ Accesați albumul
Despre
Versiune
Cod sursă
@@ -109,9 +109,6 @@
Adaugă
Frecvența de eșantionare
Salvează
- Stat restabilit
- Stat salvat
- Statul a fost autorizat
Gen
Statisticile bibliotecii
Mărime
@@ -129,7 +126,7 @@
Activați colțurile rotunjite pentru elementele suplimentare ale interfeței de utilizare (necesită rotunjirea coperților albumelor)
Se încarcă biblioteca muzicală…
Monitorizarea bibliotecii muzicale pentru modificări…
- Afişa
+ Afișa
Utilizați o temă întunecată pur-negru
Coperți rotunjite ale albumelor
Listă de redare
@@ -172,8 +169,8 @@
Sortează după
Sortare corectă pentru nume care incep cu numere sau cuvinte precum \"the\" (funcționează cel mai bine cu melodii în limba engleză)
Forțează coperți de album pătrate
- Rapid
- Calitate mare
+ Rapid
+ Calitate mare
Punct și virgulă (;)
Editează
Exclude non-muzică
@@ -196,7 +193,6 @@
Pornește mereu redarea când niște căști sunt conectate (s-ar putea să nu meargă pe toate dispozitivele)
Re-scanează muzica
Șterge memoria cache cu taguri și reîncarcă biblioteca de muzică de tot (mai încet, dar mai complet)
- Restaurează starea redării
Cântece încărcate %d
Amestecă toate cântecele
Bleu
@@ -211,25 +207,18 @@
- %d de artiști
Arată doar artiști care sunt creditați direct pe albun (Funcționează mai bine pe bibloteci cu taguri puse bine)
- Dosarul ăsta nu e suportat
- Crează un nou playlist
+ Dosarul ăsta nu e suportat
Copertă album
Bibliotecă
Slash (/)
Deschide lista de așteptare
- Salvează starea redării acum
- Uită starea redării
Imagine gen pentru %s
Imagine playlist pentru %s
Artist necunoscut
- Nu s-a pututu restaura starea
- Nu s-a putut salva starea
Vezi mai mult
Configurează caracterele care denotă mai multe valori de taguri
- Foldere cu muzică
- Foldere
- Exclude
- Muzica nu va fi încărcată din dosarele pe care le adaugi aici.
+ Foldere cu muzică
+ Foldere
Fără cântece
Imagine artist pentru %s
Playlist importat
@@ -237,7 +226,6 @@
Donează
Playlist exportat
Donează proiectului ca să ai numele adăugat aici!
- Muzica va fi încărcată doar din folderele pe care le adaugi aici.
Redă automat la conectarea căștilor
N-a fost găsită nicio aplicație care poate face asta
Se editează %s
@@ -247,9 +235,8 @@
Redare
Pauză la repetare
Configurează comportamentul sunetului și redării
- Salvează starea redării
Fără track
- Configurează de unde se încarcă muzica
+ Configurează de unde se încarcă muzica
Reîncarcă muzica
Copertă album pentru %s
Gen necunoscut
@@ -258,24 +245,22 @@
Mov închis
Ține minte pauza
Ține minte pauza atunci când dai skip printre cântece
- Elimină dosarul
+ Elimină dosarul
Imagine selecție
Indigo
%d Selectate
Albume încărcate %d
Importă playlist
- Include
Reîncarcă biblioteca cu muzică, folosind taguri din memoria cache
Informații despre eroare
Copiat
Raportează
Redă cântecul fără să facă parte din nicio listă
Pune pauză atunci când un cântec se repetă
- Mod
Încărcarea muzicii a eșuat
Auxio are nevoie de permisiune ca să-ți acceseze biblioteca de muzică
Mută acest cântec
- Niciun dosar
+ Niciun dosar
Pornește sau oprește amestecarea
Oprește redarea
Elimină acest cântec
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 42fae303f..3283e2333 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -32,10 +32,9 @@
Добавлено в очередь
Перейти к исполнителю
Перейти к альбому
- Позиция сохранена
Добавить
Сохранить
- Нет папок
+ Нет папок
О программе
Версия
Исходный код
@@ -73,8 +72,6 @@
Пауза при повторе
Ставить на паузу при повторе трека
Библиотека
- Запоминать позицию
- Запоминать позицию в треке
Обновить музыку
Обновлять библиотеку, при возможности используя кэш тегов
@@ -82,7 +79,7 @@
Ошибка чтения библиотеки
Auxio требуется разрешение на чтение музыкальной библиотеки
Нет приложений для открытия данной ссылки
- Эта папка не поддерживается
+ Эта папка не поддерживается
Найти в библиотеке…
@@ -97,7 +94,7 @@
Переместить трек в очереди
Переместить вкладку
Очистить поисковый запрос
- Удалить папку
+ Удалить папку
Иконка Auxio
Обложка альбома
Обложка альбома %s
@@ -152,7 +149,6 @@
Битрейт
Диск
Трек
- Позиция восстановлена
Отмена
Внимание: Изменение предусиления на большое положительное значение может привести к появлению искажений на некоторых звуковых дорожках.
Сведения
@@ -162,7 +158,6 @@
Частота дискретизации
Предусиление применяется к существующей настройке во время воспроизведения
Статистика библиотеки
- Восстановить состояние воспроизведения
Продолжительность
Мини-альбом
Мини-альбомы
@@ -170,11 +165,8 @@
Дата добавления
Синглы
Предусиление ReplayGain
- Исключить
AAC
- Очистить состояние воспроизведения
- Музыка не будет загружена из указанных папок.
- Укажите, откуда надо загружать музыку
+ Укажите, откуда надо загружать музыку
%d кбит/с
Автоматическая перезагрузка
MPEG-1
@@ -189,9 +181,7 @@
Концертный альбом
Концертный
Мониторинг изменений в музыкальной библиотеке…
- Позиция сброшена
- Папки с музыкой
- Включить
+ Папки с музыкой
Альбом ремиксов
Концертный мини-альбом
Мини-альбом ремиксов
@@ -199,14 +189,9 @@
Концертный сингл
Ремикс сингл
Сборники
- Очистить ранее сохраненное состояние воспроизведения (если есть)
Перезагружать библиотеку при изменении (требует постоянное уведомление)
−%.1f дБ
Жанров загружено: %d
- Восстановить предыдущее состояние воспроизведения (если есть)
- Режим
- Музыка будет загружена только из указанных папок.
- Не удалось восстановить состояние воспроизведения
Нет трека
%d Гц
Исполнителей загружено: %d
@@ -240,8 +225,8 @@
Изменение по тегам
Изменения без тегов
Отключены
- Исходные (Быстрая загрузка)
- Повышенного качества (Медленная загрузка)
+ Исходные (Быстрая загрузка)
+ Повышенного качества (Медленная загрузка)
Сборник концертных записей
Пользовательское поведение панели воспроизведения
Пересканировать музыку
@@ -253,8 +238,6 @@
- %d исполнителей
- %d исполнителей
- Не удалось очистить состояние
- Не удалось сохранить состояние
Предупреждение: Использование этой настройки может привести к тому, что некоторые теги будут неправильно интерпретироваться как имеющие несколько значений. Вы можете решить эту проблему, добавив к нежелательным символам-разделителям обратную косую черту (\\).
%d выбрано
Вики
@@ -262,7 +245,7 @@
%1$s,%2$s
Играть жанр
Поведение
- Выравнивание громкости ReplayGain
+ Выравнивание громкости
Музыка
Изображения
Библиотека
@@ -271,15 +254,13 @@
Управляйте загрузкой музыки и изображений
Настройка звука и поведения при воспроизведении
Воспроизведение
- Папки
- Состояние воспроизведения
+ Папки
По убыванию
Плейлист
Плейлисты
Обложка плейлиста для %s
Игнорировать артикли при сортировке
Игнорировать такие слова, как «the», при сортировке по имени (лучше всего работает с англоязычной музыкой)
- Создать новый плейлист
Новый плейлист
Плейлист %d
Добавить в плейлист
@@ -337,4 +318,25 @@
Сделайте пожертвование проекту, чтобы ваше имя было добавлено сюда!
Оставлять воспроизведение/паузу во время пропуска или редактирования очереди
Запоминать паузу
+ Откл.
+ Запускать Auxio, используя ранее сохранённое состояние. если сохранённое состояние недоступно, все песни будут перетасованы. Воспроизведение начнётся немедленно.
+\n
+\nПредупреждение: будьте осторожны при управлении этой службой, если вы закроете её, а затем попытаетесь использовать снова, вы, вероятно, приведёте к сбою приложения.
+ Начать воспроизведение
+ Больше
+ Отзывы
+ Отправить электронное письмо
+ Выберите папки
+ MPEG-4, содержащий %s
+ Неизвестный альбом
+ Здесь появятся ваши исполнители.
+ Новая папка
+ Apple Lossless Audio Codec (ALAC)
+ Неизвестный
+ Здесь будут отображаться ваши плейлисты.
+ Ваши песни будут отображаться здесь.
+ Ваши альбомы будут отображаться здесь.
+ Здесь будут отображаться ваши жанры.
+ Экономия места
+ Сообщить о проблеме на GitHub
\ No newline at end of file
diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml
index 08fc56f5c..69638c1d6 100644
--- a/app/src/main/res/values-sl/strings.xml
+++ b/app/src/main/res/values-sl/strings.xml
@@ -5,7 +5,6 @@
Zbirke
Albumi
Zbirka remiksov
- Počisti stanje predvajanja
Pojdi na album
Slika izvajalca za %s
Smer
@@ -26,7 +25,6 @@
Prednost albumu
Predvajaj iz prikazanega elementa
Samodejno ponovno nalaganje
- Ni mogoče počistiti stanja
Podaljšane
Lastnosti pesmi
Spremenite način ponavljanja
@@ -48,15 +46,12 @@
Ko se predvaja iz podrobnosti elementa
Ponovno naloži glasbo
Remiksi
- Shrani stanje predvajanja
Opozorilo: Sprememba pred-ojačevalca na visoko pozitivno vrednost lahko privede do preseganja na nekaterih avdio posnetkih.
Ni datuma
Ponovno naloži glasbeno knjižnico, uporabi predpomnjene oznake, kadar je mogoče
Najdena ni bila nobena aplikacija, ki bi lahko opravila to nalogo
Prekliči
- Vključi
Seznam predvajanja %d
- Shrani trenutno stanje predvajanja zdaj
Preskoči na zadnjo pesem
Ponovno naloži glasbeno knjižnico vsakič, ko se zazna sprememba (zahteva vztrajno obvestilo)
@@ -79,13 +74,11 @@
Izvajalec
Pravilno razvrsti imena, ki se začnejo z številkami ali besedami, kot so \'the\' (najbolje deluje z angleško glasbo)
Zelenkasto modra
- Vztrajnost
Premešaj vse pesmi
Seznam predavanja ustvarjen
Celoten čas predvajanja: %s
- Ni mogoče shraniti stanja
Pavza ob ponavljanju
- Mape za glasbo
+ Mape za glasbo
Zapomni si naključno predvajanje
Pojdi na izvajalca
Naloženih pesmi: %d
@@ -100,7 +93,6 @@
Naloženih žanrov: %d
Se predvaja
Odstrani to pesem
- Stanje predvajanja shranjeno
Ni diska
Išči
Vedno začnite predvajati, ko se slušalke priključijo (morda ne deluje na vseh napravah)
@@ -109,13 +101,11 @@
Dodaj v čakalno vrsto
Pred-ojačevalnik ReplayGain
MPEG-1 Audio
- Ni mogoče obnoviti stanja
Spremenite temo in barve aplikacije
Poskusi znova
Prilagodi zvok in obnašanje predvajanja
Nadzorujte kako se glasba in slike nalagajo
Izgled in občutek
- Izključi
Matroska Audio
Začasna prekinitev ob ponavljanju
Predvajaj
@@ -126,15 +116,14 @@
Pred-ojačevalec se uporablja na obstoječi prilagoditvi med predvajanjem
Predvajaj iz albuma
Glasba
- Ta mapa ni podprta
- Obnovi prej shranjeno stanje predvajanja (če obstaja)
+ Ta mapa ni podprta
Razvil Alexander Capehart
- Odstrani mapo
+ Odstrani mapo
Kopirano
Nalaganje glasbe ni uspelo
Album
Ko se predvaja iz knjižnice
- Visoka kvaliteta
+ Visoka kvaliteta
Prilagoditev brez oznak
Dodaj na seznam predvajanja
Datum vnosa
@@ -144,7 +133,6 @@
Naslovnica albuma
Preimenuj
Plus (+)
- Stanje predvajanja obnovljeno
%d Izbrano
Neznan izvajatelj
Slike
@@ -154,7 +142,6 @@
- %d izvajalci
- %d izvajalcev
- Stanje predvajanja počiščeno
Prikaz
%1$s, %2$s
Ogg Audio
@@ -165,7 +152,7 @@
Ni pesmi
Podaljšano
Pesmi
- Mape
+ Mape
Prireži vse naslovnice albumov v razmerje 1:1
Prilagojeno dejanje na vrstici za predvajanje
Indigo modra
@@ -200,13 +187,10 @@
Naslovnica albuma za %s
V živo
Spremenite vidnost in vrstni red zavihkov knjižnice
- Počisti shranjeno stanje predvajanja (če obstaja)
Sortiraj po
Ogled
Ustavi predvajanje
Mežanica
- Glasba se bo nalagala samo iz map, ki jih dodate.
- Način
Remiks singla
Auxio potrebuje dovoljenje za branje vaše glasbene knjižnice
Tema
@@ -216,7 +200,7 @@
Premakni ta zavihek
Pesem
Slika žanra za %s
- Nastavitev virov za nalaganje glasbe
+ Nastavitev virov za nalaganje glasbe
DJ Miksi
Odstrani seznam predvajanja\?
Modra
@@ -224,7 +208,6 @@
Zaobljen način
Naloženih izvajalcev: %d
Zvok
- Glasba se ne bo nalagala iz the map, ki jih dodate.
Rdeča
Dinamično
Temno
@@ -232,12 +215,10 @@
Žanr
Predvajanje
Vredu
- Ustvari nov seznam predvajanja
Singl
Seznam predvajanja odstranjen
Dovoli
Predvajaj iz vseh pesmi
- Obnovi stanje predvajanja
Prilagoditev z oznakami
Predvajanje ob priključitvi slušalk
Vejica (,)
@@ -269,19 +250,19 @@
Padajoče
Podaljšan remiks
Podpičje (;)
- Hitro
+ Hitro
Seznam predvajanja preimenovan
Predvajaj ali začasno ustavi
Rjava
ReplayGain strategija
Izvorna koda
Predvajaj iz izvajalca
- Ni map
+ Ni map
Prilagoditev kontrol uporabniškega vmesnika in obnašanja
Hitrost vzorčenja
Čakalna vrsta
Ločila za več vrednosti
- ReplayGain Tehnologija
+ Normalizacija glasnosti
Singli
Pregled in nadzor predvajanja glasbe
Uporabite čisto črno temo
@@ -302,4 +283,31 @@
Limeta
Sodeloval pri
%d Hz
+ Izključeno
+ Ni mogoče izvoziti seznama predvajanja v to datoteko
+ Uvožen seznam predvajanja
+ Demo posnetki
+ Demo
+ ReplayGain Prilagoditev Posnetka
+ ReplayGain Prilagoditev Albuma
+ Avtor
+ Donacija
+ Podporniki
+ Seznam predvajanja uspešno uvožen
+ Seznam predvajanja uspešno izvožen
+ Donirajte projektu, da se vaše ime doda tukaj!
+ Ni albumov
+ Zapomni si pavzo
+ Predvajaj/pavza med preskakovanjem ali urejanjem čakalne vrste
+ Prazen seznam predvajanja
+ Uvozite seznam predvajanja
+ Ni mogoče uvoziti seznama predvajanja iz te datoteke
+ Uvozite
+ Pot
+ Uporabi poti kompatibilne z Windows
+ Izvozite seznam predvajanja
+ Stil poti
+ Izvozite
+ Absolutna
+ Relativna
\ No newline at end of file
diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml
new file mode 100644
index 000000000..de17dd795
--- /dev/null
+++ b/app/src/main/res/values-sq/strings.xml
@@ -0,0 +1,325 @@
+
+
+ Ngarkimi i muzikës
+ Po ngarkohet muzika
+ Një luajtës muzikor i thjeshtë dhe racional për android.
+ Kompilim Live
+ Monitorimi i bibliotekës muzikore
+ Përsëritje
+ Më shumë
+ Këngët
+ Këngë
+ Të gjitha këngët
+ Albumet
+ Albumi
+ Album live
+ Album remix
+ EPs
+ EP
+ EP Live
+ EP Remix
+ Këngët
+ Këng
+ Këng Live
+ Këng Remix
+ Kompilimet
+ Kompilime
+ Kompilim Remix
+ Soundtracks
+ Soundtrack
+ Mixtapes
+ Mixtape
+ Riemërto listën e këngëve
+ Lejoj
+ Demo
+ Demos
+ DJ Mixes
+ DJ Mix
+ Jetoni
+ Remixes
+ Shfaqet në
+ Artist
+ Artistët
+ Zhanri
+ Zhanret
+ Lista e dëgjimit
+ Listat e dëgjimit
+ List të rej
+ Lista e dëgjimit bosh
+ Lista e importuar
+ Importo
+ Importo listën e këngëve
+ Eksporto
+ Eksporto listën e këngëve
+ Riemërto
+ Fshije
+ Të fshihet lista e këngëve?
+ Modifiko
+ Kërko
+ Filtro
+ Të gjitha
+ Emri
+ Data
+ Kohëzgjatja
+ Tani po luan
+ Numri i këngëve
+ Disku
+ Pista
+ Data e shtimit
+ Rendit
+ Rendit sipas
+ Drejtimi
+ Në ngjitje
+ Në zbritje
+ Përziej
+ Radhë
+ Ekualizues
+ Luaj
+ Luaj tjetrën
+ Shto në radhë
+ Shto në listën e luajtjes
+ Shko te albumi
+ Shko te artisti
+ Shiko vetitë
+ Shiko
+ Shpërndaje
+ Cilësitë e këngës
+ Formati
+ Madhësia
+ Shpejtësia e bitit
+ Përziej
+ Përziej të gjitha
+ Fillo riprodhimin
+ OK
+ Anulo
+ Ruaj
+ Rivendos
+ Shto
+ Më shumë
+ Absolute
+ Relativ
+ Rreth
+ Versioni
+ Kodi burimor
+ Wiki
+ Licencat
+ Statistikat e bibliotekës
+ Përzgjedhja
+ Informacioni i gabimit
+ E kopjuar
+ Raportoni
+ Autori
+ Alexander Capehart
+ Dhuroni
+ Mbështetësit
+ Shikoni dhe kontrolloni riprodhimin e muzikës
+ Biblioteka juaj muzikore po ngarkohet…
+ Duke monitoruar bibliotekën tuaj të muzikës për ndryshime…
+ Lista e këngëve u krijua
+ Lista e këngëve u importua
+ Lista e muzikës e rinovuar
+ Lista e këngëve e eksportuar
+ Lista e muzikës u fshi
+ U shtuar në listën e këngëve
+ Dhuroni në projekt për të shtuar emrin tuaj këtu!
+ Kërkoni në bibliotekën tuaj…
+ Shtuar në radhë
+ Norma e mostrës
+ Rregullimi i gjurmës ReplayGain
+ Rregullimi i albumit ReplayGain
+ Rruga
+ Stili i rrugës
+ Përdorni shtigje të pajtueshme me Windows
+ Ndryshoni temën dhe ngjyrat e aplikacionit
+ Tema
+ Automatik
+ Nis Auxio duke përdorur gjendjen e ruajtur më parë. Nëse nuk ka një gjendje të ruajtur, të gjitha këngët do të luhen në mënyrë të rastësishme. Riprodhimi do të fillojë menjëherë. \n\nKUJDES: Kini kujdes kur kontrolloni këtë shërbim; nëse e mbyllni dhe pastaj përpiqeni ta përdorni përsëri, ka mundësi që aplikacioni të bllokohet.
+ Cilësimet
+ Shikoni dhe ndjeni
+ E ndritshme
+ E errët
+ Skema e ngjyrave
+ Tema e zezë
+ Përdorni një temë të errët në të zezë
+ Veprim i personalizuar i shiritit të riprodhimit
+ Veprim i personalizuar i njoftimit
+ Kalo te tjetra
+ Mënyra e përsëritjes
+ Sjellja
+ Kur luani nga biblioteka
+ Kur luani nga detajet e artikullit
+ Luaj nga artikulli i treguar
+ Luaj nga të gjitha këngët
+ Luaj nga albumi
+ Luaj nga artisti
+ Luaj nga zhanri
+ Luaj këngën vetë
+ prerje (/)
+ Plus (+)
+ Modaliteti i rrumbullakët
+ Personalizoje
+ shfaqja
+ Skedat e bibliotekës
+ Mbani mend përzierjen
+ Vazhdoni të aktivizoni përzierjen kur luani një këngë të re
+ Kontrolloni se si ngarkohen muzika dhe imazhet
+ Muzikë
+ Përjashto jo-muzikën
+ Injoroni skedarët audio që nuk janë muzikë, të tilla si podkastet
+ Ndarës me shumë vlera
+ Ampersand (&)
+ Kujdes: Përdorimi i këtij cilësimi mund të rezultojë në interpretimin e gabuar të disa etiketave si të kenë vlera të shumta. Ju mund ta zgjidhni këtë duke e paraqitur karakterin ndarës të padëshiruar me një backslash (\\).
+ Renditja inteligjente
+ Fshih bashkëpunëtorët
+ Imazhet
+ Fikur
+ Shpejt
+ Forco kopertinat e albumeve katrore
+ Pritini të gjitha kopertinat e albumeve në një raport pamjeje 1:1
+ Audio
+ Konfiguro sjelljen e zërit dhe riprodhimit
+ Riprodhimi
+ Luajtja automatike me kufje
+ Gjithmonë filloni të luani kur një kufje është e lidhur (mund të mos funksionojë në të gjitha pajisjet)
+ Ktheje prapa përpara se të kapërcesh prapa
+ Kthejeni prapa përpara se të kaloni te kënga e mëparshme
+ Pushoni në përsëritje
+ Pushoni kur një këngë përsëritet
+ Krijo një çështje në GitHub
+ Dërgo një email
+ Përshtypje
+ Kujto pushimin
+ Aktivizo qoshet e rrumbullakosura në elementë shtesë të ndërfaqes së përdoruesit (kërkon që kopertinat e albumit të jenë të rrumbullakosura)
+ Ndryshoni dukshmërinë dhe renditjen e skedave të bibliotekës
+ Personalizo kontrollet dhe sjelljen e UI
+ përmbajtja
+ Rimbushje automatike
+ Rifresko bibliotekën muzikore sa herë që ndryshon (kërkon njoftim të vazhdueshëm)
+ Konfiguro karakteret që tregojnë vlera të shumta etiketash
+ pikëpresje (;)
+ Presja (,)
+ Renditni saktë emrat që fillojnë me numra ose fjalë si \"the\" (funksionon më së miri me muzikën në anglisht).
+ Kopertinat e albumeve
+ Shfaq vetëm artistë që vlerësohen drejtpërdrejt në një album (funksionon më mirë në bibliotekat e etiketuara mirë)
+ Cilësi e lartë
+ Normalizimi i volumit
+ Mbajeni duke luajtur/ndërprerë kur kaloni ose redaktoni radhën
+ Strategjia ReplayGain
+ Fike
+ Prefero këngën
+ +%.1f dB
+ %d Hz
+
+ - %d album
+ - %d albume
+
+ Dosjet e muzikës
+ Kjo dosje nuk mbështetet
+ Ndryshoni mënyrën e përsëritjes
+ Hiq këtë këngë
+ Free Lossless Audio Codec (FLAC)
+ Gri
+ Dinamik
+ %1$s, %2$s
+ %d e zgjedhur
+ Duke redaktuar %s
+ Disc %d
+ Lista e këngëve %d
+
+ - %d këngë
+ - %d këngë
+
+
+ - %d artist
+ - %d artistë
+
+ Lëviz këtë skedë
+ Imazhi i listës së këngëve për %s
+ Nuk ka këngë
+ Prefero albumin
+ Prefero albumin nëse njëri është duke luajtur
+ ReplayGain pre-amp
+ Përforcuesi paraprak aplikohet në rregullimin ekzistues gjatë riprodhimit.
+ Rregullim me etiketa
+ Rregullim pa etiketa
+ Kujdes: Ndryshimi i pre-amplitudës në një vlerë të lartë pozitive mund të shkaktojë teprim në disa shtegtarë audio.
+ Biblioteka
+ Menaxho se nga ku duhet të ngarkohet muzika
+ Dosjet
+ Rifresko muzikën
+ Rifreskoni bibliotekën muzikore, duke përdorur etiketat e ruajtura kur është e mundur
+ Rikërkoni muzikën
+ Pastroni cache-n e etiketave dhe rifreskoni plotësisht bibliotekën muzikore (më ngadalë, por më të plotë)
+ Nuk u gjet muzikë
+ Ngarkimi i muzikës dështoi
+ Auxio ka nevojë për leje për të lexuar bibliotekën tuaj muzikore
+ Nuk mund të importoni një listë këngësh nga ky skedar
+ Nuk mund të eksportoni listën e këngëve në këtë skedar
+ Nuk u gjet asnjë aplikacion që mund ta përballojë këtë detyrë
+ Nuk ka dosje
+ Kënga %d
+ Luaj ose ndalo
+ Kaloni te kënga tjetër
+ Kaloni te kënga e fundit
+ Aktivizoni ose çaktivizoni përzierjen
+ Përziej të gjitha këngët
+ Ndalo riprodhimin
+ Lëviz këtë këngë
+ Hap radhën
+ Pastro kërkimin
+ Hiq dosjen
+ Ikona e Auxio
+ Kopertina e albumit
+ Kopertina e albumit për %s
+ Imazhi i artistit për %s
+ Imazhi i zhanrit për %s
+ Imazhi i përzgjedhjes
+ Artist i panjohur
+ Zhanër i panjohur
+ Nuk ka datë
+ Nuk ka disqe
+ Nuk ka këngë
+ Nuk ka albume
+ Nuk po luhet muzikë
+ MPEG-1 audio
+ MPEG-4 audio
+ Ogg audio
+ Matroska audio
+ Advanced Audio Coding (AAC)
+ Kuq
+ Rozë
+ Vjollcë
+ Vjollcë e thellë
+ Blu
+ Blu e thellë
+ Cian
+ Teal
+ Gjelbërt
+ Gjelbër e thellë
+ Gëlqere
+ Verdh
+ Portokall
+ Kafe
+ -%.1f dB
+ %d kbps
+ Duke ngarkuar bibliotekën tuaj muzikore... (%1$d/%2$d)
+ Këngët e ngarkuara: %d
+ Albumet e ngarkuara: %d
+ Artistët e ngarkuar: %d
+ Zhanret e ngarkuara: %d
+ Kohëzgjatja totale: %s
+ A dëshironi të fshini %s? Kjo nuk mund të anulohet.
+ Lejla
+ Zgjidh dosjet
+ Album i panjohur
+ Këngët e tua do të shfaqen këtu.
+ Albumet tuaja do të shfaqen këtu.
+ Artistët e tu do të shfaqen këtu.
+ Listat tuaja për luajtje do të shfaqen këtu.
+ Zhanret tuaja do të shfaqen këtu.
+ Dosje e re
+ Apple Lossless Audio Codec (ALAC)
+ E panjohur
+ Kurseni hapësirë
+ MPEG-4 që përmban %s
+
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index 8043ac526..f0c5a6b39 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -40,13 +40,13 @@
Datum tillagt
Stigande
Fallande
- Nu spelar
+ Spelas nu
Utjämnare
Spela
Blanda
Kö
Spela nästa
- Lägg till spellista
+ Lägg till i spellista
Gå till artist
Gå till album
Visa egenskaper
@@ -60,7 +60,6 @@
Okej
Avbryt
Spara
- Tillstånd återställt
Om
Källkod
Wiki
@@ -70,12 +69,12 @@
Övervakar ändringar i ditt musikbibliotek…
Tillagd i kö
Spellista skapad
- Tillagd till spellista
+ Tillagd i spellista
Sök i ditt musikbibliotek…
Inställningar
Utseende
Ändra färger och tema
- Automatisk
+ Automatiskt
Ljust
Svart tema
Runt läge
@@ -97,16 +96,14 @@
Alla
Disk
Sortera
- Lägg till kö
+ Lägg till i kö
Lägg till
- Tillstånd togs bort
Överföringskapacitet
Återställ
- Tillstånd sparat
Version
Bibliotekstatistik
Bytt namn på spellista
- Spellista tog bort
+ Spellistan togs bort
Alexander Capeheart
Tema
Mörkt
@@ -147,12 +144,11 @@
Komma (,)
Snedstreck (/)
Konfigurera tecken som separerar flera värden i taggar
- Varning: Denna inställning kan leda till att vissa taggar separeras felaktigt. För att åtgärda detta, prefixa oönskade separatortecken med ett backslash (\\).
+ Varning: Denna inställning kan leda till att vissa etiketter separeras felaktigt. För att åtgärda detta, prefixa oönskade separatortecken med ett omvänt snedstreck ().
Anpassa UI-kontroller och beteende
Av
Autouppspelning med hörlurar
Pausa när en låt upprepas
- Musik laddas inte från mapparna som ni lägger till.
Öppna kön
Dynamisk
Inlästa artister: %d
@@ -165,26 +161,19 @@
Konfigurera ljud- och uppspelningsbeteende
Spola tillbaka innan spår hoppar tillbaka
ReplayGain-strategi
- Rensa det tidigare sparade uppspelningsläget om det finns
- Återställ uppspelningsläge
-%.1f dB
Ta bort %s? Detta kan inte ångras.
Endast visa artister som är direkt krediterade på ett album (funkar bäst på välmärkta bibliotek)
Albumomslag
- Snabbt
+ Snabbt
Bibliotek
- Inkludera
Uppdatera musik
- Läs in musik på nytt, vid möjlighet med användning av cachade taggar
- Persistens
- Rensa uppspelningsläge
- Återställ det tidigare lagrade uppspelningsläget om det finns
- Misslyckades att spara uppspelningsläget
+ Läs in musik på nytt, vid möjlighet med användning av cachade etiketter
Blanda alla låtar
Rensa sökfrågan
- Ta bort mapp
+ Ta bort mapp
Genrebild för %s
- Bild spellista för %s
+ Spellistabild för %s
MPEG-1-ljud
MPEG-4-ljud
OGG-ljud
@@ -203,26 +192,21 @@
Uppspelning
Orange
Brun
- Alltid börja uppspelning när hörlurar kopplas till (kanske inte fungerar på alla enheter)
+ Påbörja alltid uppspelning när hörlurar kopplas till (kanske inte fungerar på alla enheter)
Pausa vid upprepning
ReplayGain försteg
- Justering utan taggar
- Musikmappar
- Varning: Om man ändrar förförstärkaren till ett högt positivt värde kan det leda till toppning på vissa ljudspår.
- Hantera vart musik läses in ifrån
- Mappar
- Modus
- Utesluta
- Musik laddas endast från mapparna som ni lägger till.
- Spara aktuellt uppspelningsläge
+ Justering utan etiketter
+ Musikmappar
+ Varning: Att ändra förförstärkaren till ett högt positivt värde kan leda till ett förhöjt ljudtryck på vissa ljudspår.
+ Hantera var musik läses in ifrån
+ Mappar
Skanna om musik
- Rensa tagbiblioteket och ladda komplett om musikbiblioteket (långsammare, men mer komplett)
- Ingen musik på gång
- Läsa in musik misslyckades
+ Rensa etikettbiblioteket och ladda komplett om musikbiblioteket (långsammare, men mer komplett)
+ Ingen musik hittades
+ Musikinläsning misslyckades
Auxio måste ges behörighet för att läsa in ditt musikbibliotek
Ingen lämplig app kunde hittas
- Denna mapp stöds inte
- Misslyckades att återställa uppspelningsläget
+ Denna mapp stöds inte
Spår %d
Spela eller pausa
Flytta detta spår
@@ -256,14 +240,12 @@
Inlästa album: %d
Inlästa genrer: %d
Spela endast vald låt
- Hög kvalitet
+ Hög kvalitet
Tvinga fyrkantiga skivomslag
Beskär alla albumomslag till ett 1:1 sidförhållande
Spola tillbaka innan att hoppa till föregående låt
- Justering med taggar
- Inga mappar
- Misslyckades att rensa uppspelningsläget
- Skapa en ny spellista
+ Justering med etiketter
+ Inga mappar
Stoppa uppspelning
Ta bort låt
Auxio-ikon
@@ -273,7 +255,6 @@
Mörklila
Indigo
Skiva %d
- Spara uppspelningsläge
Hoppa till nästa låt
Hoppa till sista låt
Ändra upprepningsläge
@@ -285,7 +266,7 @@
Rosa
Laddar ditt musikbibliotek… (%1$d/%2$d)
Ampersand (&)
- ReplayGain
+ Volymnormalisering
Föredra spår
Föredra album
Föredra album om ett album spelar
@@ -299,16 +280,16 @@
Riktning
Demo
Demos
- Upphovsperson
+ Utvecklare
Spellistan har importerats
Spellistan har exporterats
- Skänk projektet ett bidrag så lägger vi till ditt namn här!
+ Bidra till projektet så läggs ditt namn till här!
Kom ihåg pausat läge
Fortsätt uppspelning/pausat läge vid spårbyte och listredigering
Kunde inte importera spellistan till denna fil
Tom spellista
Importera spellista
- Vy
+ Visa
Sökväg
ReplayGain Spårbaserad Volymjustering
ReplayGain Albumbaserad Volymjustering
@@ -322,4 +303,23 @@
Relativ
Använd Windowskompatibla sökvägar
Inga album
-
\ No newline at end of file
+ Av
+ Välj mappar
+ Skapa ett ärende på GitHub
+ Mer
+ Okänt album
+ Ny mapp
+ Okänt
+ Skicka ett mejl
+ Feedback
+ Dina album kommer visas här.
+ Dina låtar kommer visas här.
+ Dina artister kommer visas här.
+ Dina spellistor kommer visas här.
+ Dina genrer kommer visas här.
+ Apples förlustfria ljudkodek (ALAC)
+ Påbörja uppspelning
+ Spara utrymme
+ MPEG-4 innehållande %s
+ Påbörjar Auxio med det tidigare sparade tillståndet. Om inget sparad tillstånd är tillgängligt kommer alla låtar blandas. Uppspelning börjar omedelbart. \n\nOBS: Var försiktig vid användning av denna tjänst, om du stänger ner den och sedan försöker använda den igen kommer du troligen få appen att krascha.
+
diff --git a/app/src/main/res/values-sw600dp/styles_ui.xml b/app/src/main/res/values-sw600dp/styles_ui.xml
index 3a3175896..1d55cdcbb 100644
--- a/app/src/main/res/values-sw600dp/styles_ui.xml
+++ b/app/src/main/res/values-sw600dp/styles_ui.xml
@@ -1,10 +1,5 @@
-
-
-
diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml
new file mode 100644
index 000000000..bb6179527
--- /dev/null
+++ b/app/src/main/res/values-ta/strings.xml
@@ -0,0 +1,314 @@
+
+
+ மிக்ச்டேப்
+ டி.சே கலவைகள்
+ வாழ
+ ஏற்றுமதி பிளேலிச்ட்
+ காலம்
+ பாடல் எண்ணிக்கை
+ வட்டு
+ மின்தடம்
+ மீட்டமை
+ கூட்டு
+ அமைப்புகள்
+ பாருங்கள் மற்றும் உணருங்கள்
+ இருண்ட
+ இடைமுகம் கட்டுப்பாடுகள் மற்றும் நடத்தை தனிப்பயனாக்கவும்
+ தனிப்பயன் அறிவிப்பு நடவடிக்கை
+ அடுத்து செல்லவும்
+ மீண்டும் பயன்முறை
+ பல மதிப்பு பிரிப்பான்கள்
+ பிளச் (+)
+ ஆம்பர்சண்ட் (&)
+ ஒத்துழைப்பாளர்களை மறைக்கவும்
+ படங்கள்
+ ஆடியோ
+ குறிச்சொல் தற்காலிக சேமிப்பை அழித்து, இசை நூலகத்தை முழுமையாக மீண்டும் ஏற்றவும் (மெதுவாக, ஆனால் இன்னும் முழுமையானது)
+ தேதி இல்லை
+ வட்டு இல்லை
+ தடமில்லை
+ பாடல்கள் இல்லை
+ ஆரஞ்சு
+ பழுப்பு
+ சாம்பல்
+ மாறும்
+ %1$s, %2$s
+ %d தேர்ந்தெடுக்கப்பட்டது
+ திருத்துதல் %s
+ வட்டு %d
+ பிளேலிச்ட் %d
+ +%.1f டெசிபெல்
+ பாடல்கள் ஏற்றப்பட்டன: %d
+
+ - %d ஆல்பம்
+ - %d ஆல்பங்கள்
+
+
+ - %d கலைஞர்
+ - %d கலைஞர்கள்
+
+ ஆண்ட்ராய்டு க்கான எளிய, பகுத்தறிவு இசை வீரர்.
+ இசை ஏற்றுதல்
+ இசையை ஏற்றுகிறது
+ கண்காணிப்பு இசை நூலகம்
+ மீண்டும் முயற்சிக்கவும்
+ கோப்புறைகளைத் தேர்ந்தெடுங்கள்
+ மேலும்
+ மானியம்
+ பாடல்கள்
+ பாடல்
+ அனைத்து பாடல்களும்
+ ஆல்பம்
+ ஆல்பம்
+ நேரடி ஆல்பம்
+ ரீமிக்ச் ஆல்பம்
+ இபிஎச்
+ ஈ.பி.
+ ஒற்றையர்
+ ஒற்றை
+ ஒற்றை வாழ
+ ரீமிக்ச் ஒற்றை
+ தொகுப்புகள்
+ தொகுப்பு
+ ரீமிக்ச் ஈபி
+ நேரடி தொகுப்பு
+ ரீமிக்ச் தொகுப்பு
+ ஒலிப்பதிவுகள்
+ ஒலிப்பதிவு
+ மிக்ச்டேப்ச்
+ டெமோ
+ டெமோச்
+ டி.சே கலவை
+ ரீமிக்ச்
+ தோன்றும்
+ கலைஞர்
+ கலைஞர்கள்
+ வகை
+ வகைகள்
+ பிளேலிச்ட்
+ பிளேலிச்ட்கள்
+ புதிய பிளேலிச்ட்
+ வெற்று பிளேலிச்ட்
+ இறக்குமதி செய்யப்பட்ட பிளேலிச்ட்
+ இறக்குமதி
+ பிளேலிச்ட்டை இறக்குமதி செய்யுங்கள்
+ ஏற்றுமதி
+ மறுபெயரிடுங்கள்
+ பிளேலிச்ட்டை மறுபெயரிடுங்கள்
+ நீக்கு
+ பிளேலிச்ட்டை நீக்கவா?
+ தொகு
+ தேடல்
+ வடிப்பி
+ அனைத்தும்
+ பெயர்
+ திகதி
+ தேதி சேர்க்கப்பட்டது
+ வரிசைப்படுத்து
+ வரிசைப்படுத்தவும்
+ திசை
+ ஏறுதல்
+ இறங்கு
+ இப்போது விளையாடுகிறது
+ சமநிலைப்படுத்தி
+ விளையாடுங்கள்
+ கலக்கு
+ வரிசை
+ அடுத்து விளையாடுங்கள்
+ வரிசையில் சேர்க்கவும்
+ பிளேலிச்ட்டில் சேர்க்கவும்
+ கலைஞரிடம் செல்லுங்கள்
+ ஆல்பத்திற்குச் செல்லுங்கள்
+ பண்புகளைக் காண்க
+ பார்வை
+ பங்கு
+ பாடல் பண்புகள்
+ பாதை
+ வடிவம்
+ அளவு
+ துகள் வீதம்
+ மாதிரி வீதம்
+ Replaygain மின்தடம் சரிசெய்தல்
+ மறுபதிப்பு ஆல்பம் சரிசெய்தல்
+ கலக்கு
+ அனைத்தையும் மாற்றவும்
+ பிளேபேக்கைத் தொடங்குங்கள்
+ சரி
+ ரத்துசெய்
+ சேமி
+ மேலும்
+ பாதை நடை
+ தனி, சார்பிலா
+ உறவினர்
+ விண்டோச்-இணக்கமான பாதைகளைப் பயன்படுத்தவும்
+ பற்றி
+ பதிப்பு
+ மூலக் குறியீடு
+ விக்கி
+ உரிமங்கள்
+ நூலக புள்ளிவிவரங்கள்
+ தேர்வு
+ பிழை செய்தி
+ நகலெடுக்கப்பட்டது
+ அறிக்கை
+ நூலாசிரியர்
+ அலெக்சாண்டர் கேப்ஆர்ட்
+ கருத்து
+ கிதுபில் ஒரு சிக்கலை உருவாக்குங்கள்
+ மின்னஞ்சல் அனுப்புங்கள்
+ நன்கொடை
+ ஆதரவாளர்கள்
+ இசை பின்னணியைக் காணவும் கட்டுப்படுத்தவும்
+ உங்கள் இசை நூலகத்தை ஏற்றுகிறது…
+ மாற்றங்களுக்காக உங்கள் இசை நூலகத்தை கண்காணித்தல்…
+ வரிசையில் சேர்க்கப்பட்டது
+ பிளேலிச்ட் உருவாக்கப்பட்டது
+ பிளேலிச்ட் இறக்குமதி செய்யப்பட்டது
+ பிளேலிச்ட் மறுபெயரிடப்பட்டது
+ பிளேலிச்ட் ஏற்றுமதி செய்யப்பட்டது
+ பிளேலிச்ட் நீக்கப்பட்டது
+ பிளேலிச்ட்டில் சேர்க்கப்பட்டது
+ உங்கள் பெயரை இங்கே சேர்க்க திட்டத்திற்கு நன்கொடை அளிக்கவும்!
+ உங்கள் நூலகத்தைத் தேடுங்கள்…
+ முன்னர் சேமிக்கப்பட்ட நிலையைப் பயன்படுத்தி ஆக்சியோவைத் தொடங்குகிறது. சேமிக்கப்பட்ட நிலை எதுவும் கிடைக்கவில்லை என்றால், எல்லா பாடல்களும் மாற்றப்படும். பிளேபேக் உடனடியாக தொடங்கும்.\n\n எச்சரிக்கை: இந்த சேவையை கட்டுப்படுத்துவதில் கவனமாக இருங்கள், நீங்கள் அதை மூடிவிட்டு அதை மீண்டும் பயன்படுத்த முயற்சித்தால், நீங்கள் பயன்பாட்டை செயலிழக்கச் செய்வீர்கள்.
+ பயன்பாட்டின் கருப்பொருள் மற்றும் வண்ணங்களை மாற்றவும்
+ கருப்பொருள்
+ தானியங்கி
+ ஒளி
+ வண்ணத் திட்டம்
+ கருப்பு கருப்பொருள்
+ தூய-கருப்பு இருண்ட கருப்பொருளைப் பயன்படுத்தவும்
+ சுற்று பயன்முறை
+ கூடுதல் இடைமுகம் கூறுகளில் வட்டமான மூலைகளை இயக்கவும் (ஆல்பம் கவர்கள் வட்டமிட வேண்டும்)
+ தனிப்பயனாக்கு
+ காட்சி
+ நூலக தாவல்கள்
+ நூலக தாவல்களின் தெரிவுநிலை மற்றும் வரிசையை மாற்றவும்
+ தனிப்பயன் பின்னணி பார் நடவடிக்கை
+ நடத்தை
+ நூலகத்திலிருந்து விளையாடும்போது
+ உருப்படி விவரங்களிலிருந்து விளையாடும்போது
+ காட்டப்பட்ட உருப்படியிலிருந்து விளையாடுங்கள்
+ எல்லா பாடல்களிலிருந்தும் விளையாடுங்கள்
+ ஆல்பத்திலிருந்து விளையாடுங்கள்
+ கலைஞரிடமிருந்து விளையாடுங்கள்
+ வகையிலிருந்து விளையாடுங்கள்
+ பாடலை தானே வாசிக்கவும்
+ கலக்கு நினைவில் கொள்ளுங்கள்
+ புதிய பாடலை இசைக்கும்போது கலக்கிக் கொள்ளுங்கள்
+ உள்ளடக்கம்
+ இசை மற்றும் படங்கள் எவ்வாறு ஏற்றப்படுகின்றன என்பதைக் கட்டுப்படுத்தவும்
+ இசை
+ தானியங்கி மறுஏற்றம்
+ இசை நூலகத்தை மாற்றும்போதெல்லாம் மீண்டும் ஏற்றவும் (தொடர்ச்சியான அறிவிப்பு தேவை)
+ இசை அல்லாதவை
+ பாட்காச்ட்கள் போன்ற இசை இல்லாத ஆடியோ கோப்புகளை புறக்கணிக்கவும்
+ பல குறிச்சொல் மதிப்புகளைக் குறிக்கும் எழுத்துக்களை உள்ளமைக்கவும்
+ கமா (,)
+ அரைப்புள்ளி (;)
+ (பழம்) குறைத்தல்
+ எச்சரிக்கை: இந்த அமைப்பைப் பயன்படுத்துவதால் சில குறிச்சொற்கள் பல மதிப்புகளைக் கொண்டிருப்பதாக தவறாக விளக்கலாம். பின்சாய்வுக்கோடான (\\) மூலம் தேவையற்ற பிரிப்பான் எழுத்துக்களை முன்னொட்டு செய்வதன் மூலம் இதை நீங்கள் தீர்க்கலாம்.
+ நுண்ணறிவு வரிசையாக்கம்
+ எண்கள் அல்லது \"தி\" போன்ற சொற்களுடன் தொடங்கும் பெயர்களை சரியாக வரிசைப்படுத்தவும் (ஆங்கில மொழி இசையுடன் சிறப்பாக செயல்படுகிறது)
+ ஒரு ஆல்பத்தில் நேரடியாக வரவு வைக்கப்படும் கலைஞர்களை மட்டுமே காண்பி (நன்கு குறியிடப்பட்ட நூலகங்களில் சிறப்பாக செயல்படுகிறது)
+ ஆல்பம் கவர்கள்
+ அணை
+ வேகமாக
+ உயர் தகுதி
+ படை சதுர ஆல்பம் கவர்கள்
+ அனைத்து ஆல்பத்தையும் 1: 1 விகித விகிதத்திற்கு பயிர்
+ ஒலி மற்றும் பின்னணி நடத்தை உள்ளமைக்கவும்
+ பின்னணி
+ எட்செட் ஆட்டோபிளே
+ எட்செட் இணைக்கப்படும்போது எப்போதும் விளையாடத் தொடங்குங்கள் (எல்லா சாதனங்களிலும் வேலை செய்யாது)
+ பின்வாங்குவதற்கு முன் முன்னாடி
+ முந்தைய பாடலைத் தவிர்ப்பதற்கு முன் முன்னாடி
+ ஒரு பாடல் மீண்டும் நிகழும்போது இடைநிறுத்துங்கள்
+ இடைநிறுத்தம் நினைவில் கொள்ளுங்கள்
+ வரிசையைத் தவிர்க்கும்போது அல்லது திருத்தும்போது விளையாட/இடைநிறுத்தப்படுங்கள்
+ தொகுதி இயல்பாக்கம்
+ Replaygain மூலோபாயம்
+ அணை
+ பாதையை விரும்புகிறேன்
+ ஆல்பத்தை விரும்புங்கள்
+ ஒருவர் விளையாடினால் ஆல்பத்தை விரும்புங்கள்
+ Replaygain pre-amp
+ பிளேபேக்கின் போது இருக்கும் சரிசெய்தலுக்கு முன் ஆம்ப் பயன்படுத்தப்படுகிறது
+ குறிச்சொற்களுடன் சரிசெய்தல்
+ குறிச்சொற்கள் இல்லாமல் சரிசெய்தல்
+ எச்சரிக்கை: முன் ஆம்பை அதிக நேர்மறையான மதிப்புக்கு மாற்றுவது சில ஆடியோ தடங்களில் உயர்ந்தது.
+ நூலகம்
+ இசை கோப்புறைகள்
+ இசையை எங்கிருந்து ஏற்ற வேண்டும் என்பதை நிர்வகிக்கவும்
+ கோப்புறைகள்
+ இசையைப் புதுப்பிக்கவும்
+ மீண்டும்இயக்கையில் இடைநிறுத்தம்
+ முடிந்தவரை தற்காலிக சேமிப்பு குறிச்சொற்களைப் பயன்படுத்தி இசை நூலகத்தை மீண்டும் ஏற்றவும்
+ ரெசான் மியூசிக்
+ இசை எதுவும் கிடைக்கவில்லை
+ இசை ஏற்றுதல் தோல்வியடைந்தது
+ உங்கள் இசை நூலகத்தைப் படிக்க ஆக்சியோவுக்கு இசைவு தேவை
+ இந்த கோப்பிலிருந்து ஒரு பிளேலிச்ட்டை இறக்குமதி செய்ய முடியவில்லை
+ இந்த கோப்பில் பிளேலிச்ட்டை ஏற்றுமதி செய்ய முடியவில்லை
+ இந்த பணியைக் கையாளக்கூடிய எந்த பயன்பாடும் கிடைக்கவில்லை
+ கோப்புறைகள் இல்லை
+ இந்த கோப்புறை ஆதரிக்கப்படவில்லை
+ ட்ராக் %d
+ விளையாடுங்கள் அல்லது இடைநிறுத்தம்
+ அடுத்த பாடலுக்குச் செல்லுங்கள்
+ கடைசி பாடலுக்குச் செல்லுங்கள்
+ மீண்டும் பயன்முறையை மாற்றவும்
+ கலக்கலை ஆன் அல்லது ஆஃப் செய்யுங்கள்
+ எல்லா பாடல்களையும் மாற்றவும்
+ பிளேபேக்கை நிறுத்துங்கள்
+ இந்த பாடலை அகற்று
+ இந்த பாடலை நகர்த்தவும்
+ வரிசையைத் திறக்கவும்
+ இந்த தாவலை நகர்த்தவும்
+ தேடல் வினவலை அழிக்கவும்
+ கோப்புறையை அகற்று
+ ஆக்சியோ படவுரு
+ ஆல்பம் கவர்
+ %s ஆல்பம் கவர்
+ %s க்கான கலைஞர் படம்
+ %s க்கான வகை படம்
+ %s க்கான பிளேலிச்ட் படம்
+ தேர்வு படம்
+ தெரியாத கலைஞர்
+ தெரியாத வகை
+ ஆல்பம்
+ இசை இல்லை
+ MPEG-1 ஆடியோ
+ MPEG-4 ஆடியோ
+ OGG ஆடியோ
+ திருமண ஆடியோ
+ மேம்பட்ட ஆடியோ குறியீட்டு முறை (AAC)
+ இலவச இழப்பு இல்லாத ஆடியோ கோடெக் (FLAC)
+ சிவப்பு
+ இளஞ்சிவப்பு
+ ஊதா
+ ஆழமான ஊதா
+ இண்டிகோ
+ நீலம்
+ ஆழமான நீலம்
+ சியான்
+ டீல்
+ பச்சை
+ ஆழமான பச்சை
+ சுண்ணாம்பு
+ மஞ்சள்
+ %d kbps
+ %d hz
+ உங்கள் இசை நூலகத்தை ஏற்றுகிறது… (%1$d/%2$d)
+ %s ஐ நீக்கவா? இதை செயல்தவிர்க்க முடியாது.
+ ஆல்பங்கள் ஏற்றப்பட்டன: %d
+ கலைஞர்கள் ஏற்றப்பட்டனர்: %d
+ வகைகள் ஏற்றப்பட்டவை: %d
+ மொத்த காலம்: %s
+
+ - %d பாடல்
+ - %d பாடல்கள்
+
+ நிகழ்நிலை ஈபி
+ -%.1f டெசிபெல்
+
\ No newline at end of file
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 16eacfc8a..d04d0cc05 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -77,7 +77,6 @@
Albüm
Yıl
Süre
- Durum kaydedildi
Alexander Capehart tarafından geliştirildi
Siyah tema
Kitaplık istatistikleri
@@ -93,8 +92,7 @@
Gösterilen öğeden çal
Tüm şarkılardan çal
Albümden çal
- Müzik klasörleri
- Müzik yalnızca eklediğiniz klasörlerden yüklenecektir.
+ Müzik klasörleri
%s Albümünün kapağı
%s Sanatçısının resmi
Parça yok
@@ -145,14 +143,12 @@
Önceki şarkıya atlamadan önce geri sar
Bir şarkı tekrarlandığında duraklat
İçerik
- Çalma durumunu kaydet
- Mevcut çalma durumunu şimdi kaydet
Karıştırmayı hatırla
Yeni bir şarkı çalarken karışık çalmayı açık tut
Müziği yenile
Mümkün olduğunda önbelleğe alınmış etiketleri kullanarak müzik kitaplığını yeniden yükleyin
- Klasör yok
- Bu klasör desteklenmiyor
+ Klasör yok
+ Bu klasör desteklenmiyor
Sonraki şarkıya geç
Son şarkıya geç
Tekrarlama modunu değiştir
@@ -160,27 +156,19 @@
Auxio\'nun müzik kitaplığınızı görüntülemek için izne ihtiyacı var
Bu görevi yerine getirebilecek bir uygulama bulunamadı
Karıştırmayı açın veya kapatın
- Klasörü kaldır
+ Klasörü kaldır
Bütün şarkıları karıştır
Auxio simgesi
Bu sekmeyi taşı
Albüm kapağı
Arama sorgusunu temizle
- Müziğin nereden yükleneceğini yönetin
- Mod
- Hariç tut
- Eklediğiniz klasörlerden müzik yüklenmeyecek.
- Dahil et
+ Müziğin nereden yükleneceğini yönetin
Tarih yok
Koyu mavi
Camgöbeği
Deniz mavisi
Ek arayüz öğelerinde yuvarlatılmış köşeleri etkinleştirir (Albüm kapaklarının yuvarlatılmış olmasını gerektirir)
- Durum geri yüklendi
- Önceden kayıtlı çalma durumunu geri getir (varsa)
Yuvarlak mod
- Çalma durumunu eski haline getir
- Durum geri getirelemedi
Tekrarda duraklat
Müzik yükleniyor
Müzik kitaplığı denetleniyor
@@ -209,18 +197,15 @@
EP\'ler
EP
Karışık kasetler
- Durum temizlendi
Remiksler
Film Müzikleri
Film Müziği
- Yüksek kaliteli
+ Yüksek kaliteli
Katılımcaları gizleyin
Yalnızca bir albümde doğrudan adı geçen sanatçıları gösterin (iyi etiketlenmiş kütüphanelerde en iyi sonucu verir)
Albüm kapakları
Kapalı
- Hızlı
- Çalma durumunu temizle
- Önceki kayıtlı çalma durumunu temizle (varsa)
+ Hızlı
%d Seçili
- %d sanatçı
@@ -235,7 +220,6 @@
Noktalı virgül (;)
Artı (+)
Ve (&)
- Durum kaydedilemedi
Çalmayı durdur
Viki
Müzikleri yeniden tara
@@ -248,8 +232,7 @@
Podcast\'ler gibi müzik olmayan ses dosyalarını yok say
Uyarı: Bu ayarın kullanılması bazı etiketlerin yanlışlıkla birden fazla değere sahip olarak yorumlanmasına neden olabilir. Bunu, istenmeyen ayırıcı karakterlerin önüne ters eğik çizgi (\\) koyarak çözebilirsiniz.
Müzik olmayanları hariç tut
- Durum temizlenemedi
- ReplayGain stratejisi
+ ReplayGain yöntemi
Bu şarkıyı kuyrukta taşı
%1$s, %2$s
Müzik ve görüntülerin nasıl yükleneceğini denetleyin
@@ -258,19 +241,17 @@
Sesi ve oynatma davranışını yapılandırın
Oynatma
Kütüphane
- Kalıcılık
Azalan
Uygulamanın temasını ve renklerini değiştirin
- Klasörler
+ Klasörler
Arayüz kontrollerini ve davranışını özelleştirin
Davranış
- Ses yüksekliği dengesi ReplayGain
+ Ses normaleştirmesi
%s için oynatma listesi resmi
oynatma listesi
çalma listeleri
Sıralama yaparken makaleleri yoksay
Ada göre sıralarken \"the\" gibi kelimeleri yok sayın (en iyi ingilizce müzikle çalışır)
- Yeni bir oynatma listesi oluştur
Yeni Oynatma Listesi
Sil
Yeniden Adlandır
@@ -302,4 +283,49 @@
Çalma listesi yeniden adlandırıldı
%s silinsin mi\? Geri alınamaz.
Üzerinde görünür
+ Albüm yok
+ Klasör şeç
+ Oynatma listesini dışa aktar
+ Denemeler
+ ReplayGain albüm ayarlaması
+ Dosya yolu biçimi
+ Yakın
+ Kesin
+ Oynatma listesi içe aktarıldı
+ Oynatma listesi dışa aktarıldı
+ Destekçiler
+ İsminizin buraya eklenmesi için projeye bağış yapabilirsiniz!
+ Durmayı hatırla
+ Şarkı durduğunda sonraki şarkılara geçildiği zaman ve ya şarkı kuyruğunu düzenlerken durmaya devam et
+ Kapalı
+ Bu dosyadan bir oynatma listesi yüklenemiyor
+ Bilinmeyen biçim
+ İçe aktarılmış oynatma listesi
+ Bilimeyen albüm
+ Deneme
+ Albümleriniz burada gösterilecek.
+ Oynatma listeleriniz burada gösterilecek.
+ Türleriniz burada gösterilecek.
+ ReplayGain parça ayarlaması
+ Yazar
+ Bağış yap
+ Oynatma listesi aktar
+ Boş oynatma listesi
+ Dosya yolu
+ Yeni klasör
+ Dışa aktar
+ İçe aktar
+ Daha fazla
+ Sanatçılarınız burada gösterilecek.
+ Şarkılarınız burada gösterilecek.
+ Oynatmayı başlat
+ Windows uyumlu yollar kullan
+ Auxio\'yu önceden kaydedilen durumla başlatır. Eğer kayıtlı bir durum yoksa bütün şarkılar hemen karışık olarak çalmaya başlanacak.\n\nUYARI: Bu servisi kullanırken dikkatli olun. Kapatırsanız ve sonra tekrar kullanmaya çalışırsanız muhtemelen uygulamayı çökertirsiniz.
+ Az alan kullan
+ MPEG-4 %s içeriyor
+ Apple Kayıpsız Ses Kodeği (ALAC)
+ Geri bildirim
+ E-posta yolla
+ Github\'da rapor aç
+ Bu dosyaya bir oynatma listesi aktarılamıyor
\ No newline at end of file
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 1429775f1..0d889edf5 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -84,9 +84,8 @@
Приховати співавторів
Вимкнено
Перейти до наступної
- Швидкі
- Вкажіть звідки слід завантажувати музику
- Виключити
+ Швидкі
+ Вкажіть звідки слід завантажувати музику
Загальна тривалість: %s
Мікстейпи
Виконавець
@@ -100,11 +99,11 @@
Ставити на паузу при повторенні пісні
Дата додавання
Частота дискретизації
- Висока якість
+ Висока якість
Оновити музику
Ремікси
Мікстейп
- Папки з музикою
+ Папки з музикою
Скинути
Чорна тема
Зміна видимості та порядку вкладок бібліотеки
@@ -117,7 +116,6 @@
Переглянути властивості
Ігнорувати аудіо файли які не являються музикою, наприклад, подкасти
Виключити інші звукові файли
- Включити
Завантажено альбомів: %d
Концертна збірка
Мініальбом
@@ -137,12 +135,10 @@
Вкладки бібліотеки
Автовідтворення в навушниках
Режим повторення
- Режим
Попередній підсилювач ReplayGain
Відтворити альбом
При відтворенні з бібліотеки
Віддавати перевагу альбому, якщо він відтворюється
- Стан відтворення очищено
Використовувати повністю чорну тему
Показувати лише тих виконавців, які безпосередньо зазначені в альбомі (найкраще працює в добре позначених бібліотеках)
Увага: Встановлення високих позитивних значень попереднього підсилювача може призвести до спотворення звуку в деяких піснях.
@@ -150,13 +146,10 @@
Очистити кеш тегів і повністю перезавантажити музичну бібліотеку (повільніше, але ефективніше)
Автоматичне перезавантаження
Перезавантажувати бібліотеку при виявленні змін (потрібне постійне сповіщення)
- Музика не буде завантажена з вибраних папок.
Налаштуйте символи, які позначають кілька значень тегів
- Стан відтворення збережено
За альбомом
За піснею
Зміст
- Очистити раніше збережений стан відтворення (якщо є)
Відстеження змін в музичній бібліотеці…
Власна дія для панелі відтворення
Регулювання без тегів
@@ -165,20 +158,13 @@
Відтворити виконавця
Відтворити жанр
Перемотати назад перед відтворенням попередньої пісні
- Зберегти поточний стан відтворення
Пересканувати музику
Попередження: Використання цього параметра може призвести до того, що деякі теги будуть неправильно інтерпретовані як такі, що мають кілька значень. Ви можете вирішити це, додавши перед небажаними символами-роздільниками зворотну скісну риску (\\).
Кнопка в сповіщенні
Багатозначні роздільники
- Музика буде завантажена тільки з вибраних папок.
- Відновити раніше збережений стан відтворення (якщо є)
Регулювання на основі тегів
Налаштування ReplayGain
- Зберегти стан відтворення
- Очистити стан відтворення
- Відновити стан відтворення
Пісня
- Стан відтворення відновлено
Перегляд і керування відтворенням музики
Перемотайте на початок пісні перед відтворенням попередньої
Увімкнути заокруглені кути на додаткових елементах інтерфейсу (потрібно заокруглення обкладинок альбомів)
@@ -189,12 +175,11 @@
Плюс (+)
Кома (,)
Крапка з комою (;)
- Ця папка не підтримується
+ Ця папка не підтримується
Невідомий виконавець
Лаймовий
- Амперсанд (&)
- Немає папок
- Не вдалось відновити статус відтворення
+ Амперсанд &
+ Немає папок
Перейти до наступної пісні
Ввімкніть або вимкніть перемішування
Зупинити відтворення
@@ -240,8 +225,6 @@
Дата відсутня
Немає пісні
Музика не грає
- Не вдалось очистити статус відтворення
- Не вдалось зберегти статус відтворення
Перейти до попередньої пісні
Червоний
Звук MPEG-4
@@ -253,7 +236,7 @@
Сірий
Диск %d
Не вдалося завантажити музику
- Видалити папку
+ Видалити папку
Розширене кодування звуку (AAC)
Звук Matroska
%1$s, %2$s
@@ -265,18 +248,16 @@
Музика
Зображення
Відтворення
- Вирівнювання гучності (ReplayGain)
+ Вирівнювання гучності
Бібліотека
- Стан відтворення
Налаштуйте звук і поведінку при відтворенні
- Папки
+ Папки
За спаданням
Зображення списку відтворення для %s
Список відтворення
Списки відтворення
Інтелектуальне сортування
Ігнорування таких слів, як \"the\", або цифр під час сортування за назвою (найкраще працює з англомовною музикою)
- Створити новий список відтворення
Новий список відтворення
Список відтворення %d
Додати до списку відтворення
@@ -334,4 +315,25 @@
Автор
Залишати відтворення/паузу під час пропуску або редагування черги
Запам\'ятовувати паузу
+ Вимкнено
+ Почати відтворення
+ Запускати auxio, використовуючи раніше збережений стан. Якщо збережений стан недоступний, усі пісні будуть перетасовані. відтворення розпочнеться негайно.
+\n
+\nПопередження: будьте обережні при керуванні цією службою, якщо ви закриєте її, а потім спробуєте використовувати знову, ви, ймовірно, призведе до збою застосунка.
+ Більше
+ Виберіть папки
+ Зворотній зв\'язок
+ Зробіть випуск на GitHub
+ Надіслати електронний лист
+ Невідомий альбом
+ MPEG-4, що містить %s
+ Невідомий
+ Аудіокодек Apple без втрат (ALAC)
+ Нова папка
+ Економія місця
+ Ваші пісні відображатимуться тут.
+ Ваші альбоми відображатимуться тут.
+ Тут відображатимуться ваші списки відтворення.
+ Тут відображатимуться ваші жанри.
+ Тут з’являться ваші виконавці.
\ No newline at end of file
diff --git a/app/src/main/res/values-v31/styles_core.xml b/app/src/main/res/values-v31/styles_core.xml
index 714a9a9fb..d538418f2 100644
--- a/app/src/main/res/values-v31/styles_core.xml
+++ b/app/src/main/res/values-v31/styles_core.xml
@@ -3,6 +3,7 @@
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 825fd6716..9c56b4c75 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -32,10 +32,9 @@
已加入播放队列
查看艺术家
查看专辑
- 已保存播放进度
添加
保存
- 没有文件夹
+ 没有文件夹
关于
版本
源代码
@@ -73,8 +72,6 @@
重复播放前暂停
曲目重复播放前暂停
内容
- 保存播放状态
- 立即保存当前播放状态
刷新音乐
重新加载音乐库,如有可能使用缓存的标签
@@ -82,7 +79,7 @@
加载音乐失败
Auxio 需要权限来读取音乐库
未找到可以处理此任务的应用
- 该目录不受支持
+ 该目录不受支持
在曲库中搜索…
@@ -97,7 +94,7 @@
移动队列曲目
移动该标签
清除搜索队列
- 移除文件夹
+ 移除文件夹
Auxio 图标
专辑封面
%s 的专辑封面
@@ -142,11 +139,8 @@
带有标签的调节项
没有标签的调节项
警告:将前置增益更改为正向高数值或导致某些音轨峰值过高。
- 音乐文件夹
- 管理音乐的加载位置
- 模式
- 排除
- 包含
+ 音乐文件夹
+ 管理音乐的加载位置
MPEG-1 音频
MPEG-4 音频
Ogg 音频
@@ -163,9 +157,7 @@
已加载流派数量:%d
总计时长:%s
从展示的项目播放
- 将仅从您添加的目录中加载音乐。
从项目详情中选择播放时
- 将 不会从您添加的目录中加载音乐。
高级音乐编码 (AAC)
已加载专辑数量:%d
音乐加载中
@@ -182,19 +174,12 @@
采样率
好的
取消
- 清除播放状态
- 清除此前保存的播放状态(如果有)
- 无法恢复状态
- 恢复播放状态
- 恢复此前保存的播放状态(如果有)
自动重载
只要发生更改就重新加载曲库(需要持久性通知)
打开队列
正在加载音乐
正在监测曲库
正在监测您的曲库以查找更改…
- 已清除状态
- 已恢复状态
EP 专辑
EP 专辑
单曲
@@ -234,17 +219,15 @@
警告:使用此设置可能会导致某些标签被错误地阐释为具有多个值。要解决这个问题,你可以在不想要的分隔符前加上反斜杠 (\\)。
忽略不是音乐的音频文件,例如播客
排除非音乐
- 高质量
+ 高质量
专辑封面
关
- 快速
+ 快速
隐藏协作者
在库中仅显示在出现在“专辑艺术家”标签中的艺术家(在标记良好的库上效果最好)
- %d 位艺术家
- 无法保存状态
- 无法清除状态
重新扫描音乐
清除标签缓存并完全重新加载音乐库(更慢,但更完整)
选中了 %d 首
@@ -253,15 +236,14 @@
%1$s, %2$s
重置
行为
- 回放增益
- 持久性
+ 音量正常化
更改应用的主题和颜色
定制用户界面操控和行为
控制音乐和图片加载方式
图片
播放
曲库
- 文件夹
+ 文件夹
音乐
配置声音和播放行为
降序
@@ -269,8 +251,7 @@
播放列表
%s 的播放列表图片
排序时忽略冠词
- 按名称排序时忽略类似“the”这样的冠词(对英文歌曲的效果最好)
- 创建新的播放列表
+ 正确排列以数字或像是 \"the\" 这样的单词开头的名称(对英文歌曲效果最好)
新建播放列表
播放列表 %d
已创建播放列表
@@ -328,4 +309,25 @@
要在此添加您的名字请给项目捐款!
跳过或编辑队列时保留播放/暂停状态
记住暂停状态
+ 关闭
+ 开始播放
+ 使用之前使用的状态启动 Auxio。如果没有可用的已保存状态,将打乱所有歌曲的顺序。播放将立刻开始。
+\n
+\n警告:请小心控制此服务,如果将其关闭然后试图再次使用可能造成应用崩溃。
+ 更多
+ 在 GithHub 上开 issue
+ 发送电子邮件
+ 反馈
+ 选择文件夹
+ 未知专辑
+ 含 %s 的 MPEG-4
+ Apple 无损音频编解码器(ALAC)
+ 未知
+ 新文件夹
+ 节省空间
+ 歌曲会出现在此处。
+ 艺术家会出现在此处。
+ 流派会出现在此处。
+ 专辑会出现在此处。
+ 播放列表会出现在此处。
\ No newline at end of file
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index c59791d5c..45407a509 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -7,7 +7,7 @@
演出者
專輯
歌曲
- 歌曲
+ 所有歌曲
搜尋
篩選器
全部
@@ -73,4 +73,95 @@
單曲
單曲
在更多的用戶界面元素上啟用圓角(需要專輯封面也要設定圓角)
-
\ No newline at end of file
+ 更多
+ 演出者
+ 新播放隊列
+ 匯入播放隊列
+ 路徑
+ 路徑樣式
+ 支援者
+ 已匯入播放隊列
+ 已匯出播放隊列
+ 絕對
+ 更多
+ 錯誤信息
+ 已複製
+ 查看和控制音樂回放
+ 歌曲屬性
+ 隨機
+ 歌曲
+ 實況單曲
+ 重新混音單曲
+ 使用與Windows相容的路徑
+ 已匯入的播放隊列
+ 查看屬性
+ 添加到播放隊列
+ 播放隊列
+ 播放隊列
+ 刪除
+ 歌曲計數器
+ Dj混音
+ 刪除播放隊列?
+ 降序排列
+ 報告
+ 實況專輯
+ 重新混音專輯
+ 實況
+ 重新混音
+ 日期
+ 已重新命名播放隊列
+ 已刪除播放隊列
+ 重新命名
+ 重新命名播放隊列
+ 編輯
+ 名稱
+ 碟片
+ 音軌
+ 添加日期
+ 排序依據
+ 等化器
+ 分享
+ 隨機全部
+ 取消
+ 已建立播放隊列
+ 開始回放
+ 匯入
+ 匯出
+ 匯出播放隊列
+ 空播放隊列
+ 查看
+ 回報
+ 正在監視你的音樂資料庫更改…
+ 自定義回放條動作
+ 自定義通知動作
+ 行為
+ 控制如何載入音樂和圖片
+ 圖片
+ 關閉
+ 配置聲音和回放行為
+ 回放
+ 關閉
+ 偏好的音軌
+ 捐贈此專案以將你的名字添加到此處!
+ 耳機自動播放
+ 智慧排序
+ 你的專輯將顯示在這裡.
+ 強制使用方形專輯封面
+ 自動重新載入
+ 多參數分隔符
+ 你的歌曲將顯示在這裡.
+ 你的演出者將顯示在這裡.
+ 你的類型將顯示在這裡.
+ 你的播放隊列將顯示在這裡.
+ 忽略不是音訊的檔案比如博客
+ 已添加到播放隊列
+ 圓角模式
+ 排除非音樂
+ 音樂
+ 自定義使用者介面控制和行為
+ 更改媒體庫標籤的可見性和排序
+ 儲存空間
+ 記住暫停
+ 媒體庫標籤
+ 更改應用程式的主題和顏色
+
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index a320f9ea5..c3558e528 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -11,12 +11,8 @@
100
-
-
-
-
-
-
+
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
deleted file mode 100644
index c1f8a7371..000000000
--- a/app/src/main/res/values/colors.xml
+++ /dev/null
@@ -1,402 +0,0 @@
-
-
-
- #80000000
-
-
- #01ffffff
-
-
- @color/material_dynamic_primary95
- @color/material_dynamic_neutral20
- @color/material_dynamic_neutral95
-
- #BC1714
- #FFFFFF
- #FFDAD3
- #410001
- #FFB4A8
- #775652
- #FFFFFF
- #FFDAD4
- #2C1512
- #715C2E
- #FFFFFF
- #FCDFA6
- #261A00
- #BA1B1B
- #FFFFFF
- #FFDAD4
- #410001
- #FCFCFC
- #211A19
- #F4DDDA
- #534341
- #362F2E
- #FBEEEC
-
- #BC0049
- #FFFFFF
- #FFD9DF
- #400013
- #FFB2C0
- #76565B
- #FFFFFF
- #FFD9DE
- #2B1519
- #795831
- #FFFFFF
- #FFDDB8
- #2C1700
- #BA1B1B
- #FFFFFF
- #FFDAD4
- #410001
- #FCFCFC
- #201A1B
- #F4DDDF
- #524345
- #362F30
- #FAEEEE
-
- #9A25AE
- #FFFFFF
- #FFD5FF
- #350040
- #FBAAFF
- #6B586B
- #FFFFFF
- #F5DBF2
- #251626
- #82524A
- #FFFFFF
- #FFDAD2
- #32110C
- #BA1B1B
- #FFFFFF
- #FFDAD4
- #410001
- #FCFCFC
- #1E1A1D
- #ECDEE8
- #4D444C
- #332F32
- #F7EEF3
-
- #6F43BF
- #FFFFFF
- #ECDCFF
- #25005A
- #D4BAFF
- #635B70
- #FFFFFF
- #E9DEF7
- #1F182B
- #7F525E
- #FFFFFF
- #FFD9E2
- #32101B
- #BA1B1B
- #FFFFFF
- #FFDAD4
- #410001
- #FFFBFD
- #1D1B1F
- #E7E0EB
- #49454E
- #323033
- #F5EFF4
-
- #4355B9
- #FFFFFF
- #DDE0FF
- #000D61
- #B9C3FF
- #5B5D71
- #FFFFFF
- #E0E1FA
- #171A2C
- #77536D
- #FFFFFF
- #FFD7F3
- #2D1228
- #BA1B1B
- #FFFFFF
- #FFDAD4
- #410001
- #FEFBFF
- #1B1B1F
- #E3E1EC
- #46464F
- #303034
- #F3F0F5
-
- #0061A6
- #FFFFFF
- #D0E4FF
- #001D36
- #9CCAFF
- #535F70
- #FFFFFF
- #D6E3F7
- #101C2B
- #6B5778
- #FFFFFF
- #F3DAFF
- #251432
- #BA1B1B
- #FFDAD4
- #FFFFFF
- #410001
- #FDFCFF
- #1B1B1B
- #DFE2EB
- #42474E
- #2F3033
- #F1F0F4
-
- #006684
- #FFFFFF
- #BAE9FF
- #001F2A
- #62D3FF
- #4D616B
- #FFFFFF
- #D0E6F2
- #081E27
- #5D5B7E
- #FFFFFF
- #E3DFFF
- #191836
- #BA1B1B
- #FFFFFF
- #FFDAD4
- #410001
- #FBFCFE
- #191C1E
- #DCE4E9
- #40484C
- #2E3133
- #F0F1F3
-
- #006877
- #FFFFFF
- #9CEFFF
- #001F25
- #44D8F1
- #4A6267
- #FFFFFF
- #CDE7ED
- #051F23
- #545D7D
- #FFFFFF
- #DAE1FF
- #101A37
- #BA1B1B
- #FFFFFF
- #FFDAD4
- #410001
- #FBFDFE
- #191C1D
- #DBE4E6
- #3F484A
- #2D3132
- #EFF1F2
-
- #006A5F
- #FFFFFF
- #74F7E5
- #00201C
- #53DBC9
- #4A635F
- #FFFFFF
- #CDE8E2
- #05201C
- #466179
- #FFFFFF
- #CBE5FF
- #001D31
- #BA1B1B
- #FFFFFF
- #FFDAD4
- #410001
- #FAFDFA
- #191C1B
- #DBE5E2
- #3F4947
- #2D3130
- #EFF1EF
-
- #006E17
- #FFFFFF
- #93F990
- #002203
- #78DC77
- #52634F
- #FFFFFF
- #D5E8CE
- #101F0F
- #38656A
- #FFFFFF
- #BCEBF0
- #001F23
- #BA1B1B
- #FFFFFF
- #FFDAD4
- #410001
- #FCFDF6
- #1A1C19
- #DEE5D8
- #424840
- #2F312D
- #F0F1EB
-
- #3C6A00
- #FFFFFF
- #B9F475
- #0E2000
- #9ED75C
- #58624A
- #FFFFFF
- #DBE7C7
- #151E0B
- #386663
- #FFFFFF
- #BCECE8
- #00201E
- #BA1B1B
- #FFFFFF
- #FFDAD4
- #410001
- #FDFCF5
- #1A1C17
- #E1E4D6
- #44483D
- #30312D
- #F2F1EA
-
- #5A6400
- #FFFFFF
- #DEED49
- #1A1E00
- #C1D02C
- #5E6044
- #FFFFFF
- #E4E5C1
- #1B1D07
- #3C665A
- #FFFFFF
- #BEECDC
- #002019
- #BA1B1B
- #FFFFFF
- #FFDAD4
- #410001
- #FFFCF3
- #1C1C17
- #E5E3D2
- #47473B
- #31312B
- #F3F0E8
-
- #FFB300
- #FFFFFF
- #FFDF99
- #261A00
- #FABD00
- #6B5C3F
- #FFFFFF
- #F4E0BB
- #241A04
- #496546
- #FFFFFF
- #CCEBC5
- #082009
- #BA1B1B
- #FFFFFF
- #FFDAD4
- #410001
- #FFFBF8
- #1E1B16
- #EDE1CF
- #4D4639
- #34302A
- #F8F0E7
-
- #FB8C00
- #FFFFFF
- #FFDCBB
- #2D1600
- #FFB86D
- #735A42
- #FFFFFF
- #FEDCBE
- #291806
- #586339
- #FFFFFF
- #DCE8B4
- #161E01
- #BA1B1B
- #FFDAD4
- #FFFFFF
- #410001
- #FCFCFC
- #1F1B17
- #F2DFD1
- #51453A
- #352F2A
- #FAEFE7
-
- #77574C
- #FFFFFF
- #FFDBCD
- #2C160D
- #E7BEB0
- #9A4523
- #FFFFFF
- #FFDBCD
- #380C00
- #695E2F
- #FFFFFF
- #F1E2A7
- #221B00
- #BA1B1B
- #FFFFFF
- #FFDAD4
- #410001
- #FCFCFC
- #201A18
- #F5DED6
- #52433E
- #362F2D
- #FCEEEA
-
- #757575
- #FFFFFF
- #EEEEEE
- #E0E0E0
- #212121
- #4D4D4D
- #FFFFFF
- #D0D0D0
- #080808
- #5D5D5D
- #FFFFFF
- #E3E3E3
- #191919
- #BA1B1B
- #FFFFFF
- #FFDADA
- #410000
- #fafafa
- #191919
- #DCDCDC
- #484848
- #1f1f1f
- #F0F0F0
-
- @color/m3_ref_palette_dynamic_primary40
-
\ No newline at end of file
diff --git a/app/src/main/res/values/colors_android.xml b/app/src/main/res/values/colors_android.xml
new file mode 100644
index 000000000..f107ae056
--- /dev/null
+++ b/app/src/main/res/values/colors_android.xml
@@ -0,0 +1,18 @@
+
+
+
+ #80000000
+
+
+ #01ffffff
+
+
+ @color/material_dynamic_primary95
+ @color/material_dynamic_neutral20
+ @color/material_dynamic_neutral95
+
+ @color/m3_ref_palette_dynamic_primary40
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors_ui.xml b/app/src/main/res/values/colors_ui.xml
new file mode 100644
index 000000000..984e35604
--- /dev/null
+++ b/app/src/main/res/values/colors_ui.xml
@@ -0,0 +1,2274 @@
+
+
+ #904A42
+ #FFFFFF
+ #FFDAD5
+ #3B0906
+ #775652
+ #FFFFFF
+ #FFDAD5
+ #2C1512
+ #705C2E
+ #FFFFFF
+ #FCDFA6
+ #261A00
+ #BA1A1A
+ #FFFFFF
+ #FFDAD6
+ #410002
+ #FFF8F7
+ #231918
+ #FFF8F7
+ #231918
+ #F5DDDA
+ #534341
+ #857370
+ #D8C2BE
+ #000000
+ #392E2C
+ #FFEDEA
+ #FFB4A9
+ #FFDAD5
+ #3B0906
+ #FFB4A9
+ #73342C
+ #FFDAD5
+ #2C1512
+ #E7BDB7
+ #5D3F3B
+ #FCDFA6
+ #261A00
+ #DFC38C
+ #574419
+ #E8D6D3
+ #FFF8F7
+ #FFFFFF
+ #FFF0EE
+ #FCEAE7
+ #F7E4E1
+ #F1DEDC
+ #6E3028
+ #FFFFFF
+ #AA6056
+ #FFFFFF
+ #593B37
+ #FFFFFF
+ #8F6C67
+ #FFFFFF
+ #534015
+ #FFFFFF
+ #887242
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #DA342E
+ #FFFFFF
+ #FFF8F7
+ #231918
+ #FFF8F7
+ #231918
+ #F5DDDA
+ #4F3F3D
+ #6C5B59
+ #897674
+ #000000
+ #392E2C
+ #FFEDEA
+ #FFB4A9
+ #AA6056
+ #FFFFFF
+ #8D483F
+ #FFFFFF
+ #8F6C67
+ #FFFFFF
+ #745450
+ #FFFFFF
+ #887242
+ #FFFFFF
+ #6E592C
+ #FFFFFF
+ #E8D6D3
+ #FFF8F7
+ #FFFFFF
+ #FFF0EE
+ #FCEAE7
+ #F7E4E1
+ #F1DEDC
+ #44100B
+ #FFFFFF
+ #6E3028
+ #FFFFFF
+ #341C18
+ #FFFFFF
+ #593B37
+ #FFFFFF
+ #2E2000
+ #FFFFFF
+ #534015
+ #FFFFFF
+ #4E0002
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #FFF8F7
+ #231918
+ #FFF8F7
+ #000000
+ #F5DDDA
+ #2E211F
+ #4F3F3D
+ #4F3F3D
+ #000000
+ #392E2C
+ #FFFFFF
+ #FFE7E3
+ #6E3028
+ #FFFFFF
+ #521A15
+ #FFFFFF
+ #593B37
+ #FFFFFF
+ #402622
+ #FFFFFF
+ #534015
+ #FFFFFF
+ #3A2A02
+ #FFFFFF
+ #E8D6D3
+ #FFF8F7
+ #FFFFFF
+ #FFF0EE
+ #FCEAE7
+ #F7E4E1
+ #F1DEDC
+
+ #8E4956
+ #FFFFFF
+ #FFD9DD
+ #3B0715
+ #76565A
+ #FFFFFF
+ #FFD9DD
+ #2C1519
+ #795831
+ #FFFFFF
+ #FFDDB9
+ #2B1700
+ #BA1A1A
+ #FFFFFF
+ #FFDAD6
+ #410002
+ #FFF8F7
+ #22191A
+ #FFF8F7
+ #22191A
+ #F3DDDF
+ #524345
+ #847375
+ #D6C2C3
+ #000000
+ #382E2F
+ #FEEDEE
+ #FFB2BD
+ #FFD9DD
+ #3B0715
+ #FFB2BD
+ #72333F
+ #FFD9DD
+ #2C1519
+ #E5BDC1
+ #5C3F43
+ #FFDDB9
+ #2B1700
+ #EABF8F
+ #5E411C
+ #E7D6D7
+ #FFF8F7
+ #FFFFFF
+ #FFF0F1
+ #FBEAEB
+ #F6E4E5
+ #F0DEDF
+ #6D2F3B
+ #FFFFFF
+ #A85F6B
+ #FFFFFF
+ #583B3F
+ #FFFFFF
+ #8D6C70
+ #FFFFFF
+ #5A3D18
+ #FFFFFF
+ #916E45
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #DA342E
+ #FFFFFF
+ #FFF8F7
+ #22191A
+ #FFF8F7
+ #22191A
+ #F3DDDF
+ #4E3F41
+ #6B5B5D
+ #887678
+ #000000
+ #382E2F
+ #FEEDEE
+ #FFB2BD
+ #A85F6B
+ #FFFFFF
+ #8B4753
+ #FFFFFF
+ #8D6C70
+ #FFFFFF
+ #735458
+ #FFFFFF
+ #916E45
+ #FFFFFF
+ #76562F
+ #FFFFFF
+ #E7D6D7
+ #FFF8F7
+ #FFFFFF
+ #FFF0F1
+ #FBEAEB
+ #F6E4E5
+ #F0DEDF
+ #430E1B
+ #FFFFFF
+ #6D2F3B
+ #FFFFFF
+ #331B1F
+ #FFFFFF
+ #583B3F
+ #FFFFFF
+ #341D00
+ #FFFFFF
+ #5A3D18
+ #FFFFFF
+ #4E0002
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #FFF8F7
+ #22191A
+ #FFF8F7
+ #000000
+ #F3DDDF
+ #2D2122
+ #4E3F41
+ #4E3F41
+ #000000
+ #382E2F
+ #FFFFFF
+ #FFE6E8
+ #6D2F3B
+ #FFFFFF
+ #511926
+ #FFFFFF
+ #583B3F
+ #FFFFFF
+ #3F2629
+ #FFFFFF
+ #5A3D18
+ #FFFFFF
+ #412705
+ #FFFFFF
+ #E7D6D7
+ #FFF8F7
+ #FFFFFF
+ #FFF0F1
+ #FBEAEB
+ #F6E4E5
+ #F0DEDF
+
+ #7B4E7F
+ #FFFFFF
+ #FFD6FE
+ #310937
+ #6B586B
+ #FFFFFF
+ #F4DBF1
+ #251626
+ #82524A
+ #FFFFFF
+ #FFDAD4
+ #33110C
+ #BA1A1A
+ #FFFFFF
+ #FFDAD6
+ #410002
+ #FFF7FA
+ #1F1A1F
+ #FFF7FA
+ #1F1A1F
+ #ECDFE8
+ #4D444C
+ #7F747D
+ #D0C3CC
+ #000000
+ #352F34
+ #F9EEF5
+ #EBB5ED
+ #FFD6FE
+ #310937
+ #EBB5ED
+ #613766
+ #F4DBF1
+ #251626
+ #D7BFD5
+ #534153
+ #FFDAD4
+ #33110C
+ #F6B8AD
+ #673B34
+ #E2D7DE
+ #FFF7FA
+ #FFFFFF
+ #FCF0F7
+ #F6EBF2
+ #F0E5EC
+ #EBDFE6
+ #5D3362
+ #FFFFFF
+ #936497
+ #FFFFFF
+ #4F3D4F
+ #FFFFFF
+ #826E82
+ #FFFFFF
+ #623730
+ #FFFFFF
+ #9B685F
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #DA342E
+ #FFFFFF
+ #FFF7FA
+ #1F1A1F
+ #FFF7FA
+ #1F1A1F
+ #ECDFE8
+ #494048
+ #665C64
+ #827880
+ #000000
+ #352F34
+ #F9EEF5
+ #EBB5ED
+ #936497
+ #FFFFFF
+ #794C7D
+ #FFFFFF
+ #826E82
+ #FFFFFF
+ #695668
+ #FFFFFF
+ #9B685F
+ #FFFFFF
+ #7F5048
+ #FFFFFF
+ #E2D7DE
+ #FFF7FA
+ #FFFFFF
+ #FCF0F7
+ #F6EBF2
+ #F0E5EC
+ #EBDFE6
+ #38113F
+ #FFFFFF
+ #5D3362
+ #FFFFFF
+ #2C1D2D
+ #FFFFFF
+ #4F3D4F
+ #FFFFFF
+ #3B1812
+ #FFFFFF
+ #623730
+ #FFFFFF
+ #4E0002
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #FFF7FA
+ #1F1A1F
+ #FFF7FA
+ #000000
+ #ECDFE8
+ #292229
+ #494048
+ #494048
+ #000000
+ #352F34
+ #FFFFFF
+ #FFE4FC
+ #5D3362
+ #FFFFFF
+ #441C4A
+ #FFFFFF
+ #4F3D4F
+ #FFFFFF
+ #372738
+ #FFFFFF
+ #623730
+ #FFFFFF
+ #48221C
+ #FFFFFF
+ #E2D7DE
+ #FFF7FA
+ #FFFFFF
+ #FCF0F7
+ #F6EBF2
+ #F0E5EC
+ #EBDFE6
+
+ #68548E
+ #FFFFFF
+ #EBDDFF
+ #230F46
+ #635B70
+ #FFFFFF
+ #E9DEF8
+ #1F182B
+ #7E525D
+ #FFFFFF
+ #FFD9E1
+ #31101B
+ #BA1A1A
+ #FFFFFF
+ #FFDAD6
+ #410002
+ #FEF7FF
+ #1D1B20
+ #FEF7FF
+ #1D1B20
+ #E7E0EB
+ #49454E
+ #7A757F
+ #CBC4CF
+ #000000
+ #322F35
+ #F5EFF7
+ #D3BCFD
+ #EBDDFF
+ #230F46
+ #D3BCFD
+ #4F3D74
+ #E9DEF8
+ #1F182B
+ #CDC2DB
+ #4B4358
+ #FFD9E1
+ #31101B
+ #F0B7C5
+ #643B46
+ #DED8E0
+ #FEF7FF
+ #FFFFFF
+ #F8F1FA
+ #F2ECF4
+ #EDE6EE
+ #E7E0E8
+ #4B3970
+ #FFFFFF
+ #7F6AA5
+ #FFFFFF
+ #473F54
+ #FFFFFF
+ #797187
+ #FFFFFF
+ #5F3742
+ #FFFFFF
+ #976774
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #DA342E
+ #FFFFFF
+ #FEF7FF
+ #1D1B20
+ #FEF7FF
+ #1D1B20
+ #E7E0EB
+ #45414A
+ #625D67
+ #7E7983
+ #000000
+ #322F35
+ #F5EFF7
+ #D3BCFD
+ #7F6AA5
+ #FFFFFF
+ #65528B
+ #FFFFFF
+ #797187
+ #FFFFFF
+ #60586E
+ #FFFFFF
+ #976774
+ #FFFFFF
+ #7C4F5B
+ #FFFFFF
+ #DED8E0
+ #FEF7FF
+ #FFFFFF
+ #F8F1FA
+ #F2ECF4
+ #EDE6EE
+ #E7E0E8
+ #2A164D
+ #FFFFFF
+ #4B3970
+ #FFFFFF
+ #251F32
+ #FFFFFF
+ #473F54
+ #FFFFFF
+ #391722
+ #FFFFFF
+ #5F3742
+ #FFFFFF
+ #4E0002
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #FEF7FF
+ #1D1B20
+ #FEF7FF
+ #000000
+ #E7E0EB
+ #26222B
+ #45414A
+ #45414A
+ #000000
+ #322F35
+ #FFFFFF
+ #F3E8FF
+ #4B3970
+ #FFFFFF
+ #352258
+ #FFFFFF
+ #473F54
+ #FFFFFF
+ #30293D
+ #FFFFFF
+ #5F3742
+ #FFFFFF
+ #46212C
+ #FFFFFF
+ #DED8E0
+ #FEF7FF
+ #FFFFFF
+ #F8F1FA
+ #F2ECF4
+ #EDE6EE
+ #E7E0E8
+
+ #515B92
+ #FFFFFF
+ #DEE0FF
+ #0B154B
+ #5B5D72
+ #FFFFFF
+ #E0E1F9
+ #181A2C
+ #77536D
+ #FFFFFF
+ #FFD7F1
+ #2D1228
+ #BA1A1A
+ #FFFFFF
+ #FFDAD6
+ #410002
+ #FBF8FF
+ #1B1B21
+ #FBF8FF
+ #1B1B21
+ #E3E1EC
+ #46464F
+ #767680
+ #C7C5D0
+ #000000
+ #303036
+ #F2EFF7
+ #BAC3FF
+ #DEE0FF
+ #0B154B
+ #BAC3FF
+ #394379
+ #E0E1F9
+ #181A2C
+ #C3C5DD
+ #434659
+ #FFD7F1
+ #2D1228
+ #E6BAD7
+ #5D3C55
+ #DBD9E0
+ #FBF8FF
+ #FFFFFF
+ #F5F2FA
+ #EFEDF4
+ #E9E7EF
+ #E4E1E9
+ #353F74
+ #FFFFFF
+ #6871AA
+ #FFFFFF
+ #3F4255
+ #FFFFFF
+ #717389
+ #FFFFFF
+ #593851
+ #FFFFFF
+ #8E6984
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #DA342E
+ #FFFFFF
+ #FBF8FF
+ #1B1B21
+ #FBF8FF
+ #1B1B21
+ #E3E1EC
+ #42424B
+ #5E5E67
+ #7A7A83
+ #000000
+ #303036
+ #F2EFF7
+ #BAC3FF
+ #6871AA
+ #FFFFFF
+ #4F5890
+ #FFFFFF
+ #717389
+ #FFFFFF
+ #585B6F
+ #FFFFFF
+ #8E6984
+ #FFFFFF
+ #74516B
+ #FFFFFF
+ #DBD9E0
+ #FBF8FF
+ #FFFFFF
+ #F5F2FA
+ #EFEDF4
+ #E9E7EF
+ #E4E1E9
+ #131D52
+ #FFFFFF
+ #353F74
+ #FFFFFF
+ #1E2133
+ #FFFFFF
+ #3F4255
+ #FFFFFF
+ #34182F
+ #FFFFFF
+ #593851
+ #FFFFFF
+ #4E0002
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #FBF8FF
+ #1B1B21
+ #FBF8FF
+ #000000
+ #E3E1EC
+ #23232B
+ #42424B
+ #42424B
+ #000000
+ #303036
+ #FFFFFF
+ #EAEAFF
+ #353F74
+ #FFFFFF
+ #1E285D
+ #FFFFFF
+ #3F4255
+ #FFFFFF
+ #292C3E
+ #FFFFFF
+ #593851
+ #FFFFFF
+ #40233A
+ #FFFFFF
+ #DBD9E0
+ #FBF8FF
+ #FFFFFF
+ #F5F2FA
+ #EFEDF4
+ #E9E7EF
+ #E4E1E9
+
+ #37618E
+ #FFFFFF
+ #D2E4FF
+ #001C37
+ #535F70
+ #FFFFFF
+ #D7E3F8
+ #101C2B
+ #6B5778
+ #FFFFFF
+ #F3DAFF
+ #251431
+ #BA1A1A
+ #FFFFFF
+ #FFDAD6
+ #410002
+ #F8F9FF
+ #191C20
+ #F8F9FF
+ #191C20
+ #DFE2EB
+ #43474E
+ #73777F
+ #C3C6CF
+ #000000
+ #2E3135
+ #EFF0F7
+ #A1C9FD
+ #D2E4FF
+ #001C37
+ #A1C9FD
+ #1B4975
+ #D7E3F8
+ #101C2B
+ #BBC7DB
+ #3C4858
+ #F3DAFF
+ #251431
+ #D7BDE4
+ #533F5F
+ #D8DAE0
+ #F8F9FF
+ #FFFFFF
+ #F2F3FA
+ #ECEEF4
+ #E7E8EE
+ #E1E2E8
+ #164571
+ #FFFFFF
+ #4F77A6
+ #FFFFFF
+ #384454
+ #FFFFFF
+ #697587
+ #FFFFFF
+ #4F3B5B
+ #FFFFFF
+ #826D8F
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #DA342E
+ #FFFFFF
+ #F8F9FF
+ #191C20
+ #F8F9FF
+ #191C20
+ #DFE2EB
+ #3F434A
+ #5B5F67
+ #777B83
+ #000000
+ #2E3135
+ #EFF0F7
+ #A1C9FD
+ #4F77A6
+ #FFFFFF
+ #345E8C
+ #FFFFFF
+ #697587
+ #FFFFFF
+ #515D6E
+ #FFFFFF
+ #826D8F
+ #FFFFFF
+ #695475
+ #FFFFFF
+ #D8DAE0
+ #F8F9FF
+ #FFFFFF
+ #F2F3FA
+ #ECEEF4
+ #E7E8EE
+ #E1E2E8
+ #002342
+ #FFFFFF
+ #164571
+ #FFFFFF
+ #172332
+ #FFFFFF
+ #384454
+ #FFFFFF
+ #2C1B38
+ #FFFFFF
+ #4F3B5B
+ #FFFFFF
+ #4E0002
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #F8F9FF
+ #191C20
+ #F8F9FF
+ #000000
+ #DFE2EB
+ #20242B
+ #3F434A
+ #3F434A
+ #000000
+ #2E3135
+ #FFFFFF
+ #E2EDFF
+ #164571
+ #FFFFFF
+ #002E53
+ #FFFFFF
+ #384454
+ #FFFFFF
+ #222E3D
+ #FFFFFF
+ #4F3B5B
+ #FFFFFF
+ #372543
+ #FFFFFF
+ #D8DAE0
+ #F8F9FF
+ #FFFFFF
+ #F2F3FA
+ #ECEEF4
+ #E7E8EE
+ #E1E2E8
+
+ #136682
+ #FFFFFF
+ #BEE9FF
+ #001F2A
+ #4D616C
+ #FFFFFF
+ #D0E6F2
+ #081E27
+ #5D5B7D
+ #FFFFFF
+ #E3DFFF
+ #1A1836
+ #BA1A1A
+ #FFFFFF
+ #FFDAD6
+ #410002
+ #F6FAFE
+ #171C1F
+ #F6FAFE
+ #171C1F
+ #DCE4E9
+ #40484C
+ #70787D
+ #C0C8CD
+ #000000
+ #2C3134
+ #EDF1F5
+ #8BD0F0
+ #BEE9FF
+ #001F2A
+ #8BD0F0
+ #004D64
+ #D0E6F2
+ #081E27
+ #B4CAD6
+ #354A54
+ #E3DFFF
+ #1A1836
+ #C6C2EA
+ #454364
+ #D6DBDE
+ #F6FAFE
+ #FFFFFF
+ #F0F4F8
+ #EAEEF2
+ #E4E9EC
+ #DFE3E7
+ #00495F
+ #FFFFFF
+ #347D9A
+ #FFFFFF
+ #314650
+ #FFFFFF
+ #637882
+ #FFFFFF
+ #413F60
+ #FFFFFF
+ #747195
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #DA342E
+ #FFFFFF
+ #F6FAFE
+ #171C1F
+ #F6FAFE
+ #171C1F
+ #DCE4E9
+ #3C4448
+ #586065
+ #747C81
+ #000000
+ #2C3134
+ #EDF1F5
+ #8BD0F0
+ #347D9A
+ #FFFFFF
+ #0D6480
+ #FFFFFF
+ #637882
+ #FFFFFF
+ #4A5F69
+ #FFFFFF
+ #747195
+ #FFFFFF
+ #5B587B
+ #FFFFFF
+ #D6DBDE
+ #F6FAFE
+ #FFFFFF
+ #F0F4F8
+ #EAEEF2
+ #E4E9EC
+ #DFE3E7
+ #002633
+ #FFFFFF
+ #00495F
+ #FFFFFF
+ #10252E
+ #FFFFFF
+ #314650
+ #FFFFFF
+ #211E3D
+ #FFFFFF
+ #413F60
+ #FFFFFF
+ #4E0002
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #F6FAFE
+ #171C1F
+ #F6FAFE
+ #000000
+ #DCE4E9
+ #1D2529
+ #3C4448
+ #3C4448
+ #000000
+ #2C3134
+ #FFFFFF
+ #D5F0FF
+ #00495F
+ #FFFFFF
+ #003141
+ #FFFFFF
+ #314650
+ #FFFFFF
+ #1B2F39
+ #FFFFFF
+ #413F60
+ #FFFFFF
+ #2B2949
+ #FFFFFF
+ #D6DBDE
+ #F6FAFE
+ #FFFFFF
+ #F0F4F8
+ #EAEEF2
+ #E4E9EC
+ #DFE3E7
+
+ #006877
+ #FFFFFF
+ #A4EEFF
+ #001F25
+ #4B6268
+ #FFFFFF
+ #CDE7ED
+ #051F24
+ #545D7E
+ #FFFFFF
+ #DCE1FF
+ #111A37
+ #BA1A1A
+ #FFFFFF
+ #FFDAD6
+ #410002
+ #F5FAFC
+ #171D1E
+ #F5FAFC
+ #171D1E
+ #DBE4E7
+ #3F484B
+ #6F797B
+ #BFC8CB
+ #000000
+ #2B3133
+ #ECF2F3
+ #83D2E4
+ #A4EEFF
+ #001F25
+ #83D2E4
+ #004E5A
+ #CDE7ED
+ #051F24
+ #B2CBD1
+ #334A50
+ #DCE1FF
+ #111A37
+ #BDC5EB
+ #3D4565
+ #D5DBDD
+ #F5FAFC
+ #FFFFFF
+ #EFF4F6
+ #E9EFF0
+ #E3E9EB
+ #DEE3E5
+ #004A55
+ #FFFFFF
+ #277F8F
+ #FFFFFF
+ #2F464C
+ #FFFFFF
+ #60797E
+ #FFFFFF
+ #394261
+ #FFFFFF
+ #6B7395
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #DA342E
+ #FFFFFF
+ #F5FAFC
+ #171D1E
+ #F5FAFC
+ #171D1E
+ #DBE4E7
+ #3B4447
+ #576163
+ #737C7F
+ #000000
+ #2B3133
+ #ECF2F3
+ #83D2E4
+ #277F8F
+ #FFFFFF
+ #006574
+ #FFFFFF
+ #60797E
+ #FFFFFF
+ #486065
+ #FFFFFF
+ #6B7395
+ #FFFFFF
+ #525B7B
+ #FFFFFF
+ #D5DBDD
+ #F5FAFC
+ #FFFFFF
+ #EFF4F6
+ #E9EFF0
+ #E3E9EB
+ #DEE3E5
+ #00262D
+ #FFFFFF
+ #004A55
+ #FFFFFF
+ #0D252A
+ #FFFFFF
+ #2F464C
+ #FFFFFF
+ #18213E
+ #FFFFFF
+ #394261
+ #FFFFFF
+ #4E0002
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #F5FAFC
+ #171D1E
+ #F5FAFC
+ #000000
+ #DBE4E7
+ #1D2527
+ #3B4447
+ #3B4447
+ #000000
+ #2B3133
+ #FFFFFF
+ #C6F4FF
+ #004A55
+ #FFFFFF
+ #00323A
+ #FFFFFF
+ #2F464C
+ #FFFFFF
+ #183035
+ #FFFFFF
+ #394261
+ #FFFFFF
+ #232B49
+ #FFFFFF
+ #D5DBDD
+ #F5FAFC
+ #FFFFFF
+ #EFF4F6
+ #E9EFF0
+ #E3E9EB
+ #DEE3E5
+
+ #006B5F
+ #FFFFFF
+ #9EF2E3
+ #00201C
+ #4A635E
+ #FFFFFF
+ #CCE8E2
+ #06201C
+ #456179
+ #FFFFFF
+ #CCE5FF
+ #001E31
+ #BA1A1A
+ #FFFFFF
+ #FFDAD6
+ #410002
+ #F4FBF8
+ #161D1B
+ #F4FBF8
+ #161D1B
+ #DAE5E1
+ #3F4946
+ #6F7976
+ #BEC9C5
+ #000000
+ #2B3230
+ #ECF2EF
+ #82D5C7
+ #9EF2E3
+ #00201C
+ #82D5C7
+ #005048
+ #CCE8E2
+ #06201C
+ #B1CCC6
+ #334B47
+ #CCE5FF
+ #001E31
+ #ADCAE5
+ #2D4960
+ #D5DBD9
+ #F4FBF8
+ #FFFFFF
+ #EFF5F2
+ #E9EFEC
+ #E3EAE7
+ #DDE4E1
+ #004C44
+ #FFFFFF
+ #298176
+ #FFFFFF
+ #2F4743
+ #FFFFFF
+ #607A74
+ #FFFFFF
+ #29465C
+ #FFFFFF
+ #5B7890
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #DA342E
+ #FFFFFF
+ #F4FBF8
+ #161D1B
+ #F4FBF8
+ #161D1B
+ #DAE5E1
+ #3B4543
+ #57615F
+ #737D7A
+ #000000
+ #2B3230
+ #ECF2EF
+ #82D5C7
+ #298176
+ #FFFFFF
+ #00685D
+ #FFFFFF
+ #607A74
+ #FFFFFF
+ #48615C
+ #FFFFFF
+ #5B7890
+ #FFFFFF
+ #435F77
+ #FFFFFF
+ #D5DBD9
+ #F4FBF8
+ #FFFFFF
+ #EFF5F2
+ #E9EFEC
+ #E3EAE7
+ #DDE4E1
+ #002823
+ #FFFFFF
+ #004C44
+ #FFFFFF
+ #0D2622
+ #FFFFFF
+ #2F4743
+ #FFFFFF
+ #02243A
+ #FFFFFF
+ #29465C
+ #FFFFFF
+ #4E0002
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #F4FBF8
+ #161D1B
+ #F4FBF8
+ #000000
+ #DAE5E1
+ #1C2624
+ #3B4543
+ #3B4543
+ #000000
+ #2B3230
+ #FFFFFF
+ #A8FCED
+ #004C44
+ #FFFFFF
+ #00332D
+ #FFFFFF
+ #2F4743
+ #FFFFFF
+ #18312D
+ #FFFFFF
+ #29465C
+ #FFFFFF
+ #102F45
+ #FFFFFF
+ #D5DBD9
+ #F4FBF8
+ #FFFFFF
+ #EFF5F2
+ #E9EFEC
+ #E3EAE7
+ #DDE4E1
+
+ #3C6838
+ #FFFFFF
+ #BDF0B3
+ #002203
+ #53634E
+ #FFFFFF
+ #D6E8CE
+ #111F0F
+ #38656A
+ #FFFFFF
+ #BCEBF0
+ #002022
+ #BA1A1A
+ #FFFFFF
+ #FFDAD6
+ #410002
+ #F7FBF1
+ #191D17
+ #F7FBF1
+ #191D17
+ #DEE5D8
+ #424940
+ #73796F
+ #C2C8BD
+ #000000
+ #2D322B
+ #EFF2E9
+ #A2D399
+ #BDF0B3
+ #002203
+ #A2D399
+ #255023
+ #D6E8CE
+ #111F0F
+ #BACCB3
+ #3B4B38
+ #BCEBF0
+ #002022
+ #A0CFD4
+ #1E4D52
+ #D8DBD2
+ #F7FBF1
+ #FFFFFF
+ #F2F5EB
+ #ECEFE6
+ #E6E9E0
+ #E0E4DA
+ #214C1F
+ #FFFFFF
+ #527F4D
+ #FFFFFF
+ #384734
+ #FFFFFF
+ #697964
+ #FFFFFF
+ #1A494E
+ #FFFFFF
+ #4F7C80
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #DA342E
+ #FFFFFF
+ #F7FBF1
+ #191D17
+ #F7FBF1
+ #191D17
+ #DEE5D8
+ #3E453C
+ #5B6157
+ #767D72
+ #000000
+ #2D322B
+ #EFF2E9
+ #A2D399
+ #527F4D
+ #FFFFFF
+ #3A6636
+ #FFFFFF
+ #697964
+ #FFFFFF
+ #50604C
+ #FFFFFF
+ #4F7C80
+ #FFFFFF
+ #366367
+ #FFFFFF
+ #D8DBD2
+ #F7FBF1
+ #FFFFFF
+ #F2F5EB
+ #ECEFE6
+ #E6E9E0
+ #E0E4DA
+ #002904
+ #FFFFFF
+ #214C1F
+ #FFFFFF
+ #172615
+ #FFFFFF
+ #384734
+ #FFFFFF
+ #00272A
+ #FFFFFF
+ #1A494E
+ #FFFFFF
+ #4E0002
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #F7FBF1
+ #191D17
+ #F7FBF1
+ #000000
+ #DEE5D8
+ #20261E
+ #3E453C
+ #3E453C
+ #000000
+ #2D322B
+ #FFFFFF
+ #C6FABC
+ #214C1F
+ #FFFFFF
+ #07350B
+ #FFFFFF
+ #384734
+ #FFFFFF
+ #22301F
+ #FFFFFF
+ #1A494E
+ #FFFFFF
+ #003236
+ #FFFFFF
+ #D8DBD2
+ #F7FBF1
+ #FFFFFF
+ #F2F5EB
+ #ECEFE6
+ #E6E9E0
+ #E0E4DA
+
+ #4A672D
+ #FFFFFF
+ #CBEDA5
+ #0E2000
+ #57624A
+ #FFFFFF
+ #DBE7C8
+ #151E0B
+ #386664
+ #FFFFFF
+ #BBECE8
+ #00201F
+ #BA1A1A
+ #FFFFFF
+ #FFDAD6
+ #410002
+ #F9FAEF
+ #1A1C16
+ #F9FAEF
+ #1A1C16
+ #E1E4D5
+ #44483D
+ #75796C
+ #C4C8BA
+ #000000
+ #2F312A
+ #F0F2E6
+ #B0D18B
+ #CBEDA5
+ #0E2000
+ #B0D18B
+ #334E17
+ #DBE7C8
+ #151E0B
+ #BFCBAD
+ #404A34
+ #BBECE8
+ #00201F
+ #A0CFCC
+ #1F4E4C
+ #D9DBD0
+ #F9FAEF
+ #FFFFFF
+ #F3F5E9
+ #EDEFE4
+ #E8E9DE
+ #E2E3D8
+ #304A13
+ #FFFFFF
+ #607D41
+ #FFFFFF
+ #3C4630
+ #FFFFFF
+ #6D785F
+ #FFFFFF
+ #1A4A48
+ #FFFFFF
+ #4F7C7A
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #DA342E
+ #FFFFFF
+ #F9FAEF
+ #1A1C16
+ #F9FAEF
+ #1A1C16
+ #E1E4D5
+ #40443A
+ #5C6155
+ #787C70
+ #000000
+ #2F312A
+ #F0F2E6
+ #B0D18B
+ #607D41
+ #FFFFFF
+ #48642A
+ #FFFFFF
+ #6D785F
+ #FFFFFF
+ #556048
+ #FFFFFF
+ #4F7C7A
+ #FFFFFF
+ #366361
+ #FFFFFF
+ #D9DBD0
+ #F9FAEF
+ #FFFFFF
+ #F3F5E9
+ #EDEFE4
+ #E8E9DE
+ #E2E3D8
+ #132700
+ #FFFFFF
+ #304A13
+ #FFFFFF
+ #1C2512
+ #FFFFFF
+ #3C4630
+ #FFFFFF
+ #002726
+ #FFFFFF
+ #1A4A48
+ #FFFFFF
+ #4E0002
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #F9FAEF
+ #1A1C16
+ #F9FAEF
+ #000000
+ #E1E4D5
+ #21251C
+ #40443A
+ #40443A
+ #000000
+ #2F312A
+ #FFFFFF
+ #D5F7AE
+ #304A13
+ #FFFFFF
+ #1A3300
+ #FFFFFF
+ #3C4630
+ #FFFFFF
+ #26301B
+ #FFFFFF
+ #1A4A48
+ #FFFFFF
+ #003331
+ #FFFFFF
+ #D9DBD0
+ #F9FAEF
+ #FFFFFF
+ #F3F5E9
+ #EDEFE4
+ #E8E9DE
+ #E2E3D8
+
+ #5B631E
+ #FFFFFF
+ #E0E995
+ #1A1E00
+ #5E6144
+ #FFFFFF
+ #E3E5C1
+ #1B1D07
+ #3B665B
+ #FFFFFF
+ #BEECDD
+ #002019
+ #BA1A1A
+ #FFFFFF
+ #FFDAD6
+ #410002
+ #FCFAED
+ #1B1C14
+ #FCFAED
+ #1B1C14
+ #E4E3D2
+ #47483B
+ #78786A
+ #C8C7B7
+ #000000
+ #313128
+ #F3F1E4
+ #C3CD7B
+ #E0E995
+ #1A1E00
+ #C3CD7B
+ #434B05
+ #E3E5C1
+ #1B1D07
+ #C7C9A7
+ #46492F
+ #BEECDD
+ #002019
+ #A2D0C1
+ #224E43
+ #DCDACE
+ #FCFAED
+ #FFFFFF
+ #F6F4E7
+ #F0EEE1
+ #EAE9DC
+ #E5E3D6
+ #404701
+ #FFFFFF
+ #717A32
+ #FFFFFF
+ #42452B
+ #FFFFFF
+ #747759
+ #FFFFFF
+ #1E4A3F
+ #FFFFFF
+ #517D70
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #DA342E
+ #FFFFFF
+ #FCFAED
+ #1B1C14
+ #FCFAED
+ #1B1C14
+ #E4E3D2
+ #434437
+ #5F6052
+ #7B7C6D
+ #000000
+ #313128
+ #F3F1E4
+ #C3CD7B
+ #717A32
+ #FFFFFF
+ #59611B
+ #FFFFFF
+ #747759
+ #FFFFFF
+ #5B5E42
+ #FFFFFF
+ #517D70
+ #FFFFFF
+ #396458
+ #FFFFFF
+ #DCDACE
+ #FCFAED
+ #FFFFFF
+ #F6F4E7
+ #F0EEE1
+ #EAE9DC
+ #E5E3D6
+ #202500
+ #FFFFFF
+ #404701
+ #FFFFFF
+ #21240D
+ #FFFFFF
+ #42452B
+ #FFFFFF
+ #002820
+ #FFFFFF
+ #1E4A3F
+ #FFFFFF
+ #4E0002
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #FCFAED
+ #1B1C14
+ #FCFAED
+ #000000
+ #E4E3D2
+ #24251A
+ #434437
+ #434437
+ #000000
+ #313128
+ #FFFFFF
+ #E9F39D
+ #404701
+ #FFFFFF
+ #2A3000
+ #FFFFFF
+ #42452B
+ #FFFFFF
+ #2C2E17
+ #FFFFFF
+ #1E4A3F
+ #FFFFFF
+ #01332A
+ #FFFFFF
+ #DCDACE
+ #FCFAED
+ #FFFFFF
+ #F6F4E7
+ #F0EEE1
+ #EAE9DC
+ #E5E3D6
+
+ #7D570D
+ #FFFFFF
+ #FFDEAC
+ #281900
+ #6E5C40
+ #FFFFFF
+ #F8DFBB
+ #261904
+ #4E6542
+ #FFFFFF
+ #D1EABF
+ #0D2005
+ #BA1A1A
+ #FFFFFF
+ #FFDAD6
+ #410002
+ #FFF8F3
+ #201B13
+ #FFF8F3
+ #201B13
+ #EFE0CF
+ #4E4539
+ #807567
+ #D2C4B4
+ #000000
+ #362F27
+ #FBEFE2
+ #F0BE6D
+ #FFDEAC
+ #281900
+ #F0BE6D
+ #604100
+ #F8DFBB
+ #261904
+ #DBC3A1
+ #55442A
+ #D1EABF
+ #0D2005
+ #B5CEA4
+ #374C2C
+ #E4D8CC
+ #FFF8F3
+ #FFFFFF
+ #FEF2E5
+ #F8ECDF
+ #F2E6D9
+ #ECE1D4
+ #5B3D00
+ #FFFFFF
+ #966D24
+ #FFFFFF
+ #504026
+ #FFFFFF
+ #857254
+ #FFFFFF
+ #334829
+ #FFFFFF
+ #647B57
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #DA342E
+ #FFFFFF
+ #FFF8F3
+ #201B13
+ #FFF8F3
+ #201B13
+ #EFE0CF
+ #4A4135
+ #685E50
+ #84796B
+ #000000
+ #362F27
+ #FBEFE2
+ #F0BE6D
+ #966D24
+ #FFFFFF
+ #7A550A
+ #FFFFFF
+ #857254
+ #FFFFFF
+ #6B593D
+ #FFFFFF
+ #647B57
+ #FFFFFF
+ #4C6240
+ #FFFFFF
+ #E4D8CC
+ #FFF8F3
+ #FFFFFF
+ #FEF2E5
+ #F8ECDF
+ #F2E6D9
+ #ECE1D4
+ #301F00
+ #FFFFFF
+ #5B3D00
+ #FFFFFF
+ #2D2009
+ #FFFFFF
+ #504026
+ #FFFFFF
+ #14270B
+ #FFFFFF
+ #334829
+ #FFFFFF
+ #4E0002
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #FFF8F3
+ #201B13
+ #FFF8F3
+ #000000
+ #EFE0CF
+ #2A2318
+ #4A4135
+ #4A4135
+ #000000
+ #362F27
+ #FFFFFF
+ #FFE9CB
+ #5B3D00
+ #FFFFFF
+ #3E2900
+ #FFFFFF
+ #504026
+ #FFFFFF
+ #392A12
+ #FFFFFF
+ #334829
+ #FFFFFF
+ #1E3214
+ #FFFFFF
+ #E4D8CC
+ #FFF8F3
+ #FFFFFF
+ #FEF2E5
+ #F8ECDF
+ #F2E6D9
+ #ECE1D4
+
+ #88511D
+ #FFFFFF
+ #FFDCC2
+ #2E1500
+ #745944
+ #FFFFFF
+ #FFDCC2
+ #2A1707
+ #5B6237
+ #FFFFFF
+ #E0E7B1
+ #191E00
+ #BA1A1A
+ #FFFFFF
+ #FFDAD6
+ #410002
+ #FFF8F5
+ #221A14
+ #FFF8F5
+ #221A14
+ #F3DFD1
+ #51443B
+ #847469
+ #D6C3B6
+ #000000
+ #382F28
+ #FEEEE4
+ #FFB77B
+ #FFDCC2
+ #2E1500
+ #FFB77B
+ #6B3A05
+ #FFDCC2
+ #2A1707
+ #E3C0A5
+ #5A422E
+ #E0E7B1
+ #191E00
+ #C4CB97
+ #444A22
+ #E7D7CD
+ #FFF8F5
+ #FFFFFF
+ #FFF1E8
+ #FBEBE1
+ #F5E5DB
+ #EFE0D6
+ #673702
+ #FFFFFF
+ #A26731
+ #FFFFFF
+ #563E2A
+ #FFFFFF
+ #8C6F58
+ #FFFFFF
+ #40461E
+ #FFFFFF
+ #72784B
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #DA342E
+ #FFFFFF
+ #FFF8F5
+ #221A14
+ #FFF8F5
+ #221A14
+ #F3DFD1
+ #4D4037
+ #6B5C52
+ #87786D
+ #000000
+ #382F28
+ #FEEEE4
+ #FFB77B
+ #A26731
+ #FFFFFF
+ #854F1B
+ #FFFFFF
+ #8C6F58
+ #FFFFFF
+ #715741
+ #FFFFFF
+ #72784B
+ #FFFFFF
+ #596035
+ #FFFFFF
+ #E7D7CD
+ #FFF8F5
+ #FFFFFF
+ #FFF1E8
+ #FBEBE1
+ #F5E5DB
+ #EFE0D6
+ #381B00
+ #FFFFFF
+ #673702
+ #FFFFFF
+ #311E0C
+ #FFFFFF
+ #563E2A
+ #FFFFFF
+ #1F2502
+ #FFFFFF
+ #40461E
+ #FFFFFF
+ #4E0002
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #FFF8F5
+ #221A14
+ #FFF8F5
+ #000000
+ #F3DFD1
+ #2C2219
+ #4D4037
+ #4D4037
+ #000000
+ #382F28
+ #FFFFFF
+ #FFE8D8
+ #673702
+ #FFFFFF
+ #472400
+ #FFFFFF
+ #563E2A
+ #FFFFFF
+ #3D2816
+ #FFFFFF
+ #40461E
+ #FFFFFF
+ #2A2F0A
+ #FFFFFF
+ #E7D7CD
+ #FFF8F5
+ #FFFFFF
+ #FFF1E8
+ #FBEBE1
+ #F5E5DB
+ #EFE0D6
+
+ #604238
+ #FFFFFF
+ #87665A
+ #FFFFFF
+ #6B5B55
+ #FFFFFF
+ #F9E2DB
+ #574742
+ #4F4A2D
+ #FFFFFF
+ #746E4E
+ #FFFFFF
+ #BA1A1A
+ #FFFFFF
+ #FFDAD6
+ #410002
+ #FFF8F6
+ #1E1B1A
+ #FFF8F6
+ #1E1B1A
+ #F1DFD9
+ #504440
+ #827470
+ #D4C3BE
+ #000000
+ #33302E
+ #F7EFED
+ #E7BDB0
+ #FFDBCF
+ #2C160D
+ #E7BDB0
+ #5D4036
+ #F5DED6
+ #251915
+ #D8C2BB
+ #53433E
+ #ECE3BB
+ #201C04
+ #CFC7A1
+ #4C472A
+ #E0D8D6
+ #FFF8F6
+ #FFFFFF
+ #FAF2F0
+ #F4ECEA
+ #EFE6E4
+ #E9E1DF
+ #593C32
+ #FFFFFF
+ #87665A
+ #FFFFFF
+ #4F3F3B
+ #FFFFFF
+ #83716B
+ #FFFFFF
+ #484327
+ #FFFFFF
+ #746E4E
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #DA342E
+ #FFFFFF
+ #FFF8F6
+ #1E1B1A
+ #FFF8F6
+ #1E1B1A
+ #F1DFD9
+ #4C403D
+ #695C58
+ #867873
+ #000000
+ #33302E
+ #F7EFED
+ #E7BDB0
+ #8F6D61
+ #FFFFFF
+ #74554A
+ #FFFFFF
+ #83716B
+ #FFFFFF
+ #695853
+ #FFFFFF
+ #7B7554
+ #FFFFFF
+ #625C3E
+ #FFFFFF
+ #E0D8D6
+ #FFF8F6
+ #FFFFFF
+ #FAF2F0
+ #F4ECEA
+ #EFE6E4
+ #E9E1DF
+ #341C14
+ #FFFFFF
+ #593C32
+ #FFFFFF
+ #2C1F1B
+ #FFFFFF
+ #4F3F3B
+ #FFFFFF
+ #272209
+ #FFFFFF
+ #484327
+ #FFFFFF
+ #4E0002
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #FFF8F6
+ #1E1B1A
+ #FFF8F6
+ #000000
+ #F1DFD9
+ #2B221F
+ #4C403D
+ #4C403D
+ #000000
+ #33302E
+ #FFFFFF
+ #FFE7E0
+ #593C32
+ #FFFFFF
+ #40261D
+ #FFFFFF
+ #4F3F3B
+ #FFFFFF
+ #372A25
+ #FFFFFF
+ #484327
+ #FFFFFF
+ #312D12
+ #FFFFFF
+ #E0D8D6
+ #FFF8F6
+ #FFFFFF
+ #FAF2F0
+ #F4ECEA
+ #EFE6E4
+ #E9E1DF
+
+ #505151
+ #FFFFFF
+ #757575
+ #FFFFFF
+ #5F5E5E
+ #FFFFFF
+ #E6E3E2
+ #494848
+ #535052
+ #FFFFFF
+ #787476
+ #FFFFFF
+ #BA1A1A
+ #FFFFFF
+ #FFDAD6
+ #410002
+ #FCF8F8
+ #1C1B1B
+ #FCF8F8
+ #1C1B1B
+ #E0E3E3
+ #444748
+ #747878
+ #C4C7C7
+ #000000
+ #313030
+ #F4F0EF
+ #C7C6C6
+ #E3E2E2
+ #1B1C1C
+ #C7C6C6
+ #464747
+ #E5E2E1
+ #1B1C1C
+ #C8C6C5
+ #474746
+ #E7E1E3
+ #1D1B1D
+ #CBC5C7
+ #494648
+ #DDD9D8
+ #FCF8F8
+ #FFFFFF
+ #F7F3F2
+ #F1EDEC
+ #EBE7E7
+ #E5E2E1
+ #424343
+ #FFFFFF
+ #757575
+ #FFFFFF
+ #434343
+ #FFFFFF
+ #757474
+ #FFFFFF
+ #454244
+ #FFFFFF
+ #787476
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #DA342E
+ #FFFFFF
+ #FCF8F8
+ #1C1B1B
+ #FCF8F8
+ #1C1B1B
+ #E0E3E3
+ #404344
+ #5C6060
+ #787B7C
+ #000000
+ #313030
+ #F4F0EF
+ #C7C6C6
+ #747474
+ #FFFFFF
+ #5C5C5C
+ #FFFFFF
+ #757474
+ #FFFFFF
+ #5C5C5C
+ #FFFFFF
+ #777375
+ #FFFFFF
+ #5E5B5D
+ #FFFFFF
+ #DDD9D8
+ #FCF8F8
+ #FFFFFF
+ #F7F3F2
+ #F1EDEC
+ #EBE7E7
+ #E5E2E1
+ #212222
+ #FFFFFF
+ #424343
+ #FFFFFF
+ #222222
+ #FFFFFF
+ #434343
+ #FFFFFF
+ #242223
+ #FFFFFF
+ #454244
+ #FFFFFF
+ #4E0002
+ #FFFFFF
+ #8C0009
+ #FFFFFF
+ #FCF8F8
+ #1C1B1B
+ #FCF8F8
+ #000000
+ #E0E3E3
+ #212525
+ #404344
+ #404344
+ #000000
+ #313030
+ #FFFFFF
+ #EDECEB
+ #424343
+ #FFFFFF
+ #2C2D2D
+ #FFFFFF
+ #434343
+ #FFFFFF
+ #2D2D2D
+ #FFFFFF
+ #454244
+ #FFFFFF
+ #2E2C2E
+ #FFFFFF
+ #DDD9D8
+ #FCF8F8
+ #FFFFFF
+ #F7F3F2
+ #F1EDEC
+ #EBE7E7
+ #E5E2E1
+
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 140539c9b..cc1da0f4c 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -7,44 +7,43 @@
16dp
20dp
24dp
- 32dp
+ 28dp
- 48dp
- 56dp
- 128dp
- 192dp
- 256dp
+ 48dp
+ 56dp
+ 64dp
+ 76dp
+ 80dp
- 8dp
- 16dp
- 24dp
-
- 48dp
- 56dp
- 64dp
- 64dp
- 72dp
+ 128dp
+ 192dp
+ 256dp
24dp
32dp
+ 40dp
+ 48dp
- 56dp
+ 48dp
+ 48dp
+
+
+ 72dp
+ 52dp
+ @dimen/spacing_medium
+ 30dp
+ 48dp
+
+ 16dp
+ 128dp
14sp
22sp
2sp
-
- 3dp
-
- 78dp
- 64dp
- @dimen/spacing_medium
- 28dp
- 48dp
-
- 6dp
+ 10dp
+ 24dp
88dp
128dp
diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml
index 3256b7dca..a1cd46ef0 100644
--- a/app/src/main/res/values/donottranslate.xml
+++ b/app/src/main/res/values/donottranslate.xml
@@ -9,12 +9,15 @@
%d
%1$s (%2$s)
%s - %s
- %1$s/%2$s
Vorbis
Opus
Microsoft WAVE
+ Ogg %s
+
+
+ org.oxycblt.auxio.image.CoverProvider
\ No newline at end of file
diff --git a/app/src/main/res/values/settings.xml b/app/src/main/res/values/settings.xml
index 49dbceebd..901803a0c 100644
--- a/app/src/main/res/values/settings.xml
+++ b/app/src/main/res/values/settings.xml
@@ -14,10 +14,11 @@
auxio_rescan
auxio_observing
auxio_music_dirs
- auxio_cover_mode
+ auxio_cover_mode2
auxio_square_covers
auxio_include_dirs
auxio_exclude_non_music
+ auxio_music_locations2
auxio_separators
auxio_auto_sort_names
@@ -52,6 +53,8 @@
auxio_artist_sort
auxio_genre_sort
+ auxio_library_revision
+
- @string/set_theme_auto
- @string/set_theme_day
@@ -72,14 +75,16 @@
- @string/set_cover_mode_off
- - @string/set_cover_mode_media_store
- - @string/set_cover_mode_quality
+ - @string/set_cover_mode_save_space
+ - @string/set_cover_mode_balanced
+ - @string/set_cover_mode_high_quality
- @integer/cover_mode_off
- - @integer/cover_mode_media_store
- - @integer/cover_mode_quality
+ - @integer/cover_mode_save_space
+ - @integer/cover_mode_balanced
+ - @integer/cover_mode_high_quality
@@ -173,6 +178,7 @@
0xA11B
0xA11C
- 0xA11D
- 0xA11E
+ 0xA125
+ 0xA11D
+ 0xA11E
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 8bdd5fa2e..37e68cc51 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -16,6 +16,7 @@
Monitoring music library
Retry
+ Pick folders
More
@@ -171,13 +172,6 @@
Use Windows-compatible paths
-
- State saved
-
- State cleared
-
- State restored
-
About
Version
Source code
@@ -195,6 +189,9 @@
Author
Alexander Capehart
+ Feedback
+ Make an issue on GitHub
+ Send an email
Donate
Supporters
@@ -218,6 +215,11 @@
Starts Auxio using the previously saved state. If no saved state is available, all songs will be shuffled. Playback will start immediately.
\n\nWARNING: Be careful controlling this service, if you close it and then try to use it again, you will probably crash the app.
+ Your songs will show up here.
+ Your albums will show up here.
+ Your artists will show up here.
+ Your genres will show up here.
+ Your playlists will show up here.
@@ -280,8 +282,9 @@
Images
Album covers
Off
- Fast
- High quality
+ Save space
+ Balanced
+ High quality
Force square album covers
Crop all album covers to a 1:1 aspect ratio
@@ -309,31 +312,16 @@
Warning: Changing the pre-amp to a high positive value may result in peaking on some audio tracks.
Library
- Music folders
- Manage where music should be loaded from
- Folders
-
- Mode
-
- Exclude
- Music will not be loaded from the folders you add.
-
- Include
- Music will only be loaded from the folders you add.
+ Music folders
+ Manage where music should be loaded from
+ Folders
+ New folder
Refresh music
Reload the music library, using cached tags when possible
Rescan music
Clear the tag cache and fully reload the music library (slower, but more complete)
- Persistence
- Save playback state
- Save the current playback state now
- Clear playback state
- Clear the previously saved playback state (if any)
- Restore playback state
- Restore the previously saved playback state (if any)
-
No music found
Music loading failed
@@ -342,14 +330,8 @@
Unable to export the playlist to this file
No app found that can handle this task
- No folders
- This folder is not supported
-
- Unable to restore state
-
- Unable to clear state
-
- Unable to save state
+ No folders
+ This folder is not supported
@@ -361,7 +343,6 @@
Change repeat mode
Turn shuffle on or off
Shuffle all songs
- Create a new playlist
Stop playback
Remove this song
@@ -369,7 +350,7 @@
Open the queue
Move this tab
Clear search query
- Remove folder
+ Remove folder
Auxio icon
Album cover
@@ -382,6 +363,7 @@
+ Unknown album
Unknown artist
Unknown genre
No date
@@ -398,14 +380,20 @@
MPEG-1 audio
MPEG-4 audio
+
+ MPEG-4 containing %s
+
+ Advanced Audio Coding (AAC)
+
+ Apple Lossless Audio Codec (ALAC)
Ogg audio
Matroska audio
-
- Advanced Audio Coding (AAC)
Free Lossless Audio Codec (FLAC)
+
+ Unknown
diff --git a/app/src/main/res/values/styles_android.xml b/app/src/main/res/values/styles_android.xml
index 98812140d..e31f6660e 100644
--- a/app/src/main/res/values/styles_android.xml
+++ b/app/src/main/res/values/styles_android.xml
@@ -7,7 +7,7 @@
56dp to 48dp.
-->
@@ -85,13 +85,13 @@
diff --git a/app/src/main/res/values/styles_core.xml b/app/src/main/res/values/styles_core.xml
index f43098404..6cc3b58da 100644
--- a/app/src/main/res/values/styles_core.xml
+++ b/app/src/main/res/values/styles_core.xml
@@ -18,7 +18,7 @@
+
+
+
+
+
+
+
+
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 74061c215..3667d53f9 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -1,486 +1,786 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/res/xml/preferences_music.xml b/app/src/main/res/xml/preferences_music.xml
index 86a3a6b03..607f2c7f2 100644
--- a/app/src/main/res/xml/preferences_music.xml
+++ b/app/src/main/res/xml/preferences_music.xml
@@ -37,7 +37,7 @@
+ app:summary="@string/set_locations_desc"
+ app:title="@string/set_locations" />
diff --git a/app/src/test/java/org/oxycblt/auxio/music/cache/CacheRepositoryTest.kt b/app/src/test/java/org/oxycblt/auxio/music/cache/CacheRepositoryTest.kt
deleted file mode 100644
index 9914dbe5f..000000000
--- a/app/src/test/java/org/oxycblt/auxio/music/cache/CacheRepositoryTest.kt
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * Copyright (c) 2023 Auxio Project
- * CacheRepositoryTest.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 .
- */
-
-package org.oxycblt.auxio.music.cache
-
-import io.mockk.Runs
-import io.mockk.coEvery
-import io.mockk.coVerifyAll
-import io.mockk.coVerifySequence
-import io.mockk.just
-import io.mockk.mockk
-import io.mockk.slot
-import java.lang.IllegalStateException
-import kotlinx.coroutines.runBlocking
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
-import org.junit.Test
-import org.oxycblt.auxio.music.device.RawSong
-import org.oxycblt.auxio.music.info.Date
-
-class CacheRepositoryTest {
- @Test
- fun cache_read_noInvalidate() {
- val dao =
- mockk {
- coEvery { readSongs() }.returnsMany(listOf(CACHED_SONG_A, CACHED_SONG_B))
- }
- val cacheRepository = CacheRepositoryImpl(dao)
- val cache = requireNotNull(runBlocking { cacheRepository.readCache() })
- coVerifyAll { dao.readSongs() }
- assertFalse(cache.invalidated)
-
- val songA = RawSong(mediaStoreId = 0, dateAdded = 1, dateModified = 2)
- assertTrue(cache.populate(songA))
- assertEquals(RAW_SONG_A, songA)
-
- assertFalse(cache.invalidated)
-
- val songB = RawSong(mediaStoreId = 9, dateAdded = 10, dateModified = 11)
- assertTrue(cache.populate(songB))
- assertEquals(RAW_SONG_B, songB)
-
- assertFalse(cache.invalidated)
- }
-
- @Test
- fun cache_read_invalidate() {
- val dao =
- mockk {
- coEvery { readSongs() }.returnsMany(listOf(CACHED_SONG_A, CACHED_SONG_B))
- }
- val cacheRepository = CacheRepositoryImpl(dao)
- val cache = requireNotNull(runBlocking { cacheRepository.readCache() })
- coVerifyAll { dao.readSongs() }
- assertFalse(cache.invalidated)
-
- val nullStart = RawSong(mediaStoreId = 0, dateAdded = 0, dateModified = 0)
- val nullEnd = RawSong(mediaStoreId = 0, dateAdded = 0, dateModified = 0)
- assertFalse(cache.populate(nullStart))
- assertEquals(nullStart, nullEnd)
-
- assertTrue(cache.invalidated)
-
- val songB = RawSong(mediaStoreId = 9, dateAdded = 10, dateModified = 11)
- assertTrue(cache.populate(songB))
- assertEquals(RAW_SONG_B, songB)
-
- assertTrue(cache.invalidated)
- }
-
- @Test
- fun cache_read_crashes() {
- val dao = mockk { coEvery { readSongs() } throws IllegalStateException() }
- val cacheRepository = CacheRepositoryImpl(dao)
- assertEquals(null, runBlocking { cacheRepository.readCache() })
- coVerifyAll { dao.readSongs() }
- }
-
- @Test
- fun cache_write() {
- var currentlyStoredSongs = listOf()
- val insertSongsArg = slot>()
- val dao =
- mockk {
- coEvery { nukeSongs() } answers { currentlyStoredSongs = listOf() }
-
- coEvery { insertSongs(capture(insertSongsArg)) } answers
- {
- currentlyStoredSongs = insertSongsArg.captured
- }
- }
-
- val cacheRepository = CacheRepositoryImpl(dao)
-
- val rawSongs = listOf(RAW_SONG_A, RAW_SONG_B)
- runBlocking { cacheRepository.writeCache(rawSongs) }
-
- val cachedSongs = listOf(CACHED_SONG_A, CACHED_SONG_B)
- coVerifySequence {
- dao.nukeSongs()
- dao.insertSongs(cachedSongs)
- }
- assertEquals(cachedSongs, currentlyStoredSongs)
- }
-
- @Test
- fun cache_write_nukeCrashes() {
- val dao =
- mockk {
- coEvery { nukeSongs() } throws IllegalStateException()
- coEvery { insertSongs(listOf()) } just Runs
- }
- val cacheRepository = CacheRepositoryImpl(dao)
- runBlocking { cacheRepository.writeCache(listOf()) }
- coVerifyAll { dao.nukeSongs() }
- }
-
- @Test
- fun cache_write_insertCrashes() {
- val dao =
- mockk {
- coEvery { nukeSongs() } just Runs
- coEvery { insertSongs(listOf()) } throws IllegalStateException()
- }
- val cacheRepository = CacheRepositoryImpl(dao)
- runBlocking { cacheRepository.writeCache(listOf()) }
- coVerifySequence {
- dao.nukeSongs()
- dao.insertSongs(listOf())
- }
- }
-
- private companion object {
- val CACHED_SONG_A =
- CachedSong(
- mediaStoreId = 0,
- dateAdded = 1,
- dateModified = 2,
- size = 3,
- durationMs = 4,
- replayGainTrackAdjustment = 5.5f,
- replayGainAlbumAdjustment = 6.6f,
- musicBrainzId = "Song MBID A",
- name = "Song Name A",
- sortName = "Song Sort Name A",
- track = 7,
- disc = 8,
- subtitle = "Subtitle A",
- date = Date.from("2020-10-10"),
- albumMusicBrainzId = "Album MBID A",
- albumName = "Album Name A",
- albumSortName = "Album Sort Name A",
- releaseTypes = listOf("Release Type A"),
- artistMusicBrainzIds = listOf("Artist MBID A"),
- artistNames = listOf("Artist Name A"),
- artistSortNames = listOf("Artist Sort Name A"),
- albumArtistMusicBrainzIds = listOf("Album Artist MBID A"),
- albumArtistNames = listOf("Album Artist Name A"),
- albumArtistSortNames = listOf("Album Artist Sort Name A"),
- genreNames = listOf("Genre Name A"),
- )
-
- val RAW_SONG_A =
- RawSong(
- mediaStoreId = 0,
- dateAdded = 1,
- dateModified = 2,
- size = 3,
- durationMs = 4,
- replayGainTrackAdjustment = 5.5f,
- replayGainAlbumAdjustment = 6.6f,
- musicBrainzId = "Song MBID A",
- name = "Song Name A",
- sortName = "Song Sort Name A",
- track = 7,
- disc = 8,
- subtitle = "Subtitle A",
- date = Date.from("2020-10-10"),
- albumMusicBrainzId = "Album MBID A",
- albumName = "Album Name A",
- albumSortName = "Album Sort Name A",
- releaseTypes = listOf("Release Type A"),
- artistMusicBrainzIds = listOf("Artist MBID A"),
- artistNames = listOf("Artist Name A"),
- artistSortNames = listOf("Artist Sort Name A"),
- albumArtistMusicBrainzIds = listOf("Album Artist MBID A"),
- albumArtistNames = listOf("Album Artist Name A"),
- albumArtistSortNames = listOf("Album Artist Sort Name A"),
- genreNames = listOf("Genre Name A"),
- )
-
- val CACHED_SONG_B =
- CachedSong(
- mediaStoreId = 9,
- dateAdded = 10,
- dateModified = 11,
- size = 12,
- durationMs = 13,
- replayGainTrackAdjustment = 14.14f,
- replayGainAlbumAdjustment = 15.15f,
- musicBrainzId = "Song MBID B",
- name = "Song Name B",
- sortName = "Song Sort Name B",
- track = 16,
- disc = 17,
- subtitle = "Subtitle B",
- date = Date.from("2021-11-11"),
- albumMusicBrainzId = "Album MBID B",
- albumName = "Album Name B",
- albumSortName = "Album Sort Name B",
- releaseTypes = listOf("Release Type B"),
- artistMusicBrainzIds = listOf("Artist MBID B"),
- artistNames = listOf("Artist Name B"),
- artistSortNames = listOf("Artist Sort Name B"),
- albumArtistMusicBrainzIds = listOf("Album Artist MBID B"),
- albumArtistNames = listOf("Album Artist Name B"),
- albumArtistSortNames = listOf("Album Artist Sort Name B"),
- genreNames = listOf("Genre Name B"),
- )
-
- val RAW_SONG_B =
- RawSong(
- mediaStoreId = 9,
- dateAdded = 10,
- dateModified = 11,
- size = 12,
- durationMs = 13,
- replayGainTrackAdjustment = 14.14f,
- replayGainAlbumAdjustment = 15.15f,
- musicBrainzId = "Song MBID B",
- name = "Song Name B",
- sortName = "Song Sort Name B",
- track = 16,
- disc = 17,
- subtitle = "Subtitle B",
- date = Date.from("2021-11-11"),
- albumMusicBrainzId = "Album MBID B",
- albumName = "Album Name B",
- albumSortName = "Album Sort Name B",
- releaseTypes = listOf("Release Type B"),
- artistMusicBrainzIds = listOf("Artist MBID B"),
- artistNames = listOf("Artist Name B"),
- artistSortNames = listOf("Artist Sort Name B"),
- albumArtistMusicBrainzIds = listOf("Album Artist MBID B"),
- albumArtistNames = listOf("Album Artist Name B"),
- albumArtistSortNames = listOf("Album Artist Sort Name B"),
- genreNames = listOf("Genre Name B"),
- )
- }
-}
diff --git a/app/src/test/java/org/oxycblt/auxio/music/metadata/TextTagsTest.kt b/app/src/test/java/org/oxycblt/auxio/music/metadata/TextTagsTest.kt
deleted file mode 100644
index 9966c16e9..000000000
--- a/app/src/test/java/org/oxycblt/auxio/music/metadata/TextTagsTest.kt
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (c) 2023 Auxio Project
- * TextTagsTest.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 .
- */
-
-package org.oxycblt.auxio.music.metadata
-
-import androidx.media3.common.Metadata
-import androidx.media3.extractor.metadata.flac.PictureFrame
-import androidx.media3.extractor.metadata.id3.ApicFrame
-import androidx.media3.extractor.metadata.id3.InternalFrame
-import androidx.media3.extractor.metadata.id3.TextInformationFrame
-import androidx.media3.extractor.metadata.vorbis.VorbisComment
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertTrue
-import org.junit.Test
-
-class TextTagsTest {
- @Test
- fun textTags_vorbis() {
- val textTags = TextTags(VORBIS_METADATA)
- assertTrue(textTags.id3v2.isEmpty())
- assertEquals(listOf("Wheel"), textTags.vorbis["title"])
- assertEquals(listOf("Paraglow"), textTags.vorbis["album"])
- assertEquals(listOf("Parannoul", "Asian Glow"), textTags.vorbis["artist"])
- assertEquals(listOf("2022"), textTags.vorbis["date"])
- assertEquals(listOf("ep"), textTags.vorbis["releasetype"])
- assertEquals(listOf("+2 dB"), textTags.vorbis["replaygain_track_gain"])
- assertEquals(null, textTags.id3v2["APIC"])
- }
-
- @Test
- fun textTags_id3v2() {
- val textTags = TextTags(ID3V2_METADATA)
- assertTrue(textTags.vorbis.isEmpty())
- assertEquals(listOf("Wheel"), textTags.id3v2["TIT2"])
- assertEquals(listOf("Paraglow"), textTags.id3v2["TALB"])
- assertEquals(listOf("Parannoul", "Asian Glow"), textTags.id3v2["TPE1"])
- assertEquals(listOf("2022"), textTags.id3v2["TDRC"])
- assertEquals(listOf("ep"), textTags.id3v2["TXXX:musicbrainz album type"])
- assertEquals(listOf("+2 dB"), textTags.id3v2["TXXX:replaygain_track_gain"])
- assertEquals(null, textTags.id3v2["metadata_block_picture"])
- }
-
- @Test
- fun textTags_mp4() {
- val textTags = TextTags(MP4_METADATA)
- assertTrue(textTags.vorbis.isEmpty())
- assertEquals(listOf("Wheel"), textTags.id3v2["TIT2"])
- assertEquals(listOf("Paraglow"), textTags.id3v2["TALB"])
- assertEquals(listOf("Parannoul", "Asian Glow"), textTags.id3v2["TPE1"])
- assertEquals(listOf("2022"), textTags.id3v2["TDRC"])
- assertEquals(listOf("ep"), textTags.id3v2["TXXX:musicbrainz album type"])
- assertEquals(listOf("+2 dB"), textTags.id3v2["TXXX:replaygain_track_gain"])
- assertEquals(null, textTags.id3v2["metadata_block_picture"])
- }
-
- @Test
- fun textTags_id3v2_vorbis_combined() {
- val textTags = TextTags(VORBIS_METADATA.copyWithAppendedEntriesFrom(ID3V2_METADATA))
- assertEquals(listOf("Wheel"), textTags.vorbis["title"])
- assertEquals(listOf("Paraglow"), textTags.vorbis["album"])
- assertEquals(listOf("Parannoul", "Asian Glow"), textTags.vorbis["artist"])
- assertEquals(listOf("2022"), textTags.vorbis["date"])
- assertEquals(listOf("ep"), textTags.vorbis["releasetype"])
- assertEquals(listOf("+2 dB"), textTags.vorbis["replaygain_track_gain"])
- assertEquals(null, textTags.id3v2["metadata_block_picture"])
-
- assertEquals(listOf("Wheel"), textTags.id3v2["TIT2"])
- assertEquals(listOf("Paraglow"), textTags.id3v2["TALB"])
- assertEquals(listOf("Parannoul", "Asian Glow"), textTags.id3v2["TPE1"])
- assertEquals(listOf("2022"), textTags.id3v2["TDRC"])
- assertEquals(null, textTags.id3v2["APIC"])
- assertEquals(listOf("ep"), textTags.id3v2["TXXX:musicbrainz album type"])
- assertEquals(listOf("+2 dB"), textTags.id3v2["TXXX:replaygain_track_gain"])
- }
-
- companion object {
- private val VORBIS_METADATA =
- Metadata(
- VorbisComment("TITLE", "Wheel"),
- VorbisComment("ALBUM", "Paraglow"),
- VorbisComment("ARTIST", "Parannoul"),
- VorbisComment("ARTIST", "Asian Glow"),
- VorbisComment("DATE", "2022"),
- VorbisComment("RELEASETYPE", "ep"),
- VorbisComment("METADATA_BLOCK_PICTURE", ""),
- VorbisComment("REPLAYGAIN_TRACK_GAIN", "+2 dB"),
- PictureFrame(0, "", "", 0, 0, 0, 0, byteArrayOf()))
-
- private val ID3V2_METADATA =
- Metadata(
- TextInformationFrame("TIT2", null, listOf("Wheel")),
- TextInformationFrame("TALB", null, listOf("Paraglow")),
- TextInformationFrame("TPE1", null, listOf("Parannoul", "Asian Glow")),
- TextInformationFrame("TDRC", null, listOf("2022")),
- TextInformationFrame("TXXX", "MusicBrainz Album Type", listOf("ep")),
- TextInformationFrame("TXXX", "replaygain_track_gain", listOf("+2 dB")),
- ApicFrame("", "", 0, byteArrayOf()))
-
- // MP4 atoms are mapped to ID3v2 text information frames by ExoPlayer, but can
- // duplicate frames and have ---- mapped to InternalFrame.
- private val MP4_METADATA =
- Metadata(
- TextInformationFrame("TIT2", null, listOf("Wheel")),
- TextInformationFrame("TALB", null, listOf("Paraglow")),
- TextInformationFrame("TPE1", null, listOf("Parannoul")),
- TextInformationFrame("TPE1", null, listOf("Asian Glow")),
- TextInformationFrame("TDRC", null, listOf("2022")),
- TextInformationFrame("TXXX", "MusicBrainz Album Type", listOf("ep")),
- InternalFrame("com.apple.iTunes", "replaygain_track_gain", "+2 dB"),
- ApicFrame("", "", 0, byteArrayOf()))
- }
-}
diff --git a/app/src/test/java/org/oxycblt/auxio/music/user/DeviceLibraryTest.kt b/app/src/test/java/org/oxycblt/auxio/music/user/DeviceLibraryTest.kt
deleted file mode 100644
index e89c8d241..000000000
--- a/app/src/test/java/org/oxycblt/auxio/music/user/DeviceLibraryTest.kt
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (c) 2023 Auxio Project
- * DeviceLibraryTest.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 .
- */
-
-package org.oxycblt.auxio.music.user
-
-import io.mockk.every
-import io.mockk.mockk
-import io.mockk.verify
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertNotEquals
-import org.junit.Test
-import org.oxycblt.auxio.music.Music
-import org.oxycblt.auxio.music.MusicType
-import org.oxycblt.auxio.music.device.AlbumImpl
-import org.oxycblt.auxio.music.device.ArtistImpl
-import org.oxycblt.auxio.music.device.DeviceLibraryImpl
-import org.oxycblt.auxio.music.device.GenreImpl
-import org.oxycblt.auxio.music.device.SongImpl
-import org.oxycblt.auxio.music.fs.Components
-import org.oxycblt.auxio.music.fs.Path
-
-class DeviceLibraryTest {
-
- @Test
- fun deviceLibrary_withSongs() {
- val songUidA = Music.UID.auxio(MusicType.SONGS)
- val songUidB = Music.UID.auxio(MusicType.SONGS)
- val songA =
- mockk {
- every { uid } returns songUidA
- every { durationMs } returns 0
- every { path } returns Path(mockk(), Components.parseUnix("./"))
- every { finalize() } returns this
- }
- val songB =
- mockk {
- every { uid } returns songUidB
- every { durationMs } returns 1
- every { path } returns Path(mockk(), Components.parseUnix("./"))
- every { finalize() } returns this
- }
- val deviceLibrary = DeviceLibraryImpl(listOf(songA, songB), listOf(), listOf(), listOf())
- verify {
- songA.finalize()
- songB.finalize()
- }
- val foundSongA = deviceLibrary.findSong(songUidA)!!
- assertEquals(songUidA, foundSongA.uid)
- assertEquals(0L, foundSongA.durationMs)
- val foundSongB = deviceLibrary.findSong(songUidB)!!
- assertEquals(songUidB, foundSongB.uid)
- assertEquals(1L, foundSongB.durationMs)
- }
-
- @Test
- fun deviceLibrary_withAlbums() {
- val albumUidA = Music.UID.auxio(MusicType.ALBUMS)
- val albumUidB = Music.UID.auxio(MusicType.ALBUMS)
- val albumA =
- mockk {
- every { uid } returns albumUidA
- every { durationMs } returns 0
- every { finalize() } returns this
- }
- val albumB =
- mockk {
- every { uid } returns albumUidB
- every { durationMs } returns 1
- every { finalize() } returns this
- }
- val deviceLibrary = DeviceLibraryImpl(listOf(), listOf(albumA, albumB), listOf(), listOf())
- verify {
- albumA.finalize()
- albumB.finalize()
- }
- val foundAlbumA = deviceLibrary.findAlbum(albumUidA)!!
- assertEquals(albumUidA, foundAlbumA.uid)
- assertEquals(0L, foundAlbumA.durationMs)
- val foundAlbumB = deviceLibrary.findAlbum(albumUidB)!!
- assertEquals(albumUidB, foundAlbumB.uid)
- assertEquals(1L, foundAlbumB.durationMs)
- }
-
- @Test
- fun deviceLibrary_withArtists() {
- val artistUidA = Music.UID.auxio(MusicType.ARTISTS)
- val artistUidB = Music.UID.auxio(MusicType.ARTISTS)
- val artistA =
- mockk {
- every { uid } returns artistUidA
- every { durationMs } returns 0
- every { finalize() } returns this
- }
- val artistB =
- mockk {
- every { uid } returns artistUidB
- every { durationMs } returns 1
- every { finalize() } returns this
- }
- val deviceLibrary =
- DeviceLibraryImpl(listOf(), listOf(), listOf(artistA, artistB), listOf())
- verify {
- artistA.finalize()
- artistB.finalize()
- }
- val foundArtistA = deviceLibrary.findArtist(artistUidA)!!
- assertEquals(artistUidA, foundArtistA.uid)
- assertEquals(0L, foundArtistA.durationMs)
- val foundArtistB = deviceLibrary.findArtist(artistUidB)!!
- assertEquals(artistUidB, foundArtistB.uid)
- assertEquals(1L, foundArtistB.durationMs)
- }
-
- @Test
- fun deviceLibrary_withGenres() {
- val genreUidA = Music.UID.auxio(MusicType.GENRES)
- val genreUidB = Music.UID.auxio(MusicType.GENRES)
- val genreA =
- mockk {
- every { uid } returns genreUidA
- every { durationMs } returns 0
- every { finalize() } returns this
- }
- val genreB =
- mockk {
- every { uid } returns genreUidB
- every { durationMs } returns 1
- every { finalize() } returns this
- }
- val deviceLibrary = DeviceLibraryImpl(listOf(), listOf(), listOf(), listOf(genreA, genreB))
- verify {
- genreA.finalize()
- genreB.finalize()
- }
- val foundGenreA = deviceLibrary.findGenre(genreUidA)!!
- assertEquals(genreUidA, foundGenreA.uid)
- assertEquals(0L, foundGenreA.durationMs)
- val foundGenreB = deviceLibrary.findGenre(genreUidB)!!
- assertEquals(genreUidB, foundGenreB.uid)
- assertEquals(1L, foundGenreB.durationMs)
- }
-
- @Test
- fun deviceLibrary_equals() {
- val songA =
- mockk {
- every { uid } returns Music.UID.auxio(MusicType.SONGS)
- every { path } returns Path(mockk(), Components.parseUnix("./"))
- every { finalize() } returns this
- }
- val songB =
- mockk {
- every { uid } returns Music.UID.auxio(MusicType.SONGS)
- every { path } returns Path(mockk(), Components.parseUnix("./"))
- every { finalize() } returns this
- }
- val album =
- mockk {
- every { uid } returns mockk()
- every { finalize() } returns this
- }
-
- val deviceLibraryA = DeviceLibraryImpl(listOf(songA), listOf(album), listOf(), listOf())
- val deviceLibraryB = DeviceLibraryImpl(listOf(songA), listOf(), listOf(), listOf())
- val deviceLibraryC = DeviceLibraryImpl(listOf(songB), listOf(album), listOf(), listOf())
- assertEquals(deviceLibraryA, deviceLibraryB)
- assertEquals(deviceLibraryA.hashCode(), deviceLibraryA.hashCode())
- assertNotEquals(deviceLibraryA, deviceLibraryC)
- assertNotEquals(deviceLibraryA.hashCode(), deviceLibraryC.hashCode())
- }
-}
diff --git a/build.gradle b/build.gradle
index a81768d25..7bc5b84a9 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,24 +1,50 @@
buildscript {
ext {
- kotlin_version = '1.9.23'
- navigation_version = "2.5.3"
+ agp_version = '8.7.3'
+ kotlin_version = '2.0.21'
+ kotlin_coroutines_version = '1.10.1'
+ navigation_version = "2.8.3"
hilt_version = '2.51.1'
+ room_version = '2.6.1'
+ core_version = '1.15.0'
+ desugaring_version = '2.1.3'
+
+ min_sdk = 24
+ target_sdk = 35
+ ndk_version = "27.2.12479018"
+
}
dependencies {
// Hilt isn't compatible with the new plugin syntax yet.
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
+ classpath "org.jetbrains.dokka:dokka-gradle-plugin:2.0.0"
}
}
plugins {
- id "com.android.application" version '8.4.0' apply false
+ // Android studio doesn't understand this syntax
+ //noinspection GradlePluginVersion
+ id "com.android.application" version "$agp_version" apply false
id "androidx.navigation.safeargs.kotlin" version "$navigation_version" apply false
+ //noinspection GradlePluginVersion
+ id 'com.android.library' version "$agp_version" apply false
id "org.jetbrains.kotlin.android" version "$kotlin_version" apply false
- id "com.google.devtools.ksp" version '1.9.23-1.0.20' apply false
- id "com.diffplug.spotless" version "6.20.0" apply false
+ id "com.google.devtools.ksp" version '2.0.21-1.0.25' apply false
+ // We use spotless in the root build.gradle to apply to all modules.
+ id "com.diffplug.spotless" version "6.25.0" apply true
}
-tasks.register('clean', Delete) {
- delete rootProject.buildDir
-}
\ No newline at end of file
+spotless {
+ kotlin {
+ target "*/src/**/*.kt"
+ ktfmt().dropboxStyle()
+ licenseHeaderFile("NOTICE")
+ }
+
+ cpp {
+ target("*/src/**/cpp/*.cpp", "*/src/**/cpp/*.h", "*/src/**/cpp/*.hpp")
+ eclipseCdt().configFile("eclipse-cdt.xml")
+ licenseHeaderFile("NOTICE")
+ }
+}
diff --git a/eclipse-cdt.xml b/eclipse-cdt.xml
new file mode 100644
index 000000000..4e6c20576
--- /dev/null
+++ b/eclipse-cdt.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/fastlane/metadata/android/ar-IQ/full_description.txt b/fastlane/metadata/android/ar-IQ/full_description.txt
new file mode 100644
index 000000000..3a19d3c9b
--- /dev/null
+++ b/fastlane/metadata/android/ar-IQ/full_description.txt
@@ -0,0 +1,10 @@
+هذا النص يصف تطبيق كمشغل موسيقى محلي يتميز بواجهة مستخدم سريعة وموثوقة بدون الميزات غير الضرورية الموجودة في مشغلات الموسيقى الأخرى. يعتمد على مكتبات تشغيل الوسائط الحديثة ويقدم جودة مكتبة واستماع أفضل مقارنة بالتطبيقات التي تستخدم وظائف أندرويد قديمة. يتضمن ميزات مثل:
+
+- تشغيل مستند على
+- واجهة مستخدم حديثة
+- إدارة مجلدات تدعم بطاقات SD
+- دعم Android Auto
+- تشغيل بلا فجوات
+
+
+
diff --git a/fastlane/metadata/android/ar-IQ/short_description.txt b/fastlane/metadata/android/ar-IQ/short_description.txt
new file mode 100644
index 000000000..3f1d0c14f
--- /dev/null
+++ b/fastlane/metadata/android/ar-IQ/short_description.txt
@@ -0,0 +1 @@
+مشغل موسيقى بسيط وعقلاني
diff --git a/fastlane/metadata/android/ar-SA/full_description.txt b/fastlane/metadata/android/ar-SA/full_description.txt
new file mode 100644
index 000000000..637993ae6
--- /dev/null
+++ b/fastlane/metadata/android/ar-SA/full_description.txt
@@ -0,0 +1,7 @@
+هذا النص يصف تطبيق كمشغل موسيقى محلي يتميز بواجهة مستخدم سريعة وموثوقة بدون الميزات غير الضرورية الموجودة في مشغلات الموسيقى الأخرى. يعتمد على مكتبات تشغيل الوسائط الحديثة ويقدم جودة مكتبة واستماع أفضل مقارنة بالتطبيقات التي تستخدم وظائف أندرويد قديمة. يتضمن ميزات مثل:
+
+- تشغيل مستند على
+- واجهة مستخدم حديثة
+- إدارة مجلدات تدعم بطاقات
+- تشغيل بلا فجوات
+-
diff --git a/fastlane/metadata/android/ar-SA/short_description.txt b/fastlane/metadata/android/ar-SA/short_description.txt
new file mode 100644
index 000000000..3f1d0c14f
--- /dev/null
+++ b/fastlane/metadata/android/ar-SA/short_description.txt
@@ -0,0 +1 @@
+مشغل موسيقى بسيط وعقلاني
diff --git a/fastlane/metadata/android/az/short_description.txt b/fastlane/metadata/android/az/short_description.txt
new file mode 100644
index 000000000..66ca50172
--- /dev/null
+++ b/fastlane/metadata/android/az/short_description.txt
@@ -0,0 +1 @@
+Sadə, səmərəli musiqi səsləndirici
diff --git a/fastlane/metadata/android/bg/full_description.txt b/fastlane/metadata/android/bg/full_description.txt
new file mode 100644
index 000000000..b959b6bc8
--- /dev/null
+++ b/fastlane/metadata/android/bg/full_description.txt
@@ -0,0 +1,25 @@
+Auxio е локален музикален плейър с бърз, надежден UI/UX без много безполезни функции, присъстващи в други музикални плейъри. Изграден от модерни библиотеки за възпроизвеждане на мултимедия, Auxio има превъзходна поддръжка на библиотека и качество на слушане в сравнение с други приложения, които използват остаряла функционалност на Android. Накратко,Възпроизвежда музика.
+
+Характеристики
+
+- Възпроизвеждане на базата на Media3 ExoPlayer
+- Snappy UI, извлечен от най-новите указания за Material Design
+- Убеден UX, който дава приоритет на лекотата на използване пред крайните случаи
+- Персонализирано поведение
+- Поддръжка за номера на дискове, множество изпълнители, типове издания,
+точни/оригинални дати, етикети за сортиране и др
+- Усъвършенствана система за изпълнители, която обединява изпълнители и изпълнители на албуми
+- Управление на папки с SD карта
+- Надеждна функционалност за плейлисти
+- Устойчивост на състоянието на възпроизвеждане
+- Android автоматична поддръжка
+- Автоматично възпроизвеждане без пропуски
+- Пълна поддръжка на ReplayGain (на MP3, FLAC, OGG, OPUS и MP4 файлове)
+- Поддръжка на външен еквалайзер (напр. Wavelet)
+- От край до край
+- Поддръжка на вградени корици
+- Функция за търсене
+- Автоматично пускане на слушалки
+- Стилни джаджи, които автоматично се адаптират към техния размер
+- Напълно частно и офлайн
+- Без заоблени корици на албуми (по подразбиране)
diff --git a/fastlane/metadata/android/bg/short_description.txt b/fastlane/metadata/android/bg/short_description.txt
new file mode 100644
index 000000000..b53e6a67c
--- /dev/null
+++ b/fastlane/metadata/android/bg/short_description.txt
@@ -0,0 +1 @@
+Прост, рационален музикален плейър
diff --git a/fastlane/metadata/android/cs/full_description.txt b/fastlane/metadata/android/cs/full_description.txt
index f47808edf..e1e6af29c 100644
--- a/fastlane/metadata/android/cs/full_description.txt
+++ b/fastlane/metadata/android/cs/full_description.txt
@@ -12,6 +12,7 @@ přesná/původní data, štítky pro řazení a další
- Správa složek podporující SD karty
- Spolehlivá funkce seznamů skladeb
- Uchovávání stavu přehrávání
+- Podpora Android Auto
- Automatické přehrávání bez mezer
- Plná podpora ReplayGain (u souborů MP3, FLAC, OGG, OPUS a MP4)
- Podpora externích přehrávačů (např. Wavelet)
@@ -21,4 +22,4 @@ přesná/původní data, štítky pro řazení a další
- Automatické přehrávání při připojení sluchátek
- Stylové widgety, které se automaticky adaptují své velikosti
- Plně soukromý a offline
-- Žádné zakulacené obaly alb (ve výchozím nastavení)
+- Žádné zaoblené obaly alb (ve výchozím nastavení)
diff --git a/fastlane/metadata/android/cy/short_description.txt b/fastlane/metadata/android/cy/short_description.txt
new file mode 100644
index 000000000..f69dd656d
--- /dev/null
+++ b/fastlane/metadata/android/cy/short_description.txt
@@ -0,0 +1 @@
+Chwaraewr cerddoriaeth syml a synhwyrol
diff --git a/fastlane/metadata/android/de/full_description.txt b/fastlane/metadata/android/de/full_description.txt
index fc52bfe41..62f87e1ad 100644
--- a/fastlane/metadata/android/de/full_description.txt
+++ b/fastlane/metadata/android/de/full_description.txt
@@ -13,6 +13,7 @@ Auxio ist ein lokaler Musik-Player mit einer schnellen, verlässlichen UI/UX, ab
- verlässliche Wiedergabelisten-Verwaltung
- verlässliches Speichern des Wiedergabezustands
- automatische lückenlose Wiedergabe
+- Unterstützung für Android Auto
- Vollständiger ReplayGain-Support (für MP3-, FLAC-, OGG-, OPUS- und MP4-Dateien)
- Externer Equalizerunterstützung (z.B. Wavelet)
- Edge-to-Edge
diff --git a/fastlane/metadata/android/el/short_description.txt b/fastlane/metadata/android/el/short_description.txt
new file mode 100644
index 000000000..752cd28a5
--- /dev/null
+++ b/fastlane/metadata/android/el/short_description.txt
@@ -0,0 +1 @@
+Μία απλή, λογική συσκευή αναπαραγωγής μουσικής
diff --git a/fastlane/metadata/android/en-US/changelogs/59.txt b/fastlane/metadata/android/en-US/changelogs/59.txt
new file mode 100644
index 000000000..22e2eff4d
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/59.txt
@@ -0,0 +1,3 @@
+Auxio 4.0.0 completely overhauls the user experience, with a refreshed design based on the latest Material Design specs
+and a brand new music loader with signifigant improvements to device and tag support.
+For more information, see https://github.com/OxygenCobalt/Auxio/releases/tag/v4.0.0.
diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt
index c4a9fe74c..2ac55755c 100644
--- a/fastlane/metadata/android/en-US/full_description.txt
+++ b/fastlane/metadata/android/en-US/full_description.txt
@@ -6,8 +6,7 @@ Auxio is a local music player with a fast, reliable UI/UX without the many usele
- Snappy UI derived from the latest Material Design guidelines
- Opinionated UX that prioritizes ease of use over edge cases
- Customizable behavior
-- Support for disc numbers, multiple artists, release types,
-precise/original dates, sort tags, and more
+- Support for disc numbers, multiple artists, release types, precise/original dates, sort tags, and more
- Advanced artist system that unifies artists and album artists
- SD Card-aware folder management
- Reliable playlisting functionality
@@ -22,4 +21,4 @@ precise/original dates, sort tags, and more
- Headset autoplay
- Stylish widgets that automatically adapt to their size
- Completely private and offline
-- No rounded album covers (by default)
\ No newline at end of file
+- No rounded album covers (if you want them)
diff --git a/fastlane/metadata/android/en-US/images/featureGraphic.png b/fastlane/metadata/android/en-US/images/featureGraphic.png
index 8330d9666..52a9ff852 100644
Binary files a/fastlane/metadata/android/en-US/images/featureGraphic.png and b/fastlane/metadata/android/en-US/images/featureGraphic.png differ
diff --git a/fastlane/metadata/android/en-US/images/icon.png b/fastlane/metadata/android/en-US/images/icon.png
index e8519e33f..cc059cc53 100644
Binary files a/fastlane/metadata/android/en-US/images/icon.png and b/fastlane/metadata/android/en-US/images/icon.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/shot0.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/shot0.png
index 6067c95ae..6657a9e38 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/shot0.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/shot0.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/shot1.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/shot1.png
index f36816339..fc7667973 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/shot1.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/shot1.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/shot2.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/shot2.png
index 958fdbc4c..8b71fa259 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/shot2.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/shot2.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/shot3.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/shot3.png
index d12b24814..ac44384b7 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/shot3.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/shot3.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/shot4.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/shot4.png
index e9c15227e..2a5d413a3 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/shot4.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/shot4.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/shot5.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/shot5.png
index 1ed0f49ec..0dc12f9d7 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/shot5.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/shot5.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/shot6.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/shot6.png
deleted file mode 100644
index cc9ef761e..000000000
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/shot6.png and /dev/null differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/shot7.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/shot7.png
deleted file mode 100644
index c5b2a17ad..000000000
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/shot7.png and /dev/null differ
diff --git a/fastlane/metadata/android/es-ES/full_description.txt b/fastlane/metadata/android/es-ES/full_description.txt
index 854a23c2f..1ffe22d7e 100644
--- a/fastlane/metadata/android/es-ES/full_description.txt
+++ b/fastlane/metadata/android/es-ES/full_description.txt
@@ -1,24 +1,25 @@
-Auxio es un reproductor de música local con una UI/UX rápida y confiable sin las muchas características inútiles presentes en otros reproductores de música. Construido a partir de bibliotecas de reproducción de medios modernas, Auxio tiene un soporte de biblioteca y una calidad de escucha superiores en comparación con otras aplicaciones que usan una funcionalidad de Android obsoleta. En resumen, Reproduce música.
+Auxio es un reproductor de música local con una interfaz de usuario y una experiencia de usuario rápidas y fiables sin las numerosas funciones inútiles presentes en otros reproductores de música. Creado a partir de bibliotecas de reproducción de medios modernas, Auxio tiene un soporte de biblioteca y una calidad de escucha superiores en comparación con otras aplicaciones que utilizan funciones obsoletas de Android. En resumen, reproduce música.
Características
-- Reproducción basada en Media3 ExoPlayer
-- Interfaz de usuario ágil derivada de las últimas pautas de diseño de materiales
-- UX obstinado que prioriza la facilidad de uso sobre los casos extremos
+- Reproducción basada en ExoPlayer media 3
+- Interfaz de usuario ágil basada en las últimas directrices de Material Design
+- Experiencia de usuario que prioriza la facilidad de uso sobre los casos extremos
- Comportamiento personalizable
-- Soporte para números de disco, múltiples artistas, tipos de lanzamiento,
-fechas precisas/originales, ordenar etiquetas y más
-- Sistema de artista avanzado que unifica artistas y artistas de álbumes
+- Soporte para números de disco, múltiples artistas, tipos de lanzamiento
+fechas precisas/originales, etiquetas de clasificación, etc.
+- Sistema avanzado de artistas que unifica artistas y artistas de álbumes
- Gestión de carpetas compatible con tarjetas SD
-- Funcionalidad de lista de reproducción confiable
-- Reproducción automática sin pausas
+- Funcionalidad fiable de listas de reproducción
- Persistencia del estado de reproducción
+- Compatibilidad con Android auto
+- Reproducción automática sin pausas
- Compatibilidad total con ReplayGain (en archivos MP3, FLAC, OGG, OPUS y MP4)
-- Soporte de ecualizador externo (ej. Wavelet)
+- Compatibilidad con ecualizadores externos (por ejemplo, Wavelet)
- Borde a borde
-- Soporte de cubiertas incrustadas
-- Funcionalidad de búsqueda
-- Reproducción automática de auriculares
-- Widgets con estilo que se adaptan automáticamente a su tamaño
-- Completamente privado y fuera de línea
+- Compatibilidad con carátulas incrustadas
+- Función de búsqueda
+- Reproducción automática con auriculares
+- Widgets elegantes que se adaptan automáticamente a su tamaño
+- Completamente privado y sin conexión
- Sin carátulas redondeadas (por defecto)
diff --git a/fastlane/metadata/android/et/full_description.txt b/fastlane/metadata/android/et/full_description.txt
new file mode 100644
index 000000000..14417fef1
--- /dev/null
+++ b/fastlane/metadata/android/et/full_description.txt
@@ -0,0 +1,25 @@
+Auxio on kohalikus nutiseadmes töötav kiire ja usaldusväärse kasutajaliidesega muusikamängija. Rakendusest on eemaldatud teises sarnastes rakendustes leiduv kasutu lisafunktsionaalsus. Kuna kasutusel on tänapäevased muusikateegid, siis Auxiol on teiste sarnaste rakendustega (kes kasutavad Androidis leiduvat aegunud funktsionaalsust) võrreldes suurepärane muusikakogu tugi ning taasesituse kvaliteet. Lühidalt: Auxio lihtsalt esitab muusikat.
+
+Funktsionaalsus
+
+- taasesitus kasutab Media3 ExoPlayeri teeke
+- viimastest Material Designi põhimõtetest lähtuv kiire kasutajaliides
+- arendajate omal hinnangul põhinev kasutajaliides, mis eelistab kasutuse lihtsust harvaesinevate olukordade lahendamisele
+- kohandatav käitumine
+- plaadinumbrite, mitme esitaja, väljaande tüübi,
+täpsete avaldamise kuupäevade sortimiseks mõeldud siltide ja palju muu sarnase tugi
+- tavalisest tõhusam esitajate haldus, mis normaliseerib esitajad ning albumi esitajad
+- kaustade haldus, mis saab hakkama SD-kaartidega
+- usaldusväärse esitusloendi loomine
+- taasesituse oleku meeldejätmine
+- tugi Android Auto liidestusele
+- automaatne taasesitus lugudevahelise vaikusteta
+- taasesituse valjuse tundlikkuse tugi (MP3, FLAC, OGG, OPUS ja MP4 failide pihul)
+- välise ekvalaiseri tugi (nt. Wavelet)
+- äärest-ääreni visuaal
+- lõimitud albumikaante tugi
+- otsing
+- automaatne taasesitus kõrvaklappidest
+- stiilsed vidinad, mis automaatselt kohandavad oma suurust
+- täiesti privaatne ja võrguühendust mittevajav
+- plaadikaante ümarad nurgad puuduvad (vaikimisi)
diff --git a/fastlane/metadata/android/et/short_description.txt b/fastlane/metadata/android/et/short_description.txt
new file mode 100644
index 000000000..b48a64763
--- /dev/null
+++ b/fastlane/metadata/android/et/short_description.txt
@@ -0,0 +1 @@
+Lihtne ja ratsionaalne muusikamängija
diff --git a/fastlane/metadata/android/fi/full_description.txt b/fastlane/metadata/android/fi/full_description.txt
new file mode 100644
index 000000000..a1b92b47a
--- /dev/null
+++ b/fastlane/metadata/android/fi/full_description.txt
@@ -0,0 +1,23 @@
+Auxio on paikallisen musiikin soitin nopealla ja luotettavalla käyttöliittymällä ilman useita turhia ominaisuuksia kuten muissa soittimissa. Perustuen moderneihin mediantoistokirjastoihin, Auxiolla on parempi tuki kirjastoille ja äänenlaatu verrattuna muihin sovelluksiin, jotka käyttävät vanhentuneita android toimintoja. Lyhyesti, Se soittaa musiikkia.
+
+Ominaisuudet
+
+- Toisto perustuu Media3 ExoPlayeriin
+- Tyylikäs käyttöliittymä johdettu viimeisimmistä Material Design ohjeista
+- Omapäinen käyttökokemus joka asettaa helppokäyttöisyyden etusijalle reunatapauksien sijaan
+- Mukautettava käyttäytyminen
+- Tukee levyjen numeroita, moninkertaisia artisteja, julkaisutyyppejä, tarkkoja/alkuperäisiä päivämääriä, tagijärjestystä ynnä muuta
+- Edistynyt esittäjäjärjestelmä, joka yhdistää artistit ja albumiartistit
+- SD-korttitietoinen kansionhallinta
+- Varmatoimiset soittolistatoiminnot
+- Android auto-tuki
+- Automaattinen katkoton toisto
+- Täysi ReplayGain tuki (MP3, FLAC, OGG, OPUS ja MP4 tiedostoissa)
+- Tuki ulkoiselle taajuuskorjaimelle (esim. Wavelet)
+- Reunasta reunaan
+- Tukee kappaleen sisältämiä kansikuvia
+- Hakutoiminto
+- Automaattinen toisto kuulokkeilla
+- Tyylikkäitä vimpaimia, jotka mukautuvat automaattisesti kokoonsa
+- Täysin yksityinen ja verkkoyhteydetön
+- Pyöristämättömät albumin kansikuvat (oletusarvoisesti)
diff --git a/fastlane/metadata/android/fil/full_description.txt b/fastlane/metadata/android/fil/full_description.txt
new file mode 100644
index 000000000..155bbc5bc
--- /dev/null
+++ b/fastlane/metadata/android/fil/full_description.txt
@@ -0,0 +1,25 @@
+Ang Auxio ay isang na lokal ng manunugtog at meron siya ng malakas, at maaasahan ng "UI" o "UX" ay walang dami ng mga walang-halaga sa mga iba pang ng mga manunugtog. Ito ay gumawa ng mga bago ng "media playback libraries", at ang Auxio naman ay meron ng superyor ng "library support" at ang mahusay na pakikinig ng mga musika mo at kaysa sa mga iba ng mga "manunugtog na apps" ng gamit ng mga obseleto ng mga tungkulin. Madaling salita, Tumugtog ito ng mga musika.
+
+Mga Tampok
+
+- Pag-playback batay sa Media3 ExoPlayer
+- Snappy UI na nagmula sa pinakabagong mga alituntunin sa Material Design
+- Opinionated UX na inuuna ang kadalian ng paggamit kaysa sa mga edge case
+- Nako-customize na pag-uugali
+- Suporta para sa mga numero ng disc, maramihang mga artist, mga uri ng release,
+tumpak/orihinal na mga petsa, pag-uuri ng mga tag, at higit pa
+- Advanced na sistema ng artist na pinag-iisa ang mga artist at album artist
+- Pamamahala ng folder na may kamalayan sa SD Card
+- Maaasahang pag-andar ng playlist
+- Pagpapatuloy ng estado ng pag-playback
+- Suporta sa Android auto
+- Awtomatikong walang gap na pag-playback
+- Buong suporta sa ReplayGain (Sa mga MP3, FLAC, OGG, OPUS, at MP4 na mga file)
+- Panlabas na suporta sa equalizer (hal. Wavelet)
+- Gilid-sa-gilid
+- Naka-embed na sumasaklaw sa suporta
+- Pag-andar ng paghahanap
+- Autoplay ng headset
+- Mga naka-istilong widget na awtomatikong umaangkop sa kanilang laki
+- Ganap na pribado at offline
+- Walang mga bilugan na cover ng album (bilang default).
diff --git a/fastlane/metadata/android/fil/short_description.txt b/fastlane/metadata/android/fil/short_description.txt
new file mode 100644
index 000000000..756b1420d
--- /dev/null
+++ b/fastlane/metadata/android/fil/short_description.txt
@@ -0,0 +1 @@
+Napakasimple, rasyonal na manunugtog.
diff --git a/fastlane/metadata/android/fr-FR/full_description.txt b/fastlane/metadata/android/fr-FR/full_description.txt
index aeb864c64..a2e737b5a 100644
--- a/fastlane/metadata/android/fr-FR/full_description.txt
+++ b/fastlane/metadata/android/fr-FR/full_description.txt
@@ -12,6 +12,7 @@ les dates précises/originales, le classement par tags, and plus encore
- Carte SD reconnue par le système de dossiers
- Fonction de liste de lecture efficace
- Statut de lecture persistant
+- Support d'Android auto
- Support complet de ReplayGain (pour les fichiers MP3, FLAC, OGG, OPUS, et MP4)
- Support pour égaliseur externe (ex. Wavelet)
- Navigation bord-à-bord
diff --git a/fastlane/metadata/android/he/full_description.txt b/fastlane/metadata/android/he/full_description.txt
index 1cadc6eb3..f36d22df6 100644
--- a/fastlane/metadata/android/he/full_description.txt
+++ b/fastlane/metadata/android/he/full_description.txt
@@ -12,6 +12,8 @@
- ניהול תיקיות מודע לכרטיסי SD
- פונקציונליות פלייליסטים אמינה
- התמדה במצב ההשמעה
+- תמיכה ב-Android Auto
+- השמעה ללא פערים אוטומטית
- תמיכה מלאה ב-ReplayGain (בקבצי MP3, FLAC, OGG, OPUS, ו-MP4)
- תמיכה באיקוולייזר חיצוני (למשל, Wavelet)
- קצה לקצה
@@ -20,4 +22,4 @@
- ניגון אוטומטי באוזניות
- ווידג'טים אלגנטיים שמתאימים את עצמם לגודלם אוטומטית
- פרטי לגמרי ולא מקוון
-- ללא עטיפות אלבום מעוגלות (אלא אם את.ה מעוניינ.ת בהם. אז זה אפשרי.)
+- ללא עטיפות אלבום מעוגלות (כברירת מחדל. ניתן לשנות את זה.)
diff --git a/fastlane/metadata/android/hi/full_description.txt b/fastlane/metadata/android/hi/full_description.txt
index be3a02a95..73982b4f2 100644
--- a/fastlane/metadata/android/hi/full_description.txt
+++ b/fastlane/metadata/android/hi/full_description.txt
@@ -12,6 +12,7 @@ Auxio एक तेज़, विश्वसनीय UI/UX वाला एक
- एसडी कार्ड-जागरूक फ़ोल्डर प्रबंधन
- विश्वसनीय प्लेलिस्टिंग कार्यक्षमता
- प्लेबैक अवस्था दृढ़ता
+- एंड्रॉयड ऑटो समर्थन
- स्वचालित गैपलेस प्लेबैक
- पूर्ण रीप्लेगैन समर्थन (MP3, FLAC, OGG, OPUS और MP4 फ़ाइलों पर)
- बाहरी तुल्यकारक समर्थन (उदा: वेवलेट)
diff --git a/fastlane/metadata/android/hr/full_description.txt b/fastlane/metadata/android/hr/full_description.txt
index 6647c70f4..0d40db862 100644
--- a/fastlane/metadata/android/hr/full_description.txt
+++ b/fastlane/metadata/android/hr/full_description.txt
@@ -11,6 +11,8 @@ precizni/izvorni datumi, sortiranje oznaka i više
- Napredni sustav izvođača koji ujedinjuje izvođače i izvođače albuma
- Upravljanje mapama koje podržava SD karticu
- Pouzdana funkcija popisa pjesama
+- Automaska podrška za Android
+- Automatska reprodukcija bez prekida
- Postojanost stanja reprodukcije
- Puna podrška za ReplayGain (na MP3, FLAC, OGG, OPUS i MP4 datotekama)
- Podrška za vanjski ekvilizator (npr. Wavelet)
diff --git a/fastlane/metadata/android/hu/full_description.txt b/fastlane/metadata/android/hu/full_description.txt
new file mode 100644
index 000000000..3cb5b16cd
--- /dev/null
+++ b/fastlane/metadata/android/hu/full_description.txt
@@ -0,0 +1,25 @@
+Az Auxio egy helyi zenelejátszó gyors, megbízható felhasználói felülettel/UX-szel, anélkül, hogy a többi zenelejátszóban jelen lévő sok haszontalan funkciót tartalmazna. A modern médialejátszó könyvtárakra épülő Auxio kiváló könyvtártámogatással és zenehallgatási minőséggel rendelkezik, mint más, elavult Android-funkciókat használó alkalmazások. Röviden: Zenét játszik.
+
+Funkciók
+
+- Lejátszás Media3 ExoPlayer alapú
+- A legfrissebb anyagtervezési irányelvekből származó, lendületes felhasználói felület
+- Véleményes UX, amely előnyben részesíti a könnyű használatot a szélső esetekben
+- Testreszabható viselkedés
+- Lemezszámok, több előadó, kiadástípusok támogatása,
+pontos/eredeti dátumok, címkék rendezése stb
+- Fejlett előadói rendszer, amely egyesíti az előadókat és az album előadókat
+- SD-kártya-tudatos mappakezelés
+- Megbízható lejátszási lista funkció
+- Lejátszási állapot fennmaradása
+- Android automatikus támogatás
+- Automatikus hézagmentes lejátszás
+- Teljes ReplayGain támogatás (MP3, FLAC, OGG, OPUS és MP4 fájlokon)
+- Külső hangszínszabályzó támogatás (pl. Wavelet)
+- Éltől szélig
+- Beágyazott borítók támogatása
+- Keresés funkció
+- Headset automatikus lejátszása
+- Stílusos kütyük, amelyek automatikusan alkalmazkodnak a méretükhöz
+- Teljesen privát és offline
+- Nincsenek lekerekített albumborítók (alapértelmezés szerint)
diff --git a/fastlane/metadata/android/it/full_description.txt b/fastlane/metadata/android/it/full_description.txt
index db4cabec8..e72f8defb 100644
--- a/fastlane/metadata/android/it/full_description.txt
+++ b/fastlane/metadata/android/it/full_description.txt
@@ -1,4 +1,4 @@
-Auxio è un lettore musicale locale con un'interfaccia ed espereinza utente veloce e affidabile, senza le numerose funzioni inutili presenti in altri lettori musicali. Basato su moderne librerie di riproduzione multimediale, Auxio ha un supporto di libreria e una qualità di ascolto superiore rispetto ad altre applicazioni che utilizzano funzionalità Android obsolete. In breve, riproduce musica.
+Auxio è un lettore musicale locale veloce e con un'interfaccia immediata, senza le numerose funzioni inutili presenti in altri lettori musicali. Basato su moderne librerie di riproduzione multimediale, Auxio ha un supporto di libreria e una qualità di ascolto superiore rispetto ad altre applicazioni che utilizzano funzionalità Android obsolete. In breve, riproduce musica.
Caratteristiche
@@ -12,6 +12,8 @@ date precise/originali, tag di ordinamento e altro ancora
- Gestione delle cartelle consapevole delle schede SD
- Gestione affidabile delle playlist
- Persistenza dello stato di riproduzione
+- Riproduzione automatica senza silenzio
+- Supporto Android Auto
- Supporto completo di ReplayGain (su file MP3, FLAC, OGG, OPUS e MP4)
- Supporto di equalizzatori esterni (es. Wavelet)
- Edge-to-edge
diff --git a/fastlane/metadata/android/ko/full_description.txt b/fastlane/metadata/android/ko/full_description.txt
index 45f7cc6ca..c03243343 100644
--- a/fastlane/metadata/android/ko/full_description.txt
+++ b/fastlane/metadata/android/ko/full_description.txt
@@ -12,6 +12,7 @@ Auxio는 다른 음악 플레이어에 존재하는 쓸모없는 기능 없이,
- SD 카드를 지원하는 폴더 관리 기능
- 안정적인 재생 목록 기능
- 이전 재생 상태 기억
+- Android Auto 지원
- 자동 갭리스 재생 지원
- ReplayGain 완벽 지원 (MP3, FLAC, OGG, OPUS, MP4)
- 외부 이퀄라이저 지원 (Wavelet 등)
diff --git a/fastlane/metadata/android/lt/full_description.txt b/fastlane/metadata/android/lt/full_description.txt
index 043666dfc..0cb30af0c 100644
--- a/fastlane/metadata/android/lt/full_description.txt
+++ b/fastlane/metadata/android/lt/full_description.txt
@@ -1,23 +1,25 @@
-Auxio yra vietinis muzikos grotuvas su greita, patikima UI/UX be daugybės nenaudingų funkcijų, esančių kituose muzikos grotuvuose. Sukurta remiantis iš šiuolaikinių medijos grojimo bibliotekų, Auxio turi geresnį bibliotekos palaikymą ir klausymo kokybę, palyginti su kitomis programomis, kurios naudoja pasenusias Android funkcijas. Trumpai tariant, jame groja muziką.
+„Auxio“ – tai vietinis muzikos leistuvė su greita, patikima naudotojo sąsaja ir potyris be daugybės nenaudingų funkcijų, esančių kituose muzikos leistuvėse. Sukurta remiantis iš šiuolaikinių medijos įrašo perklausos bibliotekų, „Auxio“ turi geresnį bibliotekos palaikymą ir klausymo kokybę, palyginti su kitomis programeles, kurios naudoja pasenusias „Android“ funkcijas. Trumpai tariant, jame leidžiama muziką.
Funkcijos
-- Media3 ExoPlayer pagrįstas grojimas
-- Sparti UI, sukurta pagal naujausias Material Design gaires
-- Nuomonę turintis UX, kuriame prioritetas teikiamas naudojimo paprastumui, o ne kraštutiniam atvejui
+- „Media3 ExoPlayer“ pagrįstas įrašo perklausa
+- Sparti naudotojo sąsaja, sukurta pagal naujausias „Material Design“ gaires
+- Nuomonę turintis naudotojo potyris, kuriame prioritetas teikiamas naudojimo paprastumui, o ne kraštutiniam atvejui
- Pasirinktas elgesys
- Palaikomas diskų numerių, kelių atlikėjų, leidinių tipų palaikymas,
-tikslias/originalias datas, rūšiavimo žymas ir dar daugiau
+tikslias / originalias datas, rūšiavimo žymes ir dar daugiau
- Išplėstinė atlikėjų sistema, kuri suvienija atlikėjus ir albumų atlikėjus
- SD kortelių aplankų valdymas
- Patikima grojaraščių sudarymo funkcija
-- Grojimo būsenos išsaugojimas
-- Visiškas ReplayGain palaikymas (MP3, MP4, FLAC, OGG, OPUS ir MP4 failus)
-- Išorinio ekvalaizerio funkcija (pvz., Wavelet)
+- Įrašo perklausos būsenos išsaugojimas
+- „Android Auto“ palaikymas
+- Automatinis įrašo perklausa be tarpų
+- Visiškas „ReplayGain“ palaikymas (MP3, MP4, FLAC, OGG, OPUS ir MP4 failuose)
+- Išorinio ekvalaizerio funkcija (pvz., „Wavelet“)
- Krašto iki krašto
- Įterptųjų viršelių palaikymas
- Paieškos funkcija
-- Automatinis ausinių grojimas
+- Automatinis ausinių leidimas
- Stilingi valdikliai, kurie automatiškai prisitaiko prie savo dydžio
-- Visiškai privatus ir neprisijungęs
+- Visiškai privati ir neprisijungę
- Jokių suapvalintų albumų viršelių (pagal numatytuosius nustatymus)
diff --git a/fastlane/metadata/android/lt/short_description.txt b/fastlane/metadata/android/lt/short_description.txt
index aaa18cd6a..0a1cd0b79 100644
--- a/fastlane/metadata/android/lt/short_description.txt
+++ b/fastlane/metadata/android/lt/short_description.txt
@@ -1 +1 @@
-Paprastas, racionalus muzikos grotuvas
+Paprastas, racionalus muzikos leistuvė
diff --git a/fastlane/metadata/android/lv/short_description.txt b/fastlane/metadata/android/lv/short_description.txt
new file mode 100644
index 000000000..99729a7f6
--- /dev/null
+++ b/fastlane/metadata/android/lv/short_description.txt
@@ -0,0 +1 @@
+Ērts un racionāls mūzikas atskaņotājs
diff --git a/fastlane/metadata/android/ne/short_description.txt b/fastlane/metadata/android/ne/short_description.txt
new file mode 100644
index 000000000..6bc2f1915
--- /dev/null
+++ b/fastlane/metadata/android/ne/short_description.txt
@@ -0,0 +1 @@
+एक सरल, विवेकशिल संगीत प्लेयर
diff --git a/fastlane/metadata/android/nl-NL/full_description.txt b/fastlane/metadata/android/nl-NL/full_description.txt
new file mode 100644
index 000000000..895806109
--- /dev/null
+++ b/fastlane/metadata/android/nl-NL/full_description.txt
@@ -0,0 +1,24 @@
+Auxio is een lokale muziekspeler met een snelle, betrouwbare UI/UX zonder vele nutteloze functies in andere muziekspelers. Gebouwd op basis van moderne media-afspeelbibliotheken bevat Auxio superieure bibliotheekondersteuning en luisterkwaliteit vergeleken met andere apps die verouderde Android-functionaliteit gebruiken. Kortom, Het speelt muziek.
+
+Functies
+
+- Afspelen gebaseerd op Media3 ExoPlayer
+- Snelle gebruikersinterface afgeleid van de nieuwste Material Design-richtlijnen
+- Eigenzinnige UX die gebruiksgemak boven randgevallen stelt
+- Aanpasbaar gedrag
+- Ondersteuning voor disc nummers, meerdere artiesten, release types,
+exacte/originele data, sorteertags en meer
+- Geavanceerd artiestensysteem dat artiesten en albumartiesten verenigt
+- Beheer van SD-kaartmappen
+- Betrouwbare functionaliteit voor afspeellijsten
+- Persistentie van afspeelstatus
+- Automatisch gapless afspelen
+- Volledige ondersteuning voor ReplayGain (op MP3-, FLAC-, OGG-, OPUS- en MP4-bestanden)
+- Ondersteuning voor externe equalizer (bijv. Wavelet)
+- Rand-tot-rand
+- Ondersteuning voor ingesloten omslagen
+- Zoek functionaliteit
+- Automatisch afspelen voor koptelefoon
+- Stijlvolle widgets die zich automatisch aanpassen aan hun grootte
+- Volledig privé en offline
+- Geen afgeronde albumhoezen (standaard)
diff --git a/fastlane/metadata/android/nl-NL/short_description.txt b/fastlane/metadata/android/nl-NL/short_description.txt
new file mode 100644
index 000000000..7b12963ee
--- /dev/null
+++ b/fastlane/metadata/android/nl-NL/short_description.txt
@@ -0,0 +1 @@
+Een eenvoudige, rationele muziekspeler
diff --git a/fastlane/metadata/android/pa/full_description.txt b/fastlane/metadata/android/pa/full_description.txt
index 646c41be8..25d95cc5c 100644
--- a/fastlane/metadata/android/pa/full_description.txt
+++ b/fastlane/metadata/android/pa/full_description.txt
@@ -12,6 +12,7 @@ Auxio ਇੱਕ ਤੇਜ਼, ਭਰੋਸੇਮੰਦ UI/UX ਵਾਲਾ ਇੱ
- ਭਰੋਸੇਯੋਗ ਪਲੇਅਲਿਸਟਿੰਗ ਕਾਰਜਕੁਸ਼ਲਤਾ
- ਭਰੋਸੇਯੋਗ ਪਲੇਅਬੈਕ ਸਥਿਤੀ ਸਥਿਰਤਾ
+- ਐਂਡਰੌਇਡ ਆਟੋ ਸਪੋਰਟ
- ਆਟੋਮੈਟਿਕ ਗੈਪਲੈੱਸ ਪਲੇਅਬੈਕ
- ਪੂਰਾ ਰੀਪਲੇਅ-ਗੇਨ ਸਮਰਥਨ (MP3, FLAC, OGG, OPUS, ਅਤੇ MP4 ਫਾਈਲਾਂ 'ਤੇ)
- ਬਾਹਰੀ ਈਕੋਲਾਈਜ਼ਰ ਦਾ ਸਮਰਥਨ (ਉਦਾਹਰਨ. ਵੇਵਲੇਟ)
diff --git a/fastlane/metadata/android/pl/full_description.txt b/fastlane/metadata/android/pl/full_description.txt
new file mode 100644
index 000000000..d7a85fdbd
--- /dev/null
+++ b/fastlane/metadata/android/pl/full_description.txt
@@ -0,0 +1,25 @@
+Auxio jest lokalnym odtwarzaczem muzyki z szybkim i niezawodnym interfejsem użytkownika, pozbawionym wielu zbędnych funkcji obecnych w innych odtwarzaczach. Dzięki nowoczesnym bibliotekom odtwarzania mediów, Auxio oferuje lepszą obsługę biblioteki i wyższą jakość dźwięku w porównaniu do aplikacji korzystających z przestarzałych funkcji Androida. W skrócie, odtwarza muzykę.
+
+Funkcje
+
+- Odtwarzanie oparte na Media3 ExoPlayer
+- Szybki interfejs oparty na najnowszych wytycznych Material Design
+- Przemyślane UX, które priorytyzuje prostotę użytkowania nad skrajnymi przypadkami
+- Personalizowalne zachowanie
+- Wsparcie dla numerów dysków, wielu artystów, rodzajów wydań,
+precyzyjnych/oryginalnych dat wydania, tagów sortujących i więcej
+- Zaawansowany system artistów, który łączy artystów i artystów z albumu
+- Zarządzanie folderami z wsparciem dla kart SD
+- Niezawodna funkcjonalność z playlistami
+- Utrzymanie stanu odtwarzania
+- Wsparcie dla Android auto
+- Automatyczne bezproblemowe odtwarzanie
+- Pełne wsparcie dla ReplayGain (dla plików MP3, FLAC, OGG, OPUS, i MP4)
+- Wsparcie dla zewnętrznego korektora (np. Wavelet)
+- Obsługa ekranu od krawędzi do krawędzi
+- Obsługa wbudowanych okładek
+- Funkcje wyszukiwania
+- Automatyczne odtwarzanie z headsetem
+- Stylowe widgety dostosowujące się do ich rozmiaru
+- Całkowicie prywatny i działający offline
+- Bez zakrąglonych okładek (domyślnie)
diff --git a/fastlane/metadata/android/pl/short_description.txt b/fastlane/metadata/android/pl/short_description.txt
new file mode 100644
index 000000000..8fa861c52
--- /dev/null
+++ b/fastlane/metadata/android/pl/short_description.txt
@@ -0,0 +1 @@
+Prosty i praktyczny odtwarzacz muzyki
diff --git a/fastlane/metadata/android/pt-BR/full_description.txt b/fastlane/metadata/android/pt-BR/full_description.txt
index bff79436f..3c263cbcf 100644
--- a/fastlane/metadata/android/pt-BR/full_description.txt
+++ b/fastlane/metadata/android/pt-BR/full_description.txt
@@ -1,21 +1,24 @@
-O Auxio é um reprodutor de música local com UI/UX rápido e confiável, sem os muitos recursos inúteis presentes em outros reprodutores de música. Construído a partir do Exoplayer, o Auxio tem uma experiência de audição muito melhor em comparação com outros aplicativos que usam a API MediaPlayer nativa. Resumindo, Toca música.
+Auxio é um reprodutor de música local com uma interface/experiência de usuário rápida e confiável sem os tantos recursos inúteis presentes em outros reprodutores de música. Baseado em bibliotecas modernas de reprodução de mídia, Auxio possui um suporte à biblioteca e qualidade de reprodução superior comparado a outros aplicativos que usam recursos desatualizadas do Android. Em resumo, Auxio toca música.
-Recursos
+Recursos:
-- Reprodução baseada em ExoPlayer
-- Snappy UI derivada das diretrizes mais recentes do Material Design
-- UX opinativo que prioriza a facilidade de uso
-- Comportamento personalizável
-- Suporte à numeração de músicas nos discos, múltiplos artistas, tipos de lançamento, datas precisas/originais, ordenar tags, e mais
-- Sistema de artistas avançado que unifica artistas e seus álbuns
-- Gerenciamento de pastas com reconhecimento de cartão SD
-- Persistência confiável da lista de reprodução
-- Suporte completo ao ReplayGain (MP3, MP4, FLAC, OGG e OPUS)
-- Funcionalidade de equalizador externo (como o Wavelet)
-- De ponta a ponta
-- Suporte para capas embutidas
-- Funcionalidade de pesquisa
-- Reprodução automática em fones de ouvido
-- Widgets elegantes que se adaptam automaticamente ao seu tamanho
-- Completamente privado e offline
-- Sem capas de álbuns arredondadas (a menos que você as queira. Daí pode.)
+- Reprodução baseada no Media3 ExoPlayer.
+- Interface de usuário rápida derivada das últimas diretrizes do Material Design.
+- Experiência de usuário opinativa que prioriza a facilidade de uso ao invés de priorizar situações incomuns.
+- Comportamento customizável.
+- Suporte a números de disco, vários artistas, tipos de lançamento, datas precisas/originais, metadados organizados e mais.
+- Sistema de artistas avançado que unifica artistas e artistas de álbum.
+- Gerenciamento de pastas com suporte a cartão SD.
+- Funcionalidade confiável de criação de playlists.
+- Persistência do estado de reprodução.
+- Suporte ao Android Auto.
+- Reprodução automática sem interrupções.
+- Suporte completo a ReplayGain (nos arquivos FLAC, MP3, MP4, OGG e OPUS).
+- Suporte externo a equalizador (por exemplo, Wavelet).
+- De ponta a ponta.
+- Suporte a capas embutidas.
+- Função de pesquisa.
+- Reprodução automática em fones de ouvido.
+- Widgets elegantes que se adaptam automaticamente ao tamanho.
+- Completamente privado e off-line.
+- Sem capas de álbuns arredondadas (por padrão).
diff --git a/fastlane/metadata/android/ru/full_description.txt b/fastlane/metadata/android/ru/full_description.txt
index 0220f53b7..c54e911c6 100644
--- a/fastlane/metadata/android/ru/full_description.txt
+++ b/fastlane/metadata/android/ru/full_description.txt
@@ -1,23 +1,25 @@
-Auxio — это локальный музыкальный плеер с быстрым и надежным UI/UX без многих бесполезных функций, имеющихся в других музыкальных плеерах. Создан на основе Exoplayer, Auxio имеет лучшую поддержку библиотеки и качество прослушивания по сравнению с другими приложениями, которые используют устаревшие функции Android. Вкратце,он воспроизводит музыку < /b>.
+Auxio — это локальный музыкальный плеер с быстрым и надежным UI/UX без многих бесполезных функций, имеющихся в других музыкальных плеерах. Основываясь на современных библиотеках воспроизведения мультимедийных файлов, Auxio имеет лучшую поддержку библиотеки и лучшее качество прослушивания по сравнению с другими приложениями, которые используют устаревшие функции Android. Вкратце,он воспроизводит музыку.
Особенности
-- Воспроизведение на основе ExoPlayer
+- Воспроизведение на основе Media3 ExoPlayer
- Быстрый UI создан в соответствии с последними рекомендациями Material Design
- Продуманный UX, который предпочитает простоту использования над крайностями
- Настраиваемое поведение
- Поддержка номеров дисков, нескольких исполнителей, типов выпусков,
-точные / оригинальные даты, теги сортировки и т. д
+точные/оригинальные даты, теги сортировки и т.д.
- Расширенная система исполнителей, объединяющая исполнителей и исполнителей альбомов
- Управление папками на SD-карте
-- Надёжное сохранение состояния воспроизведения
+- Надежная функция составления плейлистов
+- Сохранение состояния воспроизведения
+- Поддержка Android Auto
- Автоматическое воспроизведение без пауз
- Полная поддержка ReplayGain (в файлах MP3, FLAC, OGG, OPUS и MP4)
- Поддержка внешнего эквалайзера (например, Wavelet)
- Дизайн от края до края
- Поддержка встроенных обложек
-- Функциональный поиск
+- Поиск
- Автоматическое воспроизведение в наушниках
- Адаптивные виджеты
-- Полностью частный и офлайн
-- Никаких закруглённых обложек альбомов (по умолчанию)
+- Полностью приватный и офлайн
+- Никаких закруглённых обложек альбомов (по умолчанию)
diff --git a/fastlane/metadata/android/sl/full_description.txt b/fastlane/metadata/android/sl/full_description.txt
index c8ee9819d..b4e6bdeef 100644
--- a/fastlane/metadata/android/sl/full_description.txt
+++ b/fastlane/metadata/android/sl/full_description.txt
@@ -11,6 +11,7 @@ Auxio je lokalni predvajalnik glasbe z hitrim in zanesljivim uporabniškim vmesn
- Upravljanje map na SD kartici
- Zanesljiva funkcionalnost ustvarjanja seznama predvajanja
- Trajnost stanja predvajanja
+- Samodejno predvajanje brez lukenj (praznega zvoka)
- Popolna podpora za ReplayGain tehnologijo (za MP3, FLAC, OGG, OPUS in MP4 datoteke)
- Podpora za zunanje izenačevalnike (npr. Wavelet)
- Od roba do roba
diff --git a/fastlane/metadata/android/sq/full_description.txt b/fastlane/metadata/android/sq/full_description.txt
new file mode 100644
index 000000000..464cc4e84
--- /dev/null
+++ b/fastlane/metadata/android/sq/full_description.txt
@@ -0,0 +1,24 @@
+Auxio është një lexues muzikor lokal me një UI/UX të shpejtë dhe të besueshëm, pa shumë veçori të panevojshme të pranishme në lexues të tjerë muzikorë. I ndërtuar mbi bibliotekat moderne të riprodhimit të mediave, Auxio ofron mbështetje superiore për bibliotekën dhe cilësi dëgjimi më të mirë krahasuar me aplikacionet e tjera që përdorin funksione të vjetruara të Android. Me pak fjalë, luan muzikë.
+
+Veçori
+
+- Riprodhim i bazuar në Media3 ExoPlayer
+- UI i shpejtë, i ndërtuar sipas udhëzimeve më të fundit të Material Design
+- UX i thjeshtë dhe funksional që përparëson lehtësinë e përdorimit mbi rastet e rralla
+- Sjellje e personalizueshme
+- Mbështetje për numrat e disqeve, artistë të shumtë, lloje publikimesh, data të sakta/origjinale, etiketa renditjeje, dhe më shumë
+- Sistem i avancuar për artistët, që bashkon artistët dhe artistët e albumeve
+-Menaxhim i dosjeve me njohje për SD Card
+-Funksionalitet i besueshëm për krijimin e listave
+- Ruajtje e gjendjes së riprodhimit
+- Mbështetje për Android Auto
+- Riprodhim automatik pa ndërprerje
+- Mbështetje e plotë për ReplayGain (në skedarët MP3, FLAC, OGG, OPUS dhe MP4)
+- Mbështetje për ekualizues të jashtëm (p.sh., Wavelet)
+- Shtrirje nga një skaj në tjetrin
+- Mbështetje për kopertina të integruara
+- Funksionalitet kërkimi
+- Riprodhim automatik me kufje
+- Widget-e elegante që përshtaten automatikisht me madhësinë e tyre
+- Plotësisht privat dhe offline
+- Nuk përdor kopertina albumesh me kënde të rrumbullakosura (në parazgjedhje)
diff --git a/fastlane/metadata/android/sq/short_description.txt b/fastlane/metadata/android/sq/short_description.txt
new file mode 100644
index 000000000..25b2c606b
--- /dev/null
+++ b/fastlane/metadata/android/sq/short_description.txt
@@ -0,0 +1 @@
+Një muzikë-luajtës i thjeshtë dhe racional
diff --git a/fastlane/metadata/android/sv/full_description.txt b/fastlane/metadata/android/sv/full_description.txt
new file mode 100644
index 000000000..810c803d6
--- /dev/null
+++ b/fastlane/metadata/android/sv/full_description.txt
@@ -0,0 +1 @@
+Auxio är en lokal musikspelare med en snabb och pålitlig UI/UX utan de många oanvändbara egenskaperna hos andra musikspelare.
diff --git a/fastlane/metadata/android/ta/full_description.txt b/fastlane/metadata/android/ta/full_description.txt
new file mode 100644
index 000000000..59d2cfb13
--- /dev/null
+++ b/fastlane/metadata/android/ta/full_description.txt
@@ -0,0 +1,25 @@
+ஆக்சியோ ஒரு உள்ளக இசை வீரர், மற்ற இசை வீரர்களில் பல பயனற்ற நற்பொருத்தங்கள் இல்லாமல் வேகமான, நம்பகமான UI/UX உடன். நவீன மீடியா பிளேபேக் நூலகங்களால் கட்டப்பட்ட ஆக்சியோ, காலாவதியான ஆண்ட்ராய்டு செயல்பாட்டைப் பயன்படுத்தும் பிற பயன்பாடுகளுடன் ஒப்பிடும்போது சிறந்த நூலக உதவி மற்றும் கேட்கும் தரத்தைக் கொண்டுள்ளது. சுருக்கமாக, இது இசையை இயக்குகிறது.
+
+ நற்பொருத்தங்கள்
+
+ - மீடியா 3 எக்சோப்ளேயரை அடிப்படையாகக் கொண்ட பிளேபேக்
+ - அண்மைக் கால பொருள் வடிவமைப்பு வழிகாட்டுதல்களிலிருந்து பெறப்பட்ட ச்னாப்பி இடைமுகம்
+ - எட்ச் வழக்குகளில் பயன்பாட்டை எளிதாக்கும் கருத்தியல் யுஎக்ச்
+ - தனிப்பயனாக்கக்கூடிய நடத்தை
+ - வட்டு எண்களுக்கான உதவி, பல கலைஞர்கள், வெளியீட்டு வகைகள்,
+ துல்லியமான/அசல் தேதிகள், வரிசைப்படுத்தப்பட்ட குறிச்சொற்கள் மற்றும் பல
+ - கலைஞர்கள் மற்றும் ஆல்பம் கலைஞர்களை ஒன்றிணைக்கும் மேம்பட்ட கலைஞர் அமைப்பு
+ - எச்டி கார்டு-விழிப்புணர்வு கோப்புறை மேலாண்மை
+ - நம்பகமான பிளேலிச்டிங் செயல்பாடு
+ - பிளேபேக் நிலை விடாமுயற்சி
+ - ஆண்ட்ராய்டு ஆட்டோ உதவி
+ - தானியங்கி இடைவெளி பின்னணி
+ .
+ - வெளிப்புற சமநிலை உதவி (எ.கா. அலைவரிசை)
+ -விளிம்பு-க்கு-விளிம்பு
+ - உட்பொதிக்கப்பட்ட உதவி உதவி
+ - செயல்பாட்டைத் தேடுங்கள்
+ - எட்செட் ஆட்டோபிளே
+ - ச்டைலான விட்செட்டுகள் அவற்றின் அளவிற்கு தானாகவே மாற்றியமைக்கின்றன
+ - முற்றிலும் தனிப்பட்ட மற்றும் இணைப்பில்லாத
+ - வட்டமான ஆல்பம் கவர்கள் இல்லை (இயல்பாக)
diff --git a/fastlane/metadata/android/ta/short_description.txt b/fastlane/metadata/android/ta/short_description.txt
new file mode 100644
index 000000000..a25ec04e8
--- /dev/null
+++ b/fastlane/metadata/android/ta/short_description.txt
@@ -0,0 +1 @@
+ஒரு எளிய, பகுத்தறிவு இசை வீரர்
diff --git a/fastlane/metadata/android/tr/full_description.txt b/fastlane/metadata/android/tr/full_description.txt
index 86460ef08..9a442c520 100644
--- a/fastlane/metadata/android/tr/full_description.txt
+++ b/fastlane/metadata/android/tr/full_description.txt
@@ -12,6 +12,7 @@ kesin/orijinal tarihler, sıralama etiketleri ve daha fazlası
- SD Card-aware klasör yönetimi
- Güvenilir çalma listesi işlevi
- Oynatma durumu kalıcılığı
+- Android Auto desteği
- Tam ReplayGain desteği (MP3, FLAC, OGG, OPUS ve MP4 dosyalarında)
- Harici ekolayzer desteği (örn. Wavelet)
- Kenardan kenara
diff --git a/fastlane/metadata/android/uk/full_description.txt b/fastlane/metadata/android/uk/full_description.txt
index b2bf2642f..4fbd4baca 100644
--- a/fastlane/metadata/android/uk/full_description.txt
+++ b/fastlane/metadata/android/uk/full_description.txt
@@ -1,24 +1,25 @@
-Auxio – це локальний музичний плеєр зі швидким і надійним UI/UX без багатьох непотрібних функцій, наявних в інших музичних плеєрах. Створений на основі сучасних медіа-бібліотек відтворення, Auxio має кращу підтримку бібліотеки та якість прослуховування порівняно з іншими застосунками, які використовують застарілі функції Android. Одним словом, він відтворює музику.
+Auxio — це локальний музичний плеєр із швидким і надійним інтерфейсом користувача/UX без багатьох марних функцій, наявних в інших музичних плеєрах. Створений на основі сучасних медіа-бібліотек, Auxio має чудову підтримку бібліотек і якість прослуховування порівняно з іншими програмами, які використовують застарілі функції Android. Коротше кажучи, Він відтворює музику.
-Особливості
+Функції
-- Відтворення на основі Media 3 ExoPlayer
-- Швидкий UI створений на основі останніх рекомендацій Material Design
-- Продуманий UX, який надає перевагу простоті використання над крайнощами
-- Налаштовувана поведінка
-- Підтримка номерів дисків, кількох виконавців, типів випусків,
-точні/оригінальні дати, теги сортування тощо
-- Розширена система виконавців, яка об’єднує виконавців і виконавців альбомів
-- Керування теками з підтримкою SD-картки
-- Надійне функція списків відтворення
-- Збереження стану відтворення
-- Автоматичне відтворення без пропусків
-- Повна підтримка ReplayGain (у файлах MP3, FLAC, OGG, OPUS і MP4)
-- Підтримка зовнішнього еквалайзера (наприклад, Wavelet)
-- Дизайн від краю до краю
-- Підтримка вбудованих обкладинок
-- Функціональний пошук
-- Автоматичне відтворення в навушниках
-- Стильні віджети, які автоматично підлаштовуються під розмір
-- Повністю приватний і офлайн
-- Жодних заокруглених обкладинок альбомів (за замовчуванням)
+— Відтворення на основі Media3 ExoPlayer
+- Snappy UI, створений на основі останніх рекомендацій Material Design
+- Переконливий UX, який надає перевагу простоті використання над крайніми випадками
+- Настроювана поведінка
+- Підтримка номерів дисків, кількох виконавців, типів випусків,
+точні/оригінальні дати, теги сортування тощо
+— Розширена система виконавців, яка об’єднує виконавців і виконавців альбомів
+- Керування папками з підтримкою SD-карти
+— Надійна функція списків відтворення
+— Постійність стану відтворення
+- Підтримка Android auto
+- Автоматичне безперервне відтворення
+- Повна підтримка ReplayGain (у файлах MP3, FLAC, OGG, OPUS і MP4)
+- Підтримка зовнішнього еквалайзера (наприклад, Wavelet)
+- Від краю до краю
+— Підтримка вбудованих обкладинок
+- Функція пошуку
+- Автовідтворення гарнітури
+- Стильні віджети, які автоматично адаптуються до їх розміру
+- Повністю приватний і офлайн
+- Немає округлених обкладинок альбомів (за замовчуванням)
diff --git a/fastlane/metadata/android/zh-CN/full_description.txt b/fastlane/metadata/android/zh-CN/full_description.txt
index 5eeab94cf..cc2ef74c1 100644
--- a/fastlane/metadata/android/zh-CN/full_description.txt
+++ b/fastlane/metadata/android/zh-CN/full_description.txt
@@ -11,7 +11,8 @@ Auxio 是一款本地音乐播放器,它拥有快速、可靠的 UI/UX,没
- 统一“艺术家”和“专辑艺术家”的高级“艺术家”系统
- 文件夹管理功能可以感知到 SD 卡
- 可靠的播放列表功能
-- 回放状态持久化
+- 播放状态持久化
+- 支持 Android auto
- 自动无缝回放
- 完整的回放增益支持(包括 MP3、FLAC、OGG、OPUS 和 MP4 文件)
- 支持外部均衡器(如 Wavelet 这样的应用)
diff --git a/fastlane/metadata/android/zh-Hant/full_description.txt b/fastlane/metadata/android/zh-Hant/full_description.txt
index 26596541a..efd745793 100644
--- a/fastlane/metadata/android/zh-Hant/full_description.txt
+++ b/fastlane/metadata/android/zh-Hant/full_description.txt
@@ -10,6 +10,7 @@ Auxio 是一款本機音樂播放器,擁有快速且可靠的 UI/UX,不含
- 進階藝術家系統,統一藝術家與專輯藝術家
- 支援 SD 卡的資料夾管理
- 可靠的播放列表功能
+- 支援 Android Auto
- 播放狀態持久性
- 完整的 ReplayGain 支援(適用於 MP3、FLAC、OGG、OPUS 和 MP4 檔案)
- 外部均衡器支援(例如 Wavelet)
diff --git a/gradle.properties b/gradle.properties
index 87899875b..f28e39824 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -20,6 +20,8 @@ android.enableJetifier=false
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
android.enableR8.fullMode=true
-android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=true
-android.nonFinalResIds=true
\ No newline at end of file
+android.nonFinalResIds=true
+org.gradle.parallel=true
+org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
+org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 43a6e163d..6a93cb7a1 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
+distributionSha256Sum=31c55713e40233a8303827ceb42ca48a47267a0ad4bab9177123121e71524c26
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/media b/media
index 34b33175c..4b3084e1b 160000
--- a/media
+++ b/media
@@ -1 +1 @@
-Subproject commit 34b33175c00183dc95cdcb8c735033b6785041e1
+Subproject commit 4b3084e1b63185eaeffa7cac9d7015040e0e2aa5
diff --git a/musikr/.Rhistory b/musikr/.Rhistory
new file mode 100644
index 000000000..e69de29bb
diff --git a/musikr/.gitignore b/musikr/.gitignore
new file mode 100644
index 000000000..42afabfd2
--- /dev/null
+++ b/musikr/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/musikr/README.md b/musikr/README.md
new file mode 100644
index 000000000..7ce1cb1aa
--- /dev/null
+++ b/musikr/README.md
@@ -0,0 +1,23 @@
+# musikr
+
+Musikr is a highly opinionated multithreaded music loader that enables Auxio's advanced music functionality.
+It completely bypasses Android's MediaStore and uses the [storage access framework (SAF)](https://developer.android.com/guide/topics/providers/document-provider)
+and [taglib](https://taglib.org/) to replicate it's functionality with less bugs and more flexibility, further
+expanding it with an advanced music model that leverages the wide variety of tags available in modern extended
+specs.
+
+Warning that the API surface is:
+- Extremely unstable, as it's a very thin shim on top of a constantly optimzied and updated music loader
+- Minimized to only what the rest of the app uses or builds on, so you will need to patch it to extend
+certain components
+
+Feel free to use this library as long as you follow Auxio's GPLv3 license. Note that the license is viral, so
+if you wind up using this in a proprietary project, the entire project must be GPLv3 too.
+
+If you want to generate some docs for the unstable API, you can run
+
+```bash
+./gradlew musikr:dokkaGeneratePublicationHtml
+```
+
+In the project root and it should produce a webpage in `musikr/build/dokka/html`
diff --git a/musikr/build.gradle b/musikr/build.gradle
new file mode 100644
index 000000000..80007d3bf
--- /dev/null
+++ b/musikr/build.gradle
@@ -0,0 +1,103 @@
+import org.apache.tools.ant.taskdefs.condition.Os
+
+plugins {
+ id 'com.android.library'
+ id 'org.jetbrains.kotlin.android'
+ id "com.google.devtools.ksp"
+ id "com.diffplug.spotless"
+ id "kotlin-parcelize"
+ id "org.jetbrains.dokka"
+}
+
+android {
+ namespace 'org.oxycblt.musikr'
+ compileSdk target_sdk
+ ndkVersion "$ndk_version"
+
+ defaultConfig {
+ minSdk min_sdk
+ targetSdk target_sdk
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles "consumer-rules.pro"
+ externalNativeBuild {
+ cmake {
+ cppFlags ""
+ }
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ externalNativeBuild {
+ cmake {
+ path "src/main/cpp/CMakeLists.txt"
+ version "3.22.1"
+ }
+ }
+
+ compileOptions {
+ coreLibraryDesugaringEnabled true
+ sourceCompatibility JavaVersion.VERSION_17
+ targetCompatibility JavaVersion.VERSION_17
+ }
+
+ kotlinOptions {
+ jvmTarget = "17"
+ freeCompilerArgs += "-Xjvm-default=all"
+ }
+
+ buildFeatures {
+ buildConfig true
+ }
+}
+
+dependencies {
+ // Kotlin
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
+
+ // AndroidX
+ implementation "androidx.core:core-ktx:$core_version"
+
+ // Database
+ implementation "androidx.room:room-runtime:$room_version"
+ ksp "androidx.room:room-compiler:$room_version"
+ implementation "androidx.room:room-ktx:$room_version"
+
+ // Build
+ coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:$desugaring_version"
+
+ // Testing
+ testImplementation "junit:junit:4.13.2"
+ testImplementation "io.mockk:mockk:1.13.7"
+ testImplementation "org.robolectric:robolectric:4.11"
+ testImplementation 'androidx.test:core-ktx:1.6.1'
+ androidTestImplementation 'androidx.test.ext:junit:1.2.1'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
+}
+
+task assembleTaglib(type: Exec) {
+ def jniDir = "$projectDir/src/main/cpp"
+ def libs = new File("$jniDir/taglib/pkg")
+ if (libs.exists()) {
+ commandLine "true"
+ return
+ }
+ commandLine "sh", "-c", "$jniDir/build_taglib.sh $jniDir $android.ndkDirectory"
+}
+
+afterEvaluate {
+ preDebugBuild.dependsOn assembleTaglib
+ preReleaseBuild.dependsOn assembleTaglib
+}
+
+clean {
+ delete "$projectDir/src/main/cpp/taglib/pkg"
+ delete "$projectDir/src/main/cpp/taglib/build"
+}
diff --git a/musikr/consumer-rules.pro b/musikr/consumer-rules.pro
new file mode 100644
index 000000000..f15090dbc
--- /dev/null
+++ b/musikr/consumer-rules.pro
@@ -0,0 +1,4 @@
+-keep class org.oxycblt.musikr.metadata.NativeInputStream { *; }
+-keep class org.oxycblt.musikr.metadata.Metadata { *; }
+-keep class org.oxycblt.musikr.metadata.Properties { *; }
+-keep class org.oxycblt.musikr.metadata.NativeTagMap { *; }
\ No newline at end of file
diff --git a/musikr/proguard-rules.pro b/musikr/proguard-rules.pro
new file mode 100644
index 000000000..8cc776ba8
--- /dev/null
+++ b/musikr/proguard-rules.pro
@@ -0,0 +1,26 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
+
+-keep class org.oxycblt.musikr.metadata.NativeInputStream { *; }
+-keep class org.oxycblt.musikr.metadata.Metadata { *; }
+-keep class org.oxycblt.musikr.metadata.Properties { *; }
+-keep class org.oxycblt.musikr.metadata.NativeTagMap { *; }
\ No newline at end of file
diff --git a/musikr/src/main/AndroidManifest.xml b/musikr/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..5a898a4a2
--- /dev/null
+++ b/musikr/src/main/AndroidManifest.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/musikr/src/main/cpp/CMakeLists.txt b/musikr/src/main/cpp/CMakeLists.txt
new file mode 100644
index 000000000..23bb28d13
--- /dev/null
+++ b/musikr/src/main/cpp/CMakeLists.txt
@@ -0,0 +1,71 @@
+# For more information about using CMake with Android Studio, read the
+# documentation: https://d.android.com/studio/projects/add-native-code.html.
+# For more examples on how to use CMake, see https://github.com/android/ndk-samples.
+
+# Sets the minimum CMake version required for this project.
+cmake_minimum_required(VERSION 3.22.1)
+
+# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
+# Since this is the top level CMakeLists.txt, the project name is also accessible
+# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
+# build script scope).
+project("tagJNI") # becomes "libtagJNI.so"
+
+# Creates and names a library, sets it as either STATIC
+# or SHARED, and provides the relative paths to its source code.
+# You can define multiple libraries, and CMake builds them for you.
+# Gradle automatically packages shared libraries with your APK.
+#
+# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
+# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
+# is preferred for the same purpose.
+#
+# In order to load a library into your app from Java/Kotlin, you must call
+# System.loadLibrary() and pass the name of the library defined here;
+# for GameActivity/NativeActivity derived applications, the same library name must be
+# used in the AndroidManifest.xml file.
+set(taglib_location "${CMAKE_CURRENT_SOURCE_DIR}/taglib")
+set(taglib_pkg "${taglib_location}/pkg/${ANDROID_ABI}")
+set(taglib_lib "${taglib_pkg}/lib")
+set(taglib_include "${taglib_pkg}/include")
+
+set(taglib_file_name libtag.a)
+set(taglib_file_path ${taglib_lib}/${taglib_file_name})
+set(taglib_lib_name, "taglib")
+add_library(
+ "taglib"
+ STATIC
+ IMPORTED)
+set_target_properties(
+ "taglib" PROPERTIES
+ IMPORTED_LOCATION
+ ${taglib_file_path}
+ INTERFACE_INCLUDE_DIRECTORIES
+ ${taglib_include})
+add_library(${CMAKE_PROJECT_NAME} SHARED
+ # List C/C++ source files with relative paths to this CMakeLists.txt.
+ taglib_jni.cpp
+ JInputStream.cpp
+ JTagMap.cpp
+ JMetadataBuilder.cpp
+ JClassRef.cpp
+ JObjectRef.cpp
+ JStringRef.cpp
+ JByteArrayRef.cpp
+)
+target_link_options(${CMAKE_PROJECT_NAME}
+ # @Tolriq found that these flags can reduce the size of the linked
+ # taglib + jni shim shared library. Kudos to them.
+ # https://github.com/taglib/taglib/issues/1212#issuecomment-2326456903
+ # Additionally, enable 16kb page size. I believe taglib can support this fine,
+ # as a cursory glance indicates that it doesn't hardcode any page sizes.
+ PRIVATE "-Wl,--exclude-libs,ALL,-z,max-page-size=16384")
+
+# Specifies libraries CMake should link to your target library. You
+# can link libraries from various origins, such as libraries defined in this
+# build script, prebuilt third-party libraries, or Android system libraries.
+target_link_libraries(${CMAKE_PROJECT_NAME}
+ # List libraries link to the target library
+ PRIVATE android
+ PRIVATE log
+ PRIVATE taglib)
diff --git a/musikr/src/main/cpp/JByteArrayRef.cpp b/musikr/src/main/cpp/JByteArrayRef.cpp
new file mode 100644
index 000000000..297d9d859
--- /dev/null
+++ b/musikr/src/main/cpp/JByteArrayRef.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2025 Auxio Project
+ * JByteArrayRef.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 .
+ */
+
+#include "JByteArrayRef.h"
+
+JByteArrayRef::JByteArrayRef(JNIEnv *env, TagLib::ByteVector &data) : env(env) {
+ auto size = static_cast(data.size());
+ array = env->NewByteArray(size);
+ env->SetByteArrayRegion(array, 0, static_cast(size),
+ reinterpret_cast(data.data()));
+}
+
+JByteArrayRef::JByteArrayRef(JNIEnv *env, jbyteArray array) : env(env), array(
+ array) {
+}
+
+JByteArrayRef::~JByteArrayRef() {
+ env->DeleteLocalRef(array);
+}
+
+TagLib::ByteVector JByteArrayRef::copy() {
+ jsize length = env->GetArrayLength(array);
+ auto data = env->GetByteArrayElements(array, nullptr);
+ TagLib::ByteVector byteVector(reinterpret_cast(data), length);
+ env->ReleaseByteArrayElements(array, data, JNI_ABORT);
+ return byteVector;
+}
+
+jbyteArray& JByteArrayRef::operator*() {
+ return array;
+}
+
diff --git a/musikr/src/main/cpp/JByteArrayRef.h b/musikr/src/main/cpp/JByteArrayRef.h
new file mode 100644
index 000000000..02ce6e032
--- /dev/null
+++ b/musikr/src/main/cpp/JByteArrayRef.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2025 Auxio Project
+ * JByteArrayRef.h 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 .
+ */
+
+#ifndef AUXIO_JBYTEARRAYREF_H
+#define AUXIO_JBYTEARRAYREF_H
+
+#include
+#include
+
+class JByteArrayRef {
+public:
+ JByteArrayRef(JNIEnv *env, TagLib::ByteVector &data);
+ JByteArrayRef(JNIEnv *env, jbyteArray array);
+
+ ~JByteArrayRef();
+
+ JByteArrayRef(const JByteArrayRef&) = delete;
+
+ JByteArrayRef& operator=(const JByteArrayRef&) = delete;
+
+ TagLib::ByteVector copy();
+
+ jbyteArray& operator*();
+
+private:
+ JNIEnv *env;
+ jbyteArray array;
+};
+
+#endif //AUXIO_JBYTEARRAYREF_H
diff --git a/musikr/src/main/cpp/JClassRef.cpp b/musikr/src/main/cpp/JClassRef.cpp
new file mode 100644
index 000000000..8bf38a3b5
--- /dev/null
+++ b/musikr/src/main/cpp/JClassRef.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2025 Auxio Project
+ * JClassRef.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 .
+ */
+
+#include "JClassRef.h"
+JClassRef::JClassRef(JNIEnv *env, const char *classpath) : env(env) {
+ clazz = env->FindClass(classpath);
+}
+
+JClassRef::~JClassRef() {
+ env->DeleteLocalRef(clazz);
+}
+
+jmethodID JClassRef::method(const char *name, const char *signature) {
+ return env->GetMethodID(clazz, name, signature);
+}
+
+jclass& JClassRef::operator*() {
+ return clazz;
+}
diff --git a/musikr/src/main/cpp/JClassRef.h b/musikr/src/main/cpp/JClassRef.h
new file mode 100644
index 000000000..b9c5336b1
--- /dev/null
+++ b/musikr/src/main/cpp/JClassRef.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2025 Auxio Project
+ * JClassRef.h 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 .
+ */
+
+#ifndef AUXIO_JCLASSREF_H
+#define AUXIO_JCLASSREF_H
+
+#include
+
+class JClassRef {
+public:
+ JClassRef(JNIEnv *env, const char *classpath);
+
+ ~JClassRef();
+
+ JClassRef(const JClassRef&) = delete;
+
+ JClassRef& operator=(const JClassRef&) = delete;
+
+ // Only exists to work around a broken lint that doesn't
+ // realize that this class is a smart pointer to jclass.
+ jmethodID method(const char *name, const char *signature);
+
+ jclass& operator*();
+
+private:
+ JNIEnv *env;
+ jclass clazz;
+};
+
+#endif //AUXIO_JCLASSREF_H
diff --git a/musikr/src/main/cpp/JInputStream.cpp b/musikr/src/main/cpp/JInputStream.cpp
new file mode 100644
index 000000000..8c729a8db
--- /dev/null
+++ b/musikr/src/main/cpp/JInputStream.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * JInputStream.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 .
+ */
+
+#include "JInputStream.h"
+
+#include
+
+#include "JClassRef.h"
+#include "JByteArrayRef.h"
+#include "JStringRef.h"
+
+JInputStream::JInputStream(JNIEnv *env, jobject jInputStream) : env(env), jInputStream(
+ jInputStream) {
+ JClassRef jInputStreamClass = { env,
+ "org/oxycblt/musikr/metadata/NativeInputStream" };
+ if (!env->IsInstanceOf(jInputStream, *jInputStreamClass)) {
+ throw std::runtime_error("Object is not NativeInputStream");
+ }
+ jmethodID jInputStreamNameMethod = jInputStreamClass.method("name",
+ "()Ljava/lang/String;");
+ jInputStreamReadBlockMethod = jInputStreamClass.method("readBlock",
+ "(Ljava/nio/ByteBuffer;)Z");
+ jInputStreamIsOpenMethod = jInputStreamClass.method("isOpen", "()Z");
+ jInputStreamSeekFromBeginningMethod = jInputStreamClass.method(
+ "seekFromBeginning", "(J)Z");
+ jInputStreamSeekFromCurrentMethod = jInputStreamClass.method(
+ "seekFromCurrent", "(J)Z");
+ jInputStreamSeekFromEndMethod = jInputStreamClass.method("seekFromEnd",
+ "(J)Z");
+ jInputStreamTellMethod = jInputStreamClass.method("tell", "()J");
+ jInputStreamLengthMethod = jInputStreamClass.method("length", "()J");
+ JStringRef jName = { env, reinterpret_cast(env->CallObjectMethod(
+ jInputStream, jInputStreamNameMethod)) };
+ _name = TagLib::String(env->GetStringUTFChars(*jName, nullptr));
+}
+
+JInputStream::~JInputStream() {
+ // The implicit assumption is that inputStream is managed by the owner,
+ // so we don't need to delete any references here
+}
+
+TagLib::FileName /* const char * */JInputStream::name() const {
+ return _name.toCString(true);
+}
+
+TagLib::ByteVector JInputStream::readBlock(size_t length) {
+ // We have to invert the buffer allocation here siits not a perfect system (vykeen instead of korvax0 but i warped all over the hub and i dont think its possible to find a "perfect" purple system like you would withnce the JVM ByteBuffer allocation system
+ // uses a bugged caching mechanism that leaks memory if used in multithreaded contexts.
+ TagLib::ByteVector buf { static_cast(length), 0 };
+ jobject wrappedByteBuffer = env->NewDirectByteBuffer(buf.data(),
+ buf.size());
+ if (wrappedByteBuffer == nullptr) {
+ throw std::runtime_error("Failed to wrap ByteBuffer");
+ }
+ JObjectRef byteBuffer = { env, wrappedByteBuffer };
+ jboolean result = env->CallBooleanMethod(jInputStream,
+ jInputStreamReadBlockMethod, *byteBuffer);
+ if (!result) {
+ throw std::runtime_error("Failed to read block, see logs");
+ }
+ return buf;
+}
+
+void JInputStream::writeBlock(const TagLib::ByteVector &data) {
+ throw std::runtime_error("Not implemented");
+}
+
+void JInputStream::insert(const TagLib::ByteVector &data,
+ TagLib::offset_t start, size_t replace) {
+ throw std::runtime_error("Not implemented");
+}
+
+void JInputStream::removeBlock(TagLib::offset_t start, size_t length) {
+ throw std::runtime_error("Not implemented");
+}
+
+bool JInputStream::readOnly() const {
+ return true;
+}
+
+bool JInputStream::isOpen() const {
+ return env->CallBooleanMethod(jInputStream, jInputStreamIsOpenMethod);
+}
+
+void JInputStream::seek(TagLib::offset_t offset, Position p) {
+ auto joffset = static_cast(std::llround(offset));
+ jboolean result;
+ switch (p) {
+ case Beginning:
+ result = env->CallBooleanMethod(jInputStream,
+ jInputStreamSeekFromBeginningMethod, joffset);
+ break;
+ case Current:
+ result = env->CallBooleanMethod(jInputStream,
+ jInputStreamSeekFromCurrentMethod, joffset);
+ break;
+ case End:
+ result = env->CallBooleanMethod(jInputStream,
+ jInputStreamSeekFromEndMethod, joffset);
+ break;
+ }
+ if (!result) {
+ throw std::runtime_error("Failed to seek, see logs");
+ }
+}
+
+void JInputStream::clear() {
+ // Nothing to do
+}
+
+TagLib::offset_t JInputStream::tell() const {
+ jlong jposition = env->CallLongMethod(jInputStream, jInputStreamTellMethod);
+ if (jposition == INT64_MIN) {
+ throw std::runtime_error("Failed to get position, see logs");
+ }
+ return static_cast(jposition);
+}
+
+TagLib::offset_t JInputStream::length() {
+ jlong jlength = env->CallLongMethod(jInputStream, jInputStreamLengthMethod);
+ if (jlength == INT64_MIN) {
+ throw std::runtime_error("Failed to get length, see logs");
+ }
+ return static_cast(jlength);
+}
+
+void JInputStream::truncate(TagLib::offset_t length) {
+ throw std::runtime_error("Not implemented");
+}
+
diff --git a/musikr/src/main/cpp/JInputStream.h b/musikr/src/main/cpp/JInputStream.h
new file mode 100644
index 000000000..026e6c3c3
--- /dev/null
+++ b/musikr/src/main/cpp/JInputStream.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * JInputStream.h 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 .
+ */
+
+#ifndef AUXIO_JINPUTSTREAM_H
+#define AUXIO_JINPUTSTREAM_H
+
+#include
+#include "JObjectRef.h"
+
+#include "taglib/tiostream.h"
+#include "taglib/tstring.h"
+
+class JInputStream: public TagLib::IOStream {
+public:
+ JInputStream(JNIEnv *env, jobject jInputStream);
+
+ ~JInputStream();
+
+ JInputStream(const JInputStream&) = delete;
+ JInputStream& operator=(const JInputStream&) = delete;
+
+ /*!
+ * Returns the stream name in the local file system encoding.
+ */
+ TagLib::FileName /* const char * */name() const override;
+
+ /*!
+ * Reads a block of size \a length at the current get pointer.
+ */
+ TagLib::ByteVector readBlock(size_t length) override;
+
+ /*!
+ * Attempts to write the block \a data at the current get pointer. If the
+ * file is currently only opened read only -- i.e. readOnly() returns \c true --
+ * this attempts to reopen the file in read/write mode.
+ *
+ * \note This should be used instead of using the streaming output operator
+ * for a ByteVector. And even this function is significantly slower than
+ * doing output with a char[].
+ */
+ void writeBlock(const TagLib::ByteVector &data) override;
+
+ /*!
+ * Insert \a data at position \a start in the file overwriting \a replace
+ * bytes of the original content.
+ *
+ * \note This method is slow since it requires rewriting all of the file
+ * after the insertion point.
+ */
+ void insert(const TagLib::ByteVector &data, TagLib::offset_t start = 0,
+ size_t replace = 0) override;
+
+ /*!
+ * Removes a block of the file starting a \a start and continuing for
+ * \a length bytes.
+ *
+ * \note This method is slow since it involves rewriting all of the file
+ * after the removed portion.
+ */
+ void removeBlock(TagLib::offset_t start = 0, size_t length = 0) override;
+
+ /*!
+ * Returns \c true if the file is read only (or if the file can not be opened).
+ */
+ bool readOnly() const override;
+
+ /*!
+ * Since the file can currently only be opened as an argument to the
+ * constructor (sort-of by design), this returns if that open succeeded.
+ */
+ bool isOpen() const override;
+
+ /*!
+ * Move the I/O pointer to \a offset in the stream from position \a p. This
+ * defaults to seeking from the beginning of the stream.
+ *
+ * \see Position
+ */
+ void seek(TagLib::offset_t offset, Position p = Beginning) override;
+
+ /*!
+ * Reset the end-of-stream and error flags on the stream.
+ */
+ void clear() override;
+
+ /*!
+ * Returns the current offset within the stream.
+ */
+ TagLib::offset_t tell() const override;
+
+ /*!
+ * Returns the length of the stream.
+ */
+ TagLib::offset_t length() override;
+
+ /*!
+ * Truncates the stream to a \a length.
+ */
+ void truncate(TagLib::offset_t length) override;
+
+private:
+ JNIEnv *env;
+ jobject jInputStream;
+ TagLib::String _name;
+ jmethodID jInputStreamReadBlockMethod;
+ jmethodID jInputStreamIsOpenMethod;
+ jmethodID jInputStreamSeekFromBeginningMethod;
+ jmethodID jInputStreamSeekFromCurrentMethod;
+ jmethodID jInputStreamSeekFromEndMethod;
+ jmethodID jInputStreamTellMethod;
+ jmethodID jInputStreamLengthMethod;
+};
+
+#endif //AUXIO_JINPUTSTREAM_H
diff --git a/musikr/src/main/cpp/JMetadataBuilder.cpp b/musikr/src/main/cpp/JMetadataBuilder.cpp
new file mode 100644
index 000000000..baab4df1a
--- /dev/null
+++ b/musikr/src/main/cpp/JMetadataBuilder.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * JMetadataBuilder.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 .
+ */
+
+#include "JMetadataBuilder.h"
+
+#include "util.h"
+
+#include
+#include
+#include
+
+#include
+
+#include "JObjectRef.h"
+#include "JClassRef.h"
+#include "JStringRef.h"
+#include "JByteArrayRef.h"
+
+JMetadataBuilder::JMetadataBuilder(JNIEnv *env) : env(env), id3v2(env), xiph(
+ env), mp4(env), cover(), properties(nullptr) {
+}
+
+void JMetadataBuilder::setMimeType(TagLib::String type) {
+ mimeType = type;
+}
+
+void JMetadataBuilder::setId3v1(TagLib::ID3v1::Tag &tag) {
+ id3v2.add_id("TIT2", tag.title());
+ id3v2.add_id("TPE1", tag.artist());
+ id3v2.add_id("TALB", tag.album());
+ id3v2.add_id("TRCK", std::to_string(tag.track()));
+ id3v2.add_id("TYER", std::to_string(tag.year()));
+ const int genreNumber = tag.genreNumber();
+ if (genreNumber != 255) {
+ id3v2.add_id("TCON", std::to_string(genreNumber));
+ }
+}
+
+void JMetadataBuilder::setId3v2(TagLib::ID3v2::Tag &tag) {
+ // We want to ideally find the front cover, fall back to the first picture otherwise.
+ std::optional firstPic;
+ std::optional frontCoverPic;
+ for (auto frame : tag.frameList()) {
+ if (auto txxxFrame =
+ dynamic_cast(frame)) {
+ TagLib::String id = frame->frameID();
+ TagLib::StringList frameText = txxxFrame->fieldList();
+ if (frameText.isEmpty())
+ continue;
+ auto begin = frameText.begin();
+ TagLib::String description = *begin;
+ frameText.erase(begin);
+ id3v2.add_combined(id, description, frameText);
+ } else if (auto textFrame =
+ dynamic_cast(frame)) {
+ TagLib::String key = frame->frameID();
+ TagLib::StringList frameText = textFrame->fieldList();
+ id3v2.add_id(key, frameText);
+ } else if (auto pictureFrame =
+ dynamic_cast(frame)) {
+ if (!firstPic) {
+ firstPic = pictureFrame;
+ }
+ if (!frontCoverPic
+ && pictureFrame->type()
+ == TagLib::ID3v2::AttachedPictureFrame::FrontCover) {
+ frontCoverPic = pictureFrame;
+ }
+ } else {
+ continue;
+ }
+ }
+ if (frontCoverPic) {
+ auto pic = *frontCoverPic;
+ cover = pic->picture();
+ } else if (firstPic) {
+ auto pic = *firstPic;
+ cover = pic->picture();
+ }
+}
+
+void JMetadataBuilder::setXiph(TagLib::Ogg::XiphComment &tag) {
+ for (auto field : tag.fieldListMap()) {
+ auto key = field.first.upper();
+ auto values = field.second;
+ xiph.add_custom(key, values);
+ }
+ auto pics = tag.pictureList();
+ setFlacPictures(pics);
+}
+
+template
+void mp4AddImpl(JTagMap &map, TagLib::String &itemName, T itemValue) {
+ if (itemName.startsWith("----")) {
+ // Split this into it's atom name and description
+ auto split = itemName.find(':');
+ auto atomName = itemName.substr(0, split);
+ auto atomDescription = itemName.substr(split + 1);
+ map.add_combined(atomName, atomDescription, itemValue);
+ } else {
+ map.add_id(itemName, itemValue);
+ }
+}
+
+void JMetadataBuilder::setMp4(TagLib::MP4::Tag &tag) {
+ auto map = tag.itemMap();
+ std::optional < TagLib::MP4::CoverArt > firstCover;
+ for (auto item : map) {
+ auto itemName = item.first;
+ auto itemValue = item.second;
+ if (itemName == "covr") {
+ // Special cover case.
+ // MP4 has no types, so just prioritize easier to decode covers (PNG, JPEG)
+ auto pics = itemValue.toCoverArtList();
+ for (auto &pic : pics) {
+ auto format = pic.format();
+ if (format == TagLib::MP4::CoverArt::PNG
+ || format == TagLib::MP4::CoverArt::JPEG) {
+ cover = pic.data();
+ continue;
+ }
+ }
+ if (!pics.isEmpty()) {
+ cover = pics.front().data();
+ }
+ continue;
+ }
+ auto type = itemValue.type();
+ std::string serializedValue;
+ switch (type) {
+ // Normal expected MP4 items
+ case TagLib::MP4::Item::Type::StringList:
+ mp4AddImpl(mp4, itemName, itemValue.toStringList());
+ break;
+ // Weird MP4 items I'm 90% sure I'll encounter.
+ case TagLib::MP4::Item::Type::Int:
+ serializedValue = std::to_string(itemValue.toInt());
+ break;
+ case TagLib::MP4::Item::Type::UInt:
+ serializedValue = std::to_string(itemValue.toUInt());
+ break;
+ case TagLib::MP4::Item::Type::LongLong:
+ serializedValue = std::to_string(itemValue.toLongLong());
+ break;
+ case TagLib::MP4::Item::Type::IntPair:
+ // It's inefficient going from the integer representation back into
+ // a string, but I fully expect taggers to just write "NN/TT" strings
+ // anyway, and musikr doesn't have to do as much fiddly variant handling.
+ serializedValue = std::to_string(itemValue.toIntPair().first) + "/"
+ + std::to_string(itemValue.toIntPair().second);
+ break;
+ default:
+ // Don't care about the other types
+ continue;
+ }
+ mp4AddImpl(mp4, itemName, TagLib::String(serializedValue));
+ }
+}
+
+void JMetadataBuilder::setFlacPictures(
+ TagLib::List &pics) {
+ // Find the front cover image. If it doesn't exist, fall back to the first image.
+ for (auto pic : pics) {
+ if (pic->type() == TagLib::FLAC::Picture::FrontCover) {
+ cover = pic->data();
+ return;
+ }
+ }
+ if (!pics.isEmpty()) {
+ cover = pics.front()->data();
+ }
+}
+
+void JMetadataBuilder::setProperties(TagLib::AudioProperties *properties) {
+ this->properties = properties;
+}
+
+jobject JMetadataBuilder::build() {
+ JClassRef jPropertiesClass { env, "org/oxycblt/musikr/metadata/Properties" };
+ jmethodID jPropertiesInitMethod = jPropertiesClass.method("",
+ "(Ljava/lang/String;JII)V");
+ JStringRef jMimeType { env, this->mimeType };
+
+ JObjectRef jProperties { env, env->NewObject(*jPropertiesClass,
+ jPropertiesInitMethod, *jMimeType,
+ (jlong) properties->lengthInMilliseconds(), properties->bitrate(),
+ properties->sampleRate()) };
+
+ JClassRef jMetadataClass { env, "org/oxycblt/musikr/metadata/Metadata" };
+ jmethodID jMetadataInitMethod = jMetadataClass.method("",
+ "(Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;[BLorg/"
+ "oxycblt/musikr/metadata/Properties;)V");
+ auto jId3v2Map = id3v2.getObject();
+ auto jXiphMap = xiph.getObject();
+ auto jMp4Map = mp4.getObject();
+ if (cover.has_value()) {
+ JByteArrayRef jCoverArray { env, cover.value() };
+ jobject result = env->NewObject(*jMetadataClass, jMetadataInitMethod,
+ **jId3v2Map, **jXiphMap, **jMp4Map, *jCoverArray, *jProperties);
+ return result;
+ }
+ return env->NewObject(*jMetadataClass, jMetadataInitMethod, **jId3v2Map,
+ **jXiphMap, **jMp4Map, nullptr, *jProperties);
+}
diff --git a/musikr/src/main/cpp/JMetadataBuilder.h b/musikr/src/main/cpp/JMetadataBuilder.h
new file mode 100644
index 000000000..7bca6cbbb
--- /dev/null
+++ b/musikr/src/main/cpp/JMetadataBuilder.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * JMetadataBuilder.h 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 .
+ */
+
+#ifndef AUXIO_JMETADATABUILDER_H
+#define AUXIO_JMETADATABUILDER_H
+
+#include
+#include
+#include
+
+#include "taglib/id3v1tag.h"
+#include "taglib/id3v2tag.h"
+#include "taglib/xiphcomment.h"
+#include "taglib/mp4tag.h"
+#include "taglib/audioproperties.h"
+
+#include "JTagMap.h"
+
+class JMetadataBuilder {
+public:
+ JMetadataBuilder(JNIEnv *env);
+
+ void setMimeType(TagLib::String type);
+ void setId3v1(TagLib::ID3v1::Tag &tag);
+ void setId3v2(TagLib::ID3v2::Tag &tag);
+ void setXiph(TagLib::Ogg::XiphComment &tag);
+ void setMp4(TagLib::MP4::Tag &tag);
+ void setFlacPictures(TagLib::List &pics);
+ void setProperties(TagLib::AudioProperties *properties);
+
+ jobject build();
+
+private:
+ JNIEnv *env;
+
+ TagLib::String mimeType;
+
+ std::optional cover;
+ TagLib::AudioProperties *properties;
+
+ JTagMap id3v2;
+ JTagMap xiph;
+ JTagMap mp4;
+};
+
+#endif //AUXIO_JMETADATABUILDER_H
diff --git a/app/src/main/java/org/oxycblt/auxio/music/device/DeviceModule.kt b/musikr/src/main/cpp/JObjectRef.cpp
similarity index 62%
rename from app/src/main/java/org/oxycblt/auxio/music/device/DeviceModule.kt
rename to musikr/src/main/cpp/JObjectRef.cpp
index 85e8e511e..9d6914cbf 100644
--- a/app/src/main/java/org/oxycblt/auxio/music/device/DeviceModule.kt
+++ b/musikr/src/main/cpp/JObjectRef.cpp
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2023 Auxio Project
- * DeviceModule.kt is part of Auxio.
+ * Copyright (c) 2025 Auxio Project
+ * JObjectRef.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
@@ -16,15 +16,15 @@
* along with this program. If not, see .
*/
-package org.oxycblt.auxio.music.device
+#include "JObjectRef.h"
-import dagger.Binds
-import dagger.Module
-import dagger.hilt.InstallIn
-import dagger.hilt.components.SingletonComponent
-
-@Module
-@InstallIn(SingletonComponent::class)
-interface DeviceModule {
- @Binds fun deviceLibraryFactory(factory: DeviceLibraryFactoryImpl): DeviceLibrary.Factory
+JObjectRef::JObjectRef(JNIEnv *env, jobject object) : env(env), object(object) {
+}
+
+JObjectRef::~JObjectRef() {
+ env->DeleteLocalRef(object);
+}
+
+jobject& JObjectRef::operator*() {
+ return object;
}
diff --git a/musikr/src/main/cpp/JObjectRef.h b/musikr/src/main/cpp/JObjectRef.h
new file mode 100644
index 000000000..affc383e5
--- /dev/null
+++ b/musikr/src/main/cpp/JObjectRef.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2025 Auxio Project
+ * JObjectRef.h 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 .
+ */
+
+#ifndef AUXIO_JOBJECTREF_H
+#define AUXIO_JOBJECTREF_H
+
+#include
+#include
+#include
+#include "JObjectRef.h"
+
+class JObjectRef {
+public:
+ JObjectRef(JNIEnv *env, jobject object);
+
+ ~JObjectRef();
+
+ JObjectRef(const JObjectRef&) = delete;
+
+ JObjectRef& operator=(const JObjectRef&) = delete;
+
+ jobject& operator*();
+
+private:
+ JNIEnv *env;
+ jobject object;
+};
+
+#endif //AUXIO_JOBJECTREF_H
diff --git a/musikr/src/main/cpp/JStringRef.cpp b/musikr/src/main/cpp/JStringRef.cpp
new file mode 100644
index 000000000..c91b863d5
--- /dev/null
+++ b/musikr/src/main/cpp/JStringRef.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2025 Auxio Project
+ * JStringRef.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 .
+ */
+
+#include "JStringRef.h"
+#include "util.h"
+
+JStringRef::JStringRef(JNIEnv *env, jstring jString) : env(env), string(jString) {
+}
+
+JStringRef::JStringRef(JNIEnv *env, const TagLib::String string) {
+ this->env = env;
+ this->string = env->NewStringUTF(string.toCString(true));
+}
+
+JStringRef::~JStringRef() {
+ env->DeleteLocalRef(string);
+}
+
+TagLib::String JStringRef::copy() {
+ auto chars = env->GetStringUTFChars(string, nullptr);
+ TagLib::String result = chars;
+ env->ReleaseStringUTFChars(string, chars);
+ return result;
+}
+
+jstring& JStringRef::operator*() {
+ return string;
+}
diff --git a/musikr/src/main/cpp/JStringRef.h b/musikr/src/main/cpp/JStringRef.h
new file mode 100644
index 000000000..89fcb046d
--- /dev/null
+++ b/musikr/src/main/cpp/JStringRef.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2025 Auxio Project
+ * JStringRef.h 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 .
+ */
+
+#ifndef AUXIO_JSTRINGREF_H
+#define AUXIO_JSTRINGREF_H
+
+#include
+#include
+
+class JStringRef {
+public:
+ JStringRef(JNIEnv *env, jstring jString);
+
+ JStringRef(JNIEnv *env, TagLib::String string);
+
+ ~JStringRef();
+
+ JStringRef(const JStringRef&) = delete;
+
+ JStringRef& operator=(const JStringRef&) = delete;
+
+ TagLib::String copy();
+
+ jstring& operator*();
+
+private:
+ JNIEnv *env;
+ jstring string;
+};
+
+#endif //AUXIO_JSTRINGREF_H
diff --git a/musikr/src/main/cpp/JTagMap.cpp b/musikr/src/main/cpp/JTagMap.cpp
new file mode 100644
index 000000000..f229f3387
--- /dev/null
+++ b/musikr/src/main/cpp/JTagMap.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * JTagMap.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 .
+ */
+
+#include "JTagMap.h"
+
+#include "JStringRef.h"
+
+JTagMap::JTagMap(JNIEnv *env) : env(env) {
+ auto jTagMapClass = std::make_unique < JClassRef
+ > (env, "org/oxycblt/musikr/metadata/NativeTagMap");
+ auto jTagMapInitMethod = jTagMapClass->method("", "()V");
+ jTagMap = std::move(
+ std::make_unique < JObjectRef
+ > (env, env->NewObject(**jTagMapClass, jTagMapInitMethod)));
+ jTagMapAddIdSingleMethod = jTagMapClass->method("addID",
+ "(Ljava/lang/String;Ljava/lang/String;)V");
+ jTagMapAddIdListMethod = jTagMapClass->method("addID",
+ "(Ljava/lang/String;Ljava/util/List;)V");
+ jTagMapAddCustomSingleMethod = jTagMapClass->method("addCustom",
+ "(Ljava/lang/String;Ljava/lang/String;)V");
+ jTagMapAddCustomListMethod = jTagMapClass->method("addCustom",
+ "(Ljava/lang/String;Ljava/util/List;)V");
+ jTagMapAddCombinedSingleMethod = jTagMapClass->method("addCombined",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
+ jTagMapAddCombinedListMethod = jTagMapClass->method("addCombined",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)V");
+ jTagMapGetObjectMethod = jTagMapClass->method("getObject",
+ "()Ljava/util/Map;");
+
+ jArrayListClass = std::make_unique < JClassRef
+ > (env, "java/util/ArrayList");
+ jArrayListInitMethod = jArrayListClass->method("", "()V");
+ jArrayListAddMethod = jArrayListClass->method("add",
+ "(Ljava/lang/Object;)Z");
+}
+
+void JTagMap::add_id(const TagLib::String id, const TagLib::String value) {
+ JStringRef jId { env, id };
+ JStringRef jValue { env, value };
+ env->CallVoidMethod(**jTagMap, jTagMapAddIdSingleMethod, *jId, *jValue);
+}
+
+void JTagMap::add_id(const TagLib::String id, const TagLib::StringList values) {
+ JStringRef jId { env, id };
+ JObjectRef jValues { env, env->NewObject(**jArrayListClass,
+ jArrayListInitMethod) };
+ for (auto &value : values) {
+ JStringRef jValue { env, value };
+ env->CallBooleanMethod(*jValues, jArrayListAddMethod, *jValue);
+ }
+ env->CallVoidMethod(**jTagMap, jTagMapAddIdListMethod, *jId, *jValues);
+}
+
+void JTagMap::add_custom(const TagLib::String description,
+ const TagLib::String value) {
+ JStringRef jDescription { env, description };
+ JStringRef jValue { env, value };
+ env->CallVoidMethod(**jTagMap, jTagMapAddCustomSingleMethod, *jDescription,
+ *jValue);
+}
+
+void JTagMap::add_custom(const TagLib::String description,
+ const TagLib::StringList values) {
+ JStringRef jDescription { env, description };
+ JObjectRef jValues { env, env->NewObject(**jArrayListClass,
+ jArrayListInitMethod) };
+ for (auto &value : values) {
+ JStringRef jValue { env, value };
+ env->CallBooleanMethod(*jValues, jArrayListAddMethod, *jValue);
+ }
+ env->CallVoidMethod(**jTagMap, jTagMapAddCustomListMethod, *jDescription,
+ *jValues);
+}
+
+void JTagMap::add_combined(const TagLib::String id,
+ const TagLib::String description, const TagLib::String value) {
+ JStringRef jId { env, id };
+ JStringRef jDescription { env, description };
+ JStringRef jValue { env, value };
+ env->CallVoidMethod(**jTagMap, jTagMapAddCombinedSingleMethod, *jId,
+ *jDescription, *jValue);
+}
+
+void JTagMap::add_combined(const TagLib::String id,
+ const TagLib::String description, const TagLib::StringList values) {
+ JStringRef jId { env, id };
+ JStringRef jDescription { env, description };
+ JObjectRef jValues { env, env->NewObject(**jArrayListClass,
+ jArrayListInitMethod) };
+ for (auto &value : values) {
+ JStringRef jValue { env, value };
+ env->CallBooleanMethod(*jValues, jArrayListAddMethod, *jValue);
+ }
+ env->CallVoidMethod(**jTagMap, jTagMapAddCombinedListMethod, *jId,
+ *jDescription, *jValues);
+}
+
+std::unique_ptr JTagMap::getObject() {
+ return std::move(
+ std::make_unique < JObjectRef
+ > (env, env->CallObjectMethod(**jTagMap,
+ jTagMapGetObjectMethod)));
+}
diff --git a/musikr/src/main/cpp/JTagMap.h b/musikr/src/main/cpp/JTagMap.h
new file mode 100644
index 000000000..95293db7b
--- /dev/null
+++ b/musikr/src/main/cpp/JTagMap.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * JTagMap.h 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 .
+ */
+
+#ifndef AUXIO_JTAGMAP_H
+#define AUXIO_JTAGMAP_H
+
+#include
+#include
+#include
+#include
+
+#include "JObjectRef.h"
+#include "JClassRef.h"
+
+class JTagMap {
+public:
+ JTagMap(JNIEnv *env);
+
+ JTagMap(const JTagMap&) = delete;
+ JTagMap& operator=(const JTagMap&) = delete;
+
+ void add_id(TagLib::String id, TagLib::String value);
+ void add_id(TagLib::String id, TagLib::StringList values);
+
+ void add_custom(TagLib::String description, TagLib::String value);
+ void add_custom(TagLib::String description, TagLib::StringList values);
+
+ void add_combined(TagLib::String id, TagLib::String description,
+ TagLib::String value);
+ void add_combined(TagLib::String id, TagLib::String description,
+ TagLib::StringList values);
+
+ std::unique_ptr getObject();
+
+private:
+ JNIEnv *env;
+
+ std::unique_ptr jTagMap;
+ jmethodID jTagMapAddIdSingleMethod;
+ jmethodID jTagMapAddIdListMethod;
+ jmethodID jTagMapAddCustomSingleMethod;
+ jmethodID jTagMapAddCustomListMethod;
+ jmethodID jTagMapAddCombinedSingleMethod;
+ jmethodID jTagMapAddCombinedListMethod;
+ jmethodID jTagMapGetObjectMethod;
+
+ std::unique_ptr jArrayListClass;
+ jmethodID jArrayListInitMethod;
+ jmethodID jArrayListAddMethod;
+};
+
+#endif //AUXIO_JTAGMAP_H
diff --git a/musikr/src/main/cpp/android.toolchain.cmake b/musikr/src/main/cpp/android.toolchain.cmake
new file mode 100644
index 000000000..53123fef9
--- /dev/null
+++ b/musikr/src/main/cpp/android.toolchain.cmake
@@ -0,0 +1,15 @@
+# Define the minimum CMake version and project name
+cmake_minimum_required(VERSION 3.22.1)
+
+# Set the Android NDK path
+option(ANDROID_NDK_PATH "Path to Android NDK Install. Should be same version specified in gradle." REQUIRED)
+
+# Specify the target Android API level
+set(ANDROID_PLATFORM android-24)
+
+# Define the toolchain
+set(CMAKE_SYSTEM_NAME Android)
+set(CMAKE_ANDROID_ARCH_ABI ${ANDROID_ABI})
+set(CMAKE_ANDROID_NDK ${ANDROID_NDK_PATH})
+set(CMAKE_ANDROID_STL_TYPE c++_static)
+set(CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION clang)
\ No newline at end of file
diff --git a/musikr/src/main/cpp/build_taglib.sh b/musikr/src/main/cpp/build_taglib.sh
new file mode 100755
index 000000000..3a4363d4b
--- /dev/null
+++ b/musikr/src/main/cpp/build_taglib.sh
@@ -0,0 +1,43 @@
+set -e
+WORKING_DIR=$1
+echo "Working directory is at $WORKING_DIR"
+cd "$WORKING_DIR"
+
+TAGLIB_SRC_DIR=${WORKING_DIR}/taglib
+TAGLIB_DST_DIR=${WORKING_DIR}/taglib/build
+TAGLIB_PKG_DIR=${WORKING_DIR}/taglib/pkg
+NDK_TOOLCHAIN=${WORKING_DIR}/android.toolchain.cmake
+NDK_PATH=$2
+echo "Taglib source is at $TAGLIB_SRC_DIR"
+echo "Taglib build is at $TAGLIB_DST_DIR"
+echo "Taglib package is at $TAGLIB_PKG_DIR"
+echo "NDK toolchain is at $NDK_TOOLCHAIN"
+echo "NDK path is at $NDK_PATH"
+
+X86_ARCH=x86
+X86_64_ARCH=x86_64
+ARMV7_ARCH=armeabi-v7a
+ARMV8_ARCH=arm64-v8a
+
+build_for_arch() {
+ local ARCH=$1
+ local DST_DIR=$TAGLIB_DST_DIR/$ARCH
+ local PKG_DIR=$TAGLIB_PKG_DIR/$ARCH
+
+ cd $TAGLIB_SRC_DIR
+ cmake -B $DST_DIR -DANDROID_NDK_PATH=${NDK_PATH} -DCMAKE_TOOLCHAIN_FILE=${NDK_TOOLCHAIN} \
+ -DANDROID_ABI=$ARCH -DBUILD_SHARED_LIBS=OFF -DVISIBILITY_HIDDEN=ON -DBUILD_TESTING=OFF \
+ -DBUILD_EXAMPLES=OFF -DBUILD_BINDINGS=OFF -DWITH_ZLIB=OFF -DCMAKE_BUILD_TYPE=Release \
+ -DWITH_APE=OFF -DWITH_ASF=OFF -DWITH_ASF=OFF -DWITH_MOD=OFF -DWITH_SHORTEN=OFF \
+ -DWITH_TRUEAUDIO=OFF -DCMAKE_CXX_FLAGS="-fPIC"
+ # Try to parallelize the build
+ cmake --build $DST_DIR --config Release -j$(nproc)
+ cd $WORKING_DIR
+
+ cmake --install $DST_DIR --config Release --prefix $PKG_DIR --strip
+}
+
+build_for_arch $X86_ARCH
+build_for_arch $X86_64_ARCH
+build_for_arch $ARMV7_ARCH
+build_for_arch $ARMV8_ARCH
diff --git a/musikr/src/main/cpp/taglib b/musikr/src/main/cpp/taglib
new file mode 160000
index 000000000..ee1931b81
--- /dev/null
+++ b/musikr/src/main/cpp/taglib
@@ -0,0 +1 @@
+Subproject commit ee1931b81116cd0091c906896f6f4fb74850be51
diff --git a/musikr/src/main/cpp/taglib_jni.cpp b/musikr/src/main/cpp/taglib_jni.cpp
new file mode 100644
index 000000000..abb94e9f7
--- /dev/null
+++ b/musikr/src/main/cpp/taglib_jni.cpp
@@ -0,0 +1,200 @@
+/*
+ * 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 .
+ */
+
+#include
+#include
+#include "JInputStream.h"
+#include "JMetadataBuilder.h"
+#include "util.h"
+
+#include "taglib/fileref.h"
+#include "taglib/flacfile.h"
+#include "taglib/mp4file.h"
+#include "taglib/mpegfile.h"
+#include "taglib/opusfile.h"
+#include "taglib/vorbisfile.h"
+#include "taglib/wavfile.h"
+
+bool parseMpeg(const char *name, TagLib::File *file,
+ JMetadataBuilder &jBuilder) {
+ auto *mpegFile = dynamic_cast(file);
+ if (mpegFile == nullptr) {
+ return false;
+ }
+ auto id3v1Tag = mpegFile->ID3v1Tag();
+ if (id3v1Tag != nullptr) {
+ try {
+ jBuilder.setId3v1(*id3v1Tag);
+ } catch (std::exception &e) {
+ LOGE("Unable to parse ID3v1 tag in %s: %s", name, e.what());
+ }
+ }
+ auto id3v2Tag = mpegFile->ID3v2Tag();
+ if (id3v2Tag != nullptr) {
+ try {
+ jBuilder.setId3v2(*id3v2Tag);
+ } catch (std::exception &e) {
+ LOGE("Unable to parse ID3v2 tag in %s: %s", name, e.what());
+ }
+ }
+ return true;
+}
+
+bool parseMp4(const char *name, TagLib::File *file,
+ JMetadataBuilder &jBuilder) {
+ auto *mp4File = dynamic_cast(file);
+ if (mp4File == nullptr) {
+ return false;
+ }
+ auto tag = mp4File->tag();
+ if (tag != nullptr) {
+ try {
+ jBuilder.setMp4(*tag);
+ } catch (std::exception &e) {
+ LOGE("Unable to parse MP4 tag in %s: %s", name, e.what());
+ }
+ }
+ return true;
+}
+
+bool parseFlac(const char *name, TagLib::File *file,
+ JMetadataBuilder &jBuilder) {
+ auto *flacFile = dynamic_cast(file);
+ if (flacFile == nullptr) {
+ return false;
+ }
+ auto id3v1Tag = flacFile->ID3v1Tag();
+ if (id3v1Tag != nullptr) {
+ try {
+ jBuilder.setId3v1(*id3v1Tag);
+ } catch (std::exception &e) {
+ LOGE("Unable to parse ID3v1 tag in %s: %s", name, e.what());
+ }
+ }
+ auto id3v2Tag = flacFile->ID3v2Tag();
+ if (id3v2Tag != nullptr) {
+ try {
+ jBuilder.setId3v2(*id3v2Tag);
+ } catch (std::exception &e) {
+ LOGE("Unable to parse ID3v2 tag in %s: %s", name, e.what());
+ }
+ }
+ auto xiphComment = flacFile->xiphComment();
+ if (xiphComment != nullptr) {
+ try {
+ jBuilder.setXiph(*xiphComment);
+ } catch (std::exception &e) {
+ LOGE("Unable to parse Xiph comment in %s: %s", name, e.what());
+ }
+ }
+ auto pics = flacFile->pictureList();
+ jBuilder.setFlacPictures(pics);
+ return true;
+}
+
+bool parseOpus(const char *name, TagLib::File *file,
+ JMetadataBuilder &jBuilder) {
+ auto *opusFile = dynamic_cast(file);
+ if (opusFile == nullptr) {
+ return false;
+ }
+ auto tag = opusFile->tag();
+ if (tag != nullptr) {
+ try {
+ jBuilder.setXiph(*tag);
+ } catch (std::exception &e) {
+ LOGE("Unable to parse Xiph comment in %s: %s", name, e.what());
+ }
+ }
+ return true;
+}
+
+bool parseVorbis(const char *name, TagLib::File *file,
+ JMetadataBuilder &jBuilder) {
+ auto *vorbisFile = dynamic_cast(file);
+ if (vorbisFile == nullptr) {
+ return false;
+ }
+ auto tag = vorbisFile->tag();
+ if (tag != nullptr) {
+ try {
+ jBuilder.setXiph(*tag);
+ } catch (std::exception &e) {
+ LOGE("Unable to parse Xiph comment %s: %s", name, e.what());
+ }
+ }
+ return true;
+}
+
+bool parseWav(const char *name, TagLib::File *file,
+ JMetadataBuilder &jBuilder) {
+ auto *wavFile = dynamic_cast(file);
+ if (wavFile == nullptr) {
+ return false;
+ }
+ auto tag = wavFile->ID3v2Tag();
+ if (tag != nullptr) {
+ try {
+ jBuilder.setId3v2(*tag);
+ } catch (std::exception &e) {
+ LOGE("Unable to parse ID3v2 tag in %s: %s", name, e.what());
+ }
+ }
+ return true;
+}
+
+extern "C" JNIEXPORT jobject JNICALL
+Java_org_oxycblt_musikr_metadata_TagLibJNI_openNative(JNIEnv *env,
+ jobject /* this */,
+ jobject inputStream) {
+ const char *name = nullptr;
+ try {
+ JInputStream jStream {env, inputStream};
+ name = jStream.name();
+ TagLib::FileRef fileRef {&jStream};
+ if (fileRef.isNull()) {
+ throw std::runtime_error("Invalid file");
+ }
+ TagLib::File *file = fileRef.file();
+ JMetadataBuilder jBuilder {env};
+ jBuilder.setProperties(file->audioProperties());
+
+ // TODO: Make some type of composable logger so I don't
+ // have to shoehorn this into the native code.
+ if (parseMpeg(name, file, jBuilder)) {
+ jBuilder.setMimeType("audio/mpeg");
+ } else if (parseMp4(name, file, jBuilder)) {
+ jBuilder.setMimeType("audio/mp4");
+ } else if (parseFlac(name, file, jBuilder)) {
+ jBuilder.setMimeType("audio/flac");
+ } else if (parseOpus(name, file, jBuilder)) {
+ jBuilder.setMimeType("audio/opus");
+ } else if (parseVorbis(name, file, jBuilder)) {
+ jBuilder.setMimeType("audio/vorbis");
+ } else if (parseWav(name, file, jBuilder)) {
+ jBuilder.setMimeType("audio/wav");
+ } else {
+ LOGE("File format in %s is not supported", name);
+ return nullptr;
+ }
+ return jBuilder.build();
+ } catch (std::exception &e) {
+ LOGE("Unable to parse metadata in %s: %s", name != nullptr ? name : "unknown file", e.what());
+ return nullptr;
+ }
+}
diff --git a/musikr/src/main/cpp/util.h b/musikr/src/main/cpp/util.h
new file mode 100644
index 000000000..ce9ad7255
--- /dev/null
+++ b/musikr/src/main/cpp/util.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * util.h 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 .
+ */
+
+#ifndef AUXIO_UTIL_H
+#define AUXIO_UTIL_H
+
+#include
+#include
+
+#define LOG_TAG "taglib_jni"
+#define LOGE(...) \
+ ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
+#define LOGD(...) \
+ ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
+
+#endif //AUXIO_UTIL_H
diff --git a/musikr/src/main/java/org/oxycblt/musikr/Config.kt b/musikr/src/main/java/org/oxycblt/musikr/Config.kt
new file mode 100644
index 000000000..a793680db
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/Config.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * Config.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 .
+ */
+
+package org.oxycblt.musikr
+
+import org.oxycblt.musikr.cache.Cache
+import org.oxycblt.musikr.cover.MutableCovers
+import org.oxycblt.musikr.playlist.db.StoredPlaylists
+import org.oxycblt.musikr.tag.interpret.Naming
+import org.oxycblt.musikr.tag.interpret.Separators
+
+/** Side-effect laden [Storage] for use during music loading and [MutableLibrary] operation. */
+data class Storage(
+ /**
+ * A factory producing a repository of cached metadata to read and write from over the course of
+ * music loading. This will only be used during music loading.
+ */
+ val cache: Cache.Factory,
+
+ /**
+ * A repository of cover images to for re-use during music loading. Should be kept in lock-step
+ * with the cache for best performance. This will be used during music loading and when
+ * retrieving cover information from the library.
+ */
+ val storedCovers: MutableCovers,
+
+ /**
+ * A repository of user-created playlists that should also be loaded into the library. This will
+ * be used during music loading and mutated when creating, renaming, or deleting playlists in
+ * the library.
+ */
+ val storedPlaylists: StoredPlaylists
+)
+
+/** Configuration for how to interpret and extrapolate certain audio tags. */
+data class Interpretation(
+ /** How to construct names from audio tags. */
+ val naming: Naming,
+
+ /** What separators delimit multi-value audio tags. */
+ val separators: Separators
+)
diff --git a/musikr/src/main/java/org/oxycblt/musikr/Library.kt b/musikr/src/main/java/org/oxycblt/musikr/Library.kt
new file mode 100644
index 000000000..fea071da0
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/Library.kt
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * Library.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 .
+ */
+
+package org.oxycblt.musikr
+
+import org.oxycblt.musikr.fs.Path
+
+/**
+ * An immutable music library.
+ *
+ * No operations here will create side effects.
+ */
+interface Library {
+ val songs: Collection
+ val albums: Collection
+ val artists: Collection
+ val genres: Collection
+ val playlists: Collection
+
+ /**
+ * Whether this library is empty (i.e no songs, which means no other music item)
+ *
+ * @return true if this library is empty, false otherwise
+ */
+ fun empty(): Boolean
+
+ /**
+ * Find a [Song] by it's [Music.UID]
+ *
+ * @param uid the [Music.UID] of the song
+ * @return the song if found, null otherwise
+ */
+ fun findSong(uid: Music.UID): Song?
+
+ /**
+ * Find a [Song] by it's [Path]
+ *
+ * @param path the [Path] of the song
+ * @return the song if found, null otherwise
+ */
+ fun findSongByPath(path: Path): Song?
+
+ /**
+ * Find an [Album] by it's [Music.UID]
+ *
+ * @param uid the [Music.UID] of the album
+ * @return the album if found, null otherwise
+ */
+ fun findAlbum(uid: Music.UID): Album?
+
+ /**
+ * Find an [Artist] by it's [Music.UID]
+ *
+ * @param uid the [Music.UID] of the artist
+ * @return the artist if found, null otherwise
+ */
+ fun findArtist(uid: Music.UID): Artist?
+
+ /**
+ * Find a [Genre] by it's [Music.UID]
+ *
+ * @param uid the [Music.UID] of the genre
+ * @return the genre if found, null otherwise
+ */
+ fun findGenre(uid: Music.UID): Genre?
+
+ /**
+ * Find a [Playlist] by it's [Music.UID]
+ *
+ * @param uid the [Music.UID] of the playlist
+ * @return the playlist if found, null otherwise
+ */
+ fun findPlaylist(uid: Music.UID): Playlist?
+
+ /**
+ * Find a [Playlist] by it's name
+ *
+ * @param name the name of the playlist
+ * @return the playlist if found, null otherwise
+ */
+ fun findPlaylistByName(name: String): Playlist?
+}
+
+/**
+ * A mutable extension of [Library].
+ *
+ * Operations here will cause side-effects within the [Storage] used when this library was loaded.
+ * However, it won't actually mutate the [Library] itself, rather return a cloned instance with the
+ * changes applied. It is up to the client to update their reference to the library within their
+ * state handling.
+ */
+interface MutableLibrary : Library {
+ /**
+ * Create a new [Playlist] with the given name and songs.
+ *
+ * This will commit the new playlist to the stored playlists in the [Storage] used to load the
+ * library.
+ *
+ * @param name the name of the playlist
+ * @param songs the songs to add to the playlist
+ * @return a new [MutableLibrary] with the new playlist
+ */
+ suspend fun createPlaylist(name: String, songs: List): MutableLibrary
+
+ /**
+ * Rename a [Playlist].
+ *
+ * This will commit to whatever playlist source the given [Playlist] was loaded from.
+ *
+ * @param playlist the playlist to rename
+ * @param name the new name of the playlist
+ * @return a new [MutableLibrary] with the renamed playlist
+ */
+ suspend fun renamePlaylist(playlist: Playlist, name: String): MutableLibrary
+
+ /**
+ * Add songs to a [Playlist].
+ *
+ * This will commit to whatever playlist source the given [Playlist] was loaded from.
+ *
+ * @param playlist the playlist to add songs to
+ * @param songs the songs to add to the playlist
+ * @return a new [MutableLibrary] with the edited playlist
+ */
+ suspend fun addToPlaylist(playlist: Playlist, songs: List): MutableLibrary
+
+ /**
+ * Remove songs from a [Playlist].
+ *
+ * This will commit to whatever playlist source the given [Playlist] was loaded from.
+ *
+ * @param playlist the playlist to remove songs from
+ * @param songs the songs to remove from the playlist
+ * @return a new [MutableLibrary] with the edited playlist
+ */
+ suspend fun rewritePlaylist(playlist: Playlist, songs: List): MutableLibrary
+
+ /**
+ * Remove a [Playlist].
+ *
+ * This will commit to whatever playlist source the given [Playlist] was loaded from.
+ *
+ * @param playlist the playlist to delete
+ * @return a new [MutableLibrary] with the edited playlist
+ */
+ suspend fun deletePlaylist(playlist: Playlist): MutableLibrary
+}
diff --git a/app/src/main/java/org/oxycblt/auxio/music/Music.kt b/musikr/src/main/java/org/oxycblt/musikr/Music.kt
similarity index 79%
rename from app/src/main/java/org/oxycblt/auxio/music/Music.kt
rename to musikr/src/main/java/org/oxycblt/musikr/Music.kt
index 359afd9c3..5ae85f2d2 100644
--- a/app/src/main/java/org/oxycblt/auxio/music/Music.kt
+++ b/musikr/src/main/java/org/oxycblt/musikr/Music.kt
@@ -16,29 +16,25 @@
* along with this program. If not, see .
*/
-package org.oxycblt.auxio.music
+package org.oxycblt.musikr
-import android.content.Context
import android.net.Uri
import android.os.Parcelable
import androidx.room.TypeConverter
import java.security.MessageDigest
import java.util.UUID
-import kotlin.math.max
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
-import org.oxycblt.auxio.image.extractor.Cover
-import org.oxycblt.auxio.image.extractor.ParentCover
-import org.oxycblt.auxio.list.Item
-import org.oxycblt.auxio.music.fs.MimeType
-import org.oxycblt.auxio.music.fs.Path
-import org.oxycblt.auxio.music.info.Date
-import org.oxycblt.auxio.music.info.Disc
-import org.oxycblt.auxio.music.info.Name
-import org.oxycblt.auxio.music.info.ReleaseType
-import org.oxycblt.auxio.playback.replaygain.ReplayGainAdjustment
-import org.oxycblt.auxio.util.concatLocalized
-import org.oxycblt.auxio.util.toUuidOrNull
+import org.oxycblt.musikr.cover.Cover
+import org.oxycblt.musikr.cover.CoverCollection
+import org.oxycblt.musikr.fs.Format
+import org.oxycblt.musikr.fs.Path
+import org.oxycblt.musikr.tag.Date
+import org.oxycblt.musikr.tag.Disc
+import org.oxycblt.musikr.tag.Name
+import org.oxycblt.musikr.tag.ReleaseType
+import org.oxycblt.musikr.tag.ReplayGainAdjustment
+import org.oxycblt.musikr.util.toUuidOrNull
/**
* Abstract music data. This contains universal information about all concrete music
@@ -46,7 +42,7 @@ import org.oxycblt.auxio.util.toUuidOrNull
*
* @author Alexander Capehart (OxygenCobalt)
*/
-sealed interface Music : Item {
+sealed interface Music {
/**
* A unique identifier for this music item.
*
@@ -81,23 +77,34 @@ sealed interface Music : Item {
class UID
private constructor(
private val format: Format,
- private val type: MusicType,
+ private val item: Item,
private val uuid: UUID
) : Parcelable {
// Cache the hashCode for HashMap efficiency.
@IgnoredOnParcel private var hashCode = format.hashCode()
init {
- hashCode = 31 * hashCode + type.hashCode()
+ hashCode = 31 * hashCode + item.hashCode()
hashCode = 31 * hashCode + uuid.hashCode()
}
override fun hashCode() = hashCode
override fun equals(other: Any?) =
- other is UID && format == other.format && type == other.type && uuid == other.uuid
+ other is UID && format == other.format && item == other.item && uuid == other.uuid
- override fun toString() = "${format.namespace}:${type.intCode.toString(16)}-$uuid"
+ override fun toString() = "${format.namespace}:${item.intCode.toString(16)}-$uuid"
+
+ internal enum class Item(val intCode: Int) {
+ // Item used to be MusicType back when the music module was
+ // part of Auxio, so these old integer codes remain.
+ // TODO: Introduce new UID format that removes these.
+ SONG(0xA10B),
+ ALBUM(0xA10A),
+ ARTIST(0xA109),
+ GENRE(0xA108),
+ PLAYLIST(0xA107)
+ }
/**
* Internal marker of [Music.UID] format type.
@@ -117,7 +124,7 @@ sealed interface Music : Item {
@TypeConverter fun fromMusicUID(uid: UID?) = uid?.toString()
/** @see [Music.UID.fromString] */
- @TypeConverter fun toMusicUid(string: String?) = string?.let(UID::fromString)
+ @TypeConverter fun toMusicUid(string: String?) = string?.let(Companion::fromString)
}
companion object {
@@ -125,23 +132,23 @@ sealed interface Music : Item {
* Creates an Auxio-style [UID] of random composition. Used if there is no
* non-subjective, unlikely-to-change metadata of the music.
*
- * @param type The analogous [MusicType] of the item that created this [UID].
+ * @param item The type of [Item] that created this [UID].
*/
- fun auxio(type: MusicType): UID {
- return UID(Format.AUXIO, type, UUID.randomUUID())
+ internal fun auxio(item: Item): UID {
+ return UID(Format.AUXIO, item, UUID.randomUUID())
}
/**
* Creates an Auxio-style [UID] with a [UUID] composed of a hash of the non-subjective,
* unlikely-to-change metadata of the music.
*
- * @param type The analogous [MusicType] of the item that created this [UID].
+ * @param item The type of [Item] that created this [UID].
* @param updates Block to update the [MessageDigest] hash with the metadata of the
* item. Make sure the metadata hashed semantically aligns with the format
* specification.
* @return A new auxio-style [UID].
*/
- fun auxio(type: MusicType, updates: MessageDigest.() -> Unit): UID {
+ internal fun auxio(item: Item, updates: MessageDigest.() -> Unit): UID {
val digest =
MessageDigest.getInstance("SHA-256").run {
updates()
@@ -171,19 +178,19 @@ sealed interface Music : Item {
.or(digest[13].toLong().and(0xFF).shl(16))
.or(digest[14].toLong().and(0xFF).shl(8))
.or(digest[15].toLong().and(0xFF)))
- return UID(Format.AUXIO, type, uuid)
+ return UID(Format.AUXIO, item, uuid)
}
/**
* Creates a MusicBrainz-style [UID] with a [UUID] derived from the MusicBrainz ID
* extracted from a file.
*
- * @param type The analogous [MusicType] of the item that created this [UID].
+ * @param item The [Item] that created this [UID].
* @param mbid The analogous MusicBrainz ID for this item that was extracted from a
* file.
* @return A new MusicBrainz-style [UID].
*/
- fun musicBrainz(type: MusicType, mbid: UUID) = UID(Format.MUSICBRAINZ, type, mbid)
+ internal fun musicBrainz(item: Item, mbid: UUID) = UID(Format.MUSICBRAINZ, item, mbid)
/**
* Convert a [UID]'s string representation back into a concrete [UID] instance.
@@ -211,8 +218,8 @@ sealed interface Music : Item {
return null
}
- val type =
- MusicType.fromIntCode(ids[0].toIntOrNull(16) ?: return null) ?: return null
+ val intCode = ids[0].toIntOrNull(16) ?: return null
+ val type = Item.entries.firstOrNull { it.intCode == intCode } ?: return null
val uuid = ids[1].toUuidOrNull() ?: return null
return UID(format, type, uuid)
}
@@ -236,6 +243,7 @@ sealed interface MusicParent : Music {
* @author Alexander Capehart (OxygenCobalt)
*/
interface Song : Music {
+ override val name: Name.Known
/** The track number. Will be null if no valid track number was present in the metadata. */
val track: Int?
/** The [Disc] number. Will be null if no valid disc number was present in the metadata. */
@@ -247,23 +255,32 @@ interface Song : Music {
* audio file in a way that is scoped-storage-safe.
*/
val uri: Uri
- /** Useful information to quickly obtain the album cover. */
- val cover: Cover
/**
* The [Path] to this audio file. This is only intended for display, [uri] should be favored
* instead for accessing the audio file.
*/
val path: Path
- /** The [MimeType] of the audio file. Only intended for display. */
- val mimeType: MimeType
+ /** The [Format] of the audio file. Only intended for display. */
+ val format: Format
/** The size of the audio file, in bytes. */
val size: Long
/** The duration of the audio file, in milliseconds. */
val durationMs: Long
+ /** The bitrate of the audio file, in kbps. */
+ val bitrateKbps: Int
+ /** The sample rate of the audio file, in Hz. */
+ val sampleRateHz: Int
/** The ReplayGain adjustment to apply during playback. */
val replayGainAdjustment: ReplayGainAdjustment
- /** The date the audio file was added to the device, as a unix epoch timestamp. */
- val dateAdded: Long
+ /**
+ * The date last modified the audio file was last modified, in milliseconds since the unix
+ * epoch.
+ */
+ val modifiedMs: Long
+ /** The time the audio file was added to the device, in milliseconds since the unix epoch. */
+ val addedMs: Long
+ /** Useful information to quickly obtain the album cover. */
+ val cover: Cover?
/**
* The parent [Album]. If the metadata did not specify an album, it's parent directory is used
* instead.
@@ -296,12 +313,12 @@ interface Album : MusicParent {
* [ReleaseType.Album].
*/
val releaseType: ReleaseType
- /** Cover information from the template song used for the album. */
- val cover: ParentCover
+ /** Cover information from album's songs. */
+ val covers: CoverCollection
/** The duration of all songs in the album, in milliseconds. */
val durationMs: Long
- /** The earliest date a song in this album was added, as a unix epoch timestamp. */
- val dateAdded: Long
+ /** The earliest date a song in this album was added, in milliseconds since the unix epoch. */
+ val addedMs: Long
/**
* The parent [Artist]s of this [Album]. Is often one, but there can be multiple if more than
* one [Artist] name was specified in the metadata of the [Song]'s. Unlike [Song], album artists
@@ -327,7 +344,7 @@ interface Artist : MusicParent {
*/
val durationMs: Long?
/** Useful information to quickly obtain a (single) cover for a Genre. */
- val cover: ParentCover
+ val covers: CoverCollection
/** The [Genre]s of this artist. */
val genres: List
}
@@ -343,7 +360,7 @@ interface Genre : MusicParent {
/** The total duration of the songs in this genre, in milliseconds. */
val durationMs: Long
/** Useful information to quickly obtain a (single) cover for a Genre. */
- val cover: ParentCover
+ val covers: CoverCollection
}
/**
@@ -357,34 +374,5 @@ interface Playlist : MusicParent {
/** The total duration of the songs in this genre, in milliseconds. */
val durationMs: Long
/** Useful information to quickly obtain a (single) cover for a Genre. */
- val cover: ParentCover?
-}
-
-/**
- * Run [Name.resolve] on each instance in the given list and concatenate them into a [String] in a
- * localized manner.
- *
- * @param context [Context] required
- * @return A concatenated string.
- */
-fun List.resolveNames(context: Context) =
- concatLocalized(context) { it.name.resolve(context) }
-
-/**
- * Returns if [Music.name] matches for each item in a list. Useful for scenarios where the display
- * information of an item must be compared without a context.
- *
- * @param other The list of items to compare to.
- * @return True if they are the same (by [Music.name]), false otherwise.
- */
-fun List.areNamesTheSame(other: List): Boolean {
- for (i in 0 until max(size, other.size)) {
- val a = getOrNull(i) ?: return false
- val b = other.getOrNull(i) ?: return false
- if (a.name != b.name) {
- return false
- }
- }
-
- return true
+ val covers: CoverCollection
}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/Musikr.kt b/musikr/src/main/java/org/oxycblt/musikr/Musikr.kt
new file mode 100644
index 000000000..c18a01684
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/Musikr.kt
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * Musikr.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 .
+ */
+
+package org.oxycblt.musikr
+
+import android.content.Context
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.buffer
+import kotlinx.coroutines.flow.onCompletion
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.onStart
+import org.oxycblt.musikr.fs.MusicLocation
+import org.oxycblt.musikr.pipeline.EvaluateStep
+import org.oxycblt.musikr.pipeline.ExploreStep
+import org.oxycblt.musikr.pipeline.ExtractStep
+
+/**
+ * A highly opinionated, multi-threaded device music library.
+ *
+ * Use this to load music with [run].
+ *
+ * Note the following:
+ * 1. Musikr's API surface is intended to be primarily "stateless", with side-effects mostly
+ * contained within [Storage]. It's your job to manage long-term state.
+ * 2. There are no "defaults" in Musikr. You should think carefully about the parameters you are
+ * specifying and know consider they are desirable or not.
+ * 3. Musikr is currently not extendable, so if you're embedding this elsewhere you should be ready
+ * to fork and modify the source code.
+ */
+interface Musikr {
+ /**
+ * Start loading music from the given [locations] and the configuration provided earlier.
+ *
+ * @param locations The [MusicLocation]s to search for music in.
+ * @param onProgress Optional callback to receive progress on the current status of the music
+ * pipeline. Warning: These events will be rapid-fire.
+ * @return A handle to the newly created library alongside further cleanup.
+ */
+ suspend fun run(
+ locations: List,
+ onProgress: suspend (IndexingProgress) -> Unit = {}
+ ): LibraryResult
+
+ companion object {
+ /**
+ * Create a new instance from the given configuration.
+ *
+ * @param context The context to use for loading resources.
+ * @param storage Side-effect laden storage for use within the music loader **and** when
+ * mutating [MutableLibrary]. You should take responsibility for managing their long-term
+ * state.
+ * @param interpretation The configuration to use for interpreting certain vague tags. This
+ * should be configured by the user, if possible.
+ */
+ fun new(context: Context, storage: Storage, interpretation: Interpretation): Musikr =
+ MusikrImpl(
+ storage,
+ ExploreStep.from(context, storage),
+ ExtractStep.from(context, storage),
+ EvaluateStep.new(storage, interpretation))
+ }
+}
+
+/** Simple library handle returned by [Musikr.run]. */
+interface LibraryResult {
+ val library: MutableLibrary
+
+ /**
+ * Clean up expired resources. This should be done as soon as possible after music loading to
+ * reduce storage use.
+ *
+ * This may have unexpected results if previous [Library]s are in circulation across your app,
+ * so use it once you've fully updated your state.
+ */
+ suspend fun cleanup()
+}
+
+/** Music loading progress as reported by the music pipeline. */
+sealed interface IndexingProgress {
+ /**
+ * Currently indexing and extracting tags from device music.
+ *
+ * @param explored The amount of music currently found from the given [MusicLocation]s.
+ * @param loaded The amount of music that has had metadata extracted and parsed.
+ */
+ data class Songs(val loaded: Int, val explored: Int) : IndexingProgress
+
+ /**
+ * Currently creating the music graph alongside I/O finalization.
+ *
+ * There is no way to measure progress on these events.
+ */
+ data object Indeterminate : IndexingProgress
+}
+
+private class MusikrImpl(
+ private val storage: Storage,
+ private val exploreStep: ExploreStep,
+ private val extractStep: ExtractStep,
+ private val evaluateStep: EvaluateStep
+) : Musikr {
+ override suspend fun run(
+ locations: List,
+ onProgress: suspend (IndexingProgress) -> Unit
+ ) = coroutineScope {
+ var exploredCount = 0
+ var extractedCount = 0
+ val explored =
+ exploreStep
+ .explore(locations)
+ .buffer(Channel.UNLIMITED)
+ .onStart { onProgress(IndexingProgress.Songs(0, 0)) }
+ .onEach { onProgress(IndexingProgress.Songs(extractedCount, ++exploredCount)) }
+ val extracted =
+ extractStep
+ .extract(explored)
+ .buffer(Channel.UNLIMITED)
+ .onEach { onProgress(IndexingProgress.Songs(++extractedCount, exploredCount)) }
+ .onCompletion { onProgress(IndexingProgress.Indeterminate) }
+ val library = evaluateStep.evaluate(extracted)
+ LibraryResultImpl(storage, library)
+ }
+}
+
+private class LibraryResultImpl(
+ private val storage: Storage,
+ override val library: MutableLibrary
+) : LibraryResult {
+ override suspend fun cleanup() {
+ storage.storedCovers.cleanup(library.songs.mapNotNull { it.cover })
+ }
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/cache/Cache.kt b/musikr/src/main/java/org/oxycblt/musikr/cache/Cache.kt
new file mode 100644
index 000000000..277495d3a
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/cache/Cache.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * Cache.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 .
+ */
+
+package org.oxycblt.musikr.cache
+
+import org.oxycblt.musikr.cover.Covers
+import org.oxycblt.musikr.fs.DeviceFile
+import org.oxycblt.musikr.pipeline.RawSong
+
+abstract class Cache {
+ internal abstract suspend fun read(file: DeviceFile, covers: Covers): CacheResult
+
+ internal abstract suspend fun write(song: RawSong)
+
+ internal abstract suspend fun finalize()
+
+ abstract class Factory {
+ internal abstract fun open(): Cache
+ }
+}
+
+internal sealed interface CacheResult {
+ data class Hit(val song: RawSong) : CacheResult
+
+ data class Miss(val file: DeviceFile, val addedMs: Long?) : CacheResult
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/cache/CacheDatabase.kt b/musikr/src/main/java/org/oxycblt/musikr/cache/CacheDatabase.kt
new file mode 100644
index 000000000..c4744c29e
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/cache/CacheDatabase.kt
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2023 Auxio Project
+ * CacheDatabase.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 .
+ */
+
+package org.oxycblt.musikr.cache
+
+import android.content.Context
+import androidx.room.Dao
+import androidx.room.Database
+import androidx.room.Entity
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.PrimaryKey
+import androidx.room.Query
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import androidx.room.Transaction
+import androidx.room.TypeConverter
+import androidx.room.TypeConverters
+import org.oxycblt.musikr.cover.Covers
+import org.oxycblt.musikr.cover.ObtainResult
+import org.oxycblt.musikr.fs.DeviceFile
+import org.oxycblt.musikr.metadata.Properties
+import org.oxycblt.musikr.pipeline.RawSong
+import org.oxycblt.musikr.tag.Date
+import org.oxycblt.musikr.tag.parse.ParsedTags
+import org.oxycblt.musikr.util.correctWhitespace
+import org.oxycblt.musikr.util.splitEscaped
+
+@Database(entities = [CachedSong::class], version = 58, exportSchema = false)
+internal abstract class CacheDatabase : RoomDatabase() {
+ abstract fun visibleDao(): VisibleCacheDao
+
+ abstract fun invisibleDao(): InvisibleCacheDao
+
+ abstract fun writeDao(): CacheWriteDao
+
+ companion object {
+ fun from(context: Context) =
+ Room.databaseBuilder(
+ context.applicationContext, CacheDatabase::class.java, "music_cache.db")
+ .fallbackToDestructiveMigration()
+ .build()
+ }
+}
+
+@Dao
+internal interface VisibleCacheDao {
+ @Query("SELECT * FROM CachedSong WHERE uri = :uri")
+ suspend fun selectSong(uri: String): CachedSong?
+
+ @Query("SELECT addedMs FROM CachedSong WHERE uri = :uri")
+ suspend fun selectAddedMs(uri: String): Long?
+
+ @Transaction suspend fun touch(uri: String) = updateTouchedNs(uri, System.nanoTime())
+
+ @Query("UPDATE CachedSong SET touchedNs = :nowNs WHERE uri = :uri")
+ suspend fun updateTouchedNs(uri: String, nowNs: Long)
+}
+
+@Dao
+internal interface InvisibleCacheDao {
+ @Query("SELECT addedMs FROM CachedSong WHERE uri = :uri")
+ suspend fun selectAddedMs(uri: String): Long?
+}
+
+@Dao
+internal interface CacheWriteDao {
+ @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun updateSong(cachedSong: CachedSong)
+
+ @Query("DELETE FROM CachedSong WHERE touchedNs < :now") suspend fun pruneOlderThan(now: Long)
+}
+
+@Entity
+@TypeConverters(CachedSong.Converters::class)
+internal data class CachedSong(
+ @PrimaryKey val uri: String,
+ val modifiedMs: Long,
+ val addedMs: Long,
+ val touchedNs: Long,
+ val mimeType: String,
+ val durationMs: Long,
+ val bitrateHz: Int,
+ val sampleRateHz: Int,
+ val musicBrainzId: String?,
+ val name: String,
+ val sortName: String?,
+ val track: Int?,
+ val disc: Int?,
+ val subtitle: String?,
+ val date: Date?,
+ val albumMusicBrainzId: String?,
+ val albumName: String?,
+ val albumSortName: String?,
+ val releaseTypes: List,
+ val artistMusicBrainzIds: List,
+ val artistNames: List,
+ val artistSortNames: List,
+ val albumArtistMusicBrainzIds: List,
+ val albumArtistNames: List,
+ val albumArtistSortNames: List,
+ val genreNames: List,
+ val replayGainTrackAdjustment: Float?,
+ val replayGainAlbumAdjustment: Float?,
+ val coverId: String?,
+) {
+ suspend fun intoRawSong(file: DeviceFile, covers: Covers): RawSong? {
+ val cover =
+ when (val result = coverId?.let { covers.obtain(it) }) {
+ // We found the cover.
+ is ObtainResult.Hit -> result.cover
+ // We actually didn't find the cover, can't safely convert.
+ is ObtainResult.Miss -> return null
+ // No cover in the first place, can ignore.
+ null -> null
+ }
+ return RawSong(
+ file,
+ Properties(mimeType, durationMs, bitrateHz, sampleRateHz),
+ ParsedTags(
+ musicBrainzId = musicBrainzId,
+ name = name,
+ sortName = sortName,
+ durationMs = durationMs,
+ track = track,
+ disc = disc,
+ subtitle = subtitle,
+ date = date,
+ albumMusicBrainzId = albumMusicBrainzId,
+ albumName = albumName,
+ albumSortName = albumSortName,
+ releaseTypes = releaseTypes,
+ artistMusicBrainzIds = artistMusicBrainzIds,
+ artistNames = artistNames,
+ artistSortNames = artistSortNames,
+ albumArtistMusicBrainzIds = albumArtistMusicBrainzIds,
+ albumArtistNames = albumArtistNames,
+ albumArtistSortNames = albumArtistSortNames,
+ genreNames = genreNames,
+ replayGainTrackAdjustment = replayGainTrackAdjustment,
+ replayGainAlbumAdjustment = replayGainAlbumAdjustment),
+ cover = cover,
+ addedMs = addedMs)
+ }
+
+ object Converters {
+ @TypeConverter
+ fun fromMultiValue(values: List) =
+ values.joinToString(";") { it.replace(";", "\\;") }
+
+ @TypeConverter
+ fun toMultiValue(string: String) = string.splitEscaped { it == ';' }.correctWhitespace()
+
+ @TypeConverter fun fromDate(date: Date?) = date?.toString()
+
+ @TypeConverter fun toDate(string: String?) = string?.let(Date::from)
+ }
+
+ companion object {
+ fun fromRawSong(rawSong: RawSong) =
+ CachedSong(
+ uri = rawSong.file.uri.toString(),
+ modifiedMs = rawSong.file.modifiedMs,
+ addedMs = rawSong.addedMs,
+ // Should be strictly monotonic so we don't prune this
+ // by accident later.
+ touchedNs = System.nanoTime(),
+ musicBrainzId = rawSong.tags.musicBrainzId,
+ name = rawSong.tags.name,
+ sortName = rawSong.tags.sortName,
+ durationMs = rawSong.tags.durationMs,
+ track = rawSong.tags.track,
+ disc = rawSong.tags.disc,
+ subtitle = rawSong.tags.subtitle,
+ date = rawSong.tags.date,
+ albumMusicBrainzId = rawSong.tags.albumMusicBrainzId,
+ albumName = rawSong.tags.albumName,
+ albumSortName = rawSong.tags.albumSortName,
+ releaseTypes = rawSong.tags.releaseTypes,
+ artistMusicBrainzIds = rawSong.tags.artistMusicBrainzIds,
+ artistNames = rawSong.tags.artistNames,
+ artistSortNames = rawSong.tags.artistSortNames,
+ albumArtistMusicBrainzIds = rawSong.tags.albumArtistMusicBrainzIds,
+ albumArtistNames = rawSong.tags.albumArtistNames,
+ albumArtistSortNames = rawSong.tags.albumArtistSortNames,
+ genreNames = rawSong.tags.genreNames,
+ replayGainTrackAdjustment = rawSong.tags.replayGainTrackAdjustment,
+ replayGainAlbumAdjustment = rawSong.tags.replayGainAlbumAdjustment,
+ coverId = rawSong.cover?.id,
+ mimeType = rawSong.properties.mimeType,
+ bitrateHz = rawSong.properties.bitrateKbps,
+ sampleRateHz = rawSong.properties.sampleRateHz)
+ }
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/cache/StoredCache.kt b/musikr/src/main/java/org/oxycblt/musikr/cache/StoredCache.kt
new file mode 100644
index 000000000..4707ffe3f
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/cache/StoredCache.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * StoredCache.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 .
+ */
+
+package org.oxycblt.musikr.cache
+
+import android.content.Context
+import org.oxycblt.musikr.cover.Covers
+import org.oxycblt.musikr.fs.DeviceFile
+import org.oxycblt.musikr.pipeline.RawSong
+
+interface StoredCache {
+ fun visible(): Cache.Factory
+
+ fun invisible(): Cache.Factory
+
+ companion object {
+ fun from(context: Context): StoredCache = StoredCacheImpl(CacheDatabase.from(context))
+ }
+}
+
+private class StoredCacheImpl(private val cacheDatabase: CacheDatabase) : StoredCache {
+ override fun visible(): Cache.Factory = VisibleStoredCache.Factory(cacheDatabase)
+
+ override fun invisible(): Cache.Factory = InvisibleStoredCache.Factory(cacheDatabase)
+}
+
+private abstract class BaseStoredCache(protected val writeDao: CacheWriteDao) : Cache() {
+ private val created = System.nanoTime()
+
+ override suspend fun write(song: RawSong) = writeDao.updateSong(CachedSong.fromRawSong(song))
+
+ override suspend fun finalize() {
+ // Anything not create during this cache's use implies that it has not been
+ // access during this run and should be pruned.
+ writeDao.pruneOlderThan(created)
+ }
+}
+
+private class VisibleStoredCache(private val visibleDao: VisibleCacheDao, writeDao: CacheWriteDao) :
+ BaseStoredCache(writeDao) {
+ override suspend fun read(file: DeviceFile, covers: Covers): CacheResult {
+ val song = visibleDao.selectSong(file.uri.toString()) ?: return CacheResult.Miss(file, null)
+ if (song.modifiedMs != file.modifiedMs) {
+ // We *found* this file earlier, but it's out of date.
+ // Send back it with the timestamp so it will be re-used.
+ // The touch timestamp will be updated on write.
+ return CacheResult.Miss(file, song.addedMs)
+ }
+ // Valid file, update the touch time.
+ visibleDao.touch(file.uri.toString())
+ val rawSong = song.intoRawSong(file, covers) ?: return CacheResult.Miss(file, song.addedMs)
+ return CacheResult.Hit(rawSong)
+ }
+
+ class Factory(private val cacheDatabase: CacheDatabase) : Cache.Factory() {
+ override fun open() =
+ VisibleStoredCache(cacheDatabase.visibleDao(), cacheDatabase.writeDao())
+ }
+}
+
+private class InvisibleStoredCache(
+ private val invisibleCacheDao: InvisibleCacheDao,
+ writeDao: CacheWriteDao
+) : BaseStoredCache(writeDao) {
+ override suspend fun read(file: DeviceFile, covers: Covers) =
+ CacheResult.Miss(file, invisibleCacheDao.selectAddedMs(file.uri.toString()))
+
+ class Factory(private val cacheDatabase: CacheDatabase) : Cache.Factory() {
+ override fun open() =
+ InvisibleStoredCache(cacheDatabase.invisibleDao(), cacheDatabase.writeDao())
+ }
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/cover/CoverFormat.kt b/musikr/src/main/java/org/oxycblt/musikr/cover/CoverFormat.kt
new file mode 100644
index 000000000..52f9e15e3
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/cover/CoverFormat.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * CoverFormat.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 .
+ */
+
+package org.oxycblt.musikr.cover
+
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import java.io.OutputStream
+
+abstract class CoverFormat {
+ internal abstract val extension: String
+
+ internal abstract fun transcodeInto(data: ByteArray, output: OutputStream): Boolean
+
+ companion object {
+ fun jpeg(params: CoverParams): CoverFormat =
+ CoverFormatImpl("jpg", params, Bitmap.CompressFormat.JPEG)
+ }
+}
+
+private class CoverFormatImpl(
+ override val extension: String,
+ private val params: CoverParams,
+ private val format: Bitmap.CompressFormat,
+) : CoverFormat() {
+ override fun transcodeInto(data: ByteArray, output: OutputStream) =
+ BitmapFactory.Options().run {
+ inJustDecodeBounds = true
+ BitmapFactory.decodeByteArray(data, 0, data.size, this)
+ inSampleSize = calculateInSampleSize(params.resolution)
+ inJustDecodeBounds = false
+ val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size, this) ?: return@run false
+ bitmap.compress(format, params.quality, output)
+ true
+ }
+
+ private fun BitmapFactory.Options.calculateInSampleSize(size: Int): Int {
+ var inSampleSize = 1
+ val (height, width) = outHeight to outWidth
+
+ if (height > size || width > size) {
+ val halfHeight = height / 2
+ val halfWidth = width / 2
+ while ((halfHeight / inSampleSize) >= size && (halfWidth / inSampleSize) >= size) {
+ inSampleSize *= 2
+ }
+ }
+ return inSampleSize
+ }
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/cover/CoverIdentifier.kt b/musikr/src/main/java/org/oxycblt/musikr/cover/CoverIdentifier.kt
new file mode 100644
index 000000000..ef0917e05
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/cover/CoverIdentifier.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * CoverIdentifier.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 .
+ */
+
+package org.oxycblt.musikr.cover
+
+import java.security.MessageDigest
+
+interface CoverIdentifier {
+ suspend fun identify(data: ByteArray): String
+
+ companion object {
+ fun md5(): CoverIdentifier = MD5CoverIdentifier()
+ }
+}
+
+private class MD5CoverIdentifier() : CoverIdentifier {
+ @OptIn(ExperimentalStdlibApi::class)
+ override suspend fun identify(data: ByteArray): String {
+ val digest =
+ MessageDigest.getInstance("MD5").run {
+ update(data)
+ digest()
+ }
+ return digest.toHexString()
+ }
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/cover/CoverParams.kt b/musikr/src/main/java/org/oxycblt/musikr/cover/CoverParams.kt
new file mode 100644
index 000000000..1b26dc63f
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/cover/CoverParams.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * CoverParams.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 .
+ */
+
+package org.oxycblt.musikr.cover
+
+class CoverParams private constructor(val resolution: Int, val quality: Int) {
+ override fun hashCode() = 31 * resolution + quality
+
+ override fun equals(other: Any?) =
+ other is CoverParams && other.resolution == resolution && other.quality == quality
+
+ companion object {
+ fun of(resolution: Int, quality: Int): CoverParams {
+ check(resolution > 0) { "Resolution must be positive" }
+ check(quality in 0..100) { "Quality must be between 0 and 100" }
+ return CoverParams(resolution, quality)
+ }
+ }
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/cover/Covers.kt b/musikr/src/main/java/org/oxycblt/musikr/cover/Covers.kt
new file mode 100644
index 000000000..e3f41b386
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/cover/Covers.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * Covers.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 .
+ */
+
+package org.oxycblt.musikr.cover
+
+import java.io.InputStream
+
+interface Covers {
+ suspend fun obtain(id: String): ObtainResult
+}
+
+interface MutableCovers : Covers {
+ suspend fun write(data: ByteArray): Cover
+
+ suspend fun cleanup(excluding: Collection)
+}
+
+sealed interface ObtainResult {
+ data class Hit(val cover: T) : ObtainResult
+
+ class Miss : ObtainResult
+}
+
+interface Cover {
+ val id: String
+
+ suspend fun open(): InputStream?
+}
+
+class CoverCollection private constructor(val covers: List) {
+ override fun hashCode() = covers.hashCode()
+
+ override fun equals(other: Any?) = other is CoverCollection && covers == other.covers
+
+ companion object {
+ fun from(covers: Collection) =
+ CoverCollection(
+ covers
+ .groupBy { it.id }
+ .entries
+ .sortedByDescending { it.key }
+ .sortedByDescending { it.value.size }
+ .map { it.value.first() })
+ }
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/cover/FileCovers.kt b/musikr/src/main/java/org/oxycblt/musikr/cover/FileCovers.kt
new file mode 100644
index 000000000..4c8390869
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/cover/FileCovers.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2025 Auxio Project
+ * FileCovers.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 .
+ */
+
+package org.oxycblt.musikr.cover
+
+import android.os.ParcelFileDescriptor
+import org.oxycblt.musikr.fs.app.AppFile
+import org.oxycblt.musikr.fs.app.AppFiles
+
+open class FileCovers(private val appFiles: AppFiles, private val coverFormat: CoverFormat) :
+ Covers {
+ override suspend fun obtain(id: String): ObtainResult {
+ val file = appFiles.find(getFileName(id))
+ return if (file != null) {
+ ObtainResult.Hit(FileCoverImpl(id, file))
+ } else {
+ ObtainResult.Miss()
+ }
+ }
+
+ protected fun getFileName(id: String) = "$id.${coverFormat.extension}"
+}
+
+class MutableFileCovers(
+ private val appFiles: AppFiles,
+ private val coverFormat: CoverFormat,
+ private val coverIdentifier: CoverIdentifier
+) : FileCovers(appFiles, coverFormat), MutableCovers {
+ override suspend fun write(data: ByteArray): FileCover {
+ val id = coverIdentifier.identify(data)
+ val file = appFiles.write(getFileName(id)) { coverFormat.transcodeInto(data, it) }
+ return FileCoverImpl(id, file)
+ }
+
+ override suspend fun cleanup(excluding: Collection) {
+ val used = excluding.mapTo(mutableSetOf()) { getFileName(it.id) }
+ appFiles.deleteWhere { it !in used }
+ }
+}
+
+interface FileCover : Cover {
+ suspend fun fd(): ParcelFileDescriptor?
+}
+
+private data class FileCoverImpl(override val id: String, private val appFile: AppFile) :
+ FileCover {
+ override suspend fun fd() = appFile.fd()
+
+ override suspend fun open() = appFile.open()
+}
diff --git a/app/src/main/java/org/oxycblt/auxio/music/dirs/DirectoryModule.kt b/musikr/src/main/java/org/oxycblt/musikr/fs/DeviceFile.kt
similarity index 69%
rename from app/src/main/java/org/oxycblt/auxio/music/dirs/DirectoryModule.kt
rename to musikr/src/main/java/org/oxycblt/musikr/fs/DeviceFile.kt
index eec4918ea..6baac772f 100644
--- a/app/src/main/java/org/oxycblt/auxio/music/dirs/DirectoryModule.kt
+++ b/musikr/src/main/java/org/oxycblt/musikr/fs/DeviceFile.kt
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2023 Auxio Project
- * DirectoryModule.kt is part of Auxio.
+ * Copyright (c) 2024 Auxio Project
+ * DeviceFile.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
@@ -16,10 +16,14 @@
* along with this program. If not, see .
*/
-package org.oxycblt.auxio.music.dirs
+package org.oxycblt.musikr.fs
-import dagger.Module
-import dagger.hilt.InstallIn
-import dagger.hilt.components.SingletonComponent
+import android.net.Uri
-@Module @InstallIn(SingletonComponent::class) interface DirectoryModule {}
+internal data class DeviceFile(
+ val uri: Uri,
+ val mimeType: String,
+ val path: Path,
+ val size: Long,
+ val modifiedMs: Long
+)
diff --git a/musikr/src/main/java/org/oxycblt/musikr/fs/Format.kt b/musikr/src/main/java/org/oxycblt/musikr/fs/Format.kt
new file mode 100644
index 000000000..113bd3583
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/fs/Format.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * Format.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 .
+ */
+
+package org.oxycblt.musikr.fs
+
+import android.webkit.MimeTypeMap
+import org.oxycblt.musikr.util.unlikelyToBeNull
+
+sealed interface Format {
+ val mimeType: String
+
+ data object MPEG3 : Format {
+ override val mimeType = "audio/mpeg"
+ }
+
+ data class MPEG4(val containing: Format?) : Format {
+ override val mimeType = "audio/mp4"
+ }
+
+ data object AAC : Format {
+ override val mimeType = "audio/aac"
+ }
+
+ data object ALAC : Format {
+ override val mimeType = "audio/alac"
+ }
+
+ data class Ogg(val containing: Format?) : Format {
+ override val mimeType = "audio/ogg"
+ }
+
+ data object Opus : Format {
+ override val mimeType = "audio/opus"
+ }
+
+ data object Vorbis : Format {
+ override val mimeType = "audio/vorbis"
+ }
+
+ data object FLAC : Format {
+ override val mimeType = "audio/flac"
+ }
+
+ data object Wav : Format {
+ override val mimeType = "audio/wav"
+ }
+
+ data class Unknown(override val mimeType: String) : Format {
+ val extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType)?.uppercase()
+ }
+
+ companion object {
+ private val CODEC_MAP =
+ mapOf(
+ "audio/mpeg" to MPEG3,
+ "audio/mp3" to MPEG3,
+ "audio/aac" to AAC,
+ "audio/aacp" to AAC,
+ "audio/3gpp" to AAC,
+ "audio/3gpp2" to AAC,
+ "audio/alac" to ALAC,
+ "audio/opus" to Opus,
+ "audio/vorbis" to Vorbis,
+ "audio/flac" to FLAC,
+ "audio/wav" to Wav,
+ "audio/raw" to Wav,
+ "audio/x-wav" to Wav,
+ "audio/vnd.wave" to Wav,
+ "audio/wave" to Wav,
+ )
+
+ internal fun infer(containerMimeType: String, codecMimeType: String): Format {
+ val codecFormat = CODEC_MAP[codecMimeType]
+ if (codecFormat != null) {
+ // Codec found, possibly wrap in container.
+ return unlikelyToBeNull(wrapInContainer(containerMimeType, codecFormat))
+ }
+ val extensionFormat = CODEC_MAP[containerMimeType]
+ if (extensionFormat != null) {
+ // Standalone container of some codec.
+ return extensionFormat
+ }
+ return wrapInContainer(containerMimeType, null) ?: Unknown(containerMimeType)
+ }
+
+ private fun wrapInContainer(containerMimeType: String, format: Format?) =
+ when (containerMimeType) {
+ "audio/mp4",
+ "audio/mp4a-latm",
+ "audio/mpeg4-generic" -> MPEG4(format)
+ "audio/ogg",
+ "application/ogg",
+ "application/x-ogg" -> Ogg(format)
+ else -> format
+ }
+ }
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/fs/MusicLocation.kt b/musikr/src/main/java/org/oxycblt/musikr/fs/MusicLocation.kt
new file mode 100644
index 000000000..636d16c61
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/fs/MusicLocation.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * MusicLocation.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 .
+ */
+
+package org.oxycblt.musikr.fs
+
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.provider.DocumentsContract
+import org.oxycblt.musikr.fs.device.contentResolverSafe
+import org.oxycblt.musikr.fs.path.DocumentPathFactory
+import org.oxycblt.musikr.util.splitEscaped
+
+class MusicLocation private constructor(val uri: Uri, val path: Path) {
+ override fun equals(other: Any?) = other is MusicLocation && uri == other.uri
+
+ override fun hashCode() = 31 * uri.hashCode()
+
+ override fun toString(): String = uri.toString()
+
+ companion object {
+ fun new(context: Context, uri: Uri): MusicLocation? {
+ if (!DocumentsContract.isTreeUri(uri)) return null
+ val documentPathFactory = DocumentPathFactory.from(context)
+ val path = documentPathFactory.unpackDocumentTreeUri(uri) ?: return null
+ val notPersisted =
+ context.contentResolverSafe.persistedUriPermissions.none {
+ it.uri == uri && it.isReadPermission && it.isWritePermission
+ }
+ if (notPersisted) {
+ context.contentResolverSafe.takePersistableUriPermission(
+ uri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
+ }
+ return MusicLocation(uri, path)
+ }
+
+ fun existing(context: Context, uri: Uri): MusicLocation? {
+ val documentPathFactory = DocumentPathFactory.from(context)
+ if (!DocumentsContract.isTreeUri(uri)) return null
+ val notPersisted =
+ context.contentResolverSafe.persistedUriPermissions.none {
+ it.uri == uri && it.isReadPermission && it.isWritePermission
+ }
+ if (notPersisted) return null
+ val path = documentPathFactory.unpackDocumentTreeUri(uri) ?: return null
+ return MusicLocation(uri, path)
+ }
+
+ fun toString(list: List) =
+ list.joinToString(";") { it.uri.toString().replace(";", "\\;") }
+
+ fun existing(context: Context, string: String): List {
+ return string.splitEscaped { it == ';' }.mapNotNull { existing(context, Uri.parse(it)) }
+ }
+ }
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/fs/Path.kt b/musikr/src/main/java/org/oxycblt/musikr/fs/Path.kt
new file mode 100644
index 000000000..f889d516b
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/fs/Path.kt
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * Path.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 .
+ */
+
+package org.oxycblt.musikr.fs
+
+import android.content.Context
+import java.io.File
+
+/**
+ * An abstraction of an android file system path, including the volume and relative path.
+ *
+ * @param volume The volume that the path is on.
+ * @param components The components of the path of the file, relative to the root of the volume.
+ */
+data class Path(
+ val volume: Volume,
+ val components: Components,
+) {
+ /** The name of the file/directory. */
+ val name: String?
+ get() = components.name
+
+ /** The parent directory of the path, or itself if it's the root path. */
+ val directory: Path
+ get() = Path(volume, components.parent())
+
+ /**
+ * Transforms this [Path] into a "file" of the given name that's within the "directory"
+ * represented by the current path. Ex. "/storage/emulated/0/Music" ->
+ * "/storage/emulated/0/Music/file.mp3"
+ *
+ * @param fileName The name of the file to append to the path.
+ * @return The new [Path] instance.
+ */
+ fun file(fileName: String) = Path(volume, components.child(fileName))
+
+ /**
+ * Resolves the [Path] in a human-readable format.
+ *
+ * @param context [Context] required to obtain human-readable strings.
+ */
+ fun resolve(context: Context) = "${volume.resolveName(context)}/$components"
+}
+
+sealed interface Volume {
+ /** The name of the volume as it appears in MediaStore. */
+ val mediaStoreName: String?
+
+ /**
+ * The components of the path to the volume, relative from the system root. Should not be used
+ * except for compatibility purposes.
+ */
+ val components: Components?
+
+ /** Resolves the name of the volume in a human-readable format. */
+ fun resolveName(context: Context): String
+
+ /** A volume representing the device's internal storage. */
+ interface Internal : Volume
+
+ /** A volume representing an external storage device, identified by a UUID. */
+ interface External : Volume {
+ /** The UUID of the volume. */
+ val id: String?
+ }
+}
+
+/**
+ * The components of a path. This allows the path to be manipulated without having tp handle
+ * separator parsing.
+ *
+ * @param components The components of the path.
+ */
+@JvmInline
+value class Components private constructor(val components: List) {
+ /** The name of the file/directory. */
+ val name: String?
+ get() = components.lastOrNull()
+
+ override fun toString() = unixString
+
+ /** Formats these components using the unix file separator (/) */
+ val unixString: String
+ get() = components.joinToString(File.separator)
+
+ /** Formats these components using the windows file separator (\). */
+ val windowsString: String
+ get() = components.joinToString("\\")
+
+ /**
+ * Returns a new [Components] instance with the last element of the path removed as a "parent"
+ * element of the original instance.
+ *
+ * @return The new [Components] instance, or the original instance if it's the root path.
+ */
+ fun parent() = Components(components.dropLast(1))
+
+ /**
+ * Returns a new [Components] instance with the given name appended to the end of the path as a
+ * "child" element of the original instance.
+ *
+ * @param name The name of the file/directory to append to the path.
+ */
+ fun child(name: String) =
+ if (name.isNotEmpty()) {
+ Components(components + name.trimSlashes())
+ } else {
+ this
+ }
+
+ /**
+ * Removes the first [n] elements of the path, effectively resulting in a path that is n levels
+ * deep.
+ *
+ * @param n The number of elements to remove.
+ * @return The new [Components] instance.
+ */
+ fun depth(n: Int) = Components(components.drop(n))
+
+ /**
+ * Concatenates this [Components] instance with another.
+ *
+ * @param other The [Components] instance to concatenate with.
+ * @return The new [Components] instance.
+ */
+ fun child(other: Components) = Components(components + other.components)
+
+ /**
+ * Returns the given [Components] has a prefix equal to this [Components] instance. Effectively,
+ * as if the given [Components] instance was a child of this [Components] instance.
+ */
+ fun contains(other: Components): Boolean {
+ if (other.components.size < components.size) {
+ return false
+ }
+
+ return components == other.components.take(components.size)
+ }
+
+ fun containing(other: Components) = Components(other.components.drop(components.size))
+
+ internal companion object {
+ /**
+ * Parses a path string into a [Components] instance by the unix path separator (/).
+ *
+ * @param path The path string to parse.
+ * @return The [Components] instance.
+ */
+ fun parseUnix(path: String) =
+ Components(path.trimSlashes().split(File.separatorChar).filter { it.isNotEmpty() })
+
+ /**
+ * Parses a path string into a [Components] instance by the windows path separator.
+ *
+ * @param path The path string to parse.
+ * @return The [Components] instance.
+ */
+ fun parseWindows(path: String) =
+ Components(path.trimSlashes().split('\\').filter { it.isNotEmpty() })
+
+ private fun String.trimSlashes() = trimStart(File.separatorChar).trimEnd(File.separatorChar)
+ }
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/fs/app/AppFiles.kt b/musikr/src/main/java/org/oxycblt/musikr/fs/app/AppFiles.kt
new file mode 100644
index 000000000..9c8f2a407
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/fs/app/AppFiles.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * AppFiles.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 .
+ */
+
+package org.oxycblt.musikr.fs.app
+
+import android.os.ParcelFileDescriptor
+import java.io.File
+import java.io.IOException
+import java.io.InputStream
+import java.io.OutputStream
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+import kotlinx.coroutines.withContext
+
+interface AppFiles {
+ suspend fun find(name: String): AppFile?
+
+ suspend fun write(name: String, block: suspend (OutputStream) -> Unit): AppFile
+
+ suspend fun deleteWhere(block: (String) -> Boolean)
+
+ companion object {
+ suspend fun at(dir: File): AppFiles {
+ withContext(Dispatchers.IO) { check(dir.exists() && dir.isDirectory) }
+ return AppFilesImpl(dir)
+ }
+ }
+}
+
+interface AppFile {
+ suspend fun fd(): ParcelFileDescriptor?
+
+ suspend fun open(): InputStream?
+}
+
+private class AppFilesImpl(private val dir: File) : AppFiles {
+ private val fileMutexes = mutableMapOf()
+ private val mapMutex = Mutex()
+
+ private suspend fun getMutexForFile(file: String): Mutex {
+ return mapMutex.withLock { fileMutexes.getOrPut(file) { Mutex() } }
+ }
+
+ override suspend fun find(name: String): AppFile? =
+ withContext(Dispatchers.IO) {
+ try {
+ File(dir, name).takeIf { it.exists() }?.let { AppFileImpl(it) }
+ } catch (e: IOException) {
+ null
+ }
+ }
+
+ override suspend fun write(name: String, block: suspend (OutputStream) -> Unit): AppFile {
+ val fileMutex = getMutexForFile(name)
+ return fileMutex.withLock {
+ val targetFile = File(dir, name)
+ if (!targetFile.exists()) {
+ withContext(Dispatchers.IO) {
+ val tempFile = File(dir, "$name.tmp")
+
+ try {
+ tempFile.outputStream().use { block(it) }
+ tempFile.renameTo(targetFile)
+ AppFileImpl(targetFile)
+ } catch (e: IOException) {
+ tempFile.delete()
+ throw e
+ }
+ }
+ } else {
+ AppFileImpl(targetFile)
+ }
+ }
+ }
+
+ override suspend fun deleteWhere(block: (String) -> Boolean) {
+ withContext(Dispatchers.IO) {
+ dir.listFiles { file -> block(file.name) }?.forEach { it.deleteRecursively() }
+ }
+ }
+}
+
+private data class AppFileImpl(private val file: File) : AppFile {
+ override suspend fun fd() =
+ withContext(Dispatchers.IO) {
+ try {
+ ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)
+ } catch (e: IOException) {
+ null
+ }
+ }
+
+ override suspend fun open() = withContext(Dispatchers.IO) { file.inputStream() }
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/fs/device/DeviceFiles.kt b/musikr/src/main/java/org/oxycblt/musikr/fs/device/DeviceFiles.kt
new file mode 100644
index 000000000..2f737995c
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/fs/device/DeviceFiles.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * DeviceFiles.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 .
+ */
+
+package org.oxycblt.musikr.fs.device
+
+import android.content.ContentResolver
+import android.content.Context
+import android.net.Uri
+import android.provider.DocumentsContract
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.asFlow
+import kotlinx.coroutines.flow.emitAll
+import kotlinx.coroutines.flow.flatMapMerge
+import kotlinx.coroutines.flow.flattenMerge
+import kotlinx.coroutines.flow.flow
+import org.oxycblt.musikr.fs.DeviceFile
+import org.oxycblt.musikr.fs.MusicLocation
+import org.oxycblt.musikr.fs.Path
+
+internal interface DeviceFiles {
+ fun explore(locations: Flow): Flow
+
+ companion object {
+ fun from(context: Context): DeviceFiles = DeviceFilesImpl(context.contentResolverSafe)
+ }
+}
+
+@OptIn(ExperimentalCoroutinesApi::class)
+private class DeviceFilesImpl(private val contentResolver: ContentResolver) : DeviceFiles {
+ override fun explore(locations: Flow): Flow =
+ locations.flatMapMerge { location ->
+ exploreImpl(
+ contentResolver,
+ location.uri,
+ DocumentsContract.getTreeDocumentId(location.uri),
+ location.path)
+ }
+
+ private fun exploreImpl(
+ contentResolver: ContentResolver,
+ rootUri: Uri,
+ treeDocumentId: String,
+ relativePath: Path
+ ): Flow = flow {
+ contentResolver.useQuery(
+ DocumentsContract.buildChildDocumentsUriUsingTree(rootUri, treeDocumentId),
+ PROJECTION) { cursor ->
+ val childUriIndex =
+ cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_DOCUMENT_ID)
+ val displayNameIndex =
+ cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_DISPLAY_NAME)
+ val mimeTypeIndex =
+ cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_MIME_TYPE)
+ val sizeIndex = cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_SIZE)
+ val lastModifiedIndex =
+ cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_LAST_MODIFIED)
+ val recursive = mutableListOf>()
+ while (cursor.moveToNext()) {
+ val childId = cursor.getString(childUriIndex)
+ val displayName = cursor.getString(displayNameIndex)
+ val newPath = relativePath.file(displayName)
+ val mimeType = cursor.getString(mimeTypeIndex)
+ if (mimeType == DocumentsContract.Document.MIME_TYPE_DIR) {
+ // This does NOT block the current coroutine. Instead, we will
+ // evaluate this flow in parallel later to maximize throughput.
+ recursive.add(exploreImpl(contentResolver, rootUri, childId, newPath))
+ } else if (mimeType.startsWith("audio/") && mimeType != "audio/x-mpegurl") {
+ // Immediately emit all files given that it's just an O(1) op.
+ // This also just makes sure the outer flow has a reason to exist
+ // rather than just being a glorified async.
+ val lastModified = cursor.getLong(lastModifiedIndex)
+ val size = cursor.getLong(sizeIndex)
+ emit(
+ DeviceFile(
+ DocumentsContract.buildDocumentUriUsingTree(rootUri, childId),
+ mimeType,
+ newPath,
+ size,
+ lastModified))
+ }
+ }
+ emitAll(recursive.asFlow().flattenMerge())
+ }
+ }
+
+ private companion object {
+ val PROJECTION =
+ arrayOf(
+ DocumentsContract.Document.COLUMN_DOCUMENT_ID,
+ DocumentsContract.Document.COLUMN_DISPLAY_NAME,
+ DocumentsContract.Document.COLUMN_MIME_TYPE,
+ DocumentsContract.Document.COLUMN_SIZE,
+ DocumentsContract.Document.COLUMN_LAST_MODIFIED)
+ }
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/fs/device/QueryUtil.kt b/musikr/src/main/java/org/oxycblt/musikr/fs/device/QueryUtil.kt
new file mode 100644
index 000000000..a305108d6
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/fs/device/QueryUtil.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2022 Auxio Project
+ * QueryUtil.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 .
+ */
+
+package org.oxycblt.musikr.fs.device
+
+import android.content.ContentResolver
+import android.content.Context
+import android.database.Cursor
+import android.net.Uri
+
+/**
+ * Get a content resolver that will not mangle MediaStore queries on certain devices. See
+ * https://github.com/OxygenCobalt/Auxio/issues/50 for more info.
+ */
+internal val Context.contentResolverSafe: ContentResolver
+ get() = applicationContext.contentResolver
+
+/**
+ * A shortcut for querying the [ContentResolver] database.
+ *
+ * @param uri The [Uri] of content to retrieve.
+ * @param projection A list of SQL columns to query from the database.
+ * @param selector A SQL selection statement to filter results. Spaces where arguments should be
+ * filled in are represented with a "?".
+ * @param args The arguments used for the selector.
+ * @return A [Cursor] of the queried values, organized by the column projection.
+ * @throws IllegalStateException If the [ContentResolver] did not return the queried [Cursor].
+ * @see ContentResolver.query
+ */
+internal fun ContentResolver.safeQuery(
+ uri: Uri,
+ projection: Array,
+ selector: String? = null,
+ args: Array? = null
+) = requireNotNull(query(uri, projection, selector, args, null)) { "ContentResolver query failed" }
+
+/**
+ * A shortcut for [safeQuery] with [use] applied, automatically cleaning up the [Cursor]'s resources
+ * when no longer used.
+ *
+ * @param uri The [Uri] of content to retrieve.
+ * @param projection A list of SQL columns to query from the database.
+ * @param selector A SQL selection statement to filter results. Spaces where arguments should be
+ * filled in are represented with a "?".
+ * @param args The arguments used for the selector.
+ * @param block The block of code to run with the queried [Cursor]. Will not be ran if the [Cursor]
+ * is empty.
+ * @throws IllegalStateException If the [ContentResolver] did not return the queried [Cursor].
+ * @see ContentResolver.query
+ */
+internal inline fun ContentResolver.useQuery(
+ uri: Uri,
+ projection: Array,
+ selector: String? = null,
+ args: Array? = null,
+ block: (Cursor) -> R
+) = safeQuery(uri, projection, selector, args).use(block)
diff --git a/app/src/main/java/org/oxycblt/auxio/music/fs/DocumentPathFactory.kt b/musikr/src/main/java/org/oxycblt/musikr/fs/path/DocumentPathFactory.kt
similarity index 88%
rename from app/src/main/java/org/oxycblt/auxio/music/fs/DocumentPathFactory.kt
rename to musikr/src/main/java/org/oxycblt/musikr/fs/path/DocumentPathFactory.kt
index eb977a1fe..92b7f825f 100644
--- a/app/src/main/java/org/oxycblt/auxio/music/fs/DocumentPathFactory.kt
+++ b/musikr/src/main/java/org/oxycblt/musikr/fs/path/DocumentPathFactory.kt
@@ -16,22 +16,25 @@
* along with this program. If not, see .
*/
-package org.oxycblt.auxio.music.fs
+package org.oxycblt.musikr.fs.path
import android.content.ContentUris
import android.content.Context
import android.net.Uri
import android.provider.DocumentsContract
-import dagger.hilt.android.qualifiers.ApplicationContext
import java.io.File
-import javax.inject.Inject
+import org.oxycblt.musikr.fs.Components
+import org.oxycblt.musikr.fs.Path
+import org.oxycblt.musikr.fs.Volume
+import org.oxycblt.musikr.fs.device.contentResolverSafe
+import org.oxycblt.musikr.fs.device.useQuery
/**
* A factory for parsing the reverse-engineered format of the URIs obtained from document picker.
*
* @author Alexander Capehart (OxygenCobalt)
*/
-interface DocumentPathFactory {
+internal interface DocumentPathFactory {
/**
* Unpacks a document URI into a [Path] instance, using [fromDocumentId].
*
@@ -63,12 +66,18 @@ interface DocumentPathFactory {
* @return The [Path] instance, or null if the path could not be deserialized.
*/
fun fromDocumentId(path: String): Path?
+
+ companion object {
+ fun from(context: Context): DocumentPathFactory {
+ val volumeManager = VolumeManager.from(context)
+ val pathInterpreter = MediaStorePathInterpreter.Factory.from(volumeManager)
+ return DocumentPathFactoryImpl(context, volumeManager, pathInterpreter)
+ }
+ }
}
-class DocumentPathFactoryImpl
-@Inject
-constructor(
- @ApplicationContext private val context: Context,
+private class DocumentPathFactoryImpl(
+ private val context: Context,
private val volumeManager: VolumeManager,
private val mediaStorePathInterpreterFactory: MediaStorePathInterpreter.Factory
) : DocumentPathFactory {
diff --git a/app/src/main/java/org/oxycblt/auxio/music/fs/MediaStorePathInterpreter.kt b/musikr/src/main/java/org/oxycblt/musikr/fs/path/MediaStorePathInterpreter.kt
similarity index 96%
rename from app/src/main/java/org/oxycblt/auxio/music/fs/MediaStorePathInterpreter.kt
rename to musikr/src/main/java/org/oxycblt/musikr/fs/path/MediaStorePathInterpreter.kt
index f3a6f6aa4..d62d1cb45 100644
--- a/app/src/main/java/org/oxycblt/auxio/music/fs/MediaStorePathInterpreter.kt
+++ b/musikr/src/main/java/org/oxycblt/musikr/fs/path/MediaStorePathInterpreter.kt
@@ -16,19 +16,20 @@
* along with this program. If not, see .
*/
-package org.oxycblt.auxio.music.fs
+package org.oxycblt.musikr.fs.path
import android.database.Cursor
import android.os.Build
import android.provider.MediaStore
-import org.oxycblt.auxio.util.logE
+import org.oxycblt.musikr.fs.Components
+import org.oxycblt.musikr.fs.Path
/**
* Wrapper around a [Cursor] that interprets path information on a per-API/manufacturer basis.
*
* @author Alexander Capehart (OxygenCobalt)
*/
-sealed interface MediaStorePathInterpreter {
+internal sealed interface MediaStorePathInterpreter {
/**
* Extract a [Path] from the wrapped [Cursor]. This should be called after the cursor has been
* moved to the row that should be interpreted.
@@ -112,8 +113,6 @@ private constructor(private val cursor: Cursor, volumeManager: VolumeManager) :
}
}
- logE("Could not find volume for $data [tried: ${volumes.map { it.components }}]")
-
return null
}
@@ -181,8 +180,6 @@ private constructor(private val cursor: Cursor, volumeManager: VolumeManager) :
val displayName = cursor.getString(displayNameIndex)
val volume = volumes.find { it.mediaStoreName == volumeName }
if (volume == null) {
- logE(
- "Could not find volume for $volumeName:$relativePath/$displayName [tried: ${volumes.map { it.mediaStoreName }}]")
return null
}
val components = Components.parseUnix(relativePath).child(displayName)
diff --git a/app/src/main/java/org/oxycblt/auxio/music/fs/StorageUtil.kt b/musikr/src/main/java/org/oxycblt/musikr/fs/path/VolumeCompat.kt
similarity index 51%
rename from app/src/main/java/org/oxycblt/auxio/music/fs/StorageUtil.kt
rename to musikr/src/main/java/org/oxycblt/musikr/fs/path/VolumeCompat.kt
index 6cdc7df67..2c0b87453 100644
--- a/app/src/main/java/org/oxycblt/auxio/music/fs/StorageUtil.kt
+++ b/musikr/src/main/java/org/oxycblt/musikr/fs/path/VolumeCompat.kt
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2022 Auxio Project
- * StorageUtil.kt is part of Auxio.
+ * Copyright (c) 2024 Auxio Project
+ * VolumeCompat.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
@@ -16,13 +16,10 @@
* along with this program. If not, see .
*/
-package org.oxycblt.auxio.music.fs
+package org.oxycblt.musikr.fs.path
import android.annotation.SuppressLint
-import android.content.ContentResolver
-import android.content.ContentUris
import android.content.Context
-import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.os.Environment
@@ -30,88 +27,8 @@ import android.os.storage.StorageManager
import android.os.storage.StorageVolume
import android.provider.MediaStore
import java.lang.reflect.Method
-import org.oxycblt.auxio.util.lazyReflectedMethod
+import org.oxycblt.musikr.util.lazyReflectedMethod
-// --- MEDIASTORE UTILITIES ---
-
-/**
- * Get a content resolver that will not mangle MediaStore queries on certain devices. See
- * https://github.com/OxygenCobalt/Auxio/issues/50 for more info.
- */
-val Context.contentResolverSafe: ContentResolver
- get() = applicationContext.contentResolver
-
-/**
- * A shortcut for querying the [ContentResolver] database.
- *
- * @param uri The [Uri] of content to retrieve.
- * @param projection A list of SQL columns to query from the database.
- * @param selector A SQL selection statement to filter results. Spaces where arguments should be
- * filled in are represented with a "?".
- * @param args The arguments used for the selector.
- * @return A [Cursor] of the queried values, organized by the column projection.
- * @throws IllegalStateException If the [ContentResolver] did not return the queried [Cursor].
- * @see ContentResolver.query
- */
-fun ContentResolver.safeQuery(
- uri: Uri,
- projection: Array,
- selector: String? = null,
- args: Array? = null
-) = requireNotNull(query(uri, projection, selector, args, null)) { "ContentResolver query failed" }
-
-/**
- * A shortcut for [safeQuery] with [use] applied, automatically cleaning up the [Cursor]'s resources
- * when no longer used.
- *
- * @param uri The [Uri] of content to retrieve.
- * @param projection A list of SQL columns to query from the database.
- * @param selector A SQL selection statement to filter results. Spaces where arguments should be
- * filled in are represented with a "?".
- * @param args The arguments used for the selector.
- * @param block The block of code to run with the queried [Cursor]. Will not be ran if the [Cursor]
- * is empty.
- * @throws IllegalStateException If the [ContentResolver] did not return the queried [Cursor].
- * @see ContentResolver.query
- */
-inline fun ContentResolver.useQuery(
- uri: Uri,
- projection: Array,
- selector: String? = null,
- args: Array? = null,
- block: (Cursor) -> R
-) = safeQuery(uri, projection, selector, args).use(block)
-
-/** Album art [MediaStore] database is not a built-in constant, have to define it ourselves. */
-private val externalCoversUri = Uri.parse("content://media/external/audio/albumart")
-
-/**
- * Convert a [MediaStore] Song ID into a [Uri] to it's audio file.
- *
- * @return An external storage audio file [Uri]. May not exist.
- * @see ContentUris.withAppendedId
- * @see MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
- */
-fun Long.toAudioUri() =
- ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, this)
-
-/**
- * Convert a [MediaStore] Album ID into a [Uri] to it's system-provided album cover. This cover will
- * be fast to load, but will be lower quality.
- *
- * @return An external storage image [Uri]. May not exist.
- * @see ContentUris.withAppendedId
- */
-fun Long.toSongCoverUri(): Uri =
- MediaStore.Audio.Media.EXTERNAL_CONTENT_URI.buildUpon().run {
- appendPath(this@toSongCoverUri.toString())
- appendPath("albumart")
- build()
- }
-
-fun Long.toAlbumCoverUri(): Uri = ContentUris.withAppendedId(externalCoversUri, this)
-
-// --- STORAGEMANAGER UTILITIES ---
// Largely derived from Material Files: https://github.com/zhanghai/MaterialFiles
/**
@@ -123,23 +40,13 @@ fun Long.toAlbumCoverUri(): Uri = ContentUris.withAppendedId(externalCoversUri,
@Suppress("NewApi")
private val svApi21GetPathMethod: Method by lazyReflectedMethod(StorageVolume::class, "getPath")
-/**
- * The [StorageVolume] considered the "primary" volume by the system, obtained in a
- * version-compatible manner.
- *
- * @see StorageManager.getPrimaryStorageVolume
- * @see StorageVolume.isPrimary
- */
-val StorageManager.primaryStorageVolumeCompat: StorageVolume
- @Suppress("NewApi") get() = primaryStorageVolume
-
/**
* The list of [StorageVolume]s currently recognized by [StorageManager], in a version-compatible
* manner.
*
* @see StorageManager.getStorageVolumes
*/
-val StorageManager.storageVolumesCompat: List
+internal val StorageManager.storageVolumesCompat: List
get() = storageVolumes.toList()
/**
@@ -148,7 +55,7 @@ val StorageManager.storageVolumesCompat: List
*
* @see StorageVolume.getDirectory
*/
-val StorageVolume.directoryCompat: String?
+internal val StorageVolume.directoryCompat: String?
@SuppressLint("NewApi")
get() =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
@@ -169,7 +76,7 @@ val StorageVolume.directoryCompat: String?
* @return A human-readable name for this volume.
*/
@SuppressLint("NewApi")
-fun StorageVolume.getDescriptionCompat(context: Context): String = getDescription(context)
+internal fun StorageVolume.getDescriptionCompat(context: Context): String = getDescription(context)
/**
* If this [StorageVolume] is considered the "Primary" volume where the Android System is kept. May
@@ -177,7 +84,7 @@ fun StorageVolume.getDescriptionCompat(context: Context): String = getDescriptio
*
* @see StorageVolume.isPrimary
*/
-val StorageVolume.isPrimaryCompat: Boolean
+internal val StorageVolume.isPrimaryCompat: Boolean
@SuppressLint("NewApi") get() = isPrimary
/**
@@ -186,14 +93,14 @@ val StorageVolume.isPrimaryCompat: Boolean
*
* @see StorageVolume.isEmulated
*/
-val StorageVolume.isEmulatedCompat: Boolean
+internal val StorageVolume.isEmulatedCompat: Boolean
@SuppressLint("NewApi") get() = isEmulated
/**
* If this [StorageVolume] represents the "Internal Shared Storage" volume, also known as "primary"
* to [MediaStore] and Document [Uri]s, obtained in a version compatible manner.
*/
-val StorageVolume.isInternalCompat: Boolean
+internal val StorageVolume.isInternalCompat: Boolean
// Must contain the android system AND be an emulated drive, as non-emulated system
// volumes use their UUID instead of primary in MediaStore/Document URIs.
get() = isPrimaryCompat && isEmulatedCompat
@@ -204,7 +111,7 @@ val StorageVolume.isInternalCompat: Boolean
*
* @see StorageVolume.getUuid
*/
-val StorageVolume.uuidCompat: String?
+internal val StorageVolume.uuidCompat: String?
@SuppressLint("NewApi") get() = uuid
/**
@@ -213,7 +120,7 @@ val StorageVolume.uuidCompat: String?
*
* @see StorageVolume.getState
*/
-val StorageVolume.stateCompat: String
+internal val StorageVolume.stateCompat: String
@SuppressLint("NewApi") get() = state
/**
@@ -222,7 +129,7 @@ val StorageVolume.stateCompat: String
*
* @see StorageVolume.getMediaStoreVolumeName
*/
-val StorageVolume.mediaStoreVolumeNameCompat: String?
+internal val StorageVolume.mediaStoreVolumeNameCompat: String?
get() =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
mediaStoreVolumeName
diff --git a/musikr/src/main/java/org/oxycblt/musikr/fs/path/VolumeManager.kt b/musikr/src/main/java/org/oxycblt/musikr/fs/path/VolumeManager.kt
new file mode 100644
index 000000000..3e67ef5f7
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/fs/path/VolumeManager.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * VolumeManager.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 .
+ */
+
+package org.oxycblt.musikr.fs.path
+
+import android.content.Context
+import android.os.storage.StorageManager
+import android.os.storage.StorageVolume
+import org.oxycblt.musikr.fs.Components
+import org.oxycblt.musikr.fs.Volume
+
+/** A wrapper around [StorageManager] that provides instances of the [Volume] interface. */
+internal interface VolumeManager {
+ /**
+ * The internal storage volume of the device.
+ *
+ * @see StorageManager.getPrimaryStorageVolume
+ */
+ fun getInternalVolume(): Volume.Internal
+
+ /**
+ * The list of [Volume]s currently recognized by [StorageManager].
+ *
+ * @see StorageManager.getStorageVolumes
+ */
+ fun getVolumes(): List
+
+ companion object {
+ fun from(context: Context): VolumeManager =
+ VolumeManagerImpl(context.getSystemService(StorageManager::class.java))
+ }
+}
+
+private class VolumeManagerImpl(private val storageManager: StorageManager) : VolumeManager {
+ override fun getInternalVolume(): Volume.Internal =
+ InternalVolumeImpl(storageManager.primaryStorageVolume)
+
+ override fun getVolumes() =
+ storageManager.storageVolumesCompat.map {
+ if (it.isInternalCompat) {
+ InternalVolumeImpl(it)
+ } else {
+ ExternalVolumeImpl(it)
+ }
+ }
+
+ private data class InternalVolumeImpl(val storageVolume: StorageVolume) : Volume.Internal {
+ override val mediaStoreName
+ get() = storageVolume.mediaStoreVolumeNameCompat
+
+ override val components
+ get() = storageVolume.directoryCompat?.let(Components.Companion::parseUnix)
+
+ override fun resolveName(context: Context) = storageVolume.getDescriptionCompat(context)
+ }
+
+ private data class ExternalVolumeImpl(val storageVolume: StorageVolume) : Volume.External {
+ override val id
+ get() = storageVolume.uuidCompat
+
+ override val mediaStoreName
+ get() = storageVolume.mediaStoreVolumeNameCompat
+
+ override val components
+ get() = storageVolume.directoryCompat?.let(Components.Companion::parseUnix)
+
+ override fun resolveName(context: Context) = storageVolume.getDescriptionCompat(context)
+ }
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/graph/MusicGraph.kt b/musikr/src/main/java/org/oxycblt/musikr/graph/MusicGraph.kt
new file mode 100644
index 000000000..0d0e6104f
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/graph/MusicGraph.kt
@@ -0,0 +1,384 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * MusicGraph.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 .
+ */
+
+package org.oxycblt.musikr.graph
+
+import org.oxycblt.musikr.Music
+import org.oxycblt.musikr.playlist.SongPointer
+import org.oxycblt.musikr.playlist.interpret.PrePlaylist
+import org.oxycblt.musikr.tag.interpret.PreAlbum
+import org.oxycblt.musikr.tag.interpret.PreArtist
+import org.oxycblt.musikr.tag.interpret.PreGenre
+import org.oxycblt.musikr.tag.interpret.PreSong
+import org.oxycblt.musikr.util.unlikelyToBeNull
+
+internal data class MusicGraph(
+ val songVertex: List,
+ val albumVertex: List,
+ val artistVertex: List,
+ val genreVertex: List,
+ val playlistVertex: Set
+) {
+ interface Builder {
+ fun add(preSong: PreSong)
+
+ fun add(prePlaylist: PrePlaylist)
+
+ fun build(): MusicGraph
+ }
+
+ companion object {
+ fun builder(): Builder = MusicGraphBuilderImpl()
+ }
+}
+
+private class MusicGraphBuilderImpl : MusicGraph.Builder {
+ private val songVertices = mutableMapOf()
+ private val albumVertices = mutableMapOf()
+ private val artistVertices = mutableMapOf()
+ private val genreVertices = mutableMapOf()
+ private val playlistVertices = mutableSetOf()
+
+ override fun add(preSong: PreSong) {
+ val uid = preSong.uid
+ if (songVertices.containsKey(uid)) {
+ return
+ }
+
+ val songGenreVertices =
+ preSong.preGenres.map { preGenre ->
+ genreVertices.getOrPut(preGenre) { GenreVertex(preGenre) }
+ }
+
+ val songArtistVertices =
+ preSong.preArtists.map { preArtist ->
+ artistVertices.getOrPut(preArtist) { ArtistVertex(preArtist) }
+ }
+
+ val albumVertex =
+ albumVertices.getOrPut(preSong.preAlbum) {
+ // Albums themselves have their own parent artists that also need to be
+ // linked up.
+ val albumArtistVertices =
+ preSong.preAlbum.preArtists.map { preArtist ->
+ artistVertices.getOrPut(preArtist) { ArtistVertex(preArtist) }
+ }
+ val albumVertex = AlbumVertex(preSong.preAlbum, albumArtistVertices.toMutableList())
+ // Album vertex is linked, now link artists back to album.
+ albumArtistVertices.forEach { artistVertex ->
+ artistVertex.albumVertices.add(albumVertex)
+ }
+ albumVertex
+ }
+
+ val songVertex =
+ SongVertex(
+ preSong,
+ albumVertex,
+ songArtistVertices.toMutableList(),
+ songGenreVertices.toMutableList())
+ albumVertex.songVertices.add(songVertex)
+
+ songArtistVertices.forEach { artistVertex ->
+ artistVertex.songVertices.add(songVertex)
+ songGenreVertices.forEach { genreVertex ->
+ // Mutually link any new genres to the artist
+ artistVertex.genreVertices.add(genreVertex)
+ genreVertex.artistVertices.add(artistVertex)
+ }
+ }
+
+ songGenreVertices.forEach { genreVertex -> genreVertex.songVertices.add(songVertex) }
+
+ songVertices[uid] = songVertex
+ }
+
+ override fun add(prePlaylist: PrePlaylist) {
+ playlistVertices.add(PlaylistVertex(prePlaylist))
+ }
+
+ override fun build(): MusicGraph {
+ val genreClusters = genreVertices.values.groupBy { it.preGenre.rawName?.lowercase() }
+ for (cluster in genreClusters.values) {
+ simplifyGenreCluster(cluster)
+ }
+
+ val artistClusters = artistVertices.values.groupBy { it.preArtist.rawName?.lowercase() }
+ for (cluster in artistClusters.values) {
+ simplifyArtistCluster(cluster)
+ }
+
+ val albumClusters = albumVertices.values.groupBy { it.preAlbum.rawName?.lowercase() }
+ for (cluster in albumClusters.values) {
+ simplifyAlbumCluster(cluster)
+ }
+
+ // Remove any edges that wound up connecting to the same artist or genre
+ // in the end after simplification.
+ albumVertices.values.forEach {
+ it.artistVertices = it.artistVertices.distinct().toMutableList()
+ }
+
+ songVertices.entries.forEach { entry ->
+ val vertex = entry.value
+ vertex.artistVertices = vertex.artistVertices.distinct().toMutableList()
+ vertex.genreVertices = vertex.genreVertices.distinct().toMutableList()
+
+ playlistVertices.forEach {
+ val pointer = SongPointer.UID(entry.key)
+ it.pointerMap[pointer]?.forEach { index -> it.songVertices[index] = vertex }
+ }
+ }
+
+ val graph =
+ MusicGraph(
+ songVertices.values.toList(),
+ albumVertices.values.toList(),
+ artistVertices.values.toList(),
+ genreVertices.values.toList(),
+ playlistVertices)
+
+ return graph
+ }
+
+ private fun simplifyGenreCluster(cluster: Collection) {
+ if (cluster.size == 1) {
+ // Nothing to do.
+ return
+ }
+ // All of these genres are semantically equivalent. Pick the most popular variation
+ // and merge all the others into it.
+ val clusterSet = cluster.toMutableSet()
+ val dst = clusterSet.maxBy { it.songVertices.size }
+ clusterSet.remove(dst)
+ for (src in clusterSet) {
+ meldGenreVertices(src, dst)
+ }
+ }
+
+ private fun meldGenreVertices(src: GenreVertex, dst: GenreVertex) {
+ if (src == dst) {
+ // Same vertex, do nothing
+ return
+ }
+ // Link all songs and artists from the irrelevant genre to the relevant genre.
+ dst.songVertices.addAll(src.songVertices)
+ dst.artistVertices.addAll(src.artistVertices)
+ // Update all songs and artists to point to the relevant genre.
+ src.songVertices.forEach {
+ val index = it.genreVertices.indexOf(src)
+ check(index >= 0) { "Illegal state: directed edge between genre and song" }
+ it.genreVertices[index] = dst
+ }
+ src.artistVertices.forEach {
+ it.genreVertices.remove(src)
+ it.genreVertices.add(dst)
+ }
+ // Remove the irrelevant genre from the graph.
+ genreVertices.remove(src.preGenre)
+ }
+
+ private fun simplifyArtistCluster(cluster: Collection) {
+ if (cluster.size == 1) {
+ // Nothing to do.
+ return
+ }
+ val fullMusicBrainzIdCoverage = cluster.all { it.preArtist.musicBrainzId != null }
+ if (fullMusicBrainzIdCoverage) {
+ // All artists have MBIDs, nothing needs to be merged.
+ val mbidClusters = cluster.groupBy { unlikelyToBeNull(it.preArtist.musicBrainzId) }
+ for (mbidCluster in mbidClusters.values) {
+ simplifyArtistClusterImpl(mbidCluster)
+ }
+ return
+ }
+ // No full MBID coverage, discard the MBIDs from the graph.
+ val strippedCluster =
+ cluster.map {
+ val noMbidPreArtist = it.preArtist.copy(musicBrainzId = null)
+ val simpleMbidVertex =
+ artistVertices.getOrPut(noMbidPreArtist) { ArtistVertex(noMbidPreArtist) }
+ meldArtistVertices(it, simpleMbidVertex)
+ simpleMbidVertex
+ }
+ simplifyArtistClusterImpl(strippedCluster)
+ }
+
+ private fun simplifyArtistClusterImpl(cluster: Collection) {
+ if (cluster.size == 1) {
+ // One canonical artist, nothing to collapse
+ return
+ }
+ val clusterSet = cluster.toMutableSet()
+ val relevantArtistVertex = clusterSet.maxBy { it.songVertices.size }
+ clusterSet.remove(relevantArtistVertex)
+ for (irrelevantArtistVertex in clusterSet) {
+ meldArtistVertices(irrelevantArtistVertex, relevantArtistVertex)
+ }
+ }
+
+ private fun meldArtistVertices(src: ArtistVertex, dst: ArtistVertex) {
+ if (src == dst) {
+ // Same vertex, do nothing
+ return
+ }
+ // Link all songs and albums from the irrelevant artist to the relevant artist.
+ dst.songVertices.addAll(src.songVertices)
+ dst.albumVertices.addAll(src.albumVertices)
+ dst.genreVertices.addAll(src.genreVertices)
+ // Update all songs, albums, and genres to point to the relevant artist.
+ src.songVertices.forEach {
+ // There can be duplicate artist vertices that we need to
+ // all replace when melding.
+ for (idx in it.artistVertices.indices) {
+ if (it.artistVertices[idx] == src) {
+ it.artistVertices[idx] = dst
+ }
+ }
+ }
+ src.albumVertices.forEach {
+ // There can be duplicate artist vertices that we need to
+ // all replace when melding.
+ for (idx in it.artistVertices.indices) {
+ if (it.artistVertices[idx] == src) {
+ it.artistVertices[idx] = dst
+ }
+ }
+ }
+ src.genreVertices.forEach {
+ it.artistVertices.remove(src)
+ it.artistVertices.add(dst)
+ }
+
+ // Remove the irrelevant artist from the graph.
+ artistVertices.remove(src.preArtist)
+ }
+
+ private fun simplifyAlbumCluster(cluster: Collection) {
+ if (cluster.size == 1) {
+ // Nothing to do.
+ return
+ }
+ val fullMusicBrainzIdCoverage = cluster.all { it.preAlbum.musicBrainzId != null }
+ if (fullMusicBrainzIdCoverage) {
+ // All albums have MBIDs, nothing needs to be merged.
+ val mbidClusters = cluster.groupBy { unlikelyToBeNull(it.preAlbum.musicBrainzId) }
+ for (mbidCluster in mbidClusters.values) {
+ simplifyAlbumClusterImpl(mbidCluster)
+ }
+ return
+ }
+ // No full MBID coverage, discard the MBIDs from the graph.
+ val strippedCluster =
+ cluster.map {
+ val noMbidPreAlbum = it.preAlbum.copy(musicBrainzId = null)
+ val simpleMbidVertex =
+ albumVertices.getOrPut(noMbidPreAlbum) {
+ AlbumVertex(noMbidPreAlbum, it.artistVertices.toMutableList())
+ }
+ meldAlbumVertices(it, simpleMbidVertex)
+ simpleMbidVertex
+ }
+ simplifyAlbumClusterImpl(strippedCluster)
+ }
+
+ private fun simplifyAlbumClusterImpl(cluster: Collection) {
+ // All of these albums are semantically equivalent. Pick the most popular variation
+ // and merge all the others into it.
+ if (cluster.size == 1) {
+ // Nothing to do.
+ return
+ }
+ val clusterSet = cluster.toMutableSet()
+ val dst = clusterSet.maxBy { it.songVertices.size }
+ clusterSet.remove(dst)
+ for (src in clusterSet) {
+ meldAlbumVertices(src, dst)
+ }
+ }
+
+ private fun meldAlbumVertices(src: AlbumVertex, dst: AlbumVertex) {
+ if (src == dst) {
+ // Same vertex, do nothing
+ return
+ }
+ // Link all songs and artists from the irrelevant album to the relevant album.
+ dst.songVertices.addAll(src.songVertices)
+ dst.artistVertices.addAll(src.artistVertices)
+ // Update all songs and artists to point to the relevant album.
+ src.songVertices.forEach { it.albumVertex = dst }
+ src.artistVertices.forEach {
+ it.albumVertices.remove(src)
+ it.albumVertices.add(dst)
+ }
+ // Remove the irrelevant album from the graph.
+ albumVertices.remove(src.preAlbum)
+ }
+}
+
+internal interface Vertex {
+ val tag: Any?
+}
+
+internal class SongVertex(
+ val preSong: PreSong,
+ var albumVertex: AlbumVertex,
+ var artistVertices: MutableList,
+ var genreVertices: MutableList
+) : Vertex {
+ override var tag: Any? = null
+
+ override fun toString() = "SongVertex(preSong=$preSong)"
+}
+
+internal class AlbumVertex(val preAlbum: PreAlbum, var artistVertices: MutableList) :
+ Vertex {
+ val songVertices = mutableSetOf()
+ override var tag: Any? = null
+
+ override fun toString() = "AlbumVertex(preAlbum=$preAlbum)"
+}
+
+internal class ArtistVertex(
+ val preArtist: PreArtist,
+) : Vertex {
+ val songVertices = mutableSetOf()
+ val albumVertices = mutableSetOf()
+ val genreVertices = mutableSetOf()
+ override var tag: Any? = null
+
+ override fun toString() = "ArtistVertex(preArtist=$preArtist)"
+}
+
+internal class GenreVertex(val preGenre: PreGenre) : Vertex {
+ val songVertices = mutableSetOf()
+ val artistVertices = mutableSetOf()
+ override var tag: Any? = null
+
+ override fun toString() = "GenreVertex(preGenre=$preGenre)"
+}
+
+internal class PlaylistVertex(val prePlaylist: PrePlaylist) {
+ val songVertices = Array(prePlaylist.songPointers.size) { null }
+ val pointerMap =
+ prePlaylist.songPointers
+ .withIndex()
+ .groupBy { it.value }
+ .mapValuesTo(mutableMapOf()) { entry -> entry.value.map { it.index } }
+ val tag: Any? = null
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/metadata/Metadata.kt b/musikr/src/main/java/org/oxycblt/musikr/metadata/Metadata.kt
new file mode 100644
index 000000000..758e4483a
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/metadata/Metadata.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * Metadata.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 .
+ */
+
+package org.oxycblt.musikr.metadata
+
+internal data class Metadata(
+ val id3v2: Map>,
+ val xiph: Map>,
+ val mp4: Map>,
+ val cover: ByteArray?,
+ val properties: Properties
+) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as Metadata
+
+ if (id3v2 != other.id3v2) return false
+ if (xiph != other.xiph) return false
+ if (mp4 != other.mp4) return false
+ if (cover != null) {
+ if (other.cover == null) return false
+ if (!cover.contentEquals(other.cover)) return false
+ } else if (other.cover != null) return false
+ if (properties != other.properties) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = id3v2.hashCode()
+ result = 31 * result + xiph.hashCode()
+ result = 31 * result + mp4.hashCode()
+ result = 31 * result + (cover?.contentHashCode() ?: 0)
+ result = 31 * result + properties.hashCode()
+ return result
+ }
+}
+
+internal data class Properties(
+ val mimeType: String,
+ val durationMs: Long,
+ val bitrateKbps: Int,
+ val sampleRateHz: Int,
+)
diff --git a/musikr/src/main/java/org/oxycblt/musikr/metadata/MetadataExtractor.kt b/musikr/src/main/java/org/oxycblt/musikr/metadata/MetadataExtractor.kt
new file mode 100644
index 000000000..0b0cfc48d
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/metadata/MetadataExtractor.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * MetadataExtractor.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 .
+ */
+
+package org.oxycblt.musikr.metadata
+
+import android.os.ParcelFileDescriptor
+import java.io.FileInputStream
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import org.oxycblt.musikr.fs.DeviceFile
+
+internal interface MetadataExtractor {
+ suspend fun extract(deviceFile: DeviceFile, fd: ParcelFileDescriptor): Metadata?
+
+ companion object {
+ fun new(): MetadataExtractor = MetadataExtractorImpl
+ }
+}
+
+private object MetadataExtractorImpl : MetadataExtractor {
+ override suspend fun extract(deviceFile: DeviceFile, fd: ParcelFileDescriptor) =
+ withContext(Dispatchers.IO) {
+ val fis = FileInputStream(fd.fileDescriptor)
+ TagLibJNI.open(deviceFile, fis).also { fis.close() }
+ }
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/metadata/NativeInputStream.kt b/musikr/src/main/java/org/oxycblt/musikr/metadata/NativeInputStream.kt
new file mode 100644
index 000000000..b8c22fb24
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/metadata/NativeInputStream.kt
@@ -0,0 +1,94 @@
+/*
+ * 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 .
+ */
+
+package org.oxycblt.musikr.metadata
+
+import android.util.Log
+import java.io.FileInputStream
+import java.nio.ByteBuffer
+import org.oxycblt.musikr.fs.DeviceFile
+
+internal class NativeInputStream(private val deviceFile: DeviceFile, fis: FileInputStream) {
+ private val channel = fis.channel
+
+ fun name() = requireNotNull(deviceFile.path.name)
+
+ fun readBlock(buf: ByteBuffer): Boolean {
+ try {
+ channel.read(buf)
+ return true
+ } catch (e: Exception) {
+ Log.d("NativeInputStream", "Error reading block", e)
+ return false
+ }
+ }
+
+ fun isOpen(): Boolean {
+ return channel.isOpen
+ }
+
+ fun seekFromBeginning(offset: Long): Boolean {
+ try {
+ channel.position(offset)
+ return true
+ } catch (e: Exception) {
+ Log.d("NativeInputStream", "Error seeking from beginning", e)
+ return false
+ }
+ }
+
+ fun seekFromCurrent(offset: Long): Boolean {
+ try {
+ channel.position(channel.position() + offset)
+ return true
+ } catch (e: Exception) {
+ Log.d("NativeInputStream", "Error seeking from current", e)
+ return false
+ }
+ }
+
+ fun seekFromEnd(offset: Long): Boolean {
+ try {
+ channel.position(channel.size() + offset)
+ return true
+ } catch (e: Exception) {
+ Log.d("NativeInputStream", "Error seeking from end", e)
+ return false
+ }
+ }
+
+ fun tell() =
+ try {
+ channel.position()
+ } catch (e: Exception) {
+ Log.d("NativeInputStream", "Error getting position", e)
+ Long.MIN_VALUE
+ }
+
+ fun length() =
+ try {
+ channel.size()
+ } catch (e: Exception) {
+ Log.d("NativeInputStream", "Error getting length", e)
+ Long.MIN_VALUE
+ }
+
+ fun close() {
+ channel.close()
+ }
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/metadata/NativeTagMap.kt b/musikr/src/main/java/org/oxycblt/musikr/metadata/NativeTagMap.kt
new file mode 100644
index 000000000..0e04519b2
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/metadata/NativeTagMap.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2025 Auxio Project
+ * NativeTagMap.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 .
+ */
+
+package org.oxycblt.musikr.metadata
+
+import org.oxycblt.musikr.util.correctWhitespace
+
+internal class NativeTagMap {
+ private val map = mutableMapOf>()
+
+ fun addID(id: String, value: String) {
+ addID(id, listOf(value))
+ }
+
+ fun addID(id: String, values: List) {
+ if (values.isEmpty()) {
+ return
+ }
+ val correctedValues = values.mapNotNull { it.correctWhitespace() }
+ if (correctedValues.isEmpty()) {
+ return
+ }
+ map[id] = correctedValues
+ }
+
+ fun addCustom(description: String, value: String) {
+ addCustom(description, listOf(value))
+ }
+
+ fun addCustom(description: String, values: List) {
+ if (values.isEmpty()) {
+ return
+ }
+ val correctedValues = values.mapNotNull { it.correctWhitespace() }
+ if (correctedValues.isEmpty()) {
+ return
+ }
+ map[description.uppercase()] = correctedValues
+ }
+
+ fun addCombined(id: String, description: String, value: String) {
+ addCombined(id, description, listOf(value))
+ }
+
+ fun addCombined(id: String, description: String, values: List) {
+ if (values.isEmpty()) {
+ return
+ }
+ val correctedValues = values.mapNotNull { it.correctWhitespace() }
+ if (correctedValues.isEmpty()) {
+ return
+ }
+ map["$id:${description.uppercase()}"] = correctedValues
+ }
+
+ fun getObject(): Map> {
+ return map
+ }
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/metadata/TagLibJNI.kt b/musikr/src/main/java/org/oxycblt/musikr/metadata/TagLibJNI.kt
new file mode 100644
index 000000000..d5105f3a8
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/metadata/TagLibJNI.kt
@@ -0,0 +1,42 @@
+/*
+ * 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 .
+ */
+
+package org.oxycblt.musikr.metadata
+
+import java.io.FileInputStream
+import org.oxycblt.musikr.fs.DeviceFile
+
+internal object TagLibJNI {
+ init {
+ System.loadLibrary("tagJNI")
+ }
+
+ /**
+ * Open a file and extract a tag.
+ *
+ * Note: This method is blocking and should be handled as such if calling from a coroutine.
+ */
+ fun open(deviceFile: DeviceFile, fis: FileInputStream): Metadata? {
+ val inputStream = NativeInputStream(deviceFile, fis)
+ val tag = openNative(inputStream)
+ inputStream.close()
+ return tag
+ }
+
+ private external fun openNative(inputStream: NativeInputStream): Metadata?
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/model/AlbumImpl.kt b/musikr/src/main/java/org/oxycblt/musikr/model/AlbumImpl.kt
new file mode 100644
index 000000000..99817b6eb
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/model/AlbumImpl.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * AlbumImpl.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 .
+ */
+
+package org.oxycblt.musikr.model
+
+import org.oxycblt.musikr.Album
+import org.oxycblt.musikr.Artist
+import org.oxycblt.musikr.Music
+import org.oxycblt.musikr.Song
+import org.oxycblt.musikr.cover.CoverCollection
+import org.oxycblt.musikr.tag.Date
+import org.oxycblt.musikr.tag.interpret.PreAlbum
+import org.oxycblt.musikr.util.update
+
+internal interface AlbumCore {
+ val preAlbum: PreAlbum
+ val songs: Set
+
+ fun resolveArtists(): List
+}
+
+/**
+ * Library-backed implementation of [Album].
+ *
+ * @author Alexander Capehart (OxygenCobalt)
+ */
+class AlbumImpl internal constructor(private val core: AlbumCore) : Album {
+ private val preAlbum = core.preAlbum
+
+ override val uid =
+ // Attempt to use a MusicBrainz ID first before falling back to a hashed UID.
+ preAlbum.musicBrainzId?.let { Music.UID.musicBrainz(Music.UID.Item.ALBUM, it) }
+ ?: Music.UID.auxio(Music.UID.Item.ALBUM) {
+ // Hash based on only names despite the presence of a date to increase stability.
+ // I don't know if there is any situation where an artist will have two albums with
+ // the exact same name, but if there is, I would love to know.
+ update(preAlbum.rawName)
+ update(preAlbum.preArtists.map { it.rawName })
+ }
+ override val name = preAlbum.name
+ override val releaseType = preAlbum.releaseType
+ override val durationMs = core.songs.sumOf { it.durationMs }
+ override val addedMs = core.songs.minOf { it.addedMs }
+ override val covers = CoverCollection.from(core.songs.mapNotNull { it.cover })
+ override val dates: Date.Range? =
+ core.songs.mapNotNull { it.date }.ifEmpty { null }?.run { Date.Range(min(), max()) }
+
+ override val artists: List
+ get() = core.resolveArtists()
+
+ override val songs = core.songs
+
+ private val hashCode = 31 * (31 * uid.hashCode() + preAlbum.hashCode()) + songs.hashCode()
+
+ override fun hashCode() = hashCode
+
+ override fun equals(other: Any?) =
+ other is AlbumImpl && uid == other.uid && preAlbum == other.preAlbum && songs == other.songs
+
+ override fun toString() = "Album(uid=$uid, name=$name)"
+
+ fun a(other: AlbumImpl) = uid == other.uid
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/model/ArtistImpl.kt b/musikr/src/main/java/org/oxycblt/musikr/model/ArtistImpl.kt
new file mode 100644
index 000000000..e05740401
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/model/ArtistImpl.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * ArtistImpl.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 .
+ */
+
+package org.oxycblt.musikr.model
+
+import org.oxycblt.musikr.Album
+import org.oxycblt.musikr.Artist
+import org.oxycblt.musikr.Genre
+import org.oxycblt.musikr.Music
+import org.oxycblt.musikr.Song
+import org.oxycblt.musikr.cover.CoverCollection
+import org.oxycblt.musikr.tag.interpret.PreArtist
+import org.oxycblt.musikr.util.update
+
+internal interface ArtistCore {
+ val preArtist: PreArtist
+ val songs: Set
+ val albums: Set
+
+ fun resolveGenres(): Set
+}
+
+/**
+ * Library-backed implementation of [Artist].
+ *
+ * @author Alexander Capehart (OxygenCobalt)
+ */
+internal class ArtistImpl(private val core: ArtistCore) : Artist {
+ override val uid =
+ // Attempt to use a MusicBrainz ID first before falling back to a hashed UID.
+ core.preArtist.musicBrainzId?.let { Music.UID.musicBrainz(Music.UID.Item.ARTIST, it) }
+ ?: Music.UID.auxio(Music.UID.Item.ARTIST) { update(core.preArtist.rawName) }
+ override val name = core.preArtist.name
+
+ override val songs = core.songs
+ override var explicitAlbums = core.albums
+ override var implicitAlbums = core.songs.mapTo(mutableSetOf()) { it.album } - core.albums
+
+ override val genres: List
+ get() = core.resolveGenres().toList()
+
+ override val durationMs = core.songs.sumOf { it.durationMs }
+ override val covers = CoverCollection.from(core.songs.mapNotNull { it.cover })
+
+ private val hashCode =
+ 31 * (31 * uid.hashCode() + core.preArtist.hashCode()) * core.songs.hashCode()
+
+ override fun hashCode() = hashCode
+
+ override fun equals(other: Any?) =
+ other is ArtistImpl &&
+ uid == other.uid &&
+ core.preArtist == other.core.preArtist &&
+ songs == other.songs
+
+ override fun toString() = "Artist(uid=$uid, name=$name)"
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/model/GenreImpl.kt b/musikr/src/main/java/org/oxycblt/musikr/model/GenreImpl.kt
new file mode 100644
index 000000000..0805f284b
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/model/GenreImpl.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2023 Auxio Project
+ * GenreImpl.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 .
+ */
+
+package org.oxycblt.musikr.model
+
+import org.oxycblt.musikr.Artist
+import org.oxycblt.musikr.Genre
+import org.oxycblt.musikr.Music
+import org.oxycblt.musikr.Song
+import org.oxycblt.musikr.cover.CoverCollection
+import org.oxycblt.musikr.tag.interpret.PreGenre
+import org.oxycblt.musikr.util.update
+
+internal interface GenreCore {
+ val preGenre: PreGenre
+ val songs: Set
+ val artists: Set
+}
+
+/**
+ * Library-backed implementation of [Genre].
+ *
+ * @author Alexander Capehart (OxygenCobalt)
+ */
+internal class GenreImpl(private val core: GenreCore) : Genre {
+ override val uid = Music.UID.auxio(Music.UID.Item.GENRE) { update(core.preGenre.rawName) }
+ override val name = core.preGenre.name
+
+ override val songs = core.songs
+ override val artists = core.artists
+ override val durationMs = core.songs.sumOf { it.durationMs }
+ override val covers = CoverCollection.from(core.songs.mapNotNull { it.cover })
+
+ private val hashCode = 31 * (31 * uid.hashCode() + core.preGenre.hashCode()) + songs.hashCode()
+
+ override fun hashCode() = hashCode
+
+ override fun equals(other: Any?) =
+ other is GenreImpl &&
+ uid == other.uid &&
+ core.preGenre == other.core.preGenre &&
+ songs == other.songs
+
+ override fun toString() = "Genre(uid=$uid, name=$name)"
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/model/LibraryFactory.kt b/musikr/src/main/java/org/oxycblt/musikr/model/LibraryFactory.kt
new file mode 100644
index 000000000..92296c80c
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/model/LibraryFactory.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * LibraryFactory.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 .
+ */
+
+package org.oxycblt.musikr.model
+
+import org.oxycblt.musikr.Album
+import org.oxycblt.musikr.Artist
+import org.oxycblt.musikr.Genre
+import org.oxycblt.musikr.Music
+import org.oxycblt.musikr.MutableLibrary
+import org.oxycblt.musikr.Song
+import org.oxycblt.musikr.graph.AlbumVertex
+import org.oxycblt.musikr.graph.ArtistVertex
+import org.oxycblt.musikr.graph.GenreVertex
+import org.oxycblt.musikr.graph.MusicGraph
+import org.oxycblt.musikr.graph.PlaylistVertex
+import org.oxycblt.musikr.graph.SongVertex
+import org.oxycblt.musikr.graph.Vertex
+import org.oxycblt.musikr.playlist.db.StoredPlaylists
+import org.oxycblt.musikr.playlist.interpret.PlaylistInterpreter
+
+internal interface LibraryFactory {
+ fun create(
+ graph: MusicGraph,
+ storedPlaylists: StoredPlaylists,
+ playlistInterpreter: PlaylistInterpreter
+ ): MutableLibrary
+
+ companion object {
+ fun new(): LibraryFactory = LibraryFactoryImpl()
+ }
+}
+
+private class LibraryFactoryImpl() : LibraryFactory {
+ override fun create(
+ graph: MusicGraph,
+ storedPlaylists: StoredPlaylists,
+ playlistInterpreter: PlaylistInterpreter
+ ): MutableLibrary {
+ val songs =
+ graph.songVertex.mapTo(mutableSetOf()) { vertex ->
+ SongImpl(SongVertexCore(vertex)).also { vertex.tag = it }
+ }
+ val albums =
+ graph.albumVertex.mapTo(mutableSetOf()) { vertex ->
+ AlbumImpl(AlbumVertexCore(vertex)).also { vertex.tag = it }
+ }
+ val artists =
+ graph.artistVertex.mapTo(mutableSetOf()) { vertex ->
+ ArtistImpl(ArtistVertexCore(vertex)).also { vertex.tag = it }
+ }
+ val genres =
+ graph.genreVertex.mapTo(mutableSetOf()) { vertex ->
+ GenreImpl(GenreVertexCore(vertex)).also { vertex.tag = it }
+ }
+ val playlists =
+ graph.playlistVertex.mapTo(mutableSetOf()) { vertex ->
+ PlaylistImpl(PlaylistVertexCore(vertex))
+ }
+ return LibraryImpl(
+ songs, albums, artists, genres, playlists, storedPlaylists, playlistInterpreter)
+ }
+
+ private class SongVertexCore(private val vertex: SongVertex) : SongCore {
+ override val preSong = vertex.preSong
+
+ override fun resolveAlbum(): Album = tag(vertex.albumVertex)
+
+ override fun resolveArtists(): List = vertex.artistVertices.map { tag(it) }
+
+ override fun resolveGenres(): List = vertex.genreVertices.map { tag(it) }
+ }
+
+ private class AlbumVertexCore(private val vertex: AlbumVertex) : AlbumCore {
+ override val preAlbum = vertex.preAlbum
+
+ override val songs: Set = vertex.songVertices.mapTo(mutableSetOf()) { tag(it) }
+
+ override fun resolveArtists(): List = vertex.artistVertices.map { tag(it) }
+ }
+
+ private class ArtistVertexCore(private val vertex: ArtistVertex) : ArtistCore {
+ override val preArtist = vertex.preArtist
+
+ override val songs: Set = vertex.songVertices.mapTo(mutableSetOf()) { tag(it) }
+
+ override val albums: Set = vertex.albumVertices.mapTo(mutableSetOf()) { tag(it) }
+
+ override fun resolveGenres(): Set =
+ vertex.genreVertices.mapTo(mutableSetOf()) { tag(it) }
+ }
+
+ private class GenreVertexCore(vertex: GenreVertex) : GenreCore {
+ override val preGenre = vertex.preGenre
+
+ override val songs: Set = vertex.songVertices.mapTo(mutableSetOf()) { tag(it) }
+
+ override val artists: Set = vertex.artistVertices.mapTo(mutableSetOf()) { tag(it) }
+ }
+
+ private class PlaylistVertexCore(vertex: PlaylistVertex) : PlaylistCore {
+ override val prePlaylist = vertex.prePlaylist
+
+ override val songs: List =
+ vertex.songVertices.mapNotNull { vertex -> vertex?.let { tag(it) } }
+ }
+
+ private companion object {
+ private inline fun tag(vertex: Vertex): T {
+ val tag = vertex.tag
+ check(tag is T) { "Dead Vertex Detected: $vertex" }
+ return tag
+ }
+ }
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/model/LibraryImpl.kt b/musikr/src/main/java/org/oxycblt/musikr/model/LibraryImpl.kt
new file mode 100644
index 000000000..badcada45
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/model/LibraryImpl.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * LibraryImpl.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 .
+ */
+
+package org.oxycblt.musikr.model
+
+import org.oxycblt.musikr.Music
+import org.oxycblt.musikr.MutableLibrary
+import org.oxycblt.musikr.Playlist
+import org.oxycblt.musikr.Song
+import org.oxycblt.musikr.fs.Path
+import org.oxycblt.musikr.playlist.db.StoredPlaylists
+import org.oxycblt.musikr.playlist.interpret.PlaylistInterpreter
+import org.oxycblt.musikr.playlist.interpret.PrePlaylistInfo
+
+internal data class LibraryImpl(
+ override val songs: Collection,
+ override val albums: Collection,
+ override val artists: Collection,
+ override val genres: Collection,
+ override val playlists: Collection,
+ private val storedPlaylists: StoredPlaylists,
+ private val playlistInterpreter: PlaylistInterpreter
+) : MutableLibrary {
+ private val songUidMap = songs.associateBy { it.uid }
+ private val albumUidMap = albums.associateBy { it.uid }
+ private val artistUidMap = artists.associateBy { it.uid }
+ private val genreUidMap = genres.associateBy { it.uid }
+ private val playlistUidMap = playlists.associateBy { it.uid }
+
+ override fun empty() = songs.isEmpty()
+
+ override fun findSong(uid: Music.UID) = songUidMap[uid]
+
+ override fun findSongByPath(path: Path) = songs.find { it.path == path }
+
+ override fun findAlbum(uid: Music.UID) = albumUidMap[uid]
+
+ override fun findArtist(uid: Music.UID) = artistUidMap[uid]
+
+ override fun findGenre(uid: Music.UID) = genreUidMap[uid]
+
+ override fun findPlaylist(uid: Music.UID) = playlistUidMap[uid]
+
+ override fun findPlaylistByName(name: String) = playlists.find { it.name.raw == name }
+
+ override suspend fun createPlaylist(name: String, songs: List): MutableLibrary {
+ val handle = storedPlaylists.new(name, songs)
+ val postPlaylist = playlistInterpreter.interpret(name, handle)
+ val core = NewPlaylistCore(postPlaylist, songs)
+ val playlist = PlaylistImpl(core)
+ return copy(playlists = playlists + playlist)
+ }
+
+ override suspend fun renamePlaylist(playlist: Playlist, name: String): MutableLibrary {
+ val playlistImpl =
+ requireNotNull(playlistUidMap[playlist.uid]) {
+ "Playlist to rename is not in this library"
+ }
+ val prePlaylist = playlistImpl.core.prePlaylist
+ prePlaylist.handle.rename(name)
+ val postPlaylist = playlistInterpreter.interpret(name, prePlaylist.handle)
+ val core = NewPlaylistCore(postPlaylist, playlist.songs)
+ val newPlaylist = PlaylistImpl(core)
+ return copy(playlists = playlists - playlistImpl + newPlaylist)
+ }
+
+ override suspend fun addToPlaylist(playlist: Playlist, songs: List): MutableLibrary {
+ val playlistImpl =
+ requireNotNull(playlistUidMap[playlist.uid]) {
+ "Playlist to add to is not in this library"
+ }
+ playlistImpl.core.prePlaylist.handle.add(songs)
+ val core = NewPlaylistCore(playlistImpl.core.prePlaylist, playlistImpl.songs + songs)
+ val newPlaylist = PlaylistImpl(core)
+ return copy(playlists = playlists - playlistImpl + newPlaylist)
+ }
+
+ override suspend fun rewritePlaylist(playlist: Playlist, songs: List): MutableLibrary {
+ val playlistImpl =
+ requireNotNull(playlistUidMap[playlist.uid]) {
+ "Playlist to rewrite is not in this library"
+ }
+ playlistImpl.core.prePlaylist.handle.rewrite(songs)
+ val core = NewPlaylistCore(playlistImpl.core.prePlaylist, songs)
+ val newPlaylist = PlaylistImpl(core)
+ return copy(playlists = playlists - playlistImpl + newPlaylist)
+ }
+
+ override suspend fun deletePlaylist(playlist: Playlist): MutableLibrary {
+ val playlistImpl =
+ requireNotNull(playlistUidMap[playlist.uid]) {
+ "Playlist to delete is not in this library"
+ }
+ playlistImpl.core.prePlaylist.handle.delete()
+ return copy(playlists = playlists - playlistImpl)
+ }
+
+ private class NewPlaylistCore(
+ override val prePlaylist: PrePlaylistInfo,
+ override val songs: List
+ ) : PlaylistCore
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/model/PlaylistImpl.kt b/musikr/src/main/java/org/oxycblt/musikr/model/PlaylistImpl.kt
new file mode 100644
index 000000000..a92197df5
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/model/PlaylistImpl.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2023 Auxio Project
+ * PlaylistImpl.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 .
+ */
+
+package org.oxycblt.musikr.model
+
+import org.oxycblt.musikr.Playlist
+import org.oxycblt.musikr.Song
+import org.oxycblt.musikr.cover.CoverCollection
+import org.oxycblt.musikr.playlist.interpret.PrePlaylistInfo
+import org.oxycblt.musikr.tag.Name
+
+internal interface PlaylistCore {
+ val prePlaylist: PrePlaylistInfo
+ val songs: List
+}
+
+internal class PlaylistImpl(val core: PlaylistCore) : Playlist {
+ override val uid = core.prePlaylist.handle.uid
+ override val name: Name.Known = core.prePlaylist.name
+ override val durationMs = core.songs.sumOf { it.durationMs }
+ override val covers = CoverCollection.from(core.songs.mapNotNull { it.cover })
+ override val songs = core.songs
+
+ private var hashCode =
+ 31 * (31 * uid.hashCode() + core.prePlaylist.hashCode()) + songs.hashCode()
+
+ override fun equals(other: Any?) =
+ other is PlaylistImpl && core.prePlaylist == other.core.prePlaylist && songs == other.songs
+
+ override fun hashCode() = hashCode
+
+ override fun toString() = "Playlist(uid=$uid, name=$name)"
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/model/SongImpl.kt b/musikr/src/main/java/org/oxycblt/musikr/model/SongImpl.kt
new file mode 100644
index 000000000..6a34168c6
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/model/SongImpl.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * SongImpl.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 .
+ */
+
+package org.oxycblt.musikr.model
+
+import org.oxycblt.musikr.Album
+import org.oxycblt.musikr.Artist
+import org.oxycblt.musikr.Genre
+import org.oxycblt.musikr.Song
+import org.oxycblt.musikr.tag.interpret.PreSong
+
+internal interface SongCore {
+ val preSong: PreSong
+
+ fun resolveAlbum(): Album
+
+ fun resolveArtists(): List
+
+ fun resolveGenres(): List
+}
+
+/**
+ * Library-backed implementation of [Song].
+ *
+ * @author Alexander Capehart (OxygenCobalt)
+ */
+internal class SongImpl(private val handle: SongCore) : Song {
+ private val preSong = handle.preSong
+
+ override val uid = preSong.uid
+ override val name = preSong.name
+ override val track = preSong.track
+ override val disc = preSong.disc
+ override val date = preSong.date
+ override val uri = preSong.uri
+ override val path = preSong.path
+ override val format = preSong.format
+ override val size = preSong.size
+ override val durationMs = preSong.durationMs
+ override val bitrateKbps = preSong.bitrateKbps
+ override val sampleRateHz = preSong.sampleRateHz
+ override val replayGainAdjustment = preSong.replayGainAdjustment
+ override val modifiedMs = preSong.modifiedMs
+ override val addedMs = preSong.addedMs
+ override val cover = preSong.cover
+ override val album: Album
+ get() = handle.resolveAlbum()
+
+ override val artists: List
+ get() = handle.resolveArtists()
+
+ override val genres: List
+ get() = handle.resolveGenres()
+
+ private val hashCode = 31 * uid.hashCode() + preSong.hashCode()
+
+ override fun hashCode() = hashCode
+
+ override fun equals(other: Any?) =
+ other is SongImpl && uid == other.uid && preSong == other.preSong
+
+ override fun toString() = "Song(uid=$uid, name=$name)"
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/pipeline/EvaluateStep.kt b/musikr/src/main/java/org/oxycblt/musikr/pipeline/EvaluateStep.kt
new file mode 100644
index 000000000..df4f72cb1
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/pipeline/EvaluateStep.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * EvaluateStep.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 .
+ */
+
+package org.oxycblt.musikr.pipeline
+
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.buffer
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.filterIsInstance
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onEach
+import org.oxycblt.musikr.Interpretation
+import org.oxycblt.musikr.MutableLibrary
+import org.oxycblt.musikr.Storage
+import org.oxycblt.musikr.graph.MusicGraph
+import org.oxycblt.musikr.model.LibraryFactory
+import org.oxycblt.musikr.playlist.db.StoredPlaylists
+import org.oxycblt.musikr.playlist.interpret.PlaylistInterpreter
+import org.oxycblt.musikr.tag.interpret.TagInterpreter
+
+internal interface EvaluateStep {
+ suspend fun evaluate(extractedMusic: Flow): MutableLibrary
+
+ companion object {
+ fun new(storage: Storage, interpretation: Interpretation): EvaluateStep =
+ EvaluateStepImpl(
+ TagInterpreter.new(interpretation),
+ PlaylistInterpreter.new(interpretation),
+ storage.storedPlaylists,
+ LibraryFactory.new())
+ }
+}
+
+private class EvaluateStepImpl(
+ private val tagInterpreter: TagInterpreter,
+ private val playlistInterpreter: PlaylistInterpreter,
+ private val storedPlaylists: StoredPlaylists,
+ private val libraryFactory: LibraryFactory
+) : EvaluateStep {
+ override suspend fun evaluate(extractedMusic: Flow): MutableLibrary {
+ val filterFlow =
+ extractedMusic.filterIsInstance().divert {
+ when (it) {
+ is ExtractedMusic.Valid.Song -> Divert.Right(it.song)
+ is ExtractedMusic.Valid.Playlist -> Divert.Left(it.file)
+ }
+ }
+ val rawSongs = filterFlow.right
+ val preSongs =
+ rawSongs
+ .map { wrap(it, tagInterpreter::interpret) }
+ .flowOn(Dispatchers.Default)
+ .buffer(Channel.UNLIMITED)
+ val prePlaylists =
+ filterFlow.left
+ .map { wrap(it, playlistInterpreter::interpret) }
+ .flowOn(Dispatchers.Default)
+ .buffer(Channel.UNLIMITED)
+ val graphBuilder = MusicGraph.builder()
+ val graphBuild =
+ merge(
+ filterFlow.manager,
+ preSongs.onEach { wrap(it, graphBuilder::add) },
+ prePlaylists.onEach { wrap(it, graphBuilder::add) })
+ graphBuild.collect()
+ val graph = graphBuilder.build()
+ return libraryFactory.create(graph, storedPlaylists, playlistInterpreter)
+ }
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/pipeline/ExploreStep.kt b/musikr/src/main/java/org/oxycblt/musikr/pipeline/ExploreStep.kt
new file mode 100644
index 000000000..1e77659e1
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/pipeline/ExploreStep.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * ExploreStep.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 .
+ */
+
+package org.oxycblt.musikr.pipeline
+
+import android.content.Context
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.asFlow
+import kotlinx.coroutines.flow.buffer
+import kotlinx.coroutines.flow.emitAll
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.merge
+import org.oxycblt.musikr.Storage
+import org.oxycblt.musikr.fs.DeviceFile
+import org.oxycblt.musikr.fs.MusicLocation
+import org.oxycblt.musikr.fs.device.DeviceFiles
+import org.oxycblt.musikr.playlist.PlaylistFile
+import org.oxycblt.musikr.playlist.db.StoredPlaylists
+import org.oxycblt.musikr.playlist.m3u.M3U
+
+internal interface ExploreStep {
+ fun explore(locations: List): Flow
+
+ companion object {
+ fun from(context: Context, storage: Storage): ExploreStep =
+ ExploreStepImpl(DeviceFiles.from(context), storage.storedPlaylists)
+ }
+}
+
+private class ExploreStepImpl(
+ private val deviceFiles: DeviceFiles,
+ private val storedPlaylists: StoredPlaylists
+) : ExploreStep {
+ override fun explore(locations: List): Flow {
+ val audios =
+ deviceFiles
+ .explore(locations.asFlow())
+ .mapNotNull {
+ when {
+ it.mimeType == M3U.MIME_TYPE -> null
+ it.mimeType.startsWith("audio/") -> ExploreNode.Audio(it)
+ else -> null
+ }
+ }
+ .flowOn(Dispatchers.IO)
+ .buffer()
+ val playlists =
+ flow { emitAll(storedPlaylists.read().asFlow()) }
+ .map { ExploreNode.Playlist(it) }
+ .flowOn(Dispatchers.IO)
+ .buffer()
+ return merge(audios, playlists)
+ }
+}
+
+internal sealed interface ExploreNode {
+ data class Audio(val file: DeviceFile) : ExploreNode
+
+ data class Playlist(val file: PlaylistFile) : ExploreNode
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/pipeline/ExtractStep.kt b/musikr/src/main/java/org/oxycblt/musikr/pipeline/ExtractStep.kt
new file mode 100644
index 000000000..858421457
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/pipeline/ExtractStep.kt
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * ExtractStep.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 .
+ */
+
+package org.oxycblt.musikr.pipeline
+
+import android.content.Context
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.buffer
+import kotlinx.coroutines.flow.flattenMerge
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onCompletion
+import kotlinx.coroutines.withContext
+import org.oxycblt.musikr.Storage
+import org.oxycblt.musikr.cache.Cache
+import org.oxycblt.musikr.cache.CacheResult
+import org.oxycblt.musikr.cover.Cover
+import org.oxycblt.musikr.cover.MutableCovers
+import org.oxycblt.musikr.fs.DeviceFile
+import org.oxycblt.musikr.metadata.MetadataExtractor
+import org.oxycblt.musikr.metadata.Properties
+import org.oxycblt.musikr.playlist.PlaylistFile
+import org.oxycblt.musikr.tag.parse.ParsedTags
+import org.oxycblt.musikr.tag.parse.TagParser
+
+internal interface ExtractStep {
+ fun extract(nodes: Flow): Flow
+
+ companion object {
+ fun from(context: Context, storage: Storage): ExtractStep =
+ ExtractStepImpl(
+ context,
+ MetadataExtractor.new(),
+ TagParser.new(),
+ storage.cache,
+ storage.storedCovers)
+ }
+}
+
+private class ExtractStepImpl(
+ private val context: Context,
+ private val metadataExtractor: MetadataExtractor,
+ private val tagParser: TagParser,
+ private val cacheFactory: Cache.Factory,
+ private val storedCovers: MutableCovers
+) : ExtractStep {
+ @OptIn(ExperimentalCoroutinesApi::class)
+ override fun extract(nodes: Flow): Flow {
+ val cache = cacheFactory.open()
+ val addingMs = System.currentTimeMillis()
+ val filterFlow =
+ nodes.divert {
+ when (it) {
+ is ExploreNode.Audio -> Divert.Right(it.file)
+ is ExploreNode.Playlist -> Divert.Left(it.file)
+ }
+ }
+ val audioNodes = filterFlow.right
+ val playlistNodes = filterFlow.left.map { ExtractedMusic.Valid.Playlist(it) }
+
+ val readDistributedFlow = audioNodes.distribute(8)
+ val cacheResults =
+ readDistributedFlow.flows
+ .map { flow ->
+ flow
+ .map { wrap(it) { file -> cache.read(file, storedCovers) } }
+ .flowOn(Dispatchers.IO)
+ .buffer(Channel.UNLIMITED)
+ }
+ .flattenMerge()
+ .buffer(Channel.UNLIMITED)
+ val cacheFlow =
+ cacheResults.divert {
+ when (it) {
+ is CacheResult.Hit -> Divert.Left(it.song)
+ is CacheResult.Miss -> Divert.Right(it.file)
+ }
+ }
+ val cachedSongs = cacheFlow.left.map { ExtractedMusic.Valid.Song(it) }
+ val uncachedSongs = cacheFlow.right
+
+ val fds =
+ uncachedSongs
+ .mapNotNull {
+ wrap(it) { file ->
+ withContext(Dispatchers.IO) {
+ context.contentResolver.openFileDescriptor(file.uri, "r")?.let { fd ->
+ FileWith(file, fd)
+ }
+ }
+ }
+ }
+ .flowOn(Dispatchers.IO)
+ .buffer(Channel.UNLIMITED)
+
+ val metadata =
+ fds.mapNotNull { fileWith ->
+ wrap(fileWith.file) { _ ->
+ metadataExtractor
+ .extract(fileWith.file, fileWith.with)
+ .let { FileWith(fileWith.file, it) }
+ .also { withContext(Dispatchers.IO) { fileWith.with.close() } }
+ }
+ }
+ .flowOn(Dispatchers.IO)
+ // Covers are pretty big, so cap the amount of parsed metadata in-memory to at most
+ // 8 to minimize GCs.
+ .buffer(8)
+
+ val extractedSongs =
+ metadata
+ .map { fileWith ->
+ if (fileWith.with != null) {
+ val tags = tagParser.parse(fileWith.file, fileWith.with)
+ val cover = fileWith.with.cover?.let { storedCovers.write(it) }
+ RawSong(fileWith.file, fileWith.with.properties, tags, cover, addingMs)
+ } else {
+ null
+ }
+ }
+ .flowOn(Dispatchers.IO)
+ .buffer(Channel.UNLIMITED)
+
+ val extractedFilter =
+ extractedSongs.divert {
+ if (it != null) Divert.Left(it) else Divert.Right(ExtractedMusic.Invalid)
+ }
+
+ val write = extractedFilter.left
+ val invalid = extractedFilter.right
+
+ val writeDistributedFlow = write.distribute(8)
+ val writtenSongs =
+ writeDistributedFlow.flows
+ .map { flow ->
+ flow
+ .map {
+ wrap(it, cache::write)
+ ExtractedMusic.Valid.Song(it)
+ }
+ .flowOn(Dispatchers.IO)
+ .buffer(Channel.UNLIMITED)
+ }
+ .flattenMerge()
+
+ val merged =
+ merge(
+ filterFlow.manager,
+ readDistributedFlow.manager,
+ cacheFlow.manager,
+ cachedSongs,
+ extractedFilter.manager,
+ writeDistributedFlow.manager,
+ writtenSongs,
+ invalid,
+ playlistNodes)
+
+ return merged.onCompletion { cache.finalize() }
+ }
+
+ private data class FileWith(val file: DeviceFile, val with: T)
+}
+
+internal data class RawSong(
+ val file: DeviceFile,
+ val properties: Properties,
+ val tags: ParsedTags,
+ val cover: Cover?,
+ val addedMs: Long
+)
+
+internal sealed interface ExtractedMusic {
+ sealed interface Valid : ExtractedMusic {
+ data class Song(val song: RawSong) : Valid
+
+ data class Playlist(val file: PlaylistFile) : Valid
+ }
+
+ data object Invalid : ExtractedMusic
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/pipeline/FlowUtil.kt b/musikr/src/main/java/org/oxycblt/musikr/pipeline/FlowUtil.kt
new file mode 100644
index 000000000..58f2c6eb0
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/pipeline/FlowUtil.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * FlowUtil.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 .
+ */
+
+package org.oxycblt.musikr.pipeline
+
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.asFlow
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.flow.withIndex
+
+internal sealed interface Divert {
+ data class Left(val value: L) : Divert
+
+ data class Right(val value: R) : Divert
+}
+
+internal class DivertedFlow(
+ val manager: Flow,
+ val left: Flow,
+ val right: Flow
+)
+
+internal inline fun Flow.divert(
+ crossinline predicate: (T) -> Divert
+): DivertedFlow {
+ val leftChannel = Channel(Channel.UNLIMITED)
+ val rightChannel = Channel(Channel.UNLIMITED)
+ val managedFlow =
+ flow {
+ collect {
+ when (val result = predicate(it)) {
+ is Divert.Left -> leftChannel.send(result.value)
+ is Divert.Right -> rightChannel.send(result.value)
+ }
+ }
+ leftChannel.close()
+ rightChannel.close()
+ }
+ return DivertedFlow(managedFlow, leftChannel.receiveAsFlow(), rightChannel.receiveAsFlow())
+}
+
+internal class DistributedFlow(val manager: Flow, val flows: Flow>)
+
+/**
+ * Equally "distributes" the values of some flow across n new flows.
+ *
+ * Note that this function requires the "manager" flow to be consumed alongside the split flows in
+ * order to function. Without this, all of the newly split flows will simply block.
+ */
+internal fun Flow.distribute(n: Int): DistributedFlow {
+ val posChannels = List(n) { Channel(Channel.UNLIMITED) }
+ val managerFlow =
+ flow {
+ withIndex().collect {
+ val index = it.index % n
+ posChannels[index].send(it.value)
+ }
+ for (channel in posChannels) {
+ channel.close()
+ }
+ }
+ val hotFlows = posChannels.asFlow().map { it.receiveAsFlow() }
+ return DistributedFlow(managerFlow, hotFlows)
+}
diff --git a/musikr/src/main/java/org/oxycblt/musikr/pipeline/PipelineException.kt b/musikr/src/main/java/org/oxycblt/musikr/pipeline/PipelineException.kt
new file mode 100644
index 000000000..1f6efc892
--- /dev/null
+++ b/musikr/src/main/java/org/oxycblt/musikr/pipeline/PipelineException.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2024 Auxio Project
+ * PipelineException.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