package ani.dantotsu.download.video import android.annotation.SuppressLint import android.content.Context import android.net.Uri import androidx.media3.common.C import androidx.media3.common.MediaItem import androidx.media3.common.MimeTypes import androidx.media3.common.TrackSelectionParameters import androidx.media3.common.util.UnstableApi import androidx.media3.database.StandaloneDatabaseProvider import androidx.media3.datasource.DataSource import androidx.media3.datasource.HttpDataSource import androidx.media3.datasource.cache.NoOpCacheEvictor import androidx.media3.datasource.cache.SimpleCache import androidx.media3.datasource.okhttp.OkHttpDataSource import androidx.media3.exoplayer.DefaultRenderersFactory import androidx.media3.exoplayer.offline.DownloadHelper import androidx.media3.exoplayer.offline.DownloadManager import androidx.media3.exoplayer.offline.DownloadService import androidx.media3.exoplayer.scheduler.Requirements import androidx.media3.ui.TrackSelectionDialogBuilder import ani.dantotsu.R import ani.dantotsu.defaultHeaders import ani.dantotsu.logError import ani.dantotsu.okHttpClient import ani.dantotsu.parsers.Subtitle import ani.dantotsu.parsers.SubtitleType import ani.dantotsu.parsers.Video import ani.dantotsu.parsers.VideoType import java.io.File import java.io.IOException import java.util.concurrent.* object Helper { @SuppressLint("UnsafeOptInUsageError") fun downloadVideo(context : Context, video: Video, subtitle: Subtitle?){ val dataSourceFactory = DataSource.Factory { val dataSource: HttpDataSource = OkHttpDataSource.Factory(okHttpClient).createDataSource() defaultHeaders.forEach { dataSource.setRequestProperty(it.key, it.value) } video.file.headers.forEach { dataSource.setRequestProperty(it.key, it.value) } dataSource } val mimeType = when (video.format) { VideoType.M3U8 -> MimeTypes.APPLICATION_M3U8 VideoType.DASH -> MimeTypes.APPLICATION_MPD else -> MimeTypes.APPLICATION_MP4 } val builder = MediaItem.Builder().setUri(video.file.url).setMimeType(mimeType) var sub: MediaItem.SubtitleConfiguration? = null if (subtitle != null) { sub = MediaItem.SubtitleConfiguration .Builder(Uri.parse(subtitle.file.url)) .setSelectionFlags(C.SELECTION_FLAG_FORCED) .setMimeType( when (subtitle.type) { SubtitleType.VTT -> MimeTypes.TEXT_VTT SubtitleType.ASS -> MimeTypes.TEXT_SSA SubtitleType.SRT -> MimeTypes.APPLICATION_SUBRIP SubtitleType.UNKNOWN -> MimeTypes.TEXT_SSA } ) .build() } if (sub != null) builder.setSubtitleConfigurations(mutableListOf(sub)) val mediaItem = builder.build() val downloadHelper = DownloadHelper.forMediaItem( context, mediaItem, DefaultRenderersFactory(context), dataSourceFactory ) downloadHelper.prepare(object : DownloadHelper.Callback{ override fun onPrepared(helper: DownloadHelper) { TrackSelectionDialogBuilder(context,"Select thingy",helper.getTracks(0).groups ) { _, overrides -> val params = TrackSelectionParameters.Builder(context) overrides.forEach{ params.addOverride(it.value) } helper.addTrackSelection(0, params.build()) MyDownloadService DownloadService.sendAddDownload( context, MyDownloadService::class.java, helper.getDownloadRequest(null), false ) }.apply { setTheme(R.style.DialogTheme) setTrackNameProvider { if (it.frameRate > 0f) it.height.toString() + "p" else it.height.toString() + "p (fps : N/A)" } build().show() } } override fun onPrepareError(helper: DownloadHelper, e: IOException) { logError(e) } }) } private var download: DownloadManager? = null private const val DOWNLOAD_CONTENT_DIRECTORY = "downloads" @Synchronized @UnstableApi fun downloadManager(context: Context): DownloadManager { return download ?: let { val database = StandaloneDatabaseProvider(context) val downloadDirectory = File(getDownloadDirectory(context), DOWNLOAD_CONTENT_DIRECTORY) val dataSourceFactory = DataSource.Factory { val dataSource: HttpDataSource = OkHttpDataSource.Factory(okHttpClient).createDataSource() defaultHeaders.forEach { dataSource.setRequestProperty(it.key, it.value) } dataSource } DownloadManager( context, database, SimpleCache(downloadDirectory, NoOpCacheEvictor(), database), dataSourceFactory, Executor(Runnable::run) ).apply { requirements = Requirements(Requirements.NETWORK or Requirements.DEVICE_STORAGE_NOT_LOW) maxParallelDownloads = 3 } } } private var downloadDirectory: File? = null @Synchronized private fun getDownloadDirectory(context: Context): File { if (downloadDirectory == null) { downloadDirectory = context.getExternalFilesDir(null) if (downloadDirectory == null) { downloadDirectory = context.filesDir } } return downloadDirectory!! } }