버그 수정 및 최적화

This commit is contained in:
tvmon-dev
2026-04-16 19:55:32 +09:00
parent 626517c9ef
commit f864ea6d1c
5 changed files with 148 additions and 196 deletions

View File

@@ -16,7 +16,11 @@ import com.example.tvmon.data.scraper.TvmonScraper
import com.example.tvmon.ui.detail.DetailsActivity import com.example.tvmon.ui.detail.DetailsActivity
import com.example.tvmon.ui.presenter.ContentCardPresenter import com.example.tvmon.ui.presenter.ContentCardPresenter
import com.example.tvmon.ui.settings.SettingsActivity import com.example.tvmon.ui.settings.SettingsActivity
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
class MainFragment : BrowseSupportFragment(), OnItemViewClickedListener, OnItemViewSelectedListener { class MainFragment : BrowseSupportFragment(), OnItemViewClickedListener, OnItemViewSelectedListener {
@@ -96,10 +100,26 @@ private fun setupUI() {
var successCount = 0 var successCount = 0
var failCount = 0 var failCount = 0
for (category in TvmonScraper.CATEGORIES.values) { val deferredRows = coroutineScope {
TvmonScraper.CATEGORIES.values.map { category ->
Log.w(TAG, "Loading category: ${category.key} - ${category.name}") Log.w(TAG, "Loading category: ${category.key} - ${category.name}")
val success = loadCategoryWithCache(category) async {
if (success) successCount++ else failCount++ val row = loadCategoryRow(category)
category.key to row
}
}
}
for (deferred in deferredRows) {
val (key, row) = deferred.await()
if (row != null) {
activity?.runOnUiThread {
rowsAdapter.add(row)
}
successCount++
} else {
failCount++
}
} }
Log.w(TAG, "=== loadCategories COMPLETE: success=$successCount, fail=$failCount ===") Log.w(TAG, "=== loadCategories COMPLETE: success=$successCount, fail=$failCount ===")
@@ -114,6 +134,34 @@ addSettingsRow()
} }
} }
private suspend fun loadCategoryRow(category: com.example.tvmon.data.model.Category): ListRow? {
return try {
Log.w(TAG, "loadCategoryRow: ${category.key}")
val result = categoryCacheRepository.getCategoryWithCache(category.key, page = 1)
if (result.success && result.items.isNotEmpty()) {
val listRowAdapter = ArrayObjectAdapter(ContentCardPresenter())
result.items.forEach { content ->
listRowAdapter.add(content)
}
val header = HeaderItem(category.name)
val row = ListRow(header, listRowAdapter)
categoryRowAdapters[category.key] = listRowAdapter
Log.w(TAG, "Prepared ROW: ${category.name} with ${result.items.size} items (fromCache=${result.fromCache})")
row
} else {
Log.w(TAG, "No items for ${category.key}")
null
}
} catch (e: Exception) {
Log.e(TAG, "ERROR loading ${category.key}: ${e.javaClass.simpleName}: ${e.message}", e)
null
}
}
private fun addSettingsRow() { private fun addSettingsRow() {
val settingsAdapter = ArrayObjectAdapter(ContentCardPresenter()) val settingsAdapter = ArrayObjectAdapter(ContentCardPresenter())
settingsAdapter.add(SettingsItem()) settingsAdapter.add(SettingsItem())
@@ -186,11 +234,17 @@ Log.w(TAG, "Unknown item type: ${item?.javaClass?.simpleName}")
rowViewHolder: RowPresenter.ViewHolder?, rowViewHolder: RowPresenter.ViewHolder?,
row: Row? row: Row?
) { ) {
// Early return for null items
if (item == null) return
// Early return for non-Content items (e.g., SettingsItem)
if (item !is Content) return
if (row is ListRow) { if (row is ListRow) {
val rowIndex = rowsAdapter.indexOf(row) val rowIndex = rowsAdapter.indexOf(row)
if (rowIndex >= 0) { if (rowIndex >= 0) {
val categoryKey = findCategoryKeyForRow(rowIndex) val categoryKey = findCategoryKeyForRow(rowIndex)
if (categoryKey != null && item is Content) { if (categoryKey != null) {
checkAndLoadMore(categoryKey) checkAndLoadMore(categoryKey)
} }
} }

View File

@@ -183,38 +183,6 @@ runOnUiThread {
loadingOverlay.visibility = View.GONE loadingOverlay.visibility = View.GONE
} }
} }
@android.webkit.JavascriptInterface
fun isVideoPlaying() {
webView.evaluateJavascript(
"""
(function() {
var videos = document.querySelectorAll('video');
var playing = false;
videos.forEach(function(v) {
if (!v.paused && v.currentTime > 0) {
playing = true;
}
});
return playing ? 'playing' : 'not_playing';
})();
""".trimIndent()
) { result ->
android.util.Log.i("PlaybackActivity", "Video playing status: $result")
if (result == "\"playing\"") {
runOnUiThread {
loadingOverlay.visibility = View.GONE
}
}
}
}
@android.webkit.JavascriptInterface
fun onVideoPlay() {
runOnUiThread {
loadingOverlay.visibility = View.GONE
android.util.Log.d("PlaybackActivity", "Video started playing")
}
}
}, "Android") }, "Android")
val cookieManager = CookieManager.getInstance() val cookieManager = CookieManager.getInstance()
@@ -279,6 +247,7 @@ return super.shouldInterceptRequest(view, request)
override fun onPageStarted(view: WebView?, url: String?, favicon: android.graphics.Bitmap?) { override fun onPageStarted(view: WebView?, url: String?, favicon: android.graphics.Bitmap?) {
super.onPageStarted(view, url, favicon) super.onPageStarted(view, url, favicon)
handler.removeCallbacksAndMessages(null)
runOnUiThread { runOnUiThread {
loadingOverlay.visibility = View.VISIBLE loadingOverlay.visibility = View.VISIBLE
} }
@@ -288,10 +257,8 @@ override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url) super.onPageFinished(view, url)
android.util.Log.i("PlaybackActivity", "Page finished: $url") android.util.Log.i("PlaybackActivity", "Page finished: $url")
// Reduced delays and calls to minimize memory pressure
handler.postDelayed({ handler.postDelayed({
injectFullscreenScript() injectFullscreenScript()
injectEnhancedAutoPlayScript()
}, 300) }, 300)
handler.postDelayed({ handler.postDelayed({
@@ -456,71 +423,6 @@ webView.setLayerType(View.LAYER_TYPE_NONE, null)
}; };
makePlayerFullscreen(); makePlayerFullscreen();
setTimeout(makePlayerFullscreen, 500);
setTimeout(makePlayerFullscreen, 2000);
})();
""".trimIndent(),
null
)
}
private fun injectEnhancedAutoPlayScript() {
webView.evaluateJavascript(
"""
(function() {
console.log('AutoPlay: Starting...');
var tryPlay = function(v) {
console.log('AutoPlay: Trying to play video...');
v.muted = true;
v.setAttribute('playsinline', 'true');
v.play().then(function() {
console.log('AutoPlay: Video play() succeeded');
if (window.Android) {
window.Android.onVideoPlay();
}
}).catch(function(e) {
console.log('AutoPlay: Video play() failed: ' + e.message);
});
};
var initVideo = function(v) {
v.muted = true;
v.setAttribute('playsinline', 'true');
v.setAttribute('webkit-playsinline', 'true');
v.addEventListener('canplaythrough', function() {
console.log('AutoPlay: canplaythrough event fired');
tryPlay(v);
}, {once: true});
v.addEventListener('playing', function() {
console.log('AutoPlay: Video is now playing');
if (window.Android) {
window.Android.onVideoPlay();
}
}, {once: true});
if (v.readyState >= 2) {
tryPlay(v);
}
};
var videos = document.querySelectorAll('video');
console.log('AutoPlay: Found ' + videos.length + ' videos');
videos.forEach(initVideo);
var iframes = document.querySelectorAll('iframe');
iframes.forEach(function(f) {
try {
var innerDoc = f.contentDocument || f.contentWindow.document;
var innerVideos = innerDoc.querySelectorAll('video');
innerVideos.forEach(initVideo);
} catch(e) {
console.log('AutoPlay: Cannot access iframe');
}
});
})(); })();
""".trimIndent(), """.trimIndent(),
null null
@@ -533,9 +435,8 @@ setTimeout(makePlayerFullscreen, 2000);
if ((keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER) if ((keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER)
&& action == KeyEvent.ACTION_DOWN) { && action == KeyEvent.ACTION_DOWN) {
android.util.Log.d("PlaybackActivity", "OK button pressed, triggering enhanced autoplay") android.util.Log.d("PlaybackActivity", "OK button pressed")
simulateCenterClick() simulateCenterClick()
injectEnhancedAutoPlayScript()
toggleVideoPlayback() toggleVideoPlayback()
return true return true
} }

