Обновление 0.5.4.1

Добавлен прокси,
Перевод оставшихся слов и исправление орфографических ошибок.
Удаление трека из очереди свайпом
This commit is contained in:
2023-08-25 21:50:48 +05:00
parent 013a7127e7
commit 6dc89ff95e
12 changed files with 215 additions and 31 deletions

View File

@@ -12,7 +12,7 @@ android {
minSdk = 21
targetSdk = 33
versionCode = 20
versionName = "0.5.4ru"
versionName = "0.5.4.1rus"
}
splits {
@@ -74,6 +74,7 @@ dependencies {
implementation(projects.composeRouting)
implementation(projects.composeReordering)
implementation(libs.compose.activity)
implementation(libs.compose.foundation)
implementation(libs.compose.ui)
@@ -85,8 +86,10 @@ dependencies {
implementation(libs.palette)
implementation(libs.exoplayer)
implementation(libs.exoplayer.okhttp)
implementation(libs.room)
implementation("androidx.media3:media3-datasource-okhttp:1.0.0-alpha03")
kapt(libs.room.compiler)
implementation(projects.innertube)

View File

@@ -60,6 +60,16 @@ import com.valentinilk.shimmer.LocalShimmerTheme
import com.valentinilk.shimmer.defaultShimmerTheme
import it.hamy.compose.persist.PersistMap
import it.hamy.compose.persist.PersistMapOwner
import it.hamy.innertube.utils.ProxyPreferenceItem
import it.hamy.innertube.utils.ProxyPreferences
import it.hamy.muza.utils.isProxyEnabledKey
import it.hamy.muza.utils.proxyHostNameKey
import it.hamy.muza.utils.proxyModeKey
import it.hamy.muza.utils.proxyPortKey
import java.net.Proxy
import it.hamy.innertube.Innertube
import it.hamy.innertube.models.bodies.BrowseBody
import it.hamy.innertube.requests.playlistPage
@@ -127,6 +137,7 @@ class MainActivity : ComponentActivity(), PersistMapOwner {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@Suppress("DEPRECATION", "UNCHECKED_CAST")
persistMap = lastCustomNonConfigurationInstance as? PersistMap ?: PersistMap()
@@ -134,6 +145,17 @@ class MainActivity : ComponentActivity(), PersistMapOwner {
val launchedFromNotification = intent?.extras?.getBoolean("expandPlayerBottomSheet") == true
with(preferences){
if(getBoolean(isProxyEnabledKey,false)) {
val hostName = getString(proxyHostNameKey,null)
val proxyPort = getInt(proxyPortKey, 8080)
val proxyMode = getEnum(proxyModeKey, Proxy.Type.HTTP)
hostName?.let { hName->
ProxyPreferences.preference = ProxyPreferenceItem(hName,proxyPort,proxyMode)
}
}
}
setContent {
val coroutineScope = rememberCoroutineScope()
val isSystemInDarkTheme = isSystemInDarkTheme()

View File

@@ -43,7 +43,14 @@ import androidx.media3.common.Player
import androidx.media3.common.Timeline
import androidx.media3.database.StandaloneDatabaseProvider
import androidx.media3.datasource.DataSource
import androidx.media3.datasource.DefaultHttpDataSource
import androidx.media3.datasource.okhttp.OkHttpDataSource
import it.hamy.innertube.utils.ProxyPreferences
import okhttp3.OkHttpClient
import java.net.InetSocketAddress
import java.net.Proxy
import java.time.Duration
import androidx.media3.datasource.ResolvingDataSource
import androidx.media3.datasource.cache.Cache
import androidx.media3.datasource.cache.CacheDataSource
@@ -737,12 +744,24 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene
}
}
private fun okHttpClient() : OkHttpClient{
ProxyPreferences.preference?.let{
return OkHttpClient.Builder()
.proxy(Proxy(it.proxyMode,InetSocketAddress(it.proxyHost,it.proxyPort)))
.connectTimeout(Duration.ofSeconds(16))
.readTimeout(Duration.ofSeconds(8))
.build()
}
return OkHttpClient.Builder()
.connectTimeout(Duration.ofSeconds(16))
.readTimeout(Duration.ofSeconds(8))
.build()
}
private fun createCacheDataSource(): DataSource.Factory {
return CacheDataSource.Factory().setCache(cache).apply {
setUpstreamDataSourceFactory(
DefaultHttpDataSource.Factory()
.setConnectTimeoutMs(16000)
.setReadTimeoutMs(8000)
OkHttpDataSource.Factory(okHttpClient())
.setUserAgent("Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0")
)
}

View File

@@ -89,7 +89,7 @@ fun InHistoryMediaItemMenu(
if (isHiding) {
ConfirmationDialog(
text = "Do you really want to hide this song? Its playback time and cache will be wiped.\nThis action is irreversible.",
text = "Вы действительно хотите скрыть эту песню? Время воспроизведения и кэш будут удалены.\n" + "Это действие необратимо",
onDismiss = { isHiding = false },
onConfirm = {
onDismiss()
@@ -330,7 +330,7 @@ fun MediaItemMenu(
if (isCreatingNewPlaylist && onAddToPlaylist != null) {
TextFieldDialog(
hintText = "Enter the playlist name",
hintText = "Введите название плейлиста",
onDismiss = { isCreatingNewPlaylist = false },
onDone = { text ->
onDismiss()
@@ -365,7 +365,7 @@ fun MediaItemMenu(
if (onAddToPlaylist != null) {
SecondaryTextButton(
text = "New playlist",
text = "Новый плейлист",
onClick = { isCreatingNewPlaylist = true },
alternative = true
)
@@ -377,7 +377,7 @@ fun MediaItemMenu(
MenuEntry(
icon = R.drawable.playlist,
text = playlistPreview.playlist.name,
secondaryText = "${playlistPreview.songCount} songs",
secondaryText = "${playlistPreview.songCount} песен",
onClick = {
onDismiss()
onAddToPlaylist(playlistPreview.playlist, playlistPreview.songCount)
@@ -463,7 +463,7 @@ fun MediaItemMenu(
onStartRadio?.let { onStartRadio ->
MenuEntry(
icon = R.drawable.radio,
text = "Start radio",
text = "Включить радио",
onClick = {
onDismiss()
onStartRadio()
@@ -474,7 +474,7 @@ fun MediaItemMenu(
onPlayNext?.let { onPlayNext ->
MenuEntry(
icon = R.drawable.play_skip_forward,
text = "Play next",
text = "Следующая",
onClick = {
onDismiss()
onPlayNext()
@@ -485,7 +485,7 @@ fun MediaItemMenu(
onEnqueue?.let { onEnqueue ->
MenuEntry(
icon = R.drawable.enqueue,
text = "Enqueue",
text = "В очередь",
onClick = {
onDismiss()
onEnqueue()
@@ -496,7 +496,7 @@ fun MediaItemMenu(
onGoToEqualizer?.let { onGoToEqualizer ->
MenuEntry(
icon = R.drawable.equalizer,
text = "Equalizer",
text = "Эквалайзер",
onClick = {
onDismiss()
onGoToEqualizer()
@@ -520,9 +520,9 @@ fun MediaItemMenu(
if (isShowingSleepTimerDialog) {
if (sleepTimerMillisLeft != null) {
ConfirmationDialog(
text = "Do you want to stop the sleep timer?",
cancelText = "No",
confirmText = "Stop",
text = "Вы хотите отключить таймер сна?",
cancelText = "нет",
confirmText = "отключить",
onDismiss = { isShowingSleepTimerDialog = false },
onConfirm = {
binder?.cancelSleepTimer()
@@ -538,7 +538,7 @@ fun MediaItemMenu(
}
BasicText(
text = "Set sleep timer",
text = "Установить таймер сна",
style = typography.s.semiBold,
modifier = Modifier
.padding(vertical = 8.dp, horizontal = 24.dp)
@@ -570,13 +570,13 @@ fun MediaItemMenu(
Box(contentAlignment = Alignment.Center) {
BasicText(
text = "88h 88m",
text = "88ч 88м",
style = typography.s.semiBold,
modifier = Modifier
.alpha(0f)
)
BasicText(
text = "${amount / 6}h ${(amount % 6) * 10}m",
text = "${amount / 6}ч ${(amount % 6) * 10}м",
style = typography.s.semiBold
)
}
@@ -603,12 +603,12 @@ fun MediaItemMenu(
.fillMaxWidth()
) {
DialogTextButton(
text = "Cancel",
text = "Отмена",
onClick = { isShowingSleepTimerDialog = false }
)
DialogTextButton(
text = "Set",
text = "Установить",
enabled = amount > 0,
primary = true,
onClick = {
@@ -623,12 +623,12 @@ fun MediaItemMenu(
MenuEntry(
icon = R.drawable.alarm,
text = "Sleep timer",
text = "Таймер сна",
onClick = { isShowingSleepTimerDialog = true },
trailingContent = sleepTimerMillisLeft?.let {
{
BasicText(
text = "${formatAsDuration(it)} left",
text = "Осталось ${formatAsDuration(it)}",
style = typography.xxs.medium,
modifier = modifier
.background(
@@ -646,7 +646,7 @@ fun MediaItemMenu(
if (onAddToPlaylist != null) {
MenuEntry(
icon = R.drawable.playlist,
text = "Add to playlist",
text = "Добавить в плейлист",
onClick = { isViewingPlaylists = true },
trailingContent = {
Image(
@@ -666,7 +666,7 @@ fun MediaItemMenu(
albumInfo?.let { (albumId) ->
MenuEntry(
icon = R.drawable.disc,
text = "Go to album",
text = "Перейти в альбом",
onClick = {
onDismiss()
onGoToAlbum(albumId)
@@ -679,7 +679,7 @@ fun MediaItemMenu(
artistsInfo?.forEach { (authorId, authorName) ->
MenuEntry(
icon = R.drawable.person,
text = "More of $authorName",
text = "Больше от $authorName",
onClick = {
onDismiss()
onGoToArtist(authorId)
@@ -691,7 +691,7 @@ fun MediaItemMenu(
onRemoveFromQueue?.let { onRemoveFromQueue ->
MenuEntry(
icon = R.drawable.trash,
text = "Remove from queue",
text = "Убрать из очереди",
onClick = {
onDismiss()
onRemoveFromQueue()
@@ -702,7 +702,7 @@ fun MediaItemMenu(
onRemoveFromPlaylist?.let { onRemoveFromPlaylist ->
MenuEntry(
icon = R.drawable.trash,
text = "Remove from playlist",
text = "Удалить из плейлиста",
onClick = {
onDismiss()
onRemoveFromPlaylist()
@@ -713,7 +713,7 @@ fun MediaItemMenu(
onHideFromDatabase?.let { onHideFromDatabase ->
MenuEntry(
icon = R.drawable.trash,
text = "Hide",
text = "Скрыть",
onClick = onHideFromDatabase
)
}
@@ -721,7 +721,7 @@ fun MediaItemMenu(
onRemoveFromQuickPicks?.let {
MenuEntry(
icon = R.drawable.trash,
text = "Hide from \"Quick picks\"",
text = "Скрыть из \"Обзора\"",
onClick = {
onDismiss()
onRemoveFromQuickPicks()

View File

@@ -235,7 +235,7 @@ fun Lyrics(
.align(Alignment.TopCenter)
) {
BasicText(
text = "${if (isShowingSynchronizedLyrics) "Синхронизирован текст" else "Т"}екст песни не доступен",
text = "${if (isShowingSynchronizedLyrics) "Синхронизированный т" else "Т"}екст не доступен",
style = typography.xs.center.medium.color(PureBlackColorPalette.text),
modifier = Modifier
.background(Color.Black.copy(0.4f))

View File

@@ -83,6 +83,13 @@ import it.hamy.muza.utils.smoothScrollToTop
import it.hamy.muza.utils.windows
import kotlinx.coroutines.launch
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.draggable
import androidx.compose.foundation.gestures.rememberDraggableState
import androidx.compose.foundation.layout.offset
import androidx.compose.ui.unit.IntOffset
import kotlin.math.roundToInt
@ExperimentalFoundationApi
@ExperimentalAnimationApi
@Composable
@@ -202,6 +209,7 @@ fun Queue(
key = { it.uid.hashCode() }
) { window ->
val isPlayingThisMediaItem = mediaItemIndex == window.firstPeriodIndex
var offsetX by remember { mutableStateOf(0f) }
SongItem(
song = window.mediaItem,
@@ -283,6 +291,23 @@ fun Queue(
reorderingState = reorderingState,
index = window.firstPeriodIndex
)
.draggable(
orientation = Orientation.Horizontal,
state = rememberDraggableState(onDelta = { delta ->
if (isPlayingThisMediaItem) return@rememberDraggableState
offsetX += delta
}),
onDragStopped = { velocity ->
if ((offsetX <= -300.0f && velocity <= -3000.0f) || (offsetX >= 300.0f && velocity >= 3000.0f)) {
binder.player.removeMediaItem(window.firstPeriodIndex)
} else {
offsetX = 0f
}
}
)
.offset{ IntOffset(offsetX.roundToInt(), 0) }
)
}

View File

@@ -10,6 +10,7 @@ import android.provider.Settings
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsetsSides
@@ -38,10 +39,17 @@ import it.hamy.muza.utils.isAtLeastAndroid12
import it.hamy.muza.utils.isAtLeastAndroid6
import it.hamy.muza.utils.isIgnoringBatteryOptimizations
import it.hamy.muza.utils.isInvincibilityEnabledKey
import it.hamy.muza.utils.isProxyEnabledKey
import it.hamy.muza.utils.pauseSearchHistoryKey
import it.hamy.muza.utils.proxyHostNameKey
import it.hamy.muza.utils.proxyModeKey
import it.hamy.muza.utils.proxyPortKey
import it.hamy.muza.utils.rememberPreference
import it.hamy.muza.utils.toast
import kotlinx.coroutines.flow.distinctUntilChanged
import java.net.Proxy
@SuppressLint("BatteryLife")
@ExperimentalAnimationApi
@@ -72,6 +80,14 @@ fun OtherSettings() {
var isInvincibilityEnabled by rememberPreference(isInvincibilityEnabledKey, false)
var isProxyEnabled by rememberPreference(isProxyEnabledKey, false)
var proxyHost by rememberPreference(proxyHostNameKey, defaultValue = "")
var proxyPort by rememberPreference(proxyPortKey, defaultValue = 1080)
var proxyMode by rememberPreference(proxyModeKey, defaultValue = Proxy.Type.HTTP)
var isIgnoringBatteryOptimizations by remember {
mutableStateOf(context.isIgnoringBatteryOptimizations)
}
@@ -178,5 +194,33 @@ fun OtherSettings() {
isChecked = isInvincibilityEnabled,
onCheckedChange = { isInvincibilityEnabled = it }
)
SettingsEntryGroupText(title = "PROXY")
SwitchSettingEntry(
title = "HTTP Proxy",
text = "Enable HTTP Proxy",
isChecked = isProxyEnabled,
onCheckedChange = { isProxyEnabled = it }
)
AnimatedVisibility(visible = isProxyEnabled) {
Column {
EnumValueSelectorSettingsEntry(title = "Proxy",
selectedValue = proxyMode, onValueSelected = {proxyMode = it})
TextDialogSettingEntry(
title = "Хост",
text = "Введите http хост",
currentText = proxyHost,
onTextSave = { proxyHost = it })
TextDialogSettingEntry(
title = "Порт",
text = "Введите порт",
currentText = proxyPort.toString(),
onTextSave = { proxyPort = it.toIntOrNull() ?: 1080 })
}
}
}
}

View File

@@ -21,17 +21,20 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import it.hamy.compose.routing.RouteHandler
import it.hamy.muza.R
import it.hamy.muza.ui.components.themed.Scaffold
import it.hamy.muza.ui.components.themed.Switch
import it.hamy.muza.ui.components.themed.TextFieldDialog
import it.hamy.muza.ui.components.themed.ValueSelectorDialog
import it.hamy.muza.ui.screens.globalRoutes
import it.hamy.muza.ui.styling.LocalAppearance
import it.hamy.muza.utils.color
import it.hamy.muza.utils.secondary
import it.hamy.muza.utils.semiBold
import it.hamy.muza.utils.toast
@ExperimentalFoundationApi
@ExperimentalAnimationApi
@@ -193,6 +196,37 @@ fun SettingsEntry(
}
}
@Composable
fun TextDialogSettingEntry(
title: String,
text: String,
currentText: String,
onTextSave: (String) -> Unit,
modifier: Modifier = Modifier,
isEnabled: Boolean = true
) {
var showDialog by remember { mutableStateOf(false) }
val context = LocalContext.current
if (showDialog) {
TextFieldDialog(hintText =title ,
onDismiss = { showDialog = false },
onDone ={value->
onTextSave(value)
context.toast("Сохранено!")
} , doneText = "Save", initialTextInput = currentText)
}
SettingsEntry(
title = title,
text = text,
isEnabled = isEnabled,
onClick = { showDialog = true },
trailingContent = { },
modifier = modifier
)
}
@Composable
fun SettingsDescription(
text: String,

View File

@@ -38,6 +38,10 @@ const val homeScreenTabIndexKey = "homeScreenTabIndex"
const val searchResultScreenTabIndexKey = "searchResultScreenTabIndex"
const val artistScreenTabIndexKey = "artistScreenTabIndex"
const val pauseSearchHistoryKey = "pauseSearchHistory"
const val isProxyEnabledKey = "isProxyEnabled"
const val proxyHostNameKey = "proxyHostname"
const val proxyPortKey = "proxyPortKey"
const val proxyModeKey = "proxyModeKey"
inline fun <reified T : Enum<T>> SharedPreferences.getEnum(
key: String,

View File

@@ -15,8 +15,12 @@ import io.ktor.serialization.kotlinx.json.json
import it.hamy.innertube.models.NavigationEndpoint
import it.hamy.innertube.models.Runs
import it.hamy.innertube.models.Thumbnail
import it.hamy.innertube.utils.ProxyPreferences
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import java.net.InetSocketAddress
import java.net.Proxy
object Innertube {
val client = HttpClient(OkHttp) {
@@ -44,6 +48,19 @@ object Innertube {
parameters.append("prettyPrint", "false")
}
}
ProxyPreferences.preference?.let {
engine {
proxy = Proxy(
it.proxyMode,
InetSocketAddress(
it.proxyHost,
it.proxyPort
)
)
}
}
}
internal const val browse = "/youtubei/v1/browse"

View File

@@ -0,0 +1,13 @@
package it.hamy.innertube.utils
import java.net.Proxy
object ProxyPreferences {
var preference: ProxyPreferenceItem? = null
}
data class ProxyPreferenceItem(
var proxyHost: String,
var proxyPort: Int,
var proxyMode: Proxy.Type
)

View File

@@ -7,6 +7,7 @@ dependencyResolutionManagement {
google()
mavenCentral()
maven { setUrl("https://jitpack.io") }
}
versionCatalogs {
@@ -36,6 +37,8 @@ dependencyResolutionManagement {
version("media3", "1.0.0-beta03")
library("exoplayer", "androidx.media3", "media3-exoplayer").versionRef("media3")
library("exoplayer-okhttp", "androidx.media3", "media3-datasource-okhttp").versionRef("media3")
version("ktor", "2.1.2")
library("ktor-client-core", "io.ktor", "ktor-client-core").versionRef("ktor")