mirror of
https://github.com/KRTirtho/spotube.git
synced 2026-11-04 10:13:59 +00:00
Compare commits
5 Commits
fa9b42cf10
...
bba725a139
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bba725a139 | ||
|
|
c058074cff | ||
|
|
2aaa0db81e | ||
|
|
14bc279653 | ||
|
|
2c1148a126 |
34
CHANGELOG.md
34
CHANGELOG.md
@ -290,8 +290,8 @@
|
|||||||
- **desktop**: double titlebar in local library folders and massive space in
|
- **desktop**: double titlebar in local library folders and massive space in
|
||||||
overlay player
|
overlay player
|
||||||
- lastfm form broken in other locales #2447
|
- lastfm form broken in other locales #2447
|
||||||
- spotify login broken due to new totp requirement #2494
|
- Greedy Green Corp login broken due to new totp requirement #2494
|
||||||
- spotify authentication 429 errors
|
- Greedy Green Corp authentication 429 errors
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
@ -326,7 +326,7 @@
|
|||||||
- **android**: home widget support (#2148)
|
- **android**: home widget support (#2148)
|
||||||
- rewrite entire app in shadcn-ui replacing material eww
|
- rewrite entire app in shadcn-ui replacing material eww
|
||||||
- grid/list customizable playbutton view
|
- grid/list customizable playbutton view
|
||||||
- flag to hide spotify generated images with patterns
|
- flag to hide Greedy Green Corp generated images with patterns
|
||||||
- show placeholder images where there is no item or on empty page
|
- show placeholder images where there is no item or on empty page
|
||||||
- pause playback when no internet connection
|
- pause playback when no internet connection
|
||||||
- implement yt-dlp for desktop and NewPipeExtractor for Android (#2316)
|
- implement yt-dlp for desktop and NewPipeExtractor for Android (#2316)
|
||||||
@ -552,7 +552,7 @@
|
|||||||
[#1463](https://github.com/team-spotube/spotube/issues/1463)
|
[#1463](https://github.com/team-spotube/spotube/issues/1463)
|
||||||
[#1505](https://github.com/team-spotube/spotube/issues/1505)
|
[#1505](https://github.com/team-spotube/spotube/issues/1505)
|
||||||
([d2683c5](https://github.com/team-spotube/spotube/commit/d2683c52d81d807be6ff72f15b8e9eb18181e211))
|
([d2683c5](https://github.com/team-spotube/spotube/commit/d2683c52d81d807be6ff72f15b8e9eb18181e211))
|
||||||
- spotify friends and user profile icon (mobile) showing when not authenticated
|
- Greedy Green Corp friends and user profile icon (mobile) showing when not authenticated
|
||||||
[#1410](https://github.com/team-spotube/spotube/issues/1410)
|
[#1410](https://github.com/team-spotube/spotube/issues/1410)
|
||||||
([9bccbc9](https://github.com/team-spotube/spotube/commit/9bccbc93c63dd34f6e15ff68c276976ecd1d9a33))
|
([9bccbc9](https://github.com/team-spotube/spotube/commit/9bccbc93c63dd34f6e15ff68c276976ecd1d9a33))
|
||||||
- **updater:** dead link
|
- **updater:** dead link
|
||||||
@ -576,7 +576,7 @@
|
|||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
- add Spotify homepage personalized recommendations
|
- add Greedy Green Corp homepage personalized recommendations
|
||||||
([#1402](https://github.com/team-spotube/spotube/issues/1402))
|
([#1402](https://github.com/team-spotube/spotube/issues/1402))
|
||||||
([9e25c74](https://github.com/team-spotube/spotube/commit/9e25c742d4e43e4e10d2b48afb8e6d90288ffa11))
|
([9e25c74](https://github.com/team-spotube/spotube/commit/9e25c742d4e43e4e10d2b48afb8e6d90288ffa11))
|
||||||
- add user profile page
|
- add user profile page
|
||||||
@ -684,7 +684,7 @@
|
|||||||
|
|
||||||
- add create playlist button in add playlist dialog
|
- add create playlist button in add playlist dialog
|
||||||
([2168a64](https://personal.github.com/team-spotube/spotube/commit/2168a640af3104a43139c303d78e2c2326a1bda7))
|
([2168a64](https://personal.github.com/team-spotube/spotube/commit/2168a640af3104a43139c303d78e2c2326a1bda7))
|
||||||
- add spotify friends activity
|
- add Greedy Green Corp friends activity
|
||||||
([#1130](https://personal.github.com/team-spotube/spotube/issues/1130))
|
([#1130](https://personal.github.com/team-spotube/spotube/issues/1130))
|
||||||
([7983932](https://personal.github.com/team-spotube/spotube/commit/79839329b0970acccb0c566a31eee508adbc8557))
|
([7983932](https://personal.github.com/team-spotube/spotube/commit/79839329b0970acccb0c566a31eee508adbc8557))
|
||||||
- **deep-link:** add track opening page
|
- **deep-link:** add track opening page
|
||||||
@ -1436,7 +1436,7 @@
|
|||||||
([448c9b3](https://github.com/team-spotube/spotube/commit/448c9b39f407668ad92a695afe3c9741baeca20d))
|
([448c9b3](https://github.com/team-spotube/spotube/commit/448c9b39f407668ad92a695afe3c9741baeca20d))
|
||||||
- **macos:** crashing on startup
|
- **macos:** crashing on startup
|
||||||
([c46b428](https://github.com/team-spotube/spotube/commit/c46b4284b1d46a614cbcebc8c2f2e52714921b9b))
|
([c46b428](https://github.com/team-spotube/spotube/commit/c46b4284b1d46a614cbcebc8c2f2e52714921b9b))
|
||||||
- spotify query hooks overriding default query params
|
- Greedy Green Corp query hooks overriding default query params
|
||||||
([ec9a02e](https://github.com/team-spotube/spotube/commit/ec9a02e8b8d988e15ed58027054d2a9090d98873))
|
([ec9a02e](https://github.com/team-spotube/spotube/commit/ec9a02e8b8d988e15ed58027054d2a9090d98873))
|
||||||
|
|
||||||
## [2.7.0](https://github.com/team-spotube/spotube/compare/v2.6.0...v2.7.0) (2023-03-07)
|
## [2.7.0](https://github.com/team-spotube/spotube/compare/v2.6.0...v2.7.0) (2023-03-07)
|
||||||
@ -1469,7 +1469,7 @@
|
|||||||
([9080441](https://github.com/team-spotube/spotube/commit/9080441b875ceb91260bbad79291365a98d5be95))
|
([9080441](https://github.com/team-spotube/spotube/commit/9080441b875ceb91260bbad79291365a98d5be95))
|
||||||
- individual shuffle and repeat/loop button of player
|
- individual shuffle and repeat/loop button of player
|
||||||
([f79223c](https://github.com/team-spotube/spotube/commit/f79223cd41c61d9836d25e7bc2811c6515ba00c8))
|
([f79223c](https://github.com/team-spotube/spotube/commit/f79223cd41c61d9836d25e7bc2811c6515ba00c8))
|
||||||
- **lyrics:** use official spotify API for fetching lyrics and add zoom controls
|
- **lyrics:** use official Greedy Green Corp API for fetching lyrics and add zoom controls
|
||||||
([10d0660](https://github.com/team-spotube/spotube/commit/10d0660972f008df0d11c280b681ce3b78f05d0b))
|
([10d0660](https://github.com/team-spotube/spotube/commit/10d0660972f008df0d11c280b681ce3b78f05d0b))
|
||||||
- **mobile:** pull to refresh support in all refreshable list views
|
- **mobile:** pull to refresh support in all refreshable list views
|
||||||
([9f959ce](https://github.com/team-spotube/spotube/commit/9f959ce77cd95cfc34d01af1f5cf53dd4206b6a6))
|
([9f959ce](https://github.com/team-spotube/spotube/commit/9f959ce77cd95cfc34d01af1f5cf53dd4206b6a6))
|
||||||
@ -1921,7 +1921,7 @@
|
|||||||
|
|
||||||
- private playlists of current user aren't shown fix
|
- private playlists of current user aren't shown fix
|
||||||
(https://github.com/team-spotube/spotube/issues/92)
|
(https://github.com/team-spotube/spotube/issues/92)
|
||||||
- refresh token error causing re-login (culprit: internal lib spotify-dart)
|
- refresh token error causing re-login (culprit: internal lib Greedy Green Corp-dart)
|
||||||
- Typo in Login instructions URL
|
- Typo in Login instructions URL
|
||||||
|
|
||||||
# v2.1.0
|
# v2.1.0
|
||||||
@ -1937,7 +1937,7 @@
|
|||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Spotify track to youtube track algorithm
|
- Greedy Green Corp track to youtube track algorithm
|
||||||
- Genius lyrics matching algorithm
|
- Genius lyrics matching algorithm
|
||||||
- Download track. Checks if already exists & replaces on user command
|
- Download track. Checks if already exists & replaces on user command
|
||||||
- Wide screen responsiveness & adaptation
|
- Wide screen responsiveness & adaptation
|
||||||
@ -1950,7 +1950,7 @@
|
|||||||
(https://github.com/team-spotube/spotube/issues/46)
|
(https://github.com/team-spotube/spotube/issues/46)
|
||||||
- Theme modification state doesn't persist
|
- Theme modification state doesn't persist
|
||||||
(https://github.com/team-spotube/spotube/issues/54)
|
(https://github.com/team-spotube/spotube/issues/54)
|
||||||
- Wrong URI path for "Login with Spotify" tutorial
|
- Wrong URI path for "Login with Greedy Green Corp" tutorial
|
||||||
(https://github.com/team-spotube/spotube/issues/69)
|
(https://github.com/team-spotube/spotube/issues/69)
|
||||||
- Card shadow showing in the background of TitleBar & Searchbar
|
- Card shadow showing in the background of TitleBar & Searchbar
|
||||||
|
|
||||||
@ -2124,17 +2124,17 @@
|
|||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
- `access_token not found` Error after OAuth Login with Spotify credentials
|
- `access_token not found` Error after OAuth Login with Greedy Green Corp credentials
|
||||||
(used to need a restart of the app to load the access_token)
|
(used to need a restart of the app to load the access_token)
|
||||||
- `access_token not found` Error after OAuth Login with Spotify credentials
|
- `access_token not found` Error after OAuth Login with Greedy Green Corp credentials
|
||||||
(used to need a restart of the app to load the access_token)
|
(used to need a restart of the app to load the access_token)
|
||||||
- Volume level wasn't cached even after changing volume
|
- Volume level wasn't cached even after changing volume
|
||||||
|
|
||||||
# v0.0.1
|
# v0.0.1
|
||||||
|
|
||||||
Spotube v0.0.1 - initial release of the open source software for playing Spotify
|
Spotube v0.0.1 - initial release of the open source software for playing Greedy Green Corp
|
||||||
music using Youtube public API
|
music using Youtube public API
|
||||||
Spotube v0.0.1 - initial release of the open source software for playing Spotify
|
Spotube v0.0.1 - initial release of the open source software for playing Greedy Green Corp
|
||||||
music using Youtube public API
|
music using Youtube public API
|
||||||
|
|
||||||
### New
|
### New
|
||||||
@ -2143,7 +2143,7 @@ music using Youtube public API
|
|||||||
- Playback Queue
|
- Playback Queue
|
||||||
- Save to Liked Tracks/Playlists
|
- Save to Liked Tracks/Playlists
|
||||||
- Bypass API rate limitation on basic usage using personal developer Apps for
|
- Bypass API rate limitation on basic usage using personal developer Apps for
|
||||||
spotify API
|
Greedy Green Corp API
|
||||||
- Bypass API rate limitation on basic usage using personal developer Apps for
|
- Bypass API rate limitation on basic usage using personal developer Apps for
|
||||||
spotify API
|
Greedy Green Corp API
|
||||||
- Youtube search & get handled using scrape-yt
|
- Youtube search & get handled using scrape-yt
|
||||||
|
|||||||
@ -82,10 +82,16 @@ class NewPipeDownloader(private val cookieJar: PersistentCookieJar) : Downloader
|
|||||||
companion object {
|
companion object {
|
||||||
private var instance: NewPipeDownloader? = null
|
private var instance: NewPipeDownloader? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safe to call on every Activity creation: the process (and so this singleton) outlives
|
||||||
|
* the Activity, and a second [PersistentCookieJar] would open a second DataStore on the
|
||||||
|
* same cookies file, which DataStore rejects.
|
||||||
|
*/
|
||||||
|
@Synchronized
|
||||||
fun init(paths: Paths) {
|
fun init(paths: Paths) {
|
||||||
|
if (instance != null) return
|
||||||
val cookieJar = PersistentCookieJar(paths)
|
val cookieJar = PersistentCookieJar(paths)
|
||||||
instance = NewPipeDownloader(cookieJar)
|
instance = NewPipeDownloader(cookieJar).also { NewPipe.init(it) }
|
||||||
NewPipe.init(instance)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import android.app.NotificationManager
|
|||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.os.Process
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.annotation.OptIn
|
import androidx.annotation.OptIn
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
@ -61,6 +62,8 @@ import spotube.composeapp.generated.resources.notification_playback_channel_name
|
|||||||
|
|
||||||
@OptIn(UnstableApi::class)
|
@OptIn(UnstableApi::class)
|
||||||
class PlaybackService : MediaLibraryService(), KoinComponent {
|
class PlaybackService : MediaLibraryService(), KoinComponent {
|
||||||
|
private var taskRemoved = false
|
||||||
|
|
||||||
|
|
||||||
private val audioPlayer = inject<AudioPlayerInterface>().value as AudioPlayer
|
private val audioPlayer = inject<AudioPlayerInterface>().value as AudioPlayer
|
||||||
private val audioPlayerQueue: AudioPlayerQueue by inject()
|
private val audioPlayerQueue: AudioPlayerQueue by inject()
|
||||||
@ -161,9 +164,13 @@ class PlaybackService : MediaLibraryService(), KoinComponent {
|
|||||||
removeSession(librarySession)
|
removeSession(librarySession)
|
||||||
librarySession.release()
|
librarySession.release()
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
|
// Swiping the app out of recents should fully close it: Android otherwise keeps this
|
||||||
|
// process cached with the proxy server, MQTT client and every singleton still alive.
|
||||||
|
if (taskRemoved) Process.killProcess(Process.myPid())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTaskRemoved(rootIntent: Intent?) {
|
override fun onTaskRemoved(rootIntent: Intent?) {
|
||||||
|
taskRemoved = true
|
||||||
pauseAllPlayersAndStopSelf()
|
pauseAllPlayersAndStopSelf()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -402,7 +402,7 @@
|
|||||||
<string name="welcome_guide_title">كيفية استخدام Spotube</string>
|
<string name="welcome_guide_title">كيفية استخدام Spotube</string>
|
||||||
<string name="welcome_guide_subtitle">أربع خطوات سريعة وتبدأ الاستماع.</string>
|
<string name="welcome_guide_subtitle">أربع خطوات سريعة وتبدأ الاستماع.</string>
|
||||||
<string name="welcome_guide_signin_title">تسجيل الدخول</string>
|
<string name="welcome_guide_signin_title">تسجيل الدخول</string>
|
||||||
<string name="welcome_guide_signin_desc">اربط حسابك على Spotify، أو اختر إضافة الكتالوج الخاصة بك.</string>
|
<string name="welcome_guide_signin_desc">اربط حسابك، أو اختر إضافة الكتالوج الخاصة بك.</string>
|
||||||
<string name="welcome_guide_search_title">ابحث وشغّل</string>
|
<string name="welcome_guide_search_title">ابحث وشغّل</string>
|
||||||
<string name="welcome_guide_search_desc">اعثر على أي أغنية أو ألبوم أو فنان، ثم اضغط تشغيل.</string>
|
<string name="welcome_guide_search_desc">اعثر على أي أغنية أو ألبوم أو فنان، ثم اضغط تشغيل.</string>
|
||||||
<string name="welcome_guide_offline_title">استمع بلا إنترنت</string>
|
<string name="welcome_guide_offline_title">استمع بلا إنترنت</string>
|
||||||
|
|||||||
@ -386,7 +386,7 @@
|
|||||||
<string name="welcome_guide_title">Spotube যেভাবে ব্যবহার করবেন</string>
|
<string name="welcome_guide_title">Spotube যেভাবে ব্যবহার করবেন</string>
|
||||||
<string name="welcome_guide_subtitle">চারটি সহজ ধাপ, তারপরই গান শুনুন।</string>
|
<string name="welcome_guide_subtitle">চারটি সহজ ধাপ, তারপরই গান শুনুন।</string>
|
||||||
<string name="welcome_guide_signin_title">সাইন ইন</string>
|
<string name="welcome_guide_signin_title">সাইন ইন</string>
|
||||||
<string name="welcome_guide_signin_desc">আপনার Spotify অ্যাকাউন্ট কানেক্ট করুন, অথবা নিজের পছন্দের ক্যাটালগ প্লাগইন বেছে নিন।</string>
|
<string name="welcome_guide_signin_desc">আপনার অ্যাকাউন্ট কানেক্ট করুন, অথবা নিজের পছন্দের ক্যাটালগ প্লাগইন বেছে নিন।</string>
|
||||||
<string name="welcome_guide_search_title">খুঁজুন ও প্লে করুন</string>
|
<string name="welcome_guide_search_title">খুঁজুন ও প্লে করুন</string>
|
||||||
<string name="welcome_guide_search_desc">যেকোনো গান, অ্যালবাম বা শিল্পী খুঁজুন, তারপর প্লে চাপুন।</string>
|
<string name="welcome_guide_search_desc">যেকোনো গান, অ্যালবাম বা শিল্পী খুঁজুন, তারপর প্লে চাপুন।</string>
|
||||||
<string name="welcome_guide_offline_title">অফলাইনে শুনুন</string>
|
<string name="welcome_guide_offline_title">অফলাইনে শুনুন</string>
|
||||||
|
|||||||
@ -386,7 +386,7 @@
|
|||||||
<string name="welcome_guide_title">So nutzt du Spotube</string>
|
<string name="welcome_guide_title">So nutzt du Spotube</string>
|
||||||
<string name="welcome_guide_subtitle">Vier schnelle Schritte und du hörst Musik.</string>
|
<string name="welcome_guide_subtitle">Vier schnelle Schritte und du hörst Musik.</string>
|
||||||
<string name="welcome_guide_signin_title">Anmelden</string>
|
<string name="welcome_guide_signin_title">Anmelden</string>
|
||||||
<string name="welcome_guide_signin_desc">Verbinde dein Spotify-Konto oder wähle ein eigenes Katalog-Plugin.</string>
|
<string name="welcome_guide_signin_desc">Verbinde dein Konto oder wähle ein eigenes Katalog-Plugin.</string>
|
||||||
<string name="welcome_guide_search_title">Suchen und abspielen</string>
|
<string name="welcome_guide_search_title">Suchen und abspielen</string>
|
||||||
<string name="welcome_guide_search_desc">Finde einen Song, ein Album oder einen Künstler und tippe auf Abspielen.</string>
|
<string name="welcome_guide_search_desc">Finde einen Song, ein Album oder einen Künstler und tippe auf Abspielen.</string>
|
||||||
<string name="welcome_guide_offline_title">Offline hören</string>
|
<string name="welcome_guide_offline_title">Offline hören</string>
|
||||||
|
|||||||
@ -390,7 +390,7 @@
|
|||||||
<string name="welcome_guide_title">Cómo usar Spotube</string>
|
<string name="welcome_guide_title">Cómo usar Spotube</string>
|
||||||
<string name="welcome_guide_subtitle">Cuatro pasos rápidos y a escuchar.</string>
|
<string name="welcome_guide_subtitle">Cuatro pasos rápidos y a escuchar.</string>
|
||||||
<string name="welcome_guide_signin_title">Inicia sesión</string>
|
<string name="welcome_guide_signin_title">Inicia sesión</string>
|
||||||
<string name="welcome_guide_signin_desc">Conecta tu cuenta de Spotify o elige tu propio plugin de catálogo.</string>
|
<string name="welcome_guide_signin_desc">Conecta tu cuenta o elige tu propio plugin de catálogo.</string>
|
||||||
<string name="welcome_guide_search_title">Busca y reproduce</string>
|
<string name="welcome_guide_search_title">Busca y reproduce</string>
|
||||||
<string name="welcome_guide_search_desc">Encuentra cualquier canción, álbum o artista y dale al play.</string>
|
<string name="welcome_guide_search_desc">Encuentra cualquier canción, álbum o artista y dale al play.</string>
|
||||||
<string name="welcome_guide_offline_title">Escucha sin conexión</string>
|
<string name="welcome_guide_offline_title">Escucha sin conexión</string>
|
||||||
|
|||||||
@ -386,7 +386,7 @@
|
|||||||
<string name="welcome_guide_title">نحوهٔ استفاده از Spotube</string>
|
<string name="welcome_guide_title">نحوهٔ استفاده از Spotube</string>
|
||||||
<string name="welcome_guide_subtitle">چهار گام سریع و مشغول شنیدن هستید.</string>
|
<string name="welcome_guide_subtitle">چهار گام سریع و مشغول شنیدن هستید.</string>
|
||||||
<string name="welcome_guide_signin_title">ورود</string>
|
<string name="welcome_guide_signin_title">ورود</string>
|
||||||
<string name="welcome_guide_signin_desc">حساب Spotify خود را متصل کنید یا افزونهٔ کاتالوگ دلخواهتان را انتخاب کنید.</string>
|
<string name="welcome_guide_signin_desc">حساب خود را متصل کنید یا افزونهٔ کاتالوگ دلخواهتان را انتخاب کنید.</string>
|
||||||
<string name="welcome_guide_search_title">جستجو و پخش</string>
|
<string name="welcome_guide_search_title">جستجو و پخش</string>
|
||||||
<string name="welcome_guide_search_desc">هر آهنگ، آلبوم یا هنرمندی را پیدا کنید و پخش را بزنید.</string>
|
<string name="welcome_guide_search_desc">هر آهنگ، آلبوم یا هنرمندی را پیدا کنید و پخش را بزنید.</string>
|
||||||
<string name="welcome_guide_offline_title">گوش دادن آفلاین</string>
|
<string name="welcome_guide_offline_title">گوش دادن آفلاین</string>
|
||||||
|
|||||||
@ -390,7 +390,7 @@
|
|||||||
<string name="welcome_guide_title">Comment utiliser Spotube</string>
|
<string name="welcome_guide_title">Comment utiliser Spotube</string>
|
||||||
<string name="welcome_guide_subtitle">Quatre étapes rapides et c\'est parti.</string>
|
<string name="welcome_guide_subtitle">Quatre étapes rapides et c\'est parti.</string>
|
||||||
<string name="welcome_guide_signin_title">Se connecter</string>
|
<string name="welcome_guide_signin_title">Se connecter</string>
|
||||||
<string name="welcome_guide_signin_desc">Connectez votre compte Spotify ou choisissez votre propre plugin de catalogue.</string>
|
<string name="welcome_guide_signin_desc">Connectez votre compte ou choisissez votre propre plugin de catalogue.</string>
|
||||||
<string name="welcome_guide_search_title">Rechercher et écouter</string>
|
<string name="welcome_guide_search_title">Rechercher et écouter</string>
|
||||||
<string name="welcome_guide_search_desc">Trouvez n\'importe quel titre, album ou artiste, puis lancez la lecture.</string>
|
<string name="welcome_guide_search_desc">Trouvez n\'importe quel titre, album ou artiste, puis lancez la lecture.</string>
|
||||||
<string name="welcome_guide_offline_title">Écouter hors connexion</string>
|
<string name="welcome_guide_offline_title">Écouter hors connexion</string>
|
||||||
|
|||||||
@ -385,7 +385,7 @@
|
|||||||
<string name="welcome_guide_title">Spotube कैसे इस्तेमाल करें</string>
|
<string name="welcome_guide_title">Spotube कैसे इस्तेमाल करें</string>
|
||||||
<string name="welcome_guide_subtitle">चार आसान स्टेप और आप सुनना शुरू कर सकते हैं।</string>
|
<string name="welcome_guide_subtitle">चार आसान स्टेप और आप सुनना शुरू कर सकते हैं।</string>
|
||||||
<string name="welcome_guide_signin_title">साइन इन करें</string>
|
<string name="welcome_guide_signin_title">साइन इन करें</string>
|
||||||
<string name="welcome_guide_signin_desc">अपना Spotify अकाउंट कनेक्ट करें, या अपना कैटलॉग प्लगइन चुनें।</string>
|
<string name="welcome_guide_signin_desc">अपना अकाउंट कनेक्ट करें, या अपना कैटलॉग प्लगइन चुनें।</string>
|
||||||
<string name="welcome_guide_search_title">खोजें और चलाएं</string>
|
<string name="welcome_guide_search_title">खोजें और चलाएं</string>
|
||||||
<string name="welcome_guide_search_desc">कोई भी गाना, एल्बम या कलाकार खोजें, फिर प्ले दबाएं।</string>
|
<string name="welcome_guide_search_desc">कोई भी गाना, एल्बम या कलाकार खोजें, फिर प्ले दबाएं।</string>
|
||||||
<string name="welcome_guide_offline_title">ऑफ़लाइन सुनें</string>
|
<string name="welcome_guide_offline_title">ऑफ़लाइन सुनें</string>
|
||||||
|
|||||||
@ -382,7 +382,7 @@
|
|||||||
<string name="welcome_guide_title">Cara menggunakan Spotube</string>
|
<string name="welcome_guide_title">Cara menggunakan Spotube</string>
|
||||||
<string name="welcome_guide_subtitle">Empat langkah singkat dan kamu siap mendengarkan.</string>
|
<string name="welcome_guide_subtitle">Empat langkah singkat dan kamu siap mendengarkan.</string>
|
||||||
<string name="welcome_guide_signin_title">Masuk</string>
|
<string name="welcome_guide_signin_title">Masuk</string>
|
||||||
<string name="welcome_guide_signin_desc">Hubungkan akun Spotify-mu, atau pilih plugin katalogmu sendiri.</string>
|
<string name="welcome_guide_signin_desc">Hubungkan akunmu, atau pilih plugin katalogmu sendiri.</string>
|
||||||
<string name="welcome_guide_search_title">Cari dan putar</string>
|
<string name="welcome_guide_search_title">Cari dan putar</string>
|
||||||
<string name="welcome_guide_search_desc">Temukan lagu, album, atau artis apa pun, lalu tekan putar.</string>
|
<string name="welcome_guide_search_desc">Temukan lagu, album, atau artis apa pun, lalu tekan putar.</string>
|
||||||
<string name="welcome_guide_offline_title">Dengarkan offline</string>
|
<string name="welcome_guide_offline_title">Dengarkan offline</string>
|
||||||
|
|||||||
@ -390,7 +390,7 @@
|
|||||||
<string name="welcome_guide_title">Come usare Spotube</string>
|
<string name="welcome_guide_title">Come usare Spotube</string>
|
||||||
<string name="welcome_guide_subtitle">Quattro rapidi passaggi e sei pronto ad ascoltare.</string>
|
<string name="welcome_guide_subtitle">Quattro rapidi passaggi e sei pronto ad ascoltare.</string>
|
||||||
<string name="welcome_guide_signin_title">Accedi</string>
|
<string name="welcome_guide_signin_title">Accedi</string>
|
||||||
<string name="welcome_guide_signin_desc">Collega il tuo account Spotify o scegli il tuo plugin di catalogo.</string>
|
<string name="welcome_guide_signin_desc">Collega il tuo account o scegli il tuo plugin di catalogo.</string>
|
||||||
<string name="welcome_guide_search_title">Cerca e riproduci</string>
|
<string name="welcome_guide_search_title">Cerca e riproduci</string>
|
||||||
<string name="welcome_guide_search_desc">Trova qualsiasi brano, album o artista, poi premi play.</string>
|
<string name="welcome_guide_search_desc">Trova qualsiasi brano, album o artista, poi premi play.</string>
|
||||||
<string name="welcome_guide_offline_title">Ascolta offline</string>
|
<string name="welcome_guide_offline_title">Ascolta offline</string>
|
||||||
|
|||||||
@ -382,7 +382,7 @@
|
|||||||
<string name="welcome_guide_title">Spotubeの使い方</string>
|
<string name="welcome_guide_title">Spotubeの使い方</string>
|
||||||
<string name="welcome_guide_subtitle">簡単な4ステップで、すぐに聴けます。</string>
|
<string name="welcome_guide_subtitle">簡単な4ステップで、すぐに聴けます。</string>
|
||||||
<string name="welcome_guide_signin_title">サインイン</string>
|
<string name="welcome_guide_signin_title">サインイン</string>
|
||||||
<string name="welcome_guide_signin_desc">Spotifyアカウントを連携するか、お好みのカタログプラグインを選んでください。</string>
|
<string name="welcome_guide_signin_desc">アカウントを連携するか、お好みのカタログプラグインを選んでください。</string>
|
||||||
<string name="welcome_guide_search_title">検索して再生</string>
|
<string name="welcome_guide_search_title">検索して再生</string>
|
||||||
<string name="welcome_guide_search_desc">曲、アルバム、アーティストを探して再生しましょう。</string>
|
<string name="welcome_guide_search_desc">曲、アルバム、アーティストを探して再生しましょう。</string>
|
||||||
<string name="welcome_guide_offline_title">オフラインで聴く</string>
|
<string name="welcome_guide_offline_title">オフラインで聴く</string>
|
||||||
|
|||||||
@ -382,7 +382,7 @@
|
|||||||
<string name="welcome_guide_title">Spotube 사용 방법</string>
|
<string name="welcome_guide_title">Spotube 사용 방법</string>
|
||||||
<string name="welcome_guide_subtitle">간단한 네 단계면 바로 감상할 수 있어요.</string>
|
<string name="welcome_guide_subtitle">간단한 네 단계면 바로 감상할 수 있어요.</string>
|
||||||
<string name="welcome_guide_signin_title">로그인</string>
|
<string name="welcome_guide_signin_title">로그인</string>
|
||||||
<string name="welcome_guide_signin_desc">Spotify 계정을 연결하거나 원하는 카탈로그 플러그인을 선택하세요.</string>
|
<string name="welcome_guide_signin_desc">계정을 연결하거나 원하는 카탈로그 플러그인을 선택하세요.</string>
|
||||||
<string name="welcome_guide_search_title">검색하고 재생하기</string>
|
<string name="welcome_guide_search_title">검색하고 재생하기</string>
|
||||||
<string name="welcome_guide_search_desc">노래, 앨범, 아티스트를 찾은 다음 재생을 누르세요.</string>
|
<string name="welcome_guide_search_desc">노래, 앨범, 아티스트를 찾은 다음 재생을 누르세요.</string>
|
||||||
<string name="welcome_guide_offline_title">오프라인으로 감상하기</string>
|
<string name="welcome_guide_offline_title">오프라인으로 감상하기</string>
|
||||||
|
|||||||
@ -386,7 +386,7 @@
|
|||||||
<string name="welcome_guide_title">Zo gebruik je Spotube</string>
|
<string name="welcome_guide_title">Zo gebruik je Spotube</string>
|
||||||
<string name="welcome_guide_subtitle">Vier snelle stappen en je luistert al.</string>
|
<string name="welcome_guide_subtitle">Vier snelle stappen en je luistert al.</string>
|
||||||
<string name="welcome_guide_signin_title">Inloggen</string>
|
<string name="welcome_guide_signin_title">Inloggen</string>
|
||||||
<string name="welcome_guide_signin_desc">Koppel je Spotify-account of kies je eigen catalogusplug-in.</string>
|
<string name="welcome_guide_signin_desc">Koppel je account of kies je eigen catalogusplug-in.</string>
|
||||||
<string name="welcome_guide_search_title">Zoeken en afspelen</string>
|
<string name="welcome_guide_search_title">Zoeken en afspelen</string>
|
||||||
<string name="welcome_guide_search_desc">Vind elk nummer, album of elke artiest en druk op afspelen.</string>
|
<string name="welcome_guide_search_desc">Vind elk nummer, album of elke artiest en druk op afspelen.</string>
|
||||||
<string name="welcome_guide_offline_title">Offline luisteren</string>
|
<string name="welcome_guide_offline_title">Offline luisteren</string>
|
||||||
|
|||||||
@ -394,7 +394,7 @@
|
|||||||
<string name="welcome_guide_title">Jak korzystać ze Spotube</string>
|
<string name="welcome_guide_title">Jak korzystać ze Spotube</string>
|
||||||
<string name="welcome_guide_subtitle">Cztery szybkie kroki i już słuchasz.</string>
|
<string name="welcome_guide_subtitle">Cztery szybkie kroki i już słuchasz.</string>
|
||||||
<string name="welcome_guide_signin_title">Zaloguj się</string>
|
<string name="welcome_guide_signin_title">Zaloguj się</string>
|
||||||
<string name="welcome_guide_signin_desc">Połącz konto Spotify lub wybierz własną wtyczkę katalogu.</string>
|
<string name="welcome_guide_signin_desc">Połącz konto lub wybierz własną wtyczkę katalogu.</string>
|
||||||
<string name="welcome_guide_search_title">Szukaj i odtwarzaj</string>
|
<string name="welcome_guide_search_title">Szukaj i odtwarzaj</string>
|
||||||
<string name="welcome_guide_search_desc">Znajdź dowolny utwór, album lub wykonawcę i naciśnij Odtwórz.</string>
|
<string name="welcome_guide_search_desc">Znajdź dowolny utwór, album lub wykonawcę i naciśnij Odtwórz.</string>
|
||||||
<string name="welcome_guide_offline_title">Słuchaj offline</string>
|
<string name="welcome_guide_offline_title">Słuchaj offline</string>
|
||||||
|
|||||||
@ -390,7 +390,7 @@
|
|||||||
<string name="welcome_guide_title">Como usar o Spotube</string>
|
<string name="welcome_guide_title">Como usar o Spotube</string>
|
||||||
<string name="welcome_guide_subtitle">Quatro passos rápidos e você já está ouvindo.</string>
|
<string name="welcome_guide_subtitle">Quatro passos rápidos e você já está ouvindo.</string>
|
||||||
<string name="welcome_guide_signin_title">Entrar</string>
|
<string name="welcome_guide_signin_title">Entrar</string>
|
||||||
<string name="welcome_guide_signin_desc">Conecte sua conta do Spotify ou escolha seu próprio plugin de catálogo.</string>
|
<string name="welcome_guide_signin_desc">Conecte sua conta ou escolha seu próprio plugin de catálogo.</string>
|
||||||
<string name="welcome_guide_search_title">Busque e toque</string>
|
<string name="welcome_guide_search_title">Busque e toque</string>
|
||||||
<string name="welcome_guide_search_desc">Encontre qualquer música, álbum ou artista e aperte o play.</string>
|
<string name="welcome_guide_search_desc">Encontre qualquer música, álbum ou artista e aperte o play.</string>
|
||||||
<string name="welcome_guide_offline_title">Ouça offline</string>
|
<string name="welcome_guide_offline_title">Ouça offline</string>
|
||||||
|
|||||||
@ -394,7 +394,7 @@
|
|||||||
<string name="welcome_guide_title">Как пользоваться Spotube</string>
|
<string name="welcome_guide_title">Как пользоваться Spotube</string>
|
||||||
<string name="welcome_guide_subtitle">Четыре простых шага — и музыка уже играет.</string>
|
<string name="welcome_guide_subtitle">Четыре простых шага — и музыка уже играет.</string>
|
||||||
<string name="welcome_guide_signin_title">Вход</string>
|
<string name="welcome_guide_signin_title">Вход</string>
|
||||||
<string name="welcome_guide_signin_desc">Подключите аккаунт Spotify или выберите свой плагин каталога.</string>
|
<string name="welcome_guide_signin_desc">Подключите аккаунт или выберите свой плагин каталога.</string>
|
||||||
<string name="welcome_guide_search_title">Ищите и слушайте</string>
|
<string name="welcome_guide_search_title">Ищите и слушайте</string>
|
||||||
<string name="welcome_guide_search_desc">Найдите любой трек, альбом или исполнителя и нажмите «Воспроизвести».</string>
|
<string name="welcome_guide_search_desc">Найдите любой трек, альбом или исполнителя и нажмите «Воспроизвести».</string>
|
||||||
<string name="welcome_guide_offline_title">Слушайте офлайн</string>
|
<string name="welcome_guide_offline_title">Слушайте офлайн</string>
|
||||||
|
|||||||
@ -382,7 +382,7 @@
|
|||||||
<string name="welcome_guide_title">วิธีใช้ Spotube</string>
|
<string name="welcome_guide_title">วิธีใช้ Spotube</string>
|
||||||
<string name="welcome_guide_subtitle">แค่ 4 ขั้นตอนง่าย ๆ ก็เริ่มฟังได้เลย</string>
|
<string name="welcome_guide_subtitle">แค่ 4 ขั้นตอนง่าย ๆ ก็เริ่มฟังได้เลย</string>
|
||||||
<string name="welcome_guide_signin_title">เข้าสู่ระบบ</string>
|
<string name="welcome_guide_signin_title">เข้าสู่ระบบ</string>
|
||||||
<string name="welcome_guide_signin_desc">เชื่อมต่อบัญชี Spotify ของคุณ หรือเลือกปลั๊กอินแคตตาล็อกของคุณเอง</string>
|
<string name="welcome_guide_signin_desc">เชื่อมต่อบัญชีของคุณ หรือเลือกปลั๊กอินแคตตาล็อกของคุณเอง</string>
|
||||||
<string name="welcome_guide_search_title">ค้นหาและเล่น</string>
|
<string name="welcome_guide_search_title">ค้นหาและเล่น</string>
|
||||||
<string name="welcome_guide_search_desc">ค้นหาเพลง อัลบั้ม หรือศิลปิน แล้วกดเล่น</string>
|
<string name="welcome_guide_search_desc">ค้นหาเพลง อัลบั้ม หรือศิลปิน แล้วกดเล่น</string>
|
||||||
<string name="welcome_guide_offline_title">ฟังแบบออฟไลน์</string>
|
<string name="welcome_guide_offline_title">ฟังแบบออฟไลน์</string>
|
||||||
|
|||||||
@ -386,7 +386,7 @@
|
|||||||
<string name="welcome_guide_title">Spotube nasıl kullanılır</string>
|
<string name="welcome_guide_title">Spotube nasıl kullanılır</string>
|
||||||
<string name="welcome_guide_subtitle">Dört hızlı adım ve dinlemeye başlıyorsun.</string>
|
<string name="welcome_guide_subtitle">Dört hızlı adım ve dinlemeye başlıyorsun.</string>
|
||||||
<string name="welcome_guide_signin_title">Giriş yap</string>
|
<string name="welcome_guide_signin_title">Giriş yap</string>
|
||||||
<string name="welcome_guide_signin_desc">Spotify hesabını bağla veya kendi katalog eklentini seç.</string>
|
<string name="welcome_guide_signin_desc">hesabını bağla veya kendi katalog eklentini seç.</string>
|
||||||
<string name="welcome_guide_search_title">Ara ve çal</string>
|
<string name="welcome_guide_search_title">Ara ve çal</string>
|
||||||
<string name="welcome_guide_search_desc">Herhangi bir şarkıyı, albümü veya sanatçıyı bul, sonra çal\'a bas.</string>
|
<string name="welcome_guide_search_desc">Herhangi bir şarkıyı, albümü veya sanatçıyı bul, sonra çal\'a bas.</string>
|
||||||
<string name="welcome_guide_offline_title">Çevrimdışı dinle</string>
|
<string name="welcome_guide_offline_title">Çevrimdışı dinle</string>
|
||||||
|
|||||||
@ -394,7 +394,7 @@
|
|||||||
<string name="welcome_guide_title">Як користуватися Spotube</string>
|
<string name="welcome_guide_title">Як користуватися Spotube</string>
|
||||||
<string name="welcome_guide_subtitle">Чотири прості кроки — і музика вже грає.</string>
|
<string name="welcome_guide_subtitle">Чотири прості кроки — і музика вже грає.</string>
|
||||||
<string name="welcome_guide_signin_title">Вхід</string>
|
<string name="welcome_guide_signin_title">Вхід</string>
|
||||||
<string name="welcome_guide_signin_desc">Підключіть акаунт Spotify або виберіть власний плагін каталогу.</string>
|
<string name="welcome_guide_signin_desc">Підключіть акаунт або виберіть власний плагін каталогу.</string>
|
||||||
<string name="welcome_guide_search_title">Шукайте й слухайте</string>
|
<string name="welcome_guide_search_title">Шукайте й слухайте</string>
|
||||||
<string name="welcome_guide_search_desc">Знайдіть будь-який трек, альбом чи виконавця й натисніть «Відтворити».</string>
|
<string name="welcome_guide_search_desc">Знайдіть будь-який трек, альбом чи виконавця й натисніть «Відтворити».</string>
|
||||||
<string name="welcome_guide_offline_title">Слухайте офлайн</string>
|
<string name="welcome_guide_offline_title">Слухайте офлайн</string>
|
||||||
|
|||||||
@ -382,7 +382,7 @@
|
|||||||
<string name="welcome_guide_title">Cách dùng Spotube</string>
|
<string name="welcome_guide_title">Cách dùng Spotube</string>
|
||||||
<string name="welcome_guide_subtitle">Bốn bước nhanh là bạn đã có thể nghe nhạc.</string>
|
<string name="welcome_guide_subtitle">Bốn bước nhanh là bạn đã có thể nghe nhạc.</string>
|
||||||
<string name="welcome_guide_signin_title">Đăng nhập</string>
|
<string name="welcome_guide_signin_title">Đăng nhập</string>
|
||||||
<string name="welcome_guide_signin_desc">Kết nối tài khoản Spotify hoặc chọn plugin danh mục của riêng bạn.</string>
|
<string name="welcome_guide_signin_desc">Kết nối tài khoản hoặc chọn plugin danh mục của riêng bạn.</string>
|
||||||
<string name="welcome_guide_search_title">Tìm kiếm và phát</string>
|
<string name="welcome_guide_search_title">Tìm kiếm và phát</string>
|
||||||
<string name="welcome_guide_search_desc">Tìm bất kỳ bài hát, album hay nghệ sĩ nào rồi nhấn phát.</string>
|
<string name="welcome_guide_search_desc">Tìm bất kỳ bài hát, album hay nghệ sĩ nào rồi nhấn phát.</string>
|
||||||
<string name="welcome_guide_offline_title">Nghe ngoại tuyến</string>
|
<string name="welcome_guide_offline_title">Nghe ngoại tuyến</string>
|
||||||
|
|||||||
@ -382,7 +382,7 @@
|
|||||||
<string name="welcome_guide_title">如何使用 Spotube</string>
|
<string name="welcome_guide_title">如何使用 Spotube</string>
|
||||||
<string name="welcome_guide_subtitle">只要四個簡單步驟,就能開始聆聽。</string>
|
<string name="welcome_guide_subtitle">只要四個簡單步驟,就能開始聆聽。</string>
|
||||||
<string name="welcome_guide_signin_title">登入</string>
|
<string name="welcome_guide_signin_title">登入</string>
|
||||||
<string name="welcome_guide_signin_desc">連結你的 Spotify 帳戶,或選擇你自己的目錄外掛程式。</string>
|
<string name="welcome_guide_signin_desc">連結你的帳戶,或選擇你自己的目錄外掛程式。</string>
|
||||||
<string name="welcome_guide_search_title">搜尋並播放</string>
|
<string name="welcome_guide_search_title">搜尋並播放</string>
|
||||||
<string name="welcome_guide_search_desc">找到任何歌曲、專輯或藝人,然後按下播放。</string>
|
<string name="welcome_guide_search_desc">找到任何歌曲、專輯或藝人,然後按下播放。</string>
|
||||||
<string name="welcome_guide_offline_title">離線聆聽</string>
|
<string name="welcome_guide_offline_title">離線聆聽</string>
|
||||||
|
|||||||
@ -382,7 +382,7 @@
|
|||||||
<string name="welcome_guide_title">如何使用 Spotube</string>
|
<string name="welcome_guide_title">如何使用 Spotube</string>
|
||||||
<string name="welcome_guide_subtitle">只需四个简单步骤,即可开始聆听。</string>
|
<string name="welcome_guide_subtitle">只需四个简单步骤,即可开始聆听。</string>
|
||||||
<string name="welcome_guide_signin_title">登录</string>
|
<string name="welcome_guide_signin_title">登录</string>
|
||||||
<string name="welcome_guide_signin_desc">连接你的 Spotify 帐号,或选择你自己的曲库插件。</string>
|
<string name="welcome_guide_signin_desc">连接你的帐号,或选择你自己的曲库插件。</string>
|
||||||
<string name="welcome_guide_search_title">搜索并播放</string>
|
<string name="welcome_guide_search_title">搜索并播放</string>
|
||||||
<string name="welcome_guide_search_desc">查找任意歌曲、专辑或艺人,然后点击播放。</string>
|
<string name="welcome_guide_search_desc">查找任意歌曲、专辑或艺人,然后点击播放。</string>
|
||||||
<string name="welcome_guide_offline_title">离线收听</string>
|
<string name="welcome_guide_offline_title">离线收听</string>
|
||||||
|
|||||||
@ -404,7 +404,7 @@
|
|||||||
<string name="welcome_guide_title">How to use Spotube</string>
|
<string name="welcome_guide_title">How to use Spotube</string>
|
||||||
<string name="welcome_guide_subtitle">Four quick steps and you’re listening.</string>
|
<string name="welcome_guide_subtitle">Four quick steps and you’re listening.</string>
|
||||||
<string name="welcome_guide_signin_title">Sign in</string>
|
<string name="welcome_guide_signin_title">Sign in</string>
|
||||||
<string name="welcome_guide_signin_desc">Connect your Spotify account, or pick your own catalog plugin.</string>
|
<string name="welcome_guide_signin_desc">Connect your account, or pick your own catalog plugin.</string>
|
||||||
<string name="welcome_guide_search_title">Search and play</string>
|
<string name="welcome_guide_search_title">Search and play</string>
|
||||||
<string name="welcome_guide_search_desc">Find any song, album, or artist, then press play.</string>
|
<string name="welcome_guide_search_desc">Find any song, album, or artist, then press play.</string>
|
||||||
<string name="welcome_guide_offline_title">Listen offline</string>
|
<string name="welcome_guide_offline_title">Listen offline</string>
|
||||||
@ -553,6 +553,7 @@
|
|||||||
<string name="jam_error_broker_host_empty">Broker host is empty</string>
|
<string name="jam_error_broker_host_empty">Broker host is empty</string>
|
||||||
<string name="jam_error_connection_failed">Connection failed</string>
|
<string name="jam_error_connection_failed">Connection failed</string>
|
||||||
<string name="jam_error_connection_lost">Connection lost</string>
|
<string name="jam_error_connection_lost">Connection lost</string>
|
||||||
|
<string name="jam_error_broker_unreachable">Couldn't reach %1$s:%2$d. Check your network or pick another broker in Settings → Jam.</string>
|
||||||
|
|
||||||
<!-- Settings: Group Jam broker -->
|
<!-- Settings: Group Jam broker -->
|
||||||
<string name="settings_jam_broker_content_description">Broker</string>
|
<string name="settings_jam_broker_content_description">Broker</string>
|
||||||
|
|||||||
@ -33,12 +33,14 @@ import org.spotube.spotube.modules.settings.JamBroker
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.TimeoutCancellationException
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -47,6 +49,7 @@ import kotlinx.serialization.json.Json
|
|||||||
import org.jetbrains.compose.resources.getString
|
import org.jetbrains.compose.resources.getString
|
||||||
import spotube.composeapp.generated.resources.Res
|
import spotube.composeapp.generated.resources.Res
|
||||||
import spotube.composeapp.generated.resources.jam_error_broker_host_empty
|
import spotube.composeapp.generated.resources.jam_error_broker_host_empty
|
||||||
|
import spotube.composeapp.generated.resources.jam_error_broker_unreachable
|
||||||
import spotube.composeapp.generated.resources.jam_error_connection_failed
|
import spotube.composeapp.generated.resources.jam_error_connection_failed
|
||||||
import spotube.composeapp.generated.resources.jam_error_connection_lost
|
import spotube.composeapp.generated.resources.jam_error_connection_lost
|
||||||
import spotube.composeapp.generated.resources.jam_error_no_broker
|
import spotube.composeapp.generated.resources.jam_error_no_broker
|
||||||
@ -135,8 +138,15 @@ class JamRoomClient : KoinComponent {
|
|||||||
|
|
||||||
val mqtt = startClient(broker, clientId)
|
val mqtt = startClient(broker, clientId)
|
||||||
client = mqtt
|
client = mqtt
|
||||||
withTimeout(broker.connectionTimeoutSeconds.seconds) {
|
try {
|
||||||
mqtt.awaitConnectivity()
|
withTimeout(broker.connectionTimeoutSeconds.seconds) {
|
||||||
|
// connectionState replays its current value, unlike awaitConnectivity's
|
||||||
|
// broadcast flow, which loses a CONNACK that lands before it is collected.
|
||||||
|
mqtt.connectionState.first { it is ConnectionState.Connected }
|
||||||
|
}
|
||||||
|
} catch (e: TimeoutCancellationException) {
|
||||||
|
throw IllegalStateException(
|
||||||
|
getString(Res.string.jam_error_broker_unreachable, broker.host, broker.port), e)
|
||||||
}
|
}
|
||||||
_connectionError.value = null
|
_connectionError.value = null
|
||||||
log.i { "Connected to ${broker.host}:${broker.port} room=$code as $clientId" }
|
log.i { "Connected to ${broker.host}:${broker.port} room=$code as $clientId" }
|
||||||
|
|||||||
@ -149,8 +149,11 @@ class PluginManager(
|
|||||||
val state: StateFlow<PluginManagerStates> = database.settingsDataStore.data
|
val state: StateFlow<PluginManagerStates> = database.settingsDataStore.data
|
||||||
.map { p ->
|
.map { p ->
|
||||||
val json = p[DatabaseKeys.PLUGINS_STATE_KEY]
|
val json = p[DatabaseKeys.PLUGINS_STATE_KEY]
|
||||||
val defaultSelectedPlugins = mapOf(
|
// Each category starts out with the built-in plugin that provides it.
|
||||||
PluginAbility.AUDIO to NEWPIPE_YOUTUBE_BUILT_IN_PLUGIN)
|
val defaultSelectedPlugins = BUILT_IN_PLUGINS
|
||||||
|
.flatMap { plugin -> plugin.abilities.map { it to plugin } }
|
||||||
|
.distinctBy { it.first }
|
||||||
|
.toMap()
|
||||||
if (json == null) {
|
if (json == null) {
|
||||||
PluginManagerStates.Data(
|
PluginManagerStates.Data(
|
||||||
plugins = BUILT_IN_PLUGINS,
|
plugins = BUILT_IN_PLUGINS,
|
||||||
|
|||||||
@ -18,15 +18,6 @@
|
|||||||
package org.spotube.spotube.modules.search
|
package org.spotube.spotube.modules.search
|
||||||
|
|
||||||
|
|
||||||
import org.spotube.millennium.composables.MillenniumMarqueeProgress
|
|
||||||
import org.spotube.millennium.composables.MillenniumIcon
|
|
||||||
import org.spotube.millennium.composables.MillenniumButton
|
|
||||||
import org.spotube.millennium.composables.MillenniumButtonStyle
|
|
||||||
import org.spotube.millennium.composables.MillenniumListTile
|
|
||||||
import org.spotube.millennium.composables.MillenniumAutocomplete
|
|
||||||
import org.spotube.millennium.composables.MillenniumText
|
|
||||||
import org.spotube.millennium.composables.MillenniumTextField
|
|
||||||
import org.spotube.millennium.theme.MillenniumTheme
|
|
||||||
import androidx.compose.animation.AnimatedContent
|
import androidx.compose.animation.AnimatedContent
|
||||||
import androidx.compose.animation.core.LinearEasing
|
import androidx.compose.animation.core.LinearEasing
|
||||||
import androidx.compose.animation.core.RepeatMode
|
import androidx.compose.animation.core.RepeatMode
|
||||||
@ -60,9 +51,6 @@ import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
|||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.text.input.rememberTextFieldState
|
import androidx.compose.foundation.text.input.rememberTextFieldState
|
||||||
|
|
||||||
import org.spotube.spotube.core.ui.theming.AppTheme
|
|
||||||
import com.composeunstyled.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@ -77,17 +65,32 @@ import androidx.compose.ui.graphics.graphicsLayer
|
|||||||
import androidx.compose.ui.platform.LocalFocusManager
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import com.composeunstyled.Text
|
||||||
import com.composeunstyled.currentWindowWidthBreakpoint
|
import com.composeunstyled.currentWindowWidthBreakpoint
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.jetbrains.compose.resources.StringResource
|
||||||
|
import org.jetbrains.compose.resources.stringResource
|
||||||
|
import org.koin.compose.koinInject
|
||||||
|
import org.koin.compose.viewmodel.koinViewModel
|
||||||
|
import org.spotube.millennium.adaptive.WindowBreakpoints
|
||||||
|
import org.spotube.millennium.composables.MillenniumAutocomplete
|
||||||
|
import org.spotube.millennium.composables.MillenniumButton
|
||||||
|
import org.spotube.millennium.composables.MillenniumButtonStyle
|
||||||
|
import org.spotube.millennium.composables.MillenniumIcon
|
||||||
|
import org.spotube.millennium.composables.MillenniumListTile
|
||||||
|
import org.spotube.millennium.composables.MillenniumMarqueeProgress
|
||||||
|
import org.spotube.millennium.composables.MillenniumText
|
||||||
|
import org.spotube.millennium.theme.MillenniumTheme
|
||||||
import org.spotube.plugin_interfaces.plugin_apis.metadata.album.MetadataAlbum
|
import org.spotube.plugin_interfaces.plugin_apis.metadata.album.MetadataAlbum
|
||||||
import org.spotube.plugin_interfaces.plugin_apis.metadata.artist.MetadataArtist
|
import org.spotube.plugin_interfaces.plugin_apis.metadata.artist.MetadataArtist
|
||||||
import org.spotube.plugin_interfaces.plugin_apis.metadata.playlist.MetadataPlaylist
|
import org.spotube.plugin_interfaces.plugin_apis.metadata.playlist.MetadataPlaylist
|
||||||
import org.spotube.plugin_interfaces.plugin_apis.metadata.search.MetadataSupportedSearchType
|
import org.spotube.plugin_interfaces.plugin_apis.metadata.search.MetadataSupportedSearchType
|
||||||
import org.spotube.plugin_interfaces.plugin_apis.metadata.track.MetadataTrack
|
import org.spotube.plugin_interfaces.plugin_apis.metadata.track.MetadataTrack
|
||||||
import org.spotube.plugin_interfaces.plugin_apis.metadata.user.MetadataUser
|
import org.spotube.plugin_interfaces.plugin_apis.metadata.user.MetadataUser
|
||||||
import org.spotube.spotube.Platform
|
|
||||||
import org.spotube.spotube.core.audioplayer.AudioPlayerQueue
|
import org.spotube.spotube.core.audioplayer.AudioPlayerQueue
|
||||||
import org.spotube.spotube.core.audioplayer.QueueEntry
|
import org.spotube.spotube.core.audioplayer.QueueEntry
|
||||||
import org.spotube.spotube.core.jam.JamRole
|
import org.spotube.spotube.core.jam.JamRole
|
||||||
@ -108,6 +111,7 @@ import org.spotube.spotube.core.ui.component.UserCard
|
|||||||
import org.spotube.spotube.core.ui.component.cards.PlayableCard
|
import org.spotube.spotube.core.ui.component.cards.PlayableCard
|
||||||
import org.spotube.spotube.core.ui.component.dragScrollable
|
import org.spotube.spotube.core.ui.component.dragScrollable
|
||||||
import org.spotube.spotube.core.ui.misc.SkeletonTree
|
import org.spotube.spotube.core.ui.misc.SkeletonTree
|
||||||
|
import org.spotube.spotube.core.ui.theming.AppTheme
|
||||||
import org.spotube.spotube.getPlatform
|
import org.spotube.spotube.getPlatform
|
||||||
import org.spotube.spotube.isDesktop
|
import org.spotube.spotube.isDesktop
|
||||||
import org.spotube.spotube.modules.blacklist.BlacklistRepository
|
import org.spotube.spotube.modules.blacklist.BlacklistRepository
|
||||||
@ -119,46 +123,38 @@ import org.spotube.spotube.resources.iconsax.Iconsax
|
|||||||
import org.spotube.spotube.resources.iconsax.IconsaxSearchBroken
|
import org.spotube.spotube.resources.iconsax.IconsaxSearchBroken
|
||||||
import org.spotube.spotube.resources.iconsax.IconsaxTrash
|
import org.spotube.spotube.resources.iconsax.IconsaxTrash
|
||||||
import org.spotube.spotube.resources.iconsax.InconsaxClock
|
import org.spotube.spotube.resources.iconsax.InconsaxClock
|
||||||
import org.jetbrains.compose.resources.stringResource
|
|
||||||
import spotube.composeapp.generated.resources.Res
|
import spotube.composeapp.generated.resources.Res
|
||||||
import spotube.composeapp.generated.resources.search_clear_history
|
|
||||||
import spotube.composeapp.generated.resources.search_placeholder
|
|
||||||
import spotube.composeapp.generated.resources.library_tab_albums
|
import spotube.composeapp.generated.resources.library_tab_albums
|
||||||
import spotube.composeapp.generated.resources.library_tab_artists
|
import spotube.composeapp.generated.resources.library_tab_artists
|
||||||
import spotube.composeapp.generated.resources.library_tab_playlists
|
import spotube.composeapp.generated.resources.library_tab_playlists
|
||||||
import spotube.composeapp.generated.resources.search_results_title
|
import spotube.composeapp.generated.resources.search_clear_history
|
||||||
import spotube.composeapp.generated.resources.search_no_search_types
|
import spotube.composeapp.generated.resources.search_loading_albums
|
||||||
import spotube.composeapp.generated.resources.search_start_typing
|
import spotube.composeapp.generated.resources.search_loading_artists
|
||||||
|
import spotube.composeapp.generated.resources.search_loading_playlists
|
||||||
|
import spotube.composeapp.generated.resources.search_loading_tracks
|
||||||
|
import spotube.composeapp.generated.resources.search_loading_users
|
||||||
|
import spotube.composeapp.generated.resources.search_no_albums
|
||||||
|
import spotube.composeapp.generated.resources.search_no_artists
|
||||||
|
import spotube.composeapp.generated.resources.search_no_playlists
|
||||||
import spotube.composeapp.generated.resources.search_no_results
|
import spotube.composeapp.generated.resources.search_no_results
|
||||||
|
import spotube.composeapp.generated.resources.search_no_search_types
|
||||||
|
import spotube.composeapp.generated.resources.search_no_users
|
||||||
|
import spotube.composeapp.generated.resources.search_placeholder
|
||||||
|
import spotube.composeapp.generated.resources.search_results_title
|
||||||
|
import spotube.composeapp.generated.resources.search_searching
|
||||||
import spotube.composeapp.generated.resources.search_section_tracks
|
import spotube.composeapp.generated.resources.search_section_tracks
|
||||||
import spotube.composeapp.generated.resources.search_section_users
|
import spotube.composeapp.generated.resources.search_section_users
|
||||||
import spotube.composeapp.generated.resources.search_start_typing_tracks
|
|
||||||
import spotube.composeapp.generated.resources.search_loading_tracks
|
|
||||||
import spotube.composeapp.generated.resources.search_no_albums
|
|
||||||
import spotube.composeapp.generated.resources.search_loading_albums
|
|
||||||
import spotube.composeapp.generated.resources.search_no_artists
|
|
||||||
import spotube.composeapp.generated.resources.search_loading_artists
|
|
||||||
import spotube.composeapp.generated.resources.search_no_playlists
|
|
||||||
import spotube.composeapp.generated.resources.search_loading_playlists
|
|
||||||
import spotube.composeapp.generated.resources.search_no_users
|
|
||||||
import spotube.composeapp.generated.resources.search_loading_users
|
|
||||||
import spotube.composeapp.generated.resources.search_see_all
|
import spotube.composeapp.generated.resources.search_see_all
|
||||||
import spotube.composeapp.generated.resources.search_searching
|
import spotube.composeapp.generated.resources.search_start_typing
|
||||||
import spotube.composeapp.generated.resources.search_warm_crates
|
import spotube.composeapp.generated.resources.search_start_typing_tracks
|
||||||
|
import spotube.composeapp.generated.resources.search_tab_all
|
||||||
|
import spotube.composeapp.generated.resources.search_warm_antennas
|
||||||
import spotube.composeapp.generated.resources.search_warm_cosmos
|
import spotube.composeapp.generated.resources.search_warm_cosmos
|
||||||
|
import spotube.composeapp.generated.resources.search_warm_crates
|
||||||
|
import spotube.composeapp.generated.resources.search_warm_music_gods
|
||||||
|
import spotube.composeapp.generated.resources.search_warm_records
|
||||||
import spotube.composeapp.generated.resources.search_warm_speakers
|
import spotube.composeapp.generated.resources.search_warm_speakers
|
||||||
import spotube.composeapp.generated.resources.search_warm_vinyl
|
import spotube.composeapp.generated.resources.search_warm_vinyl
|
||||||
import spotube.composeapp.generated.resources.search_warm_music_gods
|
|
||||||
import spotube.composeapp.generated.resources.search_warm_antennas
|
|
||||||
import spotube.composeapp.generated.resources.search_warm_records
|
|
||||||
import spotube.composeapp.generated.resources.search_tab_all
|
|
||||||
import org.jetbrains.compose.resources.StringResource
|
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.koin.compose.koinInject
|
|
||||||
import org.koin.compose.viewmodel.koinViewModel
|
|
||||||
import org.spotube.millennium.adaptive.WindowBreakpoints
|
|
||||||
|
|
||||||
private val GridMinCellSize = 180.dp
|
private val GridMinCellSize = 180.dp
|
||||||
|
|
||||||
@ -183,7 +179,7 @@ fun SearchScreen(viewModel: SearchScreenViewModel = koinViewModel()) {
|
|||||||
val savedTrackIds by viewModel.savedTrackIds.collectAsStateWithLifecycle()
|
val savedTrackIds by viewModel.savedTrackIds.collectAsStateWithLifecycle()
|
||||||
var showAddToPlaylistPicker by remember { mutableStateOf(false) }
|
var showAddToPlaylistPicker by remember { mutableStateOf(false) }
|
||||||
var tracksToAddToPlaylist by remember { mutableStateOf<List<MetadataTrack>>(emptyList()) }
|
var tracksToAddToPlaylist by remember { mutableStateOf<List<MetadataTrack>>(emptyList()) }
|
||||||
var currentUserId by remember { mutableStateOf<String?>(null) }
|
val currentUserId by viewModel.currentUserId.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
val blacklistedTracks by blacklistRepository.blacklistedTracks.collectAsStateWithLifecycle(
|
val blacklistedTracks by blacklistRepository.blacklistedTracks.collectAsStateWithLifecycle(
|
||||||
emptyList()
|
emptyList()
|
||||||
@ -207,10 +203,6 @@ fun SearchScreen(viewModel: SearchScreenViewModel = koinViewModel()) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
currentUserId = libraryRepository.currentUser()?.id
|
|
||||||
}
|
|
||||||
|
|
||||||
val focusManager = LocalFocusManager.current
|
val focusManager = LocalFocusManager.current
|
||||||
|
|
||||||
fun playSingleTrack(track: MetadataTrack) {
|
fun playSingleTrack(track: MetadataTrack) {
|
||||||
|
|||||||
@ -30,6 +30,9 @@ import org.spotube.plugin_interfaces.plugin_apis.metadata.search.MetadataSupport
|
|||||||
import org.spotube.plugin_interfaces.plugin_apis.metadata.track.MetadataTrack
|
import org.spotube.plugin_interfaces.plugin_apis.metadata.track.MetadataTrack
|
||||||
import org.spotube.plugin_interfaces.plugin_apis.metadata.user.MetadataUser
|
import org.spotube.plugin_interfaces.plugin_apis.metadata.user.MetadataUser
|
||||||
import org.spotube.spotube.core.db.Database
|
import org.spotube.spotube.core.db.Database
|
||||||
|
import org.spotube.spotube.core.di.injectLogger
|
||||||
|
import org.koin.core.component.KoinComponent
|
||||||
|
import org.spotube.spotube.modules.library.LibraryRepository
|
||||||
import org.spotube.spotube.modules.saved_tracks.SavedTracksRepository
|
import org.spotube.spotube.modules.saved_tracks.SavedTracksRepository
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
@ -81,7 +84,8 @@ data class SearchScreenState(
|
|||||||
class SearchScreenViewModel(
|
class SearchScreenViewModel(
|
||||||
private val repository: SearchRepository,
|
private val repository: SearchRepository,
|
||||||
private val savedTracksRepository: SavedTracksRepository,
|
private val savedTracksRepository: SavedTracksRepository,
|
||||||
private val database: Database) : ViewModel() {
|
private val libraryRepository: LibraryRepository,
|
||||||
|
private val database: Database) : ViewModel(), KoinComponent {
|
||||||
companion object {
|
companion object {
|
||||||
private val RECENT_SEARCHES_KEY = stringPreferencesKey("recent_searches")
|
private val RECENT_SEARCHES_KEY = stringPreferencesKey("recent_searches")
|
||||||
private val SEARCH_TYPES_ORDER = listOf(
|
private val SEARCH_TYPES_ORDER = listOf(
|
||||||
@ -97,11 +101,16 @@ class SearchScreenViewModel(
|
|||||||
private const val ALL_ROW_PAGE_SIZE = 12
|
private const val ALL_ROW_PAGE_SIZE = 12
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val logger by injectLogger<SearchScreenViewModel>()
|
||||||
|
|
||||||
private val json = Json { ignoreUnknownKeys = true }
|
private val json = Json { ignoreUnknownKeys = true }
|
||||||
|
|
||||||
private val _state = MutableStateFlow(SearchScreenState())
|
private val _state = MutableStateFlow(SearchScreenState())
|
||||||
val state: StateFlow<SearchScreenState> = _state.asStateFlow()
|
val state: StateFlow<SearchScreenState> = _state.asStateFlow()
|
||||||
|
|
||||||
|
private val _currentUserId = MutableStateFlow<String?>(null)
|
||||||
|
val currentUserId: StateFlow<String?> = _currentUserId.asStateFlow()
|
||||||
|
|
||||||
private var queryJob: Job? = null
|
private var queryJob: Job? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -113,6 +122,15 @@ class SearchScreenViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadSupportedSearchTypes()
|
loadSupportedSearchTypes()
|
||||||
|
viewModelScope.launch { loadCurrentUser() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun loadCurrentUser() {
|
||||||
|
runCatching {
|
||||||
|
_currentUserId.value = libraryRepository.currentUser()?.id
|
||||||
|
}.onFailure { e ->
|
||||||
|
logger.e(e) { "Failed to load current user" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onQueryChange(query: String) {
|
fun onQueryChange(query: String) {
|
||||||
|
|||||||
@ -98,9 +98,9 @@ data class UserSettings(
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class JamBroker(
|
data class JamBroker(
|
||||||
val name: String = "",
|
val name: String = "",
|
||||||
val host: String = "test.mosquitto.org",
|
val host: String = "broker.hivemq.com",
|
||||||
val port: Int = 1883,
|
val port: Int = 8883,
|
||||||
val useTls: Boolean = false,
|
val useTls: Boolean = true,
|
||||||
val username: String? = null,
|
val username: String? = null,
|
||||||
val password: String? = null,
|
val password: String? = null,
|
||||||
val clientIdPrefix: String = "spotube",
|
val clientIdPrefix: String = "spotube",
|
||||||
|
|||||||
@ -30,7 +30,7 @@ import platform.WebKit.WKWebViewConfiguration
|
|||||||
* Mobile Safari's exact User-Agent on this device.
|
* Mobile Safari's exact User-Agent on this device.
|
||||||
*
|
*
|
||||||
* WKWebView's default UA ends in `Mobile/15E148` without Safari's `Version/x … Safari/604.1`,
|
* WKWebView's default UA ends in `Mobile/15E148` without Safari's `Version/x … Safari/604.1`,
|
||||||
* which sites like accounts.spotify.com treat as an embedded browser ("Oops, something went
|
* which sites like accounts.greedygreencorp.com treat as an embedded browser ("Oops, something went
|
||||||
* wrong"). Hand-building the string isn't safe either: Safari freezes the OS part (iOS 26
|
* wrong"). Hand-building the string isn't safe either: Safari freezes the OS part (iOS 26
|
||||||
* reports `iPhone OS 18_7`). So WebKit builds it: a probe WKWebView gets Safari's application
|
* reports `iPhone OS 18_7`). So WebKit builds it: a probe WKWebView gets Safari's application
|
||||||
* name and reports `navigator.userAgent`, which is cached for every web view.
|
* name and reports `navigator.userAgent`, which is cached for every web view.
|
||||||
|
|||||||
@ -28,6 +28,6 @@ data class ScrobbleTrack(
|
|||||||
val trackName: String,
|
val trackName: String,
|
||||||
val artistName: String,
|
val artistName: String,
|
||||||
val albumName: String?,
|
val albumName: String?,
|
||||||
val streamingProvider: String, // e.g. "spotify", "apple_music" etc
|
val streamingProvider: String, // e.g. "greedy_green_corp", "apple_music" etc
|
||||||
val durationMs: Long? = null,
|
val durationMs: Long? = null,
|
||||||
)
|
)
|
||||||
Loading…
Reference in New Issue
Block a user