Skip to content

Commit 123ee94

Browse files
authored
Merge branch 'recloudstream:master' into master
2 parents 1328a61 + 3c9120f commit 123ee94

File tree

24 files changed

+306
-122
lines changed

24 files changed

+306
-122
lines changed

app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import android.widget.CheckBox
2323
import android.widget.ImageView
2424
import android.widget.LinearLayout
2525
import android.widget.Toast
26-
import androidx.activity.OnBackPressedCallback
2726
import androidx.activity.result.ActivityResultLauncher
2827
import androidx.annotation.IdRes
2928
import androidx.annotation.MainThread
@@ -107,6 +106,7 @@ import com.lagradost.cloudstream3.ui.SyncWatchType
107106
import com.lagradost.cloudstream3.ui.WatchType
108107
import com.lagradost.cloudstream3.ui.account.AccountHelper.showAccountSelectLinear
109108
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO
109+
import com.lagradost.cloudstream3.ui.home.HomeFragment
110110
import com.lagradost.cloudstream3.ui.home.HomeViewModel
111111
import com.lagradost.cloudstream3.ui.library.LibraryViewModel
112112
import com.lagradost.cloudstream3.ui.player.BasicLink
@@ -162,6 +162,7 @@ import com.lagradost.cloudstream3.utils.ImageLoader.loadImage
162162
import com.lagradost.cloudstream3.utils.InAppUpdater.runAutoUpdate
163163
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
164164
import com.lagradost.cloudstream3.utils.SnackbarHelper.showSnackbar
165+
import com.lagradost.cloudstream3.utils.TvChannelUtils
165166
import com.lagradost.cloudstream3.utils.UIHelper.changeStatusBarState
166167
import com.lagradost.cloudstream3.utils.UIHelper.checkWrite
167168
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
@@ -195,9 +196,6 @@ import androidx.tvprovider.media.tv.TvContractCompat
195196
import android.content.ComponentName
196197
import android.content.ContentUris
197198

198-
import com.lagradost.cloudstream3.ui.home.HomeFragment
199-
import com.lagradost.cloudstream3.utils.TvChannelUtils
200-
201199
class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCallback {
202200
companion object {
203201
var activityResultLauncher: ActivityResultLauncher<Intent>? = null
@@ -708,6 +706,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa
708706
broadcastIntent.setClass(this, VideoDownloadRestartReceiver::class.java)
709707
this.sendBroadcast(broadcastIntent)
710708
afterPluginsLoadedEvent -= ::onAllPluginsLoaded
709+
detachBackPressedCallback("MainActivityDefault")
711710
super.onDestroy()
712711
}
713712

@@ -1663,8 +1662,6 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa
16631662
if (navDestination.matchDestination(R.id.navigation_home)) {
16641663
attachBackPressedCallback("MainActivity") {
16651664
showConfirmExitDialog(settingsManager)
1666-
setNavigationBarColorCompat(R.attr.primaryGrayBackground)
1667-
updateLocale()
16681665
}
16691666
} else detachBackPressedCallback("MainActivity")
16701667
}
@@ -2028,24 +2025,11 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa
20282025
// }
20292026
// }
20302027

2031-
onBackPressedDispatcher.addCallback(
2032-
this,
2033-
object : OnBackPressedCallback(true) {
2034-
override fun handleOnBackPressed() {
2035-
setNavigationBarColorCompat(R.attr.primaryGrayBackground)
2036-
updateLocale()
2037-
2038-
// If we don't disable we end up in a loop with default behavior calling
2039-
// this callback as well, so we disable it, run default behavior,
2040-
// then re-enable this callback so it can be used for next back press.
2041-
isEnabled = false
2042-
onBackPressedDispatcher.onBackPressed()
2043-
isEnabled = true
2044-
}
2045-
}
2046-
)
2047-
2048-
2028+
attachBackPressedCallback("MainActivityDefault") {
2029+
setNavigationBarColorCompat(R.attr.primaryGrayBackground)
2030+
updateLocale()
2031+
runDefault()
2032+
}
20492033
}
20502034

