implement test MediaRepository
This commit is contained in:
parent
9786a5634d
commit
bdbd8caf43
|
@ -41,6 +41,7 @@ fragmentKtx = "1.8.1"
|
||||||
lifecycleRuntimeKtx = "2.8.3"
|
lifecycleRuntimeKtx = "2.8.3"
|
||||||
kotlinParcelize = "2.0.20-Beta2"
|
kotlinParcelize = "2.0.20-Beta2"
|
||||||
newplayer = "master-SNAPSHOT"
|
newplayer = "master-SNAPSHOT"
|
||||||
|
okhttpAndroid = "5.0.0-alpha.14"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||||
|
@ -73,6 +74,7 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin
|
||||||
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||||
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||||
newplayer = { group = "com.github.theScrabi.NewPlayer", name = "new-player", version.ref = "newplayer" }
|
newplayer = { group = "com.github.theScrabi.NewPlayer", name = "new-player", version.ref = "newplayer" }
|
||||||
|
okhttp-android = { group = "com.squareup.okhttp3", name = "okhttp-android", version.ref = "okhttpAndroid" }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package net.newpipe.newplayer
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.net.Uri
|
||||||
|
|
||||||
|
interface MediaRepository {
|
||||||
|
suspend fun getTitle(item: String) : String
|
||||||
|
suspend fun getChannelName(item: String): String
|
||||||
|
suspend fun getThumbnail(item: String): Bitmap
|
||||||
|
|
||||||
|
suspend fun getAvailableStreams(item: String): List<String>
|
||||||
|
|
||||||
|
suspend fun getStream(item: String, streamSelector: String) : String
|
||||||
|
suspend fun getLinkWithStreamOffset(item: String) : String
|
||||||
|
|
||||||
|
suspend fun getPreviewThumbnails(item: String) : List<Bitmap>
|
||||||
|
suspend fun getChapters(item: String): List<Long>
|
||||||
|
suspend fun getChapterThumbnail(item: String, chapter: Long) : Bitmap
|
||||||
|
}
|
|
@ -24,53 +24,97 @@ import android.app.Application
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
import androidx.media3.exoplayer.ExoPlayer
|
import androidx.media3.exoplayer.ExoPlayer
|
||||||
|
import java.lang.Exception
|
||||||
|
|
||||||
|
enum class PlayMode {
|
||||||
|
EMBEDDED_VIDEO,
|
||||||
|
FULLSCREEN_VIDEO,
|
||||||
|
PIP,
|
||||||
|
BACKGROND,
|
||||||
|
AUDIO_FORGROUND,
|
||||||
|
}
|
||||||
|
|
||||||
interface NewPlayer {
|
interface NewPlayer {
|
||||||
val player: Player
|
val internal_player: Player
|
||||||
var playWhenReady: Boolean
|
var playWhenReady: Boolean
|
||||||
|
val duartion: Long
|
||||||
|
val bufferedPercentage: Int
|
||||||
|
val repository: MediaRepository
|
||||||
|
var currentPosition: Long
|
||||||
|
var fastSeekAmountSec: Long
|
||||||
|
var playBackMode: PlayMode
|
||||||
|
var playList: MutableList<String>
|
||||||
|
|
||||||
fun prepare()
|
fun prepare()
|
||||||
fun play()
|
fun play()
|
||||||
fun pause()
|
fun pause()
|
||||||
|
fun fastSeekForward()
|
||||||
|
fun fastSeekBackward()
|
||||||
|
fun addToPlaylist(newItem: String)
|
||||||
|
fun addListener(callbackListener: Listener)
|
||||||
|
|
||||||
//TODO: This is only temporary
|
//TODO: This is only temporary
|
||||||
fun setStream(uri: String)
|
fun setStream(uri: String)
|
||||||
|
|
||||||
data class Builder(val app: Application) {
|
data class Builder(val app: Application, val repository: MediaRepository) {
|
||||||
fun build(): NewPlayer {
|
fun build(): NewPlayer {
|
||||||
return NewPlayerImpl(ExoPlayer.Builder(app).build())
|
return NewPlayerImpl(ExoPlayer.Builder(app).build(), repository = repository)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Listener {
|
||||||
|
fun onError(exception: Exception)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NewPlayerImpl(internal_player: Player) : NewPlayer {
|
class NewPlayerImpl(override val internal_player: Player, override val repository: MediaRepository) : NewPlayer {
|
||||||
override val player = internal_player
|
override val duartion: Long = internal_player.duration
|
||||||
|
override val bufferedPercentage: Int = internal_player.bufferedPercentage
|
||||||
|
override var currentPosition: Long = internal_player.currentPosition
|
||||||
|
override var fastSeekAmountSec: Long = 100
|
||||||
|
override var playBackMode: PlayMode = PlayMode.EMBEDDED_VIDEO
|
||||||
|
override var playList: MutableList<String> = ArrayList<String>()
|
||||||
|
|
||||||
override var playWhenReady: Boolean
|
override var playWhenReady: Boolean
|
||||||
set(value) {
|
set(value) {
|
||||||
player.playWhenReady = value
|
internal_player.playWhenReady = value
|
||||||
}
|
}
|
||||||
get() = player.playWhenReady
|
get() = internal_player.playWhenReady
|
||||||
|
|
||||||
override fun prepare() {
|
override fun prepare() {
|
||||||
player.prepare()
|
internal_player.prepare()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun play() {
|
override fun play() {
|
||||||
player.play()
|
internal_player.play()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pause() {
|
override fun pause() {
|
||||||
player.pause()
|
internal_player.pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fastSeekForward() {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fastSeekBackward() {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addToPlaylist(newItem: String) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addListener(callbackListener: NewPlayer.Listener) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun setStream(uri: String) {
|
override fun setStream(uri: String) {
|
||||||
if (player.playbackState == Player.STATE_IDLE) {
|
if (internal_player.playbackState == Player.STATE_IDLE) {
|
||||||
player.prepare()
|
internal_player.prepare()
|
||||||
}
|
}
|
||||||
|
|
||||||
player.setMediaItem(MediaItem.fromUri(uri))
|
internal_player.setMediaItem(MediaItem.fromUri(uri))
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -70,7 +70,7 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
||||||
override val uiState = mutableUiState.asStateFlow()
|
override val uiState = mutableUiState.asStateFlow()
|
||||||
|
|
||||||
override val player: Player?
|
override val player: Player?
|
||||||
get() = newPlayer?.player
|
get() = newPlayer?.internal_player
|
||||||
|
|
||||||
override var minContentRatio: Float = 4F / 3F
|
override var minContentRatio: Float = 4F / 3F
|
||||||
set(value) {
|
set(value) {
|
||||||
|
|
|
@ -104,6 +104,7 @@ dependencies {
|
||||||
|
|
||||||
// development impl
|
// development impl
|
||||||
implementation(project(":new-player"))
|
implementation(project(":new-player"))
|
||||||
|
implementation(libs.okhttp.android)
|
||||||
|
|
||||||
//jitpack test
|
//jitpack test
|
||||||
//implementation(libs.newplayer)
|
//implementation(libs.newplayer)
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
package net.newpipe.newplayer.testapp
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.media.Image
|
||||||
|
import android.net.Uri
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import net.newpipe.newplayer.MediaRepository
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
|
||||||
|
class TestMediaRepository(val context: Context) : MediaRepository {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
private fun get(url: String): Response {
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.build()
|
||||||
|
return client.newCall(request).execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getTitle(item: String) =
|
||||||
|
when (item) {
|
||||||
|
"6502" -> "Reverse engineering the MOS 6502"
|
||||||
|
"portrait" -> "Imitating generative AI videos"
|
||||||
|
"imu" -> "Intel Management Engine deep dive "
|
||||||
|
else -> throw Exception("Unknown stream: $item")
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getChannelName(item: String) =
|
||||||
|
when (item) {
|
||||||
|
"6502" -> "27c3"
|
||||||
|
"portrait" -> "无所吊谓~"
|
||||||
|
"imu" -> "36c3"
|
||||||
|
else -> throw Exception("Unknown stream: $item")
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getThumbnail(item: String) =
|
||||||
|
when (item) {
|
||||||
|
"6502" -> withContext(Dispatchers.IO) {
|
||||||
|
val response =
|
||||||
|
get("https://static.media.ccc.de/media/congress/2010/27c3-4159-en-reverse_engineering_mos_6502_preview.jpg")
|
||||||
|
|
||||||
|
BitmapFactory.decodeStream(response.body.byteStream())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
"portrait" -> withContext(Dispatchers.IO) {
|
||||||
|
val response =
|
||||||
|
get("https://64.media.tumblr.com/13f7e4065b4c583573a9a3e40750ccf8/9e8cf97a92704864-4b/s540x810/d966c97f755384b46dbe6d5350d35d0e9d4128ad.jpg")
|
||||||
|
|
||||||
|
BitmapFactory.decodeStream(response.body.byteStream())
|
||||||
|
}
|
||||||
|
|
||||||
|
"imu" -> withContext(Dispatchers.IO) {
|
||||||
|
val response =
|
||||||
|
get("https://static.media.ccc.de/media/congress/2019/10694-hd_preview.jpg")
|
||||||
|
|
||||||
|
BitmapFactory.decodeStream(response.body.byteStream())
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> throw Exception("Unknown stream: $item")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override suspend fun getAvailableStreams(item: String): List<String> =
|
||||||
|
when (item) {
|
||||||
|
"6502" -> listOf("576p")
|
||||||
|
"portrait" -> listOf("720p")
|
||||||
|
"imu" -> listOf("1080p", "576p")
|
||||||
|
else -> throw Exception("Unknown stream: $item")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override suspend fun getStream(item: String, streamSelector: String) =
|
||||||
|
when (item) {
|
||||||
|
"6502" -> context.getString(R.string.ccc_6502_video)
|
||||||
|
"portrait" -> context.getString(R.string.portrait_video_example)
|
||||||
|
"imu" -> when(streamSelector) {
|
||||||
|
"1080p" -> context.getString(R.string.ccc_imu_1080_mp4)
|
||||||
|
"576p" -> context.getString(R.string.ccc_imu_576_mp4)
|
||||||
|
else -> throw Exception("Unknown stream selector for $item: $streamSelector")
|
||||||
|
}
|
||||||
|
else -> throw Exception("Unknown stream: $item")
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getLinkWithStreamOffset(item: String): String {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getPreviewThumbnails(item: String): List<Bitmap> {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getChapters(item: String): List<Long> {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getChapterThumbnail(item: String, chapter: Long): Bitmap {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
</resources>
|
|
@ -22,5 +22,14 @@
|
||||||
<resources>
|
<resources>
|
||||||
<string name="ccc_6502_video" translatable="false">https://ftp.fau.de/cdn.media.ccc.de/congress/2010/mp4-h264-HQ/27c3-4159-en-reverse_engineering_mos_6502.mp4</string>
|
<string name="ccc_6502_video" translatable="false">https://ftp.fau.de/cdn.media.ccc.de/congress/2010/mp4-h264-HQ/27c3-4159-en-reverse_engineering_mos_6502.mp4</string>
|
||||||
<string name="ccc_6502_audio" translatable="false">https://ftp.fau.de/cdn.media.ccc.de/congress/2010/ogg-audio-only/27c3-4159-en-reverse_engineering_mos_6502.ogg</string>
|
<string name="ccc_6502_audio" translatable="false">https://ftp.fau.de/cdn.media.ccc.de/congress/2010/ogg-audio-only/27c3-4159-en-reverse_engineering_mos_6502.ogg</string>
|
||||||
<string name="portrait_video_example" translatable="false">https://videos.pexels.com/video-files/5512609/5512609-hd_1080_1920_25fps.mp4</string>
|
|
||||||
|
<string name="portrait_video_example" translatable="false">https://va.media.tumblr.com/tumblr_sh62vjBX0j1z8ckep.mp4</string>
|
||||||
|
|
||||||
|
<string name="ccc_imu_1080_mp4" translatable="false">https://ftp.fau.de/cdn.media.ccc.de/congress/2019/h264-hd/36c3-10694-eng-deu-Intel_Management_Engine_deep_dive_hd.mp4</string>
|
||||||
|
<string name="ccc_imu_576_mp4" translatable="false">https://ftp.fau.de/cdn.media.ccc.de/congress/2019/h264-sd/36c3-10694-eng-deu-Intel_Management_Engine_deep_dive_sd.mp4</string>
|
||||||
|
<string name="ccc_imu_1080_webm" translatable="false">https://ftp.fau.de/cdn.media.ccc.de/congress/2019/webm-hd/36c3-10694-eng-deu-Intel_Management_Engine_deep_dive_webm-hd.webm</string>
|
||||||
|
<string name="ccc_imu_576_webm" translatable="false">https://ftp.fau.de/cdn.media.ccc.de/congress/2019/webm-sd/36c3-10694-eng-deu-Intel_Management_Engine_deep_dive_webm-sd.webm</string>
|
||||||
|
<string name="ccc_imu_mp3" translatable="false">https://ftp.fau.de/cdn.media.ccc.de/congress/2019/mp3/36c3-10694-eng-Intel_Management_Engine_deep_dive_mp3.mp3</string>
|
||||||
|
<string name="ccc_imu_opus" translatable="false">https://ftp.fau.de/cdn.media.ccc.de/congress/2019/opus/36c3-10694-eng-Intel_Management_Engine_deep_dive_opus.opus</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in New Issue