From e6d36314b967921341188b5d9a109e7f8a9d8c33 Mon Sep 17 00:00:00 2001 From: Zion Huang Date: Wed, 2 Nov 2022 15:30:57 +0800 Subject: [PATCH 01/17] Update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5d8fb6535..17ec310cf 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ Use other music scrobbler apps. I recommend [Pano Scrobbler](https://play.google ### Q: How to export downloaded song files? -*InnerTune* support SAF. You can find the provider in Android native file manager. You can also use [Material Files](https://play.google.com/store/apps/details?id=me.zhanghai.android.files) with [instruction](https://github.com/z-huang/InnerTune/issues/117#issuecomment-1295090708) (recommended). +*InnerTune* supports SAF. You can find the provider in Android native file manager. You can also use [Material Files](https://play.google.com/store/apps/details?id=me.zhanghai.android.files) with [instruction](https://github.com/z-huang/InnerTune/issues/117#issuecomment-1295090708) (recommended). ## Contribution From 5c041225a1ddde0c8e43dcdf6a598fd18b11d623 Mon Sep 17 00:00:00 2001 From: Zion Huang Date: Thu, 3 Nov 2022 20:30:15 +0800 Subject: [PATCH 02/17] Remove ripple effect on unclickable items --- .../com/zionhuang/music/ui/viewholders/YouTubeViewHolder.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/zionhuang/music/ui/viewholders/YouTubeViewHolder.kt b/app/src/main/java/com/zionhuang/music/ui/viewholders/YouTubeViewHolder.kt index a3d07c2ea..a7eb2b8bc 100644 --- a/app/src/main/java/com/zionhuang/music/ui/viewholders/YouTubeViewHolder.kt +++ b/app/src/main/java/com/zionhuang/music/ui/viewholders/YouTubeViewHolder.kt @@ -36,7 +36,7 @@ class YouTubeHeaderViewHolder( ) : YouTubeViewHolder(viewGroup, R.layout.item_youtube_header) { fun bind(header: Header) { binding.header = header - binding.root.isClickable = header.moreNavigationEndpoint != null + binding.root.isEnabled = header.moreNavigationEndpoint != null if (header.moreNavigationEndpoint != null) { binding.root.setOnClickListener { navigationEndpointHandler.handle(header.moreNavigationEndpoint) From 144801789de75e117c884eefd7676330643a9d31 Mon Sep 17 00:00:00 2001 From: Zion Huang Date: Thu, 3 Nov 2022 21:52:07 +0800 Subject: [PATCH 03/17] [Feature] Login YTM (#88) --- app/src/main/java/com/zionhuang/music/App.kt | 23 +++-- .../zionhuang/music/constants/Constants.kt | 5 ++ .../zionhuang/music/extensions/ContextExt.kt | 1 + .../zionhuang/music/extensions/FragmentExt.kt | 2 + .../music/ui/fragments/QueueSheetFragment.kt | 1 - .../music/ui/fragments/WebViewFragment.kt | 86 +++++++++++++++++++ .../ui/fragments/base/BaseSettingsFragment.kt | 2 +- .../settings/ContentSettingsFragment.kt | 30 ++++++- .../youtube/YouTubeSuggestionFragment.kt | 2 +- .../utils/preference/PreferenceLiveData.kt | 5 +- app/src/main/res/drawable/ic_person.xml | 10 +++ app/src/main/res/layout/fragment_webview.xml | 5 ++ .../navigation/settings_navigation_graph.xml | 3 + app/src/main/res/values-es-rUS/strings.xml | 1 + app/src/main/res/values-es/strings.xml | 1 + app/src/main/res/values-fa-rIR/strings.xml | 1 + app/src/main/res/values-fi-rFI/strings.xml | 1 + app/src/main/res/values-fr-rFR/strings.xml | 1 + app/src/main/res/values-hu/strings.xml | 1 + app/src/main/res/values-it/strings.xml | 1 + app/src/main/res/values-ja-rJP/strings.xml | 1 + app/src/main/res/values-ko-rKR/strings.xml | 1 + app/src/main/res/values-ml-rIN/strings.xml | 1 + app/src/main/res/values-pt-rBR/strings.xml | 1 + app/src/main/res/values-sv-rSE/strings.xml | 1 + app/src/main/res/values-zh-rCN/strings.xml | 1 + app/src/main/res/values-zh-rTW/strings.xml | 1 + app/src/main/res/values/constants.xml | 2 +- app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/pref_content.xml | 7 +- .../java/com/zionhuang/innertube/InnerTube.kt | 38 ++++++-- .../java/com/zionhuang/innertube/YouTube.kt | 16 +++- .../zionhuang/innertube/models/AccountInfo.kt | 6 ++ .../innertube/models/Continuation.kt | 6 +- .../models/MusicResponsiveListItemRenderer.kt | 6 +- .../innertube/models/SectionListRenderer.kt | 2 +- .../com/zionhuang/innertube/models/YTItem.kt | 22 ++--- .../innertube/models/body/AccountMenuBody.kt | 11 +++ .../models/response/AccountMenuResponse.kt | 46 ++++++++++ .../com/zionhuang/innertube/utils/Utils.kt | 12 +++ 40 files changed, 320 insertions(+), 44 deletions(-) create mode 100644 app/src/main/java/com/zionhuang/music/ui/fragments/WebViewFragment.kt create mode 100644 app/src/main/res/drawable/ic_person.xml create mode 100644 app/src/main/res/layout/fragment_webview.xml create mode 100644 innertube/src/main/java/com/zionhuang/innertube/models/AccountInfo.kt create mode 100644 innertube/src/main/java/com/zionhuang/innertube/models/body/AccountMenuBody.kt create mode 100644 innertube/src/main/java/com/zionhuang/innertube/models/response/AccountMenuResponse.kt diff --git a/app/src/main/java/com/zionhuang/music/App.kt b/app/src/main/java/com/zionhuang/music/App.kt index ba5f31344..dc9985c00 100644 --- a/app/src/main/java/com/zionhuang/music/App.kt +++ b/app/src/main/java/com/zionhuang/music/App.kt @@ -1,6 +1,7 @@ package com.zionhuang.music import android.app.Application +import android.content.SharedPreferences import android.os.Build import android.util.Log import android.widget.Toast @@ -12,6 +13,8 @@ import coil.disk.DiskCache import com.zionhuang.innertube.YouTube import com.zionhuang.innertube.models.YouTubeLocale import com.zionhuang.kugou.KuGou +import com.zionhuang.music.constants.Constants.INNERTUBE_COOKIE +import com.zionhuang.music.constants.Constants.VISITOR_DATA import com.zionhuang.music.extensions.getEnum import com.zionhuang.music.extensions.sharedPreferences import com.zionhuang.music.extensions.toInetSocketAddress @@ -22,7 +25,7 @@ import kotlinx.coroutines.launch import java.net.Proxy import java.util.* -class App : Application(), ImageLoaderFactory { +class App : Application(), ImageLoaderFactory, SharedPreferences.OnSharedPreferenceChangeListener { @OptIn(DelicateCoroutinesApi::class) override fun onCreate() { super.onCreate() @@ -61,14 +64,20 @@ class App : Application(), ImageLoaderFactory { } GlobalScope.launch { - val visitorData = sharedPreferences.getString(getString(R.string.pref_visitor_data), null) ?: YouTube.generateVisitorData().getOrNull()?.also { + YouTube.visitorData = sharedPreferences.getString(VISITOR_DATA, null) ?: YouTube.generateVisitorData().getOrNull()?.also { sharedPreferences.edit { - putString(getString(R.string.pref_visitor_data), it) + putString(VISITOR_DATA, it) } - } - visitorData?.let { - YouTube.visitorData = it - } + } ?: YouTube.DEFAULT_VISITOR_DATA + } + YouTube.cookie = sharedPreferences.getString(INNERTUBE_COOKIE, null) + sharedPreferences.registerOnSharedPreferenceChangeListener(this) + } + + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { + when (key) { + VISITOR_DATA -> YouTube.visitorData = sharedPreferences.getString(VISITOR_DATA, null) ?: YouTube.DEFAULT_VISITOR_DATA + INNERTUBE_COOKIE -> YouTube.cookie = sharedPreferences.getString(INNERTUBE_COOKIE, null) } } diff --git a/app/src/main/java/com/zionhuang/music/constants/Constants.kt b/app/src/main/java/com/zionhuang/music/constants/Constants.kt index cb424594a..9bff59d51 100644 --- a/app/src/main/java/com/zionhuang/music/constants/Constants.kt +++ b/app/src/main/java/com/zionhuang/music/constants/Constants.kt @@ -20,4 +20,9 @@ object Constants { const val GITHUB_ISSUE_URL = "https://github.com/z-huang/InnerTune/issues" const val ERROR_INFO = "error_info" + + const val VISITOR_DATA = "visitor_data" + const val INNERTUBE_COOKIE = "innertube_cookie" + const val ACCOUNT_NAME = "account_name" + const val ACCOUNT_EMAIL = "account_email" } \ No newline at end of file diff --git a/app/src/main/java/com/zionhuang/music/extensions/ContextExt.kt b/app/src/main/java/com/zionhuang/music/extensions/ContextExt.kt index 41582c28c..992fc5977 100644 --- a/app/src/main/java/com/zionhuang/music/extensions/ContextExt.kt +++ b/app/src/main/java/com/zionhuang/music/extensions/ContextExt.kt @@ -45,6 +45,7 @@ val Context.sharedPreferences: SharedPreferences fun Context.preference(@StringRes keyId: Int, defaultValue: T) = Preference(this, keyId, defaultValue) fun Context.preferenceLiveData(@StringRes keyId: Int, defaultValue: T) = PreferenceLiveData(this, keyId, defaultValue) +fun Context.preferenceLiveData(key: String, defaultValue: T) = PreferenceLiveData(this, key, defaultValue) fun Context.tryOrReport(block: () -> Unit) = try { block() diff --git a/app/src/main/java/com/zionhuang/music/extensions/FragmentExt.kt b/app/src/main/java/com/zionhuang/music/extensions/FragmentExt.kt index c34f5ff7e..4a9ba0172 100644 --- a/app/src/main/java/com/zionhuang/music/extensions/FragmentExt.kt +++ b/app/src/main/java/com/zionhuang/music/extensions/FragmentExt.kt @@ -7,6 +7,8 @@ import androidx.fragment.app.Fragment fun Fragment.requireAppCompatActivity(): AppCompatActivity = requireActivity() as AppCompatActivity +val Fragment.sharedPreferences get() = requireContext().sharedPreferences + fun DialogFragment.show(context: Context, tag: String? = null) { context.getActivity()?.let { show(it.supportFragmentManager, tag) diff --git a/app/src/main/java/com/zionhuang/music/ui/fragments/QueueSheetFragment.kt b/app/src/main/java/com/zionhuang/music/ui/fragments/QueueSheetFragment.kt index 798d16c41..9f0d268cf 100644 --- a/app/src/main/java/com/zionhuang/music/ui/fragments/QueueSheetFragment.kt +++ b/app/src/main/java/com/zionhuang/music/ui/fragments/QueueSheetFragment.kt @@ -130,7 +130,6 @@ class QueueSheetFragment : Fragment() { mainActivity.queueSheetBehavior.state = STATE_COLLAPSED } binding.btnLyrics.setOnClickListener { - val sharedPreferences = requireContext().sharedPreferences sharedPreferences.edit { putBoolean(getString(R.string.pref_show_lyrics), !sharedPreferences.getBoolean(getString(R.string.pref_show_lyrics), false)) } diff --git a/app/src/main/java/com/zionhuang/music/ui/fragments/WebViewFragment.kt b/app/src/main/java/com/zionhuang/music/ui/fragments/WebViewFragment.kt new file mode 100644 index 000000000..db2279478 --- /dev/null +++ b/app/src/main/java/com/zionhuang/music/ui/fragments/WebViewFragment.kt @@ -0,0 +1,86 @@ +package com.zionhuang.music.ui.fragments + +import android.annotation.SuppressLint +import android.os.Bundle +import android.view.View +import android.webkit.CookieManager +import android.webkit.JavascriptInterface +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.core.content.edit +import com.zionhuang.innertube.YouTube +import com.zionhuang.music.constants.Constants.ACCOUNT_EMAIL +import com.zionhuang.music.constants.Constants.ACCOUNT_NAME +import com.zionhuang.music.constants.Constants.INNERTUBE_COOKIE +import com.zionhuang.music.constants.Constants.VISITOR_DATA +import com.zionhuang.music.databinding.FragmentWebviewBinding +import com.zionhuang.music.extensions.sharedPreferences +import com.zionhuang.music.ui.fragments.base.BindingFragment +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch + +class WebViewFragment : BindingFragment() { + override fun getViewBinding() = FragmentWebviewBinding.inflate(layoutInflater) + + @OptIn(DelicateCoroutinesApi::class) + private val webViewClient = object : WebViewClient() { + override fun doUpdateVisitedHistory(view: WebView, url: String, isReload: Boolean) { + if (url.startsWith("https://music.youtube.com")) { + val cookies = CookieManager.getInstance().getCookie(url) + if (sharedPreferences.getString(INNERTUBE_COOKIE, null) != cookies) { + sharedPreferences.edit { + putString(INNERTUBE_COOKIE, cookies) + } + GlobalScope.launch { + YouTube.getAccountInfo().onSuccess { + sharedPreferences.edit { + putString(ACCOUNT_NAME, it?.name) + putString(ACCOUNT_EMAIL, it?.email) + } + }.onFailure { + it.printStackTrace() + } + } + } + } + } + + override fun onPageFinished(view: WebView, url: String?) { + binding.webview.loadUrl("javascript:Android.onRetrieveVisitorData(window.yt.config_.VISITOR_DATA)") + } + } + + @SuppressLint("SetJavaScriptEnabled") + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.webview.apply { + if (savedInstanceState != null) { + restoreState(savedInstanceState) + } else { + loadUrl("https://accounts.google.com/ServiceLogin?ltmpl=music&service=youtube&passive=true&continue=https%3A%2F%2Fwww.youtube.com%2Fsignin%3Faction_handle_signin%3Dtrue%26next%3Dhttps%253A%252F%252Fmusic.youtube.com%252F") + } + webViewClient = this@WebViewFragment.webViewClient + settings.apply { + javaScriptEnabled = true + setSupportZoom(true) + builtInZoomControls = true + } + addJavascriptInterface(this@WebViewFragment, "Android") + } + } + + @JavascriptInterface + fun onRetrieveVisitorData(visitorData: String?) { + if (visitorData != null && sharedPreferences.getString(VISITOR_DATA, null) != visitorData) { + sharedPreferences.edit { + putString(VISITOR_DATA, visitorData) + } + } + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + binding.webview.saveState(outState) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/zionhuang/music/ui/fragments/base/BaseSettingsFragment.kt b/app/src/main/java/com/zionhuang/music/ui/fragments/base/BaseSettingsFragment.kt index 77606c345..fcd7d8ddf 100644 --- a/app/src/main/java/com/zionhuang/music/ui/fragments/base/BaseSettingsFragment.kt +++ b/app/src/main/java/com/zionhuang/music/ui/fragments/base/BaseSettingsFragment.kt @@ -30,7 +30,7 @@ abstract class BaseSettingsFragment : PreferenceFragmentCompat() { } is EditTextPreference -> { val binding = DialogEditTextPreferenceBinding.inflate(layoutInflater) - binding.editText.setText(requireContext().sharedPreferences.getString(preference.key, "")) + binding.editText.setText(sharedPreferences.getString(preference.key, "")) MaterialAlertDialogBuilder(requireContext()) .setTitle(preference.title) .setView(binding.root) diff --git a/app/src/main/java/com/zionhuang/music/ui/fragments/settings/ContentSettingsFragment.kt b/app/src/main/java/com/zionhuang/music/ui/fragments/settings/ContentSettingsFragment.kt index c4ced9f54..cb1a65f36 100644 --- a/app/src/main/java/com/zionhuang/music/ui/fragments/settings/ContentSettingsFragment.kt +++ b/app/src/main/java/com/zionhuang/music/ui/fragments/settings/ContentSettingsFragment.kt @@ -1,25 +1,40 @@ package com.zionhuang.music.ui.fragments.settings import android.content.Intent +import android.content.SharedPreferences +import android.content.SharedPreferences.OnSharedPreferenceChangeListener import android.os.Bundle import android.view.View +import androidx.navigation.fragment.findNavController import androidx.preference.EditTextPreference import androidx.preference.ListPreference import androidx.preference.Preference import androidx.preference.SwitchPreferenceCompat import com.zionhuang.music.R +import com.zionhuang.music.constants.Constants.ACCOUNT_EMAIL +import com.zionhuang.music.constants.Constants.ACCOUNT_NAME import com.zionhuang.music.extensions.preferenceLiveData +import com.zionhuang.music.extensions.sharedPreferences import com.zionhuang.music.ui.activities.MainActivity import com.zionhuang.music.ui.fragments.base.BaseSettingsFragment import kotlin.system.exitProcess -class ContentSettingsFragment : BaseSettingsFragment() { +class ContentSettingsFragment : BaseSettingsFragment(), OnSharedPreferenceChangeListener { + private lateinit var accountPreference: Preference private lateinit var proxyTypePreference: ListPreference private lateinit var proxyUrlPreference: EditTextPreference override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.pref_content) + accountPreference = findPreference(getString(R.string.pref_account))!!.apply { + title = sharedPreferences?.getString(ACCOUNT_NAME, null) ?: getString(R.string.login) + summary = sharedPreferences?.getString(ACCOUNT_EMAIL, null).orEmpty() + setOnPreferenceClickListener { + findNavController().navigate(R.id.webviewFragment) + true + } + } val proxyEnabledPreference = findPreference(getString(R.string.pref_proxy_enabled))!! proxyTypePreference = findPreference(getString(R.string.pref_proxy_type))!!.apply { isVisible = proxyEnabledPreference.isChecked @@ -39,5 +54,18 @@ class ContentSettingsFragment : BaseSettingsFragment() { proxyTypePreference.isVisible = it proxyUrlPreference.isVisible = it } + sharedPreferences.registerOnSharedPreferenceChangeListener(this) + } + + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { + when (key) { + ACCOUNT_NAME -> accountPreference.title = sharedPreferences.getString(ACCOUNT_NAME, null) ?: getString(R.string.login) + ACCOUNT_EMAIL -> accountPreference.summary = sharedPreferences.getString(ACCOUNT_EMAIL, null).orEmpty() + } + } + + override fun onDestroy() { + super.onDestroy() + sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) } } \ No newline at end of file diff --git a/app/src/main/java/com/zionhuang/music/ui/fragments/youtube/YouTubeSuggestionFragment.kt b/app/src/main/java/com/zionhuang/music/ui/fragments/youtube/YouTubeSuggestionFragment.kt index d6d3ced52..46f8af454 100644 --- a/app/src/main/java/com/zionhuang/music/ui/fragments/youtube/YouTubeSuggestionFragment.kt +++ b/app/src/main/java/com/zionhuang/music/ui/fragments/youtube/YouTubeSuggestionFragment.kt @@ -130,7 +130,7 @@ class YouTubeSuggestionFragment : NavigationFragment( context: Context, - @StringRes private val keyId: Int, + val key: String, private val defValue: T, ) : SafeLiveData(defValue) { protected val sharedPreferences: SharedPreferences = context.sharedPreferences - protected val key = context.getString(keyId) + + constructor(context: Context, @StringRes keyId: Int, defValue: T) : this(context, context.getString(keyId), defValue) protected fun getPreferenceValue() = sharedPreferences.get(key, defValue) diff --git a/app/src/main/res/drawable/ic_person.xml b/app/src/main/res/drawable/ic_person.xml new file mode 100644 index 000000000..a37adc634 --- /dev/null +++ b/app/src/main/res/drawable/ic_person.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/fragment_webview.xml b/app/src/main/res/layout/fragment_webview.xml new file mode 100644 index 000000000..9ee060794 --- /dev/null +++ b/app/src/main/res/layout/fragment_webview.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/app/src/main/res/navigation/settings_navigation_graph.xml b/app/src/main/res/navigation/settings_navigation_graph.xml index 094ba0973..73336723d 100644 --- a/app/src/main/res/navigation/settings_navigation_graph.xml +++ b/app/src/main/res/navigation/settings_navigation_graph.xml @@ -39,4 +39,7 @@ android:id="@+id/aboutFragment" android:name="com.zionhuang.music.ui.fragments.settings.AboutFragment" android:label="@string/pref_about_title" /> + \ No newline at end of file diff --git a/app/src/main/res/values-es-rUS/strings.xml b/app/src/main/res/values-es-rUS/strings.xml index b0e2c246e..4607c22ac 100644 --- a/app/src/main/res/values-es-rUS/strings.xml +++ b/app/src/main/res/values-es-rUS/strings.xml @@ -26,6 +26,7 @@ Right Contenido + Login Idioma por defecto del contenido País por defecto del contenido Enable proxy diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 697f7bb53..5bd70707c 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -26,6 +26,7 @@ Right Contenido + Login Idioma de contenido predeterminado País de contenido predeterminado Enable proxy diff --git a/app/src/main/res/values-fa-rIR/strings.xml b/app/src/main/res/values-fa-rIR/strings.xml index 91e20af97..ea9fc90f3 100644 --- a/app/src/main/res/values-fa-rIR/strings.xml +++ b/app/src/main/res/values-fa-rIR/strings.xml @@ -26,6 +26,7 @@ Right محتوا + Login زبان پیش‌فرض محتوا کشور پیش‌فرض محتوا فعال‌کردن پروکسی diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml index 69eea1e76..e0d72a89e 100644 --- a/app/src/main/res/values-fi-rFI/strings.xml +++ b/app/src/main/res/values-fi-rFI/strings.xml @@ -26,6 +26,7 @@ Right Sisältö + Login Sisällön oletuskieli Sisällön oletusmaa Enable proxy diff --git a/app/src/main/res/values-fr-rFR/strings.xml b/app/src/main/res/values-fr-rFR/strings.xml index 2b4237d39..22710c1f9 100644 --- a/app/src/main/res/values-fr-rFR/strings.xml +++ b/app/src/main/res/values-fr-rFR/strings.xml @@ -26,6 +26,7 @@ Right Content + Login Langue du contenu par défaut Pays du contenu par défaut Enable proxy diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 13aa6d506..6e67461c4 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -26,6 +26,7 @@ Jobbra Tartalom + Login A tartalom alapért. nyelve A tartalom alapért.t országa Proxy bekapcsolása diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 2f39585ec..be758369c 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -26,6 +26,7 @@ Destra Contenuti + Login Lingua predefinita dei contenuti Paese predefinito dei contenuti Attiva proxy diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index 9076f9f92..e8ad6f2e2 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -26,6 +26,7 @@ Right コンテンツ + Login デフォルトのコンテンツの言語 デフォルトのコンテンツの国 プロキシを有効化 diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml index 0eb0a529b..1edd31e0b 100644 --- a/app/src/main/res/values-ko-rKR/strings.xml +++ b/app/src/main/res/values-ko-rKR/strings.xml @@ -26,6 +26,7 @@ Right 콘텐츠 + Login 기본 콘텐츠 언어 기본 콘텐츠 국가 Enable proxy diff --git a/app/src/main/res/values-ml-rIN/strings.xml b/app/src/main/res/values-ml-rIN/strings.xml index f8edcba75..2ec038156 100644 --- a/app/src/main/res/values-ml-rIN/strings.xml +++ b/app/src/main/res/values-ml-rIN/strings.xml @@ -26,6 +26,7 @@ Right കന്റെന്റ് + Login സ്ഥിര കന്റെന്റ് ഭാഷ സ്ഥിര കന്റെന്റ് രാജ്യം പ്രോക്സി പ്രവർത്തനക്ഷമമാക്കുക diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 3751e9caf..f50e19d45 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -26,6 +26,7 @@ Right Conteúdo + Login Idioma padrão do conteúdo País padrão do conteúdo Ativar proxy diff --git a/app/src/main/res/values-sv-rSE/strings.xml b/app/src/main/res/values-sv-rSE/strings.xml index 7b29b8866..4ddea00a4 100644 --- a/app/src/main/res/values-sv-rSE/strings.xml +++ b/app/src/main/res/values-sv-rSE/strings.xml @@ -26,6 +26,7 @@ Right Innehåll + Login Standard innehållsspråk Standard innehållsland Enable proxy diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 1255a10c7..77301c113 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -26,6 +26,7 @@ 靠右 内容 + Login 默认内容语言 默认内容国家 启用代理 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 14d1c21e3..9c3e0e31b 100755 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -26,6 +26,7 @@ 靠右 內容 + 登入 預設內容語言 預設內容國家 啟用 Proxy diff --git a/app/src/main/res/values/constants.xml b/app/src/main/res/values/constants.xml index 5a3c64880..c4308ef0e 100644 --- a/app/src/main/res/values/constants.xml +++ b/app/src/main/res/values/constants.xml @@ -25,9 +25,9 @@ NAV_TAB_CONFIG LRC_TEXT_POS + ACCOUNT CONTENT_LANGUAGE CONTENT_COUNTRY - VISITOR_DATA PROXY_ENABLED PROXY_TYPE PROXY_URL diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1864dbd94..a2af0e499 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -26,6 +26,7 @@ Right Content + Login Default content language Default content country Enable proxy diff --git a/app/src/main/res/xml/pref_content.xml b/app/src/main/res/xml/pref_content.xml index 62729a536..b9ec68b9e 100644 --- a/app/src/main/res/xml/pref_content.xml +++ b/app/src/main/res/xml/pref_content.xml @@ -1,6 +1,11 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools"> + () + + var proxy: Proxy? = null + set(value) { + field = value httpClient.close() httpClient = createClient() } - var visitorData: String = "CgtsZG1ySnZiQWtSbyiMjuGSBg%3D%3D" @OptIn(ExperimentalSerializationApi::class) private fun createClient() = HttpClient(OkHttp) { @@ -56,9 +63,9 @@ class InnerTube { deflate(0.8F) } - if (_proxy != null) { + if (proxy != null) { engine { - this.proxy = _proxy + proxy = this@InnerTube.proxy } } @@ -73,9 +80,17 @@ class InnerTube { append("X-Goog-Api-Format-Version", "1") append("X-YouTube-Client-Name", client.clientName) append("X-YouTube-Client-Version", client.clientVersion) + append("x-origin", "https://music.youtube.com") if (client.referer != null) { append("Referer", client.referer) } + cookie?.let { cookie -> + append("cookie", cookie) + if ("SAPISID" !in cookieMap) return@let + val currentTime = System.currentTimeMillis() / 1000 + val sapisidHash = sha1("$currentTime ${cookieMap["SAPISID"]} https://music.youtube.com") + append("Authorization", "SAPISIDHASH ${currentTime}_${sapisidHash}") + } } userAgent(client.userAgent) parameter("key", client.api_key) @@ -174,4 +189,11 @@ class InnerTube { playlistId = playlistId )) } + + suspend fun getSwJsData() = httpClient.get("https://music.youtube.com/sw.js_data") + + suspend fun accountMenu(client: YouTubeClient) = httpClient.post("account/account_menu") { + configYTClient(client) + setBody(AccountMenuBody(client.toContext(locale, visitorData))) + } } \ No newline at end of file diff --git a/innertube/src/main/java/com/zionhuang/innertube/YouTube.kt b/innertube/src/main/java/com/zionhuang/innertube/YouTube.kt index 3549e2645..7f33cb81d 100644 --- a/innertube/src/main/java/com/zionhuang/innertube/YouTube.kt +++ b/innertube/src/main/java/com/zionhuang/innertube/YouTube.kt @@ -6,7 +6,6 @@ import com.zionhuang.innertube.models.YouTubeClient.Companion.WEB_REMIX import com.zionhuang.innertube.models.response.* import com.zionhuang.innertube.utils.insertSeparator import io.ktor.client.call.* -import io.ktor.client.request.* import io.ktor.client.statement.* import io.ktor.http.* import kotlinx.serialization.json.Json @@ -31,6 +30,11 @@ object YouTube { set(value) { innerTube.visitorData = value } + var cookie: String? + get() = innerTube.cookie + set(value) { + innerTube.cookie = value + } var proxy: Proxy? get() = innerTube.proxy set(value) { @@ -155,14 +159,18 @@ object YouTube { .mapNotNull { it.content.playlistPanelVideoRenderer?.toSongItem() } } - suspend fun generateVisitorData() = runCatching { - Json.parseToJsonElement(innerTube.httpClient.get("https://music.youtube.com/sw.js_data").bodyAsText().substring(5)) + suspend fun generateVisitorData(): Result = runCatching { + Json.parseToJsonElement(innerTube.getSwJsData().bodyAsText().substring(5)) .jsonArray[0] .jsonArray[2] .jsonArray[6] .jsonPrimitive.content } + suspend fun getAccountInfo(): Result = runCatching { + innerTube.accountMenu(WEB_REMIX).body().actions[0].openPopupAction.popup.multiPageMenuRenderer.header?.activeAccountHeaderRenderer?.toAccountInfo() + } + @JvmInline value class SearchFilter(val value: String) { companion object { @@ -179,4 +187,6 @@ object YouTube { const val EXPLORE_BROWSE_ID = "FEmusic_explore" const val MAX_GET_QUEUE_SIZE = 1000 + + const val DEFAULT_VISITOR_DATA = "CgtsZG1ySnZiQWtSbyiMjuGSBg%3D%3D" } \ No newline at end of file diff --git a/innertube/src/main/java/com/zionhuang/innertube/models/AccountInfo.kt b/innertube/src/main/java/com/zionhuang/innertube/models/AccountInfo.kt new file mode 100644 index 000000000..cacedbfed --- /dev/null +++ b/innertube/src/main/java/com/zionhuang/innertube/models/AccountInfo.kt @@ -0,0 +1,6 @@ +package com.zionhuang.innertube.models + +data class AccountInfo( + val name: String, + val email: String, +) diff --git a/innertube/src/main/java/com/zionhuang/innertube/models/Continuation.kt b/innertube/src/main/java/com/zionhuang/innertube/models/Continuation.kt index 2e34a03f4..a079a30f7 100644 --- a/innertube/src/main/java/com/zionhuang/innertube/models/Continuation.kt +++ b/innertube/src/main/java/com/zionhuang/innertube/models/Continuation.kt @@ -9,7 +9,7 @@ import kotlinx.serialization.json.JsonNames @Serializable data class Continuation( @JsonNames("nextContinuationData", "nextRadioContinuationData") - val nextContinuationData: NextContinuationData, + val nextContinuationData: NextContinuationData?, ) { @Serializable data class NextContinuationData( @@ -18,9 +18,9 @@ data class Continuation( } fun List.getContinuation() = - get(0).nextContinuationData.continuation + get(0).nextContinuationData?.continuation fun List.getContinuations() = - map { it.nextContinuationData.continuation } + mapNotNull { it.nextContinuationData?.continuation } .ifEmpty { null } \ No newline at end of file diff --git a/innertube/src/main/java/com/zionhuang/innertube/models/MusicResponsiveListItemRenderer.kt b/innertube/src/main/java/com/zionhuang/innertube/models/MusicResponsiveListItemRenderer.kt index 6dfaebd49..7fa0d1aa8 100644 --- a/innertube/src/main/java/com/zionhuang/innertube/models/MusicResponsiveListItemRenderer.kt +++ b/innertube/src/main/java/com/zionhuang/innertube/models/MusicResponsiveListItemRenderer.kt @@ -27,8 +27,8 @@ data class MusicResponsiveListItemRenderer( ) { fun getTitle() = flexColumns[0].musicResponsiveListItemFlexColumnRenderer.text.toString() fun getSubtitle() = (flexColumns.drop(1) + fixedColumns.orEmpty()) - .filter { - it.musicResponsiveListItemFlexColumnRenderer.text.runs.isNotEmpty() + .filterNot { + it.musicResponsiveListItemFlexColumnRenderer.text?.runs.isNullOrEmpty() }.joinToString(separator = " • ") { it.musicResponsiveListItemFlexColumnRenderer.text.toString() } @@ -60,7 +60,7 @@ data class MusicResponsiveListItemRenderer( ) { @Serializable data class MusicResponsiveListItemFlexColumnRenderer( - val text: Runs, + val text: Runs?, ) } diff --git a/innertube/src/main/java/com/zionhuang/innertube/models/SectionListRenderer.kt b/innertube/src/main/java/com/zionhuang/innertube/models/SectionListRenderer.kt index 98a627c92..3eb74a843 100644 --- a/innertube/src/main/java/com/zionhuang/innertube/models/SectionListRenderer.kt +++ b/innertube/src/main/java/com/zionhuang/innertube/models/SectionListRenderer.kt @@ -10,7 +10,7 @@ import kotlinx.serialization.json.JsonNames @Serializable data class SectionListRenderer( val header: Header?, - val contents: List, + val contents: List?, val continuations: List?, ) { @Serializable diff --git a/innertube/src/main/java/com/zionhuang/innertube/models/YTItem.kt b/innertube/src/main/java/com/zionhuang/innertube/models/YTItem.kt index 264d6264e..9f085fb25 100644 --- a/innertube/src/main/java/com/zionhuang/innertube/models/YTItem.kt +++ b/innertube/src/main/java/com/zionhuang/innertube/models/YTItem.kt @@ -53,35 +53,35 @@ data class SongItem( val menu = item.menu.toItemMenu() return SongItem( id = item.playlistItemData?.videoId - ?: item.flexColumns[0].musicResponsiveListItemFlexColumnRenderer.text.runs[0].navigationEndpoint?.watchEndpoint?.videoId + ?: item.flexColumns[0].musicResponsiveListItemFlexColumnRenderer.text?.runs?.firstOrNull()?.navigationEndpoint?.watchEndpoint?.videoId ?: menu.radioEndpoint?.watchEndpoint?.videoId ?: return null, title = item.getTitle(), subtitle = item.getSubtitle(), index = item.index?.toString(), - artists = item.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs - .filter { it.navigationEndpoint?.getEndpointType() == ITEM_ARTIST } - .ifEmpty { + artists = item.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text?.runs + ?.filter { it.navigationEndpoint?.getEndpointType() == ITEM_ARTIST } + ?.ifEmpty { listOfNotNull( if (item.fixedColumns != null) { // Table style - item.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs.getOrNull(0) + item.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text?.runs?.getOrNull(0) } else { // From search - item.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs.let { + item.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text?.runs?.let { it.getOrNull(it.lastIndex - 4) ?: it.getOrNull(it.lastIndex - 2) } } ) - }, - album = item.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs - .find { it.navigationEndpoint?.getEndpointType() == ITEM_ALBUM } + }.orEmpty(), + album = item.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text?.runs + ?.find { it.navigationEndpoint?.getEndpointType() == ITEM_ALBUM } ?.toLink(), - duration = item.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs.lastOrNull()?.text?.let { TimeParser.parse(it) } + duration = item.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text?.runs?.lastOrNull()?.text?.let { TimeParser.parse(it) } ?: item.fixedColumns?.firstOrNull()?.musicResponsiveListItemFlexColumnRenderer?.text?.runs?.firstOrNull()?.text?.let { TimeParser.parse(it) }, thumbnails = item.thumbnail?.getThumbnails().orEmpty(), menu = menu, - navigationEndpoint = item.flexColumns[0].musicResponsiveListItemFlexColumnRenderer.text.runs[0].navigationEndpoint!! + navigationEndpoint = item.flexColumns[0].musicResponsiveListItemFlexColumnRenderer.text?.runs?.firstOrNull()?.navigationEndpoint!! ) } diff --git a/innertube/src/main/java/com/zionhuang/innertube/models/body/AccountMenuBody.kt b/innertube/src/main/java/com/zionhuang/innertube/models/body/AccountMenuBody.kt new file mode 100644 index 000000000..d92b60200 --- /dev/null +++ b/innertube/src/main/java/com/zionhuang/innertube/models/body/AccountMenuBody.kt @@ -0,0 +1,11 @@ +package com.zionhuang.innertube.models.body + +import com.zionhuang.innertube.models.Context +import kotlinx.serialization.Serializable + +@Serializable +data class AccountMenuBody( + val context: Context, + val deviceTheme: String = "DEVICE_THEME_SELECTED", + val userInterfaceTheme: String = "USER_INTERFACE_THEME_DARK", +) diff --git a/innertube/src/main/java/com/zionhuang/innertube/models/response/AccountMenuResponse.kt b/innertube/src/main/java/com/zionhuang/innertube/models/response/AccountMenuResponse.kt new file mode 100644 index 000000000..7c1118771 --- /dev/null +++ b/innertube/src/main/java/com/zionhuang/innertube/models/response/AccountMenuResponse.kt @@ -0,0 +1,46 @@ +package com.zionhuang.innertube.models.response + +import com.zionhuang.innertube.models.AccountInfo +import com.zionhuang.innertube.models.Runs +import kotlinx.serialization.Serializable + +@Serializable +data class AccountMenuResponse( + val actions: List, +) { + @Serializable + data class Action( + val openPopupAction: OpenPopupAction, + ) { + @Serializable + data class OpenPopupAction( + val popup: Popup, + ) { + @Serializable + data class Popup( + val multiPageMenuRenderer: MultiPageMenuRenderer, + ) { + @Serializable + data class MultiPageMenuRenderer( + val header: Header?, + ) { + @Serializable + data class Header( + val activeAccountHeaderRenderer: ActiveAccountHeaderRenderer, + ) { + @Serializable + data class ActiveAccountHeaderRenderer( + val accountName: Runs, + val email: Runs, + ) { + fun toAccountInfo() = AccountInfo( + accountName.toString(), + email.toString() + ) + } + } + } + } + } + } +} diff --git a/innertube/src/main/java/com/zionhuang/innertube/utils/Utils.kt b/innertube/src/main/java/com/zionhuang/innertube/utils/Utils.kt index df7bbc0b4..a6da8b408 100644 --- a/innertube/src/main/java/com/zionhuang/innertube/utils/Utils.kt +++ b/innertube/src/main/java/com/zionhuang/innertube/utils/Utils.kt @@ -3,6 +3,18 @@ package com.zionhuang.innertube.utils import java.io.UnsupportedEncodingException import java.net.URL import java.net.URLDecoder +import java.security.MessageDigest + +fun ByteArray.toHex(): String = joinToString(separator = "") { eachByte -> "%02x".format(eachByte) } + +fun sha1(str: String): String = MessageDigest.getInstance("SHA-1").digest(str.toByteArray()).toHex() + +fun parseCookieString(cookie: String): Map = + cookie.split("; ").associate { + val (key, value) = it.split("=") + key to value + } + fun isHTTP(url: URL): Boolean { // Make sure it's HTTP or HTTPS From a8107b77ef596b9fd2876ac11d6a3d082d1f9f4b Mon Sep 17 00:00:00 2001 From: Zion Huang Date: Thu, 3 Nov 2022 22:01:49 +0800 Subject: [PATCH 04/17] Fix #401 --- kugou/src/main/java/com/zionhuang/kugou/KuGou.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kugou/src/main/java/com/zionhuang/kugou/KuGou.kt b/kugou/src/main/java/com/zionhuang/kugou/KuGou.kt index 6a5268875..1127eaf3c 100644 --- a/kugou/src/main/java/com/zionhuang/kugou/KuGou.kt +++ b/kugou/src/main/java/com/zionhuang/kugou/KuGou.kt @@ -131,7 +131,7 @@ object KuGou { private fun generateKeyword(title: String, artist: String) = normalizeTitle(title) to normalizeArtist(artist) - private fun String.normalize(keyword: Pair): String = lines().filter { line -> + private fun String.normalize(keyword: Pair): String = replace("'", "'").lines().filter { line -> line matches ACCEPTED_REGEX }.let { // Remove useless information such as singer, writer, composer, guitar, etc. From 0c9c8e9699fa79c3632bd66cde740a500d544815 Mon Sep 17 00:00:00 2001 From: Zion Huang Date: Thu, 3 Nov 2022 22:23:02 +0800 Subject: [PATCH 05/17] Add shuffle button in Android Auto --- .../music/constants/MediaSessionConstants.kt | 1 + .../java/com/zionhuang/music/playback/SongPlayer.kt | 13 +++++++++++++ app/src/main/res/drawable/ic_shuffle.xml | 2 +- app/src/main/res/drawable/ic_shuffle_on.xml | 11 +++++++++++ 4 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 app/src/main/res/drawable/ic_shuffle_on.xml diff --git a/app/src/main/java/com/zionhuang/music/constants/MediaSessionConstants.kt b/app/src/main/java/com/zionhuang/music/constants/MediaSessionConstants.kt index 509e0e710..e11505ed4 100644 --- a/app/src/main/java/com/zionhuang/music/constants/MediaSessionConstants.kt +++ b/app/src/main/java/com/zionhuang/music/constants/MediaSessionConstants.kt @@ -7,6 +7,7 @@ object MediaSessionConstants { const val ACTION_TOGGLE_LIKE = "action_toggle_like" const val ACTION_LIKE = "action_like" const val ACTION_UNLIKE = "action_unlike" + const val ACTION_TOGGLE_SHUFFLE = "action_shuffle" const val COMMAND_SEEK_TO_QUEUE_ITEM = "seek_to_queue_item" const val COMMAND_PLAY_NEXT = "action_play_next" const val COMMAND_ADD_TO_QUEUE = "action_add_to_queue" diff --git a/app/src/main/java/com/zionhuang/music/playback/SongPlayer.kt b/app/src/main/java/com/zionhuang/music/playback/SongPlayer.kt index ecee98c03..7d1b0ea39 100644 --- a/app/src/main/java/com/zionhuang/music/playback/SongPlayer.kt +++ b/app/src/main/java/com/zionhuang/music/playback/SongPlayer.kt @@ -61,6 +61,7 @@ import com.zionhuang.music.constants.MediaSessionConstants.ACTION_LIKE import com.zionhuang.music.constants.MediaSessionConstants.ACTION_REMOVE_FROM_LIBRARY import com.zionhuang.music.constants.MediaSessionConstants.ACTION_TOGGLE_LIBRARY import com.zionhuang.music.constants.MediaSessionConstants.ACTION_TOGGLE_LIKE +import com.zionhuang.music.constants.MediaSessionConstants.ACTION_TOGGLE_SHUFFLE import com.zionhuang.music.constants.MediaSessionConstants.ACTION_UNLIKE import com.zionhuang.music.constants.MediaSessionConstants.COMMAND_ADD_TO_QUEUE import com.zionhuang.music.constants.MediaSessionConstants.COMMAND_PLAY_NEXT @@ -275,6 +276,18 @@ class SongPlayer( if (currentSong != null) R.drawable.ic_library_add_check else R.drawable.ic_library_add ).build() } else null + }, + object : MediaSessionConnector.CustomActionProvider { + override fun onCustomAction(player: Player, action: String, extras: Bundle?) { + player.shuffleModeEnabled = !player.shuffleModeEnabled + } + + override fun getCustomAction(player: Player) = + CustomAction.Builder( + ACTION_TOGGLE_SHUFFLE, + context.getString(R.string.btn_shuffle), + if (player.shuffleModeEnabled) R.drawable.ic_shuffle_on else R.drawable.ic_shuffle + ).build() } ) setQueueNavigator { player, windowIndex -> player.getMediaItemAt(windowIndex).metadata!!.toMediaDescription(context) } diff --git a/app/src/main/res/drawable/ic_shuffle.xml b/app/src/main/res/drawable/ic_shuffle.xml index 41876a2bd..3e52f67f1 100644 --- a/app/src/main/res/drawable/ic_shuffle.xml +++ b/app/src/main/res/drawable/ic_shuffle.xml @@ -5,6 +5,6 @@ android:viewportWidth="24" android:viewportHeight="24"> diff --git a/app/src/main/res/drawable/ic_shuffle_on.xml b/app/src/main/res/drawable/ic_shuffle_on.xml new file mode 100644 index 000000000..3bb8d2afa --- /dev/null +++ b/app/src/main/res/drawable/ic_shuffle_on.xml @@ -0,0 +1,11 @@ + + + From d8cdc6b7cac09f412e680b06155a4a849fd62316 Mon Sep 17 00:00:00 2001 From: gidano Date: Fri, 4 Nov 2022 15:43:26 +0100 Subject: [PATCH 06/17] Update strings.xml --- app/src/main/res/values-hu/strings.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 6e67461c4..e80ba5db0 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -26,7 +26,7 @@ Jobbra Tartalom - Login + Bejelentkezés A tartalom alapért. nyelve A tartalom alapért.t országa Proxy bekapcsolása @@ -122,7 +122,7 @@ Újrahív Megosztás Törlés - Search online + Keresés online Válasszon más dalszövegeket @@ -138,7 +138,7 @@ Ismeretlen Dalszöveg szerkesztése - Search lyrics + Dalszöveg keresése Válassza ki a dalszövegeket Dal szerkesztése From 1db6dd3ea6b52bea9ca2e99840924aa366671f8b Mon Sep 17 00:00:00 2001 From: X-Noid Date: Sun, 6 Nov 2022 13:06:56 +0900 Subject: [PATCH 07/17] Added Indonesia Translation --- app/src/main/res/values-id/strings.xml | 306 +++++++++++++++++++++++++ 1 file changed, 306 insertions(+) create mode 100644 app/src/main/res/values-id/strings.xml diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml new file mode 100644 index 000000000..7b3d4937d --- /dev/null +++ b/app/src/main/res/values-id/strings.xml @@ -0,0 +1,306 @@ + + + Beranda + Lagu + Artis + Album + Daftar putar + Jelajah + Pengaturan + Sedang diputar + Laporkan kesalahan + + + Tampilan + Ikuti tema sistem + Warna tema + Tema gelap + Aktif + Tidak aktif + Ikuti sistem + Tab buka bawaan + Sesuaikan tab navigasi + Posisi teks lirik + Kiri + Tengah + Kanan + + Konten + Masuk + Bahasa konten bawaan + Negara konten bawaan + Aktifkan proxy + Tipe proxy + URL proxy + Mulai ulang agar berlaku + + Pemutar dan audio + Kualitas audio + Otomatis + Tinggi + Rendah + Dalam antrian + Lewati keheningan + Normalisasi audio + Ekualiser + + Penyimpanan + Lihat file yang diunduh di SAF + Ini mungkin tidak berfungsi di beberapa perangkat + Data sementara (cache) + Ukuran gambar data sementara (cache) maksimum + Bersihkan gambar data sementara (cache) + Ukuran lagu data sementara (cache) maksimum + %s digunakan + + Umum + Unduh secara otomatis + Unduh lagu saat ditambahkan ke perpustakaan + Tambahkan lagu secara otomatis ke perpustakaan + Tambahkan lagu ke perpustakaan Anda saat selesai diputar + Perluas pemutar bawah saat diputar + Lebih banyak tindakan dalam pemberitahuan + Tampilkan tombol tambahkan ke perpustakaan dan suka + + Privasi + Jeda riwayat pencarian + Bersihkan riwayat pencarian + Apakah anda yakin untuk menghapus semua riwayat penelusuran? + Aktifkan penyedia lirik KuGou + + Cadangkan dan pulihkan + Cadangkan + Pulihkan + + Tentang + Versi aplikasi + + + Sakura + Merah + Merah muda + Ungu + Ungu gelap + Nila + Biru + Biru terang + Biru Kehijau-hijauan + Hijau laut + Hijau + Hijau terang + Hijau kekuningan + Kuning + Kuning lulur + Jingga + Jingga gelap + Cokelat + Biru keabu-abuan + + + Caru + Cari di YouTube Music… + Cari di perpustakaan… + + + Lagu yang disukai + Lagu yang diunduh + + + Rincian + Sunting + Putar radio + Putar + Putar selanjutnya + Tambahkan ke antrian + Tambahkan ke perpustakaan + Unduh + Hapus unduhan + Pindahkan ke daftar putar + Tambahkan ke daftar putar + Lihat artis + Lihat album + Ambil kembali + Bagikan + Hapus + Cari secara online + Pilih lirik lain + + + Rincian + ID media + Tipe MIME + Codecs + Kecepatan bit + Tingkat sampel + Kekerasan + Volume + Ukuran berkas + Tidak diketahui + + Sunting lirik + Cari lirik + Pilih lirik + + Sunting lagu + Judul lagu + Artis lagu + Judul lagu wajib diisi. + Artis lagu wajib diisi. + Simpan + + Buat daftar putar + Nama daftar putar + Nama daftar putar wajib diisi. + + Sunting artis + Nama artis + Nama artis wajib diisi. + + Artis duplikat + Artis %1$s sudah ada. + + Pilih daftar putar + + Sunting daftar putar + + Pilih cadangan konten + Pilih pulihkan konten + Preferensi + Basis Data + Lagu yang telah diunduh + Cadangan berhasil dibuat + Tidak dapat membuat cadangan + Gagal untuk memulihkan cadangan + + + Pemutar Musik + Unduh + + + + %d lagu + %d lagu + + + %d artis + %d artis + + + %d album + %d album + + + %d daftar putar + %d daftar putar + + + + Mencoba kembali + Putar + Putar semua + Radio + Acak + Salin stacktrace + Laporkan + Laporkan di GitHub + + + Tanggal ditambahkan + Nama + Artis + Tahun + Hitungan lagu + Ukuran + Waktu dimainkan + + + + %d lagu telah dihapus. + %d lagu telah dihapus. + + + %d dipilih + + Kembali + Tidak dapat mengidentifikasi url ini. + + %d lagu akan diputar selanjutnya + %d lagu akan diputar selanjutnya + + + %d artis akan diputar selanjutnya + %d artis akan diputar selanjutnya + + + %d album akan diputar selanjutnya + %d album akan diputar selanjutnya + + + %d daftar putar akan diputar selanjutnya + %d daftar putar akan diputar selanjutnya + + Dipilih akan diputar selanjutnya + + %d lagu ditambahkan ke antrian + %d lagu ditambahkan ke antrian + + + %d artis ditambahkan ke antrian + %d artis ditambahkan ke antrian + + + %d album ditambahkan ke antrian + %d album ditambahkan ke antrian + + + %d daftar putar ditambahkan ke antrian + %d daftar putar ditambahkan ke antrian + + Dipilih ditambahkan ke antrean + Ditambahkan ke perpustakaan + Dihapus dari perpustakaan + Daftar putar dipindahkan + Ditambahkan ke %1$s + + Mulai mengunduh %d lagu + Mulai mengunduh %d lagu + + Unduhan dihapus + Lihat + + + Suka + Hapus suka + Tambahkan ke perpustakaan + Hapus dari perpustakaan + + + Semua + Lagu + Video + Album + Artis + Daftar putar + Daftar putar komunitas + Daftar putar unggulan + + System default + Dari perpustakaan anda + + + Maaf, itu seharusnya tidak terjadi. + Disalin ke papan klip + + + Tidak ada stream yang tersedia + Tidak ada koneksi jaringan + Waktu habis + Kesalahan yang tidak diketahui + + + Semua lagu + Lagu yang telah dicari + + + Lirik tidak ditemukan + From 65c14e1faf8ebf009d592b991b500183fd1fd097 Mon Sep 17 00:00:00 2001 From: Zion Huang Date: Sun, 6 Nov 2022 15:04:55 +0800 Subject: [PATCH 08/17] Update readme --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 17ec310cf..c7e720992 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,12 @@ Use other music scrobbler apps. I recommend [Pano Scrobbler](https://play.google *InnerTune* supports SAF. You can find the provider in Android native file manager. You can also use [Material Files](https://play.google.com/store/apps/details?id=me.zhanghai.android.files) with [instruction](https://github.com/z-huang/InnerTune/issues/117#issuecomment-1295090708) (recommended). +### Q: Why InnerTune isn't showing in Android Auto? + +1. Go to Android Auto's settings and tap multiple times on the version in the bottom to enable developer settings +2. In the three dots menu at the top-right of the screen, click "Developer settings" +3. Enable "Unknown sources" + ## Contribution ### Contributing Translations From f7e40c34915197d76016d3a781fa8729e1834ce7 Mon Sep 17 00:00:00 2001 From: Zion Huang Date: Sun, 6 Nov 2022 17:47:45 +0800 Subject: [PATCH 09/17] Add F-Droid badge in readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c7e720992..ad1124f23 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ Make your own music library with any song from YouTube Music. No ads, free, and simple. +[Get it on F-Droid](https://f-droid.org/packages/com.zionhuang.music) [](https://apt.izzysoft.de/fdroid/index/apk/com.zionhuang.music) [![Latest release](https://img.shields.io/github/v/release/z-huang/InnerTune?include_prereleases)](https://github.com/z-huang/music/releases) From a34d7cb482c9cf0c3d99d4aa0ecc5594d0882fbb Mon Sep 17 00:00:00 2001 From: Sdarfeesh <50188628+Sdarfeesh@users.noreply.github.com> Date: Sun, 6 Nov 2022 21:39:23 +0800 Subject: [PATCH 10/17] Update Simplified Chinese Translation --- app/src/main/res/values-zh-rCN/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 77301c113..12d9ecd9c 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -26,7 +26,7 @@ 靠右 内容 - Login + 登录 默认内容语言 默认内容国家 启用代理 From 7274578ccd5cb680e0f08e28af74e8fc18fb40fe Mon Sep 17 00:00:00 2001 From: HiSubway Date: Tue, 8 Nov 2022 01:58:26 +0900 Subject: [PATCH 11/17] Update app name in Fastlane description --- fastlane/metadata/android/ja/full_description.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastlane/metadata/android/ja/full_description.txt b/fastlane/metadata/android/ja/full_description.txt index 3828042ab..c5993c415 100644 --- a/fastlane/metadata/android/ja/full_description.txt +++ b/fastlane/metadata/android/ja/full_description.txt @@ -1,4 +1,4 @@ -このアプリを使ったら、無料の音楽ストリーミングサービスを手に入れたようなものです。YouTube Musicの音楽を聴いたり、自分だけのライブラリを作り上げたりできます。さらには、音楽をダウンロードし、オフラインで再生することもできます。また、プレイリストを作って、曲を整理することも可能です。簡単で、実用的で、広告のないアプリで、誰もが無料で音楽を聴くことができるようにすることが、Musicの目的です。 +このアプリを使ったら、無料の音楽ストリーミングサービスを手に入れたようなものです。YouTube Musicの音楽を聴いたり、自分だけのライブラリを作り上げたりできます。さらには、音楽をダウンロードし、オフラインで再生することもできます。また、プレイリストを作って、曲を整理することも可能です。簡単で、実用的で、広告のないアプリで、誰もが無料で音楽を聴くことができるようにすることが、InnerTuneの目的です。
注意: From f52004669bb6533101685bd220b8a33c98f377c2 Mon Sep 17 00:00:00 2001 From: HiSubway Date: Tue, 8 Nov 2022 02:20:35 +0900 Subject: [PATCH 12/17] Update Japanese translation --- app/src/main/res/values-ja-rJP/strings.xml | 122 ++++++++++----------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index e8ad6f2e2..6999ef7d5 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -19,14 +19,14 @@ オフ システムに従う 起動時に開くタブ - Customize navigation tabs - Lyrics text position - Left - Center - Right + ナビゲーションタブのカスタマイズ + 歌詞テキストの位置 + + 中央 + コンテンツ - Login + ログイン デフォルトのコンテンツの言語 デフォルトのコンテンツの国 プロキシを有効化 @@ -39,34 +39,34 @@ 自動 - Persistent queue - Skip silence - Audio normalization + 再生キューを保持 + 無音部分をスキップ + オーディオノーマライゼーション イコライザー - Storage - View downloaded files in SAF - This may not work in some devices - Cache - Max image cache size - Clear image cache - Max song cache size - %s used + ストレージ + SAFにダウンロードされたファイルを表示 + 一部の端末では動作しない場合があります + キャッシュ + 画像の最大キャッシュサイズ + 画像のキャッシュを削除 + 曲の最大キャッシュサイズ + %s 使用中 一般 自動ダウンロード ライブラリに追加したら曲をダウンロードする 曲を自動でライブラリに追加 再生が終了したら曲をライブラリに追加する - 再生時にボトムプレイヤーを展開する - More actions in notification - Show add to library and like buttons + プレイヤーを再生時に展開 + 通知にもっとアクションを表示 + 「ライブラリに追加」と「いいね」ボタンを表示する プライバシー 履歴の記録を一時停止 履歴を削除 すべての検索履歴を削除しますか? - Enable KuGou lyrics provider + 酷狗からの歌詞取得を有効化 バックアップとリストア バックアップ @@ -76,24 +76,24 @@ アプリのバージョン - 桜色 - + サクラ + レッド ピンク - - 深紫 - - + パープル + ディープパープル + インディゴ + ブルー ライトブルー シアン - 青緑 - + ティール + グリーン ライトグリーン - 黄緑 - + ライム + イエロー アンバー オレンジ - 深いオレンジ - + ディープオレンジ + ブラウン ブルーグレー @@ -102,8 +102,8 @@ ライブラリを検索… - Liked songs - Downloaded songs + いいねした曲 + ダウンロードした曲 Details @@ -122,24 +122,24 @@ 再取得 共有 削除 - Search online - Choose other lyrics + オンラインで検索 + ほかの歌詞を選択 - Details - Media id - MIME type - Codecs - Bitrate - Sample rate - Loudness - Volume - File size - Unknown - - Edit lyrics - Search lyrics - Choose lyrics + 詳細 + メディアID + MIMEタイプ + コーデック + ビットレート + サンプリングレート + ラウドネス + ボリューム + ファイルサイズ + 不明 + + 歌詞を編集 + 歌詞を検索 + 歌詞を選択 曲を編集 曲名 @@ -264,10 +264,10 @@ 見る - Like - Remove like + いいね + いいねを削除 ライブラリに追加 - Remove from library + ライブラリから削除 すべて @@ -287,15 +287,15 @@ クリップボードにコピー - No stream available - No network connection - Timeout - Unknown error + ストリームが利用できません + ネットワーク接続がありません + タイムアウトしました + 不明なエラーです - All songs - Searched songs + すべての曲 + 検索した曲 - Lyrics not found + 歌詞が見つかりません From 7de406fac72a861ed7611fcb99f7560adbc08937 Mon Sep 17 00:00:00 2001 From: gidano Date: Sat, 12 Nov 2022 09:31:53 +0100 Subject: [PATCH 13/17] Update HU strings HU text correction --- app/src/main/res/values-hu/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index e80ba5db0..86d4be3f3 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -205,7 +205,7 @@ Jelentés GitHub-on - Dátum hozzáadva + Létrehozás dátuma Név Előadó Év From 2608e5e0bd546d67d5840f20b332f88ae905268d Mon Sep 17 00:00:00 2001 From: Zion Huang Date: Thu, 24 Nov 2022 12:52:03 +0800 Subject: [PATCH 14/17] Update YouTube search filter key (#436) --- .../src/main/java/com/zionhuang/innertube/YouTube.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/innertube/src/main/java/com/zionhuang/innertube/YouTube.kt b/innertube/src/main/java/com/zionhuang/innertube/YouTube.kt index 7f33cb81d..2b739f22a 100644 --- a/innertube/src/main/java/com/zionhuang/innertube/YouTube.kt +++ b/innertube/src/main/java/com/zionhuang/innertube/YouTube.kt @@ -174,12 +174,12 @@ object YouTube { @JvmInline value class SearchFilter(val value: String) { companion object { - val FILTER_SONG = SearchFilter("EgWKAQIIAWoMEAMQDhAEEAkQChAF") - val FILTER_VIDEO = SearchFilter("EgWKAQIQAWoMEAMQDhAEEAkQChAF") - val FILTER_ALBUM = SearchFilter("EgWKAQIYAWoMEAMQDhAEEAkQChAF") - val FILTER_ARTIST = SearchFilter("EgWKAQIgAWoMEAMQDhAEEAkQChAF") - val FILTER_FEATURED_PLAYLIST = SearchFilter("EgeKAQQoADgBagwQAxAOEAQQCRAKEAU%3D") - val FILTER_COMMUNITY_PLAYLIST = SearchFilter("EgeKAQQoAEABagwQAxAOEAQQCRAKEAU%3D") + val FILTER_SONG = SearchFilter("EgWKAQIIAWoKEAkQBRAKEAMQBA%3D%3D") + val FILTER_VIDEO = SearchFilter("EgWKAQIQAWoKEAkQChAFEAMQBA%3D%3D") + val FILTER_ALBUM = SearchFilter("EgWKAQIYAWoKEAkQChAFEAMQBA%3D%3D") + val FILTER_ARTIST = SearchFilter("EgWKAQIgAWoKEAkQChAFEAMQBA%3D%3D") + val FILTER_FEATURED_PLAYLIST = SearchFilter("EgeKAQQoADgBagwQDhAKEAMQBRAJEAQ%3D") + val FILTER_COMMUNITY_PLAYLIST = SearchFilter("EgeKAQQoAEABagoQAxAEEAoQCRAF") } } From 7a28cf90a50d9fb36130ab77542cce028e7e3ac6 Mon Sep 17 00:00:00 2001 From: Zion Huang Date: Sun, 27 Nov 2022 12:58:41 +0800 Subject: [PATCH 15/17] Fix #432 --- .../com/zionhuang/innertube/models/response/PlayerResponse.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/innertube/src/main/java/com/zionhuang/innertube/models/response/PlayerResponse.kt b/innertube/src/main/java/com/zionhuang/innertube/models/response/PlayerResponse.kt index a98f0291f..678e62906 100644 --- a/innertube/src/main/java/com/zionhuang/innertube/models/response/PlayerResponse.kt +++ b/innertube/src/main/java/com/zionhuang/innertube/models/response/PlayerResponse.kt @@ -34,7 +34,7 @@ data class PlayerResponse( @Serializable data class StreamingData( - val formats: List, + val formats: List?, val adaptiveFormats: List, val expiresInSeconds: Int, ) { From deb7268c15491a02b5653273ff47cbd47860ae68 Mon Sep 17 00:00:00 2001 From: Zion Huang Date: Tue, 27 Dec 2022 09:38:26 +0800 Subject: [PATCH 16/17] Hide login button --- app/src/main/res/xml/pref_content.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/xml/pref_content.xml b/app/src/main/res/xml/pref_content.xml index b9ec68b9e..4b362a93c 100644 --- a/app/src/main/res/xml/pref_content.xml +++ b/app/src/main/res/xml/pref_content.xml @@ -5,6 +5,7 @@ Date: Tue, 27 Dec 2022 11:12:25 +0800 Subject: [PATCH 17/17] Bump version to 0.4.4 --- app/build.gradle.kts | 4 ++-- .../metadata/android/en-US/changelogs/15.txt | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/15.txt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9d1ecfb8d..3248af4c7 100755 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -15,8 +15,8 @@ android { applicationId = "com.zionhuang.music" minSdk = 24 targetSdk = 32 - versionCode = 14 - versionName = "0.4.3" + versionCode = 15 + versionName = "0.4.4" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" javaCompileOptions { annotationProcessorOptions { diff --git a/fastlane/metadata/android/en-US/changelogs/15.txt b/fastlane/metadata/android/en-US/changelogs/15.txt new file mode 100644 index 000000000..1257ef4c1 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/15.txt @@ -0,0 +1,16 @@ +Improvement + +- Add shuffle button in Android Auto + +Fixed + +- Fix apostrophe displayed as HTML entity in lyrics #401 +- Update YouTube search filter key #436 +- Fix #432 + +Translation + +- Update Hungarian translation #407 #425 +- Add Indonesia translation #415 +- Update Simplified Chinese translation #416 +- Update Japanese translation #419 \ No newline at end of file