View File

@@ -17,8 +17,8 @@ data class VersionInfo(
object UpdateChecker { object UpdateChecker {
private const val TAG = "UpdateChecker" private const val TAG = "UpdateChecker"
private const val VERSION_JSON_URL = "https://tvmon.site/version.json" private const val VERSION_JSON_URL = "https://git.webpluss.net/sanjeok77/tvmon_release/releases/download/version/version.json"
private const val APK_BASE_URL = "https://tvmon.site/releases" private const val APK_BASE_URL = "https://git.webpluss.net/sanjeok77/tvmon_release/releases/download"
fun getCurrentVersionCode(context: Context): Int { fun getCurrentVersionCode(context: Context): Int {
return try { return try {

View File

@@ -5,43 +5,36 @@ android:height="108dp"
android:viewportWidth="108" android:viewportWidth="108"
android:viewportHeight="108"> android:viewportHeight="108">
<!-- TV Screen with white color --> <!-- TV Screen outline - white stroke style for visibility -->
<path <path
android:fillColor="#FFFFFF" android:fillColor="#FFFFFF"
android:pathData="M34,38h40v28h-40z"/> android:pathData="M26,26h56v40h-56z" />
<!-- TV Stand --> <!-- Inner screen area (transparent to show background) -->
<path <path
android:fillColor="#FFFFFF" android:fillColor="#00000000"
android:pathData="M44,70h20v4h-20z"/> android:strokeColor="#E50914"
<path android:strokeWidth="2"
android:fillColor="#FFFFFF" android:pathData="M30,30h48v32h-48z" />
android:pathData="M40,74h28v2h-28z"/>
<!-- Play Button - using Netflix red --> <!-- Play Button - white triangle centered in screen -->
<path <path
android:fillColor="#E50914" android:fillColor="#FFFFFF"
android:pathData="M50,44l12,8l-12,8z"/> android:pathData="M46,38 L46,56 L62,47 Z" />
<!-- tvmon Text - white --> <!-- TV Stand - left leg -->
<!-- Letter 't' -->
<path <path
android:fillColor="#FFFFFF" android:fillColor="#FFFFFF"
android:pathData="M38,82h4v-2h-4v-2h6v10h-2v-6h-4z"/> android:pathData="M38,66 L42,66 L42,76 L38,76 Z" />
<!-- Letter 'v' -->
<!-- TV Stand - right leg -->
<path <path
android:fillColor="#FFFFFF" android:fillColor="#FFFFFF"
android:pathData="M45,78h2l2,6l2,-6h2l-3,8h-2z"/> android:pathData="M66,66 L70,66 L70,76 L70,76 Z" />
<!-- Letter 'm' -->
<!-- TV Stand base -->
<path <path
android:fillColor="#FFFFFF" android:fillColor="#FFFFFF"
android:pathData="M54,86v-8h2v1c0.5,-0.7 1.2,-1 2,-1c0.9,0 1.6,0.4 2,1.1c0.5,-0.7 1.3,-1.1 2.2,-1.1c1.6,0 2.8,1.2 2.8,2.8v5.2h-2v-5c0,-0.9 -0.7,-1.5 -1.5,-1.5c-0.9,0 -1.5,0.6 -1.5,1.5v5h-2v-5c0,-0.9 -0.7,-1.5 -1.5,-1.5c-0.9,0 -1.5,0.6 -1.5,1.5v5z"/> android:pathData="M34,76 L74,76 L74,80 L34,80 Z" />
<!-- Letter 'o' -->
<path
android:fillColor="#FFFFFF"
android:pathData="M66,82c0,-2.2 1.8,-4 4,-4s4,1.8 4,4s-1.8,4 -4,4s-4,-1.8 -4,-4zm2,0c0,1.1 0.9,2 2,2s2,-0.9 2,-2s-0.9,-2 -2,-2s-2,0.9 -2,2z"/>
<!-- Letter 'n' -->
<path
android:fillColor="#FFFFFF"
android:pathData="M76,86v-8h2v1c0.6,-0.7 1.4,-1 2.3,-1c1.9,0 3.2,1.3 3.2,3.2v4.8h-2v-4.5c0,-1 -0.7,-1.7 -1.7,-1.7c-1,0 -1.8,0.7 -1.8,1.7v4.5z"/>
</vector> </vector>

View File

@@ -5,4 +5,8 @@
<certificates src="system" /> <certificates src="system" />
</trust-anchors> </trust-anchors>
</base-config> </base-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">tvmon.site</domain>
<domain includeSubdomains="true">git.webpluss.net</domain>
</domain-config>
</network-security-config> </network-security-config>