add tv shows support to parser, start implementing account fragment

This commit is contained in:
Jannik 2020-10-11 23:16:47 +02:00
parent b27a218b51
commit 7b708f5aa0
Signed by: Seil0
GPG Key ID: E8459F3723C52C24
18 changed files with 330 additions and 65 deletions

View File

@ -14,6 +14,7 @@ android {
versionName "0.1-alpha1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "build_time", buildTime()
setProperty("archivesBaseName", "teapod-$versionName")
}
@ -62,4 +63,8 @@ dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
static def buildTime() {
return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
}

View File

@ -79,7 +79,7 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS
Log.i(javaClass.name, "please login!")
LoginDialog(this).positiveButton {
EncryptedPreferences.saveCredentials(email, password, context)
EncryptedPreferences.saveCredentials(login, password, context)
}.negativeButton {
Log.i(javaClass.name, "Login canceled, exiting.")
finish()
@ -88,17 +88,14 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS
}
fun showDetailFragment(media: GUIMedia) {
val streams = AoDParser().loadStreams(media.link) // load the streams for the selected media
val streamMedia = AoDParser().loadStreams(media.link) // load the streams for the selected media
val mediaFragment = MediaFragment(media, streams)
val mediaFragment = MediaFragment(media, streamMedia)
supportFragmentManager.commit {
add(R.id.nav_host_fragment, mediaFragment, "MediaFragment")
addToBackStack(null)
show(mediaFragment)
}
println("visible !!!: " + mediaFragment.isVisible)
println(supportFragmentManager.backStackEntryCount)
}
fun startPlayer(streamUrl: String) {

View File

@ -1,11 +1,15 @@
package org.mosad.teapod.parser
import android.util.Log
import com.google.gson.JsonParser
import kotlinx.coroutines.*
import org.jsoup.Connection
import org.jsoup.Jsoup
import org.mosad.teapod.preferences.EncryptedPreferences
import org.mosad.teapod.util.DataTypes.MediaType
import org.mosad.teapod.util.GUIMedia
import org.mosad.teapod.util.StreamMedia
import kotlin.collections.ArrayList
class AoDParser {
@ -60,7 +64,9 @@ class AoDParser {
}
}
// https://www.anime-on-demand.de/animes
/**
* list all animes from the website
*/
fun listAnimes(): ArrayList<GUIMedia> = runBlocking {
if (sessionCookies.isEmpty()) login()
@ -92,12 +98,12 @@ class AoDParser {
/**
* load streams for the media path
*/
fun loadStreams(mediaPath: String): List<String> = runBlocking {
fun loadStreams(mediaPath: String): StreamMedia = runBlocking {
if (sessionCookies.isEmpty()) login()
if (!loginSuccess) {
println("please log in") // TODO
return@runBlocking listOf()
return@runBlocking StreamMedia(MediaType.OTHER)
}
withContext(Dispatchers.Default) {
@ -114,14 +120,20 @@ class AoDParser {
//println("first entry: ${playlists.first()}")
//println("csrf token is: $csrfToken")
return@withContext loadStreamInfo(playlists.first(), csrfToken)
val type = if (res.select("h2").eachText().filter { it == "Episoden" }.any()) {
MediaType.TVSHOW
} else {
MediaType.MOVIE
}
return@withContext loadStreamInfo(playlists.first(), csrfToken, type)
}
}
/**
* load the playlist path and parse it, read the stream info from json
*/
private fun loadStreamInfo(playlistPath: String, csrfToken: String): List<String> = runBlocking {
private fun loadStreamInfo(playlistPath: String, csrfToken: String, type: MediaType): StreamMedia = runBlocking {
withContext(Dispatchers.Default) {
val headers = mutableMapOf(
Pair("Accept", "application/json, text/javascript, */*; q=0.01"),
@ -139,13 +151,37 @@ class AoDParser {
//println(res.body())
// TODO if it's a series there sources for each episode
val sources = JsonParser.parseString(res.body()).asJsonObject
.get("playlist").asJsonArray.first().asJsonObject
.get("sources").asJsonArray
println(type)
return@withContext when (type) {
MediaType.MOVIE -> {
val movie = JsonParser.parseString(res.body()).asJsonObject
.get("playlist").asJsonArray
return@withContext sources.toList().map {
it.asJsonObject.get("file").asString
val streamList = arrayListOf<String>()
movie.first().asJsonObject.get("sources").asJsonArray.toList().forEach {
streamList.add(it.asJsonObject.get("file").asString)
}
StreamMedia(MediaType.MOVIE, streamList)
}
MediaType.TVSHOW -> {
val episodes = JsonParser.parseString(res.body()).asJsonObject
.get("playlist").asJsonArray
val streamList = arrayListOf<String>()
episodes.forEach {
val streamUrl = it.asJsonObject.get("sources").asJsonArray
.first().asJsonObject
.get("file").asString
streamList.add(streamUrl)
}
StreamMedia(MediaType.TVSHOW, streamList)
}
else -> {
Log.e(javaClass.name, "Wrong Type, please report this issue.")
StreamMedia(MediaType.OTHER)
}
}
}
}

View File

@ -1,19 +1,23 @@
package org.mosad.teapod.ui
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import com.bumptech.glide.Glide
import kotlinx.android.synthetic.main.fragment_media.*
import org.mosad.teapod.MainActivity
import org.mosad.teapod.R
import org.mosad.teapod.util.DataTypes.MediaType
import org.mosad.teapod.util.GUIMedia
import java.net.URL
import java.net.URLEncoder
import org.mosad.teapod.util.StreamMedia
class MediaFragment(val media: GUIMedia, val streams: List<String>) : Fragment() {
class MediaFragment(private val guiMedia: GUIMedia, private val streamMedia: StreamMedia) : Fragment() {
private lateinit var adapterEpisodes: ArrayAdapter<String>
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_media, container, false)
@ -22,25 +26,51 @@ class MediaFragment(val media: GUIMedia, val streams: List<String>) : Fragment()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// load poster
Glide.with(requireContext()).load(media.posterLink).into(image_poster)
text_title.text = media.title
text_desc.text = media.shortDesc
// generic gui
Glide.with(requireContext()).load(guiMedia.posterLink).into(image_poster)
text_title.text = guiMedia.title
text_desc.text = guiMedia.shortDesc
println("media streams: $streams")
// specific gui
if (streamMedia.type == MediaType.TVSHOW) {
val episodes = streamMedia.streams.mapIndexed { index, _ ->
"${guiMedia.title} - Ep. ${index + 1}"
}
adapterEpisodes = ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, episodes)
list_episodes.adapter = adapterEpisodes
} else if (streamMedia.type == MediaType.MOVIE) {
list_episodes.visibility = View.GONE
}
println("media streams: ${streamMedia.streams}")
initActions()
}
private fun initActions() {
button_play.setOnClickListener { onClickButtonPlay() }
button_play.setOnClickListener {
onClickButtonPlay()
}
list_episodes.setOnItemClickListener { _, _, position, _ ->
playStream(streamMedia.streams[position])
}
}
private fun onClickButtonPlay() {
println("play ${streams.first()}")
when (streamMedia.type) {
MediaType.MOVIE -> playStream(streamMedia.streams.first())
MediaType.TVSHOW -> playStream(streamMedia.streams.first())
MediaType.OTHER -> Log.e(javaClass.name, "Wrong Type, please report this issue.")
}
}
private fun playStream(url: String) {
val mainActivity = activity as MainActivity
mainActivity.startPlayer(streams.first())
mainActivity.startPlayer(url)
}
}

View File

@ -5,8 +5,12 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.afollestad.materialdialogs.MaterialDialog
import kotlinx.android.synthetic.main.fragment_account.*
import org.mosad.teapod.BuildConfig
import org.mosad.teapod.R
import org.mosad.teapod.preferences.EncryptedPreferences
import org.mosad.teapod.ui.components.LoginDialog
class AccountFragment : Fragment() {
@ -17,6 +21,27 @@ class AccountFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
text_account.text = "This is the Account Fragment"
text_account_login.text = EncryptedPreferences.login
text_info_about_desc.text = getString(R.string.info_about_desc, BuildConfig.VERSION_NAME, getString(R.string.build_time))
initActions()
}
private fun initActions() {
linear_account_login.setOnClickListener {
LoginDialog(requireContext()).positiveButton {
EncryptedPreferences.saveCredentials(login, password, context)
}.show {
login = EncryptedPreferences.login
password = ""
}
}
linear_about.setOnClickListener {
MaterialDialog(requireContext())
.title(R.string.info_about)
.message(R.string.info_about_dialog)
.show()
}
}
}

View File

@ -25,8 +25,6 @@ package org.mosad.teapod.ui.components
import android.content.Context
import android.widget.EditText
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.WhichButton
import com.afollestad.materialdialogs.actions.getActionButton
import com.afollestad.materialdialogs.bottomsheets.BottomSheet
import com.afollestad.materialdialogs.bottomsheets.setPeekHeight
import com.afollestad.materialdialogs.customview.customView
@ -40,7 +38,7 @@ class LoginDialog(val context: Context) {
private val editTextLogin: EditText
private val editTextPassword: EditText
var email = ""
var login = ""
var password = ""
init {
@ -61,7 +59,7 @@ class LoginDialog(val context: Context) {
fun positiveButton(func: LoginDialog.() -> Unit): LoginDialog = apply {
dialog.positiveButton {
email = editTextLogin.text.toString()
login = editTextLogin.text.toString()
password = editTextPassword.text.toString()
func()
@ -81,7 +79,7 @@ class LoginDialog(val context: Context) {
fun show(func: LoginDialog.() -> Unit): LoginDialog = apply {
func()
editTextLogin.setText(email)
editTextLogin.setText(login)
editTextPassword.setText(password)
show()

View File

@ -19,7 +19,5 @@ class HomeFragment : Fragment() {
super.onViewCreated(view, savedInstanceState)
text_home.text = "This is the home fragment"
println("HomeFragment created")
}
}

View File

@ -48,10 +48,8 @@ class SearchFragment : Fragment() {
}
override fun onQueryTextChange(newText: String?): Boolean {
println("new text is: $newText")
adapter.filter.filter(newText)
adapter.notifyDataSetChanged()
return false
}
})

View File

@ -20,10 +20,6 @@ class CustomAdapter(val context: Context, private val originalMedia: ArrayList<G
private var filteredMedia = originalMedia.map { it.copy() }
private val customFilter = CustomFilter()
init {
println("initial filtered size is: ${filteredMedia.size}")
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = convertView ?: LayoutInflater.from(context).inflate(R.layout.linear_media, parent, false)
@ -68,8 +64,6 @@ class CustomAdapter(val context: Context, private val originalMedia: ArrayList<G
results.values = filteredList
results.count = filteredList.size
println("filtered size is: ${results.count}")
return results
}

View File

@ -1,7 +1,17 @@
package org.mosad.teapod.util
class DataTypes {
enum class MediaType {
OTHER,
MOVIE,
TVSHOW
}
}
data class GUIMedia(val title: String, val posterLink: String, val shortDesc : String, val link: String) {
override fun toString(): String {
return title
}
}
}
data class StreamMedia(val type: DataTypes.MediaType, val streams: ArrayList<String> = arrayListOf())

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M8,5v14l11,-7z"/>
</vector>

View File

@ -4,20 +4,144 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fafafa"
android:background="#f5f5f5"
tools:context=".ui.account.AccountFragment">
<TextView
android:id="@+id/text_account"
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:textAlignment="center"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/linear_account"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="12dp"
android:background="#ffffff"
android:orientation="vertical">
<TextView
android:id="@+id/text_account"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="7dp"
android:paddingEnd="7dp"
android:text="@string/account"
android:textColor="@android:color/primary_text_light"
android:textSize="16sp"
android:textStyle="bold" />
<LinearLayout
android:id="@+id/linear_account_login"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/account"
android:minWidth="48dp"
android:minHeight="48dp"
android:padding="5dp"
android:scaleType="fitXY"
android:src="@drawable/ic_baseline_account_box_24"
app:srcCompat="@drawable/ic_baseline_account_box_24" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/text_account_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/account_login_ex"
android:textColor="@android:color/primary_text_light"
android:textSize="16sp" />
<TextView
android:id="@+id/text_account_login_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/account_login_desc"
android:textColor="@android:color/secondary_text_light" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/linear_info"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="12dp"
android:background="#ffffff"
android:orientation="vertical">
<TextView
android:id="@+id/text_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="7dp"
android:paddingEnd="7dp"
android:text="@string/info"
android:textColor="@android:color/primary_text_light"
android:textSize="16sp"
android:textStyle="bold" />
<LinearLayout
android:id="@+id/linear_about"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="48dp"
android:minHeight="48dp"
android:padding="5dp"
android:scaleType="fitXY"
app:srcCompat="@drawable/ic_baseline_info_24" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/text_info_about"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/info_about"
android:textColor="@android:color/primary_text_light"
android:textSize="16sp" />
<TextView
android:id="@+id/text_info_about_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/info_about_desc"
android:textColor="@android:color/secondary_text_light" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fafafa"
android:background="#f5f5f5"
tools:context=".ui.home.HomeFragment">
<TextView

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fafafa"
android:background="#f5f5f5"
tools:context=".ui.library.LibraryFragment">
<ListView

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fafafa"
android:background="#f5f5f5"
tools:context=".ui.MediaFragment">
<androidx.constraintlayout.widget.ConstraintLayout
@ -24,11 +24,21 @@
<Button
android:id="@+id/button_play"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_height="wrap_content"
android:layout_marginStart="7dp"
android:layout_marginTop="25dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="7dp"
android:text="Play"
android:background="#4A4141"
android:drawableStart="@drawable/ic_baseline_play_arrow_24"
android:drawablePadding="10dp"
android:drawableTint="#FFFFFF"
android:gravity="left|center_vertical"
android:paddingStart="160dp"
android:paddingEnd="160dp"
android:text="@string/button_play"
android:textAllCaps="false"
android:textColor="@android:color/primary_text_dark"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/image_poster" />
@ -57,5 +67,13 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_title" />
<ListView
android:id="@+id/list_episodes"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="17dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_desc" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fafafa"
android:background="#f5f5f5"
tools:context=".ui.search.SearchFragment">
<SearchView

View File

@ -8,6 +8,18 @@
<!-- search fragment -->
<string name="search_hint">Search for movies and series</string>
<!-- media fragment -->
<string name="button_play">Play</string>
<!-- settings fragment -->
<string name="account">Account</string>
<string name="account_login_ex">user@example.com</string>
<string name="account_login_desc">Tap to edit</string>
<string name="info">Info</string>
<string name="info_about">Teapod by @Seil0</string>
<string name="info_about_desc" translatable="false">Version %1$s (%2$s)</string>
<string name="info_about_dialog" translatable="false">This software is published under the terms and conditions of GPL 3. For further information visit git.mosad.xyz/Seil0 \n\n© 2020 seil0@mosad.xyz</string>
<!-- dialogs -->
<string name="save">save</string>
<string name="cancel">@android:string/cancel</string>