mirror of
				https://github.com/recloudstream/cloudstream.git
				synced 2024-08-15 01:53:11 +00:00 
			
		
		
		
	download not working
This commit is contained in:
		
							parent
							
								
									2c312cd9b4
								
							
						
					
					
						commit
						672dc8c8d1
					
				
					 15 changed files with 559 additions and 6 deletions
				
			
		|  | @ -4,6 +4,7 @@ | |||
| 
 | ||||
|     <uses-permission android:name="android.permission.INTERNET"/> | ||||
|     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> | ||||
|     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> | ||||
|     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> | ||||
|     <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> | ||||
|     <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/> | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| package com.lagradost.cloudstream3 | ||||
| 
 | ||||
| import android.app.PictureInPictureParams | ||||
| import android.content.Context | ||||
| import android.content.pm.PackageManager | ||||
| import android.os.Build | ||||
| import android.os.Bundle | ||||
|  | @ -36,6 +37,7 @@ class MainActivity : AppCompatActivity() { | |||
|         var isInPlayer: Boolean = false | ||||
|         var canShowPipMode: Boolean = false | ||||
|         var isInPIPMode: Boolean = false | ||||
|         lateinit var mainContext : MainActivity | ||||
|     } | ||||
| 
 | ||||
|     private fun enterPIPMode() { | ||||
|  | @ -83,6 +85,8 @@ class MainActivity : AppCompatActivity() { | |||
| 
 | ||||
|     override fun onCreate(savedInstanceState: Bundle?) { | ||||
|         super.onCreate(savedInstanceState) | ||||
|         mainContext = this | ||||
| 
 | ||||
|         setContentView(R.layout.activity_main) | ||||
|         val navView: BottomNavigationView = findViewById(R.id.nav_view) | ||||
| 
 | ||||
|  |  | |||
|  | @ -22,9 +22,10 @@ import kotlinx.android.synthetic.main.result_episode.view.episode_holder | |||
| import kotlinx.android.synthetic.main.result_episode.view.episode_text | ||||
| import kotlinx.android.synthetic.main.result_episode_large.view.* | ||||
| 
 | ||||
| const val ACTION_PLAY_EPISODE_IN_PLAYER = 1 | ||||
| const val ACTION_RELOAD_EPISODE = 4 | ||||
| const val ACTION_CHROME_CAST_EPISODE = 2 | ||||
| const val ACTION_DOWNLOAD_EPISODE = 3 | ||||
| const val ACTION_PLAY_EPISODE_IN_PLAYER = 1 | ||||
| 
 | ||||
| data class EpisodeClickEvent(val action: Int, val data: ResultEpisode) | ||||
| 
 | ||||
|  | @ -145,10 +146,12 @@ class EpisodeAdapter( | |||
|                     if (castContext.castState == CastState.CONNECTED) { | ||||
|                         clickCallback.invoke(EpisodeClickEvent(ACTION_CHROME_CAST_EPISODE, card)) | ||||
|                     } else { | ||||
|                         clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card)) | ||||
|                         // clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card)) | ||||
|                         clickCallback.invoke(EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, card)) | ||||
|                     } | ||||
|                 } else { | ||||
|                     clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card)) | ||||
|                     // clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card)) | ||||
|                     clickCallback.invoke(EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, card)) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  |  | |||
|  | @ -45,7 +45,9 @@ import com.lagradost.cloudstream3.ui.player.PlayerData | |||
| import com.lagradost.cloudstream3.ui.player.PlayerFragment | ||||
| import com.lagradost.cloudstream3.utils.CastHelper.startCast | ||||
| import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos | ||||
| 
 | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.VideoDownloadManager | ||||
| import jp.wasabeef.glide.transformations.BlurTransformation | ||||
| import kotlinx.android.synthetic.main.fragment_result.* | ||||
| 
 | ||||
