diff --git a/composeApp/src/commonMain/composeResources/values-ar/strings.xml b/composeApp/src/commonMain/composeResources/values-ar/strings.xml index 3f2a39678..68f8c7429 100644 --- a/composeApp/src/commonMain/composeResources/values-ar/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-ar/strings.xml @@ -266,6 +266,12 @@ فرز إظهار الأقدم أولاً إظهار الأحدث أولاً + العنوان (أ-ي) + العنوان (ي-أ) + الأحدث أولاً + الأقدم أولاً + الفنان (أ-ي) + الفنان (ي-أ) تاريخ الإضافة تطبيق موسيقي بسيط يستخدم YouTube Music for backend \n\nSimpMusic هو مشروع مفتوح المصدر لبث الموسيقى من YouTube و YouTube Music بدون إعلانات وتتبُّع. يوفر SimpMusic العديد من الميزات:\n\n - بث الموسيقى \n - مزامنة كلمات الاغاني\n - تخصيص البيانات\n - إلخ… \n\nSimpMusic مجاني دائمًا وبدون إعلانات\n\nتم تطويره بـ ❤️ بواسطة Tuan Minh Nguyen Duc (maxrave-dev) متعقب المشاكل diff --git a/composeApp/src/commonMain/composeResources/values-az/strings.xml b/composeApp/src/commonMain/composeResources/values-az/strings.xml index a3fe1205c..5be24c5d3 100644 --- a/composeApp/src/commonMain/composeResources/values-az/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-az/strings.xml @@ -256,6 +256,12 @@ Boş yer Tövsiyə Təkrar yüklə + Başlıq (A-Z) + Başlıq (Z-A) + Ən yenilər əvvəl + Ən köhnələr əvvəl + Ifacı (A-Z) + Ifacı (Z-A) Əlavə edilmə tarixi Geri çıxış üçün YouTube Music işlədən sadə musiqi tətbiqi \n\nSimpMusic reklam və izləmə olmadan YouTube və YouTube Music-dən musiqi yayımlamaq üçün açıq mənbə layihədir. SimpMusic bir çox xüsusiyyətləri təmin edir:\n\n - Musiqi yayımı\n - Sinxronlaşdırılan lirika\n - Məlumatları fərdiləşdir\n - və s... \n\nSimpMusic həmişə pulsuzdur və reklam yoxdur\n\nTuan Minh Nguyen Duc (maxrave-dev) ❤️- ilə qurur Problem İzləyici diff --git a/composeApp/src/commonMain/composeResources/values-bg/strings.xml b/composeApp/src/commonMain/composeResources/values-bg/strings.xml index 8fc34dad7..08133cd7d 100644 --- a/composeApp/src/commonMain/composeResources/values-bg/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-bg/strings.xml @@ -256,6 +256,12 @@ Свободно пространство Предложение Презареди + Заглавие (А-Я) + Заглавие (Я-А) + Най-нови първо + Най-стари първо + Изпълнител (А-Я) + Изпълнител (Я-А) Дата на добавяне Опростено приложение за музика, което използва YouTube Music за бекенд \n\nSimpMusic е проект с отворен код, който стриймва музика от YouTube и YouTube Music без реклами и проследяване. SimpMusic идва с много функции:\n\n - Стрийминг \n - Текстове\n - Персонализиране на данните\n - и т. н… \n\nSimpMusic е безплатен и без реклами\n\nBuild с ❤️ от Tuan Minh Nguyen Duc (maxrave-dev) Тракър на задачи diff --git a/composeApp/src/commonMain/composeResources/values-ca/strings.xml b/composeApp/src/commonMain/composeResources/values-ca/strings.xml index 7c77ec9eb..f315ea61d 100644 --- a/composeApp/src/commonMain/composeResources/values-ca/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-ca/strings.xml @@ -265,6 +265,12 @@ Ordena Més antics primer Més nous primer + Títol (A-Z) + Títol (Z-A) + Més nous primer + Més antics primer + Artista (A-Z) + Artista (Z-A) Data afegida Una aplicació de música senzilla que utilitza YouTube Music per al backend \n\nSimpMusic és un projecte de codi obert per reproduir música de YouTube i YouTube Music sense anuncis ni seguiment. SimpMusic ofereix moltes funcions:\n\n - Reproducció de música en temps real \n - Lletres sincronitzades\n - Personalització de dades\n - etc... \n\nSimpMusic sempre és gratuït i sense anuncis\n\nCrea amb ❤️ de Tuan Minh Nguyen Duc (maxrave -dev) Detector de problemes diff --git a/composeApp/src/commonMain/composeResources/values-de/strings.xml b/composeApp/src/commonMain/composeResources/values-de/strings.xml index c2395415f..65c7d4d12 100644 --- a/composeApp/src/commonMain/composeResources/values-de/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-de/strings.xml @@ -270,6 +270,12 @@ Sortieren Ältere zuerst Neue zuerst + Titel (A-Z) + Titel (Z-A) + Neueste zuerst + Älteste zuerst + Künstler (A-Z) + Künstler (Z-A) Datum hinzugefügt Eine einfache Musik-App mit YouTube Music als Backend \n\nSimpMusic ist ein Open-Source-Projekt zum Streamen von Musik von YouTube und YouTube Music ohne Werbung und Tracking. SimpMusic bietet viele Funktionen:\n\n - Musik streamen \n - Synchronisierte Liedtexte\n - Daten personalisieren\n - etc… \n\nSimpMusic ist immer kostenlos und ohne Werbung\n\nErstellen Sie mit ❤️ von Tuan Minh Nguyen Duc (maxrave-dev) Issue-Tracker diff --git a/composeApp/src/commonMain/composeResources/values-es/strings.xml b/composeApp/src/commonMain/composeResources/values-es/strings.xml index 96eeb2709..dad95f1f6 100644 --- a/composeApp/src/commonMain/composeResources/values-es/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-es/strings.xml @@ -271,6 +271,12 @@ Clasificar Primero antiguo Primero nuevo + Título (A-Z) + Título (Z-A) + Más recientes primero + Más antiguos primero + Artista (A-Z) + Artista (Z-A) Fecha añadida Una aplicación de música sencilla que utiliza YouTube Music como backend \n\nSimpMusic es un proyecto de código abierto para transmitir música desde YouTube y YouTube Music sin anuncios ni seguimiento. SimpMusic ofrece muchas funciones:\n\n - Transmisión de música \n - Letras sincronizadas\n - Personalizar datos\n - etc... \n\nSimpMusic es siempre gratuito y sin anuncios\n\nCompilado con ❤️ de Tuan Minh Nguyen Duc (maxrave-dev) Rastreador de errores diff --git a/composeApp/src/commonMain/composeResources/values-fa/strings.xml b/composeApp/src/commonMain/composeResources/values-fa/strings.xml index 58165c5f3..54f435dc1 100644 --- a/composeApp/src/commonMain/composeResources/values-fa/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-fa/strings.xml @@ -279,6 +279,12 @@ ترتیب ابتدا قدیمی تر ابتدا جدیدتر + عنوان (الف-ی) + عنوان (ی-الف) + جدیدترین ابتدا + قدیمی‌ترین ابتدا + هنرمند (الف-ی) + هنرمند (ی-الف) تاریخ افزوده شدن یک برنامه موسیقی ساده که از هسته یوتیوب موزیک بهره میبرد\n\nSimpMusic یک پروژه متن‌باز برای پخش موسیقی از یوتیوب و یوتیوب موزیک بدون آگهی و ردیابی است. SimpMusic ویژگی‌های زیادی ارائه می‌دهد:\n\n - پخش موسیقی \n - متن ترانه همگام‌سازی شده\n - داده‌های شخصی‌سازی شده\n - و غیره… \n\nSimpMusic همیشه رایگان و بدون آگهی است\n\nساخته شده با ❤️ از Tuan Minh Nguyen Duc (maxrave-dev) ردیاب مشکلات diff --git a/composeApp/src/commonMain/composeResources/values-fi/strings.xml b/composeApp/src/commonMain/composeResources/values-fi/strings.xml index 870ed5ab7..6b1516794 100644 --- a/composeApp/src/commonMain/composeResources/values-fi/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-fi/strings.xml @@ -256,6 +256,12 @@ Ehdotus Lataa uudelleen Uusimmat ensin + Otsikko (A-Ö) + Otsikko (Ö-A) + Uusimmat ensin + Vanhimmat ensin + Artisti (A-Ö) + Artisti (Ö-A) Lisäyspäivämäärä Yksinkertainen musiikkisovellus, joka käyttää YouTube Musicin taustajärjestelmää \n\nSimpMusic on avoimen lähdekoodin projekti musiikin suoratoistoon YouTubesta ja YouTube Musicista ilman mainoksia ja seurantaa. SimpMusic tarjoaa monia ominaisuuksia:\n\n - Musiikin suoratoisto \n - Synkronoidut sanoitukset\n - Tietojen mukauttaminen\n - jne… \n\nSimpMusic on aina ilmainen ja vailla mainoksia\n\nTehty ❤️:llä, Tuan Minh Nguyen Duc (maxrave-dev) Tikettijärjestelmä diff --git a/composeApp/src/commonMain/composeResources/values-fr/strings.xml b/composeApp/src/commonMain/composeResources/values-fr/strings.xml index 7d9cd08c7..9acf3b067 100644 --- a/composeApp/src/commonMain/composeResources/values-fr/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-fr/strings.xml @@ -271,6 +271,12 @@ Trier Les plus anciens d'abord Les plus récents d'abord + Titre (A-Z) + Titre (Z-A) + Plus récents d'abord + Plus anciens d'abord + Artiste (A-Z) + Artiste (Z-A) Date d'ajout Une application musicale simple utilisant YouTube Music pour le backend \n\nSimpMusic est un projet open source pour diffuser de la musique à partir de YouTube et YouTube Music sans publicité ni suivi. SimpMusic offre de nombreuses fonctionnalités :\n\n - Streaming de musique \n - Paroles synchronisées\n - Personnaliser les données\n - etc… \n\nSimpMusic est toujours gratuit et sans publicité\n\nConstruit avec ❤️ de Tuan Minh Nguyen Duc (maxrave-dev) Suivi des problèmes diff --git a/composeApp/src/commonMain/composeResources/values-hi/strings.xml b/composeApp/src/commonMain/composeResources/values-hi/strings.xml index b78faa8a4..bf419373d 100644 --- a/composeApp/src/commonMain/composeResources/values-hi/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-hi/strings.xml @@ -266,6 +266,12 @@ क्रम से लगाएं पुराने पहले नए पहले + शीर्षक (A-Z) + शीर्षक (Z-A) + नवीनतम पहले + सबसे पुराने पहले + कलाकार (A-Z) + कलाकार (Z-A) जोड़ने की तिथि बैकएंड के लिए YouTube Music का उपयोग करने वाला एक सरल संगीत ऐप \n\nSimpMusic YouTube और YouTube Music से बिना विज्ञापनों और ट्रैकिंग के संगीत स्ट्रीम करने के लिए एक ओपन सोर्स प्रोजेक्ट है। SimpMusic कई सुविधाएं प्रदान करता है:\n\n - संगीत स्ट्रीमिंग \n - सिंक किए गए गाने के बोल\n - व्यक्तिगत डेटा\n - और बहुत कुछ… \n\nSimpMusic हमेशा मुफ्त और बिना विज्ञापनों के है\n\nतुआन मिन्ह न्गुयेन डक (maxrave-dev) द्वारा ❤️ से बनाया गया समस्या ट्रैकर diff --git a/composeApp/src/commonMain/composeResources/values-in/strings.xml b/composeApp/src/commonMain/composeResources/values-in/strings.xml index 7c9e7cc28..4914a59a0 100644 --- a/composeApp/src/commonMain/composeResources/values-in/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-in/strings.xml @@ -258,6 +258,12 @@ Ruang kosong Saran Memuat ulang + Judul (A-Z) + Judul (Z-A) + Terbaru dulu + Terlama dulu + Artis (A-Z) + Artis (Z-A) Tanggal ditambahkan Aplikasi musik sederhana yang menggunakan YouTube Music untuk backend \n\nSimpMusic adalah proyek sumber terbuka untuk streaming musik dari YouTube dan YouTube Music tanpa iklan dan pelacakan. SimpMusic menyediakan banyak fitur:\n\n - Streaming musik\n - Lirik yang disinkronkan\n - Personalisasi data\n - dll... \n\nSimpMusic selalu gratis dan tidak ada iklan\n\nDibangun dengan ❤️ oleh Tuan Minh Nguyen Duc (maxrave-dev) Pelacak Masalah diff --git a/composeApp/src/commonMain/composeResources/values-it/strings.xml b/composeApp/src/commonMain/composeResources/values-it/strings.xml index 9d8293129..1eba4bddb 100644 --- a/composeApp/src/commonMain/composeResources/values-it/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-it/strings.xml @@ -270,6 +270,12 @@ Ordina Prima Più Recente + Titolo (A-Z) + Titolo (Z-A) + Più recenti prima + Più vecchi prima + Artista (A-Z) + Artista (Z-A) Data aggiunta Una semplice app musicale che utilizza YouTube Music come backend \n\nSimpMusic è un progetto open source per lo streaming di musica da YouTube e YouTube Music senza pubblicità e tracciamento. SimpMusic offre molte funzionalità:\n\n - Musica in streaming\n - Testi sincronizzati\n - Personalizza dati\n - ecc... \n\nSimpMusic è sempre gratuito e senza pubblicità\n\nCrea con ❤️ di Tuan Minh Nguyen Duc (maxrave-dev) Tracker dei problemi diff --git a/composeApp/src/commonMain/composeResources/values-iw/strings.xml b/composeApp/src/commonMain/composeResources/values-iw/strings.xml index 100be0375..4bcc96686 100644 --- a/composeApp/src/commonMain/composeResources/values-iw/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-iw/strings.xml @@ -258,6 +258,12 @@ מקום פנוי הצעה טען מחדש + כותרת (א-ת) + כותרת (ת-א) + חדשים ראשון + ישנים ראשון + אמן (א-ת) + אמן (ת-א) נוסף תאריך אפליקציית מוזיקה פשוטה באמצעות YouTube Music עבור backend \n\nSimpMusic הוא פרויקט קוד פתוח להזרמת מוזיקה מ-YouTube ו-YouTube Music ללא מודעות ומעקב. SimpMusic מספקת תכונות רבות:\n\n - הזרמת מוזיקה \n - מילים מסונכרנות\n - התאמה אישית של נתונים\n - אקו… \n\n SimpMusic תמיד בחינם וללא פרסומות\n\nבונה מ ❤️מ-Tuan Minh Nguyen Duc (maxrave-dev) diff --git a/composeApp/src/commonMain/composeResources/values-ja/strings.xml b/composeApp/src/commonMain/composeResources/values-ja/strings.xml index 370a592bb..52dc92116 100644 --- a/composeApp/src/commonMain/composeResources/values-ja/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-ja/strings.xml @@ -268,6 +268,12 @@ 並び替え 古い順 新しい順 + タイトル (A-Z) + タイトル (Z-A) + 新しい順 + 古い順 + アーティスト (A-Z) + アーティスト (Z-A) 追加日 バックエンドに YouTube ミュージックを使用するシンプルな音楽アプリ \n\nSimpMusic は、広告や追跡なしで YouTube や YouTube ミュージックから音楽をストリーミングするオープンソースのプロジェクトです。SimpMusic の豊富な機能:\n\n - 音楽のストリーミング \n - 歌詞の同期\n - あなたに最適化されたデータ\n - など... \n\nSimpMusic は常に無料、そして広告もなしです。\n\nTuan Minh Nguyen Duc (maxrave) が❤️を込めて開発しました 既知の問題一覧 diff --git a/composeApp/src/commonMain/composeResources/values-ko/strings.xml b/composeApp/src/commonMain/composeResources/values-ko/strings.xml index 7a29b92b7..45ba8c46d 100644 --- a/composeApp/src/commonMain/composeResources/values-ko/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-ko/strings.xml @@ -266,6 +266,12 @@ 정렬 날짜순 최신순 + 제목 (가-하) + 제목 (하-가) + 최신순 + 오래된순 + 아티스트 (가-하) + 아티스트 (하-가) 추가한 날짜 YouTube Music을 백엔드로 사용하는 간단한 음악 앱 \n\nSimpMusic은 광고나 추적 없이 YouTube나 YouTube Music에서 음악을 스트리밍하는 오픈 소스 프로젝트입니다. SimpMusic에서는 다양한 기능들을 이용할 수 있습니다.\n\n - 음악 스트리밍 \n - 가사 싱크\n - 데이터 개인화\n - 기타… \n\nSimpMusic은 항상 무료이며 광고도 없습니다\n\nTuan Minh Nguyen Duc (maxrave-dev)가 ❤️을 담아 만듦 이슈 트래커 diff --git a/composeApp/src/commonMain/composeResources/values-nl/strings.xml b/composeApp/src/commonMain/composeResources/values-nl/strings.xml index 58b435e9e..66f72e01c 100644 --- a/composeApp/src/commonMain/composeResources/values-nl/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-nl/strings.xml @@ -212,4 +212,10 @@ SimpMusic maakt automatisch een YouTube-afspeellijst aan. Weet je het zeker? Synchroniseren "Deze afspeellijst de-synchroniseren? Deze afspeellijst zal niet verwijderd worden van YouTube Muziek " + Titel (A-Z) + Titel (Z-A) + Nieuwste eerst + Oudste eerst + Artiest (A-Z) + Artiest (Z-A) diff --git a/composeApp/src/commonMain/composeResources/values-pl/strings.xml b/composeApp/src/commonMain/composeResources/values-pl/strings.xml index ddbccc198..595a51c9b 100644 --- a/composeApp/src/commonMain/composeResources/values-pl/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-pl/strings.xml @@ -255,6 +255,12 @@ Wolne miejsce Sugestia Przeładować + Tytuł (A-Z) + Tytuł (Z-A) + Najnowsze najpierw + Najstarsze najpierw + Artysta (A-Z) + Artysta (Z-A) Dodana data Prosta aplikacja muzyczna wykorzystująca YouTube Music jako zaplecze \n\nSimpMusic to projekt typu open source umożliwiający strumieniowe przesyłanie muzyki z YouTube i YouTube Music bez reklam i śledzenia. SimpMusic zapewnia wiele funkcji:\n\n – Przesyłanie strumieniowe muzyki \n – Zsynchronizowane teksty\n – Personalizacja danych\n – itp… \n\nSimpMusic jest zawsze darmowy i nie zawiera reklam\n\nTwórz za pomocą ❤️ od Tuan Minh Nguyen Duc (maxrave-dev) Lista problemow diff --git a/composeApp/src/commonMain/composeResources/values-pt/strings.xml b/composeApp/src/commonMain/composeResources/values-pt/strings.xml index 6837b651d..8509d8894 100644 --- a/composeApp/src/commonMain/composeResources/values-pt/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-pt/strings.xml @@ -267,6 +267,12 @@ Ordenar Mais Antigas Primeiro Mais Recentes Primeiro + Título (A-Z) + Título (Z-A) + Mais recentes primeiro + Mais antigos primeiro + Artista (A-Z) + Artista (Z-A) Data adicionada A simple music app using YouTube Music for backend \n\nSimpMusic is an open source project to stream music from YouTube and YouTube Music without ads and tracking. SimpMusic provides many features:\n\n - Streaming music \n - Synced lyrics\n - Personalize data\n - etc… \n\nSimpMusic is always free and no ads\n\nBuild with ❤️ from Tuan Minh Nguyen Duc (maxrave-dev) Rastreador de problemas diff --git a/composeApp/src/commonMain/composeResources/values-ru/strings.xml b/composeApp/src/commonMain/composeResources/values-ru/strings.xml index b92746b0d..cd8bf1615 100644 --- a/composeApp/src/commonMain/composeResources/values-ru/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-ru/strings.xml @@ -270,6 +270,12 @@ Сортировать Сначала старые Сначала новые + Название (А-Я) + Название (Я-А) + Сначала новые + Сначала старые + Исполнитель (А-Я) + Исполнитель (Я-А) Дата добавления Простое музыкальное приложение, использующее YouTube Music в качестве серверной части. \n\nSimpMusic — это проект с открытым исходным кодом для потоковой передачи музыки с YouTube и YouTube Music без рекламы и отслеживания. SimpMusic предоставляет множество функций:\n\n - Потоковое воспроизведение музыки \n - Синхронизированные тексты песен\n - Персонализация данных\n - и т. д. \n\nSimpMusic бесплатен и без рекламы\n\nСоздал с ❤️ Туан Минь Нгуен Дык (maxrave-dev) Трекер проблем diff --git a/composeApp/src/commonMain/composeResources/values-th/strings.xml b/composeApp/src/commonMain/composeResources/values-th/strings.xml index d066e05dd..466cc99eb 100644 --- a/composeApp/src/commonMain/composeResources/values-th/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-th/strings.xml @@ -266,6 +266,12 @@ เรียงลำดับ เรียงจากเก่าไปใหม่ เรียงจากใหม่ไปเก่า + ชื่อเพลง (ก-ฮ) + ชื่อเพลง (ฮ-ก) + ใหม่สุดก่อน + เก่าสุดก่อน + ศิลปิน (ก-ฮ) + ศิลปิน (ฮ-ก) เพิ่มวันที่ แอปเพลงง่ายๆ ที่ใช้ YouTube Music สำหรับแบ็กเอนด์ \n\nSimpMusic เป็นโครงการโอเพ่นซอร์สสำหรับสตรีมเพลงจาก YouTube และ YouTube Music โดยไม่มีโฆษณาและการติดตาม SimpMusic มีคุณสมบัติมากมาย:\n\n - สตรีมเพลง \n - เนื้อเพลงที่ซิงค์กัน\n - ปรับแต่งข้อมูล\n - ฯลฯ…\n\nSimpMusic ฟรีเสมอและไม่มีโฆษณา\n\nสร้างด้วย ❤️ จาก Tuan Minh Nguyen Duc (maxrave-dev) ติดตามปัญหา diff --git a/composeApp/src/commonMain/composeResources/values-tr/strings.xml b/composeApp/src/commonMain/composeResources/values-tr/strings.xml index da0d7a60d..712f1f229 100644 --- a/composeApp/src/commonMain/composeResources/values-tr/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-tr/strings.xml @@ -266,6 +266,12 @@ Sırala Eskiden Yeniye Yeniden Eskiye + Başlık (A-Z) + Başlık (Z-A) + En yeniler önce + En eskiler önce + Sanatçı (A-Z) + Sanatçı (Z-A) Tarih eklendi Arka uç olarak YouTube Music'i kullanan basit bir müzik uygulaması \n\nSimpMusic, reklamlar ve izleme olmadan YouTube ve YouTube Music'ten müzik akışı sağlayan açık kaynaklı bir projedir. SimpMusic birçok özellik sunar:\n\n - Müzik akışı \n - Senkronize şarkı sözleri\n - Verileri kişiselleştirin\n - vb… \n\nSimpMusic her zaman ücretsizdir ve reklam içermez\n\nTuan Minh Nguyen Duc'tan ❤️ ile oluşturun (maxrave-dev) Sorun Takibi diff --git a/composeApp/src/commonMain/composeResources/values-uk/strings.xml b/composeApp/src/commonMain/composeResources/values-uk/strings.xml index 82224ca80..e4b66eabe 100644 --- a/composeApp/src/commonMain/composeResources/values-uk/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-uk/strings.xml @@ -270,6 +270,12 @@ Упорядкувати Спочатку старіші Спочатку новіші + Назва (А-Я) + Назва (Я-А) + Спочатку новіші + Спочатку старіші + Виконавець (А-Я) + Виконавець (Я-А) Дата додавання Простий музичний застосунок з використанням YouTube Music для бекенду \n\nSimpMusic - це проєкт з відкритим вихідним кодом для потокової передачі музики з YouTube та YouTube Music без реклами та відстеження. SimpMusic надає багато можливостей:\n\n - Потокова музика\n - Синхронізовані тексти\n - Персоналізація даних\n - тощо... \n\nSimpMusic завжди безкоштовний і без реклами\n\nСтворено з ❤️ від Tuan Minh Nguyen Duc (maxrave-dev) Відстежувач випусків diff --git a/composeApp/src/commonMain/composeResources/values-vi/strings.xml b/composeApp/src/commonMain/composeResources/values-vi/strings.xml index 53c8fd5bc..38d779208 100644 --- a/composeApp/src/commonMain/composeResources/values-vi/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-vi/strings.xml @@ -271,6 +271,12 @@ Sắp xếp Cũ trước Mới trước + Tên bài hát (A-Z) + Tên bài hát (Z-A) + Mới nhất trước + Cũ nhất trước + Nghệ sĩ (A-Z) + Nghệ sĩ (Z-A) Ngày thêm vào Một ứng dụng âm nhạc đơn giản sử dụng dữ liệu từ YouTube Music \n\nSimpMusic là một dự án nguồn mở để phát nhạc từ YouTube và YouTube Music mà không bị quảng cáo và theo dõi. SimpMusic cung cấp nhiều tính năng:\n\n - Phát nhạc \n - Lời bài hát được đồng bộ hóa\n - Cá nhân hóa dữ liệu\n - v.v… \n\nSimpMusic luôn miễn phí và không có quảng cáo\n\nXây dựng với ❤️ bởi Nguyên Đức Tuấn Minh (maxrave-dev) Theo dõi lỗi diff --git a/composeApp/src/commonMain/composeResources/values-zh-rTW/strings.xml b/composeApp/src/commonMain/composeResources/values-zh-rTW/strings.xml index 675f51ef5..d3c7910c0 100644 --- a/composeApp/src/commonMain/composeResources/values-zh-rTW/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-zh-rTW/strings.xml @@ -268,6 +268,12 @@ 排序 由舊至新 最新作品 + 標題 (A-Z) + 標題 (Z-A) + 最新優先 + 最舊優先 + 藝人 (A-Z) + 藝人 (Z-A) 已添加日期 SimpMusic是一個開放原始碼、零廣告、使用YouTube Music作為後端的簡單音樂應用程式 \n\n用於從YouTube和YouTube Music串流音樂。SimpMusic提供許多功能:\n\n - 串流音樂\n - 同步歌詞\n - 個人化資料\n - 等等... \n\nSimpMusic總是免費,無廣告\n\n由Tuan Minh Nguyen Duc (maxrave) 開發 ❤️ 回報問題 diff --git a/composeApp/src/commonMain/composeResources/values-zh/strings.xml b/composeApp/src/commonMain/composeResources/values-zh/strings.xml index 3b07c7658..edd3f2c15 100644 --- a/composeApp/src/commonMain/composeResources/values-zh/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-zh/strings.xml @@ -270,6 +270,12 @@ 排序方式 较早的优先 较新的在前 + 标题 (A-Z) + 标题 (Z-A) + 最新优先 + 最旧优先 + 艺术家 (A-Z) + 艺术家 (Z-A) 已添加日期 使用 YouTube Music 作为后端的简单音乐应用 \n\nSimpMusic 是一个开源项目,可以在没有广告和跟踪的情况下从 YouTube 和 YouTube Music 中流式播放音乐。SimpMusic 提供许多功能:\n\n - 流媒体音乐 \n - 同步歌词 \n - 个性化数据 \n -等等… \n\nSimpMusic始终是免费的,没有广告\n\n Tuan Minh Nguyen Duc(Maxrave-dev) 用 ❤️ 构建 问题跟踪 diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml index f112195ea..04c794b5a 100644 --- a/composeApp/src/commonMain/composeResources/values/strings.xml +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -273,6 +273,12 @@ Sort Older First Newer First + Title (A-Z) + Title (Z-A) + Newest first + Oldest first + Artist (A-Z) + Artist (Z-A) Added date A simple music app using YouTube Music for backend \n\nSimpMusic is an open source project to stream music from YouTube and YouTube Music without ads and tracking. SimpMusic provides many features:\n\n - Streaming music \n - Synced lyrics\n - Personalize data\n - etc… \n\nSimpMusic is always free and no ads\n\nBuild with ❤️ from Tuan Minh Nguyen Duc (maxrave-dev) Issue Tracker diff --git a/composeApp/src/commonMain/kotlin/com/maxrave/simpmusic/extension/AllExt.kt b/composeApp/src/commonMain/kotlin/com/maxrave/simpmusic/extension/AllExt.kt index 1cc2b1e58..13721cd5a 100644 --- a/composeApp/src/commonMain/kotlin/com/maxrave/simpmusic/extension/AllExt.kt +++ b/composeApp/src/commonMain/kotlin/com/maxrave/simpmusic/extension/AllExt.kt @@ -6,6 +6,7 @@ import com.maxrave.domain.data.model.browse.artist.ArtistBrowse import com.maxrave.domain.extension.now import com.maxrave.domain.utils.FilterState import com.maxrave.domain.utils.toTrack +import com.maxrave.simpmusic.ui.screen.library.DownloadSortType import com.maxrave.simpmusic.viewModel.ArtistScreenData import kotlinx.datetime.LocalDateTime import kotlinx.datetime.TimeZone @@ -225,6 +226,16 @@ fun FilterState.displayNameRes(): StringResource = FilterState.CustomOrder -> Res.string.custom_order } +fun DownloadSortType.displayNameRes(): StringResource = + when (this) { + DownloadSortType.TitleAsc -> Res.string.download_sort_title_asc + DownloadSortType.TitleDesc -> Res.string.download_sort_title_desc + DownloadSortType.DateNewest -> Res.string.download_sort_date_newest + DownloadSortType.DateOldest -> Res.string.download_sort_date_oldest + DownloadSortType.ArtistAsc -> Res.string.download_sort_artist_asc + DownloadSortType.ArtistDesc -> Res.string.download_sort_artist_desc + } + @Composable fun String?.ifNullOrEmpty(defaultValue: @Composable () -> String): String = if (isNullOrEmpty()) defaultValue() else this diff --git a/composeApp/src/commonMain/kotlin/com/maxrave/simpmusic/ui/component/ModalBottomSheet.kt b/composeApp/src/commonMain/kotlin/com/maxrave/simpmusic/ui/component/ModalBottomSheet.kt index 8b55115ab..5a5949248 100644 --- a/composeApp/src/commonMain/kotlin/com/maxrave/simpmusic/ui/component/ModalBottomSheet.kt +++ b/composeApp/src/commonMain/kotlin/com/maxrave/simpmusic/ui/component/ModalBottomSheet.kt @@ -133,6 +133,7 @@ import com.maxrave.simpmusic.extension.displayNameRes import com.maxrave.simpmusic.extension.greyScale import com.maxrave.simpmusic.ui.navigation.destination.list.AlbumDestination import com.maxrave.simpmusic.ui.navigation.destination.list.ArtistDestination +import com.maxrave.simpmusic.ui.screen.library.DownloadSortType import com.maxrave.simpmusic.ui.theme.seed import com.maxrave.simpmusic.ui.theme.typo import com.maxrave.simpmusic.ui.theme.white @@ -3173,4 +3174,100 @@ sealed class DevLogInType { is YouTube -> getString(Res.string.your_youtube_cookie) is Discord -> getString(Res.string.your_discord_token) } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SortDownloadsBottomSheet( + selectedState: DownloadSortType, + onDismiss: () -> Unit, + onSortChanged: (DownloadSortType) -> Unit, +) { + val modelBottomSheetState = + rememberModalBottomSheetState(skipPartiallyExpanded = true) + + val sortOptions = + remember { + DownloadSortType.entries + } + + ModalBottomSheet( + onDismissRequest = onDismiss, + sheetState = modelBottomSheetState, + containerColor = Color.Transparent, + contentColor = Color.Transparent, + dragHandle = null, + scrimColor = Color.Black.copy(alpha = .5f), + contentWindowInsets = { WindowInsets(0, 0, 0, 0) }, + ) { + Card( + modifier = + Modifier + .fillMaxWidth() + .wrapContentHeight(), + shape = RoundedCornerShape(topStart = 8.dp, topEnd = 8.dp), + colors = CardDefaults.cardColors().copy(containerColor = Color(0xFF242424)), + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Spacer(modifier = Modifier.height(5.dp)) + Card( + modifier = + Modifier + .width(60.dp) + .height(4.dp), + colors = + CardDefaults.cardColors().copy( + containerColor = Color(0xFF474545), + ), + shape = RoundedCornerShape(50), + ) {} + Text( + stringResource(Res.string.sort_by), + style = typo().labelSmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = + Modifier + .padding(start = 16.dp, top = 16.dp, bottom = 24.dp) + .align(Alignment.Start), + ) + LazyColumn( + contentPadding = PaddingValues(horizontal = 16.dp), + ) { + items(sortOptions, key = { sortOption -> sortOption.hashCode() }) { sortOption -> + val isSelected = sortOption == selectedState + Row( + Modifier + .padding(vertical = 4.dp) + .clickable { + onSortChanged(sortOption) + onDismiss() + }.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = stringResource(sortOption.displayNameRes()), + style = typo().labelMedium, + fontWeight = FontWeight.Medium, + color = if (isSelected) seed else Color.White, + ) + Spacer(modifier = Modifier.weight(1f)) + if (isSelected) { + Image( + painter = painterResource(Res.drawable.done), + contentDescription = "Selected", + colorFilter = ColorFilter.tint(seed), + modifier = Modifier.size(32.dp), + ) + } else { + Spacer(modifier = Modifier.size(32.dp)) + } + } + } + } + EndOfModalBottomSheet() + } + } + } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/maxrave/simpmusic/ui/screen/library/DownloadSortType.kt b/composeApp/src/commonMain/kotlin/com/maxrave/simpmusic/ui/screen/library/DownloadSortType.kt new file mode 100644 index 000000000..9724be99b --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/maxrave/simpmusic/ui/screen/library/DownloadSortType.kt @@ -0,0 +1,55 @@ +package com.maxrave.simpmusic.ui.screen.library + +/** + * Sort types for the Downloaded songs screen. + * Each variant represents a sort criterion + direction. + * Persistence is handled via [toKey]/[fromKey] serialization. + */ +sealed class DownloadSortType { + data object TitleAsc : DownloadSortType() + data object TitleDesc : DownloadSortType() + data object DateNewest : DownloadSortType() + data object DateOldest : DownloadSortType() + data object ArtistAsc : DownloadSortType() + data object ArtistDesc : DownloadSortType() + + fun toKey(): String = + when (this) { + TitleAsc -> KEY_TITLE_ASC + TitleDesc -> KEY_TITLE_DESC + DateNewest -> KEY_DATE_NEWEST + DateOldest -> KEY_DATE_OLDEST + ArtistAsc -> KEY_ARTIST_ASC + ArtistDesc -> KEY_ARTIST_DESC + } + + companion object { + private const val KEY_TITLE_ASC = "title_asc" + private const val KEY_TITLE_DESC = "title_desc" + private const val KEY_DATE_NEWEST = "date_newest" + private const val KEY_DATE_OLDEST = "date_oldest" + private const val KEY_ARTIST_ASC = "artist_asc" + private const val KEY_ARTIST_DESC = "artist_desc" + + /** Preference key used with DataStoreManager */ + const val PREFERENCE_KEY = "download_sort_type" + + /** Default sort when no preference is stored */ + val DEFAULT = DateNewest + + fun fromKey(key: String?): DownloadSortType = + when (key) { + KEY_TITLE_ASC -> TitleAsc + KEY_TITLE_DESC -> TitleDesc + KEY_DATE_NEWEST -> DateNewest + KEY_DATE_OLDEST -> DateOldest + KEY_ARTIST_ASC -> ArtistAsc + KEY_ARTIST_DESC -> ArtistDesc + else -> DEFAULT + } + + /** All available sort options, in display order */ + val entries: List = + listOf(DateNewest, DateOldest, TitleAsc, TitleDesc, ArtistAsc, ArtistDesc) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/maxrave/simpmusic/ui/screen/library/LibraryDynamicPlaylistScreen.kt b/composeApp/src/commonMain/kotlin/com/maxrave/simpmusic/ui/screen/library/LibraryDynamicPlaylistScreen.kt index fe742d5c2..8e608fb0c 100644 --- a/composeApp/src/commonMain/kotlin/com/maxrave/simpmusic/ui/screen/library/LibraryDynamicPlaylistScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/maxrave/simpmusic/ui/screen/library/LibraryDynamicPlaylistScreen.kt @@ -54,6 +54,7 @@ import com.maxrave.simpmusic.ui.component.NowPlayingBottomSheet import com.maxrave.simpmusic.ui.component.PlaylistFullWidthItems import com.maxrave.simpmusic.ui.component.RippleIconButton import com.maxrave.simpmusic.ui.component.SongFullWidthItems +import com.maxrave.simpmusic.ui.component.SortDownloadsBottomSheet import com.maxrave.simpmusic.ui.navigation.destination.list.AlbumDestination import com.maxrave.simpmusic.ui.navigation.destination.list.ArtistDestination import com.maxrave.simpmusic.ui.theme.typo @@ -73,6 +74,7 @@ import simpmusic.composeapp.generated.resources.Res import simpmusic.composeapp.generated.resources.baseline_arrow_back_ios_new_24 import simpmusic.composeapp.generated.resources.baseline_close_24 import simpmusic.composeapp.generated.resources.baseline_search_24 +import simpmusic.composeapp.generated.resources.baseline_sort_24 import simpmusic.composeapp.generated.resources.downloaded import simpmusic.composeapp.generated.resources.favorite import simpmusic.composeapp.generated.resources.followed @@ -100,8 +102,11 @@ fun LibraryDynamicPlaylistScreen( var chosenSong: SongEntity? by remember { mutableStateOf(null) } var showBottomSheet by rememberSaveable { mutableStateOf(false) } var showSearchBar by rememberSaveable { mutableStateOf(false) } + var sortBottomSheetShow by rememberSaveable { mutableStateOf(false) } var query by rememberSaveable { mutableStateOf("") } + val downloadSortType by viewModel.downloadSortType.collectAsStateWithLifecycle() + val favorite by viewModel.listFavoriteSong.collectAsStateWithLifecycle() var tempFavorite by rememberSaveable { mutableStateOf(emptyList()) } val followed by viewModel.listFollowedArtist.collectAsStateWithLifecycle() @@ -378,6 +383,16 @@ fun LibraryDynamicPlaylistScreen( song = chosenSong ?: return, ) } + if (sortBottomSheetShow) { + SortDownloadsBottomSheet( + selectedState = downloadSortType, + onDismiss = { sortBottomSheetShow = false }, + onSortChanged = { + viewModel.setDownloadSort(it) + sortBottomSheetShow = false + }, + ) + } Column( horizontalAlignment = Alignment.CenterHorizontally, ) { @@ -405,6 +420,18 @@ fun LibraryDynamicPlaylistScreen( } }, actions = { + if (LibraryDynamicPlaylistType.toType(type) == LibraryDynamicPlaylistType.Downloaded) { + Box(Modifier.padding(horizontal = 5.dp)) { + RippleIconButton( + Res.drawable.baseline_sort_24, + Modifier + .size(32.dp), + true, + ) { + sortBottomSheetShow = true + } + } + } Box(Modifier.padding(horizontal = 5.dp)) { RippleIconButton( if (showSearchBar) Res.drawable.baseline_close_24 else Res.drawable.baseline_search_24, diff --git a/composeApp/src/commonMain/kotlin/com/maxrave/simpmusic/viewModel/LibraryDynamicPlaylistViewModel.kt b/composeApp/src/commonMain/kotlin/com/maxrave/simpmusic/viewModel/LibraryDynamicPlaylistViewModel.kt index 3d1741578..1fa243847 100644 --- a/composeApp/src/commonMain/kotlin/com/maxrave/simpmusic/viewModel/LibraryDynamicPlaylistViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/maxrave/simpmusic/viewModel/LibraryDynamicPlaylistViewModel.kt @@ -4,18 +4,23 @@ import androidx.lifecycle.viewModelScope import com.maxrave.common.Config import com.maxrave.domain.data.entities.ArtistEntity import com.maxrave.domain.data.entities.SongEntity +import com.maxrave.domain.manager.DataStoreManager import com.maxrave.domain.mediaservice.handler.PlaylistType import com.maxrave.domain.mediaservice.handler.QueueData import com.maxrave.domain.repository.ArtistRepository import com.maxrave.domain.repository.SongRepository import com.maxrave.domain.utils.toArrayListTrack import com.maxrave.domain.utils.toTrack +import com.maxrave.simpmusic.ui.screen.library.DownloadSortType import com.maxrave.simpmusic.ui.screen.library.LibraryDynamicPlaylistType import com.maxrave.simpmusic.viewModel.base.BaseViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch +import org.koin.core.component.inject import simpmusic.composeapp.generated.resources.Res import simpmusic.composeapp.generated.resources.playlist @@ -23,6 +28,8 @@ class LibraryDynamicPlaylistViewModel( private val songRepository: SongRepository, private val artistRepository: ArtistRepository, ) : BaseViewModel() { + private val dataStoreManager: DataStoreManager by inject() + private val _listFavoriteSong: MutableStateFlow> = MutableStateFlow(emptyList()) val listFavoriteSong: StateFlow> get() = _listFavoriteSong @@ -32,6 +39,14 @@ class LibraryDynamicPlaylistViewModel( private val _listMostPlayedSong: MutableStateFlow> = MutableStateFlow(emptyList()) val listMostPlayedSong: StateFlow> get() = _listMostPlayedSong + // Raw downloaded songs from repository (unsorted) + private val _rawDownloadedSongs: MutableStateFlow> = MutableStateFlow(emptyList()) + + // Current download sort preference + private val _downloadSortType: MutableStateFlow = MutableStateFlow(DownloadSortType.DEFAULT) + val downloadSortType: StateFlow get() = _downloadSortType + + // Sorted downloaded songs (combines raw data + sort type) private val _listDownloadedSong: MutableStateFlow> = MutableStateFlow(emptyList()) val listDownloadedSong: StateFlow> get() = _listDownloadedSong @@ -39,7 +54,23 @@ class LibraryDynamicPlaylistViewModel( getFavoriteSong() getFollowedArtist() getMostPlayedSong() + loadDownloadSortPreference() getDownloadedSong() + observeSortedDownloads() + } + + private fun loadDownloadSortPreference() { + viewModelScope.launch { + val savedKey = dataStoreManager.getString(DownloadSortType.PREFERENCE_KEY).first() + _downloadSortType.value = DownloadSortType.fromKey(savedKey) + } + } + + fun setDownloadSort(sortType: DownloadSortType) { + _downloadSortType.value = sortType + viewModelScope.launch { + dataStoreManager.putString(DownloadSortType.PREFERENCE_KEY, sortType.toKey()) + } } private fun getFavoriteSong() { @@ -75,14 +106,38 @@ class LibraryDynamicPlaylistViewModel( private fun getDownloadedSong() { viewModelScope.launch { songRepository.getDownloadedSongs().collectLatest { downloadedSong -> - _listDownloadedSong.value = - (downloadedSong ?: emptyList()).sortedByDescending { - it.downloadedAt ?: it.inLibrary - } + _rawDownloadedSongs.value = downloadedSong ?: emptyList() } } } + /** + * Observes both the raw downloaded songs list and the current sort type, + * automatically re-sorting whenever either changes. + */ + private fun observeSortedDownloads() { + viewModelScope.launch { + combine(_rawDownloadedSongs, _downloadSortType) { songs, sortType -> + applySorting(songs, sortType) + }.collectLatest { sorted -> + _listDownloadedSong.value = sorted + } + } + } + + private fun applySorting( + songs: List, + sortType: DownloadSortType, + ): List = + when (sortType) { + DownloadSortType.TitleAsc -> songs.sortedBy { it.title.lowercase() } + DownloadSortType.TitleDesc -> songs.sortedByDescending { it.title.lowercase() } + DownloadSortType.DateNewest -> songs.sortedByDescending { it.downloadedAt ?: it.inLibrary } + DownloadSortType.DateOldest -> songs.sortedBy { it.downloadedAt ?: it.inLibrary } + DownloadSortType.ArtistAsc -> songs.sortedBy { it.artistName?.firstOrNull()?.lowercase() ?: "" } + DownloadSortType.ArtistDesc -> songs.sortedByDescending { it.artistName?.firstOrNull()?.lowercase() ?: "" } + } + fun playSong( videoId: String, type: LibraryDynamicPlaylistType, diff --git a/composeApp/src/commonTest/kotlin/com/maxrave/simpmusic/ui/screen/library/DownloadSortTypeTest.kt b/composeApp/src/commonTest/kotlin/com/maxrave/simpmusic/ui/screen/library/DownloadSortTypeTest.kt new file mode 100644 index 000000000..abd536041 --- /dev/null +++ b/composeApp/src/commonTest/kotlin/com/maxrave/simpmusic/ui/screen/library/DownloadSortTypeTest.kt @@ -0,0 +1,178 @@ +package com.maxrave.simpmusic.ui.screen.library + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertIs +import kotlin.test.assertNotEquals + +/** + * Unit tests for [DownloadSortType] sealed class. + * + * Covers: + * - Serialization round-trip (toKey ↔ fromKey) + * - Default/fallback behavior + * - Entries list completeness + * - Equality semantics + */ +class DownloadSortTypeTest { + + // ─── toKey / fromKey round-trip ───────────────────────────────── + + @Test + fun titleAsc_roundTrip() { + val key = DownloadSortType.TitleAsc.toKey() + assertEquals("title_asc", key) + assertEquals(DownloadSortType.TitleAsc, DownloadSortType.fromKey(key)) + } + + @Test + fun titleDesc_roundTrip() { + val key = DownloadSortType.TitleDesc.toKey() + assertEquals("title_desc", key) + assertEquals(DownloadSortType.TitleDesc, DownloadSortType.fromKey(key)) + } + + @Test + fun dateNewest_roundTrip() { + val key = DownloadSortType.DateNewest.toKey() + assertEquals("date_newest", key) + assertEquals(DownloadSortType.DateNewest, DownloadSortType.fromKey(key)) + } + + @Test + fun dateOldest_roundTrip() { + val key = DownloadSortType.DateOldest.toKey() + assertEquals("date_oldest", key) + assertEquals(DownloadSortType.DateOldest, DownloadSortType.fromKey(key)) + } + + @Test + fun artistAsc_roundTrip() { + val key = DownloadSortType.ArtistAsc.toKey() + assertEquals("artist_asc", key) + assertEquals(DownloadSortType.ArtistAsc, DownloadSortType.fromKey(key)) + } + + @Test + fun artistDesc_roundTrip() { + val key = DownloadSortType.ArtistDesc.toKey() + assertEquals("artist_desc", key) + assertEquals(DownloadSortType.ArtistDesc, DownloadSortType.fromKey(key)) + } + + // ─── All variants round-trip (parameterized-style) ────────────── + + @Test + fun allVariants_roundTrip() { + DownloadSortType.entries.forEach { sortType -> + val key = sortType.toKey() + val restored = DownloadSortType.fromKey(key) + assertEquals( + sortType, + restored, + "Round-trip failed for $sortType (key=$key)", + ) + } + } + + // ─── fromKey defaults / fallbacks ─────────────────────────────── + + @Test + fun fromKey_null_returnsDefault() { + val result = DownloadSortType.fromKey(null) + assertEquals(DownloadSortType.DEFAULT, result) + assertIs(result) + } + + @Test + fun fromKey_emptyString_returnsDefault() { + assertEquals(DownloadSortType.DEFAULT, DownloadSortType.fromKey("")) + } + + @Test + fun fromKey_unknownKey_returnsDefault() { + assertEquals(DownloadSortType.DEFAULT, DownloadSortType.fromKey("unknown_sort")) + } + + @Test + fun fromKey_caseSensitive_unknownReturnsDefault() { + // Keys should be exact match, not case-insensitive + assertEquals(DownloadSortType.DEFAULT, DownloadSortType.fromKey("Title_Asc")) + assertEquals(DownloadSortType.DEFAULT, DownloadSortType.fromKey("TITLE_ASC")) + } + + // ─── DEFAULT constant ─────────────────────────────────────────── + + @Test + fun default_isDateNewest() { + assertIs(DownloadSortType.DEFAULT) + } + + // ─── entries list ─────────────────────────────────────────────── + + @Test + fun entries_containsAllSixVariants() { + assertEquals(6, DownloadSortType.entries.size) + } + + @Test + fun entries_containsEachVariant() { + val entries = DownloadSortType.entries + assertIs(entries[0]) + assertIs(entries[1]) + assertIs(entries[2]) + assertIs(entries[3]) + assertIs(entries[4]) + assertIs(entries[5]) + } + + @Test + fun entries_hasNoDuplicates() { + val entries = DownloadSortType.entries + assertEquals(entries.size, entries.toSet().size) + } + + // ─── toKey uniqueness ─────────────────────────────────────────── + + @Test + fun allKeys_areUnique() { + val keys = DownloadSortType.entries.map { it.toKey() } + assertEquals(keys.size, keys.toSet().size, "Duplicate keys found: $keys") + } + + @Test + fun allKeys_areNonEmpty() { + DownloadSortType.entries.forEach { sortType -> + val key = sortType.toKey() + assertNotEquals("", key, "Key for $sortType should not be empty") + } + } + + // ─── PREFERENCE_KEY ───────────────────────────────────────────── + + @Test + fun preferenceKey_isNonEmpty() { + assertNotEquals("", DownloadSortType.PREFERENCE_KEY) + } + + @Test + fun preferenceKey_expectedValue() { + assertEquals("download_sort_type", DownloadSortType.PREFERENCE_KEY) + } + + // ─── Equality ─────────────────────────────────────────────────── + + @Test + fun sameVariants_areEqual() { + assertEquals(DownloadSortType.TitleAsc, DownloadSortType.TitleAsc) + assertEquals(DownloadSortType.ArtistDesc, DownloadSortType.ArtistDesc) + } + + @Test + fun differentVariants_areNotEqual() { + assertNotEquals(DownloadSortType.TitleAsc, DownloadSortType.TitleDesc) + assertNotEquals(DownloadSortType.DateNewest, DownloadSortType.DateOldest) + assertNotEquals(DownloadSortType.ArtistAsc, DownloadSortType.ArtistDesc) + assertNotEquals(DownloadSortType.TitleAsc, DownloadSortType.ArtistAsc) + } +}