Fix episode number matching, add scroll-based preloading, and fix search UI
This commit is contained in:
@@ -58,7 +58,8 @@
|
||||
android:name=".ui.search.SearchActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/search_title"
|
||||
android:theme="@style/Theme.Tvmon.Search">
|
||||
android:screenOrientation="landscape"
|
||||
android:theme="@style/Theme.Leanback">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEARCH" />
|
||||
</intent-filter>
|
||||
|
||||
@@ -410,6 +410,7 @@ class TvmonScraper {
|
||||
val episodes = mutableListOf<Episode>()
|
||||
val videoLinks = mutableListOf<VideoLink>()
|
||||
val seenEpisodeIds = mutableSetOf<String>()
|
||||
val seenNumbers = mutableSetOf<String>()
|
||||
|
||||
val seriesId = seriesUrl.substringAfterLast("/").substringBefore("?")
|
||||
|
||||
@@ -437,14 +438,18 @@ val episodes = mutableListOf<Episode>()
|
||||
|
||||
val episodeNumMatch = Pattern.compile("(\\d+)\\s*화|(\\d+)\\s*회|EP\\.?(\\d+)|제\\s*(\\d+)\\s*부").matcher(linkText)
|
||||
val episodeTitle = if (episodeNumMatch.find()) {
|
||||
episodeNumMatch.group(1) ?: episodeNumMatch.group(2) ?: episodeNumMatch.group(3) ?: episodeNumMatch.group(4) ?: episodeId
|
||||
episodeNumMatch.group(1) ?: episodeNumMatch.group(2) ?: episodeNumMatch.group(3) ?: episodeNumMatch.group(4)
|
||||
} else {
|
||||
episodeId
|
||||
null
|
||||
}
|
||||
|
||||
val finalNumber = episodeTitle ?: episodeId
|
||||
if (finalNumber in seenNumbers) continue
|
||||
seenNumbers.add(finalNumber)
|
||||
|
||||
episodes.add(Episode(
|
||||
number = episodeTitle,
|
||||
title = linkText.ifBlank { episodeTitle },
|
||||
number = finalNumber,
|
||||
title = linkText.ifBlank { finalNumber },
|
||||
url = fullUrl,
|
||||
type = "webview"
|
||||
))
|
||||
@@ -452,7 +457,7 @@ val episodes = mutableListOf<Episode>()
|
||||
videoLinks.add(VideoLink(
|
||||
type = "play_page",
|
||||
url = fullUrl,
|
||||
title = linkText.ifBlank { episodeTitle }
|
||||
title = linkText.ifBlank { finalNumber }
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class MainFragment : BrowseSupportFragment(), OnItemViewClickedListener, OnItemV
|
||||
|
||||
companion object {
|
||||
private const val TAG = "TVMON_MAIN"
|
||||
private const val PRELOAD_THRESHOLD = 5 // 로딩 시작 위치
|
||||
private const val PRELOAD_THRESHOLD = 20
|
||||
}
|
||||
|
||||
private val scraper: TvmonScraper by inject()
|
||||
@@ -38,6 +38,8 @@ class MainFragment : BrowseSupportFragment(), OnItemViewClickedListener, OnItemV
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private var currentSelectedRowIndex = -1
|
||||
private var isDataLoaded = false
|
||||
private var lastPreloadedPage = mutableMapOf<String, Int>()
|
||||
private var currentCategoryKey: String? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -312,8 +314,24 @@ private fun loadNextPage(categoryKey: String, page: Int) {
|
||||
if (row is ListRow) {
|
||||
val rowIndex = rowsAdapter.indexOf(row)
|
||||
if (rowIndex >= 0) {
|
||||
val categoryKey = findCategoryKeyForRow(rowIndex)
|
||||
currentCategoryKey = categoryKey
|
||||
handleRowSelection(rowIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkAndPreload(categoryKey: String, position: Int) {
|
||||
if (position >= PRELOAD_THRESHOLD) {
|
||||
val currentPage = categoryPages[categoryKey] ?: 1
|
||||
val maxPage = categoryMaxPage[categoryKey] ?: 1
|
||||
val lastPreload = lastPreloadedPage[categoryKey] ?: 0
|
||||
|
||||
if (currentPage < maxPage && currentPage > lastPreload) {
|
||||
Log.w(TAG, "checkAndPreload: $categoryKey at position $position, preloading page ${currentPage + 1}")
|
||||
lastPreloadedPage[categoryKey] = currentPage
|
||||
preloadNextPage(categoryKey, currentPage + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.example.tvmon.ui.search
|
||||
|
||||
import android.app.SearchManager
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
@@ -17,23 +16,20 @@ import org.koin.android.ext.android.inject
|
||||
|
||||
class SearchActivity : AppCompatActivity() {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "TVMON_SEARCH"
|
||||
}
|
||||
|
||||
private val scraper: TvmonScraper by inject()
|
||||
private lateinit var searchView: SearchView
|
||||
private lateinit var recyclerView: RecyclerView
|
||||
private lateinit var adapter: SearchResultsAdapter
|
||||
private lateinit var searchView: SearchView
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_search)
|
||||
|
||||
setupUI()
|
||||
|
||||
if (Intent.ACTION_SEARCH == intent.action) {
|
||||
val query = intent.getStringExtra(SearchManager.QUERY)
|
||||
if (!query.isNullOrBlank()) {
|
||||
search(query)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupUI() {
|
||||
@@ -43,19 +39,27 @@ class SearchActivity : AppCompatActivity() {
|
||||
adapter = SearchResultsAdapter { content ->
|
||||
openDetail(content)
|
||||
}
|
||||
|
||||
recyclerView.layoutManager = LinearLayoutManager(this)
|
||||
recyclerView.adapter = adapter
|
||||
|
||||
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||
if (!query.isNullOrBlank()) {
|
||||
search(query)
|
||||
query?.let {
|
||||
if (it.isNotBlank()) {
|
||||
search(it)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String?): Boolean {
|
||||
return false
|
||||
newText?.let {
|
||||
if (it.length >= 2) {
|
||||
search(it)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
@@ -65,8 +69,10 @@ class SearchActivity : AppCompatActivity() {
|
||||
private fun search(query: String) {
|
||||
lifecycleScope.launch {
|
||||
val result = scraper.search(query)
|
||||
if (result.success) {
|
||||
adapter.updateResults(result.results)
|
||||
runOnUiThread {
|
||||
if (result.success) {
|
||||
adapter.updateResults(result.results)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#333333" />
|
||||
<corners android:radius="8dp" />
|
||||
<padding android:left="16dp" android:right="16dp" android:top="12dp" android:bottom="12dp" />
|
||||
</shape>
|
||||
@@ -3,7 +3,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
android:background="@color/default_background"
|
||||
android:padding="32dp">
|
||||
|
||||
<androidx.appcompat.widget.SearchView
|
||||
android:id="@+id/search_view"
|
||||
@@ -16,5 +17,6 @@
|
||||
android:id="@+id/search_results"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="16dp" />
|
||||
android:layout_marginTop="24dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"versionCode": 1,
|
||||
"versionName": "1.0.0",
|
||||
"apkUrl": "https://git.webpluss.net/sanjeok77/NeFLIX_release/releases/download/v1.0.0/app-release.apk",
|
||||
"updateMessage": "tvmon v1.0.0 - Android TV app with pagination fix and cast display improvements"
|
||||
"versionCode": 2,
|
||||
"versionName": "1.0.1",
|
||||
"apkUrl": "https://git.webpluss.net/sanjeok77/tvmon_release/releases/download/v1.0.1/app-release.apk",
|
||||
"updateMessage": "v1.0.1 - Bug fixes"
|
||||
}
|
||||
Reference in New Issue
Block a user