diff --git a/data/src/main/java/com/m3u/data/parser/m3u/M3UParserImpl.kt b/data/src/main/java/com/m3u/data/parser/m3u/M3UParserImpl.kt index 5ec0465f0..54268449c 100644 --- a/data/src/main/java/com/m3u/data/parser/m3u/M3UParserImpl.kt +++ b/data/src/main/java/com/m3u/data/parser/m3u/M3UParserImpl.kt @@ -25,7 +25,7 @@ internal class M3UParserImpl @Inject constructor( private const val KODI_MARK = "#KODIPROP:" private val infoRegex = """(-?\d+)(.*),(.+)""".toRegex() - private val kodiPropRegex = """(.+)=(.+)""".toRegex() + private val kodiPropRegex = """([^=]+)=(.+)""".toRegex() private val metadataRegex = """([\w-_.]+)=\s*(?:"([^"]*)"|(\S+))""".toRegex() private const val M3U_TVG_LOGO_MARK = "tvg-logo" @@ -82,8 +82,8 @@ internal class M3UParserImpl @Inject constructor( } val kodiMetadata = buildMap { for (match in kodiMatches) { - val key = match.groups[1]!!.value - val value = match.groups[2]?.value?.ifBlank { null } ?: continue + val key = match.groups[1]!!.value.also { println(it) } + val value = match.groups[2]?.value?.ifBlank { null }.also { println(it) } ?: continue put(key.trim(), value.trim()) } } diff --git a/data/src/main/java/com/m3u/data/service/internal/PlayerManagerImpl.kt b/data/src/main/java/com/m3u/data/service/internal/PlayerManagerImpl.kt index a4f6658fc..f54f8d1e1 100644 --- a/data/src/main/java/com/m3u/data/service/internal/PlayerManagerImpl.kt +++ b/data/src/main/java/com/m3u/data/service/internal/PlayerManagerImpl.kt @@ -20,6 +20,10 @@ import androidx.media3.datasource.okhttp.OkHttpDataSource import androidx.media3.datasource.rtmp.RtmpDataSource import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.RenderersFactory +import androidx.media3.exoplayer.drm.DefaultDrmSessionManager +import androidx.media3.exoplayer.drm.FrameworkMediaDrm +import androidx.media3.exoplayer.drm.HttpMediaDrmCallback +import androidx.media3.exoplayer.drm.LocalMediaDrmCallback import androidx.media3.exoplayer.hls.HlsMediaSource import androidx.media3.exoplayer.offline.Download import androidx.media3.exoplayer.offline.DownloadManager @@ -161,6 +165,8 @@ class PlayerManagerImpl @Inject constructor( } if (channel != null) { val channelUrl = channel.url + val licenseType = channel.licenseType.orEmpty() + val licenseKey = channel.licenseKey.orEmpty() channelRepository.reportPlayed(channel.id) val playlist = playlistRepository.get(channel.playlistUrl) @@ -171,6 +177,8 @@ class PlayerManagerImpl @Inject constructor( mimeType = null, url = channelUrl, userAgent = playlist?.userAgent, + licenseType = licenseType, + licenseKey = licenseKey ) observePreferencesChangingJob?.cancel() @@ -200,7 +208,9 @@ class PlayerManagerImpl @Inject constructor( private fun tryPlay( mimeType: String?, url: String = channel.value?.url.orEmpty(), - userAgent: String? = playlist.value?.userAgent + userAgent: String? = playlist.value?.userAgent, + licenseType: String = channel.value?.licenseType.orEmpty(), + licenseKey: String = channel.value?.licenseKey.orEmpty(), ) { val rtmp: Boolean = Url(url).protocol.name == "rtmp" val tunneling: Boolean = preferences.tunneling @@ -229,6 +239,30 @@ class PlayerManagerImpl @Inject constructor( else -> DefaultMediaSourceFactory(dataSourceFactory) } logger.post { "media-source-factory: ${mediaSourceFactory::class.qualifiedName}" } + if (licenseType.isNotEmpty()) { + val drmCallback = when { + (licenseType == Channel.LICENSE_TYPE_CLEAR_KEY) && + !licenseKey.startsWith("http") -> LocalMediaDrmCallback(licenseKey.toByteArray()) + + else -> HttpMediaDrmCallback( + licenseKey, + dataSourceFactory + ) + } + val uuid = when (licenseType) { + Channel.LICENSE_TYPE_CLEAR_KEY -> C.CLEARKEY_UUID + Channel.LICENSE_TYPE_WIDEVINE -> C.WIDEVINE_UUID + Channel.LICENSE_TYPE_PLAY_READY -> C.PLAYREADY_UUID + else -> C.UUID_NIL + } + if (uuid != C.UUID_NIL && FrameworkMediaDrm.isCryptoSchemeSupported(uuid)) { + val drmSessionManager = DefaultDrmSessionManager.Builder() + .setUuidAndExoMediaDrmProvider(uuid, FrameworkMediaDrm.DEFAULT_PROVIDER) + .setMultiSession(licenseType != Channel.LICENSE_TYPE_CLEAR_KEY) + .build(drmCallback) + mediaSourceFactory.setDrmSessionManagerProvider { drmSessionManager } + } + } val player = player.updateAndGet { prev -> prev ?: createPlayer(mediaSourceFactory, tunneling) }!!