This commit is contained in:
lunaticbum 2025-08-01 18:36:16 +09:00
parent 2a25b49baa
commit 1272c91430
15 changed files with 1051 additions and 676 deletions

View File

@ -115,8 +115,8 @@ dependencies {
// implementation("org.mozilla.geckoview:geckoview:139.0.20250523173407")
// https://mvnrepository.com/artifact/org.mozilla.geckoview/geckoview
implementation("org.mozilla.geckoview:geckoview:139.0.20250523173407")
implementation("com.vladsch.flexmark:flexmark-all:0.64.0")
// implementation 'com.vladsch.flexmark:flexmark-all:0.64.8'
// implementation("org.opencv:opencv-android:4.11.0")

View File

@ -0,0 +1,13 @@
version: '3'
services:
couchdb:
image: couchdb:latest
container_name: couchdb
restart: always
ports:
- "5984:5984" # 외부접근: 5984포트
environment:
- COUCHDB_USER=obsidian
- COUCHDB_PASSWORD=HiVi88131921
volumes:
- ./data:/opt/couchdb/data

View File

@ -74,13 +74,7 @@ port.onMessage.addListener(response => {
}
break;
case "saveContent":{
sendMessage(
{
type: "SHOWVIEWER",
contents : document.documentElement.outerHTML
}
);
scrollToLazyImg()
}
default:
port.postMessage(`Received: ${JSON.stringify(response)}`);
@ -121,11 +115,6 @@ if(document.querySelector("#html_encoder_div")) {
}
);
}
// else {
// sendMessage({type:"MSG",msg:"connected has Nothings"});
// loadComplete()
// }
function getList(children) {
// port.postMessage("Start of getList!" + children);
@ -213,7 +202,7 @@ function scrollByPercent(current , max) {
const moveAmount = pageHeight / max;
window.scrollTo({ top: moveAmount * current, behavior: "smooth" });
}
document.querySelector(".header__inner")
function scrollByPercentUpDown(isToDown , max) {
const pageHeight = Math.max(
document.body.scrollHeight,
@ -227,364 +216,68 @@ function scrollByPercentUpDown(isToDown , max) {
const moveAmount = Math.max(pageHeight / max,150);
window.scrollTo({ top: currentScroll + (moveAmount * isToDown) , behavior: "smooth" });
}
function loadComplete() {
// try {port.postMessage(JSON.stringify({type: "NotRegistered"}));}catch (e) {}
}
function sendMessage(msg) {
port.postMessage(JSON.stringify(msg));
}
function toast(msg) {
port.postMessage(JSON.stringify({type:"MSG",msg:msg}));
}
var time1 = null
var mainContentsEl = null
if (port) {
sendMessage({type: "MSG", msg:"connect prot"});
sendMessage({type: "MSG", msg: "connect prot"});
time1 = setTimeout(autoScrollAndSave(false), 2000)
}
function scrollToLazyImg() {
(function(autoScrollAndSave){
// 한 번에 이동할 픽셀
const step = 200;
// 반복 간격(ms) (느릴수록 로딩에 더 여유 생김)
const delay = 600;
// 스크롤 현재 위치 추적
let currentY = window.scrollY;
let maxY = Math.max(
document.body.scrollHeight,
document.documentElement.scrollHeight
);
time1 = setTimeout(
function(){
if (location.hostname.search("clien") > -1 && document.querySelectorAll('[class^="view_top"]')) {
document.querySelectorAll('[class^="view_top"]').forEach(e => e.remove())
}
if (location.hostname.search("toki") > -1 && document.querySelectorAll('[id^="id_mbv"]')) {
document.querySelectorAll('[id^="id_mbv"]').forEach(e => e.remove())
}
if (location.hostname.search("toki") > -1 && document.querySelectorAll('[class^="basic-banner"]')) {
document.querySelectorAll('[class^="basic-banner"]').forEach(e => e.remove())
}
if (document.querySelector(".top_google_ad_space")) {
document.querySelector(".top_google_ad_space").remove()
}
if (document.querySelectorAll(".adv-group")) {
document.querySelectorAll(".adv-group").forEach(e =>
e.remove()
)
}
if (document.querySelectorAll('[id^="div-gpt-ad"]')) {
document.querySelectorAll('[id^="div-gpt-ad"]').forEach(e =>
e.remove()
)
}
if (document.location.href.search("reddit") > -1) {
if (document.querySelector('#xpromo-bottom-sheet')) {
document.querySelector('#xpromo-bottom-sheet').remove()
}
}
if (document.querySelectorAll('[id^="div_adnmore_area"]')) {
document.querySelectorAll('[id^="div_adnmore_area"]').forEach(e =>
e.remove()
)
}
if (document.querySelectorAll('[id^="div_adnmore_area"]')) {
document.querySelectorAll('[id^="div_adnmore_area"]').forEach(e =>
e.remove()
)
}
if (document.querySelectorAll('[class^="adv-groupno"]')) {
document.querySelectorAll('[class^="adv-groupno"]').forEach(e =>
e.remove()
)
}
if (document.querySelectorAll('[class^="code-block"]')) {
document.querySelectorAll('[class^="code-block"]').forEach(e => e.remove())
}
if (document.querySelectorAll('.ad-template')) {
document.querySelectorAll('.ad-template').forEach(function (e) {
e.remove()
})
}
if (document.querySelectorAll('[class^="popupBanner_w popupOpen"]')) {
document.querySelectorAll('[class^="popupBanner_w popupOpen"]').forEach(function (e) {
e.remove()
})
}
if (document.querySelectorAll('iframe')) {
document.querySelectorAll('iframe').forEach(function (e) {
if (e.getAttribute("src") != null && (e.getAttribute("src").search("ads") > -1 || e.getAttribute("src").search("coupang") > -1)) {
e.remove()
function scrollStep() {
// 페이지 최대값(일정 margin 여유)
maxY = Math.max(
document.body.scrollHeight,
document.documentElement.scrollHeight
);
if (currentY + step < maxY - window.innerHeight) {
currentY += step;
window.scrollTo({ top: currentY, behavior: 'smooth' });
setTimeout(scrollStep, delay);
} else {
// 끝까지 도달하면 마지막으로 최하단 이동
window.scrollTo({ top: maxY, behavior: 'smooth' });
setTimeout(function() {
// == 여기에 저장 등 후처리 콜백 코드 호출 ==
if (typeof autoScrollAndSave === 'function') {
autoScrollAndSave(true);
} else {
// 직접 메시지 보내거나, 원하는 최종 동작 실행
// 예: window.postMessage('saveToObsidian', '*');
}
})
}, 800);
}
if (location.href.search("x.com") > -1) {
var mainClass = document.querySelector('section').className
document.querySelector('section').querySelectorAll('div[class="'+ mainClass +'"]').forEach(function (e) {
if (e.hasAttribute("data-testid") && e.querySelector('video')) {
e.querySelectorAll('[poster]').forEach(function (e) {
})
console.log(e.innerHTML)
}
})
}
if (document.querySelectorAll('[data-banner^="coupang-"]')) {
document.querySelectorAll('[data-banner^="coupang-"]').forEach(e => e.remove())
}
if (document.querySelectorAll('[id^="mobonDivBanner"]')) {
document.querySelectorAll('[id^="mobonDivBanner"]').forEach(e => e.remove())
}
if (document.querySelectorAll('[id^="pnlTopAd"]')) {
document.querySelectorAll('[id^="pnlTopAd"]').forEach(e => e.remove())
}
if (document.querySelectorAll('[class^="ad-tag"]')) {
document.querySelectorAll('[class^="ad-tag"]').forEach(e => e.remove())
}
if (document.querySelectorAll('[class^="science-banner-area"]')) {
document.querySelectorAll('[class^="science-banner-area"]').forEach(e=> e.remove())
}
if (location.href.search("torrentzota") > -1 && document.querySelectorAll('a')) {
document.querySelectorAll('a').forEach(function (e) {
if (e.getAttribute('href') != null && e.getAttribute('href').startsWith("/adver-")) {
e.remove()
}
}
)
if(document.querySelectorAll('[class^="py-4 flex flex-row border-b topic-item"]').length > 1) {
var datas = []
document.querySelectorAll('[class^="py-4 flex flex-row border-b topic-item"]').forEach(function (e) {
var title = ""
var link = ""
e.querySelectorAll("a").forEach(function (e){
if(e.getAttribute("class") === "item-link"){
title = e.getAttribute('title')
}
if(e.getAttribute("class") === "item-link"){
link = e.getAttribute('href')
if (link.length > 0) {
link = location.protocol + '//' + location.hostname + link
}
}
})
var description = e.querySelector('.flex-none w-16 text-center').textContent + e.querySelector('.badge badge-third w-auto px-1 flex-none mr-2 float-left').textContent
var md = e.querySelector(".flex-none w-14 text-center hidden md:block").textContent
let todayYear = new Date().getFullYear();
let now = new Date();
let hh = String(now.getHours()).padStart(2, '0'); // 시 (2자리)
let mm = String(now.getMinutes()).padStart(2, '0'); // 분 (2자리)
let ss = String(now.getSeconds()).padStart(2, '0'); // 초 (2자리)
let time = `${hh}:${mm}:${ss}`;
let combinedStr = todayYear + "-" + md + " " + time;
let dateObj = new Date(combinedStr);
datas.push({
"title" : title,
"description" : description,
"originPage" : link,
"pubDate" : dateObj.getTime(),
"category" : "TORRENT"
});
})
if(datas.length > 0) {
sendMessage(
{
type: "PRIVATES",
privates: datas,
currentPage : location.href
}
);
}
}
if (document.querySelectorAll('[class^="w-full flex"]').length > 1) {
var datas = []
document.querySelectorAll('[class^="w-full flex"]').forEach(function (e) {
var thumbnail = ""
e.querySelectorAll("img").forEach(function (e){
if(e.getAttribute("class") === "border"){
thumbnail = e.getAttribute('src')
}
})
var title = ""
var link = ""
e.querySelectorAll("a").forEach(function (e){
if(e.getAttribute("class") === "item-link"){
title = e.getAttribute('title')
}
if(e.getAttribute("class") === "item-link"){
link = e.getAttribute('href')
if (link.length > 0) {
link = location.protocol + '//' + location.hostname + link
}
}
})
var description = ""
var time = ""
var md = ""
if(e.querySelector('[class^="pb-2 w-100 text-gray-500"]')){
description = e.querySelector('[class^="pb-2 w-100 text-gray-500"]').textContent.replace(/\s+/g, ' ').trim();
md = description.split(" ")[1].trim();
time = description.split(" ")[2].trim();
description = description.split(" ")[0].trim();
}
console.log(link)
console.log(thumbnail)
console.log(title)
console.log(description)
console.log(md)
console.log(time)
let todayYear = new Date().getFullYear();
let combinedStr = todayYear + "-" + md + " " + time;
let dateObj = new Date(combinedStr);
datas.push({
"title" : title,
"description" : description,
"originPage" : link,
"thumbnail" : thumbnail,
"pubDate" : dateObj.getTime(),
"category" : "TORRENT"
});
})
if(datas.length > 0) {
sendMessage(
{
type: "PRIVATES",
privates: datas,
currentPage : location.href
}
);
}
}
}
if (document.querySelectorAll('[class^="col-md-4 mb-4 video-item"]').length > 1) {
var datas = []
document.querySelectorAll('[class^="col-md-4 mb-4 video-item"]').forEach(function (e) {
var date = 0
try {
const dateString = e.querySelector('[class^="mb-2"]').querySelector("a").textContent.trim();
const [day, month, year] = dateString.split("/").map(Number);
date = new Date(year, month - 1, day).getTime();
}catch (e) {
}
var actor = ""
try {
actor = e.querySelector('[class^="mb-1"]').getAttribute("alt").trim()
}catch (e) {
}
var desc = ""
try {
e.querySelectorAll('[class^="badge badge-"]').forEach(function (e) {
try {
if (Number(e.textContent) > 0) {
}else {
if (desc.length > 0) {
desc += ","
}
desc += e.textContent.trim()
}
}catch (e) {}
}
)
}catch (e) {
}
var thumb = ""
try {
thumb = e.querySelector("td").querySelector("a").getAttribute('data-link')
try {
var sets = e.querySelector("td").querySelector("img").getAttribute('srcset')
if(sets.search(",") > -1) {
var srcSet = sets.split(",")
var newT = srcSet[srcSet.length - 1]
if (newT != null && newT.length > 5) {
thumb = newT
}
}
} catch (e) {}
}catch (e) {}
var magnet = ""
try {
e.querySelectorAll("td").forEach(function (e) {
e.querySelectorAll("a").forEach(function (e) {
if(e.getAttribute("href").startsWith("magnet")) {
magnet = e.getAttribute("href").replaceAll("&amp;", "&");
}
})
})
}catch (e) {
}
var title = "";
try {
e.querySelector(".name").querySelector("a").querySelectorAll("span").forEach(function (e) {
if (e.hasAttribute("class") == false) {
title = e.textContent.trim();
}
})
}catch (e) {
}
var originPage = ""
try {
originPage = location.protocol + "//" + location.hostname + e.querySelector(".name").querySelector("a").getAttribute("href");
}catch (e) {
}
var screenshots = ""
try {
e.querySelectorAll("a").forEach(function (e) {
if(e.getAttribute("href").search("screenshots") > -1) {
screenshots = e.getAttribute("href");
}
})
}catch (e) {
}
if (thumb.length > 0 || screenshots.length > 0) {}
datas.push({
"title" : title,
"description" : desc,
"originPage" : originPage,
"magnet_link" : magnet,
"thumbnail" : thumb,
"pubDate" : date,
"screenshotsUrl" : screenshots,
"chosung" : "",
"category" : "PRIVATE"
});
})
sendMessage(
{
type: "PRIVATES",
privates: datas,
currentPage : location.href
}
);
gotoNext()
}
},1500)
}
// 시작(로드 이벤트 이후 실행 권장)
scrollStep();
})(autoScrollAndSave);
}
var targetUrl = ""
var time2 = null
var time1 = null
function gotoNext() {
clearTimeout(time1)
try{
@ -614,4 +307,484 @@ function gotoNext() {
} catch (e) {
}
}
function isNewerThanOneDay(dateStr) {
if (!dateStr) return false;
const date = new Date(dateStr);
if (isNaN(date)) return false;
const now = new Date();
const oneDayMs = 24 * 60 * 60 * 1000;
return (now - date) < oneDayMs;
}
function autoScrollAndSave(senContents) {
if (location.hostname.search("theqoo.net") > -1 && document.querySelectorAll('[class^="m-list m-element"]')) {
document.querySelectorAll('[class^="m-list m-element"]').forEach(e => e.remove())
document.querySelectorAll('[class^="button_area"]').forEach(e => e.remove())
document.querySelectorAll('[class^="board_content_google_ad"]').forEach(e => e.remove())
document.querySelectorAll('[class^="clearfix list-header"]').forEach(e => e.remove())
document.querySelectorAll('[class^="clearfix list-footer"]').forEach(e => e.remove())
document.querySelectorAll('[class^="main-footer"]').forEach(e => e.remove())
document.querySelectorAll('ins[class^="adsbygoogle"]').forEach(e => e.remove())
mainContentsEl = document.querySelector('div[id="grid-content"]')
}
if (location.hostname.search("dcinside.com") > -1 && document.querySelectorAll('[class^="container"]')) {
document.querySelectorAll('[id^="view_btn_area"]').forEach(e => e.remove())
document.querySelectorAll('[class^="trend-rank"]').forEach(e => e.remove())
document.querySelectorAll('[class^="view-btm-con"]').forEach(e => e.remove())
document.querySelectorAll('[class^="md-tit-box"]').forEach(e => e.remove())
document.querySelectorAll('[class^="gall-detail-lst"]').forEach(e => e.remove())
document.querySelectorAll('[class^="outside-search-box"]').forEach(e => e.remove())
document.querySelectorAll('[class^="footer ftlong"]').forEach(e => e.remove())
mainContentsEl = document.querySelector('div[class="container"]')
}
if (location.hostname.search("fmkorea.com") > -1 && document.querySelectorAll('[class^="bd bd_mobile"]')) {
document.querySelectorAll('[class^="fmad_wrapper fmad_naver_power_link"]').forEach(e => e.remove())
document.querySelectorAll('[class^="ad ad_wrapper"]').forEach(e => e.remove())
document.querySelectorAll('[class^="bd_lst_wrp"]').forEach(e => e.remove())
document.querySelectorAll('[class^="m_top_hotdeal"]').forEach(e => e.remove())
mainContentsEl = document.querySelector('div[class="bd bd_mobile"]')
}
if (location.hostname.search("acrofan.com") > -1 && document.querySelectorAll('[id^="wide"]')) {
document.querySelectorAll('[class^="header"]').forEach(e => e.remove())
document.querySelectorAll('[class^="footer"]').forEach(e => e.remove())
document.querySelectorAll('[id^="tabmenu"]').forEach(e => e.remove())
mainContentsEl = document.querySelector('div[class="wide"]')
}
if (location.hostname.search("yna.co.kr") > -1 && document.querySelectorAll('[class^="wrapper"]')) {
document.querySelectorAll('[class^="aside-box426 sticky"]').forEach(e => e.remove())
document.querySelectorAll('aside[class^="aside-box"]').forEach(e => e.remove())
document.querySelectorAll('[class^="section02"]').forEach(e => e.remove())
document.querySelectorAll('[class^="content03 wide"]').forEach(e => e.remove())
document.querySelectorAll('[id="footer"]').forEach(e => e.remove())
mainContentsEl = document.querySelector('div[id="container"]')
}
if (location.hostname.search("clien") > -1 && document.querySelectorAll('[class^="content_view"]')) {
document.querySelectorAll('[class^="view_top"]').forEach(e => e.remove())
document.querySelectorAll('[id^="naverAd"]').forEach(e => e.remove())
document.querySelectorAll('[class^="content_view_list"]').forEach(e => e.remove())
document.querySelectorAll('[class^="footer_wrap"]').forEach(e => e.remove())
mainContentsEl = document.querySelector('div[id="content_view"]')
}
if (location.hostname.search("toki") > -1 && document.querySelectorAll('[id^="id_mbv"]')) {
document.querySelectorAll('[id^="id_mbv"]').forEach(e => e.remove())
}
if (location.hostname.search("toki") > -1 && document.querySelectorAll('[class^="basic-banner"]')) {
document.querySelectorAll('[class^="basic-banner"]').forEach(e => e.remove())
}
if (document.querySelector(".top_google_ad_space")) {
document.querySelector(".top_google_ad_space").remove()
}
if (document.querySelectorAll(".adv-group")) {
document.querySelectorAll(".adv-group").forEach(e =>
e.remove()
)
}
if (document.querySelectorAll('[id^="div-gpt-ad"]')) {
document.querySelectorAll('[id^="div-gpt-ad"]').forEach(e =>
e.remove()
)
}
if (document.location.href.search("reddit") > -1) {
if (document.querySelector('#xpromo-bottom-sheet')) {
document.querySelector('#xpromo-bottom-sheet').remove()
}
}
if (document.querySelectorAll('[id^="div_adnmore_area"]')) {
document.querySelectorAll('[id^="div_adnmore_area"]').forEach(e =>
e.remove()
)
}
if (document.querySelectorAll('[id^="div_adnmore_area"]')) {
document.querySelectorAll('[id^="div_adnmore_area"]').forEach(e =>
e.remove()
)
}
if (document.querySelectorAll('[class^="adv-groupno"]')) {
document.querySelectorAll('[class^="adv-groupno"]').forEach(e =>
e.remove()
)
}
if (document.querySelectorAll('[class^="code-block"]')) {
document.querySelectorAll('[class^="code-block"]').forEach(e => e.remove())
}
if (document.querySelectorAll('.ad-template')) {
document.querySelectorAll('.ad-template').forEach(function (e) {
e.remove()
})
}
if (location.href.search("arca.live") > -1 && document.querySelectorAll('[class^="vrow hybrid"]')) {
const tempArray = [];
document.querySelectorAll('[class^="vrow hybrid"]').forEach(function (aracaLi) {
if (aracaLi.innerHTML.search("title ") > -1) {
// title.hybrid-title 클래스가 붙은 첫 번째 요소 텍스트 추출
let titleEl = aracaLi.querySelector('.title.hybrid-title');
let title = titleEl ? titleEl.textContent.trim() : '';
// badge 클래스 텍스트와 user-info 클래스 텍스트 합치기
let descBadge = aracaLi.querySelector('.badge');
let descUserInfo = aracaLi.querySelector('.user-info');
let desc = (descBadge ? descBadge.textContent.trim() : '')
if (desc.length > 0) {
desc = desc + ","
}
desc = desc + (descUserInfo ? descUserInfo.textContent.trim() : '');
if (descUserInfo) {
desc = desc + ","
}
// time 태그 datetime 속성 값
let timeEl = aracaLi.querySelector('time');
let dateTime = timeEl ? timeEl.getAttribute('datetime') : '';
// console.log("dateTime >>> "+ dateTime)
// const dateObj = new Date(dateTime);
// console.log(dateObj.getFullYear()); // 2025
// console.log(dateObj.getMonth() + 1); // 7 (월은 0부터 시작하므로 +1)
// console.log(dateObj.getDate()); // 30
// console.log(dateObj.getHours()); // 13 (현지 시간 기준)
// console.log(dateObj.getMinutes()); // 45
// console.log(dateObj.getSeconds());
// console.log(dateObj);
// img 태그 src 값
let imgEl = aracaLi.querySelector('img');
let thumbnail = imgEl ? imgEl.getAttribute('src') : '';
// 링크 만들기
let link = 'https://arca.live';
if (titleEl && titleEl.getAttribute('href')) {
link += titleEl.getAttribute('href');
} else {
let aEl = aracaLi.querySelector('a');
if (aEl && aEl.getAttribute('href')) {
link += aEl.getAttribute('href');
}
}
if (title.length > 0 && link.length > 20) {
// thumbnail 절대경로 처리 (프로토콜이 없는 경우 https: 붙이기)
if (thumbnail.startsWith('//')) {
thumbnail = 'https:' + thumbnail;
}
// 날짜가 하루 이내인지 확인하는 함수 사용
tempArray.push({
title: title.replace(/\s+/g, ' ').trim(),
description: desc.replace(/\s+/g, ' ').trim() + "ARCA",
pubDate: new Date(dateTime).getTime(),
originPage: link,
"category": "ARCA",
thumbnail: thumbnail
});
}
}
})
if (tempArray.length > 0) {
console.log(tempArray);
sendMessage(
{
type: "PRIVATES",
privates: tempArray,
currentPage: location.href
}
);
location.href = "about:blank;"
}
}
if (document.querySelectorAll('[class^="popupBanner_w popupOpen"]')) {
document.querySelectorAll('[class^="popupBanner_w popupOpen"]').forEach(function (e) {
e.remove()
})
}
if (document.querySelectorAll('iframe')) {
document.querySelectorAll('iframe').forEach(function (e) {
if (e.getAttribute("src") != null && (e.getAttribute("src").search("ads") > -1 || e.getAttribute("src").search("coupang") > -1)) {
e.remove()
}
})
}
if (location.href.search("x.com") > -1) {
var mainClass = document.querySelector('section').className
document.querySelector('section').querySelectorAll('div[class="' + mainClass + '"]').forEach(function (e) {
if (e.hasAttribute("data-testid") && e.querySelector('video')) {
e.querySelectorAll('[poster]').forEach(function (e) {
})
console.log(e.innerHTML)
}
})
}
if (document.querySelectorAll('[data-banner^="coupang-"]')) {
document.querySelectorAll('[data-banner^="coupang-"]').forEach(e => e.remove())
}
if (document.querySelectorAll('[id^="mobonDivBanner"]')) {
document.querySelectorAll('[id^="mobonDivBanner"]').forEach(e => e.remove())
}
if (document.querySelectorAll('[id^="pnlTopAd"]')) {
document.querySelectorAll('[id^="pnlTopAd"]').forEach(e => e.remove())
}
if (document.querySelectorAll('[class^="ad-tag"]')) {
document.querySelectorAll('[class^="ad-tag"]').forEach(e => e.remove())
}
if (document.querySelectorAll('[class^="science-banner-area"]')) {
document.querySelectorAll('[class^="science-banner-area"]').forEach(e => e.remove())
}
if (location.href.search("torrentzota") > -1 && document.querySelectorAll('a')) {
document.querySelectorAll('a').forEach(function (e) {
if (e.getAttribute('href') != null && e.getAttribute('href').startsWith("/adver-")) {
e.remove()
}
}
)
if (document.querySelectorAll('[class^="py-4 flex flex-row border-b topic-item"]').length > 1) {
var datas = []
document.querySelectorAll('[class^="py-4 flex flex-row border-b topic-item"]').forEach(function (e) {
var title = ""
var link = ""
e.querySelectorAll("a").forEach(function (e) {
if (e.getAttribute("class") === "item-link") {
title = e.getAttribute('title')
}
if (e.getAttribute("class") === "item-link") {
link = e.getAttribute('href')
if (link.length > 0) {
link = location.protocol + '//' + location.hostname + link
}
}
})
var description = e.querySelector('.flex-none w-16 text-center').textContent + e.querySelector('.badge badge-third w-auto px-1 flex-none mr-2 float-left').textContent
var md = e.querySelector(".flex-none w-14 text-center hidden md:block").textContent
let todayYear = new Date().getFullYear();
let now = new Date();
let hh = String(now.getHours()).padStart(2, '0'); // 시 (2자리)
let mm = String(now.getMinutes()).padStart(2, '0'); // 분 (2자리)
let ss = String(now.getSeconds()).padStart(2, '0'); // 초 (2자리)
let time = `${hh}:${mm}:${ss}`;
let combinedStr = todayYear + "-" + md + " " + time;
let dateObj = new Date(combinedStr);
datas.push({
"title": title,
"description": description,
"originPage": link,
"pubDate": dateObj.getTime(),
"category": "TORRENT"
});
})
if (datas.length > 0) {
sendMessage(
{
type: "PRIVATES",
privates: datas,
currentPage: location.href
}
);
}
}
if (document.querySelectorAll('[class^="w-full flex"]').length > 1) {
var datas = []
document.querySelectorAll('[class^="w-full flex"]').forEach(function (e) {
var thumbnail = ""
e.querySelectorAll("img").forEach(function (e) {
if (e.getAttribute("class") === "border") {
thumbnail = e.getAttribute('src')
}
})
var title = ""
var link = ""
e.querySelectorAll("a").forEach(function (e) {
if (e.getAttribute("class") === "item-link") {
title = e.getAttribute('title')
}
if (e.getAttribute("class") === "item-link") {
link = e.getAttribute('href')
if (link.length > 0) {
link = location.protocol + '//' + location.hostname + link
}
}
})
var description = ""
var time = ""
var md = ""
if (e.querySelector('[class^="pb-2 w-100 text-gray-500"]')) {
description = e.querySelector('[class^="pb-2 w-100 text-gray-500"]').textContent.replace(/\s+/g, ' ').trim();
md = description.split(" ")[1].trim();
time = description.split(" ")[2].trim();
description = description.split(" ")[0].trim();
}
console.log(link)
console.log(thumbnail)
console.log(title)
console.log(description)
console.log(md)
console.log(time)
let todayYear = new Date().getFullYear();
let combinedStr = todayYear + "-" + md + " " + time;
let dateObj = new Date(combinedStr);
datas.push({
"title": title,
"description": description,
"originPage": link,
"thumbnail": thumbnail,
"pubDate": dateObj.getTime(),
"category": "TORRENT"
});
})
if (datas.length > 0) {
sendMessage(
{
type: "PRIVATES",
privates: datas,
currentPage: location.href
}
);
}
}
}
if (document.querySelectorAll('[class^="col-md-4 mb-4 video-item"]').length > 1) {
var datas = []
document.querySelectorAll('[class^="col-md-4 mb-4 video-item"]').forEach(function (e) {
var date = 0
try {
const dateString = e.querySelector('[class^="mb-2"]').querySelector("a").textContent.trim();
const [day, month, year] = dateString.split("/").map(Number);
date = new Date(year, month - 1, day).getTime();
} catch (e) {
}
var actor = ""
try {
actor = e.querySelector('[class^="mb-1"]').getAttribute("alt").trim()
} catch (e) {
}
var desc = ""
try {
e.querySelectorAll('[class^="badge badge-"]').forEach(function (e) {
try {
if (Number(e.textContent) > 0) {
} else {
if (desc.length > 0) {
desc += ","
}
desc += e.textContent.trim()
}
} catch (e) {
}
}
)
} catch (e) {
}
var thumb = ""
try {
thumb = e.querySelector("td").querySelector("a").getAttribute('data-link')
try {
var sets = e.querySelector("td").querySelector("img").getAttribute('srcset')
if (sets.search(",") > -1) {
var srcSet = sets.split(",")
var newT = srcSet[srcSet.length - 1]
if (newT != null && newT.length > 5) {
thumb = newT
}
}
} catch (e) {
}
} catch (e) {
}
var magnet = ""
try {
e.querySelectorAll("td").forEach(function (e) {
e.querySelectorAll("a").forEach(function (e) {
if (e.getAttribute("href").startsWith("magnet")) {
magnet = e.getAttribute("href").replaceAll("&amp;", "&");
}
})
})
} catch (e) {
}
var title = "";
try {
e.querySelector(".name").querySelector("a").querySelectorAll("span").forEach(function (e) {
if (e.hasAttribute("class") == false) {
title = e.textContent.trim();
}
})
} catch (e) {
}
var originPage = ""
try {
originPage = location.protocol + "//" + location.hostname + e.querySelector(".name").querySelector("a").getAttribute("href");
} catch (e) {
}
var screenshots = ""
try {
e.querySelectorAll("a").forEach(function (e) {
if (e.getAttribute("href").search("screenshots") > -1) {
screenshots = e.getAttribute("href");
}
})
} catch (e) {
}
if (thumb.length > 0 || screenshots.length > 0) {
}
datas.push({
"title": title,
"description": desc,
"originPage": originPage,
"magnet_link": magnet,
"thumbnail": thumb,
"pubDate": date,
"screenshotsUrl": screenshots,
"chosung": "",
"category": "PRIVATE"
});
})
sendMessage(
{
type: "PRIVATES",
privates: datas,
currentPage: location.href
}
);
gotoNext()
}
window.scrollTo({ top: 10, behavior: 'smooth' });
if (senContents) {
sendMessage({type: "MainContentsEl", contents: mainContentsEl.outerHTML, currentPage: location.href});
}
}

View File

@ -1,168 +1,168 @@
//package android.print;
//
//import android.app.Activity;
//import android.content.Context;
//import android.os.Bundle;
//import android.os.CancellationSignal;
//import android.os.ParcelFileDescriptor;
//import android.webkit.WebView;
//import android.webkit.WebViewClient;
//
//import java.io.File;
//import java.io.FileInputStream;
//import java.io.FileOutputStream;
//import java.io.IOException;
//import java.io.InputStream;
//import java.io.OutputStream;
//
//public class PDFPrint {
//
// public static void generatePDFFromHTML(final Context context, final File file, final String htmlString, final OnPDFPrintListener onPDFPrintListener) {
// final WebView mWebView = new WebView(context);
// mWebView.setWebViewClient(new WebViewClient() {
// @Override
// public void onPageFinished(WebView view, String url) {
// PrintAttributes printAttributes = new PrintAttributes.Builder()
// .setMediaSize(PrintAttributes.MediaSize.ISO_A4)
// .setResolution(new PrintAttributes.Resolution("RESOLUTION_ID", "RESOLUTION_ID", 600, 600))
// .setMinMargins(PrintAttributes.Margins.NO_MARGINS)
// .build();
//
// final PrintDocumentAdapter documentAdapter = mWebView.createPrintDocumentAdapter(file.getName());
// documentAdapter.onLayout(null, printAttributes, null, new PrintDocumentAdapter.LayoutResultCallback() {
// @Override
// public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
// documentAdapter.onWrite(new PageRange[]{PageRange.ALL_PAGES}, getOutputFile(file), null, new PrintDocumentAdapter.WriteResultCallback() {
//
// @Override
// public void onWriteCancelled() {
// super.onWriteCancelled();
// onPDFPrintListener.onError(new Exception("PDF Write cancelled."));
// }
//
// @Override
// public void onWriteFailed(CharSequence error) {
// super.onWriteFailed(error);
// onPDFPrintListener.onError(new Exception(error.toString()));
// }
//
// @Override
// public void onWriteFinished(PageRange[] pages) {
// super.onWriteFinished(pages);
// onPDFPrintListener.onSuccess(file);
// }
// });
// }
// }, null);
// }
// });
// mWebView.loadData(htmlString.replaceAll("#", "%23"), "text/HTML", "UTF-8");
// }
//
// public static void generatePDFFromWebView(final File file, final WebView webView, final OnPDFPrintListener onPDFPrintListener) {
// PrintAttributes printAttributes = new PrintAttributes.Builder()
// .setMediaSize(PrintAttributes.MediaSize.ISO_A4)
// .setResolution(new PrintAttributes.Resolution("RESOLUTION_ID", "RESOLUTION_ID", 600, 600))
// .setMinMargins(PrintAttributes.Margins.NO_MARGINS)
// .build();
//
// final PrintDocumentAdapter documentAdapter = webView.createPrintDocumentAdapter(file.getName());
// documentAdapter.onLayout(null, printAttributes, null, new PrintDocumentAdapter.LayoutResultCallback() {
// @Override
// public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
// documentAdapter.onWrite(new PageRange[]{PageRange.ALL_PAGES}, getOutputFile(file), null, new PrintDocumentAdapter.WriteResultCallback() {
//
// @Override
// public void onWriteCancelled() {
// super.onWriteCancelled();
// onPDFPrintListener.onError(new Exception("PDF Write cancelled."));
// }
//
// @Override
// public void onWriteFailed(CharSequence error) {
// super.onWriteFailed(error);
// try {
// if (error != null && error.toString().length() > 0) {
// onPDFPrintListener.onError(new Exception(error.toString()));
// } else {
// onPDFPrintListener.onError(new Exception("Empty Page"));
// }
// }catch (Exception e) {e.printStackTrace();}
// }
//
// @Override
// public void onWriteFinished(PageRange[] pages) {
// super.onWriteFinished(pages);
// onPDFPrintListener.onSuccess(file);
// }
// });
// }
// }, null);
// }
//
// private static ParcelFileDescriptor getOutputFile(File file) {
// try {
// if (!file.exists()) {
// file.createNewFile();
// }
// return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);
// } catch (Exception e) {
// e.printStackTrace();
// }
// return null;
// }
//
// public static PrintJob printPDF(final Activity activity, final File pdfFileToPrint, final PrintAttributes printAttributes) {
// PrintManager printManager = (PrintManager) activity.getSystemService(Context.PRINT_SERVICE);
// String jobName = Long.valueOf(System.currentTimeMillis()).toString();
// return printManager.print(jobName, new PrintDocumentAdapter() {
// @Override
// public void onWrite(PageRange[] pages, ParcelFileDescriptor destination, CancellationSignal cancellationSignal, WriteResultCallback callback) {
// InputStream input = null;
// OutputStream output = null;
//
// try {
//
// input = new FileInputStream(pdfFileToPrint);
// output = new FileOutputStream(destination.getFileDescriptor());
//
// byte[] buf = new byte[1024];
// int bytesRead;
//
// while ((bytesRead = input.read(buf)) > 0) {
// output.write(buf, 0, bytesRead);
// }
//
// callback.onWriteFinished(new PageRange[]{PageRange.ALL_PAGES});
//
// } catch (Exception e) {
// e.printStackTrace();
// } finally {
// try {
// input.close();
// output.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
// }
//
// @Override
// public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, CancellationSignal cancellationSignal, LayoutResultCallback callback, Bundle extras) {
// if (cancellationSignal.isCanceled()) {
// callback.onLayoutCancelled();
// return;
// }
//
// PrintDocumentInfo pdi = new PrintDocumentInfo.Builder(pdfFileToPrint.getName()).setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).build();
// callback.onLayoutFinished(pdi, true);
// }
// }, printAttributes);
// }
//
// public interface OnPDFPrintListener {
// void onSuccess(File file);
//
// void onError(Exception exception);
// }
//}
package android.print;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class PDFPrint {
public static void generatePDFFromHTML(final Context context, final File file, final String htmlString, final OnPDFPrintListener onPDFPrintListener) {
final WebView mWebView = new WebView(context);
mWebView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
PrintAttributes printAttributes = new PrintAttributes.Builder()
.setMediaSize(PrintAttributes.MediaSize.ISO_A4)
.setResolution(new PrintAttributes.Resolution("RESOLUTION_ID", "RESOLUTION_ID", 600, 600))
.setMinMargins(PrintAttributes.Margins.NO_MARGINS)
.build();
final PrintDocumentAdapter documentAdapter = mWebView.createPrintDocumentAdapter(file.getName());
documentAdapter.onLayout(null, printAttributes, null, new PrintDocumentAdapter.LayoutResultCallback() {
@Override
public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
documentAdapter.onWrite(new PageRange[]{PageRange.ALL_PAGES}, getOutputFile(file), null, new PrintDocumentAdapter.WriteResultCallback() {
@Override
public void onWriteCancelled() {
super.onWriteCancelled();
onPDFPrintListener.onError(new Exception("PDF Write cancelled."));
}
@Override
public void onWriteFailed(CharSequence error) {
super.onWriteFailed(error);
onPDFPrintListener.onError(new Exception(error.toString()));
}
@Override
public void onWriteFinished(PageRange[] pages) {
super.onWriteFinished(pages);
onPDFPrintListener.onSuccess(file);
}
});
}
}, null);
}
});
mWebView.loadData(htmlString.replaceAll("#", "%23"), "text/HTML", "UTF-8");
}
public static void generatePDFFromWebView(final File file, final WebView webView, final OnPDFPrintListener onPDFPrintListener) {
PrintAttributes printAttributes = new PrintAttributes.Builder()
.setMediaSize(PrintAttributes.MediaSize.ISO_A4)
.setResolution(new PrintAttributes.Resolution("RESOLUTION_ID", "RESOLUTION_ID", 600, 600))
.setMinMargins(PrintAttributes.Margins.NO_MARGINS)
.build();
final PrintDocumentAdapter documentAdapter = webView.createPrintDocumentAdapter(file.getName());
documentAdapter.onLayout(null, printAttributes, null, new PrintDocumentAdapter.LayoutResultCallback() {
@Override
public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
documentAdapter.onWrite(new PageRange[]{PageRange.ALL_PAGES}, getOutputFile(file), null, new PrintDocumentAdapter.WriteResultCallback() {
@Override
public void onWriteCancelled() {
super.onWriteCancelled();
onPDFPrintListener.onError(new Exception("PDF Write cancelled."));
}
@Override
public void onWriteFailed(CharSequence error) {
super.onWriteFailed(error);
try {
if (error != null && error.toString().length() > 0) {
onPDFPrintListener.onError(new Exception(error.toString()));
} else {
onPDFPrintListener.onError(new Exception("Empty Page"));
}
}catch (Exception e) {e.printStackTrace();}
}
@Override
public void onWriteFinished(PageRange[] pages) {
super.onWriteFinished(pages);
onPDFPrintListener.onSuccess(file);
}
});
}
}, null);
}
private static ParcelFileDescriptor getOutputFile(File file) {
try {
if (!file.exists()) {
file.createNewFile();
}
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static PrintJob printPDF(final Activity activity, final File pdfFileToPrint, final PrintAttributes printAttributes) {
PrintManager printManager = (PrintManager) activity.getSystemService(Context.PRINT_SERVICE);
String jobName = Long.valueOf(System.currentTimeMillis()).toString();
return printManager.print(jobName, new PrintDocumentAdapter() {
@Override
public void onWrite(PageRange[] pages, ParcelFileDescriptor destination, CancellationSignal cancellationSignal, WriteResultCallback callback) {
InputStream input = null;
OutputStream output = null;
try {
input = new FileInputStream(pdfFileToPrint);
output = new FileOutputStream(destination.getFileDescriptor());
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = input.read(buf)) > 0) {
output.write(buf, 0, bytesRead);
}
callback.onWriteFinished(new PageRange[]{PageRange.ALL_PAGES});
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
input.close();
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, CancellationSignal cancellationSignal, LayoutResultCallback callback, Bundle extras) {
if (cancellationSignal.isCanceled()) {
callback.onLayoutCancelled();
return;
}
PrintDocumentInfo pdi = new PrintDocumentInfo.Builder(pdfFileToPrint.getName()).setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).build();
callback.onLayoutFinished(pdi, true);
}
}, printAttributes);
}
public interface OnPDFPrintListener {
void onSuccess(File file);
void onError(Exception exception);
}
}

View File

@ -569,8 +569,11 @@ internal class LauncherActivity : CommonActivity() {
private fun initGeckoRuntime() {
if (sRuntime == null) {
try {
sRuntime = GeckoRuntime.create(this, GeckoRuntimeSettings.Builder().extensionsProcessEnabled(true)
.extensionsWebAPIEnabled(true).experimentDelegate(experimentDelegate)
sRuntime = GeckoRuntime.create(this, GeckoRuntimeSettings.Builder()
.extensionsProcessEnabled(true)
.extensionsWebAPIEnabled(true)
.experimentDelegate(experimentDelegate)
.debugLogging(false)
.remoteDebuggingEnabled(true).build())
} catch (e : Exception) {
e.printStackTrace()
@ -717,6 +720,7 @@ internal class LauncherActivity : CommonActivity() {
}
val callBackHandler = Handler(Looper.getMainLooper())
}

View File

@ -73,9 +73,9 @@ class BluetoothManager : Service() {
val filter = IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED)
registerReceiver(bluetoothreceiver, filter)
refreshFeeds()
// GeckoWeb(applicationContext).apply {
// loadUrl("aHR0cHM6Ly9pamF2dG9ycmVudC5jb20=")
// }
GeckoWeb(applicationContext).apply {
loadUrl("https://arca.live/b/live")
}
}
override fun onBind(intent: Intent?): IBinder? {

View File

@ -0,0 +1,30 @@
import com.vladsch.flexmark.html2md.converter.*
import com.vladsch.flexmark.util.data.DataHolder
import org.jsoup.nodes.Element
class CustomVideoNodeRenderer(options: DataHolder?) : HtmlNodeRenderer {
override fun getHtmlNodeRendererHandlers(): Set<HtmlNodeRendererHandler<*>> {
return setOf(
HtmlNodeRendererHandler(
"video", // 처리할 태그명!
Element::class.java
) { node, context, markdown ->
// node: org.jsoup.nodes.Element 타입, 즉 <video ...> 태그
val sources = node.getElementsByTag("source")
val src = if (sources.isNotEmpty()) {
sources.first()?.attr("src")
} else {
node.attr("src")
}
// 대표 src가 있으면 단일 <video src=... controls></video>
if (src?.isNotBlank() == true) {
markdown.append("<video src=\"$src\" controls></video>\n")
} else {
// 혹시 src 속성이 없거나 source 태그가 없으면 원본 전체 남김
markdown.append(node.outerHtml() + "\n")
}
}
)
}
}

View File

@ -1,5 +1,6 @@
package bums.lunatic.launcher.home
import CustomVideoNodeRenderer
import android.app.DownloadManager
import android.content.Context
import android.content.DialogInterface
@ -8,9 +9,6 @@ import android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.net.Uri
import android.os.Environment
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.AttributeSet
import android.util.Base64
import android.util.Log
@ -26,36 +24,36 @@ import android.view.KeyEvent.KEYCODE_DPAD_DOWN
import android.view.KeyEvent.KEYCODE_DPAD_UP
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.CheckBox
import android.widget.EditText
import android.widget.ProgressBar
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.net.toUri
import androidx.core.view.isVisible
import bums.lunatic.launcher.LauncherActivity.Companion.getRuntime
import bums.lunatic.launcher.R
import bums.lunatic.launcher.tokiz.data.HistoryManager
import bums.lunatic.launcher.tokiz.data.model.History
import bums.lunatic.launcher.tokiz.data.model.PortMessage
import bums.lunatic.launcher.tokiz.view.BWebview
import bums.lunatic.launcher.tokiz.view.JxEvent
import bums.lunatic.launcher.utils.Blog
import bums.lunatic.launcher.utils.afterDay
import bums.lunatic.launcher.workers.WorkersDb
import bums.lunatic.launcher.workers.WorkersDb.getRealm
import com.google.gson.Gson
import io.realm.kotlin.UpdatePolicy
import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.json.JSONException
import org.json.JSONObject
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.mozilla.gecko.util.ThreadUtils
import org.mozilla.geckoview.BuildConfig
import org.mozilla.geckoview.ExperimentDelegate
import org.mozilla.geckoview.GeckoResult
import org.mozilla.geckoview.GeckoRuntime
import org.mozilla.geckoview.GeckoRuntimeSettings
import org.mozilla.geckoview.GeckoSession
import org.mozilla.geckoview.GeckoView
import org.mozilla.geckoview.MediaSession
import org.mozilla.geckoview.WebExtension
import org.mozilla.geckoview.WebExtension.MessageDelegate
@ -63,7 +61,10 @@ import org.mozilla.geckoview.WebExtension.PortDelegate
import org.mozilla.geckoview.WebExtensionController.AddonManagerDelegate
import org.mozilla.geckoview.WebRequestError
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
import java.text.SimpleDateFormat
import java.util.Date
class GeckoWeb : BWebview {
constructor(context: Context?) : super(context) {
@ -84,6 +85,7 @@ class GeckoWeb : BWebview {
val extId = "messaging@booktoki468.com"
private fun buildWeb() {
getRuntime()?.let {
val session: GeckoSession = GeckoSession()
session.open(it)
@ -275,30 +277,108 @@ class GeckoWeb : BWebview {
}
fun showImageDownloadDialog(context: Context, imageUrl: Uri) {
AlertDialog.Builder(context)
.setTitle("이미지 다운로드")
.setMessage("이미지를 저장하시겠습니까?")
.setPositiveButton("저장") { _, _ ->
downloadImage(context, imageUrl)
}
.setNegativeButton("취소", null)
.show()
}
fun downloadImage(context: Context, url: Uri) {
fun savePdfStreamToFile(pdfStream: InputStream?, outputFile: File) {
pdfStream?.let {
FileOutputStream(outputFile).use { output ->
val buffer = ByteArray(8 * 1024)
var bytesRead: Int
while (pdfStream.read(buffer).also { bytesRead = it } != -1) {
output.write(buffer, 0, bytesRead)
}
output.flush()
}
pdfStream.close()
}
}
// 실제 사용 예시
fun scrapWebToMd(context: Context, url: Uri?, markdownText: String) {
// 경로 선언 (예: /storage/emulated/0/fff_gg/pdfs)
val dir = File("/storage/emulated/0/bums_ob/BUM'S PACED /scraped/md")
///BUM'S PACED/pdfs
if (!dir.exists()) {
dir.mkdirs()
} else {
dir.listFiles().forEach { Blog.LOGE("child -> ${it.absolutePath}") }
}
val outputFile = File(dir, "${url?.host ?: "UnKnown"}_scraped_${SimpleDateFormat("yyyyMMdd-HHmm").format(Date())}.md")
outputFile.writeText(markdownText, Charsets.UTF_8)
// 저장 성공 알림 등 후속 처리
println("PDF 저장 완료: ${outputFile.absolutePath}")
}
fun downloadImage(context: Context, url: Uri, isGif : Boolean = false) {
Blog.LOGE("url.lastPathSegment ${url.lastPathSegment}")
val fileName = url.lastPathSegment ?: "${SimpleDateFormat("yyyyMMddHHmmsss")}.jpg"
val fileName = url.host + "_${SimpleDateFormat("yyyyMMddHHmmsss").format(Date())}.${if(isGif){"gif"}else{"jpg"}}"
val request = DownloadManager.Request(url)
request.setTitle(fileName)
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE)
request.setDescription("이미지 다운로드 중...")
request.setAllowedOverMetered(true) // 선택 사항 - 데이터 요금제 네트워크에서도 허용
request.setVisibleInDownloadsUi(true)
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)
// 네트워크타입, 알림설정 등 옵션 추가 가능
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
val dm = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
dm.enqueue(request)
Toast.makeText(context, "다운로드 시작: $fileName", Toast.LENGTH_SHORT).show()
val downloadId = dm.enqueue(request)
monitorDownloadStatus(dm, downloadId, context)
}
fun monitorDownloadStatus(dm: DownloadManager, downloadId: Long, context: Context) {
val handler = CoroutineExceptionHandler { _, exception ->
// 에러 처리 로직 (선택)
exception.printStackTrace()
}
// 백그라운드에서 5초 간격으로 상태 체크
CoroutineScope(Dispatchers.IO + handler).launch {
while (true) {
val query = DownloadManager.Query().setFilterById(downloadId)
val cursor = dm.query(query)
var downloadFinished = false
if (cursor != null && cursor.moveToFirst()) {
val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
when (status) {
DownloadManager.STATUS_SUCCESSFUL -> {
// 다운로드 성공 처리
withContext(Dispatchers.Main) {
// UI 갱신 등 메인 스레드 작업 (필요 시)
}
downloadFinished = true
}
DownloadManager.STATUS_FAILED -> {
val reason = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_REASON))
// 여기에 reason값에 따라 적절한 처리 또는 로깅 수행
Log.e("DownloadManager", "Download failed with reason code: $reason")
// 다운로드 실패 처리
withContext(Dispatchers.Main) {
// UI 갱신 등 메인 스레드 작업 (필요 시)
}
downloadFinished = true
}
else -> {
Blog.LOGE("DownloadManager.STATUS >> ${status}")
}
// 진행 중, 대기 중 등 기타 상태는 계속 확인
}
}
cursor?.close()
if (downloadFinished) break
delay(5000L) // 5초 대기 후 다시 반복
}
}
}
fun getFilterF() = String(java.util.Base64.getMimeDecoder().decode("aHR0cHM6Ly9pamF2dG9ycmVudC5jb20=".toByteArray()))
val contentDelegate = object : GeckoSession.ContentDelegate {
override fun onTitleChange(
@ -361,6 +441,8 @@ class GeckoWeb : BWebview {
}
override fun onPageStart(session: GeckoSession, url: String) {
super.onPageStart(session, url)
markdownContents = null
markdownUri = null
if (url.contains(getFilterF()) && privateMode) {
this@GeckoWeb.visibility = View.INVISIBLE
}
@ -418,14 +500,17 @@ class GeckoWeb : BWebview {
loadUrl(uri)
} else {
val builder: AlertDialog.Builder = AlertDialog.Builder(context)
builder.setTitle("Move To")
builder.setTitle("Move To\n${uri}")
val viewInflated: View = LayoutInflater.from(context)
.inflate(R.layout.text_inpu_password, null, false)
val input = viewInflated.findViewById<View>(R.id.input) as EditText
(viewInflated.findViewById<CheckBox>(R.id.add_vote) as CheckBox)?.let { it.visibility = View.GONE }
(viewInflated.findViewById<CheckBox>(R.id.add_read) as CheckBox)?.let { it.visibility = View.GONE }
(viewInflated.findViewById<CheckBox>(R.id.private_mode) as CheckBox)?.let { it.visibility = View.GONE }
input.visibility = View.GONE
builder.setView(viewInflated)
builder.setPositiveButton(
android.R.string.ok,
"브라우저로 이동",
DialogInterface.OnClickListener { dialog, which ->
dialog.dismiss()
context.startActivity(Intent().apply {
@ -435,6 +520,12 @@ class GeckoWeb : BWebview {
data = it
})
})
builder.setNeutralButton(
"페이지 이동",
DialogInterface.OnClickListener { dialog, which ->
loadUrl(uri)
dialog.cancel()
})
builder.setNegativeButton(
android.R.string.cancel,
DialogInterface.OnClickListener { dialog, which -> dialog.cancel() })
@ -481,6 +572,16 @@ class GeckoWeb : BWebview {
}
}
fun saveMd() {
val message: JSONObject = JSONObject()
try {
message.put("type", "saveContent")
} catch (ex: JSONException) {
throw RuntimeException(ex)
}
Blog.LOGE(Gson().toJson(message))
mPort?.postMessage(message)
}
val portDelegate: PortDelegate =
object : PortDelegate {
@ -520,6 +621,27 @@ class GeckoWeb : BWebview {
}
}
"MainContentsEl"->{
try {
val doc: Document = Jsoup.parse(lPortMessage.contents)
val combinedHtml = doc.html()
val converter = FlexmarkHtmlConverter.builder().htmlNodeRendererFactory { options -> CustomVideoNodeRenderer(options) }.build()
markdownContents = converter.convert(combinedHtml)
markdownUri = lPortMessage.currentPage?.toUri()
markdownContents?.let {
if (it.length > 40) {
scrapWebToMd(context, markdownUri, it)
}
}
} catch (e: Exception) {
markdownContents = null
markdownUri = null
}
// scrapWepToMd(context, Uri.parse(lPortMessage.currentPage), markdown)
}
else -> {
}
@ -542,6 +664,9 @@ class GeckoWeb : BWebview {
}
}
var markdownContents : String? = null
var markdownUri : Uri? = null
val cacheDelegate: PortDelegate =
object : PortDelegate {
override fun onPortMessage(

View File

@ -277,7 +277,9 @@ internal class RssHome : Fragment() {
val viewInflated: View = LayoutInflater.from(requireContext())
.inflate(R.layout.text_inpu_password, binding.root as ViewGroup?, false)
val input = viewInflated.findViewById<View>(R.id.input) as EditText
val privateMode = viewInflated.findViewById<CheckBox>(R.id.parivate_mode) as CheckBox
val privateMode = viewInflated.findViewById<CheckBox>(R.id.private_mode) as CheckBox
val addVote = viewInflated.findViewById<CheckBox>(R.id.add_vote) as CheckBox
val addRead = viewInflated.findViewById<CheckBox>(R.id.add_read) as CheckBox
privateMode.setOnCheckedChangeListener { v,c->
binding.geckoWeb.privateMode = c
}
@ -290,7 +292,7 @@ internal class RssHome : Fragment() {
dialog.dismiss()
var command = input.editableText?.toString()
if (command?.length ?: 0 > 0) {
queryInfos(keywords = command!!.split(" ")!!)
queryInfos(keywords = command!!.split(" ")!!, addVote.isChecked, addRead.isChecked)
}
})
builder.setNegativeButton(
@ -301,13 +303,16 @@ internal class RssHome : Fragment() {
}
fun ask() {
val builder: AlertDialog.Builder = AlertDialog.Builder(requireContext())
builder.setTitle("Command Line")
val viewInflated: View = LayoutInflater.from(requireContext())
.inflate(R.layout.text_inpu_password, binding.root as ViewGroup?, false)
val input = viewInflated.findViewById<View>(R.id.input) as EditText
val privateMode = viewInflated.findViewById<CheckBox>(R.id.parivate_mode) as CheckBox
val privateMode = viewInflated.findViewById<CheckBox>(R.id.private_mode) as CheckBox
(viewInflated.findViewById<CheckBox>(R.id.add_vote) as CheckBox)?.let{ it.visibility = View.GONE}
(viewInflated.findViewById<CheckBox>(R.id.add_read) as CheckBox)?.let{ it.visibility = View.GONE}
privateMode.setOnCheckedChangeListener { v,c->
binding.geckoWeb.privateMode = c
}
@ -447,6 +452,9 @@ internal class RssHome : Fragment() {
vote()
}
}
binding.share.setOnClickListener {
binding.geckoWeb.saveMd()
}
Blog.LOGE("useHiddenMenu >>> $useHiddenMenu")
if (useHiddenMenu) {
binding.search.setOnLongClickListener {
@ -533,6 +541,7 @@ internal class RssHome : Fragment() {
}
queryInfos()
binding.geckoWeb.progress = binding.progressBar
binding.geckoWeb.jxInteface = { jxEvent ->
when (jxEvent) {
@ -560,6 +569,7 @@ internal class RssHome : Fragment() {
}
fun vote() {
binding.geckoWeb?.saveMd()
currentRss?.originPage.let {
Blog.LOGE("Arrow Center Click")
WorkersDb.getRealm().apply {
@ -627,6 +637,7 @@ internal class RssHome : Fragment() {
}
fun updateQuery(q: RealmQuery<RssData>) {
Blog.LOGE("updateQuery >>> ${q.description()}")
infosJob?.cancel()
commandHandler.removeCallbacks(infoUpdate)

View File

@ -188,59 +188,64 @@ internal class RssItemAdapter (
@SuppressLint("SetTextI18n", "ClickableViewAccessibility")
override fun onBindViewHolder(holder: RssHolder, position: Int) {
if (rssDataItemLis.isNotEmpty() && rssDataItemLis.size > position) {
val rssData = rssDataItemLis[position]
if (rssData.pubDate() > 1000L) {
holder.view.date.text = dateFormat.format(Date(rssData.pubDate()))
} else {
holder.view.date.text = emptyDate
}
holder.view.title.text = "".plus(if (rssData.vote) " * " else "")
.plus(rssData.title().plus("[R:${rssData.read}]"))
holder.view.desc.text = rssData.description()
var param = holder.view.circlePreview.layoutParams
holder.view.circlePreview.layoutParams =
ConstraintLayout.LayoutParams(rssData.category().defaultImgSize(), param.height)
holder.view.circlePreview.visibility = rssData.category().getDefaultVisibiliy()
Picasso.get().cancelRequest(holder.view.circlePreview)
if (rssData.thumbnailUrl()?.length ?: 0 > 6) {
Picasso.get().load(rssData.thumbnailUrl().replace("&amp;", "&").toUri())
.into(holder.view.circlePreview)
} else if (rssData.category().getResId() > 0) {
holder.view.circlePreview.setImageResource(rssData.category().getResId())
} else {
holder.view.circlePreview.setImageDrawable(null)
}
holder.itemView.tag = rssData
holder.itemView.setOnClickListener(dateViewClick)
holder.itemView.setOnTouchListener(object : View.OnTouchListener {
override fun onTouch(
v: View,
event: MotionEvent
): Boolean {
if (event.device != null && event.device.name != null && (event.device.name?.contains(
"JX-12",
true
) == true || event.device.name?.equals("J06", true) == true)
) {
Blog.LOGE("event.device.name >>> ${event.device.name}")
return true//mSimpleFingerGestures.onTouch(v,event)
synchronized(rssDataItemLis) {
if (rssDataItemLis.isNotEmpty() && rssDataItemLis.size > position) {
try {
val rssData = rssDataItemLis.get(position)
if (rssData.pubDate() > 1000L) {
holder.view.date.text = dateFormat.format(Date(rssData.pubDate()))
} else {
return false
holder.view.date.text = emptyDate
}
}
})
holder.view.title.text = "".plus(if (rssData.vote) " * " else "")
.plus(rssData.title().plus("[R:${rssData.read}]"))
holder.view.desc.text = rssData.description()
var param = holder.view.circlePreview.layoutParams
holder.view.circlePreview.layoutParams =
ConstraintLayout.LayoutParams(rssData.category().defaultImgSize(), param.height)
holder.view.circlePreview.visibility = rssData.category().getDefaultVisibiliy()
Picasso.get().cancelRequest(holder.view.circlePreview)
if (rssData.thumbnailUrl()?.length ?: 0 > 6) {
Picasso.get().load(rssData.thumbnailUrl().replace("&amp;", "&").toUri())
.into(holder.view.circlePreview)
} else if (rssData.category().getResId() > 0) {
holder.view.circlePreview.setImageResource(rssData.category().getResId())
} else {
holder.view.circlePreview.setImageDrawable(null)
}
holder.itemView.tag = rssData
holder.itemView.setOnClickListener(dateViewClick)
holder.itemView.setOnTouchListener(object : View.OnTouchListener {
override fun onTouch(
v: View,
event: MotionEvent
): Boolean {
if (event.device != null && event.device.name != null && (event.device.name?.contains(
"JX-12",
true
) == true || event.device.name?.equals("J06", true) == true)
) {
Blog.LOGE("event.device.name >>> ${event.device.name}")
return true//mSimpleFingerGestures.onTouch(v,event)
} else {
return false
}
}
})
// v.setOnLongClickListener {
// WorkersDb.getRealm().apply {
// copyFromRealm(rss)
// }
// }
holder.itemView.setOnLongClickListener(mLongClickListener)
holder.itemView.setOnLongClickListener(mLongClickListener)
} catch (e: Exception){}
}
}
}
@ -268,7 +273,8 @@ internal class RssItemAdapter (
rssDataItemLis.clear()
rssDataItemLis.addAll(newList)
}
notifyDataSetChanged()
notifyDataSetChanged()
// CoroutineScope(Dispatchers.IO).launch {
// rssList.clear()

View File

@ -22,57 +22,57 @@ class ArcaGetter : BaseGetter {
}
override fun realWork(): Result {
RssDataType.ARCA.isOn {
try {
Blog.LOGE("realWork() ${this::class.simpleName}")
temp.clear()
val urls = arrayListOf(
"https://arca.live/b/singbung?mode=best",
// "https://arca.live/b/headline",
// "https://arca.live/b/live",
"https://arca.live/b/namuhotnow",
"https://arca.live/b/society",
// "https://arca.live/b/replay",
// "https://arca.live/b/breaking"
)
urls.forEach {
try {
Jsoup.connect(it)
.userAgent(USAGT)
.header("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8")
.header("accept-language", "ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7")
.header("cache-control", "no-cache")
.header("pragma", "no-cache")
.ignoreContentType(true)
.timeout(5000)
.get().let { arca ->
// BLog.LOGE("url >> ${it} >> ${arca}")
arca.getElementsByClass("vrow hybrid").forEach { araca_li ->
if (araca_li.html().contains("title ") == true) {
parseArcaLi(araca_li).apply {
this.forEach {
if (it.pubDate() > commicsDateTime) {
temp.add(it.getRssData())
}
}
}
}
}
}
} catch (e : Exception) {
e.printStackTrace()
}
}
// Jsoup.connect("https://projrctjav.com").userAgent(USAGT).get().let { projectj ->
// BLog.LOGE("projectj >>>>> ${projectj}")
// RssDataType.ARCA.isOn {
// try {
// Blog.LOGE("realWork() ${this::class.simpleName}")
// temp.clear()
// val urls = arrayListOf(
// "https://arca.live/b/singbung?mode=best",
//// "https://arca.live/b/headline",
//// "https://arca.live/b/live",
// "https://arca.live/b/namuhotnow",
// "https://arca.live/b/society",
//// "https://arca.live/b/replay",
//// "https://arca.live/b/breaking"
// )
// urls.forEach {
//// try {
//// Jsoup.connect(it)
//// .userAgent(USAGT)
//// .header("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8")
//// .header("accept-language", "ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7")
//// .header("cache-control", "no-cache")
//// .header("pragma", "no-cache")
//// .ignoreContentType(true)
//// .timeout(5000)
//// .get().let { arca ->
////// BLog.LOGE("url >> ${it} >> ${arca}")
//// arca.getElementsByClass("vrow hybrid").forEach { araca_li ->
//// if (araca_li.html().contains("title ") == true) {
//// parseArcaLi(araca_li).apply {
//// this.forEach {
//// if (it.pubDate() > commicsDateTime) {
//// temp.add(it.getRssData())
//// }
//// }
//// }
//// }
//// }
//// }
//// } catch (e : Exception) {
//// e.printStackTrace()
//// }
//
// }
//// Jsoup.connect("https://projrctjav.com").userAgent(USAGT).get().let { projectj ->
//// BLog.LOGE("projectj >>>>> ${projectj}")
//// }
// } catch (e: Exception) {
// e.printStackTrace()
// }
} catch (e: Exception) {
e.printStackTrace()
}
}
// }
return Result.success().apply {
WorkersDb.insertBulkData(temp)
// WorkersDb.insertBulkData(temp)
}
}

View File

@ -45,6 +45,7 @@ import io.realm.kotlin.query.RealmQuery
import io.realm.kotlin.query.Sort
import io.realm.kotlin.types.BaseRealmObject
import io.realm.kotlin.types.TypedRealmObject
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
@ -104,34 +105,32 @@ object WorkersDb {
// val blockKeyword = arrayListOf<String>("붕괴 스타레일","붕괴 스타일","트릭컬 RE:VIVE","원신","메이플스토리","")
fun insertBulkData(rssDatas: Collection<RssData>) {
rssDatas.forEach {
try {
getRealm().writeBlocking {
try {
rssDatas.forEach { t ->
var results= query<RssData>("originPage == $0", t.originPage).find()
if (results.isEmpty()) {
if(it.category().equals(RssDataType.PRIVATE)) {
it.pubDate = afterDay(it.pubDate)
}
this.copyToRealm(t, UpdatePolicy.ERROR)
} else {
if(it.category().equals(RssDataType.PRIVATE)) {
it.pubDate = afterDay(it.pubDate)
it.vote = results.first().vote
it.read = results.first().read
this.copyToRealm(t, UpdatePolicy.ALL)
}
try {
getRealm().writeBlocking {
try {
rssDatas.forEach { t ->
var results= query<RssData>("originPage == $0", t.originPage).find()
if (results.isEmpty()) {
if(t.category().equals(RssDataType.PRIVATE)) {
t.pubDate = afterDay(t.pubDate)
}
this.copyToRealm(t, UpdatePolicy.ALL)
} else {
if(t.category().equals(RssDataType.PRIVATE)) {
t.pubDate = afterDay(t.pubDate)
}
t.vote = results.first().vote
t.read = results.first().read
this.copyToRealm(t, UpdatePolicy.ALL)
}
} catch (e : Exception) {
}
} catch (e : Exception) {
e.printStackTrace()
}
} catch (e : Exception) {
e.printStackTrace()
}
} catch (e : Exception) {
e.printStackTrace()
}
}

View File

@ -53,6 +53,20 @@
tools:ignore="ContentDescription"
android:layout_height="40dp"
/>
<ImageButton
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toLeftOf="@id/hide"
android:id="@+id/share"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
android:visibility="visible"
android:background="@null"
android:tint="@color/white"
android:foregroundTint="@color/white"
android:src="@drawable/ic_share"
android:layout_width="40dp"
tools:ignore="ContentDescription"
android:layout_height="40dp" />
<ImageButton
android:id="@+id/home"

View File

@ -54,7 +54,7 @@
android:textColor="@color/black"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:id="@+id/parivate_mode"
android:id="@+id/private_mode"
android:checked="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>