From b83843d3ae457326fba3d110f74c30dfa033e85e Mon Sep 17 00:00:00 2001 From: Hexated <37908684+hexated@users.noreply.github.com> Date: Sat, 1 Oct 2022 18:20:41 +0700 Subject: [PATCH 01/15] small fix IndexSubtitle (#126) --- .../cloudstream3/syncproviders/providers/IndexSubtitleApi.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/IndexSubtitleApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/IndexSubtitleApi.kt index be4f695f..2fc97477 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/IndexSubtitleApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/IndexSubtitleApi.kt @@ -244,7 +244,7 @@ class IndexSubtitleApi : AbstractSubApi { } else { document.select("div.my-3.p-3 div.media").mapNotNull { block -> val name = - block.selectFirst("strong.d-block.text-primary")?.text()?.trim().toString() + block.selectFirst("strong.d-block")?.text()?.trim().toString() if (seasonNum!! > 0) { if (isRightEps(name, seasonNum, epNum)) { fixUrl(block.selectFirst("a")!!.attr("href")) From 03eb17149f3c9bd03b630dd25fe13d8cfcf94ef5 Mon Sep 17 00:00:00 2001 From: Jace <54625750+Jacekun@users.noreply.github.com> Date: Sat, 1 Oct 2022 21:06:39 +0800 Subject: [PATCH 02/15] [Extractor] AStreamHub (#129) --- .../cloudstream3/extractors/AStreamHub.kt | 39 +++++++++++++++++++ .../cloudstream3/utils/ExtractorApi.kt | 1 + 2 files changed, 40 insertions(+) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/extractors/AStreamHub.kt diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/AStreamHub.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/AStreamHub.kt new file mode 100644 index 00000000..18602664 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/AStreamHub.kt @@ -0,0 +1,39 @@ +package com.lagradost.cloudstream3.extractors + +import android.util.Log +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.base64Decode +import com.lagradost.cloudstream3.utils.* + +class AStreamHub : ExtractorApi() { + override val name = "AStreamHub" + override val mainUrl = "https://astreamhub.com" + override val requiresReferer = true + + override suspend fun getUrl(url: String, referer: String?): List { + val sources = mutableListOf() + app.get(url).document.selectFirst("body > script").let { script -> + val text = script?.html() ?: "" + Log.i("Dev", "text => $text") + if (text.isNotBlank()) { + val m3link = "(?<=file:)(.*)(?=,)".toRegex().find(text) + ?.groupValues?.get(0)?.trim()?.trim('"') ?: "" + Log.i("Dev", "m3link => $m3link") + if (m3link.isNotBlank()) { + sources.add( + ExtractorLink( + name = name, + source = name, + url = m3link, + isM3u8 = true, + quality = Qualities.Unknown.value, + referer = referer ?: url + ) + ) + } + } + } + return sources + } + +} \ 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 b5c2cd44..199f0398 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -344,6 +344,7 @@ val extractorApis: MutableList = arrayListOf( VidSrcExtractor(), VidSrcExtractor2(), PlayLtXyz(), + AStreamHub(), ) 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 03/15] 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 04/15] 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 05/15] 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 06/15] 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 07/15] 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 08/15] 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 09/15] 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 10/15] 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 11/15] :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 12/15] 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 13/15] [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 14/15] 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 15/15] 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 +}