Обновление 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 minSdk = 21
targetSdk = 33 targetSdk = 33
versionCode = 20 versionCode = 20
versionName = "0.5.4ru" versionName = "0.5.4.1rus"
} }
splits { splits {
@@ -74,6 +74,7 @@ dependencies {
implementation(projects.composeRouting) implementation(projects.composeRouting)
implementation(projects.composeReordering) implementation(projects.composeReordering)
implementation(libs.compose.activity) implementation(libs.compose.activity)
implementation(libs.compose.foundation) implementation(libs.compose.foundation)
implementation(libs.compose.ui) implementation(libs.compose.ui)
@@ -85,8 +86,10 @@ dependencies {
implementation(libs.palette) implementation(libs.palette)
implementation(libs.exoplayer) implementation(libs.exoplayer)
implementation(libs.exoplayer.okhttp)
implementation(libs.room) implementation(libs.room)
implementation("androidx.media3:media3-datasource-okhttp:1.0.0-alpha03")
kapt(libs.room.compiler) kapt(libs.room.compiler)
implementation(projects.innertube) implementation(projects.innertube)

View File

@@ -60,6 +60,16 @@ import com.valentinilk.shimmer.LocalShimmerTheme
import com.valentinilk.shimmer.defaultShimmerTheme import com.valentinilk.shimmer.defaultShimmerTheme
import it.hamy.compose.persist.PersistMap import it.hamy.compose.persist.PersistMap
import it.hamy.compose.persist.PersistMapOwner 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.Innertube
import it.hamy.innertube.models.bodies.BrowseBody import it.hamy.innertube.models.bodies.BrowseBody
import it.hamy.innertube.requests.playlistPage import it.hamy.innertube.requests.playlistPage
@@ -127,6 +137,7 @@ class MainActivity : ComponentActivity(), PersistMapOwner {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@Suppress("DEPRECATION", "UNCHECKED_CAST") @Suppress("DEPRECATION", "UNCHECKED_CAST")
persistMap = lastCustomNonConfigurationInstance as? PersistMap ?: PersistMap() persistMap = lastCustomNonConfigurationInstance as? PersistMap ?: PersistMap()
@@ -134,6 +145,17 @@ class MainActivity : ComponentActivity(), PersistMapOwner {
val launchedFromNotification = intent?.extras?.getBoolean("expandPlayerBottomSheet") == true 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 { setContent {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val isSystemInDarkTheme = isSystemInDarkTheme() val isSystemInDarkTheme = isSystemInDarkTheme()

View File

@@ -43,7 +43,14 @@ import androidx.media3.common.Player
import androidx.media3.common.Timeline import androidx.media3.common.Timeline
import androidx.media3.database.StandaloneDatabaseProvider import androidx.media3.database.StandaloneDatabaseProvider
import androidx.media3.datasource.DataSource 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.ResolvingDataSource
import androidx.media3.datasource.cache.Cache import androidx.media3.datasource.cache.Cache
import androidx.media3.datasource.cache.CacheDataSource 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 { private fun createCacheDataSource(): DataSource.Factory {
return CacheDataSource.Factory().setCache(cache).apply { return CacheDataSource.Factory().setCache(cache).apply {
setUpstreamDataSourceFactory( setUpstreamDataSourceFactory(
DefaultHttpDataSource.Factory() OkHttpDataSource.Factory(okHttpClient())
.setConnectTimeoutMs(16000)
.setReadTimeoutMs(8000)
.setUserAgent("Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0") .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) { if (isHiding) {
ConfirmationDialog( 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 }, onDismiss = { isHiding = false },
onConfirm = { onConfirm = {
onDismiss() onDismiss()
@@ -330,7 +330,7 @@ fun MediaItemMenu(
if (isCreatingNewPlaylist && onAddToPlaylist != null) { if (isCreatingNewPlaylist && onAddToPlaylist != null) {
TextFieldDialog( TextFieldDialog(
hintText = "Enter the playlist name", hintText = "Введите название плейлиста",
onDismiss = { isCreatingNewPlaylist = false }, onDismiss = { isCreatingNewPlaylist = false },
onDone = { text -> onDone = { text ->
onDismiss() onDismiss()
@@ -365,7 +365,7 @@ fun MediaItemMenu(
if (onAddToPlaylist != null) { if (onAddToPlaylist != null) {
SecondaryTextButton( SecondaryTextButton(
text = "New playlist", text = "Новый плейлист",
onClick = { isCreatingNewPlaylist = true }, onClick = { isCreatingNewPlaylist = true },
alternative = true alternative = true
) )
@@ -377,7 +377,7 @@ fun MediaItemMenu(
MenuEntry( MenuEntry(
icon = R.drawable.playlist, icon = R.drawable.playlist,
text = playlistPreview.playlist.name, text = playlistPreview.playlist.name,
secondaryText = "${playlistPreview.songCount} songs", secondaryText = "${playlistPreview.songCount} песен",
onClick = { onClick = {
onDismiss() onDismiss()
onAddToPlaylist(playlistPreview.playlist, playlistPreview.songCount) onAddToPlaylist(playlistPreview.playlist, playlistPreview.songCount)
@@ -463,7 +463,7 @@ fun MediaItemMenu(
onStartRadio?.let { onStartRadio -> onStartRadio?.let { onStartRadio ->
MenuEntry( MenuEntry(
icon = R.drawable.radio, icon = R.drawable.radio,
text = "Start radio", text = "Включить радио",
onClick = { onClick = {
onDismiss() onDismiss()
onStartRadio() onStartRadio()
@@ -474,7 +474,7 @@ fun MediaItemMenu(
onPlayNext?.let { onPlayNext -> onPlayNext?.let { onPlayNext ->
MenuEntry( MenuEntry(
icon = R.drawable.play_skip_forward, icon = R.drawable.play_skip_forward,
text = "Play next", text = "Следующая",
onClick = { onClick = {
onDismiss() onDismiss()
onPlayNext() onPlayNext()
@@ -485,7 +485,7 @@ fun MediaItemMenu(
onEnqueue?.let { onEnqueue -> onEnqueue?.let { onEnqueue ->
MenuEntry( MenuEntry(
icon = R.drawable.enqueue, icon = R.drawable.enqueue,
text = "Enqueue", text = "В очередь",
onClick = { onClick = {
onDismiss() onDismiss()
onEnqueue() onEnqueue()
@@ -496,7 +496,7 @@ fun MediaItemMenu(
onGoToEqualizer?.let { onGoToEqualizer -> onGoToEqualizer?.let { onGoToEqualizer ->
MenuEntry( MenuEntry(
icon = R.drawable.equalizer, icon = R.drawable.equalizer,
text = "Equalizer", text = "Эквалайзер",
onClick = { onClick = {
onDismiss() onDismiss()
onGoToEqualizer() onGoToEqualizer()
@@ -520,9 +520,9 @@ fun MediaItemMenu(
if (isShowingSleepTimerDialog) { if (isShowingSleepTimerDialog) {
if (sleepTimerMillisLeft != null) { if (sleepTimerMillisLeft != null) {
ConfirmationDialog( ConfirmationDialog(
text = "Do you want to stop the sleep timer?", text = "Вы хотите отключить таймер сна?",
cancelText = "No", cancelText = "нет",
confirmText = "Stop", confirmText = "отключить",
onDismiss = { isShowingSleepTimerDialog = false }, onDismiss = { isShowingSleepTimerDialog = false },
onConfirm = { onConfirm = {
binder?.cancelSleepTimer() binder?.cancelSleepTimer()
@@ -538,7 +538,7 @@ fun MediaItemMenu(
} }
BasicText( BasicText(
text = "Set sleep timer", text = "Установить таймер сна",
style = typography.s.semiBold, style = typography.s.semiBold,
modifier = Modifier modifier = Modifier
.padding(vertical = 8.dp, horizontal = 24.dp) .padding(vertical = 8.dp, horizontal = 24.dp)
@@ -570,13 +570,13 @@ fun MediaItemMenu(
Box(contentAlignment = Alignment.Center) { Box(contentAlignment = Alignment.Center) {
BasicText( BasicText(
text = "88h 88m", text = "88ч 88м",
style = typography.s.semiBold, style = typography.s.semiBold,
modifier = Modifier modifier = Modifier
.alpha(0f) .alpha(0f)
) )
BasicText( BasicText(
text = "${amount / 6}h ${(amount % 6) * 10}m", text = "${amount / 6}ч ${(amount % 6) * 10}м",
style = typography.s.semiBold style = typography.s.semiBold
) )
} }
@@ -603,12 +603,12 @@ fun MediaItemMenu(
.fillMaxWidth() .fillMaxWidth()
) { ) {
DialogTextButton( DialogTextButton(
text = "Cancel", text = "Отмена",
onClick = { isShowingSleepTimerDialog = false } onClick = { isShowingSleepTimerDialog = false }
) )
DialogTextButton( DialogTextButton(
text = "Set", text = "Установить",
enabled = amount > 0, enabled = amount > 0,
primary = true, primary = true,
onClick = { onClick = {
@@ -623,12 +623,12 @@ fun MediaItemMenu(
MenuEntry( MenuEntry(
icon = R.drawable.alarm, icon = R.drawable.alarm,
text = "Sleep timer", text = "Таймер сна",
onClick = { isShowingSleepTimerDialog = true }, onClick = { isShowingSleepTimerDialog = true },
trailingContent = sleepTimerMillisLeft?.let { trailingContent = sleepTimerMillisLeft?.let {
{ {
BasicText( BasicText(
text = "${formatAsDuration(it)} left", text = "Осталось ${formatAsDuration(it)}",
style = typography.xxs.medium, style = typography.xxs.medium,
modifier = modifier modifier = modifier
.background( .background(
@@ -646,7 +646,7 @@ fun MediaItemMenu(
if (onAddToPlaylist != null) { if (onAddToPlaylist != null) {
MenuEntry( MenuEntry(
icon = R.drawable.playlist, icon = R.drawable.playlist,
text = "Add to playlist", text = "Добавить в плейлист",
onClick = { isViewingPlaylists = true }, onClick = { isViewingPlaylists = true },
trailingContent = { trailingContent = {
Image( Image(
@@ -666,7 +666,7 @@ fun MediaItemMenu(
albumInfo?.let { (albumId) -> albumInfo?.let { (albumId) ->
MenuEntry( MenuEntry(
icon = R.drawable.disc, icon = R.drawable.disc,
text = "Go to album", text = "Перейти в альбом",
onClick = { onClick = {
onDismiss() onDismiss()
onGoToAlbum(albumId) onGoToAlbum(albumId)
@@ -679,7 +679,7 @@ fun MediaItemMenu(
artistsInfo?.forEach { (authorId, authorName) -> artistsInfo?.forEach { (authorId, authorName) ->
MenuEntry( MenuEntry(
icon = R.drawable.person, icon = R.drawable.person,
text = "More of $authorName", text = "Больше от $authorName",
onClick = { onClick = {
onDismiss() onDismiss()
onGoToArtist(authorId) onGoToArtist(authorId)
@@ -691,7 +691,7 @@ fun MediaItemMenu(
onRemoveFromQueue?.let { onRemoveFromQueue -> onRemoveFromQueue?.let { onRemoveFromQueue ->
MenuEntry( MenuEntry(
icon = R.drawable.trash, icon = R.drawable.trash,
text = "Remove from queue", text = "Убрать из очереди",
onClick = { onClick = {
onDismiss() onDismiss()
onRemoveFromQueue() onRemoveFromQueue()
@@ -702,7 +702,7 @@ fun MediaItemMenu(
onRemoveFromPlaylist?.let { onRemoveFromPlaylist -> onRemoveFromPlaylist?.let { onRemoveFromPlaylist ->
MenuEntry( MenuEntry(
icon = R.drawable.trash, icon = R.drawable.trash,
text = "Remove from playlist", text = "Удалить из плейлиста",
onClick = { onClick = {
onDismiss() onDismiss()
onRemoveFromPlaylist() onRemoveFromPlaylist()
@@ -713,7 +713,7 @@ fun MediaItemMenu(
onHideFromDatabase?.let { onHideFromDatabase -> onHideFromDatabase?.let { onHideFromDatabase ->
MenuEntry( MenuEntry(
icon = R.drawable.trash, icon = R.drawable.trash,
text = "Hide", text = "Скрыть",
onClick = onHideFromDatabase onClick = onHideFromDatabase
) )
} }
@@ -721,7 +721,7 @@ fun MediaItemMenu(
onRemoveFromQuickPicks?.let { onRemoveFromQuickPicks?.let {
MenuEntry( MenuEntry(
icon = R.drawable.trash, icon = R.drawable.trash,
text = "Hide from \"Quick picks\"", text = "Скрыть из \"Обзора\"",
onClick = { onClick = {
onDismiss() onDismiss()
onRemoveFromQuickPicks() onRemoveFromQuickPicks()

View File

@@ -235,7 +235,7 @@ fun Lyrics(
.align(Alignment.TopCenter) .align(Alignment.TopCenter)
) { ) {
BasicText( BasicText(
text = "${if (isShowingSynchronizedLyrics) "Синхронизирован текст" else "Т"}екст песни не доступен", text = "${if (isShowingSynchronizedLyrics) "Синхронизированный т" else "Т"}екст не доступен",
style = typography.xs.center.medium.color(PureBlackColorPalette.text), style = typography.xs.center.medium.color(PureBlackColorPalette.text),
modifier = Modifier modifier = Modifier
.background(Color.Black.copy(0.4f)) .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 it.hamy.muza.utils.windows
import kotlinx.coroutines.launch 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 @ExperimentalFoundationApi
@ExperimentalAnimationApi @ExperimentalAnimationApi
@Composable @Composable
@@ -202,6 +209,7 @@ fun Queue(
key = { it.uid.hashCode() } key = { it.uid.hashCode() }
) { window -> ) { window ->
val isPlayingThisMediaItem = mediaItemIndex == window.firstPeriodIndex val isPlayingThisMediaItem = mediaItemIndex == window.firstPeriodIndex
var offsetX by remember { mutableStateOf(0f) }
SongItem( SongItem(
song = window.mediaItem, song = window.mediaItem,
@@ -283,6 +291,23 @@ fun Queue(
reorderingState = reorderingState, reorderingState = reorderingState,
index = window.firstPeriodIndex 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.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsetsSides 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.isAtLeastAndroid6
import it.hamy.muza.utils.isIgnoringBatteryOptimizations import it.hamy.muza.utils.isIgnoringBatteryOptimizations
import it.hamy.muza.utils.isInvincibilityEnabledKey import it.hamy.muza.utils.isInvincibilityEnabledKey
import it.hamy.muza.utils.isProxyEnabledKey
import it.hamy.muza.utils.pauseSearchHistoryKey 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.rememberPreference
import it.hamy.muza.utils.toast import it.hamy.muza.utils.toast
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import java.net.Proxy
@SuppressLint("BatteryLife") @SuppressLint("BatteryLife")
@ExperimentalAnimationApi @ExperimentalAnimationApi
@@ -72,6 +80,14 @@ fun OtherSettings() {
var isInvincibilityEnabled by rememberPreference(isInvincibilityEnabledKey, false) 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 { var isIgnoringBatteryOptimizations by remember {
mutableStateOf(context.isIgnoringBatteryOptimizations) mutableStateOf(context.isIgnoringBatteryOptimizations)
} }
@@ -178,5 +194,33 @@ fun OtherSettings() {
isChecked = isInvincibilityEnabled, isChecked = isInvincibilityEnabled,
onCheckedChange = { isInvincibilityEnabled = it } 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.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import it.hamy.compose.routing.RouteHandler import it.hamy.compose.routing.RouteHandler
import it.hamy.muza.R import it.hamy.muza.R
import it.hamy.muza.ui.components.themed.Scaffold import it.hamy.muza.ui.components.themed.Scaffold
import it.hamy.muza.ui.components.themed.Switch 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.components.themed.ValueSelectorDialog
import it.hamy.muza.ui.screens.globalRoutes import it.hamy.muza.ui.screens.globalRoutes
import it.hamy.muza.ui.styling.LocalAppearance import it.hamy.muza.ui.styling.LocalAppearance
import it.hamy.muza.utils.color import it.hamy.muza.utils.color
import it.hamy.muza.utils.secondary import it.hamy.muza.utils.secondary
import it.hamy.muza.utils.semiBold import it.hamy.muza.utils.semiBold
import it.hamy.muza.utils.toast
@ExperimentalFoundationApi @ExperimentalFoundationApi
@ExperimentalAnimationApi @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 @Composable
fun SettingsDescription( fun SettingsDescription(
text: String, text: String,

View File

@@ -38,6 +38,10 @@ const val homeScreenTabIndexKey = "homeScreenTabIndex"
const val searchResultScreenTabIndexKey = "searchResultScreenTabIndex" const val searchResultScreenTabIndexKey = "searchResultScreenTabIndex"
const val artistScreenTabIndexKey = "artistScreenTabIndex" const val artistScreenTabIndexKey = "artistScreenTabIndex"
const val pauseSearchHistoryKey = "pauseSearchHistory" 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( inline fun <reified T : Enum<T>> SharedPreferences.getEnum(
key: String, 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.NavigationEndpoint
import it.hamy.innertube.models.Runs import it.hamy.innertube.models.Runs
import it.hamy.innertube.models.Thumbnail import it.hamy.innertube.models.Thumbnail
import it.hamy.innertube.utils.ProxyPreferences
import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.net.InetSocketAddress
import java.net.Proxy
object Innertube { object Innertube {
val client = HttpClient(OkHttp) { val client = HttpClient(OkHttp) {
@@ -44,6 +48,19 @@ object Innertube {
parameters.append("prettyPrint", "false") parameters.append("prettyPrint", "false")
} }
} }
ProxyPreferences.preference?.let {
engine {
proxy = Proxy(
it.proxyMode,
InetSocketAddress(
it.proxyHost,
it.proxyPort
)
)
}
}
} }
internal const val browse = "/youtubei/v1/browse" 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() google()
mavenCentral() mavenCentral()
maven { setUrl("https://jitpack.io") } maven { setUrl("https://jitpack.io") }
} }
versionCatalogs { versionCatalogs {
@@ -36,6 +37,8 @@ dependencyResolutionManagement {
version("media3", "1.0.0-beta03") version("media3", "1.0.0-beta03")
library("exoplayer", "androidx.media3", "media3-exoplayer").versionRef("media3") library("exoplayer", "androidx.media3", "media3-exoplayer").versionRef("media3")
library("exoplayer-okhttp", "androidx.media3", "media3-datasource-okhttp").versionRef("media3")
version("ktor", "2.1.2") version("ktor", "2.1.2")
library("ktor-client-core", "io.ktor", "ktor-client-core").versionRef("ktor") library("ktor-client-core", "io.ktor", "ktor-client-core").versionRef("ktor")