make initial logic for NewPlayer and MediaReop
This commit is contained in:
parent
ea099253a1
commit
47ad16c03d
|
@ -31,17 +31,18 @@ interface MediaRepository {
|
||||||
suspend fun getTitle(item: String) : String
|
suspend fun getTitle(item: String) : String
|
||||||
suspend fun getChannelName(item: String): String
|
suspend fun getChannelName(item: String): String
|
||||||
suspend fun getThumbnail(item: String): Thumbnail
|
suspend fun getThumbnail(item: String): Thumbnail
|
||||||
suspend fun getAvailableStreamVariants(item: String): List<String>
|
|
||||||
suspend fun getAvailableSubtitleVariants(item: String): List<String>
|
|
||||||
|
|
||||||
|
suspend fun getAvailableStreamVariants(item: String): List<String>
|
||||||
suspend fun getStream(item: String, streamSelector: String) : Uri
|
suspend fun getStream(item: String, streamSelector: String) : Uri
|
||||||
suspend fun getSubtitle(item: String, )
|
|
||||||
|
suspend fun getAvailableSubtitleVariants(item: String): List<String>
|
||||||
|
suspend fun getSubtitle(item: String, variant: String): Uri
|
||||||
|
|
||||||
suspend fun getPreviewThumbnails(item: String) : HashMap<Long, Thumbnail>?
|
suspend fun getPreviewThumbnails(item: String) : HashMap<Long, Thumbnail>?
|
||||||
suspend fun getChapters(item: String): List<Chapter>
|
suspend fun getChapters(item: String): List<Chapter>
|
||||||
suspend fun getChapterThumbnail(item: String, chapter: Long) : Thumbnail
|
suspend fun getChapterThumbnail(item: String, chapter: Long) : Thumbnail?
|
||||||
|
|
||||||
suspend fun getTimestampLink(item: String, timestampInSeconds: Long)
|
suspend fun getTimestampLink(item: String, timestampInSeconds: Long): String
|
||||||
|
|
||||||
suspend fun tryAndRescueError(item: String?, exception: PlaybackException) : Uri?
|
suspend fun tryAndRescueError(item: String?, exception: PlaybackException) : Uri?
|
||||||
}
|
}
|
|
@ -128,7 +128,7 @@ class NewPlayerImpl(
|
||||||
override var fastSeekAmountSec: Int = 10
|
override var fastSeekAmountSec: Int = 10
|
||||||
override var playBackMode: PlayMode = PlayMode.EMBEDDED_VIDEO
|
override var playBackMode: PlayMode = PlayMode.EMBEDDED_VIDEO
|
||||||
|
|
||||||
private var playerScope = CoroutineScope(Dispatchers.Default + Job())
|
private var playerScope = CoroutineScope(Dispatchers.Main + Job())
|
||||||
|
|
||||||
override var playMode = MutableStateFlow<PlayMode?>(null)
|
override var playMode = MutableStateFlow<PlayMode?>(null)
|
||||||
|
|
||||||
|
@ -192,7 +192,7 @@ class NewPlayerImpl(
|
||||||
|
|
||||||
override fun playStream(item: String, streamVariant: String, playMode: PlayMode) {
|
override fun playStream(item: String, streamVariant: String, playMode: PlayMode) {
|
||||||
launchJobAndCollectError {
|
launchJobAndCollectError {
|
||||||
val stream = toMediaItem(item)
|
val stream = toMediaItem(item, streamVariant)
|
||||||
internalPlayStream(stream, playMode)
|
internalPlayStream(stream, playMode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,6 +202,8 @@ class NewPlayerImpl(
|
||||||
internalPlayer.prepare()
|
internalPlayer.prepare()
|
||||||
}
|
}
|
||||||
this.playMode.update { playMode }
|
this.playMode.update { playMode }
|
||||||
|
this.internalPlayer.setMediaItem(mediaItem)
|
||||||
|
this.internalPlayer.play()
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun toMediaItem(item: String, streamVariant: String): MediaItem {
|
private suspend fun toMediaItem(item: String, streamVariant: String): MediaItem {
|
||||||
|
|
|
@ -98,7 +98,8 @@ enum class UIModeState {
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromPlayMode(playMode: PlayMode) =
|
fun fromPlayMode(playMode: PlayMode?) =
|
||||||
|
if (playMode != null)
|
||||||
when (playMode) {
|
when (playMode) {
|
||||||
PlayMode.EMBEDDED_VIDEO -> EMBEDDED_VIDEO
|
PlayMode.EMBEDDED_VIDEO -> EMBEDDED_VIDEO
|
||||||
PlayMode.FULLSCREEN_VIDEO -> FULLSCREEN_VIDEO
|
PlayMode.FULLSCREEN_VIDEO -> FULLSCREEN_VIDEO
|
||||||
|
@ -106,5 +107,7 @@ enum class UIModeState {
|
||||||
PlayMode.BACKGROUND -> TODO()
|
PlayMode.BACKGROUND -> TODO()
|
||||||
PlayMode.AUDIO_FOREGROUND -> TODO()
|
PlayMode.AUDIO_FOREGROUND -> TODO()
|
||||||
}
|
}
|
||||||
|
else PLACEHOLDER
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -28,6 +28,7 @@ import android.util.Log
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.core.content.ContextCompat.getSystemService
|
import androidx.core.content.ContextCompat.getSystemService
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.SavedStateHandle
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
|
@ -77,7 +78,7 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
||||||
override var newPlayer: NewPlayer? = null
|
override var newPlayer: NewPlayer? = null
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
installExoPlayer()
|
installNewPlayer()
|
||||||
}
|
}
|
||||||
|
|
||||||
override val uiState = mutableUiState.asStateFlow()
|
override val uiState = mutableUiState.asStateFlow()
|
||||||
|
@ -118,7 +119,7 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun installExoPlayer() {
|
private fun installNewPlayer() {
|
||||||
internalPlayer?.let { player ->
|
internalPlayer?.let { player ->
|
||||||
Log.d(TAG, "Install player: ${player.videoSize.width}")
|
Log.d(TAG, "Install player: ${player.videoSize.width}")
|
||||||
|
|
||||||
|
@ -137,17 +138,29 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
||||||
updateContentRatio(VideoSize.fromMedia3VideoSize(videoSize))
|
updateContentRatio(VideoSize.fromMedia3VideoSize(videoSize))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: This is not correctly applicable for loading indicator
|
||||||
override fun onIsLoadingChanged(isLoading: Boolean) {
|
override fun onIsLoadingChanged(isLoading: Boolean) {
|
||||||
super.onIsLoadingChanged(isLoading)
|
super.onIsLoadingChanged(isLoading)
|
||||||
mutableUiState.update {
|
mutableUiState.update {
|
||||||
it.copy(isLoading = isLoading)
|
it.copy(isLoading = isLoading)
|
||||||
}
|
}
|
||||||
Log.i(
|
|
||||||
TAG, if (isLoading) "Player started loading" else "Player finished loading"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
newPlayer?.let{ newPlayer ->
|
||||||
|
viewModelScope.launch {
|
||||||
|
while(true) {
|
||||||
|
newPlayer.playMode.collect { mode ->
|
||||||
|
println("blub: $mode")
|
||||||
|
mutableUiState.update {
|
||||||
|
it.copy(uiMode = UIModeState.fromPlayMode(mode))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateContentRatio(videoSize: VideoSize) {
|
fun updateContentRatio(videoSize: VideoSize) {
|
||||||
|
|
|
@ -31,6 +31,10 @@ android {
|
||||||
namespace = "net.newpipe.newplayer.testapp"
|
namespace = "net.newpipe.newplayer.testapp"
|
||||||
compileSdk = 34
|
compileSdk = 34
|
||||||
|
|
||||||
|
viewBinding {
|
||||||
|
enable = true
|
||||||
|
}
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
compose = true
|
compose = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ import net.newpipe.newplayer.PlayMode
|
||||||
import net.newpipe.newplayer.VideoPlayerView
|
import net.newpipe.newplayer.VideoPlayerView
|
||||||
import net.newpipe.newplayer.model.VideoPlayerViewModel
|
import net.newpipe.newplayer.model.VideoPlayerViewModel
|
||||||
import net.newpipe.newplayer.model.VideoPlayerViewModelImpl
|
import net.newpipe.newplayer.model.VideoPlayerViewModelImpl
|
||||||
|
import net.newpipe.newplayer.testapp.databinding.ActivityMainBinding
|
||||||
import net.newpipe.newplayer.ui.ContentScale
|
import net.newpipe.newplayer.ui.ContentScale
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -51,20 +52,17 @@ class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
var activityBrainSlug: ActivityBrainSlug? = null
|
var activityBrainSlug: ActivityBrainSlug? = null
|
||||||
|
|
||||||
|
lateinit var binding: ActivityMainBinding
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
setContentView(R.layout.activity_main)
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
val embeddedPlayer = findViewById<VideoPlayerView>(R.id.new_player_video_view)
|
binding.startStreamButton.setOnClickListener {
|
||||||
val startStreamButton = findViewById<Button>(R.id.start_stream_button)
|
|
||||||
val buttonsLayout = findViewById<View>(R.id.buttons_layout)
|
|
||||||
val embeddedPlayerLayout = findViewById<View>(R.id.player_column)
|
|
||||||
val fullscreenPlayer = findViewById<VideoPlayerView>(R.id.fullscreen_player)
|
|
||||||
|
|
||||||
startStreamButton.setOnClickListener {
|
|
||||||
newPlayer.playWhenReady = true
|
newPlayer.playWhenReady = true
|
||||||
newPlayer.playStream(getString(R.string.ccc_6502_video), PlayMode.EMBEDDED_VIDEO)
|
newPlayer.playStream("6502", PlayMode.EMBEDDED_VIDEO)
|
||||||
}
|
}
|
||||||
|
|
||||||
videoPlayerViewModel.newPlayer = newPlayer
|
videoPlayerViewModel.newPlayer = newPlayer
|
||||||
|
@ -72,10 +70,10 @@ class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
activityBrainSlug = ActivityBrainSlug(videoPlayerViewModel)
|
activityBrainSlug = ActivityBrainSlug(videoPlayerViewModel)
|
||||||
activityBrainSlug?.let {
|
activityBrainSlug?.let {
|
||||||
it.embeddedPlayerView = embeddedPlayer
|
it.embeddedPlayerView = binding.embeddedPlayer
|
||||||
it.addViewToHideOnFullscreen(buttonsLayout)
|
it.addViewToHideOnFullscreen(binding.buttonsLayout as View)
|
||||||
it.addViewToHideOnFullscreen(embeddedPlayerLayout)
|
it.addViewToHideOnFullscreen(binding.embeddedPlayerLayout as View)
|
||||||
it.fullscreenPlayerView = fullscreenPlayer
|
it.fullscreenPlayerView = binding.fullscreenPlayer
|
||||||
it.rootView = findViewById(R.id.main)
|
it.rootView = findViewById(R.id.main)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,22 @@
|
||||||
package net.newpipe.newplayer.testapp
|
package net.newpipe.newplayer.testapp
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import android.util.Log
|
||||||
import dagger.hilt.android.HiltAndroidApp
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import net.newpipe.newplayer.NewPlayer
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private const val TAG = "NewPlayerApp"
|
||||||
|
|
||||||
@HiltAndroidApp
|
@HiltAndroidApp
|
||||||
class NewPlayerApp : Application()
|
class NewPlayerApp : Application() {
|
||||||
|
val appScope = CoroutineScope(Dispatchers.Default + Job())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,19 +21,33 @@
|
||||||
package net.newpipe.newplayer.testapp
|
package net.newpipe.newplayer.testapp
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import android.util.Log
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import net.newpipe.newplayer.NewPlayer
|
import net.newpipe.newplayer.NewPlayer
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@InstallIn(SingletonComponent::class)
|
@InstallIn(SingletonComponent::class)
|
||||||
object NewPlayerComponent {
|
object NewPlayerComponent {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideNewPlayer(app: Application) : NewPlayer {
|
fun provideNewPlayer(app: Application) : NewPlayer {
|
||||||
return NewPlayer.Builder(app, TestMediaRepository(app)).build()
|
val player = NewPlayer.Builder(app, TestMediaRepository(app)).build()
|
||||||
|
if(app is NewPlayerApp) {
|
||||||
|
app.appScope.launch {
|
||||||
|
while(true) {
|
||||||
|
player.errorFlow.collect { e ->
|
||||||
|
Log.e("NewPlayerException", e.stackTraceToString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return player
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,6 +6,7 @@ import android.graphics.BitmapFactory
|
||||||
import android.media.Image
|
import android.media.Image
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
|
import androidx.media3.common.PlaybackException
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import net.newpipe.newplayer.Chapter
|
import net.newpipe.newplayer.Chapter
|
||||||
|
@ -65,9 +66,13 @@ class TestMediaRepository(val context: Context) : MediaRepository {
|
||||||
else -> throw Exception("Unknown stream: $item")
|
else -> throw Exception("Unknown stream: $item")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getAvailableSubtitleVariants(item: String): List<String> {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override suspend fun getStream(item: String, streamSelector: String) =
|
override suspend fun getStream(item: String, streamSelector: String) =
|
||||||
MediaItem.fromUri(
|
Uri.parse(
|
||||||
when (item) {
|
when (item) {
|
||||||
"6502" -> context.getString(R.string.ccc_6502_video)
|
"6502" -> context.getString(R.string.ccc_6502_video)
|
||||||
"portrait" -> context.getString(R.string.portrait_video_example)
|
"portrait" -> context.getString(R.string.portrait_video_example)
|
||||||
|
@ -81,9 +86,13 @@ class TestMediaRepository(val context: Context) : MediaRepository {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
override suspend fun getLinkWithStreamOffset(item: String): String {
|
override suspend fun getSubtitle(item: String, variant: String) =
|
||||||
TODO("Not yet implemented")
|
Uri.parse(
|
||||||
|
when (item) {
|
||||||
|
"imu" -> context.getString(R.string.ccc_imu_subtitles)
|
||||||
|
else -> ""
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
override suspend fun getPreviewThumbnails(item: String): HashMap<Long, Thumbnail>? {
|
override suspend fun getPreviewThumbnails(item: String): HashMap<Long, Thumbnail>? {
|
||||||
val templateUrl = when (item) {
|
val templateUrl = when (item) {
|
||||||
|
@ -97,7 +106,8 @@ class TestMediaRepository(val context: Context) : MediaRepository {
|
||||||
val thumbCount = when (item) {
|
val thumbCount = when (item) {
|
||||||
"6502" -> 312
|
"6502" -> 312
|
||||||
"imu" -> 361
|
"imu" -> 361
|
||||||
else -> throw Exception("Unknown stream: $item") }
|
else -> throw Exception("Unknown stream: $item")
|
||||||
|
}
|
||||||
|
|
||||||
var thumbMap = HashMap<Long, Thumbnail>()
|
var thumbMap = HashMap<Long, Thumbnail>()
|
||||||
|
|
||||||
|
@ -112,11 +122,43 @@ class TestMediaRepository(val context: Context) : MediaRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getChapters(item: String): List<Chapter> {
|
override suspend fun getChapters(item: String) =
|
||||||
TODO("Not yet implemented")
|
when (item) {
|
||||||
|
"6502" -> context.resources.getIntArray(R.array.ccc_6502_chapters)
|
||||||
|
"imu" -> TODO()
|
||||||
|
else -> intArrayOf()
|
||||||
|
}.map {
|
||||||
|
Chapter(it.toLong(), "Dummy Chapter at timestamp $it")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getChapterThumbnail(item: String, chapter: Long): Thumbnail {
|
override suspend fun getChapterThumbnail(item: String, chapter: Long) =
|
||||||
|
when (item) {
|
||||||
|
"6502" -> OnlineThumbnail(
|
||||||
|
String.format(
|
||||||
|
context.getString(R.string.ccc_6502_preview_thumbnails),
|
||||||
|
chapter / (10 * 1000)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
"imu" -> OnlineThumbnail(
|
||||||
|
String.format(
|
||||||
|
context.getString(R.string.ccc_imu_preview_thumbnails),
|
||||||
|
chapter / (10 * 1000)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getTimestampLink(item: String, timestampInSeconds: Long) =
|
||||||
|
when (item) {
|
||||||
|
"6502" -> "${context.getString(R.string.ccc_6502_link)}#t=$timestampInSeconds"
|
||||||
|
"imu" -> "${context.getString(R.string.ccc_imu_link)}#t=$timestampInSeconds"
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override suspend fun tryAndRescueError(item: String?, exception: PlaybackException): Uri? {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -40,7 +40,7 @@
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/player_column"
|
android:id="@+id/embedded_player_layout"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
|
@ -51,7 +51,7 @@
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<net.newpipe.newplayer.VideoPlayerView
|
<net.newpipe.newplayer.VideoPlayerView
|
||||||
android:id="@+id/new_player_video_view"
|
android:id="@+id/embedded_player"
|
||||||
android:name="net.newpipe.newplayer.VideoPlayerFragment"
|
android:name="net.newpipe.newplayer.VideoPlayerFragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
app:layout_constraintHorizontal_weight="1"
|
app:layout_constraintHorizontal_weight="1"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@id/player_column"
|
app:layout_constraintStart_toEndOf="@id/embedded_player_layout"
|
||||||
app:layout_constraintTop_toTopOf="parent" >
|
app:layout_constraintTop_toTopOf="parent" >
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
|
|
@ -38,8 +38,8 @@
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<FrameLayout
|
<LinearLayout
|
||||||
android:id="@+id/player_column"
|
android:id="@+id/embedded_player_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/buttons_layout"
|
app:layout_constraintBottom_toTopOf="@+id/buttons_layout"
|
||||||
|
@ -51,7 +51,7 @@
|
||||||
app:layout_constraintVertical_weight="1">
|
app:layout_constraintVertical_weight="1">
|
||||||
|
|
||||||
<net.newpipe.newplayer.VideoPlayerView
|
<net.newpipe.newplayer.VideoPlayerView
|
||||||
android:id="@+id/new_player_video_view"
|
android:id="@+id/embedded_player"
|
||||||
android:name="net.newpipe.newplayer.VideoPlayerFragment"
|
android:name="net.newpipe.newplayer.VideoPlayerFragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -61,7 +61,7 @@
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
</FrameLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@id/buttons_layout"
|
android:id="@id/buttons_layout"
|
||||||
|
@ -69,7 +69,7 @@
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/player_column"
|
app:layout_constraintTop_toBottomOf="@id/embedded_player_layout"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintVertical_weight="1">
|
app:layout_constraintVertical_weight="1">
|
||||||
|
|
||||||
|
|
|
@ -20,22 +20,28 @@
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<resources>
|
<resources>
|
||||||
|
<!-- "Reverse Engineering the MOS 6502 CPU" a talk from 27c3 -->
|
||||||
|
<string name="ccc_6502_link" translatable="false">https://media.ccc.de/v/27c3-4159-en-reverse_engineering_mos_6502</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_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="ccc_6502_title" translatable="false">Reverse Engineering the MOS 6502 CPU </string>
|
<string name="ccc_6502_title" translatable="false">Reverse Engineering the MOS 6502 CPU </string>
|
||||||
<string name="ccc_6502_channel" translatable="false">Michael Steil</string>
|
<string name="ccc_6502_channel" translatable="false">Michael Steil</string>
|
||||||
<string name="ccc_6402_thumbnail" translatable="false">https://static.media.ccc.de/media/congress/2010/27c3-4159-en-reverse_engineering_mos_6502.jpg</string>
|
<string name="ccc_6502_thumbnail" translatable="false">https://static.media.ccc.de/media/congress/2010/27c3-4159-en-reverse_engineering_mos_6502.jpg</string>
|
||||||
<string name="ccc_6502_preview_thumbnails" translatable="false">https://cloud.newpipe-ev.de/remote.php/dav/public-files/YKGkfBlBNbkavqw/6502/onethirdsize/f000000%03d.jpg</string>
|
<string name="ccc_6502_preview_thumbnails" translatable="false">https://cloud.newpipe-ev.de/remote.php/dav/public-files/YKGkfBlBNbkavqw/6502/onethirdsize/f000000%03d.jpg</string>
|
||||||
<string-array name="ccc_6502_chapters">
|
<integer-array name="ccc_6502_chapters">
|
||||||
<item>600000</item>
|
<item>600000</item>
|
||||||
<item>1200000</item>
|
<item>1200000</item>
|
||||||
<item>1800000</item>
|
<item>1800000</item>
|
||||||
<item>2400000</item>
|
<item>2400000</item>
|
||||||
<item>3600000</item>
|
<item>3600000</item>
|
||||||
</string-array>
|
</integer-array>
|
||||||
|
|
||||||
|
<!-- A thumbler Video. The creators tried to imitate an ai generated video. I found this in a Tom Scott newsletter. -->
|
||||||
<string name="portrait_video_example" translatable="false">https://va.media.tumblr.com/tumblr_sh62vjBX0j1z8ckep.mp4</string>
|
<string name="portrait_video_example" translatable="false">https://va.media.tumblr.com/tumblr_sh62vjBX0j1z8ckep.mp4</string>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- "Intel Management Engine deep dive" a talk from 36c3 -->
|
||||||
|
<string name ="ccc_imu_link" translatable="false">https://media.ccc.de/v/36c3-10694-intel_management_engine_deep_dive</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_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_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_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>
|
||||||
|
@ -46,12 +52,12 @@
|
||||||
<string name="ccc_imu_channel" translatable="false">Peter Bosch</string>
|
<string name="ccc_imu_channel" translatable="false">Peter Bosch</string>
|
||||||
<string name="ccc_imu_thumbnail" translatable="false">https://static.media.ccc.de/media/congress/2019/10694-hd.jpg</string>
|
<string name="ccc_imu_thumbnail" translatable="false">https://static.media.ccc.de/media/congress/2019/10694-hd.jpg</string>
|
||||||
<string name="ccc_imu_preview_thumbnails" translatable="false">https://cloud.newpipe-ev.de/remote.php/dav/public-files/YKGkfBlBNbkavqw/intel_mu/oneeigthsize/f000000%03d.jpg</string>
|
<string name="ccc_imu_preview_thumbnails" translatable="false">https://cloud.newpipe-ev.de/remote.php/dav/public-files/YKGkfBlBNbkavqw/intel_mu/oneeigthsize/f000000%03d.jpg</string>
|
||||||
<string-array name="ccc_imu_chapters">
|
<integer-array name="ccc_imu_chapters">
|
||||||
<item>600000</item>
|
<item>600000</item>
|
||||||
<item>1200000</item>
|
<item>1200000</item>
|
||||||
<item>1800000</item>
|
<item>1800000</item>
|
||||||
<item>2400000</item>
|
<item>2400000</item>
|
||||||
<item>3600000</item>
|
<item>3600000</item>
|
||||||
</string-array>
|
</integer-array>
|
||||||
<string name="ccc_imu_subtitles" translatable="false">https://cdn.media.ccc.de/congress/2019/36c3-10694-eng-deu-Intel_Management_Engine_deep_dive.en.srt</string>
|
<string name="ccc_imu_subtitles" translatable="false">https://cdn.media.ccc.de/congress/2019/36c3-10694-eng-deu-Intel_Management_Engine_deep_dive.en.srt</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in New Issue