..
This commit is contained in:
parent
197d704e27
commit
e9b287cf81
@ -420,32 +420,66 @@ var time2 = null
|
||||
var time1 = null
|
||||
function gotoNext() {
|
||||
clearTimeout(time1)
|
||||
try{
|
||||
console.log("targetUrl :: " + targetUrl);
|
||||
if (document.querySelector('[class="btn-group"]')) {
|
||||
time2 = setTimeout(function () {
|
||||
clearTimeout(time2)
|
||||
var targetElement = null
|
||||
document.querySelector('[class="btn-group"]').querySelectorAll('a').forEach(function(e){
|
||||
if(e.hasAttribute("href") &&
|
||||
(
|
||||
(e.getAttribute("href").search("page=2") > -1 && location.href.search("page") < 0) ||
|
||||
(e.getAttribute("href").search("page=3") > -1 && location.href.search("page=2") > 0) ||
|
||||
(e.getAttribute("href").search("page=4") > -1 && location.href.search("page=3") > 0)
|
||||
)) {
|
||||
targetElement = e
|
||||
try {
|
||||
let attempts = 0; // 시도 횟수를 기록할 변수
|
||||
const maxAttempts = 8; // 최대 15초 동안 시도
|
||||
let currentPage = 1; // 기본 페이지
|
||||
|
||||
// 1초마다 실행될 로직을 담는 변수
|
||||
const findAndClickInterval = setInterval(function () {
|
||||
attempts++; // 시도 횟수 증가
|
||||
|
||||
// 현재 페이지 번호 가져오기 (이 로직은 한 번만 실행해도 되지만, 페이지가 동적으로 바뀔 경우를 대비해 내부에 둡니다)
|
||||
var pageMatch = location.href.match(/page=(\d+)/);
|
||||
if (pageMatch && pageMatch[1]) {
|
||||
currentPage = parseInt(pageMatch[1], 10);
|
||||
toast(`[${0}초] ⏳ 다음 페이지 링크를 찾는 중... (현재: page=${currentPage})`)
|
||||
}
|
||||
|
||||
var nextPage = currentPage + 1;
|
||||
var targetElement = null;
|
||||
|
||||
document.querySelectorAll('.btn-group')?.forEach((el) => {
|
||||
var pageLinks =el?.querySelectorAll('a');
|
||||
|
||||
if (pageLinks) {
|
||||
pageLinks.forEach(function(link) {
|
||||
toast(`[${attempts}초] ✅ 다음 페이지 링크를 찾는 중 ${link.getAttribute('href')}`);
|
||||
if (link.getAttribute('href')?.includes(`page=${nextPage}`)) {
|
||||
targetElement = link;
|
||||
toast(`[${attempts}초] ✅ 다음 페이지 링크를 찾았습니다.`);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
if(targetElement !== null) {
|
||||
targetElement.click()
|
||||
} else {
|
||||
// location.href = "https://naver.com"
|
||||
}
|
||||
}, 15000);
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
|
||||
|
||||
// 1. 다음 페이지 링크를 찾았을 경우
|
||||
if (attempts > 3 && targetElement) {
|
||||
console.log(`[${attempts}초] ✅ 다음 페이지 링크를 찾았습니다. 클릭합니다.`);
|
||||
toast(`[${attempts}초] ✅ 다음 페이지 링크를 찾았습니다. 클릭합니다.`)
|
||||
clearInterval(findAndClickInterval); // 반복을 중단
|
||||
targetElement.click(); // 링크 클릭
|
||||
}
|
||||
// 2. 시간(15초)이 초과되었을 경우
|
||||
else if (attempts >= maxAttempts) {
|
||||
console.log(`[${attempts}초] ❌ 시간 초과. 다음 페이지 링크를 찾지 못했습니다.`);
|
||||
toast(`[${attempts}초] ❌ 시간 초과. 다음 페이지 링크를 찾지 못했습니다.`)
|
||||
clearInterval(findAndClickInterval); // 반복을 중단
|
||||
// 필요하다면 이곳에 다른 페이지로 이동하는 로직을 추가하세요.
|
||||
// location.href = "https://naver.com";
|
||||
}
|
||||
// 3. 아직 링크를 찾지 못했고, 시간도 남았을 경우
|
||||
else {
|
||||
toast(`[${attempts}초] ⏳ 다음 페이지 링크를 찾는 중... (대상: page=${nextPage})`)
|
||||
console.log(`[${attempts}초] ⏳ 다음 페이지 링크를 찾는 중... (대상: page=${nextPage})`);
|
||||
}
|
||||
|
||||
}, 1500); // 1000ms = 1초마다 함수 실행
|
||||
|
||||
} catch (e) {
|
||||
console.error("스크립트 실행 중 오류 발생:", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -663,6 +697,9 @@ async function handleCommon() {
|
||||
}
|
||||
);
|
||||
|
||||
let scrollInterval;
|
||||
|
||||
async function getImg() {
|
||||
console.log(`Found TEST`);
|
||||
const imageSelector = 'img[class*="mw-100"][src*="images"]';
|
||||
const images = Array.from(document.querySelectorAll(imageSelector));
|
||||
@ -674,9 +711,15 @@ async function handleCommon() {
|
||||
|
||||
console.log(`Found ${'$'}{uniqueUrls.length} unique images to cache.`);
|
||||
|
||||
|
||||
for (const image of uniqueUrls) {
|
||||
|
||||
}
|
||||
// 3. 각 URL을 순회하며 Base64로 변환하고 즉시 네이티브로 전송
|
||||
// (모든 작업을 병렬로 처리하지 않고 순차적(또는 하나씩)으로 보내 메모리 부담을 줄임)
|
||||
var idx = 0
|
||||
for (const url of uniqueUrls) {
|
||||
try {
|
||||
const base64Data = await getBase64FromUrl(url);
|
||||
if (base64Data) {
|
||||
// 이미지 하나를 성공할 때마다 네이티브로 즉시 전송
|
||||
@ -686,9 +729,51 @@ async function handleCommon() {
|
||||
base64Data: base64Data
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
|
||||
toast(`${idx + 1} / ${uniqueUrls.length} unique images to cache.`);
|
||||
idx += 1;
|
||||
}
|
||||
gotoNext()
|
||||
}
|
||||
}
|
||||
|
||||
// 스크롤 함수 정의
|
||||
function smoothScrollDown() {
|
||||
// 1. 전체 페이지의 높이를 가져옵니다.
|
||||
const totalHeight = document.body.scrollHeight;
|
||||
|
||||
// 2. 한 번에 스크롤할 거리를 계산합니다 (전체 높이의 20분의 1).
|
||||
const scrollIncrement = totalHeight / 25;
|
||||
|
||||
// 3. 1초(1000ms) 간격으로 스크롤을 반복 실행합니다.
|
||||
scrollInterval = setInterval(() => {
|
||||
// 현재 스크롤 위치 + 화면에 보이는 높이가 전체 페이지 높이보다 크거나 같으면
|
||||
// 페이지의 끝에 도달한 것입니다.
|
||||
if (Math.ceil(window.scrollY) + Math.ceil(window.innerHeight) >= (totalHeight * 0.9)) {
|
||||
console.log("✅ 페이지 끝에 도달했습니다. 스크롤을 중지합니다.");
|
||||
toast("✅ 페이지 끝에 도달했습니다. 스크롤을 중지합니다.")
|
||||
clearInterval(scrollInterval); // 반복 실행을 멈춥니다.
|
||||
getImg()
|
||||
} else {
|
||||
// 계산된 거리만큼 부드럽게 아래로 스크롤합니다.
|
||||
window.scrollBy({
|
||||
top: scrollIncrement,
|
||||
left: 0,
|
||||
behavior: 'smooth' // 부드럽게 스크롤하는 옵션
|
||||
});
|
||||
toast(`smooth scrollBy ${scrollIncrement}, ${window.scrollY}, ${window.innerHeight}, ${totalHeight}`)
|
||||
}
|
||||
}, 2000); // 1000밀리초 = 1초
|
||||
}
|
||||
|
||||
// 스크롤 함수 실행
|
||||
smoothScrollDown();
|
||||
|
||||
|
||||
|
||||
}
|
||||
else if(location.href.search("javt")) {
|
||||
console.log(`Found TEST`);
|
||||
|
||||
@ -23,6 +23,7 @@ import android.content.ComponentCallbacks2
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import bums.lunatic.launcher.helpers.HourlyLogWriter
|
||||
import bums.lunatic.launcher.helpers.PrefHelper
|
||||
import bums.lunatic.launcher.home.Base64ImageCache
|
||||
import bums.lunatic.launcher.home.Base64RequestHandler
|
||||
import bums.lunatic.launcher.utils.Blog
|
||||
import com.squareup.picasso.OkHttp3Downloader
|
||||
@ -45,6 +46,7 @@ internal class LunaticLauncher : Application() {
|
||||
appContext = this
|
||||
// Base.initialize(this)
|
||||
PrefHelper.initialize(this)
|
||||
Base64ImageCache.init(this)
|
||||
val dir = File("/storage/emulated/0/bums_ob/BUM'S PACED /scraped/logs")
|
||||
///BUM'S PACED/pdfs
|
||||
if (!dir.exists()) {
|
||||
|
||||
@ -1,15 +1,25 @@
|
||||
package bums.lunatic.launcher.home
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.BitmapFactory
|
||||
import android.util.Base64
|
||||
import androidx.collection.LruCache
|
||||
import com.squareup.picasso.Picasso
|
||||
import com.squareup.picasso.Request
|
||||
import com.squareup.picasso.RequestHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.math.BigInteger
|
||||
import java.security.MessageDigest
|
||||
|
||||
/**
|
||||
* Base64 데이터를 URL 키와 함께 저장하는 메모리 캐시 (Singleton 또는 DI로 관리)
|
||||
* Base64 데이터를 URL 키와 함께 저장하는 2단계 캐시 (메모리 + 파일)
|
||||
* 1. 메모리 캐시(LruCache)를 먼저 확인하여 빠른 접근을 제공합니다.
|
||||
* 2. 메모리에 없는 경우 파일 캐시를 확인하여 디스크 영속성을 지원합니다.
|
||||
* OOM(메모리 부족)을 피하기 위해 비트맵 자체가 아닌 Base64 문자열을 저장합니다.
|
||||
*/
|
||||
object Base64ImageCache {
|
||||
@ -17,22 +27,83 @@ object Base64ImageCache {
|
||||
private val maxMemory = (Runtime.getRuntime().maxMemory() / 1024).toInt()
|
||||
private val cacheSize = maxMemory / 8
|
||||
|
||||
// Key: Image URL, Value: Base64 Data String
|
||||
// L1 Cache: Memory (Key: Image URL, Value: Base64 Data String)
|
||||
private val lru: LruCache<String, String> = LruCache(cacheSize)
|
||||
|
||||
// L2 Cache: File System
|
||||
private var cacheDir: File? = null
|
||||
private val coroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
|
||||
/**
|
||||
* 캐시를 사용하기 전에 반드시 Application 클래스 등에서 초기화해야 합니다.
|
||||
* @param context 애플리케이션 컨텍스트
|
||||
*/
|
||||
fun init(context: Context) {
|
||||
// 앱의 캐시 디렉토리 내에 이미지 캐시 전용 폴더 생성
|
||||
cacheDir = File(context.cacheDir, "base64_image_cache").apply { mkdirs() }
|
||||
}
|
||||
|
||||
/**
|
||||
* 메모리 캐시와 파일 캐시에 Base64 데이터를 저장합니다.
|
||||
* 파일 저장은 백그라운드에서 비동기적으로 수행됩니다.
|
||||
*/
|
||||
fun put(url: String, base64Data: String) {
|
||||
// 1. 메모리 캐시에 저장
|
||||
if (lru.get(url) == null) {
|
||||
lru.put(url, base64Data)
|
||||
}
|
||||
|
||||
// 2. 파일 캐시에 비동기적으로 저장
|
||||
coroutineScope.launch {
|
||||
cacheDir?.let {
|
||||
try {
|
||||
val file = File(it, url.toMd5())
|
||||
file.writeText(base64Data, Charsets.UTF_8)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace() // 실제 앱에서는 로깅 라이브러리 사용 권장
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 메모리 또는 파일 캐시에서 Base64 데이터를 가져옵니다.
|
||||
* 파일 캐시에서 가져온 경우, 메모리 캐시에도 추가합니다.
|
||||
*/
|
||||
fun get(url: String): String? {
|
||||
return lru.get(url)
|
||||
// 1. 메모리 캐시에서 먼저 조회
|
||||
lru.get(url)?.let { return it }
|
||||
|
||||
// 2. 메모리에 없으면 파일 캐시에서 조회
|
||||
cacheDir?.let {
|
||||
try {
|
||||
val file = File(it, url.toMd5())
|
||||
if (file.exists()) {
|
||||
val base64Data = file.readText(Charsets.UTF_8)
|
||||
// 파일에서 읽은 데이터를 메모리에 올려서 다음 접근 속도를 높임
|
||||
lru.put(url, base64Data)
|
||||
return base64Data
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* URL을 MD5 해시로 변환하여 안전한 파일 이름으로 사용합니다.
|
||||
*/
|
||||
private fun String.toMd5(): String {
|
||||
val md = MessageDigest.getInstance("MD5")
|
||||
return BigInteger(1, md.digest(toByteArray())).toString(16).padStart(32, '0')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Picasso가 요청한 URL이 Base64ImageCache에 있는지 확인하는 커스텀 핸들러
|
||||
* (이 클래스는 변경할 필요가 없습니다)
|
||||
*/
|
||||
class Base64RequestHandler : RequestHandler() {
|
||||
|
||||
@ -44,7 +115,7 @@ class Base64RequestHandler : RequestHandler() {
|
||||
*/
|
||||
override fun canHandleRequest(data: Request): Boolean {
|
||||
val url = data.uri.toString()
|
||||
// data: 스킴이 아니고, 우리 캐시에 URL 키가 존재할 때만 true 반환
|
||||
// data: 스킴이 아니고, 우리 캐시(메모리 또는 파일)에 URL 키가 존재할 때만 true 반환
|
||||
return !url.startsWith(DATA_SCHEME) && Base64ImageCache.get(url) != null
|
||||
}
|
||||
|
||||
|
||||
@ -947,6 +947,7 @@ class GeckoWeb : BWebview {
|
||||
"WebtoonContents"-> {
|
||||
}
|
||||
"MSG" -> {
|
||||
context.toast("Received Msg privates form ${lPortMessage.msg}")
|
||||
}
|
||||
"SHOWVIEWER" -> {
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user