From dae2fa60828eca7b4b39fcc6558701d1636ded41 Mon Sep 17 00:00:00 2001 From: tvmon-dev Date: Wed, 15 Apr 2026 20:45:28 +0900 Subject: [PATCH] Fix episode number matching, add scroll-based preloading, and fix search UI --- tvmon-app/app/src/main/AndroidManifest.xml | 3 +- .../tvmon/data/scraper/TvmonScraper.kt | 15 ++++-- .../com/example/tvmon/ui/main/MainFragment.kt | 20 +++++++- .../example/tvmon/ui/search/SearchActivity.kt | 46 +++++++++++-------- .../main/res/drawable/search_background.xml | 7 +++ .../src/main/res/layout/activity_search.xml | 10 ++-- version.json | 10 ++-- 7 files changed, 75 insertions(+), 36 deletions(-) create mode 100644 tvmon-app/app/src/main/res/drawable/search_background.xml 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" +}