....
This commit is contained in:
parent
112012ce0c
commit
68c16339ea
@ -81,6 +81,19 @@ port.onMessage.addListener(response => {
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
case "PAUSE_YT": // 5초 뒤로
|
||||
{
|
||||
if (document.location.href.search("youtube") > -1) {
|
||||
let btn =
|
||||
document.querySelector('button[aria-label="동영상 일시중지"]') ;
|
||||
|
||||
if (btn) {
|
||||
btn.click();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
case "SEEK_PREV": // 5초 뒤로
|
||||
{
|
||||
@ -594,6 +607,36 @@ var mainContentsEl = null;
|
||||
let lastState = null; // 이전 상태 저장 (-1, 0, 1)
|
||||
let throttleTimer = null;
|
||||
|
||||
function sendCookiesToNative() {
|
||||
try {
|
||||
const cookies = document.cookie;
|
||||
// 쿠키가 존재할 때만 전송
|
||||
if (cookies && cookies.length > 0) {
|
||||
const netscapeCookies = '# Netscape HTTP Cookie File\\n' +
|
||||
document.cookie.split('; ')
|
||||
.map(c => {
|
||||
const eqIdx = c.indexOf('=');
|
||||
const name = c.substring(0, eqIdx).trim();
|
||||
const value = decodeURIComponent(c.substring(eqIdx + 1));
|
||||
return `.youtube.com\\tTRUE\\t/\\tFALSE\\t0\\t${name}\\t${value}`;
|
||||
})
|
||||
.filter(c => c.includes('youtube') || c.includes('google'))
|
||||
.join('\\n');
|
||||
|
||||
|
||||
|
||||
sendMessage({
|
||||
type: "COOKIES_REPORT",
|
||||
value: netscapeCookies,
|
||||
url: location.href
|
||||
});
|
||||
console.log("Cookies sent to native.");
|
||||
}
|
||||
} catch (e) {
|
||||
// 예외 무시
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
|
||||
const currentUrl = location.href;
|
||||
@ -650,6 +693,13 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
throttleTimer = null;
|
||||
}, 150); // 0.15초 간격으로 체크 (사용성에 따라 조절 가능)
|
||||
};
|
||||
|
||||
if (document.readyState === 'complete') {
|
||||
sendCookiesToNative();
|
||||
} else {
|
||||
// 2. 아직 로딩 중이라면 window.onload(모든 리소스 로드 완료) 시점에 실행
|
||||
window.addEventListener('load', sendCookiesToNative);
|
||||
}
|
||||
})
|
||||
|
||||
const keywords = ["youtube", "mojeek"];
|
||||
|
||||
@ -6,6 +6,7 @@ import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.app.WallpaperManager
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.content.BroadcastReceiver
|
||||
@ -47,14 +48,66 @@ import java.io.File
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.TimeUnit
|
||||
import android.bluetooth.BluetoothClass
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.RectF
|
||||
import android.media.AudioManager
|
||||
import android.os.SystemClock
|
||||
import android.os.VibrationEffect
|
||||
import android.os.Vibrator
|
||||
import android.view.KeyEvent
|
||||
import androidx.annotation.RequiresPermission
|
||||
import androidx.core.content.ContentProviderCompat.requireContext
|
||||
import androidx.work.workDataOf
|
||||
import bums.lunatic.launcher.home.GeckoWeb.Companion.currentCookieString
|
||||
import bums.lunatic.launcher.home.GeckoWeb.Companion.currentCookieUrlString
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
class WallpaperAutoChangeWorker(private val context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
|
||||
override suspend fun doWork(): Result {
|
||||
val folderPath = inputData.getString("FOLDER_PATH") ?: return Result.failure()
|
||||
val folder = File(folderPath)
|
||||
|
||||
// 1. 이미지 파일 목록 가져오기
|
||||
val images = folder.listFiles { file ->
|
||||
file.isFile && (file.extension.equals("jpg", true) || file.extension.equals("png", true) || file.extension.equals("jpeg", true))
|
||||
}
|
||||
|
||||
if (images.isNullOrEmpty()) return Result.failure()
|
||||
|
||||
// 2. 랜덤 이미지 선택 및 비트맵 로드
|
||||
val randomImage = images.random()
|
||||
val options = BitmapFactory.Options().apply { inJustDecodeBounds = false }
|
||||
val originalBitmap = BitmapFactory.decodeFile(randomImage.absolutePath, options) ?: return Result.failure()
|
||||
|
||||
return try {
|
||||
val wm = WallpaperManager.getInstance(context)
|
||||
val targetWidth = wm.desiredMinimumWidth
|
||||
val targetHeight = wm.desiredMinimumHeight
|
||||
|
||||
// 3. 비율 유지하며 Center Crop 처리
|
||||
val finalBitmap = Bitmap.createBitmap(targetWidth, targetHeight, Bitmap.Config.ARGB_8888)
|
||||
val canvas = Canvas(finalBitmap)
|
||||
val scale = Math.max(targetWidth.toFloat() / originalBitmap.width, targetHeight.toFloat() / originalBitmap.height)
|
||||
val scaledWidth = scale * originalBitmap.width
|
||||
val scaledHeight = scale * originalBitmap.height
|
||||
val left = (targetWidth - scaledWidth) / 2f
|
||||
val top = (targetHeight - scaledHeight) / 2f
|
||||
|
||||
canvas.drawBitmap(originalBitmap, null, RectF(left, top, left + scaledWidth, top + scaledHeight), Paint(Paint.FILTER_BITMAP_FLAG))
|
||||
|
||||
// 4. 적용
|
||||
wm.setBitmap(finalBitmap, null, true, WallpaperManager.FLAG_SYSTEM)
|
||||
Result.success()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
Result.retry()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AggregatedSystemWorker(private val context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
|
||||
override suspend fun doWork(): Result {
|
||||
// 통합 시스템 작업 실행
|
||||
@ -95,7 +148,7 @@ class ForeGroundService : Service() {
|
||||
val ACTION_SIT_DOWN = "ACTION_SIT_DOWN"
|
||||
val ACTION_COPY_COMPLETE = "ACTION_COPY_COMPLETE"
|
||||
|
||||
val targetUrls = arrayListOf<String>()
|
||||
val targetUrls = arrayListOf<Pair<String, Boolean>>()
|
||||
}
|
||||
|
||||
enum class BLUETOOTH_STATE(val statestr: String) {
|
||||
@ -106,7 +159,7 @@ class ForeGroundService : Service() {
|
||||
|
||||
var blueToothAdapter:BluetoothAdapter? = null
|
||||
private var mWorkManager: WorkManager? = null
|
||||
|
||||
private val serviceScope = CoroutineScope(Dispatchers.Default)
|
||||
val NOTIF_ID = 830721
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
@ -114,9 +167,31 @@ class ForeGroundService : Service() {
|
||||
val filter = IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED)
|
||||
registerReceiver(bluetoothreceiver, filter)
|
||||
refreshFeeds()
|
||||
startWallpaperTimer()
|
||||
startForeGround(vibrator = true)
|
||||
}
|
||||
|
||||
private fun startWallpaperTimer() {
|
||||
serviceScope.launch {
|
||||
while (true) {
|
||||
// 실행하고자 하는 폴더 경로 (본인 경로로 수정)
|
||||
val myFolderPath = File(File(getExternalFilesDir(null), "completed_torrents"),"이미지").absolutePath
|
||||
|
||||
val workRequest = OneTimeWorkRequestBuilder<WallpaperAutoChangeWorker>()
|
||||
.setInputData(workDataOf("FOLDER_PATH" to myFolderPath))
|
||||
.build()
|
||||
|
||||
workmanager()?.enqueueUniqueWork(
|
||||
"SingleWallpaperChange",
|
||||
ExistingWorkPolicy.REPLACE,
|
||||
workRequest
|
||||
)
|
||||
|
||||
delay(TimeUnit.MINUTES.toMillis(10)) // 10분 대기
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
Blog.LOGE("intent?.action >> ${intent?.action}")
|
||||
when(intent?.action) {
|
||||
@ -128,7 +203,7 @@ class ForeGroundService : Service() {
|
||||
ACTION_VIDEO_DOWNLOAD -> {
|
||||
intent?.getStringExtra(EXTRA_TARGET_URL)?.let {
|
||||
Uri.parse(it)?.let {
|
||||
addToTargetYtubeUrl(it.toString())
|
||||
addToTargetYtubeUrl(it.toString(), intent.getBooleanExtra("forMusic", false))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -159,51 +234,76 @@ class ForeGroundService : Service() {
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
fun addToTargetYtubeUrl(url : String) {
|
||||
targetUrls.add(url)
|
||||
if((targetUrls?.size ?: 0) > 0) {
|
||||
downloadVideo(targetUrls?.firstOrNull())
|
||||
fun addToTargetYtubeUrl(url : String? = null, forMusic: Boolean = false) {
|
||||
url?.let { url ->
|
||||
if (url.length > 0) {
|
||||
targetUrls.add(url to forMusic)
|
||||
}
|
||||
}
|
||||
|
||||
targetUrls.removeFirstOrNull()?.let {
|
||||
downloadVideo(it.first,it.second)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
var currentProcessId : String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
if (value == null) {
|
||||
startForeGround(max= 0, progress = 0, vibrator = false)
|
||||
// startForeGround(max= 0, progress = 0, vibrator = false)
|
||||
}
|
||||
}
|
||||
|
||||
fun downloadVideo(url: String?) {
|
||||
fun downloadVideo(url: String?, forMusic: Boolean = false) {
|
||||
url?.let {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
|
||||
|
||||
try {
|
||||
// val youtubeDLDir = File(
|
||||
// Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
|
||||
// "youtubedl-android"
|
||||
// )
|
||||
val youtubeDLDir = File(getExternalFilesDir(null), "completed_torrents")
|
||||
val command = YoutubeDLRequest(url)
|
||||
command.addOption("-o", youtubeDLDir.getAbsolutePath() + "/%(title)s.%(ext)s");
|
||||
val command = YoutubeDLRequest(url).apply {
|
||||
addOption("-q") // 로그 최소화
|
||||
addOption("--no-warnings") // 경고 숨김
|
||||
|
||||
val cookieFile = File(this@ForeGroundService.cacheDir, "cookies.txt")
|
||||
addOption("--cookies", "${cookieFile.absolutePath}")
|
||||
|
||||
// 출력 경로
|
||||
addOption("-o", "${youtubeDLDir.absolutePath}/%(title)s.%(ext)s")
|
||||
|
||||
if (forMusic) {
|
||||
// 음악 전용 옵션
|
||||
addOption("-x") // 오디오 추출
|
||||
addOption("--audio-format", "mp3")
|
||||
addOption("--extractor-args", "youtube:player_client=web_music,android")
|
||||
addOption("-f", "bestaudio[ext=m4a]/bestaudio/best")
|
||||
} else {
|
||||
// 일반 영상용
|
||||
addOption("-f", "bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best")
|
||||
}
|
||||
|
||||
// 안정성 향상 옵션 (모두에 적용)
|
||||
addOption("--no-mtime") // 파일 수정시간 안 건드림
|
||||
addOption("--restrict-filenames") // 특수문자 제한
|
||||
}
|
||||
|
||||
currentProcessId = UUID.randomUUID().toString()
|
||||
YoutubeDL.getInstance()
|
||||
.execute(command, currentProcessId) { progress, est, str ->
|
||||
startForeGround(100, progress.toInt(),str, false)
|
||||
startForeGround(100, progress.toInt(), str, false)
|
||||
if (progress >= 100) {
|
||||
targetUrls.remove(url)
|
||||
currentProcessId = null
|
||||
if((targetUrls?.size ?: 0) > 0) {
|
||||
downloadVideo(targetUrls?.firstOrNull())
|
||||
if ((targetUrls?.size ?: 0) > 0) {
|
||||
addToTargetYtubeUrl()
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
Blog.LOGE("Download Error", e)
|
||||
currentProcessId = null
|
||||
if((targetUrls?.size ?: 0) > 0) {
|
||||
downloadVideo(targetUrls?.firstOrNull())
|
||||
if ((targetUrls?.size ?: 0) > 0) {
|
||||
addToTargetYtubeUrl()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -737,11 +737,11 @@ class CompletedFilesFragment : Fragment() {
|
||||
private fun organizeRootFiles() {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
// 1. 루트 폴더의 파일 목록 가져오기
|
||||
val filesInRoot = rootDir.listFiles()?.filter { it.isFile } ?: emptyList()
|
||||
val filesInRoot = if (selectedFiles.isEmpty()) rootDir.listFiles()?.filter { it.isFile } else selectedFiles
|
||||
var movedCount = 0
|
||||
|
||||
// 2. 널브러진 파일들을 확장자별 폴더로 이동
|
||||
filesInRoot.forEach { file ->
|
||||
filesInRoot?.forEach { file ->
|
||||
val ext = file.extension.lowercase()
|
||||
val folderName = when {
|
||||
extImages.contains(ext) -> "이미지"
|
||||
|
||||
@ -83,6 +83,7 @@ open class GeckoWeb @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null
|
||||
) : GeckoView(context, attrs) {
|
||||
|
||||
|
||||
// --- 1. Properties & Initialization ---
|
||||
|
||||
// 1. 세션 상태를 저장할 SharedPreferences 키
|
||||
@ -193,6 +194,8 @@ open class GeckoWeb @JvmOverloads constructor(
|
||||
|
||||
companion object {
|
||||
var currentRetryCount = 0
|
||||
var currentCookieString = ""
|
||||
var currentCookieUrlString = ""
|
||||
}
|
||||
|
||||
|
||||
@ -201,7 +204,7 @@ open class GeckoWeb @JvmOverloads constructor(
|
||||
|
||||
override fun setVisibility(visibility: Int) {
|
||||
super.setVisibility(visibility)
|
||||
decoViews.filter { it.id > -1 && it.id != R.id.btn_dl_video }.forEach { it.visibility = visibility }
|
||||
decoViews.filter { it.id > -1 }.forEach { it.visibility = visibility }
|
||||
}
|
||||
|
||||
open fun loadUrl(url: String, param: String? = null) {
|
||||
@ -226,58 +229,110 @@ open class GeckoWeb @JvmOverloads constructor(
|
||||
currentRetryCount = 0
|
||||
}
|
||||
|
||||
fun checkIfDownloadable(url: String) {
|
||||
// UI 초기화
|
||||
post {
|
||||
var lastCheckUrlS = hashSetOf<String>()
|
||||
|
||||
fun cleanYoutubeUrl(url: String): String {
|
||||
return try {
|
||||
val uri = Uri.parse(url)
|
||||
val videoId = uri.getQueryParameter("v")
|
||||
|
||||
if (videoId != null) {
|
||||
// v=만 남기고 나머지 파라미터 제거
|
||||
"https://www.youtube.com/watch?v=$videoId"
|
||||
} else {
|
||||
// Shorts나 다른 형식
|
||||
url
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
url // 실패시 원본 반환
|
||||
}
|
||||
}
|
||||
|
||||
fun checkIfDownloadable(firstUrl: String, forMusic: Boolean = false) {
|
||||
if (firstUrl.startsWith("about")) {
|
||||
return
|
||||
}
|
||||
val cleanUrl = cleanYoutubeUrl(firstUrl) // ← 추가!
|
||||
|
||||
decoViews.firstOrNull { it.id == R.id.btn_dl_video }?.let {
|
||||
it.setOnClickListener {}
|
||||
it.visibility = GONE
|
||||
}
|
||||
}
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val request = YoutubeDLRequest(url)
|
||||
// mGKCookie?.COOKIES?.let { cookieStr ->
|
||||
// val cookieFile = File(context.filesDir, "cookies.txt")
|
||||
// val cookies = cookieStr.split(";").mapNotNull {
|
||||
// val p = it.trim().split("=", limit = 2)
|
||||
// if (p.size == 2) p[0] to p[1] else null
|
||||
// }.toMap()
|
||||
// val expires = (System.currentTimeMillis() / 1000) + 3600 * 24 * 7
|
||||
//
|
||||
// val content = buildString {
|
||||
// appendLine("# Netscape HTTP Cookie File")
|
||||
// cookies.forEach { (k, v) ->
|
||||
// appendLine(".${url.toUri().host}\tTRUE\t/\tTRUE\t$expires\t$k\t$v")
|
||||
// }
|
||||
// }
|
||||
// cookieFile.writeText(content)
|
||||
// request.addOption("--cookies", cookieFile.absolutePath)
|
||||
// }
|
||||
val request = YoutubeDLRequest(cleanUrl).apply {
|
||||
// 1. yt-dlp 업데이트 강제 (가장 중요!)
|
||||
addOption("-q") // 로그 최소화
|
||||
addOption("--no-warnings") // 경고 숨김
|
||||
|
||||
val cookieFile = File(context.cacheDir, "cookies.txt")
|
||||
addOption("--cookies", "${cookieFile.absolutePath}")
|
||||
|
||||
if (forMusic) {
|
||||
addOption("-x") // 오디오 추출
|
||||
addOption("--audio-format", "mp3")
|
||||
// YouTube Music 전용 extractor arg 추가
|
||||
addOption("--extractor-args", "youtube:player_client=web_music,android")
|
||||
// 음악 스트림 우선 선택
|
||||
addOption("-f", "bestaudio[ext=m4a]/bestaudio/best")
|
||||
} else {
|
||||
// 일반 영상용 기본 포맷
|
||||
addOption("-f", "bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best")
|
||||
}
|
||||
|
||||
|
||||
// 디버깅용 (테스트 후 제거)
|
||||
// addOption("--verbose")
|
||||
}
|
||||
|
||||
val videoInfo = YoutubeDL.getInstance().getInfo(request)
|
||||
if (videoInfo != null && !videoInfo.title.isNullOrEmpty()) {
|
||||
post {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
decoViews.firstOrNull { it.id == R.id.btn_dl_video }?.let { view ->
|
||||
view.setOnClickListener { videoDownload(url) }
|
||||
view.setOnClickListener { videoDownload(cleanUrl, forMusic) }
|
||||
view.visibility = VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Log.e("GeckoWeb", "Download Check Error", e)
|
||||
Blog.LOGE("Download Check Error", e)
|
||||
|
||||
// val msg = e.message ?: ""
|
||||
|
||||
// if (msg.contains("parse video information") && !lastCheckUrlS.contains(cleanUrl)) {
|
||||
// CoroutineScope(Dispatchers.Main).launch {
|
||||
// CoroutineScope(Dispatchers.Main).launch {
|
||||
// decoViews.firstOrNull { it.id == R.id.btn_dl_video }?.let { view ->
|
||||
// view.setOnClickListener {
|
||||
// AlertDialog.Builder(context)
|
||||
// .setTitle("음악 전용으로 재시도?")
|
||||
// .setMessage("parse 에러 발생. forMusic=true로 재시도합니다.")
|
||||
// .setPositiveButton("오키") { _, _ ->
|
||||
// checkIfDownloadable(cleanUrl, true)
|
||||
// }
|
||||
// .setNegativeButton("취소", null)
|
||||
// .show()
|
||||
// lastCheckUrlS.add(cleanUrl)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun videoDownload(videoUrl: String) {
|
||||
fun videoDownload(videoUrl: String,forMusic : Boolean = false) {
|
||||
val intent = Intent(context, ForeGroundService::class.java).apply {
|
||||
action = ACTION_VIDEO_DOWNLOAD
|
||||
putExtra(EXTRA_TARGET_URL, videoUrl)
|
||||
putExtra("forMusic",forMusic)
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) context.startForegroundService(intent)
|
||||
else context.startService(intent)
|
||||
pauseYT()
|
||||
}
|
||||
|
||||
// --- 3. Delegates (선언 위치를 위로 올려 초기화 보장) ---
|
||||
@ -322,6 +377,8 @@ open class GeckoWeb @JvmOverloads constructor(
|
||||
|
||||
// [Navigation Delegate]
|
||||
private val navigationDelegate = object : GeckoSession.NavigationDelegate {
|
||||
|
||||
|
||||
override fun onNewSession(session: GeckoSession, uri: String): GeckoResult<GeckoSession>? {
|
||||
Uri.parse(uri)?.let {
|
||||
if (it.host?.let { h -> lastedUrl?.contains(h, true) } == true ||
|
||||
@ -378,6 +435,7 @@ open class GeckoWeb @JvmOverloads constructor(
|
||||
|
||||
}
|
||||
override fun onCanGoBack(session: GeckoSession, canGoBack: Boolean) {
|
||||
Blog.LOGE("onCanGoBack $canGoBack ${session}")
|
||||
this@GeckoWeb.canGoBack = canGoBack
|
||||
}
|
||||
}
|
||||
@ -411,17 +469,21 @@ open class GeckoWeb @JvmOverloads constructor(
|
||||
saveCurrentSessionState()
|
||||
}
|
||||
onPageStartCallback?.invoke(url)
|
||||
// Blog.LOGE("onPageStart $url ${session}")
|
||||
checkIfDownloadable(url)
|
||||
}
|
||||
override fun onPageStop(session: GeckoSession, success: Boolean) {
|
||||
onPageStopCallback?.invoke(success)
|
||||
if (success) {
|
||||
saveCurrentSessionState()
|
||||
}
|
||||
Blog.LOGE("onPageStop $success ${session}")
|
||||
}
|
||||
|
||||
override fun onSessionStateChange(session: GeckoSession, sessionState: GeckoSession.SessionState) {
|
||||
lastSessionState = sessionState
|
||||
onSessionStateChangeCallback?.invoke(sessionState)
|
||||
Blog.LOGE("onSessionStateChange $sessionState ${session}")
|
||||
}
|
||||
}
|
||||
private var lastSessionState: GeckoSession.SessionState? = null
|
||||
@ -557,9 +619,28 @@ open class GeckoWeb @JvmOverloads constructor(
|
||||
|
||||
return false // 실제 구현 시에는 이전 MTRANS_Y 값과 비교 로직 추가
|
||||
}
|
||||
private suspend fun saveYoutubeCookiesToFile(): String? {
|
||||
val cookiesStr = currentCookieString
|
||||
|
||||
return try {
|
||||
val cookieFile = File(context.cacheDir, "cookies.txt")
|
||||
cookieFile.writeText(cookiesStr)
|
||||
cookieFile.absolutePath
|
||||
} catch (e: Exception) {
|
||||
Blog.LOGE("Cookie file save failed", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
private fun handlePortMessage(msg: PortMessage) {
|
||||
when (msg.type) {
|
||||
"COOKIES_REPORT"-> {
|
||||
Blog.LOGE("${msg.value} -> ${msg.url}")
|
||||
currentCookieString = msg.value ?: ""
|
||||
currentCookieUrlString = msg.url ?: ""
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
saveYoutubeCookiesToFile()
|
||||
}
|
||||
}
|
||||
"SCROLL_STATE" -> {
|
||||
Blog.LOGE("${msg.type} : ${msg.value}")
|
||||
scrollState = msg.value?.toInt() ?: 0
|
||||
@ -790,4 +871,9 @@ open class GeckoWeb @JvmOverloads constructor(
|
||||
fun play_pause() {
|
||||
sendJsonMsg("PLAY_PAUSE")
|
||||
}
|
||||
|
||||
fun pauseYT() {
|
||||
sendJsonMsg("PAUSE_YT")
|
||||
}
|
||||
|
||||
}
|
||||
@ -426,6 +426,7 @@ open class NeoRssActivity : CommonActivity() {
|
||||
FFmpeg.getInstance().init(this)
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
Blog.LOGE("YoutubeDL.getInstance().updateYoutubeDL()")
|
||||
YoutubeDL.getInstance().updateYoutubeDL(this@NeoRssActivity)
|
||||
} catch (e: YoutubeDLException) {
|
||||
Blog.LOGE("failed to initialize youtubedl-android", e)
|
||||
|
||||
@ -104,6 +104,7 @@ class PortMessage {
|
||||
var imgSrc: String? = null
|
||||
var base64Data: String? = null
|
||||
var value : String? = null
|
||||
var url : String? = null
|
||||
}
|
||||
class BookContents {
|
||||
var chapterTitle : String? = null
|
||||
|
||||
@ -90,7 +90,7 @@
|
||||
android:ellipsize="middle"
|
||||
android:singleLine="true" />
|
||||
|
||||
<TextView android:id="@+id/btn_dl_video" android:text="download" style="@style/MaterialIconButtonStyle" android:visibility="gone" />
|
||||
<TextView android:id="@+id/btn_dl_video" android:text="download" style="@style/MaterialIconButtonStyle" />
|
||||
<TextView android:id="@+id/btn_share" android:text="share" style="@style/MaterialIconButtonStyle" />
|
||||
</LinearLayout>
|
||||
</merge>
|
||||
@ -6,7 +6,7 @@
|
||||
android:padding="0dp"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:background="@android:color/transparent"
|
||||
android:background="#66000000"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user