From 10c945f4971ada31bf7198be36a8b5c899c9c2d5 Mon Sep 17 00:00:00 2001 From: LagradOst <11805592+LagradOst@users.noreply.github.com> Date: Sun, 3 Apr 2022 22:14:51 +0200 Subject: [PATCH] added download subtitles option and fixed subtitle download --- .../ui/player/DownloadFileGenerator.kt | 3 + .../cloudstream3/ui/result/EpisodeAdapter.kt | 3 + .../cloudstream3/ui/result/ResultFragment.kt | 325 +++++++++++------- .../utils/VideoDownloadManager.kt | 45 ++- app/src/main/res/values/array.xml | 3 + app/src/main/res/values/strings.xml | 17 +- 6 files changed, 257 insertions(+), 139 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt index cedb711e..1f7bb103 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt @@ -71,6 +71,9 @@ class DownloadFileGenerator( .removeSuffix(".vtt") .removeSuffix(".srt") .removeSuffix(".txt") + .trim() + .removePrefix("(") + .removeSuffix(")") subtitleCallback( SubtitleData( 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 122a27c4..f3f5ba9b 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 @@ -48,6 +48,9 @@ const val ACTION_SHOW_OPTIONS = 10 const val ACTION_CLICK_DEFAULT = 11 const val ACTION_SHOW_TOAST = 12 +const val ACTION_DOWNLOAD_EPISODE_SUBTITLE = 13 +const val ACTION_DOWNLOAD_EPISODE_SUBTITLE_MIRROR = 14 + data class EpisodeClickEvent(val action: Int, val data: ResultEpisode) class EpisodeAdapter( 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 80e69125..4b09f188 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 @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.app.Activity import android.content.ClipData import android.content.ClipboardManager +import android.content.Context import android.content.Context.CLIPBOARD_SERVICE import android.content.Intent import android.content.Intent.* @@ -43,7 +44,6 @@ import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings import com.lagradost.cloudstream3.APIHolder.getApiFromName import com.lagradost.cloudstream3.APIHolder.getId -import com.lagradost.cloudstream3.AcraApplication.Companion.context import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.CommonActivity.getCastSession import com.lagradost.cloudstream3.CommonActivity.showToast @@ -70,6 +70,7 @@ import com.lagradost.cloudstream3.utils.AppUtils.isConnectedToChromecast import com.lagradost.cloudstream3.utils.AppUtils.loadCache import com.lagradost.cloudstream3.utils.AppUtils.openBrowser import com.lagradost.cloudstream3.utils.CastHelper.startCast +import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.DataStore.getFolderName import com.lagradost.cloudstream3.utils.DataStore.setKey @@ -88,6 +89,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes import com.lagradost.cloudstream3.utils.UIHelper.requestRW import com.lagradost.cloudstream3.utils.UIHelper.setImage import com.lagradost.cloudstream3.utils.UIHelper.setImageBlur +import com.lagradost.cloudstream3.utils.VideoDownloadManager.getFileName import com.lagradost.cloudstream3.utils.VideoDownloadManager.sanitizeFilename import kotlinx.android.synthetic.main.fragment_result.* import kotlinx.android.synthetic.main.fragment_result_swipe.* @@ -205,7 +207,80 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio private var updateUIListener: (() -> Unit)? = null - suspend fun startDownload( + private fun downloadSubtitle( + context: Context?, + link: SubtitleData, + meta: VideoDownloadManager.DownloadEpisodeMetadata, + ) { + context?.let { ctx -> + val fileName = getFileName(ctx, meta) + val folder = getFolder(meta.type ?: return, meta.mainName) + downloadSubtitle( + ctx, + ExtractorSubtitleLink(link.name, link.url, ""), + fileName, + folder + ) + } + } + + private fun downloadSubtitle( + context: Context?, + link: ExtractorSubtitleLink, + fileName: String, + folder: String + ) { + ioSafe { + VideoDownloadManager.downloadThing( + context ?: return@ioSafe, + link, + "$fileName ${link.name}", + folder, + if (link.url.contains(".srt")) ".srt" else "vtt", + false, + null + ) { + // no notification + } + } + } + + private fun getMeta( + episode: ResultEpisode, + titleName: String, + apiName: String, + currentPoster: String, + currentIsMovie: Boolean, + tvType: TvType, + ): VideoDownloadManager.DownloadEpisodeMetadata { + return VideoDownloadManager.DownloadEpisodeMetadata( + episode.id, + sanitizeFilename(titleName), + apiName, + episode.poster ?: currentPoster, + episode.name, + if (currentIsMovie) null else episode.season, + if (currentIsMovie) null else episode.episode, + tvType, + ) + } + + private fun getFolder(currentType: TvType, titleName: String): String { + return when (currentType) { + TvType.Anime -> "Anime/$titleName" + TvType.Movie -> "Movies" + TvType.AnimeMovie -> "Movies" + TvType.TvSeries -> "TVSeries/$titleName" + TvType.OVA -> "OVA" + TvType.Cartoon -> "Cartoons/$titleName" + TvType.Torrent -> "Torrent" + TvType.Documentary -> "Documentaries" + TvType.AsianDrama -> "AsianDrama" + } + } + + fun startDownload( + context: Context?, episode: ResultEpisode, currentIsMovie: Boolean, currentHeaderName: String, @@ -217,110 +292,87 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio links: List, subs: List? ) { - val titleName = sanitizeFilename(currentHeaderName) + try { + if (context == null) return - val meta = VideoDownloadManager.DownloadEpisodeMetadata( - episode.id, - titleName, - apiName, - episode.poster ?: currentPoster, - episode.name, - if (currentIsMovie) null else episode.season, - if (currentIsMovie) null else episode.episode - ) - - val folder = when (currentType) { - TvType.Anime -> "Anime/$titleName" - TvType.Movie -> "Movies" - TvType.AnimeMovie -> "Movies" - TvType.TvSeries -> "TVSeries/$titleName" - TvType.OVA -> "OVA" - TvType.Cartoon -> "Cartoons/$titleName" - TvType.Torrent -> "Torrent" - TvType.Documentary -> "Documentaries" - TvType.AsianDrama -> "AsianDrama" - } - - val src = "$DOWNLOAD_NAVIGATE_TO/$parentId" // url ?: return@let - - // SET VISUAL KEYS - setKey( - DOWNLOAD_HEADER_CACHE, - parentId.toString(), - VideoDownloadHelper.DownloadHeaderCached( - apiName, - url, - currentType, - currentHeaderName, - currentPoster, - parentId, - System.currentTimeMillis(), - ) - ) - - setKey( - getFolderName( - DOWNLOAD_EPISODE_CACHE, - parentId.toString() - ), // 3 deep folder for faster acess - episode.id.toString(), - VideoDownloadHelper.DownloadEpisodeCached( - episode.name, - episode.poster, - episode.episode, - episode.season, - episode.id, - parentId, - episode.rating, - episode.description, - System.currentTimeMillis(), - ) - ) - - // DOWNLOAD VIDEO - VideoDownloadManager.downloadEpisodeUsingWorker( - context ?: return, - src,//url ?: return, - folder, - meta, - links - ) - // 1. Checks if the lang should be downloaded - // 2. Makes it into the download format - // 3. Downloads it as a .vtt file - val downloadList = getDownloadSubsLanguageISO639_1() - subs?.let { subsList -> - subsList.filter { - downloadList.contains( - SubtitleHelper.fromLanguageToTwoLetters( - it.name, - true - ) + val meta = + getMeta( + episode, + currentHeaderName, + apiName, + currentPoster, + currentIsMovie, + currentType ) - } - .map { ExtractorSubtitleLink(it.name, it.url, "") } - .forEach { link -> - val epName = meta.name - ?: "${context?.getString(R.string.episode)} ${meta.episode}" - val fileName = - sanitizeFilename(epName + if (downloadList.size > 1) " ${link.name}" else "") - withContext(Dispatchers.IO) { - normalSafeApiCall { - VideoDownloadManager.downloadThing( - context ?: return@normalSafeApiCall, - link, - fileName, - folder, - "vtt", - false, - null - ) { - // no notification - } - } - } + val folder = getFolder(currentType, currentHeaderName) + + val src = "$DOWNLOAD_NAVIGATE_TO/$parentId" // url ?: return@let + + // SET VISUAL KEYS + setKey( + DOWNLOAD_HEADER_CACHE, + parentId.toString(), + VideoDownloadHelper.DownloadHeaderCached( + apiName, + url, + currentType, + currentHeaderName, + currentPoster, + parentId, + System.currentTimeMillis(), + ) + ) + + setKey( + getFolderName( + DOWNLOAD_EPISODE_CACHE, + parentId.toString() + ), // 3 deep folder for faster acess + episode.id.toString(), + VideoDownloadHelper.DownloadEpisodeCached( + episode.name, + episode.poster, + episode.episode, + episode.season, + episode.id, + parentId, + episode.rating, + episode.description, + System.currentTimeMillis(), + ) + ) + + // DOWNLOAD VIDEO + VideoDownloadManager.downloadEpisodeUsingWorker( + context, + src,//url ?: return, + folder, + meta, + links + ) + + // 1. Checks if the lang should be downloaded + // 2. Makes it into the download format + // 3. Downloads it as a .vtt file + val downloadList = getDownloadSubsLanguageISO639_1() + subs?.let { subsList -> + subsList.filter { + downloadList.contains( + SubtitleHelper.fromLanguageToTwoLetters( + it.name, + true + ) + ) } + .map { ExtractorSubtitleLink(it.name, it.url, "") } + .forEach { link -> + val fileName = getFileName(context, meta) + downloadSubtitle(context, link, fileName, folder) + } + } + } catch (e: Exception) { + logError(e) } } @@ -353,6 +405,7 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio } startDownload( + activity, episode, currentIsMovie, currentHeaderName, @@ -737,6 +790,21 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio builder.create().show() } + fun acquireSingleSubtitleLink( + links: List, + title: String, + callback: (SubtitleData) -> Unit + ) { + val builder = AlertDialog.Builder(requireContext(), R.style.AlertDialogCustom) + + builder.setTitle(title) + builder.setItems(links.map { it.name }.toTypedArray()) { dia, which -> + callback.invoke(links[which]) + dia?.dismiss() + } + builder.create().show() + } + fun acquireSingeExtractorLink(title: String, callback: (ExtractorLink) -> Unit) { acquireSingleExtractorLink(sortUrls(currentLinks ?: return), title, callback) } @@ -839,6 +907,29 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio } } + ACTION_DOWNLOAD_EPISODE_SUBTITLE -> { + acquireSingleSubtitleLink( + sortSubs( + currentSubs ?: return@main + ),//(currentLinks ?: return@main).filter { !it.isM3u8 }, + getString(R.string.episode_action_download_subtitle) + ) { link -> + downloadSubtitle( + context, + link, + getMeta( + episodeClick.data, + currentHeaderName ?: return@acquireSingleSubtitleLink, + apiName, + currentPoster ?: return@acquireSingleSubtitleLink, + currentIsMovie ?: return@acquireSingleSubtitleLink, + currentType ?: return@acquireSingleSubtitleLink + ) + ) + showToast(activity, R.string.download_started, Toast.LENGTH_SHORT) + } + } + ACTION_SHOW_OPTIONS -> { context?.let { ctx -> val builder = AlertDialog.Builder(ctx, R.style.AlertDialogCustom) @@ -862,6 +953,7 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio val add = when (opv) { ACTION_CHROME_CAST_EPISODE -> isConnected ACTION_CHROME_CAST_MIRROR -> isConnected + ACTION_DOWNLOAD_EPISODE_SUBTITLE -> !currentSubs.isNullOrEmpty() ACTION_DOWNLOAD_EPISODE -> hasDownloadSupport ACTION_DOWNLOAD_MIRROR -> hasDownloadSupport ACTION_PLAY_EPISODE_IN_VLC_PLAYER -> context?.isAppInstalled( @@ -1015,21 +1107,20 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio ),//(currentLinks ?: return@main).filter { !it.isM3u8 }, getString(R.string.episode_action_download_mirror) ) { link -> - main { - startDownload( - episodeClick.data, - currentIsMovie ?: return@main, - currentHeaderName ?: return@main, - currentType ?: return@main, - currentPoster ?: return@main, - apiName, - currentId ?: return@main, - url ?: return@main, - listOf(link), - sortSubs(currentSubs ?: return@main) - ) - showToast(activity, R.string.download_started, Toast.LENGTH_SHORT) - } + startDownload( + context, + episodeClick.data, + currentIsMovie ?: return@acquireSingleExtractorLink, + currentHeaderName ?: return@acquireSingleExtractorLink, + currentType ?: return@acquireSingleExtractorLink, + currentPoster ?: return@acquireSingleExtractorLink, + apiName, + currentId ?: return@acquireSingleExtractorLink, + url ?: return@acquireSingleExtractorLink, + listOf(link), + sortSubs(currentSubs ?: return@acquireSingleExtractorLink), + ) + showToast(activity, R.string.download_started, Toast.LENGTH_SHORT) } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt index 92ff7201..b1d0c18b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt @@ -27,6 +27,7 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.MainActivity import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.services.VideoDownloadService @@ -116,7 +117,8 @@ object VideoDownloadManager { @JsonProperty("poster") val poster: String?, @JsonProperty("name") val name: String?, @JsonProperty("season") val season: Int?, - @JsonProperty("episode") val episode: Int? + @JsonProperty("episode") val episode: Int?, + @JsonProperty("type") val type: TvType?, ) data class DownloadItem( @@ -1335,6 +1337,33 @@ object VideoDownloadManager { return SUCCESS_DOWNLOAD_DONE } + fun getFileName(context: Context, metadata: DownloadEpisodeMetadata): String { + return getFileName(context, metadata.name, metadata.episode, metadata.season) + } + + private fun getFileName(context: Context, epName: String?, episode: Int?, season: Int?): String { + // kinda ugly ik + return sanitizeFilename( + if (epName == null) { + if (season != null) { + "${context.getString(R.string.season)} $season ${context.getString(R.string.episode)} $episode" + } else { + "${context.getString(R.string.episode)} $episode" + } + } else { + if (episode != null) { + if (season != null) { + "${context.getString(R.string.season)} $season ${context.getString(R.string.episode)} $episode - $epName" + } else { + "${context.getString(R.string.episode)} $episode - $epName" + } + } else { + epName + } + } + ) + } + private fun downloadSingleEpisode( context: Context, source: String?, @@ -1344,19 +1373,7 @@ object VideoDownloadManager { notificationCallback: (Int, Notification) -> Unit, tryResume: Boolean = false, ): Int { - val name = - // kinda ugly ik - sanitizeFilename( - if (ep.name == null) { - "${context.getString(R.string.episode)} ${ep.episode}" - } else { - if (ep.episode != null) { - "${context.getString(R.string.episode)} ${ep.episode} - ${ep.name}" - } else { - ep.name - } - } - ) + val name = getFileName(context, ep) // Make sure this is cancelled when download is done or cancelled. val extractorJob = ioSafe { diff --git a/app/src/main/res/values/array.xml b/app/src/main/res/values/array.xml index e08c7280..f2d1101d 100644 --- a/app/src/main/res/values/array.xml +++ b/app/src/main/res/values/array.xml @@ -150,8 +150,10 @@ @string/episode_action_copy_link @string/episode_action_auto_download @string/episode_action_download_mirror + @string/episode_action_download_subtitle @string/episode_action_reload_links + 4 5 @@ -161,6 +163,7 @@ 9 6 7 + 13 8 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2247e39e..c3b1319f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -304,15 +304,16 @@ Unexpected player error Download error, check storage permissions - Chromecast Episode - Chromecast Mirror - Play In App - Play In VLC + Chromecast episode + Chromecast mirror + Play in app + Play in VLC Play in browser - Copy Link - Auto Download - Download Mirror - Reload Links + Copy link + Auto download + Download mirror + Reload links + Download subtitles No Update Found Check for Update