20512035
/** Biometric stuff **/

app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import androidx.media3.exoplayer.source.ConcatenatingMediaSource
5555
import androidx.media3.exoplayer.source.ConcatenatingMediaSource2
5656
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
5757
import androidx.media3.exoplayer.source.MergingMediaSource
58+
import androidx.media3.exoplayer.source.MediaSource
5859
import androidx.media3.exoplayer.source.SingleSampleMediaSource
5960
import androidx.media3.exoplayer.text.TextOutput
6061
import androidx.media3.exoplayer.text.TextRenderer
@@ -71,6 +72,7 @@ import com.lagradost.cloudstream3.MainActivity.Companion.deleteFileOnExit
7172
import com.lagradost.cloudstream3.R
7273
import com.lagradost.cloudstream3.TvType
7374
import com.lagradost.cloudstream3.USER_AGENT
75+
import com.lagradost.cloudstream3.AudioFile
7476
import com.lagradost.cloudstream3.app
7577
import com.lagradost.cloudstream3.mvvm.debugAssert
7678
import com.lagradost.cloudstream3.mvvm.logError
@@ -1059,7 +1061,9 @@ class CS3IPlayer : IPlayer {
10591061
* Sets the m3u8 preferred video quality, will not force stop anything with higher quality.
10601062
* Does not work if trackSelector is defined.
10611063
**/
1062-
maxVideoHeight: Int? = null
1064+
maxVideoHeight: Int? = null,
1065+
/** External audio tracks to merge with the video */
1066+
audioSources: List<MediaSource> = emptyList()
10631067
): ExoPlayer {
10641068
val exoPlayerBuilder =
10651069
ExoPlayer.Builder(context)
@@ -1310,10 +1314,10 @@ class CS3IPlayer : IPlayer {
13101314
return exoPlayerBuilder.build().apply {
13111315
setPlayWhenReady(playWhenReady)
13121316
seekTo(currentWindow, playbackPosition)
1317+
// Merge video, subtitles and external audio tracks
1318+
val allSources = listOf(videoMediaSource) + subSources + audioSources
13131319
setMediaSource(
1314-
MergingMediaSource(
1315-
videoMediaSource, *subSources.toTypedArray()
1316-
),
1320+
MergingMediaSource(*allSources.filterNotNull().toTypedArray()),
13171321
playbackPosition
13181322
)
13191323
setHandleAudioBecomingNoisy(true)
@@ -1325,7 +1329,8 @@ class CS3IPlayer : IPlayer {
13251329
context: Context,
13261330
mediaSlices: List<MediaItemSlice>,
13271331
subSources: List<SingleSampleMediaSource>,
1328-
cacheFactory: CacheDataSource.Factory? = null
1332+
cacheFactory: CacheDataSource.Factory? = null,
1333+
audioSources: List<MediaSource> = emptyList()
13291334
) {
13301335
Log.i(TAG, "loadExo")
13311336
val settingsManager = PreferenceManager.getDefaultSharedPreferences(context)
@@ -1351,7 +1356,8 @@ class CS3IPlayer : IPlayer {
13511356
playWhenReady = isPlaying, // this keep the current state of the player
13521357
cacheFactory = cacheFactory,
13531358
subtitleOffset = currentSubtitleOffset,
1354-
maxVideoHeight = maxVideoHeight
1359+
maxVideoHeight = maxVideoHeight,
1360+
audioSources = audioSources
13551361
)
13561362

13571363
event(PlayerAttachedEvent(exoPlayer))
@@ -1683,6 +1689,40 @@ class CS3IPlayer : IPlayer {
16831689
return Pair(subSources, activeSubtitles)
16841690
}
16851691

1692+
/**
1693+
* Creates audio media sources from ExtractorLink's audioTracks
1694+
* @param audioTracks List of audio tracks from ExtractorLink
1695+
* @param onlineSourceFactory Factory for creating online data sources
1696+
* @return List of MediaSource for audio tracks
1697+
*/
1698+
private fun getAudioSources(
1699+
audioTracks: List<AudioFile>,
1700+
onlineSourceFactory: HttpDataSource.Factory?
1701+
): List<MediaSource> {
1702+
if (onlineSourceFactory == null || audioTracks.isEmpty()) return emptyList()
1703+
1704+
return audioTracks.mapNotNull { audio ->
1705+
try {
1706+
val mediaItem = getMediaItem(MimeTypes.AUDIO_UNKNOWN, audio.url)
1707+
1708+
// Create a factory with custom headers if provided
1709+
val headers = audio.headers
1710+
val factory = if (!headers.isNullOrEmpty()) {
1711+
DefaultHttpDataSource.Factory()
1712+
.setUserAgent(USER_AGENT)
1713+
.setDefaultRequestProperties(headers)
1714+
} else {
1715+
onlineSourceFactory
1716+
}
1717+
1718+
DefaultMediaSourceFactory(factory).createMediaSource(mediaItem)
1719+
} catch (e: Exception) {
1720+
Log.e(TAG, "Failed to create audio source for ${audio.url}: ${e.message}")
1721+
null
1722+
}
1723+
}
1724+
}
1725+
16861726
override fun isActive(): Boolean {
16871727
return exoPlayer != null
16881728
}
@@ -1858,6 +1898,12 @@ class CS3IPlayer : IPlayer {
18581898
subtitleHelper
18591899
)
18601900

1901+
// Create audio sources from ExtractorLink's audioTracks
1902+
val audioSources = getAudioSources(
1903+
audioTracks = link.audioTracks,
1904+
onlineSourceFactory = onlineSourceFactory
1905+
)
1906+
18611907
subtitleHelper.setActiveSubtitles(activeSubtitles.toSet())
18621908

18631909
if (simpleCache == null)
@@ -1868,7 +1914,7 @@ class CS3IPlayer : IPlayer {
18681914
setUpstreamDataSourceFactory(onlineSourceFactory)
18691915
}
18701916

1871-
loadExo(context, mediaItems, subSources, cacheFactory)
1917+
loadExo(context, mediaItems, subSources, cacheFactory, audioSources)
18721918
} catch (t: Throwable) {
18731919
Log.e(TAG, "loadOnlinePlayer error", t)
18741920
event(ErrorEvent(t))

app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,6 @@ import com.lagradost.cloudstream3.utils.AppContextUtils.openBrowser
7070
import com.lagradost.cloudstream3.utils.AppContextUtils.updateHasTrailers
7171
import com.lagradost.cloudstream3.utils.BackPressedCallbackHelper.attachBackPressedCallback
7272
import com.lagradost.cloudstream3.utils.BackPressedCallbackHelper.detachBackPressedCallback
73-
import com.lagradost.cloudstream3.utils.BackPressedCallbackHelper.disableBackPressedCallback
74-
import com.lagradost.cloudstream3.utils.BackPressedCallbackHelper.enableBackPressedCallback
7573
import com.lagradost.cloudstream3.utils.BatteryOptimizationChecker.openBatteryOptimizationSettings
7674
import com.lagradost.cloudstream3.utils.ExtractorLink
7775
import com.lagradost.cloudstream3.utils.ImageLoader.loadImage
@@ -476,12 +474,7 @@ open class ResultFragmentPhone : FullScreenPlayer() {
476474

477475
activity?.attachBackPressedCallback(this@ResultFragmentPhone.toString()) {
478476
if (resultOverlappingPanels.getSelectedPanel().ordinal == 1) {
479-
// If we don't disable we end up in a loop with default behavior calling
480-
// this callback as well, so we disable it, run default behavior,
481-
// then re-enable this callback so it can be used for next back press.
482-
activity?.disableBackPressedCallback(this@ResultFragmentPhone.toString())
483-
activity?.onBackPressedDispatcher?.onBackPressed()
484-
activity?.enableBackPressedCallback(this@ResultFragmentPhone.toString())
477+
runDefault()
485478
} else resultOverlappingPanels.closePanels()
486479
}
487480

app/src/main/java/com/lagradost/cloudstream3/utils/BackPressedCallbackHelper.kt

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,46 @@ package com.lagradost.cloudstream3.utils
22

33
import androidx.activity.ComponentActivity
44
import androidx.activity.OnBackPressedCallback
5+
import java.lang.ref.WeakReference
56
import java.util.WeakHashMap
67

78
object BackPressedCallbackHelper {
8-
private val backPressedCallbacks = WeakHashMap<ComponentActivity, MutableMap<String, OnBackPressedCallback>>()
99

10-
fun ComponentActivity.attachBackPressedCallback(id: String, callback: () -> Unit) {
11-
val callbackMap = backPressedCallbacks.getOrPut(this) { mutableMapOf() }
10+
private val backPressedCallbacks =
11+
WeakHashMap<ComponentActivity, MutableMap<String, OnBackPressedCallback>>()
12+
13+
class CallbackHelper(
14+
private val activityRef: WeakReference<ComponentActivity>,
15+
private val callback: OnBackPressedCallback
16+
) {
17+
fun runDefault() {
18+
val activity = activityRef.get() ?: return
19+
val wasEnabled = callback.isEnabled
20+
callback.isEnabled = false
21+
try {
22+
activity.onBackPressedDispatcher.onBackPressed()
23+
} finally {
24+
callback.isEnabled = wasEnabled
25+
}
26+
}
27+
}
1228

29+
fun ComponentActivity.attachBackPressedCallback(
30+
id: String,
31+
callback: CallbackHelper.() -> Unit
32+
) {
33+
val callbackMap = backPressedCallbacks.getOrPut(this) { mutableMapOf() }
1334
if (callbackMap.containsKey(id)) return
1435

36+
// We use WeakReference to protect against potential leaks.
37+
val activityRef = WeakReference(this)
1538
val newCallback = object : OnBackPressedCallback(true) {
1639
override fun handleOnBackPressed() {
17-
callback.invoke()
40+
CallbackHelper(activityRef, this).callback()
1841
}
1942
}
20-
callbackMap[id] = newCallback
2143

44+
callbackMap[id] = newCallback
2245
onBackPressedDispatcher.addCallback(this, newCallback)
2346
}
2447

@@ -32,7 +55,6 @@ object BackPressedCallbackHelper {
3255

3356
fun ComponentActivity.detachBackPressedCallback(id: String) {
3457
val callbackMap = backPressedCallbacks[this] ?: return
35-
3658
callbackMap[id]?.let { callback ->
3759
callback.isEnabled = false
3860
callbackMap.remove(id)
@@ -42,4 +64,4 @@ object BackPressedCallbackHelper {
4264
backPressedCallbacks.remove(this)
4365
}
4466
}
45-
}
67+
}

app/src/main/res/layout/fragment_result_tv.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -747,7 +747,7 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
747747
<LinearLayout
748748
android:id="@+id/episode_holder_tv"
749749
android:layout_width="wrap_content"
750-
android:layout_height="wrap_content"
750+
android:layout_height="match_parent"
751751
android:layout_gravity="end"
752752
android:orientation="horizontal"
753753
android:paddingStart="@dimen/result_padding"

app/src/main/res/values-b+ar/strings.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,7 @@
699699
<string name="player_load_one_subtitle_online">تحميل أول ترجمة متاحة</string>
700700
<string name="torrent_preferred_media">تفعيل التورنت من خلال الاعدادات/المصادر/المحتوى المفضل</string>
701701
<string name="torrent_not_accepted">قم بإعادة تشغيل التطبيق و قبول التنبيه الخاص بالتورنت للمتابعة.</string>
702-
<string name="software_decoding_desc">يمكّن فك تشفير البرمجي المشغل من تشغيل ملفات الفيديو التي لا يدعمها جهازك، ولكنه قد يتسبب في بطء أو عدم استقرار التشغيل على الدقة العالية</string>
702+
<string name="software_decoding_desc">يمكّن فك تشفير البرمجي المشغل من تشغيل ملفات الفيديو التي لا يدعمها جهازك، ولكنه قد يتسبب في بطء أو عدم استقرار التشغيل على الدقة العالية.</string>
703703
<string name="software_decoding">فك الترميز البرمجي</string>
704704
<string name="sort_episodes_rating_high_low">تقييم (الأعلى)</string>
705705
<string name="sort_episodes_rating_low_high">تقييم (الأقل)</string>
@@ -766,4 +766,5 @@
766766
<string name="top_left">اعلى يسار</string>
767767
<string name="top_center">اعلى وسط</string>
768768
<string name="top_right">اعلى يمين</string>
769+
<string name="play_full_series_button">شاهد المسلسل كاملاً</string>
769770
</resources>

app/src/main/res/values-b+fr/strings.xml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@
428428
<string name="skip_type_mixed_op">Introduction mitigée</string>
429429
<string name="update_notification_installing">Installation de la mise a jour de l\'application…</string>
430430
<string name="update_notification_failed">Impossible d\'installer la nouvelle version de l\'application</string>
431-
<string name="apk_installer_settings_des">Certains téléphones ne supporte pas le nouvel installateur d\'application. Essayez l\'option de l\'ancien installateur si les mises-à-jour ne s\'installe pas.</string>
431+
<string name="apk_installer_settings_des">Certains appareils ne supportent pas le nouvel installateur d\'application. Essayez l\'option de l\'ancien installateur si les mises-à-jour ne s\'installe pas.</string>
432432
<string name="previous">Précédent</string>
433433
<string name="skip_setup">Ignorer la configuration</string>
434434
<string name="repository_name_hint">Nom de dépôt (optionnel)</string>
@@ -652,7 +652,7 @@
652652
<string name="confirm_before_exiting_title">Confirmer avant de quitter</string>
653653
<string name="unsupported_error">Erreur non supportée</string>
654654
<string name="encoding_error">Erreur d\'encodage</string>
655-
<string name="software_decoding_desc">Le décodage logiciel permet au lecteur de lire des fichiers vidéo non pris en charge par votre téléphone, mais peut entraîner une lecture lente ou instable en haute résolution</string>
655+
<string name="software_decoding_desc">Le décodage logiciel permet au lecteur de lire des fichiers vidéo non pris en charge par votre appareil, mais peut entraîner une lecture lente ou instable en haute résolution.</string>
656656
<string name="software_decoding">Décodage logiciel</string>
657657
<string name="torrent_preferred_media">Activer les torrents dans Paramètres/Sources/Média préféré</string>
658658
<string name="torrent_not_accepted">Redémarrez l\'application et acceptez la fenêtre contextuelle Stream Torrent pour continuer.</string>
@@ -714,4 +714,15 @@
714714
<string name="resolution_and_name">Solution et nom</string>
715715
<string name="name">Nom</string>
716716
<string name="episode_action_play_mirror">Mise en miroir</string>"
717-
</resources>
717+
<string name="subs_subtitle_alignment">Alignement des Sous-titres</string>
718+
<string name="bottom_left">En bas à gauche</string>
719+
<string name="bottom_center">En bas au centre</string>
720+
<string name="bottom_right">En bas à droite</string>
721+
<string name="middle_left">Au milieu à gauche</string>
722+
<string name="middle_center">Au milieu au centre</string>
723+
<string name="middle_right">Au milieu à droite</string>
724+
<string name="top_left">En haut à gauche</string>
725+
<string name="top_center">En haut au centre</string>
726+
<string name="top_right">En haut à droite</string>
727+
<string name="play_full_series_button">Jouer la série complète</string>
728+
</resources>

0 commit comments

Comments
 (0)