diff --git a/tvmon-app/app/src/main/AndroidManifest.xml b/tvmon-app/app/src/main/AndroidManifest.xml
index eca769d..74543c9 100644
--- a/tvmon-app/app/src/main/AndroidManifest.xml
+++ b/tvmon-app/app/src/main/AndroidManifest.xml
@@ -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">
diff --git a/tvmon-app/app/src/main/java/com/example/tvmon/data/scraper/TvmonScraper.kt b/tvmon-app/app/src/main/java/com/example/tvmon/data/scraper/TvmonScraper.kt
index 59ac797..6d2dd03 100644
--- a/tvmon-app/app/src/main/java/com/example/tvmon/data/scraper/TvmonScraper.kt
+++ b/tvmon-app/app/src/main/java/com/example/tvmon/data/scraper/TvmonScraper.kt
@@ -410,6 +410,7 @@ class TvmonScraper {
val episodes = mutableListOf()
val videoLinks = mutableListOf()
val seenEpisodeIds = mutableSetOf()
+ val seenNumbers = mutableSetOf()
val seriesId = seriesUrl.substringAfterLast("/").substringBefore("?")
@@ -437,14 +438,18 @@ val episodes = mutableListOf()
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()
videoLinks.add(VideoLink(
type = "play_page",
url = fullUrl,
- title = linkText.ifBlank { episodeTitle }
+ title = linkText.ifBlank { finalNumber }
))
}
diff --git a/tvmon-app/app/src/main/java/com/example/tvmon/ui/main/MainFragment.kt b/tvmon-app/app/src/main/java/com/example/tvmon/ui/main/MainFragment.kt
index 9febf75..fcebe05 100644
--- a/tvmon-app/app/src/main/java/com/example/tvmon/ui/main/MainFragment.kt
+++ b/tvmon-app/app/src/main/java/com/example/tvmon/ui/main/MainFragment.kt
@@ -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()
+ 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)
+ }
+ }
+ }
}
diff --git a/tvmon-app/app/src/main/java/com/example/tvmon/ui/search/SearchActivity.kt b/tvmon-app/app/src/main/java/com/example/tvmon/ui/search/SearchActivity.kt
index 000f393..8b9477e 100644
--- a/tvmon-app/app/src/main/java/com/example/tvmon/ui/search/SearchActivity.kt
+++ b/tvmon-app/app/src/main/java/com/example/tvmon/ui/search/SearchActivity.kt
@@ -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
@@ -16,26 +15,23 @@ import kotlinx.coroutines.launch
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() {
searchView = findViewById(R.id.search_view)
recyclerView = findViewById(R.id.search_results)
@@ -43,34 +39,44 @@ 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
}
})
searchView.requestFocus()
}
-
+
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)
+ }
}
}
}
-
+
private fun openDetail(content: Content) {
val intent = Intent(this, DetailsActivity::class.java).apply {
putExtra(DetailsActivity.EXTRA_CONTENT, content)
diff --git a/tvmon-app/app/src/main/res/drawable/search_background.xml b/tvmon-app/app/src/main/res/drawable/search_background.xml
new file mode 100644
index 0000000..d6d54f9
--- /dev/null
+++ b/tvmon-app/app/src/main/res/drawable/search_background.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
diff --git a/tvmon-app/app/src/main/res/layout/activity_search.xml b/tvmon-app/app/src/main/res/layout/activity_search.xml
index e2b68f8..9c6c1ad 100644
--- a/tvmon-app/app/src/main/res/layout/activity_search.xml
+++ b/tvmon-app/app/src/main/res/layout/activity_search.xml
@@ -3,18 +3,20 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:padding="16dp">
-
+ android:background="@color/default_background"
+ android:padding="32dp">
+
-
+
+ android:layout_marginTop="24dp" />
+
diff --git a/version.json b/version.json
index 7244dcc..18d175a 100644
--- a/version.json
+++ b/version.json
@@ -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"
-}
\ No newline at end of file
+ "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"
+}