|  | @ -258,7 +260,6 @@ class ResultFragment : Fragment() { | |||
|             currentLoadingCount++ | ||||
|             when (episodeClick.action) { | ||||
|                 ACTION_CHROME_CAST_EPISODE -> { | ||||
| 
 | ||||
|                     val skipLoading = if (apiName != null) { | ||||
|                         getApiFromName(apiName).instantLinkLoading | ||||
|                     } else false | ||||
|  | @ -327,7 +328,19 @@ class ResultFragment : Fragment() { | |||
|                         } | ||||
|                     }*/ | ||||
|                 } | ||||
| 
 | ||||
|                 ACTION_DOWNLOAD_EPISODE -> { | ||||
|                     val tempUrl = url | ||||
|                     if (tempUrl != null) { | ||||
|                         viewModel.loadEpisode(episodeClick.data, true) { data -> | ||||
|                             if (data is Resource.Success) { | ||||
|                                 VideoDownloadManager.DownloadEpisode(requireContext(), | ||||
|                                     tempUrl, | ||||
|                                     episodeClick.data, | ||||
|                                     data.value) | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -148,7 +148,7 @@ class SearchFragment : Fragment() { | |||
|         allApi.providersActive = requireActivity().getApiSettings() | ||||
| 
 | ||||
|         //searchViewModel.search("iron man") | ||||
|         //(activity as AppCompatActivity).loadResult("https://shiro.is/overlord-dubbed", "overlord-dubbed", "Shiro") | ||||
|         (activity as AppCompatActivity).loadResult("https://shiro.is/overlord-dubbed", "overlord-dubbed", "Shiro") | ||||
| /* | ||||
|         (requireActivity() as AppCompatActivity).supportFragmentManager.beginTransaction() | ||||
|             .setCustomAnimations(R.anim.enter_anim, | ||||
|  |  | |||
|  | @ -0,0 +1,310 @@ | |||
| package com.lagradost.cloudstream3.utils | ||||
| 
 | ||||
| import android.app.NotificationChannel | ||||
| import android.app.NotificationManager | ||||
| import android.app.PendingIntent | ||||
| import android.content.Context | ||||
| import android.content.Intent | ||||
| import android.graphics.Bitmap | ||||
| import android.net.Uri | ||||
| import android.os.Build | ||||
| import android.os.Environment | ||||
| import androidx.annotation.DrawableRes | ||||
| import androidx.core.app.NotificationCompat | ||||
| import androidx.core.app.NotificationManagerCompat | ||||
| import androidx.core.net.toUri | ||||
| import com.bumptech.glide.Glide | ||||
| import com.google.android.exoplayer2.database.ExoDatabaseProvider | ||||
| import com.google.android.exoplayer2.offline.DownloadManager | ||||
| import com.google.android.exoplayer2.offline.DownloadRequest | ||||
| import com.google.android.exoplayer2.offline.DownloadService | ||||
| import com.google.android.exoplayer2.offline.DownloadService.sendAddDownload | ||||
| import com.google.android.exoplayer2.offline.StreamKey | ||||
| import com.google.android.exoplayer2.scheduler.Requirements | ||||
| import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory | ||||
| import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor | ||||
| import com.google.android.exoplayer2.upstream.cache.SimpleCache | ||||
| import com.lagradost.cloudstream3.MainActivity | ||||
| import com.lagradost.cloudstream3.R | ||||
| import com.lagradost.cloudstream3.UIHelper.colorFromAttribute | ||||
| import com.lagradost.cloudstream3.USER_AGENT | ||||
| import com.lagradost.cloudstream3.ui.result.ResultEpisode | ||||
| import java.io.File | ||||
| import java.util.concurrent.Executor | ||||
| 
 | ||||
| 
 | ||||
| const val CHANNEL_ID = "cloudstream3.general" | ||||
| const val CHANNEL_NAME = "Downloads" | ||||
| const val CHANNEL_DESCRIPT = "The download notification channel" | ||||
| 
 | ||||
| object VideoDownloadManager { | ||||
|     @DrawableRes | ||||
|     const val imgDone = R.drawable.rddone | ||||
| 
 | ||||
|     @DrawableRes | ||||
|     const val imgDownloading = R.drawable.rdload | ||||
| 
 | ||||
|     @DrawableRes | ||||
|     const val imgPaused = R.drawable.rdpause | ||||
| 
 | ||||
|     @DrawableRes | ||||
|     const val imgStopped = R.drawable.rderror | ||||
| 
 | ||||
|     @DrawableRes | ||||
|     const val imgError = R.drawable.rderror | ||||
| 
 | ||||
|     @DrawableRes | ||||
|     const val pressToPauseIcon = R.drawable.ic_baseline_pause_24 | ||||
| 
 | ||||
|     @DrawableRes | ||||
|     const val pressToResumeIcon = R.drawable.ic_baseline_play_arrow_24 | ||||
| 
 | ||||
|     @DrawableRes | ||||
|     const val pressToStopIcon = R.drawable.exo_icon_stop | ||||
| 
 | ||||
|     enum class DownloadType { | ||||
|         IsPaused, | ||||
|         IsDownloading, | ||||
|         IsDone, | ||||
|         IsFailed, | ||||
|         IsStopped, | ||||
|     } | ||||
| 
 | ||||
|     enum class DownloadActionType { | ||||
|         Pause, | ||||
|         Resume, | ||||
|         Stop, | ||||
|     } | ||||
| 
 | ||||
|     private var hasCreatedNotChanel = false | ||||
|     private fun Context.createNotificationChannel() { | ||||
|         hasCreatedNotChanel = true | ||||
|         // Create the NotificationChannel, but only on API 26+ because | ||||
|         // the NotificationChannel class is new and not in the support library | ||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||||
|             val name = CHANNEL_NAME //getString(R.string.channel_name) | ||||
|             val descriptionText = CHANNEL_DESCRIPT//getString(R.string.channel_description) | ||||
|             val importance = NotificationManager.IMPORTANCE_DEFAULT | ||||
|             val channel = NotificationChannel(CHANNEL_ID, name, importance).apply { | ||||
|                 description = descriptionText | ||||
|             } | ||||
|             // Register the channel with the system | ||||
|             val notificationManager: NotificationManager = | ||||
|                 this.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager | ||||
|             notificationManager.createNotificationChannel(channel) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private val cachedBitmaps = hashMapOf<String, Bitmap>() | ||||
|     private fun Context.getImageBitmapFromUrl(url: String): Bitmap? { | ||||
|         if (cachedBitmaps.containsKey(url)) { | ||||
|             return cachedBitmaps[url] | ||||
|         } | ||||
| 
 | ||||
|         val bitmap = Glide.with(this) | ||||
|             .asBitmap() | ||||
|             .load(url).into(720, 720) | ||||
|             .get() | ||||
|         if (bitmap != null) { | ||||
|             cachedBitmaps[url] = bitmap | ||||
|         } | ||||
|         return null | ||||
|     } | ||||
| 
 | ||||
|     fun createNotification( | ||||
|         context: Context, | ||||
|         text: String, | ||||
|         source: String, | ||||
|         ep: ResultEpisode, | ||||
|         state: DownloadType, | ||||
|         progress: Long, | ||||
|         total: Long, | ||||
|     ) { | ||||
|         val intent = Intent(context, MainActivity::class.java).apply { | ||||
|             data = source.toUri() | ||||
|             flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK | ||||
|         } | ||||
| 
 | ||||
|         val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, 0) | ||||
|         val builder = NotificationCompat.Builder(context, CHANNEL_ID) | ||||
|             .setAutoCancel(true) | ||||
|             .setColorized(true) | ||||
|             .setAutoCancel(true) | ||||
|             .setOnlyAlertOnce(true) | ||||
|             .setPriority(NotificationCompat.PRIORITY_DEFAULT) | ||||
|             .setColor(context.colorFromAttribute(R.attr.colorPrimary)) | ||||
|             .setContentText(text) | ||||
|             .setSmallIcon( | ||||
|                 when (state) { | ||||
|                     DownloadType.IsDone -> imgDone | ||||
|                     DownloadType.IsDownloading -> imgDownloading | ||||
|                     DownloadType.IsPaused -> imgPaused | ||||
|                     DownloadType.IsFailed -> imgError | ||||
|                     DownloadType.IsStopped -> imgStopped | ||||
|                 } | ||||
|             ) | ||||
|             .setContentIntent(pendingIntent) | ||||
| 
 | ||||
|         if (state == DownloadType.IsDownloading || state == DownloadType.IsPaused) { | ||||
|             builder.setProgress(total.toInt(), progress.toInt(), false) | ||||
|         } | ||||
| 
 | ||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||||
|             if (ep.poster != null) { | ||||
|                 val poster = context.getImageBitmapFromUrl(ep.poster) | ||||
|                 if (poster != null) | ||||
|                     builder.setLargeIcon(poster) | ||||
|             } | ||||
|         } | ||||
|         if ((state == DownloadType.IsDownloading || state == DownloadType.IsPaused) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||||
|             val actionTypes: MutableList<DownloadActionType> = ArrayList() | ||||
|             // INIT | ||||
|             if (state == DownloadType.IsDownloading) { | ||||
|                 actionTypes.add(DownloadActionType.Pause) | ||||
|                 actionTypes.add(DownloadActionType.Stop) | ||||
|             } | ||||
| 
 | ||||
|             if (state == DownloadType.IsPaused) { | ||||
|                 actionTypes.add(DownloadActionType.Resume) | ||||
|                 actionTypes.add(DownloadActionType.Stop) | ||||
|             } | ||||
| 
 | ||||
|             // ADD ACTIONS | ||||
|             for ((index, i) in actionTypes.withIndex()) { | ||||
|                 val _resultIntent = Intent(context, DownloadService::class.java) | ||||
| 
 | ||||
|                 _resultIntent.putExtra( | ||||
|                     "type", when (i) { | ||||
|                         DownloadActionType.Resume -> "resume" | ||||
|                         DownloadActionType.Pause -> "pause" | ||||
|                         DownloadActionType.Stop -> "stop" | ||||
|                     } | ||||
|                 ) | ||||
| 
 | ||||
|                 _resultIntent.putExtra("id", ep.id) | ||||
| 
 | ||||
|                 val pending: PendingIntent = PendingIntent.getService( | ||||
|                     context, 4337 + index + ep.id, | ||||
|                     _resultIntent, | ||||
|                     PendingIntent.FLAG_UPDATE_CURRENT | ||||
|                 ) | ||||
| 
 | ||||
|                 builder.addAction( | ||||
|                     NotificationCompat.Action( | ||||
|                         when (i) { | ||||
|                             DownloadActionType.Resume -> pressToResumeIcon | ||||
|                             DownloadActionType.Pause -> pressToPauseIcon | ||||
|                             DownloadActionType.Stop -> pressToStopIcon | ||||
|                         }, when (i) { | ||||
|                             DownloadActionType.Resume -> "Resume" | ||||
|                             DownloadActionType.Pause -> "Pause" | ||||
|                             DownloadActionType.Stop -> "Stop" | ||||
|                         }, pending | ||||
|                     ) | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (!hasCreatedNotChanel) { | ||||
|             context.createNotificationChannel() | ||||
|         } | ||||
| 
 | ||||
|         with(NotificationManagerCompat.from(context)) { | ||||
|             // notificationId is a unique int for each notification that you must define | ||||
|             notify(ep.id, builder.build()) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     //https://exoplayer.dev/downloading-media.html | ||||
|     fun DownloadSingleEpisode(context: Context, source: String, ep: ResultEpisode, link: ExtractorLink) { | ||||
|         val url = link.url | ||||
|         val headers = mapOf("User-Agent" to USER_AGENT, "Referer" to link.referer) | ||||
| 
 | ||||
|         // Note: This should be a singleton in your app. | ||||
|         val databaseProvider = ExoDatabaseProvider(context) | ||||
| 
 | ||||
|         val downloadDirectory = File(Environment.getExternalStorageDirectory().path + "/Download/" + (ep.name ?: "Episode ${ep.episode}")) // File(context.cacheDir, "video_${ep.id}") | ||||
| 
 | ||||
|         // A download cache should not evict media, so should use a NoopCacheEvictor. | ||||
|         val downloadCache = SimpleCache( | ||||
|             downloadDirectory, | ||||
|             NoOpCacheEvictor(), | ||||
|             databaseProvider) | ||||
| 
 | ||||
|         // Create a factory for reading the data from the network. | ||||
|         val dataSourceFactory = DefaultHttpDataSourceFactory() | ||||
| 
 | ||||
|         // Choose an executor for downloading data. Using Runnable::run will cause each download task to | ||||
|         // download data on its own thread. Passing an executor that uses multiple threads will speed up | ||||
|         // download tasks that can be split into smaller parts for parallel execution. Applications that | ||||
|         // already have an executor for background downloads may wish to reuse their existing executor. | ||||
|         val downloadExecutor = Executor { obj: Runnable -> obj.run() } | ||||
| 
 | ||||
| 
 | ||||
|         // Create the download manager. | ||||
|         val downloadManager = DownloadManager( | ||||
|             context, | ||||
|             databaseProvider, | ||||
|             downloadCache, | ||||
|             dataSourceFactory, | ||||
|             downloadExecutor) | ||||
| 
 | ||||
|         val requirements = Requirements(Requirements.NETWORK) | ||||
|         // Optionally, setters can be called to configure the download manager. | ||||
|         downloadManager.requirements = requirements | ||||
|         downloadManager.maxParallelDownloads = 3 | ||||
|         val builder = DownloadRequest.Builder(ep.id.toString(), link.url.toUri()) | ||||
| 
 | ||||
|         val downloadRequest: DownloadRequest = builder.build() | ||||
| 
 | ||||
|         DownloadService.sendAddDownload( | ||||
|             context, | ||||
|             VideoDownloadService::class.java, | ||||
|             downloadRequest, | ||||
|             /* foreground= */ true) | ||||
| /* | ||||
|         val disposable = url.download(header = headers) | ||||
|             .observeOn(AndroidSchedulers.mainThread()) | ||||
|             .subscribeBy( | ||||
|                 onNext = { progress -> | ||||
|                     createNotification( | ||||
|                         context, | ||||
|                         "Downloading ${progress.downloadSizeStr()}/${progress.totalSizeStr()}", | ||||
|                         source, | ||||
|                         ep, | ||||
|                         DownloadType.IsDownloading, | ||||
|                         progress.downloadSize, | ||||
|                         progress.totalSize | ||||
|                     ) | ||||
|                 }, | ||||
|                 onComplete = { | ||||
|                     createNotification( | ||||
|                         context, | ||||
|                         "Download Done", | ||||
|                         source, | ||||
|                         ep, | ||||
|                         DownloadType.IsDone, | ||||
|                         0, 0 | ||||
|                     ) | ||||
|                 }, | ||||
|                 onError = { | ||||
|                     createNotification( | ||||
|                         context, | ||||
|                         "Download Failed", | ||||
|                         source, | ||||
|                         ep, | ||||
|                         DownloadType.IsFailed, | ||||
|                         0, 0 | ||||
|                     ) | ||||
|                 } | ||||
|             )*/ | ||||
|     } | ||||
| 
 | ||||
|     public fun DownloadEpisode(context: Context, source: String, ep: ResultEpisode, links: List<ExtractorLink>) { | ||||
|         val validLinks = links.filter { !it.isM3u8 } | ||||
|         if (validLinks.isNotEmpty()) { | ||||
|             DownloadSingleEpisode(context, source, ep, validLinks.first()) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,97 @@ | |||
| package com.lagradost.cloudstream3.utils | ||||
| 
 | ||||
| import android.app.IntentService | ||||
| import android.app.Notification | ||||
| import android.app.PendingIntent | ||||
| import android.content.Intent | ||||
| import androidx.core.app.NotificationCompat | ||||
| import androidx.core.app.NotificationManagerCompat | ||||
| import androidx.core.net.toUri | ||||
| import com.google.android.exoplayer2.offline.Download | ||||
| import com.google.android.exoplayer2.offline.DownloadManager | ||||
| import com.google.android.exoplayer2.offline.DownloadService | ||||
| import com.google.android.exoplayer2.scheduler.PlatformScheduler | ||||
| import com.google.android.exoplayer2.scheduler.Scheduler | ||||
| import com.lagradost.cloudstream3.MainActivity | ||||
| import com.lagradost.cloudstream3.R | ||||
| import com.lagradost.cloudstream3.UIHelper.colorFromAttribute | ||||
| import java.lang.Exception | ||||
| 
 | ||||
| private const val JOB_ID = 1 | ||||
| private const val FOREGROUND_NOTIFICATION_ID = 1 | ||||
| 
 | ||||
| class VideoDownloadService : DownloadService( | ||||
|     FOREGROUND_NOTIFICATION_ID, | ||||
|     DEFAULT_FOREGROUND_NOTIFICATION_UPDATE_INTERVAL, | ||||
|     CHANNEL_ID, | ||||
|     R.string.exo_download_notification_channel_name,  /* channelDescriptionResourceId= */ | ||||
|     0) { | ||||
|     override fun getDownloadManager(): DownloadManager { | ||||
|         val ctx = this | ||||
|         return ExoPlayerHelper.downloadManager.apply { | ||||
|             requirements = DownloadManager.DEFAULT_REQUIREMENTS | ||||
|             maxParallelDownloads = 3 | ||||
|             addListener( | ||||
|                 object : DownloadManager.Listener { | ||||
| 
 | ||||
|                     override fun onDownloadChanged( | ||||
|                         downloadManager: DownloadManager, | ||||
|                         download: Download, | ||||
|                         finalException: Exception?, | ||||
|                     ) { | ||||
|                         val intent = Intent(ctx, MainActivity::class.java).apply { | ||||
|                             flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK | ||||
|                         } | ||||
| 
 | ||||
|                         val pendingIntent: PendingIntent = PendingIntent.getActivity(ctx, 0, intent, 0) | ||||
|                         val builder = NotificationCompat.Builder(ctx, CHANNEL_ID) | ||||
|                             .setAutoCancel(true) | ||||
|                             .setColorized(true) | ||||
|                             .setAutoCancel(true) | ||||
|                             .setOnlyAlertOnce(true) | ||||
|                             .setPriority(NotificationCompat.PRIORITY_DEFAULT) | ||||
|                             .setColor(colorFromAttribute(R.attr.colorPrimary)) | ||||
|                             .setContentText("${download.bytesDownloaded} / ${download.contentLength}") | ||||
|                             .setSmallIcon( | ||||
|                                 VideoDownloadManager.imgDownloading | ||||
|                             ) | ||||
|                             .setProgress((download.bytesDownloaded / 1000).toInt(), | ||||
|                                 (download.contentLength / 1000).toInt(), | ||||
|                                 false) // in case the size is over 2gb / 1000 | ||||
|                             .setContentIntent(pendingIntent) | ||||
|                         builder.build() | ||||
|                         with(NotificationManagerCompat.from(ctx)) { | ||||
|                             // notificationId is a unique int for each notification that you must define | ||||
|                             notify(download.request.id.hashCode(), builder.build()) | ||||
|                         } | ||||
|                         super.onDownloadChanged(downloadManager, download, finalException) | ||||
|                     } | ||||
|                 } | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override fun getScheduler(): Scheduler = | ||||
|         PlatformScheduler(this, JOB_ID) | ||||
| 
 | ||||
|     override fun getForegroundNotification(downloads: MutableList<Download>): Notification { | ||||
|         val intent = Intent(this, MainActivity::class.java).apply { | ||||
|             flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK | ||||
|         } | ||||
| 
 | ||||
|         val pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, 0) | ||||
|         val builder = NotificationCompat.Builder(this, CHANNEL_ID) | ||||
|             .setAutoCancel(true) | ||||
|             .setColorized(true) | ||||
|             .setAutoCancel(true) | ||||
|             .setOnlyAlertOnce(true) | ||||
|             .setPriority(NotificationCompat.PRIORITY_DEFAULT) | ||||
|             .setColor(colorFromAttribute(R.attr.colorPrimary)) | ||||
|             .setContentText("Downloading ${downloads.size} item${if (downloads.size == 1) "" else "s"}") | ||||
|             .setSmallIcon( | ||||
|                 VideoDownloadManager.imgDownloading | ||||
|             ) | ||||
|             .setContentIntent(pendingIntent) | ||||
|         return builder.build() | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,33 @@ | |||
| package com.lagradost.cloudstream3.utils | ||||
| 
 | ||||
| import com.google.android.exoplayer2.database.ExoDatabaseProvider | ||||
| import com.google.android.exoplayer2.offline.DownloadManager | ||||
| import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory | ||||
| import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor | ||||
| import com.google.android.exoplayer2.upstream.cache.SimpleCache | ||||
| import com.lagradost.cloudstream3.MainActivity | ||||
| import java.util.concurrent.Executor | ||||
| 
 | ||||
| object ExoPlayerHelper { | ||||
|     private val context = MainActivity.mainContext | ||||
|     val databaseProvider = ExoDatabaseProvider(context) | ||||
|     val downloadExecutor = Executor { obj: Runnable -> obj.run() } | ||||
|     val dataSourceFactory = DefaultHttpDataSourceFactory() | ||||
| 
 | ||||
|     val downloadCache: SimpleCache by lazy { | ||||
|         SimpleCache( | ||||
|             context.cacheDir, | ||||
|             LeastRecentlyUsedCacheEvictor(20 * 1024 * 1024), | ||||
|             databaseProvider | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     val downloadManager: DownloadManager by lazy { | ||||
|         DownloadManager(context, | ||||
|             databaseProvider, | ||||
|             downloadCache, | ||||
|             dataSourceFactory, | ||||
|             downloadExecutor) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										6
									
								
								app/src/main/res/drawable/.idea/misc.xml
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								app/src/main/res/drawable/.idea/misc.xml
									
										
									
										generated
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="ProjectRootManager"> | ||||
|     <output url="file://$PROJECT_DIR$/out" /> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										8
									
								
								app/src/main/res/drawable/.idea/modules.xml
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								app/src/main/res/drawable/.idea/modules.xml
									
										
									
										generated
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="ProjectModuleManager"> | ||||
|     <modules> | ||||
|       <module fileurl="file://$PROJECT_DIR$/.idea/drawable.iml" filepath="$PROJECT_DIR$/.idea/drawable.iml" /> | ||||
|     </modules> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										6
									
								
								app/src/main/res/drawable/.idea/vcs.xml
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								app/src/main/res/drawable/.idea/vcs.xml
									
										
									
										generated
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="VcsDirectoryMappings"> | ||||
|     <mapping directory="$PROJECT_DIR$/../../../../.." vcs="Git" /> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										24
									
								
								app/src/main/res/drawable/rddone.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								app/src/main/res/drawable/rddone.xml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,24 @@ | |||
| <vector | ||||
|         xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|         android:name="vector" | ||||
|         android:width="24dp" | ||||
|         android:height="24dp" | ||||
|         android:tint="?attr/colorPrimary" | ||||
|         android:viewportWidth="24" | ||||
|         android:viewportHeight="24"> | ||||
|     <path | ||||
|             android:name="path" | ||||
|             android:pathData="M 17 1.01 L 7 1 C 5.9 1 5 1.9 5 3 L 5 21 C 5 22.1 5.9 23 7 23 L 17 23 C 18.1 23 19 22.1 19 21 L 19 3 C 19 1.9 18.1 1.01 17 1.01 Z M 17 19 L 7 19 L 7 5 L 17 5 Z" | ||||
|             android:fillColor="@color/white" | ||||
|             android:strokeWidth="1"/> | ||||
|     <path | ||||
|             android:name="path_1" | ||||
|             android:pathData="M 7.488 13.004 L 9.132 11.36 L 12.492 14.72 L 10.848 16.364 Z" | ||||
|             android:fillColor="@color/white" | ||||
|             android:strokeWidth="1"/> | ||||
|     <path | ||||
|             android:name="path_2" | ||||
|             android:pathData="M 10.848 16.364 L 9.203 14.72 L 14.894 9.029 L 16.539 10.673 Z" | ||||
|             android:fillColor="@color/white" | ||||
|             android:strokeWidth="1"/> | ||||
| </vector> | ||||
							
								
								
									
										24
									
								
								app/src/main/res/drawable/rderror.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								app/src/main/res/drawable/rderror.xml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,24 @@ | |||
| <vector | ||||
|         xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|         android:name="vector" | ||||
|         android:width="24dp" | ||||
|         android:height="24dp" | ||||
|         android:tint="?attr/colorPrimary" | ||||
|         android:viewportWidth="24" | ||||
|         android:viewportHeight="24"> | ||||
|     <path | ||||
|             android:name="path" | ||||
|             android:pathData="M 17 1.01 L 7 1 C 5.9 1 5 1.9 5 3 L 5 21 C 5 22.1 5.9 23 7 23 L 17 23 C 18.1 23 19 22.1 19 21 L 19 3 C 19 1.9 18.1 1.01 17 1.01 Z M 17 19 L 7 19 L 7 5 L 17 5 Z M 10.101 13.289 L 10.513 10.311 L 9.848 9.33 L 9.496 8.867 L 11 13 L 9.706 13 L 9.745 15.93 Z" | ||||
|             android:fillColor="@color/white" | ||||
|             android:strokeWidth="1"/> | ||||
|     <path | ||||
|             android:name="path_1" | ||||
|             android:pathData="M 15.801 14.763 L 14.157 16.407 L 7.873 10.124 L 9.518 8.479 Z" | ||||
|             android:fillColor="@color/white" | ||||
|             android:strokeWidth="1"/> | ||||
|     <path | ||||
|             android:name="path_2" | ||||
|             android:pathData="M 14.157 8.479 L 15.801 10.124 L 9.518 16.407 L 7.873 14.763 Z" | ||||
|             android:fillColor="@color/white" | ||||
|             android:strokeWidth="1"/> | ||||
| </vector> | ||||
							
								
								
									
										5
									
								
								app/src/main/res/drawable/rdload.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								app/src/main/res/drawable/rdload.xml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | |||
| <vector android:height="24dp" android:tint="?attr/colorPrimary" | ||||
|         android:viewportHeight="24" android:viewportWidth="24" | ||||
|         android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <path android:fillColor="@android:color/white" android:pathData="M17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19L7,19L7,5h10v14zM16,13h-3L13,8h-2v5L8,13l4,4 4,-4z"/> | ||||
| </vector> | ||||
							
								
								
									
										19
									
								
								app/src/main/res/drawable/rdpause.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								app/src/main/res/drawable/rdpause.xml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | |||
| <vector | ||||
|         xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|         android:name="vector" | ||||
|         android:width="24dp" | ||||
|         android:height="24dp" | ||||
|         android:tint="?attr/colorPrimary" | ||||
|         android:viewportWidth="24" | ||||
|         android:viewportHeight="24"> | ||||
|     <path | ||||
|             android:name="path" | ||||
|             android:pathData="M 17 1.01 L 7 1 C 5.9 1 5 1.9 5 3 L 5 21 C 5 22.1 5.9 23 7 23 L 17 23 C 18.1 23 19 22.1 19 21 L 19 3 C 19 1.9 18.1 1.01 17 1.01 Z M 17 19 L 7 19 L 7 5 L 17 5 Z M 10.101 13.289 L 10.513 10.311 L 9.848 9.33 L 9.496 8.867 L 11 13 L 9.706 13 L 9.745 15.93 Z" | ||||
|             android:fillColor="@color/white" | ||||
|             android:strokeWidth="1"/> | ||||
|     <path | ||||
|             android:name="path_1" | ||||
|             android:pathData="M 9.104 8.931 L 11.129 8.931 L 11.129 15.956 L 9.104 15.956 Z M 12.871 8.931 L 14.896 8.931 L 14.896 15.956 L 12.871 15.956 Z" | ||||
|             android:fillColor="@color/white" | ||||
|             android:strokeWidth="1"/> | ||||
| </vector> | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue