Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Add download support to AES encrypted video #22

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class OfflineVideoInfoRepository(context: Context) {
private val offlineVideoInfoDao = TPStreamsDatabase(context).offlineVideoInfoDao()

suspend fun updateDownloadStatus(download: Download) {
val offlineVideoInfo = offlineVideoInfoDao.getOfflineVideoInfoByUrl(download.request.uri.toString())
val offlineVideoInfo = getOfflineVideoInfoByUrl(download.request.uri.toString())
offlineVideoInfo?.let {
offlineVideoInfo.percentageDownloaded = download.percentDownloaded.toInt()
offlineVideoInfo.bytesDownloaded = download.bytesDownloaded
Expand All @@ -23,12 +23,22 @@ class OfflineVideoInfoRepository(context: Context) {
}
}

private fun getOfflineVideoInfoByUrl(url:String):OfflineVideoInfo? {
if (url.contains(".m3u8")){
return offlineVideoInfoDao.getOfflineVideoInfoByUrl(url)
}
return offlineVideoInfoDao.getOfflineVideoInfoByDashUrl(url)
}

fun get(videoId: String): LiveData<OfflineVideoInfo?> {
return offlineVideoInfoDao.getOfflineVideoInfoById(videoId)
}

fun grtVideoIdByUrl(url:String):String? {
return offlineVideoInfoDao.getOfflineVideoInfoByUrl(url)?.videoId
fun getVideoIdByUrl(url:String):String? {
if (url.contains(".m3u8")){
return offlineVideoInfoDao.getOfflineVideoInfoByUrl(url)?.videoId
}
return offlineVideoInfoDao.getOfflineVideoInfoByDashUrl(url)?.videoId
}

suspend fun insert(offlineVideoInfo: OfflineVideoInfo){
Expand Down
41 changes: 30 additions & 11 deletions player/src/main/java/com/tpstream/player/TpStreamPlayer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ package com.tpstream.player
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.util.Log
import androidx.media3.common.Format
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.MimeTypes
import androidx.media3.common.Tracks
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.drm.DefaultDrmSessionManager
Expand Down Expand Up @@ -38,6 +36,10 @@ public interface TpStreamPlayer {
fun getDuration(): Long
}

internal object Params{
lateinit var params:TpInitParams
}

class TpStreamPlayerImpl(val player: ExoPlayer, val context: Context) : TpStreamPlayer {
override lateinit var params: TpInitParams
override lateinit var videoInfo: VideoInfo
Expand All @@ -51,8 +53,8 @@ class TpStreamPlayerImpl(val player: ExoPlayer, val context: Context) : TpStream

private fun getMediaSourceFactory(): MediaSource.Factory {
val mediaSourceFactory = DefaultMediaSourceFactory(context)
.setDataSourceFactory(VideoDownloadManager(context).build(params))
if (offlineVideoInfo == null) {
.setDataSourceFactory(VideoDownloadManager(context).build())
if (!(videoInfo.dashUrl == null || videoInfo.dashUrl.equals("")) && offlineVideoInfo == null) {
mediaSourceFactory.setDrmSessionManagerProvider {
DefaultDrmSessionManager.Builder().build(
CustomHttpDrmMediaCallback(context, params)
Expand All @@ -71,13 +73,16 @@ class TpStreamPlayerImpl(val player: ExoPlayer, val context: Context) : TpStream
}

private fun buildMediaItem(url: String): MediaItem {
return MediaItem.Builder()
val builder = MediaItem.Builder()
.setUri(url)
.setDrmConfiguration(
if (!videoInfo.dashUrl.equals("")){
builder.setDrmConfiguration(
MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID)
.setMultiSession(true)
.build()
).build()
)
}
return builder.build()
}

private fun buildDownloadedMediaItem(downloadRequest: DownloadRequest):MediaItem{
Expand All @@ -88,21 +93,28 @@ class TpStreamPlayerImpl(val player: ExoPlayer, val context: Context) : TpStream
.setCustomCacheKey(downloadRequest.customCacheKey)
.setMimeType(downloadRequest.mimeType)
.setStreamKeys(downloadRequest.streamKeys)
.setDrmConfiguration(
if (!offlineVideoInfo?.dashUrl.equals("")){
builder.setDrmConfiguration(
MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID)
.setKeySetId(downloadRequest.keySetId)
.build()
)
}
return builder.build()
}

override fun load(parameters: TpInitParams, onError:(exception: TPException) -> Unit) {
params = parameters
Params.params = parameters
populateOfflineVideoInfo(parameters)
if (checkIsVideoDownloaded()){
videoInfo = offlineVideoInfo?.asVideoInfo()!!
Handler(Looper.getMainLooper()).post {
load(offlineVideoInfo?.dashUrl!!)
if (offlineVideoInfo?.dashUrl.equals("")){
load(offlineVideoInfo?.url!!)
} else {
load(offlineVideoInfo?.dashUrl!!)
}
}
return
}
Expand Down Expand Up @@ -130,8 +142,15 @@ class TpStreamPlayerImpl(val player: ExoPlayer, val context: Context) : TpStream
}

private fun checkIsVideoDownloaded():Boolean{
if (offlineVideoInfo != null && DownloadTask(context).isDownloaded(offlineVideoInfo?.dashUrl!!)){
return true
if (offlineVideoInfo != null){
val url = if (offlineVideoInfo?.dashUrl.equals("")){
offlineVideoInfo?.url!!
} else {
offlineVideoInfo?.dashUrl!!
}
if (DownloadTask(context).isDownloaded(url)) {
return true
}
}
return false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,11 @@ class TpStreamPlayerFragment : Fragment(), DownloadCallback.Listener {

private fun reloadVideo(){
val currentPosition = player?.getCurrentTime()
val url = player?.videoInfo?.dashUrl!!
val url = if (player?.videoInfo?.dashUrl == null || player?.videoInfo?.dashUrl.equals("")) {
player?.videoInfo?.url!!
} else {
player?.videoInfo?.dashUrl!!
}
val tpImp = player as TpStreamPlayerImpl
tpImp.load(url,currentPosition!!)
}
Expand Down
13 changes: 7 additions & 6 deletions player/src/main/java/com/tpstream/player/VideoDownloadManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.tpstream.player

import android.annotation.SuppressLint
import android.content.Context
import android.util.Log
import androidx.media3.database.DatabaseProvider
import androidx.media3.database.StandaloneDatabaseProvider
import androidx.media3.datasource.DataSource
Expand Down Expand Up @@ -51,19 +52,20 @@ class VideoDownloadManager {
return databaseProvider
}

fun getHttpDataSourceFactory(params: TpInitParams? = null): DataSource.Factory {
fun getHttpDataSourceFactory(): DataSource.Factory {
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(VideoPlayerInterceptor(context,params))
.addInterceptor(VideoPlayerInterceptor(context))
.build()
httpDataSourceFactory = OkHttpDataSource.Factory(okHttpClient)
return httpDataSourceFactory
}

fun build(params: TpInitParams? = null): CacheDataSource.Factory {
fun build(): CacheDataSource.Factory {
Log.d("TAG", "build: ")
val cache = VideoDownloadManager(context).getDownloadCache()
return CacheDataSource.Factory()
.setCache(cache)
.setUpstreamDataSourceFactory(getHttpDataSourceFactory(params))
.setUpstreamDataSourceFactory(getHttpDataSourceFactory())
.setCacheWriteDataSinkFactory(null)
.setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR)
}
Expand All @@ -80,10 +82,9 @@ class VideoDownloadManager {
}

fun getDownloadIndex(): DefaultDownloadIndex {
return DefaultDownloadIndex(databaseProvider)
return DefaultDownloadIndex(getDatabaseProvider(context))
}

@Synchronized
private fun getDownloadDirectory(): File {
if (!::downloadDirectory.isInitialized) {
downloadDirectory = if (context.getExternalFilesDir(null) != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import androidx.media3.exoplayer.drm.DefaultDrmSessionManager
import androidx.media3.exoplayer.offline.DownloadHelper
import androidx.media3.exoplayer.offline.DownloadRequest
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
import com.tpstream.player.models.VideoInfo
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
Expand All @@ -30,7 +29,7 @@ class VideoDownloadRequestCreationHandler(
private var keySetId: ByteArray? = null

init {
val url = player.videoInfo?.dashUrl
val url = player.videoInfo?.dashUrl?:player.videoInfo?.url
trackSelectionParameters = DownloadHelper.getDefaultTrackSelectorParameters(context)
mediaItem = MediaItem.Builder()
.setUri(url)
Expand Down Expand Up @@ -94,7 +93,7 @@ class VideoDownloadRequestCreationHandler(
fun buildDownloadRequest(overrides: MutableMap<TrackGroup, TrackSelectionOverride>): DownloadRequest {
override = overrides
setSelectedTracks(overrides)
val name = player.videoInfo?.title!!
val name = player.videoInfo?.title?:""
return downloadHelper.getDownloadRequest(Util.getUtf8Bytes(name)).copyWithKeySetId(keySetId)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class VideoDownloadService:DownloadService(
var videoId : String?

runBlocking(Dispatchers.IO) {
videoId = offlineVideoInfoRepository.grtVideoIdByUrl(download.request.uri.toString())
videoId = offlineVideoInfoRepository.getVideoIdByUrl(download.request.uri.toString())
}

when (download.state) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@ import android.content.Context
import okhttp3.Interceptor
import okhttp3.Response

class VideoPlayerInterceptor(val context: Context, private val params: TpInitParams?) : Interceptor {
class VideoPlayerInterceptor(val context: Context) : Interceptor {

override fun intercept(chain: Interceptor.Chain): Response {
private lateinit var params: TpInitParams

override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()

if(request.url.toString().contains("encryption_key")) {
params = Params.params
request = request.newBuilder()
.url( "https://${params?.orgCode}.testpress.in/api/v2.5/encryption_key/${params?.videoId}/?access_token=${params?.accessToken}")
.url( "https://${params.orgCode}.testpress.in/api/v2.5/encryption_key/${params.videoId}/?access_token=${params.accessToken}")
.build()
}
return chain.proceed(request)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ interface OfflineVideoInfoDao {
fun getOfflineVideoInfoByVideoId(videoID:String): OfflineVideoInfo?

@Query("SELECT * FROM OfflineVideoInfo WHERE dashUrl=:dashUrl")
fun getOfflineVideoInfoByUrl(dashUrl:String): OfflineVideoInfo?
fun getOfflineVideoInfoByDashUrl(dashUrl:String): OfflineVideoInfo?

@Query("SELECT * FROM OfflineVideoInfo WHERE url=:url")
fun getOfflineVideoInfoByUrl(url:String): OfflineVideoInfo?

@Query("SELECT * FROM OfflineVideoInfo WHERE videoId=:videoId")
fun getOfflineVideoInfoById(videoId:String): LiveData<OfflineVideoInfo?>
Expand Down
4 changes: 2 additions & 2 deletions player/src/main/java/com/tpstream/player/models/VideoInfo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ internal fun VideoInfo.asOfflineVideoInfo():OfflineVideoInfo{
return OfflineVideoInfo(
title = title!!,
url = url!!,
dashUrl = dashUrl!!,
hlsUrl = hlsUrl!!,
dashUrl = dashUrl?:"",
hlsUrl = hlsUrl?:"",
duration = duration!!,
transcodingStatus = transcodingStatus!!
)
Expand Down