From fbbcdb488975abf8788717402a0a3ecd15cd64ec Mon Sep 17 00:00:00 2001
From: Blatzar <46196380+Blatzar@users.noreply.github.com>
Date: Thu, 6 Oct 2022 00:14:42 +0200
Subject: [PATCH 01/22] Add option for default media player (Internal, VLC and
browser) & fixed VLC detection
---
app/src/main/AndroidManifest.xml | 4 ++
.../cloudstream3/ui/result/EpisodeAdapter.kt | 18 +++++++
.../cloudstream3/ui/result/ResultFragment.kt | 11 +++--
.../ui/result/ResultViewModel2.kt | 47 ++++++++++++-------
.../ui/settings/SettingsPlayer.kt | 16 +++++++
app/src/main/res/values/array.xml | 12 +++++
app/src/main/res/values/strings.xml | 11 ++++-
app/src/main/res/xml/settings_player.xml | 5 ++
8 files changed, 102 insertions(+), 22 deletions(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 460a47ea..f9e0e7de 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -21,6 +21,10 @@
android:name="android.software.leanback"
android:required="false" />
+
+
+
+
Unit,
private val downloadClickCallback: (DownloadClickEvent) -> Unit,
) : RecyclerView.Adapter() {
+ companion object {
+ /**
+ * @return ACTION_PLAY_EPISODE_IN_PLAYER, ACTION_PLAY_EPISODE_IN_BROWSER or ACTION_PLAY_EPISODE_IN_VLC_PLAYER depending on player settings.
+ * See array.xml/player_pref_values
+ **/
+ fun getPlayerAction(context: Context): Int {
+ val settingsManager = PreferenceManager.getDefaultSharedPreferences(context)
+ return when (settingsManager.getInt(context.getString(R.string.player_pref_key), 1)) {
+ 1 -> ACTION_PLAY_EPISODE_IN_PLAYER
+ 2 -> ACTION_PLAY_EPISODE_IN_VLC_PLAYER
+ 3 -> ACTION_PLAY_EPISODE_IN_BROWSER
+ else -> ACTION_PLAY_EPISODE_IN_PLAYER
+ }
+ }
+ }
+
var cardList: MutableList = mutableListOf()
private val mBoundViewHolders: HashSet = HashSet()
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt
index 1ec2dd39..a173f1c1 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt
@@ -38,6 +38,7 @@ import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick
import com.lagradost.cloudstream3.ui.download.EasyDownloadButton
import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment
+import com.lagradost.cloudstream3.ui.result.EpisodeAdapter.Companion.getPlayerAction
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.utils.*
@@ -455,7 +456,8 @@ open class ResultFragment : ResultTrailerPlayer() {
val apiName: String,
val showFillers: Boolean,
val dubStatus: DubStatus,
- val start: AutoResume?
+ val start: AutoResume?,
+ val playerAction: Int
)
private fun getStoredData(context: Context): StoredData? {
@@ -469,6 +471,8 @@ open class ResultFragment : ResultTrailerPlayer() {
) DubStatus.Dubbed else DubStatus.Subbed
val startAction = arguments?.getInt(START_ACTION_BUNDLE)
+ val playerAction = getPlayerAction(context)
+
val start = startAction?.let { action ->
val startValue = arguments?.getInt(START_VALUE_BUNDLE)
val resumeEpisode = arguments?.getInt(EPISODE_BUNDLE)
@@ -483,7 +487,7 @@ open class ResultFragment : ResultTrailerPlayer() {
season = resumeSeason
)
}
- return StoredData(url, apiName, showFillers, dubStatus, start)
+ return StoredData(url, apiName, showFillers, dubStatus, start, playerAction)
}
private fun reloadViewModel(success: Boolean = false) {
@@ -774,7 +778,8 @@ open class ResultFragment : ResultTrailerPlayer() {
viewModel.handleAction(
activity,
EpisodeClickEvent(
- ACTION_PLAY_EPISODE_IN_PLAYER, value.result
+ storedData?.playerAction ?: ACTION_PLAY_EPISODE_IN_PLAYER,
+ value.result
)
)
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
index 48919308..37c76ee3 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
@@ -1,10 +1,7 @@
package com.lagradost.cloudstream3.ui.result
import android.app.Activity
-import android.content.ClipData
-import android.content.ClipboardManager
-import android.content.Context
-import android.content.Intent
+import android.content.*
import android.net.Uri
import android.util.Log
import android.widget.Toast
@@ -33,6 +30,7 @@ import com.lagradost.cloudstream3.ui.player.GeneratorPlayer
import com.lagradost.cloudstream3.ui.player.IGenerator
import com.lagradost.cloudstream3.ui.player.RepoLinkGenerator
import com.lagradost.cloudstream3.ui.player.SubtitleData
+import com.lagradost.cloudstream3.ui.result.EpisodeAdapter.Companion.getPlayerAction
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
@@ -973,9 +971,11 @@ class ResultViewModel2 : ViewModel() {
File.createTempFile("mirrorlist", ".m3u8", outputDir)
}
var text = "#EXTM3U"
- for (sub in data.subs) {
- text += "\n#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"${sub.name}\",DEFAULT=NO,AUTOSELECT=NO,FORCED=NO,LANGUAGE=\"${sub.name}\",URI=\"${sub.url}\""
- }
+
+ // With subtitles it doesn't work for no reason :(
+// for (sub in data.subs) {
+// text += "\n#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"${sub.name}\",DEFAULT=NO,AUTOSELECT=NO,FORCED=NO,LANGUAGE=\"${sub.name}\",URI=\"${sub.url}\""
+// }
for (link in data.links) {
text += "\n#EXTINF:, ${link.name}\n${link.url}"
}
@@ -999,11 +999,10 @@ class ResultViewModel2 : ViewModel() {
val startId = VLC_FROM_PROGRESS
- var position = startId
- if (startId == VLC_FROM_START) {
- position = 1
- } else if (startId == VLC_FROM_PROGRESS) {
- position = 0
+ val position = when (startId) {
+ VLC_FROM_START -> 1
+ VLC_FROM_PROGRESS -> 0
+ else -> 0
}
vlcIntent.putExtra("position", position)
@@ -1013,7 +1012,13 @@ class ResultViewModel2 : ViewModel() {
act.startActivityForResult(vlcIntent, VLC_REQUEST_CODE)
} catch (e: Exception) {
logError(e)
- showToast(act, e.toString(), Toast.LENGTH_LONG)
+ main {
+ if (e is ActivityNotFoundException) {
+ showToast(act, txt(R.string.vlc_not_found_error), Toast.LENGTH_LONG)
+ } else {
+ showToast(act, e.toString(), Toast.LENGTH_LONG)
+ }
+ }
}
}
@@ -1073,9 +1078,10 @@ class ResultViewModel2 : ViewModel() {
click.copy(action = ACTION_CHROME_CAST_EPISODE)
)
} else {
+ val action = getPlayerAction(ctx)
handleEpisodeClickEvent(
activity,
- click.copy(action = ACTION_PLAY_EPISODE_IN_PLAYER)
+ click.copy(action = action)
)
}
}
@@ -1592,7 +1598,8 @@ class ResultViewModel2 : ViewModel() {
val idIndex = ep.key.id
for ((index, i) in ep.value.withIndex()) {
val episode = i.episode ?: (index + 1)
- val id = mainId + episode + idIndex * 1_000_000 + (i.season?.times(10_000) ?: 0)
+ val id =
+ mainId + episode + idIndex * 1_000_000 + (i.season?.times(10_000) ?: 0)
if (!existingEpisodes.contains(id)) {
existingEpisodes.add(id)
val seasonData = loadResponse.seasonNames.getSeason(i.season)
@@ -1888,7 +1895,10 @@ class ResultViewModel2 : ViewModel() {
if (ep.getWatchProgress() > 0.9) continue
handleAction(
activity,
- EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, ep)
+ EpisodeClickEvent(
+ getPlayerAction(activity),
+ ep
+ )
)
break
}
@@ -1905,7 +1915,10 @@ class ResultViewModel2 : ViewModel() {
?: return@launchSafe
handleAction(
activity,
- EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, episode)
+ EpisodeClickEvent(
+ getPlayerAction(activity),
+ episode
+ )
)
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt
index 125fadec..33d41934 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt
@@ -113,6 +113,22 @@ class SettingsPlayer : PreferenceFragmentCompat() {
return@setOnPreferenceClickListener true
}
+ getPref(R.string.player_pref_key)?.setOnPreferenceClickListener {
+ val prefNames = resources.getStringArray(R.array.player_pref_names)
+ val prefValues = resources.getIntArray(R.array.player_pref_values)
+ val current = settingsManager.getInt(getString(R.string.player_pref_key), 1)
+
+ activity?.showBottomDialog(
+ prefNames.toList(),
+ prefValues.indexOf(current),
+ getString(R.string.player_pref),
+ true,
+ {}) {
+ settingsManager.edit().putInt(getString(R.string.player_pref_key), prefValues[it]).apply()
+ }
+ return@setOnPreferenceClickListener true
+ }
+
getPref(R.string.subtitle_settings_key)?.setOnPreferenceClickListener {
SubtitlesFragment.push(activity, false)
return@setOnPreferenceClickListener true
diff --git a/app/src/main/res/values/array.xml b/app/src/main/res/values/array.xml
index fedc4219..230f00d7 100644
--- a/app/src/main/res/values/array.xml
+++ b/app/src/main/res/values/array.xml
@@ -33,6 +33,18 @@
- 6
+
+ - @string/player_settings_play_in_app
+ - @string/player_settings_play_in_vlc
+ - @string/player_settings_play_in_browser
+
+
+
+ - 1
+ - 2
+ - 3
+
+
- @string/resolution_and_title
- @string/title
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d086ad27..7556aed2 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -14,6 +14,7 @@
subtitle_settings_key
subtitle_settings_chromecast_key
quality_pref_key
+ player_pref_key
prefer_limit_title_key
prefer_limit_title_rez_key
video_buffer_size_key
@@ -255,7 +256,7 @@
Search
Accounts
Updates and backup
-
+
Info
Advanced Search
Gives you the search results separated by provider
@@ -626,6 +627,12 @@
Supported
Language
Install the extension first
-
+
HLS Playlist
+
+ Preferred video player
+ Internal player
+ VLC
+ Browser
+ VLC not found
diff --git a/app/src/main/res/xml/settings_player.xml b/app/src/main/res/xml/settings_player.xml
index a7e36488..6946a5c9 100644
--- a/app/src/main/res/xml/settings_player.xml
+++ b/app/src/main/res/xml/settings_player.xml
@@ -18,6 +18,11 @@
android:title="@string/watch_quality_pref"
android:icon="@drawable/ic_baseline_hd_24" />
+
+
Date: Thu, 6 Oct 2022 12:09:28 +0200
Subject: [PATCH 02/22] Update issue_action.yml
---
.github/workflows/issue_action.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/issue_action.yml b/.github/workflows/issue_action.yml
index 9ca9ff04..79e7766c 100644
--- a/.github/workflows/issue_action.yml
+++ b/.github/workflows/issue_action.yml
@@ -2,7 +2,7 @@ name: Issue automatic actions
on:
issues:
- types: [opened, edited]
+ types: [opened]
jobs:
issue-moderator:
From fb248b6192e25109a79df5e4d93f8d83159e062c Mon Sep 17 00:00:00 2001
From: Blatzar <46196380+Blatzar@users.noreply.github.com>
Date: Thu, 6 Oct 2022 18:30:58 +0200
Subject: [PATCH 03/22] Fixed internal plugin manager to fix name conflicts
---
.../cloudstream3/plugins/PluginManager.kt | 32 ++++++++++++++---
.../ui/settings/extensions/PluginsFragment.kt | 2 +-
.../settings/extensions/PluginsViewModel.kt | 35 ++++++++-----------
3 files changed, 42 insertions(+), 27 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt
index bf7e694c..ddb87856 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt
@@ -174,8 +174,12 @@ object PluginManager {
val onlineData: Pair,
) {
val isOutdated =
- onlineData.second.version != savedData.version || onlineData.second.version == PLUGIN_VERSION_ALWAYS_UPDATE
+ onlineData.second.version > savedData.version || onlineData.second.version == PLUGIN_VERSION_ALWAYS_UPDATE
val isDisabled = onlineData.second.status == PROVIDER_STATUS_DOWN
+
+ fun validOnlineData(context: Context): Boolean {
+ return getPluginPath(context, savedData.internalName, onlineData.first).absolutePath == savedData.filePath
+ }
}
// var allCurrentOutDatedPlugins: Set = emptySet()
@@ -225,6 +229,8 @@ object PluginManager {
.filter { onlineData -> savedData.internalName == onlineData.second.internalName }
.map { onlineData ->
OnlinePluginData(savedData, onlineData)
+ }.filter {
+ it.validOnlineData(activity)
}
}.flatten().distinctBy { it.onlineData.second.url }
@@ -416,6 +422,18 @@ object PluginManager {
) + "." + name.hashCode()
}
+ /**
+ * This should not be changed as it is used to also detect if a plugin is installed!
+ **/
+ fun getPluginPath(
+ context: Context,
+ internalName: String,
+ repositoryUrl: String
+ ): File {
+ val folderName = getPluginSanitizedFileName(repositoryUrl) // Guaranteed unique
+ val fileName = getPluginSanitizedFileName(internalName)
+ return File("${context.filesDir}/${ONLINE_PLUGINS_FOLDER}/${folderName}/$fileName.cs3")
+ }
/**
* Used for fresh installs
@@ -426,9 +444,7 @@ object PluginManager {
internalName: String,
repositoryUrl: String
): Boolean {
- val folderName = getPluginSanitizedFileName(repositoryUrl) // Guaranteed unique
- val fileName = getPluginSanitizedFileName(internalName)
- val file = File("${activity.filesDir}/${ONLINE_PLUGINS_FOLDER}/${folderName}/$fileName.cs3")
+ val file = getPluginPath(activity, internalName, repositoryUrl)
downloadAndLoadPlugin(activity, pluginUrl, internalName, file)
return true
}
@@ -454,7 +470,13 @@ object PluginManager {
return loadPlugin(
activity,
newFile ?: return false,
- PluginData(internalName, pluginUrl, true, newFile.absolutePath, PLUGIN_VERSION_NOT_SET)
+ PluginData(
+ internalName,
+ pluginUrl,
+ true,
+ newFile.absolutePath,
+ PLUGIN_VERSION_NOT_SET
+ )
)
} catch (e: Exception) {
logError(e)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt
index 7b944f62..e4435fff 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt
@@ -147,7 +147,7 @@ class PluginsFragment : Fragment() {
pluginViewModel.updatePluginListLocal()
tv_types_scroll_view?.isVisible = false
} else {
- pluginViewModel.updatePluginList(url)
+ pluginViewModel.updatePluginList(context, url)
tv_types_scroll_view?.isVisible = true
// 💀💀💀💀💀💀💀 Recyclerview when
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt
index 0a71c17a..709a3d8e 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt
@@ -1,6 +1,7 @@
package com.lagradost.cloudstream3.ui.settings.extensions
import android.app.Activity
+import android.content.Context
import android.util.Log
import android.widget.Toast
import androidx.lifecycle.LiveData
@@ -13,6 +14,7 @@ import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.mvvm.launchSafe
import com.lagradost.cloudstream3.plugins.PluginData
import com.lagradost.cloudstream3.plugins.PluginManager
+import com.lagradost.cloudstream3.plugins.PluginManager.getPluginPath
import com.lagradost.cloudstream3.plugins.RepositoryManager
import com.lagradost.cloudstream3.plugins.SitePlugin
import com.lagradost.cloudstream3.ui.result.txt
@@ -45,8 +47,8 @@ class PluginsViewModel : ViewModel() {
private val repositoryCache: MutableMap> = mutableMapOf()
const val TAG = "PLG"
- private fun isDownloaded(plugin: Plugin, data: Set? = null): Boolean {
- return (data ?: getDownloads()).contains(plugin.second.internalName)
+ private fun isDownloaded(context: Context, pluginName: String, repositoryUrl: String): Boolean {
+ return getPluginPath(context, pluginName, repositoryUrl).exists()
}
private suspend fun getPlugins(
@@ -63,24 +65,15 @@ class PluginsViewModel : ViewModel() {
?.also { repositoryCache[repositoryUrl] = it } ?: emptyList()
}
- private fun getStoredPlugins(): Array {
- return PluginManager.getPluginsOnline()
- }
-
- private fun getDownloads(): Set {
- return getStoredPlugins().map { it.internalName }.toSet()
- }
-
/**
* @param viewModel optional, updates the plugins livedata for that viewModel if included
* */
fun downloadAll(activity: Activity?, repositoryUrl: String, viewModel: PluginsViewModel?) =
ioSafe {
if (activity == null) return@ioSafe
- val stored = getDownloads()
val plugins = getPlugins(repositoryUrl)
- plugins.filter { plugin -> !isDownloaded(plugin, stored) }.also { list ->
+ plugins.filter { plugin -> !isDownloaded(activity, plugin.second.internalName, repositoryUrl) }.also { list ->
main {
showToast(
activity,
@@ -103,7 +96,7 @@ class PluginsViewModel : ViewModel() {
PluginManager.downloadAndLoadPlugin(
activity,
metadata.url,
- metadata.name,
+ metadata.internalName,
repo
)
}.main { list ->
@@ -117,7 +110,7 @@ class PluginsViewModel : ViewModel() {
),
Toast.LENGTH_SHORT
)
- viewModel?.updatePluginListPrivate(repositoryUrl)
+ viewModel?.updatePluginListPrivate(activity, repositoryUrl)
} else if (list.isNotEmpty()) {
showToast(activity, R.string.download_failed, Toast.LENGTH_SHORT)
}
@@ -140,7 +133,7 @@ class PluginsViewModel : ViewModel() {
if (activity == null) return@ioSafe
val (repo, metadata) = plugin
- val (success, message) = if (isDownloaded(plugin) || isLocal) {
+ val (success, message) = if (isDownloaded(activity, plugin.second.internalName, plugin.first) || isLocal) {
PluginManager.deletePlugin(
metadata.url,
isLocal
@@ -165,14 +158,13 @@ class PluginsViewModel : ViewModel() {
if (isLocal)
updatePluginListLocal()
else
- updatePluginListPrivate(repositoryUrl)
+ updatePluginListPrivate(activity, repositoryUrl)
}
- private suspend fun updatePluginListPrivate(repositoryUrl: String) {
- val stored = getDownloads()
+ private suspend fun updatePluginListPrivate(context: Context, repositoryUrl: String) {
val plugins = getPlugins(repositoryUrl)
val list = plugins.map { plugin ->
- PluginViewData(plugin, isDownloaded(plugin, stored))
+ PluginViewData(plugin, isDownloaded(context, plugin.second.internalName, plugin.first))
}
this.plugins = list
@@ -211,9 +203,10 @@ class PluginsViewModel : ViewModel() {
_filteredPlugins.postValue(false to plugins.filterTvTypes().filterLang().sortByQuery(currentQuery))
}
- fun updatePluginList(repositoryUrl: String) = viewModelScope.launchSafe {
+ fun updatePluginList(context: Context?, repositoryUrl: String) = viewModelScope.launchSafe {
+ if (context == null) return@launchSafe
Log.i(TAG, "updatePluginList = $repositoryUrl")
- updatePluginListPrivate(repositoryUrl)
+ updatePluginListPrivate(context, repositoryUrl)
}
fun search(query: String?) {
From 601f4c5a77feb5224da443a3a7ca956a1174bf4f Mon Sep 17 00:00:00 2001
From: Blatzar <46196380+Blatzar@users.noreply.github.com>
Date: Thu, 6 Oct 2022 20:04:43 +0200
Subject: [PATCH 04/22] Added .otf files to the manual subtitle whitelist
---
.../lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt
index b1d82b76..b97468e7 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt
@@ -135,7 +135,8 @@ class SubtitlesFragment : Fragment() {
it.mkdir()
}
return fontDir.list()?.mapNotNull {
- if (it.endsWith(".ttf")) {
+ // No idea which formats are supported, but these should be.
+ if (it.endsWith(".ttf") || it.endsWith(".otf")) {
File(fontDir.absolutePath + "/" + it)
} else null
} ?: listOf()
From 4c4b6b1787d28cfd0c59cdb584a4318564bc466d Mon Sep 17 00:00:00 2001
From: Blatzar <46196380+Blatzar@users.noreply.github.com>
Date: Fri, 7 Oct 2022 00:06:14 +0200
Subject: [PATCH 05/22] Fix selecting random api
---
.../com/lagradost/cloudstream3/ui/home/HomeViewModel.kt | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt
index 6254cb9b..d8497876 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt
@@ -276,9 +276,6 @@ class HomeViewModel : ViewModel() {
if (preferredApiName == noneApi.name) {
setKey(USER_SELECTED_HOMEPAGE_API, noneApi.name)
loadAndCancel(noneApi)
- // If the plugin isn't loaded yet. (Does not set the key)
- } else if (api == null) {
- loadAndCancel(noneApi)
} else if (preferredApiName == randomApi.name) {
val validAPIs = context?.filterProviderByPreferredMedia()
if (validAPIs.isNullOrEmpty()) {
@@ -289,6 +286,9 @@ class HomeViewModel : ViewModel() {
loadAndCancel(apiRandom)
setKey(USER_SELECTED_HOMEPAGE_API, apiRandom.name)
}
+ // If the plugin isn't loaded yet. (Does not set the key)
+ } else if (api == null) {
+ loadAndCancel(noneApi)
} else {
setKey(USER_SELECTED_HOMEPAGE_API, api.name)
loadAndCancel(api)
From 9e66245066bad26d59204326d93593729a98c585 Mon Sep 17 00:00:00 2001
From: Blatzar <46196380+Blatzar@users.noreply.github.com>
Date: Fri, 7 Oct 2022 14:53:51 +0200
Subject: [PATCH 06/22] Actually delete the internal files when you delete a
repo
---
.../cloudstream3/plugins/PluginManager.kt | 26 +++++++++++--------
.../settings/extensions/PluginsViewModel.kt | 9 +++----
2 files changed, 19 insertions(+), 16 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt
index ddb87856..35e041be 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt
@@ -28,6 +28,7 @@ import com.lagradost.cloudstream3.APIHolder.removePluginMapping
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
import com.lagradost.cloudstream3.mvvm.debugPrint
import com.lagradost.cloudstream3.mvvm.logError
+import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.main
@@ -123,6 +124,10 @@ object PluginManager {
val plugins = getPluginsOnline().filter {
!it.filePath.contains(repositoryPath)
}
+ val file = File(repositoryPath)
+ normalSafeApiCall {
+ if (file.exists()) file.deleteRecursively()
+ }
setKey(PLUGINS_KEY, plugins)
}
}
@@ -178,7 +183,11 @@ object PluginManager {
val isDisabled = onlineData.second.status == PROVIDER_STATUS_DOWN
fun validOnlineData(context: Context): Boolean {
- return getPluginPath(context, savedData.internalName, onlineData.first).absolutePath == savedData.filePath
+ return getPluginPath(
+ context,
+ savedData.internalName,
+ onlineData.first
+ ).absolutePath == savedData.filePath
}
}
@@ -484,18 +493,13 @@ object PluginManager {
}
}
- /**
- * @param isFilePath will treat the pluginUrl as as the filepath instead of url
- * */
- suspend fun deletePlugin(pluginIdentifier: String, isFilePath: Boolean): Boolean {
- val data =
- (if (isFilePath) (getPluginsLocal() + getPluginsOnline()).firstOrNull { it.filePath == pluginIdentifier }
- else getPluginsOnline().firstOrNull { it.url == pluginIdentifier }) ?: return false
+ suspend fun deletePlugin(file: File): Boolean {
+ val list = (getPluginsLocal() + getPluginsOnline()).filter { it.filePath == file.absolutePath }
return try {
- if (File(data.filePath).delete()) {
- unloadPlugin(data.filePath)
- deletePluginData(data)
+ if (File(file.absolutePath).delete()) {
+ unloadPlugin(file.absolutePath)
+ list.forEach { deletePluginData(it) }
return true
}
false
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt
index 709a3d8e..6d94f91e 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt
@@ -133,11 +133,10 @@ class PluginsViewModel : ViewModel() {
if (activity == null) return@ioSafe
val (repo, metadata) = plugin
- val (success, message) = if (isDownloaded(activity, plugin.second.internalName, plugin.first) || isLocal) {
- PluginManager.deletePlugin(
- metadata.url,
- isLocal
- ) to R.string.plugin_deleted
+ val file = getPluginPath(activity, plugin.second.internalName, plugin.first)
+
+ val (success, message) = if (file.exists() || isLocal) {
+ PluginManager.deletePlugin(file) to R.string.plugin_deleted
} else {
PluginManager.downloadAndLoadPlugin(
activity,
From 1e83d21db469fa68c3edad836b197093cf005dd3 Mon Sep 17 00:00:00 2001
From: Blatzar <46196380+Blatzar@users.noreply.github.com>
Date: Fri, 7 Oct 2022 18:17:31 +0200
Subject: [PATCH 07/22] Fix selecting online and local subtitles on Android TV
---
.../cloudstream3/ui/player/GeneratorPlayer.kt | 25 +++++++++++++++----
1 file changed, 20 insertions(+), 5 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt
index c69dc476..e20a6c7b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt
@@ -58,6 +58,7 @@ import kotlinx.android.synthetic.main.player_select_source_and_subs.*
import kotlinx.android.synthetic.main.player_select_source_and_subs.subtitles_click_settings
import kotlinx.android.synthetic.main.player_select_tracks.*
import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
class GeneratorPlayer : FullScreenPlayer() {
companion object {
@@ -115,10 +116,11 @@ class GeneratorPlayer : FullScreenPlayer() {
override fun onTracksInfoChanged() {
val tracks = player.getVideoTracks()
- player_tracks_btt?.isVisible = tracks.allVideoTracks.size > 1 || tracks.allAudioTracks.size > 1
+ player_tracks_btt?.isVisible =
+ tracks.allVideoTracks.size > 1 || tracks.allAudioTracks.size > 1
// Only set the preferred language if it is available.
// Otherwise it may give some users audio track init failed!
- if (tracks.allAudioTracks.any { it.language == preferredAudioTrackLanguage }){
+ if (tracks.allAudioTracks.any { it.language == preferredAudioTrackLanguage }) {
player.setPreferredAudioTrack(preferredAudioTrackLanguage)
}
}
@@ -602,8 +604,20 @@ class GeneratorPlayer : FullScreenPlayer() {
subtitleList.setItemChecked(subtitleIndex, true)
subtitleList.setOnItemClickListener { _, _, which, _ ->
- subtitleIndex = which
- subtitleList.setItemChecked(which, true)
+ if (which > currentSubtitles.size) {
+ // Since android TV is funky the setOnItemClickListener will be triggered
+ // instead of setOnClickListener when selecting. To override this we programmatically
+ // click the view when selecting an item outside the list.
+
+ // Cheeky way of getting the view at that position to click it
+ // to avoid keeping track of the various footers.
+ // getChildAt() gives null :(
+ val child = subtitleList.adapter.getView(which, null, subtitleList)
+ child?.performClick()
+ } else {
+ subtitleIndex = which
+ subtitleList.setItemChecked(which, true)
+ }
}
sourceDialog.cancel_btt?.setOnClickListener {
@@ -762,7 +776,8 @@ class GeneratorPlayer : FullScreenPlayer() {
ArrayAdapter(ctx, R.layout.sort_bottom_single_choice)
// audioArrayAdapter.add(ctx.getString(R.string.no_subtitles))
audioArrayAdapter.addAll(currentAudioTracks.mapIndexed { index, format ->
- format.label ?: format.language?.let { fromTwoLettersToLanguage(it) } ?: index.toString()
+ format.label ?: format.language?.let { fromTwoLettersToLanguage(it) }
+ ?: index.toString()
})
audioList.adapter = audioArrayAdapter
From 0b95d6ad33886106d1e2ca529c9d33a5e3517099 Mon Sep 17 00:00:00 2001
From: reduplicated <110570621+reduplicated@users.noreply.github.com>
Date: Sat, 8 Oct 2022 16:56:05 +0200
Subject: [PATCH 08/22] vlc stuff
---
.../ui/result/ResultViewModel2.kt | 85 +++++++++++++------
1 file changed, 57 insertions(+), 28 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
index 37c76ee3..392f38c0 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
@@ -6,6 +6,7 @@ import android.net.Uri
import android.util.Log
import android.widget.Toast
import androidx.core.content.FileProvider
+import androidx.core.net.toUri
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
@@ -954,7 +955,15 @@ class ResultViewModel2 : ViewModel() {
return LinkLoadingResult(sortUrls(links), sortSubs(subs))
}
- private fun playWithVlc(act: Activity?, data: LinkLoadingResult, id: Int) = ioSafe {
+ // https://wiki.videolan.org/Android_Player_Intents/
+ private fun playWithVlc(
+ act: Activity?,
+ data: LinkLoadingResult,
+ id: Int,
+ resume: Boolean = true,
+ // if it is only a single link then resume works correctly
+ singleFile: Boolean? = true
+ ) = ioSafe {
if (act == null) return@ioSafe
if (data.links.isEmpty()) {
showToast(act, R.string.no_links_found_toast, Toast.LENGTH_SHORT)
@@ -966,21 +975,6 @@ class ResultViewModel2 : ViewModel() {
if (act.checkWrite()) return@ioSafe
}
- val outputDir = act.cacheDir
- val outputFile = withContext(Dispatchers.IO) {
- File.createTempFile("mirrorlist", ".m3u8", outputDir)
- }
- var text = "#EXTM3U"
-
- // With subtitles it doesn't work for no reason :(
-// for (sub in data.subs) {
-// text += "\n#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"${sub.name}\",DEFAULT=NO,AUTOSELECT=NO,FORCED=NO,LANGUAGE=\"${sub.name}\",URI=\"${sub.url}\""
-// }
- for (link in data.links) {
- text += "\n#EXTINF:, ${link.name}\n${link.url}"
- }
- outputFile.writeText(text)
-
val vlcIntent = Intent(VLC_INTENT_ACTION_RESULT)
vlcIntent.setPackage(VLC_PACKAGE)
@@ -988,24 +982,59 @@ class ResultViewModel2 : ViewModel() {
vlcIntent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)
vlcIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
vlcIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
+ val outputDir = act.cacheDir
- vlcIntent.setDataAndType(
- FileProvider.getUriForFile(
- act,
- act.applicationContext.packageName + ".provider",
- outputFile
- ), "video/*"
- )
+ if (singleFile ?: (data.links.size == 1)) {
+ vlcIntent.setDataAndType(data.links.first().url.toUri(), "video/*")
+ } else {
+ val outputFile = File.createTempFile("mirrorlist", ".m3u8", outputDir)
- val startId = VLC_FROM_PROGRESS
+ var text = "#EXTM3U"
- val position = when (startId) {
- VLC_FROM_START -> 1
- VLC_FROM_PROGRESS -> 0
- else -> 0
+ // With subtitles it doesn't work for no reason :(
+// for (sub in data.subs) {
+// text += "\n#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"${sub.name}\",DEFAULT=NO,AUTOSELECT=NO,FORCED=NO,LANGUAGE=\"${sub.name}\",URI=\"${sub.url}\""
+// }
+ for (link in data.links) {
+ text += "\n#EXTINF:, ${link.name}\n${link.url}"
+ }
+ outputFile.writeText(text)
+
+ vlcIntent.setDataAndType(
+ FileProvider.getUriForFile(
+ act,
+ act.applicationContext.packageName + ".provider",
+ outputFile
+ ), "video/*"
+ )
}
+ val position = if (resume) {
+ getViewPos(id)?.position ?: 0L
+ } else {
+ 1L
+ }
+ vlcIntent.putExtra("from_start", !resume)
vlcIntent.putExtra("position", position)
+ //vlcIntent.putExtra("subtitles_location", data.subs.first().url)
+ /*for (s in data.subs) {
+ if (s.origin == SubtitleOrigin.URL) {
+ try {
+ val txt = app.get(s.url, s.headers).text
+ val subtitleFile = File.createTempFile("subtitle1", ".srt", outputDir)
+ subtitleFile.writeText(txt)
+ println("Subtitles::::::${subtitleFile.path}")
+ vlcIntent.putExtra("subtitles_location", FileProvider.getUriForFile(
+ act,
+ act.applicationContext.packageName + ".provider",
+ subtitleFile
+ ))
+ break
+ } catch (t : Throwable) {
+ logError(t)
+ }
+ }
+ }*/
vlcIntent.component = VLC_COMPONENT
act.setKey(VLC_LAST_ID_KEY, id)
From 88a7248e4798d7530914dc195aa16971b54402f7 Mon Sep 17 00:00:00 2001
From: reduplicated <110570621+reduplicated@users.noreply.github.com>
Date: Sat, 8 Oct 2022 16:58:14 +0200
Subject: [PATCH 09/22] :skull: i forgor
---
.../com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
index 392f38c0..6f3cb686 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
@@ -962,7 +962,7 @@ class ResultViewModel2 : ViewModel() {
id: Int,
resume: Boolean = true,
// if it is only a single link then resume works correctly
- singleFile: Boolean? = true
+ singleFile: Boolean? = null
) = ioSafe {
if (act == null) return@ioSafe
if (data.links.isEmpty()) {
From 071004f6c247bbaaf1f61202eecd007a6f697b1f Mon Sep 17 00:00:00 2001
From: reduplicated <110570621+reduplicated@users.noreply.github.com>
Date: Sat, 8 Oct 2022 17:48:46 +0200
Subject: [PATCH 10/22] added webvideocaster
---
app/src/main/AndroidManifest.xml | 1 +
.../lagradost/cloudstream3/MainActivity.kt | 5 +-
.../cloudstream3/ui/result/EpisodeAdapter.kt | 4 +
.../ui/result/ResultViewModel2.kt | 254 ++++++++++++------
app/src/main/res/values-ar/strings.xml | 2 +-
app/src/main/res/values-bp/strings.xml | 2 +-
app/src/main/res/values-cs/strings.xml | 2 +-
app/src/main/res/values-de/strings-de.xml | 2 +-
app/src/main/res/values-es/array.xml | 2 +-
app/src/main/res/values-es/strings-es.xml | 2 +-
app/src/main/res/values-fr/strings.xml | 2 +-
app/src/main/res/values-hi/strings.xml | 2 +-
app/src/main/res/values-hr/strings.xml | 2 +-
app/src/main/res/values-in/strings.xml | 2 +-
app/src/main/res/values-it/strings.xml | 2 +-
app/src/main/res/values-mk/strings.xml | 2 +-
app/src/main/res/values-ml/strings.xml | 2 +-
app/src/main/res/values-mo/string.xml | 2 +-
app/src/main/res/values-nl/strings.xml | 2 +-
app/src/main/res/values-no/strings.xml | 2 +-
app/src/main/res/values-pl/array.xml | 2 +-
app/src/main/res/values-pl/strings.xml | 2 +-
app/src/main/res/values-pt/strings-pt.xml | 2 +-
app/src/main/res/values-ro/strings.xml | 2 +-
app/src/main/res/values-sv/strings.xml | 2 +-
app/src/main/res/values-tl/strings.xml | 2 +-
app/src/main/res/values-tr/array.xml | 2 +-
app/src/main/res/values-tr/strings.xml | 2 +-
app/src/main/res/values-vi/array.xml | 2 +-
app/src/main/res/values-vi/strings.xml | 2 +-
app/src/main/res/values-zh/strings.xml | 2 +-
app/src/main/res/values/array.xml | 4 +-
app/src/main/res/values/strings.xml | 5 +-
33 files changed, 209 insertions(+), 118 deletions(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f9e0e7de..043195f5 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -23,6 +23,7 @@
+
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
index cd320060..de01ef9f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
@@ -101,12 +101,13 @@ val VLC_COMPONENT: ComponentName =
ComponentName(VLC_PACKAGE, "org.videolan.vlc.gui.video.VideoPlayerActivity")
const val VLC_REQUEST_CODE = 42
-const val VLC_FROM_START = -1
-const val VLC_FROM_PROGRESS = -2
const val VLC_EXTRA_POSITION_OUT = "extra_position"
const val VLC_EXTRA_DURATION_OUT = "extra_duration"
const val VLC_LAST_ID_KEY = "vlc_last_open_id"
+const val WEB_VIDEO_CAST_PACKAGE = "com.instantbits.cast.webvideo"
+
+
// Short name for requests client to make it nicer to use
var app = Requests(responseParser = object : ResponseParser {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
index 09eae138..1997edbf 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
@@ -55,6 +55,9 @@ const val ACTION_SHOW_DESCRIPTION = 15
const val ACTION_DOWNLOAD_EPISODE_SUBTITLE = 13
const val ACTION_DOWNLOAD_EPISODE_SUBTITLE_MIRROR = 14
+const val ACTION_PLAY_EPISODE_IN_WEB_VIDEO = 16
+
+
data class EpisodeClickEvent(val action: Int, val data: ResultEpisode)
class EpisodeAdapter(
@@ -73,6 +76,7 @@ class EpisodeAdapter(
1 -> ACTION_PLAY_EPISODE_IN_PLAYER
2 -> ACTION_PLAY_EPISODE_IN_VLC_PLAYER
3 -> ACTION_PLAY_EPISODE_IN_BROWSER
+ 4 -> ACTION_PLAY_EPISODE_IN_WEB_VIDEO
else -> ACTION_PLAY_EPISODE_IN_PLAYER
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
index 6f3cb686..9bf378eb 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
@@ -3,6 +3,7 @@ package com.lagradost.cloudstream3.ui.result
import android.app.Activity
import android.content.*
import android.net.Uri
+import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.core.content.FileProvider
@@ -51,9 +52,7 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
import com.lagradost.cloudstream3.utils.DataStoreHelper.setDub
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultEpisode
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultSeason
-import com.lagradost.cloudstream3.utils.UIHelper.checkWrite
import com.lagradost.cloudstream3.utils.UIHelper.navigate
-import com.lagradost.cloudstream3.utils.UIHelper.requestRW
import kotlinx.coroutines.*
import java.io.File
import java.lang.Math.abs
@@ -955,105 +954,152 @@ class ResultViewModel2 : ViewModel() {
return LinkLoadingResult(sortUrls(links), sortSubs(subs))
}
+ private fun launchActivity(
+ activity: Activity?,
+ work: suspend (CoroutineScope.(Activity) -> Unit)
+ ): Job? {
+ val act = activity ?: return null
+ return CoroutineScope(Dispatchers.IO).launch {
+ try {
+ work(act)
+ } catch (t: Throwable) {
+ logError(t)
+ main {
+ if (t is ActivityNotFoundException) {
+ showToast(activity, txt(R.string.app_not_found_error), Toast.LENGTH_LONG)
+ } else {
+ showToast(activity, t.toString(), Toast.LENGTH_LONG)
+ }
+ }
+ }
+ }
+ }
+
+ private fun playInWebVideo(
+ activity: Activity?,
+ link: ExtractorLink,
+ title: String?,
+ posterUrl: String?,
+ subtitles: List
+ ) = launchActivity(activity) { act ->
+ val shareVideo = Intent(Intent.ACTION_VIEW)
+
+ shareVideo.setDataAndType(Uri.parse(link.url), "video/*")
+ shareVideo.setPackage(WEB_VIDEO_CAST_PACKAGE)
+ shareVideo.putExtra("subs", subtitles.map { it.url.toUri() }.toTypedArray())
+ title?.let { shareVideo.putExtra("title", title) }
+ posterUrl?.let { shareVideo.putExtra("poster", posterUrl) }
+ val headers = Bundle().apply {
+ if (link.referer.isNotBlank())
+ putString("Referer", link.referer)
+ putString("User-Agent", USER_AGENT)
+ for ((key, value) in link.headers) {
+ putString(key, value)
+ }
+ }
+ shareVideo.putExtra("android.media.intent.extra.HTTP_HEADERS", headers)
+ shareVideo.putExtra("secure_uri", true)
+
+ act.startActivity(shareVideo)
+ }
+
// https://wiki.videolan.org/Android_Player_Intents/
private fun playWithVlc(
- act: Activity?,
+ activity: Activity?,
data: LinkLoadingResult,
id: Int,
resume: Boolean = true,
// if it is only a single link then resume works correctly
singleFile: Boolean? = null
- ) = ioSafe {
- if (act == null) return@ioSafe
- if (data.links.isEmpty()) {
- showToast(act, R.string.no_links_found_toast, Toast.LENGTH_SHORT)
- return@ioSafe
- }
- try {
- if (!act.checkWrite()) {
- act.requestRW()
- if (act.checkWrite()) return@ioSafe
- }
+ ) = launchActivity(activity) { act ->
+ val vlcIntent = Intent(VLC_INTENT_ACTION_RESULT)
- val vlcIntent = Intent(VLC_INTENT_ACTION_RESULT)
+ vlcIntent.setPackage(VLC_PACKAGE)
+ vlcIntent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
+ vlcIntent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)
+ vlcIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ vlcIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
+ val outputDir = act.cacheDir
- vlcIntent.setPackage(VLC_PACKAGE)
- vlcIntent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
- vlcIntent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)
- vlcIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
- vlcIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
- val outputDir = act.cacheDir
+ if (singleFile ?: (data.links.size == 1)) {
+ vlcIntent.setDataAndType(data.links.first().url.toUri(), "video/*")
+ } else {
+ val outputFile = File.createTempFile("mirrorlist", ".m3u8", outputDir)
- if (singleFile ?: (data.links.size == 1)) {
- vlcIntent.setDataAndType(data.links.first().url.toUri(), "video/*")
- } else {
- val outputFile = File.createTempFile("mirrorlist", ".m3u8", outputDir)
+ var text = "#EXTM3U"
- var text = "#EXTM3U"
-
- // With subtitles it doesn't work for no reason :(
+ // With subtitles it doesn't work for no reason :(
// for (sub in data.subs) {
// text += "\n#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"${sub.name}\",DEFAULT=NO,AUTOSELECT=NO,FORCED=NO,LANGUAGE=\"${sub.name}\",URI=\"${sub.url}\""
// }
- for (link in data.links) {
- text += "\n#EXTINF:, ${link.name}\n${link.url}"
- }
- outputFile.writeText(text)
+ for (link in data.links) {
+ text += "\n#EXTINF:, ${link.name}\n${link.url}"
+ }
+ outputFile.writeText(text)
- vlcIntent.setDataAndType(
- FileProvider.getUriForFile(
+ vlcIntent.setDataAndType(
+ FileProvider.getUriForFile(
+ act,
+ act.applicationContext.packageName + ".provider",
+ outputFile
+ ), "video/*"
+ )
+ }
+
+ val position = if (resume) {
+ getViewPos(id)?.position ?: 0L
+ } else {
+ 1L
+ }
+ vlcIntent.putExtra("from_start", !resume)
+ vlcIntent.putExtra("position", position)
+ //vlcIntent.putExtra("subtitles_location", data.subs.first().url)
+ /*for (s in data.subs) {
+ if (s.origin == SubtitleOrigin.URL) {
+ try {
+ val txt = app.get(s.url, s.headers).text
+ val subtitleFile = File.createTempFile("subtitle1", ".srt", outputDir)
+ subtitleFile.writeText(txt)
+ println("Subtitles::::::${subtitleFile.path}")
+ vlcIntent.putExtra("subtitles_location", FileProvider.getUriForFile(
act,
act.applicationContext.packageName + ".provider",
- outputFile
- ), "video/*"
- )
- }
-
- val position = if (resume) {
- getViewPos(id)?.position ?: 0L
- } else {
- 1L
- }
- vlcIntent.putExtra("from_start", !resume)
- vlcIntent.putExtra("position", position)
- //vlcIntent.putExtra("subtitles_location", data.subs.first().url)
- /*for (s in data.subs) {
- if (s.origin == SubtitleOrigin.URL) {
- try {
- val txt = app.get(s.url, s.headers).text
- val subtitleFile = File.createTempFile("subtitle1", ".srt", outputDir)
- subtitleFile.writeText(txt)
- println("Subtitles::::::${subtitleFile.path}")
- vlcIntent.putExtra("subtitles_location", FileProvider.getUriForFile(
- act,
- act.applicationContext.packageName + ".provider",
- subtitleFile
- ))
- break
- } catch (t : Throwable) {
- logError(t)
- }
- }
- }*/
-
- vlcIntent.component = VLC_COMPONENT
- act.setKey(VLC_LAST_ID_KEY, id)
- act.startActivityForResult(vlcIntent, VLC_REQUEST_CODE)
- } catch (e: Exception) {
- logError(e)
- main {
- if (e is ActivityNotFoundException) {
- showToast(act, txt(R.string.vlc_not_found_error), Toast.LENGTH_LONG)
- } else {
- showToast(act, e.toString(), Toast.LENGTH_LONG)
+ subtitleFile
+ ))
+ break
+ } catch (t : Throwable) {
+ logError(t)
}
}
+ }*/
+
+ vlcIntent.component = VLC_COMPONENT
+ act.setKey(VLC_LAST_ID_KEY, id)
+ act.startActivityForResult(vlcIntent, VLC_REQUEST_CODE)
+ }
+
+ fun handleAction(activity: Activity?, click: EpisodeClickEvent) =
+ viewModelScope.launchSafe {
+ handleEpisodeClickEvent(activity, click)
}
- }
- fun handleAction(activity: Activity?, click: EpisodeClickEvent) = viewModelScope.launchSafe {
- handleEpisodeClickEvent(activity, click)
- }
+ data class ExternalApp(
+ val packageString: String,
+ val name: Int,
+ val action: Int,
+ )
+
+ private val apps = listOf(
+ ExternalApp(
+ VLC_PACKAGE,
+ R.string.player_settings_play_in_vlc,
+ ACTION_PLAY_EPISODE_IN_VLC_PLAYER
+ ), ExternalApp(
+ WEB_VIDEO_CAST_PACKAGE,
+ R.string.player_settings_play_in_web,
+ ACTION_PLAY_EPISODE_IN_WEB_VIDEO
+ )
+ )
private suspend fun handleEpisodeClickEvent(activity: Activity?, click: EpisodeClickEvent) {
when (click.action) {
@@ -1069,9 +1115,17 @@ class ResultViewModel2 : ViewModel() {
}
options.add(txt(R.string.episode_action_play_in_app) to ACTION_PLAY_EPISODE_IN_PLAYER)
- if (activity?.isAppInstalled(VLC_PACKAGE) == true) {
- options.add(txt(R.string.episode_action_play_in_vlc) to ACTION_PLAY_EPISODE_IN_VLC_PLAYER)
+ for (app in apps) {
+ if (activity?.isAppInstalled(app.packageString) == true) {
+ options.add(
+ txt(
+ R.string.episode_action_play_in_format,
+ txt(app.name)
+ ) to app.action
+ )
+ }
}
+
options.addAll(
listOf(
txt(R.string.episode_action_play_in_browser) to ACTION_PLAY_EPISODE_IN_BROWSER,
@@ -1247,6 +1301,11 @@ class ResultViewModel2 : ViewModel() {
}
ACTION_PLAY_EPISODE_IN_VLC_PLAYER -> {
loadLinks(click.data, isVisible = true, isCasting = true) { links ->
+ if (links.links.isEmpty()) {
+ showToast(activity, R.string.no_links_found_toast, Toast.LENGTH_SHORT)
+ return@loadLinks
+ }
+
playWithVlc(
activity,
links,
@@ -1254,6 +1313,22 @@ class ResultViewModel2 : ViewModel() {
)
}
}
+ ACTION_PLAY_EPISODE_IN_WEB_VIDEO -> acquireSingleLink(
+ click.data,
+ isCasting = true,
+ txt(
+ R.string.episode_action_play_in_format,
+ txt(R.string.player_settings_play_in_web)
+ )
+ ) { (result, index) ->
+ playInWebVideo(
+ activity,
+ result.links[index],
+ click.data.name ?: click.data.headerName,
+ click.data.poster,
+ result.subs
+ )
+ }
ACTION_PLAY_EPISODE_IN_PLAYER -> {
val data = currentResponse?.syncData?.toList() ?: emptyList()
val list =
@@ -1319,7 +1394,11 @@ class ResultViewModel2 : ViewModel() {
}, {
if (this !is AnimeLoadResponse) return@argamap
val map =
- Kitsu.getEpisodesDetails(getMalId(), getAniListId(), isResponseRequired = false)
+ Kitsu.getEpisodesDetails(
+ getMalId(),
+ getAniListId(),
+ isResponseRequired = false
+ )
if (map.isNullOrEmpty()) return@argamap
updateEpisodes = DubStatus.values().map { dubStatus ->
val current =
@@ -1339,8 +1418,10 @@ class ResultViewModel2 : ViewModel() {
val currentBack = this
this.description = this.description ?: node.description?.en
this.name = this.name ?: node.titles?.canonical
- this.episode = this.episode ?: node.num ?: episodeNumbers[index]
- this.posterUrl = this.posterUrl ?: node.thumbnail?.original?.url
+ this.episode =
+ this.episode ?: node.num ?: episodeNumbers[index]
+ this.posterUrl =
+ this.posterUrl ?: node.thumbnail?.original?.url
}
}
}
@@ -1628,7 +1709,8 @@ class ResultViewModel2 : ViewModel() {
for ((index, i) in ep.value.withIndex()) {
val episode = i.episode ?: (index + 1)
val id =
- mainId + episode + idIndex * 1_000_000 + (i.season?.times(10_000) ?: 0)
+ mainId + episode + idIndex * 1_000_000 + (i.season?.times(10_000)
+ ?: 0)
if (!existingEpisodes.contains(id)) {
existingEpisodes.add(id)
val seasonData = loadResponse.seasonNames.getSeason(i.season)
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 23b9af9b..3fb2e26c 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -274,7 +274,7 @@
حلقة كروم كاست
مرآة كروم كاست
تشغيل في التطبيق
- VLC تشغيل في
+ %s تشغيل في
تشغيل في الويب
نسخ الرابط
التحميل التلقائي
diff --git a/app/src/main/res/values-bp/strings.xml b/app/src/main/res/values-bp/strings.xml
index d4cb4caa..bffaf804 100644
--- a/app/src/main/res/values-bp/strings.xml
+++ b/app/src/main/res/values-bp/strings.xml
@@ -279,7 +279,7 @@
Episódio pelo Chromecast
Alternativa pelo Chromecast
Assistir no App
- Assistir no VLC
+ Assistir no %s
Assistir no navegador
Copiar link
Auto download
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index aa840760..9e00f17b 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -268,7 +268,7 @@
Chromecastovat epizodu
Chromecast jako zrcadlo
Přehrát v aplikace
- Přehrát ve VLC
+ Přehrát ve %s
Přehrát v prohlížeči
Zkopírovat odkaz
Automaticky stáhnout
diff --git a/app/src/main/res/values-de/strings-de.xml b/app/src/main/res/values-de/strings-de.xml
index 621080b5..e1d657c7 100644
--- a/app/src/main/res/values-de/strings-de.xml
+++ b/app/src/main/res/values-de/strings-de.xml
@@ -281,7 +281,7 @@
Chromecast-Episode
Chromecastmirror
In App wiedergeben
- In VLC wiedergeben
+ In %s wiedergeben
In Browser wiedergeben
Link kopieren
Auto Download
diff --git a/app/src/main/res/values-es/array.xml b/app/src/main/res/values-es/array.xml
index ae986642..d1e5be2f 100644
--- a/app/src/main/res/values-es/array.xml
+++ b/app/src/main/res/values-es/array.xml
@@ -156,7 +156,7 @@
- @string/episode_action_chromecast_episode
- @string/episode_action_chromecast_mirror
- @string/episode_action_play_in_app
- - @string/episode_action_play_in_vlc
+ - @string/episode_action_play_in_format
- @string/episode_action_play_in_browser
- @string/episode_action_copy_link
- @string/episode_action_auto_download
diff --git a/app/src/main/res/values-es/strings-es.xml b/app/src/main/res/values-es/strings-es.xml
index 9b739b39..172d079c 100644
--- a/app/src/main/res/values-es/strings-es.xml
+++ b/app/src/main/res/values-es/strings-es.xml
@@ -269,7 +269,7 @@
Episodio Chromecast
Espejo Chromecast
Reproducir en la app
- Reproducir en VLC
+ Reproducir en %s
Reproducir en el navegador
Copiar enlace
Descarga automática
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 7d259b31..c98173ce 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -164,7 +164,7 @@
Episode Chromecast
Miroir Chromecast
Lecture dans l\'application
- Lecture dans VLC
+ Lecture dans %s
Lecture dans le navigateur
Copier le lien
Téléchargement Automatique
diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml
index e3f0a233..a6ad0af7 100644
--- a/app/src/main/res/values-hi/strings.xml
+++ b/app/src/main/res/values-hi/strings.xml
@@ -136,7 +136,7 @@
क्रोमकास्ट एपिसोड
कक्रोमकास्ट मिरर
एप्प मैं चलाये
- VLC में चलाए
+ %s में चलाए
Browser में चलाए
लिंक कॉपी करें
डाउनलोड करे
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index bc2a7098..2a7bff1c 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -299,7 +299,7 @@
Chromecast epizoda
Chromecast mirror
Pokreni u aplikaciji
- Pokreni u VLC-u
+ Pokreni u %s
Pokreni u pregledniku
Kopiraj poveznicu
Automatsko preuzimanje
diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml
index e8f7e9e9..31b84bfd 100644
--- a/app/src/main/res/values-in/strings.xml
+++ b/app/src/main/res/values-in/strings.xml
@@ -264,7 +264,7 @@
Episode Chromecast
Mirror Chromecast
Putar di aplikasi
- Putar di VLC
+ Putar di %s
Putar di browser
Salin tautan
Download otomatis
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index db72b31d..86206213 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -271,7 +271,7 @@
Chromecast
Chromecast mirror
Riproduci in app
- Riproduci in VLC
+ Riproduci in %s
Riproduci nel browser
Copia link
Download
diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml
index 96044dc7..85aee997 100644
--- a/app/src/main/res/values-mk/strings.xml
+++ b/app/src/main/res/values-mk/strings.xml
@@ -190,7 +190,7 @@
Епизода на Chromecast
Огледало на Chromecastr
Пушти во апликацијата
- Пушти на VLC
+ Пушти на %s
Пушти на прелистувач
Копирај линк
Авто превземање
diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml
index d8fab674..78f45e26 100644
--- a/app/src/main/res/values-ml/strings.xml
+++ b/app/src/main/res/values-ml/strings.xml
@@ -175,7 +175,7 @@
ആപ്പിൽ പ്ലേയ് ചെയ്യുക
- VLCയിൽ പ്ലേയ് ചെയ്യുക
+ %sയിൽ പ്ലേയ് ചെയ്യുക
ബ്രൗസറിൽ പ്ലേയ് ചെയ്യുക
ലിങ്ക് പകർത്തുക
ഡൌൺലോഡ് ചെയ്യൂ
diff --git a/app/src/main/res/values-mo/string.xml b/app/src/main/res/values-mo/string.xml
index 340428b9..361aaf56 100644
--- a/app/src/main/res/values-mo/string.xml
+++ b/app/src/main/res/values-mo/string.xml
@@ -145,7 +145,7 @@
aauugghhooo-ahah ohaaauugghh
aoohaaahhu ahouuhhh
ooo-ahahaauuh aaahhu
- ooo-ahah ohaauuh
+ ooo-ahah ohaauuh
ahoha ooo-ahahohoohah oooohh
aauugghhahhaauugghh
aaaghhoooohh aaahhu ahooo
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index a5afd785..7daca143 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -274,7 +274,7 @@
Chromecast aflevering
Chromecast mirror
Speel in app
- Speel in VLC
+ Speel in %s
Speel in browser
Kopieer link
Automatisch downloaden
diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml
index fc7cbbe2..ebd6ee49 100644
--- a/app/src/main/res/values-no/strings.xml
+++ b/app/src/main/res/values-no/strings.xml
@@ -196,7 +196,7 @@
Støpt Episode
Støpt Speil
Spill i appen
- Spill i VLC
+ Spill i %s
Spill i nettleseren
Kopier link
Automatisk nedlasting
diff --git a/app/src/main/res/values-pl/array.xml b/app/src/main/res/values-pl/array.xml
index 3fe30d95..30b6f4a1 100644
--- a/app/src/main/res/values-pl/array.xml
+++ b/app/src/main/res/values-pl/array.xml
@@ -165,7 +165,7 @@
- @string/episode_action_chromecast_episode
- @string/episode_action_chromecast_mirror
- @string/episode_action_play_in_app
- - @string/episode_action_play_in_vlc
+ - @string/episode_action_play_in_format
- @string/episode_action_play_in_browser
- @string/episode_action_copy_link
- @string/episode_action_auto_download
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index fdc89692..6da1cc8f 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -252,7 +252,7 @@
Chromecast odcinka
Chromecast mirroru
Odtwórz w aplikacji
- Odtwórz w VLC
+ Odtwórz w %s
Odtwórz w przeglądarce
Kopiuj link
Automatyczne pobieranie
diff --git a/app/src/main/res/values-pt/strings-pt.xml b/app/src/main/res/values-pt/strings-pt.xml
index de70f746..375c3193 100644
--- a/app/src/main/res/values-pt/strings-pt.xml
+++ b/app/src/main/res/values-pt/strings-pt.xml
@@ -268,7 +268,7 @@
Episódio pelo Chromecast
Alternativa pelo Chromecast
Reproduzir na app
- Reproduzir no VLC
+ Reproduzir no %s
Reproduzir no navegador
Copiar link
Transferência Automática
diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml
index 7d1f3458..ce8f328c 100644
--- a/app/src/main/res/values-ro/strings.xml
+++ b/app/src/main/res/values-ro/strings.xml
@@ -267,7 +267,7 @@
Chromecast
Chromecast alternativ
Redă în Aplicație
- Redă în VLC
+ Redă în %s
Redă în Browser
Copiază link-ul
Auto-descărcare
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index 3f3cead1..58ff060e 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -167,7 +167,7 @@
Chromecasta ett Avsnitt
Chromecasta en Länk
Spela upp i appen
- Spela upp i VLC
+ Spela upp i %s
Spela upp i webbläsaren
Kopiera länk
Automatisk nerladdning
diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml
index 9d0e0e02..dfe922ee 100644
--- a/app/src/main/res/values-tl/strings.xml
+++ b/app/src/main/res/values-tl/strings.xml
@@ -204,7 +204,7 @@
Chromecast Episode
Chromecast Mirror
I-play sa App
- I-play sa VLC
+ I-play sa %s
I-play sa browser
Kopyahin ang Link
Awtomatiking i-download
diff --git a/app/src/main/res/values-tr/array.xml b/app/src/main/res/values-tr/array.xml
index dbb17d36..177be03b 100644
--- a/app/src/main/res/values-tr/array.xml
+++ b/app/src/main/res/values-tr/array.xml
@@ -156,7 +156,7 @@
- @string/episode_action_chromecast_episode
- @string/episode_action_chromecast_mirror
- @string/episode_action_play_in_app
- - @string/episode_action_play_in_vlc
+ - @string/episode_action_play_in_format
- @string/episode_action_play_in_browser
- @string/episode_action_copy_link
- @string/episode_action_auto_download
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 14db2f6b..48e36013 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -272,7 +272,7 @@
Bölümü Chromecast ile yayınla
Bağlantıyı Chromecast ile yayınla
Uygulamada oynat
- VLC\'de oynat
+ %s\'de oynat
Tarayıcıda oynat
Linki kopyala
Otomatik indir
diff --git a/app/src/main/res/values-vi/array.xml b/app/src/main/res/values-vi/array.xml
index 5fee2d29..a5145c9e 100644
--- a/app/src/main/res/values-vi/array.xml
+++ b/app/src/main/res/values-vi/array.xml
@@ -157,7 +157,7 @@
- @string/episode_action_chromecast_episode
- @string/episode_action_chromecast_mirror
- @string/episode_action_play_in_app
- - @string/episode_action_play_in_vlc
+ - @string/episode_action_play_in_format
- @string/episode_action_play_in_browser
- @string/episode_action_copy_link
- @string/episode_action_auto_download
diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml
index 302c13b5..ce8358cf 100644
--- a/app/src/main/res/values-vi/strings.xml
+++ b/app/src/main/res/values-vi/strings.xml
@@ -292,7 +292,7 @@
Tập Chromecast
Chiếu Chromecast
Xem với trình phát mặc định
- Xem với trình phát VLC
+ Xem với trình phát %s
Xem tại trình duyệt
Sao chép liên kết
Tự động tải xuống
diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml
index d3ff6e1e..a6557990 100644
--- a/app/src/main/res/values-zh/strings.xml
+++ b/app/src/main/res/values-zh/strings.xml
@@ -303,7 +303,7 @@
投屏剧集
投屏镜像
在应用中播放
- 在 VLC 中播放
+ 在 %s 中播放
在浏览器中播放
复制链接
自动下载
diff --git a/app/src/main/res/values/array.xml b/app/src/main/res/values/array.xml
index 230f00d7..e4c8eb78 100644
--- a/app/src/main/res/values/array.xml
+++ b/app/src/main/res/values/array.xml
@@ -36,12 +36,14 @@
- @string/player_settings_play_in_app
- @string/player_settings_play_in_vlc
+ - @string/player_settings_play_in_web
- @string/player_settings_play_in_browser
- 1
- 2
+ - 4
- 3
@@ -187,7 +189,7 @@
- @string/episode_action_chromecast_episode
- @string/episode_action_chromecast_mirror
- @string/episode_action_play_in_app
- - @string/episode_action_play_in_vlc
+ - @string/episode_action_play_in_format
- @string/episode_action_play_in_browser
- @string/episode_action_copy_link
- @string/episode_action_auto_download
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 7556aed2..7940eca9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -364,7 +364,7 @@
Chromecast episode
Chromecast mirror
Play in app
- Play in VLC
+ Play in %s
Play in browser
Copy link
Auto download
@@ -633,6 +633,7 @@
Preferred video player
Internal player
VLC
+ Web Video Cast
Browser
- VLC not found
+ App not found
From 4538909d9ab22f1e07adf2b8316cb24dc291ee17 Mon Sep 17 00:00:00 2001
From: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Date: Sat, 8 Oct 2022 19:26:41 +0200
Subject: [PATCH 11/22] [skip_ci] update issue config
---
.github/ISSUE_TEMPLATE/config.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index cd3c2574..250734cd 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,8 +1,8 @@
blank_issues_enabled: false
contact_links:
- - name: Report provider bug
+ - name: Request a new provider or report bug with an existing provider
url: https://github.com/recloudstream
- about: Please do not report any provider bugs here. This repository does not contain any providers. Please find the appropriate repository and report your issue there or join the discord.
+ about: Please do not report any provider bugs here or request new providers. This repository does not contain any providers. Please find the appropriate repository and report your issue there or join the discord.
- name: Discord
url: https://discord.gg/5Hus6fM
about: Join our discord for faster support on smaller issues.
From c2d245e8b46dc55a5763e747f8d3ba969c5d6d19 Mon Sep 17 00:00:00 2001
From: reduplicated <110570621+reduplicated@users.noreply.github.com>
Date: Sat, 8 Oct 2022 22:29:17 +0200
Subject: [PATCH 12/22] mpv
---
app/src/main/AndroidManifest.xml | 1 +
.../lagradost/cloudstream3/CommonActivity.kt | 27 +++-
.../lagradost/cloudstream3/MainActivity.kt | 97 +++++++++------
.../cloudstream3/ui/result/EpisodeAdapter.kt | 3 +
.../ui/result/ResultViewModel2.kt | 116 ++++++++++--------
app/src/main/res/values/array.xml | 2 +
app/src/main/res/values/strings.xml | 1 +
7 files changed, 158 insertions(+), 89 deletions(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 043195f5..216a5f21 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -24,6 +24,7 @@
+
diff --git a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt
index 8f22c01a..32df314f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt
@@ -10,16 +10,22 @@ import android.util.Log
import android.view.*
import android.widget.TextView
import android.widget.Toast
+import androidx.activity.ComponentActivity
+import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.MainThread
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SearchView
import androidx.preference.PreferenceManager
import com.google.android.gms.cast.framework.CastSession
+import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
+import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.player.PlayerEventType
+import com.lagradost.cloudstream3.ui.result.ResultFragment
import com.lagradost.cloudstream3.ui.result.UiText
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.updateTv
+import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.Event
import com.lagradost.cloudstream3.utils.UIHelper
import com.lagradost.cloudstream3.utils.UIHelper.hasPIPPermission
@@ -34,6 +40,7 @@ object CommonActivity {
return (this as MainActivity?)?.mSessionManager?.currentCastSession
}
+
var canEnterPipMode: Boolean = false
var canShowPipMode: Boolean = false
var isInPIPMode: Boolean = false
@@ -117,7 +124,7 @@ object CommonActivity {
setLocale(this, localeCode)
}
- fun init(act: Activity?) {
+ fun init(act: ComponentActivity?) {
if (act == null) return
//https://stackoverflow.com/questions/52594181/how-to-know-if-user-has-disabled-picture-in-picture-feature-permission
//https://developer.android.com/guide/topics/ui/picture-in-picture
@@ -129,6 +136,22 @@ object CommonActivity {
act.updateLocale()
act.updateTv()
NewPipe.init(DownloaderTestImpl.getInstance())
+
+ for (resumeApp in resumeApps) {
+ resumeApp.launcher =
+ act.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
+ val resultCode = result.resultCode
+ val data = result.data
+ if (resultCode == AppCompatActivity.RESULT_OK && data != null && resumeApp.position != null && resumeApp.duration != null) {
+ val pos = data.getLongExtra(resumeApp.position, -1L)
+ val dur = data.getLongExtra(resumeApp.duration, -1L)
+ if (dur > 0L && pos > 0L)
+ DataStoreHelper.setViewPos(getKey(resumeApp.lastId), pos, dur)
+ removeKey(resumeApp.lastId)
+ ResultFragment.updateUI()
+ }
+ }
+ }
}
private fun Activity.enterPIPMode() {
@@ -167,7 +190,7 @@ object CommonActivity {
"Amoled" -> R.style.AmoledMode
"AmoledLight" -> R.style.AmoledModeLight
"Monet" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
- R.style.MonetMode else R.style.AppTheme
+ R.style.MonetMode else R.style.AppTheme
else -> R.style.AppTheme
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
index de01ef9f..4c193f42 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
@@ -10,7 +10,7 @@ import android.view.KeyEvent
import android.view.Menu
import android.view.MenuItem
import android.view.WindowManager
-import android.widget.Toast
+import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.IdRes
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
@@ -35,6 +35,8 @@ import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
import com.lagradost.cloudstream3.APIHolder.initAll
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
+import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
+import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.CommonActivity.loadThemes
import com.lagradost.cloudstream3.CommonActivity.onColorSelectedEvent
import com.lagradost.cloudstream3.CommonActivity.onDialogDismissedEvent
@@ -53,7 +55,6 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStri
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.inAppAuths
import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO
-import com.lagradost.cloudstream3.ui.result.ResultFragment
import com.lagradost.cloudstream3.ui.search.SearchResultBuilder
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
@@ -67,10 +68,8 @@ import com.lagradost.cloudstream3.utils.AppUtils.loadResult
import com.lagradost.cloudstream3.utils.BackupUtils.setUpBackup
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.DataStore.getKey
-import com.lagradost.cloudstream3.utils.DataStore.removeKey
import com.lagradost.cloudstream3.utils.DataStore.setKey
import com.lagradost.cloudstream3.utils.DataStoreHelper.migrateResumeWatching
-import com.lagradost.cloudstream3.utils.DataStoreHelper.setViewPos
import com.lagradost.cloudstream3.utils.Event
import com.lagradost.cloudstream3.utils.IOnBackPressed
import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate
@@ -95,18 +94,65 @@ import java.nio.charset.Charset
import kotlin.reflect.KClass
+//https://github.com/videolan/vlc-android/blob/3706c4be2da6800b3d26344fc04fab03ffa4b860/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt#L1898
+//https://wiki.videolan.org/Android_Player_Intents/
+
+//https://github.com/mpv-android/mpv-android/blob/0eb3cdc6f1632636b9c30d52ec50e4b017661980/app/src/main/java/is/xyz/mpv/MPVActivity.kt#L904
+//https://mpv-android.github.io/mpv-android/intent.html
+
+// https://www.webvideocaster.com/integrations
+
+//https://github.com/jellyfin/jellyfin-android/blob/6cbf0edf84a3da82347c8d59b5d5590749da81a9/app/src/main/java/org/jellyfin/mobile/bridge/ExternalPlayer.kt#L225
+
const val VLC_PACKAGE = "org.videolan.vlc"
-const val VLC_INTENT_ACTION_RESULT = "org.videolan.vlc.player.result"
-val VLC_COMPONENT: ComponentName =
- ComponentName(VLC_PACKAGE, "org.videolan.vlc.gui.video.VideoPlayerActivity")
-const val VLC_REQUEST_CODE = 42
-
-const val VLC_EXTRA_POSITION_OUT = "extra_position"
-const val VLC_EXTRA_DURATION_OUT = "extra_duration"
-const val VLC_LAST_ID_KEY = "vlc_last_open_id"
-
+const val MPV_PACKAGE = "is.xyz.mpv"
const val WEB_VIDEO_CAST_PACKAGE = "com.instantbits.cast.webvideo"
+val VLC_COMPONENT = ComponentName(VLC_PACKAGE, "$VLC_PACKAGE.gui.video.VideoPlayerActivity")
+val MPV_COMPONENT = ComponentName(MPV_PACKAGE, "$MPV_PACKAGE.MPVActivity")
+
+//TODO REFACTOR AF
+data class ResultResume(
+ val packageString: String,
+ val action: String = Intent.ACTION_VIEW,
+ val position: String? = null,
+ val duration: String? = null,
+ var launcher: ActivityResultLauncher? = null,
+) {
+ val lastId get() = "${packageString}_last_open_id"
+ suspend fun launch(id: Int?, callback: suspend Intent.() -> Unit) {
+ val intent = Intent(action)
+
+ if (id != null)
+ setKey(lastId, id)
+ else
+ removeKey(lastId)
+
+ intent.setPackage(packageString)
+ callback.invoke(intent)
+ launcher?.launch(intent)
+ }
+}
+
+val VLC = ResultResume(
+ VLC_PACKAGE,
+ "org.videolan.vlc.player.result",
+ "extra_position",
+ "extra_duration",
+)
+
+val MPV = ResultResume(
+ MPV_PACKAGE,
+ //"is.xyz.mpv.MPVActivity.result", // resume not working :pensive:
+ position = "position",
+ duration = "duration",
+)
+
+val WEB_VIDEO = ResultResume(WEB_VIDEO_CAST_PACKAGE)
+
+val resumeApps = arrayOf(
+ VLC, MPV, WEB_VIDEO
+)
// Short name for requests client to make it nicer to use
@@ -373,31 +419,6 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
}
}
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- if (requestCode == VLC_REQUEST_CODE) {
- if (resultCode == RESULT_OK && data != null) {
- val pos: Long =
- data.getLongExtra(
- VLC_EXTRA_POSITION_OUT,
- -1
- ) //Last position in media when player exited
- val dur: Long =
- data.getLongExtra(
- VLC_EXTRA_DURATION_OUT,
- -1
- ) //Last position in media when player exited
- val id = getKey(VLC_LAST_ID_KEY)
- println("SET KEY $id at $pos / $dur")
- if (dur > 0 && pos > 0) {
- setViewPos(id, pos, dur)
- }
- removeKey(VLC_LAST_ID_KEY)
- ResultFragment.updateUI()
- }
- }
- super.onActivityResult(requestCode, resultCode, data)
- }
-
override fun onDestroy() {
val broadcastIntent = Intent()
broadcastIntent.action = "restart_service"
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
index 1997edbf..e9fbd5f9 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
@@ -56,6 +56,7 @@ const val ACTION_DOWNLOAD_EPISODE_SUBTITLE = 13
const val ACTION_DOWNLOAD_EPISODE_SUBTITLE_MIRROR = 14
const val ACTION_PLAY_EPISODE_IN_WEB_VIDEO = 16
+const val ACTION_PLAY_EPISODE_IN_MPV = 17
data class EpisodeClickEvent(val action: Int, val data: ResultEpisode)
@@ -71,12 +72,14 @@ class EpisodeAdapter(
* See array.xml/player_pref_values
**/
fun getPlayerAction(context: Context): Int {
+
val settingsManager = PreferenceManager.getDefaultSharedPreferences(context)
return when (settingsManager.getInt(context.getString(R.string.player_pref_key), 1)) {
1 -> ACTION_PLAY_EPISODE_IN_PLAYER
2 -> ACTION_PLAY_EPISODE_IN_VLC_PLAYER
3 -> ACTION_PLAY_EPISODE_IN_BROWSER
4 -> ACTION_PLAY_EPISODE_IN_WEB_VIDEO
+ 5 -> ACTION_PLAY_EPISODE_IN_MPV
else -> ACTION_PLAY_EPISODE_IN_PLAYER
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
index 9bf378eb..906b652d 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
@@ -15,6 +15,7 @@ import androidx.lifecycle.viewModelScope
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getId
import com.lagradost.cloudstream3.APIHolder.unixTime
+import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.CommonActivity.getCastSession
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
@@ -43,7 +44,6 @@ import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.ioWork
import com.lagradost.cloudstream3.utils.Coroutines.ioWorkSafe
import com.lagradost.cloudstream3.utils.Coroutines.main
-import com.lagradost.cloudstream3.utils.DataStore.setKey
import com.lagradost.cloudstream3.utils.DataStoreHelper.getDub
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultEpisode
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultSeason
@@ -613,7 +613,7 @@ class ResultViewModel2 : ViewModel() {
val src = "$DOWNLOAD_NAVIGATE_TO/$parentId" // url ?: return@let
// SET VISUAL KEYS
- AcraApplication.setKey(
+ setKey(
DOWNLOAD_HEADER_CACHE,
parentId.toString(),
VideoDownloadHelper.DownloadHeaderCached(
@@ -627,7 +627,7 @@ class ResultViewModel2 : ViewModel() {
)
)
- AcraApplication.setKey(
+ setKey(
DataStore.getFolderName(
DOWNLOAD_EPISODE_CACHE,
parentId.toString()
@@ -956,12 +956,16 @@ class ResultViewModel2 : ViewModel() {
private fun launchActivity(
activity: Activity?,
- work: suspend (CoroutineScope.(Activity) -> Unit)
+ resumeApp: ResultResume,
+ id: Int? = null,
+ work: suspend (Intent.(Activity) -> Unit)
): Job? {
val act = activity ?: return null
return CoroutineScope(Dispatchers.IO).launch {
try {
- work(act)
+ resumeApp.launch(id) {
+ work(act)
+ }
} catch (t: Throwable) {
logError(t)
main {
@@ -981,14 +985,12 @@ class ResultViewModel2 : ViewModel() {
title: String?,
posterUrl: String?,
subtitles: List
- ) = launchActivity(activity) { act ->
- val shareVideo = Intent(Intent.ACTION_VIEW)
+ ) = launchActivity(activity, WEB_VIDEO) {
+ setDataAndType(Uri.parse(link.url), "video/*")
- shareVideo.setDataAndType(Uri.parse(link.url), "video/*")
- shareVideo.setPackage(WEB_VIDEO_CAST_PACKAGE)
- shareVideo.putExtra("subs", subtitles.map { it.url.toUri() }.toTypedArray())
- title?.let { shareVideo.putExtra("title", title) }
- posterUrl?.let { shareVideo.putExtra("poster", posterUrl) }
+ putExtra("subs", subtitles.map { it.url.toUri() }.toTypedArray())
+ title?.let { putExtra("title", title) }
+ posterUrl?.let { putExtra("poster", posterUrl) }
val headers = Bundle().apply {
if (link.referer.isNotBlank())
putString("Referer", link.referer)
@@ -997,10 +999,27 @@ class ResultViewModel2 : ViewModel() {
putString(key, value)
}
}
- shareVideo.putExtra("android.media.intent.extra.HTTP_HEADERS", headers)
- shareVideo.putExtra("secure_uri", true)
+ putExtra("android.media.intent.extra.HTTP_HEADERS", headers)
+ putExtra("secure_uri", true)
+ }
- act.startActivity(shareVideo)
+ private fun playWithMpv(
+ activity: Activity?,
+ id: Int,
+ link: ExtractorLink,
+ subtitles: List,
+ resume: Boolean = true,
+ ) = launchActivity(activity, MPV, id) {
+ putExtra("subs", subtitles.map { it.url.toUri() }.toTypedArray())
+ putExtra("subs.name", subtitles.map { it.name }.toTypedArray())
+ putExtra("subs.filename", subtitles.map { it.name }.toTypedArray())
+ setDataAndType(Uri.parse(link.url), "video/*")
+ component = MPV_COMPONENT
+ putExtra("secure_uri", true)
+ putExtra("return_result", true)
+ val position = getViewPos(id)?.position
+ if (resume && position != null)
+ putExtra("position", position.toInt())
}
// https://wiki.videolan.org/Android_Player_Intents/
@@ -1011,18 +1030,16 @@ class ResultViewModel2 : ViewModel() {
resume: Boolean = true,
// if it is only a single link then resume works correctly
singleFile: Boolean? = null
- ) = launchActivity(activity) { act ->
- val vlcIntent = Intent(VLC_INTENT_ACTION_RESULT)
+ ) = launchActivity(activity, VLC, id) { act ->
+ addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
+ addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)
+ addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
- vlcIntent.setPackage(VLC_PACKAGE)
- vlcIntent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
- vlcIntent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)
- vlcIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
- vlcIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
val outputDir = act.cacheDir
if (singleFile ?: (data.links.size == 1)) {
- vlcIntent.setDataAndType(data.links.first().url.toUri(), "video/*")
+ setDataAndType(data.links.first().url.toUri(), "video/*")
} else {
val outputFile = File.createTempFile("mirrorlist", ".m3u8", outputDir)
@@ -1037,7 +1054,7 @@ class ResultViewModel2 : ViewModel() {
}
outputFile.writeText(text)
- vlcIntent.setDataAndType(
+ setDataAndType(
FileProvider.getUriForFile(
act,
act.applicationContext.packageName + ".provider",
@@ -1051,33 +1068,14 @@ class ResultViewModel2 : ViewModel() {
} else {
1L
}
- vlcIntent.putExtra("from_start", !resume)
- vlcIntent.putExtra("position", position)
- //vlcIntent.putExtra("subtitles_location", data.subs.first().url)
- /*for (s in data.subs) {
- if (s.origin == SubtitleOrigin.URL) {
- try {
- val txt = app.get(s.url, s.headers).text
- val subtitleFile = File.createTempFile("subtitle1", ".srt", outputDir)
- subtitleFile.writeText(txt)
- println("Subtitles::::::${subtitleFile.path}")
- vlcIntent.putExtra("subtitles_location", FileProvider.getUriForFile(
- act,
- act.applicationContext.packageName + ".provider",
- subtitleFile
- ))
- break
- } catch (t : Throwable) {
- logError(t)
- }
- }
- }*/
- vlcIntent.component = VLC_COMPONENT
- act.setKey(VLC_LAST_ID_KEY, id)
- act.startActivityForResult(vlcIntent, VLC_REQUEST_CODE)
+ component = VLC_COMPONENT
+
+ putExtra("from_start", !resume)
+ putExtra("position", position)
}
+
fun handleAction(activity: Activity?, click: EpisodeClickEvent) =
viewModelScope.launchSafe {
handleEpisodeClickEvent(activity, click)
@@ -1098,6 +1096,11 @@ class ResultViewModel2 : ViewModel() {
WEB_VIDEO_CAST_PACKAGE,
R.string.player_settings_play_in_web,
ACTION_PLAY_EPISODE_IN_WEB_VIDEO
+ ),
+ ExternalApp(
+ MPV_PACKAGE,
+ R.string.player_settings_play_in_mpv,
+ ACTION_PLAY_EPISODE_IN_MPV
)
)
@@ -1329,6 +1332,21 @@ class ResultViewModel2 : ViewModel() {
result.subs
)
}
+ ACTION_PLAY_EPISODE_IN_MPV -> acquireSingleLink(
+ click.data,
+ isCasting = true,
+ txt(
+ R.string.episode_action_play_in_format,
+ txt(R.string.player_settings_play_in_mpv)
+ )
+ ) { (result, index) ->
+ playWithMpv(
+ activity,
+ click.data.id,
+ result.links[index],
+ result.subs
+ )
+ }
ACTION_PLAY_EPISODE_IN_PLAYER -> {
val data = currentResponse?.syncData?.toList() ?: emptyList()
val list =
@@ -2107,7 +2125,7 @@ class ResultViewModel2 : ViewModel() {
preferStartEpisode = getResultEpisode(mainId)
preferStartSeason = getResultSeason(mainId)
- AcraApplication.setKey(
+ setKey(
DOWNLOAD_HEADER_CACHE,
mainId.toString(),
VideoDownloadHelper.DownloadHeaderCached(
diff --git a/app/src/main/res/values/array.xml b/app/src/main/res/values/array.xml
index e4c8eb78..3554beb2 100644
--- a/app/src/main/res/values/array.xml
+++ b/app/src/main/res/values/array.xml
@@ -36,6 +36,7 @@
- @string/player_settings_play_in_app
- @string/player_settings_play_in_vlc
+ - @string/player_settings_play_in_mpv
- @string/player_settings_play_in_web
- @string/player_settings_play_in_browser
@@ -43,6 +44,7 @@
- 1
- 2
+ - 5
- 4
- 3
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 7940eca9..924e5eb9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -633,6 +633,7 @@
Preferred video player
Internal player
VLC
+ MPV
Web Video Cast
Browser
App not found
From b6d0141cd9b65c6383aeeab089102873ffbca01b Mon Sep 17 00:00:00 2001
From: Jace <54625750+Jacekun@users.noreply.github.com>
Date: Sun, 9 Oct 2022 04:34:41 +0800
Subject: [PATCH 13/22] Fix Random button on Main page. (#136)
* Fix Random button on Main page.
* Removed unnecessary line
---
.../cloudstream3/ui/home/HomeFragment.kt | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt
index 4620b11f..520b6b99 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt
@@ -521,15 +521,12 @@ class HomeFragment : Fragment() {
}
}
- //Disable Random button, if its toggled off on settings
+ //Load value for toggling Random button. Hide at startup
context?.let {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(it)
toggleRandomButton =
settingsManager.getBoolean(getString(R.string.random_button_key), false)
- home_random?.isVisible = toggleRandomButton
- if (!toggleRandomButton) {
- home_random?.visibility = View.GONE
- }
+ home_random?.visibility = View.GONE
}
observe(homeViewModel.apiName) { apiName ->
@@ -626,6 +623,7 @@ class HomeFragment : Fragment() {
home_loading_shimmer?.stopShimmer()
val d = data.value
+ val mutableListOfResponse = mutableListOf()
listHomepageItems.clear()
// println("ITEMCOUNT: ${d.values.size} ${home_master_recycler?.adapter?.itemCount}")
@@ -638,6 +636,11 @@ class HomeFragment : Fragment() {
home_loading_error?.isVisible = false
home_loaded?.isVisible = true
if (toggleRandomButton) {
+ //Flatten list
+ d.values.forEach { dlist ->
+ mutableListOfResponse.addAll(dlist.list.list)
+ }
+ listHomepageItems.addAll(mutableListOfResponse.distinctBy { it.url })
home_random?.isVisible = listHomepageItems.isNotEmpty()
} else {
home_random?.isGone = true
@@ -1031,4 +1034,4 @@ class HomeFragment : Fragment() {
}
}
}
-}
\ No newline at end of file
+}
From 61fb302a373814380257eac5f0e03d2d96a3d99f Mon Sep 17 00:00:00 2001
From: reduplicated <110570621+reduplicated@users.noreply.github.com>
Date: Sun, 9 Oct 2022 01:36:06 +0200
Subject: [PATCH 14/22] delay mainpage
---
.../com/lagradost/cloudstream3/MainAPI.kt | 11 +++++++-
.../cloudstream3/ui/APIRepository.kt | 27 +++++++++++++++++--
.../cloudstream3/ui/home/HomeViewModel.kt | 2 ++
3 files changed, 37 insertions(+), 3 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt
index 47afbc42..416a7238 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt
@@ -381,7 +381,16 @@ abstract class MainAPI {
open var storedCredentials: String? = null
open var canBeOverridden: Boolean = true
- //open val uniqueId : Int by lazy { this.name.hashCode() } // in case of duplicate providers you can have a shared id
+ /** if this is turned on then it will request the homepage one after the other,
+ used to delay if they block many request at the same time*/
+ open var sequentialMainPage : Boolean = false
+ /** in milliseconds, this can be used to add more delay between homepage requests
+ * on first load if sequentialMainPage is turned on */
+ open var sequentialMainPageDelay : Long = 0L
+ /** in milliseconds, this can be used to add more delay between homepage requests when scrolling */
+ open var sequentialMainPageScrollDelay : Long = 0L
+ /** used to keep track when last homepage request was in unixtime ms */
+ var lastHomepageRequest : Long = 0L
open var lang = "en" // ISO_639_1 check SubtitleHelper
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt
index 34cb262c..b43b1434 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt
@@ -1,10 +1,12 @@
package com.lagradost.cloudstream3.ui
import com.lagradost.cloudstream3.*
+import com.lagradost.cloudstream3.APIHolder.unixTimeMS
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.ExtractorLink
+import kotlinx.coroutines.delay
class APIRepository(val api: MainAPI) {
companion object {
@@ -62,12 +64,33 @@ class APIRepository(val api: MainAPI) {
}
}
+ suspend fun waitForHomeDelay() {
+ val delta = api.sequentialMainPageScrollDelay + api.lastHomepageRequest - unixTimeMS
+ if(delta < 0) return
+ delay(delta)
+ }
+
suspend fun getMainPage(page: Int, nameIndex: Int? = null): Resource> {
return safeApiCall {
+ api.lastHomepageRequest = unixTimeMS
+
nameIndex?.let { api.mainPage.getOrNull(it) }?.let { data ->
listOf(api.getMainPage(page, MainPageRequest(data.name, data.data)))
- } ?: api.mainPage.apmap { data ->
- api.getMainPage(page, MainPageRequest(data.name, data.data))
+ } ?: run {
+ if (api.sequentialMainPage) {
+ var first = true
+ api.mainPage.map { data ->
+ if (!first) // dont want to sleep on first request
+ delay(api.sequentialMainPageDelay)
+ first = false
+
+ api.getMainPage(page, MainPageRequest(data.name, data.data))
+ }
+ } else {
+ api.mainPage.apmap { data ->
+ api.getMainPage(page, MainPageRequest(data.name, data.data))
+ }
+ }
}
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt
index d8497876..30fd45c1 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt
@@ -162,6 +162,8 @@ class HomeViewModel : ViewModel() {
lock += name
repo?.apply {
+ waitForHomeDelay()
+
expandable[name]?.let { current ->
debugAssert({ !current.hasNext }) {
"Expand called when not needed"
From e2118c3271ef29dd7f1ebe3b30eea3fb12c60bf0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Samet=20Mert=20Karata=C5=9F?=
<107170849+samertkaratas@users.noreply.github.com>
Date: Sun, 9 Oct 2022 00:35:19 +0000
Subject: [PATCH 15/22] Update Turkish translation (#143)
* Update strings.xml and array.xml
* Fix escape characters
* Little changes
---
app/src/main/res/values-tr/array.xml | 46 ++++++++++-
app/src/main/res/values-tr/strings.xml | 105 ++++++++++++++++++++++++-
2 files changed, 145 insertions(+), 6 deletions(-)
diff --git a/app/src/main/res/values-tr/array.xml b/app/src/main/res/values-tr/array.xml
index 177be03b..dca01736 100644
--- a/app/src/main/res/values-tr/array.xml
+++ b/app/src/main/res/values-tr/array.xml
@@ -14,6 +14,41 @@
- @id/cast_button_type_forward_30_seconds
+
+ - @string/none
+ - Google
+ - Cloudflare
+
+ - AdGuard
+ - DNS.WATCH
+ - Quad9
+
+
+ - 0
+ - 1
+ - 2
+
+ - 4
+ - 5
+ - 6
+
+
+
+ - @string/player_settings_play_in_app
+ - @string/player_settings_play_in_vlc
+ - @string/player_settings_play_in_mpv
+ - @string/player_settings_play_in_web
+ - @string/player_settings_play_in_browser
+
+
+
+ - 1
+ - 2
+ - 5
+ - 4
+ - 3
+
+
- @string/resolution_and_title
- @string/title
@@ -210,7 +245,7 @@
- Parti
- Pembe
- Material You
- - Material You (Secondary)
+ - Material You (İkincil)
- Normal
@@ -233,7 +268,6 @@
- Monet2
-
- Koyu
- Gri
@@ -249,6 +283,14 @@
- Monet
+
+ - Çöktü
+
+ - Ok
+ - Yavaş
+ - Beta
+
+
- @string/automatic
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 48e36013..54b9ebea 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -1,6 +1,16 @@
+ %d %s | %s
+ %s • %s
+ %s / %s
+ %s %s
+ +%d
+ -%d
+ %d
+ %d
+ %.1f/10.0
+ %d
%s Ep %d
Cast: %s
Bölüm %d şu tarihte yayınlanacak:
@@ -14,7 +24,9 @@
Episode Poster
Main Poster
Next Random
+ @string/play_episode
Go back
+ @string/home_change_provider_img_des
Change Provider
Preview Background
@@ -38,6 +50,7 @@
Veri yok
Daha fazla seçenek
Sonraki bölüm
+ @string/synopsis
Türler
Paylaş
Tarayıcıda aç
@@ -70,6 +83,7 @@
İndirme başarısız oldu
İndirme iptal edildi
İndirme bitti
+ %s - %s
Yayınla
Bağlantılar yüklenirken hata oluştu
@@ -149,11 +163,15 @@
Chromecast alt yazı ayarları
Eigengravy modu
- Oynatıcıya bir hız seçeneği ekler
+ Oynatıcıya bir hız seçeneği ekle
Gözlemek için kaydır
Zamanı ayarlamak için sağa veya sola kaydır
Ayarları değiştirmek için kaydır
Sol ve sağ taraftan kaydırarak parlaklık ve sesi ayarla
+
+ Sonraki bölümü otomatik oynat
+ Mevcut bölüm bittiğinde sonraki bölüme başla
+
Gözlemek için çift tıkla
Durdurmak için çift tıkla
Oynatıcı gözleme miktarı
@@ -177,16 +195,18 @@
Ara
Hesaplar
Güncellemeler ve yedek
-
+
Bilgi
Gelişmiş arama
- Sağlayıcılara göre ayrılmış arama sonuçlarını verir
+ Sağlayıcılara göre ayrılmış arama sonuçlarını ver
Yalnızca çökmelerle ilgili verileri gönderir
Hiç veri göndermez
Anime için filler bölümleri gösterir
Fragmanları göster
Kitsu\'dan posterleri göster
+ Arama sonuçlarında seçilen video kalitelerini gizle
+ Otomatik eklenti güncellemeleri
Uygulama güncellemelerini göster
Başlangıçta yeni güncellemeleri otomatik olarak ara
Ön sürümlere güncelle
@@ -208,15 +228,19 @@
Üzgünüz, uygulama çöktü. Geliştiricilere isimsiz bir hata raporu gönderilecek
Sezon
+ %s %d%s
Sezon yok
Bölüm
Bölümler
+ %d-%d
+ %d %s
S
B
Bölüm bulunamadı
Dosyayı sil
Sil
+ @string/sort_cancel
Durdur
Sürdür
-30
@@ -251,6 +275,8 @@
OVA
Asya dramaları
Canlı yayınlar
+ NSFW
+ Diğerleri
Film
@@ -262,6 +288,8 @@
Belgesel
Asya draması
Canlı yayın
+ NSFW
+ Video
Kaynak hatası
Sunucu hatası
@@ -272,7 +300,7 @@
Bölümü Chromecast ile yayınla
Bağlantıyı Chromecast ile yayınla
Uygulamada oynat
- %s\'de oynat
+ %s\'de\/da oynat
Tarayıcıda oynat
Linki kopyala
Otomatik indir
@@ -284,6 +312,10 @@
Dublaj etiketi
Alt yazı etiketi
Başlık
+ show_hd_key
+ show_dub_key
+ show_sub_key
+ show_title_key
Poster üzerindeki öğeler
Güncelleme bulunamadı
@@ -327,6 +359,7 @@
Yakınlaştır
Disclaimer
+ legal_notice_key
Any legal issues regarding the content on this application
should be taken up with the actual file hosts and providers themselves as we are not affiliated with them.
@@ -349,7 +382,9 @@
Sağlayıcı dilleri
Uygulama düzeni
Tercih edilen medya
+ Desteklenen sağlayıcılarda NSFW\'yi etkinleştir
Alt yazı kodlaması
+ Sağlayıcılar
Düzen
Otomatik
@@ -364,6 +399,10 @@
+ anilist_key
+ mal_key
+ opensubtitles_key
+ nginx_key
şifre123
HavalıKullanıcıAdı
hello@world.com
@@ -403,6 +442,7 @@
Hepsi
Maksimum
Minimum
+ @string/none
Dış hat
Çökmüş
Gölge
@@ -464,6 +504,7 @@
Hata
Alt yazılardan seçmeli alt yazıyı kaldır
Alt yazılardaki şişkinliği kaldır
+ Tercih edilen medya diline göre filtrele
Ekstralar
Fragman
Yayına bağlan
@@ -476,5 +517,61 @@
Çökme raporları
Ne izlemek istiyorsunuz?
Bitti
+ Eklentiler
+ Depo ekle
+ Depo ismi
+ Depo URL\'i
+ Eklenti yüklendi
+ Eklenti silindi
+ %s yüklenemedi
+ +18
+ %d %s indirilmeye başlandı
+ %d %s başarıyla indirildi
+ %s\'nin tamamı zaten indirildi
+ Toplu indir
+ eklenti
+ eklentiler
+ Bu aynı zamanda tüm depo eklentilerini de siler
+ Depoyu sil
+ Kullanmak istediğiniz sitelerin listesini indirin
+ İndirilen: %d
+ Devre dışı: %d
+ İndirilmeyen: %d
+ %d eklenti(ler) güncellendi
+ Site eklentilerini yüklemek için bir depo ekleyin
+ Topluluk depolarını görüntüle
+ Herkese açık liste
+ Tüm alt yazılar büyük harf
+
+ Bu depodaki tüm eklentiler indirilsin mi?
+ %s devre dışı bırakıldı
+ Parçalar
+ Ses parçaları
+ Video parçaları
+ Yeniden başlatmada uygula
+
+ Güvenli mod etkin
+ Kurtarılamaz bir çökme meydana geldi ve soruna neden olan eklentiyi bulup kaldırabilmeniz için tüm eklentileri otomatik olarak devre dışı bıraktık.
+ Çökme bilgisini göster
+
+ Puan: %s
+ Açıklama
+ Versiyon
+ Durum
+ Boyut
+ Geliştiriciler
+ Desteklenen
+ Dil
+ Önce eklentiyi yükleyin
+
+ HLS Oynatma Listesi
+
+ Tercih edilen video oynatıcısı
+ Dahili oynatıcı
+ VLC
+ MPV
+ Web Video Yayını
+ Tarayıcı
+ Uygulama bulunamadı
From b3ff3ec086f2fd7aa62d545da06218f64c65a732 Mon Sep 17 00:00:00 2001
From: Thanasis Trispiotis <79643637+thanasistrisp@users.noreply.github.com>
Date: Mon, 10 Oct 2022 22:42:59 +0300
Subject: [PATCH 16/22] update strings.xml Greek (#145)
* update strings.xml
Add new translations to greek language (el) and fix typos.
* remove duplicate
---
app/src/main/res/values-el/strings.xml | 401 +++++++++++++++++++++----
1 file changed, 348 insertions(+), 53 deletions(-)
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index de9ac4f4..11b74938 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -5,39 +5,39 @@
Αναζήτηση
Λήψεις
Ρυθμίσεις
- Ψάξε…
+ Άνοιγμα με CloudStream
+ Αναζήτηση…
Πόστερ
Χωρίς δεδομένα
Περισσότερες Επιλογές
Πίσω
- Επόμενο Επισόδειο
+ Επόμενο Επεισόδιο
Πόστερ
- Πλοκή
- Genres
- Μοίρασε
+ Κατηγορίες
+ Κοινοποίηση
Άνοιγμα στον περιηγητή
- Προσπέραση φορτώματος
+ Παράλειψη φόρτωσης
Φόρτωση…
- Watching
- On-Hold
- Completed
- Dropped
- Plan to Watch
- None
+ Παρακολούθηση
+ Σε αναμονή
+ Ολοκληρώθηκε
+ Διακόπηκε
+ Για παρακολούθηση
+ Τίποτα
- Αναπαραγωγή Ταινείας
+ Αναπαραγωγή Ταινίας
Μετάδοση Torrent
Πηγές
Υπότιτλοι
- Ξανά φόρτωσε…
+ Προσπάθεια επανασύνδεσης…
Πίσω
Πόστερ
- Αναπαραγωγή Επισοδείου
-
- Λήξη
- Σφάλμα φόρτωσεις συνδέσμων
- Εσωτερικός χώρος
+ Αναπαραγωγή Επεισοδίου
+
+ Λήψη
+ Σφάλμα φόρτωσης συνδέσμων
+ Εσωτερικός χώρος αποθήκευσης
Dub
@@ -50,77 +50,372 @@
Λυπούμαστε, η εφαρμογή κατέρρευσε. Μια ανώνυμη αναφορά σφαλμάτων θα σταλεί στους προγραμματιστές
Απενεργοποιήστε την αυτόματη αναφορά σφαλμάτων
+ Εμφάνιση logcat 🐈
Παραπάνω πληροφορίες
- Κρύψιμο
+ Απόκρυψη
Κύριο Πόστερ
Αναπαραγωγή
Πληροφορίες
Next Random
- Change Provider
- Filter Bookmarks
- Bookmarks
- Remove
- Αναπαραγωγή Episode
+ Αλλαγή Παρόχου
+ Φιλτράρισμα Σελιδοδεικτών
+ Σελιδοδείκτες
+ Αφαίρεση
+ Αναπαραγωγή Επεισοδίου
Υποβολή
Ακύρωση
Ταχύτητα αναπαραγωγής
Ρυθμίσεις υπότιτλων
- Χρώμα κείμενου
+ Χρώμα κειμένου
Χρώμα περιγράμματος
Χρώμα φόντου
Χρώμα παραθύρου
Τύπος άκρων
Ύψωση υπότιτλων
- Επαναφορά στην προεπιλεγμένη τιμή
+ Επαναφορά στις προεπιλεγμένες τιμές
Προεπισκόπηση φόντου
Γραμματοσειρά
- Αναζήτηση με τους παρόχους
- Αναζήτηση με τύπους
- %d Benenes given to devs
- No Benenes given
+ Αναζήτηση βάσει παρόχων
+ Αναζήτηση βάσει τύπων
+ %d μπανάνες δόθηκαν στους προγραμματιστές
+ Καμία μπανάνα δεν δόθηκε
Αυτόματη επιλογή γλώσσας
Λήψη γλωσσών
- Κρατήστε πατημένο για επαναφορά στα προεπιλεγμένα
+ Κρατήστε πατημένο για επαναφορά στις προεπιλεγμένες τιμές
Συνέχεια Παρακολούθησης
Αφαίρεση
- Παραπάνω Πληροφορίες
+ Επιπλέον Πληροφορίες
- A VPN might be needed for this provider to work correctly
- This providers is a torrent, a VPN is recommended
+ Η χρήση ενός VPN ίσως χρειαστεί για την ομαλή λειτουργία του τρέχοντος παρόχου
+ Πρόκειται για torrent, η χρήση ενός VPN συνιστάται
Περιγραφή
Δεν βρέθηκε περιγραφή
Δεν βρέθηκε περιγραφή
- Picture-in-picture
- Συνεχίζει την αναπαραγωγή σε ένα μίνι παίκτη πάνω από άλλες εφαρμογές
- Αλλαγή μεγέθους παίκτη
+ Εικόνα-σε-Εικόνα
+ Συνεχίζει την αναπαραγωγή σε ένα μίνι παράθυρο πάνω από άλλες εφαρμογές
+ Αλλαγή μεγέθους παραθύρου
Αφαίρεση μαύρων περιγραμμάτων
Υπότιτλοι
- Ρυθμίσεις υποτίτλων του παίκτη
+ Ρυθμίσεις υποτίτλων του προγράμματος αναπαραγωγής
+ Chromecast Υπότιτλοι
+ Ρυθμίσεις Chromecast υποτίτλων
Eigengrau Mode
- Προσθέτει την επιλογή ταχύτητας στον παίκτη
- Σύρετε για seek
- Σύρετε αριστερά ή δεξιά για να ελέγξετε τον χρόνο στον παίκτη
+ Προσθέτει την επιλογή ταχύτητας στο πρόγραμμα αναπαραγωγής
+ Σύρετε για αναζήτηση
+ Σύρετε αριστερά ή δεξιά για να ελέγξετε τον χρόνο στην κάτω μπάρα
Σύρετε για να αλλάξετε ρυθμίσεις
Σύρετε αριστερά ή δεξιά για να αλλάξετε τη φωτεινότητα ή την ένταση
- Διπλό πάτημα για seek
- Διπλό πάτημα στα αριστερά ή δεξιά για seek μπροστά ή πίσω
+ Διπλό πάτημα για αναζήτηση
+ Διπλό πάτημα στα αριστερά ή δεξιά για αναζήτηση μπροστά ή πίσω
Αναζήτηση
- Πληροφορείες
+ Πληροφορίες
Προχωρημένη Αναζήτηση
- Δίνει τα αποτελέσματα αναζήτησης χωρισμένα ανά πάροχο
+ Δίνει τα αποτελέσματα αναζήτησης ταξινομημένα ανά πάροχο
Αποστέλλει δεδομένα μόνο για σφάλματα
Δεν στέλνει δεδομένα
Εμφάνιση ενημερώσεων
Αυτόματη αναζήτηση νέων ενημερώσεων
- Ενημέρωση σε προ-εκδόσεις
- Αναζητήστε ενημερώσεις προ-εκδόσεων αντί για κανονικές εκδόσεις
+ Ενημέρωση σε προ-εκδόσεις (beta)
+ Αναζητήστε ενημερώσεις προ-εκδόσεων (beta) αντί για σταθερές εκδόσεις
Github
- Light novel app by the same devs
- Anime app by the same devs
- Join Discord
- Δώσε benene στους devs
- Βenene δώθηκε
+ Ελαφριά novel εφαρμογή από τους ίδιους προγραμματιστές
+ Anime εφαρμογή από τους ίδιους προγραμματιστές
+ Εγγραφείτε στο Discord
+ Δώστε μπανάνα στους προγραμματιστές
+ Μπανάνα δόθηκε δώθηκε
+
+ Ταχύτητα (%.2fx)
+ Βαθμολογία: %.1f
+ Νέα ενημέρωση διαθέσιμη!\n%s -> %s
+
+ Πάτημα στη μέση για παύση
+ Χρήση φωτεινότητας συστήματος
+ Χρήση φωτεινότητας συστήματος στο ενσωματωμένο πρόγραμμα αναπαραγωγής αντί να εφαρμοστεί το προεπιλεγμένο σκούρο επικάλυμμα
+
+
+ Ενημέρωση προόδου παρακολούθησης
+ Αυτόματος συγχρονισμός της προόδου του τρέχοντος επεισοδίου
+
+ Επαναφορά δεδομένων από αντίγραφο ασφαλείας
+
+ Αντίγραφα ασφαλείας
+ Τα αντίγραφα ασφαλείας φορτώθηκαν
+ Η επαναφορά αντιγράφων ασφαλαείας απέτυχε από το αρχείο %s
+ Επιτυχής αποθήκευση δεδομένων
+ Δεν έχει δοθεί άδεια για πρόσβαση στον αποθηκευτικό χώρο, προσπαθήστε ξανά
+ Σφάλμα δημιουργίας αντιγράφων ασφαλείας %s
+
+ Λογαριασμοί
+ Ενημερώσεις και αντίγραφα ασφαλείας
+
+ Εμφάνιση filler επεισοδίου για anime
+ Εμφάνιση trailers
+ Εμφάνιση posters από kitsu
+ Απόκρυψη επιλεγμένης ποιότητας βίντεο στα αποτελέσματα αναζήτησης
+
+ App Language
+
+ Αυτός ο πάροχος δεν έχει υποστήριξη Chromecast
+ Δεν βρέθηκαν διαθέσιμοι σύνδεσμοι
+ Ο σύνδεσμος αντιγράφηκε στο πρόχειρο
+
+ Season
+ %s %d%s
+ No Season
+ Episode
+ Episodes
+ %d-%d
+ %d %s
+ S
+ E
+ No Episodes found
+
+ Διαγραφή αρχείου
+ Διαγραφή
+ Πάυση
+ Συνέχιση
+ Αυτό θα διαγράψει μόνιμα το %s\nΕπιβεβαίωση;
+ %dm\nαπομένουν
+
+ Σε εξέλιξη
+ Κατάσταση
+ Έτος
+ Διάρκεια
+ Ιστότοπος
+ Περίληψη
+
+ προστέθηκε στην ουρά
+ Δεν υπάρχουν διαθέσιμοι υπότιτλοι
+ Προεπιλεγμένοι υπότιτλοι
+
+ Ελέυθερος
+ Σε χρήση
+ Εφαρμογή
+
+ Ταινίες
+ Τηλεοπτικές Σειρές
+ Κινούμενα σχέδια
+ Torrents
+ Ντοκιμαντέρ
+ Ασιατικά Δράμα
+ Ζωντανές ροές
+ Άλλα
+
+ Ταινία
+ Σειρά
+ Cartoon
+ Ντοκιμαντέρ
+ Ασιατικό Δράμα
+ Ζωντανή ροή
+ Άλλο
+
+ Σφάλμα πηγής
+ Απομακρυσμένο σφάλμα
+ Σφάλμα απόδοσης
+ Μη αναμενόμενο σφάλμα αναπαραγωγής
+ Σφάλμα λήψης, επιβεβαιώστε ότι η άδεια αποθήκευσης είναι ενεργοποιημένη
+
+ Chromecast επεισόδο
+ Αναπαραγωγή εντός της εφαρμογής
+ Αναπαραγωγή σε %s
+ Αναπαραγωγή στον περιηγητή
+ Αντιγραφή συνδέσμου
+ Αυτόματη λήψη
+ Λήψη mirror
+ Επαναφόρτωση συνδέσμων
+ Λήψη υποτίτλων
+
+ Ποιότητα
+ Dub
+ Sub
+ Τίτλος
+ Εναλλαγή των στοιχείων UI στο poster
+
+ Κλείδωμα
+ Αλλαγή μεγέθους
+ Πηγή
+ Παράλειψη OP
+
+ Να μην εμφανιστεί ξανά
+ Παράλειψη της τρέχουσας ενημέρωσης
+ Ενημέρωση
+ Προτίμηση ποιότητας παρακολούθησης
+ Μέγιστοι χαρακτήρες για τίτλο
+ Ανάλυση αναπαραγωγής βίντεο
+
+ Μέγεθος buffer βίντεο
+ Μήκος buffer βίντεο
+ Προσωρινή μνήμη βίντεο στο δίσκο
+ Εκκαθάριση προσωρινής μνήμης βίντεο και εικόνων
+
+ Θα προκαλέσει τυχαία σφάλματα εάν οριστεί πολύ ψηλά. Μην το αλλάξετε εάν έχετε χαμηλή ποσότητα μνήμης ram, όπως σε Android TV ή παλιό τηλέφωνο
+ Μπορεί να προκαλέσει προβλήματα σε συστήματα με χαμηλό αποθηκευτικό χώρο, όπως σε συσκευές Android TV, εάν τον ρυθμίσετε πολύ ψηλά
+
+ Χρήσιμο για παράκαμψη μπλοκ ISP
+
+ Αντίγραφο ιστοτόπου
+ Αφαίρεση ιστοτόπου
+ Προσθήκη αντιγράφου ενός υπάρχοντος ιστοτόπου, με έναν διαφορετικό σύνδεσμο
+
+ Διαδρομή λήψης
+
+ Εμφάνιση Dubbed/Subbed Anime
+
+ Προσαρμογή στην οθόνη
+ Τέντωμα
+ Μεγέθυνση
+
+ Αποποίηση ευθυνών
+
+ Γενικά
+ Τυχαίο κουμπί
+ Εμφάνιση τυχαίου κουμπιού στην Αρχική οθόνη
+ Γλώσσες παρόχων
+ Διάταξη εφαρμογής
+ Προτιμώμενα μέσα
+ Ενεργοποίηση NSFW σε υποστηριζόμενους παρόχους
+ Κωδικοποίηση υποτίτλων
+ Πάροχοι
+ Διάταξη
+
+ Αυτόματο
+ Διάταξη TV
+ Διάταξη τηλεφώνου
+ Διάταξη emulator
+
+ Πρωτεύον χρώμα
+ Θέμα εφαρμογής
+ Τοποθεσία τίτλου Poster
+ Τοποθετήστε τον τίτλο κάτω από το poster
+
+ Κωδικός γλώσσας (el)
+
+ Λογαριασμός
+ Αποσύνδεση
+ Σύνδεση
+ Εναλλαγή λογαριασμού
+ Προσθήκη λογαριασμού
+ Δημιουργία λογαριασμού
+ Προσθήκη παρακολούθησης
+ Προστέθηκε %s
+ Συγχρονισμός
+ Βαθμολογήθηκε
+ Πιστοποιήθηκε %s
+ Αποτυχία πιστοποίησης σε %s
+
+ Τίποτα
+ Κανονικά
+ Όλα
+ Μέγιστο
+ Ελάχιστο
+ Περίγραμμα
+ Σε κατάθλιψη
+ Σκιά
+ Ανεβασμένοι
+ Συγχρονισμός υποτίτλων
+ 1000ms
+ Καθυστέρηση υποτίτλων
+ Χρησιμοποιήστε αυτό αν οι υπότιτλοι εμφανίζονται %dms πολύ νωρίς
+ Χρησιμοποιήστε αυτό αν οι υπότιτλοι εμφανίζονται %dms πολύ αργά
+ Καμία καθυστέρηση υποτίτλων
+
+ Συνιστώμενο
+ Φόρτωση %s
+ Φόρτωση από αρχείο
+ Φόρτωση από το Ίντερνετ
+ Λήψη αρχείου
+ Κύριο
+ Υποστηρίζεται
+ Φόντο
+
+ Πηγή
+ Τυχαίο
+
+ Έρχεται σύντομα…
+
+
+ Εικόνα Poster
+ Πρόγραμμα αναπαραγωγής
+ Ανάλυση και τίτλος
+ Τίτλος
+ Ανάλυση
+ Μη έγκυρο id
+ Μη έγκυρα δεδομένα
+ Μη έγκυρος σύνδεσμος
+ Σφάλμα
+ Αφαίρεση closed captions (για άτομα με προβλήματα ακοής) από τους υπότιτλους
+ Αφαίρεση bloat από τους υπότιτλους
+ Φιλτράρισμα ανά την προτεινόμενη γλώσσα του μέσου
+ Έξτρα
+ Τρέιλερ
+ Σύνδεσμος για stream
+ Παραπομπή
+ Επόμενο
+ Παρακολούθηση βίντεο σε αυτή την γλώσσα
+ Προηγούμενο
+ Παράλειψη διαμόρφωσης
+ Αλλαγή της εμφάνισης της συσκευής για να ταιριάζει με την συσκευή σας
+ Αναφορά κατάρρευσης
+ Τι θα θέλατε να δείτε
+ Έγινε
+ Πρόσθετα
+ Προσθήκη αποθετηρίου
+ Όνομα αποθετηρίου
+ Σύνδεσμος αποθετηρίου
+ Το πρόσθετο φορτώθηκε
+ Το πρόσθετο διαγράφηκε
+ Απέτυχε να φορτωθεί το %s
+ Ξεκίνησε η λήψη %d %s
+ Κατέβηκε το %d %s επιτυχώς
+ Όλα τα %s έχουν ήδη κατέβει
+ Μαζική λήψη
+ Πρόσθετο
+ Πρόσθετα
+ Αυτό θα διαγράψει όλα τα πρόσθετα του αποθετηρίου
+ Διαγραφή αποθετηρίου
+ Λήψη της λίστας των ιστοσελίδων που θέλετε να δείτε
+ Κατέβηκε: %d
+ Απενεργοποιήθηκε: %d
+ Δεν κατέβηκε: %d
+ Ενημερώθηκαν %d πρόσθετα
+ Προσθήκη ενός αποθετηρίου για να εγκαταστήσετε πρόσθετα ιστοσελίδας
+ Προβολή αποθετηρίων κοινότητας
+ Δημόσια λίστα
+ Κεφαλοποίηση υποτίτλων
+
+ Λήψη όλων των προσθέτων από αυτό το αποθετήριο;
+ %s (Απενεργοποιήθηκε)
+ Κομμάτια
+ Ηχητικά κομμάτια
+ Κομμάτια βίντεο
+ Εφαρμογή στην επανεκκίνηση
+
+ Η ασφαλής λειτουργία ενεργοποιήθηκε
+ Ένα μη αντιστρέψιμο σφάλμα συνέβη και απενεργοποιήσαμε όλα τα πρόσθετα, ώστε να μπορέσετε να διαπιστώσετε ποιο πρόσθετο προκάλεσε αυτή τη κατάρρευση.
+ Προβολή πληροφορίας κατάρρευσης
+
+ Βαθμολογία: %s
+ Περιγραφή
+ Έκδοση
+ Κατάσταση
+ Μέγεθος
+ Συγγραφείς
+ Υποστηρίζονται
+ Γλώσσα
+ Εγκατάσταση προσθέτου πρώτα
+
+ HLS Playlist
+
+ Προτεινόμενο πρόγραμμα αναπαραγωγής
+ Ενσωματωμένο πρόγραμμα αναπαραγωγής
+ VLC
+ MPV
+ Web Video Cast
+ Περιηγητής
+ Η εφαρμογή δεν βρέθηκε
+
+
From 98ef6a3f160d73f66095643892858a24f4ed43ce Mon Sep 17 00:00:00 2001
From: Hexated <37908684+hexated@users.noreply.github.com>
Date: Tue, 11 Oct 2022 02:43:56 +0700
Subject: [PATCH 17/22] added Vidmoly & Voe (extractor) (#147)
---
.../cloudstream3/extractors/Vidmoly.kt | 69 +++++++++++++++++++
.../lagradost/cloudstream3/extractors/Voe.kt | 32 +++++++++
.../cloudstream3/utils/ExtractorApi.kt | 30 ++++++--
3 files changed, 127 insertions(+), 4 deletions(-)
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/extractors/Vidmoly.kt
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/extractors/Voe.kt
diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Vidmoly.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Vidmoly.kt
new file mode 100644
index 00000000..615cfd74
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Vidmoly.kt
@@ -0,0 +1,69 @@
+package com.lagradost.cloudstream3.extractors
+
+import com.fasterxml.jackson.annotation.JsonProperty
+import com.lagradost.cloudstream3.SubtitleFile
+import com.lagradost.cloudstream3.app
+import com.lagradost.cloudstream3.utils.*
+import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
+
+class Vidmolyme : Vidmoly() {
+ override val mainUrl = "https://vidmoly.me"
+}
+
+open class Vidmoly : ExtractorApi() {
+ override val name = "Vidmoly"
+ override val mainUrl = "https://vidmoly.to"
+ override val requiresReferer = true
+
+ private fun String.addMarks(str: String): String {
+ return this.replace(Regex("\"?$str\"?"), "\"$str\"")
+ }
+
+ override suspend fun getUrl(
+ url: String,
+ referer: String?,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ callback: (ExtractorLink) -> Unit
+ ) {
+
+ val script = app.get(
+ url,
+ referer = referer,
+ ).document.select("script")
+ .find { it.data().contains("sources:") }?.data()
+ val videoData = script?.substringAfter("sources: [")
+ ?.substringBefore("],")?.addMarks("file")
+ val subData = script?.substringAfter("tracks: [")?.substringBefore("]")?.addMarks("file")
+ ?.addMarks("label")?.addMarks("kind")
+
+ tryParseJson(videoData)?.file?.let { m3uLink ->
+ M3u8Helper.generateM3u8(
+ name,
+ m3uLink,
+ "$mainUrl/"
+ ).forEach(callback)
+ }
+
+ tryParseJson>("[${subData}]")
+ ?.filter { it.kind == "captions" }?.map {
+ subtitleCallback.invoke(
+ SubtitleFile(
+ it.label.toString(),
+ fixUrl(it.file.toString())
+ )
+ )
+ }
+
+ }
+
+ private data class Source(
+ @JsonProperty("file") val file: String? = null,
+ )
+
+ private data class SubSource(
+ @JsonProperty("file") val file: String? = null,
+ @JsonProperty("label") val label: String? = null,
+ @JsonProperty("kind") val kind: String? = null,
+ )
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Voe.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Voe.kt
new file mode 100644
index 00000000..12a76a9b
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Voe.kt
@@ -0,0 +1,32 @@
+package com.lagradost.cloudstream3.extractors
+
+import com.lagradost.cloudstream3.SubtitleFile
+import com.lagradost.cloudstream3.app
+import com.lagradost.cloudstream3.utils.ExtractorApi
+import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.M3u8Helper
+
+open class Voe : ExtractorApi() {
+ override val name = "Voe"
+ override val mainUrl = "https://voe.sx"
+ override val requiresReferer = true
+
+ override suspend fun getUrl(
+ url: String,
+ referer: String?,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ callback: (ExtractorLink) -> Unit
+ ) {
+ val res = app.get(url, referer = referer).document
+ val link = res.select("script").find { it.data().contains("const sources") }?.data()
+ ?.substringAfter("\"hls\": \"")?.substringBefore("\",")
+
+ M3u8Helper.generateM3u8(
+ name,
+ link ?: return,
+ "$mainUrl/",
+ headers = mapOf("Origin" to "$mainUrl/")
+ ).forEach(callback)
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt
index 199f0398..ebaaa12b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt
@@ -1,10 +1,7 @@
package com.lagradost.cloudstream3.utils
import android.net.Uri
-import com.lagradost.cloudstream3.SubtitleFile
-import com.lagradost.cloudstream3.TvType
-import com.lagradost.cloudstream3.USER_AGENT
-import com.lagradost.cloudstream3.app
+import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.extractors.*
import kotlinx.coroutines.delay
@@ -323,6 +320,9 @@ val extractorApis: MutableList = arrayListOf(
Embedgram(),
Mvidoo(),
Streamplay(),
+ Vidmoly(),
+ Vidmolyme(),
+ Voe(),
Gdriveplayerapi(),
Gdriveplayerapp(),
@@ -399,6 +399,28 @@ suspend fun getPostForm(requestUrl: String, html: String): String? {
).text
}
+fun ExtractorApi.fixUrl(url: String): String {
+ if (url.startsWith("http") ||
+ // Do not fix JSON objects when passed as urls.
+ url.startsWith("{\"")
+ ) {
+ return url
+ }
+ if (url.isEmpty()) {
+ return ""
+ }
+
+ val startsWithNoHttp = url.startsWith("//")
+ if (startsWithNoHttp) {
+ return "https:$url"
+ } else {
+ if (url.startsWith('/')) {
+ return mainUrl + url
+ }
+ return "$mainUrl/$url"
+ }
+}
+
abstract class ExtractorApi {
abstract val name: String
abstract val mainUrl: String
From a43e950a488637873553d4a46f49a8bb80a86542 Mon Sep 17 00:00:00 2001
From: LagradOst <46196380+Blatzar@users.noreply.github.com>
Date: Mon, 10 Oct 2022 21:51:03 +0200
Subject: [PATCH 18/22] Remove provider language (#141)
---
.../com/lagradost/cloudstream3/MainAPI.kt | 30 ++++++++++++-------
.../ui/setup/SetupFragmentExtensions.kt | 2 +-
.../ui/setup/SetupFragmentLanguage.kt | 2 +-
.../main/res/navigation/mobile_navigation.xml | 14 +++++++++
app/src/main/res/xml/settings_providers.xml | 1 +
5 files changed, 36 insertions(+), 13 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt
index 416a7238..a06dd95e 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt
@@ -17,8 +17,7 @@ import com.lagradost.cloudstream3.ui.player.SubtitleData
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.ExtractorLink
-import com.lagradost.cloudstream3.utils.Qualities
-import com.lagradost.cloudstream3.utils.loadExtractor
+import com.lagradost.cloudstream3.utils.SubtitleHelper
import okhttp3.Interceptor
import java.text.SimpleDateFormat
import java.util.*
@@ -191,17 +190,26 @@ object APIHolder {
return list.filter { names.contains(it) }.map { DubStatus.valueOf(it) }.toHashSet()
}
+ /**
+ * Gets all the activated provider languages
+ * Used to obey the preference provider_lang_key
+ * but it turned out too complicated and unnecessary with extensions.
+ **/
fun Context.getApiProviderLangSettings(): HashSet {
- val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
- val hashSet = HashSet()
- hashSet.add("en") // def is only en
- val list = settingsManager.getStringSet(
- this.getString(R.string.provider_lang_key),
- hashSet.toMutableSet()
- )
+ val langs = apis.map { it.lang }.toSet()
+ .sortedBy { SubtitleHelper.fromTwoLettersToLanguage(it) }
+ return langs.toHashSet()
- if (list.isNullOrEmpty()) return hashSet
- return list.toHashSet()
+// val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
+// val hashSet = HashSet()
+// hashSet.add("en") // def is only en
+// val list = settingsManager.getStringSet(
+// this.getString(R.string.provider_lang_key),
+// hashSet.toMutableSet()
+// )
+//
+// if (list.isNullOrEmpty()) return hashSet
+// return list.toHashSet()
}
fun Context.getApiTypeSettings(): HashSet {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentExtensions.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentExtensions.kt
index 43037038..0f11f214 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentExtensions.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentExtensions.kt
@@ -100,7 +100,7 @@ class SetupFragmentExtensions : Fragment() {
next_btt?.setOnClickListener {
// Continue setup
if (isSetup)
- findNavController().navigate(R.id.action_navigation_setup_extensions_to_navigation_setup_provider_languages)
+ findNavController().navigate(R.id.action_navigation_setup_extensions_to_navigation_setup_media)
else
findNavController().navigate(R.id.navigation_home)
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentLanguage.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentLanguage.kt
index 71472328..f9268d77 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentLanguage.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentLanguage.kt
@@ -85,7 +85,7 @@ class SetupFragmentLanguage : Fragment() {
&& PluginManager.getPluginsLocal().isEmpty()
//&& PREBUILT_REPOSITORIES.isNotEmpty()
) R.id.action_navigation_global_to_navigation_setup_extensions
- else R.id.action_navigation_setup_language_to_navigation_setup_provider_languages
+ else R.id.action_navigation_setup_language_to_navigation_setup_media
findNavController().navigate(
nextDestination,
diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml
index 3c45ee70..94d489fd 100644
--- a/app/src/main/res/navigation/mobile_navigation.xml
+++ b/app/src/main/res/navigation/mobile_navigation.xml
@@ -522,6 +522,13 @@
app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim" />
+
+
From fc7e39e3ccd0a18ec2cf95a445d3f481a58b3473 Mon Sep 17 00:00:00 2001
From: SANCTI-afk <63229113+SANCTI-afk@users.noreply.github.com>
Date: Mon, 10 Oct 2022 21:58:28 +0200
Subject: [PATCH 19/22] arabicLanguage (#118)
* arabicLanguage100%
* Update strings.xml
* Arabic Full
* translated(preffVplayerBtn)
---
app/src/main/res/values-ar/strings.xml | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 3fb2e26c..edac2760 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -13,16 +13,16 @@
سرعة (%.2fx)
- Rated: %.1f
+ تقييم: %.1f
- !تم إيجاد تحديث جديد\n%s -> %s
+ !تم العثور علي تحديث جديد\n%s -> %s
%d دقيقة
CloudStream
تشغيل بواسطة CloudStream
الصفحة الرئيسية
البحث
- التحميلات
+ التنزيلات
الإعدادات
…بحث
@@ -210,14 +210,14 @@
لا موسم
حلقة
حلقات
- ح
- م
+ م
+ ح
لم يتم العثور على أي حلقات
حذف الملف
حذف
إيقاف مؤقت
- أكمل
+ إستئناف
-٣٠
+٣٠
سوف يتم الحذف نهائيا %s\nهل أنت متأكد?
@@ -292,7 +292,7 @@
show_title_key
التحكم في عناصر الواجهة علي الملصق
- لم يتم العثور على تحديث
+ لم يتم العثور على تحديثات
تحقق من التحديثات
قفل
@@ -404,7 +404,7 @@
إضافة تتبع
تم إضافة %s
مزامنة
- مقيّم
+ تقييم
%d / 10
/??
/%d
@@ -538,4 +538,5 @@
اللغة
قائمة HLS
+ مُشغل الفيديو المفضل
From a565319ecb60d3e1f8519619776337126ad92161 Mon Sep 17 00:00:00 2001
From: Blatzar <46196380+Blatzar@users.noreply.github.com>
Date: Mon, 10 Oct 2022 23:57:50 +0200
Subject: [PATCH 20/22] Remove links to website in app & fix plugin deletion
bug
---
.../settings/extensions/ExtensionsFragment.kt | 41 ++++++++---------
.../settings/extensions/PluginsViewModel.kt | 44 ++++++++++++++-----
.../ui/setup/SetupFragmentExtensions.kt | 24 +++++-----
app/src/main/res/layout/add_repo_input.xml | 19 ++++----
.../main/res/layout/fragment_extensions.xml | 12 ++---
.../res/layout/fragment_setup_extensions.xml | 22 +++++-----
app/src/main/res/values/strings.xml | 2 +-
7 files changed, 89 insertions(+), 75 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt
index 8480c94d..f674cafe 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt
@@ -16,7 +16,6 @@ import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
-import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.MainActivity.Companion.afterRepositoryLoadedEvent
import com.lagradost.cloudstream3.R
@@ -25,7 +24,6 @@ import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.plugins.RepositoryManager
import com.lagradost.cloudstream3.ui.result.setText
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
-import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.utils.AppUtils.downloadAllPluginsDialog
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
@@ -34,7 +32,6 @@ import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
import com.lagradost.cloudstream3.widget.LinearRecycleViewLayoutManager
import kotlinx.android.synthetic.main.add_repo_input.*
import kotlinx.android.synthetic.main.fragment_extensions.*
-import kotlinx.android.synthetic.main.fragment_extensions.list_repositories
const val PUBLIC_REPOSITORIES_LIST = "https://recloudstream.github.io/repos/"
@@ -128,20 +125,20 @@ class ExtensionsFragment : Fragment() {
}
}
- list_repositories?.setOnClickListener {
- // Open webview on tv if browser fails
- val isTv = isTvSettings()
- openBrowser(PUBLIC_REPOSITORIES_LIST, isTv, this)
-
- // Set clipboard on TV because the browser might not exist or work properly
- if (isTv) {
- val serviceClipboard =
- (activity?.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager?)
- ?: return@setOnClickListener
- val clip = ClipData.newPlainText("Repository url", PUBLIC_REPOSITORIES_LIST)
- serviceClipboard.setPrimaryClip(clip)
- }
- }
+// list_repositories?.setOnClickListener {
+// // Open webview on tv if browser fails
+// val isTv = isTvSettings()
+// openBrowser(PUBLIC_REPOSITORIES_LIST, isTv, this)
+//
+// // Set clipboard on TV because the browser might not exist or work properly
+// if (isTv) {
+// val serviceClipboard =
+// (activity?.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager?)
+// ?: return@setOnClickListener
+// val clip = ClipData.newPlainText("Repository url", PUBLIC_REPOSITORIES_LIST)
+// serviceClipboard.setPrimaryClip(clip)
+// }
+// }
observe(extensionViewModel.pluginStats) {
when (it) {
@@ -200,11 +197,11 @@ class ExtensionsFragment : Fragment() {
}
}
- dialog.list_repositories?.setOnClickListener {
- // Open webview on tv if browser fails
- openBrowser(PUBLIC_REPOSITORIES_LIST, isTvSettings(), this)
- dialog.dismissSafe()
- }
+// dialog.list_repositories?.setOnClickListener {
+// // Open webview on tv if browser fails
+// openBrowser(PUBLIC_REPOSITORIES_LIST, isTvSettings(), this)
+// dialog.dismissSafe()
+// }
// dialog.text2?.text = provider.name
dialog.apply_btt?.setOnClickListener secondListener@{
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt
index 6d94f91e..b0e253be 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt
@@ -12,7 +12,6 @@ import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.mvvm.launchSafe
-import com.lagradost.cloudstream3.plugins.PluginData
import com.lagradost.cloudstream3.plugins.PluginManager
import com.lagradost.cloudstream3.plugins.PluginManager.getPluginPath
import com.lagradost.cloudstream3.plugins.RepositoryManager
@@ -21,8 +20,8 @@ import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.Coroutines.runOnMainThread
-import kotlinx.coroutines.launch
import me.xdrop.fuzzywuzzy.FuzzySearch
+import java.io.File
typealias Plugin = Pair
/**
@@ -47,7 +46,11 @@ class PluginsViewModel : ViewModel() {
private val repositoryCache: MutableMap> = mutableMapOf()
const val TAG = "PLG"
- private fun isDownloaded(context: Context, pluginName: String, repositoryUrl: String): Boolean {
+ private fun isDownloaded(
+ context: Context,
+ pluginName: String,
+ repositoryUrl: String
+ ): Boolean {
return getPluginPath(context, pluginName, repositoryUrl).exists()
}
@@ -73,7 +76,13 @@ class PluginsViewModel : ViewModel() {
if (activity == null) return@ioSafe
val plugins = getPlugins(repositoryUrl)
- plugins.filter { plugin -> !isDownloaded(activity, plugin.second.internalName, repositoryUrl) }.also { list ->
+ plugins.filter { plugin ->
+ !isDownloaded(
+ activity,
+ plugin.second.internalName,
+ repositoryUrl
+ )
+ }.also { list ->
main {
showToast(
activity,
@@ -133,9 +142,13 @@ class PluginsViewModel : ViewModel() {
if (activity == null) return@ioSafe
val (repo, metadata) = plugin
- val file = getPluginPath(activity, plugin.second.internalName, plugin.first)
+ val file = if (isLocal) File(plugin.second.url) else getPluginPath(
+ activity,
+ plugin.second.internalName,
+ plugin.first
+ )
- val (success, message) = if (file.exists() || isLocal) {
+ val (success, message) = if (file.exists()) {
PluginManager.deletePlugin(file) to R.string.plugin_deleted
} else {
PluginManager.downloadAndLoadPlugin(
@@ -167,7 +180,9 @@ class PluginsViewModel : ViewModel() {
}
this.plugins = list
- _filteredPlugins.postValue(false to list.filterTvTypes().filterLang().sortByQuery(currentQuery))
+ _filteredPlugins.postValue(
+ false to list.filterTvTypes().filterLang().sortByQuery(currentQuery)
+ )
}
// Perhaps can be optimized?
@@ -175,7 +190,8 @@ class PluginsViewModel : ViewModel() {
if (tvTypes.isEmpty()) return this
return this.filter {
(it.plugin.second.tvTypes?.any { type -> tvTypes.contains(type) } == true) ||
- (tvTypes.contains("Others") && (it.plugin.second.tvTypes ?: emptyList()).isEmpty())
+ (tvTypes.contains("Others") && (it.plugin.second.tvTypes
+ ?: emptyList()).isEmpty())
}
}
@@ -199,7 +215,9 @@ class PluginsViewModel : ViewModel() {
}
fun updateFilteredPlugins() {
- _filteredPlugins.postValue(false to plugins.filterTvTypes().filterLang().sortByQuery(currentQuery))
+ _filteredPlugins.postValue(
+ false to plugins.filterTvTypes().filterLang().sortByQuery(currentQuery)
+ )
}
fun updatePluginList(context: Context?, repositoryUrl: String) = viewModelScope.launchSafe {
@@ -210,7 +228,9 @@ class PluginsViewModel : ViewModel() {
fun search(query: String?) {
currentQuery = query
- _filteredPlugins.postValue(true to (filteredPlugins.value?.second?.sortByQuery(query) ?: emptyList()))
+ _filteredPlugins.postValue(
+ true to (filteredPlugins.value?.second?.sortByQuery(query) ?: emptyList())
+ )
}
/**
@@ -226,6 +246,8 @@ class PluginsViewModel : ViewModel() {
}
plugins = downloadedPlugins
- _filteredPlugins.postValue(false to downloadedPlugins.filterTvTypes().filterLang().sortByQuery(currentQuery))
+ _filteredPlugins.postValue(
+ false to downloadedPlugins.filterTvTypes().filterLang().sortByQuery(currentQuery)
+ )
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentExtensions.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentExtensions.kt
index 43037038..7b7e5458 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentExtensions.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentExtensions.kt
@@ -7,21 +7,16 @@ import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
-import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser
import com.lagradost.cloudstream3.MainActivity.Companion.afterRepositoryLoadedEvent
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.plugins.RepositoryManager
import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES
-import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
-import com.lagradost.cloudstream3.ui.settings.extensions.PUBLIC_REPOSITORIES_LIST
import com.lagradost.cloudstream3.ui.settings.extensions.PluginsViewModel
import com.lagradost.cloudstream3.ui.settings.extensions.RepoAdapter
import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import kotlinx.android.synthetic.main.fragment_extensions.blank_repo_screen
-import kotlinx.android.synthetic.main.fragment_extensions.list_repositories
import kotlinx.android.synthetic.main.fragment_extensions.repo_recycler_view
-import kotlinx.android.synthetic.main.fragment_setup_extensions.*
import kotlinx.android.synthetic.main.fragment_setup_media.next_btt
import kotlinx.android.synthetic.main.fragment_setup_media.prev_btt
import kotlinx.android.synthetic.main.fragment_setup_media.setup_root
@@ -64,18 +59,19 @@ class SetupFragmentExtensions : Fragment() {
val hasRepos = repositories.isNotEmpty()
repo_recycler_view?.isVisible = hasRepos
blank_repo_screen?.isVisible = !hasRepos
- view_public_repositories_button?.isVisible = hasRepos
+// view_public_repositories_button?.isVisible = hasRepos
if (hasRepos) {
repo_recycler_view?.adapter = RepoAdapter(true, {}, {
PluginsViewModel.downloadAll(activity, it.url, null)
}).apply { updateList(repositories) }
- } else {
- list_repositories?.setOnClickListener {
- // Open webview on tv if browser fails
- openBrowser(PUBLIC_REPOSITORIES_LIST, isTvSettings(), this)
- }
}
+// else {
+// list_repositories?.setOnClickListener {
+// // Open webview on tv if browser fails
+// openBrowser(PUBLIC_REPOSITORIES_LIST, isTvSettings(), this)
+// }
+// }
}
}
@@ -84,9 +80,9 @@ class SetupFragmentExtensions : Fragment() {
context?.fixPaddingStatusbar(setup_root)
val isSetup = arguments?.getBoolean(SETUP_EXTENSION_BUNDLE_IS_SETUP) ?: false
- view_public_repositories_button?.setOnClickListener {
- openBrowser(PUBLIC_REPOSITORIES_LIST, isTvSettings(), this)
- }
+// view_public_repositories_button?.setOnClickListener {
+// openBrowser(PUBLIC_REPOSITORIES_LIST, isTvSettings(), this)
+// }
with(context) {
if (this == null) return
diff --git a/app/src/main/res/layout/add_repo_input.xml b/app/src/main/res/layout/add_repo_input.xml
index 445e71ec..6f6b4d5b 100644
--- a/app/src/main/res/layout/add_repo_input.xml
+++ b/app/src/main/res/layout/add_repo_input.xml
@@ -28,15 +28,15 @@
android:textSize="20sp"
android:textStyle="bold" />
-
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
-
+
+
+
+
+
-
+
+
+
+
+
+
Disabled: %d
Not downloaded: %d
Updated %d plugins
- Add a repository to install site extensions
+ CloudStream has no sites installed by default. You need to install the sites from repositories.\n\nBecause of a brainless DMCA takedown by Sky Uk Limited 🤮 we cannot link the repository sites in app.\n\nJoin our discord for links or search online.
View community repositories
Public list
Uppercase all subtitles
From 661f8c3c4e8b6ee72f40477d73bd8677db15174e Mon Sep 17 00:00:00 2001
From: reduplicated <110570621+reduplicated@users.noreply.github.com>
Date: Tue, 11 Oct 2022 15:24:16 +0200
Subject: [PATCH 21/22] mini api fix
---
.../com/lagradost/cloudstream3/MainAPI.kt | 39 +++++++++++++++----
.../cloudstream3/ui/APIRepository.kt | 14 +++++--
2 files changed, 42 insertions(+), 11 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt
index a06dd95e..e5896434 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt
@@ -22,7 +22,6 @@ import okhttp3.Interceptor
import java.text.SimpleDateFormat
import java.util.*
import kotlin.math.absoluteValue
-import kotlin.collections.MutableList
const val USER_AGENT =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
@@ -332,13 +331,24 @@ data class SettingsJson(
data class MainPageData(
val name: String,
val data: String,
+ val horizontalImages: Boolean = false
)
data class MainPageRequest(
val name: String,
val data: String,
+ val horizontalImages: Boolean,
+ //TODO genre selection or smth
)
+fun mainPage(url: String, name: String, horizontalImages: Boolean = false): MainPageData {
+ return MainPageData(name = name, data = url, horizontalImages = horizontalImages)
+}
+
+fun mainPageOf(vararg elements: MainPageData): List {
+ return elements.toList()
+}
+
/** return list of MainPageData with url to name, make for more readable code */
fun mainPageOf(vararg elements: Pair): List {
return elements.map { (url, name) -> MainPageData(name = name, data = url) }
@@ -347,7 +357,7 @@ fun mainPageOf(vararg elements: Pair): List {
fun newHomePageResponse(
name: String,
list: List,
- hasNext: Boolean? = null
+ hasNext: Boolean? = null,
): HomePageResponse {
return HomePageResponse(
listOf(HomePageList(name, list)),
@@ -355,6 +365,17 @@ fun newHomePageResponse(
)
}
+fun newHomePageResponse(
+ data: MainPageRequest,
+ list: List,
+ hasNext: Boolean? = null,
+): HomePageResponse {
+ return HomePageResponse(
+ listOf(HomePageList(data.name, list, data.horizontalImages)),
+ hasNext = hasNext ?: list.isNotEmpty()
+ )
+}
+
fun newHomePageResponse(list: HomePageList, hasNext: Boolean? = null): HomePageResponse {
return HomePageResponse(listOf(list), hasNext = hasNext ?: list.list.isNotEmpty())
}
@@ -391,14 +412,17 @@ abstract class MainAPI {
/** if this is turned on then it will request the homepage one after the other,
used to delay if they block many request at the same time*/
- open var sequentialMainPage : Boolean = false
+ open var sequentialMainPage: Boolean = false
+
/** in milliseconds, this can be used to add more delay between homepage requests
* on first load if sequentialMainPage is turned on */
- open var sequentialMainPageDelay : Long = 0L
+ open var sequentialMainPageDelay: Long = 0L
+
/** in milliseconds, this can be used to add more delay between homepage requests when scrolling */
- open var sequentialMainPageScrollDelay : Long = 0L
+ open var sequentialMainPageScrollDelay: Long = 0L
+
/** used to keep track when last homepage request was in unixtime ms */
- var lastHomepageRequest : Long = 0L
+ var lastHomepageRequest: Long = 0L
open var lang = "en" // ISO_639_1 check SubtitleHelper
@@ -431,7 +455,8 @@ abstract class MainAPI {
open val vpnStatus = VPNStatus.None
open val providerType = ProviderType.DirectProvider
- open val mainPage = listOf(MainPageData("", ""))
+ //emptyList() //
+ open val mainPage = listOf(MainPageData("", "", false))
@WorkerThread
open suspend fun getMainPage(
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt
index b43b1434..0e5e544b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt
@@ -66,7 +66,7 @@ class APIRepository(val api: MainAPI) {
suspend fun waitForHomeDelay() {
val delta = api.sequentialMainPageScrollDelay + api.lastHomepageRequest - unixTimeMS
- if(delta < 0) return
+ if (delta < 0) return
delay(delta)
}
@@ -75,7 +75,7 @@ class APIRepository(val api: MainAPI) {
api.lastHomepageRequest = unixTimeMS
nameIndex?.let { api.mainPage.getOrNull(it) }?.let { data ->
- listOf(api.getMainPage(page, MainPageRequest(data.name, data.data)))
+ listOf(api.getMainPage(page, MainPageRequest(data.name, data.data, data.horizontalImages)))
} ?: run {
if (api.sequentialMainPage) {
var first = true
@@ -84,11 +84,17 @@ class APIRepository(val api: MainAPI) {
delay(api.sequentialMainPageDelay)
first = false
- api.getMainPage(page, MainPageRequest(data.name, data.data))
+ api.getMainPage(
+ page,
+ MainPageRequest(data.name, data.data, data.horizontalImages)
+ )
}
} else {
api.mainPage.apmap { data ->
- api.getMainPage(page, MainPageRequest(data.name, data.data))
+ api.getMainPage(
+ page,
+ MainPageRequest(data.name, data.data, data.horizontalImages)
+ )
}
}
}
From 1228701f0eddb40d0b4b1957068d26b736e8c344 Mon Sep 17 00:00:00 2001
From: Blatzar <46196380+Blatzar@users.noreply.github.com>
Date: Thu, 13 Oct 2022 22:58:18 +0200
Subject: [PATCH 22/22] Fix focusing add repo on firestick
---
.../ui/settings/extensions/ExtensionsFragment.kt | 5 +++++
app/src/main/res/layout/fragment_extensions.xml | 2 ++
2 files changed, 7 insertions(+)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt
index f674cafe..418519e2 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt
@@ -232,6 +232,11 @@ class ExtensionsFragment : Fragment() {
val isTv = isTrueTvSettings()
add_repo_button?.isGone = isTv
add_repo_button_imageview_holder?.isVisible = isTv
+
+ // Band-aid for Fire TV
+ plugin_storage_appbar?.isFocusableInTouchMode = isTv
+ add_repo_button_imageview?.isFocusableInTouchMode = isTv
+
add_repo_button?.setOnClickListener(addRepositoryClick)
add_repo_button_imageview?.setOnClickListener(addRepositoryClick)
diff --git a/app/src/main/res/layout/fragment_extensions.xml b/app/src/main/res/layout/fragment_extensions.xml
index 7d262bf3..c786e2bc 100644
--- a/app/src/main/res/layout/fragment_extensions.xml
+++ b/app/src/main/res/layout/fragment_extensions.xml
@@ -56,6 +56,7 @@