From 333f898346b783b029a44b084925f42e33d0d175 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Sun, 14 Jul 2024 22:32:07 +0700 Subject: [PATCH 01/47] guland vip --- popup/tabs.js | 1 + scripts/_index.js | 1 + scripts/guland_VIP.js | 26 ++++++++++++++++++++++++++ scripts/guland_VIP.png | Bin 0 -> 140756 bytes 4 files changed, 28 insertions(+) create mode 100644 scripts/guland_VIP.js create mode 100644 scripts/guland_VIP.png diff --git a/popup/tabs.js b/popup/tabs.js index 9dc1a71a..f59694ff 100644 --- a/popup/tabs.js +++ b/popup/tabs.js @@ -837,6 +837,7 @@ const tabs = [ s.scribd_bypassPreview, s.studyphim_unlimited, s.bypass_learnAnything, + s.guland_VIP, createTitle("--- Unlock function ---", "--- Mở khoá chức năng ---"), s.simpleAllowCopy, s.detect_zeroWidthCharacters, diff --git a/scripts/_index.js b/scripts/_index.js index 492beea0..f50da941 100644 --- a/scripts/_index.js +++ b/scripts/_index.js @@ -172,3 +172,4 @@ export { default as youtube_getVideoThumbnail } from "./youtube_getVideoThumbnai export { default as youtube_getVideoCaption } from "./youtube_getVideoCaption.js"; export { default as youtube_changeCountry } from "./youtube_changeCountry.js"; export { default as fb_autoLike } from "./fb_autoLike.js"; +export { default as guland_VIP } from "./guland_VIP.js"; diff --git a/scripts/guland_VIP.js b/scripts/guland_VIP.js new file mode 100644 index 00000000..fa3172ba --- /dev/null +++ b/scripts/guland_VIP.js @@ -0,0 +1,26 @@ +import { UfsGlobal } from "./content-scripts/ufs_global.js"; + +export default { + icon: "https://guland.vn/bds/img/apple-touch-icon.png", + name: { + en: "Guland VIP", + vi: "Guland VIP - Xem quy hoạch đất", + }, + description: { + en: "VIP for Guland.vn, view map without restriction", + vi: "Xem quy hoạch đất không bị làm phiền bởi popup vip tại Guland.vn", + img: "/scripts/guland_VIP.png", + }, + infoLink: "https://guland.vn", + changeLogs: { + "2024-07-14": "init", + }, + whiteList: ["https://guland.vn/*"], + + contentScript: { + onDocumentStart: (details) => { + UfsGlobal.DOM.deleteElements(".modal-backdrop"); + UfsGlobal.DOM.deleteElements("#Modal-NotificationWithButton"); + }, + }, +}; diff --git a/scripts/guland_VIP.png b/scripts/guland_VIP.png new file mode 100644 index 0000000000000000000000000000000000000000..31c7b350cea07727cd5a7efbae43f35a6b047d06 GIT binary patch literal 140756 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℘0IA~>by&l2TXX{F5h@t0rzWbd<=eHHa7{NLK^ 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 z⁡kk1bck#!8${nXtU*4}S8XceuSXEp?(({AbK&fX?PON1}>m%wD$a$nzUEY!zX( z5v*&;9YEq#6sugaZsR6aNM%_i8CoZI%;S)*NZoOg9Sfz^W~`YIRm>q+4aCjT&mii=A}DsjJF_J6at@08|8TOMUbOis~ayGYknztJQ=U z*22g_l=W3lAg5!}i67c=@h1~1QpGu+i_@a03Sdgm&W6f*DoGkgbF-LTf?!zx^@5#+t6uL!})B3iL|9-s18Y+qND zlw_-Tz8Xdvaf%gtvrYvczS)H}(qy%nIJKJPh>J1>=*${OP{qf)+kWbZ&zOO9Ju?@d1^v9eE`bc+*=*zU+yN& z`<`<;Te75%AV_q!paRU8!8b1h!r-mCEJPfYA<>d#h`zNOwxIMKM4$d}d(!H1boL3?*I29;RbF09z#QlWb0$Kb-tSV8lTxHkSlJBV=)-AL z)K-yF0n%mcn}b?*1di4+7M_GrSxGVV5kh_vVLNZdyd6YoQQGr+s;3dAT%MZb0UEVc ziy�B{9(zpoc6X&sh_DXG7pic3R5_%Zoa={$y3k1 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 O0000 Date: Sun, 14 Jul 2024 22:32:13 +0700 Subject: [PATCH 02/47] fix icon --- scripts/studyphim_unlimited.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/studyphim_unlimited.js b/scripts/studyphim_unlimited.js index aaf8dc71..24830838 100644 --- a/scripts/studyphim_unlimited.js +++ b/scripts/studyphim_unlimited.js @@ -1,7 +1,7 @@ import { UfsGlobal } from "./content-scripts/ufs_global.js"; export default { - icon: "https://www.studyphim.vn/assets/ico/favicon.ico", + icon: "https://s2.googleusercontent.com/s2/favicons?domain=www.studyphim.vn", name: { en: "Studyphim - Watch free movies", vi: "Studyphim - Xem miễn phí", From 25614649e7a2426377b7731ff08053172927b0f6 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Tue, 16 Jul 2024 18:42:10 +0700 Subject: [PATCH 03/47] pip anything + pick element (WIP) --- popup/tabs.js | 1 + scripts/_index.js | 1 + scripts/content-scripts/ufs_global.js | 215 ++++++++++++++++++++++++++ scripts/pip_anything.js | 72 +++++++++ 4 files changed, 289 insertions(+) create mode 100644 scripts/pip_anything.js diff --git a/popup/tabs.js b/popup/tabs.js index f59694ff..d433a7bf 100644 --- a/popup/tabs.js +++ b/popup/tabs.js @@ -577,6 +577,7 @@ const tabs = [ s.youtube_changeCountry, s.youtube_getVideoThumbnail, s.youtube_toggleLight, + s.pip_anything, s.pip_fullWebsite, s.pip_canvas, { diff --git a/scripts/_index.js b/scripts/_index.js index f50da941..78752e72 100644 --- a/scripts/_index.js +++ b/scripts/_index.js @@ -173,3 +173,4 @@ export { default as youtube_getVideoCaption } from "./youtube_getVideoCaption.js export { default as youtube_changeCountry } from "./youtube_changeCountry.js"; export { default as fb_autoLike } from "./fb_autoLike.js"; export { default as guland_VIP } from "./guland_VIP.js"; +export { default as pip_anything } from "./pip_anything.js"; diff --git a/scripts/content-scripts/ufs_global.js b/scripts/content-scripts/ufs_global.js index 463e518e..4a4ea57f 100644 --- a/scripts/content-scripts/ufs_global.js +++ b/scripts/content-scripts/ufs_global.js @@ -10,6 +10,7 @@ export const UfsGlobal = { waitForTabToLoad, }, DOM: { + pickElement, getMousePos, isInCrossOriginFrame, isInIframe, @@ -19,6 +20,7 @@ export const UfsGlobal = { addLoadingAnimationAtPos, addEventListener, enableDragAndZoom, + getElementBoundingClientRect, getContentClientRect, dataURLToCanvas, notifyStack, @@ -199,6 +201,188 @@ function download(options) { // #endregion // #region DOM +function pickElement() { + return new Promise((resolve) => { + // #region init elements + const host = document.createElement("div"); + const shadow = host.attachShadow({ mode: "closed" }); + + const style = document.createElement("style"); + style.textContent = /*css*/ ` + :host { + position: fixed; + display: block; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background: rgba(0, 0, 0, 0); + z-index: 99999999; + pointer-events: none; + } + #highlighter { + position: absolute; + top: 0; + left: 0; + width: 0; + height: 0; + background: #ff3f3f33; + border: 1px solid #F00; + mix-blend-mode: screen; + } + #confirmer { + position: absolute; + bottom: 0; + right: 0; + width: 200px; + height: 200px; + } + #confirmer input { + width: 100%; + } + `; + shadow.append(style); + + const highlighter = document.createElement("div"); + highlighter.id = "highlighter"; + shadow.append(highlighter); + + // const confirmer = document.createElement("div"); + // confirmer.id = "confirmer"; + // confirmer.innerHTML = ` + // + // Pick + // + // `; + // shadow.append(confirmer); + + document.documentElement.append(host); + // #endregion + + // #region pick element + const STATES = { + picking: "picking", + confirming: "confirming", + }; + let state = STATES.picking; + let currentTarget = null; + function onMouseOver(e) { + e.preventDefault(); + const target = e.target; + if (state !== STATES.picking || !target || target === highlighter) return; + + highlightElement(target); + } + + function highlightElement(elem) { + const rect = getElementBoundingClientRect(elem); + highlighter.style.cssText = ` + top: ${rect.top}px; + left: ${rect.left}px; + width: ${rect.width}px; + height: ${rect.height}px; + `; + currentTarget = elem; + } + + window.addEventListener("mouseover", onMouseOver, { capture: true }); + window.addEventListener( + "click", + (e) => { + e.preventDefault(); + // confirmPick(currentTarget); + + host.remove(); + resolve(currentTarget); + window.removeEventListener("mouseover", onMouseOver); + }, + { once: true, capture: true } + ); + // #endregion + + // #region confirm pick + // function confirmPick(elem) { + // state = STATES.confirming; + + // const nodes = getAllNodes(elem); + // const input = confirmer.querySelector("input"); + // confirmer.style.display = "block"; + // input.min = 0; + // input.max = nodes.length - 1; + // input.value = 0; + // input.step = 1; + // input.oninput = (e) => { + // const index = parseInt(e.target.value); + // const node = nodes[index]; + // highlightElement(node); + // }; + // } + // function getAllNodes(elem) { + // const result = []; + // result.push(...getAllChilds(elem)); + // result.unshift(...(getAllParents(elem).reverse() || [])); + // return result; + // } + // function getAllChilds(elem) { + // const result = []; + // if (elem.children) { + // for (const child of elem.children) { + // result.push(child); + // result.push(...getAllChilds(child)); + // } + // } + // return result; + // } + // function getAllParents(elem) { + // const result = []; + // if (elem.parentElement) { + // result.push(elem.parentElement); + // result.push(...getAllParents(elem.parentElement)); + // } + // return result; + // } + // #endregion + }); +} +// https://stackoverflow.com/a/5178132/11898496 +function createXPathFromElement(elm) { + var allNodes = document.getElementsByTagName("*"); + for (var segs = []; elm && elm.nodeType == 1; elm = elm.parentNode) { + if (elm.hasAttribute("id")) { + var uniqueIdCount = 0; + for (var n = 0; n < allNodes.length; n++) { + if (allNodes[n].hasAttribute("id") && allNodes[n].id == elm.id) + uniqueIdCount++; + if (uniqueIdCount > 1) break; + } + if (uniqueIdCount == 1) { + segs.unshift('id("' + elm.getAttribute("id") + '")'); + return segs.join("/"); + } else { + segs.unshift( + elm.localName.toLowerCase() + '[@id="' + elm.getAttribute("id") + '"]' + ); + } + } else if (elm.hasAttribute("class")) { + segs.unshift( + elm.localName.toLowerCase() + + '[@class="' + + elm.getAttribute("class") + + '"]' + ); + } else { + for ( + var i = 1, sib = elm.previousSibling; + sib; + sib = sib.previousSibling + ) { + if (sib.localName == elm.localName) i++; + } + segs.unshift(elm.localName.toLowerCase() + "[" + i + "]"); + } + } + return segs.length ? "/" + segs.join("/") : null; +} function isInCrossOriginFrame() { let result = true; try { @@ -443,6 +627,37 @@ function enableDragAndZoom(element, container, onUpdateCallback) { }, }; } +function getElementBoundingClientRect(elem) { + let rect = + typeof elem.getBoundingClientRect === "function" + ? elem.getBoundingClientRect() + : { height: 0, left: 0, top: 0, width: 0 }; + + // https://github.com/gorhill/uBlock/issues/1024 + // Try not returning an empty bounding rect. + if (rect.width !== 0 && rect.height !== 0) return rect; + if (elem.shadowRoot instanceof DocumentFragment) + return getElementBoundingClientRect(elem.shadowRoot); + + let left = rect.left, + right = left + rect.width, + top = rect.top, + bottom = top + rect.height; + + for (const child of elem.children) { + rect = getElementBoundingClientRect(child); + if (rect.width === 0 || rect.height === 0) continue; + if (rect.left < left) left = rect.left; + if (rect.right > right) right = rect.right; + if (rect.top < top) top = rect.top; + if (rect.bottom > bottom) bottom = rect.bottom; + } + + let height = bottom - top, + width = right - left; + + return { bottom, height, left, right, top, width }; +} // prettier-ignore function getContentClientRect(target, win = window) { let rect = target.getBoundingClientRect(); diff --git a/scripts/pip_anything.js b/scripts/pip_anything.js new file mode 100644 index 00000000..0abac430 --- /dev/null +++ b/scripts/pip_anything.js @@ -0,0 +1,72 @@ +import { UfsGlobal } from "./content-scripts/ufs_global.js"; + +export default { + icon: '', + name: { + en: "PIP anything", + vi: "PIP mọi thứ", + }, + description: { + en: "Picture in picture mode for anything, not just video, choose website content to show in PIP mode", + vi: "Xem bất kỳ giao diện nào trong cửa sổ nổi (Picture in picture), không chỉ mỗi video, click chọn phần tử từ website để xem trong cửa sổ nổi.", + img: "", + }, + + changeLogs: { + date: "description", + }, + + popupScript: { + onClick: () => { + window.close(); + }, + }, + + contentScript: { + // original from https://chromewebstore.google.com/detail/gepffghbolhjojibgohkdecdibdpbali + // document: https://developer.chrome.com/docs/web-platform/document-picture-in-picture + onClick: () => { + const copyStyleSheets = (pipWindow) => { + [...document.styleSheets].forEach((styleSheet) => { + try { + const cssRules = [...styleSheet.cssRules] + .map((rule) => rule.cssText) + .join(""); + const style = document.createElement("style"); + style.textContent = cssRules; + pipWindow.document.head.appendChild(style); + } catch (e) { + const link = document.createElement("link"); + link.rel = "stylesheet"; + link.type = styleSheet.type; + link.media = styleSheet.media; + link.href = styleSheet.href; + pipWindow.document.head.appendChild(link); + } + }); + }; + const replaceWithPlaceHolder = (element) => { + const placeHolder = document.createElement("div"); + placeHolder.id = "PIPPlaceHolder"; + placeHolder.style.display = "none"; + element.replaceWith(placeHolder); + }; + const restorePIPElement = (event) => { + const pipElement = event.target.body.firstChild; + const placeHolder = document.querySelector("#PIPPlaceHolder"); + placeHolder.replaceWith(pipElement); + }; + const openInPip = async (element) => { + const pipWindow = await documentPictureInPicture.requestWindow(); + copyStyleSheets(pipWindow); + replaceWithPlaceHolder(element); + pipWindow.document.body.append(element); + pipWindow.addEventListener("pagehide", restorePIPElement); + }; + + UfsGlobal.DOM.pickElement().then((element) => { + openInPip(element); + }); + }, + }, +}; From 3d43d72be3dc8e2f389577e004238c6c38147b57 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Wed, 17 Jul 2024 10:35:58 +0700 Subject: [PATCH 04/47] fix restore --- popup/index.js | 85 ++++++++++++++++++++++++----------------- popup/tabs.js | 2 - scripts/smoothScroll.js | 2 +- scripts/web_timer.js | 4 +- 4 files changed, 53 insertions(+), 40 deletions(-) diff --git a/popup/index.js b/popup/index.js index cf436134..54713f1c 100644 --- a/popup/index.js +++ b/popup/index.js @@ -15,6 +15,7 @@ import { Storage, checkBlackWhiteList, runScriptInTabWithEventChain, + getAllActiveScriptIds, } from "../scripts/helpers/utils.js"; import { LANG, @@ -39,6 +40,7 @@ import { viewScriptSource, } from "./helpers/utils.js"; import { checkPass } from "../scripts/auto_lockWebsite.js"; +import allScripts from "../scripts/_allScripts.js"; // import _ from "../md/exportScriptsToMd.js"; const settingsBtn = document.querySelector(".settings"); @@ -810,7 +812,7 @@ async function restore() { )) ) return; - Swal.fire({ + const result = await Swal.fire({ title: t({ en: "Restore data", vi: "Khôi phục dữ liệu" }), text: t({ en: "Select file to restore", vi: "Chọn file để khôi phục" }), input: "file", @@ -834,44 +836,57 @@ async function restore() { }); }, allowOutsideClick: () => !Swal.isLoading(), - }).then(async (result) => { - if (result.isConfirmed) { - try { - trackEvent("RESTORE"); - const json = JSON.parse(result.value); - const { localStorage: l, chromeStorage } = json; - - if (l) { - localStorage.clear(); - Object.keys(l).forEach((key) => { - localStorage[key] = l[key]; - }); - } + }); - if (chromeStorage) { - await chrome.storage.local.clear(); - for (let key in chromeStorage) { - await chrome.storage.local.set({ [key]: chromeStorage[key] }); - } - } + if (!result.isConfirmed) return; + try { + trackEvent("RESTORE"); + const json = JSON.parse(result.value); + const { localStorage: l, chromeStorage } = json; + + // override localStorage + if (l) { + localStorage.clear(); + Object.keys(l).forEach((key) => { + localStorage[key] = l[key]; + }); + } - Swal.fire({ - icon: "success", - title: t({ en: "Restore Success", vi: "Khôi phục thành công" }), - text: t({ - en: "Imported data from", - vi: "Đã nạp dữ liệu", - }), - }); - } catch (e) { - Swal.fire({ - icon: "error", - title: t({ en: "Error", vi: "Lỗi" }), - text: e?.message || e, - }); + if (chromeStorage) { + // trigger onDisable current active scripts + const oldActiveScriptIds = await getAllActiveScriptIds(); + for (let s of oldActiveScriptIds.map((id) => allScripts[id])) { + if (typeof s?.popupScript?.onDisable === "function") + await s.popupScript.onDisable(); + } + + // override chrome.storage + await chrome.storage.local.clear(); + await chrome.storage.local.set(chromeStorage); + + // trigger onEnable new active scripts + const newActiveScriptIds = await getAllActiveScriptIds(); + for (let s of newActiveScriptIds.map((id) => allScripts[id])) { + if (typeof s?.popupScript?.onEnable === "function") + await s.popupScript.onEnable(); } } - }); + + Swal.fire({ + icon: "success", + title: t({ en: "Restore Success", vi: "Khôi phục thành công" }), + text: t({ + en: "Imported data from", + vi: "Đã nạp dữ liệu", + }), + }); + } catch (e) { + Swal.fire({ + icon: "error", + title: t({ en: "Error", vi: "Lỗi" }), + text: e?.message || e, + }); + } } async function reset() { diff --git a/popup/tabs.js b/popup/tabs.js index d433a7bf..05cca66d 100644 --- a/popup/tabs.js +++ b/popup/tabs.js @@ -9,8 +9,6 @@ import { recentScriptsSaver, } from "./helpers/storageScripts.js"; -console.log(s); - const createTitle = (en, vi) => ({ name: { en, vi } }); const specialTabs = [ diff --git a/scripts/smoothScroll.js b/scripts/smoothScroll.js index 8583f78f..f95f97ec 100644 --- a/scripts/smoothScroll.js +++ b/scripts/smoothScroll.js @@ -92,7 +92,7 @@ async function setEnableForAllTab(enable) { : { vi: "Tắt", en: "Disabled" }; if (count) - Swal.fire({ + await Swal.fire({ icon: "success", title: t({ vi: "Đã " + text.vi + " Cuộn chuột Siêu mượt", diff --git a/scripts/web_timer.js b/scripts/web_timer.js index 9abd4bae..db25fbaa 100644 --- a/scripts/web_timer.js +++ b/scripts/web_timer.js @@ -68,7 +68,7 @@ export default { } } if (count) - Swal.fire({ + await Swal.fire({ icon: "success", title: t({ vi: "Đã bật", @@ -107,7 +107,7 @@ export default { } } if (count) - Swal.fire({ + await Swal.fire({ icon: "success", title: t({ vi: "Đã tắt", From b0d7421c1f9c55cdb0b0e808986f97f34778c116 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Wed, 17 Jul 2024 13:53:25 +0700 Subject: [PATCH 05/47] pip anything - WIP --- popup/main.js | 10 ++-- scripts/content-scripts/ufs_global.js | 48 +---------------- scripts/pip_anything.js | 76 +++++++++++++-------------- 3 files changed, 47 insertions(+), 87 deletions(-) diff --git a/popup/main.js b/popup/main.js index c19f610c..40b24ca2 100644 --- a/popup/main.js +++ b/popup/main.js @@ -1,3 +1,7 @@ -import("./index.js").then(() => { - document.querySelector("#loading-fullscreen")?.remove(); -}); +setTimeout( + () => + import("./index.js").then(() => { + document.querySelector("#loading-fullscreen")?.remove(); + }), + 0 +); diff --git a/scripts/content-scripts/ufs_global.js b/scripts/content-scripts/ufs_global.js index 4a4ea57f..1d5acd0c 100644 --- a/scripts/content-scripts/ufs_global.js +++ b/scripts/content-scripts/ufs_global.js @@ -216,7 +216,6 @@ function pickElement() { left: 0; width: 100vw; height: 100vh; - background: rgba(0, 0, 0, 0); z-index: 99999999; pointer-events: none; } @@ -226,9 +225,9 @@ function pickElement() { left: 0; width: 0; height: 0; - background: #ff3f3f33; + background: #ff3f3f22; border: 1px solid #F00; - mix-blend-mode: screen; + box-shadow: 0 0 0 99999px rgba(0, 0, 0, .5); } #confirmer { position: absolute; @@ -299,49 +298,6 @@ function pickElement() { { once: true, capture: true } ); // #endregion - - // #region confirm pick - // function confirmPick(elem) { - // state = STATES.confirming; - - // const nodes = getAllNodes(elem); - // const input = confirmer.querySelector("input"); - // confirmer.style.display = "block"; - // input.min = 0; - // input.max = nodes.length - 1; - // input.value = 0; - // input.step = 1; - // input.oninput = (e) => { - // const index = parseInt(e.target.value); - // const node = nodes[index]; - // highlightElement(node); - // }; - // } - // function getAllNodes(elem) { - // const result = []; - // result.push(...getAllChilds(elem)); - // result.unshift(...(getAllParents(elem).reverse() || [])); - // return result; - // } - // function getAllChilds(elem) { - // const result = []; - // if (elem.children) { - // for (const child of elem.children) { - // result.push(child); - // result.push(...getAllChilds(child)); - // } - // } - // return result; - // } - // function getAllParents(elem) { - // const result = []; - // if (elem.parentElement) { - // result.push(elem.parentElement); - // result.push(...getAllParents(elem.parentElement)); - // } - // return result; - // } - // #endregion }); } // https://stackoverflow.com/a/5178132/11898496 diff --git a/scripts/pip_anything.js b/scripts/pip_anything.js index 0abac430..40f5304f 100644 --- a/scripts/pip_anything.js +++ b/scripts/pip_anything.js @@ -26,47 +26,47 @@ export default { // original from https://chromewebstore.google.com/detail/gepffghbolhjojibgohkdecdibdpbali // document: https://developer.chrome.com/docs/web-platform/document-picture-in-picture onClick: () => { - const copyStyleSheets = (pipWindow) => { - [...document.styleSheets].forEach((styleSheet) => { - try { - const cssRules = [...styleSheet.cssRules] - .map((rule) => rule.cssText) - .join(""); - const style = document.createElement("style"); - style.textContent = cssRules; - pipWindow.document.head.appendChild(style); - } catch (e) { - const link = document.createElement("link"); - link.rel = "stylesheet"; - link.type = styleSheet.type; - link.media = styleSheet.media; - link.href = styleSheet.href; - pipWindow.document.head.appendChild(link); - } - }); - }; - const replaceWithPlaceHolder = (element) => { - const placeHolder = document.createElement("div"); - placeHolder.id = "PIPPlaceHolder"; - placeHolder.style.display = "none"; - element.replaceWith(placeHolder); - }; - const restorePIPElement = (event) => { - const pipElement = event.target.body.firstChild; - const placeHolder = document.querySelector("#PIPPlaceHolder"); - placeHolder.replaceWith(pipElement); - }; - const openInPip = async (element) => { - const pipWindow = await documentPictureInPicture.requestWindow(); - copyStyleSheets(pipWindow); - replaceWithPlaceHolder(element); - pipWindow.document.body.append(element); - pipWindow.addEventListener("pagehide", restorePIPElement); - }; - UfsGlobal.DOM.pickElement().then((element) => { openInPip(element); }); }, }, }; + +const copyStyleSheets = (pipWindow) => { + [...document.styleSheets].forEach((styleSheet) => { + try { + const cssRules = [...styleSheet.cssRules] + .map((rule) => rule.cssText) + .join(""); + const style = document.createElement("style"); + style.textContent = cssRules; + pipWindow.document.head.appendChild(style); + } catch (e) { + const link = document.createElement("link"); + link.rel = "stylesheet"; + link.type = styleSheet.type; + link.media = styleSheet.media; + link.href = styleSheet.href; + pipWindow.document.head.appendChild(link); + } + }); +}; +const replaceWithPlaceHolder = (element) => { + const placeHolder = document.createElement("div"); + placeHolder.id = "PIPPlaceHolder"; + placeHolder.style.display = "none"; + element.replaceWith(placeHolder); +}; +const restorePIPElement = (event) => { + const pipElement = event.target.body.firstChild; + const placeHolder = document.querySelector("#PIPPlaceHolder"); + placeHolder.replaceWith(pipElement); +}; +export const openInPip = async (element) => { + const pipWindow = await documentPictureInPicture.requestWindow(); + copyStyleSheets(pipWindow); + replaceWithPlaceHolder(element); + pipWindow.document.body.append(element); + pipWindow.addEventListener("pagehide", restorePIPElement); +}; From cdfa4f85f768ad238a1ad0fda2deb4401e3880fc Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Wed, 17 Jul 2024 16:59:05 +0700 Subject: [PATCH 06/47] reload after restore --- popup/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/popup/index.js b/popup/index.js index 54713f1c..a3bf4aa2 100644 --- a/popup/index.js +++ b/popup/index.js @@ -872,7 +872,7 @@ async function restore() { } } - Swal.fire({ + await Swal.fire({ icon: "success", title: t({ en: "Restore Success", vi: "Khôi phục thành công" }), text: t({ @@ -880,6 +880,8 @@ async function restore() { vi: "Đã nạp dữ liệu", }), }); + + location.reload(); } catch (e) { Swal.fire({ icon: "error", From d25039ff74f8e561b8f3411b4f43379d4faf876d Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Fri, 19 Jul 2024 14:09:03 +0700 Subject: [PATCH 07/47] fix --- scripts/pictureInPicture.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/scripts/pictureInPicture.js b/scripts/pictureInPicture.js index cb6e8599..a6af26e7 100644 --- a/scripts/pictureInPicture.js +++ b/scripts/pictureInPicture.js @@ -13,16 +13,18 @@ export default { badges: [BADGES.hot], changeLogs: { "2024-06-05": "fix video in iframes", + "2024-07-19": "fix video that disable pip", }, contentScript: { onClick_: async function () { const { UfsGlobal } = await import("./content-scripts/ufs_global.js"); + const Mark = "__pip__"; + function findLargestPlayingVideoInViewport() { const videos = Array.from(document.querySelectorAll("video")) .filter((video) => video.readyState != 0) - .filter((video) => video.disablePictureInPicture == false) .sort( (v1, v2) => UfsGlobal.DOM.getOverlapScore(v2) - @@ -35,11 +37,11 @@ export default { } async function requestPictureInPicture(video) { await video.requestPictureInPicture(); - video.setAttribute("__pip__", true); + video.setAttribute(Mark, true); video.addEventListener( "leavepictureinpicture", (event) => { - video.removeAttribute("__pip__"); + video.removeAttribute(Mark); }, { once: true, @@ -49,12 +51,12 @@ export default { } function maybeUpdatePictureInPictureVideo(entries, observer) { const observedVideo = entries[0].target; - if (!document.querySelector("[__pip__]")) { + if (!document.querySelector("[" + Mark + "]")) { observer.unobserve(observedVideo); return; } const video = findLargestPlayingVideoInViewport(); - if (video && !video.hasAttribute("__pip__")) { + if (video && !video.hasAttribute(Mark)) { observer.unobserve(observedVideo); requestPictureInPicture(video); } @@ -62,7 +64,9 @@ export default { (async () => { const video = findLargestPlayingVideoInViewport(); if (!video) return; - if (video.hasAttribute("__pip__")) { + if (video.disablePictureInPicture) + video.disablePictureInPicture = false; + if (video.hasAttribute(Mark)) { document.exitPictureInPicture(); return; } From d6af36b25d917ad7ea96e608fb776ce07301bad6 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Fri, 19 Jul 2024 14:34:20 +0700 Subject: [PATCH 08/47] fix medium --- scripts/medium_readFullArticle.js | 5 ++--- working_note.md | 4 ++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/medium_readFullArticle.js b/scripts/medium_readFullArticle.js index e7e2b0d7..73ed6677 100644 --- a/scripts/medium_readFullArticle.js +++ b/scripts/medium_readFullArticle.js @@ -1,5 +1,3 @@ -// javascript:window.open("https://freedium.cfd/"+encodeURIComponent(window.location)) - export default { icon: "https://cdn-icons-png.flaticon.com/512/5968/5968906.png", name: { @@ -20,7 +18,8 @@ export default { url = prompt("Nhập link medium:", url); if (url) { - window.open("https://freedium.cfd/" + url); + window.open("https://readmedium.com/" + url); + // window.open("https://freedium.cfd/" + url); } }, }, diff --git a/working_note.md b/working_note.md index 3a4891ac..3ae570bc 100644 --- a/working_note.md +++ b/working_note.md @@ -2,6 +2,10 @@ ## 01/07/2024 - ? +- [ ] good feature - medium unlock + +- [ ] good feature - facebook adblocker (SEEM broken) + - [ ] take a look at - [ ] ffmpeg in browser From 2490786512d1df464ce481ff32190729f98661de Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Fri, 19 Jul 2024 14:46:25 +0700 Subject: [PATCH 09/47] add --- working_note.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/working_note.md b/working_note.md index 3ae570bc..fe9dae71 100644 --- a/working_note.md +++ b/working_note.md @@ -2,9 +2,11 @@ ## 01/07/2024 - ? +- [ ] good feature - global video speed + - [ ] good feature - medium unlock -- [ ] good feature - facebook adblocker (SEEM broken) +- [ ] good feature - facebook adblocker - [ ] take a look at From c20650db3b7da7d075b4dd941faa9ad14f83ca70 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Tue, 23 Jul 2024 00:32:22 +0700 Subject: [PATCH 10/47] optimize tiktok batch download - WIP --- scripts/content-scripts/ufs_global.js | 81 +++++++++++++++++++++++++ scripts/libs/ajax-hook/index.js | 38 ++++++++++-- scripts/tiktok_batchDownload.js | 87 ++++++++++++++++++++++++--- 3 files changed, 193 insertions(+), 13 deletions(-) diff --git a/scripts/content-scripts/ufs_global.js b/scripts/content-scripts/ufs_global.js index 1d5acd0c..a480c77b 100644 --- a/scripts/content-scripts/ufs_global.js +++ b/scripts/content-scripts/ufs_global.js @@ -90,6 +90,10 @@ export const UfsGlobal = { zipAndDownloadBlobs, getBlobFromUrl, getBlobFromUrlWithProgress, + sanitizeFileName, + getOriginalWindowFunction, + chooseFolderToDownload, + downloadToFolder, downloadBlobUrl, downloadBlob, downloadURL, @@ -2014,6 +2018,83 @@ async function getBlobFromUrlWithProgress(url, progressCallback) { return blob; } +function sanitizeFileName(fileName) { + // Định nghĩa các ký tự hợp lệ (chữ cái, số, dấu gạch dưới, dấu gạch ngang và dấu chấm) + const validChars = /^[a-zA-Z0-9_\-.]+$/; + + // Lược bỏ các ký tự không hợp lệ + let sanitizedFileName = ""; + for (let char of fileName) { + if (validChars.test(char)) { + sanitizedFileName += char; + } + } + + // Trả về tên tệp đã lược bỏ các ký tự không hợp lệ + return sanitizedFileName; +} + +// Ví dụ sử dụng hàm +let originalFileName = "tên-tệp!không@hợp#lệ$.txt"; +let sanitizedFileName = sanitizeFileName(originalFileName); +console.log(sanitizedFileName); // Output: tên-tệpkhônghợpệ.txt + +// https://stackoverflow.com/a/69543476/11898496 +function getOriginalWindowFunction(fnName) { + const key = "ufs_original_windown_fn"; + if (!window[key]) window[key] = {}; + + if (!window[key][fnName]) { + const iframe = document.createElement("iframe"); + + iframe.style.display = "none"; + document.body.appendChild(iframe); // add element + + window[key][fnName] = iframe.contentWindow[fnName]; + } + + return window[key][fnName]; +} +async function chooseFolderToDownload(subDirName = "") { + const dirHandler = await window.showDirectoryPicker({ + mode: "readwrite", + // startIn: 'downloads', + }); + await dirHandler.requestPermission({ writable: true }); + if (!subDirName) return dirHandler; + + const subDir = await dirHandler.getDirectoryHandle(subDirName, { + create: true, + }); + return subDir; +} +async function downloadToFolder(url, fileName, dirHandler, subFolderName = "") { + try { + const f = getOriginalWindowFunction("fetch"); + + // try download directly, using fetch blob + const res = await f(url); + const blob = await res.blob(); + const fileHandler = await dirHandler.getFileHandle(fileName, { + create: true, + }); + const writable = await fileHandler.createWritable(); + await writable.write(blob); + await writable.close(); + return true; + } catch (e) { + console.error(e); + debugger; + + // backup download: using extension api + await download({ + url: url, + conflictAction: "overwrite", + filename: (subFolderName ? subFolderName + "/" : "") + fileName, + }); + return false; + } +} // TODO use saveAs instead all of these download functions async function downloadBlobUrl(url, title) { diff --git a/scripts/libs/ajax-hook/index.js b/scripts/libs/ajax-hook/index.js index 0a72e061..cc371639 100644 --- a/scripts/libs/ajax-hook/index.js +++ b/scripts/libs/ajax-hook/index.js @@ -26,17 +26,47 @@ let readyFetch = false; function initFetch() { const originalFetch = window.fetch; - window.fetch = async function (url, options) { + window.fetch = async function (urlOrRequest, options) { + let url = urlOrRequest; + if (urlOrRequest instanceof Request) { + url = urlOrRequest?.url; + options = options || {}; + for (const key in urlOrRequest) { + const type = typeof urlOrRequest[key]; + if (type === "string" || type === "number" || type === "boolean") { + options[key] = urlOrRequest[key]; + } + } + } + let request = { url, options }; for (const { fn } of onBeforeFetchFn) { - const res = await fn?.(request.url, request.options); + const res = await fn?.(request.url, request.options)?.catch( + console.error + ); if (res) request = res; if (res === null) return null; } - let response = await originalFetch(...request); + if (urlOrRequest instanceof Request) { + try { + // TODO modify options + // for (const key in request.options) { + // urlOrRequest[key] = request.options[key]; + // } + if (urlOrRequest.url !== request.url) { + urlOrRequest = new Request(request.url, urlOrRequest); + } + } catch (e) { + debugger; + } + } + + let response = await originalFetch(urlOrRequest); for (const { fn } of onAfterFetchFn) { - const res = await fn?.(request.url, request.options, response); + const res = await fn?.(request.url, request.options, response)?.catch( + console.error + ); if (res) response = res; if (res === null) return null; } diff --git a/scripts/tiktok_batchDownload.js b/scripts/tiktok_batchDownload.js index 2e6c4aa3..23acf4bf 100644 --- a/scripts/tiktok_batchDownload.js +++ b/scripts/tiktok_batchDownload.js @@ -1,9 +1,17 @@ import { UfsGlobal } from "./content-scripts/ufs_global.js"; +import { hookFetch } from "./libs/ajax-hook/index.js"; import { downloadTiktokVideoFromUrl, downloadTiktokVideoFromId, } from "./tiktok_GLOBAL.js"; +const CACHED = { + list: [], + videoById: new Map(), +}; + +const commId = "ufs_tiktok_batchDownload_startDownload"; + export default { icon: "https://www.tiktok.com/favicon.ico", name: { @@ -32,7 +40,46 @@ export default { }, }, + contentScript: { + onDocumentStart: () => { + window.addEventListener("message", async (event) => { + console.log(event.data); + if (event.data?.type === commId) { + const dir = await UfsGlobal.Utils.chooseFolderToDownload("tiktok"); + for (const { url, name } of event.data.data) { + await UfsGlobal.Utils.downloadToFolder(url, name, dir); + } + } + }); + }, + }, + pageScript: { + onDocumentStart: () => { + // reference to Cached + window.ufs_tiktok_batchDownload = CACHED; + + hookFetch({ + onAfter: async (url, options, response) => { + if (url.includes("api/post/item_list")) { + // clone to new response + const res = response.clone(); + const json = await res.json(); + console.log(json); + + if (json?.itemList) { + CACHED.list.push(...json.itemList); + json.itemList.forEach((_) => { + CACHED.videoById.set(_.video.id, { + url: _.video.playAddr, + name: _.desc, + }); + }); + } + } + }, + }); + }, onDocumentIdle: async () => { let checkboxes = []; @@ -193,14 +240,18 @@ export default { videoUrls.length }]`; try { - console.log(`${progress} Đang tìm link cho video ${queue[0]}`); - progressDiv.innerText = `${progress} Đang tìm link video ${queue[0]}...`; + const url = queue[0]; + const id = getId(url); + + console.log(`${progress} Đang tìm link cho video ${url}`); + progressDiv.innerText = `${progress} Đang tìm link video ${id}...`; downloadBtn.innerText = `Đang get link ${progress}...`; - let link = await downloadTiktokVideoFromUrl(queue[0], true); - if (!link) { - link = await downloadTiktokVideoFromId(getId(queue[0])); - } + const cached = CACHED.videoById.get(id); + const link = + cached?.url || + (await downloadTiktokVideoFromUrl(url, true)) || + (await downloadTiktokVideoFromId(id)); if (link) { resultTxt.hidden = false; @@ -208,9 +259,24 @@ export default { let count = resultTxt.value.split("\n").filter((i) => i).length; resultLabel.innerText = `Link tại đây, ${count} video, copy bỏ vào IDM tải hàng loạt nhé:`; - links.push(link); + // await UfsGlobal.Utils.downloadToFolder(link, id + ".mp4", dir); + // await UfsGlobal.Extension.download({ + // url: link, + // conflictAction: "overwrite", + // filename: + // "tiktok/" + + // UfsGlobal.Utils.sanitizeFileName( + // CACHED.videoById.get(id)?.name || id + // ) + + // ".mp4", + // }); + links.push({ + url: link, + name: + UfsGlobal.Utils.sanitizeFileName(cached?.name || id) + ".mp4", + }); } else { - progressDiv.innerText = `[LỖI] Không thể tải video ${queue[0]}.`; + progressDiv.innerText = `[LỖI] Không thể tải video ${url}.`; await sleep(1000); } queue.shift(); @@ -226,7 +292,10 @@ export default { downloadBtn.disabled = false; downloadBtn.innerText = "GET LINK 🔗"; - if (links?.length) UfsGlobal.Utils.copyToClipboard(links.join("\n")); + if (links?.length) { + UfsGlobal.Utils.copyToClipboard(links.map((_) => _.url).join("\n")); + // window.postMessage({ type: commId, data: links }, "*"); // send to content script to download + } console.log(links); } From 64f24381c19f804a63d0ef3896ad9c310e1adb6c Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Tue, 23 Jul 2024 12:19:58 +0700 Subject: [PATCH 11/47] optimize ufsGlobal + tabs --- popup/recommend.js | 891 ++++++++++++++ popup/tabs.js | 964 +-------------- scripts/_index.js | 3 - scripts/auto_redirectLargestImageSrc.js | 407 ++++++- scripts/backup/auto-like-fb.js | 59 - scripts/content-scripts/backup.js | 296 +++++ scripts/content-scripts/ufs_global.js | 1400 +--------------------- scripts/fb_downloadAlbumMedia.js | 121 -- scripts/fb_downloadWallMediaFromPosts.js | 221 ---- scripts/fb_getAllUidOfGroupMembers.js | 4 - scripts/fb_getTokenBusinessStudio.js | 51 - scripts/fb_moreReactionStory.js | 8 +- scripts/magnify_image.js | 370 +++++- scripts/pip_anything.js | 134 ++- scripts/simpleAllowCopy.js | 2 +- scripts/tiktok_GLOBAL.js | 17 +- scripts/tiktok_batchDownload.js | 25 +- scripts/tiktok_downloadWatchingVideo.js | 35 +- scripts/web_timer.js | 18 +- 19 files changed, 2245 insertions(+), 2781 deletions(-) create mode 100644 popup/recommend.js delete mode 100644 scripts/backup/auto-like-fb.js create mode 100644 scripts/content-scripts/backup.js delete mode 100644 scripts/fb_downloadAlbumMedia.js delete mode 100644 scripts/fb_downloadWallMediaFromPosts.js delete mode 100644 scripts/fb_getTokenBusinessStudio.js diff --git a/popup/recommend.js b/popup/recommend.js new file mode 100644 index 00000000..1d40572e --- /dev/null +++ b/popup/recommend.js @@ -0,0 +1,891 @@ +import { BADGES } from "../scripts/helpers/badge.js"; +import { getLang } from "./helpers/lang.js"; + +export const Recommend = { + theresanaiforthat: { + id: "recommend_theresanaiforthat", + icon: "https://theresanaiforthat.com/favicon.ico", + name: { + en: "There's an AI for that", + vi: "There's an AI for that - Tìm AI", + }, + description: { + en: "Collection of thousand of AI tools. Easy to search by category", + vi: "Tổng hợp hàng ngàn công cụ AI hiện có. Dễ dàng tìm kiếm theo chủ đề", + }, + popupScript: { + onClick: () => window.open("https://theresanaiforthat.com/"), + }, + }, + timeis: { + id: "recommend_timeis", + icon: "https://time.is/favicon.ico", + name: { + en: "Time.is - Check your time", + vi: "Time.is - Kiểm tra thời gian", + }, + description: { + en: "Exact time for any time zone.", + vi: "Đồng hồ chính xác nhất. Kiểm tra đồng hồ trên máy của bạn nhanh hay chậm.", + }, + popupScript: { + onClick: () => window.open("https://time.is/"), + }, + }, + googleTrending: { + id: "recommend_googleTrending", + icon: "https://www.gstatic.com/trends/favicon.ico", + name: { + en: "Google trending - See what trending now", + vi: "Google trending - Nội dung nổi bật", + }, + description: { + en: "See what people are searching on Google. Top treding every day, realtime.", + vi: "Xem mọi người đang tìm gì trên google. Thống kê từng ngày, thời gian thực.", + }, + popupScript: { + onClick: () => window.open("https://trends.google.com/"), + }, + }, + archive: { + id: "recommend_archive", + icon: "https://archive.org/favicon.ico", + name: { + en: "Internet archive - Free library", + vi: "Internet archive - Thư viện miễn phí", + }, + description: { + en: "Non-profit library of millions of free books, movies, software, music, websites, and more.", + vi: "Thư viện với hàng triệu sách, báo, phim, phần mềm, nhạc, website, ... miễn phí", + img: "/scripts/internet_archive.png", + }, + badges: [BADGES.recommend], + popupScript: { + onClick: () => window.open("https://archive.org/"), + }, + }, + wappalyzer: { + id: "recommend_wappalyzer", + icon: "https://www.wappalyzer.com/favicon.ico", + name: { + en: "Wappalyzer - view website stacks", + vi: "Wappalyzer - Web dùng công nghệ gì?", + }, + description: { + en: "Technology that current website is using", + vi: "Xem những công nghệ/thư viện trang web đang dùng", + }, + badges: [BADGES.recommend], + popupScript: { + onClick: () => window.open("https://www.wappalyzer.com/apps/"), + }, + }, + search_musicTreding: { + id: "recommend_search_musicTreding", + icon: "https://kworb.net/favicon.ico", + name: { + en: "Top global treding music?", + vi: "Bài nhạc top treding toàn cầu?", + }, + description: { + en: "The web to find all kinds of music-related data.", + vi: "Trang web thống kê top trending âm nhạc toàn cầu.", + }, + popupScript: { + onClick: () => + window.open("https://kworb.net/youtube/trending_music.html"), + }, + }, + search_userscript: { + id: "recommend_search_userscript", + icon: "https://www.userscript.zone/favicon.ico", + name: { + en: "Search Userscripts", + vi: "Tìm Userscripts", + }, + description: { + en: "Search Userscripts on Usersript.zone", + vi: "Tìm Userscripts trên Usersript.zone", + }, + + popupScript: { + onClick: () => window.open("https://www.userscript.zone/"), + }, + }, + cobalt: { + id: "recommend_cobalt", + icon: "https://cobalt.tools/favicon.ico", + name: { + en: "Cobalt - Media downloader", + vi: "Cobalt - Tải video/nhạc", + }, + description: { + en: "Support youtube, tiktok, instagram, twitter/x, bilibili, twitch, vimeo, soundcloud, dailymotion, pinterest, reddit, tumblr, ...", + vi: "Hỗ trợ youtube, tiktok, instagram, twitter/x, bilibili, twitch, vimeo, soundcloud, dailymotion, pinterest, reddit, tumblr, ...", + }, + badges: [BADGES.recommend, BADGES.new], + buttons: [ + { + icon: '', + name: { + vi: "Github", + en: "Github", + }, + onClick: () => window.open("https://github.com/imputnet/cobalt"), + }, + ], + popupScript: { + onClick: () => window.open("https://cobalt.tools/"), + }, + }, + luanxt: { + id: "recommend_getLinkLuanxt", + icon: "https://luanxt.com/get-link-mp3-320-lossless-vip-zing/favicon.ico", + name: { + en: "Get audio/video (luanxt)", + vi: "Tải nhạc/video (luanxt)", + }, + description: { + en: "Using API from luanxt.com. Download Zing MP3, Zing Video Clip, Zing TV, NhacCuaTui, YouTube, SoundCloud, Nhac.vn, ChiaSeNhac.vn, Facebook Video, Keeng Audio, Keeng Video, Keeng Phim", + vi: "Sử dụng API của luanxt.com. Tải Zing MP3, Zing Video Clip, Zing TV, NhacCuaTui, YouTube, SoundCloud, Nhac.vn, ChiaSeNhac.vn, Facebook Video, Keeng Audio, Keeng Video, Keeng Phim", + }, + infoLink: "https://luanxt.com/get-link-mp3-320-lossless-vip-zing/", + badges: [BADGES.recommend], + popupScript: { + onClick: () => + window.open("https://luanxt.com/get-link-mp3-320-lossless-vip-zing/"), + }, + }, + picviewer_ce: { + id: "recommend_picviewer_ce+", + icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAV1BMVEUAAAD////29vbKysoqKioiIiKysrKhoaGTk5N9fX3z8/Pv7+/r6+vk5OTb29vOzs6Ojo5UVFQzMzMZGRkREREMDAy4uLisrKylpaV4eHhkZGRPT08/Pz/IfxjQAAAAgklEQVQoz53RRw7DIBBAUb5pxr2m3/+ckfDImwyJlL9DDzQgDIUMRu1vWOxTBdeM+onApENF0qHjpkOk2VTwLVEF40Kbfj1wK8AVu2pQA1aBBYDHJ1wy9Cf4cXD5chzNAvsAnc8TjoLAhIzsBao9w1rlVTIvkOYMd9nm6xPi168t9AYkbANdajpjcwAAAABJRU5ErkJggg==", + name: { + en: "Picviewer CE+ download images", + vi: "Picviewer CE+ tải ảnh", + }, + description: { + en: "Powerful picture viewing tool online, which can popup/scale/rotate/batch save pictures automatically", + vi: "Công cụ mạnh mẽ để xem/tải ảnh hàng loạt, cho tất cả trang web", + img: "https://v2fy.com/asset/063_picviewer_ce/73130353-c4598e00-4031-11ea-810e-9498677a40d1.gif", + }, + badges: [BADGES.recommend], + popupScript: { + onClick: () => + window.open("https://greasyfork.org/en/scripts/24204-picviewer-ce"), + }, + }, + file_converter: { + id: "recommend_file_converter", + icon: "https://file-converter.io/favicon.ico", + name: { + en: "File-converter.io - change image type", + vi: "File-converter.io - chuyển đổi ảnh", + }, + description: { + en: "Powerful tool which allows you to convert and compress files using the context menu in windows explorer.", + vi: "Công cụ nén ảnh, đổi định dạng ảnh hàng loạt, trực tiếp bằng chuột phải.", + img: "https://file-converter.io/images/file-converter-usage.gif", + }, + badges: [BADGES.recommend], + popupScript: { + onClick: () => window.open("https://file-converter.io/"), + }, + }, + squoosh_app: { + id: "recommend_squoosh_app", + icon: "https://squoosh.app/c/icon-large-maskable-c2078ced.png", + name: { + en: "Squoosh.app - compress images", + vi: "Squoosh.app - nén ảnh", + }, + description: { + en: "Make images smaller using best-in-class codecs, right in the browser.", + vi: "Công cụ nén ảnh mạnh mẽ, giảm kích thước ngay trên trình duyệt", + }, + badges: [BADGES.recommend], + popupScript: { + onClick: () => window.open("https://squoosh.app/"), + }, + }, + docsdownloader: { + id: "recommend_docsdownloader", + icon: "https://docsdownloader.com/assets/img/android-icon-192x192.png", + name: { + en: "DocDownloader - Download document", + vi: "DocDownloader - Tải document", + }, + description: { + en: "Download document on Scribd, Everand, Slideshare, Issuu, Academia, Chegg, Researchgate, Coursehero, Studocu, Perlego, Yumpu, Tiendeo, Fliphtml5, Anyflip, Docsity, Passei direto, Udocz", + vi: "Tải document từ Scribd, Everand, Slideshare, Issuu, Academia, Chegg, Researchgate, Coursehero, Studocu, Perlego, Yumpu, Tiendeo, Fliphtml5, Anyflip, Docsity, Passei direto, Udocz", + }, + badges: [BADGES.recommend], + popupScript: { + onClick: () => window.open("https://docdownloader.com/"), + }, + }, + bookmarkSidebar: { + id: "recommend_BookmarkSidebar", + icon: "https://lh3.googleusercontent.com/4kT7DxtoPSmSLzTit1w2Vbx7b1L2zkASTrqGzEpBW-qs2EwmLYzBTyv0cvlGZo-rD-s732OIrUXX-C33RHPSFvOj=s0", + name: { + en: "Bookmark Sidebar", + vi: "Bookmark Sidebar", + }, + description: { + en: "Very good Bookmark manager, find your bookmarks faster.", + vi: "Trình quản lý bookmark ngon, tìm kiếm bookmark nhanh hơn bao giờ hết.", + }, + badges: [BADGES.recommend], + popupScript: { + onClick: () => + window.open( + "https://chromewebstore.google.com/detail/thanh-d%E1%BA%A5u-trang/jdbnofccmhefkmjbkkdkfiicjkgofkdh" + ), + }, + }, + googleAdvanced: { + id: "recommend_googleAdvanced", + icon: "https://www.google.com/favicon.ico", + name: { + en: "Google search advanced", + vi: "Google tìm kiếm nâng cao", + }, + description: { + en: "Search google with a lot of advanced features", + vi: "Tìm kiếm google với nhiều tuỳ chọn nâng cao", + }, + badges: [BADGES.recommend], + popupScript: { + onClick: () => + window.open( + "https://www.google.com/advanced_search?hl=" + getLang() + "&fg=1" + ), + }, + }, + fb_openSaved: { + id: "fb_openSaved", + icon: '', + name: { + en: "View your facebook saved", + vi: "Xem mục đã lưu trên facebook", + }, + description: { + en: "View saved contents on Facebook", + vi: "Xem nội dung bạn đã lưu trên Facebook", + }, + + popupScript: { + onClick: () => window.open("https://www.facebook.com/saved"), + }, + }, + fb_openMemories: { + id: "fb_openMemories", + icon: '', + name: { + en: "View your memories on facebook", + vi: "Xem kỷ niệm của bạn trên facebook", + }, + description: { + en: "View your memories on facebook", + vi: "Xem kỷ niệm (memories) của bạn trên facebook", + }, + popupScript: { + onClick: () => window.open("https://www.facebook.com/memories/"), + }, + }, + fb_openAdsActivities: { + id: "fb_openAdsActivities", + icon: '', + name: { + en: "View your ads activities", + vi: "Xem các quảng cáo fb bạn đã xem", + }, + description: { + en: "View ads you have seen on facebook", + vi: "Xem các quảng cáo bạn đã xem trên facebook", + }, + popupScript: { + onClick: () => window.open("https://www.facebook.com/ads/activity"), + }, + }, + fb_openAllActivities: { + id: "fb_openAllActivities", + icon: '', + name: { + en: "Check your activities on Facebook", + vi: "Xem nhật ký hoạt động trên facebook", + }, + description: { + en: "Check all your activities on facebook", + vi: "Kiểm tra nhật ký hoạt động của bạn trên facebook", + }, + popupScript: { + onClick: () => window.open("https://www.facebook.com/me/allactivity"), + }, + }, + fb_openVideoActivities: { + id: "fb_openVideoActivities", + icon: '', + name: { + en: "Video you watched on facebook", + vi: "Video bạn vừa xem trên facebook", + }, + description: { + en: "View all videos you watched on facebook", + vi: "Xem lại những video bạn đã xem trên facebook", + }, + badges: [BADGES.new], + popupScript: { + onClick: () => + window.open( + "https://www.facebook.com/100004848287494/allactivity?activity_history=false&category_key=VIDEOWATCH&manage_mode=false&should_load_landing_page=false" + ), + }, + }, + fb_openPassEvents: { + id: "fb_openPassEvents", + icon: '', + name: { + en: "Events joined on facebook", + vi: "Sự kiện đã tham gia trên facebook", + }, + description: { + en: "View pass events that you have joined on facebook.", + vi: "Xem tất cả sự kiện bạn từng tham gia trên facebook.", + }, + badges: [BADGES.new], + popupScript: { + onClick: () => window.open("https://www.facebook.com/events/past"), + }, + }, + fb_openBirthdays: { + id: "fb_openBirthdays", + icon: '', + name: { + en: "Facebook friend's birthdays", + vi: "Sinh nhật bạn bè facebook", + }, + description: { + en: "View your friend's birthdays each month on facebook", + vi: "Xem từng tháng có những sinh nhật nào của bạn bè trên facebook.", + }, + badges: [BADGES.new], + popupScript: { + onClick: () => window.open("https://www.facebook.com/events/birthdays"), + }, + }, + fb_openChangeLanguage: { + id: "fb_openChangeLanguage", + icon: '', + name: { + en: "Change language facebook", + vi: "Đổi ngôn ngữ facebook", + }, + description: { + en: "Change display language on facebook", + vi: "Đổi ngôn ngữ hiển thị trên facebook", + }, + popupScript: { + onClick: () => + window.open("https://www.facebook.com/settings/?tab=language"), + }, + }, + fb_openAccountHacked: { + id: "fb_openAccountHacked", + icon: '', + name: { + en: "Recover facebook account", + vi: "Khôi phục tài khoản facebook", + }, + description: { + en: "Your fb account has been hacked? Facebook can help you.", + vi: "Tài khoản fb của bạn bị hack? Facebook có thể giúp bạn.", + }, + badges: [BADGES.new], + popupScript: { + onClick: () => window.open("https://fb.com/hacked"), + }, + }, + improve_youtube: { + id: "recommend_improve_youtube", + icon: "https://lh3.googleusercontent.com/WDytHNO8o0Ev6sWp_yLbya_SSS9kXZWGJIc-WJ3goInHJalzD02Aq5wVhExFlbzrzNsOxo-V1O_TgF-JLJNyTkvB=s0", + name: { + en: "Improve YouTube - 85+ features", + vi: "Improve YouTube - 85+ chức năng", + }, + description: { + en: "Make YouTube more beautiful, faster, and more useful!", + vi: "Làm cho YouTube gọn gàng+thông minh!", + }, + badges: [BADGES.recommend], + popupScript: { + onClick: () => + window.open( + "https://chromewebstore.google.com/detail/improve-youtube-%F0%9F%8E%A7-for-yo/bnomihfieiccainjcjblhegjgglakjdd" + ), + }, + }, + itTools: { + id: "recommend_ItTools", + icon: "https://it-tools.tech/favicon-32x32.png", + name: { + en: "IT Tools - All for Developers", + vi: "IT Tools - Vì tương lai Developer", + }, + description: { + en: "Handy tools for developers (open source)", + vi: "Tổng hợp tools hữu ích cho IT (mã nguồn mở)", + }, + badges: [BADGES.recommend], + popupScript: { + onClick: () => window.open("https://it-tools.tech/"), + }, + }, + copyicon: { + id: "recommend_copyicon", + icon: "https://copyicon.com/favicon.ico", + name: { + en: "CopyIcon - FREE emoji, icon, generator", + vi: "CopyIcon - emoji, icon, svg miễn phí", + }, + description: { + en: "285,000 free Icons, Emoji, SVG generator, and more...", + vi: "285,000 Icons, Emiji, trình tạo SVG, và hơn thế nữa...", + }, + badges: [BADGES.recommend], + popupScript: { + onClick: () => window.open("https://copyicon.com/generator/svg-chart"), + }, + }, + beautifytools: { + id: "recommend_beautifytools", + icon: "https://beautifytools.com/img/favicon.ico", + name: { en: "Beautify Tools", vi: "Beautify Tools" }, + description: { + en: `Handy tools for developers +
    +
  1. Beautifiers And Minifiers
  2. +
  3. CSS Preprocessors
  4. +
  5. Converters
  6. +
  7. String Utilities
  8. +
  9. SEO Tools
  10. +
  11. IP Tools
  12. +
  13. Code Validators
  14. +
  15. Cryptography
  16. +
  17. Code Editors
  18. +
`, + vi: `Tổng hợp tools hữu ích cho IT +
    +
  1. Beautifiers And Minifiers
  2. +
  3. CSS Preprocessors
  4. +
  5. Converters
  6. +
  7. String Utilities
  8. +
  9. SEO Tools
  10. +
  11. IP Tools
  12. +
  13. Code Validators
  14. +
  15. Cryptography
  16. +
  17. Code Editors
  18. +
`, + }, + badges: [BADGES.recommend], + popupScript: { + onClick: () => window.open("https://beautifytools.com/"), + }, + }, + cloc: { + id: "recommend_cloc", + icon: '', + name: { + en: "Cloc - count line of code", + vi: "Cloc - đếm số dòng code", + }, + description: { + en: "Count blank lines, comment lines, and physical lines of source code in many programming languages.", + vi: "Đếm dòng trống, comment, dòng code trong repo, hỗ trợ nhiều ngôn ngữ lập trình.", + img: "/scripts/recommend_cloc.png", + }, + badges: [BADGES.recommend], + popupScript: { + onClick: () => + window.open("https://github.com/AlDanial/cloc?tab=readme-ov-file"), + }, + }, + refined_github: { + id: "recommend_refined_github", + icon: "https://lh3.googleusercontent.com/4N2wipmBVx1qK0R0E0XdADE31-8IuMylOtO9AyFopOA9i3IQKoCC5L4nYFDy55xpxpk6qKusHuqXyKJqvw8jcJaiqg=s60", + name: { + en: "Refined GitHub ", + vi: "Refined GitHub", + }, + description: { + en: "Simplifies the GitHub interface and adds useful features", + vi: "Sửa giao diện github và thêm hàng tá chức năng hay", + }, + badges: [BADGES.recommend], + popupScript: { + onClick: () => + window.open( + "https://chromewebstore.google.com/detail/refined-github/hlepfoohegkhhmjieoechaddaejaokhf" + ), + }, + }, + beecost: { + id: "recommend_Beecost", + icon: "https://lh3.googleusercontent.com/QeCUs-fM4mwAmBVRS0VU8NrjJnDnbSsXoqUrCbd8ZbHou03FBPEQOYHAcdcL_rn7NMrUpWMcXoG2m_CrKtAhc-wLgLU=w128-h128-e365-rj-sc0x00ffffff", + name: { en: "Beecost", vi: "Beecost" }, + description: { + en: "Check deals/prices in ecommerce websites", + vi: "Kiểm tra giá/ưu đãi giả khi mua hàng online", + }, + badges: [BADGES.recommend], + popupScript: { + onClick: () => window.open("https://beecost.vn/"), + }, + }, + fastDoc: { + id: "recommend_fastDoc", + icon: "https://fastdoc.vn/favicon.png", + name: { + en: "FastDoc - Convert PDF/Photo to Word/Excel", + vi: "FastDoc - Chuyển PDF/Ảnh sang Word/Excel", + }, + badges: [BADGES.recommend], + description: { + en: "Convert Photos & PDF to Excel, Word, Searchable PDF for free", + vi: "Chuyển đổi hình ảnh và pdf sang Excel, Word, Searchable PDF miễn phí", + }, + popupScript: { + onClick: () => window.open("https://fastdoc.vn/"), + }, + }, + smartPDF: { + id: "recommend_smartPDF", + icon: "https://smallpdf.com/favicon.ico", + name: { + en: "SmartPDF - Tools for PDF", + vi: "SmartPDF - Công cụ cho PDF", + }, + description: { + en: "Compress PDF, PDF Converter, PPT to PDF, PDF to PPT, JPG to PDF, PDF to JPG, Excel to PDF, PDF to Excel, Edit PDF, PDF Reader, Number Pages, Delete PDF Pages, Rotate PDF, Word to PDF, PDF to Word, Merge PDF, Split PDF, eSign PDF, Unlock PDF, Protect PDF, PDF Scanner", + vi: "Giảm dung lượng PDF, Chuyển đổi PDF, PPT sang PDF, PDF sang PPT, JPG sang PDF, PDF sang JPG, Excel sang PDF, PDF sang Excel, Chỉnh sửa PDF, Trình đọc PDF, Số trang, Xóa các trang PDF, Xoay PDF, Word sang PDF, PDF sang Word, Ghép PDF, Cắt PDF, Ký tên PDF, Mở khóa PDF, Bảo vệ PDF, Máy quét PDF", + }, + badges: [BADGES.recommend], + popupScript: { + onClick: () => window.open("https://smallpdf.com/vi/cac-cong-cu-pdf"), + }, + }, + pdfstuffs: { + id: "recommend_pdfstuffs", + icon: "https://pdfstuff.com/themes/pdfstuff/img/favicons/apple-icon-57x57.png", + name: { + en: "PDF Stuffs - Tools for PDF", + vi: "PDF Stuffs - Công cụ PDF", + }, + description: { + en: "Free PDF converter online service: Merge PDF, Split PDF, Compress PDF, PDF to Word, PDF to PPT, PDF to Excel, Word to PDF, Excel to PDF, PPT to PDF, PDF to JPG, JPG to PDF, PDF to HTML, HTML to PDF, Unlock PDF, Protect PDF, Rotate PDF, Crop PDF, Delete pages, Add page numbers, Watermark PDF", + vi: "Công cụ chuyển đổi PDF online miễn phí: Ghép file PDF, Tách file PDF, Nén file PDF, PDF sang Word, PDF sang PPT, PDF sang Excel, Word sang PDF, Excel sang PDF, PPT sang PDF, PDF sang JPG, JPG sang PDF, PDF sang HTML, HTML sang PDF, Mở khóa PDF, Khóa file PDF, Xoay file PDF, Cắt file PDF, Xóa trang PDF, Đánh số trang PDF, Chèn watermark", + }, + badges: [BADGES.recommend], + popupScript: { + onClick: () => window.open("https://pdfstuff.com/"), + }, + }, + chromeFlags: { + id: "recommend_chromeFlags", + icon: '', + name: { + en: "Make browser super fast", + vi: "Tăng tốc tối đa trình duyệt", + }, + description: { + en: "Some flags experiments that can make your browser super fast", + vi: "Các flags giúp trình duyệt của bạn chạy nhanh hơn thỏ", + }, + badges: [BADGES.recommend], + popupScript: { + onClick: () => + window.open("https://www.androidauthority.com/chrome-flags-1009941/"), + }, + }, + viewSavedWifiPass: { + id: "recommend_viewSavedWifiPass", + icon: '', + name: { + en: "View saved wifi passwords", + vi: "Xem mật khẩu wifi đã lưu", + }, + description: { + en: "PowerShell script to view saved wifi passwords on your computer", + vi: "Powershell script giúp xem mật khẩu wifi đã lưu trên máy tính", + }, + infoLink: + "https://www.facebook.com/groups/j2team.community/posts/2328915024107271/", + + popupScript: { + onClick: () => { + prompt( + `File danh sách mật khẩu Wifi sẽ lưu ở: + "C:\\WifiPasswords\\listWifiPasswords.txt" + có dạng: [Tên Wifi]:[Mật khẩu] + + Mở Powershell và chạy lệnh sau:`, + `irm https://tinyurl.com/GetListWifiPasswords | iex` + ); + }, + }, + }, + leakCheck: { + id: "recommend_leakCheck", + icon: "https://leakcheck.io/favicon.ico", + name: { + en: "Leak check - your password has been leaked?", + vi: "Leak check - lộ mật khẩu email?", + }, + description: { + en: "Check your password has been leaked on internet or not", + vi: "Kiểm tra xem mật khẩu email/username của bạn có bị phát tán trên mạng hay không", + }, + infoLink: + "https://www.facebook.com/groups/j2team.community/posts/2329878560677584/", + popupScript: { + onClick: () => { + window.open("https://okela.fun/"); + }, + }, + }, + darkReader: { + id: "recommend_DarkReader", + icon: "https://lh3.googleusercontent.com/T66wTLk-gpBBGsMm0SDJJ3VaI8YM0Utr8NaGCSANmXOfb84K-9GmyXORLKoslfxtasKtQ4spDCdq_zlp_t3QQ6SI0A=w128-h128-e365-rj-sc0x00ffffff", + name: { en: "Dark reader", vi: "Dark reader" }, + description: { + en: "Darkmode for every website", + vi: "Chế độ tối cho mọi trang web", + }, + badges: [BADGES.recommend], + popupScript: { + onClick: () => + window.open( + "https://chrome.google.com/webstore/detail/dark-reader/eimadpbcbfnmbkopoojfekhnkhdbieeh" + ), + }, + }, + cssportal: { + id: "recommend_cssportal", + icon: "https://www.cssportal.com/favicon.ico", + name: { + en: "CSS Portal - Empowered your CSS skills", + vi: "CSS Portal - Nâng trình CSS", + }, + description: { + en: "Empowered your CSS skills with hundreds of CSS tools.", + vi: "Công cụ tự động giúp nâng trình CSS của bạn với hàng trăm chức năng.", + }, + badges: [BADGES.recommend], + popupScript: { + onClick: () => + window.open("https://www.cssportal.com/css-animated-text-generator/"), + }, + }, + cssloaders: { + id: "recommend_cssloaders", + icon: "https://css-loaders.com/fav.png", + name: { + en: "CSS Loaders - 600+ css loader", + vi: "CSS Loaders - 600+ css loading", + }, + description: { + en: "The Biggest Collection of Loading Animations. Over 600+ CSS-only loaders made using a single element", + vi: "Hơn 600 animation loading css miễn phí", + }, + badges: [BADGES.recommend], + popupScript: { + onClick: () => window.open("https://css-loaders.com/"), + }, + }, + uiverse: { + id: "recommend_uiverse", + icon: "https://uiverse.io/favicon.ico", + name: { + en: "UIverse - Open-Source UI elements", + vi: "UIverse - Tổng hợp code UI xịn", + }, + description: { + en: "Open-Source UI elements for any project.", + vi: "Tổng hợp code UI mã nguồn mở cho mọi trang web.", + }, + badges: [BADGES.recommend], + popupScript: { + onClick: () => window.open("https://uiverse.io/"), + }, + }, + fontRendering: { + id: "recommend_fontRendering", + icon: '', + name: { + en: "Font Rendering - better font display", + vi: "Font Rendering - font chữ dễ nhìn", + }, + description: { + en: "Improve browser displaying, font rewriting, smoothing, scaling, stroke, shadow, special style elements, custom monospaced, etc", + vi: "Cải thiện font chữ web, giúp lướt web dễ chịu hơn.", + }, + badges: [BADGES.recommend], + popupScript: { + onClick: () => { + window.open("https://greasyfork.org/scripts/416688"); + }, + }, + }, + lol2d: { + id: "recommend_LOL2D", + icon: "https://hoangtran0410.github.io/LOL2D/favicon/apple-touch-icon.png", + name: { + en: "LOL2D - League of Legends 2D", + vi: "LOL2D - Liên minh huyền thoại 2D", + }, + description: { + en: "Play League of Legends right on your browser", + vi: "Chơi Liên minh huyền thoại ngay trên trình duyệt", + img: "https://raw.githubusercontent.com/HoangTran0410/LOL2D/main/assets/images/screenshots/Screenshot_4.jpg", + }, + popupScript: { + onClick: () => window.open("https://github.com/HoangTran0410/LOL2D"), + }, + }, + revealDeletedFBMessage: { + id: "recommend_RevealDeletedFBMessage", + icon: "https://github.com/HoangTran0410/RevealDeletedFBMessages/raw/master/icons/icon48.png", + name: { + en: "Reveal Deleted FB Message", + vi: "Xem tin nhắn FB bị gỡ", + }, + description: { + en: "Know what your friends have sent you", + vi: "Xem bạn bè đã gửi gì cho bạn", + }, + popupScript: { + onClick: () => + window.open("https://github.com/HoangTran0410/RevealDeletedFBMessages"), + }, + }, + FBMediaDownloader: { + id: "recommend_FBMediaDownloader", + icon: "https://www.facebook.com/favicon.ico", + name: { en: "FB Media Downloader", vi: "FB Media Downloader" }, + description: { + en: "Tool download media from facebook automatic", + vi: "Công cụ tải ảnh/video từ facebook tự động cực nhanh", + }, + popupScript: { + onClick: () => + window.open("https://github.com/HoangTran0410/FBMediaDownloader"), + }, + }, + nirsoft: { + id: "recommend_nirsoft", + icon: "https://www.nirsoft.net/favicon.ico", + name: { en: "Nirsoft", vi: "Nirsoft" }, + description: { + en: "A unique collection of small and useful freeware utilities", + vi: "Tổng hợp bộ công cụ nhanh, nhẹ, miễn phí dành cho windows", + }, + popupScript: { + onClick: () => window.open("https://www.nirsoft.net/"), + }, + }, + CRXViewer: { + id: "recommend_CRXViewer", + icon: "https://lh3.googleusercontent.com/fD5QA80tZj1up43xmnxnxiqKNEq7515-HNtLfjoZlz_I626zxXmjlhKaQPUme_evpCEnN5-U7VnG3VfOcnTPzv_i=w128-h128-e365-rj-sc0x00ffffff", + name: { en: "CRX Viewer", vi: "CRX Viewer" }, + description: { + en: "View/Download source code of any extension", + vi: "Xem/Tải source code của mọi extension", + }, + popupScript: { + onClick: () => + window.open( + "https://chrome.google.com/webstore/detail/chrome-extension-source-v/jifpbeccnghkjeaalbbjmodiffmgedin" + ), + }, + }, + uBlockOrigin: { + id: "recommend_uBlockOrigin", + icon: "https://lh3.googleusercontent.com/rrgyVBVte7CfjjeTU-rCHDKba7vtq-yn3o8-10p5b6QOj_2VCDAO3VdggV5fUnugbG2eDGPPjoJ9rsiU_tUZBExgLGc=s60", + name: { en: "uBlock Origin", vi: "uBlock Origin" }, + description: { + en: "Block advertisements for all website", + vi: "Chặn quảng cáo cho mọi website", + }, + popupScript: { + onClick: () => + window.open( + "https://chromewebstore.google.com/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm" + ), + }, + }, + GoogleTranslate: { + id: "recommend_GoogleTranslate", + icon: "https://lh3.googleusercontent.com/3ZU5aHnsnQUl9ySPrGBqe5LXz_z9DK05DEfk10tpKHv5cvG19elbOr0BdW_k8GjLMFDexT2QHlDwAmW62iLVdek--Q=w128-h128-e365-rj-sc0x00ffffff", + name: { en: "Google translate", vi: "Google dịch" }, + description: { + en: "Instant translation for all website", + vi: "Dịch nhanh, trực tiếp trong mọi website", + }, + popupScript: { + onClick: () => + window.open( + "https://chrome.google.com/webstore/detail/google-translate/aapbdbdomjkkjkaonfhkkikfgjllcleb" + ), + }, + }, + NSFWFilter: { + id: "recommend_NSFWFilter", + icon: "https://lh3.googleusercontent.com/M_2Q8eJAj1ejsRg30LuJs_Q94Jk7d-6ZbE5cyddULweH5LrfsVJtjK8zbpSjwA3G9oHwZeyHyrYrr971kqLwtNNP=w128-h128-e365-rj-sc0x00ffffff", + name: { + en: "NSFW Filter: Hide NSFW content", + vi: "NSFW Filter: Ẩn nội dung 18+", + }, + description: { + en: "Hide NSFW content from websites using this extension powered by AI", + vi: "Ẩn mọi nội dung 18+ trên website, sử dụng trí tuệ nhân tạo", + }, + popupScript: { + onClick: () => + window.open( + "https://chrome.google.com/webstore/detail/nsfw-filter/kmgagnlkckiamnenbpigfaljmanlbbhh" + ), + }, + }, + Violentmonkey: { + id: "recommend_Violentmonkey", + icon: "https://violentmonkey.github.io/favicon-32x32.png?v=e0d9ed50fb982761b0f7cdea8b093ae9", + name: { + en: "Violentmonkey", + vi: "Violentmonkey", + }, + description: { + en: "An open source userscript manager.", + vi: "Trình quản lý userscript tốt.", + }, + popupScript: { + onClick: () => window.open("https://violentmonkey.github.io/"), + }, + }, + Extensity: { + id: "recommend_Extensity", + icon: "https://lh3.googleusercontent.com/mgOg2hnGuthlYj-MEUXedWn_s9QjTXBwusffIAhbIuHM8L3K2c5cq1xf7bCzbRE5f9E6RXaGLPNEuJEt4hP6sLDL=s60", + name: { + en: "Extensity", + vi: "Extensity", + }, + description: { + en: "Extension manager - Quickly enable/disable browser extensions", + vi: "Trình quản lý extension - Nhanh chóng tắt/mở extension của trình duyệt", + }, + popupScript: { + onClick: () => + window.open( + "https://chromewebstore.google.com/detail/extensity/jjmflmamggggndanpgfnpelongoepncg" + ), + }, + }, +}; diff --git a/popup/tabs.js b/popup/tabs.js index 05cca66d..6504f380 100644 --- a/popup/tabs.js +++ b/popup/tabs.js @@ -1,8 +1,7 @@ import s from "../scripts/_allScripts.js"; -import { getLang } from "./helpers/lang.js"; +import { Recommend as R } from "./recommend.js"; import { canAutoRun } from "./helpers/utils.js"; import { CATEGORY } from "./helpers/category.js"; -import { BADGES } from "../scripts/helpers/badge.js"; import { getCurrentTab } from "../scripts/helpers/utils.js"; import { favoriteScriptsSaver, @@ -36,236 +35,41 @@ const tabs = [ scripts: [ // s._test, // s._ufs_statistic, - { - id: "recommend_theresanaiforthat", - icon: "https://theresanaiforthat.com/favicon.ico", - name: { - en: "There's an AI for that", - vi: "There's an AI for that - Tìm AI", - }, - description: { - en: "Collection of thousand of AI tools. Easy to search by category", - vi: "Tổng hợp hàng ngàn công cụ AI hiện có. Dễ dàng tìm kiếm theo chủ đề", - }, - popupScript: { - onClick: () => window.open("https://theresanaiforthat.com/"), - }, - }, - { - id: "recommend_timeis", - icon: "https://time.is/favicon.ico", - name: { - en: "Time.is - Check your time", - vi: "Time.is - Kiểm tra thời gian", - }, - description: { - en: "Exact time for any time zone.", - vi: "Đồng hồ chính xác nhất. Kiểm tra đồng hồ trên máy của bạn nhanh hay chậm.", - }, - popupScript: { - onClick: () => window.open("https://time.is/"), - }, - }, - { - id: "recommend_googleTrending", - icon: "https://www.gstatic.com/trends/favicon.ico", - name: { - en: "Google trending - See what trending now", - vi: "Google trending - Nội dung nổi bật", - }, - description: { - en: "See what people are searching on Google. Top treding every day, realtime.", - vi: "Xem mọi người đang tìm gì trên google. Thống kê từng ngày, thời gian thực.", - }, - popupScript: { - onClick: () => window.open("https://trends.google.com/"), - }, - }, + R.theresanaiforthat, + R.timeis, + R.googleTrending, s.similarWeb, s.similarWeb_bypassLimit, s.search_sharedAccount, - { - id: "recommend_archive", - icon: "https://archive.org/favicon.ico", - name: { - en: "Internet archive - Free library", - vi: "Internet archive - Thư viện miễn phí", - }, - description: { - en: "Non-profit library of millions of free books, movies, software, music, websites, and more.", - vi: "Thư viện với hàng triệu sách, báo, phim, phần mềm, nhạc, website, ... miễn phí", - img: "/scripts/internet_archive.png", - }, - badges: [BADGES.recommend], - popupScript: { - onClick: () => window.open("https://archive.org/"), - }, - }, - { - id: "recommend_wappalyzer", - icon: "https://www.wappalyzer.com/favicon.ico", - name: { - en: "Wappalyzer - view website stacks", - vi: "Wappalyzer - Web dùng công nghệ gì?", - }, - description: { - en: "Technology that current website is using", - vi: "Xem những công nghệ/thư viện trang web đang dùng", - }, - badges: [BADGES.recommend], - popupScript: { - onClick: () => window.open("https://www.wappalyzer.com/apps/"), - }, - }, + R.archive, + R.wappalyzer, s.whois, s.viewWebMetaInfo, - { - id: "recommend_search_musicTreding", - icon: "https://kworb.net/favicon.ico", - name: { - en: "Top global treding music?", - vi: "Bài nhạc top treding toàn cầu?", - }, - description: { - en: "The web to find all kinds of music-related data.", - vi: "Trang web thống kê top trending âm nhạc toàn cầu.", - }, - popupScript: { - onClick: () => - window.open("https://kworb.net/youtube/trending_music.html"), - }, - }, + R.search_musicTreding, s.search_paperWhere, s.search_hopamchuan, s.checkWebDie, s.downDetector, s.openWaybackUrl, s.archiveToday, - { - id: "recommend_search_userscript", - icon: "https://www.userscript.zone/favicon.ico", - name: { - en: "Search Userscripts", - vi: "Tìm Userscripts", - }, - description: { - en: "Search Userscripts on Usersript.zone", - vi: "Tìm Userscripts trên Usersript.zone", - }, - - popupScript: { - onClick: () => window.open("https://www.userscript.zone/"), - }, - }, + R.search_userscript, ], }, { ...CATEGORY.download, scripts: [ createTitle("--- All in one ---", "--- Tổng hợp ---"), - { - id: "recommend_cobalt", - icon: "https://cobalt.tools/favicon.ico", - name: { - en: "Cobalt - Media downloader", - vi: "Cobalt - Tải video/nhạc", - }, - description: { - en: "Support youtube, tiktok, instagram, twitter/x, bilibili, twitch, vimeo, soundcloud, dailymotion, pinterest, reddit, tumblr, ...", - vi: "Hỗ trợ youtube, tiktok, instagram, twitter/x, bilibili, twitch, vimeo, soundcloud, dailymotion, pinterest, reddit, tumblr, ...", - }, - badges: [BADGES.recommend, BADGES.new], - buttons: [ - { - icon: '', - name: { - vi: "Github", - en: "Github", - }, - onClick: () => window.open("https://github.com/imputnet/cobalt"), - }, - ], - popupScript: { - onClick: () => window.open("https://cobalt.tools/"), - }, - }, + R.cobalt, s.saveAllVideo, s.vuiz_getLink, s.savevideo_me, - { - id: "getLinkLuanxt_newtab", - icon: "https://luanxt.com/get-link-mp3-320-lossless-vip-zing/favicon.ico", - name: { - en: "Get audio/video (luanxt)", - vi: "Tải nhạc/video (luanxt)", - }, - description: { - en: "Using API from luanxt.com. Download Zing MP3, Zing Video Clip, Zing TV, NhacCuaTui, YouTube, SoundCloud, Nhac.vn, ChiaSeNhac.vn, Facebook Video, Keeng Audio, Keeng Video, Keeng Phim", - vi: "Sử dụng API của luanxt.com. Tải Zing MP3, Zing Video Clip, Zing TV, NhacCuaTui, YouTube, SoundCloud, Nhac.vn, ChiaSeNhac.vn, Facebook Video, Keeng Audio, Keeng Video, Keeng Phim", - }, - infoLink: "https://luanxt.com/get-link-mp3-320-lossless-vip-zing/", - badges: [BADGES.recommend], - popupScript: { - onClick: () => - window.open( - "https://luanxt.com/get-link-mp3-320-lossless-vip-zing/" - ), - }, - }, + R.luanxt, createTitle("--- Photos ---", "--- Ảnh ---"), s.twitter_downloadButton, s.getFavicon, - { - id: "recommend_picviewer_ce+", - icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAV1BMVEUAAAD////29vbKysoqKioiIiKysrKhoaGTk5N9fX3z8/Pv7+/r6+vk5OTb29vOzs6Ojo5UVFQzMzMZGRkREREMDAy4uLisrKylpaV4eHhkZGRPT08/Pz/IfxjQAAAAgklEQVQoz53RRw7DIBBAUb5pxr2m3/+ckfDImwyJlL9DDzQgDIUMRu1vWOxTBdeM+onApENF0qHjpkOk2VTwLVEF40Kbfj1wK8AVu2pQA1aBBYDHJ1wy9Cf4cXD5chzNAvsAnc8TjoLAhIzsBao9w1rlVTIvkOYMd9nm6xPi168t9AYkbANdajpjcwAAAABJRU5ErkJggg==", - name: { - en: "Picviewer CE+ download images", - vi: "Picviewer CE+ tải ảnh", - }, - description: { - en: "Powerful picture viewing tool online, which can popup/scale/rotate/batch save pictures automatically", - vi: "Công cụ mạnh mẽ để xem/tải ảnh hàng loạt, cho tất cả trang web", - img: "https://v2fy.com/asset/063_picviewer_ce/73130353-c4598e00-4031-11ea-810e-9498677a40d1.gif", - }, - badges: [BADGES.recommend], - popupScript: { - onClick: () => - window.open("https://greasyfork.org/en/scripts/24204-picviewer-ce"), - }, - }, - { - id: "recommend_file_converter", - icon: "https://file-converter.io/favicon.ico", - name: { - en: "File-converter.io - change image type", - vi: "File-converter.io - chuyển đổi ảnh", - }, - description: { - en: "Powerful tool which allows you to convert and compress files using the context menu in windows explorer.", - vi: "Công cụ nén ảnh, đổi định dạng ảnh hàng loạt, trực tiếp bằng chuột phải.", - img: "https://file-converter.io/images/file-converter-usage.gif", - }, - badges: [BADGES.recommend], - popupScript: { - onClick: () => window.open("https://file-converter.io/"), - }, - }, - { - id: "recommend_squoosh_app", - icon: "https://squoosh.app/c/icon-large-maskable-c2078ced.png", - name: { - en: "Squoosh.app - compress images", - vi: "Squoosh.app - nén ảnh", - }, - description: { - en: "Make images smaller using best-in-class codecs, right in the browser.", - vi: "Công cụ nén ảnh mạnh mẽ, giảm kích thước ngay trên trình duyệt", - }, - badges: [BADGES.recommend], - popupScript: { - onClick: () => window.open("https://squoosh.app/"), - }, - }, + R.picviewer_ce, + R.file_converter, + R.squoosh_app, createTitle("--- Music ---", "--- Nhạc ---"), s.spotify_downloadButton, s.soundcloud_downloadMusic, @@ -280,42 +84,9 @@ const tabs = [ s.studocu_downs, s.scribd_downloadDocuments, s.tailieu_vn, - { - id: "recommend_docsdownloader", - icon: "https://docsdownloader.com/assets/img/android-icon-192x192.png", - name: { - en: "DocDownloader - Download document", - vi: "DocDownloader - Tải document", - }, - description: { - en: "Download document on Scribd, Everand, Slideshare, Issuu, Academia, Chegg, Researchgate, Coursehero, Studocu, Perlego, Yumpu, Tiendeo, Fliphtml5, Anyflip, Docsity, Passei direto, Udocz", - vi: "Tải document từ Scribd, Everand, Slideshare, Issuu, Academia, Chegg, Researchgate, Coursehero, Studocu, Perlego, Yumpu, Tiendeo, Fliphtml5, Anyflip, Docsity, Passei direto, Udocz", - }, - badges: [BADGES.recommend], - popupScript: { - onClick: () => window.open("https://docdownloader.com/"), - }, - }, + R.docsdownloader, s.bookmark_exporter, - { - id: "recommend_BookmarkSidebar", - icon: "https://lh3.googleusercontent.com/4kT7DxtoPSmSLzTit1w2Vbx7b1L2zkASTrqGzEpBW-qs2EwmLYzBTyv0cvlGZo-rD-s732OIrUXX-C33RHPSFvOj=s0", - name: { - en: "Bookmark Sidebar", - vi: "Bookmark Sidebar", - }, - description: { - en: "Very good Bookmark manager, find your bookmarks faster.", - vi: "Trình quản lý bookmark ngon, tìm kiếm bookmark nhanh hơn bao giờ hết.", - }, - badges: [BADGES.recommend], - popupScript: { - onClick: () => - window.open( - "https://chromewebstore.google.com/detail/thanh-d%E1%BA%A5u-trang/jdbnofccmhefkmjbkkdkfiicjkgofkdh" - ), - }, - }, + R.bookmarkSidebar, ], }, { @@ -332,25 +103,7 @@ const tabs = [ createTitle("--- Bulk Download ---", "--- Tải hàng loạt ---"), s.ggDrive_downloadAllVideosInFolder, createTitle("--- More ---", "--- Khác ---"), - { - id: "recommend_googleAdvanced", - icon: "https://www.google.com/favicon.ico", - name: { - en: "Google search advanced", - vi: "Google tìm kiếm nâng cao", - }, - description: { - en: "Search google with a lot of advanced features", - vi: "Tìm kiếm google với nhiều tuỳ chọn nâng cao", - }, - badges: [BADGES.recommend], - popupScript: { - onClick: () => - window.open( - "https://www.google.com/advanced_search?hl=" + getLang() + "&fg=1" - ), - }, - }, + R.googleAdvanced, s.search_totalIndexedPages, s.search_googleSite, s.googleShortcuts, @@ -369,10 +122,6 @@ const tabs = [ s.fb_videoDownloader, s.fb_getAvatarFromUid, s.fb_exportSaved, - // createTitle("--- Bulk Download ---", "--- Tải hàng loạt ---"), - // s.fb_downloadAlbumMedia, - // s.fb_downloadWallMediaFromPosts, - // s.fb_getAllAlbumInformation, createTitle("--- Hot ---", "--- Nổi bật ---"), s.fb_autoLike, s.fb_revealDeletedMessages, @@ -393,7 +142,6 @@ const tabs = [ s.fb_getTokenFacebook, s.fb_getTokenMessage, s.fb_getTokenBussinessLocation, - // s.fb_getTokenBusinessStudio, s.fb_getTokenCampaigns, s.fb_getTokenFfb, createTitle("--- Get ID ---", "--- Lấy ID ---"), @@ -406,151 +154,15 @@ const tabs = [ s.fb_getAllUidFromFbSearch, s.fb_getAllUidOfGroupMembers, createTitle("--- Shortcut ---", "--- Phím tắt ---"), - { - id: "fb_openSaved", - icon: '', - name: { - en: "View your facebook saved", - vi: "Xem mục đã lưu trên facebook", - }, - description: { - en: "View saved contents on Facebook", - vi: "Xem nội dung bạn đã lưu trên Facebook", - }, - - popupScript: { - onClick: () => window.open("https://www.facebook.com/saved"), - }, - }, - { - id: "fb_openMemories", - icon: '', - name: { - en: "View your memories on facebook", - vi: "Xem kỷ niệm của bạn trên facebook", - }, - description: { - en: "View your memories on facebook", - vi: "Xem kỷ niệm (memories) của bạn trên facebook", - }, - popupScript: { - onClick: () => window.open("https://www.facebook.com/memories/"), - }, - }, - { - id: "fb_openAdsActivities", - icon: '', - name: { - en: "View your ads activities", - vi: "Xem các quảng cáo fb bạn đã xem", - }, - description: { - en: "View ads you have seen on facebook", - vi: "Xem các quảng cáo bạn đã xem trên facebook", - }, - popupScript: { - onClick: () => window.open("https://www.facebook.com/ads/activity"), - }, - }, - { - id: "fb_openAllActivities", - icon: '', - name: { - en: "Check your activities on Facebook", - vi: "Xem nhật ký hoạt động trên facebook", - }, - description: { - en: "Check all your activities on facebook", - vi: "Kiểm tra nhật ký hoạt động của bạn trên facebook", - }, - popupScript: { - onClick: () => window.open("https://www.facebook.com/me/allactivity"), - }, - }, - { - id: "fb_openVideoActivities", - icon: '', - name: { - en: "Video you watched on facebook", - vi: "Video bạn vừa xem trên facebook", - }, - description: { - en: "View all videos you watched on facebook", - vi: "Xem lại những video bạn đã xem trên facebook", - }, - badges: [BADGES.new], - popupScript: { - onClick: () => - window.open( - "https://www.facebook.com/100004848287494/allactivity?activity_history=false&category_key=VIDEOWATCH&manage_mode=false&should_load_landing_page=false" - ), - }, - }, - { - id: "fb_openPassEvents", - icon: '', - name: { - en: "Events joined on facebook", - vi: "Sự kiện đã tham gia trên facebook", - }, - description: { - en: "View pass events that you have joined on facebook.", - vi: "Xem tất cả sự kiện bạn từng tham gia trên facebook.", - }, - badges: [BADGES.new], - popupScript: { - onClick: () => window.open("https://www.facebook.com/events/past"), - }, - }, - { - id: "fb_openBirthdays", - icon: '', - name: { - en: "Facebook friend's birthdays", - vi: "Sinh nhật bạn bè facebook", - }, - description: { - en: "View your friend's birthdays each month on facebook", - vi: "Xem từng tháng có những sinh nhật nào của bạn bè trên facebook.", - }, - badges: [BADGES.new], - popupScript: { - onClick: () => - window.open("https://www.facebook.com/events/birthdays"), - }, - }, - { - id: "fb_openChangeLanguage", - icon: '', - name: { - en: "Change language facebook", - vi: "Đổi ngôn ngữ facebook", - }, - description: { - en: "Change display language on facebook", - vi: "Đổi ngôn ngữ hiển thị trên facebook", - }, - popupScript: { - onClick: () => - window.open("https://www.facebook.com/settings/?tab=language"), - }, - }, - { - id: "fb_openAccountHacked", - icon: '', - name: { - en: "Recover facebook account", - vi: "Khôi phục tài khoản facebook", - }, - description: { - en: "Your fb account has been hacked? Facebook can help you.", - vi: "Tài khoản fb của bạn bị hack? Facebook có thể giúp bạn.", - }, - badges: [BADGES.new], - popupScript: { - onClick: () => window.open("https://fb.com/hacked"), - }, - }, + R.fb_openSaved, + R.fb_openMemories, + R.fb_openAdsActivities, + R.fb_openAllActivities, + R.fb_openVideoActivities, + R.fb_openPassEvents, + R.fb_openBirthdays, + R.fb_openChangeLanguage, + R.fb_openAccountHacked, ], }, { @@ -578,25 +190,7 @@ const tabs = [ s.pip_anything, s.pip_fullWebsite, s.pip_canvas, - { - id: "recommend_improve_youtube", - icon: "https://lh3.googleusercontent.com/WDytHNO8o0Ev6sWp_yLbya_SSS9kXZWGJIc-WJ3goInHJalzD02Aq5wVhExFlbzrzNsOxo-V1O_TgF-JLJNyTkvB=s0", - name: { - en: "Improve YouTube - 85+ features", - vi: "Improve YouTube - 85+ chức năng", - }, - description: { - en: "Make YouTube more beautiful, faster, and more useful!", - vi: "Làm cho YouTube gọn gàng+thông minh!", - }, - badges: [BADGES.recommend], - popupScript: { - onClick: () => - window.open( - "https://chromewebstore.google.com/detail/improve-youtube-%F0%9F%8E%A7-for-yo/bnomihfieiccainjcjblhegjgglakjdd" - ), - }, - }, + R.improve_youtube, ], }, { @@ -642,185 +236,27 @@ const tabs = [ s.send_shareFiles, s.vuiz_createLogo, s.performanceAnalyzer, - { - id: "recommend_ItTools", - icon: "https://it-tools.tech/favicon-32x32.png", - name: { - en: "IT Tools - All for Developers", - vi: "IT Tools - Vì tương lai Developer", - }, - description: { - en: "Handy tools for developers (open source)", - vi: "Tổng hợp tools hữu ích cho IT (mã nguồn mở)", - }, - badges: [BADGES.recommend], - popupScript: { - onClick: () => window.open("https://it-tools.tech/"), - }, - }, - // https://copyicon.com/generator/svg-chart - { - id: "recommend_copyicon", - icon: "https://copyicon.com/favicon.ico", - name: { - en: "CopyIcon - FREE emoji, icon, generator", - vi: "CopyIcon - emoji, icon, svg miễn phí", - }, - description: { - en: "285,000 free Icons, Emoji, SVG generator, and more...", - vi: "285,000 Icons, Emiji, trình tạo SVG, và hơn thế nữa...", - }, - badges: [BADGES.recommend], - popupScript: { - onClick: () => - window.open("https://copyicon.com/generator/svg-chart"), - }, - }, - { - id: "recommend_beautifytools", - icon: "https://beautifytools.com/img/favicon.ico", - name: { en: "Beautify Tools", vi: "Beautify Tools" }, - description: { - en: `Handy tools for developers -
    -
  1. Beautifiers And Minifiers
  2. -
  3. CSS Preprocessors
  4. -
  5. Converters
  6. -
  7. String Utilities
  8. -
  9. SEO Tools
  10. -
  11. IP Tools
  12. -
  13. Code Validators
  14. -
  15. Cryptography
  16. -
  17. Code Editors
  18. -
`, - vi: `Tổng hợp tools hữu ích cho IT -
    -
  1. Beautifiers And Minifiers
  2. -
  3. CSS Preprocessors
  4. -
  5. Converters
  6. -
  7. String Utilities
  8. -
  9. SEO Tools
  10. -
  11. IP Tools
  12. -
  13. Code Validators
  14. -
  15. Cryptography
  16. -
  17. Code Editors
  18. -
`, - }, - badges: [BADGES.recommend], - popupScript: { - onClick: () => window.open("https://beautifytools.com/"), - }, - }, + R.itTools, + R.copyicon, + R.beautifytools, createTitle("--- Github ---", "--- Github ---"), s.github_goToAnyCommit, s.github_HTMLPreview, s.github_openRepoPages, s.githubdev, s.github1s, - { - id: "recommend_cloc", - icon: '', - name: { - en: "Cloc - count line of code", - vi: "Cloc - đếm số dòng code", - }, - description: { - en: "Count blank lines, comment lines, and physical lines of source code in many programming languages.", - vi: "Đếm dòng trống, comment, dòng code trong repo, hỗ trợ nhiều ngôn ngữ lập trình.", - img: "/scripts/recommend_cloc.png", - }, - badges: [BADGES.recommend], - popupScript: { - onClick: () => - window.open("https://github.com/AlDanial/cloc?tab=readme-ov-file"), - }, - }, - { - id: "recommend_refined_github", - icon: "https://lh3.googleusercontent.com/4N2wipmBVx1qK0R0E0XdADE31-8IuMylOtO9AyFopOA9i3IQKoCC5L4nYFDy55xpxpk6qKusHuqXyKJqvw8jcJaiqg=s60", - name: { - en: "Refined GitHub ", - vi: "Refined GitHub", - }, - description: { - en: "Simplifies the GitHub interface and adds useful features", - vi: "Sửa giao diện github và thêm hàng tá chức năng hay", - }, - badges: [BADGES.recommend], - popupScript: { - onClick: () => - window.open( - "https://chromewebstore.google.com/detail/refined-github/hlepfoohegkhhmjieoechaddaejaokhf" - ), - }, - }, + R.cloc, + R.refined_github, createTitle("--- Shopping ---", "--- Mua sắm ---"), s.shopee_topVariation, s.shopee_totalSpendMoney, s.shopee_totalSpendMoney_excel, s.tiki_totalSpendMoney, - { - id: "recommend_Beecost", - icon: "https://lh3.googleusercontent.com/QeCUs-fM4mwAmBVRS0VU8NrjJnDnbSsXoqUrCbd8ZbHou03FBPEQOYHAcdcL_rn7NMrUpWMcXoG2m_CrKtAhc-wLgLU=w128-h128-e365-rj-sc0x00ffffff", - name: { en: "Beecost", vi: "Beecost" }, - description: { - en: "Check deals/prices in ecommerce websites", - vi: "Kiểm tra giá/ưu đãi giả khi mua hàng online", - }, - badges: [BADGES.recommend], - popupScript: { - onClick: () => window.open("https://beecost.vn/"), - }, - }, + R.beecost, createTitle("--- PDF ---", "--- PDF ---"), - { - id: "recommend_fastDoc", - icon: "https://fastdoc.vn/favicon.png", - name: { - en: "FastDoc - Convert PDF/Photo to Word/Excel", - vi: "FastDoc - Chuyển PDF/Ảnh sang Word/Excel", - }, - badges: [BADGES.recommend], - description: { - en: "Convert Photos & PDF to Excel, Word, Searchable PDF for free", - vi: "Chuyển đổi hình ảnh và pdf sang Excel, Word, Searchable PDF miễn phí", - }, - popupScript: { - onClick: () => window.open("https://fastdoc.vn/"), - }, - }, - { - id: "recommend_smartPDF", - icon: "https://smallpdf.com/favicon.ico", - name: { - en: "SmartPDF - Tools for PDF", - vi: "SmartPDF - Công cụ cho PDF", - }, - description: { - en: "Compress PDF, PDF Converter, PPT to PDF, PDF to PPT, JPG to PDF, PDF to JPG, Excel to PDF, PDF to Excel, Edit PDF, PDF Reader, Number Pages, Delete PDF Pages, Rotate PDF, Word to PDF, PDF to Word, Merge PDF, Split PDF, eSign PDF, Unlock PDF, Protect PDF, PDF Scanner", - vi: "Giảm dung lượng PDF, Chuyển đổi PDF, PPT sang PDF, PDF sang PPT, JPG sang PDF, PDF sang JPG, Excel sang PDF, PDF sang Excel, Chỉnh sửa PDF, Trình đọc PDF, Số trang, Xóa các trang PDF, Xoay PDF, Word sang PDF, PDF sang Word, Ghép PDF, Cắt PDF, Ký tên PDF, Mở khóa PDF, Bảo vệ PDF, Máy quét PDF", - }, - badges: [BADGES.recommend], - popupScript: { - onClick: () => window.open("https://smallpdf.com/vi/cac-cong-cu-pdf"), - }, - }, - { - id: "recommend_pdfstuffs", - icon: "https://pdfstuff.com/themes/pdfstuff/img/favicons/apple-icon-57x57.png", - name: { - en: "PDF Stuffs - Tools for PDF", - vi: "PDF Stuffs - Công cụ PDF", - }, - description: { - en: "Free PDF converter online service: Merge PDF, Split PDF, Compress PDF, PDF to Word, PDF to PPT, PDF to Excel, Word to PDF, Excel to PDF, PPT to PDF, PDF to JPG, JPG to PDF, PDF to HTML, HTML to PDF, Unlock PDF, Protect PDF, Rotate PDF, Crop PDF, Delete pages, Add page numbers, Watermark PDF", - vi: "Công cụ chuyển đổi PDF online miễn phí: Ghép file PDF, Tách file PDF, Nén file PDF, PDF sang Word, PDF sang PPT, PDF sang Excel, Word sang PDF, Excel sang PDF, PPT sang PDF, PDF sang JPG, JPG sang PDF, PDF sang HTML, HTML sang PDF, Mở khóa PDF, Khóa file PDF, Xoay file PDF, Cắt file PDF, Xóa trang PDF, Đánh số trang PDF, Chèn watermark", - }, - badges: [BADGES.recommend], - popupScript: { - onClick: () => window.open("https://pdfstuff.com/"), - }, - }, + R.fastDoc, + R.smartPDF, + R.pdfstuffs, ], }, { @@ -845,71 +281,9 @@ const tabs = [ s.viewCookies, s.removeCookies, createTitle("--- Other ---", "--- Khác ---"), - { - id: "recommend_chromeFlags", - icon: '', - name: { - en: "Make browser super fast", - vi: "Tăng tốc tối đa trình duyệt", - }, - description: { - en: "Some flags experiments that can make your browser super fast", - vi: "Các flags giúp trình duyệt của bạn chạy nhanh hơn thỏ", - }, - badges: [BADGES.recommend], - popupScript: { - onClick: () => - window.open( - "https://www.androidauthority.com/chrome-flags-1009941/" - ), - }, - }, - { - id: "recommend_viewSavedWifiPass", - icon: '', - name: { - en: "View saved wifi passwords", - vi: "Xem mật khẩu wifi đã lưu", - }, - description: { - en: "PowerShell script to view saved wifi passwords on your computer", - vi: "Powershell script giúp xem mật khẩu wifi đã lưu trên máy tính", - }, - infoLink: - "https://www.facebook.com/groups/j2team.community/posts/2328915024107271/", - - popupScript: { - onClick: () => { - prompt( - `File danh sách mật khẩu Wifi sẽ lưu ở: - "C:\\WifiPasswords\\listWifiPasswords.txt" - có dạng: [Tên Wifi]:[Mật khẩu] - - Mở Powershell và chạy lệnh sau:`, - `irm https://tinyurl.com/GetListWifiPasswords | iex` - ); - }, - }, - }, - { - id: "recommend_leakCheck", - icon: "https://leakcheck.io/favicon.ico", - name: { - en: "Leak check - your password has been leaked?", - vi: "Leak check - lộ mật khẩu email?", - }, - description: { - en: "Check your password has been leaked on internet or not", - vi: "Kiểm tra xem mật khẩu email/username của bạn có bị phát tán trên mạng hay không", - }, - infoLink: - "https://www.facebook.com/groups/j2team.community/posts/2329878560677584/", - popupScript: { - onClick: () => { - window.open("https://okela.fun/"); - }, - }, - }, + R.chromeFlags, + R.viewSavedWifiPass, + R.leakCheck, ], }, { @@ -921,92 +295,12 @@ const tabs = [ s.showFPS, s.showFps_v2, s.toggle_passwordField, - { - id: "recommend_DarkReader", - icon: "https://lh3.googleusercontent.com/T66wTLk-gpBBGsMm0SDJJ3VaI8YM0Utr8NaGCSANmXOfb84K-9GmyXORLKoslfxtasKtQ4spDCdq_zlp_t3QQ6SI0A=w128-h128-e365-rj-sc0x00ffffff", - name: { en: "Dark reader", vi: "Dark reader" }, - description: { - en: "Darkmode for every website", - vi: "Chế độ tối cho mọi trang web", - }, - badges: [BADGES.recommend], - popupScript: { - onClick: () => - window.open( - "https://chrome.google.com/webstore/detail/dark-reader/eimadpbcbfnmbkopoojfekhnkhdbieeh" - ), - }, - }, - { - id: "recommend_cssportal", - icon: "https://www.cssportal.com/favicon.ico", - name: { - en: "CSS Portal - Empowered your CSS skills", - vi: "CSS Portal - Nâng trình CSS", - }, - description: { - en: "Empowered your CSS skills with hundreds of CSS tools.", - vi: "Công cụ tự động giúp nâng trình CSS của bạn với hàng trăm chức năng.", - }, - badges: [BADGES.recommend], - popupScript: { - onClick: () => - window.open( - "https://www.cssportal.com/css-animated-text-generator/" - ), - }, - }, - { - id: "recommend_cssloaders", - icon: "https://css-loaders.com/fav.png", - name: { - en: "CSS Loaders - 600+ css loader", - vi: "CSS Loaders - 600+ css loading", - }, - description: { - en: "The Biggest Collection of Loading Animations. Over 600+ CSS-only loaders made using a single element", - vi: "Hơn 600 animation loading css miễn phí", - }, - badges: [BADGES.recommend], - popupScript: { - onClick: () => window.open("https://css-loaders.com/"), - }, - }, - { - id: "recommend_uiverse", - icon: "https://uiverse.io/favicon.ico", - name: { - en: "UIverse - Open-Source UI elements", - vi: "UIverse - Tổng hợp code UI xịn", - }, - description: { - en: "Open-Source UI elements for any project.", - vi: "Tổng hợp code UI mã nguồn mở cho mọi trang web.", - }, - badges: [BADGES.recommend], - popupScript: { - onClick: () => window.open("https://uiverse.io/"), - }, - }, + R.darkReader, + R.cssportal, + R.cssloaders, + R.uiverse, createTitle("--- View ---", "--- Xem ---"), - { - id: "recommend_fontRendering", - icon: '', - name: { - en: "Font Rendering - better font display", - vi: "Font Rendering - font chữ dễ nhìn", - }, - description: { - en: "Improve browser displaying, font rewriting, smoothing, scaling, stroke, shadow, special style elements, custom monospaced, etc", - vi: "Cải thiện font chữ web, giúp lướt web dễ chịu hơn.", - }, - badges: [BADGES.recommend], - popupScript: { - onClick: () => { - window.open("https://greasyfork.org/scripts/416688"); - }, - }, - }, + R.fontRendering, s.whatFont, s.visualEvent, s.listAllImagesInWeb, @@ -1034,165 +328,19 @@ const tabs = [ const recommendTab = { ...CATEGORY.recommend, scripts: [ - { name: { en: "--- Same author ---", vi: "--- Cùng tác giả ---" } }, - { - id: "recommend_LOL2D", - icon: "https://hoangtran0410.github.io/LOL2D/favicon/apple-touch-icon.png", - name: { - en: "LOL2D - League of Legends 2D", - vi: "LOL2D - Liên minh huyền thoại 2D", - }, - description: { - en: "Play League of Legends right on your browser", - vi: "Chơi Liên minh huyền thoại ngay trên trình duyệt", - img: "https://raw.githubusercontent.com/HoangTran0410/LOL2D/main/assets/images/screenshots/Screenshot_4.jpg", - }, - popupScript: { - onClick: () => window.open("https://github.com/HoangTran0410/LOL2D"), - }, - }, - { - id: "recommend_RevealDeletedFBMessage", - icon: "https://github.com/HoangTran0410/RevealDeletedFBMessages/raw/master/icons/icon48.png", - name: { - en: "Reveal Deleted FB Message", - vi: "Xem tin nhắn FB bị gỡ", - }, - description: { - en: "Know what your friends have sent you", - vi: "Xem bạn bè đã gửi gì cho bạn", - }, - popupScript: { - onClick: () => - window.open( - "https://github.com/HoangTran0410/RevealDeletedFBMessages" - ), - }, - }, - { - id: "recommend_FBMediaDownloader", - icon: "https://www.facebook.com/favicon.ico", - name: { en: "FB Media Downloader", vi: "FB Media Downloader" }, - description: { - en: "Tool download media from facebook automatic", - vi: "Công cụ tải ảnh/video từ facebook tự động cực nhanh", - }, - popupScript: { - onClick: () => - window.open("https://github.com/HoangTran0410/FBMediaDownloader"), - }, - }, - // https://www.nirsoft.net/ - { name: { en: "--- Tools ---", vi: "--- Công cụ hay ---" } }, - { - id: "recommend_nirsoft", - icon: "https://www.nirsoft.net/favicon.ico", - name: { en: "Nirsoft", vi: "Nirsoft" }, - description: { - en: "A unique collection of small and useful freeware utilities", - vi: "Tổng hợp bộ công cụ nhanh, nhẹ, miễn phí dành cho windows", - }, - popupScript: { - onClick: () => window.open("https://www.nirsoft.net/"), - }, - }, - { name: { en: "--- Extensions ---", vi: "--- Extensions hay ---" } }, - { - id: "recommend_CRXViewer", - icon: "https://lh3.googleusercontent.com/fD5QA80tZj1up43xmnxnxiqKNEq7515-HNtLfjoZlz_I626zxXmjlhKaQPUme_evpCEnN5-U7VnG3VfOcnTPzv_i=w128-h128-e365-rj-sc0x00ffffff", - name: { en: "CRX Viewer", vi: "CRX Viewer" }, - description: { - en: "View/Download source code of any extension", - vi: "Xem/Tải source code của mọi extension", - }, - popupScript: { - onClick: () => - window.open( - "https://chrome.google.com/webstore/detail/chrome-extension-source-v/jifpbeccnghkjeaalbbjmodiffmgedin" - ), - }, - }, - { - id: "recommend_uBlockOrigin", - icon: "https://lh3.googleusercontent.com/rrgyVBVte7CfjjeTU-rCHDKba7vtq-yn3o8-10p5b6QOj_2VCDAO3VdggV5fUnugbG2eDGPPjoJ9rsiU_tUZBExgLGc=s60", - name: { en: "uBlock Origin", vi: "uBlock Origin" }, - description: { - en: "Block advertisements for all website", - vi: "Chặn quảng cáo cho mọi website", - }, - popupScript: { - onClick: () => - window.open( - "https://chromewebstore.google.com/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm" - ), - }, - }, - { - id: "recommend_GoogleTranslate", - icon: "https://lh3.googleusercontent.com/3ZU5aHnsnQUl9ySPrGBqe5LXz_z9DK05DEfk10tpKHv5cvG19elbOr0BdW_k8GjLMFDexT2QHlDwAmW62iLVdek--Q=w128-h128-e365-rj-sc0x00ffffff", - name: { en: "Google translate", vi: "Google dịch" }, - description: { - en: "Instant translation for all website", - vi: "Dịch nhanh, trực tiếp trong mọi website", - }, - popupScript: { - onClick: () => - window.open( - "https://chrome.google.com/webstore/detail/google-translate/aapbdbdomjkkjkaonfhkkikfgjllcleb" - ), - }, - }, - { - id: "recommend_NSFWFilter", - icon: "https://lh3.googleusercontent.com/M_2Q8eJAj1ejsRg30LuJs_Q94Jk7d-6ZbE5cyddULweH5LrfsVJtjK8zbpSjwA3G9oHwZeyHyrYrr971kqLwtNNP=w128-h128-e365-rj-sc0x00ffffff", - name: { - en: "NSFW Filter: Hide NSFW content", - vi: "NSFW Filter: Ẩn nội dung 18+", - }, - description: { - en: "Hide NSFW content from websites using this extension powered by AI", - vi: "Ẩn mọi nội dung 18+ trên website, sử dụng trí tuệ nhân tạo", - }, - popupScript: { - onClick: () => - window.open( - "https://chrome.google.com/webstore/detail/nsfw-filter/kmgagnlkckiamnenbpigfaljmanlbbhh" - ), - }, - }, - { - id: "recommend_Violentmonkey", - icon: "https://violentmonkey.github.io/favicon-32x32.png?v=e0d9ed50fb982761b0f7cdea8b093ae9", - name: { - en: "Violentmonkey", - vi: "Violentmonkey", - }, - description: { - en: "An open source userscript manager.", - vi: "Trình quản lý userscript tốt.", - }, - popupScript: { - onClick: () => window.open("https://violentmonkey.github.io/"), - }, - }, - { - id: "recommend_Extensity", - icon: "https://lh3.googleusercontent.com/mgOg2hnGuthlYj-MEUXedWn_s9QjTXBwusffIAhbIuHM8L3K2c5cq1xf7bCzbRE5f9E6RXaGLPNEuJEt4hP6sLDL=s60", - name: { - en: "Extensity", - vi: "Extensity", - }, - description: { - en: "Extension manager - Quickly enable/disable browser extensions", - vi: "Trình quản lý extension - Nhanh chóng tắt/mở extension của trình duyệt", - }, - popupScript: { - onClick: () => - window.open( - "https://chromewebstore.google.com/detail/extensity/jjmflmamggggndanpgfnpelongoepncg" - ), - }, - }, + createTitle("--- Same author ---", "--- Cùng tác giả ---"), + R.lol2d, + R.revealDeletedFBMessage, + R.FBMediaDownloader, + createTitle("--- Tools ---", "--- Công cụ hay ---"), + R.nirsoft, + createTitle("--- Extensions ---", "--- Extensions hay ---"), + R.CRXViewer, + R.uBlockOrigin, + R.GoogleTranslate, + R.NSFWFilter, + R.Violentmonkey, + R.Extensity, ], }; diff --git a/scripts/_index.js b/scripts/_index.js index 78752e72..1b11da8f 100644 --- a/scripts/_index.js +++ b/scripts/_index.js @@ -2,7 +2,6 @@ export { default as _test } from "./_test.js"; export { default as fb_toggleLight } from "./fb_toggleLight.js"; -export { default as fb_getTokenBusinessStudio } from "./fb_getTokenBusinessStudio.js"; export { default as fb_getTokenFacebook } from "./fb_getTokenFacebook.js"; export { default as fb_getUid } from "./fb_getUid.js"; export { default as fb_getPageId } from "./fb_getPageId.js"; @@ -13,7 +12,6 @@ export { default as fb_getUidFromUrl } from "./fb_getUidFromUrl.js"; export { default as fb_getAllUidFromFbSearch } from "./fb_getAllUidFromFbSearch.js"; export { default as fb_getAllUidOfGroupMembers } from "./fb_getAllUidOfGroupMembers.js"; export { default as fb_getAvatarFromUid } from "./fb_getAvatarFromUid.js"; -export { default as fb_downloadAlbumMedia } from "./fb_downloadAlbumMedia.js"; export { default as insta_getUserInfo } from "./insta_getUserInfo.js"; export { default as insta_getAllUserMedia } from "./insta_getAllUserMedia.js"; export { default as pictureInPicture } from "./pictureInPicture.js"; @@ -116,7 +114,6 @@ export { default as scribd_bypassPreview } from "./scribd_bypassPreview.js"; export { default as fb_searchGroupForOther } from "./fb_searchGroupForOther.js"; export { default as fb_searchPageForOther } from "./fb_searchPageForOther.js"; export { default as tailieu_vn } from "./tailieu_vn.js"; -export { default as fb_downloadWallMediaFromPosts } from "./fb_downloadWallMediaFromPosts.js"; export { default as fb_getAllAlbumInformation } from "./fb_getAllAlbumInformation.js"; export { default as textToSpeech } from "./textToSpeech.js"; export { default as shopee_totalSpendMoney_excel } from "./shopee_totalSpendMoney_excel.js"; diff --git a/scripts/auto_redirectLargestImageSrc.js b/scripts/auto_redirectLargestImageSrc.js index 32219ba4..e48a066a 100644 --- a/scripts/auto_redirectLargestImageSrc.js +++ b/scripts/auto_redirectLargestImageSrc.js @@ -48,7 +48,7 @@ export default { UfsGlobal.DOM.onHrefChanged((oldHref, newHref) => check(newHref)); async function check(href) { - let url = await UfsGlobal.Utils.getLargestImageSrc(href, href); + let url = await getLargestImageSrc(href, href); if (url && url != href) { if ( confirm( @@ -63,3 +63,408 @@ export default { }, }, }; +const CACHED = {}; +export async function getLargestImageSrc(imgSrc, webUrl) { + if (/^data:/i.test(imgSrc)) { + return null; + } + + // bypass redirect + imgSrc = UfsGlobal.Utils.makeUrlValid(imgSrc); + let redirectedUrl = await getRedirectedUrl(imgSrc); + if (redirectedUrl) { + imgSrc = redirectedUrl; + } + + function try1() { + const url = new URL(imgSrc); + switch (url.hostname) { + // https://atlassiansuite.mservice.com.vn:8443/secure/useravatar?size=small&ownerId=JIRAUSER14656&avatarId=11605 + case "atlassiansuite.mservice.com.vn": + case "atlassiantool.mservice.com.vn": + if (url.href.includes("avatar")) { + if (url.searchParams.get("size")) { + url.searchParams.set("size", "256"); + } else { + url.searchParams.append("size", "256"); + } + } + if (url.href.includes("/thumbnail/")) { + return url.href.replace("/thumbnail/", "/attachments/"); + } + if (url.href.includes("/thumbnails/")) { + return url.href.replace("/thumbnails/", "/attachments/"); + } + return url.toString(); + } + return null; + } + + async function try2() { + if (!CACHED.largeImgSiteRules) { + let s = await import("./auto_redirectLargestImageSrc_rules.js"); + CACHED.largeImgSiteRules = s.default; + } + for (let rule of CACHED.largeImgSiteRules) { + if (rule.url && !testRegex(webUrl, rule.url)) continue; + if (rule.src && !testRegex(imgSrc, rule.src)) continue; + if (rule.exclude && testRegex(imgSrc, rule.exclude)) continue; + if (rule.r) { + let newSrc = replaceUsingRegex(imgSrc, rule.r, rule.s); + if (newSrc?.length) { + return newSrc; + } + } + } + return null; + } + + // https://greasyfork.org/en/scripts/2312-resize-image-on-open-image-in-new-tab + function try3() { + return new Promise((resolve) => { + let m = null; + //google + if ( + (m = imgSrc.match( + /^(https?:\/\/lh\d+\.googleusercontent\.com\/.+\/)([^\/]+)(\/[^\/]+(\.(jpg|jpeg|gif|png|bmp|webp))?)(?:\?.+)?$/i + )) + ) { + if (m[2] != "s0") { + resolve(m[1] + "s0" + m[3]); + } + } else if ( + (m = imgSrc.match( + /^(https?:\/\/lh\d+\.googleusercontent\.com\/.+=)(.+)(?:\?.+)?$/i + )) + ) { + if (m[2] != "s0") { + resolve(m[1] + "s0"); + } + } else if ( + (m = imgSrc.match( + /^(https?:\/\/\w+\.ggpht\.com\/.+\/)([^\/]+)(\/[^\/]+(\.(jpg|jpeg|gif|png|bmp|webp))?)(?:\?.+)?$/i + )) + ) { + if (m[2] != "s0") { + resolve(m[1] + "s0" + m[3]); + } + } + + //blogspot + else if ( + (m = imgSrc.match( + /^(https?:\/\/\w+\.bp\.blogspot\.com\/.+\/)([^\/]+)(\/[^\/]+(\.(jpg|jpeg|gif|png|bmp|webp))?)(?:\?.+)?$/i + )) + ) { + if (m[2] != "s0") { + resolve(m[1] + "s0" + m[3]); + } + } + + //tumblr + else if ( + (m = imgSrc.match( + /^(https?:\/\/\d+\.media\.tumblr\.com\/.*tumblr_\w+_)(\d+)(\.(jpg|jpeg|gif|png|bmp|webp))(?:\?.+)?$/i + )) + ) { + if (m[2] < 1280) { + let ajax = new XMLHttpRequest(); + ajax.onreadystatechange = function () { + if (ajax.status == 200) { + resolve(m[1] + "1280" + m[3]); + } + }; + ajax.open("HEAD", m[1] + "1280" + m[3], true); + ajax.send(); + } + } + + //twitter + else if ( + (m = imgSrc.match( + /^(https?:\/\/\w+\.twimg\.com\/media\/[^\/:]+)\.(jpg|jpeg|gif|png|bmp|webp)(:\w+)?$/i + )) + ) { + let format = m[2]; + if (m[2] == "jpeg") format = "jpg"; + resolve(m[1] + "?format=" + format + "&name=orig"); + } else if ( + (m = imgSrc.match(/^(https?:\/\/\w+\.twimg\.com\/.+)(\?.+)$/i)) + ) { + let url = new URL(webUrl); + let pars = url.searchParams; + if (!pars.format || !pars.name) return; + if (pars.name == "orig") return; + resolve(m[1] + "?format=" + pars.format + "&name=orig"); + } + + //Steam (Only user content) + else if ( + (m = imgSrc.match( + /^(https?:\/\/(images\.akamai\.steamusercontent\.com|steamuserimages-a\.akamaihd\.net)\/[^\?]+)\?.+$/i + )) + ) { + resolve(m[1]); + } + + //性浪微博 + else if ( + (m = imgSrc.match( + /^(https?:\/\/(?:(?:ww|wx|ws|tvax|tva)\d+|wxt|wt)\.sinaimg\.(?:cn|com)\/)([\w\.]+)(\/.+)(?:\?.+)?$/i + )) + ) { + if (m[2] != "large") { + resolve(m[1] + "large" + m[3]); + } + } + + //zhihu + else if ( + (m = imgSrc.match( + /^(https?:\/\/.+\.zhimg\.com\/)(?:\d+\/)?([\w\-]+_)(\w+)(\.(jpg|jpeg|gif|png|bmp|webp))(?:\?.+)?$/i + )) + ) { + if (m[3] != "r") { + resolve(m[1] + m[2] + "r" + m[4]); + } + } + + //pinimg + else if ( + (m = imgSrc.match(/^(https?:\/\/i\.pinimg\.com\/)(\w+)(\/.+)$/i)) + ) { + if (m[2] != "originals") { + resolve(m[1] + "originals" + m[3]); + } + } else if ( + (m = imgSrc.match( + /^(https?:\/\/s-media[\w-]+\.pinimg\.com\/)(\w+)(\/.+)$/i + )) + ) { + //need delete? + if (m[2] != "originals") { + resolve(m[1] + "originals" + m[3]); + } + } + + //bilibili + else if ( + (m = imgSrc.match( + /^(https?:\/\/\w+\.hdslb\.com\/.+\.(jpg|jpeg|gif|png|bmp|webp))(@|_).+$/i + )) + ) { + resolve(m[1]); + } + + //taobao(tmall) + else if ( + (m = imgSrc.match( + /^(https?:\/\/(?:.+?)\.alicdn\.com\/.+\.(jpg|jpeg|gif|png|bmp|webp))_.+$/i + )) + ) { + resolve(m[1]); + } + + //jd + else if ( + (m = imgSrc.match( + /^(https?:\/\/(?:img\d+)\.360buyimg\.com\/)((?:.+?)\/(?:.+?))(\/(?:.+?))(\!.+)?$/i + )) + ) { + if (m[2] != "sku/jfs") { + resolve(m[1] + "sku/jfs" + m[3]); + } + } + + // https://s01.riotpixels.net/data/2a/b2/2ab23684-6cec-41da-9bce-f72c5264353a.jpg.240p.jpg + else if ( + (m = imgSrc.match( + /^(https?:\/\/(?:.+?)\.riotpixels\.net\/.+\.(jpg|jpeg|gif|png|bmp|webp))\..+?$/i + )) + ) { + resolve(m[1]); + } + + // reddit NEED TEST + else if ( + (m = imgSrc.match( + /^https?:\/\/preview\.redd\.it\/(.+\.(jpg|jpeg|gif|png|bmp|webp))\?.+?$/i + )) + ) { + resolve("https://i.redd.it/" + m[1]); + } + + // akamaized.net/imagecache NEED TEST + else if ( + (m = imgSrc.match( + /^(https:\/\/.+\.akamaized\.net\/imagecache\/\d+\/\d+\/\d+\/\d+\/)(\d+)(\/.+)$/i + )) + ) { + if (m[2] < 1920) resolve(m[1] + "1920" + m[3]); + } + + // 微信公众号 by sbdx + else if ( + (m = imgSrc.match( + /^(https:\/\/mmbiz\.qpic\.cn\/mmbiz_jpg\/.+?\/)(\d+)(\?wx_fmt=jpeg)/i + )) + ) { + if (m[2] != 0) resolve(m[1] + "0" + m[3]); + } + + //百度贴吧(然而对于画质提升什么的并没有什么卵用...) + else if ( + (m = imgSrc.match( + /^https?:\/\/imgsrc\.baidu\.com\/forum\/pic\/item\/.+/i + )) + ) { + if ( + (m = imgSrc.match( + /^(https?):\/\/(?:imgsrc|imgsa|\w+\.hiphotos)\.(?:bdimg|baidu)\.com\/(?:forum|album)\/.+\/(\w+\.(?:jpg|jpeg|gif|png|bmp|webp))(?:\?.+)?$/i + )) + ) { + resolve(m[1] + "://imgsrc.baidu.com/forum/pic/item/" + m[2]); + } + //if( (m = imgSrc.match(/^(https?)(:\/\/(?:imgsrc|imgsa|\w+\.hiphotos|tiebapic)\.(?:bdimg|baidu)\.com\/)(?:forum|album)\/.+\/(\w+\.(?:jpg|jpeg|gif|png|bmp|webp))(?:\?.+)?$/i)) ){ + // resolve(m[1] + m[2] + "forum/pic/item/" + m[3]) + //} + } else { + resolve(null); + } + }); + } + + for (let fn of [try1, try2, try3]) { + try { + let res = await timeoutPromise(fn(), 5000); + if (res && res != imgSrc) { + if (!Array.isArray(res)) res = [res]; + if (res.length) { + let finalSrc = await findWorkingSrc(res, true); + if (finalSrc?.length) return finalSrc; + } + } + } catch (e) { + console.log("ERROR getLargestImageSrc: " + fn.name + " -> ", e); + } + } + + return null; +} +function findWorkingSrc(srcs, inOrder = true) { + return new Promise((resolve, reject) => { + if (!srcs || !Array.isArray(srcs) || srcs.length === 0) { + reject("srcs is falsy, not an array, or empty"); + } else { + const checkImage = (src) => + // prevent Error: Content Security Policy directive: "connect-src 'self' + isImageSrc(src).then((value) => { + if (inOrder) return value; + if (value) resolve(src); + return value; + }); + + const promises = srcs.map(checkImage); + Promise.all(promises).then((res) => { + let trueIndex = res.indexOf(true); + if (trueIndex > -1) { + resolve(srcs[trueIndex]); + } else { + reject("none of the URLs are valid images"); + } + }); + } + }); +} +async function isImageSrc(src) { + try { + const res = await UfsGlobal.Extension.fetchByPassOrigin(src, { + method: "HEAD", + }); + if (res?.ok) { + // const type = res.headers.get("content-type"); + const type = res.headers?.["content-type"]; + if (type?.startsWith?.("image/")) { + return true; + } + } + } catch (error) { + console.log("ERROR isImageSrc: " + src + " -> ", error); + } + return new Promise((resolve) => { + let img = new Image(); + img.src = src; + img.onload = () => resolve(true); + img.onerror = () => resolve(false); + }); +} + +function timeoutPromise(prom, time) { + return Promise.race([ + prom, + new Promise((_r, rej) => setTimeout(() => rej("time out " + time), time)), + ]); +} +function uniqueArray(array) { + return Array.from(new Set(array)); +} +function replaceUsingRegex(str, r, s) { + let results = []; + + if (!Array.isArray(r) && !Array.isArray(s)) { + if (r?.test?.(str)) { + results.push(str.replace(r, s)); + } + } else if (!Array.isArray(r) && Array.isArray(s)) { + if (r?.test?.(str)) { + for (const si of s) { + results.push(str.replace(r, si)); + } + } + } else if (Array.isArray(r) && !Array.isArray(s)) { + for (const ri of r) { + if (ri?.test?.(str)) { + results.push(str.replace(ri, s)); + } + } + } else if (Array.isArray(r) && Array.isArray(s)) { + for (let ri = 0; ri < r.length; ri++) { + let _r = r[ri]; + if (_r?.test?.(str)) { + let _s = Array.isArray(s[ri]) ? s[ri] : [s[ri]]; + for (const si of _s) { + results.push(str.replace(_r, si)); + } + } + } + } + + return uniqueArray(results); +} +function testRegex(str, regexs) { + if (!Array.isArray(regexs)) regexs = [regexs]; + for (let regex of regexs) { + if (regex?.test?.(str)) { + return true; + } + } + return false; +} +async function getRedirectedUrl(url) { + try { + while (true) { + let res = await UfsGlobal.Extension.fetchByPassOrigin(url, { + method: "HEAD", + }); + if (res?.redirected) { + console.log("redirected:", url, "->", res.url); + url = res.url; + } else { + return url; + } + } + } catch (e) { + console.log("ERROR:", e); + return url; + } +} diff --git a/scripts/backup/auto-like-fb.js b/scripts/backup/auto-like-fb.js deleted file mode 100644 index e1277934..00000000 --- a/scripts/backup/auto-like-fb.js +++ /dev/null @@ -1,59 +0,0 @@ -javascript: (async function () { - function sleep(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); - } - function focusTo(element) { - element.dispatchEvent( - new MouseEvent("pointerover", { - view: window, - bubbles: true, - cancelable: true, - }) - ); - } - - function scrollToBottom() { - window.scrollTo(0, document.body.scrollHeight, { - // behavior: "smooth", - }); - } - - const doneKey = "auto-like-done"; - const btns = []; - while (true) { - if (!btns.length) { - let curBtns = Array.from( - document.querySelectorAll("[aria-label='Bày tỏ cảm xúc']:not(li *)") - ); - let added = 0; - for (let btn of curBtns) { - if (btn.getAttribute(doneKey) === null) { - btns.push(btn); - btn.setAttribute(doneKey, true); - added++; - } - } - if (added === 0) break; - } - - for (let btn of btns) { - btn.scrollIntoView({ - block: "center", - // behavior: "smooth", - }); - btn.click(); - await sleep(500); - let loveBtn = document.querySelector("[aria-label='Yêu thích']"); - if (loveBtn) { - focusTo(loveBtn); - await sleep(500); - loveBtn.click(); - await sleep(500); - } - btns.splice(btns.indexOf(btn), 1); - } - scrollToBottom(); - await sleep(3000); - } - alert("xong"); -})(); diff --git a/scripts/content-scripts/backup.js b/scripts/content-scripts/backup.js new file mode 100644 index 00000000..974e27f6 --- /dev/null +++ b/scripts/content-scripts/backup.js @@ -0,0 +1,296 @@ +// Những hàm hay nhưng chưa được xài ở đâu sẽ được để ở đây +// Giúp giảm dung lượng cho UfsGlobal.js + +// https://stackoverflow.com/a/3381522 +function createFlashTitle(newMsg, howManyTimes) { + let original = document.title; + let timeout; + + function step() { + document.title = document.title == original ? newMsg : original; + if (--howManyTimes > 0) { + timeout = setTimeout(step, 1000); + } + } + howManyTimes = parseInt(howManyTimes); + if (isNaN(howManyTimes)) { + howManyTimes = 5; + } + clearTimeout(timeout); + step(); + + function cancel() { + clearTimeout(timeout); + document.title = original; + } + + return cancel; +} + +// click element as soon as possible +function clickASAP(selector, sleepTime = 0) { + const events = ["mouseover", "mousedown", "mouseup", "click"]; + const selectors = selector.split(", "); + if (selectors.length > 1) { + return selectors.forEach(clickASAP); + } + if (sleepTime > 0) { + return sleep(sleepTime * 1000).then(function () { + clickASAP(selector, 0); + }); + } + waitForElements(selector).then(function (element) { + element.removeAttribute("disabled"); + element.removeAttribute("target"); + events.forEach((eventName) => { + const eventObject = new MouseEvent(eventName, { bubbles: true }); + element.dispatchEvent(eventObject); + }); + }); +} + +function waitForElements(selector) { + return new Promise((resolve, reject) => { + onElementsAdded(selector, resolve, true); + }); +} +function onElementVisibilityChanged(el, option, callback) { + // use interaction observer + let observer = new IntersectionObserver( + (entries, observer) => { + entries.forEach((entry) => { + callback?.(entry.isIntersecting); + }); + }, + { + root: option?.root ?? null, + rootMargin: option?.rootMargin ?? "0px", + threshold: option?.threshold ?? 0, + } + ); + observer.observe(el); + return () => observer.unobserve(el); +} +/** + * Waits for a condition to be true within a specified timeout. + * + * @param {function} condition - The condition to be evaluated. + * @param {number} [timeout=1000] - The timeout in milliseconds. + * @return {Promise} A Promise that resolves when the condition is true. + */ +function waitFor(condition, timeout = 0) { + // return new Promise((resolve) => { + // let timer = setInterval(() => { + // if (condition()) { + // clearInterval(timer); + // resolve(); + // } + // }, timeout); + // }); + return new Promise(async (resolve) => { + while (true) { + if (condition()) { + resolve(); + break; + } + await new Promise((resolve) => setTimeout(resolve, timeout)); + } + }); +} +// https://stackoverflow.com/a/7616484/23648002 +function hashString(str) { + let hash = 0, + i, + chr; + if (str.length === 0) return hash; + for (i = 0; i < str.length; i++) { + chr = str.charCodeAt(i); + hash = (hash << 5) - hash + chr; + hash |= 0; // Convert to 32bit integer + } + return hash; +} +/** + * Make a deep copy of an object + * @source - required (Object|Array) the object or array to be copied + */ +function deepClone(source) { + let result = {}; + + if (typeof source !== "object") { + return source; + } + if (Object.prototype.toString.call(source) === "[object Array]") { + result = []; + } + if (Object.prototype.toString.call(source) === "[object Null]") { + result = null; + } + for (let key in source) { + result[key] = + typeof source[key] === "object" ? deepClone(source[key]) : source[key]; + } + return result; +} +function domainCheck(domains) { + return new RegExp(domains).test(location.host); +} +function setCookie(name, value, days) { + if (days) { + let date = new Date(); + date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); + let expires = "; expires=" + date.toGMTString(); + } else { + let expires = ""; + document.cookie = name + "=" + value + expires + "; path=/"; + } +} +function delCookie(name) { + setCookie(name, "", -1); +} +function strBetween(s, front, back, trim = false) { + if (trim) { + s = s.replaceAll(" ", ""); + s = s.trim(); + s = s.replaceAll("\n", " "); + } + return s.slice( + s.indexOf(front) + front.length, + s.indexOf(back, s.indexOf(front) + front.length) + ); +} +async function json2xml(json) { + if (!window.json2xml) { + await import("../libs/xml-json/json2xml.js"); + } + return window.json2xml(json); +} +async function xml2json(xml) { + if (!window.xml2json) { + await import("../libs/xml-json/xml2json.js"); + } + return window.xml2json(xml); +} +function formatTimeToHHMMSSDD(date) { + const hours = ("0" + date.getHours()).slice(-2); + const minutes = ("0" + date.getMinutes()).slice(-2); + const seconds = ("0" + date.getSeconds()).slice(-2); + const milliseconds = ("00" + date.getMilliseconds()).slice(-3); + return `${hours}:${minutes}:${seconds}:${milliseconds}`; +} +// resolve relative URLs into canonical absolute URLs based on the current location. +function canonicalUri(src, location = window.location) { + if (src.charAt(0) == "#") return location.href + src; + if (src.charAt(0) == "?") + return location.href.replace(/^([^\?#]+).*/, "$1" + src); + let root_page = /^[^?#]*\//.exec(location.href)[0], + base_path = location.pathname.replace(/\/[^\/]+\.[^\/]+$/, "/"), + root_domain = /^\w+\:\/\/\/?[^\/]+/.exec(root_page)[0], + absolute_regex = /^\w+\:\/\//; + src = src.replace("./", ""); + if (/^\/\/\/?/.test(src)) { + src = location.protocol + src; + } else if (!absolute_regex.test(src) && src.charAt(0) != "/") { + src = (base_path || "") + src; + } + return absolute_regex.test(src) + ? src + : (src.charAt(0) == "/" ? root_domain : root_page) + src; +} +// https://stackoverflow.com/a/7960435 +function isEmptyFunction(func) { + try { + let m = func.toString().match(/\{([\s\S]*)\}/m)[1]; + return !m.replace(/^\s*\/\/.*$/gm, ""); + } catch (e) { + console.log("Error isEmptyFunction", e); + return false; + } +} +// https://stackoverflow.com/a/9310752 +function escapeRegExp(text) { + return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); +} +// https://stackoverflow.com/q/38849009 +function unescapeRegExp(text) { + return text.replace(/\\(.)/g, "$1"); +} +function encodeQueryString(obj) { + let str = []; + for (let p in obj) + if (obj.hasOwnProperty(p)) { + str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p])); + } + return str.join("&"); +} +async function zipAndDownloadBlobs( + blobList, + zipFileName, + progressCallback, + successCallback +) { + if (!window.JSZip) { + await import("../libs/jzip/index.js"); + } + const zip = new window.JSZip(); + console.log(zip); + + // Add each Blob to the ZIP archive with a unique name + blobList.forEach(({ blob, fileName }, index) => { + console.log(fileName); + zip.file(fileName, blob); + }); + + // Generate the ZIP content with progress callback + zip + .generateAsync({ type: "blob" }, (metadata) => { + if (progressCallback) { + // Calculate progress as a percentage + const progress = metadata.percent | 0; + progressCallback(progress); + } + }) + .then((content) => { + successCallback?.(); + saveAs(content, zipFileName); + }); +} +async function getBlobFromUrl(url) { + try { + const response = await fetch(url); + const blob = await response.blob(); + return blob; + } catch (error) { + alert("Error: " + error); + } +} +// https://stackoverflow.com/a/15832662/11898496 +// TODO: chrome.downloads: https://developer.chrome.com/docs/extensions/reference/downloads/#method-download +function downloadURL(url, name) { + let link = document.createElement("a"); + link.target = "_blank"; + link.download = name; + link.href = url; + link.click(); +} + +// store cache for all functions in UfsGlobal +const CACHED = { + mouse: { + x: 0, + y: 0, + }, +}; + +if (typeof window !== "undefined") { + window.UfsGlobal = UfsGlobal; + if (typeof window?.addEventListener === "function") { + window.addEventListener("mousemove", (e) => { + CACHED.mouse.x = e.clientX; + CACHED.mouse.y = e.clientY; + }); + } +} +function getMousePos() { + return CACHED.mouse; +} diff --git a/scripts/content-scripts/ufs_global.js b/scripts/content-scripts/ufs_global.js index a480c77b..65436499 100644 --- a/scripts/content-scripts/ufs_global.js +++ b/scripts/content-scripts/ufs_global.js @@ -10,32 +10,15 @@ export const UfsGlobal = { waitForTabToLoad, }, DOM: { - pickElement, - getMousePos, - isInCrossOriginFrame, - isInIframe, - checkElementvisibility, closest, - addLoadingAnimation, - addLoadingAnimationAtPos, - addEventListener, - enableDragAndZoom, - getElementBoundingClientRect, - getContentClientRect, - dataURLToCanvas, notifyStack, notify, - onDoublePress, - createFlashTitle, - clickASAP, deleteElements, - waitForElements, onElementsAdded, onElementRemoved, onElementAttributeChanged, onElementTextContentChanged, onHrefChanged, - onElementVisibilityChanged, injectCssCode, injectCssFile, getTrustedPolicy, @@ -46,81 +29,28 @@ export const UfsGlobal = { injectScriptSrcAsync, isElementInViewport, getOverlapScore, - makeUrlValid, getWatchingVideoSrc, }, Utils: { - waitFor, - hashString, - lerp, - getNumberFormatter, - deepClone, debounce, - throttle, - domainCheck, + makeUrlValid, + getNumberFormatter, sleep, - setCookie, - delCookie, - strBetween, - json2xml, - xml2json, - getLargestSrcset, - svgBase64ToUrl, - svgToBlobUrl, - svgToBase64, - getResolutionCategory, saveAs, - isEmoji, - formatTimeToHHMMSSDD, - timeoutPromise, - getRedirectedUrl, - getLargestImageSrc, - isImageSrc, - findWorkingSrc, - canonicalUri, formatSize, promiseAllStepN, - parseJwt, copyToClipboard, - isEmptyFunction, - escapeRegExp, - unescapeRegExp, - encodeQueryString, moneyFormat, - zipAndDownloadBlobs, - getBlobFromUrl, - getBlobFromUrlWithProgress, - sanitizeFileName, getOriginalWindowFunction, chooseFolderToDownload, downloadToFolder, downloadBlobUrl, downloadBlob, - downloadURL, downloadData, }, }; -// store cache for all functions in UfsGlobal -const CACHED = { - mouse: { - x: 0, - y: 0, - }, -}; - -if (typeof window !== "undefined") { - window.UfsGlobal = UfsGlobal; - if (typeof window?.addEventListener === "function") { - window.addEventListener("mousemove", (e) => { - CACHED.mouse.x = e.clientX; - CACHED.mouse.y = e.clientY; - }); - } -} -function getMousePos() { - return CACHED.mouse; -} +if (typeof window !== "undefined") window.UfsGlobal = UfsGlobal; // #region Extension @@ -205,164 +135,6 @@ function download(options) { // #endregion // #region DOM -function pickElement() { - return new Promise((resolve) => { - // #region init elements - const host = document.createElement("div"); - const shadow = host.attachShadow({ mode: "closed" }); - - const style = document.createElement("style"); - style.textContent = /*css*/ ` - :host { - position: fixed; - display: block; - top: 0; - left: 0; - width: 100vw; - height: 100vh; - z-index: 99999999; - pointer-events: none; - } - #highlighter { - position: absolute; - top: 0; - left: 0; - width: 0; - height: 0; - background: #ff3f3f22; - border: 1px solid #F00; - box-shadow: 0 0 0 99999px rgba(0, 0, 0, .5); - } - #confirmer { - position: absolute; - bottom: 0; - right: 0; - width: 200px; - height: 200px; - } - #confirmer input { - width: 100%; - } - `; - shadow.append(style); - - const highlighter = document.createElement("div"); - highlighter.id = "highlighter"; - shadow.append(highlighter); - - // const confirmer = document.createElement("div"); - // confirmer.id = "confirmer"; - // confirmer.innerHTML = ` - // - // Pick - // - // `; - // shadow.append(confirmer); - - document.documentElement.append(host); - // #endregion - - // #region pick element - const STATES = { - picking: "picking", - confirming: "confirming", - }; - let state = STATES.picking; - let currentTarget = null; - function onMouseOver(e) { - e.preventDefault(); - const target = e.target; - if (state !== STATES.picking || !target || target === highlighter) return; - - highlightElement(target); - } - - function highlightElement(elem) { - const rect = getElementBoundingClientRect(elem); - highlighter.style.cssText = ` - top: ${rect.top}px; - left: ${rect.left}px; - width: ${rect.width}px; - height: ${rect.height}px; - `; - currentTarget = elem; - } - - window.addEventListener("mouseover", onMouseOver, { capture: true }); - window.addEventListener( - "click", - (e) => { - e.preventDefault(); - // confirmPick(currentTarget); - - host.remove(); - resolve(currentTarget); - window.removeEventListener("mouseover", onMouseOver); - }, - { once: true, capture: true } - ); - // #endregion - }); -} -// https://stackoverflow.com/a/5178132/11898496 -function createXPathFromElement(elm) { - var allNodes = document.getElementsByTagName("*"); - for (var segs = []; elm && elm.nodeType == 1; elm = elm.parentNode) { - if (elm.hasAttribute("id")) { - var uniqueIdCount = 0; - for (var n = 0; n < allNodes.length; n++) { - if (allNodes[n].hasAttribute("id") && allNodes[n].id == elm.id) - uniqueIdCount++; - if (uniqueIdCount > 1) break; - } - if (uniqueIdCount == 1) { - segs.unshift('id("' + elm.getAttribute("id") + '")'); - return segs.join("/"); - } else { - segs.unshift( - elm.localName.toLowerCase() + '[@id="' + elm.getAttribute("id") + '"]' - ); - } - } else if (elm.hasAttribute("class")) { - segs.unshift( - elm.localName.toLowerCase() + - '[@class="' + - elm.getAttribute("class") + - '"]' - ); - } else { - for ( - var i = 1, sib = elm.previousSibling; - sib; - sib = sib.previousSibling - ) { - if (sib.localName == elm.localName) i++; - } - segs.unshift(elm.localName.toLowerCase() + "[" + i + "]"); - } - } - return segs.length ? "/" + segs.join("/") : null; -} -function isInCrossOriginFrame() { - let result = true; - try { - if (window.top.localStorage || window.top.location.href) { - result = false; - } - } catch (e) { - result = true; - } - return result; -} -function isInIframe() { - return window !== window.top; -} -function checkElementvisibility(elem) { - if (!elem.offsetHeight && !elem.offsetWidth) { - return false; - } - return !(getComputedStyle(elem).visibility === "hidden"); -} function closest(element, selector) { let el = element; while (el !== null) { @@ -375,285 +147,6 @@ function closest(element, selector) { } return el; } -function addLoadingAnimationAtPos( - x, - y, - size = 40, - containerStyle = "", - loadingStyle = "" -) { - let ele = document.createElement("div"); - ele.style.cssText = ` - position: fixed; - left: ${x - size / 2}px; - top: ${y - size / 2}px; - width: ${size}px; - height: ${size}px; - z-index: 2147483647; - pointer-events: none; - user-select: none; - ${containerStyle} - `; - addLoadingAnimation(ele, size, loadingStyle); - document.body.appendChild(ele); - return () => ele.remove(); -} -function addLoadingAnimation( - element, - size = Math.min(element?.clientWidth, element?.clientHeight) || 0, - customStyle = "" -) { - let id = Math.random().toString(36).substr(2, 9); - element.classList.add("ufs-loading-" + id); - - let borderSize = 4; - - // inject css code - let style = document.createElement("style"); - style.id = "ufs-loading-style-" + id; - style.textContent = ` - .ufs-loading-${id}::after { - content: ""; - display: block; - position: absolute; - top: 50%; - left: 50%; - width: ${size}px; - height: ${size}px; - margin-top: -${size / 2}px; - margin-left: -${size / 2}px; - border-radius: 50%; - border: ${borderSize}px solid #555 !important; - border-top-color: #eee !important; - animation: ufs-spin 1s ease-in-out infinite; - box-sizing: border-box !important; - ${customStyle} - } - @keyframes ufs-spin { - to { - transform: rotate(360deg); - } - } - `; - (document.head || document.documentElement).appendChild(style); - - return () => { - if (element) element.classList.remove("ufs-loading-" + id); - }; -} -function addEventListener(target, event, callback, options) { - target.addEventListener(event, callback, options); - return () => target.removeEventListener(event, callback, options); -} -function enableDragAndZoom(element, container, onUpdateCallback) { - // set style - const className = "ufs-drag-and-zoom"; - element.classList.add(className); - - let style = document.createElement("style"); - style.textContent = ` - .${className} { - cursor: grab; - position: relative !important; - -webkit-user-select: none !important; - -moz-user-select: none !important; - -ms-user-select: none !important; - user-select: none !important; - -khtml-user-select: none; - max-width: unset !important; - max-height: unset !important; - min-width: unset !important; - min-height: unset !important; - -webkit-user-drag: none !important; - }`; - (container || element).appendChild(style); - - // config - const lerpSpeed = 0.3; - const last = { x: 0, y: 0 }; - const mouse = { x: 0, y: 0 }; - const animTarget = { - left: parseFloat(element.style.left), - top: parseFloat(element.style.top), - width: parseFloat(element.style.width), - height: parseFloat(element.style.height), - }; - - let run = true; - function animate() { - let updated = false; - let updatedValue = {}; - for (let prop in animTarget) { - const currentValue = parseFloat(element.style[prop]); - const targetValue = animTarget[prop]; - let del = Math.abs(targetValue - currentValue); - - if (del > 0.1) { - const newValue = - del < 1 ? targetValue : lerp(currentValue, targetValue, lerpSpeed); - element.style[prop] = newValue + "px"; - updatedValue[prop] = newValue; - updated = true; - } - } - if (updated) onUpdateCallback?.(updatedValue); - if (run) requestAnimationFrame(animate); - } - - animate(); - - // Mouse down event listener - let dragging = false; - let _down = addEventListener(container || element, "mousedown", function (e) { - e.preventDefault(); - dragging = true; - last.x = e.clientX; - last.y = e.clientY; - element.style.cursor = "grabbing"; - }); - - // Mouse move event listener - let _move = addEventListener(document, "mousemove", function (e) { - mouse.x = e.clientX; - mouse.y = e.clientY; - if (dragging) { - let delX = e.clientX - last.x; - let delY = e.clientY - last.y; - - animTarget.left += delX; - animTarget.top += delY; - - last.x = e.clientX; - last.y = e.clientY; - } - }); - - // Mouse up event listener - let _up = addEventListener(document, "mouseup", function () { - dragging = false; - element.style.cursor = "grab"; - }); - - // Mouse leave event listener - let _leave = addEventListener(document, "mouseleave", function () { - dragging = false; - element.style.cursor = "grab"; - }); - - // Mouse wheel event listener for zooming - let _wheel = addEventListener(container || element, "wheel", function (e) { - e.preventDefault(); - - const curScale = parseFloat(element.style.width) / element.width; - const delta = -e.wheelDeltaY || -e.wheelDelta; - const factor = Math.abs((0.3 * delta) / 120); - const newScale = - delta > 0 ? curScale * (1 - factor) : curScale * (1 + factor); - - const newW = element.width * newScale; - const newH = element.height * newScale; - - if (newW < 10 || newH < 10) { - return; - } - - const left = parseFloat(element.style.left); - const top = parseFloat(element.style.top); - const offsetX = mouse.x - left; - const offsetY = mouse.y - top; - const newLeft = left - (newW - element.width) * (offsetX / element.width); - const newTop = top - (newH - element.height) * (offsetY / element.height); - - animTarget.left = newLeft; - animTarget.top = newTop; - animTarget.width = newW; - animTarget.height = newH; - }); - - let listeners = [_down, _move, _up, _leave, _wheel]; - - return { - animateTo: (x, y, w, h) => { - animTarget.left = x; - animTarget.top = y; - animTarget.width = w; - animTarget.height = h; - }, - destroy: () => { - run = false; - style.remove(); - element.classList.remove(className); - listeners.forEach((l) => l?.()); - }, - }; -} -function getElementBoundingClientRect(elem) { - let rect = - typeof elem.getBoundingClientRect === "function" - ? elem.getBoundingClientRect() - : { height: 0, left: 0, top: 0, width: 0 }; - - // https://github.com/gorhill/uBlock/issues/1024 - // Try not returning an empty bounding rect. - if (rect.width !== 0 && rect.height !== 0) return rect; - if (elem.shadowRoot instanceof DocumentFragment) - return getElementBoundingClientRect(elem.shadowRoot); - - let left = rect.left, - right = left + rect.width, - top = rect.top, - bottom = top + rect.height; - - for (const child of elem.children) { - rect = getElementBoundingClientRect(child); - if (rect.width === 0 || rect.height === 0) continue; - if (rect.left < left) left = rect.left; - if (rect.right > right) right = rect.right; - if (rect.top < top) top = rect.top; - if (rect.bottom > bottom) bottom = rect.bottom; - } - - let height = bottom - top, - width = right - left; - - return { bottom, height, left, right, top, width }; -} -// prettier-ignore -function getContentClientRect(target, win = window) { - let rect = target.getBoundingClientRect(); - let compStyle = win.getComputedStyle(target); - let pFloat = parseFloat; - let top = rect.top + pFloat(compStyle.paddingTop) + pFloat(compStyle.borderTopWidth); - let right = rect.right - pFloat(compStyle.paddingRight) - pFloat(compStyle.borderRightWidth); - let bottom = rect.bottom - pFloat(compStyle.paddingBottom) - pFloat(compStyle.borderBottomWidth); - let left = rect.left + pFloat(compStyle.paddingLeft) + pFloat(compStyle.borderLeftWidth); - return { - top : top, - right : right, - bottom : bottom, - left : left, - width : right-left, - height : bottom-top, - }; -} -function dataURLToCanvas(dataurl, cb) { - if (!dataurl) return cb(null); - let canvas = document.createElement("canvas"); - let ctx = canvas.getContext("2d"); - let img = new Image(); - img.setAttribute("crossOrigin", "anonymous"); - img.onload = function () { - canvas.width = img.width; - canvas.height = img.height; - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.drawImage(img, 0, 0); - cb(canvas); - }; - img.onerror = function () { - cb(null); - }; - img.src = dataurl; -} // TODO: finish this function notifyStack(msg) { let id = "ufs_notify_stack"; @@ -776,81 +269,6 @@ function notify({ }, }; } -function onDoublePress(key, callback, timeout = 500) { - let timer = null; - let clickCount = 0; - - const keyup = (event) => { - if (event.key !== key) { - clickCount = 0; - return; - } - - clickCount++; - if (clickCount === 2) { - callback?.(); - clickCount = 0; - return; - } - - clearTimeout(timer); - timer = setTimeout(() => { - clickCount = 0; - }, timeout); - }; - - document.addEventListener("keyup", keyup); - - return () => { - clearTimeout(timer); - document.removeEventListener("keyup", keyup); - }; -} // https://stackoverflow.com/a/3381522 -function createFlashTitle(newMsg, howManyTimes) { - let original = document.title; - let timeout; - - function step() { - document.title = document.title == original ? newMsg : original; - if (--howManyTimes > 0) { - timeout = setTimeout(step, 1000); - } - } - howManyTimes = parseInt(howManyTimes); - if (isNaN(howManyTimes)) { - howManyTimes = 5; - } - clearTimeout(timeout); - step(); - - function cancel() { - clearTimeout(timeout); - document.title = original; - } - - return cancel; -} -// click element as soon as possible -function clickASAP(selector, sleepTime = 0) { - const events = ["mouseover", "mousedown", "mouseup", "click"]; - const selectors = selector.split(", "); - if (selectors.length > 1) { - return selectors.forEach(clickASAP); - } - if (sleepTime > 0) { - return sleep(sleepTime * 1000).then(function () { - clickASAP(selector, 0); - }); - } - waitForElements(selector).then(function (element) { - element.removeAttribute("disabled"); - element.removeAttribute("target"); - events.forEach((eventName) => { - const eventObject = new MouseEvent(eventName, { bubbles: true }); - element.dispatchEvent(eventObject); - }); - }); -} function deleteElements(selector, once) { onElementsAdded( selector, @@ -863,11 +281,6 @@ function deleteElements(selector, once) { once ); } -function waitForElements(selector) { - return new Promise((resolve, reject) => { - onElementsAdded(selector, resolve, true); - }); -} // https://stackoverflow.com/a/46428962 function onHrefChanged(callback, once) { let oldHref = document.location.href; @@ -882,24 +295,6 @@ function onHrefChanged(callback, once) { }); observer.observe(body, { childList: true, subtree: true }); } -function onElementVisibilityChanged(el, option, callback) { - // use interaction observer - let observer = new IntersectionObserver( - (entries, observer) => { - entries.forEach((entry) => { - callback?.(entry.isIntersecting); - }); - }, - { - root: option?.root ?? null, - rootMargin: option?.rootMargin ?? "0px", - threshold: option?.threshold ?? 0, - } - ); - observer.observe(el); - return () => observer.unobserve(el); -} - // Idea from https://github.com/gys-dev/Unlimited-Stdphim // https://stackoverflow.com/a/61511955/11898496 function onElementsAdded(selector, callback, once) { @@ -1122,55 +517,17 @@ function getWatchingVideoSrc() { } } } +function debounce(func, delay) { + let timeout; + return (...args) => { + clearTimeout(timeout); + timeout = setTimeout(() => func.apply(this, args), delay); + }; +} // #endregion // #region Utils -/** - * Waits for a condition to be true within a specified timeout. - * - * @param {function} condition - The condition to be evaluated. - * @param {number} [timeout=1000] - The timeout in milliseconds. - * @return {Promise} A Promise that resolves when the condition is true. - */ -function waitFor(condition, timeout = 0) { - // return new Promise((resolve) => { - // let timer = setInterval(() => { - // if (condition()) { - // clearInterval(timer); - // resolve(); - // } - // }, timeout); - // }); - return new Promise(async (resolve) => { - while (true) { - if (condition()) { - resolve(); - break; - } - await new Promise((resolve) => setTimeout(resolve, timeout)); - } - }); -} - -// https://stackoverflow.com/a/7616484/23648002 -function hashString(str) { - let hash = 0, - i, - chr; - if (str.length === 0) return hash; - for (i = 0; i < str.length; i++) { - chr = str.charCodeAt(i); - hash = (hash << 5) - hash + chr; - hash |= 0; // Convert to 32bit integer - } - return hash; -} - -function lerp(from, to, speed) { - return from + (to - from) * speed; -} - const numberFormatCached = {}; /** * Get number formatter @@ -1226,158 +583,9 @@ function getNumberFormatter(optionSelect, locale) { } return numberFormatCached[key]; } - -/** - * Make a deep copy of an object - * @source - required (Object|Array) the object or array to be copied - */ -function deepClone(source) { - let result = {}; - - if (typeof source !== "object") { - return source; - } - if (Object.prototype.toString.call(source) === "[object Array]") { - result = []; - } - if (Object.prototype.toString.call(source) === "[object Null]") { - result = null; - } - for (let key in source) { - result[key] = - typeof source[key] === "object" ? deepClone(source[key]) : source[key]; - } - return result; -} - -function debounce(func, delay) { - let timeout; - return (...args) => { - clearTimeout(timeout); - timeout = setTimeout(() => func.apply(this, args), delay); - }; -} - -// https://dev.to/jeetvora331/throttling-in-javascript-easiest-explanation-1081 -function throttle(mainFunction, delay) { - let timerFlag = null; // Variable to keep track of the timer - - // Returning a throttled version - return (...args) => { - if (timerFlag === null) { - // If there is no timer currently running - mainFunction(...args); // Execute the main function - timerFlag = setTimeout(() => { - // Set a timer to clear the timerFlag after the specified delay - timerFlag = null; // Clear the timerFlag to allow the main function to be executed again - }, delay); - } - }; -} function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } -function domainCheck(domains) { - return new RegExp(domains).test(location.host); -} -function setCookie(name, value, days) { - if (days) { - let date = new Date(); - date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); - let expires = "; expires=" + date.toGMTString(); - } else { - let expires = ""; - document.cookie = name + "=" + value + expires + "; path=/"; - } -} -function delCookie(name) { - setCookie(name, "", -1); -} -function strBetween(s, front, back, trim = false) { - if (trim) { - s = s.replaceAll(" ", ""); - s = s.trim(); - s = s.replaceAll("\n", " "); - } - return s.slice( - s.indexOf(front) + front.length, - s.indexOf(back, s.indexOf(front) + front.length) - ); -} -async function json2xml(json) { - if (!window.json2xml) { - await import("../libs/xml-json/json2xml.js"); - } - return window.json2xml(json); -} -async function xml2json(xml) { - if (!window.xml2json) { - await import("../libs/xml-json/xml2json.js"); - } - return window.xml2json(xml); -} -function getLargestSrcset(srcset) { - let srcs = srcset.split(/[xw],/i), - largeSize = -1, - largeSrc = null; - if (!srcs.length) return null; - srcs.forEach((srci) => { - let srcInfo = srci.trim().split(/(\s+|%20)/), - curSize = parseInt(srcInfo[2] || 0); - if (srcInfo[0] && curSize > largeSize) { - largeSize = curSize; - largeSrc = srcInfo[0]; - } - }); - return largeSrc; -} -function svgBase64ToUrl(sgvBase64) { - try { - if (!/^data:image\/svg/.test(sgvBase64)) throw new Error("Invalid SVG"); - const svgContent = atob(sgvBase64.split(",")[1]); - const blob = new Blob([svgContent], { type: "image/svg+xml" }); - const url = URL.createObjectURL(blob); - setTimeout(() => URL.revokeObjectURL(url), 6e4); - return url; - } catch (e) { - console.log("ERROR: ", e); - return null; - } -} -function svgToBlobUrl(svg) { - let url = URL.createObjectURL(new Blob([svg], { type: "image/svg+xml" })); - return url; -} -function svgToBase64(svg) { - try { - return ( - "data:image/svg+xml;charset=utf-8;base64," + - btoa(new XMLSerializer().serializeToString(svg)) - ); - } catch (e) { - console.log("ERROR: ", e); - return null; - } -} -function getResolutionCategory(width, height) { - let min = Math.min(width, height); - let max = Math.max(width, height); - - if (max <= 256 && min <= 144) return "144p"; - if (max <= 320 && min <= 180) return "240p"; - if (max <= 640 && min <= 360) return "360p"; - if (max <= 640 && min <= 480) return "SD (480p)"; - if (max <= 1280 && min <= 720) return "HD (720p)"; - if (max <= 1600 && min <= 900) return "HD+ (900p)"; - if (max <= 1920 && min <= 1080) return "FHD (1080p)"; - if (max <= 2560 && min <= 1440) return "QHD (1440p)"; - if (max <= 3840 && min <= 2160) return "4K (2160p)"; - if (max <= 5120 && min <= 2880) return "5K (2880p)"; - if (max <= 7680 && min <= 4320) return "8K (4320p)"; - if (max <= 10240 && min <= 4320) return "10K (4320p)"; - if (max <= 15360 && min <= 8640) return "16K (8640p)"; - return "> 16K"; -} async function saveAs(url_blob_file, title = "download", options = {}) { try { if (!window.saveAs) { @@ -1389,442 +597,6 @@ async function saveAs(url_blob_file, title = "download", options = {}) { alert("Error: " + e); } } -// https://stackoverflow.com/a/67705964/23648002 -function isEmoji(text) { - return text?.match( - /^(\u00a9|\u00ae|[\u25a0-\u27bf]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])+$/ - ); -} -function formatTimeToHHMMSSDD(date) { - const hours = ("0" + date.getHours()).slice(-2); - const minutes = ("0" + date.getMinutes()).slice(-2); - const seconds = ("0" + date.getSeconds()).slice(-2); - const milliseconds = ("00" + date.getMilliseconds()).slice(-3); - return `${hours}:${minutes}:${seconds}:${milliseconds}`; -} -function timeoutPromise(prom, time) { - return Promise.race([ - prom, - new Promise((_r, rej) => setTimeout(() => rej("time out " + time), time)), - ]); -} -async function getRedirectedUrl(url) { - try { - while (true) { - let res = await fetchByPassOrigin(url, { - method: "HEAD", - }); - if (res?.redirected) { - console.log("redirected:", url, "->", res.url); - url = res.url; - } else { - return url; - } - } - } catch (e) { - console.log("ERROR:", e); - return url; - } -} -function uniqueArray(array) { - return Array.from(new Set(array)); -} -function replaceUsingRegex(str, r, s) { - let results = []; - - if (!Array.isArray(r) && !Array.isArray(s)) { - if (r?.test?.(str)) { - results.push(str.replace(r, s)); - } - } else if (!Array.isArray(r) && Array.isArray(s)) { - if (r?.test?.(str)) { - for (const si of s) { - results.push(str.replace(r, si)); - } - } - } else if (Array.isArray(r) && !Array.isArray(s)) { - for (const ri of r) { - if (ri?.test?.(str)) { - results.push(str.replace(ri, s)); - } - } - } else if (Array.isArray(r) && Array.isArray(s)) { - for (let ri = 0; ri < r.length; ri++) { - let _r = r[ri]; - if (_r?.test?.(str)) { - let _s = Array.isArray(s[ri]) ? s[ri] : [s[ri]]; - for (const si of _s) { - results.push(str.replace(_r, si)); - } - } - } - } - - return uniqueArray(results); -} -function testRegex(str, regexs) { - if (!Array.isArray(regexs)) regexs = [regexs]; - for (let regex of regexs) { - if (regex?.test?.(str)) { - return true; - } - } - return false; -} -async function getLargestImageSrc(imgSrc, webUrl) { - if (/^data:/i.test(imgSrc)) { - return null; - } - - // bypass redirect - imgSrc = makeUrlValid(imgSrc); - let redirectedUrl = await getRedirectedUrl(imgSrc); - if (redirectedUrl) { - imgSrc = redirectedUrl; - } - - function try1() { - const url = new URL(imgSrc); - switch (url.hostname) { - // https://atlassiansuite.mservice.com.vn:8443/secure/useravatar?size=small&ownerId=JIRAUSER14656&avatarId=11605 - case "atlassiansuite.mservice.com.vn": - case "atlassiantool.mservice.com.vn": - if (url.href.includes("avatar")) { - if (url.searchParams.get("size")) { - url.searchParams.set("size", "256"); - } else { - url.searchParams.append("size", "256"); - } - } - if (url.href.includes("/thumbnail/")) { - return url.href.replace("/thumbnail/", "/attachments/"); - } - if (url.href.includes("/thumbnails/")) { - return url.href.replace("/thumbnails/", "/attachments/"); - } - return url.toString(); - } - return null; - } - - async function try2() { - if (!CACHED.largeImgSiteRules) { - let s = await import("../auto_redirectLargestImageSrc_rules.js"); - CACHED.largeImgSiteRules = s.default; - } - for (let rule of CACHED.largeImgSiteRules) { - if (rule.url && !testRegex(webUrl, rule.url)) continue; - if (rule.src && !testRegex(imgSrc, rule.src)) continue; - if (rule.exclude && testRegex(imgSrc, rule.exclude)) continue; - if (rule.r) { - let newSrc = replaceUsingRegex(imgSrc, rule.r, rule.s); - if (newSrc?.length) { - return newSrc; - } - } - } - return null; - } - - // https://greasyfork.org/en/scripts/2312-resize-image-on-open-image-in-new-tab - function try3() { - return new Promise((resolve) => { - let m = null; - //google - if ( - (m = imgSrc.match( - /^(https?:\/\/lh\d+\.googleusercontent\.com\/.+\/)([^\/]+)(\/[^\/]+(\.(jpg|jpeg|gif|png|bmp|webp))?)(?:\?.+)?$/i - )) - ) { - if (m[2] != "s0") { - resolve(m[1] + "s0" + m[3]); - } - } else if ( - (m = imgSrc.match( - /^(https?:\/\/lh\d+\.googleusercontent\.com\/.+=)(.+)(?:\?.+)?$/i - )) - ) { - if (m[2] != "s0") { - resolve(m[1] + "s0"); - } - } else if ( - (m = imgSrc.match( - /^(https?:\/\/\w+\.ggpht\.com\/.+\/)([^\/]+)(\/[^\/]+(\.(jpg|jpeg|gif|png|bmp|webp))?)(?:\?.+)?$/i - )) - ) { - if (m[2] != "s0") { - resolve(m[1] + "s0" + m[3]); - } - } - - //blogspot - else if ( - (m = imgSrc.match( - /^(https?:\/\/\w+\.bp\.blogspot\.com\/.+\/)([^\/]+)(\/[^\/]+(\.(jpg|jpeg|gif|png|bmp|webp))?)(?:\?.+)?$/i - )) - ) { - if (m[2] != "s0") { - resolve(m[1] + "s0" + m[3]); - } - } - - //tumblr - else if ( - (m = imgSrc.match( - /^(https?:\/\/\d+\.media\.tumblr\.com\/.*tumblr_\w+_)(\d+)(\.(jpg|jpeg|gif|png|bmp|webp))(?:\?.+)?$/i - )) - ) { - if (m[2] < 1280) { - let ajax = new XMLHttpRequest(); - ajax.onreadystatechange = function () { - if (ajax.status == 200) { - resolve(m[1] + "1280" + m[3]); - } - }; - ajax.open("HEAD", m[1] + "1280" + m[3], true); - ajax.send(); - } - } - - //twitter - else if ( - (m = imgSrc.match( - /^(https?:\/\/\w+\.twimg\.com\/media\/[^\/:]+)\.(jpg|jpeg|gif|png|bmp|webp)(:\w+)?$/i - )) - ) { - let format = m[2]; - if (m[2] == "jpeg") format = "jpg"; - resolve(m[1] + "?format=" + format + "&name=orig"); - } else if ( - (m = imgSrc.match(/^(https?:\/\/\w+\.twimg\.com\/.+)(\?.+)$/i)) - ) { - let url = new URL(webUrl); - let pars = url.searchParams; - if (!pars.format || !pars.name) return; - if (pars.name == "orig") return; - resolve(m[1] + "?format=" + pars.format + "&name=orig"); - } - - //Steam (Only user content) - else if ( - (m = imgSrc.match( - /^(https?:\/\/(images\.akamai\.steamusercontent\.com|steamuserimages-a\.akamaihd\.net)\/[^\?]+)\?.+$/i - )) - ) { - resolve(m[1]); - } - - //性浪微博 - else if ( - (m = imgSrc.match( - /^(https?:\/\/(?:(?:ww|wx|ws|tvax|tva)\d+|wxt|wt)\.sinaimg\.(?:cn|com)\/)([\w\.]+)(\/.+)(?:\?.+)?$/i - )) - ) { - if (m[2] != "large") { - resolve(m[1] + "large" + m[3]); - } - } - - //zhihu - else if ( - (m = imgSrc.match( - /^(https?:\/\/.+\.zhimg\.com\/)(?:\d+\/)?([\w\-]+_)(\w+)(\.(jpg|jpeg|gif|png|bmp|webp))(?:\?.+)?$/i - )) - ) { - if (m[3] != "r") { - resolve(m[1] + m[2] + "r" + m[4]); - } - } - - //pinimg - else if ( - (m = imgSrc.match(/^(https?:\/\/i\.pinimg\.com\/)(\w+)(\/.+)$/i)) - ) { - if (m[2] != "originals") { - resolve(m[1] + "originals" + m[3]); - } - } else if ( - (m = imgSrc.match( - /^(https?:\/\/s-media[\w-]+\.pinimg\.com\/)(\w+)(\/.+)$/i - )) - ) { - //need delete? - if (m[2] != "originals") { - resolve(m[1] + "originals" + m[3]); - } - } - - //bilibili - else if ( - (m = imgSrc.match( - /^(https?:\/\/\w+\.hdslb\.com\/.+\.(jpg|jpeg|gif|png|bmp|webp))(@|_).+$/i - )) - ) { - resolve(m[1]); - } - - //taobao(tmall) - else if ( - (m = imgSrc.match( - /^(https?:\/\/(?:.+?)\.alicdn\.com\/.+\.(jpg|jpeg|gif|png|bmp|webp))_.+$/i - )) - ) { - resolve(m[1]); - } - - //jd - else if ( - (m = imgSrc.match( - /^(https?:\/\/(?:img\d+)\.360buyimg\.com\/)((?:.+?)\/(?:.+?))(\/(?:.+?))(\!.+)?$/i - )) - ) { - if (m[2] != "sku/jfs") { - resolve(m[1] + "sku/jfs" + m[3]); - } - } - - // https://s01.riotpixels.net/data/2a/b2/2ab23684-6cec-41da-9bce-f72c5264353a.jpg.240p.jpg - else if ( - (m = imgSrc.match( - /^(https?:\/\/(?:.+?)\.riotpixels\.net\/.+\.(jpg|jpeg|gif|png|bmp|webp))\..+?$/i - )) - ) { - resolve(m[1]); - } - - // reddit NEED TEST - else if ( - (m = imgSrc.match( - /^https?:\/\/preview\.redd\.it\/(.+\.(jpg|jpeg|gif|png|bmp|webp))\?.+?$/i - )) - ) { - resolve("https://i.redd.it/" + m[1]); - } - - // akamaized.net/imagecache NEED TEST - else if ( - (m = imgSrc.match( - /^(https:\/\/.+\.akamaized\.net\/imagecache\/\d+\/\d+\/\d+\/\d+\/)(\d+)(\/.+)$/i - )) - ) { - if (m[2] < 1920) resolve(m[1] + "1920" + m[3]); - } - - // 微信公众号 by sbdx - else if ( - (m = imgSrc.match( - /^(https:\/\/mmbiz\.qpic\.cn\/mmbiz_jpg\/.+?\/)(\d+)(\?wx_fmt=jpeg)/i - )) - ) { - if (m[2] != 0) resolve(m[1] + "0" + m[3]); - } - - //百度贴吧(然而对于画质提升什么的并没有什么卵用...) - else if ( - (m = imgSrc.match( - /^https?:\/\/imgsrc\.baidu\.com\/forum\/pic\/item\/.+/i - )) - ) { - if ( - (m = imgSrc.match( - /^(https?):\/\/(?:imgsrc|imgsa|\w+\.hiphotos)\.(?:bdimg|baidu)\.com\/(?:forum|album)\/.+\/(\w+\.(?:jpg|jpeg|gif|png|bmp|webp))(?:\?.+)?$/i - )) - ) { - resolve(m[1] + "://imgsrc.baidu.com/forum/pic/item/" + m[2]); - } - //if( (m = imgSrc.match(/^(https?)(:\/\/(?:imgsrc|imgsa|\w+\.hiphotos|tiebapic)\.(?:bdimg|baidu)\.com\/)(?:forum|album)\/.+\/(\w+\.(?:jpg|jpeg|gif|png|bmp|webp))(?:\?.+)?$/i)) ){ - // resolve(m[1] + m[2] + "forum/pic/item/" + m[3]) - //} - } else { - resolve(null); - } - }); - } - - for (let fn of [try1, try2, try3]) { - try { - let res = await timeoutPromise(fn(), 5000); - if (res && res != imgSrc) { - if (!Array.isArray(res)) res = [res]; - if (res.length) { - let finalSrc = await findWorkingSrc(res, true); - if (finalSrc?.length) return finalSrc; - } - } - } catch (e) { - console.log("ERROR getLargestImageSrc: " + fn.name + " -> ", e); - } - } - - return null; -} -async function isImageSrc(src) { - try { - const res = await fetchByPassOrigin(src, { - method: "HEAD", - }); - if (res?.ok) { - // const type = res.headers.get("content-type"); - const type = res.headers?.["content-type"]; - if (type?.startsWith?.("image/")) { - return true; - } - } - } catch (error) { - console.log("ERROR isImageSrc: " + src + " -> ", error); - } - - return new Promise((resolve) => { - let img = new Image(); - img.src = src; - img.onload = () => resolve(true); - img.onerror = () => resolve(false); - }); -} -function findWorkingSrc(srcs, inOrder = true) { - return new Promise((resolve, reject) => { - if (!srcs || !Array.isArray(srcs) || srcs.length === 0) { - reject("srcs is falsy, not an array, or empty"); - } else { - const checkImage = (src) => - // prevent Error: Content Security Policy directive: "connect-src 'self' - isImageSrc(src).then((value) => { - if (inOrder) return value; - if (value) resolve(src); - return value; - }); - - const promises = srcs.map(checkImage); - Promise.all(promises).then((res) => { - let trueIndex = res.indexOf(true); - if (trueIndex > -1) { - resolve(srcs[trueIndex]); - } else { - reject("none of the URLs are valid images"); - } - }); - } - }); -} -// resolve relative URLs into canonical absolute URLs based on the current location. -function canonicalUri(src, location = window.location) { - if (src.charAt(0) == "#") return location.href + src; - if (src.charAt(0) == "?") - return location.href.replace(/^([^\?#]+).*/, "$1" + src); - let root_page = /^[^?#]*\//.exec(location.href)[0], - base_path = location.pathname.replace(/\/[^\/]+\.[^\/]+$/, "/"), - root_domain = /^\w+\:\/\/\/?[^\/]+/.exec(root_page)[0], - absolute_regex = /^\w+\:\/\//; - src = src.replace("./", ""); - if (/^\/\/\/?/.test(src)) { - src = location.protocol + src; - } else if (!absolute_regex.test(src) && src.charAt(0) != "/") { - src = (base_path || "") + src; - } - return absolute_regex.test(src) - ? src - : (src.charAt(0) == "/" ? root_domain : root_page) + src; -} function formatSize(size, fixed = 0) { size = Number(size); if (!size) return "?"; @@ -1869,20 +641,6 @@ function promiseAllStepN(n, list) { }); }); } -// https://stackoverflow.com/a/38552302/11898496 -function parseJwt(token) { - let base64Url = token.split(".")[1]; - let base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/"); - let jsonPayload = decodeURIComponent( - atob(base64) - .split("") - .map(function (c) { - return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2); - }) - .join("") - ); - return JSON.parse(jsonPayload); -} function copyToClipboard(text) { // Check if Clipboard API is supported if (!navigator.clipboard) { @@ -1909,32 +667,6 @@ function copyToClipboard(text) { console.error("Failed to copy text to clipboard:", err); }); } -// https://stackoverflow.com/a/7960435 -function isEmptyFunction(func) { - try { - let m = func.toString().match(/\{([\s\S]*)\}/m)[1]; - return !m.replace(/^\s*\/\/.*$/gm, ""); - } catch (e) { - console.log("Error isEmptyFunction", e); - return false; - } -} -// https://stackoverflow.com/a/9310752 -function escapeRegExp(text) { - return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); -} -// https://stackoverflow.com/q/38849009 -function unescapeRegExp(text) { - return text.replace(/\\(.)/g, "$1"); -} -function encodeQueryString(obj) { - let str = []; - for (let p in obj) - if (obj.hasOwnProperty(p)) { - str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p])); - } - return str.join("&"); -} function moneyFormat(number, fixed = 0) { if (isNaN(number)) return 0; number = number.toFixed(fixed); @@ -1946,109 +678,20 @@ function moneyFormat(number, fixed = 0) { } return number; } -async function zipAndDownloadBlobs( - blobList, - zipFileName, - progressCallback, - successCallback -) { - if (!window.JSZip) { - await import("../libs/jzip/index.js"); - } - const zip = new window.JSZip(); - console.log(zip); - - // Add each Blob to the ZIP archive with a unique name - blobList.forEach(({ blob, fileName }, index) => { - console.log(fileName); - zip.file(fileName, blob); - }); - - // Generate the ZIP content with progress callback - zip - .generateAsync({ type: "blob" }, (metadata) => { - if (progressCallback) { - // Calculate progress as a percentage - const progress = metadata.percent | 0; - progressCallback(progress); - } - }) - .then((content) => { - successCallback?.(); - saveAs(content, zipFileName); - }); -} -async function getBlobFromUrl(url) { - try { - const response = await fetch(url); - const blob = await response.blob(); - return blob; - } catch (error) { - alert("Error: " + error); - } -} -async function getBlobFromUrlWithProgress(url, progressCallback) { - const response = await fetch(url, {}); - if (!response.ok) { - throw new Error(`Error: ${response.status} - ${response.statusText}`); - } - const contentLength = response.headers.get("content-length"); - const total = parseInt(contentLength, 10); - let loaded = 0; - const reader = response.body.getReader(); - const chunks = []; - - const startTime = Date.now(); - while (true) { - const { done, value } = await reader.read(); - if (done) break; - loaded += value.byteLength; - const ds = (Date.now() - startTime + 1) / 1000; - progressCallback?.({ - loaded, - total, - speed: loaded / ds, - }); - chunks.push(value); - } - - const blob = new Blob(chunks, { - type: response.headers.get("content-type"), - }); - - return blob; -} -function sanitizeFileName(fileName) { - // Định nghĩa các ký tự hợp lệ (chữ cái, số, dấu gạch dưới, dấu gạch ngang và dấu chấm) - const validChars = /^[a-zA-Z0-9_\-.]+$/; - - // Lược bỏ các ký tự không hợp lệ - let sanitizedFileName = ""; - for (let char of fileName) { - if (validChars.test(char)) { - sanitizedFileName += char; - } - } - - // Trả về tên tệp đã lược bỏ các ký tự không hợp lệ - return sanitizedFileName; -} - -// Ví dụ sử dụng hàm -let originalFileName = "tên-tệp!không@hợp#lệ$.txt"; -let sanitizedFileName = sanitizeFileName(originalFileName); -console.log(sanitizedFileName); // Output: tên-tệpkhônghợpệ.txt - // https://stackoverflow.com/a/69543476/11898496 function getOriginalWindowFunction(fnName) { const key = "ufs_original_windown_fn"; if (!window[key]) window[key] = {}; if (!window[key][fnName]) { - const iframe = document.createElement("iframe"); + let iframe = window[key]["iframe"]; - iframe.style.display = "none"; - document.body.appendChild(iframe); // add element + if (!iframe) { + iframe = document.createElement("iframe"); + iframe.style.display = "none"; + document.body.appendChild(iframe); + window[key]["iframe"] = iframe; + } window[key][fnName] = iframe.contentWindow[fnName]; } @@ -2115,15 +758,6 @@ function downloadBlob(blob, filename) { a.click(); URL.revokeObjectURL(blobUrl); } -// https://stackoverflow.com/a/15832662/11898496 -// TODO: chrome.downloads: https://developer.chrome.com/docs/extensions/reference/downloads/#method-download -function downloadURL(url, name) { - let link = document.createElement("a"); - link.target = "_blank"; - link.download = name; - link.href = url; - link.click(); -} function downloadData(data, filename, type = "text/plain") { let file = new Blob([data], { type: type }); if (window.navigator.msSaveOrOpenBlob) diff --git a/scripts/fb_downloadAlbumMedia.js b/scripts/fb_downloadAlbumMedia.js deleted file mode 100644 index 215daa9d..00000000 --- a/scripts/fb_downloadAlbumMedia.js +++ /dev/null @@ -1,121 +0,0 @@ -import { UfsGlobal } from "./content-scripts/ufs_global.js"; -import { showLoading } from "./helpers/utils.js"; - -export default { - icon: '', - name: { - en: "Download album facebook", - vi: "Tải album facebook", - }, - description: { - en: "Download photo/video links from facebook album", - vi: "Tải về danh sách link ảnh/video từ album facebook", - }, - // whiteList: ["https://graph.facebook.com/*"], - - popupScript: { - onClick: async function () { - const accessToken = prompt("Nhập access token:", ""); - if (!accessToken) return; - const albumId = prompt("Nhập album id: ", ""); - if (!albumId) return; - - async function fetchAlbumPhotosFromCursor({ albumId, cursor }) { - let url = `https://graph.facebook.com/v13.0/${albumId}/photos?fields=largest_image&limit=100&access_token=${accessToken}`; - if (cursor) url += `&after=${cursor}`; - const data = await fetch(url); - const json = await data.json(); - if (!json) return null; - return { - imgData: json.data?.map((_) => ({ - id: _.id, - url: _.largest_image.source, - })), - nextCursor: json.paging?.cursors?.after || null, - }; - } - async function fetchAlbumPhotos({ - albumId, - pageLimit = Infinity, - fromPhotoId = null, - pageFetchedCallback = async () => {}, - }) { - let currentPage = 1; - let hasNextCursor = true; - let nextCursor = fromPhotoId - ? Buffer.from(fromPhotoId).toString("base64") - : null; - let allImgsData = []; - while (hasNextCursor && currentPage <= pageLimit) { - console.log( - `ĐANG TẢI TRANG: ${currentPage}, Kích thước trang: 100 ảnh...` - ); - const data = await fetchAlbumPhotosFromCursor({ - albumId, - cursor: nextCursor, - }); - if (data?.imgData) { - allImgsData.push(...data.imgData); - console.log( - `> TÌM THẤY ${data.imgData.length} ẢNH. (TỔNG: ${allImgsData.length})` - ); - await pageFetchedCallback(data.imgData); - nextCursor = data.nextCursor; - hasNextCursor = nextCursor != null; - currentPage++; - } else { - console.log("[!] ERROR."); - break; - } - } - return allImgsData; - } - async function fetchAllPhotoLinksInAlbum({ - albumId, - fromPhotoId, - progress, - }) { - const from_text = fromPhotoId - ? "vị trí photo_id=" + fromPhotoId - : "đầu album"; - console.log(`ĐANG TẢI DỮ LIỆU ALBUM ${albumId} TỪ ${from_text}...`); - const result = []; - await fetchAlbumPhotos({ - albumId, - fromPhotoId, - pageFetchedCallback: (pageImgsData) => { - result.push(...pageImgsData.map((_) => _.url)); - progress?.(result.length); - }, - }); - return result; - } - - const { closeLoading, setLoadingText } = showLoading( - "Đang thu thập link ảnh/video trong album..." - ); - fetchAllPhotoLinksInAlbum({ - albumId, - progress: (total) => { - setLoadingText("Đang thu thập " + total + " links..."); - }, - }) - .then((links) => { - if ( - confirm( - "Tìm được " + - links.length + - " links ảnh/video.\nBấm OK để tải xuống." - ) - ) - UfsGlobal.Utils.downloadData(links.join("\n"), albumId, ".txt"); - }) - .catch((e) => { - alert("ERROR: " + e); - }) - .finally(() => { - closeLoading(); - }); - }, - }, -}; diff --git a/scripts/fb_downloadWallMediaFromPosts.js b/scripts/fb_downloadWallMediaFromPosts.js deleted file mode 100644 index c1929294..00000000 --- a/scripts/fb_downloadWallMediaFromPosts.js +++ /dev/null @@ -1,221 +0,0 @@ -import { UfsGlobal } from "./content-scripts/ufs_global.js"; -import { showLoading } from "./helpers/utils.js"; - -export default { - icon: '', - name: { - en: "Download all photos from posts", - vi: "Tải tất cả hình trên bài viết", - }, - description: { - en: "Get photos from all posts of group/page/user", - vi: "Lấy tất cả hình trên các bài viết (post) của group/page/user", - }, - - popupScript: { - onClick: async () => { - const WAIT_BEFORE_NEXT_FETCH = 500; - const FB_API_HOST = "https://graph.facebook.com/v12.0"; - const MEDIA_TYPE = { - PHOTO: "photo", - VIDEO: "video", - }; - let ACCESS_TOKEN = prompt("Nhập access token của bạn vào đây"); - if (!ACCESS_TOKEN) return; - - const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); - - const myFetch = async (_url) => { - try { - const response = await fetch(_url); - const json = await response.json(); - if (json.error) { - console.log("[!] ERROR", JSON.stringify(json, null, 4)); - return null; - } - return json; - } catch (e) { - console.log("[!] ERROR", e.toString()); - return null; - } - }; - - const getMediaFromAttachment = (attachment) => { - const filtered_media = []; - - let id = attachment?.target?.id; - let type = attachment?.type; - - if (!id || !type) return filtered_media; - - /* - Attachment LOẠI PHOTO có cấu trúc như sau - { - "media": { - "image": { - "height": 720, - "src": "https://scontent.fhan2-4.fna.fbcdn.net/v/t39.30808-6/p480x480/233193975_582887376210934_3917501890611553539_n.jpg?_nc_cat=103&ccb=1-5&_nc_sid=07e735&_nc_ohc=b2Z1BxAj3PwAX_a0j-F&_nc_ht=scontent.fhan2-4.fna&oh=1100b63609d1d331a0a17721b002ae78&oe=614A6EAD", - "width": 480 - } - }, - "target": { - "id": "582887366210935", - "url": "https://www.facebook.com/photo.php?fbid=582887366210935&set=gm.1020873538672374&type=3" - }, - "type": "photo", - "url": "https://www.facebook.com/photo.php?fbid=582887366210935&set=gm.1020873538672374&type=3" - }*/ - if (type === "photo") { - filtered_media.push({ - type: MEDIA_TYPE.PHOTO, - id: id, - url: attachment.media.image.src, - }); - } - - /* - Attachment LOẠI VIDEO_AUTOPLAY, VIDEO_INLINE, VIDEO có định dạng như sau - { - "media": { - "image": { - "height": 720, - "src": "https://scontent.fsgn2-4.fna.fbcdn.net/v/t15.5256-10/s720x720/241870607_843209866352821_4272847571535179706_n.jpg?_nc_cat=101&ccb=1-5&_nc_sid=ad6a45&_nc_ohc=Ap2YChXA4fUAX_RgBT7&_nc_ht=scontent.fsgn2-4.fna&oh=f9fcc65d6c8a53207c1d03b19d036503&oe=614B4EE9", - "width": 405 - }, - "source": "https://video.fsgn2-6.fna.fbcdn.net/v/t42.1790-2/241979905_562868464766358_5763545655575200708_n.mp4?_nc_cat=110&ccb=1-5&_nc_sid=985c63&efg=eyJybHIiOjM5MiwicmxhIjo1MTIsInZlbmNvZGVfdGFnIjoic3ZlX3NkIn0%3D&_nc_ohc=1vx2K2s8m1IAX8TzDPs&rl=392&vabr=218&_nc_ht=video.fsgn2-6.fna&oh=32df5af4a31f119a16ca4fb8d30b48f0&oe=61477791" - }, - "target": { - "id": "843209423019532", - "url": "https://www.facebook.com/groups/j2team.community.girls/permalink/1045907852835609/" - }, - "type": "video_autoplay", - "url": "https://www.facebook.com/groups/j2team.community.girls/permalink/1045907852835609/" - } */ - if ( - type === "video_autoplay" || - type === "video_inline" || - type === "video" - ) { - filtered_media.push({ - type: MEDIA_TYPE.VIDEO, - id: id, - url: attachment.media.source, - }); - } - - /* - Attachment LOẠI ALBUM có định dạng như sau - { - "media": { - "image": { - "height": 720, - "src": "https://scontent.fhan2-4.fna.fbcdn.net/v/t39.30808-6/p480x480/233193975_582887376210934_3917501890611553539_n.jpg?_nc_cat=103&ccb=1-5&_nc_sid=07e735&_nc_ohc=b2Z1BxAj3PwAX_a0j-F&_nc_ht=scontent.fhan2-4.fna&oh=1100b63609d1d331a0a17721b002ae78&oe=614A6EAD", - "width": 480 - } - }, - "subattachments": { - "data": [ - {sub_attachment_1}, // Các sub attachment này có cấu trúc giống attachment PHOTO hoặc VIDEO_AUTOPLAY - {sub_attachment_2}, - ... - ] - }, - "target": { - "id": "1020873538672374", - "url": "https://www.facebook.com/media/set/?set=pcb.1020873538672374&type=1" - }, - "title": "Photos from Lê Tài's post", - "type": "album", - "url": "https://www.facebook.com/media/set/?set=pcb.1020873538672374&type=1" - } */ - if (type === "album") { - // GỌI ĐỆ QUY VỚI TỪNG SUB_ATTACHMENT - attachment?.subattachments?.data?.forEach((sub) => { - filtered_media.push(...getMediaFromAttachment(sub)); - }); - } - - return filtered_media; - }; - - // fetch tất cả bài post (feed) trong wall của 1 target (user, group, page), và lấy ra các media (ảnh, video, ..) trong các bài post đó (NẾU CÓ) - // Trả về danh sách chứa {id, url} của từng media - const fetchWallMedia = async ({ - targetId, - pageLimit = Infinity, // Số lần fetch, mỗi lần fetch được khoảng 25 bài post (?) - pageFetchedCallback = () => {}, - }) => { - const all_media = []; // store all media {id, url, type} - let page = 1; - let url = `${FB_API_HOST}/${targetId}/feed?fields=attachments{media,type,subattachments,target}&access_token=${ACCESS_TOKEN}`; - - while (url && page <= pageLimit) { - console.log("fetching", url); - const fetchData = await myFetch(url); - page++; - - if (fetchData?.data) { - // Get all media from each attachment - const media = []; - fetchData.data.forEach((feedData) => { - feedData.attachments?.data.forEach((at) => { - media.push(...getMediaFromAttachment(at)); - }); - }); - - all_media.push(...media); - console.log("media: " + all_media.length, all_media); - - // callback when each page fetched - await pageFetchedCallback(media, all_media); - - // get next paging - url = fetchData?.paging?.next; - - // wait for next fetch - if needed - if (WAIT_BEFORE_NEXT_FETCH) { - await sleep(WAIT_BEFORE_NEXT_FETCH); - } - } else { - break; - } - } - - return all_media; - }; - - let id = prompt("Nhập ID của user, group, page cần lấy media", ""); - if (id) { - alert( - "Quá trình lấy link ảnh sắp diễn ra.\nVui lòng không tắt popup extension trong quá trình này.\nThông tin chi tiết sẽ được hiển thị ở console của extension." - ); - - let { setLoadingText, closeLoading } = showLoading("Prepairing..."); - fetchWallMedia({ - targetId: id, - pageFetchedCallback: (media, all_media) => { - setLoadingText(`Tải được ${all_media.length} ảnh/video...`); - }, - }) - .then((all_media) => { - let urls = Array.from(new Set(all_media.map((_) => _.url))).join( - "\n" - ); - console.log(urls); - setLoadingText(`Đang lưu ${urls.length} links vào file...`); - alert( - "Các link ảnh sẽ được lưu vào file\nBạn có thể bỏ file vào IDM để tải tất cả hình ảnh" - ); - UfsGlobal.Utils.downloadData(urls, "urls.txt"); - }) - .catch((err) => { - console.log(err); - alert("Có lỗi xảy ra: " + err); - }) - .finally(() => { - closeLoading(); - }); - } - }, - }, -}; diff --git a/scripts/fb_getAllUidOfGroupMembers.js b/scripts/fb_getAllUidOfGroupMembers.js index e2b1fb3f..abd0a92c 100644 --- a/scripts/fb_getAllUidOfGroupMembers.js +++ b/scripts/fb_getAllUidOfGroupMembers.js @@ -21,10 +21,6 @@ export default { pageScript: { onClick: async function () { - function sleep(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); - } - function getGroupId() { try { return require("CometRouteStore").getRoute(location.pathname) diff --git a/scripts/fb_getTokenBusinessStudio.js b/scripts/fb_getTokenBusinessStudio.js deleted file mode 100644 index 9e5a7870..00000000 --- a/scripts/fb_getTokenBusinessStudio.js +++ /dev/null @@ -1,51 +0,0 @@ -export default { - icon: ``, - name: { - en: "Get fb token EAAc (studio)", - vi: "Lấy fb token EAAc (studio)", - }, - description: { - en: "Get facebook access token EAAc from business.facebook.com", - vi: "Lấy facebook access token EAAc từ trang business.facebook.com", - }, - - popupScript: { - onClick: async function () { - const { showLoading } = await import("./helpers/utils.js"); - // old - FAILED - // try { - // const accessToken = - // "EAA" + /(?<=EAA)(.*?)(?=\")/.exec(document.body.textContent)[0]; - // prompt("Access Token của bạn:", accessToken); - // } catch (e) { - // alert("LỖI: " + e.message); - // } - - const { closeLoading, setLoadingText } = showLoading( - "Đang tìm access token..." - ); - fetch("https://business.facebook.com/creatorstudio/home", { - method: "GET", - credentials: "include", - }) - .then((res) => res.text()) - .then(function (htmlText) { - let regex = htmlText.match( - /MediaManagerStatics",\[\],{"accessToken":"(.+?)"/ - ); - if (null !== regex) { - let accesstoken = regex[1]; - prompt("Access Token: ", accesstoken); - } else { - alert("Token not found"); - } - }) - .catch(function (e) { - alert("ERROR:" + JSON.stringify(e)); - }) - .finally(() => { - closeLoading(); - }); - }, - }, -}; diff --git a/scripts/fb_moreReactionStory.js b/scripts/fb_moreReactionStory.js index a1d004e6..5096a2c9 100644 --- a/scripts/fb_moreReactionStory.js +++ b/scripts/fb_moreReactionStory.js @@ -37,7 +37,7 @@ export default { window.ufs_reactStory = async (text) => { const storyId = getStoryId(); - if (!UfsGlobal.Utils.isEmoji(text)) { + if (!isEmoji(text)) { alert("Must be emoji"); return; } @@ -197,6 +197,12 @@ export default { } }); } + // https://stackoverflow.com/a/67705964/23648002 + function isEmoji(text) { + return text?.match( + /^(\u00a9|\u00ae|[\u25a0-\u27bf]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])+$/ + ); + } }, onDocumentIdle: async () => { diff --git a/scripts/magnify_image.js b/scripts/magnify_image.js index 4689542a..228c6471 100644 --- a/scripts/magnify_image.js +++ b/scripts/magnify_image.js @@ -1,8 +1,13 @@ +import { getLargestImageSrc } from "./auto_redirectLargestImageSrc.js"; import { UfsGlobal } from "./content-scripts/ufs_global.js"; import { BADGES } from "./helpers/badge.js"; const contextMenuId = "magnify-image"; +const CACHED = { + mouse: { x: 0, y: 0 }, +}; + export default { icon: '', name: { @@ -100,6 +105,11 @@ export default { }, onDocumentStart_: async () => { + window.addEventListener("mousemove", (e) => { + CACHED.mouse.x = e.clientX; + CACHED.mouse.y = e.clientY; + }); + injectCss(); let hovering = null; @@ -135,7 +145,7 @@ export default { return; } - let rect = UfsGlobal.DOM.getContentClientRect(e.target); + let rect = getContentClientRect(e.target); if (rect.width < 30 || rect.height < 30) { rect.top -= rect.width / 2; rect.left -= rect.height / 2; @@ -152,9 +162,9 @@ export default { // only main frame if (window === window.top) { // ctrl - let unsub = UfsGlobal.DOM.onDoublePress("Control", () => { + let unsub = onDoublePress("Control", () => { UfsGlobal.Extension.trackEvent("magnify-image-CTRL"); - let mouse = UfsGlobal.DOM.getMousePos(); + let mouse = getMousePos(); magnifyImage(mouse.x, mouse.y); }); @@ -234,10 +244,13 @@ function injectCss( }); } } +function getMousePos() { + return CACHED.mouse; +} function validateMouse(x, y) { if (x == null || y == null) { - let mouse = UfsGlobal.DOM.getMousePos(); + let mouse = getMousePos(); return { x: mouse.x ?? x ?? 0, y: mouse.y ?? y ?? 0, @@ -253,7 +266,7 @@ function magnifyImage(x, y) { setTimeout(() => { if (!loaded) - removeLoading = UfsGlobal.DOM.addLoadingAnimationAtPos( + removeLoading = addLoadingAnimationAtPos( mouse.x, mouse.y, 40, @@ -374,7 +387,7 @@ function getImgSrcsFromElement(ele) { } } if (!srcset) return; - return UfsGlobal.Utils.getLargestSrcset(srcset); + return getLargestSrcset(srcset); }, () => { // if (/img|picture|source|image|a/i.test(ele.tagName)) @@ -393,10 +406,7 @@ function getImgSrcsFromElement(ele) { return ele.getAttribute("href"); // reddit } if (/svg/i.test(ele.tagName)) { - return [ - UfsGlobal.Utils.svgToBase64(ele), - UfsGlobal.Utils.svgToBlobUrl(ele), - ]; + return [svgToBase64(ele), svgToBlobUrl(ele)]; } if (/canvas/i.test(ele.tagName)) { return ele.toDataURL(); @@ -471,6 +481,22 @@ async function filterImageSrcs(eleSrcs) { } } +function getLargestSrcset(srcset) { + let srcs = srcset.split(/[xw],/i), + largeSize = -1, + largeSrc = null; + if (!srcs.length) return null; + srcs.forEach((srci) => { + let srcInfo = srci.trim().split(/(\s+|%20)/), + curSize = parseInt(srcInfo[2] || 0); + if (srcInfo[0] && curSize > largeSize) { + largeSize = curSize; + largeSrc = srcInfo[0]; + } + }); + return largeSrc; +} + async function getImagesAtPos(x, y) { let eles = Array.from(document.querySelectorAll("*")); @@ -707,7 +733,7 @@ function chooseImg(srcs, _x, _y) { }; img.onclick = () => { // overlay.remove(); - let mouse = UfsGlobal.DOM.getMousePos(); + let mouse = getMousePos(); createPreview( src, mouse.x, @@ -800,7 +826,7 @@ function createPreview( function updateSize() { if (img.naturalWidth && img.naturalHeight) { - let resolution = UfsGlobal.Utils.getResolutionCategory( + let resolution = getResolutionCategory( img.naturalWidth, img.naturalHeight ); @@ -818,7 +844,7 @@ function createPreview( } } - const { destroy, animateTo } = UfsGlobal.DOM.enableDragAndZoom( + const { destroy, animateTo } = enableDragAndZoom( img, overlay, (updatedValue) => { @@ -827,7 +853,7 @@ function createPreview( ); let removeAnimLoading; setTimeout(() => { - removeAnimLoading = UfsGlobal.DOM.addLoadingAnimation(animDiv, 40); + removeAnimLoading = addLoadingAnimation(animDiv, 40); }, 500); // close on click outside @@ -983,7 +1009,7 @@ function createPreview( }; openNewTab.onclick = () => { if (/^data:image\/svg/.test(img.src)) { - const url = UfsGlobal.Utils.svgBase64ToUrl(img.src); + const url = svgBase64ToUrl(img.src); window.open(url, "_blank"); } else { window.open(img.src, "_blank"); @@ -1027,7 +1053,7 @@ function createPreview( }); setTimeout(() => { if (!loaded) { - loadingRef = UfsGlobal.DOM.addLoadingAnimationAtPos( + loadingRef = addLoadingAnimationAtPos( window.innerWidth / 2, window.innerHeight - 130 ); @@ -1042,7 +1068,7 @@ function createPreview( }, 100); }, 300); // show loading after 300ms - UfsGlobal.Utils.getLargestImageSrc(src, location.href).then((_src) => { + getLargestImageSrc(src, location.href).then((_src) => { if (!_src || _src == src) { loaded = true; UfsGlobal.DOM.notify({ @@ -1080,3 +1106,313 @@ function createPreview( } // #endregion + +// #region utils + +function addLoadingAnimationAtPos( + x, + y, + size = 40, + containerStyle = "", + loadingStyle = "" +) { + let ele = document.createElement("div"); + ele.style.cssText = ` + position: fixed; + left: ${x - size / 2}px; + top: ${y - size / 2}px; + width: ${size}px; + height: ${size}px; + z-index: 2147483647; + pointer-events: none; + user-select: none; + ${containerStyle} + `; + addLoadingAnimation(ele, size, loadingStyle); + document.body.appendChild(ele); + return () => ele.remove(); +} +function addLoadingAnimation( + element, + size = Math.min(element?.clientWidth, element?.clientHeight) || 0, + customStyle = "" +) { + let id = Math.random().toString(36).substr(2, 9); + element.classList.add("ufs-loading-" + id); + + let borderSize = 4; + + // inject css code + let style = document.createElement("style"); + style.id = "ufs-loading-style-" + id; + style.textContent = ` + .ufs-loading-${id}::after { + content: ""; + display: block; + position: absolute; + top: 50%; + left: 50%; + width: ${size}px; + height: ${size}px; + margin-top: -${size / 2}px; + margin-left: -${size / 2}px; + border-radius: 50%; + border: ${borderSize}px solid #555 !important; + border-top-color: #eee !important; + animation: ufs-spin 1s ease-in-out infinite; + box-sizing: border-box !important; + ${customStyle} + } + @keyframes ufs-spin { + to { + transform: rotate(360deg); + } + } + `; + (document.head || document.documentElement).appendChild(style); + + return () => { + if (element) element.classList.remove("ufs-loading-" + id); + }; +} +function addEventListener(target, event, callback, options) { + target.addEventListener(event, callback, options); + return () => target.removeEventListener(event, callback, options); +} +function enableDragAndZoom(element, container, onUpdateCallback) { + // set style + const className = "ufs-drag-and-zoom"; + element.classList.add(className); + + let style = document.createElement("style"); + style.textContent = ` + .${className} { + cursor: grab; + position: relative !important; + -webkit-user-select: none !important; + -moz-user-select: none !important; + -ms-user-select: none !important; + user-select: none !important; + -khtml-user-select: none; + max-width: unset !important; + max-height: unset !important; + min-width: unset !important; + min-height: unset !important; + -webkit-user-drag: none !important; + }`; + (container || element).appendChild(style); + + // config + const lerpSpeed = 0.3; + const last = { x: 0, y: 0 }; + const mouse = { x: 0, y: 0 }; + const animTarget = { + left: parseFloat(element.style.left), + top: parseFloat(element.style.top), + width: parseFloat(element.style.width), + height: parseFloat(element.style.height), + }; + + let run = true; + function animate() { + let updated = false; + let updatedValue = {}; + for (let prop in animTarget) { + const currentValue = parseFloat(element.style[prop]); + const targetValue = animTarget[prop]; + let del = Math.abs(targetValue - currentValue); + + if (del > 0.1) { + const newValue = + del < 1 ? targetValue : lerp(currentValue, targetValue, lerpSpeed); + element.style[prop] = newValue + "px"; + updatedValue[prop] = newValue; + updated = true; + } + } + if (updated) onUpdateCallback?.(updatedValue); + if (run) requestAnimationFrame(animate); + } + + animate(); + + // Mouse down event listener + let dragging = false; + let _down = addEventListener(container || element, "mousedown", function (e) { + e.preventDefault(); + dragging = true; + last.x = e.clientX; + last.y = e.clientY; + element.style.cursor = "grabbing"; + }); + + // Mouse move event listener + let _move = addEventListener(document, "mousemove", function (e) { + mouse.x = e.clientX; + mouse.y = e.clientY; + if (dragging) { + let delX = e.clientX - last.x; + let delY = e.clientY - last.y; + + animTarget.left += delX; + animTarget.top += delY; + + last.x = e.clientX; + last.y = e.clientY; + } + }); + + // Mouse up event listener + let _up = addEventListener(document, "mouseup", function () { + dragging = false; + element.style.cursor = "grab"; + }); + + // Mouse leave event listener + let _leave = addEventListener(document, "mouseleave", function () { + dragging = false; + element.style.cursor = "grab"; + }); + + // Mouse wheel event listener for zooming + let _wheel = addEventListener(container || element, "wheel", function (e) { + e.preventDefault(); + + const curScale = parseFloat(element.style.width) / element.width; + const delta = -e.wheelDeltaY || -e.wheelDelta; + const factor = Math.abs((0.3 * delta) / 120); + const newScale = + delta > 0 ? curScale * (1 - factor) : curScale * (1 + factor); + + const newW = element.width * newScale; + const newH = element.height * newScale; + + if (newW < 10 || newH < 10) { + return; + } + + const left = parseFloat(element.style.left); + const top = parseFloat(element.style.top); + const offsetX = mouse.x - left; + const offsetY = mouse.y - top; + const newLeft = left - (newW - element.width) * (offsetX / element.width); + const newTop = top - (newH - element.height) * (offsetY / element.height); + + animTarget.left = newLeft; + animTarget.top = newTop; + animTarget.width = newW; + animTarget.height = newH; + }); + + let listeners = [_down, _move, _up, _leave, _wheel]; + + return { + animateTo: (x, y, w, h) => { + animTarget.left = x; + animTarget.top = y; + animTarget.width = w; + animTarget.height = h; + }, + destroy: () => { + run = false; + style.remove(); + element.classList.remove(className); + listeners.forEach((l) => l?.()); + }, + }; +} +// prettier-ignore +function getContentClientRect(target, win = window) { + let rect = target.getBoundingClientRect(); + let compStyle = win.getComputedStyle(target); + let pFloat = parseFloat; + let top = rect.top + pFloat(compStyle.paddingTop) + pFloat(compStyle.borderTopWidth); + let right = rect.right - pFloat(compStyle.paddingRight) - pFloat(compStyle.borderRightWidth); + let bottom = rect.bottom - pFloat(compStyle.paddingBottom) - pFloat(compStyle.borderBottomWidth); + let left = rect.left + pFloat(compStyle.paddingLeft) + pFloat(compStyle.borderLeftWidth); + return { + top : top, + right : right, + bottom : bottom, + left : left, + width : right-left, + height : bottom-top, + }; +} +function onDoublePress(key, callback, timeout = 500) { + let timer = null; + let clickCount = 0; + const keyup = (event) => { + if (event.key !== key) { + clickCount = 0; + return; + } + clickCount++; + if (clickCount === 2) { + callback?.(); + clickCount = 0; + return; + } + clearTimeout(timer); + timer = setTimeout(() => { + clickCount = 0; + }, timeout); + }; + + document.addEventListener("keyup", keyup); + return () => { + clearTimeout(timer); + document.removeEventListener("keyup", keyup); + }; +} +function lerp(from, to, speed) { + return from + (to - from) * speed; +} +function svgBase64ToUrl(sgvBase64) { + try { + if (!/^data:image\/svg/.test(sgvBase64)) throw new Error("Invalid SVG"); + const svgContent = atob(sgvBase64.split(",")[1]); + const blob = new Blob([svgContent], { type: "image/svg+xml" }); + const url = URL.createObjectURL(blob); + setTimeout(() => URL.revokeObjectURL(url), 6e4); + return url; + } catch (e) { + console.log("ERROR: ", e); + return null; + } +} +function svgToBlobUrl(svg) { + let url = URL.createObjectURL(new Blob([svg], { type: "image/svg+xml" })); + return url; +} +function svgToBase64(svg) { + try { + return ( + "data:image/svg+xml;charset=utf-8;base64," + + btoa(new XMLSerializer().serializeToString(svg)) + ); + } catch (e) { + console.log("ERROR: ", e); + return null; + } +} +function getResolutionCategory(width, height) { + let min = Math.min(width, height); + let max = Math.max(width, height); + + if (max <= 256 && min <= 144) return "144p"; + if (max <= 320 && min <= 180) return "240p"; + if (max <= 640 && min <= 360) return "360p"; + if (max <= 640 && min <= 480) return "SD (480p)"; + if (max <= 1280 && min <= 720) return "HD (720p)"; + if (max <= 1600 && min <= 900) return "HD+ (900p)"; + if (max <= 1920 && min <= 1080) return "FHD (1080p)"; + if (max <= 2560 && min <= 1440) return "QHD (1440p)"; + if (max <= 3840 && min <= 2160) return "4K (2160p)"; + if (max <= 5120 && min <= 2880) return "5K (2880p)"; + if (max <= 7680 && min <= 4320) return "8K (4320p)"; + if (max <= 10240 && min <= 4320) return "10K (4320p)"; + if (max <= 15360 && min <= 8640) return "16K (8640p)"; + return "> 16K"; +} +// #endregion diff --git a/scripts/pip_anything.js b/scripts/pip_anything.js index 40f5304f..cec9c341 100644 --- a/scripts/pip_anything.js +++ b/scripts/pip_anything.js @@ -26,7 +26,7 @@ export default { // original from https://chromewebstore.google.com/detail/gepffghbolhjojibgohkdecdibdpbali // document: https://developer.chrome.com/docs/web-platform/document-picture-in-picture onClick: () => { - UfsGlobal.DOM.pickElement().then((element) => { + pickElement().then((element) => { openInPip(element); }); }, @@ -70,3 +70,135 @@ export const openInPip = async (element) => { pipWindow.document.body.append(element); pipWindow.addEventListener("pagehide", restorePIPElement); }; + +function pickElement() { + return new Promise((resolve) => { + // #region init elements + const host = document.createElement("div"); + const shadow = host.attachShadow({ mode: "closed" }); + + const style = document.createElement("style"); + style.textContent = /*css*/ ` + :host { + position: fixed; + display: block; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + z-index: 99999999; + pointer-events: none; + } + #highlighter { + position: absolute; + top: 0; + left: 0; + width: 0; + height: 0; + background: #ff3f3f22; + border: 1px solid #F00; + box-shadow: 0 0 0 99999px rgba(0, 0, 0, .5); + } + #confirmer { + position: absolute; + bottom: 0; + right: 0; + width: 200px; + height: 200px; + } + #confirmer input { + width: 100%; + } + `; + shadow.append(style); + + const highlighter = document.createElement("div"); + highlighter.id = "highlighter"; + shadow.append(highlighter); + + // const confirmer = document.createElement("div"); + // confirmer.id = "confirmer"; + // confirmer.innerHTML = ` + // + // Pick + // + // `; + // shadow.append(confirmer); + + document.documentElement.append(host); + // #endregion + + // #region pick element + const STATES = { + picking: "picking", + confirming: "confirming", + }; + let state = STATES.picking; + let currentTarget = null; + function onMouseOver(e) { + e.preventDefault(); + const target = e.target; + if (state !== STATES.picking || !target || target === highlighter) return; + + highlightElement(target); + } + + function highlightElement(elem) { + const rect = getElementBoundingClientRect(elem); + highlighter.style.cssText = ` + top: ${rect.top}px; + left: ${rect.left}px; + width: ${rect.width}px; + height: ${rect.height}px; + `; + currentTarget = elem; + } + + window.addEventListener("mouseover", onMouseOver, { capture: true }); + window.addEventListener( + "click", + (e) => { + e.preventDefault(); + // confirmPick(currentTarget); + + host.remove(); + resolve(currentTarget); + window.removeEventListener("mouseover", onMouseOver); + }, + { once: true, capture: true } + ); + // #endregion + }); +} + +function getElementBoundingClientRect(elem) { + let rect = + typeof elem.getBoundingClientRect === "function" + ? elem.getBoundingClientRect() + : { height: 0, left: 0, top: 0, width: 0 }; + + // https://github.com/gorhill/uBlock/issues/1024 + // Try not returning an empty bounding rect. + if (rect.width !== 0 && rect.height !== 0) return rect; + if (elem.shadowRoot instanceof DocumentFragment) + return getElementBoundingClientRect(elem.shadowRoot); + + let left = rect.left, + right = left + rect.width, + top = rect.top, + bottom = top + rect.height; + + for (const child of elem.children) { + rect = getElementBoundingClientRect(child); + if (rect.width === 0 || rect.height === 0) continue; + if (rect.left < left) left = rect.left; + if (rect.right > right) right = rect.right; + if (rect.top < top) top = rect.top; + if (rect.bottom > bottom) bottom = rect.bottom; + } + + let height = bottom - top, + width = right - left; + + return { bottom, height, left, right, top, width }; +} diff --git a/scripts/simpleAllowCopy.js b/scripts/simpleAllowCopy.js index 82cab80c..2fdbe405 100644 --- a/scripts/simpleAllowCopy.js +++ b/scripts/simpleAllowCopy.js @@ -32,7 +32,7 @@ export default { }, onClick_: function () { - let isMainFrame = !UfsGlobal.DOM.isInIframe(); + let isMainFrame = window === window.top; if (!window.ufs_simpleAllowCopy) { if (isMainFrame) alert("Vui lòng mở chức năng trước, rồi tải lại trang web."); diff --git a/scripts/tiktok_GLOBAL.js b/scripts/tiktok_GLOBAL.js index 0d6c35e9..b775cf33 100644 --- a/scripts/tiktok_GLOBAL.js +++ b/scripts/tiktok_GLOBAL.js @@ -171,7 +171,22 @@ export const SnapTik = { let result = c(...params); let jwt = result.match(/d\?token=(.*?)\&dl=1/)?.[1]; if (!jwt) return null; - let payload = UfsGlobal.Utils.parseJwt(jwt); + let payload = parseJwt(jwt); return payload?.url; }, }; + +// https://stackoverflow.com/a/38552302/11898496 +function parseJwt(token) { + let base64Url = token.split(".")[1]; + let base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/"); + let jsonPayload = decodeURIComponent( + atob(base64) + .split("") + .map(function (c) { + return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2); + }) + .join("") + ); + return JSON.parse(jsonPayload); +} diff --git a/scripts/tiktok_batchDownload.js b/scripts/tiktok_batchDownload.js index 23acf4bf..194139e3 100644 --- a/scripts/tiktok_batchDownload.js +++ b/scripts/tiktok_batchDownload.js @@ -224,9 +224,7 @@ export default { return checkbox; } - async function sleep(time) { - await new Promise((resolve) => setTimeout(resolve, time)); - } + const sleep = UfsGlobal.Utils.sleep; async function getLinkVideos(videoUrls) { if (!videoUrls.length) return; @@ -265,15 +263,14 @@ export default { // conflictAction: "overwrite", // filename: // "tiktok/" + - // UfsGlobal.Utils.sanitizeFileName( + // sanitizeFileName( // CACHED.videoById.get(id)?.name || id // ) + // ".mp4", // }); links.push({ url: link, - name: - UfsGlobal.Utils.sanitizeFileName(cached?.name || id) + ".mp4", + name: sanitizeFileName(cached?.name || id) + ".mp4", }); } else { progressDiv.innerText = `[LỖI] Không thể tải video ${url}.`; @@ -340,3 +337,19 @@ export default { }, }, }; + +function sanitizeFileName(fileName) { + // Định nghĩa các ký tự hợp lệ (chữ cái, số, dấu gạch dưới, dấu gạch ngang và dấu chấm) + const validChars = /^[a-zA-Z0-9_\-.]+$/; + + // Lược bỏ các ký tự không hợp lệ + let sanitizedFileName = ""; + for (let char of fileName) { + if (validChars.test(char)) { + sanitizedFileName += char; + } + } + + // Trả về tên tệp đã lược bỏ các ký tự không hợp lệ + return sanitizedFileName; +} diff --git a/scripts/tiktok_downloadWatchingVideo.js b/scripts/tiktok_downloadWatchingVideo.js index 5d804a9c..57ab4da2 100644 --- a/scripts/tiktok_downloadWatchingVideo.js +++ b/scripts/tiktok_downloadWatchingVideo.js @@ -90,8 +90,7 @@ export default { // filename: title + ".mp4", // }); - const { formatSize, downloadBlob, getBlobFromUrlWithProgress } = - UfsGlobal.Utils; + const { formatSize, downloadBlob } = UfsGlobal.Utils; const blob = await getBlobFromUrlWithProgress( link, ({ loaded, total, speed }) => { @@ -149,3 +148,35 @@ export const shared = { }); }, }; + +async function getBlobFromUrlWithProgress(url, progressCallback) { + const response = await fetch(url, {}); + if (!response.ok) { + throw new Error(`Error: ${response.status} - ${response.statusText}`); + } + const contentLength = response.headers.get("content-length"); + const total = parseInt(contentLength, 10); + let loaded = 0; + const reader = response.body.getReader(); + const chunks = []; + + const startTime = Date.now(); + while (true) { + const { done, value } = await reader.read(); + if (done) break; + loaded += value.byteLength; + const ds = (Date.now() - startTime + 1) / 1000; + progressCallback?.({ + loaded, + total, + speed: loaded / ds, + }); + chunks.push(value); + } + + const blob = new Blob(chunks, { + type: response.headers.get("content-type"), + }); + + return blob; +} diff --git a/scripts/web_timer.js b/scripts/web_timer.js index db25fbaa..eafafbd0 100644 --- a/scripts/web_timer.js +++ b/scripts/web_timer.js @@ -147,7 +147,7 @@ function run() { // track user events: mouse, keyboard, touch, ... let needUpdateLastActive = true; - let updateLastActive = UfsGlobal.Utils.throttle(function (e, mainframe) { + let updateLastActive = throttle(function (e, mainframe) { needUpdateLastActive = true; }, 1000 / 24); @@ -506,6 +506,22 @@ function run() { document.title = newTitle; } } +// https://dev.to/jeetvora331/throttling-in-javascript-easiest-explanation-1081 +function throttle(mainFunction, delay) { + let timerFlag = null; // Variable to keep track of the timer + + // Returning a throttled version + return (...args) => { + if (timerFlag === null) { + // If there is no timer currently running + mainFunction(...args); // Execute the main function + timerFlag = setTimeout(() => { + // Set a timer to clear the timerFlag after the specified delay + timerFlag = null; // Clear the timerFlag to allow the main function to be executed again + }, delay); + } + }; +} const backup = () => { // export data from https://chrome.google.com/webstore/detail/ppaojnbmmaigjmlpjaldnkgnklhicppk (async () => { From 689f77aa815eb10f93c70143ebad4ed93bb625c8 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Tue, 23 Jul 2024 14:16:11 +0700 Subject: [PATCH 12/47] delete unused --- scripts/backup/chongLuaDao_bg.js | 246 ---------------- scripts/backup/fb-albums.min.js | 416 --------------------------- scripts/backup/fb-down-all-album.js | 224 --------------- scripts/backup/tool_get_link_all.js | 156 ---------- scripts/backup/xem-tin-nhan-dau-tien | 129 --------- scripts/backup/yt-playlist-maker.js | 51 ---- 6 files changed, 1222 deletions(-) delete mode 100644 scripts/backup/chongLuaDao_bg.js delete mode 100644 scripts/backup/fb-albums.min.js delete mode 100644 scripts/backup/fb-down-all-album.js delete mode 100644 scripts/backup/tool_get_link_all.js delete mode 100644 scripts/backup/xem-tin-nhan-dau-tien delete mode 100644 scripts/backup/yt-playlist-maker.js diff --git a/scripts/backup/chongLuaDao_bg.js b/scripts/backup/chongLuaDao_bg.js deleted file mode 100644 index 1fc57d78..00000000 --- a/scripts/backup/chongLuaDao_bg.js +++ /dev/null @@ -1,246 +0,0 @@ -! function(e) { - var t = {}; - function n(i) { - if (t[i]) return t[i].exports; - var r = t[i] = { - i: i, - l: !1, - exports: {} - }; - return e[i].call(r.exports, r, r.exports, n), r.l = !0, r.exports - } - n.m = e, n.c = t, n.d = function(e, t, i) { - n.o(e, t) || Object.defineProperty(e, t, { - enumerable: !0, - get: i - }) - }, n.r = function(e) { - "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, { - value: "Module" - }), Object.defineProperty(e, "__esModule", { - value: !0 - }) - }, n.t = function(e, t) { - if (1 & t && (e = n(e)), 8 & t) return e; - if (4 & t && "object" == typeof e && e && e.__esModule) return e; - var i = Object.create(null); - if (n.r(i), Object.defineProperty(i, "default", { - enumerable: !0, - value: e - }), 2 & t && "string" != typeof e) - for (var r in e) n.d(i, r, function(t) { - return e[t] - }.bind(null, r)); - return i - }, n.n = function(e) { - var t = e && e.__esModule ? function() { - return e.default - } : function() { - return e - }; - return n.d(t, "a", t), t - }, n.o = function(e, t) { - return Object.prototype.hasOwnProperty.call(e, t) - }, n.p = "", n(n.s = 0) -}([function(e, t) { - window.isWhiteList = {}, window.isBlocked = {}, window.results = {}, window.isPhish = {}, window.legitimatePercents = {}; - let blackList = []; - const whiteList = []; - let r = !1; - const o = e => { - chrome.storage.local.get(["cache", "cacheTime"], t => { - if (t.cache && t.cacheTime) return e(t.cache); - (e => { - fetch("https://api.chongluadao.vn/classifier.json") - .then(e => e.json()) - .then(t => { - chrome.storage.local.set({ - cache: t, - cacheTime: Date.now() - }, () => e(t)) - }) - })(e) - }) - }, - s = (e, t, n) => { - if (window.isWhiteList[e] == n) return; - let i = 0, - r = 0, - s = 0; - for (const e in t) "1" == t[e] ? s++ : "0" == t[e] ? r++ : i++; - if (window.legitimatePercents[e] = i / (s + r + i) * 100, t.length) { - const n = [t.map(e => parseInt(e))]; - o((function(t) { - const i = (e => ({ - predict: t => { - let n = [e.estimators.map(e => (e => { - const t = t => { - let n = e; - for (; - "split" == n.type;) { - const e = n.threshold.split(" <= "); - n = t[e[0]] <= e[1] ? n.left : n.right - } - return n.value[0] - }; - return { - predict: e => e.map(e => t(e)), - predictOne: t - } - })(e) - .predict(t))]; - n = n[0].map((e, t) => n.map(e => e[t])); - const i = []; - for (const e in n) { - let t = 0, - r = 0; - for (const i in n[e]) t += n[e][i][1], r += n[e][i][0]; - i.push([t >= r, Math.max(t, r)]) - } - return i - } - }))(t); - window.isPhish[e] = i.predict(n)[0][0], window.isPhish[e] && window.legitimatePercents[e] > 60 && (window.isPhish[e] = !1), l(window.isPhish[e], window.legitimatePercents[e], e) - })) - } - }, - c = () => { - fetch("https://api.chongluadao.vn/v2/blacklist") - .then(e => e.json()) - .then(e => { - e.forEach(e => { - blackList.push(e.url) - }) - }) - .catch(() => {}), fetch("https://api.chongluadao.vn/v2/whitelist") - .then(e => e.json()) - .then(e => { - e.forEach(e => { - whiteList.push(e.url) - }) - }) - .catch(() => {}) - }, - redirectToBlocking = (e, t, n) => { - const i = { - site: e, - match: t, - title: e, - lenient: r, - favicon: "https://www.google.com/s2/favicons?domain=" + e - }; - window.isBlocked[n] = e, chrome.browserAction.setIcon({ - path: "../assets/cldvn_red.png", - tabId: n - }); - return { - redirectUrl: `${chrome.extension.getURL("blocking.html")}#${JSON.stringify(i)}` - } - }, - makeURL = e => { - try { - return new URL(e) - } catch (e) { - return - } - }, - l = (e, t, n) => { - if (chrome.browserAction.setTitle({ - title: `P:${e} per: ${t}` - }), e) return chrome.browserAction.setIcon({ - path: "../assets/cldvn_red.png", - tabId: n - }); - chrome.browserAction.setIcon({ - path: "../assets/cldvn128.png", - tabId: n - }) - }, - getHost = e => { - const t = e.match(/^https?:\/\/([^/?#]+)(?:[/?#]|$)/i); - return t && t[1] - }; - chrome.runtime.onStartup.addListener(c), chrome.runtime.onInstalled.addListener(() => { - c(), chrome.notifications.create({ - type: "basic", - iconUrl: chrome.extension.getURL("assets/logo.png"), - title: "Cài đặt thành công!", - message: "Khởi động lại trình duyệt của bạn để có thể bắt đầu sử dụng ChongLuaDao. Xin cảm ơn!" - }) - }), chrome.tabs.onActivated.addListener((e = null) => { - if (e && e.tabId) return l(window.isPhish[e.tabId], window.legitimatePercents[e.tabId], e.tabId); - chrome.tabs.query({ - active: !0, - currentWindow: !0 - }, ([e]) => { - l(window.isPhish[e.id], window.legitimatePercents[e.id], e.id) - }) - }), chrome.tabs.onUpdated.addListener((e, t, n) => { - "complete" == n.status && chrome.tabs.sendMessage(n.id, n) - }), chrome.runtime.onConnect.addListener(e => { - switch (e.name) { - case "REDIRECT_PORT_NAME": - e.onMessage.addListener(e => { - chrome.tabs.query({ - currentWindow: !0, - active: !0 - }, ([t]) => { - chrome.tabs.update(t.id, { - url: e.redirect - }) - }) - }); - break; - case "CLOSE_TAB_PORT_NAME": - e.onMessage.addListener(e => { - e.close_tab && chrome.tabs.query({ - currentWindow: !0, - active: !0 - }, ([e]) => { - chrome.tabs.remove(e.id) - }) - }); - break; - case "ML_PORT_NAME": - e.onMessage.addListener(e => { - const { - request: t - } = e; - void 0 !== t.input_block_list && (blackList = t.input_block_list, r = t.input_block_lenient), chrome.tabs.query({ - currentWindow: !0, - active: !0 - }, ([e]) => { - window.results[e.id] = t, s(e.id, t, e.url) - }) - }) - } - }), chrome.webRequest.onBeforeRequest.addListener(({ - url: url, - tabId: tabId, - initiator: initiator - }) => { - if (!url || 0 === url.indexOf("chrome://") || 0 === url.indexOf(chrome.extension.getURL("/"))) return; - if (!blackList || !blackList.length) return; - if (localStorage.getItem("whiteList")) return localStorage.removeItem("whiteList"); - const _blackList = blackList, - _url = makeURL(url), - psl_res = psl.parse(_url.host), - l = _url.href.replaceAll("/", ""); - for (let n = 0; n < _blackList.length; ++n) { - const black_url = makeURL(_blackList[n]); - if (!black_url) continue; - const firstPart = black_url.host.split(".")[0], - pathName = black_url.pathname; - if ("%2A" == firstPart) { - if (black_url.host.slice(4, black_url.host.length) == psl_res.domain) return redirectToBlocking(url, black_url.host, tabId) - } - if ("/*" == pathName && _url.host === black_url.host) return redirectToBlocking(url, black_url.host, tabId); - if (l && l == black_url.href.replaceAll("/", "")) return redirectToBlocking(url, black_url.host, tabId) - } - const hostName = getHost(initiator || url); - whiteList.find(e => e.includes(hostName)) && (window.isWhiteList[tabId] = hostName) - }, { - urls: ["*://*/*"], - types: ["main_frame", "sub_frame"] - }, ["blocking"]) -}]); \ No newline at end of file diff --git a/scripts/backup/fb-albums.min.js b/scripts/backup/fb-albums.min.js deleted file mode 100644 index afc07ea9..00000000 --- a/scripts/backup/fb-albums.min.js +++ /dev/null @@ -1,416 +0,0 @@ -javascript: (() => { - function _0x2e99(_0x1ba5b7, _0xdd8e24) { - const _0x52b858 = _0x56b6(); - return ( - (_0x2e99 = function (_0x1bc24e, _0x2376a4) { - _0x1bc24e = _0x1bc24e - (-0x1 * 0x131b + 0x14 * 0x58 + 0xd13); - let _0x293cc2 = _0x52b858[_0x1bc24e]; - return _0x293cc2; - }), - _0x2e99(_0x1ba5b7, _0xdd8e24) - ); - } - function _0x56b6() { - const _0x3aecb3 = [ - "revokeObjectURL", - "click", - "push", - "type", - "setRequestHeader", - "https://graph.facebook.com/v20.0/", - "POST", - "...", - "URL", - "concat", - "after", - "//www.facebook.com/v1.0/dialog/oauth/confirm", - "body", - "cursors", - "value", - "querySelector", - "exec", - "data", - "imgData", - "1207200zPKQJy", - "send", - "navigator", - "fb_dtsg", - "download", - "30ELnKbi", - "&after=", - "DTSGInitialData", - "Đang\x20tải\x20", - "status", - "from", - "paging", - "appendChild", - "cookie", - "count", - "473986yFsSiV", - "728720uPvVUd", - "863058mngagO", - "photos", - "open", - "3nLwfso", - "msSaveOrOpenBlob", - "Đang\x20tải\x20album\x20", - "href", - "Error:\x20", - "length", - "url", - "nextCursor", - "size", - "log", - "5exjSTG", - "/photos?fields=largest_image{source}&limit=100&access_token=", - "Failed\x20to\x20Get\x20Access\x20Token.", - "token", - "2024-07-15", - "https://www.facebook.com/dialog/oauth/business/cancel/?app_id=256002347743983&version=v19.0&logger_id=&user_scopes[0]=email&user_scopes[1]=read_insights&user_scopes[2]=read_page_mailboxes&user_scopes[3]=pages_show_list&redirect_uri=fbconnect%3A%2F%2Fsuccess&response_types[0]=token&response_types[1]=code&display=page&action=finish&return_scopes=false&return_format[0]=access_token&return_format[1]=code&tp=unspecified&sdk=&selected_business_id=&set_token_expires_in_60_days=false", - "Không\x20tìm\x20thấy\x20album\x20nào", - "append", - "largest_image", - "map", - "238512YXjwsZ", - "744723SlAuaS", - "readyState", - "access_token", - "23518tdVtxa", - "Nhập\x20user/page\x20id\x20muốn\x20tải:", - "json", - "Content-type", - "match", - "Không\x20tìm\x20thấy\x20uid\x20trong\x20cookie.\x20Bạn\x20đã\x20đăng\x20nhập\x20chưa?", - "onreadystatechange", - ]; - _0x56b6 = function () { - return _0x3aecb3; - }; - return _0x56b6(); - } - (function (_0x59c9d5, _0x9072f8) { - const _0x4bd475 = _0x2e99, - _0x2ebad9 = _0x59c9d5(); - while (!![]) { - try { - const _0x1742e3 = - parseInt(_0x4bd475(0xef)) / (-0x26e7 * -0x1 + 0xa9 * -0x7 + -0x2247) + - (parseInt(_0x4bd475(0x118)) / (0x1ed2 + 0x2 * 0x69c + 0xb02 * -0x4)) * - (parseInt(_0x4bd475(0x11d)) / - (-0x1524 + -0x5 * 0x613 + -0x2 * -0x19c3)) + - (-parseInt(_0x4bd475(0x119)) / (0xaf1 * -0x1 + 0x116b + -0x676)) * - (parseInt(_0x4bd475(0xe1)) / (-0x15d8 + 0x1241 + 0x39c)) + - parseInt(_0x4bd475(0x109)) / - (-0x19 * 0x106 + 0x1 * -0x23e1 + -0x21 * -0x1dd) + - parseInt(_0x4bd475(0x11a)) / (-0x169 + -0x1c11 + -0x7 * -0x437) + - parseInt(_0x4bd475(0xeb)) / (0x42 * 0x9 + -0x445 * 0x6 + 0x1754) + - (-parseInt(_0x4bd475(0xec)) / (0x3 * 0x935 + -0x1f1d + 0x387)) * - (parseInt(_0x4bd475(0x10e)) / - (0x78 * -0x3f + 0x1b3 * -0x16 + -0xd64 * -0x5)); - if (_0x1742e3 === _0x9072f8) break; - else _0x2ebad9["push"](_0x2ebad9["shift"]()); - } catch (_0x105d1a) { - _0x2ebad9["push"](_0x2ebad9["shift"]()); - } - } - })(_0x56b6, 0x5 * -0x25e5 + -0x4 * 0xbc56 + 0x67f1f), - (async () => { - const _0x50ae5a = _0x2e99; - function _0x5e3b54() { - return new Promise((_0x5b755f, _0x317202) => { - const _0x37d601 = _0x2e99; - let _0x50a8c8 = /(?<=c_user=)(\d+)/[_0x37d601(0x106)]( - document[_0x37d601(0x116)] - )?.[-0x1054 + 0x2 * -0x502 + 0x1a58]; - if (!_0x50a8c8) { - _0x317202(_0x37d601(0xf4)); - return; - } - let _0x42b070 = - require(_0x37d601(0x110))[_0x37d601(0xe4)] || - document[_0x37d601(0x105)]("[name=\x22fb_dtsg\x22]")["value"], - _0x17ad57 = new XMLHttpRequest(), - _0x2ba041 = _0x37d601(0x101), - _0x4b96fb = - "fb_dtsg=" + - _0x42b070 + - "&app_id=124024574287414&redirect_uri=fbconnect%3A%2F%2Fsuccess&display=page&access_token=&from_post=1&return_format=access_token&domain=&sso_device=ios&_CONFIRM=1&_user=" + - _0x50a8c8; - _0x17ad57["open"]( - _0x37d601(0xfc), - _0x2ba041, - !(-0x40d + 0x401 + 0xc) - ), - _0x17ad57[_0x37d601(0xfa)]( - _0x37d601(0xf2), - "application/x-www-form-urlencoded" - ), - (_0x17ad57[_0x37d601(0xf5)] = function () { - const _0xb5f30b = _0x37d601; - if ( - -0x3 * 0x937 + 0xab3 * -0x1 + 0x7ac * 0x5 == - _0x17ad57[_0xb5f30b(0xed)] && - -0x1541 + -0x67 * -0x61 + -0x10fe == _0x17ad57[_0xb5f30b(0x112)] - ) { - var _0xfe6416 = _0x17ad57["responseText"]["match"]( - /(?<=access_token=)(.*?)(?=\&)/ - ); - _0xfe6416 && _0xfe6416[0x4 * -0x646 + 0x1276 + 0x6a2] - ? _0x5b755f(_0xfe6416[-0x23e5 + 0x120d + 0x8 * 0x23b]) - : _0x317202(_0xb5f30b(0xe3)); - } - }), - (_0x17ad57["onerror"] = function () { - const _0x273597 = _0x37d601; - _0x317202(_0x273597(0xe3)); - }), - _0x17ad57[_0x37d601(0x10a)](_0x4b96fb); - }); - } - function _0x531c64() { - return new Promise((_0x19dad7, _0xf1ca1e) => { - const _0x5e979d = _0x2e99; - let _0x188a23 = /(?<=c_user=)(\d+)/[_0x5e979d(0x106)]( - document[_0x5e979d(0x116)] - )?.[0xc83 * -0x1 + -0x1 * 0xac9 + 0xba6 * 0x2]; - if (!_0x188a23) { - _0xf1ca1e(_0x5e979d(0xf4)); - return; - } - let _0x4cb9f2 = - require(_0x5e979d(0x110))[_0x5e979d(0xe4)] || - document[_0x5e979d(0x105)]("[name=\x22fb_dtsg\x22]")[ - _0x5e979d(0x104) - ], - _0x25b6e1 = new XMLHttpRequest(), - _0x4361e2 = new FormData(), - _0x560dc9 = _0x5e979d(0xe6); - _0x4361e2[_0x5e979d(0xe8)](_0x5e979d(0x10c), _0x4cb9f2), - _0x25b6e1[_0x5e979d(0x11c)]( - _0x5e979d(0xfc), - _0x560dc9, - !(-0x1a5c + 0x5 * 0x633 + -0x4a3 * 0x1) - ), - (_0x25b6e1["onreadystatechange"] = function () { - const _0x461d75 = _0x5e979d; - if ( - -0x9d0 + 0x18fb + -0xf27 == _0x25b6e1[_0x461d75(0xed)] && - -0x7f * 0x12 + -0x26dd + -0x3093 * -0x1 == - _0x25b6e1[_0x461d75(0x112)] - ) { - var _0xf5eaf1 = _0x25b6e1["responseText"][_0x461d75(0xf3)]( - /(?<=access_token=)(.*?)(?=\&)/ - ); - _0xf5eaf1 && _0xf5eaf1[-0x19bb + 0x11a8 + -0x27 * -0x35] - ? _0x19dad7(_0xf5eaf1[0x10c + 0x2c6 + -0x3d2]) - : _0xf1ca1e(_0x461d75(0xe3)); - } - }), - (_0x25b6e1["onerror"] = function () { - const _0x30cd9a = _0x5e979d; - _0xf1ca1e(_0x30cd9a(0xe3)); - }), - _0x25b6e1[_0x5e979d(0x10a)](_0x4361e2); - }); - } - function _0x58b659(_0x580cd7 = 0x32 * -0xa + -0x18e1 + 0x1ad6) { - return _0x580cd7 === 0x1373 * -0x1 + 0x1b7d + -0x79 * 0x11 - ? _0x531c64() - : _0x5e3b54(); - } - async function _0x11d7cb(_0x501108, _0x367a15) { - const _0x123cc0 = _0x2e99; - let _0x2da4dd = [], - _0x1dc208 = ""; - while (!![]) { - try { - const _0x41e58e = await fetch( - _0x123cc0(0xfb) + - _0x501108 + - "/albums?fields=type,name,count,link,created_time&limit=100&access_token=" + - _0x367a15 + - _0x123cc0(0x10f) + - _0x1dc208 - ), - _0x542834 = await _0x41e58e[_0x123cc0(0xf1)](); - if (_0x542834[_0x123cc0(0x107)]) - _0x2da4dd = _0x2da4dd[_0x123cc0(0xff)]( - _0x542834[_0x123cc0(0x107)] - ); - let _0x459968 = - _0x542834[_0x123cc0(0x114)]?.[_0x123cc0(0x103)]?.[ - _0x123cc0(0x100) - ]; - if (!_0x459968 || _0x459968 === _0x1dc208) break; - _0x1dc208 = _0x459968; - } catch (_0x478db3) { - break; - } - } - return _0x2da4dd; - } - async function _0xa2e09f({ - cursor: _0x4dcf80, - albumId: _0x2bd98a, - access_token: _0x2a011f, - }) { - const _0x4671fb = _0x2e99; - for (let _0x12b436 of [ - _0x4671fb(0xfb) + _0x2bd98a + _0x4671fb(0xe2) + _0x2a011f, - ]) { - let _0x17c470 = encodeURI(_0x12b436); - if (_0x4dcf80) _0x17c470 += _0x4671fb(0x10f) + _0x4dcf80; - const _0x41024f = await fetch(_0x17c470), - _0x5f0fd9 = await _0x41024f[_0x4671fb(0xf1)](), - _0x51c1e7 = _0x5f0fd9?.[_0x4671fb(0x11b)] || _0x5f0fd9; - if (!_0x5f0fd9 || !_0x51c1e7?.[_0x4671fb(0x107)]?.[_0x4671fb(0xdc)]) - continue; - return { - imgData: - _0x51c1e7?.[_0x4671fb(0x107)]?.[_0x4671fb(0xea)]((_0x475371) => ({ - id: _0x475371["id"], - url: _0x475371[_0x4671fb(0xe9)]["source"], - })) || [], - nextCursor: - _0x51c1e7?.[_0x4671fb(0x114)]?.[_0x4671fb(0x103)]?.[ - _0x4671fb(0x100) - ] || null, - }; - } - } - async function _0x774dd6({ - albumId: _0x42fc2d, - access_token: _0x3b190b, - pageLimit: pageLimit = Infinity, - fromPhotoId: fromPhotoId = null, - progress: progress = async () => {}, - }) { - const _0x2cb668 = _0x2e99; - let _0x6cae00 = -0x331 + -0x16bb + 0x19ed, - _0x5a200f = !![], - _0x4899e3 = fromPhotoId - ? Buffer[_0x2cb668(0x113)](fromPhotoId)["toString"]("base64") - : null, - _0x2da72c = new Set(), - _0x1f7d9a = []; - while (_0x5a200f && _0x6cae00 <= pageLimit) { - const _0x16e614 = {}; - (_0x16e614["albumId"] = _0x42fc2d), - (_0x16e614[_0x2cb668(0xee)] = _0x3b190b), - (_0x16e614["cursor"] = _0x4899e3); - const _0x5a67cd = await _0xa2e09f(_0x16e614); - if (!_0x5a67cd?.[_0x2cb668(0x108)]) break; - let _0x220591 = ![]; - for (let _0x36b261 of _0x5a67cd[_0x2cb668(0x108)]) { - !_0x2da72c["has"](_0x36b261["id"]) && - ((_0x220591 = !![]), - _0x2da72c["add"](_0x36b261["id"]), - _0x1f7d9a[_0x2cb668(0xf8)](_0x36b261[_0x2cb668(0xdd)])); - } - await progress(_0x2da72c[_0x2cb668(0xdf)]), - (_0x4899e3 = _0x5a67cd[_0x2cb668(0xde)]), - (_0x5a200f = _0x220591 && _0x4899e3 != null), - _0x6cae00++; - } - return _0x1f7d9a; - } - function _0x4ddfad(_0x1bcb3a, _0x4d04ac) { - const _0x516b29 = _0x2e99, - _0x3636c4 = {}; - _0x3636c4[_0x516b29(0xf9)] = "text/plain"; - let _0xf23e11 = new Blob([_0x1bcb3a], _0x3636c4); - if (window[_0x516b29(0x10b)][_0x516b29(0xd8)]) - window["navigator"]["msSaveOrOpenBlob"](_0xf23e11, _0x4d04ac); - else { - let _0x507bae = document["createElement"]("a"), - _0x3d1f45 = URL["createObjectURL"](_0xf23e11); - (_0x507bae[_0x516b29(0xda)] = _0x3d1f45), - (_0x507bae[_0x516b29(0x10d)] = _0x4d04ac), - document["body"][_0x516b29(0x115)](_0x507bae), - _0x507bae[_0x516b29(0xf7)](), - setTimeout(function () { - const _0x2d7b19 = _0x516b29; - document[_0x2d7b19(0x102)]["removeChild"](_0x507bae), - window[_0x2d7b19(0xfe)][_0x2d7b19(0xf6)](_0x3d1f45); - }, 0x4 * 0x2fe + -0xdb4 + 0x1bc); - } - } - if (Date["now"]() > new Date(_0x50ae5a(0xe5))["getTime"]()) { - alert( - "Script\x20đã\x20hết\x20hạn,\x20vui\x20lòng\x20liên\x20hệ\x20gia\x20hạn" - ); - return; - } - const _0xd61c0d = prompt(_0x50ae5a(0xf0)); - if (!_0xd61c0d) return; - alert( - "Thông\x20tin\x20sẽ\x20được\x20hiển\x20thị\x20trong\x20Console\x20(F12)" - ); - let _0x28de54 = [ - -0xca4 + -0x1 * 0x1a53 + 0x26f9, - 0x1feb + 0x1682 + 0x489 * -0xc, - ]; - for ( - let _0x2e7da0 = 0x1c4 * 0x6 + 0x43b * 0x1 + -0xed3; - _0x2e7da0 < _0x28de54[_0x50ae5a(0xdc)]; - _0x2e7da0++ - ) { - try { - let _0x613e5c = _0x28de54[_0x2e7da0], - _0x379726 = await _0x58b659(_0x613e5c); - console[_0x50ae5a(0xe0)](_0x379726); - const _0x4cdec9 = await _0x11d7cb(_0xd61c0d, _0x379726); - if (!_0x4cdec9?.[_0x50ae5a(0xdc)]) throw new Error(_0x50ae5a(0xe7)); - console[_0x50ae5a(0xe0)](_0x4cdec9); - const _0x390584 = []; - for ( - let _0x15f0e1 = 0x1 * -0x1f9c + -0x26d4 + 0x4670; - _0x15f0e1 < _0x4cdec9["length"]; - _0x15f0e1++ - ) { - let _0x2bcf10 = _0x4cdec9[_0x15f0e1]; - console[_0x50ae5a(0xe0)]( - _0x50ae5a(0xd9) + - _0x15f0e1 + - "/" + - _0x4cdec9[_0x50ae5a(0xdc)] + - _0x50ae5a(0xfd), - _0x2bcf10 - ); - const _0x58fb49 = await _0x774dd6({ - access_token: _0x379726, - albumId: _0x2bcf10["id"], - fromPhotoId: null, - progress: (_0x24f8c5) => { - const _0x376d3a = _0x50ae5a; - console[_0x376d3a(0xe0)]( - _0x376d3a(0x111) + - _0x24f8c5 + - "/" + - _0x2bcf10[_0x376d3a(0x117)] + - _0x376d3a(0xfd) - ); - }, - }); - console[_0x50ae5a(0xe0)](_0x58fb49); - if (_0x58fb49?.[_0x50ae5a(0xdc)]) _0x390584["push"](..._0x58fb49); - } - if (_0x390584?.[_0x50ae5a(0xdc)]) { - let _0x3a32e7 = Array["from"](new Set(_0x390584)); - console["log"](_0x3a32e7), - _0x4ddfad( - Array[_0x50ae5a(0x113)](_0x3a32e7)["join"]("\x0a"), - _0xd61c0d + ".txt" - ); - return; - } - } catch (_0x11cabf) { - let _0x13166b = - _0x2e7da0 === - _0x28de54[_0x50ae5a(0xdc)] - (0x2e * -0x60 + 0xb0 + 0x1 * 0x1091); - if (_0x13166b) alert(_0x50ae5a(0xdb) + _0x11cabf); - console[_0x50ae5a(0xe0)]("ERROR", _0x11cabf); - } - } - })(); -})(); diff --git a/scripts/backup/fb-down-all-album.js b/scripts/backup/fb-down-all-album.js deleted file mode 100644 index ad2ea9bc..00000000 --- a/scripts/backup/fb-down-all-album.js +++ /dev/null @@ -1,224 +0,0 @@ -javascript: (async () => { - function getToken2() { - return new Promise((resolve, reject) => { - let uid = /(?<=c_user=)(\d+)/.exec(document.cookie)?.[0]; - if (!uid) { - reject("Không tìm thấy uid trong cookie. Bạn đã đăng nhập chưa?"); - return; - } - let dtsg = - require("DTSGInitialData").token || - document.querySelector('[name="fb_dtsg"]').value, - xhr = new XMLHttpRequest(), - url = "//www.facebook.com/v1.0/dialog/oauth/confirm", - params = - "fb_dtsg=" + - dtsg + - "&app_id=124024574287414&redirect_uri=fbconnect%3A%2F%2Fsuccess&display=page&access_token=&from_post=1&return_format=access_token&domain=&sso_device=ios&_CONFIRM=1&_user=" + - uid; - xhr.open("POST", url, !0); - xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); - xhr.onreadystatechange = function () { - if (4 == xhr.readyState && 200 == xhr.status) { - var a = xhr.responseText.match(/(?<=access_token=)(.*?)(?=\&)/); - if (a && a[0]) { - resolve(a[0]); - } else { - reject("Failed to Get Access Token."); - } - } - }; - xhr.onerror = function () { - reject("Failed to Get Access Token."); - }; - xhr.send(params); - }); - } - function getToken1() { - return new Promise((resolve, reject) => { - let uid = /(?<=c_user=)(\d+)/.exec(document.cookie)?.[0]; - if (!uid) { - reject("Không tìm thấy uid trong cookie. Bạn đã đăng nhập chưa?"); - return; - } - let dtsg = - require("DTSGInitialData").token || - document.querySelector('[name="fb_dtsg"]').value, - xhr = new XMLHttpRequest(), - data = new FormData(), - url = - "https://www.facebook.com/dialog/oauth/business/cancel/?app_id=256002347743983&version=v19.0&logger_id=&user_scopes[0]=email&user_scopes[1]=read_insights&user_scopes[2]=read_page_mailboxes&user_scopes[3]=pages_show_list&redirect_uri=fbconnect%3A%2F%2Fsuccess&response_types[0]=token&response_types[1]=code&display=page&action=finish&return_scopes=false&return_format[0]=access_token&return_format[1]=code&tp=unspecified&sdk=&selected_business_id=&set_token_expires_in_60_days=false"; - data.append("fb_dtsg", dtsg); - xhr.open("POST", url, !0); - xhr.onreadystatechange = function () { - if (4 == xhr.readyState && 200 == xhr.status) { - var a = xhr.responseText.match(/(?<=access_token=)(.*?)(?=\&)/); - if (a && a[0]) { - resolve(a[0]); - } else { - reject("Failed to Get Access Token."); - } - } - }; - xhr.onerror = function () { - reject("Failed to Get Access Token."); - }; - xhr.send(data); - }); - } - function getToken(option = 1) { - return option === 1 ? getToken1() : getToken2(); - } - async function getAllAlbums(id, access_token) { - let result = []; - let after = ""; - while (true) { - try { - const res = await fetch( - `https://graph.facebook.com/v20.0/${id}/albums?fields=type,name,count,link,created_time&limit=100&access_token=${access_token}&after=${after}` - ); - const json = await res.json(); - if (json.data) result = result.concat(json.data); - - let nextAfter = json.paging?.cursors?.after; - if (!nextAfter || nextAfter === after) break; - after = nextAfter; - } catch (e) { - break; - } - } - return result; - } - - async function fetchAlbumPhotosFromCursor({ cursor, albumId, access_token }) { - for (let _ of [ - `https://graph.facebook.com/v20.0/${albumId}/photos?fields=largest_image{source}&limit=100&access_token=${access_token}`, - ]) { - let url = encodeURI(_); - if (cursor) url += "&after=" + cursor; - const res = await fetch(url); - const json = await res.json(); - const root = json?.photos || json; - if (!json || !root?.data?.length) { - continue; - } - return { - imgData: - root?.data?.map((_) => ({ - id: _.id, - url: _.largest_image.source, - })) || [], - nextCursor: root?.paging?.cursors?.after || null, - }; - } - } - async function fetchAlbumPhotos({ - albumId, - access_token, - pageLimit = Infinity, - fromPhotoId = null, - progress = async () => {}, - }) { - let currentPage = 1; - let hasNextCursor = true; - let nextCursor = fromPhotoId - ? Buffer.from(fromPhotoId).toString("base64") - : null; - - let photIds = new Set(); - let allImgsData = []; - while (hasNextCursor && currentPage <= pageLimit) { - const data = await fetchAlbumPhotosFromCursor({ - albumId, - access_token, - cursor: nextCursor, - }); - if (!data?.imgData) break; - let added = false; - for (let img of data.imgData) { - if (!photIds.has(img.id)) { - added = true; - photIds.add(img.id); - allImgsData.push(img.url); - } - } - await progress(photIds.size); - - nextCursor = data.nextCursor; - hasNextCursor = added && nextCursor != null; - currentPage++; - } - return allImgsData; - } - - function downloadData(data, filename) { - let file = new Blob([data], { type: "text/plain" }); - if (window.navigator.msSaveOrOpenBlob) { - window.navigator.msSaveOrOpenBlob(file, filename); - } else { - let a = document.createElement("a"), - url = URL.createObjectURL(file); - a.href = url; - a.download = filename; - document.body.appendChild(a); - a.click(); - setTimeout(function () { - document.body.removeChild(a); - window.URL.revokeObjectURL(url); - }, 0); - } - } - - if (Date.now() > new Date("2024-07-15").getTime()) { - alert("Script đã hết hạn, vui lòng liên hệ gia hạn"); - return; - } - - const id = prompt("Nhập user/page id muốn tải:"); - if (!id) return; - - alert("Thông tin sẽ được hiển thị trong Console (F12)"); - - let options = [2, 1]; - for (let i = 0; i < options.length; i++) { - try { - let option = options[i]; - let access_token = await getToken(option); - console.log(access_token); - - const albums = await getAllAlbums(id, access_token); - if (!albums?.length) throw new Error("Không tìm thấy album nào"); - - console.log(albums); - - const allResult = []; - for (let i = 0; i < albums.length; i++) { - let album = albums[i]; - console.log(`Đang tải album ${i}/${albums.length}...`, album); - - const result = await fetchAlbumPhotos({ - access_token, - albumId: album.id, - fromPhotoId: null, - progress: (loaded) => { - console.log(`Đang tải ${loaded}/${album.count}...`); - }, - }); - - console.log(result); - if (result?.length) allResult.push(...result); - } - - if (allResult?.length) { - let uniqueResult = Array.from(new Set(allResult)); - console.log(uniqueResult); - downloadData(Array.from(uniqueResult).join("\n"), id + ".txt"); - return; - } - } catch (e) { - let outOfOption = i === options.length - 1; - if (outOfOption) alert("Error: " + e); - console.log("ERROR", e); - } - } -})(); diff --git a/scripts/backup/tool_get_link_all.js b/scripts/backup/tool_get_link_all.js deleted file mode 100644 index 08c0e60a..00000000 --- a/scripts/backup/tool_get_link_all.js +++ /dev/null @@ -1,156 +0,0 @@ -// https://openuserjs.org/scripts/we0019/Tool_Get_Link_Fshare,_4Share.vn,_Mp3zing.vn,_Nhaccuatui,_Tailieu.vn,_hoctot123.com/source - -// tool to decode: -// https://deobfuscate.relative.im/ -// + manual eval() + replace eval() with console.log() - -$(function () { - if (location.pathname.indexOf("/bai-hat/") === 0) { - var code = $("div.fb-like").data("href"); - $("#tabService").replaceWith( - ' Tải nhạc 128 kbps Tải nhạc 320 kbps Tải nhạc Lossless ' - ); - } - var linkbaihat = $("link[rel='canonical']").attr("href"); - if ( - $("#btnDownloadBox") === - 'Tải nhạc' - ) { - $("#btnDownloadBox").replaceWith( - ' Tải nhạc 128kbps Tải nhạc 320kbps Tải nhạc Lossless' - ); - } else { - $("#btnAddPlaylistNowPlaying").after( - ' Tải nhạc 128kbps Tải nhạc 320kbps Tải nhạc Lossless' - ); - } - if (location.pathname.indexOf("/file/") === 0) { - { - var link = window.location.href; - var link1 = link.replace("fshare.vn", "getlinkfshare.com"); - } - $(".policy_download").prepend( - ' ' - ); - } - if (window.location.hostname == "javhd.com") { - var link = window.location.href; - var linkget = "http://htstar.design/getjav.php?link=" + link; - $(".player-container").replaceWith( - '
' - ); - $(".downloads").replaceWith( - '' - ); - } - if (window.location.hostname == "tailieu.vn") { - var link = window.location.href; - var linkget = "https://linksvip.net/?link=" + link; - $(".btncam.marginright10").replaceWith( - 'Download qua linksvip' + - "" - ); - } - if (window.location.hostname == "hoctot123.com") { - var link = window.location.href; - var linkget = "http://bfeu.tk/getlinkhoctot123/xuly.php?url=" + link; - $("#login_pop").replaceWith( - 'DOWNLOAD TÀI LIỆU ĐÃ GET || Tool Get Link from J2TeaM' - ); - } - if (window.location.hostname == "4share.vn") { - var link = window.location.href; - var linkget = "https://linksvip.net/?link=" + link; - $("a[href='/payment/card/#FUNNY']").replaceWith( - '' - ); - } -}); -(function ($, window, document) { - "use strict"; - GM_addStyle( - ".bv-icon{background-image:url(http://static.mp3.zdn.vn/skins/zmp3-v4.1/images/icon.png)!important;background-repeat:no-repeat!important;background-position:-25px -2459px!important;}.bv-download{background-color:#70d4ff!important;border-color:#70d4ff!important;}.bv-download span{color:#fff!important;margin-left:8px!important;}.bv-disable,.bv-download:hover{background-color:#ff5e5e!important;border-color:#ff5e5e!important;}.bv-text{background-image:none!important;color:#fff!important;text-align:center!important;font-size:smaller!important;line-height:25px!important;}.bv-waiting{cursor:wait!important;background-color:#2980b9!important;border-color:#2980b9!important;}.bv-complete,.bv-complete:hover{background-color:#27ae60!important;border-color:#27ae60!important;}.bv-error,.bv-error:hover{background-color:#c0392b!important;border-color:#c0392b!important;}.bv-disable{cursor:not-allowed!important;opacity:0.4!important;}" - ); - function downloadSong(songId, progress, complete, error) { - GM_xmlhttpRequest({ - method: "GET", - url: linksVip(songId), - responseType: "blob", - onload: function (source) { - complete(source.response, source.finalUrl.split("filename=")[1]); - }, - onprogress: function (e) { - if (e.total) { - progress(Math.floor((e.loaded * 100) / e.total) + "%"); - } else { - progress(""); - } - }, - onerror: function (e) { - console.error(e); - error(); - }, - }); - } - window.URL = window.URL || window.webkitURL; - function multiDownloads() { - var $smallBtn = $(".fn-dlsong"); - if (!$smallBtn.length) return; - $smallBtn.replaceWith(function () { - var songId = $(this) - .closest("li, .item-song") - .attr("id") - .replace(/(chartitem)?song(rec)?/, ""); - return ( - '' - ); - }); - } - multiDownloads(); - $(document).on("ready", multiDownloads); - $(window).on("load", multiDownloads); -})(jQuery, window, document); diff --git a/scripts/backup/xem-tin-nhan-dau-tien b/scripts/backup/xem-tin-nhan-dau-tien deleted file mode 100644 index feb06d08..00000000 --- a/scripts/backup/xem-tin-nhan-dau-tien +++ /dev/null @@ -1,129 +0,0 @@ -#EndRegion -#include<_HttpRequest.au3> -#include -#include -#include -#include -#include -#include -#include -#include -#Region ### START Koda GUI section ### Form= -$Form1 = GUICreate("Tool Check Tin Nhắn FaceBook", 301, 156, 192, 124) -$Button1 = GUICtrlCreateButton("Check Nào !!", 72, 112, 139, 41) -GUICtrlSetFont(-1, 12, 800, 0, "MS Sans Serif") -$Label1 = GUICtrlCreateLabel("Tài Khoản : ", 0, 8, 86, 20) -GUICtrlSetFont(-1, 10, 800, 0, "MS Sans Serif") -$Input1 = GUICtrlCreateInput("", 112, 8, 185, 24) -GUICtrlSetFont(-1, 10, 800, 0, "MS Sans Serif") -$Label2 = GUICtrlCreateLabel("Mật Khẩu : ", 0, 40, 79, 20) -GUICtrlSetFont(-1, 10, 800, 0, "MS Sans Serif") -$Input2 = GUICtrlCreateInput("", 112, 40, 185, 24) -GUICtrlSetFont(-1, 10, 800, 0, "MS Sans Serif") -$Label3 = GUICtrlCreateLabel("Id Cần Check : ", 0, 72, 107, 20) -GUICtrlSetFont(-1, 10, 800, 0, "MS Sans Serif") -$Input3 = GUICtrlCreateInput("", 112, 72, 185, 24) -GUICtrlSetFont(-1, 10, 800, 0, "MS Sans Serif") -GUISetState(@SW_SHOW) -#EndRegion ### END Koda GUI section ### - -While 1 - $nMsg = GUIGetMsg() - Switch $nMsg - Case $GUI_EVENT_CLOSE - Exit - Case $Button1 - _main() - EndSwitch -WEnd - - -Func _main() - if GUICtrlRead($Input1) = '' Then - MsgBox(0,0,'Chưa Nhập Tài Khoản') - Return - elseif GUICtrlRead($Input2) = '' Then - MsgBox(0,0,'Chưa Nhập Mật Khẩu') - Return - elseif GUICtrlRead($Input3) = '' Then - MsgBox(0,0,'Chưa Nhập ID') - Return - Endif - MsgBox(0,0,'Quá Trình Check Đang Diễn Ra , Vui Lòng Bấm "OK" Và Đợi') -$tk = GUICtrlRead($Input1) -$mk = GUICtrlRead($Input2) -$id = GUICtrlRead($Input3) -_main1($tk,$mk,$id) -Endfunc - - -Func _main1($tk,$mk,$id) -$idd = $id -$a = fblogin($tk,$mk) -$cc = _HttpRequest(2, 'https://m.facebook.com/profile.php', "", $a, '', 'Connection: keep-alive') -$fb = StringRegExp($cc,'name="fb_dtsg" value="(.*?)"',3) -$idpr = StringRegExp($cc,'name="target" value="(.*?)"',3) -$i = 1 -if @error Then - MsgBox(0,0,'ERROR') - Exit -Endif -$post = '__user='&$idpr[0]&'&__a=1&__dyn=7AgNeS-aF398jgDxyIGzGomzEdpbGAdy8VdLFwgoqwWhE98nwgUaqG2yaBxebkwy6UnGi7VXDG4XzErDAxaFQ3ucDBxe6ohyUCqu58nUszaxbxm1tyrhVo9ohxGbwYUmC-UjDQ6ErKu7EgwLxqawDDgswVwjpUhCK6pESfyaBy8OcxO12zVolyoK7UyUhUKcyU4eQEx1DzXG&__af=jw&__req=u&__be=-1&__pc=EXP3%3Aholdout_pkg&__rev=3211951&fb_dtsg='&$fb[0]&'&jazoest=265817089856881797477105114586581701188790886782485370&queries=%7B%22o0%22%3A%7B%22doc_id%22%3A%221927845863895817%22%2C%22query_params%22%3A%7B%22id%22%3A%22'&$idd&'%22%2C%22message_limit%22%3A'&$i&'%2C%22load_messages%22%3A1%2C%22load_read_receipts%22%3Atrue%2C%22before%22%3A'&_TimeStampUNIX_ms()&'%7D%7D%7D' -$cc = _HttpRequest(2, 'https://www.facebook.com/api/graphqlbatch/', $post, $a, '', 'Connection: keep-alive') - -if StringInStr($cc,'"successful_results": 0') then - MsgBox(0,0,'Error') - Exit -Endif - -if Not StringInStr($cc,'messages_count') Then - MsgBox(0,0,'Error') - Exit -Endif - -$mess = _StringBetween($cc,'"messages_count":',',',3) - -if $mess[0] < 20 Then - MsgBox(0,0,'Chưa Nhắn Tới 20 Tin Nữa Check Làm Cm Gì 3') -Return -Endif - -$post1 = '__user='&$idpr[0]&'&__a=1&__dyn=7AgNeS-aF398jgDxyIGzGomzEdpbGAdy8VdLFwgoqwWhE98nwgUaqG2yaBxebkwy6UnGi7VXDG4XzErDAxaFQ3ucDBxe6ohyUCqu58nUszaxbxm1tyrhVo9ohxGbwYUmC-UjDQ6ErKu7EgwLxqawDDgswVwjpUhCK6pESfyaBy8OcxO12zVolyoK7UyUhUKcyU4eQEx1DzXG&__af=jw&__req=u&__be=-1&__pc=EXP3%3Aholdout_pkg&__rev=3211951&fb_dtsg='&$fb[0]&'&jazoest=265817089856881797477105114586581701188790886782485370&queries=%7B%22o0%22%3A%7B%22doc_id%22%3A%221927845863895817%22%2C%22query_params%22%3A%7B%22id%22%3A%22'&$idd&'%22%2C%22message_limit%22%3A'&$mess[0]&'%2C%22load_messages%22%3A1%2C%22load_read_receipts%22%3Atrue%2C%22before%22%3A'&_TimeStampUNIX_ms()&'%7D%7D%7D' -$cc1 = _HttpRequest(2, 'https://www.facebook.com/api/graphqlbatch/', $post1, $a, '', 'Connection: keep-alive') -$c1 = _StringBetween($cc1,'{"id":"','ge",',3) - -$path = @ScriptDir & '\'&$idd&'.html' - -For $a = 1 to 20 -$id = _StringBetween($c1[$a],'','",',3) -$tin1 = StringRegExp($c1[$a],'"snippet":"(.*?)"',3) -if IsArray($id) And IsArray($tin1) Then - FileWriteLine($path,$id[0] &"|"&_HTMLDecode($tin1[0]) & "
") -ENdif -Next -MsgBox(0,0,'OK') -ShellExecute(@ScriptDir & '\'&$idd&'.html') -Exit -ENdfunc - - -Func _TimeStampUNIX_ms($iYear = @YEAR, $iMonth = @MON, $iDay = @MDAY, $iHour = @HOUR, $iMin = @MIN, $iSec = @SEC) - Local $stSystemTime = DllStructCreate('ushort;ushort;ushort;ushort;ushort;ushort;ushort;ushort') - DllCall('kernel32.dll', 'none', 'GetSystemTime', 'ptr', DllStructGetPtr($stSystemTime)) - $iMSec = StringFormat('%03d', DllStructGetData($stSystemTime, 8)) - Local $nYear = $iYear - ($iMonth < 3 ? 1 : 0) - Return ((Int(Int($nYear / 100) / 4) - Int($nYear / 100) + $iDay + Int(365.25 * ($nYear + 4716)) + Int(30.6 * (($iMonth < 3 ? $iMonth + 12 : $iMonth) + 1)) - 2442110) * 86400 + ($iHour * 3600 + $iMin * 60 + $iSec)) * ($iMSec ? 1000 : 1) + $iMSec - EndFunc - - -Func fblogin($tk,$mk) -$kq1 = _HttpRequest(2, 'https://m.facebook.com/login.php?refsrc=https%3A%2F%2Fm.facebook.com%2F&lwv=101&login_try_number=1&ref=dbl', "", '', '', 'Connection: keep-alive') -$lsd = StringRegExp($kq1,'name="lsd" value="(.*?)"',3) -$mts = StringRegExp($kq1,'name="m_ts" value="(.*?)"',3) -$li = StringRegExp($kq1,'name="li" value="(.*?)"',3) -$post = 'lsd='&$lsd[0]&'&m_ts='&$mts[0]&'&li='&$li[0]&'&try_number=0&unrecognized_tries=0&email='&$tk&'&pass='&$mk&'&login=%C4%90%C4%83ng+nh%E1%BA%ADp' -$kq1 = _HttpRequest(1, 'https://m.facebook.com/login.php?refsrc=https%3A%2F%2Fm.facebook.com%2F&lwv=101&login_try_number=1&ref=dbl', $post, '', '', 'Connection: keep-alive') -$cookie = _GetCookie($kq1) -Return $cookie - -Endfunc \ No newline at end of file diff --git a/scripts/backup/yt-playlist-maker.js b/scripts/backup/yt-playlist-maker.js deleted file mode 100644 index 3cb1ec05..00000000 --- a/scripts/backup/yt-playlist-maker.js +++ /dev/null @@ -1,51 +0,0 @@ -// https://gist.github.com/J2TEAM/d8380866bb28dfb8a7f1ab72059658b0 - -/* Developed by Juno_okyo */ -(function (e, b) { - function f(a, b) { - var d = fetch, - e = - "sej=" + - encodeURIComponent( - JSON.stringify({ - playlistEditEndpoint: { - playlistId: a, - actions: [ - { - addedVideoId: b, - action: "ACTION_ADD_VIDEO", - }, - ], - }, - clickTrackingParams: "juno_okyo_j2team_community", - commandMetadata: { - webCommandMetadata: { - url: "/service_ajax", - sendPost: !0, - }, - }, - }) - ) + - "&session_token=", - f = encodeURIComponent; - var c = /"XSRF_TOKEN":"([^"]+)"/.exec(document.head.innerHTML); - c = null !== c ? c[1] : !1; - return d("https://www.youtube.com/service_ajax?name=playlistEditEndpoint", { - method: "POST", - credentials: "same-origin", - headers: { - "content-type": "application/x-www-form-urlencoded", - }, - body: e + f(c), - }); - } - - function d() { - var a = b.pop(); - a = new URL(a).searchParams.get("v"); - f(e, a).then(function (a) { - 0 < b.length && setTimeout(d, 500); - }); - } - d(); -})("YOUR_PLAYLIST_ID", "YOUR_VIDEO_URLS_IN_ARRAY"); From 7f6074bb89a1e1a31382b783189812627159f5da Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Wed, 24 Jul 2024 09:57:57 +0700 Subject: [PATCH 13/47] show unique users --- scripts/_ufs_statistic.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/_ufs_statistic.js b/scripts/_ufs_statistic.js index 33f703f0..91f46cfe 100644 --- a/scripts/_ufs_statistic.js +++ b/scripts/_ufs_statistic.js @@ -398,7 +398,8 @@ async function onDocumentEnd() { h1.innerHTML = `${allLogs.length} logs (~${_logsPerHour} logs/hour)
${eventNameCount.size} unique events

${scriptUsedTotalCount} scripts used (~${_scriptsPerHour} scripts/hour)
- ${scriptsUsed.size} unique scripts`; + ${scriptsUsed.size} unique scripts

+ ${logByUid.size} unique users`; // ======================== Append Charts ======================== container.prepend( From c094f381d19983baeeac5c59831d098becd4408c Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Wed, 24 Jul 2024 11:38:58 +0700 Subject: [PATCH 14/47] clearthis.page --- working_note.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/working_note.md b/working_note.md index fe9dae71..fd20bde1 100644 --- a/working_note.md +++ b/working_note.md @@ -2,6 +2,8 @@ ## 01/07/2024 - ? +- [ ] + - [ ] good feature - global video speed - [ ] good feature - medium unlock From 534e89ba1be68ad4b8780375b6ba99ee651192aa Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Wed, 24 Jul 2024 14:04:08 +0700 Subject: [PATCH 15/47] sanitizeName --- scripts/content-scripts/ufs_global.js | 39 ++++++++++++++++++++++++--- scripts/tiktok_batchDownload.js | 22 +++------------ 2 files changed, 39 insertions(+), 22 deletions(-) diff --git a/scripts/content-scripts/ufs_global.js b/scripts/content-scripts/ufs_global.js index 65436499..c6f6b700 100644 --- a/scripts/content-scripts/ufs_global.js +++ b/scripts/content-scripts/ufs_global.js @@ -32,6 +32,7 @@ export const UfsGlobal = { getWatchingVideoSrc, }, Utils: { + sanitizeName, debounce, makeUrlValid, getNumberFormatter, @@ -517,6 +518,41 @@ function getWatchingVideoSrc() { } } } +// #endregion + +// #region Utils + +// https://github.com/parshap/node-sanitize-filename/blob/master/index.js +// https://github.com/Dinoosauro/tiktok-to-ytdlp/blob/main/script.js +function sanitizeName(name, modifyIfPosible = true) { + if (typeof name !== "string") { + throw new Error("Input must be string"); + } + const replacement = ""; + const illegalRe = /[\/\?<>\\:\*\|"]/g; + const controlRe = /[\x00-\x1f\x80-\x9f]/g; + const reservedRe = /^\.+$/; + const windowsReservedRe = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i; + const windowsTrailingRe = /[\. ]+$/; + if (modifyIfPosible) { + name = name + .replaceAll("<", "‹") + .replaceAll(">", "›") + .replaceAll(":", "∶") + .replaceAll('"', "″") + .replaceAll("/", "∕") + .replaceAll("\\", "∖") + .replaceAll("|", "¦") + .replaceAll("?", "¿"); + } + const sanitized = name + .replace(illegalRe, replacement) + .replace(controlRe, replacement) + .replace(reservedRe, replacement) + .replace(windowsReservedRe, replacement) + .replace(windowsTrailingRe, replacement); + return sanitized; // TODO truncates to length of 255 +} function debounce(func, delay) { let timeout; return (...args) => { @@ -524,9 +560,6 @@ function debounce(func, delay) { timeout = setTimeout(() => func.apply(this, args), delay); }; } -// #endregion - -// #region Utils const numberFormatCached = {}; /** diff --git a/scripts/tiktok_batchDownload.js b/scripts/tiktok_batchDownload.js index 194139e3..3dbbbb59 100644 --- a/scripts/tiktok_batchDownload.js +++ b/scripts/tiktok_batchDownload.js @@ -224,7 +224,7 @@ export default { return checkbox; } - const sleep = UfsGlobal.Utils.sleep; + const { sleep, sanitizeName } = UfsGlobal.Utils; async function getLinkVideos(videoUrls) { if (!videoUrls.length) return; @@ -263,14 +263,14 @@ export default { // conflictAction: "overwrite", // filename: // "tiktok/" + - // sanitizeFileName( + // sanitizeName( // CACHED.videoById.get(id)?.name || id // ) + // ".mp4", // }); links.push({ url: link, - name: sanitizeFileName(cached?.name || id) + ".mp4", + name: sanitizeName(cached?.name || id) + ".mp4", }); } else { progressDiv.innerText = `[LỖI] Không thể tải video ${url}.`; @@ -337,19 +337,3 @@ export default { }, }, }; - -function sanitizeFileName(fileName) { - // Định nghĩa các ký tự hợp lệ (chữ cái, số, dấu gạch dưới, dấu gạch ngang và dấu chấm) - const validChars = /^[a-zA-Z0-9_\-.]+$/; - - // Lược bỏ các ký tự không hợp lệ - let sanitizedFileName = ""; - for (let char of fileName) { - if (validChars.test(char)) { - sanitizedFileName += char; - } - } - - // Trả về tên tệp đã lược bỏ các ký tự không hợp lệ - return sanitizedFileName; -} From bd280d5beea3191c203fdfa0cd5e4de32ce72f9e Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Wed, 24 Jul 2024 14:55:17 +0700 Subject: [PATCH 16/47] fix --- scripts/simpleAllowCopy.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/simpleAllowCopy.js b/scripts/simpleAllowCopy.js index 2fdbe405..4ae630b2 100644 --- a/scripts/simpleAllowCopy.js +++ b/scripts/simpleAllowCopy.js @@ -1,4 +1,3 @@ -import { UfsGlobal } from "./content-scripts/ufs_global.js"; import { BADGES } from "./helpers/badge.js"; export default { @@ -24,6 +23,9 @@ export default { `, }, badges: [BADGES.hot], + changeLogs: { + "2024-07-24": "hotfix", + }, contentScript: { onDocumentStart_: function () { @@ -95,7 +97,7 @@ const unlocker = (() => { const addCss = () => { try { const doc = window.document; - removeCss(wnd); + removeCss(); const style = doc.createElement("STYLE"); style.id = CSS_ELEM_ID; style.innerHTML = From 813e55329efca48d84b701ba8293e507ca87ca9e Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Wed, 24 Jul 2024 17:05:39 +0700 Subject: [PATCH 17/47] minor fix to able to run demo in github pages --- popup/index.js | 4 +++- scripts/smoothScroll.js | 41 +++++++++++++++++++++-------------------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/popup/index.js b/popup/index.js index a3bf4aa2..37f9c9fe 100644 --- a/popup/index.js +++ b/popup/index.js @@ -534,6 +534,8 @@ const updateTargetTab = UfsGlobal.Utils.debounce(async () => { }, 500); async function initOpenInNewTab() { + if (!chrome?.tabs) return; + let currentTab = await chrome.tabs.getCurrent(); isInNewTab = currentTab != null; @@ -1010,7 +1012,7 @@ window.addEventListener("scroll", onScrollEnd); initScrollToTop(); createTabs().then(restoreScroll); - chrome.windows.onFocusChanged.addListener((windowId) => { + chrome?.windows?.onFocusChanged?.addListener?.((windowId) => { setTimeout(async () => { let currentTab = await getCurrentTab(); }, 200); diff --git a/scripts/smoothScroll.js b/scripts/smoothScroll.js index f95f97ec..712948c1 100644 --- a/scripts/smoothScroll.js +++ b/scripts/smoothScroll.js @@ -182,12 +182,13 @@ export function enableSmoothScroll() { /*********************************************** * SETTINGS ***********************************************/ - chrome.storage.sync.get(defaultOptions, function (syncedOptions) { - options = syncedOptions; - // it seems that sometimes settings come late - // and we need to test again for excluded pages - initTest(); - }); + // chrome.storage.sync.get(defaultOptions, function (syncedOptions) { + // options = syncedOptions; + // // it seems that sometimes settings come late + // // and we need to test again for excluded pages + // initTest(); + // }); + initTest(); /*********************************************** * INITIALIZE ***********************************************/ @@ -735,11 +736,11 @@ export function enableSmoothScroll() { if (deltaY < 50) tp = true; clearTimeout(deltaBufferTimer); deltaBufferTimer = setTimeout(function () { - chrome.storage.local.set({ + chrome?.storage?.local?.set?.({ deltaBuffer: deltaBuffer, }); if (!tp) - chrome.storage.local.set({ + chrome?.storage?.local?.set?.({ lastDiscreetWheel: dateNow(), }); }, 1000); @@ -755,7 +756,7 @@ export function enableSmoothScroll() { isDivisible(deltaBuffer[2], divisor) ); } - chrome.storage.local.get("deltaBuffer", function (stored) { + chrome?.storage?.local?.get?.("deltaBuffer", function (stored) { if (stored.deltaBuffer) { deltaBuffer = stored.deltaBuffer; } @@ -838,21 +839,21 @@ export function enableSmoothScroll() { // we check the OS for default middle mouse behavior only! let isLinux = navigator.platform.indexOf("Linux") != -1; // get global settings - chrome.storage.sync.get(defaultOptions, function (syncedOptions) { - options = syncedOptions; - // leave time for the main script to check excluded pages - setTimeout(function () { - // if we shouldn't run, stop listening to events - if (isExcluded && !options.middleMouse) { - cleanup(); - } - }, 10); - }); + // chrome.storage.sync.get(defaultOptions, function (syncedOptions) { + // options = syncedOptions; + // // leave time for the main script to check excluded pages + // setTimeout(function () { + // // if we shouldn't run, stop listening to events + // if (isExcluded && !options.middleMouse) { + // cleanup(); + // } + // }, 10); + // }); /** * Initializes the image at the reference point. */ function init() { - let url = chrome.runtime.getURL("/scripts/smoothscroll_cursor.png"); + let url = chrome?.runtime?.getURL?.("/scripts/smoothscroll_cursor.png"); img.style.background = "url(" + url + ") no-repeat"; img.style.position = "fixed"; img.style.zIndex = "999999999"; From ca7866b8c72344e2460d45dce1a01add5a4438b5 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Wed, 24 Jul 2024 17:13:58 +0700 Subject: [PATCH 18/47] Jekyll config --- _config.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 _config.yml diff --git a/_config.yml b/_config.yml new file mode 100644 index 00000000..3373203a --- /dev/null +++ b/_config.yml @@ -0,0 +1,7 @@ +# If you’re using Jekyll to build your site and would like to include a file or directory that +# Jekyll excludes by default you can do this by specifying the path to the file/folder +# inside the include variable in your site’s _config.yml file: + +# include all /scripts/_*.js file +include: + - /scripts/_*.js From 43974d4b52824c06a8746f0709e095fc2ee920e3 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Wed, 24 Jul 2024 17:16:08 +0700 Subject: [PATCH 19/47] config --- _config.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/_config.yml b/_config.yml index 3373203a..101e4a7b 100644 --- a/_config.yml +++ b/_config.yml @@ -3,5 +3,4 @@ # inside the include variable in your site’s _config.yml file: # include all /scripts/_*.js file -include: - - /scripts/_*.js +include: ["/scripts/_*.js", "/scripts/_*.css"] From 1cc3889fa83953491281daac1de4542df01bee08 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Wed, 24 Jul 2024 17:23:47 +0700 Subject: [PATCH 20/47] change file name --- _config.yml | 6 ------ md/CONTRIBUTE.md | 2 +- popup/helpers/storageScripts.js | 2 +- popup/index.js | 2 +- popup/tabs.js | 6 +++--- scripts/{_allScripts.js => @allScripts.js} | 2 +- scripts/{_index.js => @index.js} | 4 ++-- scripts/{_test.js => @test.js} | 0 ...{_ufs_statistic.css => @ufs_statistic.css} | 0 .../{_ufs_statistic.js => @ufs_statistic.js} | 2 +- .../background-scripts/background_script.js | 2 +- scripts/helpers/utils.js | 21 ++++++++++++------- 12 files changed, 24 insertions(+), 25 deletions(-) delete mode 100644 _config.yml rename scripts/{_allScripts.js => @allScripts.js} (78%) rename scripts/{_index.js => @index.js} (99%) rename scripts/{_test.js => @test.js} (100%) rename scripts/{_ufs_statistic.css => @ufs_statistic.css} (100%) rename scripts/{_ufs_statistic.js => @ufs_statistic.js} (99%) diff --git a/_config.yml b/_config.yml deleted file mode 100644 index 101e4a7b..00000000 --- a/_config.yml +++ /dev/null @@ -1,6 +0,0 @@ -# If you’re using Jekyll to build your site and would like to include a file or directory that -# Jekyll excludes by default you can do this by specifying the path to the file/folder -# inside the include variable in your site’s _config.yml file: - -# include all /scripts/_*.js file -include: ["/scripts/_*.js", "/scripts/_*.css"] diff --git a/md/CONTRIBUTE.md b/md/CONTRIBUTE.md index 4ef37910..a6a5637f 100644 --- a/md/CONTRIBUTE.md +++ b/md/CONTRIBUTE.md @@ -20,7 +20,7 @@ Nếu bạn có 1 `script hay`, hoặc `bookmarklets hay`, muốn `thêm vào ex 3. Ghi nội dung script: - Đọc cấu trúc code và comment trong file [templates/full.js](/templates/full.js) để biết thêm chi tiết. -4. Import script của bạn trong file [/scripts/_index.js](/scripts/_index.js) +4. Import script của bạn trong file [/scripts/@index.js](/scripts/@index.js) 5. Ghi tên script của bạn trong biến `tabs` trong file [/popup/tabs.js](/popup/tabs.js) diff --git a/popup/helpers/storageScripts.js b/popup/helpers/storageScripts.js index 4e29f36b..b4c93cbb 100644 --- a/popup/helpers/storageScripts.js +++ b/popup/helpers/storageScripts.js @@ -1,4 +1,4 @@ -import allScripts from "../../scripts/_allScripts.js"; +import allScripts from "../../scripts/@allScripts.js"; const createScriptsSaver = (key, addToHead = true) => { const getIds = () => diff --git a/popup/index.js b/popup/index.js index 37f9c9fe..d65de8d5 100644 --- a/popup/index.js +++ b/popup/index.js @@ -40,7 +40,7 @@ import { viewScriptSource, } from "./helpers/utils.js"; import { checkPass } from "../scripts/auto_lockWebsite.js"; -import allScripts from "../scripts/_allScripts.js"; +import allScripts from "../scripts/@allScripts.js"; // import _ from "../md/exportScriptsToMd.js"; const settingsBtn = document.querySelector(".settings"); diff --git a/popup/tabs.js b/popup/tabs.js index 6504f380..7fe04b35 100644 --- a/popup/tabs.js +++ b/popup/tabs.js @@ -1,4 +1,4 @@ -import s from "../scripts/_allScripts.js"; +import s from "../scripts/@allScripts.js"; import { Recommend as R } from "./recommend.js"; import { canAutoRun } from "./helpers/utils.js"; import { CATEGORY } from "./helpers/category.js"; @@ -33,8 +33,8 @@ const tabs = [ { ...CATEGORY.search, scripts: [ - // s._test, - // s._ufs_statistic, + // s.test, + // s.ufs_statistic, R.theresanaiforthat, R.timeis, R.googleTrending, diff --git a/scripts/_allScripts.js b/scripts/@allScripts.js similarity index 78% rename from scripts/_allScripts.js rename to scripts/@allScripts.js index e119aa7c..1e7f3790 100644 --- a/scripts/_allScripts.js +++ b/scripts/@allScripts.js @@ -1,4 +1,4 @@ -import * as allScripts from "./_index.js"; +import * as allScripts from "./@index.js"; // inject id to all scripts Object.entries(allScripts).forEach(([variableName, script]) => { diff --git a/scripts/_index.js b/scripts/@index.js similarity index 99% rename from scripts/_index.js rename to scripts/@index.js index 1b11da8f..f5c71a36 100644 --- a/scripts/_index.js +++ b/scripts/@index.js @@ -1,6 +1,6 @@ // https://stackoverflow.com/a/59002206 -export { default as _test } from "./_test.js"; +export { default as test } from "./@test.js"; export { default as fb_toggleLight } from "./fb_toggleLight.js"; export { default as fb_getTokenFacebook } from "./fb_getTokenFacebook.js"; export { default as fb_getUid } from "./fb_getUid.js"; @@ -137,7 +137,7 @@ export { default as magnify_image } from "./magnify_image.js"; export { default as auto_redirectLargestImageSrc } from "./auto_redirectLargestImageSrc.js"; export { default as textToQrCode } from "./textToQrCode.js"; export { default as insta_anonymousStoryViewer } from "./insta_anonymousStoryViewer.js"; -export { default as _ufs_statistic } from "./_ufs_statistic.js"; +export { default as ufs_statistic } from "./@ufs_statistic.js"; export { default as pip_fullWebsite } from "./pip_fullWebsite.js"; export { default as similarWeb_bypassLimit } from "./similarWeb_bypassLimit.js"; export { default as pip_canvas } from "./pip_canvas.js"; diff --git a/scripts/_test.js b/scripts/@test.js similarity index 100% rename from scripts/_test.js rename to scripts/@test.js diff --git a/scripts/_ufs_statistic.css b/scripts/@ufs_statistic.css similarity index 100% rename from scripts/_ufs_statistic.css rename to scripts/@ufs_statistic.css diff --git a/scripts/_ufs_statistic.js b/scripts/@ufs_statistic.js similarity index 99% rename from scripts/_ufs_statistic.js rename to scripts/@ufs_statistic.js index 91f46cfe..1daae1e3 100644 --- a/scripts/_ufs_statistic.js +++ b/scripts/@ufs_statistic.js @@ -49,7 +49,7 @@ async function onDocumentEnd() { const container = document.createElement("div"); if (hasLog) { - UfsGlobal.Extension.getURL("/scripts/_ufs_statistic.css").then( + UfsGlobal.Extension.getURL("/scripts/@ufs_statistic.css").then( UfsGlobal.DOM.injectCssFile ); diff --git a/scripts/background-scripts/background_script.js b/scripts/background-scripts/background_script.js index 5710e403..ff216fa4 100644 --- a/scripts/background-scripts/background_script.js +++ b/scripts/background-scripts/background_script.js @@ -1,5 +1,5 @@ import * as utils from "../helpers/utils.js"; -import allScripts from "../_allScripts.js"; +import allScripts from "../@allScripts.js"; import { UfsGlobal } from "../content-scripts/ufs_global.js"; // import "../content-scripts/ufs_global.js"; // https://stackoverflow.com/a/62806068/23648002 // importScripts() diff --git a/scripts/helpers/utils.js b/scripts/helpers/utils.js index 883de28f..97459bd8 100644 --- a/scripts/helpers/utils.js +++ b/scripts/helpers/utils.js @@ -255,7 +255,7 @@ export const getAllTabs = async () => { }; export function getPopupURL() { - return chrome.runtime.getURL("/popup/popup.html"); + return chrome?.runtime?.getURL?.("/popup/popup.html"); } export const getLastFocusedWindowIds = () => { @@ -288,14 +288,19 @@ export const getLastFocusedTab = async () => { }; export const getCurrentTab = async () => { - // case normal popup - let tabs = await chrome.tabs.query({ - active: true, - currentWindow: true, - }); - if (tabs?.[0]?.url !== getPopupURL()) return tabs[0]; + try { + // case normal popup + let tabs = await chrome?.tabs?.query?.({ + active: true, + currentWindow: true, + }); + if (tabs?.[0]?.url !== getPopupURL()) return tabs[0]; - return await getLastFocusedTab(); + return await getLastFocusedTab(); + } catch (err) { + console.log(err); + return null; + } }; export const getCurrentTabId = async () => { From ee20d46508f4c3de671071ecc8ac4f4c0d081526 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Wed, 24 Jul 2024 17:44:02 +0700 Subject: [PATCH 21/47] fix relative img path --- popup/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/popup/index.js b/popup/index.js index d65de8d5..d1af7056 100644 --- a/popup/index.js +++ b/popup/index.js @@ -332,8 +332,10 @@ function createScriptButton(script, isFavorite = false) { tooltip.classList.add("tooltiptext"); tooltip.innerHTML = t(script.description); - if (script.description?.img) { - tooltip.innerHTML += ``; + let img = script.description?.img; + if (img) { + if (!img.startsWith("http")) img = ".." + img; // /script/abc.png => ../script/abc.png + tooltip.innerHTML += ``; } if (script.description?.video) { let video = document.createElement("video"); From 3212d695593794563654dcf3b3ad659da10c55ad Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Wed, 24 Jul 2024 17:45:13 +0700 Subject: [PATCH 22/47] fix relative img path --- popup/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/popup/index.js b/popup/index.js index d1af7056..c20917c3 100644 --- a/popup/index.js +++ b/popup/index.js @@ -334,7 +334,7 @@ function createScriptButton(script, isFavorite = false) { let img = script.description?.img; if (img) { - if (!img.startsWith("http")) img = ".." + img; // /script/abc.png => ../script/abc.png + if (img.startsWith("/scripts")) img = ".." + img; // /scripts/abc.png => ../scripts/abc.png tooltip.innerHTML += ``; } if (script.description?.video) { From d6c64ec22526d1a354a2b578a77a220a5a6630c6 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Wed, 24 Jul 2024 22:15:12 +0700 Subject: [PATCH 23/47] preview --- index.html | 12 +------ popup/index.js | 34 +++++++++++++++++++ popup/popup.html | 3 +- scripts/@index.js | 4 +-- scripts/{@test.js => test.js} | 0 .../{@ufs_statistic.css => ufs_statistic.css} | 0 .../{@ufs_statistic.js => ufs_statistic.js} | 8 +++-- 7 files changed, 44 insertions(+), 17 deletions(-) rename scripts/{@test.js => test.js} (100%) rename scripts/{@ufs_statistic.css => ufs_statistic.css} (100%) rename scripts/{@ufs_statistic.js => ufs_statistic.js} (98%) diff --git a/index.html b/index.html index 3ddf7873..7bc65451 100644 --- a/index.html +++ b/index.html @@ -8,18 +8,8 @@ - google.com - - - - - diff --git a/popup/index.js b/popup/index.js index c20917c3..365b633b 100644 --- a/popup/index.js +++ b/popup/index.js @@ -176,6 +176,8 @@ function createScriptButton(script, isFavorite = false) { const checkmark = document.createElement("button"); checkmark.className = "checkmark"; checkmark.onclick = async (e) => { + if (checkIsPreview(script)) return; + let oldVal = await isActiveScript(script.id); let newVal = !oldVal; @@ -447,7 +449,39 @@ function showError(e) { }); } +function checkIsPreview(script) { + if ( + location.hostname === "hoangtran0410.github.io" || + location.hostname === "127.0.0.1" + ) { + trackEvent("CLICK_PREVIEW_" + script.id); + Swal.fire({ + icon: "info", + title: t({ vi: "Đây là bản xem trước", en: "This is a preview" }), + text: t({ + vi: "Vui lòng tải và cài đặt tiện ích Useful-script để sử dung chức năng này nhé", + en: "Please install Useful-script extension to use these features", + }), + confirmButtonText: t({ vi: "Xem hướng dẫn", en: "View tutorial" }), + cancelButtonText: t({ vi: "Đã hiểu", en: "Ok" }), + showCancelButton: true, + reverseButtons: true, + }).then((res) => { + if (res.isConfirmed) { + window.open( + "https://www.facebook.com/groups/1154059318582088/posts/1453443235310360/", + "_blank" + ); + } + }); + return true; + } + return false; +} + async function runScript(script) { + if (checkIsPreview(script)) return; + let tab = await getCurrentTab(); let willRun = checkBlackWhiteList(script, tab.url); if (willRun) { diff --git a/popup/popup.html b/popup/popup.html index 257820b8..629514c7 100644 --- a/popup/popup.html +++ b/popup/popup.html @@ -22,8 +22,7 @@

- + donate diff --git a/scripts/@index.js b/scripts/@index.js index f5c71a36..36e07c54 100644 --- a/scripts/@index.js +++ b/scripts/@index.js @@ -1,6 +1,6 @@ // https://stackoverflow.com/a/59002206 -export { default as test } from "./@test.js"; +export { default as test } from "./test.js"; export { default as fb_toggleLight } from "./fb_toggleLight.js"; export { default as fb_getTokenFacebook } from "./fb_getTokenFacebook.js"; export { default as fb_getUid } from "./fb_getUid.js"; @@ -137,7 +137,7 @@ export { default as magnify_image } from "./magnify_image.js"; export { default as auto_redirectLargestImageSrc } from "./auto_redirectLargestImageSrc.js"; export { default as textToQrCode } from "./textToQrCode.js"; export { default as insta_anonymousStoryViewer } from "./insta_anonymousStoryViewer.js"; -export { default as ufs_statistic } from "./@ufs_statistic.js"; +export { default as ufs_statistic } from "./ufs_statistic.js"; export { default as pip_fullWebsite } from "./pip_fullWebsite.js"; export { default as similarWeb_bypassLimit } from "./similarWeb_bypassLimit.js"; export { default as pip_canvas } from "./pip_canvas.js"; diff --git a/scripts/@test.js b/scripts/test.js similarity index 100% rename from scripts/@test.js rename to scripts/test.js diff --git a/scripts/@ufs_statistic.css b/scripts/ufs_statistic.css similarity index 100% rename from scripts/@ufs_statistic.css rename to scripts/ufs_statistic.css diff --git a/scripts/@ufs_statistic.js b/scripts/ufs_statistic.js similarity index 98% rename from scripts/@ufs_statistic.js rename to scripts/ufs_statistic.js index 1daae1e3..376594ba 100644 --- a/scripts/@ufs_statistic.js +++ b/scripts/ufs_statistic.js @@ -49,7 +49,7 @@ async function onDocumentEnd() { const container = document.createElement("div"); if (hasLog) { - UfsGlobal.Extension.getURL("/scripts/@ufs_statistic.css").then( + UfsGlobal.Extension.getURL("/scripts/ufs_statistic.css").then( UfsGlobal.DOM.injectCssFile ); @@ -311,6 +311,9 @@ async function onDocumentEnd() { const logByUidSorted = new Map( [...logByUid.entries()].sort((a, b) => b[1] - a[1]) ); + const fbUsers = [...logByUid.entries()].filter(([key, value]) => { + return isFbUid(key); + }); const canvas_uid = document.createElement("canvas"); const ctx4 = canvas_uid.getContext("2d"); @@ -399,7 +402,8 @@ async function onDocumentEnd() { ${eventNameCount.size} unique events

${scriptUsedTotalCount} scripts used (~${_scriptsPerHour} scripts/hour)
${scriptsUsed.size} unique scripts

- ${logByUid.size} unique users`; + ${logByUid.size} unique users
+ ${fbUsers.length} facebook users`; // ======================== Append Charts ======================== container.prepend( From 124b1dcea82c2624c060da5c4f800939577675d1 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Wed, 24 Jul 2024 22:59:21 +0700 Subject: [PATCH 24/47] minor fix --- popup/index.js | 1 + popup/popup.html | 1 + popup/themes/default.less | 1 + 3 files changed, 3 insertions(+) diff --git a/popup/index.js b/popup/index.js index 365b633b..3a4e13b9 100644 --- a/popup/index.js +++ b/popup/index.js @@ -346,6 +346,7 @@ function createScriptButton(script, isFavorite = false) { video.muted = true; video.loop = true; video.style.width = "80vw"; + video.style.maxWidth = "100%"; // TODO why this not working?? button.addEventListener("mouseenter", () => { setTimeout(() => { diff --git a/popup/popup.html b/popup/popup.html index 629514c7..3f79fdac 100644 --- a/popup/popup.html +++ b/popup/popup.html @@ -76,6 +76,7 @@

Modal Title

+

Please wait. Loading all scripts...

diff --git a/popup/themes/default.less b/popup/themes/default.less index 515e855f..32995b3c 100644 --- a/popup/themes/default.less +++ b/popup/themes/default.less @@ -577,6 +577,7 @@ option { display: flex; justify-content: center; align-items: center; + flex-direction: column; z-index: 5; -webkit-backdrop-filter: blur(.4rem); backdrop-filter: blur(.4rem); From d3e68370a2b2531b0082dd33768bba5e375bf0e8 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Thu, 25 Jul 2024 01:42:56 +0700 Subject: [PATCH 25/47] tiktok batch download new - WIP --- scripts/tiktok_batchDownload.js | 170 ++++++++++++++++++++++++++++++-- 1 file changed, 164 insertions(+), 6 deletions(-) diff --git a/scripts/tiktok_batchDownload.js b/scripts/tiktok_batchDownload.js index 3dbbbb59..b8f65c8e 100644 --- a/scripts/tiktok_batchDownload.js +++ b/scripts/tiktok_batchDownload.js @@ -59,9 +59,169 @@ export default { // reference to Cached window.ufs_tiktok_batchDownload = CACHED; + const id = "ufs_tiktok_batchDownload"; + const floatingBtnId = `${id}_floating_btn`; + const containerId = `${id}_container`; + + const ui = document.createElement("div"); + ui.id = id; + (document.body || document.documentElement).appendChild(ui); + + ui.innerHTML = /*html*/ ` + + +
📥
+ +
+ +
+ `; + const floatingBtn = ui.querySelector("#" + floatingBtnId); + const container = ui.querySelector("#" + containerId); + + function toggle(willShow) { + if (!(typeof willShow === "boolean")) { + let isShowing = container.style.display == "flex"; + willShow = !isShowing; + } + container.style.display = willShow ? "flex" : "none"; + return willShow; + } + + function renderData() { + container.innerHTML = /*html*/ ` +
+

Tiktok - Useful Scripts

+

Found ${CACHED.videoById.size} videos

+ + + + + + + + + + + + + + + +
#VideoTitleUserViewLengthDownload
+
`; + + const tbody = container.querySelector("tbody"); + renderTable(tbody); + } + + function renderTable( + tbody, + data = Array.from(CACHED.videoById.values()) + ) { + tbody.innerHTML = data + .map( + (v, i) => /*html*/ ` + +${i + 1} + +
+ + + +

${v.desc}

+ + + + + ${v.author.uniqueId}
+ ${v.author.nickname}
+ ${v.id} + +${UfsGlobal.Utils.moneyFormat(v.stats.playCount)} +${v.video.duration}s + +

+ Video
+ + Avatar +
+ + Music: ${v.music.title} + +

+ +` + ) + .join(""); + } + + container.onclick = (e) => { + if (e.target === container) { + toggle(false); + } + }; + + floatingBtn.onclick = () => { + let isShow = toggle(); + if (isShow) renderData(); + }; + hookFetch({ onAfter: async (url, options, response) => { - if (url.includes("api/post/item_list")) { + if (url.includes("item_list/")) { // clone to new response const res = response.clone(); const json = await res.json(); @@ -70,17 +230,15 @@ export default { if (json?.itemList) { CACHED.list.push(...json.itemList); json.itemList.forEach((_) => { - CACHED.videoById.set(_.video.id, { - url: _.video.playAddr, - name: _.desc, - }); + if (_.video.playAddr) CACHED.videoById.set(_.video.id, _); }); + floatingBtn.innerHTML = `📥 (${CACHED.videoById.size})`; } } }, }); }, - onDocumentIdle: async () => { + _onDocumentIdle: async () => { let checkboxes = []; // Setup DOM From e7b49d5972285fe8054c012787da85bc5c76fa17 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Thu, 25 Jul 2024 03:49:52 +0700 Subject: [PATCH 26/47] bulk download video/audio tiktok --- scripts/auto_redirectLargestImageSrc.js | 20 +-- scripts/content-scripts/ufs_global.js | 28 ++- scripts/tiktok_batchDownload.js | 218 +++++++++++++++++++++--- 3 files changed, 221 insertions(+), 45 deletions(-) diff --git a/scripts/auto_redirectLargestImageSrc.js b/scripts/auto_redirectLargestImageSrc.js index e48a066a..4eb889f4 100644 --- a/scripts/auto_redirectLargestImageSrc.js +++ b/scripts/auto_redirectLargestImageSrc.js @@ -71,7 +71,7 @@ export async function getLargestImageSrc(imgSrc, webUrl) { // bypass redirect imgSrc = UfsGlobal.Utils.makeUrlValid(imgSrc); - let redirectedUrl = await getRedirectedUrl(imgSrc); + let redirectedUrl = await UfsGlobal.Utils.getRedirectedUrl(imgSrc); if (redirectedUrl) { imgSrc = redirectedUrl; } @@ -450,21 +450,3 @@ function testRegex(str, regexs) { } return false; } -async function getRedirectedUrl(url) { - try { - while (true) { - let res = await UfsGlobal.Extension.fetchByPassOrigin(url, { - method: "HEAD", - }); - if (res?.redirected) { - console.log("redirected:", url, "->", res.url); - url = res.url; - } else { - return url; - } - } - } catch (e) { - console.log("ERROR:", e); - return url; - } -} diff --git a/scripts/content-scripts/ufs_global.js b/scripts/content-scripts/ufs_global.js index c6f6b700..94d675dd 100644 --- a/scripts/content-scripts/ufs_global.js +++ b/scripts/content-scripts/ufs_global.js @@ -32,6 +32,7 @@ export const UfsGlobal = { getWatchingVideoSrc, }, Utils: { + getRedirectedUrl, sanitizeName, debounce, makeUrlValid, @@ -522,6 +523,25 @@ function getWatchingVideoSrc() { // #region Utils +async function getRedirectedUrl(url) { + try { + while (true) { + let res = await UfsGlobal.Extension.fetchByPassOrigin(url, { + method: "HEAD", + }); + if (res?.redirected) { + console.log("redirected:", url, "->", res.url); + url = res.url; + } else { + return url; + } + } + } catch (e) { + console.log("ERROR:", e); + return url; + } +} + // https://github.com/parshap/node-sanitize-filename/blob/master/index.js // https://github.com/Dinoosauro/tiktok-to-ytdlp/blob/main/script.js function sanitizeName(name, modifyIfPosible = true) { @@ -745,11 +765,12 @@ async function chooseFolderToDownload(subDirName = "") { return subDir; } async function downloadToFolder(url, fileName, dirHandler, subFolderName = "") { + if (!url) return false; try { - const f = getOriginalWindowFunction("fetch"); - + // const f = getOriginalWindowFunction("fetch"); // try download directly, using fetch blob - const res = await f(url); + console.log(url); + const res = await fetch(url); const blob = await res.blob(); const fileHandler = await dirHandler.getFileHandle(fileName, { create: true, @@ -760,7 +781,6 @@ async function downloadToFolder(url, fileName, dirHandler, subFolderName = "") { return true; } catch (e) { console.error(e); - debugger; // backup download: using extension api await download({ diff --git a/scripts/tiktok_batchDownload.js b/scripts/tiktok_batchDownload.js index b8f65c8e..5f307605 100644 --- a/scripts/tiktok_batchDownload.js +++ b/scripts/tiktok_batchDownload.js @@ -107,6 +107,8 @@ export default { max-height: 90vh; overflow-y: auto; overflow-x: hidden; + display: flex; + flex-direction: column; } .ufs_popup table, .ufs_popup th, .ufs_popup td { border: 1px solid #aaa; @@ -120,6 +122,19 @@ export default { top: -22px; background: #333; } + .ufs_popup input { + padding: 5px; + } + .ufs_popup button { + padding: 5px; + background: #333; + color: #eee; + border: 1px solid #777; + cursor: pointer; + } + .ufs_popup button:hover { + background: #666; + }
📥
@@ -131,6 +146,17 @@ export default { const floatingBtn = ui.querySelector("#" + floatingBtnId); const container = ui.querySelector("#" + containerId); + container.onclick = (e) => { + if (e.target === container) { + toggle(false); + } + }; + + floatingBtn.onclick = () => { + let isShow = toggle(); + if (isShow) renderData(); + }; + function toggle(willShow) { if (!(typeof willShow === "boolean")) { let isShowing = container.style.display == "flex"; @@ -146,15 +172,23 @@ export default {

Tiktok - Useful Scripts

Found ${CACHED.videoById.size} videos

+
+ + + + + +
+ - + - - - + + + @@ -164,18 +198,153 @@ export default { `; const tbody = container.querySelector("tbody"); - renderTable(tbody); + + // render initial data + let data = Array.from(CACHED.videoById.values()).map((_, index) => ({ + ..._, + index, + })); + renderTable(tbody, data); + + // btn + const clearBtn = container.querySelector("button#clear"); + clearBtn.addEventListener("click", () => { + if (confirm("Are you sure want to clear all data?")) { + CACHED.videoById.clear(); + renderData(); + floatingBtn.innerText = "📥"; + } + }); + const downVideoBtn = container.querySelector("button#video"); + downVideoBtn.addEventListener("click", () => { + download( + data.map((_, i) => { + const urlList = + _.video.bitrateInfo.find((b) => b.Bitrate === _.video.bitrate) + ?.PlayAddr?.UrlList || []; + + const bestUrl = urlList[urlList.length - 1]; + + return { + url: bestUrl || _.video.playAddr, + filename: + i + + "_" + + UfsGlobal.Utils.sanitizeName(_.desc.substr(0, 50)) + + ".mp4", + }; + }), + (i, total) => { + downVideoBtn.textContent = `🎬 Download video (${i}/${total})`; + } + ); + }); + const downAudioBtn = container.querySelector("button#audio"); + downAudioBtn.addEventListener("click", () => { + const uniqueMusic = new Map(); + for (const item of data) { + if (!uniqueMusic.has(item.music.id)) + uniqueMusic.set(item.music.id, item); + } + download( + Array.from(uniqueMusic.values()).map((_, i) => ({ + url: _.music.playUrl, + filename: + i + + "_" + + UfsGlobal.Utils.sanitizeName( + _.music.title.substr(0, 50) || "audio" + ) + + ".mp3", + })), + (i, total) => { + downAudioBtn.textContent = `🎧 Download audio (${i}/${total})`; + } + ); + }); + const downJsonBtn = container.querySelector("button#json"); + downJsonBtn.addEventListener("click", () => { + UfsGlobal.Utils.downloadData( + JSON.stringify(data, null, 4), + "tiktok.json" + ); + }); + + // search + const searchInp = container.querySelector("#search"); + searchInp.addEventListener("input", (event) => { + const value = event.target.value; + let trs = tbody.querySelectorAll("tr"); + for (const tr of trs) { + const tds = tr.querySelectorAll("td"); + let found = false; + for (const td of tds) { + if (td.textContent.toLowerCase().includes(value)) { + found = true; + break; + } + } + tr.style.display = found ? "" : "none"; + } + }); + + // sorting + const sorting = {}; + const ths = container.querySelectorAll("th"); + for (const th of ths) { + const sort = th.getAttribute("data-sort"); + // if (!sort) return; + th.style.cursor = "pointer"; + th.title = "Sort"; + + th.addEventListener("click", () => { + sorting[sort] = sorting[sort] == 1 ? -1 : 1; + switch (sort) { + case "index": + data = data.sort((a, b) => + sorting[sort] == -1 ? a.index - b.index : b.index - a.index + ); + break; + case "title": + data = data.sort((a, b) => + sorting[sort] == -1 + ? a.desc.localeCompare(b.desc) + : b.desc.localeCompare(a.desc) + ); + break; + case "user": + data = data.sort((a, b) => + sorting[sort] == -1 + ? a.author.uniqueId.localeCompare(b.author.uniqueId) + : b.author.uniqueId.localeCompare(a.author.uniqueId) + ); + break; + case "view": + data = data.sort((a, b) => + sorting[sort] == -1 + ? a.stats.playCount - b.stats.playCount + : b.stats.playCount - a.stats.playCount + ); + break; + case "length": + data = data.sort((a, b) => + sorting[sort] == -1 + ? a.video.duration - b.video.duration + : b.video.duration - a.video.duration + ); + break; + } + renderTable(tbody, data); + }); + } } - function renderTable( - tbody, - data = Array.from(CACHED.videoById.values()) - ) { + function renderTable(tbody, data) { tbody.innerHTML = data .map( (v, i) => /*html*/ ` - + @@ -208,16 +378,20 @@ export default { .join(""); } - container.onclick = (e) => { - if (e.target === container) { - toggle(false); - } - }; + async function download(data, onProgress) { + const dir = await UfsGlobal.Utils.chooseFolderToDownload("tiktok"); - floatingBtn.onclick = () => { - let isShow = toggle(); - if (isShow) renderData(); - }; + for (let i = 0; i < data.length; ++i) { + try { + const { url, filename } = data[i]; + const realUrl = await UfsGlobal.Utils.getRedirectedUrl(url); + await UfsGlobal.Utils.downloadToFolder(realUrl, filename, dir); + onProgress?.(i + 1, data.length); + } catch (e) { + console.error(e); + } + } + } hookFetch({ onAfter: async (url, options, response) => { From 463737d86602a2290e7e5bdd023a2c33345dc025 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Thu, 25 Jul 2024 04:10:09 +0700 Subject: [PATCH 27/47] fix download tiktok video --- scripts/content-scripts/ufs_global.js | 15 ++++++++-- scripts/tiktok_batchDownload.js | 41 +++++++++++++++++++++------ 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/scripts/content-scripts/ufs_global.js b/scripts/content-scripts/ufs_global.js index 94d675dd..344a7279 100644 --- a/scripts/content-scripts/ufs_global.js +++ b/scripts/content-scripts/ufs_global.js @@ -764,14 +764,23 @@ async function chooseFolderToDownload(subDirName = "") { }); return subDir; } -async function downloadToFolder(url, fileName, dirHandler, subFolderName = "") { +async function downloadToFolder({ + url, + fileName, + dirHandler, + expectBlobType, + subFolderName = "", +}) { if (!url) return false; try { // const f = getOriginalWindowFunction("fetch"); - // try download directly, using fetch blob - console.log(url); const res = await fetch(url); const blob = await res.blob(); + if (expectBlobType && blob.type !== expectBlobType) { + throw new Error( + `Blob type ${blob.type} doesn't match expected type ${expectBlobType}` + ); + } const fileHandler = await dirHandler.getFileHandle(fileName, { create: true, }); diff --git a/scripts/tiktok_batchDownload.js b/scripts/tiktok_batchDownload.js index 5f307605..8b0a6bfc 100644 --- a/scripts/tiktok_batchDownload.js +++ b/scripts/tiktok_batchDownload.js @@ -47,7 +47,11 @@ export default { if (event.data?.type === commId) { const dir = await UfsGlobal.Utils.chooseFolderToDownload("tiktok"); for (const { url, name } of event.data.data) { - await UfsGlobal.Utils.downloadToFolder(url, name, dir); + await UfsGlobal.Utils.downloadToFolder({ + url, + fileName: name, + dirHandler: dir, + }); } } }); @@ -218,10 +222,12 @@ export default { const downVideoBtn = container.querySelector("button#video"); downVideoBtn.addEventListener("click", () => { download( + "video/mp4", data.map((_, i) => { const urlList = - _.video.bitrateInfo.find((b) => b.Bitrate === _.video.bitrate) - ?.PlayAddr?.UrlList || []; + _.video?.bitrateInfo?.find?.( + (b) => b.Bitrate === _.video.bitrate + )?.PlayAddr?.UrlList || []; const bestUrl = urlList[urlList.length - 1]; @@ -378,14 +384,19 @@ export default { .join(""); } - async function download(data, onProgress) { + async function download(expectBlobType, data, onProgress) { const dir = await UfsGlobal.Utils.chooseFolderToDownload("tiktok"); for (let i = 0; i < data.length; ++i) { try { const { url, filename } = data[i]; const realUrl = await UfsGlobal.Utils.getRedirectedUrl(url); - await UfsGlobal.Utils.downloadToFolder(realUrl, filename, dir); + await UfsGlobal.Utils.downloadToFolder({ + url: realUrl, + fileName: filename, + dirHandler: dir, + expectBlobType, + }); onProgress?.(i + 1, data.length); } catch (e) { console.error(e); @@ -396,19 +407,33 @@ export default { hookFetch({ onAfter: async (url, options, response) => { if (url.includes("item_list/")) { - // clone to new response const res = response.clone(); const json = await res.json(); console.log(json); if (json?.itemList) { - CACHED.list.push(...json.itemList); json.itemList.forEach((_) => { if (_.video.playAddr) CACHED.videoById.set(_.video.id, _); }); - floatingBtn.innerHTML = `📥 (${CACHED.videoById.size})`; } } + + if (url.includes("api/search")) { + const res = response.clone(); + const json = await res.json(); + console.log(json); + + if (json.data?.length) { + json.data.forEach((_) => { + if (_.type === 1) { + CACHED.videoById.set(_.item.video.id, _.item); + } + }); + } + } + + if (CACHED.videoById.size) + floatingBtn.innerHTML = `📥 (${CACHED.videoById.size})`; }, }); }, From 9b463e73194bc0a0f46fd3300a0c05353124dd7c Mon Sep 17 00:00:00 2001 From: HoangTran <99.hoangtran@gmail.com> Date: Thu, 25 Jul 2024 21:28:35 +0700 Subject: [PATCH 28/47] cover --- scripts/tiktok_batchDownload.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tiktok_batchDownload.js b/scripts/tiktok_batchDownload.js index 8b0a6bfc..dfddb9f4 100644 --- a/scripts/tiktok_batchDownload.js +++ b/scripts/tiktok_batchDownload.js @@ -353,7 +353,7 @@ export default { From 2db1024420f493d0149f6642360bd4bff8fa296d Mon Sep 17 00:00:00 2001 From: HoangTran <99.hoangtran@gmail.com> Date: Thu, 25 Jul 2024 22:56:06 +0700 Subject: [PATCH 29/47] update UI --- scripts/content-scripts/ufs_global.js | 2 + scripts/tiktok_batchDownload.js | 142 ++++++++++++++---------- scripts/tiktok_downloadWatchingVideo.js | 50 ++++----- 3 files changed, 111 insertions(+), 83 deletions(-) diff --git a/scripts/content-scripts/ufs_global.js b/scripts/content-scripts/ufs_global.js index 344a7279..2d64e354 100644 --- a/scripts/content-scripts/ufs_global.js +++ b/scripts/content-scripts/ufs_global.js @@ -1,3 +1,5 @@ +// TODO: split this file into multiple helper files + export const UfsGlobal = { Extension: { sendToContentScript, diff --git a/scripts/tiktok_batchDownload.js b/scripts/tiktok_batchDownload.js index dfddb9f4..467c0a37 100644 --- a/scripts/tiktok_batchDownload.js +++ b/scripts/tiktok_batchDownload.js @@ -75,7 +75,7 @@ export default { - -
📥
- -
- -
- `; - const floatingBtn = ui.querySelector("#" + floatingBtnId); - const container = ui.querySelector("#" + containerId); - - container.onclick = (e) => { - if (e.target === container) { - toggle(false); - } + onDocumentStart: async () => { + const CACHED = { + videoById: new Map(), }; - floatingBtn.onclick = () => { - let isShow = toggle(); - if (isShow) renderData(); - }; - - function toggle(willShow) { - if (!(typeof willShow === "boolean")) { - let isShowing = container.style.display == "flex"; - willShow = !isShowing; - } - container.style.display = willShow ? "flex" : "none"; - return willShow; - } - - const formatter = UfsGlobal.Utils.getNumberFormatter("compactShort"); - - function renderData() { - container.innerHTML = /*html*/ ` -
-

Tiktok - Useful Scripts

-

Found ${CACHED.videoById.size} videos

- -
- - - - - -
- -
-
#Video🎬 Video TitleUserViewLength👤 User👀 View🕒 Length Download
${i + 1}${v.index} @@ -194,12 +363,13 @@ export default { ${v.video.duration}s

- Video
+ 🎬 Video
+ 🖼️ Cover
- Avatar + 👤 Avatar
- Music: ${v.music.title} + 🎧 Music: ${v.music.title}

${v.index} - +

${v.desc}

- - - - - - - - - - - - - -
#🎬 VideoTitle👤 User👀 View🕒 LengthDownload
- -`; - - const tbody = container.querySelector("tbody"); + // reference to Cached + window.ufs_tiktok_batchDownload = CACHED; - // render initial data - let allVideoData = Array.from(CACHED.videoById.values()).map( - (_, i) => ({ - ..._, - index: i + 1, - }) - ); - renderTable(tbody, allVideoData); + hookFetch({ + onAfter: async (url, options, response) => { + if (url.includes("item_list/")) { + const res = response.clone(); + const json = await res.json(); + console.log(json); - // search - const hiddenVideos = new Set(); - const searchInp = container.querySelector("#search"); - searchInp.addEventListener("input", (event) => { - const value = event.target.value; - let trs = tbody.querySelectorAll("tr"); - for (const tr of trs) { - const tds = tr.querySelectorAll("td"); - let found = false; - for (const td of tds) { - if (td.textContent.toLowerCase().includes(value)) { - found = true; - break; - } + if (json?.itemList) { + json.itemList.forEach((_) => { + if (_.video.playAddr) CACHED.videoById.set(_.video.id, _); + }); } - tr.style.display = found ? "" : "none"; - - let videoId = tr.getAttribute("data-video-id"); - if (found) hiddenVideos.delete(videoId); - else hiddenVideos.add(videoId); } - }); - - function getShowingVideos() { - return allVideoData.filter((_) => !hiddenVideos.has(_.video.id)); - } - // btn - const clearBtn = container.querySelector("button#clear"); - clearBtn.addEventListener("click", () => { - if (confirm("Are you sure want to clear all data?")) { - CACHED.videoById.clear(); - renderData(); - floatingBtn.innerText = "📥"; - } - }); - const downVideoBtn = container.querySelector("button#video"); - downVideoBtn.addEventListener("click", () => { - download({ - folderName: "tiktok_videos", - expectBlobType: "video/mp4", - data: getShowingVideos().map((_, i) => { - const urlList = - _.video?.bitrateInfo?.find?.( - (b) => b.Bitrate === _.video.bitrate - )?.PlayAddr?.UrlList || []; - - const bestUrl = urlList[urlList.length - 1]; + if (url.includes("api/search")) { + const res = response.clone(); + const json = await res.json(); + console.log(json); - return { - url: bestUrl || _.video.playAddr, - filename: - i + - 1 + - "_" + - UfsGlobal.Utils.sanitizeName(_.id, false) + - ".mp4", - }; - }), - onProgressItem: (i, total) => { - UfsGlobal.DOM.notify({ - msg: `Downloading... ${i}/${total} videos`, - duration: 30000, + if (json.data?.length) { + json.data.forEach((_) => { + if (_.type === 1) { + CACHED.videoById.set(_.item.video.id, _.item); + } }); - }, - onFinishItem: (i, total) => { - downVideoBtn.textContent = `🎬 Download video (${i}/${total})`; - UfsGlobal.DOM.notify({ msg: `Downloaded ${i}/${total} videos` }); - }, - }); - }); - const downAudioBtn = container.querySelector("button#audio"); - downAudioBtn.addEventListener("click", () => { - const uniqueMusic = new Map(); - for (const item of getShowingVideos()) { - if (!uniqueMusic.has(item.music.id)) - uniqueMusic.set(item.music.id, item); + } } - download({ - folderName: "tiktok_musics", - data: Array.from(uniqueMusic.values()).map((_, i) => ({ - url: _.music.playUrl, - filename: - i + - 1 + - "_" + - UfsGlobal.Utils.sanitizeName( - _.music.title.substr(0, 50) || "audio", - false - ) + - ".mp3", - })), - onProgressItem: (i, total) => { - UfsGlobal.DOM.notify({ - msg: `Downloading... ${i}/${total} audios`, - duration: 30000, - }); - }, - onFinishItem: (i, total) => { - downAudioBtn.textContent = `🎧 Download audio (${i}/${total})`; - UfsGlobal.DOM.notify({ msg: `Downloaded ${i}/${total} audios` }); - }, - }); - }); - const downJsonBtn = container.querySelector("button#json"); - downJsonBtn.addEventListener("click", () => { - UfsGlobal.Utils.downloadData( - JSON.stringify(getShowingVideos(), null, 4), - "tiktok.json" - ); - }); + }, + }); - // sorting - const sorting = {}; - const ths = container.querySelectorAll("th"); - for (const th of ths) { - const sort = th.getAttribute("data-sort"); - if (!sort) continue; - th.title = "Sort"; + UfsGlobal.Extension.getURL("/scripts/tiktok_batchDownload.css").then( + UfsGlobal.DOM.injectCssFile + ); + await UfsGlobal.DOM.injectScriptSrcAsync( + await UfsGlobal.Extension.getURL("/scripts/libs/vue/index.js") + ); - th.addEventListener("click", () => { - sorting[sort] = sorting[sort] == 1 ? -1 : 1; - switch (sort) { - case "index": - allVideoData = allVideoData.sort((a, b) => - sorting[sort] == -1 ? a.index - b.index : b.index - a.index - ); - break; - case "title": - allVideoData = allVideoData.sort((a, b) => - sorting[sort] == -1 - ? a.desc.localeCompare(b.desc) - : b.desc.localeCompare(a.desc) - ); - break; - case "user": - allVideoData = allVideoData.sort((a, b) => - sorting[sort] == -1 - ? a.author.uniqueId.localeCompare(b.author.uniqueId) - : b.author.uniqueId.localeCompare(a.author.uniqueId) - ); - break; - case "view": - allVideoData = allVideoData.sort((a, b) => - sorting[sort] == -1 - ? a.stats.playCount - b.stats.playCount - : b.stats.playCount - a.stats.playCount - ); - break; - case "length": - allVideoData = allVideoData.sort((a, b) => - sorting[sort] == -1 - ? a.video.duration - b.video.duration - : b.video.duration - a.video.duration - ); - break; - } - renderTable(tbody, getShowingVideos()); - }); - } - } + const div = document.createElement("div"); + document.documentElement.appendChild(div); - function renderTable(tbody, data) { - tbody.innerHTML = data - .map( - (v, i) => /*html*/ ` - -${v.index} + const formatter = UfsGlobal.Utils.getNumberFormatter("compactShort"); + + const app = new Vue({ + template: /*html*/ ` +
+
📥 {{totalCount}}
+
+
+

Tiktok - Useful Scripts

+

Found {{totalCount}} videos

+ +
+ + + + + + {{search}} +
+ +
+ + + + + + + + + + + + + + + + + + - + - - + + -` - ) - .join(""); - } + + +
#🎬 VideoTitle👤 UserViewLengthDownload

No video

{{v.index}} - - + +

${v.desc}

{{v.desc}}

- - - - ${v.author.nickname}
- ${v.author.uniqueId}
- ${v.author.id} + + {{v.author.nickname}}
+ {{v.author.uniqueId}}
+ {{v.author.id}}
${formatter.format(v.stats.playCount)}${v.video.duration}s{{format(v.stats.playCount)}}{{v.video.duration}}s

- 🎬 Video
- 🖼️ Cover
- + 🎬 Video
+ 🖼️ Cover
+ 👤 Avatar
- - 🎧 Music: ${v.music.title} + + 🎧 Music: {{v.music.title}}

+
+
+
+
`, + created() { + setInterval(() => { + this.videos = Array.from(CACHED.videoById.values()) + // inject index + .map((v, i) => ({ ...v, index: i + 1 })); + }, 1000); + }, + data() { + return { + showModal: false, + videos: [], + search: "", + sortBy: "index", + sortDir: "asc", + downloading: {}, + }; + }, + computed: { + videoTitle() { + if (this.downloading.video) { + return ( + "Downloading " + + this.downloading.video + + "/" + + this.videosToShow.length + + " videos" + ); + } + return "Downloaded " + this.videosToShow.length + " videos"; + }, + audioTitle() { + if (this.downloading.audio) { + return ( + "Downloading " + + this.downloading.audio + + "/" + + this.uniqueAudio.length + + " audios" + ); + } + return "Downloaded " + this.uniqueAudio.length + " audios"; + }, + uniqueAudio() { + const result = new Map(); + for (const item of this.videosToShow) { + if (!result.has(item.music.id)) result.set(item.music.id, item); + } + return Array.from(result.values()); + }, + totalCount() { + return this.videos.length; + }, + videosToShow() { + return ( + this.videos + // filter by search + .filter((v) => { + return [ + v.desc, + v.author.id, + v.author.nickname, + v.author.uniqueId, + ].some((s) => + s.toLowerCase().includes(this.search.toLowerCase()) + ); + }) + // sorting + .sort((a, b) => { + switch (this.sortBy) { + case "index": + return this.sortDir === "asc" + ? a.index - b.index + : b.index - a.index; + case "title": + return this.sortDir === "asc" + ? a.desc.localeCompare(b.desc) + : b.desc.localeCompare(a.desc); + case "author": + return this.sortDir === "asc" + ? a.author.nickname.localeCompare(b.author.nickname) + : b.author.nickname.localeCompare(a.author.nickname); + case "view": + return this.sortDir === "asc" + ? a.stats.playCount - b.stats.playCount + : b.stats.playCount - a.stats.playCount; + case "duration": + return this.sortDir === "asc" + ? a.video.duration - b.video.duration + : b.video.duration - a.video.duration; + } + }) + ); + }, + }, + methods: { + async downloadVideo() { + const total = this.videosToShow.length; + if (!total) return; + const success = 0; + this.downloading.video = true; + await download({ + folderName: "tiktok_videos", + expectBlobType: "video/mp4", + data: this.videosToShow.map((_, i) => { + const urlList = + _.video?.bitrateInfo?.find?.( + (b) => b.Bitrate === _.video.bitrate + )?.PlayAddr?.UrlList || []; + const bestUrl = urlList[urlList.length - 1]; + return { + url: bestUrl || _.video.playAddr, + filename: + i + + 1 + + "_" + + UfsGlobal.Utils.sanitizeName(_.id, false) + + ".mp4", + }; + }), + onProgressItem: (i, total) => { + this.downloading.video = i; + }, + onFinishItem: (i, total) => { + success++; + }, + }); + alert("Downloaded " + success + "/" + total + " videos!"); + this.downloading.video = false; + }, + async downloadAudio() { + const total = this.uniqueAudio.length; + if (!total) return; + this.downloading.audio = true; + const success = 0; + await download({ + folderName: "tiktok_musics", + data: this.uniqueAudio.map((_, i) => ({ + url: _.music.playUrl, + filename: + i + + 1 + + "_" + + UfsGlobal.Utils.sanitizeName( + _.music.title.substr(0, 50) || "audio", + false + ) + + ".mp3", + })), + onProgressItem: (i, total) => { + this.downloading.audio = i; + }, + onFinishItem: (i, total) => { + success++; + }, + }); + alert("Downloaded " + success + "/" + total + " videos!"); + this.downloading.audio = false; + }, + downloadJson() { + UfsGlobal.Utils.downloadData( + JSON.stringify(this.videosToShow, null, 4), + this.videosToShow.length + "_videos_tiktok.json" + ); + }, + clear() { + if (confirm("Are you sure want to clear all?")) { + CACHED.videoById.clear(); + this.videos = []; + } + }, + setSortBy(key) { + this.sortBy = key; + if (key === this.sortBy) + this.sortDir = this.sortDir === "asc" ? "desc" : "asc"; + }, + openUser(id) { + window.open("https://www.tiktok.com/@" + id, "_blank"); + }, + format(v) { + return formatter.format(v); + }, + onClickContainer(e) { + if (e.target === this.$el) this.showModal = false; + }, + }, + }).$mount(div); async function download({ folderName = "tiktok", @@ -455,255 +356,9 @@ export default { } } - hookFetch({ - onAfter: async (url, options, response) => { - if (url.includes("item_list/")) { - const res = response.clone(); - const json = await res.json(); - console.log(json); - - if (json?.itemList) { - json.itemList.forEach((_) => { - if (_.video.playAddr) CACHED.videoById.set(_.video.id, _); - }); - } - } - - if (url.includes("api/search")) { - const res = response.clone(); - const json = await res.json(); - console.log(json); - - if (json.data?.length) { - json.data.forEach((_) => { - if (_.type === 1) { - CACHED.videoById.set(_.item.video.id, _.item); - } - }); - } - } - - if (CACHED.videoById.size) - floatingBtn.innerHTML = `📥 (${CACHED.videoById.size})`; - }, - }); - }, - _onDocumentIdle: async () => { + // checkbox on videos let checkboxes = []; - // Setup DOM - let id = "ufs-tiktok-batch-download"; - let container = document.createElement("div"); - container.id = id; - - const style = document.createElement("style"); - style.textContent = ` - #${id} { - position: fixed; - bottom: 20px; - left: 50%; - transform: translateX(-50%); - background: #333e; - color: white; - min-height: 50px; - padding: 15px; - z-index: 6; - border-radius: 5px; - border: 1px solid #eee; - } - #${id} button { - padding: 5px 10px; - background: #444; - color: white !important; - border: none; - } - #${id} button:hover { - background: #666; - } - `; - container.appendChild(style); - - (document.body || document.documentElement).appendChild(container); - - let progressDiv = document.createElement("p"); - progressDiv.innerText = "Useful script: Tiktok tải hàng loạt"; - progressDiv.style = "margin-bottom: 5px; font-family: monospace;"; - container.appendChild(progressDiv); - - // scroll button - let scrolling = false; - const scrollBtn = document.createElement("button"); - scrollBtn.innerText = "Scroll xuống ⏬"; - scrollBtn.onclick = async () => { - scrolling = !scrolling; - - scrollBtn.innerText = scrolling - ? "Đang scroll... ⏳" - : "Scroll xuống ⏬"; - - let doubleCheck = 0; - while (scrolling) { - let previousHeight = document.body.scrollHeight; - window.scrollTo(0, document.body.scrollHeight); - await new Promise((resolve) => setTimeout(resolve, 1000)); - if (document.body.scrollHeight <= previousHeight) { - doubleCheck++; - console.log(doubleCheck); - if (doubleCheck > 5) { - scrolling = false; - scrollBtn.innerText = "Scroll xuống ⏬"; - } - } - } - }; - container.appendChild(scrollBtn); - - // Select all button - const selectAllBtn = document.createElement("button"); - selectAllBtn.innerText = "Chọn/Huỷ chọn ✅"; - selectAllBtn.onclick = function () { - let value = checkboxes[0].checked; - for (let checkbox of checkboxes) { - checkbox.checked = !value; - } - }; - container.appendChild(selectAllBtn); - - // Revert all Button - const revertAllBtn = document.createElement("button"); - revertAllBtn.innerText = "Đảo ngược 🔁"; - revertAllBtn.onclick = function () { - for (let checkbox of checkboxes) { - checkbox.checked = !checkbox.checked; - } - }; - container.appendChild(revertAllBtn); - - // Download button - const downloadBtn = document.createElement("button"); - downloadBtn.innerText = "GET LINK 🔗"; - downloadBtn.onclick = function () { - let videoUrls = []; - for (let checkbox of checkboxes) { - if (checkbox.checked) { - videoUrls.push(checkbox["data-url"]); - } - } - console.log(videoUrls); - getLinkVideos(videoUrls); - }; - container.appendChild(downloadBtn); - - // result div - let resultDiv = document.createElement("div"); - resultDiv.style = "margin-top: 10px"; - container.appendChild(resultDiv); - - let resultLabel = document.createElement("label"); - resultDiv.appendChild(resultLabel); - - let resultTxt = document.createElement("textarea"); - resultTxt.style = "width: 100%; height: 50px"; - resultTxt.hidden = true; - resultDiv.appendChild(resultTxt); - - // click listener - window.onclick = function (e) { - if ( - e.target.type === "checkbox" || - e.target == selectAllBtn || - e.target == revertAllBtn - ) { - let selected = checkboxes.filter( - (checkbox) => checkbox.checked - ).length; - progressDiv.innerText = `Đã chọn ${selected}/${checkboxes.length} video. Bấm nút Get link khi chọn xong nhé.`; - } - }; - - function createCheckBox(url) { - let checkbox = document.createElement("input"); - checkbox.type = "checkbox"; - checkbox.name = "video"; - checkbox.checked = false; - checkbox["data-url"] = url; - checkbox.style = - "z-index: 0; position: absolute; top: 0; right: 0; width: 60px; height: 60px;"; - return checkbox; - } - - const { sleep, sanitizeName } = UfsGlobal.Utils; - - async function getLinkVideos(videoUrls) { - if (!videoUrls.length) return; - const getId = (url) => url.split("/").at(-1); - const queue = [...videoUrls]; - const links = []; - downloadBtn.disabled = true; - - while (queue.length) { - let progress = `[${videoUrls.length - queue.length + 1}/${ - videoUrls.length - }]`; - try { - const url = queue[0]; - const id = getId(url); - - console.log(`${progress} Đang tìm link cho video ${url}`); - progressDiv.innerText = `${progress} Đang tìm link video ${id}...`; - downloadBtn.innerText = `Đang get link ${progress}...`; - - const cached = CACHED.videoById.get(id); - const link = - cached?.url || - (await downloadTiktokVideoFromUrl(url, true)) || - (await downloadTiktokVideoFromId(id)); - - if (link) { - resultTxt.hidden = false; - resultTxt.value += link + "\n"; - let count = resultTxt.value.split("\n").filter((i) => i).length; - resultLabel.innerText = `Link tại đây, ${count} video, copy bỏ vào IDM tải hàng loạt nhé:`; - - // await UfsGlobal.Utils.downloadToFolder(link, id + ".mp4", dir); - // await UfsGlobal.Extension.download({ - // url: link, - // conflictAction: "overwrite", - // filename: - // "tiktok/" + - // sanitizeName( - // CACHED.videoById.get(id)?.name || id - // ) + - // ".mp4", - // }); - links.push({ - url: link, - name: sanitizeName(cached?.name || id, false) + ".mp4", - }); - } else { - progressDiv.innerText = `[LỖI] Không thể tải video ${url}.`; - await sleep(1000); - } - queue.shift(); - } catch (e) { - console.log(`${progress} Lỗi tải, thử lại sau 1s...`); - let failId = queue.shift(); - queue.push(failId); - await sleep(1000); - } - } - - progressDiv.innerText = "Bạn vẫn có thể chọn thêm video để get link."; - downloadBtn.disabled = false; - downloadBtn.innerText = "GET LINK 🔗"; - - if (links?.length) { - UfsGlobal.Utils.copyToClipboard(links.map((_) => _.url).join("\n")); - // window.postMessage({ type: commId, data: links }, "*"); // send to content script to download - } - console.log(links); - } - // Listen for videos UfsGlobal.DOM.onElementsAdded('a[href*="/video/"]', (nodes) => { // remove if not in DOM @@ -742,6 +397,339 @@ export default { let selected = checkboxes.filter((checkbox) => checkbox.checked).length; progressDiv.innerText = `Đã chọn ${selected}/${checkboxes.length} video. Bấm nút Get link khi chọn xong nhé.`; }); + + function createCheckBox(url) { + let checkbox = document.createElement("input"); + checkbox.type = "checkbox"; + checkbox.name = "video"; + checkbox.checked = false; + checkbox["data-url"] = url; + checkbox.style = + "z-index: 0; position: absolute; top: 0; right: 0; width: 60px; height: 60px;"; + return checkbox; + } + }, + }, +}; + +const videoMock = { + AIGCDescription: "", + author: { + avatarLarger: + "https://p16-sign-useast2a.tiktokcdn.com/tos-useast2a-avt-0068-giso/ba2aad87bd8bafe7f4a4850d15bc4d47~c5_1080x1080.jpeg?lk3s=a5d48078&nonce=95769&refresh_token=c0ece14ea3a9bbefb2e5c536a37a220e&x-expires=1722135600&x-signature=GTDzdehNU9FLUTENcoB%2Fuu%2FboLk%3D&shp=a5d48078&shcp=81f88b70", + avatarMedium: + "https://p16-sign-useast2a.tiktokcdn.com/tos-useast2a-avt-0068-giso/ba2aad87bd8bafe7f4a4850d15bc4d47~c5_720x720.jpeg?lk3s=a5d48078&nonce=59152&refresh_token=23b71fadc0a9b7128b9ad3964dfbeace&x-expires=1722135600&x-signature=i%2FeSkXqM1e93v9iXJUwZeg7Xpdw%3D&shp=a5d48078&shcp=81f88b70", + avatarThumb: + "https://p16-sign-useast2a.tiktokcdn.com/tos-useast2a-avt-0068-giso/ba2aad87bd8bafe7f4a4850d15bc4d47~c5_100x100.jpeg?lk3s=a5d48078&nonce=59913&refresh_token=a7090d32da45fc22f704e4ca7517ce4d&x-expires=1722135600&x-signature=z5lxF2ivUkUmnwOP7H9gJvH4Cyg%3D&shp=a5d48078&shcp=81f88b70", + commentSetting: 0, + downloadSetting: 0, + duetSetting: 3, + ftc: false, + id: "64700392767", + isADVirtual: false, + isEmbedBanned: false, + nickname: "Kiệt Hà Tịnh", + openFavorite: false, + privateAccount: false, + relation: 0, + secUid: "MS4wLjABAAAAsnkRvwHb4FpMu0RLRx70Sc5TI-jwqRa5Xw_w9zVRP0A", + secret: false, + signature: + "Fb:Ngô Lê Tuấn Kiệt ☘️\nNgười Hà Tịnh ở Biên Hoà ❤️\nĐồ K dùng ở dưới nha👇🏻", + stitchSetting: 3, + uniqueId: "tuankiet.2000", + verified: true, + }, + authorStats: { + diggCount: 10600, + followerCount: 8600000, + followingCount: 161, + friendCount: 0, + heart: 375400000, + heartCount: 375400000, + videoCount: 1656, + }, + collected: false, + contents: [ + { + desc: "Clip này My xin đính chính lần cuối nha mn😌 ", + }, + ], + createTime: 1718103129, + desc: "Clip này My xin đính chính lần cuối nha mn😌 ", + digged: false, + diversificationId: 10004, + duetDisplay: 0, + forFriend: false, + id: "7379196725698841863", + itemCommentStatus: 0, + item_control: { + can_repost: true, + }, + music: { + authorName: "Trần thánh trúc", + coverLarge: + "https://p16-sign-sg.tiktokcdn.com/aweme/1080x1080/tos-alisg-avt-0068/f0e3a91d906cf4b62f3d9033ed4dc227.jpeg?lk3s=a5d48078&nonce=16594&refresh_token=6967b6cb4f75c7f354f810e073fee6a0&x-expires=1722135600&x-signature=5TAkwWXiqjuAVha1QwgkIdQ33Oo%3D&shp=a5d48078&shcp=81f88b70", + coverMedium: + "https://p16-sign-sg.tiktokcdn.com/aweme/720x720/tos-alisg-avt-0068/f0e3a91d906cf4b62f3d9033ed4dc227.jpeg?lk3s=a5d48078&nonce=1126&refresh_token=85d0f238bdd5540f40cd5b4d4176272a&x-expires=1722135600&x-signature=w3E1Oi4ZSsIyQJrxoKlPv%2BaeSDw%3D&shp=a5d48078&shcp=81f88b70", + coverThumb: + "https://p16-sign-sg.tiktokcdn.com/aweme/100x100/tos-alisg-avt-0068/f0e3a91d906cf4b62f3d9033ed4dc227.jpeg?lk3s=a5d48078&nonce=12910&refresh_token=5074541334dc629cc5ecd123a5313b3f&x-expires=1722135600&x-signature=C3uvpe9PTjEH%2FHT8WIn8%2B9RIAVI%3D&shp=a5d48078&shcp=81f88b70", + duration: 44, + id: "7374296585604451073", + original: false, + playUrl: + "https://v16-webapp-prime.tiktok.com/video/tos/alisg/tos-alisg-v-27dcd7/oUOVDf7tUEdQM7wxqKmgFBKWIWJsCcfO5BH8ad/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=0&dr=0&er=0&lr=default&cd=0%7C0%7C0%7C0&br=250&bt=125&ft=GNDpcInz7ThZTIrrXq8Zmo&mime_type=audio_mpeg&qs=6&rc=ZzhlN2ZlNjlmOzVkMzQ4aEBpM2tva3c5cmVmczMzODU8NEBhLzQ2Yl41NmAxMl9hNjBjYSM0czZuMmRjNF9gLS1kMS1zcw%3D%3D&btag=e00088000&expire=1721968037&l=20240726032632702565EB3E432D2B5481&ply_type=3&policy=3&signature=18b1024aebfed96fab659235f53caa1e&tk=7294694872060675074", + title: "nhạc nền - Ngọc Ngân", + }, + officalItem: false, + originalItem: false, + privateItem: false, + secret: false, + shareEnabled: true, + stats: { + collectCount: 12600, + commentCount: 2212, + diggCount: 376100, + playCount: 7900000, + shareCount: 4063, + }, + statsV2: { + collectCount: "12577", + commentCount: "2212", + diggCount: "376100", + playCount: "7900000", + repostCount: "0", + shareCount: "4063", + }, + stitchDisplay: 0, + video: { + VQScore: "70.92", + bitrate: 1430004, + bitrateInfo: [ + { + Bitrate: 1430004, + CodecType: "h264", + GearName: "normal_540_0", + MVMAF: '""', + PlayAddr: { + DataSize: 7939920, + FileCs: "c:0-38026-2fae", + FileHash: "517d6e7d1deb58fb809cd976094e6888", + Height: 1024, + Uri: "v14044g50000cpk2o1fog65k4egbdktg", + UrlKey: "v14044g50000cpk2o1fog65k4egbdktg_h264_540p_1430004", + UrlList: [ + "https://v16-webapp-prime.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/oQiYGHrzNAH2A0CQi06BlILAqrofEUCkCI7wIB/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=2792&bt=1396&cs=0&ds=6&ft=4fUEKM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=0&rc=NTw6Z2VmNDhkPDg3NWY4O0Bpam5laG45cjQ1czMzODczNEBfNl9gYl4xX18xLi8vMzE0YSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&btag=e00088000&expire=1721986037&l=20240726032632702565EB3E432D2B5481&ply_type=2&policy=2&signature=68a902a51943c5d88af812ab9ae59688&tk=tt_chain_token", + "https://v19-webapp-prime.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/oQiYGHrzNAH2A0CQi06BlILAqrofEUCkCI7wIB/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=2792&bt=1396&cs=0&ds=6&ft=4fUEKM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=0&rc=NTw6Z2VmNDhkPDg3NWY4O0Bpam5laG45cjQ1czMzODczNEBfNl9gYl4xX18xLi8vMzE0YSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&btag=e00088000&expire=1721986037&l=20240726032632702565EB3E432D2B5481&ply_type=2&policy=2&signature=68a902a51943c5d88af812ab9ae59688&tk=tt_chain_token", + "https://api.tiktokv.com/aweme/v1/play/?video_id=v14044g50000cpk2o1fog65k4egbdktg&line=0&is_play_url=1&file_id=78456c22241549bab541acebcdb799b2&item_id=7379196725698841863&signaturev3=dmlkZW9faWQ7ZmlsZV9pZDtpdGVtX2lkLjVlOWU2MWVjODNlZDU3OTgwMzkwMWY5Y2Q2NjQzMjhl&shp=9e36835a&shcp=-", + ], + Width: 576, + }, + QualityType: 20, + }, + { + Bitrate: 1261254, + CodecType: "h265_hvc1", + GearName: "adapt_lowest_1080_1", + MVMAF: + '"{\\"v2.0\\": {\\"ori\\": {\\"v1080\\": 89.373, \\"v960\\": 90.574, \\"v864\\": 91.529, \\"v720\\": 93.76}, \\"srv1\\": {\\"v1080\\": -1, \\"v960\\": -1, \\"v864\\": -1, \\"v720\\": -1}}}"', + PlayAddr: { + DataSize: 7002959, + FileCs: "c:0-38272-8f7c", + FileHash: "36cfe328f42240c9201093a1c96df84f", + Height: 1920, + Uri: "v14044g50000cpk2o1fog65k4egbdktg", + UrlKey: "v14044g50000cpk2o1fog65k4egbdktg_bytevc1_1080p_1261254", + UrlList: [ + "https://v16-webapp-prime.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/o0ZFwog2QEpFeFNBCWmEQDtBaId0UtWfgARFzR/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=2462&bt=1231&cs=2&ds=4&ft=4fUEKM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=15&rc=ZTk4ZDVkZDY3Zjo3OzU4NkBpam5laG45cjQ1czMzODczNEBjMV9eL19gNjIxYC0tMGExYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&btag=e00088000&expire=1721986037&l=20240726032632702565EB3E432D2B5481&ply_type=2&policy=2&signature=6703b959137d6977910b969eaf838224&tk=tt_chain_token", + "https://v19-webapp-prime.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/o0ZFwog2QEpFeFNBCWmEQDtBaId0UtWfgARFzR/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=2462&bt=1231&cs=2&ds=4&ft=4fUEKM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=15&rc=ZTk4ZDVkZDY3Zjo3OzU4NkBpam5laG45cjQ1czMzODczNEBjMV9eL19gNjIxYC0tMGExYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&btag=e00088000&expire=1721986037&l=20240726032632702565EB3E432D2B5481&ply_type=2&policy=2&signature=6703b959137d6977910b969eaf838224&tk=tt_chain_token", + "https://api.tiktokv.com/aweme/v1/play/?video_id=v14044g50000cpk2o1fog65k4egbdktg&line=0&is_play_url=1&file_id=43d5320804c74c5493caba22aab546fb&item_id=7379196725698841863&signaturev3=dmlkZW9faWQ7ZmlsZV9pZDtpdGVtX2lkLjYzMWJjOTNmN2E0ZTgxNWJlNTY5NDAzZDJiYTEyMThl&shp=9e36835a&shcp=-", + ], + Width: 1080, + }, + QualityType: 2, + }, + { + Bitrate: 827773, + CodecType: "h265_hvc1", + GearName: "adapt_lower_720_1", + MVMAF: + '"{\\"v2.0\\": {\\"ori\\": {\\"v1080\\": 81.444, \\"v960\\": 84.686, \\"v864\\": 86.95, \\"v720\\": 90.786}, \\"srv1\\": {\\"v1080\\": 89.324, \\"v960\\": 92.945, \\"v864\\": 94.345, \\"v720\\": 96.716}}}"', + PlayAddr: { + DataSize: 4596110, + FileCs: "c:0-38273-b3cb", + FileHash: "9a8275de7c719b846208535a4bda1209", + Height: 1280, + Uri: "v14044g50000cpk2o1fog65k4egbdktg", + UrlKey: "v14044g50000cpk2o1fog65k4egbdktg_bytevc1_720p_827773", + UrlList: [ + "https://v16-webapp-prime.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/oUEY06iEIAwzCaNf6Liqoph27UYCaCAIBABIQ0/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=1616&bt=808&cs=2&ds=3&ft=4fUEKM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=14&rc=aTs1O2c8ODQ1ZWg1aTNkOUBpam5laG45cjQ1czMzODczNEAuXjAuXi4zXmExYF5gMmEzYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&btag=e00088000&expire=1721986037&l=20240726032632702565EB3E432D2B5481&ply_type=2&policy=2&signature=aa0c545142bafe4efd480827937e9a44&tk=tt_chain_token", + "https://v19-webapp-prime.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/oUEY06iEIAwzCaNf6Liqoph27UYCaCAIBABIQ0/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=1616&bt=808&cs=2&ds=3&ft=4fUEKM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=14&rc=aTs1O2c8ODQ1ZWg1aTNkOUBpam5laG45cjQ1czMzODczNEAuXjAuXi4zXmExYF5gMmEzYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&btag=e00088000&expire=1721986037&l=20240726032632702565EB3E432D2B5481&ply_type=2&policy=2&signature=aa0c545142bafe4efd480827937e9a44&tk=tt_chain_token", + "https://api.tiktokv.com/aweme/v1/play/?video_id=v14044g50000cpk2o1fog65k4egbdktg&line=0&is_play_url=1&file_id=66da50cfa2ce4bd2a16519ad1832a81f&item_id=7379196725698841863&signaturev3=dmlkZW9faWQ7ZmlsZV9pZDtpdGVtX2lkLjYzZWJhYzdmMmMzZWQ1ZDk5MzU4OTIxNGZkODcyOTQ2&shp=9e36835a&shcp=-", + ], + Width: 720, + }, + QualityType: 14, + }, + { + Bitrate: 694640, + CodecType: "h265_hvc1", + GearName: "adapt_540_1", + MVMAF: + '"{\\"v2.0\\": {\\"ori\\": {\\"v1080\\": 74.423, \\"v960\\": 78.739, \\"v864\\": 81.884, \\"v720\\": 86.639}, \\"srv1\\": {\\"v1080\\": 87.574, \\"v960\\": 91.778, \\"v864\\": 93.224, \\"v720\\": 96.069}}}"', + PlayAddr: { + DataSize: 3856907, + FileCs: "c:0-38273-9698", + FileHash: "dd46f2309aa588691b3e2ae5fd3c640b", + Height: 1024, + Uri: "v14044g50000cpk2o1fog65k4egbdktg", + UrlKey: "v14044g50000cpk2o1fog65k4egbdktg_bytevc1_540p_694640", + UrlList: [ + "https://v16-webapp-prime.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/owCLiImNwY0IAICRzDEZ6YiHC2AoqBfLA0Q7Br/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=1356&bt=678&cs=2&ds=6&ft=4fUEKM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=11&rc=ZDQ7NWc2ODw7ZDk8Ojk2O0Bpam5laG45cjQ1czMzODczNEAuXy40XmFeNjExYDMuLmFeYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&btag=e00088000&expire=1721986037&l=20240726032632702565EB3E432D2B5481&ply_type=2&policy=2&signature=70dcd514149eead3286eebae6af4d4eb&tk=tt_chain_token", + "https://v19-webapp-prime.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/owCLiImNwY0IAICRzDEZ6YiHC2AoqBfLA0Q7Br/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=1356&bt=678&cs=2&ds=6&ft=4fUEKM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=11&rc=ZDQ7NWc2ODw7ZDk8Ojk2O0Bpam5laG45cjQ1czMzODczNEAuXy40XmFeNjExYDMuLmFeYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&btag=e00088000&expire=1721986037&l=20240726032632702565EB3E432D2B5481&ply_type=2&policy=2&signature=70dcd514149eead3286eebae6af4d4eb&tk=tt_chain_token", + "https://api.tiktokv.com/aweme/v1/play/?video_id=v14044g50000cpk2o1fog65k4egbdktg&line=0&is_play_url=1&file_id=a8d3161769c64a4899a5d3ad7218b11a&item_id=7379196725698841863&signaturev3=dmlkZW9faWQ7ZmlsZV9pZDtpdGVtX2lkLmI5ZDRmZDI1ZWYwNzNlNDE3ZTllNmQ4MDhmYjUyZTU4&shp=9e36835a&shcp=-", + ], + Width: 576, + }, + QualityType: 28, + }, + ], + codecType: "h264", + cover: + "https://p16-sign-sg.tiktokcdn.com/obj/tos-alisg-p-0037/f9e2ad3ce5ed4d17a20781bdac3c903c_1718103135?lk3s=81f88b70&nonce=53184&refresh_token=308e36058f33a46042f793fab36125ec&x-expires=1722135600&x-signature=82UdfuOqMxxwSdQEel0KWbGw9Kc%3D&shp=81f88b70&shcp=280c9438", + definition: "540p", + downloadAddr: + "https://v16-webapp-prime.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/ogfIICYI70C6NI6o0HAiE7CMwQibAzABN2BqXi/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=3084&bt=1542&cs=0&ds=3&ft=4fUEKM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=0&rc=NjY6NWllZGZmOjU2ZTY1ZkBpam5laG45cjQ1czMzODczNEAuYTUvYl5jXjQxMmMvXi02YSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&btag=e00088000&expire=1721986037&l=20240726032632702565EB3E432D2B5481&ply_type=2&policy=2&signature=0f49a63b5e17e036e2f70ca3b3345c62&tk=tt_chain_token", + duration: 44, + dynamicCover: + "https://p16-sign-sg.tiktokcdn.com/obj/tos-alisg-p-0037/63d392accbe548f5ae433b6d0c32de94_1718103135?lk3s=81f88b70&nonce=14449&refresh_token=56eabe5d508e4ac55d6f6a2810ce5d19&x-expires=1722135600&x-signature=PZct%2BEpR8IIll4GKxoCXR9z0Y5k%3D&shp=81f88b70&shcp=280c9438", + encodeUserTag: "", + encodedType: "normal", + format: "mp4", + height: 1024, + id: "7379196725698841863", + originCover: + "https://p16-sign-sg.tiktokcdn.com/obj/tos-alisg-p-0037/83e3606da0ec47dc87d0af4b05878a8f_1718103131?lk3s=81f88b70&x-expires=1722135600&x-signature=VLNP0WvEQn83f78RIvlwzeq7sAM%3D&shp=81f88b70&shcp=280c9438", + playAddr: + "https://v16-webapp-prime.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/oQiYGHrzNAH2A0CQi06BlILAqrofEUCkCI7wIB/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=2792&bt=1396&cs=0&ds=6&ft=4fUEKM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=0&rc=NTw6Z2VmNDhkPDg3NWY4O0Bpam5laG45cjQ1czMzODczNEBfNl9gYl4xX18xLi8vMzE0YSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&btag=e00088000&expire=1721986037&l=20240726032632702565EB3E432D2B5481&ply_type=2&policy=2&signature=68a902a51943c5d88af812ab9ae59688&tk=tt_chain_token", + ratio: "540p", + subtitleInfos: [ + { + Format: "webvtt", + LanguageCodeName: "vie-VN", + LanguageID: "10", + Size: 1220, + Source: "ASR", + Url: "https://v16-webapp.tiktok.com/a5424d768a075e69a055e72402152862/66a36bf5/video/tos/alisg/tos-alisg-pv-0037/03be2f31798f45059ea666ca47350470/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=24816&bt=12408&ds=4&ft=4b~OyM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=13&rc=am5laG45cjQ1czMzODczNEBpam5laG45cjQ1czMzODczNEBxYWQxMmRjbGhgLS1kMTFzYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&l=20240726032632702565EB3E432D2B5481&btag=e00048000", + UrlExpire: 1721986037, + Version: "1:llm", + }, + { + Format: "webvtt", + LanguageCodeName: "vie-VN", + LanguageID: "10", + Size: 1217, + Source: "ASR", + Url: "https://v16-webapp.tiktok.com/24a3a7730e8328eb2861c5510b57477a/66a36bf5/video/tos/alisg/tos-alisg-pv-0037/120af213ab354198941ef848b2c9a420/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=24816&bt=12408&ds=4&ft=4b~OyM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=13&rc=am5laG45cjQ1czMzODczNEBpam5laG45cjQ1czMzODczNEBxYWQxMmRjbGhgLS1kMTFzYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&l=20240726032632702565EB3E432D2B5481&btag=e00048000", + UrlExpire: 1721986037, + Version: "1:whisper_lid", + }, + { + Format: "webvtt", + LanguageCodeName: "eng-US", + LanguageID: "2", + Size: 1428, + Source: "MT", + Url: "https://v16-webapp.tiktok.com/63e9170fe4dd78f2271e9ee930796ce7/66a36bf5/video/tos/alisg/tos-alisg-pv-0037/56f87d05daff41a19a83c9fe3fccda6c/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=24816&bt=12408&ds=4&ft=4b~OyM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=13&rc=am5laG45cjQ1czMzODczNEBpam5laG45cjQ1czMzODczNEBxYWQxMmRjbGhgLS1kMTFzYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&l=20240726032632702565EB3E432D2B5481&btag=e00048000", + UrlExpire: 1721986037, + Version: "4:llm", + }, + { + Format: "webvtt", + LanguageCodeName: "cmn-Hans-CN", + LanguageID: "1", + Size: 635, + Source: "MT", + Url: "https://v16-webapp.tiktok.com/e34d44d91686f79e82f69a9cb4800a51/66a36bf5/video/tos/alisg/tos-alisg-pv-0037/33521bbb474a41abbd1a1deb02888ce6/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=24816&bt=12408&ds=4&ft=4b~OyM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=13&rc=am5laG45cjQ1czMzODczNEBpam5laG45cjQ1czMzODczNEBxYWQxMmRjbGhgLS1kMTFzYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&l=20240726032632702565EB3E432D2B5481&btag=e00048000", + UrlExpire: 1721986037, + Version: "4:llm", + }, + { + Format: "webvtt", + LanguageCodeName: "spa-ES", + LanguageID: "9", + Size: 1282, + Source: "MT", + Url: "https://v16-webapp.tiktok.com/89921b1bde0d325c8e92a1dffffa7501/66a36bf5/video/tos/alisg/tos-alisg-pv-0037/948dd14987094778a515dd944d2e0864/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=24816&bt=12408&ds=4&ft=4b~OyM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=13&rc=am5laG45cjQ1czMzODczNEBpam5laG45cjQ1czMzODczNEBxYWQxMmRjbGhgLS1kMTFzYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&l=20240726032632702565EB3E432D2B5481&btag=e00048000", + UrlExpire: 1721986037, + Version: "4:llm", + }, + { + Format: "webvtt", + LanguageCodeName: "kor-KR", + LanguageID: "4", + Size: 1668, + Source: "MT", + Url: "https://v16-webapp.tiktok.com/329afbeb7fdadb3d9fefa49ae9ab2f0e/66a36bf5/video/tos/alisg/tos-alisg-pv-0037/6c504e2df8544a798cd0687220cd4321/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=24816&bt=12408&ds=4&ft=4b~OyM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=13&rc=am5laG45cjQ1czMzODczNEBpam5laG45cjQ1czMzODczNEBxYWQxMmRjbGhgLS1kMTFzYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&l=20240726032632702565EB3E432D2B5481&btag=e00048000", + UrlExpire: 1721986037, + Version: "4:llm", + }, + { + Format: "webvtt", + LanguageCodeName: "jpn-JP", + LanguageID: "3", + Size: 1443, + Source: "MT", + Url: "https://v16-webapp.tiktok.com/bad1df16e007e8f7a5886c161afff1d3/66a36bf5/video/tos/alisg/tos-alisg-pv-0037/689890ff6706484797cfbf97cdd52db2/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=24816&bt=12408&ds=4&ft=4b~OyM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=13&rc=am5laG45cjQ1czMzODczNEBpam5laG45cjQ1czMzODczNEBxYWQxMmRjbGhgLS1kMTFzYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&l=20240726032632702565EB3E432D2B5481&btag=e00048000", + UrlExpire: 1721986037, + Version: "4:llm", + }, + { + Format: "webvtt", + LanguageCodeName: "tha-TH", + LanguageID: "30", + Size: 1965, + Source: "MT", + Url: "https://v16-webapp.tiktok.com/cad6f09f6fd1ad914cbb5407c9e32a1d/66a36bf5/video/tos/alisg/tos-alisg-pv-0037/8a4fbdd10065400789e10d58a1f02754/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=24816&bt=12408&ds=4&ft=4b~OyM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=13&rc=am5laG45cjQ1czMzODczNEBpam5laG45cjQ1czMzODczNEBxYWQxMmRjbGhgLS1kMTFzYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&l=20240726032632702565EB3E432D2B5481&btag=e00048000", + UrlExpire: 1721986037, + Version: "4:llm", + }, + { + Format: "webvtt", + LanguageCodeName: "cmn-Hant-CN", + LanguageID: "36", + Size: 1304, + Source: "MT", + Url: "https://v16-webapp.tiktok.com/b5b5d3fd25742b16940451e0449d790a/66a36bf5/video/tos/alisg/tos-alisg-pv-0037/1edde7a062c44ff79b8e251ecfe3113b/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=24816&bt=12408&ds=4&ft=4b~OyM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=13&rc=am5laG45cjQ1czMzODczNEBpam5laG45cjQ1czMzODczNEBxYWQxMmRjbGhgLS1kMTFzYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&l=20240726032632702565EB3E432D2B5481&btag=e00048000", + UrlExpire: 1721986037, + Version: "4:llm", + }, + ], + videoQuality: "normal", + volumeInfo: { + Loudness: -23, + Peak: 0.27861, + }, + width: 576, + zoomCover: { + 240: "https://p16-sign-sg.tiktokcdn.com/tos-alisg-p-0037/f9e2ad3ce5ed4d17a20781bdac3c903c_1718103135~tplv-photomode-zoomcover:240:240.jpeg?lk3s=81f88b70&nonce=25423&refresh_token=dfbf3ed52ed288890e22ad069e9522ef&x-expires=1722135600&x-signature=owBSNcPot4e88zZYv9WRFzz%2Foys%3D&shp=81f88b70&shcp=280c9438", + 480: "https://p16-sign-sg.tiktokcdn.com/tos-alisg-p-0037/f9e2ad3ce5ed4d17a20781bdac3c903c_1718103135~tplv-photomode-zoomcover:480:480.jpeg?lk3s=81f88b70&nonce=49991&refresh_token=ba2c395d994885b46d524c1a2de93951&x-expires=1722135600&x-signature=DfsRX2zo3q9XCS0LngxC7HQFcCA%3D&shp=81f88b70&shcp=280c9438", + 720: "https://p16-sign-sg.tiktokcdn.com/tos-alisg-p-0037/f9e2ad3ce5ed4d17a20781bdac3c903c_1718103135~tplv-photomode-zoomcover:720:720.jpeg?lk3s=81f88b70&nonce=48719&refresh_token=43b5862a367f0c3afeea5c716aab76ae&x-expires=1722135600&x-signature=NAhG0hDjXsslM8AeZh0qLnxmm5U%3D&shp=81f88b70&shcp=280c9438", + 960: "https://p16-sign-sg.tiktokcdn.com/tos-alisg-p-0037/f9e2ad3ce5ed4d17a20781bdac3c903c_1718103135~tplv-photomode-zoomcover:960:960.jpeg?lk3s=81f88b70&nonce=54572&refresh_token=0f6665448538aa1b998121665c818b61&x-expires=1722135600&x-signature=fo8K%2BDq7u8l%2BN4VB13zvMR3PZFE%3D&shp=81f88b70&shcp=280c9438", }, }, + videoSuggestWordsList: { + video_suggest_words_struct: [ + { + hint_text: "Search:", + scene: "comment_top", + words: [ + { + word: "bé mây chụp hình trước chùa", + word_id: "3850415388395388576", + }, + ], + }, + ], + }, }; From 5b89458cc9a45ffc567bb0a5e70af6b1cfeb3a65 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Fri, 26 Jul 2024 12:10:20 +0700 Subject: [PATCH 34/47] update --- scripts/tiktok_batchDownload.css | 3 ++- scripts/tiktok_batchDownload.js | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/scripts/tiktok_batchDownload.css b/scripts/tiktok_batchDownload.css index 0bce2c71..f942cbed 100644 --- a/scripts/tiktok_batchDownload.css +++ b/scripts/tiktok_batchDownload.css @@ -41,11 +41,12 @@ padding: 20px; border-radius: 10px; max-width: 90vw; - max-height: 90vh; + height: 90vh; overflow-y: auto; overflow-x: hidden; display: flex; flex-direction: column; + transition: all 0.3s ease; } .ufs_popup .ufs_popup_header { diff --git a/scripts/tiktok_batchDownload.js b/scripts/tiktok_batchDownload.js index 0d3a412c..c7416d40 100644 --- a/scripts/tiktok_batchDownload.js +++ b/scripts/tiktok_batchDownload.js @@ -77,7 +77,7 @@ export default {
📥 {{totalCount}}
-

Tiktok - Useful Scripts

+

Tiktok - Useful Scripts

Found {{totalCount}} videos

@@ -166,10 +166,10 @@ export default { this.downloading.video + "/" + this.videosToShow.length + - " videos" + " video" ); } - return "Downloaded " + this.videosToShow.length + " videos"; + return "Downloaded " + this.videosToShow.length + " video"; }, audioTitle() { if (this.downloading.audio) { @@ -178,10 +178,10 @@ export default { this.downloading.audio + "/" + this.uniqueAudio.length + - " audios" + " audio" ); } - return "Downloaded " + this.uniqueAudio.length + " audios"; + return "Downloaded " + this.uniqueAudio.length + " audio"; }, uniqueAudio() { const result = new Map(); From 4b4e31a41c184137e0e9fd4a30ffaa6050fad18f Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Fri, 26 Jul 2024 12:13:09 +0700 Subject: [PATCH 35/47] realtime search --- scripts/tiktok_batchDownload.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tiktok_batchDownload.js b/scripts/tiktok_batchDownload.js index c7416d40..a55ed3e3 100644 --- a/scripts/tiktok_batchDownload.js +++ b/scripts/tiktok_batchDownload.js @@ -85,7 +85,7 @@ export default { - + {{search}}
From 9e4c5f228900f166bb0eadb89d1b8cb6af77a209 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Fri, 26 Jul 2024 17:13:23 +0700 Subject: [PATCH 36/47] update --- scripts/content-scripts/ufs_global.js | 12 +- scripts/tiktok_batchDownload.css | 42 +++ scripts/tiktok_batchDownload.js | 495 ++++++-------------------- 3 files changed, 158 insertions(+), 391 deletions(-) diff --git a/scripts/content-scripts/ufs_global.js b/scripts/content-scripts/ufs_global.js index 406fac95..07b65868 100644 --- a/scripts/content-scripts/ufs_global.js +++ b/scripts/content-scripts/ufs_global.js @@ -770,7 +770,7 @@ async function downloadToFolder({ url, fileName, dirHandler, - expectBlobType, + expectBlobTypes, subFolderName = "", }) { if (!url) return false; @@ -778,9 +778,15 @@ async function downloadToFolder({ // const f = getOriginalWindowFunction("fetch"); const res = await fetch(url); const blob = await res.blob(); - if (expectBlobType && blob.type !== expectBlobType) { + // blobtype canbe regex + if ( + expectBlobTypes?.length && + !expectBlobTypes.find((t) => + t?.test ? t.test(blob.type) : t === blob.type + ) + ) { throw new Error( - `Blob type ${blob.type} doesn't match expected type ${expectBlobType}` + `Blob type ${blob.type} doesn't match expected type ${expectBlobTypes}` ); } const fileHandler = await dirHandler.getFileHandle(fileName, { diff --git a/scripts/tiktok_batchDownload.css b/scripts/tiktok_batchDownload.css index f942cbed..c7b1e84c 100644 --- a/scripts/tiktok_batchDownload.css +++ b/scripts/tiktok_batchDownload.css @@ -36,6 +36,7 @@ } .ufs_popup { + position: relative; background: #444; color: #eee; padding: 20px; @@ -110,3 +111,44 @@ .ufs_popup .clickable:hover { background: #666; } + +.ufs_popup .ufs-scroll-to-top { + position: absolute; + bottom: 10px; + right: 10px; + border-radius: 10px; + cursor: pointer; + background: #444; + color: white; + padding: 10px; +} + +/* check boxes */ +.ufs_is_private { + position: absolute; + top: 0; + right: 0; + color: red; + background: black; + padding: 8px; + font-weight: bold; +} + +.ufs_tiktok_checkbox { + z-index: 2; + position: absolute; + top: 0; + right: 0; + width: 50px; + height: 50px; + transition: all 0.1s ease; +} + +.ufs_tiktok_checkbox:hover { + transform: scale(1.3); +} + +.ufs-video-checkbox { + width: 30px; + height: 30px; +} diff --git a/scripts/tiktok_batchDownload.js b/scripts/tiktok_batchDownload.js index a55ed3e3..0280b827 100644 --- a/scripts/tiktok_batchDownload.js +++ b/scripts/tiktok_batchDownload.js @@ -38,7 +38,10 @@ export default { if (json?.itemList) { json.itemList.forEach((_) => { - if (_.video.playAddr) CACHED.videoById.set(_.video.id, _); + if (_.video.playAddr || _.imagePost?.images?.length) + CACHED.videoById.set(_.video.id, _); + + if (_.imagePost?.images?.length) console.log(_); }); } } @@ -77,16 +80,15 @@ export default {
📥 {{totalCount}}
-

Tiktok - Useful Scripts

+

Tiktok - Useful Scripts

Found {{totalCount}} videos

- - - - + + + + - {{search}}
@@ -107,7 +109,9 @@ export default {

No video

-{{v.index}} +{{v.index}}
+ + @@ -124,12 +128,12 @@ export default { {{v.video.duration}}s

- 🎬 Video
- 🖼️ Cover
- + 🎬 Video
+ 🖼️ Cover
+ 👤 Avatar
- + 🎧 Music: {{v.music.title}}

@@ -137,6 +141,8 @@ export default { + +
@@ -156,20 +162,46 @@ export default { sortBy: "index", sortDir: "asc", downloading: {}, + selected: {}, }; }, computed: { + hasSelected() { + return Object.values(this.selected).find((v) => v); + }, + videoToDownload() { + return this.hasSelected + ? this.videosToShow.filter((v) => this.selected[v.id]) + : this.videosToShow; + }, + audioToDownload() { + const list = this.hasSelected + ? this.videosToShow.filter((v) => this.selected[v.id]) + : this.videosToShow; + + // get unique + const result = new Map(); + for (const item of list) { + if (!result.has(item.music.id)) result.set(item.music.id, item); + } + return Array.from(result.values()); + }, videoTitle() { if (this.downloading.video) { return ( "Downloading " + this.downloading.video + "/" + - this.videosToShow.length + + this.videoToDownload.length + " video" ); } - return "Downloaded " + this.videosToShow.length + " video"; + return ( + "Download " + + this.videoToDownload.length + + (this.hasSelected ? " selected" : "") + + " video" + ); }, audioTitle() { if (this.downloading.audio) { @@ -177,18 +209,16 @@ export default { "Downloading " + this.downloading.audio + "/" + - this.uniqueAudio.length + + this.audioToDownload.length + " audio" ); } - return "Downloaded " + this.uniqueAudio.length + " audio"; - }, - uniqueAudio() { - const result = new Map(); - for (const item of this.videosToShow) { - if (!result.has(item.music.id)) result.set(item.music.id, item); - } - return Array.from(result.values()); + return ( + "Download " + + this.audioToDownload.length + + (this.hasSelected ? " selected" : "") + + " audio" + ); }, totalCount() { return this.videos.length; @@ -237,29 +267,50 @@ export default { }, methods: { async downloadVideo() { - const total = this.videosToShow.length; + const total = this.videoToDownload.length; if (!total) return; - const success = 0; - this.downloading.video = true; + let success = 0; await download({ folderName: "tiktok_videos", - expectBlobType: "video/mp4", - data: this.videosToShow.map((_, i) => { - const urlList = - _.video?.bitrateInfo?.find?.( - (b) => b.Bitrate === _.video.bitrate - )?.PlayAddr?.UrlList || []; - const bestUrl = urlList[urlList.length - 1]; - return { - url: bestUrl || _.video.playAddr, - filename: - i + - 1 + - "_" + - UfsGlobal.Utils.sanitizeName(_.id, false) + - ".mp4", - }; - }), + expectBlobTypes: ["video/mp4", "image/jpeg"], + data: this.videoToDownload + .map((_, i) => { + // image + const imgs = _.imagePost?.images; + if (imgs?.length) { + return imgs.map((img, j) => ({ + url: + img.imageURL?.urlList?.[1] || + img.imageURL?.urlList?.[0], + filename: + i + + 1 + + "_" + + (j + 1) + + "_" + + UfsGlobal.Utils.sanitizeName(_.id, false) + + ".jpg", + })); + } + + // video + const urlList = + _.video?.bitrateInfo?.find?.( + (b) => b.Bitrate === _.video.bitrate + )?.PlayAddr?.UrlList || []; + const bestUrl = urlList[urlList.length - 1]; + return { + url: bestUrl || _.video.playAddr, + filename: + i + + 1 + + "_" + + UfsGlobal.Utils.sanitizeName(_.id, false) + + ".mp4", + }; + }) + .flat() + .filter((_) => _.url), onProgressItem: (i, total) => { this.downloading.video = i; }, @@ -267,17 +318,16 @@ export default { success++; }, }); - alert("Downloaded " + success + "/" + total + " videos!"); this.downloading.video = false; + alert("Downloaded " + success + "/" + total + " videos!"); }, async downloadAudio() { - const total = this.uniqueAudio.length; + const total = this.audioToDownload.length; if (!total) return; - this.downloading.audio = true; - const success = 0; + let success = 0; await download({ folderName: "tiktok_musics", - data: this.uniqueAudio.map((_, i) => ({ + data: this.audioToDownload.map((_, i) => ({ url: _.music.playUrl, filename: i + @@ -296,8 +346,8 @@ export default { success++; }, }); - alert("Downloaded " + success + "/" + total + " videos!"); this.downloading.audio = false; + alert("Downloaded " + success + "/" + total + " videos!"); }, downloadJson() { UfsGlobal.Utils.downloadData( @@ -305,6 +355,9 @@ export default { this.videosToShow.length + "_videos_tiktok.json" ); }, + scrollToTop(e) { + e.target.parentElement.scrollTo({ top: 0, behavior: "smooth" }); + }, clear() { if (confirm("Are you sure want to clear all?")) { CACHED.videoById.clear(); @@ -330,7 +383,7 @@ export default { async function download({ folderName = "tiktok", - expectBlobType, + expectBlobTypes, data, onProgressItem, onFinishItem, @@ -347,7 +400,7 @@ export default { url: realUrl, fileName: filename, dirHandler: dir, - expectBlobType, + expectBlobTypes, }); onFinishItem?.(i + 1, data.length); } catch (e) { @@ -355,7 +408,7 @@ export default { } } } - + return; // checkbox on videos let checkboxes = []; @@ -376,15 +429,7 @@ export default { if (isPrivate) { let p = document.createElement("p"); p.innerText = "Riêng tư"; - p.style = [ - "position: absolute;", - "top: 0;", - "right: 0;", - "color: red", - "background: black", - "padding: 8px", - "font-weight: bold", - ].join(";"); + p.className = "ufs_is_private"; node.parentElement.appendChild(p); } else { let url = node.getAttribute("href"); @@ -393,343 +438,17 @@ export default { checkboxes.push(checkbox); } } - - let selected = checkboxes.filter((checkbox) => checkbox.checked).length; - progressDiv.innerText = `Đã chọn ${selected}/${checkboxes.length} video. Bấm nút Get link khi chọn xong nhé.`; }); function createCheckBox(url) { let checkbox = document.createElement("input"); + checkbox.className = "ufs_tiktok_checkbox"; checkbox.type = "checkbox"; checkbox.name = "video"; checkbox.checked = false; checkbox["data-url"] = url; - checkbox.style = - "z-index: 0; position: absolute; top: 0; right: 0; width: 60px; height: 60px;"; return checkbox; } }, }, }; - -const videoMock = { - AIGCDescription: "", - author: { - avatarLarger: - "https://p16-sign-useast2a.tiktokcdn.com/tos-useast2a-avt-0068-giso/ba2aad87bd8bafe7f4a4850d15bc4d47~c5_1080x1080.jpeg?lk3s=a5d48078&nonce=95769&refresh_token=c0ece14ea3a9bbefb2e5c536a37a220e&x-expires=1722135600&x-signature=GTDzdehNU9FLUTENcoB%2Fuu%2FboLk%3D&shp=a5d48078&shcp=81f88b70", - avatarMedium: - "https://p16-sign-useast2a.tiktokcdn.com/tos-useast2a-avt-0068-giso/ba2aad87bd8bafe7f4a4850d15bc4d47~c5_720x720.jpeg?lk3s=a5d48078&nonce=59152&refresh_token=23b71fadc0a9b7128b9ad3964dfbeace&x-expires=1722135600&x-signature=i%2FeSkXqM1e93v9iXJUwZeg7Xpdw%3D&shp=a5d48078&shcp=81f88b70", - avatarThumb: - "https://p16-sign-useast2a.tiktokcdn.com/tos-useast2a-avt-0068-giso/ba2aad87bd8bafe7f4a4850d15bc4d47~c5_100x100.jpeg?lk3s=a5d48078&nonce=59913&refresh_token=a7090d32da45fc22f704e4ca7517ce4d&x-expires=1722135600&x-signature=z5lxF2ivUkUmnwOP7H9gJvH4Cyg%3D&shp=a5d48078&shcp=81f88b70", - commentSetting: 0, - downloadSetting: 0, - duetSetting: 3, - ftc: false, - id: "64700392767", - isADVirtual: false, - isEmbedBanned: false, - nickname: "Kiệt Hà Tịnh", - openFavorite: false, - privateAccount: false, - relation: 0, - secUid: "MS4wLjABAAAAsnkRvwHb4FpMu0RLRx70Sc5TI-jwqRa5Xw_w9zVRP0A", - secret: false, - signature: - "Fb:Ngô Lê Tuấn Kiệt ☘️\nNgười Hà Tịnh ở Biên Hoà ❤️\nĐồ K dùng ở dưới nha👇🏻", - stitchSetting: 3, - uniqueId: "tuankiet.2000", - verified: true, - }, - authorStats: { - diggCount: 10600, - followerCount: 8600000, - followingCount: 161, - friendCount: 0, - heart: 375400000, - heartCount: 375400000, - videoCount: 1656, - }, - collected: false, - contents: [ - { - desc: "Clip này My xin đính chính lần cuối nha mn😌 ", - }, - ], - createTime: 1718103129, - desc: "Clip này My xin đính chính lần cuối nha mn😌 ", - digged: false, - diversificationId: 10004, - duetDisplay: 0, - forFriend: false, - id: "7379196725698841863", - itemCommentStatus: 0, - item_control: { - can_repost: true, - }, - music: { - authorName: "Trần thánh trúc", - coverLarge: - "https://p16-sign-sg.tiktokcdn.com/aweme/1080x1080/tos-alisg-avt-0068/f0e3a91d906cf4b62f3d9033ed4dc227.jpeg?lk3s=a5d48078&nonce=16594&refresh_token=6967b6cb4f75c7f354f810e073fee6a0&x-expires=1722135600&x-signature=5TAkwWXiqjuAVha1QwgkIdQ33Oo%3D&shp=a5d48078&shcp=81f88b70", - coverMedium: - "https://p16-sign-sg.tiktokcdn.com/aweme/720x720/tos-alisg-avt-0068/f0e3a91d906cf4b62f3d9033ed4dc227.jpeg?lk3s=a5d48078&nonce=1126&refresh_token=85d0f238bdd5540f40cd5b4d4176272a&x-expires=1722135600&x-signature=w3E1Oi4ZSsIyQJrxoKlPv%2BaeSDw%3D&shp=a5d48078&shcp=81f88b70", - coverThumb: - "https://p16-sign-sg.tiktokcdn.com/aweme/100x100/tos-alisg-avt-0068/f0e3a91d906cf4b62f3d9033ed4dc227.jpeg?lk3s=a5d48078&nonce=12910&refresh_token=5074541334dc629cc5ecd123a5313b3f&x-expires=1722135600&x-signature=C3uvpe9PTjEH%2FHT8WIn8%2B9RIAVI%3D&shp=a5d48078&shcp=81f88b70", - duration: 44, - id: "7374296585604451073", - original: false, - playUrl: - "https://v16-webapp-prime.tiktok.com/video/tos/alisg/tos-alisg-v-27dcd7/oUOVDf7tUEdQM7wxqKmgFBKWIWJsCcfO5BH8ad/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=0&dr=0&er=0&lr=default&cd=0%7C0%7C0%7C0&br=250&bt=125&ft=GNDpcInz7ThZTIrrXq8Zmo&mime_type=audio_mpeg&qs=6&rc=ZzhlN2ZlNjlmOzVkMzQ4aEBpM2tva3c5cmVmczMzODU8NEBhLzQ2Yl41NmAxMl9hNjBjYSM0czZuMmRjNF9gLS1kMS1zcw%3D%3D&btag=e00088000&expire=1721968037&l=20240726032632702565EB3E432D2B5481&ply_type=3&policy=3&signature=18b1024aebfed96fab659235f53caa1e&tk=7294694872060675074", - title: "nhạc nền - Ngọc Ngân", - }, - officalItem: false, - originalItem: false, - privateItem: false, - secret: false, - shareEnabled: true, - stats: { - collectCount: 12600, - commentCount: 2212, - diggCount: 376100, - playCount: 7900000, - shareCount: 4063, - }, - statsV2: { - collectCount: "12577", - commentCount: "2212", - diggCount: "376100", - playCount: "7900000", - repostCount: "0", - shareCount: "4063", - }, - stitchDisplay: 0, - video: { - VQScore: "70.92", - bitrate: 1430004, - bitrateInfo: [ - { - Bitrate: 1430004, - CodecType: "h264", - GearName: "normal_540_0", - MVMAF: '""', - PlayAddr: { - DataSize: 7939920, - FileCs: "c:0-38026-2fae", - FileHash: "517d6e7d1deb58fb809cd976094e6888", - Height: 1024, - Uri: "v14044g50000cpk2o1fog65k4egbdktg", - UrlKey: "v14044g50000cpk2o1fog65k4egbdktg_h264_540p_1430004", - UrlList: [ - "https://v16-webapp-prime.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/oQiYGHrzNAH2A0CQi06BlILAqrofEUCkCI7wIB/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=2792&bt=1396&cs=0&ds=6&ft=4fUEKM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=0&rc=NTw6Z2VmNDhkPDg3NWY4O0Bpam5laG45cjQ1czMzODczNEBfNl9gYl4xX18xLi8vMzE0YSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&btag=e00088000&expire=1721986037&l=20240726032632702565EB3E432D2B5481&ply_type=2&policy=2&signature=68a902a51943c5d88af812ab9ae59688&tk=tt_chain_token", - "https://v19-webapp-prime.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/oQiYGHrzNAH2A0CQi06BlILAqrofEUCkCI7wIB/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=2792&bt=1396&cs=0&ds=6&ft=4fUEKM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=0&rc=NTw6Z2VmNDhkPDg3NWY4O0Bpam5laG45cjQ1czMzODczNEBfNl9gYl4xX18xLi8vMzE0YSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&btag=e00088000&expire=1721986037&l=20240726032632702565EB3E432D2B5481&ply_type=2&policy=2&signature=68a902a51943c5d88af812ab9ae59688&tk=tt_chain_token", - "https://api.tiktokv.com/aweme/v1/play/?video_id=v14044g50000cpk2o1fog65k4egbdktg&line=0&is_play_url=1&file_id=78456c22241549bab541acebcdb799b2&item_id=7379196725698841863&signaturev3=dmlkZW9faWQ7ZmlsZV9pZDtpdGVtX2lkLjVlOWU2MWVjODNlZDU3OTgwMzkwMWY5Y2Q2NjQzMjhl&shp=9e36835a&shcp=-", - ], - Width: 576, - }, - QualityType: 20, - }, - { - Bitrate: 1261254, - CodecType: "h265_hvc1", - GearName: "adapt_lowest_1080_1", - MVMAF: - '"{\\"v2.0\\": {\\"ori\\": {\\"v1080\\": 89.373, \\"v960\\": 90.574, \\"v864\\": 91.529, \\"v720\\": 93.76}, \\"srv1\\": {\\"v1080\\": -1, \\"v960\\": -1, \\"v864\\": -1, \\"v720\\": -1}}}"', - PlayAddr: { - DataSize: 7002959, - FileCs: "c:0-38272-8f7c", - FileHash: "36cfe328f42240c9201093a1c96df84f", - Height: 1920, - Uri: "v14044g50000cpk2o1fog65k4egbdktg", - UrlKey: "v14044g50000cpk2o1fog65k4egbdktg_bytevc1_1080p_1261254", - UrlList: [ - "https://v16-webapp-prime.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/o0ZFwog2QEpFeFNBCWmEQDtBaId0UtWfgARFzR/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=2462&bt=1231&cs=2&ds=4&ft=4fUEKM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=15&rc=ZTk4ZDVkZDY3Zjo3OzU4NkBpam5laG45cjQ1czMzODczNEBjMV9eL19gNjIxYC0tMGExYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&btag=e00088000&expire=1721986037&l=20240726032632702565EB3E432D2B5481&ply_type=2&policy=2&signature=6703b959137d6977910b969eaf838224&tk=tt_chain_token", - "https://v19-webapp-prime.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/o0ZFwog2QEpFeFNBCWmEQDtBaId0UtWfgARFzR/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=2462&bt=1231&cs=2&ds=4&ft=4fUEKM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=15&rc=ZTk4ZDVkZDY3Zjo3OzU4NkBpam5laG45cjQ1czMzODczNEBjMV9eL19gNjIxYC0tMGExYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&btag=e00088000&expire=1721986037&l=20240726032632702565EB3E432D2B5481&ply_type=2&policy=2&signature=6703b959137d6977910b969eaf838224&tk=tt_chain_token", - "https://api.tiktokv.com/aweme/v1/play/?video_id=v14044g50000cpk2o1fog65k4egbdktg&line=0&is_play_url=1&file_id=43d5320804c74c5493caba22aab546fb&item_id=7379196725698841863&signaturev3=dmlkZW9faWQ7ZmlsZV9pZDtpdGVtX2lkLjYzMWJjOTNmN2E0ZTgxNWJlNTY5NDAzZDJiYTEyMThl&shp=9e36835a&shcp=-", - ], - Width: 1080, - }, - QualityType: 2, - }, - { - Bitrate: 827773, - CodecType: "h265_hvc1", - GearName: "adapt_lower_720_1", - MVMAF: - '"{\\"v2.0\\": {\\"ori\\": {\\"v1080\\": 81.444, \\"v960\\": 84.686, \\"v864\\": 86.95, \\"v720\\": 90.786}, \\"srv1\\": {\\"v1080\\": 89.324, \\"v960\\": 92.945, \\"v864\\": 94.345, \\"v720\\": 96.716}}}"', - PlayAddr: { - DataSize: 4596110, - FileCs: "c:0-38273-b3cb", - FileHash: "9a8275de7c719b846208535a4bda1209", - Height: 1280, - Uri: "v14044g50000cpk2o1fog65k4egbdktg", - UrlKey: "v14044g50000cpk2o1fog65k4egbdktg_bytevc1_720p_827773", - UrlList: [ - "https://v16-webapp-prime.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/oUEY06iEIAwzCaNf6Liqoph27UYCaCAIBABIQ0/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=1616&bt=808&cs=2&ds=3&ft=4fUEKM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=14&rc=aTs1O2c8ODQ1ZWg1aTNkOUBpam5laG45cjQ1czMzODczNEAuXjAuXi4zXmExYF5gMmEzYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&btag=e00088000&expire=1721986037&l=20240726032632702565EB3E432D2B5481&ply_type=2&policy=2&signature=aa0c545142bafe4efd480827937e9a44&tk=tt_chain_token", - "https://v19-webapp-prime.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/oUEY06iEIAwzCaNf6Liqoph27UYCaCAIBABIQ0/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=1616&bt=808&cs=2&ds=3&ft=4fUEKM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=14&rc=aTs1O2c8ODQ1ZWg1aTNkOUBpam5laG45cjQ1czMzODczNEAuXjAuXi4zXmExYF5gMmEzYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&btag=e00088000&expire=1721986037&l=20240726032632702565EB3E432D2B5481&ply_type=2&policy=2&signature=aa0c545142bafe4efd480827937e9a44&tk=tt_chain_token", - "https://api.tiktokv.com/aweme/v1/play/?video_id=v14044g50000cpk2o1fog65k4egbdktg&line=0&is_play_url=1&file_id=66da50cfa2ce4bd2a16519ad1832a81f&item_id=7379196725698841863&signaturev3=dmlkZW9faWQ7ZmlsZV9pZDtpdGVtX2lkLjYzZWJhYzdmMmMzZWQ1ZDk5MzU4OTIxNGZkODcyOTQ2&shp=9e36835a&shcp=-", - ], - Width: 720, - }, - QualityType: 14, - }, - { - Bitrate: 694640, - CodecType: "h265_hvc1", - GearName: "adapt_540_1", - MVMAF: - '"{\\"v2.0\\": {\\"ori\\": {\\"v1080\\": 74.423, \\"v960\\": 78.739, \\"v864\\": 81.884, \\"v720\\": 86.639}, \\"srv1\\": {\\"v1080\\": 87.574, \\"v960\\": 91.778, \\"v864\\": 93.224, \\"v720\\": 96.069}}}"', - PlayAddr: { - DataSize: 3856907, - FileCs: "c:0-38273-9698", - FileHash: "dd46f2309aa588691b3e2ae5fd3c640b", - Height: 1024, - Uri: "v14044g50000cpk2o1fog65k4egbdktg", - UrlKey: "v14044g50000cpk2o1fog65k4egbdktg_bytevc1_540p_694640", - UrlList: [ - "https://v16-webapp-prime.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/owCLiImNwY0IAICRzDEZ6YiHC2AoqBfLA0Q7Br/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=1356&bt=678&cs=2&ds=6&ft=4fUEKM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=11&rc=ZDQ7NWc2ODw7ZDk8Ojk2O0Bpam5laG45cjQ1czMzODczNEAuXy40XmFeNjExYDMuLmFeYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&btag=e00088000&expire=1721986037&l=20240726032632702565EB3E432D2B5481&ply_type=2&policy=2&signature=70dcd514149eead3286eebae6af4d4eb&tk=tt_chain_token", - "https://v19-webapp-prime.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/owCLiImNwY0IAICRzDEZ6YiHC2AoqBfLA0Q7Br/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=1356&bt=678&cs=2&ds=6&ft=4fUEKM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=11&rc=ZDQ7NWc2ODw7ZDk8Ojk2O0Bpam5laG45cjQ1czMzODczNEAuXy40XmFeNjExYDMuLmFeYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&btag=e00088000&expire=1721986037&l=20240726032632702565EB3E432D2B5481&ply_type=2&policy=2&signature=70dcd514149eead3286eebae6af4d4eb&tk=tt_chain_token", - "https://api.tiktokv.com/aweme/v1/play/?video_id=v14044g50000cpk2o1fog65k4egbdktg&line=0&is_play_url=1&file_id=a8d3161769c64a4899a5d3ad7218b11a&item_id=7379196725698841863&signaturev3=dmlkZW9faWQ7ZmlsZV9pZDtpdGVtX2lkLmI5ZDRmZDI1ZWYwNzNlNDE3ZTllNmQ4MDhmYjUyZTU4&shp=9e36835a&shcp=-", - ], - Width: 576, - }, - QualityType: 28, - }, - ], - codecType: "h264", - cover: - "https://p16-sign-sg.tiktokcdn.com/obj/tos-alisg-p-0037/f9e2ad3ce5ed4d17a20781bdac3c903c_1718103135?lk3s=81f88b70&nonce=53184&refresh_token=308e36058f33a46042f793fab36125ec&x-expires=1722135600&x-signature=82UdfuOqMxxwSdQEel0KWbGw9Kc%3D&shp=81f88b70&shcp=280c9438", - definition: "540p", - downloadAddr: - "https://v16-webapp-prime.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/ogfIICYI70C6NI6o0HAiE7CMwQibAzABN2BqXi/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=3084&bt=1542&cs=0&ds=3&ft=4fUEKM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=0&rc=NjY6NWllZGZmOjU2ZTY1ZkBpam5laG45cjQ1czMzODczNEAuYTUvYl5jXjQxMmMvXi02YSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&btag=e00088000&expire=1721986037&l=20240726032632702565EB3E432D2B5481&ply_type=2&policy=2&signature=0f49a63b5e17e036e2f70ca3b3345c62&tk=tt_chain_token", - duration: 44, - dynamicCover: - "https://p16-sign-sg.tiktokcdn.com/obj/tos-alisg-p-0037/63d392accbe548f5ae433b6d0c32de94_1718103135?lk3s=81f88b70&nonce=14449&refresh_token=56eabe5d508e4ac55d6f6a2810ce5d19&x-expires=1722135600&x-signature=PZct%2BEpR8IIll4GKxoCXR9z0Y5k%3D&shp=81f88b70&shcp=280c9438", - encodeUserTag: "", - encodedType: "normal", - format: "mp4", - height: 1024, - id: "7379196725698841863", - originCover: - "https://p16-sign-sg.tiktokcdn.com/obj/tos-alisg-p-0037/83e3606da0ec47dc87d0af4b05878a8f_1718103131?lk3s=81f88b70&x-expires=1722135600&x-signature=VLNP0WvEQn83f78RIvlwzeq7sAM%3D&shp=81f88b70&shcp=280c9438", - playAddr: - "https://v16-webapp-prime.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/oQiYGHrzNAH2A0CQi06BlILAqrofEUCkCI7wIB/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=2792&bt=1396&cs=0&ds=6&ft=4fUEKM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=0&rc=NTw6Z2VmNDhkPDg3NWY4O0Bpam5laG45cjQ1czMzODczNEBfNl9gYl4xX18xLi8vMzE0YSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&btag=e00088000&expire=1721986037&l=20240726032632702565EB3E432D2B5481&ply_type=2&policy=2&signature=68a902a51943c5d88af812ab9ae59688&tk=tt_chain_token", - ratio: "540p", - subtitleInfos: [ - { - Format: "webvtt", - LanguageCodeName: "vie-VN", - LanguageID: "10", - Size: 1220, - Source: "ASR", - Url: "https://v16-webapp.tiktok.com/a5424d768a075e69a055e72402152862/66a36bf5/video/tos/alisg/tos-alisg-pv-0037/03be2f31798f45059ea666ca47350470/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=24816&bt=12408&ds=4&ft=4b~OyM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=13&rc=am5laG45cjQ1czMzODczNEBpam5laG45cjQ1czMzODczNEBxYWQxMmRjbGhgLS1kMTFzYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&l=20240726032632702565EB3E432D2B5481&btag=e00048000", - UrlExpire: 1721986037, - Version: "1:llm", - }, - { - Format: "webvtt", - LanguageCodeName: "vie-VN", - LanguageID: "10", - Size: 1217, - Source: "ASR", - Url: "https://v16-webapp.tiktok.com/24a3a7730e8328eb2861c5510b57477a/66a36bf5/video/tos/alisg/tos-alisg-pv-0037/120af213ab354198941ef848b2c9a420/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=24816&bt=12408&ds=4&ft=4b~OyM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=13&rc=am5laG45cjQ1czMzODczNEBpam5laG45cjQ1czMzODczNEBxYWQxMmRjbGhgLS1kMTFzYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&l=20240726032632702565EB3E432D2B5481&btag=e00048000", - UrlExpire: 1721986037, - Version: "1:whisper_lid", - }, - { - Format: "webvtt", - LanguageCodeName: "eng-US", - LanguageID: "2", - Size: 1428, - Source: "MT", - Url: "https://v16-webapp.tiktok.com/63e9170fe4dd78f2271e9ee930796ce7/66a36bf5/video/tos/alisg/tos-alisg-pv-0037/56f87d05daff41a19a83c9fe3fccda6c/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=24816&bt=12408&ds=4&ft=4b~OyM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=13&rc=am5laG45cjQ1czMzODczNEBpam5laG45cjQ1czMzODczNEBxYWQxMmRjbGhgLS1kMTFzYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&l=20240726032632702565EB3E432D2B5481&btag=e00048000", - UrlExpire: 1721986037, - Version: "4:llm", - }, - { - Format: "webvtt", - LanguageCodeName: "cmn-Hans-CN", - LanguageID: "1", - Size: 635, - Source: "MT", - Url: "https://v16-webapp.tiktok.com/e34d44d91686f79e82f69a9cb4800a51/66a36bf5/video/tos/alisg/tos-alisg-pv-0037/33521bbb474a41abbd1a1deb02888ce6/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=24816&bt=12408&ds=4&ft=4b~OyM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=13&rc=am5laG45cjQ1czMzODczNEBpam5laG45cjQ1czMzODczNEBxYWQxMmRjbGhgLS1kMTFzYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&l=20240726032632702565EB3E432D2B5481&btag=e00048000", - UrlExpire: 1721986037, - Version: "4:llm", - }, - { - Format: "webvtt", - LanguageCodeName: "spa-ES", - LanguageID: "9", - Size: 1282, - Source: "MT", - Url: "https://v16-webapp.tiktok.com/89921b1bde0d325c8e92a1dffffa7501/66a36bf5/video/tos/alisg/tos-alisg-pv-0037/948dd14987094778a515dd944d2e0864/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=24816&bt=12408&ds=4&ft=4b~OyM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=13&rc=am5laG45cjQ1czMzODczNEBpam5laG45cjQ1czMzODczNEBxYWQxMmRjbGhgLS1kMTFzYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&l=20240726032632702565EB3E432D2B5481&btag=e00048000", - UrlExpire: 1721986037, - Version: "4:llm", - }, - { - Format: "webvtt", - LanguageCodeName: "kor-KR", - LanguageID: "4", - Size: 1668, - Source: "MT", - Url: "https://v16-webapp.tiktok.com/329afbeb7fdadb3d9fefa49ae9ab2f0e/66a36bf5/video/tos/alisg/tos-alisg-pv-0037/6c504e2df8544a798cd0687220cd4321/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=24816&bt=12408&ds=4&ft=4b~OyM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=13&rc=am5laG45cjQ1czMzODczNEBpam5laG45cjQ1czMzODczNEBxYWQxMmRjbGhgLS1kMTFzYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&l=20240726032632702565EB3E432D2B5481&btag=e00048000", - UrlExpire: 1721986037, - Version: "4:llm", - }, - { - Format: "webvtt", - LanguageCodeName: "jpn-JP", - LanguageID: "3", - Size: 1443, - Source: "MT", - Url: "https://v16-webapp.tiktok.com/bad1df16e007e8f7a5886c161afff1d3/66a36bf5/video/tos/alisg/tos-alisg-pv-0037/689890ff6706484797cfbf97cdd52db2/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=24816&bt=12408&ds=4&ft=4b~OyM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=13&rc=am5laG45cjQ1czMzODczNEBpam5laG45cjQ1czMzODczNEBxYWQxMmRjbGhgLS1kMTFzYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&l=20240726032632702565EB3E432D2B5481&btag=e00048000", - UrlExpire: 1721986037, - Version: "4:llm", - }, - { - Format: "webvtt", - LanguageCodeName: "tha-TH", - LanguageID: "30", - Size: 1965, - Source: "MT", - Url: "https://v16-webapp.tiktok.com/cad6f09f6fd1ad914cbb5407c9e32a1d/66a36bf5/video/tos/alisg/tos-alisg-pv-0037/8a4fbdd10065400789e10d58a1f02754/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=24816&bt=12408&ds=4&ft=4b~OyM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=13&rc=am5laG45cjQ1czMzODczNEBpam5laG45cjQ1czMzODczNEBxYWQxMmRjbGhgLS1kMTFzYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&l=20240726032632702565EB3E432D2B5481&btag=e00048000", - UrlExpire: 1721986037, - Version: "4:llm", - }, - { - Format: "webvtt", - LanguageCodeName: "cmn-Hant-CN", - LanguageID: "36", - Size: 1304, - Source: "MT", - Url: "https://v16-webapp.tiktok.com/b5b5d3fd25742b16940451e0449d790a/66a36bf5/video/tos/alisg/tos-alisg-pv-0037/1edde7a062c44ff79b8e251ecfe3113b/?a=1988&bti=ODszNWYuMDE6&ch=0&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C&cv=1&br=24816&bt=12408&ds=4&ft=4b~OyM3a8Zmo0Qoq--4jVHwB4pWrKsd.&mime_type=video_mp4&qs=13&rc=am5laG45cjQ1czMzODczNEBpam5laG45cjQ1czMzODczNEBxYWQxMmRjbGhgLS1kMTFzYSNxYWQxMmRjbGhgLS1kMTFzcw%3D%3D&l=20240726032632702565EB3E432D2B5481&btag=e00048000", - UrlExpire: 1721986037, - Version: "4:llm", - }, - ], - videoQuality: "normal", - volumeInfo: { - Loudness: -23, - Peak: 0.27861, - }, - width: 576, - zoomCover: { - 240: "https://p16-sign-sg.tiktokcdn.com/tos-alisg-p-0037/f9e2ad3ce5ed4d17a20781bdac3c903c_1718103135~tplv-photomode-zoomcover:240:240.jpeg?lk3s=81f88b70&nonce=25423&refresh_token=dfbf3ed52ed288890e22ad069e9522ef&x-expires=1722135600&x-signature=owBSNcPot4e88zZYv9WRFzz%2Foys%3D&shp=81f88b70&shcp=280c9438", - 480: "https://p16-sign-sg.tiktokcdn.com/tos-alisg-p-0037/f9e2ad3ce5ed4d17a20781bdac3c903c_1718103135~tplv-photomode-zoomcover:480:480.jpeg?lk3s=81f88b70&nonce=49991&refresh_token=ba2c395d994885b46d524c1a2de93951&x-expires=1722135600&x-signature=DfsRX2zo3q9XCS0LngxC7HQFcCA%3D&shp=81f88b70&shcp=280c9438", - 720: "https://p16-sign-sg.tiktokcdn.com/tos-alisg-p-0037/f9e2ad3ce5ed4d17a20781bdac3c903c_1718103135~tplv-photomode-zoomcover:720:720.jpeg?lk3s=81f88b70&nonce=48719&refresh_token=43b5862a367f0c3afeea5c716aab76ae&x-expires=1722135600&x-signature=NAhG0hDjXsslM8AeZh0qLnxmm5U%3D&shp=81f88b70&shcp=280c9438", - 960: "https://p16-sign-sg.tiktokcdn.com/tos-alisg-p-0037/f9e2ad3ce5ed4d17a20781bdac3c903c_1718103135~tplv-photomode-zoomcover:960:960.jpeg?lk3s=81f88b70&nonce=54572&refresh_token=0f6665448538aa1b998121665c818b61&x-expires=1722135600&x-signature=fo8K%2BDq7u8l%2BN4VB13zvMR3PZFE%3D&shp=81f88b70&shcp=280c9438", - }, - }, - videoSuggestWordsList: { - video_suggest_words_struct: [ - { - hint_text: "Search:", - scene: "comment_top", - words: [ - { - word: "bé mây chụp hình trước chùa", - word_id: "3850415388395388576", - }, - ], - }, - ], - }, -}; From 92b5724e698503419886f0a36f8825c6dcf4b7d5 Mon Sep 17 00:00:00 2001 From: HoangTran <99.hoangtran@gmail.com> Date: Fri, 26 Jul 2024 23:15:52 +0700 Subject: [PATCH 37/47] update UI --- scripts/tiktok_batchDownload.css | 33 ++++++++-- scripts/tiktok_batchDownload.js | 108 +++++++++++++++++++------------ 2 files changed, 97 insertions(+), 44 deletions(-) diff --git a/scripts/tiktok_batchDownload.css b/scripts/tiktok_batchDownload.css index c7b1e84c..b9575d00 100644 --- a/scripts/tiktok_batchDownload.css +++ b/scripts/tiktok_batchDownload.css @@ -51,8 +51,10 @@ } .ufs_popup .ufs_popup_header { - align-self: flex-end; padding: 10px; + width: 100%; + display: flex; + justify-content: flex-end; } .ufs_popup .table_wrap { @@ -85,7 +87,7 @@ } .ufs_popup button { - padding: 5px; + padding: 5px 10px; background: #333; color: #eee; border: 1px solid #777; @@ -112,7 +114,7 @@ background: #666; } -.ufs_popup .ufs-scroll-to-top { +.ufs_popup .ufs_scroll_top { position: absolute; bottom: 10px; right: 10px; @@ -148,7 +150,30 @@ transform: scale(1.3); } -.ufs-video-checkbox { +.ufs_video_checkbox { width: 30px; height: 30px; } + +.ufs_dropdown { + position: relative; + display: flex; + flex-direction: column; + justify-content: center; +} + +.ufs_dropdown .ufs_dropdown_content { + visibility: hidden; + overflow: hidden; + position: absolute; + width: max-content; + top: 100%; + z-index: 2; + display: flex; + flex-direction: column; +} + +.ufs_dropdown .ufs_dropdown_trigger:hover~.ufs_dropdown_content, +.ufs_dropdown .ufs_dropdown_content:hover { + visibility: visible; +} diff --git a/scripts/tiktok_batchDownload.js b/scripts/tiktok_batchDownload.js index 0280b827..73ab1415 100644 --- a/scripts/tiktok_batchDownload.js +++ b/scripts/tiktok_batchDownload.js @@ -1,5 +1,6 @@ import { UfsGlobal } from "./content-scripts/ufs_global.js"; import { hookFetch } from "./libs/ajax-hook/index.js"; +import { scrollToVeryEnd } from "./scrollToVeryEnd.js"; export default { icon: "https://www.tiktok.com/favicon.ico", @@ -84,11 +85,21 @@ export default {

Found {{totalCount}} videos

- - - - - + +
+ +
+ +
+
+
+ +
+ + +
+
+
@@ -105,44 +116,44 @@ export default { - -

No video

- - -{{v.index}}
- - - - - - - -

{{v.desc}}

- - - {{v.author.nickname}}
- {{v.author.uniqueId}}
- {{v.author.id}} - -{{format(v.stats.playCount)}} -{{v.video.duration}}s - -

- 🎬 Video
- 🖼️ Cover
- - 👤 Avatar -
- - 🎧 Music: {{v.music.title}} + +

No video

+ + + {{v.index}}
+ + + +
+ -

- - + +

{{v.desc}}

+ + + {{v.author.nickname}}
+ {{v.author.uniqueId}}
+ {{v.author.id}} + + {{format(v.stats.playCount)}} + {{v.video.duration}}s + +

+ 🎬 Video
+ 🖼️ Cover
+ + 👤 Avatar +
+ + 🎧 Music: {{v.music.title}} + +

+ + - +
@@ -166,8 +177,16 @@ export default { }; }, computed: { + selectedIds() { + return Object.entries(this.selected) + .filter((v) => v[1]) + .map((v) => v[0]); + }, + selectedCount() { + return Object.values(this.selected).filter((v) => v).length; + }, hasSelected() { - return Object.values(this.selected).find((v) => v); + return this.selectedCount > 0; }, videoToDownload() { return this.hasSelected @@ -355,9 +374,18 @@ export default { this.videosToShow.length + "_videos_tiktok.json" ); }, + scrollToVeryEnd() { + setTimeout(() => scrollToVeryEnd(), 100); + }, scrollToTop(e) { e.target.parentElement.scrollTo({ top: 0, behavior: "smooth" }); }, + clearSelected() { + this.selectedIds.forEach((vidId) => { + CACHED.videoById.delete(vidId); + }); + this.selected = {}; + }, clear() { if (confirm("Are you sure want to clear all?")) { CACHED.videoById.clear(); From 9215a86aa534af0e7dc2303383aea4e00ac7d20e Mon Sep 17 00:00:00 2001 From: HoangTran <99.hoangtran@gmail.com> Date: Sat, 27 Jul 2024 23:33:18 +0700 Subject: [PATCH 38/47] folder name --- scripts/tiktok_batchDownload.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/scripts/tiktok_batchDownload.js b/scripts/tiktok_batchDownload.js index 73ab1415..03ac665e 100644 --- a/scripts/tiktok_batchDownload.js +++ b/scripts/tiktok_batchDownload.js @@ -74,6 +74,15 @@ export default { document.documentElement.appendChild(div); const formatter = UfsGlobal.Utils.getNumberFormatter("compactShort"); + function getNow() { + // return year + month + day + hour + minute + second + const day = new Date(); + return ( + [day.getFullYear(), day.getMonth() + 1, day.getDate()].join("-") + + "_" + + [day.getHours(), day.getMinutes(), day.getSeconds()].join("-") + ); + } const app = new Vue({ template: /*html*/ ` @@ -290,7 +299,7 @@ export default { if (!total) return; let success = 0; await download({ - folderName: "tiktok_videos", + folderName: "tiktok_videos_" + getNow(), expectBlobTypes: ["video/mp4", "image/jpeg"], data: this.videoToDownload .map((_, i) => { @@ -345,7 +354,7 @@ export default { if (!total) return; let success = 0; await download({ - folderName: "tiktok_musics", + folderName: "tiktok_musics_" + getNow(), data: this.audioToDownload.map((_, i) => ({ url: _.music.playUrl, filename: From cb357c43b3c8718efcc6db7fa9476f625b7705fe Mon Sep 17 00:00:00 2001 From: HoangTran <99.hoangtran@gmail.com> Date: Sun, 28 Jul 2024 00:08:22 +0700 Subject: [PATCH 39/47] config for magnify image --- scripts/magnify_image.js | 78 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/scripts/magnify_image.js b/scripts/magnify_image.js index 228c6471..ae8b68cd 100644 --- a/scripts/magnify_image.js +++ b/scripts/magnify_image.js @@ -6,6 +6,22 @@ const contextMenuId = "magnify-image"; const CACHED = { mouse: { x: 0, y: 0 }, + configSize: null, +}; + +const MagnifySizeKey = "ufs_magnify_image_size"; +const getConfigSize = async () => { + if (!CACHED.configSize) { + const { Storage } = await import("./helpers/utils.js"); + const data = await Storage.get(MagnifySizeKey); + CACHED.configSize = data?.split("x") || [20, 20]; + } + return CACHED.configSize; +}; +const setConfigSize = async (width, height) => { + const { Storage } = await import("./helpers/utils.js"); + CACHED.configSize = [width, height]; + return Storage.set(MagnifySizeKey, width + "x" + height); }; export default { @@ -56,6 +72,62 @@ export default { "2024-06-07": "support video frame + right click anywhere + magnify on hover + search by images", }, + buttons: [ + { + icon: '', + name: { + vi: "Cài đặt", + en: "Setting", + }, + onClick: async () => { + const { t } = await import("../popup/helpers/lang.js"); + const [width, height] = await getConfigSize(); + + const result = await Swal.fire({ + title: t({ vi: "Cài đặt phóng to", en: "Magnify Setting" }), + html: ` +

${t({ + vi: "Chỉ hiện nút phóng to cho hình ảnh có kích thước lớn hơn:", + en: "Only show the magnify button when the image's size is larger than:", + })}

+ X + + `, + preConfirm: () => { + return [ + document.getElementById("swal-input1").value, + document.getElementById("swal-input2").value, + ]; + }, + }); + + if (result.isConfirmed) { + const [width, height] = result.value; + await setConfigSize(width, height); + Swal.fire({ + icon: "success", + title: t({ vi: "Thành công", en: "Success" }), + text: t({ + vi: "Cài đặt phóng to đã được cập nhật.", + en: "Magnify setting has been updated.", + }), + }); + } + }, + }, + ], popupScript: { onClick: () => { @@ -138,7 +210,11 @@ export default { addToDom(); UfsGlobal.DOM.onElementRemoved(div, addToDom); - window.addEventListener("mouseover", (e) => { + window.addEventListener("mouseover", async (e) => { + const [width, height] = await getConfigSize(); + if (e.target.clientWidth < width || e.target.clientHeight < height) + return; + let srcs = getImgSrcsFromElement(e.target); if (!srcs?.length) { div.classList.toggle("hide", e.target !== div); From c18848ef40247967b51775c728db128a69ebaa9a Mon Sep 17 00:00:00 2001 From: HoangTran <99.hoangtran@gmail.com> Date: Sun, 28 Jul 2024 00:47:00 +0700 Subject: [PATCH 40/47] . --- working_note.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/working_note.md b/working_note.md index 21554546..3f7a3f85 100644 --- a/working_note.md +++ b/working_note.md @@ -1,6 +1,6 @@ # WORKING NOTES -## 01/07/2024 - ? +## 01/07/2024 - 28/7/2024 - [x] Tải video bằng cách record??? => cannot record full video @@ -8,7 +8,7 @@ - [ ] good feature - global video speed -- [ ] good feature - medium unlock +- [x] good feature - medium unlock - [ ] good feature - facebook adblocker @@ -20,7 +20,7 @@ - [x] add this beautiful video downloader tool -- [ ] savefile using new api +- [x] savefile using new api - [x] hotfix - use chrome.downloads instead of library => can not apply to all scripts From fb2a065e12f7f23c8976450d9fbe9eb1808711d4 Mon Sep 17 00:00:00 2001 From: HoangTran <99.hoangtran@gmail.com> Date: Sun, 28 Jul 2024 01:07:22 +0700 Subject: [PATCH 41/47] show donate --- README-en.md | 8 ++++++-- README.md | 8 ++++++-- popup/index.js | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/README-en.md b/README-en.md index f4975c91..eb3a7607 100644 --- a/README-en.md +++ b/README-en.md @@ -6,6 +6,7 @@ - [Useful scripts - Chrome extension](#useful-scripts---chrome-extension) - [Overview](#overview) + - [Demo](#demo) - [Change logs](#change-logs) - [ScreenShots](#screenshots) - [Install](#install) @@ -21,7 +22,9 @@ An extension includes a lot of small extensions. Make your life easier. - Please join [FACEBOOK GROUP](https://www.facebook.com/groups/1154059318582088) of this extension -- View list all scripts [HERE](./md/LIST_SCRIPTS_EN.md) +## Demo + +Try online [Here](https://hoangtran0410.github.io/useful-script/popup/popup.html) ## Change logs @@ -29,11 +32,12 @@ An extension includes a lot of small extensions. Make your life easier. Current Versions: -- **v1.69**: small update (14/07/2024) +- **v1.7**: tiktok update 28/07/2024
Old versions +- v1.69: small update (14/07/2024) - v1.68: big facebook update (01/07/2024) - v1.67 - huge update (29/05/2024) - v1.66 - big update (27/04/2024) diff --git a/README.md b/README.md index 07526738..fd9b2f20 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ - [Useful scripts - Chrome extension](#useful-scripts---chrome-extension) - [Giới thiệu](#giới-thiệu) + - [Demo](#demo) - [Phiên bản](#phiên-bản) - [Ảnh chụp màn hình](#ảnh-chụp-màn-hình) - [Cài đặt](#cài-đặt) @@ -21,7 +22,9 @@ Donate? Muốn hỗ trợ mình 1 ly cafe <3 [Donate tại đây](https://github - Hãy tham gia ngay [GROUP FACEBOOK](https://www.facebook.com/groups/1154059318582088) của tiện ích -- Xem tất cả chức năng [TẠI ĐÂY](./md/LIST_SCRIPTS_VI.md) +## Demo + +Dùng thử online [Tại đây](https://hoangtran0410.github.io/useful-script/popup/popup.html) ## Phiên bản @@ -29,11 +32,12 @@ Xem [Lịch sử cập nhật](/md/CHANGELOGS.md) Phiên bản hiện tại: -- **v1.69**: small update (14/07/2024) +- **v1.7**: tiktok update 28/07/2024
Xem phiên bản cũ hơn +- v1.69: small update (14/07/2024) - v1.68: bản cập nhật facebook lớn (01/07/2024) - v1.67 - bản cập nhật siêu lớn (29/05/2024) - v1.66 - bản cập nhật lớn (27/04/2024) diff --git a/popup/index.js b/popup/index.js index 3a4e13b9..73ac9701 100644 --- a/popup/index.js +++ b/popup/index.js @@ -1013,6 +1013,43 @@ function initScrollToTop() { // }); } +async function initShowDonate() { + const clickedDonate = await Storage.get("clickedDonate"); + let count = (await Storage.get("openPopupCount")) || 0; + count++; + Storage.set("openPopupCount", count); + if (!clickedDonate && count > 0 && count % 10 === 0) { + const res = await Swal.fire({ + icon: "info", + title: t({ + vi: "Cảm ơn bạn tin dùng", + en: "Thanks for using Useful-scripts", + }), + text: t({ + vi: "Useful-scripts là miễn phí. Nhưng nếu bạn thích nó, bạn có thể hỗ trợ mình 1 ly cà phê. Một đồng cũng đáng quý 💓", + en: "It's free. But you can support me if you like. I'll appreciate if you give me some love 💓", + }), + confirmButtonText: "Donate", + showCancelButton: true, + cancelButtonText: t({ vi: "Để sau", en: "Later" }), + showDenyButton: true, + denyButtonText: t({ vi: "Tặng sao", en: "Star github" }), + reverseButtons: true, + focusConfirm: true, + }); + if (res.isConfirmed) { + Storage.set("clickedDonate", true); + window.open( + "https://hoangtran0410.github.io/HoangTran0410/DONATE", + "_blank" + ); + } + if (res.isDenied) { + window.open("https://github.com/HoangTran0410/useful-script", "_blank"); + } + } +} + function saveScroll() { const scrollY = window.scrollY; Storage.set("popupScrollY", scrollY); @@ -1056,4 +1093,5 @@ window.addEventListener("scroll", onScrollEnd); }); checkForUpdate(); + initShowDonate(); })(); From 221633c4444b9159dadc3a6f7678c6ba7ba5e211 Mon Sep 17 00:00:00 2001 From: HoangTran <99.hoangtran@gmail.com> Date: Sun, 28 Jul 2024 01:09:09 +0700 Subject: [PATCH 42/47] refactor --- md/LIST_SCRIPTS_EN.md | 1850 -------------------------------------- md/LIST_SCRIPTS_VI.md | 1849 ------------------------------------- scripts/magnify_image.js | 1 + 3 files changed, 1 insertion(+), 3699 deletions(-) delete mode 100644 md/LIST_SCRIPTS_EN.md delete mode 100644 md/LIST_SCRIPTS_VI.md diff --git a/md/LIST_SCRIPTS_EN.md b/md/LIST_SCRIPTS_EN.md deleted file mode 100644 index bbab297b..00000000 --- a/md/LIST_SCRIPTS_EN.md +++ /dev/null @@ -1,1850 +0,0 @@ -### Search - -
- 1. There's an AI for that - - [View source](/scripts/recommend_theresanaiforthat.js) - - Collection of thousand of AI tools. Easy to search by category - -
-
- 2. Time.is - Check your time - - [View source](/scripts/recommend_timeis.js) - - Exact time for any time zone. - -
-
- 3. Google trending - See what trending now - - [View source](/scripts/recommend_googleTrending.js) - - See what people are searching on Google. Top treding every day, realtime. - -
-
- 4. Find alternative web - - [View source](/scripts/similarWeb.js) - - SimilarWeb - Access behind-the-scenes analytics for every site online - -
-
- 5. Bypass limit in SimilarWeb - - [View source](/scripts/similarWeb_bypassLimit.js) - - You can use SimilarWeb forever without login.

How it work:

Your cookies will be deleted each times enter this web.
- - ![](/scripts/similarWeb_bypassLimit.png) - -
-
- 6. Find shared login - - [View source](/scripts/search_sharedAccount.js) - - Get free shared account on internet - -
-
- 7. Internet archive - Free library - - [View source](/scripts/recommend_archive.js) - - Non-profit library of millions of free books, movies, software, music, websites, and more. - - ![](/scripts/internet_archive.png) - -
-
- 8. Wappalyzer - view website stacks - - [View source](/scripts/recommend_wappalyzer.js) - - Technology that current website is using - -
-
- 9. Who.is - - [View source](/scripts/whois.js) - - Want to find out who owns a domain? Click on this! - -
-
- 10. See web meta info (SEO) - - [View source](/scripts/viewWebMetaInfo.js) - - Instantly shows meta information about the current site in an on-page iFrame. - -
-
- 11. Top global treding music? - - [View source](/scripts/recommend_search_musicTreding.js) - - The web to find all kinds of music-related data. - -
-
- 12. Where to find papers/books/pdf/... - - [View source](/scripts/search_paperWhere.js) - - Learn more and more - -
-
- 13. Search guitar chords - - [View source](/scripts/search_hopamchuan.js) - - Search guitar chords - -
-
- 14. Dowfor - Check web die - - [View source](/scripts/checkWebDie.js) - - Check web die using downforeveryoneorjustme - -
-
- 15. DownDetector - View report web crash - - [View source](/scripts/downDetector.js) - - View web bug reports - -
-
- 16. Open wayback url - - [View source](/scripts/openWaybackUrl.js) - - Open wayback url for website - -
-
- 17. Archive the current Page online - - [View source](/scripts/archiveToday.js) - - Creates an archive of the current page on archive.today. - -
-
- 18. Search Userscripts - - [View source](/scripts/recommend_search_userscript.js) - - Search Userscripts on Usersript.zone - -
- -### Download - ---- All in one --- -
- 19. Cobalt - Media downloader - - [View source](/scripts/recommend_cobalt.js) - - Support youtube, tiktok, instagram, twitter/x, bilibili, twitch, vimeo, soundcloud, dailymotion, pinterest, reddit, tumblr, ... - -
-
- 20. Save All Video - - [View source](/scripts/saveAllVideo.js) - - Download video from Douyin Twitter Instagram TikTok Youtube - -
-
- 21. Vuiz - Get link audio/video/album - - [View source](/scripts/vuiz_getLink.js) - - Support youtube, facebook, tiktok, zing tv, zing mp3, xVideo, nhac.vn, mixcloud, soundcloud, keeng.vn, chiasenhac, nhaccuatui, mediafire, ggdrive, dropbox, ondrive - -
-
- 22. SaveVideo - Download video - - [View source](/scripts/savevideo_me.js) - - Download videos from Dailymotion, Facebook, Vimeo, Twitter, Instagram / Reels, TikTok, Rumble.com, Streamable.com, Aol Video, Bilibili.com (哔哩哔哩), Bilibili.tv, Coub, DouYin (抖音), Flickr Videos, Focus.de, GMX.net / WEB.DE, ItemFix, Magisto, Reddit, Sapo.pt, T.me (Telegram), Tiscali.it Video, Tudou, Veoh, Vidmax.com, Vine (archive), WorldStarHipHop, Youku - -
-
- 23. Get audio/video (luanxt) - - [View source](/scripts/getLinkLuanxt_newtab.js) - - Using API from luanxt.com. Download Zing MP3, Zing Video Clip, Zing TV, NhacCuaTui, YouTube, SoundCloud, Nhac.vn, ChiaSeNhac.vn, Facebook Video, Keeng Audio, Keeng Video, Keeng Phim - -
- ---- Photos --- -
- 24. Twitter X - Add download button - - [View source](/scripts/twitter_downloadButton.js) - - Add the download button to all video and photo in Twitter - - ![](/scripts/twitter_downloadButton.png) - -
-
- 25. Download favicon from website - - [View source](/scripts/getFavicon.js) - - Get favicon link of current website - -
-
- 26. Picviewer CE+ download images - - [View source](/scripts/recommend_picviewer_ce+.js) - - Powerful picture viewing tool online, which can popup/scale/rotate/batch save pictures automatically - - ![](https://v2fy.com/asset/063_picviewer_ce/73130353-c4598e00-4031-11ea-810e-9498677a40d1.gif) - -
-
- 27. File-converter.io - change image type - - [View source](/scripts/recommend_file_converter.js) - - Powerful tool which allows you to convert and compress files using the context menu in windows explorer. - - ![](https://file-converter.io/images/file-converter-usage.gif) - -
-
- 28. Squoosh.app - compress images - - [View source](/scripts/recommend_squoosh_app.js) - - Make images smaller using best-in-class codecs, right in the browser. - -
- ---- Music --- -
- 29. Spotify - Add download button - - [View source](/scripts/spotify_downloadButton.js) - - Add song/playlist download button on Spotify. Use spotify-downloader.com - - ![](/scripts/spotify_downloadButton.png) - -
-
- 30. Soundcloud - Add download button - - [View source](/scripts/soundcloud_downloadMusic.js) - - Add download button on soundcloud (before like button). Use soundcloud API, no external service - - ![](/scripts/soundcloud_downloadMusic.png) - -
-
- 31. Nhaccuatui music/lyric downloader - - [View source](/scripts/nhaccuatui_downloader.js) - - Download the song that be playing in Nhaccuatui - -
-
- 32. Zingmp3 music dowloader (API) - - [View source](/scripts/zingmp3_downloadMusic.js) - - Download music on mp3.zing.vn and zingmp3.vn using zingmp3 API - -
-
- 33. Show all audio in website - - [View source](/scripts/showTheAudios.js) - - Will display all audio in website, easy to download/get link. - -
- ---- Videos --- -
- 34. Download watching video - - [View source](/scripts/download_watchingVideo.js) - - tiktok, doutu.be, phimmoi2... - -
-
- 35. Vimeo - video downloader - - [View source](/scripts/vimeo_downloader.js) - - Download video from vimeo - -
-
- 36. Show all videos in website - - [View source](/scripts/showTheVideos.js) - - Download video easier - -
- ---- Document --- -
- 37. Studocu - Download documents - - [View source](/scripts/studocu_downs.js) - - Download documents on Studocu.com for free - -
-
- 38. Scribd - Download documents - - [View source](/scripts/scribd_downloadDocuments.js) - - Download document on Scribd for free - -
-
- 39. Download free from tailieu.vn - - [View source](/scripts/tailieu_vn.js) - - Download any document on tailieu.vn without login - -
-
- 40. DocDownloader - Download document - - [View source](/scripts/recommend_docsdownloader.js) - - Download document on Scribd, Everand, Slideshare, Issuu, Academia, Chegg, Researchgate, Coursehero, Studocu, Perlego, Yumpu, Tiendeo, Fliphtml5, Anyflip, Docsity, Passei direto, Udocz - -
-
- 41. Export bookmarks to file - - [View source](/scripts/bookmark_exporter.js) - - Export all your browser's bookmarks to JSON file - -
-
- 42. Bookmark Sidebar - - [View source](/scripts/recommend_BookmarkSidebar.js) - - Very good Bookmark manager, find your bookmarks faster. - -
- -### Google - ---- Download --- -
- 43. Google drive - generate direct link - - [View source](/scripts/ggdrive_generateDirectLink.js) - - Generate a direct download link to files stored in Google Drive. A direct link will immediately start downloading the file. - -
-
- 44. GG Drive - Download PDF - - [View source](/scripts/ggdrive_downloadPdf.js) - - Download google drive PDF file that dont have download button.
Pages will be convert to image, cannot edit.
- -
-
- 45. GG Drive - Download PowerPoint (Slides/Presentation) - - [View source](/scripts/ggdrive_downloadPresentation.js) - - Download google drive Presentation file that dont have download button, covert to HTML file. - -
-
- 46. GG Drive - Download Document (to PDF) - - [View source](/scripts/ggdrive_downloadDoc.js) - - Download google drive Document file that dont have download button. Pages will be convert to PDF, cannot edit. - -
-
- 47. GG Drive - Download Sheet (copy Text) - - [View source](/scripts/ggdrive_copySheetText.js) - - Copy google drive sheet (excel) that dont have download button (Just copy text, can't copy formula) - -
-
- 48. GG Drive - Download video - - [View source](/scripts/ggdrive_downloadVideo.js) - - Download google drive video that dont have download button - -
-
- 49. Google - Download all your data - - [View source](/scripts/google_downloadAllYourData.js) - - Download all your data on Google - -
- ---- Bulk Download --- -
- 50. GGDrive - Download all videos in folder - - [View source](/scripts/ggDrive_downloadAllVideosInFolder.js) - - Download all videos in folder of google drive (bypass download permission) - -
- ---- More --- -
- 51. Google search advanced - - [View source](/scripts/recommend_googleAdvanced.js) - - Search google with a lot of advanced features - -
-
- 52. Check total indexed pages - - [View source](/scripts/search_totalIndexedPages.js) - - Know how many pages of current website is indexed in Google - -
-
- 53. Google site search - - [View source](/scripts/search_googleSite.js) - - Search in google while limiting the search result to currently opened webpage. - -
-
- 54. Google shortcuts - - [View source](/scripts/googleShortcuts.js) - - Create new google doc/sheet/slide/form/site/keep/calendar - -
-
- 55. View Google cache of website - - [View source](/scripts/googleCache.js) - - View blocked website - -
-
- 56. Google mirror - I'm elgooG - - [View source](/scripts/google_mirror.js) - - Google games. We create, restore, and discover interactive Google Easter Eggs. Just click and play them online for free. - -
- -### Facebook - ---- All in one --- -
- 57. Facebook - All In One - - [View source](/scripts/fb_allInOne.js) - - Combine all bulk download / statistic features on facebook into single page - -
- ---- Download --- -
- 58. Download watching fb video - - [View source](/scripts/fb_downloadWatchingVideo.js) - - Download any facebook video that you are watching (watch / story / comment / reel / chat) - -
-
- 59. Download watching fb Story/Comment - - [View source](/scripts/fb_storySaver.js) - - Download facebook story / comment video that you are watching - -
-
- 60. Download fb video/reel/watch from url - - [View source](/scripts/fb_videoDownloader.js) - - Download facebook video/reel/watch - -
-
- 61. Get avatar from fb user id - - [View source](/scripts/fb_getAvatarFromUid.js) - - Get avatar from list facebook user ids - -
-
- 62. Export saved facebook items - - [View source](/scripts/fb_exportSaved.js) - - Export all your saved items on facebook to JSON file - -
- ---- Hot --- -
- 63. Auto like post on Facebook - - [View source](/scripts/fb_autoLike.js) - - Auto like post on Facebook. -
    -
  • Support all post types (page, group, user, feed, ...)
  • -
  • Support bulk remove/add reactions
  • -
  • Support all reaction types
  • -
- -
-
- 64. Facebook - Reveal deleted messages - - [View source](/scripts/fb_revealDeletedMessages.js) - - View deleted messages (since function was turned on) on facebook messenger.

WARNING

Not work with end-to-end encryption
- -
-
- 65. Facebook Story - More reactions - - [View source](/scripts/fb_moreReactionStory.js) - - React story Facebook with more emojis - - ![](/scripts/fb_moreReactionStory.png) - -
-
- 66. Turn off light facebook newfeed - - [View source](/scripts/fb_toggleLight.js) - - Hide Navigator bar and complementary bar in facebook.

  • Click to temporarily hide/show on this page.
  • Enable autorun to auto hide whenever open facebook.
- -
-
- 67. Hide facebook new feed - - [View source](/scripts/fb_toggleNewFeed.js) - - Hide facebook new feed (home page) for better focus to work - -
-
- 68. Stop new feed facebook - - [View source](/scripts/fb_stopNewFeed.js) - - Stop load new feed on facebook, better for work performance
-
    -
  • Support feeds: stories, home, video, group, marketplace
  • -
- -
-
- 69. 👀 Block seen story facebook - - [View source](/scripts/fb_blockSeenStory.js) - - Block 'Seen' story in facebook. Your friend will not know that you have seen his/her stories. - -
-
- 70. Show facebook post reaction count - - [View source](/scripts/fb_getPostReactionCount.js) - - Show total reaction count on facebook posts when hover mouse over post's reaction section - - ![](/scripts/fb_getPostReactionCount.jpg) - -
-
- 71. Facebook - Who is typing to you? - - [View source](/scripts/fb_whoIsTyping.js) - - Notify when someone is typing chat to you.

WARNING

Not work with end-to-end encryption
- -
- ---- Statistic --- -
- 72. Facebook - View your friends's joined groups - - [View source](/scripts/fb_searchGroupForOther.js) - - Know about your friends's joined groups (public groups) on facebook - -
-
- 73. Facebook - View your friend's liked pages - - [View source](/scripts/fb_searchPageForOther.js) - - Know about your friends's liked pages (public pages) on facebook - -
-
- 74. Facebook - Find all posts of your friends - - [View source](/scripts/fb_searchPostsForOther.js) - - Search all public posts of your friends on facebook. Include posts in group, page, wall, ... - -
- ---- Access Token --- -
- 75. Check fb access token - - [View source](/scripts/fb_checkToken.js) - - Check type, permissions, created date, expired date, ... of faceboook access token - -
-
- 76. Get fb token EAAB (instagram) - - [View source](/scripts/fb_getTokenFacebook.js) - - Get facebook access token EAAB from www.facebook.com - -
-
- 77. Get fb token EAADo1 (messenger) - - [View source](/scripts/fb_getTokenMessage.js) - - Get facebook access token from www.facebook.com (messenger_for_android) - -
-
- 78. Get fb token EAAG (business_locations) - - [View source](/scripts/fb_getTokenBussinessLocation.js) - - Get facebook token EAAG from business.facebook.com - -
-
- 79. Get fb token EAAB (campaigns) - - [View source](/scripts/fb_getTokenCampaigns.js) - - Get facebook token EAAB from www.facebook.com (campaigns) - -
-
- 80. Get fb token from cookie (ffb.vn) - - [View source](/scripts/fb_getTokenFfb.js) - - Post your facebook cookie to ffb.vn API - -
- ---- Get ID --- -
- 81. Get fb User ID - - [View source](/scripts/fb_getUid.js) - - Get id of user in facebook website - -
-
- 82. Get fb Page ID - - [View source](/scripts/fb_getPageId.js) - - Get id of page in facebook website - -
-
- 83. Get fb Group ID - - [View source](/scripts/fb_getGroupId.js) - - Get id of group in facebook website - -
-
- 84. Get fb Album ID - - [View source](/scripts/fb_getAlbumId.js) - - Get id of facebook album in current website - -
-
- 85. Get all fb Album ID from current page - - [View source](/scripts/fb_getAllAlbumIdFromCurrentWebsite.js) - - Get all id of album in facebook website - -
-
- 86. Get fb User ID from url - - [View source](/scripts/fb_getUidFromUrl.js) - - Get id of facebook user from entered url - -
-
- 87. Get all fb User ID from search page - - [View source](/scripts/fb_getAllUidFromFbSearch.js) - - Get id of all user from facebook search page - -
-
- 88. Get all fb User ID from group - - [View source](/scripts/fb_getAllUidOfGroupMembers.js) - - Get id of all user from group members facebook - -
- ---- Shortcut --- -
- 89. View your facebook saved - - [View source](/scripts/fb_openSaved.js) - - View saved contents on Facebook - -
-
- 90. View your memories on facebook - - [View source](/scripts/fb_openMemories.js) - - View your memories on facebook - -
-
- 91. View your ads activities - - [View source](/scripts/fb_openAdsActivities.js) - - View ads you have seen on facebook - -
-
- 92. Check your activities on Facebook - - [View source](/scripts/fb_openAllActivities.js) - - Check all your activities on facebook - -
-
- 93. Video you watched on facebook - - [View source](/scripts/fb_openVideoActivities.js) - - View all videos you watched on facebook - -
-
- 94. Events joined on facebook - - [View source](/scripts/fb_openPassEvents.js) - - View pass events that you have joined on facebook. - -
-
- 95. Facebook friend's birthdays - - [View source](/scripts/fb_openBirthdays.js) - - View your friend's birthdays each month on facebook - -
-
- 96. Change language facebook - - [View source](/scripts/fb_openChangeLanguage.js) - - Change display language on facebook - -
-
- 97. Recover facebook account - - [View source](/scripts/fb_openAccountHacked.js) - - Your fb account has been hacked? Facebook can help you. - -
- -### Instagram - -
- 98. Get insta user info (uid, avatar, ...) - - [View source](/scripts/insta_getUserInfo.js) - - Get instagram uid, avatar, name, ... - -
-
- 99. Add Instagram download button - - [View source](/scripts/insta_injectDownloadBtn.js) - - Add a download button to all photo/video/post/story on Instagram - - ![](/scripts/insta_injectDownloadBtn.png) - -
-
- 100. Insta - Anonymous story viewer - - [View source](/scripts/insta_anonymousStoryViewer.js) - - Watch instagram stories anonymously - -
- ---- Bulk Download --- -
- 101. Get all media of insta user (API) - - [View source](/scripts/insta_getAllUserMedia.js) - - Get all media of instagram user (use instagram API) - -
-
- 102. Insta - Export all following/followers - - [View source](/scripts/insta_getFollowForOther.js) - - Know about your (or your friends's) following / followers on instagram. Export to json file - -
- -### Youtube - -
- 103. Picture in Picture - - [View source](/scripts/pictureInPicture.js) - - Watch videos in a floating window - -
-
- 104. Download youtube video/audio - - [View source](/scripts/youtube_downloadVideo.js) - - Bypass age restriction, without login -
    -
  • Click once to download current video
  • -
  • Enable autorun to render download button
  • -
- - ![](/scripts/youtube_downloadVideo.png) - -
-
- 105. Get Youtube video's captions - - [View source](/scripts/youtube_getVideoCaption.js) - - - Click to get all captions of playing youtube video
- Enable autorun to show realtime captions (transcript)
- - ![](/scripts/youtube_getVideoCaption.png) - -
-
- 106. Return youtube dislike - - [View source](/scripts/youtube_viewDislikes.js) - - Returns ability to see dislikes of youtube video/short - -
-
- 107. Youtube nonstop - - [View source](/scripts/youtube_nonstop.js) - - Kiss the annoying "Video paused. Continue watching?" confirmation goodbye! - - ![](/scripts/youtube_nonstop.png) - -
-
- 108. Youtube change country - - [View source](/scripts/youtube_changeCountry.js) - - Change youtube country to view content in other country - -
-
- 109. Get Youtube video's thumbnail - - [View source](/scripts/youtube_getVideoThumbnail.js) - - Get largest thumbnail of playing youtube video - -
-
- 110. Toggle light youtube - - [View source](/scripts/youtube_toggleLight.js) - - Toggle light on/off to focus to youtube video - -
-
- 111. PIP full website - - [View source](/scripts/pip_fullWebsite.js) - - Picture in picture mode for full website - -
-
- 112. PIP for canvas - - [View source](/scripts/pip_canvas.js) - - Picture in picture mode for canvas - -
-
- 113. Improve YouTube - 85+ features - - [View source](/scripts/recommend_improve_youtube.js) - - Make YouTube more beautiful, faster, and more useful! - -
- -### Tiktok - ---- Tiktok --- -
- 114. Tiktok - Download watching video - - [View source](/scripts/tiktok_downloadWatchingVideo.js) - - Download tiktok video you are watching (no watermark) - -
-
- 115. Tiktok - Download video from URL - - [View source](/scripts/tiktok_downloadVideo.js) - - Download tiktok video from url (no watermark) - -
-
- 116. Tiktok - Batch download - - [View source](/scripts/tiktok_batchDownload.js) - - Select and download all tiktok video (user profile, tiktok explore). - - ![](/scripts/tiktok_batchDownload.jpg) - -
- ---- Douyin --- -
- 117. Douyin - Download watching video - - [View source](/scripts/douyin_downloadWachingVideo.js) - - Show all downloadable videos in current douyin webpage - -
-
- 118. Douyin - Download all user videos - - [View source](/scripts/douyin_downloadAllVideoUser.js) - - Download all videos in douyin user profile. - -
- -### Automation - ---- Utility --- -
- 119. Web timer - - [View source](/scripts/web_timer.js) - - Keep track of how you spend your time on the web.
-

CLICK TO OPEN GRAPH.

- - ![](/scripts/web_timer.png) - -
-
- 120. Auto lock websites - - [View source](/scripts/auto_lockWebsite.js) - - Auto lock websites. Enter password to unlock.
-
    -
  • Click to temporarly lock current website.
  • -
  • Click to open settings.
  • -
- -
-
- 121. Super smooth scroll - - [View source](/scripts/smoothScroll.js) - - Scroll smoothly on all websites with your mouse and keyboard.
-
    -
  • Suggested if you use mouse (turn off if use touchpad)
  • -
  • Click to Disable/Enable for current website
  • -
  • Support middle click to scroll
  • -

- -
-
- 122. Magnify any Image - - [View source](/scripts/magnify_image.js) - - View any images in magnified window
- Where you are able to zoom in/out, rotate, drag, and more.
- Auto find large version of image to show. -

-

4 ways to use:

- Using now: -
    -
  1. Right click in website > click magnify image
  2. -
  3. Left click this feature then click image
  4. -
- After turn-on auto-run: -
    -
  1. Hover on any image/video > click magnify button
  2. -
  3. Double Ctrl on any image
  4. -
-
- -
-
- 123. Auto - view largest image - - [View source](/scripts/auto_redirectLargestImageSrc.js) - -
    -
  • When viewing an image in new tab.
  • -
  • This script will auto find and redirect to largest image.
  • -
  • Support hundred of websites.
  • -
- - ![](/scripts/auto_redirectLargestImageSrc.jpg) - -
-
- 124. Show image on hover link - - [View source](/scripts/showImageOnHoverLink.js) - - Show preview image when you hover mouse over an image link - -
-
- 125. Prevent tracking url - - [View source](/scripts/remove_tracking_in_url.js) - - Remove tracking parameters from url, prevent tracking from Facebook, Google, Tiktok, Twitter etc.
    -
  • fbclid
  • -
  • ttclid
  • -
  • utm_*
  • -
  • ...
  • -
- -
-
- 126. Don't close browser with last tab - - [View source](/scripts/prevent_closeBrowser_lastTab.js) - - Prevent closing of Browser after close the last tab
- Auto create new empty tab if no tab left
- -
-
- 127. Block trackers, spy and malware - - [View source](/scripts/chongLuaDao.js) - - Alert when you access a dangerous website, malware or spy content
-
    -
  • Click to analyze current website
  • -
  • Click to fetch newest websites database
  • -
- -
-
- 128. Shorten URL - - [View source](/scripts/shortenURL.js) - - Support tinyurl, tnyim, cuttly, bitly, j2team, ... - -
-
- 129. Unshorten link - - [View source](/scripts/unshorten.js) - - Get origin URL of shortened url - -
-
- 130. Create invisible message - - [View source](/scripts/createInvisibleText.js) - - Create invisible text to hide secret messages. Receiver to use this feature to decode messages. - -
- ---- Automation --- -
- 131. Web to PDF - - [View source](/scripts/webToPDF.js) - - Convert current website to PDF - -
-
- 132. Screenshot full webpage - - [View source](/scripts/screenshotFullPage.js) - - Taking a screenshot of an entire webpage - -
-
- 133. Screenshot webpage - - [View source](/scripts/screenshotVisiblePage.js) - - Taking a screenshot of visible webpage (bypass websites that block screenshots such as Netflix, ...) - -
-
- 134. Scroll to very end - - [View source](/scripts/scrollToVeryEnd.js) - - Scoll to end, then wait for load data, then scroll again... Mouse click to cancel

Tips

You can press middle mouse button to auto scroll up/down in any website.
- -
-
- 135. Extract all Emails from website - - [View source](/scripts/getAllEmailsInWeb.js) - - Extracts all emails and displays them in a popup iFrame (enable popups!) - -
-
- 136. Enable/Disable Hack T-Rex Dino Game - - [View source](/scripts/dino_hack.js) - - A bot that plays the Google Chrome T-Rex game for you - -
-
- 137. Password generator - - [View source](/scripts/passwordGenerator.js) - - You only have to remember 1 password - -
- ---- Tools --- -
- 138. Text to QRCode - - [View source](/scripts/textToQrCode.js) - - Convert text/url to QRCode - -
-
- 139. Text to Speech (j2team) - - [View source](/scripts/textToSpeech.js) - - Convert text to speech using j2team tool - -
-
- 140. Audio output switcher - - [View source](/scripts/changeAudioOutput.js) - - Pick a default audio output device, customizable for each browser tab.

eg. listen to youtube by headphone in tab 1, play music by external speaker in tab 2.
- -
-
- 141. Send - Share file faster - - [View source](/scripts/send_shareFiles.js) - - Open send.zcyph.cc - share large file up to 20Gb - -
-
- 142. Vuiz - create logo WAP online - - [View source](/scripts/vuiz_createLogo.js) - - Create logo from text online - -
-
- 143. Performance Analyzer - - [View source](/scripts/performanceAnalyzer.js) - - Check performance metrics of website - -
-
- 144. IT Tools - All for Developers - - [View source](/scripts/recommend_ItTools.js) - - Handy tools for developers (open source) - -
-
- 145. CopyIcon - FREE emoji, icon, generator - - [View source](/scripts/recommend_copyicon.js) - - 285,000 free Icons, Emoji, SVG generator, and more... - -
-
- 146. Beautify Tools - - [View source](/scripts/recommend_beautifytools.js) - - Handy tools for developers -
    -
  1. Beautifiers And Minifiers
  2. -
  3. CSS Preprocessors
  4. -
  5. Converters
  6. -
  7. String Utilities
  8. -
  9. SEO Tools
  10. -
  11. IP Tools
  12. -
  13. Code Validators
  14. -
  15. Cryptography
  16. -
  17. Code Editors
  18. -
- -
- ---- Github --- -
- 147. Github - Go to any commit - - [View source](/scripts/github_goToAnyCommit.js) - - Go to any commit of github repo. Included first commit. - -
-
- 148. Github - HTML preview - - [View source](/scripts/github_HTMLPreview.js) - - Preview github's HTML file in any repo without download code. - -
-
- 149. Github - Open repo pages - - [View source](/scripts/github_openRepoPages.js) - - Switch between github.com repo and github.io live demo pages - username.github.io/repo
- github.com/username/repo
- -
-
- 150. Github - Open repo in github.dev - - [View source](/scripts/githubdev.js) - - Open current repo in github.dev

Tip

Press dot (.) when in github repo to open github.dev
- -
-
- 151. Github - Open repo in github1s.com - - [View source](/scripts/github1s.js) - - Open current repo in github1s.com - -
-
- 152. Cloc - count line of code - - [View source](/scripts/recommend_cloc.js) - - Count blank lines, comment lines, and physical lines of source code in many programming languages. - - ![](/scripts/recommend_cloc.png) - -
-
- 153. Refined GitHub - - [View source](/scripts/recommend_refined_github.js) - - Simplifies the GitHub interface and adds useful features - -
- ---- Shopping --- -
- 154. Shopee - Top variation - - [View source](/scripts/shopee_topVariation.js) - - See how many times each product variant was purchased - - ![](/scripts/shopee_topVariation.png) - -
-
- 155. Shopee - Total spend money - - [View source](/scripts/shopee_totalSpendMoney.js) - - See how much money you have spend on Shopee - -
-
- 156. Shopee - Export order history (Excel) - - [View source](/scripts/shopee_totalSpendMoney_excel.js) - - Export all of your order history from Shopee to Excel file - -
-
- 157. Tiki - Total spend money? - - [View source](/scripts/tiki_totalSpendMoney.js) - - See how much money you have spend on Tiki - -
-
- 158. Beecost - - [View source](/scripts/recommend_Beecost.js) - - Check deals/prices in ecommerce websites - -
- ---- PDF --- -
- 159. FastDoc - Convert PDF/Photo to Word/Excel - - [View source](/scripts/recommend_fastDoc.js) - - Convert Photos & PDF to Excel, Word, Searchable PDF for free - -
-
- 160. SmartPDF - Tools for PDF - - [View source](/scripts/recommend_smartPDF.js) - - Compress PDF, PDF Converter, PPT to PDF, PDF to PPT, JPG to PDF, PDF to JPG, Excel to PDF, PDF to Excel, Edit PDF, PDF Reader, Number Pages, Delete PDF Pages, Rotate PDF, Word to PDF, PDF to Word, Merge PDF, Split PDF, eSign PDF, Unlock PDF, Protect PDF, PDF Scanner - -
-
- 161. PDF Stuffs - Tools for PDF - - [View source](/scripts/recommend_pdfstuffs.js) - - Free PDF converter online service: Merge PDF, Split PDF, Compress PDF, PDF to Word, PDF to PPT, PDF to Excel, Word to PDF, Excel to PDF, PPT to PDF, PDF to JPG, JPG to PDF, PDF to HTML, HTML to PDF, Unlock PDF, Protect PDF, Rotate PDF, Crop PDF, Delete pages, Add page numbers, Watermark PDF - -
- -### Unlock - ---- Unlock web --- -
- 162. Hack Duck race - - [View source](/scripts/duckRace_cheat.js) - - Hack result of Duck race, always get the result you want - - ![](/scripts/duckRage_cheat.png) - -
-
- 163. Hack Wheel of Names - - [View source](/scripts/wheelOfNames_hack.js) - - Hack result of
  • wheelofnames.com
  • wheelrandom.com
  • spinthewheel.io
always get the result you want.
- -
-
- 164. Read full medium article - - [View source](/scripts/medium_readFullArticle.js) - - Read full medium article without login - -
-
- 165. Medium - Fix vietnamese font - - [View source](/scripts/medium_fixVietnameseFont.js) - - Fix vietnamese font in Medium
-
    -
  • Click 1 time to fix font in current Medium page (dont need to reload)
  • -
  • Enable autorun for next times you enter Medium
  • -
- -
-
- 166. Fireship - PRO unlocked - - [View source](/scripts/fireship_vip.js) - - Unlock all Fireship PRO courses/lessons (saved $399 USD) - -
-
- 167. Studocu - Bypass preview - - [View source](/scripts/studocu_bypassPreview.js) - - View VIP document on Studocu.com, bypass preview popup / reveal blurred content. - -
-
- 168. Scribd - bypass preview - - [View source](/scripts/scribd_bypassPreview.js) - - View VIP document on Scribd.com, bypass preview popup / reveal blurred content. - -
-
- 169. Studyphim - Watch free movies - - [View source](/scripts/studyphim_unlimited.js) - - Watch movies on Studyphim for free without login - -
-
- 170. Mở khoá Learn Anything - - [View source](/scripts/bypass_learnAnything.js) - - View learn-anything.xyz content without become a member - - ![](/scripts/bypass_LearnAnything.png) - -
- ---- Unlock function --- -
- 171. Enable/Disable allow copy - - [View source](/scripts/simpleAllowCopy.js) - - Allow Copy on every websites
-

NOTES:

-
    -
  • Need to enable autorun first
  • -
  • Click this button to TURN ON cheat to allow copy/right-click for current website.
  • -
  • Click again to TURN OFF.
  • -
- -
-
- 172. Detect Zero-Width Characters - - [View source](/scripts/detect_zeroWidthCharacters.js) - - Detects zero-width characters, highlights the characters and containing DOM element. - -
-
- 173. Inject script to website - - [View source](/scripts/injectScriptToWebsite.js) - - Inject script url to current website, eg. jquery, library, etc. - -
-
- 174. Show hidden fields - - [View source](/scripts/showHiddenFields.js) - - Reveals hidden fields on a webpage. Find things like tokens, etc - -
-
- 175. View cookies - - [View source](/scripts/viewCookies.js) - - View cookies saved in current website - -
-
- 176. Remove cookies - - [View source](/scripts/removeCookies.js) - - Remove cookies from current website - -
- ---- Other --- -
- 177. Make browser super fast - - [View source](/scripts/recommend_chromeFlags.js) - - Some flags experiments that can make your browser super fast - -
-
- 178. View saved wifi passwords - - [View source](/scripts/recommend_viewSavedWifiPass.js) - - PowerShell script to view saved wifi passwords on your computer - -
-
- 179. Leak check - your password has been leaked? - - [View source](/scripts/recommend_leakCheck.js) - - Check your password has been leaked on internet or not - -
- -### Web UI - ---- Hot --- -
- 180. Darkmode for pdf - - [View source](/scripts/darkModePDF.js) - - Enable/Disable darkmode for PDF - -
-
- 181. Toggle edit page - - [View source](/scripts/toggleEditPage.js) - - Edit all text in website - -
-
- 182. Show FPS - - [View source](/scripts/showFPS.js) - - Show frames per second of current website (inject stat.js library) - -
-
- 183. Show FPS - ver 2 - - [View source](/scripts/showFps_v2.js) - - Show frames per second of current website (use debugger) - -
-
- 184. Hide/Show password field - - [View source](/scripts/toggle_passwordField.js) - - Show/hide value of password fields in website
(eg. **** -> 1234)
- -
-
- 185. Dark reader - - [View source](/scripts/recommend_DarkReader.js) - - Darkmode for every website - -
-
- 186. CSS Portal - Empowered your CSS skills - - [View source](/scripts/recommend_cssportal.js) - - Empowered your CSS skills with hundreds of CSS tools. - -
-
- 187. CSS Loaders - 600+ css loader - - [View source](/scripts/recommend_cssloaders.js) - - The Biggest Collection of Loading Animations. Over 600+ CSS-only loaders made using a single element - -
-
- 188. UIverse - Open-Source UI elements - - [View source](/scripts/recommend_uiverse.js) - - Open-Source UI elements for any project. - -
- ---- View --- -
- 189. Font Rendering - better font display - - [View source](/scripts/recommend_fontRendering.js) - - Improve browser displaying, font rewriting, smoothing, scaling, stroke, shadow, special style elements, custom monospaced, etc - -
-
- 190. What font - - [View source](/scripts/whatFont.js) - - Check font used in webpage - -
-
- 191. Show all javascript events - - [View source](/scripts/visualEvent.js) - - Visual Event - Visually show Javascript events on a page - -
-
- 192. View all images in web - - [View source](/scripts/listAllImagesInWeb.js) - - View all images in web - -
-
- 193. View all links - - [View source](/scripts/viewAllLinks.js) - - Show all links and anchor text of current page. - -
-
- 194. View scripts used - - [View source](/scripts/viewScriptsUsed.js) - - View all scripts used in current website - -
-
- 195. View stylesheet used - - [View source](/scripts/viewStylesUsed.js) - - View all stylesheet used in current website - -
-
- 196. CSS selector viewer - - [View source](/scripts/cssSelectorViewer.js) - - Inspect css at specific element on the web - -
- ---- Remove --- -
- 197. Remove all colors in web - - [View source](/scripts/removeColours.js) - - Remove all colours in the web.
Click again to undo.
- -
-
- 198. Remove stylesheet - - [View source](/scripts/removeStylesheet.js) - - Remove all stylesheet from website.
Click again to undo.
- -
-
- 199. Remove images - - [View source](/scripts/removeImages.js) - - Remove all images from website.
Click again to undo.
- -
-
- 200. Remove bloat (iframe, embed) - - [View source](/scripts/removeBloat.js) - - Remove iframe, embeds, applets from website - -
- ---- Table --- -
- 201. Add sort to table - - [View source](/scripts/table_addSortTable.js) - - Add sort functions to table - -
-
- 202. Add number columns - - [View source](/scripts/table_addNumberColumn.js) - - Add number columns to table - -
-
- 203. Swap rows and columns - - [View source](/scripts/table_swapRowAndColumn.js) - - Swap rows and columns (transpose) - -
- ---- More --- -
- 204. Highlight internal/external link - - [View source](/scripts/internalOrExternalLink.js) - - +Red = Internal_link -+Orange = Currently_opened_link -+Blue = External_link - -
-
- 205. Get window size - - [View source](/scripts/getWindowSize.js) - - Alerts the width and height in pixels of the inner window. - -
-
- 206. Let it snow - - [View source](/scripts/letItSnow.js) - - Make website like it snowing - -
diff --git a/md/LIST_SCRIPTS_VI.md b/md/LIST_SCRIPTS_VI.md deleted file mode 100644 index 34a456eb..00000000 --- a/md/LIST_SCRIPTS_VI.md +++ /dev/null @@ -1,1849 +0,0 @@ -### Tìm kiếm - -
- 1. There's an AI for that - Tìm AI - - [Xem mã nguồn](/scripts/recommend_theresanaiforthat.js) - - Tổng hợp hàng ngàn công cụ AI hiện có. Dễ dàng tìm kiếm theo chủ đề - -
-
- 2. Time.is - Kiểm tra thời gian - - [Xem mã nguồn](/scripts/recommend_timeis.js) - - Đồng hồ chính xác nhất. Kiểm tra đồng hồ trên máy của bạn nhanh hay chậm. - -
-
- 3. Google trending - Nội dung nổi bật - - [Xem mã nguồn](/scripts/recommend_googleTrending.js) - - Xem mọi người đang tìm gì trên google. Thống kê từng ngày, thời gian thực. - -
-
- 4. Tìm trang web tương tự - - [Xem mã nguồn](/scripts/similarWeb.js) - - SimilarWeb - Phân tích chi tiết cho mọi trang web trực tuyến - -
-
- 5. SimilarWeb - không bị giới hạn - - [Xem mã nguồn](/scripts/similarWeb_bypassLimit.js) - - Sử dụng SimilarWeb không giới hạn, không cần đăng nhập.

Cách hoạt động:

Cookies của bạn sẽ được xoá mỗi khi vào trang web
- - ![](/scripts/similarWeb_bypassLimit.png) - -
-
- 6. Tìm tài khoản miễn phí - - [Xem mã nguồn](/scripts/search_sharedAccount.js) - - Tìm tài khoản được chia sẻ trên mạng cho trang web hiện tại - -
-
- 7. Internet archive - Thư viện miễn phí - - [Xem mã nguồn](/scripts/recommend_archive.js) - - Thư viện với hàng triệu sách, báo, phim, phần mềm, nhạc, website, ... miễn phí - - ![](/scripts/internet_archive.png) - -
-
- 8. Wappalyzer - Web dùng công nghệ gì? - - [Xem mã nguồn](/scripts/recommend_wappalyzer.js) - - Xem những công nghệ/thư viện trang web đang dùng - -
-
- 9. Who.is - - [Xem mã nguồn](/scripts/whois.js) - - Muốn biết ai đang giữ domain này? Click ngay! - -
-
- 10. Xem thông tin meta của web (SEO) - - [Xem mã nguồn](/scripts/viewWebMetaInfo.js) - - Xem thông tin meta của website trực tiếp trong trang web - -
-
- 11. Bài nhạc top treding toàn cầu? - - [Xem mã nguồn](/scripts/recommend_search_musicTreding.js) - - Trang web thống kê top trending âm nhạc toàn cầu. - -
-
- 12. Tìm bài báo/sách/pdf/...ở đâu? - - [Xem mã nguồn](/scripts/search_paperWhere.js) - - Học, học nữa, học mãi - -
-
- 13. Tìm hợp âm guitar - - [Xem mã nguồn](/scripts/search_hopamchuan.js) - - Tra cứu hợp âm chuẩn dành cho người chơi guitar - -
-
- 14. Dowfor - Kiểm tra web die - - [Xem mã nguồn](/scripts/checkWebDie.js) - - Dùng bên thứ 3 để kiểm tra xem website có bị die thật không - -
-
- 15. DownDetector - Thống kê sự cố web - - [Xem mã nguồn](/scripts/downDetector.js) - - Xem thống kê các báo cáo về sự cố web - -
-
- 16. Xem wayback url của website - - [Xem mã nguồn](/scripts/openWaybackUrl.js) - - Giúp xem nội dung website trong quá khứ - -
-
- 17. Lưu trữ online trang hiện tại - - [Xem mã nguồn](/scripts/archiveToday.js) - - Lưu trang web hiện tại lên archive.today - -
-
- 18. Tìm Userscripts - - [Xem mã nguồn](/scripts/recommend_search_userscript.js) - - Tìm Userscripts trên Usersript.zone - -
- -### Tải xuống - ---- Tổng hợp --- -
- 19. Cobalt - Tải video/nhạc - - [Xem mã nguồn](/scripts/recommend_cobalt.js) - - Hỗ trợ youtube, tiktok, instagram, twitter/x, bilibili, twitch, vimeo, soundcloud, dailymotion, pinterest, reddit, tumblr, ... - -
-
- 20. Save All Video - - [Xem mã nguồn](/scripts/saveAllVideo.js) - - Tải video từ Douyin Twitter Instagram TikTok Youtube - -
-
- 21. Vuiz - Get link nhạc/video/album - - [Xem mã nguồn](/scripts/vuiz_getLink.js) - - Hỗ trợ youtube, facebook, tiktok, zing tv, zing mp3, xVideo, nhac.vn, mixcloud, soundcloud, keeng.vn, chiasenhac, nhaccuatui, mediafire, ggdrive, dropbox, ondrive - -
-
- 22. SaveVideo - Tải video - - [Xem mã nguồn](/scripts/savevideo_me.js) - - Tải videos từ Dailymotion, Facebook, Vimeo, Twitter, Instagram / Reels, TikTok, Rumble.com, Streamable.com, Aol Video, Bilibili.com (哔哩哔哩), Bilibili.tv, Coub, DouYin (抖音), Flickr Videos, Focus.de, GMX.net / WEB.DE, ItemFix, Magisto, Reddit, Sapo.pt, T.me (Telegram), Tiscali.it Video, Tudou, Veoh, Vidmax.com, Vine (archive), WorldStarHipHop, Youku - -
-
- 23. Tải nhạc/video (luanxt) - - [Xem mã nguồn](/scripts/getLinkLuanxt_newtab.js) - - Sử dụng API của luanxt.com. Tải Zing MP3, Zing Video Clip, Zing TV, NhacCuaTui, YouTube, SoundCloud, Nhac.vn, ChiaSeNhac.vn, Facebook Video, Keeng Audio, Keeng Video, Keeng Phim - -
- ---- Ảnh --- -
- 24. Twitter X - Thêm nút tải video/ảnh - - [Xem mã nguồn](/scripts/twitter_downloadButton.js) - - Thêm nút tải cho mọi video/ảnh trên Twitter - - ![](/scripts/twitter_downloadButton.png) - -
-
- 25. Tải favicon của trang web - - [Xem mã nguồn](/scripts/getFavicon.js) - - Lấy link favicon của trang web - -
-
- 26. Picviewer CE+ tải ảnh - - [Xem mã nguồn](/scripts/recommend_picviewer_ce+.js) - - Công cụ mạnh mẽ để xem/tải ảnh hàng loạt, cho tất cả trang web - - ![](https://v2fy.com/asset/063_picviewer_ce/73130353-c4598e00-4031-11ea-810e-9498677a40d1.gif) - -
-
- 27. File-converter.io - chuyển đổi ảnh - - [Xem mã nguồn](/scripts/recommend_file_converter.js) - - Công cụ nén ảnh, đổi định dạng ảnh hàng loạt, trực tiếp bằng chuột phải. - - ![](https://file-converter.io/images/file-converter-usage.gif) - -
-
- 28. Squoosh.app - nén ảnh - - [Xem mã nguồn](/scripts/recommend_squoosh_app.js) - - Công cụ nén ảnh mạnh mẽ, giảm kích thước ngay trên trình duyệt - -
- ---- Nhạc --- -
- 29. Spotify - Thêm nút tải nhạc - - [Xem mã nguồn](/scripts/spotify_downloadButton.js) - - Thêm nút tải nhạc/playlist trên Spotify. Sử dụng spotify-downloader.com - - ![](/scripts/spotify_downloadButton.png) - -
-
- 30. Soundcloud - Thêm nút tải nhạc - - [Xem mã nguồn](/scripts/soundcloud_downloadMusic.js) - - Thêm nút tải nhạc trên soundcloud (trước nút like). Sử dụng trực tiếp soundcloud API - - ![](/scripts/soundcloud_downloadMusic.png) - -
-
- 31. Nhaccuatui tải nhạc/lời - - [Xem mã nguồn](/scripts/nhaccuatui_downloader.js) - - Tải bài nhạc / lời bài hát đang nghe trên Nhaccuatui - -
-
- 32. Zingmp3 tải nhạc (API) - - [Xem mã nguồn](/scripts/zingmp3_downloadMusic.js) - - Tải nhạc trên mp3.zing.vn và zingmp3.vn thông qua zingmp3 API - -
-
- 33. Hiển thị mọi audio trong trang web - - [Xem mã nguồn](/scripts/showTheAudios.js) - - Hiển thị tất cả tag audio/âm thanh trong trang web, giúp dễ dàng tải xuống/lấy link. - -
- ---- Video --- -
- 34. Tải video đang xem - - [Xem mã nguồn](/scripts/download_watchingVideo.js) - - tiktok, doutu.be, phimmoi2... - -
-
- 35. Vimeo - tải video - - [Xem mã nguồn](/scripts/vimeo_downloader.js) - - Tải video trên vimeo - -
-
- 36. Hiển thị mọi video có trong web - - [Xem mã nguồn](/scripts/showTheVideos.js) - - Tải video dễ dàng hơn - -
- ---- Tài liệu --- -
- 37. Studocu - Tải documents - - [Xem mã nguồn](/scripts/studocu_downs.js) - - Tải tài liệu trên Studocu.com miễn phí - -
-
- 38. Scribd - Tải documents - - [Xem mã nguồn](/scripts/scribd_downloadDocuments.js) - - Tải miễn phí document trên Scribd - -
-
- 39. Tải miễn phí từ tailieu.vn - - [Xem mã nguồn](/scripts/tailieu_vn.js) - - Tải bất kỳ tài liệu nào trên tailieu.vn không cần đăng nhập - -
-
- 40. DocDownloader - Tải document - - [Xem mã nguồn](/scripts/recommend_docsdownloader.js) - - Tải document từ Scribd, Everand, Slideshare, Issuu, Academia, Chegg, Researchgate, Coursehero, Studocu, Perlego, Yumpu, Tiendeo, Fliphtml5, Anyflip, Docsity, Passei direto, Udocz - -
-
- 41. Xuất bookmarks ra file - - [Xem mã nguồn](/scripts/bookmark_exporter.js) - - Xuất tất cả bookmarks trong trình duyệt của bạn ra file JSON - -
-
- 42. Bookmark Sidebar - - [Xem mã nguồn](/scripts/recommend_BookmarkSidebar.js) - - Trình quản lý bookmark ngon, tìm kiếm bookmark nhanh hơn bao giờ hết. - -
- -### Google - ---- Tải xuống --- -
- 43. Google drive - tạo link tải ngay - - [Xem mã nguồn](/scripts/ggdrive_generateDirectLink.js) - - Tạo đường link direct cho file trên google drive. Bấm vào đường link sẽ tải file trực tiếp thay vì mở trang xem trước file. - -
-
- 44. GG Drive - Tải PDF - - [Xem mã nguồn](/scripts/ggdrive_downloadPdf.js) - - Tải file PDF không có nút download trên google drive.
Tải về định dạng hình ảnh, không thể sửa nội dung.
- -
-
- 45. GG Drive - Tải PowerPoint (Slides) - - [Xem mã nguồn](/scripts/ggdrive_downloadPresentation.js) - - Tải file Powerpoint (Slides) không có nút download trên google drive, tải về định dạng HTML. - -
-
- 46. GG Drive - Tải Document (sang PDF) - - [Xem mã nguồn](/scripts/ggdrive_downloadDoc.js) - - Tải file Doc không có nút download trên google drive. Tải về định dạng PDF, không thể sửa nội dung. - -
-
- 47. GG Drive - Tải Sheet (copy nội dung) - - [Xem mã nguồn](/scripts/ggdrive_copySheetText.js) - - Copy nội dung file sheet (excel) không có nút tải xuống (Chỉ copy text, không copy được công thức) - -
-
- 48. GG Drive - Tải video - - [Xem mã nguồn](/scripts/ggdrive_downloadVideo.js) - - Tải video không có nút download trên google drive - -
-
- 49. Google - Tải xuống dữ liệu của bạn - - [Xem mã nguồn](/scripts/google_downloadAllYourData.js) - - Tải xuống thông tin của bạn trên Google - -
- ---- Tải hàng loạt --- -
- 50. GGDrive - Tải mọi video trong folder - - [Xem mã nguồn](/scripts/ggDrive_downloadAllVideosInFolder.js) - - Tải tất cả video trong thư mục google drive (tải được video không cho phép tải) - -
- ---- Khác --- -
- 51. Google tìm kiếm nâng cao - - [Xem mã nguồn](/scripts/recommend_googleAdvanced.js) - - Tìm kiếm google với nhiều tuỳ chọn nâng cao - -
-
- 52. Xem các pages được google quét - - [Xem mã nguồn](/scripts/search_totalIndexedPages.js) - - Biết có bao nhiêu trang con của website hiện tại đã được quét bởi Google - -
-
- 53. Tìm kiếm trên trang web này - - [Xem mã nguồn](/scripts/search_googleSite.js) - - Sử dụng google site search - -
-
- 54. Google phím tắt - - [Xem mã nguồn](/scripts/googleShortcuts.js) - - Tạo mới google doc/sheet/slide/form/site/keep/calendar - -
-
- 55. Xem Google cache của trang web - - [Xem mã nguồn](/scripts/googleCache.js) - - Phù hơp để xem các trang web bị block - -
-
- 56. Google mirror - I'm elgooG - - [Xem mã nguồn](/scripts/google_mirror.js) - - Chơi các trò chơi (minigame) từng có trên google tìm kiếm - -
- -### Facebook - ---- Tất cả trong một --- -
- 57. Facebook - All In One - - [Xem mã nguồn](/scripts/fb_allInOne.js) - - Tổng hợp tất cả chức năng tải hàng loạt / thống kê facebook - -
- ---- Tải xuống --- -
- 58. Tải video fb đang xem - - [Xem mã nguồn](/scripts/fb_downloadWatchingVideo.js) - - Tải bất kỳ video facebook nào mà bạn đang xem (watch / story / comment / reel / chat / bình luận / tin nhắn) - -
-
- 59. Tải Story/Comment fb đang xem - - [Xem mã nguồn](/scripts/fb_storySaver.js) - - Tải facebook story / video bình luận bạn đang xem - -
-
- 60. Tải video/reel/watch fb từ url - - [Xem mã nguồn](/scripts/fb_videoDownloader.js) - - Tải facebook video/reel/watch - -
-
- 61. Tải avatar từ fb user id - - [Xem mã nguồn](/scripts/fb_getAvatarFromUid.js) - - Tải danh sách avatar từ danh sách user id facebook - -
-
- 62. Xuất mục đã lưu trên facebook - - [Xem mã nguồn](/scripts/fb_exportSaved.js) - - Xuất các mục đã lưu của bạn trên facebook ra file JSON - -
- ---- Nổi bật --- -
- 63. Tự động thích bài đăng Facebook - - [Xem mã nguồn](/scripts/fb_autoLike.js) - - Tự động thả cảm xúc cho bài đăng trên Facebook. -
    -
  • Hỗ trợ mọi loại bài đăng (trang, nhóm, người dùng, new feed, ...)
  • -
  • Hỗ trợ gỡ/thêm cảm xúc hàng loạt
  • -
  • Hỗ trợ mọi loại cảm xúc
  • -
- -
-
- 64. Facebook - Xem tin nhắn bị gỡ - - [Xem mã nguồn](/scripts/fb_revealDeletedMessages.js) - - Xem lại những tin nhắn đã bị đối phương xóa (kể từ khi bật chức năng) trong facebook messenger.

Chú ý

Không xem được nếu mã hoá đầu cuối
- -
-
- 65. Facebook Story - Thêm cảm xúc - - [Xem mã nguồn](/scripts/fb_moreReactionStory.js) - - Thả cảm xúc story Facebook với nhiều loại emoji khác nhau - - ![](/scripts/fb_moreReactionStory.png) - -
-
- 66. Tắt đèn newfeed facebook - - [Xem mã nguồn](/scripts/fb_toggleLight.js) - - Ẩn giao diện 2 bên newfeed, giúp tập trung vào newfeed facebook.

  • Click để ẩn/hiện tạm thời trong trang hiện tại.
  • Bật tự chạy để tự động ẩn mỗi khi mở facebook.
- -
-
- 67. Ẩn dòng thời gian facebook - - [Xem mã nguồn](/scripts/fb_toggleNewFeed.js) - - Ẩn dòng thời gian facebook (trang chủ) để tập trung làm việc - -
-
- 68. Dừng dòng thời gian facebook - - [Xem mã nguồn](/scripts/fb_stopNewFeed.js) - - Tạm dừng tải dòng thời gian trên facebook, giúp tập trung làm việc
-
    -
  • Hỗ trợ các tab: stories, home, video, nhóm, marketplace
  • -
- -
-
- 69. 👀 Chặn "Đã xem" story facebook - - [Xem mã nguồn](/scripts/fb_blockSeenStory.js) - - Chặn 'Đã xem' cho story facebook. Bạn bè sẽ không biết được bạn đã xem story của họ. - -
-
- 70. Hiện tổng lượt thích bài viết facebook - - [Xem mã nguồn](/scripts/fb_getPostReactionCount.js) - - Hiện tổng lượt thích bài viết khi đưa chuột vào xem lượt thích. - - ![](/scripts/fb_getPostReactionCount.jpg) - -
-
- 71. Facebook - Ai đang nhắn cho bạn? - - [Xem mã nguồn](/scripts/fb_whoIsTyping.js) - - Thông báo khi có người đang gõ tin nhắn cho bạn.

Chú ý

Không xem được nếu mã hoá đầu cuối
- -
- ---- Thống kê --- -
- 72. Facebook - Xem các nhóm bạn bè tham gia - - [Xem mã nguồn](/scripts/fb_searchGroupForOther.js) - - Biết bạn bè của bạn đang tham gia các nhóm (công khai) nào trên facebook - -
-
- 73. Facebook - Xem các trang bạn bè thích - - [Xem mã nguồn](/scripts/fb_searchPageForOther.js) - - Biết bạn bè của bạn đang thích các trang (công khai) nào trên facebook - -
-
- 74. Facebook - Tìm mọi bài viết của bạn bè - - [Xem mã nguồn](/scripts/fb_searchPostsForOther.js) - - Tìm tất cả bài posts công khai của bạn bè trên facebook. Bao gồm bài post trong group, page, trên tường, ... - -
- ---- Access Token --- -
- 75. Kiểm tra fb access token - - [Xem mã nguồn](/scripts/fb_checkToken.js) - - Kiểm tra loại, quyền, ngày tạo, ngày hết hạn, ... của facebook access token - -
-
- 76. Lấy fb token EAAB (instagram) - - [Xem mã nguồn](/scripts/fb_getTokenFacebook.js) - - Lấy facebook access token EAAB từ trang www.facebook.com - -
-
- 77. Lấy fb token EAADo1 (messenger) - - [Xem mã nguồn](/scripts/fb_getTokenMessage.js) - - Lấy facebook access token từ trang www.facebook.com (messenger_for_android) - -
-
- 78. Lấy fb token EAAG (business_locations) - - [Xem mã nguồn](/scripts/fb_getTokenBussinessLocation.js) - - Lấy facebook token EAAG từ business.facebook.com - -
-
- 79. Lấy fb token EAAB (campaigns) - - [Xem mã nguồn](/scripts/fb_getTokenCampaigns.js) - - Lấy facebook token EAAB từ www.facebook.com (campaigns) - -
-
- 80. Lấy fb token từ cookie (ffb.vn) - - [Xem mã nguồn](/scripts/fb_getTokenFfb.js) - - Gửi cookie facebook lên API của ffb.vn - -
- ---- Lấy ID --- -
- 81. Lấy fb User ID - - [Xem mã nguồn](/scripts/fb_getUid.js) - - Lấy id của user trong trang facebook hiện tại - -
-
- 82. Lấy fb Page ID - - [Xem mã nguồn](/scripts/fb_getPageId.js) - - Lấy id của page trong trang facebook hiện tại - -
-
- 83. Lấy fb Group ID - - [Xem mã nguồn](/scripts/fb_getGroupId.js) - - Lấy id của group trong trang facebook hiện tại - -
-
- 84. Lấy fb Album ID - - [Xem mã nguồn](/scripts/fb_getAlbumId.js) - - Lấy id của facebook album trong trang web hiện tại - -
-
- 85. Lấy tất cả fb album id từ trang hiện tại - - [Xem mã nguồn](/scripts/fb_getAllAlbumIdFromCurrentWebsite.js) - - Lấy tất cả album id có trong trang facebook - -
-
- 86. Lấy fb User ID từ URL - - [Xem mã nguồn](/scripts/fb_getUidFromUrl.js) - - Lấy id của facebook user từ URL truyền vào - -
-
- 87. Lấy tất cả fb user ID từ trang tìm kiếm - - [Xem mã nguồn](/scripts/fb_getAllUidFromFbSearch.js) - - Lấy id của tất cả user từ trang tìm kiếm người dùng facebook - -
-
- 88. Lấy tất cả fb user ID từ group - - [Xem mã nguồn](/scripts/fb_getAllUidOfGroupMembers.js) - - Lấy id của tất cả user từ group facebook - -
- ---- Phím tắt --- -
- 89. Xem mục đã lưu trên facebook - - [Xem mã nguồn](/scripts/fb_openSaved.js) - - Xem nội dung bạn đã lưu trên Facebook - -
-
- 90. Xem kỷ niệm của bạn trên facebook - - [Xem mã nguồn](/scripts/fb_openMemories.js) - - Xem kỷ niệm (memories) của bạn trên facebook - -
-
- 91. Xem các quảng cáo fb bạn đã xem - - [Xem mã nguồn](/scripts/fb_openAdsActivities.js) - - Xem các quảng cáo bạn đã xem trên facebook - -
-
- 92. Xem nhật ký hoạt động trên facebook - - [Xem mã nguồn](/scripts/fb_openAllActivities.js) - - Kiểm tra nhật ký hoạt động của bạn trên facebook - -
-
- 93. Video bạn vừa xem trên facebook - - [Xem mã nguồn](/scripts/fb_openVideoActivities.js) - - Xem lại những video bạn đã xem trên facebook - -
-
- 94. Sự kiện đã tham gia trên facebook - - [Xem mã nguồn](/scripts/fb_openPassEvents.js) - - Xem tất cả sự kiện bạn từng tham gia trên facebook. - -
-
- 95. Sinh nhật bạn bè facebook - - [Xem mã nguồn](/scripts/fb_openBirthdays.js) - - Xem từng tháng có những sinh nhật nào của bạn bè trên facebook. - -
-
- 96. Đổi ngôn ngữ facebook - - [Xem mã nguồn](/scripts/fb_openChangeLanguage.js) - - Đổi ngôn ngữ hiển thị trên facebook - -
-
- 97. Khôi phục tài khoản facebook - - [Xem mã nguồn](/scripts/fb_openAccountHacked.js) - - Tài khoản fb của bạn bị hack? Facebook có thể giúp bạn. - -
- -### Instagram - -
- 98. Lấy insta thông tin user (uid, avatar, ...) - - [Xem mã nguồn](/scripts/insta_getUserInfo.js) - - Lấy instagram uid, avatar, tên, ... - -
-
- 99. Thêm nút tải cho Instagram - - [Xem mã nguồn](/scripts/insta_injectDownloadBtn.js) - - Thêm nút để tải (ảnh/video/story/post) trên Instagram - - ![](/scripts/insta_injectDownloadBtn.png) - -
-
- 100. Insta - Xem story ẩn danh - - [Xem mã nguồn](/scripts/insta_anonymousStoryViewer.js) - - Xem story instagram không bị đối phương phát hiện - -
- ---- Tải hàng loạt --- -
- 101. Tải về tất cả media của insta user (API) - - [Xem mã nguồn](/scripts/insta_getAllUserMedia.js) - - Tải về tất cả ảnh/video của người dùng instagram (sử dụng API instagram) - -
-
- 102. Insta - Tải tất cả following/follower - - [Xem mã nguồn](/scripts/insta_getFollowForOther.js) - - Biết bạn bè của bạn (hoặc chính bạn) đang follow những ai / được ai follow trên instagram. Tải về file json - -
- -### Youtube - -
- 103. Picture in Picture - - [Xem mã nguồn](/scripts/pictureInPicture.js) - - Xem video trong cửa sổ nổi - -
-
- 104. Tải video/audio youtube - - [Xem mã nguồn](/scripts/youtube_downloadVideo.js) - - Tải cả video giới hạn độ tuổi, không cần đăng nhập -
    -
  • Bấm 1 lần để tải video hiện tại
  • -
  • Bật tự chạy để hiển thị nút tải
  • -
- - ![](/scripts/youtube_downloadVideo.png) - -
-
- 105. Lấy phụ đề video trên Youtube - - [Xem mã nguồn](/scripts/youtube_getVideoCaption.js) - - - Bấm để tải về tất cả phụ đề của video youtube đang xem
- Bật tự chạy để hiển thị phụ đề thời gian thực
- - ![](/scripts/youtube_getVideoCaption.png) - -
-
- 106. Hiện lượt không thích youtube - - [Xem mã nguồn](/scripts/youtube_viewDislikes.js) - - Hiển thị số lượt không thích của video/short youtube - -
-
- 107. Youtube nonstop - - [Xem mã nguồn](/scripts/youtube_nonstop.js) - - Phát youtube không còn bị làm phiền bởi popup 'Video đã tạm dừng. Bạn có muốn xem tiếp?' của youtube. - - ![](/scripts/youtube_nonstop.png) - -
-
- 108. Đổi quốc gia Youtube - - [Xem mã nguồn](/scripts/youtube_changeCountry.js) - - Đổi quốc gia youtube để xem nội dung youtube bên các nước khác - -
-
- 109. Lấy thumbnail video trên Youtube - - [Xem mã nguồn](/scripts/youtube_getVideoThumbnail.js) - - Tải về hình thumbnail độ phân giải lớn nhất của video youtube đang xem - -
-
- 110. Tắt/Mở đèn youtube - - [Xem mã nguồn](/scripts/youtube_toggleLight.js) - - Tắt/Mở đèn để tập trung xem video youtube - -
-
- 111. PIP toàn website - - [Xem mã nguồn](/scripts/pip_fullWebsite.js) - - Picture in picture: Xem toàn bộ website (thay vì chỉ video) trong của sổ nổi - -
-
- 112. PIP cho canvas - - [Xem mã nguồn](/scripts/pip_canvas.js) - - Picture in picture: Xem canvas trong của sổ nổi - -
-
- 113. Improve YouTube - 85+ chức năng - - [Xem mã nguồn](/scripts/recommend_improve_youtube.js) - - Làm cho YouTube gọn gàng+thông minh! - -
- -### Tiktok - ---- Tiktok --- -
- 114. Tiktok - Tải video đang xem - - [Xem mã nguồn](/scripts/tiktok_downloadWatchingVideo.js) - - Tải video tiktok bạn đang xem (không watermark) - -
-
- 115. Tiktok - Tải video từ URL - - [Xem mã nguồn](/scripts/tiktok_downloadVideo.js) - - Tải video tiktok từ link (không watermark) - -
-
- 116. Tiktok - Tải hàng loạt - - [Xem mã nguồn](/scripts/tiktok_batchDownload.js) - - Tải hàng loạt video tiktok (trang người dùng, trang tìm kiếm), có giao diện chọn video muốn tải. - - ![](/scripts/tiktok_batchDownload.jpg) - -
- ---- Douyin --- -
- 117. Douyin - Tải video đang xem - - [Xem mã nguồn](/scripts/douyin_downloadWachingVideo.js) - - Hiển thị mọi video có thể tải trong trang douyin hiện tại - -
-
- 118. Douyin - Tải tất cả video người dùng - - [Xem mã nguồn](/scripts/douyin_downloadAllVideoUser.js) - - Tải tất cả video trong trang cá nhân của người dùng douyin. - -
- -### Tự động - ---- Tiện ích --- -
- 119. Thời gian lướt web - - [Xem mã nguồn](/scripts/web_timer.js) - - Lưu lại / Kiểm tra thời gian lướt web của bạn cho từng trang web.
-

BẤM NÚT ĐỂ XEM BIỂU ĐỒ.

- - ![](/scripts/web_timer.png) - -
-
- 120. Tự động khoá trang web - - [Xem mã nguồn](/scripts/auto_lockWebsite.js) - - Tự động khoá trang web. Nhập mật khẩu để mở khoá.
-
    -
  • Click để khoá trang hiện tại.
  • -
  • Bấm nút để mở giao diện quản lý.
  • -
- -
-
- 121. Cuộn chuột siêu mượt - - [Xem mã nguồn](/scripts/smoothScroll.js) - - Cuộn chuột siêu mượt cho mọi trang web.
-
    -
  • Khuyên dùng với chuột (tắt nếu dùng touchpad)
  • -
  • Bấm để Tắt/Mở cho trang web hiện tại
  • -
  • Hỗ trợ bấm chuột giữa để cuộn trang
  • -

- -
-
- 122. Phóng to mọi hình ảnh - - [Xem mã nguồn](/scripts/magnify_image.js) - - Xem bất kỳ hình ảnh nào trong cửa sổ phóng đại
- Nơi bạn có thể phóng to/thu nhỏ, xoay, kéo thả, ...
- Tự động tìm ảnh chất lượng cao để hiển thị. -

-

4 cách sử dụng:

- Dùng ngay: -
    -
  1. Chuột phải vào ảnh/trang web > chọn magnify image
  2. -
  3. Click chức năng rồi click chọn ảnh
  4. -
- Cần mở tự chạy: -
    -
  1. Đưa chuột vào ảnh/video > bấm nút phóng to
  2. -
  3. Ctrl 2 lần vào ảnh
  4. -
- -
-
- 123. Tự động - xem ảnh lớn nhất - - [Xem mã nguồn](/scripts/auto_redirectLargestImageSrc.js) - -
    -
  • Khi bạn mở xem ảnh trong tab mới.
  • -
  • Chức năng này sẽ tự động tìm và chuyển trang sang ảnh chất lượng cao nhất.
  • -
  • Hỗ trợ hàng trăm trang web.
  • -
- - ![](/scripts/auto_redirectLargestImageSrc.jpg) - -
-
- 124. Hiện ảnh khi di chuột qua link - - [Xem mã nguồn](/scripts/showImageOnHoverLink.js) - - Xem trước hình ảnh khi bạn đưa chuột qua link hình ảnh - -
-
- 125. Xoá theo dõi trong url - - [Xem mã nguồn](/scripts/remove_tracking_in_url.js) - - Xoá các tham số theo dõi trong url, chặn theo dõi người dùng từ Facebook, Google, Tiktok, Twitter ...
    -
  • fbclid
  • -
  • ttclid
  • -
  • utm_*
  • -
  • ...
  • -
- -
-
- 126. Không tắt trình duyệt khi tắt tab cuối - - [Xem mã nguồn](/scripts/prevent_closeBrowser_lastTab.js) - - Không tắt trình duyệt khi tắt tab cuối cùng
- Tự động mở tab mới khi bạn tắt tab cuối cùng
- -
-
- 127. Chống lừa đảo - - [Xem mã nguồn](/scripts/chongLuaDao.js) - - Cảnh báo khi bạn truy cập các trang web có nguy cơ lừa đảo, giả mạo, có nội dung xấu hoặc phần mềm độc hại
-
    -
  • Click để tính toán độ an toàn của trang web hiện tại
  • -
  • Click để cập nhật dữ liệu website giả mạo mới nhất
  • -
- -
-
- 128. Rút gọn link - - [Xem mã nguồn](/scripts/shortenURL.js) - - Hỗ trợ tinyurl, tnyim, cuttly, bitly, j2team, ... - -
-
- 129. Giải mã link rút gọn - - [Xem mã nguồn](/scripts/unshorten.js) - - Lấy link gốc của link rút gọn - -
-
- 130. Tạo tin nhắn tàng hình - - [Xem mã nguồn](/scripts/createInvisibleText.js) - - Tạo tin nhắn tàng hình, giúp ẩn đi thông tin quan trọng, người nhận cần dùng chức năng này để có thể giải mã. - -
- ---- Tự động --- -
- 131. In web ra PDF - - [Xem mã nguồn](/scripts/webToPDF.js) - - Chuyển trang web hiện tại thành PDF - -
-
- 132. Chụp ảnh toàn bộ web - - [Xem mã nguồn](/scripts/screenshotFullPage.js) - - Tạo 1 ảnh chụp màn hình chứa toàn bộ nội dung website - -
-
- 133. Chụp ảnh web - - [Xem mã nguồn](/scripts/screenshotVisiblePage.js) - - Chụp ảnh trang web hiện tại (bypass những trang web cấm chụp màn hình như Netflix,...) - -
-
- 134. Cuộn trang xuống cuối cùng - - [Xem mã nguồn](/scripts/scrollToVeryEnd.js) - - Cuộn tới khi nào không còn data load thêm nữa (trong 5s) thì thôi. Click chuột để huỷ.

Mẹo

Bạn có thể bấm chuột giữa để tự động scroll lên/xuống trên mọi trang web.
- -
-
- 135. Trích xuất mọi emails từ trang web - - [Xem mã nguồn](/scripts/getAllEmailsInWeb.js) - - Trích xuất tất cả emails trong web và hiện trong popup mới - -
-
- 136. Bật/Tắt Hack game T-Rex Dino - - [Xem mã nguồn](/scripts/dino_hack.js) - - Tự động chơi game Google Chrome T-Rex - -
-
- 137. Tạo mật khẩu cho trang web - - [Xem mã nguồn](/scripts/passwordGenerator.js) - - Bạn chỉ còn cần phải nhớ 1 mật khẩu - -
- ---- Công cụ --- -
- 138. Chữ sang QRCode - - [Xem mã nguồn](/scripts/textToQrCode.js) - - Chuyển chữ/link sang QRCode - -
-
- 139. Văn bản thành Giọng nói (j2team) - - [Xem mã nguồn](/scripts/textToSpeech.js) - - Chuyển đổi văn bản thành giọng nói sử dụng công cụ của j2team - -
-
- 140. Thay đổi đầu ra âm thanh - - [Xem mã nguồn](/scripts/changeAudioOutput.js) - - Thay đổi đầu ra âm thanh của trang web đang mở.
Mỗi tab có thể chọn đầu ra khác nhau (tai nghe/loa).

Ví dụ: nghe youtube bằng tai nghe ở tab 1,
nghe nhạc bằng loa ở tab 2.
- -
-
- 141. Send - Chia sẻ file nhanh - - [Xem mã nguồn](/scripts/send_shareFiles.js) - - Mở send.zcyph.cc - chia sẻ file lớn lên tới 20Gb - -
-
- 142. Vuiz - Tạo logo WAP online - - [Xem mã nguồn](/scripts/vuiz_createLogo.js) - - Tạo logo chữ đẹp theo mẫu có sẵn - -
-
- 143. Phân tích hiệu suất - - [Xem mã nguồn](/scripts/performanceAnalyzer.js) - - Phân tích hiệu suất website không cần biết code - -
-
- 144. IT Tools - Vì tương lai Developer - - [Xem mã nguồn](/scripts/recommend_ItTools.js) - - Tổng hợp tools hữu ích cho IT (mã nguồn mở) - -
-
- 145. CopyIcon - emoji, icon, svg miễn phí - - [Xem mã nguồn](/scripts/recommend_copyicon.js) - - 285,000 Icons, Emiji, trình tạo SVG, và hơn thế nữa... - -
-
- 146. Beautify Tools - - [Xem mã nguồn](/scripts/recommend_beautifytools.js) - - Tổng hợp tools hữu ích cho IT -
    -
  1. Beautifiers And Minifiers
  2. -
  3. CSS Preprocessors
  4. -
  5. Converters
  6. -
  7. String Utilities
  8. -
  9. SEO Tools
  10. -
  11. IP Tools
  12. -
  13. Code Validators
  14. -
  15. Cryptography
  16. -
  17. Code Editors
  18. -
- -
- ---- Github --- -
- 147. Github - Đi tới commit bất kỳ - - [Xem mã nguồn](/scripts/github_goToAnyCommit.js) - - Đi tới commit bất kỳ của repo github. Bao gồm cả commit đầu tiên. - -
-
- 148. Github - xem trước file HTML - - [Xem mã nguồn](/scripts/github_HTMLPreview.js) - - Xem trước giao diện file HTML trên bất kỳ repo github nào mà không cần tải code về. - -
-
- 149. Github - Mở repo pages - - [Xem mã nguồn](/scripts/github_openRepoPages.js) - - Chuyển đổi giữa trang github.com repo và github.io demo pages

- username.github.io/repo
- github.com/username/repo
- -
-
- 150. Github - Mở repo trong github.dev - - [Xem mã nguồn](/scripts/githubdev.js) - - Mở repo hiện tại trong trang github.dev để xem code

Mẹo

Bấm dấu chấm (.) để mở repo github trong github.dev
- -
-
- 151. Github - Mở repo trong github1s.com - - [Xem mã nguồn](/scripts/github1s.js) - - Mở repo hiện tại trong trang github1s.com để xem code - -
-
- 152. Cloc - đếm số dòng code - - [Xem mã nguồn](/scripts/recommend_cloc.js) - - Đếm dòng trống, comment, dòng code trong repo, hỗ trợ nhiều ngôn ngữ lập trình. - - ![](/scripts/recommend_cloc.png) - -
-
- 153. Refined GitHub - - [Xem mã nguồn](/scripts/recommend_refined_github.js) - - Sửa giao diện github và thêm hàng tá chức năng hay - -
- ---- Mua sắm --- -
- 154. Shopee - Loại hàng mua nhiều nhất - - [Xem mã nguồn](/scripts/shopee_topVariation.js) - - Thống kê xem tùy chọn sản phẩm nào được mọi người mua nhiều nhất - - ![](/scripts/shopee_topVariation.png) - -
-
- 155. Shopee - Thống kê chi tiêu - - [Xem mã nguồn](/scripts/shopee_totalSpendMoney.js) - - Xem bạn đã mua hết bao nhiêu tiền trên Shopee - -
-
- 156. Shopee - Xuất lịch sử đơn hàng (Excel) - - [Xem mã nguồn](/scripts/shopee_totalSpendMoney_excel.js) - - Xuất lịch sử đơn hàng từ Shopee sang file Excel - -
-
- 157. Tiki - Đã mua bao nhiêu tiền? - - [Xem mã nguồn](/scripts/tiki_totalSpendMoney.js) - - Xem bạn đã mua hết bao nhiêu tiền trên Tiki - -
-
- 158. Beecost - - [Xem mã nguồn](/scripts/recommend_Beecost.js) - - Kiểm tra giá/ưu đãi giả khi mua hàng online - -
- ---- PDF --- -
- 159. FastDoc - Chuyển PDF/Ảnh sang Word/Excel - - [Xem mã nguồn](/scripts/recommend_fastDoc.js) - - Chuyển đổi hình ảnh và pdf sang Excel, Word, Searchable PDF miễn phí - -
-
- 160. SmartPDF - Công cụ cho PDF - - [Xem mã nguồn](/scripts/recommend_smartPDF.js) - - Giảm dung lượng PDF, Chuyển đổi PDF, PPT sang PDF, PDF sang PPT, JPG sang PDF, PDF sang JPG, Excel sang PDF, PDF sang Excel, Chỉnh sửa PDF, Trình đọc PDF, Số trang, Xóa các trang PDF, Xoay PDF, Word sang PDF, PDF sang Word, Ghép PDF, Cắt PDF, Ký tên PDF, Mở khóa PDF, Bảo vệ PDF, Máy quét PDF - -
-
- 161. PDF Stuffs - Công cụ PDF - - [Xem mã nguồn](/scripts/recommend_pdfstuffs.js) - - Công cụ chuyển đổi PDF online miễn phí: Ghép file PDF, Tách file PDF, Nén file PDF, PDF sang Word, PDF sang PPT, PDF sang Excel, Word sang PDF, Excel sang PDF, PPT sang PDF, PDF sang JPG, JPG sang PDF, PDF sang HTML, HTML sang PDF, Mở khóa PDF, Khóa file PDF, Xoay file PDF, Cắt file PDF, Xóa trang PDF, Đánh số trang PDF, Chèn watermark - -
- -### Mở khoá - ---- Mở khoá web --- -
- 162. Hack Duck race - - [Xem mã nguồn](/scripts/duckRace_cheat.js) - - Hack kết quả Duck race, sẽ luôn ra kết quả bạn mong muốn - - ![](/scripts/duckRage_cheat.png) - -
-
- 163. Hack Wheel of Names - - [Xem mã nguồn](/scripts/wheelOfNames_hack.js) - - Hack kết quả trang web
  • wheelofnames.com
  • wheelrandom.com
  • spinthewheel.io
luôn ra kết quả bạn mong muốn.
- -
-
- 164. Đọc bài viết medium full - - [Xem mã nguồn](/scripts/medium_readFullArticle.js) - - Đọc bài viết medium full không cần đăng nhập - -
-
- 165. Medium - Fix font Tiếng Việt - - [Xem mã nguồn](/scripts/medium_fixVietnameseFont.js) - - Sửa lỗi font Tiếng Việt khó nhìn trong Medium
-
    -
  • Click 1 lần để sửa trang hiện tại (không cần tải lại trang)
  • -
  • Bật tự chạy cho các lần sau vào Medium
  • -
- -
-
- 166. Fireship - Mở khoá PRO - - [Xem mã nguồn](/scripts/fireship_vip.js) - - Mở khoá tất cả khoá học/bài giảng PRO trên Fireship (tiết kiệm $399 USD) - -
-
- 167. Studocu - Xem miễn phí VIP - - [Xem mã nguồn](/scripts/studocu_bypassPreview.js) - - Xem tài liệu VIP trên Studocu.com, loại bỏ popup chặn xem trước, loại bỏ hiệu ứng làm mờ. - -
-
- 168. Scribd - Xem miễn phí VIP - - [Xem mã nguồn](/scripts/scribd_bypassPreview.js) - - Xem tài liệu VIP trên Scribd.com, loại bỏ popup chặn xem trước, loại bỏ hiệu ứng làm mờ. - -
-
- 169. Studyphim - Xem miễn phí - - [Xem mã nguồn](/scripts/studyphim_unlimited.js) - - Xem phim miễn phí trên Studyphim không cần đăng nhập - -
-
- 170. Bypass Learn Anything - - [Xem mã nguồn](/scripts/bypass_learnAnything.js) - - Xem nội dung web learn-anything.xyz không cần đăng ký member - - ![](/scripts/bypass_LearnAnything.png) - -
- ---- Mở khoá chức năng --- -
- 171. Bật/Tắt cho phép sao chép - - [Xem mã nguồn](/scripts/simpleAllowCopy.js) - - Cho phép sao chép trong mọi trang web
-

CHÚ Ý:

-
    -
  • Cần bật tự động chạy trước
  • -
  • Khi vào trang web muốn copy/chuột phải, click 1 lần chức năng để BẬT.
  • -
  • Khi copy/chuột phải xong có thể click lần nữa để TẮT.
  • -
- -
-
- 172. Phát hiện ký tự ẩn (Zero-Width) - - [Xem mã nguồn](/scripts/detect_zeroWidthCharacters.js) - - Phát hiện ký tự ẩn (zero-width) trong văn bản cho trình duyệt, e-mail client, trình soạn thảo văn bản,... - -
-
- 173. Nhúng script vào trang web - - [Xem mã nguồn](/scripts/injectScriptToWebsite.js) - - Nhúng link script vào website, ví dụ nhúng jquery, thư viện js, ... - -
-
- 174. Hiện các thành phần web bị ẩn - - [Xem mã nguồn](/scripts/showHiddenFields.js) - - Web thường ẩn mốt số thành phần như token, id, form, ... - -
-
- 175. Xem cookies - - [Xem mã nguồn](/scripts/viewCookies.js) - - Xem cookies được lưu trong website hiện tại - -
-
- 176. Xoá Cookies - - [Xem mã nguồn](/scripts/removeCookies.js) - - Xoá cookies trang hiện tại - -
- ---- Khác --- -
- 177. Tăng tốc tối đa trình duyệt - - [Xem mã nguồn](/scripts/recommend_chromeFlags.js) - - Các flags giúp trình duyệt của bạn chạy nhanh hơn thỏ - -
-
- 178. Xem mật khẩu wifi đã lưu - - [Xem mã nguồn](/scripts/recommend_viewSavedWifiPass.js) - - Powershell script giúp xem mật khẩu wifi đã lưu trên máy tính - -
-
- 179. Leak check - lộ mật khẩu email? - - [Xem mã nguồn](/scripts/recommend_leakCheck.js) - - Kiểm tra xem mật khẩu email/username của bạn có bị phát tán trên mạng hay không - -
- -### Giao diện - ---- Nổi bật --- -
- 180. Chế độ tối cho PDF - - [Xem mã nguồn](/scripts/darkModePDF.js) - - Bật/Tắt chế độ tối cho PDF bạn đang xem - -
-
- 181. Bật/tắt chế độ chỉnh sửa website - - [Xem mã nguồn](/scripts/toggleEditPage.js) - - Cho phép chỉnh sửa mọi văn bản trong website - -
-
- 182. Hiện thị FPS - - [Xem mã nguồn](/scripts/showFPS.js) - - Hiện thị tốc độ khung hình của trang web hiện tại (sử dụng thư viện stat.js) - -
-
- 183. Hiện thị FPS - ver 2 - - [Xem mã nguồn](/scripts/showFps_v2.js) - - Hiện thị tốc độ khung hình của trang web hiện tại (sử dụng debugger) - -
-
- 184. Ẩn/Hiện ô nhập mật khẩu - - [Xem mã nguồn](/scripts/toggle_passwordField.js) - - Ẩn/hiện giá trị trong các ô nhập mật khẩu
(ví dụ **** -> 1234)
- -
-
- 185. Dark reader - - [Xem mã nguồn](/scripts/recommend_DarkReader.js) - - Chế độ tối cho mọi trang web - -
-
- 186. CSS Portal - Nâng trình CSS - - [Xem mã nguồn](/scripts/recommend_cssportal.js) - - Công cụ tự động giúp nâng trình CSS của bạn với hàng trăm chức năng. - -
-
- 187. CSS Loaders - 600+ css loading - - [Xem mã nguồn](/scripts/recommend_cssloaders.js) - - Hơn 600 animation loading css miễn phí - -
-
- 188. UIverse - Tổng hợp code UI xịn - - [Xem mã nguồn](/scripts/recommend_uiverse.js) - - Tổng hợp code UI mã nguồn mở cho mọi trang web. - -
- ---- Xem --- -
- 189. Font Rendering - font chữ dễ nhìn - - [Xem mã nguồn](/scripts/recommend_fontRendering.js) - - Cải thiện font chữ web, giúp lướt web dễ chịu hơn. - -
-
- 190. Kiểm tra font chữ - - [Xem mã nguồn](/scripts/whatFont.js) - - Kiểm tra xem từng phần tử trong web dùng font chữ gì - -
-
- 191. Xem tất cả tất cả javascript events - - [Xem mã nguồn](/scripts/visualEvent.js) - - Visual Event - Hiển thị tất cả javascript events xuất hiện trong trang web - -
-
- 192. Xem mọi hình ảnh có trong website - - [Xem mã nguồn](/scripts/listAllImagesInWeb.js) - - Xem danh sách hình ảnh trong tab mới - -
-
- 193. Xem tất cả link - - [Xem mã nguồn](/scripts/viewAllLinks.js) - - Liệt kê tất cả đường link có trong website - -
-
- 194. Xem tất cả scripts - - [Xem mã nguồn](/scripts/viewScriptsUsed.js) - - Mở danh sách scripts đươc dùng bởi trang web trong tab mới - -
-
- 195. Xem tất cả stylesheet - - [Xem mã nguồn](/scripts/viewStylesUsed.js) - - Mở danh sách css được dùng bởi website trong tab mới - -
-
- 196. Trình kiểm tra css cục bộ - - [Xem mã nguồn](/scripts/cssSelectorViewer.js) - - Kiểm tra mã css cho thành phần bất kỳ trong trang web - -
- ---- Xoá --- -
- 197. Xoá màu website - - [Xem mã nguồn](/scripts/removeColours.js) - - Xoá mọi màu có trong website.
Bấm lại để hoàn tác.
- -
-
- 198. Xoá stylesheet - - [Xem mã nguồn](/scripts/removeStylesheet.js) - - Xem trang web sẽ ra sao khi không có css.
Bấm lại để hoàn tác.
- -
-
- 199. Xoá mọi hình ảnh - - [Xem mã nguồn](/scripts/removeImages.js) - - Chỉ để lại văn bản, giúp tập trung hơn.
Bấm lại để hoàn tác.
- -
-
- 200. Xoá mọi iframe/embed - - [Xem mã nguồn](/scripts/removeBloat.js) - - Xoá mọi thứ gây xao nhãng (quảng cáo, web nhúng, ..) - -
- ---- Bảng --- -
- 201. Thêm sắp xếp cho bảng - - [Xem mã nguồn](/scripts/table_addSortTable.js) - - Thêm nút chức năng sắp xếp cho từng cột trong table - -
-
- 202. Thêm cột số thứ tự - - [Xem mã nguồn](/scripts/table_addNumberColumn.js) - - Thêm cột STT vào bên trái bảng - -
-
- 203. Đổi chỗ hàng và cột - - [Xem mã nguồn](/scripts/table_swapRowAndColumn.js) - - Hàng thành cột và cột thành hàng - -
- ---- Khác --- -
- 204. Tô màu cho link - - [Xem mã nguồn](/scripts/internalOrExternalLink.js) - - +Đỏ: cùng domain -+Cam: hiện tại -+Xanh: khác domain - -
-
- 205. Lấy kích thước trang web - - [Xem mã nguồn](/scripts/getWindowSize.js) - - đơn vị pixels - -
-
- 206. Hiệu ứng tuyết rơi - - [Xem mã nguồn](/scripts/letItSnow.js) - - Thêm hiệu ứng tuyết rơi vào trang web - -
diff --git a/scripts/magnify_image.js b/scripts/magnify_image.js index ae8b68cd..9c960197 100644 --- a/scripts/magnify_image.js +++ b/scripts/magnify_image.js @@ -71,6 +71,7 @@ export default { "2024-05-21": "not require autorun", "2024-06-07": "support video frame + right click anywhere + magnify on hover + search by images", + "2024-07-28": "config img size for hover", }, buttons: [ { From a549788868633ced484919947eac277ec054b590 Mon Sep 17 00:00:00 2001 From: HoangTran <99.hoangtran@gmail.com> Date: Sun, 28 Jul 2024 01:22:48 +0700 Subject: [PATCH 43/47] refactor --- scripts/scrollToVeryEnd.js | 13 +++++++------ scripts/tiktok_batchDownload.jpg | Bin 129000 -> 0 bytes scripts/tiktok_batchDownload.js | 24 +++++++++++++++++------- scripts/tiktok_batchDownload.png | Bin 0 -> 105514 bytes 4 files changed, 24 insertions(+), 13 deletions(-) delete mode 100644 scripts/tiktok_batchDownload.jpg create mode 100644 scripts/tiktok_batchDownload.png diff --git a/scripts/scrollToVeryEnd.js b/scripts/scrollToVeryEnd.js index ded8b684..40b62240 100644 --- a/scripts/scrollToVeryEnd.js +++ b/scripts/scrollToVeryEnd.js @@ -24,7 +24,7 @@ export default { }, }; -export function scrollToVeryEnd() { +export function scrollToVeryEnd(animation = true) { return new Promise(async (resolve, reject) => { const notify = UfsGlobal.DOM.notify({ msg: "Useful-script: Scrolling to very end...", @@ -49,8 +49,6 @@ export function scrollToVeryEnd() { } } - console.log(scrollableElements); - // If only one scrollable element is found, return it if (scrollableElements.length === 1) { return scrollableElements[0]; @@ -72,7 +70,12 @@ export function scrollToVeryEnd() { } let height = (ele) => (ele || document.body).scrollHeight; - let down = (ele = document) => ele.scrollTo({ left: 0, top: height(ele) }); + let down = (ele = document) => + ele.scrollTo({ + left: 0, + top: height(ele), + behavior: animation ? "smooth" : "instant", + }); let sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); let lastScroll = { @@ -91,8 +94,6 @@ export function scrollToVeryEnd() { let scrollEle = findMainScrollableElement(); - console.log(scrollEle); - while (running) { down(scrollEle); let currentHeight = height(scrollEle); diff --git a/scripts/tiktok_batchDownload.jpg b/scripts/tiktok_batchDownload.jpg deleted file mode 100644 index 26e32362d903eca42760da03fb6a7b730f8b514a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 129000 zcmb@tWmFwaw=UX9kYGVVa1z|zJ-8Fx-QC?ixa&d^+}#}#SU5oz?(XgcIK1!v&c64K zvCp3~ZdH%&>ZKeZyP2$)(5l?Sm;Eb zK7RZ}L_&y9NJ2wSPQ%8+CMv1}_22iy{@)3B?E}yepi^L3V4z3mq>gUnLjm|6TfjpP2vOw?eHLP-~A+=-@Fg=o1OkPaTxB zss>D>Is3U)UNyXMqs-CLhndY1qFM%i;xhmM*P@c zs~E>7vGeupypGlRbXMtlT=nw4?Fy51)GliKgt$b_8gjoYH#5T({dlXQ z>CGCl@(dazj(^lwBn{wTlkN!LjSma>xAstHf|3EAXX9Pd(f5c(bDS>i2K$+|WUjyH z?1*s^g!RJU1&tt$iKU@uTM(iWh41ir73XMl(_IH5SBC=2zoACoq#zHmr;E@H7NnLQ zp8oL_P7N%MGxR&T49|qdgX=B9ov_maOnUxN6sZ0Hw{c_pk0pB8&QMwzQH|Z5DnmL> zCwequ{lm7tGdI_Je^v#x+3T_ARx|5`^8a@4>x<32;8_X(iiQgK0|2axKGOJ1lT@W2 zwHxv5OA^odmV_R24lX$}g5H#swnZ0xiuu7XNg)g0@k}bcz{jUZejAzT-6Qj{+s>R& z&ApQ{UA#4aETLHIDDyV}EsKDU=h0dXUYNl(fe&0Kld`p{guAS-cj;Zjd(+rB7 z5CFpx8>!#Uevze$F~f}NuSBjwf2R7ta(?}=?avazh8&pY-l_SA{t%pIR~Xx6lx2<{x5V*rrAU3PprnCc6c4AHr|`2qrSAd^@gSAml|8b161 zmRP%zQbF&VLI<$Mv{X-HXXXkwW4OrlxVb<^9W5h?+igP)jXhHH7jqQubr=!z{sbNTlax3XU0sZ3 z*-gSPQf!Y&jD;NycPGX1m$|9JcmTecOD+VH+*L<#?9fc^eJ9tXjaoyfdT~c5iLfE3 z0oGkWs&qClxOt=)VJFl_t%RN@FUrf!JT^T`vhsV1Du0FENBScio4b>AHK~(td=Hy{ zf10EEa$CT;tD1kdRR3smmQu&gbK?*o;GUCoE+2M_MgO67RHTP3Vk_C~#+Alc%5o0yr_8CDBFf1voupY*bXvN^yR0ePy(@?)^+y=|;ALyPynrm%Co@zUvkUg#pVJ15 z9iI$&YdA(poyNDi+C|l{mhZWZGS#YKfQS1U4-?9Bei+LuA>Kw$pvc4HPz)^06_yjk zuHO8$#fR2c5IoBQRN{f~0At*bUXg1O(o;4E?2xlR&T}8>SG^6Sr$(>%V!qZGB++^0 zut?a;T#bHHJ3#qy@T-%NNcwVop&MG+(LJA^*s7^U8Y!=0>%F2D z6pvw>u47r-Br+;PUu0LM9m?3w@g-8^gcVZuYmA^>k6iZ^H7Rj znkG@S1|*rju~y)552}8HFKXq&A36OJIVnax+hy*n8Rm?FN^fr&?J|!2Jvmo(K6L7Iv5GQX7*5n><&!!0 zce+zl2p*EAI%r2kD0jy6RGj zOG*&B?G;9p<-U0)7Hd<5FnIa7y2i_F6O_-3;xfWN5(~@B<-rnM?**%aKItlRmN??% z>w+$3bveTLB(2+<$0!ASF|wOaZ+^+Oh42yEgb$?8VWEC%+kK2=JRtn!**Sb;8LRFX zBDaW(42Q@9e!|8lzwN*uT%=6$7RhXzimtkU%rV;EoZfltRz>_+e^@IT`0#AC16gL^ z*g>}<>r^eYP^jh?9=6l&n&jp!<{r8BNoX!XW$Btw0g3pVSt%PIP!Z22B zMULCLi+}jE#Mw9wJiDw&h~(9ThqS3Qu^lWF8mriMWS(|#nqJrh&mL=>!K4WJ^x>ZM zVnfT;Ng2$(WOtP0@-fIU-Z5fH<>%Oy`6^qUTg7{h=bR0FD%Ul-kR)>rtfcTV>#I}q z%p;NV+Q5*9*L6)9%i4-olBXMXHOTzEYQpUH#9v1j?0wCZyIx#1>18Y5wVcZS`atdfc`PUGZ= zP!T(Gu`;EX`=Cb@T%MyOkV-y;F0Z&ZCjx=T9N~ zM{eHQaT$NM2(=&9ljsuWJNUoQUuN5Nduv^%mc(Ap&D_486yqlQS&vHw?nvmm?q35xR-5bG?3X-i8F zjSdimdw0C;u9Oz5kXOiZi?8m|AIo)a=E@G#@xseoxW2!2hcwBiSV1*7eeZ7fuyc{} z?0^mSFtGBY*-SwxR}h=-w&U?nX9qJ6cMAx|Vu*QpVN?UYWw3;lQH!Zlo44bnvWL1Q zBZHTDD#HPBG=2-olCBgBcwr~Pp60?CrtWBQyoN^<*Mqy?6roUZgC*mU+f-p^@F~=u zS%M_Ap>?Flrg4OTEAeI|F9MV=rZ-J;O=1PH$t#IC2w$+x#xPIzK5WZ3PQN;hx!RBI zSmmK`I2mIR;DS3LN7G%mSDKgE9%vY-*znj}F zG0*eJudDxFjL%LqX`x`%buz-$f;j%?# zVd%6ojF;D9af!~_dnqGiGDR<{P;22E-e zc^iO&D0i$OH*OszgJGS%AALD)MHUN-p5Z@hQqDF6%yoRR-cjBklQUm#fG8am`;C@u z1d<;bUQ8z)_lajBfaa2DTC@2WhturaELtjJswZ!3DE-+7GMXEHzIS)FT@wRUixz!G z_DmWAkzmfQViR)=Zzb-?x78M^#rCf0q0|hhb2sZrdI~hSEC$O518v zN-H$gOYx1WovQM*Z6p7db@jEN^P?%QdqqatVPJW5QdvreyI&o}i>WG=>OrGpa=TiW z*LA@-Kcf_0LZRC9Zj*S$*tgq-UL+%PZUyH;2b4||R*ht7f85m8cJjg*nhCv*t*X`< z&beS(6R|_BMjngujzAw`{+*7eM`m_yy#uyu?`Ev5ed!GFAK#AJk=#N$7l%TX;fUw# z2BdP^^oPy+qBvBV;uIx*A{Dm5=lqYCA>Ngf2W<yw_S<)?IV%{6?sDo8Hh%QK3A$w^9NrOOoQ@5&Bb6<9#%Q$wHelz|drGI{%Yv`P;LJ)m)@%Z}@2Frg+M`DDz5{7u{*#xJp(YcZVV{TwZt0 zgH4ivWw-F7{Lfb7pbQ%pnkx>c=}0m=CUx~4Q8`eax!>+n*Q2G#$QDeso#%1Cz8dqx z-@NaZ3>H=r;2HKq69w!gldaC?PtvKL#y@QzP6yVH@*W7g$*ydj!}lcCtoP?H%Lh1Y zoW5G|dwtSc>g`%;j8{{&&smT8tLyF>ic)Zj_dc0b%4BjUJDI`GHrO_&`n}p5HPK#F z%YFn>))(ccFyjh7?l!s!`KqpT=X&hsrV@LFm@Nl3f)g(D431mEuhUYQ)%2sMM{Xsl zmLxgs>=()Qp}D+yBW~;WpsD&bGLZ+3IHa@I@zRg_s_(ob-d&K!sisYh%b{D{jeS(H zk{T;jtF+K(3k_3mRybWm$1AmV*3m4^b&C+0qIkwHvRhq>6R01|-QeZim}B8#k)BS258<=V zayfgrd!I}CP5Uwl4c#H=rouMEpmEh zM(&nJLnIRRIG0yojFW%zW~0|^ivN&lvbRg>K%bk3=l5VYa(6vPQOzF)eLdOM{eyge z)IX&p{a)fq2!}k>(X5=ZacIDYdFHGxk z4C2&{Ltmr6vQO)wAg1SwEx{wH=4cDJn9cTiFRCxS&1Yk;LcGDjX)eFhb02XpEjPi7 z;kmuURc*UBe}WZ}H2+d;T!yJ?_+UNtA0-1R3lp>Y@Ya&!)phb}YXY zWHPPQ#SzlRWRgCR5qg}_>JPNO(}`+&*E`(pBD~vA5msqmo9=3=bomPW%E9-(-GDkF zbzL7C3cYJ89d9?6kIhB~8HW>fK7+Q|t-6vu{WKPn%o>#?_zE{T@hi z)+h2iI|cOj;9BDfwpJZueBp2OGqWqrBr*@FRp}CkbkCZm8|cSBuOL2j^P~;D(@kII za`+xj7Il2Dl95Bs8X`sF^gSufvO-S8jQl9cN%Y4NVeCb6)q0%dRkJUbeUdFN*ItqA zVO*_`tj}Sk`eYpQ;;JdF*_msr__o6lM@$Fb!M=jvBj(^o78N`Vy>R_iP+;BNtwKCW zuKsS0TFThgH5rThkT>=esGh<4K#DXLVm`4ZCf7a(F%IK4-7H3!ZB1s#>~ZETDXeK# z@m!G2q?}W^T1j%)bu&79KlQC%9=YOd_(rB_-CZ}kz9wC9Mcp+U@{D|iR?<F3i zOcp<+f+wP`P^uuV?DzcwqPmc)DVJ?7Yq>a#lvm8XHaS<#$MPruqnj7UrjT3^I$=Z2 zry2GurmdInBSZZYO(ku(*^=V`&@}(yVTx(oTl%CX@tuu%4TfbXP3FDO3SN!k_fLL2$7fyNA zSjuzfa|96lj>u1K2Prj*_3?7mwQDynnTQ8^TVKb zm{=K{0z;XQGNnqTYIjf2w4dEYMK5PVZkF{K1*MBrSvls+Dp#<~4=p{v+><`2ac7+M zlfu)1Oji6*Tc_TFy2Ihsa8Jrr#)R#9fk#C_fn)wV>7T~LeyAxLB9CpOS(_N4$Tc5* zR>_^%zUfdYe`toydf+TGRD~aH)l9|t8(|i4l?0vdY7t-Jpi*d*B0WoW`({xm^`1WN zD5f`Ah0n!Y8DW-Ai&tKHSs^o#UqJV|w6*;=`D^nS;b(Z{h}y=_DRm5qJS=Ofxyim@ z$}Rj==@sOMt4Wg7e(fEiNQ2L_84o*stVUo1xfRvY+H8_P?9w+;OV)|Sl7)eN>_m%_ zckR3u&8;y_6GZ&n9VKUY#_sWzYK2CPR=z)X&!jz{23St`1GYBVl2GOv^3!`dDJJ$F zM47rfUH4Ei@hRz@!c7(LgRQXd{}#=}h~$F!l~$A&qf8G-6NXq8ktPoQn3B@mAtxh9 zxF@?g?~nwE-mNTktcVKGC&?X4=pj55;AUpjm5P=kYv(T}6+LYWrF@J)N@IuVwc17F*zxefOZr%y1Xcn;+*+8b^ukhva2BQf;@CXYP_FW_zo`M6rac1+m6l+RJH{r(m#B@k;`BR3$W^U>Vsg6dAa%Ao*UFjCH$7I+mYrmK(hxg>|O7B^S#gKiej^@2-qqS-Ro^NeaKoLT;{zIb!3sTb0E z^yd?Q2IA$4K0%#M8l75mb;;kmeJhE9OSw){^wLg)q5by@Wx+o_$dNmSQ~mVty5XnY z)ApZfG;wN^sPc62lWLE0ZcN!%*JPC*w4_m8plLCc*wd|#ZJjV4gJOgfOR#^#X257Q z;gGzCz_>dNuq%IFi!zpgyE$s!#W#^F#^PSG>rnEVfUt-%^JotutFhMgmt)^uXG873 z*9Tvs`qlduJ^%Q#2UAk0ryn7Y(R6LVFrjD<9Bx;SpUF}eyCX>@LB$URQ@~(5Z*gYg zKhSfz?p0?&nQ@gX4bM&%Ecn=7K!Vs#4+2zPoJ_2TKT1c#F&PuYXFKOvhBp*p5}}tB z4^FmrL>NILZ?EN0e3VzkeI`n6hK=Wt^6gHro^54^wdTdg>=>2m9@-%jY-8^Sr`jHj zFPk!tagf)6ak=f63yBZ=?o0AKcbA_T_alsXbaC4iYKh%W$A9nN;wqhz8cmxO%0%M@ zP7uWJ)i__@Bo!QTehi$tKvb!9s=S@ZFL~-JybPWYRMMV7E5Z`b$v>Ce%Q?o`B8pjP zcou>D`csR;IWa#$Z)dK+K*Kr`u3qYDBBOzBOlfnT;&s)SLR(>&HEjtlP7m}eZ8p^B5fG>+xJ)LztZ* zUFu#^hw!u3UKAUi~aXn@Y3rmL9tO45P&}p4O)cy3X=&cC@retHx)lElvGaF-Lp7B1k4pYR`Xe zjN3bcakNlaT1)DESJuWlpm#(p@Fl^05}!i1Q{kI-V|CgZh8Lo8W&Fl_MWU$y<8JD( zstC7h_{MxIhfdayK0WkB9b-ofXNelFq=({GBt403^Ln(mo$w|ie7~y8w+A!LPCGq% zGqF9{2(-B!^cMvp9p~teWjyQ0c=4hhwsNr;E{Yji2HHS~*J)mef~Y(V!krpj@b!hb z+EMuNoX(1C)A2(UXcO`p9TgR9OH^O7X{A$EpRYiS{G1)nek7*ff00j8jXq4pU%5Fp z{je?8b(~IJJgKTvP?1coO;qF1xHj_L{h_lAYKW*Jt209|YQGiXS_aE!T35sE?57#P zG~L^6(REK3?NpF_@#6yPX_uPi_LFr z@}8R+B+xKIr&bx!*0ah}tos$m@X;kt&vEnRIg)Ow_<-WAZgMfc4NUpixZF)mUS^cd7y`xEQkhVd`0 zY&%cr!swf`G&lb`OvPo6E`GhrS0GJ~?*qH2LBcLSFf(YtV;L~ibR0<*H(C`}*5Yva z!Aa^@-7VSF9wzT6J)BYwvFJ!2dyk)-Np2!UA}xpECj8ejv=fT zB39#1v`Nyg$F;p?ia&Qo;$U@{_kM_{xh`iovmK%!aGIU4Aq&nE7+m_a z&cil+Tva#9G{7xenM%|-TaL#)i2sM%$#T26EqaUhsGkN)s`;#&LCblvcgJiHSHsfa zhTY-iKxuxfd1v)*YjkFSyg!bW9P&If7 zZr*%OrAsC0XE}?f>FftX0x-U}1MCAl@N&^vt*+0G-jzkCY7gmossP*#9- zRZ-b-wA;@7YREfJb0>)b$@PVTMpmLCcNl*8H<2}Oez=ovUyLokUC`E-SRbJ!o2#=T zb{70}_Bq@+W0AaAUza(Yapzb~jsf?fc;;w>0a=5Qm_$%Dor{EYIP4G%Yvo0s#yr^M zM}z`s+1eX7pnLU)FRc1rErPwTr92&K37Ne zWBZO+B}+0mIf8Xr%@lE+_|d~ZDv1VJ7AEAZ+dA&$80O~)uq*DR@0o4XBml1TD5FE!(;6m{#KMNCR|^e5*EYz zU%eX={8`KqZ3L`N8|vz`O}TE5oq|iBCP*NrQ6Ss4)~-@BPNF|i1N9Q{okq84aYN2G zLGv%&AH>a3)kwSD)CZ&=ZHIaSIJB~LjC>ahY7U=teuXx{+6odqvWoQp03}}Cgn|4R z*)1&0_HNm~9>$OS;$X$0jRtY35AlP+orj76JgKRto@Q`}Ls{fon98O<;W- z;FtkWuHQ>nq|#6L{86@9An(;Aqf@UP@x^i!SXxo#N}-G|h;jAeH^2?1byy$P<0WL-5@&3)a#d||R#5KcIlFVRNk^#1|*&RV=8}iTB^8-KKRLdfvJ~ zBeocg2gvy>4_zzdknQV|tz&D*aO?IQ&snsMg>{MTGHbEG-_f%eq2j0pqhTX?f(f-?BxLfho^}b(T0L-!IqkK z;R_~1>B(HAbWajpiI!Q#5q1}q&7Q`>(6MxmGvKEyy-Kr4)1HIR9}YGv|BOM6&sImi z(lu0uXF`~3HE)1#{%uWmi>rs0x7pxd1pvUQN>#a<=4vKC^Qk=%V4a?yj0eRV_ zO0TKE3#W~B^LDQ)KCi@Pv8IwQj3<0#!3bFb3qhU41MaR?7Kjp9F=%u>aO0H#^k~;l zV^1?O`){BBQ>C{9OfH-&OZrn1bfx#Nw^K?y<4HF@$5^>{dt65a(vziI>U4NsbC6je z0qf$HzaTL`T$2?~{$6VYrIGYQ;H=8Li&JtRho|vM?Kqm?)^r7$7uo+K9GK+&@=wD8 z0JLKfMYV2Gt>`nPR9ABdw8<*4-%_3^ys+EZxVIKNgyjHsODotC4TbJtlG7l}{Qds1 z+A41F*lMTO#y00aY>R&dBWes)>imiB0w?WqahmNh80;sKUd4(0s&j=>;U&q5^6FLSVVcLyC?Ahx z`%H2gW^Ha1NB0tJisD8z+8UX4OERY}>MiF68>SelMnlEP5z8}lqgcbNUV-j7*b0-` zs(VU-XA)I_0ug_Tj3@SHImCN%Rfp|H=zl4P|9} z?b&iWGa2Imz##lL0Oete8~GG&$p-(_1OW5PLj5eQDm%t$fsULJ_(P&){>`#*(q$}xyC(Sc<->$#POFfL!bM-qn&`&Tn<<@~Rs#UfuWRxo};P+~iI59OGFFk2xl866B4?djYTm z?gbC4YDbT1!%28YoFh4_NqOlw#*qGYl*XeDQRPg2K?>i@i=7oeo&buLGtj{9(e`2| zVJ*mWDrJ1-H2MNFJbQs)ZmG&#%|k%}`6+n=JmvTYiaD%hZfGi{v6b`Mo4dRyE5nv3 z-=`jP1iZjqoojMDfl>_?=xk|umfHRS?R??YxPL``WVrkA?0xILl!m(vwtZH;3Zh_{ zZD_c-J*ZI5Jk+m^lXKY_CzHigAfD*94?VuS=@>EAe$rV^H%ISoQ4vkTt&~})^rX#| zdot#lC?@E^RA3E{OMCM~$H@$V^Fj5HiP61^saHURMiNfOK2dGAICrhsx7G7++z(Mn z3W^-^vjdOsON&>YJf59I^B3oqW5tsS46`I=$fHWFU z{al$~&;eIuWyAHdTGRGV+)9p!sI7^yGf@fRl>J*JAxwE9U$62pg6x!A`>f*J zy)D1i8v@LxI+Bm+px>1ml4XO^7?=ta{Ctn+_g#XuzZTbc_2to^+fF7AJ08|7GJ>e& zA{vCGNesy(<@Ln%Ql9h}8K>?Q7gj2(!_y{;2=vwY?4m@AkPA6ze*#~=U{DIsx;(5_ zrK-mzC9O?qdM;dDEgewu0!nq+R$7=J(8+ob!J|u3bM-lH`Z1j+V^@pi2VPVa1(o?+ z8CuFrLL`QtG5zd`l6WtdmZq(Ym*npK%v?ZEhh*<)SsH%pA_f4kYmB5ID|5@+riRLj z2{O_C#Ko>+HG~l&gKUjw(Om;nVQw8Ds@g#gZ!|6GHsT zgrRj26@K&=K~nVc3D>-T*E{3`XzB~*7mJWy>hvJkB$kRTzNusi+{d+{IT)KxS9Ink4}6#_A(61Sou}9EpJlxYX4=!~?G zhzQYu&+%_q0k!iOm{lntQ3>4g$Hf6V)h1;7PyT@c&^^f%Fhe&cp2Q`8$7t8RKEmr4 zZUCmby~UkZlT=U!>42wSroyM|S0KAefHIH7;B5{R1pxT8L$w~hZg~m{GH65CUaE{Y z6@1;_sETTL9}OZ%gkZ_gKZgWB3FsF^y=hL4vP@BrJQ1dzo+!jqV@z!t!vKJH9o^sF z${clRGPM+$nWXEf?hdj9|_!9Q5~|3%b) z0Tmt&;a^0B`3F&d|K}#a3_!rS^BU9X)HuI99I$yGV*XjN8WmJ#f?fmQgxo%R=PMLk z{Cx!&%rmh_*#bz7bq1Mj#6qQwFjucxCzsB)HeTF^!PHX1EF}RmYaei$zD(y-B43Wk ze-V^eVsrJ&RoM0MLuMw2maQa1VNX>dGb=s3J2)@SpBvl#oST2mePj9TFSuO|&k+*| zO`SZ)w2%y`rxX;!VA1B+8%N`~yS2JxJ=mKCU-RR8*4%k?JnMA1eff&J$)>WLvBwmM z9CCAhCEpsypO8xYWB=%??)u?@!{+ku3J1v)tI=GTl1($;#V>rv{v{b6aZxkR?&8&?i)U9n9zLZL zMLN#M=Fo-UNV~(KC^YTvo9@(|14w<^oj=d!44f22#J=;@OR-%w{QOGqJO zT*L$dR1ie*NULR<{Ssl(oWCT^+|H zYA#&cgAq8QwDQMXVgi@5({naYp07qjIRf2i9M$TR>mwsuYaw8_>}*hLjcLxRRmG)e zX|8EBgQsYQX-Kary2)I(*(vVv+3wPY6sv7sEv#L@pRdJ1bQ@t(AdqtLyyWF}XsQ&fR^)wL@h(u&kgCP9l@)R=yQxy` zzWM0gu2a#Idgg2oh6)%#m--e-!Bh3OSVCUy(qdr-QtWnIdpXC|EizSc+WGIN1Dtx6 zwOE{a4!g65E-ugGvbEK}#{hs40AOncC0q;U^lP*DJ?-WF0I;FHVUi1BNxkJ&!0Ze1 z|AQZI40B(!z22-50>AL95re;XyD!nw5*wZ`VWcUU_$Ib>u;$JA-K1$~lsF8l z3DEp|sclSKvvJ(ni+7@?kTh(8#t#hu2JPqHht$t+*JhYAFT@O(lT|dHKcR!q}X|h0LYtU;l5BGc?jQVx0^ZiJ=Q_XsM)+hW_ z?A46(;brXgeCt976Z?n@UYlU4x{Vf@;;|3QC*gJLPT>lfe$h!%wU`8VwzP()>0l|U zi0+hm<(b5GcR>g3zSR$h4q;^y*$YkxO}EIw1#2@KJQMD;u+Mc{?AdU6?(_& zEg}%y=mf%>GRJxh(v$XaT=E*wefeuK;BKppWs&YBIR3Yf{5$S*2bn@l--(GJmN4}n0j0DK;pG8zHf{STJCrX;WK-9mZ413fm zZ|*m3NHgu=;vV{rUkqS5@le9oPIrw4jNz}SfsyYv9~9)`h?xeNaMW_2QfYIWQ#jk) zm9TSu4beqR^c*-=r($FwoaHGNJ@eq2{Ot<5WK+j7{;?kDX|>m#RML^czFk^r)#y<^ za6cg|5j>9n0_l9ebC_phkSIUCE{He*lgFDNcq=4Lylh!hPaAcc%Hh74WS&NP zk}Z2_xJ#yH)O1tqo$Tt{1%8#9Y^?JBb+P|&dbdzP@cz%ErE3=@cZH>Q*;>%52zKX_ zq9B+Z%+W&vC(+5o@+ZCeoP&F0Il){vL^2%=1DNN)fawD?L!uT$dj^VCsz?T&7p=v zx>nnf8`*vAKfiVJ3Lw4o*@r82?ikOHKg*|xWG`P<`ae@F#?AAQ-i5pZbx%Gl-m2(V zTG8ZFre9TiA7ssDHd=@K&685kdHLx$@q?IbOzb7f-yi4F#eMGX;|Fz_*q$_TaL{aW z%*Xvj-*4z%9jNw2Op*`&p%$;e0px{H>lOIGo~I>-I=idYyQD`_ ze8dLslV3XtplmP@T~2$PD9kooJQ<^1!HldDFqVV}9^A9peWt|;qNpK85xm9fBGh|1 zhJ;$GaB1(7>qq(@`)gZ)zsuXp#syX^XE9&Ae0mO9{nTg(^ZT1xJz>f3gr$bz_zXh2 z>P!h4ay+|MhM55lj>hP>YW&y3WL?I5F751sTH#lAAGfODcD=3DdJ+K<#;VL~FKr9t902i=H$Y9dw$5|_himTH~d*^aNcw|wiG1raFk z#(9=}#$j~k8u&8{Uje#toYW+ddGgj~8afX2aZEbMjwNPmB3YV?Lb{-|EJdG)!e!wX0NEvn%l@EDhf&R6;KG& z4bwd8;>wz1?vE>UleQnMMr%0RY_6hxE>MqSeWI~J?@HVh`O3B?hI z9pD>m7rol2RtEj4pJyIi(Boe!5O{;Sm7Ek}bD!SJC|9#b-iJ4f$S9_!JB!A-RJ+7Y zAK)k6Sc?c*`v_vb0t#yg>FKO$pa0heS`>&Q3-Yr`XYWn5)eObZ^H8<|{Z!tY|3ke6$&B7$fT$}(M5%FwMl{kir|AxF`%|$*m zwRh3PI8Vi-D~j(_DZ#d0^!FL z*Z4O6f+CcDPXEUGT&1d&OZMUsQ6Vd=1=xTJFX$#6?NG))Za#wlKn;FFek&1bMo~Iq zrhl;hDk}1%FGxqIev3QLy#Y;WQ2t$L3o0107T7v$;}Bg+3jPKS4n~~< z1t||~^L#$5OU~iQQ^955MmiAARM3gf{qyg5E8Hrz%zDUtj|Gya^AU`A1&+JbSW_u( zzNq0u1fofM*I@-0sU9C6D)V6N2_A1?X6ohhif^c?P2dwqp*bMIy#J*g$}TTS+z?Ih z@!qt`6p_;}totR&R^piGBP!kRq66A#wBq-|1Q&5|Pts2OlY=V9@HOxPDBv&gVK>Th zv618YBzLTTYPnz$y`3^d+cJMQrq0&vK!b|_qM z3Uv%fc1$WE+`)a{mx++aZ&ItqUYr|P(9W!eI+Z|Shir*_Pn~6NKaj4Ul`eH1{WX44b@p~-O#AVNH%{= zR(|+Rf>M9<^f2`st(OXYNYrDk74dgMuks^=x1~b!T#VLZKil8tkhSiBQiGUZmevkB z-9vnCvQb4a=BWa4tOD}mIETKQ5{Z~zSf{I~XIljKg>2Fc3o2eMDXv|G(fmTcsD9~c z?9JTcWorsxKtgPxD7>(SZ30V7D*~e;@c$ z$!an#!mq}`(eVFpB)Lo{2BmGjUm0EHlHR)H*8(J*R!6RW$ zY;&iT{KM@E74h~BnPF57$Kw;}WjsFtnGykjjlm3ykBC?{h`lybnUBF@_0=KTp76+$PSL|_*nt@myh zjk5<>FvHM=IkD5C={}FHzh_?O)_S7mKa#aC_M(Sx4GiT;LbJ5!aC5bBXTJq^c)Gib z!_$Wkz}xCh+TUKnZ1SSPyFF78;jdJn;_?P-H`tvP7_~KIa|I|UargD$np#E>)p^lV%K~H z3RL`e(-FB;t($OgdZ}*8;aT~ycteS2j5ZIj`7*$YYRs!^(_U%$`0F#ns{uRYHlbt; zR>2CjwC*h9AU4@$j+`=T%}Mp8`ogYt$|r~=AwC?w!z&Qq&=(TTpE`NxBUDp^M|%9o zNSLd8az^G?l~`~2Ron?PeAr8XwB}qekGDu2=AGRWoWV62RPy>tIlx#)&h^QM{uDiM z6lxeVLkq$v14}&*hw$BTcH0Hh=(om<5n4)|^3Yt7!yiTN3qjOhp@AWI=+AJXdp`kf zrkdIfCE+1xn|yfZum`Kdd#@a{eU-+Q7w)QKmk}twY%2om>TYU(Fj#ck+}jCJOwUxuloT%~ z@7qGLu-bQORAc!R&dg#-Dv)3@HhBq60OAd@?cQn9YW;e=guz+Q(BciU6$W;*T$K}RzV;-C0_%m=UHFHEJ&XvP}TLjKd-%7bgz-tGr< z?WC>U%p18AUtZBT{JY{j{r)~zVLG<7fb)#3&#Cdp1sI({IA$tR#piYt*uCqvEh0iS zx4({XCUzDt9Oku0$rH|3?MNx?r*~9zh{lLNOj77~Z`A#Ehi#}O1f@8<0exQg2i=N0lE>pWG9QEw z2&58~Hm@2?4~Lg}%A5a4o_&OpI1OGSE=@R8 zLUYLsiN&#z-;PtKu6uhd)cnSZC}M$v+3>BJ1UAa=3$&V5uv-{4G9& z4vUi5nd&%ddGBLG$tGnPx<6~PNi5OZ5JGqfQFY=JJQ%D^NEXME!*SaaXH6b;MJ!$M zPJ_51uU@gu)p8l+PC*&kYPy3} zK~d8@(Iigjf~N3JIYQR;=pzmYztc4ig#{9Ws(dxH`1Dyk(Pd$1o?4F5-YFjq)Z`E+ zgoc`==YYTOtU~;VEOetuJ|H0>0Y1!6@SzG-{fs9*Yy(F1(v}Bv>Pgc9m>pDphhzZ4 zor1FlzfQ(`IkA-3g&Ua!Z66;Q=|?Ca?r%!>{Q~~Te z5t#L{S>b26%PiTAZu*xI61Z~2q|}Glk5Zq#SPJdDC?yV5a5S~tX>8FyjX#jBkml@t zP_>KGGWca4p=q~-!EU^Pz)2fT!hFtlbAL#zD1Y4vb4cYoyb$*-cRtVJm&!BQ@KRu_ z%o-I10oTW`?X*FWyVm@3*VdiKdfB$J6 z!DVKtC(RFsLaLc~v%h1-g&}BaA?QyYiuU90#6HtoiarO%JtXC0SEJr@-oGns|FFTf zP08D4?Ii4pVl{gy%Bb;+2}ykcJt0BQorR-F+wu-k<8&}U`Db*oO7yKSr*A=ZP&O_R zRwlm~$NysND}dtYnnj5K2~Kc_5L|*gB!S?t_~O2}yIXK~4H8@ycXwUfb#d3lojkt( z{&nlht5hv6$J|jJ+=d=xw#R-oRP(`3oc^}*8iq_Lv>S>r0u**rEbDtabS@#HF z>>i@FipYz1&Cm?jsBcMxyS5Q|4?r|l68w-=O<7_OiaWPu5BUq1UR9Ni6Ne^>D%PT# zYLmp*lR0vyudmPA0U+vmM#cpA48wO3=x zRW4D6wYbvBe4jA?MOD3j;g%q^`@B@Ec8iGCHei)+9Pfoz`7GFUlmQ2IC|d=8;hyuH zC6KJDS*rLY0}!!s(`>tg#R}N%@$~?9ZW=uwef2mIjCdPWM7C1Px&Fe{$6FAY+*@|u zSS3ewAGN=jW!VXc8Wq+ok*v&Ew8*NA+FL{b7MfJ)cY)9$Wc1hZ+PNJ2=7i_&_r5X0 z7Sz!8aP6m!Y$XA5N;Q_&M8L8XRlwWd`^T^+8<{fpTsHJ(w`-%fz)0~iwXaEi#;B6( z8blx_#X4-^W1z^d<%97c0XXq`0dA(ywn?(?aQ@&<0l33ZB1#LNuAvU+tjlf-hrqAu zod9QJQzevE02Lh4qh_WOE10~;5DVYrFcrTek7rM5AuB^8u9mgrae5IEf0SDN? z>nE{y3wB4H{d8(Q!e@ROL-U!8-vAi$orbDQsZ}i6pl^EjrnK|BnH5jF&U1V-lB=@| zZ9aGip`f5v)uK5nR7VsLQDM(B^vb9 zgx#Hd$ww3NG&b<~7c}^e6mbT=cAGSLZ;iJujRhXD=i`0~{1!9T5L%hbGF2AZ4RJ3i&-vutngcEZ?4Qjs9?-YtAX1ihQ z8R`|WLzP36Hv{*U+4&b-0a2Uc;#Du~s&%S|pFdH%n^rZbs05;AVg%b(*OId^ z7hGHL2le;UPD48&@iWSkeC#tQF?hHGC_RPWA6iD1wB%V5X$o_GagT=lW@2>z3FG_R ze66WqjZM|-OBA<0A)bgH*0U*~Rls$sui>1*v=|l@sg~Al(_uLw162httuKv`OFT<( z4*{8%swUy_skwc{R_}i_(X}V2icXWlr;6qUHZT}Zp?5_vI>|dSLsFus)^Z92MbRbg z0D`kW4&`pqk-bxtgiX7M3Q)I_b+ z1l#|asC3Ml*G=)@x6ob_DL5(|Dz)-T*zbM91!msVv!OB?cd>r7T_@BYCC@~BT6EJN zbl7&ZbhJN9BB+-fJe_(QZSeZwQLlMi$((WK4G_VYHIP%&I3S3N1h^uIt#S12Na(~u zVUrh1dc%qKS*iZvW}=)+Vpag)SXTPyTJ}rPkl!4%-_1;Y<5ufYDL!@i8g^5{I|;p6kcWJ@R*O$kiE0T=RIZ~i1!sB%OnV1Iio7M@1TH&~Ur!9qE67W0ZBn2qD> z3wkw)xYlzrX!lyyrSQWIJq=@N>rF5C6J?vg_O;6KY2aj1E|iBU++azHQB&&mCW}w5 z=g#uzX9J1y&25!xX4b2sNeEun^9TrJX|ExOZ}e_8{0;(J;1$}2FLtVE;j8LDYPa@K zJ8TrUJgQ@5g><68UswT$Gu_2eYWOjlA_E^yN_!~R2DY;zi1<8D!grDc71AW4hQ53D zurBcka49)c*OhtMyP zn>Z<0ZuQ1mevzh1?@=UO5^&99Y}o>`Gq*v&dw+T)`5Fd-B}y{fpscX_bI|OCks%4n z(~TM#rCa6}wLb<_k04)fKQFQPl$o}{0(5)Ysnsk~1XY=%j%0Je7pTq5Ydjk0zWIPEokA2^Xz+K~sDK(c5 zALqQIeY2^c*ii2I`8MJ`P=L?Sd?TI&b{HO4H9L&F{4q5srxCApmqlx zm1!9y>#}G#yY9{iJ`z+Bxy1c#S10|MF#Y@oSmr?XA-L$bYU#LDo8*mVe>5-<+a6(v z(sdxe7&^p!K#S}Pldbk;8q z9#p&mX1Ly4vK}59q32SX*dXi9xg~2udyK)`6C(z)#NL$`GnA*M`f2xJ z>Izh#sxfx<_`>uqyyORA9UB8wpl&@|y1kT-ugdcrQ#H*aQrIvp^PUzBB6()z0G z>MY6LMAV5*r5@03j6z~QWBY(En|h1H=5~12shRvs{=#wNB&^%jNuC<2I!*HOvM=S{ zQY`o8NHNjS^}vfB5d6T#O32(Eh+V93SyiuRN9H|RUp*-6jz7k@;-B-z?g#QK-LXkQyQ(8{UQ)4wEJDto66<@GMs2jW=P%R1sJ> zTj4|E-{VjfZ4l}8PNx?+Zk|+2V!e0mheh$zXHOtCnr?|~OT2fte0M5u zxZ`Tb;7!!RvU=1^1@Y3x{TYd5?CziRoktr>aJZ#X;viu1M&>Hh@7YtlQ&0joBKud` z6m};kW-{}Lto5EgT83(4fwLkp>w=9kqO#GFyj$WuQ#kFMwXm(3jiljY$oL|QA5E}!4qyrHe_U=m1(y8U3o*4SR;&^D;)|Gh`uTA3T+k=9@)pLiNG9QbPpwLJ|{mpsii?b4_^ZJKIYb=+@PR>y!>Ne-tRMUkDdiQC}1vFk5 z$4wVomXe@m-*^Y>LIK}c4v+47VtUg9?Vw2fosf*X$4>09k_T!q8_T?ebA#9v!<1jV z#Kk5bV1(a&#&m$i@%nj^>38!VLjDF*T8~(MovOH+VDc-=t(;3LF1a`(mk?4E9$sO4 z%$@i~GxWn-cWl;mnhrY@vp114H-Vs&z=LdTCE#|qLn6CotpK0p+nMP@-R;!nbwP~x z={bBj3xC|Hv)*Pc6hSV8UL-7U+y0{;Mk1}`3Ds7WN3=~$c7+ZB1>3Zvn||R**)G9sYY<$l z%xk==ZNb=#7(hIC)4`Q#RcWGcc*( zLJsA-_QP%EjU|uc_8P3$;5D@&BWaO^b7-QR(0 z7oBQC_#$ttR@l;3|1dE36Q&0{-74I`y3_y+cE{-Xsb61|ump5)q3wv(Cbgb#e2~8C zeYdv#7}@zDB0)1@x*^B)@y^-)-D$Y@p)Jc>*00R|w!48?B0;#>a*m)nv<+Ei!{P5V z9b^}~wT{?p6IJG@+a*u@cKd&(%Ep6fIZO$n#JUF;FBZi{qS?@g@x8{TQtbM0)90iX zp^1#1WlKhZr#u#P123{s)E-*LXrb9U&nM#uE)3>NDaVeAD8Fi<^M4GX998{X;nfZ! z!HzG7JYy58m^2K2dpXlj(_I#F{K)azm}az&S_+a4sa2{Q_eNSSIBp>2W$bh0ch0sd zw4D(WneP2K<)suV#i*ONkXLnT5Gx4n%#Z|nmF=vY=BYmT^aC9yg^^2Q1vWMRZ*7yVHQxqNbkHlb^*#|CC5W9CzAKr&2}c1IMzyfrf7$Wg*74w?4Lv-L z&=UY*PbYy99TsM9X`PPN&+N-klBgEObLq@}?-CbT5DfboS&ca;;M+%B!N#z%o}6S% zpfcV=C--K7+?_Oqj<8cK!Q5`4ijM=#3O{cjWfbAnc5!JnU!-5;9}|QQM!x@r3w?vR z?#^8rOa&KaD6;*dg{5u7+_9e2n7YM*#S^St7Q~XOKHlZXGJtWl#AxLv9kTQ;5qWkE z+m3@cd(k}USXg6W+1b{-41B!D9Rd(q9 zg&=oF-lK#m5kJesLPw94SDr_a-3Hj^FMmv~!?TWwebpv^_pDk!y;osmtCh1qI%Ou% zP@VI@DL6?TT3ye9IsAnSsxzd*@{KNr=vrg!ZwtJ2cM11!T_@k;Zuk-+Nxm=8do;43 zV-CT!{L0F=o9Zyqw5v7E?3Vl`JnrWKNLUh2U^o+Gln5oB<93jrHcet@S1#pLcuOdh z3bn{ulA4>3>d8WrCe_9_+zO484HnHc3yfq?dRxl49!A|>L+peiqHr2j##c`sdC?;C z=UvED^ANj|LFlN1a-3kpPvjt>L(Nas!-mGUOu(5*))s_phmy;Bd#oXc)jj$w`HIUf zCQ)!Dz0QuZ5AiYeLT6_f9v?gXAo-Sy5l)U54eSzS&e$`#{kIg4xHYyieB={PQcb;D z$wnpicUL*{3Z0g_7j+W`JD?PC0cK_^Jlz^7$sqNR^1}&SZhV0Ub_CBS};DO|zVnX}Z0Ma~u z*OqGgv-LI8XNm`!itg1D8KEG$@xO4(=8s={v$RX_9%=xB4`&Q)g6fVVxlcp+y9LJsmFk6+vXVgrD*=mu zqlA~k)~}hyRY{KXmtsWL#w2y%%m_6NQELk^ac^8Gc?*BV^Skwp%y3!L`+T*)n%-UB z(j;(zVztbM9=1;&i;DzuAcY{|Wd}P)# z9&UT(z2#}%@jH#N%(Aj$u9NW~VUrfhzi^=G8&ax91}3JZy*D>gh~?tosC$!6LsQX> zTzb_@DVEdNrth7?$vF`=NDhbZx?Xm)TQW;s%>TmS;7JPqo;fZ*m4&y0LHf7F+`rxd z_A_$@1rYrwc*KikZlb4b_Q?=zVwXS1@vc8?b-I}AU$FvwUbb*mHOr>^b3|Q0}8%f}WN;0}Fj|gq7-?g_|^T*%(E`4hGDWxS-HR849NGxLA4d6N!iRo(=?S}lqf zcD@`yF;0UI(AILJf^nRt3l)vr-ATbFs&VdoL37pWTLvWEHg#d+58GxJT(nTe*rxa) z<_@28P`bZVtgzP`pE>&Yj)q9{Ehjppsl1mt7AN3l-rW?MS%21v{ghCCf)d|h1q?i| z9#eR$v3MVweWK~b9}5csC8C;9`Q*u4)MPP`8A}uV0&olF4c2-vURe2=H-my7!(e-Y z$^$cRpFaDz&MH=xEaG#9vJz2hID{LABQ`RaxgC~D_v19!XB@;8j3#{fAZSw51FkK# z>eo2UP(F585Yf~X93~)AE!H6b3yJHcJWp~It`AT! z1PgkK1DhOC-=B;Z1K%R`7H7m^84Aa4%t*aPfOJFT>*p|x~uQsK>ycSeF%!}6!Um)DOT3j6C)HX5+Ua*I`Fc{v6f zwRP0Nf@mAu3$qr_)>O8V#pW1h$eL4}7H1%w4oou_YOv(_yt9Fs`hC9q5sGU#^+dA@CsOx#<+*lKkRF~ zUT^1RlXT1;+}<^Yrf>D5YCg6rq0h&hu)}n@L+qxP6zw@CTCxOBt0`km2#Fv{|^HV z8!bN_nvp^}n<$xmn7N4<_T{WsD{<~kG;#S+bvMl$kFZ4XwVspptGR6>e&}RdJIYHF z!F&4pE3-pUX{BGgez|9vQ3q6U>N_HhGD1lVK1cEl?jY_jpgr|6Dul|uvcpO3pa3Vm z{Z6d5Bk6BTVinh5w#kfVFfjK~y(;%*viN0yiE`~*pwG40U6544}YoVEoB=VTrO0y zLb3xQEArM}TT4Kt+66jNEvP)M77>9S37yQY z`egJ}5A8Qo{DousCI0B?u!){Wojn=4N5ujPe3jT^dr{hnfags!b50dxe_p+jJe3t9 z85F6!YoQ^5;_2nlWjrC;AUXkrP;R0p$q4Sglf4rxQ|*|uxbyyU)3bqC$G=*ewZy9Z zTskcA3|6^(&Ks6?UecH5BHSsEBSw!I;wsi)uZvlFd$gyZ4m#m?%v#k1oYpx!55a`i z2&0%{d$}yhR2+^BTv2(s5dhnJL51i#${C5$fbwIywV9;-+3}R9Ci z@n+7enL^uzdqTV;MXa!NgWw688Y9D-XVF3FTz4=YE0aQUr>hQrU$x-9V&_WnI*HV^ z^;JzaRo`Kv?8%O6G#UOe7Ju=pg$B~eVgCM9bQ4yHviEw{RaiNj($=6tR8hiKGWCrj z{pjF$=pC^OF+v1Q;%5_IQIi~(wXV#`2MX0`&t75(A#)n<7cH#x&Gt+aeY;;30@d9l z;#RTh-a2O-^x=+B*^Q_>aeuE!)xU7FM-`R2Aus9T-B<^54}NB3b>b88Br1qpm$`%5?2u!K2 z6rcLTu976Ov}Grz=%2L|zoE==lt&-qRsfrck<9%(Qn6)`0cD(dLkvk3L&%!?X*&5N z^-06+2;xai;|Uw3R|%LTxa@1;B$`FTT~fUTH6e84Lo$_`fx8rx^sSH4n&?ngB{4(i zn`^;-xC7jriFaDVOj|2LpI5BpBbsFFwVN(Z|{|g5!4c6Ee zg5*mqx=6ZV7_*4Z0J|H)I>K*9|sS*T05zwN=xMNK%cv z+oz+on49An$j+#Ll{9g_g=2_g$#W)Xm8FX{v%F`=sZi~s5jbFGtX9h@U$GV~k@yQY zR7&ft3AF9N{B**s9YZW=E!tV|C}oj8*0tCC6*{riY^p}^1Ih{u=s$H(dWL+p2y<~l z<}>2Y2I?Mk+c?o3=HAjha?txvCTvT6!fj!&bx~fSx%pUV>HGBM4_4HSpb`HRR&#>D zTL$=Vh5mxMou!Q4LGzTYOn>19d7r5tHR{N&r3Rgm&(S9%aqueX1UF3A>X5AM7*_TU zi7_E_Jg>oh8gFvEhndZXXdQp_#fWh6+^2rgrM zy^km!oVv5`wDr$@te#;6G-0I%J>|}t{c-b*?xmUnu15omhlb?&ADv^mzt|{tXJMW$ zK%?$Io@doo?&McA$xOBM*G%VZ9|W$|BOUqz&`8vv_lLSLeefG$1X$i}f)^|GH7#mc zcfxvcM#Ad;H9ZAX_S972L2aTPBI38chA3c3*-e>*9=ci+fH0AHyf@(5tsiYI=ZSsG z!;i=$Zf;`}P0B03(I+L1M35LL^qH@GDDFAh_YTqDt4t&7AyHW{+nf9WMO5@y?iN1R zdw~hC2*zE%*mEC*m~O*WFkZA)bq?8Ry#;g8S#&ssED{Bc3Uyvgp0>ax37#rtNqhRB znT$(@ez%`l9HL#O42ZUr4Ps#pNm*;zu)VRDCUagO;-(%-giIzpQgq?%l|U1u80^My zETSsN>3b~rB=VRGe(-}BBUcow>o@k}xH;A*UiFND78Y&PnZJ>>DKnjaLLzBMF9!0&XgF9j%+4LZ}(zU-q;-MyQX@?>i%E8-M6xzkb-<0~dQKdifU)K~a^0 z!F|;~{#ES7J?%}uh|AE28M@sTp(sw=1Ii79Hx?Vb5KDi)HvI0yWLG-93_;oH@^utP zMRrxwWXmg@_ms`5K)LZ-q(mq6ux%UuWz;Xm_TBu-Yd9AIB{NEa412Hge8(P_#^P&| zg|bieQ7rXd&UZR>3V-tL8I(1~v-yZ*qf%w5De(E54c5A#sMN9Jcf*oL>;<1p^2T3` z$kq6^Oaju8Z~UU)P17g8 zi#@N>#mO;M6SC4!b`)z*)6twuLq?7P!9NvgK`euc)o2Z34(`{6-VE*|kFm(}62Kgm zR?jMh#xnLAI0L5nAFVUWd$RD8zG?ampj5L-tlsd?G)JvO2^&Nd4U?@oektUQ+K*5m z^R2mCQt~6&x}~|cf5ARG3Xlt+{Q9A3FiU-yWYPt2oCW1zWMqU77z^&rY33kjpXs<0 zU-EZ{k49nUpwz@8wCx)3Z}AS0aV#{g5b^kqQ@dMPj)5q7RJRVEz<(&bCPlpo5F)EK zsRJuNUxUU?HO@}D>A#u#*w#ibGbxL?7aiF+|BQ399h_0>yyv2BvD8boREO@fsd)g7 zRJ33cwoE**iYQmf8R@3_0*|Fq7a=~xvoAVv{$eU=HBIA`vqWLjr36NnqBZR1RTd*7 z6j-u1XosF~b|*(;n4hS{88hc*eeQ*5SUDqHoJFwY@+QgFMF-vx)u!1t4i31{;~$~A zbFQ^T#o@sdPeu-p^hzRGuy7P;}-ZdO-U z12-FR8t*dKFu#M!=zW(+zR3GFqm3ytRMnl3cVsicOlm3CF<_6>9vXFWt3a6>PK%C) zSLUqL++_Bx0qNMv(V78Zd<%JHqJMBF4Rt_Djv?>{1vjk)3J$iI(UFbp!h3@ki+YSp z#fnL)Y~oAb1P?NAik}Atemt*)Lc<&=1L{Vx_=cD)$a6_=`O1tDhuBH>Et`sk$b|Mf zXq&8+(M;$i4T2Hm_!Zoi!pCNGoO3Sg@Y0J~Pek|3zy7zvjQ>?w2A*?p1+kgb){A_O z;h(UL@EJe}4>GD6)qPe|`<6Wvu$N4^h2P^zc1q8om9C)h#B{c)6usP2BW2*?{E~VC z+aU$Z`Asia{Ip5JV8yxN&uw}Gu#W*p^&q9VPs*p)p<}LlU{4>I&wg`JXL)!#_ZQ9@ zQ|6|kd8++a^_0OIGhQ411iAfmSZS8240$CE_1lc+2}?@?uTWmlHit1kC}cr@+aX?X zL;Cc55{*eBeRRaWs;O=#_IyNKv|utxJG#c5AZrN6?VggV_{4_7)23Q;;x~XU32{v( zu{<92={sOS;MLEmCjYTW#e!-pa;^Y%2`>dX{ke(M1I?-4sENVqo>?tYUwc0!5HTulHy!n`z+rL`=NeyEtICVWHvg#9Ie#Uc(y6H6+ zuU#O>5S~}{J;hd&wI~!zE+4G)JPPVLFrVwN79cv&b;{3*De%y&;a~xIU;_>UDcAQG zNj})2q3{#c$ER2TA_+5NzkMeS$(kG%A0Iw z&K=osYZ6+y#Mb52|(O!M7fVMRWp1Q}fx^@q)%==RV zQAf0{`liv}*x2@$$q$!e{SzcCh<&Nbm-$Q4Ei@UTKrK-z%jwi8?{R^^ohBoWhgarS zq0RCNqL*JC1QbqdYc8un&#y`2SR^h|8JDD)x02(G1q+J zSqOoYCx(D~hAQd6X*6_~uyy==R~y#!UWybQ7NOtH<`n~T@kbzn-RM4xzKR3bdV4*h zr*j{RV$B^H1z)30TyRcKZ;JT~Pm@%48R+kaC;EpZIyBO}xvgG*)0fvjNh52vJoUtUw!>y!&<{sUHs(Fd$YR?&>#DkWi(XA;6h6wR!{8fJilFXH2eFZ+ zn*%J@T0+LmqH@t^_p7VoV|4UJH<7zI{z2_ zG*lw|H{#JuD`OONDD!QVu51Hj8rdI7BckHs*N~JiGg1} zvf*=V72h(M`|E#HC@L_pdLrSK(^&7~dV>zo8hEk)!r2jqJWXV-5R+dZOXttJ?k!?F z(=6?^RevLGy8~nus(zn;SXgkFwPU}6gV~czkO+W|tNr7j5cMBuI-NCbH>)0Kx}#5X z?h2e2R#a#VyoLgac^j4Ly?Up% z0ri+7E4n(8l5GY%i9~xE{CJmV`TDh1r(WyV+yj*M5Rw$?E2m)w<~#6{w(rAPbVo!s zDS6~6JpPJrI)4&^&VvA75i8gq5v4S2vy7hokNH22(G(YQH}EBlqg&=QC3YrpkMq1I z0P%Z8y3~F{@Fu=9G9e{^jrS-hm1 zUgfT0#=~-X<2(woln>2j*k>!-UNp1GWY*))ue?y`U0+p=1@O+-oF^fk9H?!I`r_Y>#=b-7Q4enKt~!7T zY`?17Fzn=nFrxO>Qy%0$&}23E+Vu+|L}Ui93!#~lkxH!qH=})!bekfL{#mr0bqH&2 zG;}3Gb%G;!VXSyTC|s%nS6cezhGRvax5~v7XcM#EYz{mK&j4hkxFaJq!u|Z-#QOnI z+vGqFl=IEvWJr<33^w7pXd3>6OK0|v6j0~GTmq$ekbmh2Yl|7|N$_z>B%+DlO@ghue{laX(E7;?q|WZ(71}8+>veE zh@x$6RGf)My11yCEJ@tBViP4+Sv^y3$lYR=zAk@=Pa|u+9iBKUdGKs^Q@L|0wP1OX zyWBMPvXw3RhzO?Agd{NZjtJXE4ueHUEjLS0Esi@ICFB>XzeA29KdS8hJg@kKt1wsZ zM9r%KVu^`wVPtWK(F=X{u+nUj&EWauN8Gi@p|;>4peI`6 znJWPlK_mChV@SZY+dwtZbwz|8F{@oa|mSC9i(x~3b zy_d@M2sm{KM&PYr`phz=@f>L(ErDXeMJ5I<3@lut51`XW5-n*V`0!brTh66zmSZ50 zIYR<pC7NzEd-{TMr1!N5Ii#`Sra=`o&i)3Y&9vQIQn%<%v3@9bDR9- zr@|A@xe&^&CuTp02O%Qg?W4l_17OsP?flU@Dwg=}2&RXs|Us zvFZ1@nQH68q45~!B6W0WNkiWm*W1 zP237WzGcJ`0lx-6=(&NFyxEq!hl?D2^spcg;uinm5To2(+(2g@t!3O z1~*!885GnHoAU(?U;HLOgDkih@dox`0C2GZD6S5z+ZLn!tt+vksOWRCz*2i1qpWVB?9(HItt%ng?s#F`_@}#_0|49Kc99XR zZb^kQ^RxvO_+nBOw>({066?rLwoL{ktv#wK=6eR0C^|;q5l)9Xg5c%UBhO%7UfZLf zZJdKKpzx~!o}D;NMG!l*)FPRPzdW3Fm`I_%_o!naoTJ3f>}FX|{c7)n9$(1R{vkE1 zHxnBwkHqt5|F27n9Gq7f>h;-$XbD4CqkK|J@wD@ciWPx!9tqPDi^Pa2scxak|1(K+ zfHdvs7^v)0>lhe~)9)p^g{uC~HvjE7RRM6O%J}EqSO-YJMFs0;tk(#YT2uNT^RzC? z-TtHU-&kHzC8zug|9`9cr^EkM@bAw5^JIkojq3AmqQl^82N(JdQV|!F9N}rAGx^4t!6 zl~4NwK6#37lAR((FL)x}iJZ1Wz3+4(`!z4q3_^_7fY-2*aJW^C+Ogd<41ct}R~eJk zmTtt1lf(TGo1)M`bERW&>EpbZ?--}XPA<9BWf(&)We0k;EyHI{k}<@3{n; z(_+I~m_Ay}U7$1|8K-aIxMdzX|K>kp{&?F=#pJdzz#ZGt;1i%>lxi+%R9AAEcrMq) zuaK&-5_sEe^CH9!QEQw|CX0GyvKy7e4 zA*ml{>9}M+hg9iN!bmnsydyFyN&?z{PmxCKAQ){7h5eukB_dXJygwKsXP?KI3mwl) z>X%=y$Y0FlN7vEmj1jg_bU5t-4ugj-)BAAT_Qp|cq-U340Ye1bMVR0&rLvz7+V=5F*h*s`J^}x>Xj_Rm!agdXRtUrLIqY>_yQLEV8ew&MAROhzf<~! z+?9dqz%#oTu>8IfjO|6y`y{g}tz_Yeea$EyF*TIA<|0 ztRdIV*Qzb!X5Kf80}|%*Hv?2-=LEpI`3puBv;{?X*;YZX?n>S9)dZSJ+ghqlwdnpO zOTgJWD|!~h7FW$~W@KCCF`a)%t9$$bkW%!bcdos=n4AUGiszhl7zGrf8}~CZQwR5VT{8`e-gp= z(J@K3W4J0t?X7EtIhU1@kU00xQbK9XJKvp{QN>vwcK6ofnJo5#U883}U)rcsXnw)ZMaHvuHr@LC>dBHhOD)dzBum@*N{chM3L(bvqRzcluxPdLp$m`E?9uq1 zC7=LoYI?n-CQ>|8ftx{eUOdZ@o5sUQ2q`z@<8-^``i-;5BtDhIXQ$29#~DWGnqO^p zHHObC$R1e2dfCUJe`PH1$a?{d+J}g&vR3nUv>cWYP zXY?mqI#$SU#rf@V14}jlhxm7qFV+>Wwy$~Y-}68bQ|n-%qMtTl@!t#!jLb^YC9%uK zEDjZvB7nP2CUe#F)h$*noa8ea2ke4_c^qUa)ru9Wk8`s2e1Hp_lX{cIsFuwsZF7_Q zKI9A9FE2Z3kq{y^EA!}VDOq7NsyI5Ch8?4%rjb(e%xTGJqmA%37k3s7xj3Z0#prH#r5gB@McgA*HZ4w54l`<6)%fX3pawA zRlB95hJEqU#P+>|#&NB`)H)V=I1;xlt2j>$m&vP)Yj-8Tsd~Y;|GGp8;fHuCNZf%y zRZV@T4jC_($0}=iYf~o^cXQ>BN&`B)_y%zj8C%$ja+C&PU=re~{v$G#VUq7Ux+HgR z*O7gh;Tr$%fHU0d>5VOXCmx*E{{!f#Wtsdx8eo=tlvDLKCV$%0k`A?F{@=S2k$Jx= zs~+(0gcjA#PpdYTH@gR-FUp^(3#VffDH?r*>94`!rSSr@sZyCaoQGT*Ddo!ax0)&eZT3DExykS^O#TzJa!JI9u>wdrrPN+Eq=+J_RceV*`uY^ ztR9mntl%_5QM&u_z8-|pMWwzG#hVASkPfi`O@ArF+HTo;k6<-Cn8@ky0kM0$l{P!c zjR>3FtU)hg_{;Tf>2AFZ{?$=DYn(W|TWMo8{Ck0m53?y`KsScLNVfpgTJJZ7=_oH+ zncV}tK^?7k+)}MK{kthTL#{@3vezh@?N~bc?<|vkFl@yEscYN1at9Mk`3XLFA-dX1 zKfKBNC8D8YJ2e1DtdzD(%v<)AA~*D&oHxJiPNz|K5<^2m=+m<}^YGcm)7NcR(hZ+j zU8AT*nBXtu;}%EVNAixuW#m%<;uiSVYUVDo2EW^{?HI}_^O}C;W$%G*o5>kYNxt8_-4*A(C>Wt52MfV=gR`f}-jRpm}rGr*I zcJZs_cVwvM^(4SvAkpsU8gt2F$v`U;4O1CdFmb!Ke{AVenqY=wDcVzhqL?%2heqzsugMs;-2}^` zaP=a#xXG=8#XT1FAPU@opXXwrS@7K4PrFeZV~Kso!zJf=4UjuOb(BUTW!h{XGDV9< zTOp>7M{@?*hojV#EutG$vk+v9<63~4ICo=^`!@wZ; z^iloAVP1#;nSp`U8Sv=(+RA;$NHy!1>8@&8XIM{_)xE zuXkngHA)D|P_`alhKu5v>+SM&(zHF69WX>g&Txw#^shlHJ@+9(?v_`ySZa_+M{e}w zGIiJ~9K{nY3i+aw+JLYw)_|aQ-YD~fMXPQ`;#L{i?&ZZey>prAsE)C0(J&*kU$Ane zjkp)E#*m0GpF(zrDLcFNi@$45vU4m$(+DAo+*WhMcYFnVC z(y7-!sX(hv>!;i(!JQ1+Bbn6zKJs~cD zJ0#o5@i69^%JrFCdN>T+FU9pDKH+AGjP9I9izig;$m-`uhemFQ69bSyvZsflH}r_^*TKhc(qw_BW{pFiXX%2$%Xo=H{46b(#JXZO}hC zynf1sHspymWJYy^tFOZUu?^#@Rre(^zDgb@Ug1}%s~fqV^i7P{7;ACD`&Em2&bpf+ zB2}2b0T}#K% ztQT^!0a%QcG|{X%gsIJ}aHC2U@Y%VpnMhx5yjH^m(aDAK4@@vGN7Z!`97uWZv zbFZXEoW!Hl%j^$lelbrH9)9*OpOWjR&+Wq}8@Jj*LRrV(2A|$BP!Q+f^648AXtPDz z;NFp;zfdkP2Ra^itbeMmS?f8*6h96_X1LS!@n^_6IClq%t7L`?l~(M)PdMpJO21x} zL>;`D2`+=IXr=aaK6i2N33DwMDnFOg$Bdpr>vtPp8n?3<|L8f4r0?LFu9n1p(Wv#V#leflu@;9pQN5v` za*Z@(%}Q2qQu@7ej(5s54)+gle2b+dd=7rrK7$XbO3s}A2(@SsY6So<#~C+KaeXae z0!E)=B*V;l(kzb~^Gda&Ym*yx9fQ#C@@d7X>5{g(GD1fSapje#f>t4)QPDz$bd`*( z6ej@ipi(P{*Yv~TFi9D8d4>)VFjdENzjrBKt;W99PerQrF(JSd7+yI7%Rx30ip(wGxBq*h7UR7;@V<)B?54jFSu1?KHWI8lG0z53t? z1v13wRFEuC*G?Chjn?U;UT}ivxR?)B$V)8T?Bt=jxt*;U3>X@5iB@d+Wu;+1RGPo2 zPn-WR)pQD>2ck_|&(;AlzZR^oVeDBY66hbd|Hh^ zbyVxr07vOl^WIm)0Gyrbi$PC1kCjp{lRM3tLM=x*PmMaYQBL5oyi!t2srkf`)52c9 zo=P3OKh9xl6AGdVnD`o!YdKXze2~nsbiQhdGJ6n84>)0)Fyxe+4LdgCyU7xER%wC{ zCGxJ>Y|!W%jv3Oa<6F|+H){qi5{666$^<=sELCm!wujlO77__yW$%iTx)3K#224|i zjZAhijS4L<8c#M!jW~%8T2*fo_TJF6d;p{JXJv>g?g0D1?ANhbE}u_-jfOC z4ISx39Mo?W@~<(v9B|dpg9*#bd{~*LPX^={w~LE09x~X5PvmC)IOhX4BT&VD zFP+#93sZvD{=|u@82na~8p%VxlObERSNSEHE zcLYKYorGRQ1O!A0RRTy6kP@0w6a@eAJ?DJq{m%XF|K9s-e%XDt&Fs@=W@mS1vwALK zivOWe!|1~B_)IXlVOnPUu1Y{kq<*x(bcs)3qEK7ahfv9Qlh;nqGs_)g`c{YgQ!tb@ zL!pP-wgI0+E_o}^A3f}!mnU1%?=WH~zlK0O17I-%E)V3AQ+vgSl~%ggT&8UDzcrTU zYp2Oru%AtS&$KH%z7v->WrSX9smA3eCk4xA<~o~3P^(HQz1y}#dQQ~*^)hV{sS_w- z{>d>*-W?`y!Z6+X`oD6}f3e&D!B@Ze!-R(;{}EpJT2lXS0ym}oDRcJ!#Bk#~_!po4 zZ>FBN9v=_{e|08oDU7cG_$&Me^#7U1r^A}Czi;|XLRuQKO7oAm8J!-QfW6xMB991zLWJn zg86y(BFEF=5%a4h{Ay?5S$ zFv(s#t9Yj@)whogQah7p9cJerjjy^XUUUOh5qIkvAsC#R=2QYY<0m3gyV8OA_{V1Q z(?tiDJVgEWf=z;9H-=+q+b42&fJef~8)7w7EuZJ24g*cS0z*susyBtq9hg>Hss^dm z^W_{=0`_-&j2aSjBf=bJcJ-ZVPss<_zz;Grmwyf#8?>;Uqbqa6vAVgprv#J$0=2$; zs}jCyW3W^mhHMaErS1^}ghVK+ZedUry`)L1mnN(ac;99Mc5iUr)8qT`x{2yBRQg#t zdz~udj`nlEl9Kpjn(3;E0rC}BGa~O*-G+1~f*nc=r$idqpTT~`g9{OM;3MbMNt%Y{ zGT+=so_X2j-bfS2Tr}WOLwV&k=IuNZQ^}qGn;@m>*q<0++EM;R*B?3j4~EU3`jxAG#^1=c`}e-q^BcwIX&l^uJ1 z0UY=>SZSI!ovsWfXEzyX7UoA^zZWys%1>pWCio9dGS*kdKAx{=PZe)hG+8;&0IOr2 z^RX8f*SVA>msVXrB5`vT@HGwfzK9=SzBs_<cKT!Dh?ld{%8>VO1_G_!4ZY@`%V{{8vTY&8h{*u}7(Tj#JHKoWFuo z@NP*34kq0%H2?Y(?)1$?{wA0+J1^q=!+QKCK>a036#aSA1=M&8Oj>>78X3zIZ)1|8?o87?}NO&J|qb@E;8BW~kcuWBGuEaryVO$=Y> zf*Lfa$zYB0nt8#Ufddy3l@~D_YIds22)q+PT|5VRdkP;QZU;9~^HQ0v zvjhiuw1RKK&MjLv%J?4hPchCgZy@||e(Yo=c7ggk#ClVkMT5ZQ%(oXn@4FtQ5?SA1 z$2hdn%(~dEx7j^=zg6#5-uPZ&>#zHilb8_|!wVDB{?`@J> z2KA&k;;7o-v*D3Wh2~lN%2tZHQPCEzS{T!;b)w<*llN#A9Z9vy>nLHM^dl)_PhE zcEWN8QRrM#3r8pY)IIo6;X@eMMYHO@@k~>#q*^E6c#mYEYP-TaPz3lySg(>|=4t8? z%-8bAlOmHy!JqRMN~x~-cy~8LKQ#n_FB0Jexj(Zvz}6dQHr2v?MSHiS2lYo?r*t~q zkyVq)eK|!Rh2x%gp~6jW+&-}SRYUG%VuGNbvCI89#dm#AylJx1%NaA}%aS(RBqwL3 zqnK4G_LEvBQislbDi@57qXI)r<+-@3%PAU{z)>L0#hIZ5=I^@AXXBTfDM$ffw^wp& zKM>!H*;!`FdKZ`^Aw-o<6S-%R_BQW@em`|EumUaQ_5MP8x~?f&au_KO5A^5Ej!1&&?8vfcIYx{+l~9$g zOL5jn%0PdrSs^HeJJa1jMJ#pQ0MNbshUQBpK6@nTZ5+%CI2g^YrkD0U@I}SYE60wu zpD1}psZnZ9(6`WBj1O*_XUbQ-I5^U)@O0P`2S8w$Hfg7}_0ygMPqlDiev$NktwyXHRVMw4X|k;CmI%5PQ-p zoN_=XiYkk5;cN8(gY!vrIZb}=*MY!He&mSm_z!EU#N?Bt2YYcsx$bYywsmhgOW=OK ztNaVLZ~KRQYJi^%KvuK^lEqnJT+{`rzP+VkHH$#1!7|$c>?V|klh37`rd&VDWpW^i zooXQR)ocU*epD|-w_O!OwU?KhLN#|N&rS8MC$SM%nbSniBoe;e_!EXq-o$eE_2btA z9XW{8EmOx}P`j?eSyhI?YC>PCHYjsMY&x=mUnvWbxyTF={SNTILgH%?kB&EADGUA? zaf$JLAhcZdj@o3(v9b+_`oJXp=j;QMm}MP4^#4izm9t6ooiHHmk75|#>AyoR9zZVg z54gW|`rkqP?QHWe#3KGg`W;Felz&kBXY^O5O~=0?$HHXz!lhmfQt}>JB8wN4#-7T( z*q`p_ zC8c6Y2H~kq^xO|0+cmH18>S=rNqLlw?7bq=By1)b?mh7idvo|tcMaDS@a`IZup9qh z99EeO_=7Hw|CLfvTv_{}(ny`CZpvD~-Em##AbI|y3BMw|30|N=oJ#M0V(N1}p3~== z!pDIBwdcph;mRfP!{+QPH8nL?Z!z=St>s;NXb)mQF@Dqffb)b;=qeftR5-+<*g1c= zf^Ve!lB^?FyJ9toMBuGoAr99Wlq{^fhB1q>K%zooCA#*t-Vd6LnQ3QS z@sB9RtnRK07y_V}o&&VOW=!Dp8j4RLdZwiRmt@s~?gpj84*kvF1k=Kdoadya%D)L7 zqtrvU=!I;t=1cVLDcvhc`p1Dhx*~DO_ui9C#~*>|Iq{ek0K>X_1RAVf@!XGOy@O{)3M$NeA2x3?5LIXAXh zB)CHvk68L z!(c#xk(ej?2Jm2MR@0ZoBnEP(eCJ3#Z#_vtfw%@J1YG$8cNUIH2k^U`i)yd-1@tZT zb+%R;&Td{=#dQRyd+hB!*%S0-5S~1@HNI*%z6`&zUC!{)o0GlKUsQ6UGjPQIXOJpc zCHicQ^)3An-}9!~Z|buGe4T>^mAASU#!Q_@=s`*`)?a=Nm->G+TL;kmCNR6|?0YV+ z66E&t@@s9AC9TTSiJ?6FmxOa;E5FD45!#Cc}{9W``VpL730_-0h;;on?QV8Saqt`dtVo+ zd130ScA{GLS(DRXF-RA@)+%pm*RH<$<*c;*xRH|Z&zOy%MDAzL1&4cBP%EJGIhyk% zWe;D~r`G3vQwNbHe};bllP8l5g*5l)hZL(jZFizJf<8R8UitOK+I`HF`deaXZ? zrOs!;cwnJo*q*hc%|*OqoFct1@H_J8%I=lp@Ng4_v>gLZ&Iu84XcjnpCyHgs>AQzD z4CPyYe))wo=7|VPlN`;W+E2sA?l$bSu-M@v3V?T$wZrG8t5|(1vc40A%1Q}7u5Kmv z8|;LE0tq#~c+tdvgQfo2PKRXjHKWYa`d!ZLiC>%r@R4Ov<@f>fE|ZW*7GBnu#d~^7 zuGp~0K4K~%3h#X(Vt9>j6r1=p;}8A5J@&a<23`813@e(0`y%qc3EFAeZQ6bQJbBad zZLz0)5X)!lwC&H16{fF>3QTamFZ>7KwO^1#hALaeBr`>Q-*5avKt8gYa-HNzI4)rR zfZzKmH2bu|O)yrsd3{%%JK&f)WI3E!s8daU))fCZK&Gf}1aAdw(HSrm z+>LHQnG>CQ(h=WlL!=cAM&VT!)8 z#y>HX6KdwOzN8-fqj5cKy(_`u^|f;h@u#WNA=@!p$=yKi{iVKpX{V;GfUgaJ*kkJJ z;WhvYI*6{>v8G>XJRf-6H`x+kj}-0tQtKl&lSdix0V&!OQEzrM4Y;< zK0O(JeA#zzk0BLu-aN~Mo>06uimt!5Gf#ynoy`errv*h+vg)Mu>xm0JA%+<-}7GWL=h25+z+rC(Asuo2E3R#8B5kfp^omW7+X!xqUA zYkec#^KEI}+ZoN@h2V&pA70Zho=@mK3FCOV zB0BRLk%C~qN@-hfAMNorY-m9Y7oS(8-xHaOQJ&XX407Yki&Ub2d{&~vUSt=&m*+DI zOgc{~A1;rs1v1aDVmd58^I0~|0E8*BHbF>je~}X}PCt%Fk+>eqJ=FuXw?dap?jxQ- z@f~aXmjTB&DhWRqiH$S(!4_II6Tvvs7DSpXs>5^8avAc@lD3VW$R70C z=d0T!r@t~BsD8jDu2lv;j_&Q~XWvu4m5(_q3D7A+QY)pl)ow6LvO1!#SuRag*#`D1 z>0G{Qlz!EI$dQ5$0{qwsR&$1%vfEWcr75GGX9QNn208c=qzrbDQNabW>2mH~wk>u$ zNBpf|h^C`)6eEP~gZO5mIwP*n)vYm%uElvv zH=wi6SK4Wm_m35Uk@3S4peoa4FV-uhm!;=SeDQ~>X?B~=9JW-|F-v}I!|md)lH;#E z1s*c{*Xa0qs`<@w`VW8_idQH4DuAk2FOSX7xvLJo<}t3zBRxStDMGNUwbwOIh)LC} z`xh!k4yzqwKL}xF>Djz^+H*6ifggHgv8{54Ol-bRb$# z&Z0@=SW9w{&YW>mpNEgbv1K=o*+Py|l;xh2Bb@1v@7V>)Tn?PoS&acnT*+#QT=6vK zZO#f^jlyuYgwE)LYNZ0U&x3K(`lamemS5wAz4T8#rZ*3_2YaQG4CgH)R(y~6b10no zmKDJ}5^k{GY%&1?aFwB&&BN z(w;qDCD3&d%0#-7XE}-TBTpZ!;a%-_%70vxq{`B4gu_|YCUXGsUvg9&mhR2>CzIq9 z4VFIQO7-`k5eJTHiqY4X-c3g_RC5*)YJcMirk=b1tY#8n)>}2Rs0*w4YE#NXH_M-I z-q&C^9H~awP%EmOq}ErSU7no45Vm!6JiH%3(=PSQ8VSHv)5oTPc4{p`D(2lBjlcE- z>WWtwSN04iPD#rqD#6dDp@K$_-JN~QCZhY6HNMYeKY8fw(T)I%u=DG%=DF6-u%|Up z$hwfBKo7P)t^C)c#ygiU|rvYag z_=xd@;oVB1l01%%VhnRQH(=o=y~703P!Wct`3h#$^0iljUK_+%sN1qTtm}(ZL+q-V)=J{J^*+O>UVIszgI_w3TqXigLq?%@wvYVP7M^+%0dV6wgBQ&ml+;j2|WE} z)F6K>9YwJ7;v;etsp4v~($Wt+=QgRBHiniW{SImClqkruX;Dtad^a@O5FKfD!K{`y z4Q?9C7!)w_r1=KP2~m$FZMhMvgiVVoZQ4~YivCQ3gF9sd?gcMx&n^UDR)0PH6+G)g?iPpV6{7bbl z>553YdEUh|H)E>86ldw{%Vis2)iSIc&!AHQK98|d1T{G|LZ>Iy>;fH44!jW9dL%VA zMd>$zlDpY)ikf_~b+f(qQo4#s3Xi=MbwCs@DyyFB$*C!&QeKO>$PLPSu`JD2bIM`I zJ?cYpRi{3zo^keQ|7heCMf;rTpdBDD$%421?>NA2y{52%40~)YK}zKIIEt^~$?h7!sog56 zpI*w>)@Db+Ljrmk;c?NLXzK$pw_i)lQ%%==(9L9}1_pkkyvLLJZgz5tS_JH{+0miH zyYA<(I)T4$$a{M}a&_iDD*b39=QD;ps2R1HLz0Q3-5Sw}%H{c@aO2$uLNqLi#X5a(GZ8O82mU?ZFl+j|6^lvOQj4 zDewT|maM+giB=>L@nTAj>7eXgeTCDg{o)(Bn&%yr(!ZcW_pVONAevU)gPZiBq8m8? zP|3!J{biIXGhZ460iOhuDO&~8&33RFt;N(tSr+;!($khoi}3b5+(Aym+G@Ig@Wpfx z$?d4|7u9hFCKXFjWna=3cXO*H-uw)jcA}5yU`ge=XRZqBGzdGply!hg(-0`q-2}sP~+%YGolN$v|1Hz9B8{G z)n0Kx9Ba1Qe#$Q_bvF-eLaaYC6du?n#1<78GErYriSWi2oAK0io8IRCW!>j|oPJThs14u-5s$G5E z^$9~X^;$?4#b>hm7CCcF1>Y45)=U8CPePELC_XVsUg^ zQ8wrNk2^W~U4M|qNEuWb6Bh<=CE1tkvMMkH)v<8&V=hO2Mj2t}0PHVCfFIU6N=#nw z1l~$=VYOOIq0FvbW-#sE7%;7Swnb0DNq|U|PJUC*XVtg1j->_Z>Q2)UIc`^9l2x4F zJX47TZD4(;ZSecDfU};`>ATvu-Fx=3gekG>fltl%tVY zxTavNQn#MhO7hCC5Foxnb|uHqFkJ)0!uJj(^eQlO@2u|L@%XJtQPi`_xhu`AF3o2@ zDoO7cJSR1VW6PaW%Vnwutb~8wzaR1en1WzvAoUoV`U`gWOhe%6)2~Q6q+r)gUwCq_ z6ta}*7b6@{)Q1Oc`K-2~2l&>xuw2EqVw3JIcfTEEVniCCJ@e3L&k(IU^N{|pNcJCR ze~&b$(wKO$BsL}5+=hmLj8IrYlYqbrN=^A}t_JxO+NOF0rpI4kDvF797A_assUTL^ z(+{jK$UkmQj+cjEV zwwT{A59tiAfK{wd26Yl8dzPQwT>uo93|snDFFfH}fvP>>Ldq#&uG#lX#xjw-UDDMR zn8WoOMan0*noOznJgr)^&}5^flFKyPITO;YjZwF5y8l*i z@UK|(O5m=@AX)Hd<7B`?7*eH$0o8!cb__cU7r8qt--} z#3)k3wT{n~?W-lx)RTq;{FF5-bj<_ok!F@}Dw!=T_GH-J>f}z=ZQX4!oMat4zurWa zAVDj9f30kv|8*a=4ugqiY(;Sl?!$6cMHH=$%E*Sz>bHyNKLbobR!#NzMvw%Z*fdc_ z7rUhl`W@@=6UuWvhztXr-G`(q$}zk-o{)W$nkBEL-nUtx)n313IG;A(4veWq#ka4* z=2lQuU(#3$=JNO>TNzMS6sn&-f3Evo0xBp}J6;Ytbqn(!+prD0%(D|!e5mZuD@CUy#^#OE#HY4K zj;n;;%Izw{_K^y@xVxKdL^Zd}#4|H6JiN!484paN2vlg14S~oLgH$Ih1C^?#N>?Mi z-DFBzwpeTGZ@j5KF#zA_&2@HH3!m9Lex{@sJG*W;g_f-Ahx#G+STh1;<@z;;slp(A zz8{Mj)N7b+d^@iQS?i2tmoC`uME5RdYsH@u{K$-LuQaHdG01CVi2!V)Y+~rArFRD1 zIY|Weug{%iDPTOdHfk~MCRZu@dhhSXU*A)z*S@n}|HX)5z<8`pUy+3N-#^kK#Ds)L zzxuk~pjZZ$B-DZ4_w-k%B_Ig=%C9B7hY)@q$d(mM<$Ks=O1Bl$S8@^g{aVF2xl|+H zYc6Gr>5%Is61*=a^5~SHI{!%K$p013R=k)9n>I|ner#ItIZytF{MUtVTglPF{7b}P zEr-Vs=v}Si0TBDiG0X+}Y70N5yOwiZ#{e(nPoKUx?A7wA7SZkSzh5&$;a{OqzvLDDn*bic-N6|F0~OOMRwscXa%ffKwLx;R-j}&}2ZNkg|I-Ks}qTtI{f-!D(o5{NN>O*p6C zlg&zTnK+!Tov=z3nz0n{vS1oqUG)N-0CE=Ab@n#QOEAnUnBFYqpRcLzrt^n|I@8N! zY*qrE-GPJgj#AidE?!DJo<%d5{HeB*Tu+`-azpW*4EddP3ND$dB!~L|nVJmY@^=i; z#dL=B8dba*6gBXI{TbGDOClOj#-K5b{Ej{!JsksG@AaWss8!UKRTw&N(5^{sC9dY0 zv%f|b>Qgbc9V?sF$Rvsjt{qB3w|SsUB2h_AnD^Lr(B&_UigFmUFCiJ@$4Ue&+v(Vh5t(YRraU+Z#n-}>9731{ry)*|E&D)M)&UvzS&m< z$gaJrMwDD4Td~6@4WHW0hOVYvRS?EaY{K0uWo}F8tYoS7Q$GHdr!(rOWb$)%kvki% zGyQvlkKGc=wp|h2t(H4Z@E9+GSp#aXYU>|Z-9w!J&4zpPzX_0zXYVICqSeq1^d4m4 zOFO1Gqy@uLgO?{fn#9s1T`0`fb^X`$$Gh?sM@#!7nm^g2tel@4C5yKnVi)B`O>s?4 zkmcy^?59_U!ID}n`l*xSF1}&VX0OsoyQH*Rtj4fKx zn<_tZz#m=qk z+(9hPD`5D(K1?K00yELtwN!JzH)AJ$Etkg^CQ+Vki!Js0fNIlOPOd88fT^Yi)L?u{ zXCvgRb7YT<#XtJ#Fz`3KIyzd;J+;gbO>Vb@E~H233|s?vGi-8*x&^`|rTLO~Hd^1sM%NV7i$|e3m;e|;gfGwIg&sB(avUt*5 z+_O3e#x6@6wEV~Jy=SYmyP}R`<@a>H|>sFl($x`9Swh zga&_7R{A&no^?BxNuyg#5}4N#_{%wz{@8Mop{};sJgF!0)~oxI)PpMt)B&ry0@pch zFtaQ=bPhgmY+et=AJ$V`*B}p0a=VmGcYrIlYx4aj2#io|Tv?kWiZ@kyE%mNyK&*ID zz2(H+-e2%X{4L`XyncXR(UQ-|J@_n|qBEZV;`W{Mk74@ivuteO#~{ru@NC+-5k>qO zkUHo%4r~P$MLe@acs2^`P{~f?*L8^O%An#4rXK)hZR@=)z`XKWH|`J!v)tIAJ8d_s zIO~7%F9qJAK9YBa+r>jWnrk|_OA9h+Wp4tx0$kW#}i&Tre z7F=($|6F+6H^K4se)OhhYT6T>eC@hnZg3#+gnZ)h14v#^buV1)78I{oSyQ4U_|mP^ zr-DJ=8SN$?wB~8}+0cX;8t zZhr;RrW{$Gh%%E(d=qsl`Ji2!5rR|;y6fIWKfCASTGrstqG6pz z{a_n3v^6rk^M=}+RiY6z-*a}CIG^6r%B|*ZQ@2qYDP5wThwwAjiXNnDR$cD?-Trn% z`pcqh-URjKd5eX4qe)&L5;8VtX}-mAVNC0oI@e$~w7x+5yTeJ|0A7bX6r0t{wWENQ znUmObS@(S@*q!$koz#$23Z20612d~?)>Has;#my{U*r0*YulUm+9ARl5j&AwmdSbK>s|N zz$)0J9e93PwaingQSIQ?{H=dqP&JKRa4SfP3##lE#5T5w{5&;{T_%+QJ2eXh-@6=sC%xVcV1&*=%lcfgm zcfC9z^O7ZnJ1lKml4{`C%O78|H*XCwD@SGP`yVN91?h0*Gafcg_CR>;DhHAoUN&r#~v&2IYGB z_m1@3|5lKnSU!^<@P=5E(enM}?RXV_65o!kfX0gPkpX{!v z?_qWFunhYVY5*G`K0Qo#K8Tw2G}+X4LZURiABZq*rlb7qL8#m=F6`keyOrzEQL=Wv z7}QBSQ-8zbGtuT+F08+N=-g8FM03cL=zQu;P1YwUEV?4tu+RqB9r$44jQ=?!SI~VU zr8T zQrxV>b6V7_27>K{(!?6|Q4T+GF1_YcJ;4#{qjbGE!kwH1o0JfXld0p7(VaQIMbSpr zkH1uJILsCak6Gm8GZc4Ep40oxOLxwA)+7&dQBaPcM7&)y9rwo3$!B&UVmy5hghIr_G-5Vlhgls{Ahc$p zwj(1gqN@j~61A6hZ6IO6)H-#gI9Y?eyq9;6w0O3yv5pa_Q=-9LTCrC#d5D7MK9mS& zo|ha~RP$GxV4M2vE~t^ZfBq;ND2TTfVpj@nUApL8STu4?Q4inCy00qKnbA$b+d2&j z=dKPaatDBU9^~etUcOr}#@!ZX1j=gpW~mED|1wD&AHOB@#HzgVRG5QTef)uRp)CrI z#l4wWaq)b6CWg-N5Z=R^9uF*lyColn^6d>QM3x(vr}rc`^GXq47EbxBDrNuuUt&wHRk+thx|h{_a6W z?~Fjzf!-fKuwOC zWiQM-|2(IW-#gLmEX2NzJ2L7c0!T;B zBUQ7nqQp?^DvVl9L5fZ#(NL^2MiBnAGs8sG>EqLVzUddz;n~62YSN+NOf>riFYI%m-U;#*kHD)wwk_9y5@7yUS2(5Ce8N-SzE*d zfopeM3WL+3kfR#7@S9n5X>@)^1H$ENKJ+*N*f(8GO&a! zUAUV;opr9Z$IJ1d8Dwpgs4a(1i}t+o(9 zQMxrLMHzbQT=D~X9Lt^F2e{zv+O~(5bClaXS*4p@P2e9Q_kw^Zo!b|TWMVz_QZ#|W zFGm+>;6vMPn$idt1B>L>cSrOGSSv2nhOjd4Bs$Q$wt>_wa>5*UbA|8T@2F+MvA4+; zjYjj^J;)_?WN6m&y&1c=HQcIr;9Kj0`m%QbAG(Eexz)DXvc9O%*ZxW)=0M-eBOT8Y_O42t=N{ zG8v{=NL{y_mW0U!rR|Det8rOpSGGv-IEG4b`vLpctQNY=`fYO%U>y zmn%9MCvE~J2R(KUu<@=VpR67nj|YOw1w(O|Dr%$cyg;M39z>a`t{ z1gx&GLlYyo9EwVB4vNeLBzDo4Pi%d&cpg<|{|ObbFjl%Gy8&xxX|9%4uoj7{o)O%> z!fUNB<2~=U88x_H=+Gzo${`>DA|LzS9Nq38tcY3OT78}|G@$V_bem(g25ehZv-@az zs%>9JWZZhg-f+ZbtHB@HrCqO^y>lEb{zlWAu_)PqhmXFukO)ZA0svJAN*_$9y5`kWu#oZ_ZXM zr@1}|W?ObxA)t})m9e)z4A7BR?a)FRF*)i^?qAJItHC?c{}v2y4@&hA>vMW7-60+A zA{9FnH%U`>Q?7YpvfE8B9g6K;XnffcW9J?AOhWiHBiTM*Iu3Qj-x34GP-t;Oc?q>7 zKR*_*(4rRUw;SOr0I!nxaJg#{lO(3k?n9oDs9lNDTd(AVA4pY5_t|`pEtjb}&b8uM ziYskZLw(VjJ}8EBhJ8i{Z(;gWgN>S-@?G;!vs1Brxv~&MW3WR~agI~jq-467G&@$Z z1gLBdVP$0?H>C>z-{9{uRSpLOJlaUcfFxQ9`wkstT&5VTDWpjBIgQIx>Z+qS5|+-u3vV4kN9*L zy)~~oDlkw~VeSd_1GJy<8mH=aZy3|enqdtP$m-TYB^=D-U@i?ey~h>R1jwL*4P0q7 zPcrkwF3PzF!s~#~*1ISGlFB6c7+Y8}$1ye7=h#|DN z+-?nPPwdw&nm+0%ohif~Dy!EDQO`@}%O6kL+PA((?-kG$wiO;otUBD$GTX_s%fFMX zkyz)=5A}aBhbyf{-#1HN%=!A{P{hp@z+Ai=Xe=}pMZ-Y9;75jq!yK}X^d|4ZD;|g+ z<^iwBPLAVzV0Y*HoX`8ZdB-$1VX5ErS^PjujDTsF^kQ+&``c>bsKzx}PtbeLYb_@} zT;uO%#Bd+po4eiNx&wE>${dty^^CMyg70O7+@<5#9A{6Oo~N0Ov)YCV>N4?dF1(w1 zykidY6J(T@T`BP!m7!{YR|mxd4U=EKk=pL~BnldDOQeXN97ymm>o|?4Bg1QBB9chr zgQj@Sv93I6nZ+vvpm#QY$?q2V1Whg`d^=F#cShgoc-nvQarz*IC_~r2?DC3)DllxP z(UhsMsyuE-le3qJ*i<;GewFD|&u)(07WI2M6|~*;a+qZ9Q(Pj9o}oioFlUMDz&3j1 z89L;!rpu84<_dq>q`GF?%0o`XndVSFmB2HiGrQrHpw&1Viwl#~mRNaF+-j64U4UQ~rvALW?{PsEQT{E4uvD2OYHu4~mYtw|3-2c8vd->_+KYxQijwNsDK?6{#ZD zOBNI9(mAnLUz=^mRGp)+_n)F)R@!X4jY@Hdmb&fzTdrP)e-q{YE@1vkB>hwTPtx*7 zB>h8^{+9JG;`28N^KX>vkMx*}Cs9s#5|z8TUV-oD|M?UTL0HI)x7EMeI^uta-`}AB zk#GNn6dv547JozNU&8gjYW%a|e<1ZoqWwRk{6}8@zqoe)f!p83`v2Zq_*v$jXzNQ* zVI%-YdYXQvG93#(t2CIse-FRu_^Drkp8I3jU7P22xgBDKFI3WqeQVZBawlbxsA9ak z(0cy*=Aj{K&n2DEv%11WIm;=YmSad35n6F}6$>7FR-vJOEEnGH+rcaIagv$a@gbOd z+OajfJct5kwGaY(W%PzP$y*0?W%<25AeuzHE@=BX_m*>npV;D|_&Md+{i7O<&r|8jN-)52xpm%zCE z#B>K8H4C1xClc*v)H+p3S<4~R9O122;tUOA6#{201W{~L%AZR_TT%$ua!m;|x+!Bq zDlS363C1VrVOYuPt8b6!S?hK|ucvxnq2!`NAZ86PEsrjOIu{iIK7o2@2cn5MsWN?# z*qoGzB+*Qbc)J;ru;4zfIy|Yq|Ex;?$uLa3^Ans@;4}9M=`aGCn31?uo(~;vgWfCYmVt zv8=1AbS46Nw162y(vsE}4|WK8W`9K0inorBS1?d0a`Pmr4;y5{a3C>PmIZ&`hPCJ< zj#Mk4rvXjL`qeVT44OG`M?3EWq5=~GS_9QC!R$rCi4}P znYCm(cMXJD82XfeA;+RT^r;2+jI9C&+H}b=p6>>|cX}CEg>}l-5Q5!3{Pay~?m@VDY}~%oMg}uRBEXmpBDoJYB>Dh!_O~97lu^;U z&gEyY@ZH)biSxKJMyv_%nzz)7+Q+W-Z>PfA@Itc=>O2svsg_i?Razn~=R(Ci1 zf^g0A8ToaVac1prvts(buztI9DDF2wmLPtvYF^f9&dr_mx#CLc0vX<3Ku1Ytc?~IJ zlpA@|d)gcIXU1n%#kLsvcwRVvm+PFlZj<2R-ksD#3fI>33Kcb5Ra2JJtBKRwKYcH( zeMbC5UHc?h?Mwl0kCsdbF>pH^JTKxV%GUl%Nu%OfRY_25iual~b=LGWNKy)oqZ04v z%2Fj%B#k9(VkjJ&ViSh3n%5>&ryHtdQ!;XhG->Kr*`}yD;L<%S{ox{PqMxY7piT;oYiYM$p9?NFvXb;w!Vv>IJ6iQ3DH!g=fDB}-Qf zNxC%75_UYjSGu(yJyS6Rc0rw5l}4SUII(QHp?kuWsq*Pg+)~lPpg~!Okcl|*x}0)) zv3yTl^Z^JlKcYg{O4iiz8p|}BQ;cy7?opNOqPvS_#>wv%Dcr)W_=p5I(dB7@L#cO$ z=Tw@?Mc@h))aS{FOJ~Q$(igZ!$HFPgY1-cVeXy1we{d*;i@QrPx$5RVB&Pp?cq54# zGiyYZ_e*P?rxhDvfa|x9WCnK?U+@9W4N!2hJUX}lkvbO+=>uLH%ad&qK!QI{6 zH6ggWOK=G85}d_tvEVEO*ToknxDyr$?iT(Z&nvf{dh34gt-Doc&*?KWXL@G)^z`=h z{HE25O=MqoFM@4)?~qFHcX6}@wbsO1Rz-7qxA-DKw~gGA;oJ%d#6dbYoD|^PjY_7T z=GnuOevD`$#X-IwychE?s?InvIncJNiLoXAGtli=b7P65_LX!zSNvMY86WPckvt-FZZwu|02 zPkFDCLY6mq{zGx%x5g{PU=bNte1CUrRh}`BgR1Y`HS>-?j$FT6^dC2f7F*m}tUml9 zwi*ow?8|YHPUZ{tyRu(sWH=imBxXe)n#&VJMXqhRu*SIPR&eBZjwNTqc<}4+6v-zi z2S&hB-SJ_;M=)5WgugF6l=mhN^!+?cAn?I9Ub*hvtA!UO} zrJtZ!I}9w~xl=pqp$PBq{DX9@w==}^SB;%TXMneDdw$e`q*5z_&E96g`7dD7CTYoB zvu%vw{?|`yMz=|sElX~Ax$G0BtKX=mv|6m6J`lX{Y%u+F8DvO1@2Ootv`Xg1xmWA{ zgq61WQPxeMi=Xes?;MMUNPH*?3C*E5Z3nSQ752Pe+W|^a!tp~OiygONU!PXRlz=d(*@DdLIP5 ziol&LDYDDUt8>9>#`Q*#$pTd@_MQB{$>}8ijJO)jJUI$ke{)>eMYrmDg)Mnx)H#Z~ zMOz(se>z?z)Hp?{GsUIbl!!3os}>9^(`Ni_av(s;Ebb6hUhOyJb}=#gI-ySYvhJez zYx81CMAx7IpPUfUdCyHf=xS_aQqkvHQ^fds!VEokZ7w2Ia!v_KZYaN(c~#?mFh6!b zm*q#{ruqG7mcM@UPv;h_@St%17dbH8D8|&J(%iS?Y5SFIrNGIek;Kjf#riFViF;7M z*8I2m+c!&v+Tt!BrOz6df{sDX2MqB5Z5}_PR(=tGMj6V}JSn2L%RK#xGEltJ zmSsc^%L#<`#0Qh;c@%`cE|5HJvXXs75aO>S^Ql`7ws#&ynet$+PjjJ?yuddYX%Ebv z?}Z4rgkos;s3`ZQGP$TSTSTq+mijau8&MCbd{U;dm<{>9#~lBxFrv5;7uvSDl4cLg zQYR9nZIh=mJ}3TNC}$|ew~jMEzah;MOfviVctvx{#Utxi%7ceI)T6A$fhw8Bgp+~= z&O3GJqk{gK!VBA8d5#iabOPS&qMcSk+k!g7aKvhZ!Gxa)z7_mYd1#;`7IT9+FUdNF zzMIDg2v+F^{#Jj6il}B9OXF*$gLCvLTgKCU08?!}UmwjczNA)@GVAU}EPiy?WhvNP|jF1?wvkM^%DXIIpV4B{u|Bh*c{DU^m>}s?0d=6RiG$Nwjg8!VS4_X4Sl} z?-mi`#X7Gxu^l2vzDRxk8y%wEu=Y~%?uK=Fs_2+in&iM7ZQ|eFvh*U=ApN8$utl>j z4QhT7)4k2fd1`Cw#aB!o3V2pq6D{GEbbjs%#N$!-=lOn2Et)f#7fM{bK+fI7=KA%) z9L;%PHv5aWcTfF}Bgh;oKs0J;I&+^{B2B$#Q}dXXb5VW@k$z>vi}=AupHvZCBkW@T zz&EN6wRS9Jz@*Kh7}O+TgjtO)#}O$QBNC9&1CUWsxMIMHq4!d zB1rYk?LD>Cspfe`&T12bKQ9nsoTiCT<}rOS7FDjXYSSHWMUrTtywFa?O8A96{A;A= zMA)ZOie2E?CP`>MUfAVmZ)|lTxPz=}JVx0io4>s+N4JCj{zJC2T29B?IU4ExyeZip zJjwn@86n2DvOPkh&Oo3$zqZQwS;z2|);h>37>M4RaO+lPQ2#gaWohYfSYKHxU^AXH zBx~tTUchEYr#d;%+ArIu$R1NywJcvuh5T{RRfG?+O7uLCQHuSe(rW9eYOA{4WI&ql ztJ64VOAF%GIHh9+gc_z1Lr);@!jQMkNMMxIV%ywpNKRXVke(+dxnk5@qlxpPrItsLtr6xjoL{EvMq@`J`_BIG z`;53!EYXA^(jyS|LV5}6h3>g2rfa57nB;WX3t(dKA}{Z_%5lz4@V(&67-}J-8dPXq zKW(^ER0*Us+91LBO7gQ+K!(pw#l*dLi$d#b`_==+&-nFb(lT?<)G8U&x_q7ygWq@F zOlyAw?QMEa@$Ww{PYmNq+YTx5nSHncQ9}yPl5-K#TNu#f45-M`O@wrE*`n6iz0@k% zh28~uY>$Y6s`XDdzv>$ObFOgbM0(O(kd+EZ-+VBT%>$ooVKK9{6?GOIJNE$biz zMIAKvyK_^pCIa4^kfx`0JNU3xS32Csd3r)YcxXqLp3D9`aKl8^y4^bW=PIFgQx#goxpQ6Wev1X5= z;QR;7Di=1fp9rfGZ7z-8dLJp61CsV=ODM@CI4+n}Z3Wk@%1Q+HIMoX=DSs1*Fb12u zLlYNTLzj;=IjY4Y5dRjH36$ZwmO&!whCr=ZS9Oz)Q;jYk6Ka{a$}-v*B$(lkyO({S zr3KT@0|zreSw$FF^}t@xcv;u#D`S(an5RJGFl8Q5v^@82Lk?t!5Y<79voz`-7&&XK z@WLMIxC^zMrcPD6z#^5+ewdt_G6#L3fc+088LO=z@93!hOM(Fh!@$X8yzx$C@GnVa zhp!x~KSL`)@4+1BQ|8}X(g%1f2wKi^pgOZcrUY)dm?%ecSHAwzOY6e^V$5<&vD+#x zACv0ab>Ti40ac)R&R$9wlyyRtV^5J`SCVAD6~gsS_i2WEm&~3?XV#i+C-dd$pBU08 zUXEK_BzdeL>~j(+Or3APm*3$GR!?8xd6)mws42};eC$fgLVM^+`;V*tx34Vhgbrrf z$3m4a5*)wthg>SMScm%b|797izAObTupG|v7o%mKVQ2L@XzR`%%F| zai2P>=x)5U6yIw}nVOQO&ddGpDEH^lTP%{(JGKI9h2 zi^S^(>D510n_TI1?E!sLo8y!E7ylh2 zMD!n+2fA#P6TopDPSQt1sSa>)jc-Vh^VBT?6 zaA~=vakMQw!e~-Unl7&f=61QHw9G^Q3pWK;lJ*@pMRb4)t7`jV%Z|PF#j3YC&SFnm1hHM%vTJuEq)1LN|a| z`iPh0GU1m~WKUKcqbpn4ykvcD|G)xEq2H&L&B$H^u{MFrw@VS+=WHWDBHE^o?=n8~Eq9m_1HU zLP6Qw+lYStxkj&)S3%AiV1!dRjAEixX@JIHJzYi>!WhEG_2sNDS}YviwCdqS-Sd#Y5{G9JwO=oJ zv3Yr_d2Ai^_`_{$DX4{2nyPTm0mLno0qOlY!c{(<(IUK9viT2DIJjSHWrZTWkAAF< z)|yy>SQIJHmeWB!3sRyou_pGc)6Ym6H)y-LqBomA0C`CD9)30z3DTA8d(pKldc+5x|Y?v(QM2Xcg}t+Enzs>WTzAGlRSM3 zox{ZTV)Gu0c}mS=>wR!U#3S7ix{+CIn)Na3;bHe~e`SYwvN-30+1pp7YBY-6TqrXU zjeX&qftoG(6MkWw3Tx%Tiv*4u+^<eN&jZ;^V4$@DEIHR4b8pqtz}4_DT@@QS{Nb{Ybz> za^?`^>R!DUGS!>k*rn7O<2iX&kiv!rEcvhYU4WoxltoH+WBB=H;6(}wqhpv^M(nt2 z)MC;KybSaCY9Rh%ENbe2VLtp*{i_JYnU-mr-YvFo>|#C23ah)sjbxMl)NNuU1**F7 z+ylS};N_v%#KsY8|0a81aJl>#G{v>^W*5#l%(T|@V0R_a2!n_ObEN;l@J_-K=E2~C zk}No&@9_yi&egzmWneG143ICHFRjHJ-9n+uCMgbah?si}8S_ST+=0W{CiVOW#tdgM zYe-pKY9AVKf86>_kXRz-4O1_jG@r#IzkxBCPI5%_U&R~qg zP;8j?&9z^lgl7E$-mb^M-?Y`4Q_5|eKTH0iDgBijf1mZbCDRJ!;^wq{v2r_+4wi`5 z2)j-!hl2J#AyPS-;O`+~yhx$vt@r1>$m_%$?ldR^Ei!IIS#Z!)J?+p?8>UoJ7qLBG zr;y-{RmgB|s6+x(TG7H3jL;I(qQJgp{=Ey2=XWUwpghu(H=&?Mn_jk;b>yzX1++CuzMbf6T7@ahV2`Mn*iOccj+7@{Ay6M(($uM#YT$9gl&MmToFi639f7@$EhfRZL-zZMDvb&A2h&bb$?j$K zgJej=WH6*IZFiqnb&C?oG+hDaC)cmtPrUu-ygHem@j=Tq&=X?;q-@A~KEhO%A*|KuDWPHEMDC_!hA7DG zK8Hc?u!UXHL1{t@8zrLwP!=^6Qy3BU44)0zk*B5HLjtZ3bUEXv*bMOoU+t= z&ZFgfa0DF?*i~L~FUSZd+7*HUa0ZU1+2PrYh`No+FG%`Keg*mdG2^AEPjmb_W{8J~ z3dLSbwvuh$_2!u)e1^NkqP@F4G9wNyJXry0g7=vk*oI;6^rtFv^$yGPYCLy$ZyB4?` z&ZKST_QVoAvke>?d-|?(ou}@ctQzaRaWhw(q48V)kCM|QE{Hg@h39#+N?PrWdpG>FR9&c`mO~UpOU7UimNRGO(>mOYoicbid>!sfD?U-J zyJeR+iHf>#$2hCm&`C|2_4Zsz^7y;t<}mK#W3$UoSD%g}xuCEUm@G<3ZHuE?8tHbI zSd>y2*9WOg=#z$yi5&OG${VhCo?q7xk$1^|n z8W$OX+%51JyYN}a;e@4`HN7`1q#VWb8|efJaVe@y^H59kUe$i=0TC?_D;yKeD~aUi ztDU~@H=}vX=|BEuKmY1*vlf09r!7`@nXN}yZS+e~gVIdfl+wpqta7VADgdvN!};?Q zaw>LD6jm+Z)6m%HF8|JKSMK|(y)>a(c*i|3AdiY0Pv($S+g05jN9UW`pcUpzE6I9b zV{(sYo)bCK5@D6JuWeg**q6^+o?HivUBEmEFVu7d5W{{+Nd_#RB06Qi%o2}*^+O>eX!)>TJ<<>VgG#BPF8%P zfZ-+nF>4#rh}1Sbu8>;Siqv&!Yl)~e5jKDDD$EZb_>Nfug!ACc`r-N8-4a`=($d** z?r)G_vGmEDcgjm_bk$v4A1A5tcXI=Oh%S3DVj1WDzur#Z%K~rnR3GL&O zLb@KyNxUP(3+|m5`vxVpdz~cyo-F#~aBXF5${<=Z?vIrnZrynjJt6tW=P1<_H3hh$XDbILV zNhq=7ADHa-u?d~Tc1!NLa*N;YN#?uLdE9R~NWFcbJYHpEJfiy^@i6t|acIX%O{^L@ z8gW*!A*>c&q+XfTP^_vKK0-la&?3T&)VX=yq##(AyrAkS7dP%ZovWAZSCUYBL|u?m z7`P@Ip-HS*TJf-9p#aJB`#JYxJ#lrK7is;CvVUR;*rr{sDi19YotLOAhkuk8&|NY^ zC@T+1`mL+AeEVT0B8NK{_vd96=T7%mTDW;&9;&(l5te4s1MeCYnpDC{265)#9R5I_ zrL?A{^=XK*YhH9_8!A+7mK_eMuD(I%5x>ngCCX%y{Db4(^wnp5L?r%c4kIC$!A)#nUDrXh}&JNJ2oz zSPRzU0-Tf#_}&VfJ^n)4=oVz9vp!v+msa6&kFRBgFW>zKiR;li~ zqxe$sV^P-Nko=?LV7lb}d4NtybFs^GEc&Uj<8Ww(5Pm5kjlh;I0$DziGH8-RkcC*E zAd_hkwlq1TOV@6r#7f;J7U+}ZQHGajNUu5}$hso|>$9kc(Y9-mlGJU-6XbEm8zM2E zs=LaS>&*AT_O*bR2B2aFGjVkD#S7ETFHekpefqQcl~T9HBLSqM(LY@Min|akP`0mv z(92T?qOkuTs%=m7VENJ5kNz$p7~Xp5UW2oHp@c`8DzQ0FmG)7+>1jxc)4VI0Sz+n( zv%w{IbK_d4+MzuhNn%-RS;QJ1=&iQhbdD>D7($E8mqFz?jJtqyo1Bt`f<-@Z>=U={94b!d_>`ZcnH{K>83Tlg9DNzXL zmt^WJFrDCsd8&4lKAXwEQhPldkG)bn!K3l7ExI|jp2%?8=OjNT7h+}m#HRe_T+HsW z2~(w*5_Byk;eu@7n;-IUEnedGa7tsdNvoPqmJ?LcF&umzKwGT(amcZ|nzWh?VA8;w za*#gfsR6{iX^XAO()?-r`s`JqKwl%ydr5CVRAYP|0FY6bt)jiz@eAkInVVz8^&($V z_!L;n9B77vyd<*AwGQ6(1H+vklX>lf&^2^c!^lx=8UPoF_wwne`rtM0RaQlf9A)Ns zmhK9)&NngI3HkEsUhx2xp`;RnWg5MF_ zGBv-Wv}`iDBr4M)3klI2(g7wX^xcIm+_Q#YyfCyS0b`Sy&xN$iJ{5X|RX;v1){PSV z7>YUl_4I=Skt^8=9a0c+xg?~;NJ#?*7E zFRGox6?spi5gtiPHm6pl!MLnlJi*+mz&_+ zCA;AJa4a*IWWHMx1e@6@=v{!y@Omm)7SVlU4YTZk5ok?V6$dQ1ewUMoRW5Z28-u^J z);ly?fXVrYD)~#+ashnZO@#bu^bafsp<#?|EL)Z_5!vw(+qZG6jCqX@`b7J(F+t_+}{s*XdKYxqww7su+h?sA_*U)QmdQ+@4EF;=25e; zAC*;mQowRxi(Up%F`SUadOc0MBr?MFuVoSNYxlbw?{ePZ~JVZG?YcZz0|7_1F7NvmDoyd3y4T#uW)hT(h>%9@(c)6Z$l>y6yDW95e@Z?~vnkq%ZMMQAZ3 z)Ut{dbK^Gm;zbrW)9{8J;v`(tuHwSF4t3l+X?>kEM)sc=-vF1T3ODhTiA~N`JNp)? zq3Cam8}a?%(hrjBO#GwFcSShct$e2=@|>BSaX#*2}LMQItu>>#&=r(O@`)) zh(ad4)Mp}NF>_*-!=$lVH@z9QjC8{K2NyqcSxSYmDTG%e1TQfC)09lL4~=VC0wC?r z1=A~3cu;S(A-UW>E#G0ad3R`nU|71RD zjw`r(IWH2lgH#)E{ncs&T0}S$Z2cyS#!DpYT0DbXaffzp3`xkD`0#7N=3b;BXvgSc zR-~tc7PFMl81`lH9VLSDzk0P`hMZ2*>@obvwibuwC~PvSo@n@*wvMki5gnbX?Gn_-5zy1_(2NE02 zVS5g`5j0p-Ga3Cu+UDG&#d=1J>ml%=e%}M6a=|<+s@g7+W1c{TAKUMq+;xCFpbI)r z{^JA(YpPtx62-|a^;%9Xf-^Pa?-HnZitIIYcnv|q7x8cf9FPnQ& z<^N4!Qr*qXN7-&X!+vhS9x8=_6%@?SgRj>{8^CfIi8@1WhB*qmf^oJpSdEHW` zC6(pnMMLXbq7^Ty42?VkR!qOx23i%nb?G&w>`)byi5&vuotOs|Q5OO_wJ(E(7kE}I z@vca%s&YtN*`(~Lh4$e0&-gy_3cv^2-ZutJcv4K}JY?KH>kQevi z8tI9>g`G=>jc)2>l_PUJ23Fl(#f;F9@4!&AeP(HvY1Ff_wIK&#*!kGzeVI!R@G8i( zQ38%Iu;}GzdtU7FvGwbGw-ELI5T!p58jT;>7u2Ty4M4((SLfSoZOVXb!TbTNP)~0i z$8Lp7wzGl3{nvuEH*yfGd*ZlCPuMdWW^N9GU{nPV>WJ+a$D)ZfEEyeD6 zjU}3KQt}PCv(BrAB{JCDc%BAsiKXIVa31VFo|ba*{xV>2g^kF_`{J%lV_){eGhSc)3WnWuV?yl>B+i^` zsT<{Y5RFzZMjv4Yk6WfVL-gigGULe;o$O_zH!uB;S}1A3OPc`JXFlvGo9l z%&2u-9SeOBiJHU%%O|@y&q9(_Jwji+l0X_9+qrDKRi48`o!JB`OHkL_bo9^e&!0-5 z4)B&wxe1%j;g!8~djcJvXAmI>8k|L+zHZEU+xO|0Zw}-A0|IMYzme@BL_ntbaCoTR zHgD*uPogQZv+DO*vwFMvBq?QciIbzPfW2iz#MpGw?-5lp>~ z;mC{Y8HxZ#(TgmipTy~Rzf%6fQLZ@v>r=@vac^fduG(#xKu@=3{KHi&Jq;pPqt8{7 z|A8@NU0Mi6`7yYAFE;hzXtSRK=d)b!q$o%{ovSNdk-RK1bE-Dz557U8 zMxI?tn5F1}jVh$mHuv^SMAfZW-i$!EY|-a4-qGabHZSb111nv zp)T4k>@KflA%4bKfkro*XYqFmuEK|A7j9#6@6_=?d5RW&b$Z;Qb}(qhvpP=Kz#stC z2<#kM3;~KQaBt%#R={2ed_JgtpVXfa%0h0zoYSH#K0Fcx*GDfm>+I^payj=GZ1Ry%O{H zm4WG^=Nu!Me-ouMTL))dm~DA(n!jHqJZkd%C_*_CVRN(G`N|!nsKQ_gL*;6L^poU@ z~p!ftV0gIB-%86(0)*_Hd{r)K;< zm$d(#=o2rT7~bne@Tj=up?Dgorpd8IZ)G%$w2e!_+0Co5ZI~S9zqL&;u|xLEVc|NW z_PuQ}YZODZf|h-xB_RVrDBF{C#3s^1R69a=bU?L7QGn!Fn(HqvaF)?=$I=Bn(dA1n zRp`Z21_%=^)x+`5rN33tWCAu`k$txz3gvRI)YVqjl!cR`?@o^M;Df}Jb5G*wYa0a2 zVMD21S<9N)96jTP52@qgxqVbBGCM$k(;R|q+z7fXPHS5eu&l&&LpBdzCf}dYw2!rM zMa1k)YyMui=%OuF2V9@;nKNVZQgUE87+@!| zPXje%eN?gxMP&bc>ff{dMoD;jPhTN#=Z6-6cWm*K#V&91q1?O4Ld+GJ7>A$2E~S|ZI9sF=I(r_u4R9hx^r*V;_lQgq5ct znJ(X!V!+`Ht8qje)?7F2Fu4PYuzj4NFpXn^v1!* z>@%ru`q1j^WJvS%ul+bR?sIYdTQF~VFYD%jXfRFxB3GhMTOl&Z1E>&} zYHzd48M^4iac#NJR<5Uq%F`R}T(R|X`?(?Lp|rNSiY@5E8+PSbDf&m~vV@@zh)NAA z21QolV)11He#GjWSC&msJY)phV7W{43?78glIhtP1??Aic)7G>*8j zsdm712Fa1H8hQBEk(HMe3v>ZdwO@| zZb1E+3m7lh;|vKcl{_4Cb5#ufhb(q)nhXuMj?Re|3{q)tonSA-Dfi+T z2AQVy>WQfc zM!AX=-z)J#N46_$zic@C^33qVO-sTbo914hkQ?E*-jHQUr%Wg4LD5=K+sw8N#p@}R z?5)@Rqx4T|-WR2;pX1<`_z*8A@v7WfGn1=vABNFH3$XTYyn=SoGXmbYg zv`x=ttIpQ0N6IhY}U>dd+usF2exHml`W)ayk?t48X6+bXuOT1UD$^FBB9KSw)XL%%t^Iz zD~rYPCD7@D+}doYdCVhvHNks}Gjux7C7s5$5c5(Z z8M-#1f4T{G32D+@#mTWG>u%*sh(o{Bs#p>;X6XS+53cV~F?`n9i>n(SzLh4lI7X~a zoT0wJsizPtft}L-lv1Ppw=ScL1Np;;>hEe35QB(Py3dvZK%!?gQ(F;?adn!{LjS+E3$s%9ipLCDKIj*y6lAVz+=8*9Uw;|p^SsKR`m`7vIQR2Vy)#bPKhF5j}kz87;z0>x_g8- zA_l$E*Y@@avKJFU*4UEEBD>OZVmd#hi;^o!tY@qA84yZ;$tUhNPpzBH z^a3m+xF=gg<}h{#{eVwqoF8|nqLy16`{XUd8b7&PM2~f~ubA1Hf+3xS0>v$F{;7*2 zD~ z+SjWT;>AmWy?*d_@C(+;(6}mf?3-1&VO#O(#~#It#ZBHv(x?OR!$oiJzaP+WxF=>1 zm{7Q7mBp_-*h@Hlwz-`zJ8WuhRYj{e!g~W%HdBM<@&@Xj!C$k%uGJ!$ynT2}`@}CM zDgH7^<6@c77h$D$l;0z@C%zrQQ6+I>;Uq8MU7VI>&-}@Q1md?q1_n zy?@8*RmigQ5GPX&!VSP4hUD2o2;d^58eP6JUyj>i!;{@EFuE@jrkZv0kS@lxW(x}f z##;~TkibBB8)b>d@<*pX08x71S8ryxDtw&!u)NTud%)U!aDdiwHQqLfxd*`ZeU_Y9 zyj7xu7&72rICdBP6nk^bG!8Q1~l5R&tL^zGY45!EDWBZu55Q>F1!+c`W!mC zg2ndG5&DL}Nv~cG7fS_1Uc|FV=;e#Fl3|S{<^)<;Xs^u%Hx8{(gD(02-tspXK3JBW zwph$BXE={ug^5{4#b8Y(8`JCoX+}%e<0?i_hoV2|ayVMwe&iDvrSTZX%AMVv%T*L3 zKz$TH17Ee9ATIUjM1p?yQORjPNVb%jXfJ%`m-lYGyK!>RU@-iJB>Ni6_o=DGOMH_; z^(RF}e0KZ&5>ecZB&ALb6v~t8hnthXYzaN~Cjs(#`}Psa0d8?Ef94hvb`%40LKbSS zCpirZ@62VUE; z-MZihoGDzYbZ0g=+$fyuwfFq`TSlfq`FE#?X;DPK-InDKWV=>tT693IxqN)4dw;U9 zjiwH2P68dc&e~}Z@R@p`w}JEta%MZR!vO(Ci4$T}f=usF+meyet;gg^IRm~H6rR~B z{Q|uOuMp=QHBRNgg(++qRx@s?PrWuPH?t&O^Cx8l7ksg-#Sr;04+p||OJ80yc{0cl z4$B?5^-n=iU=Z>AUbixD9i(6UX2y$behuR% zcZ$y6$|b!USMVeaZ>RIqSIPBxd^Cv9tu=GU9c>i3r!=(k$ozD&{n61^XFrQBoP#`q zZM|C_)?eLFu@&5DeP(|Gb)P{0+_FW~jBPXY2d)il)v{ zLPI597cYKp#xx*JiSpt&gsQbHdDmI;Y&FV9sDMfQy6(cCqEhfGKT@kAbFX+s@OCN8 z6J#U|ftDIM8r15-x-VeNe#apW%Mm4Npq}Q$21$HC``*ITFsdFKg)i`w6%s&i8j{ph z|Dg(?4>Fid_$(^jmGNhDz9RM-!9N=$t=sFtpOYwLarAQ0r>M2j7e5d;mW&?OPoy&y zoj92>*!buu@F~e!08)i2ljwvKK|9{uS=%HKtZi@cLlm505Ma?zB!d%)!xNDDCWx#1 z7;!D)u`wL4N}O|fZGRuGZtI=xZc<{XVTLfDEQ0D#T1=&xY@RK2!!bE_8?o}uuZ05} zhly6(IZr&maaiy~HMAT!xIe({;!=CDV>4)tc%lr_PdURZ4j%I-`11N5WADbUP$zNS zWBTb;)gNy9P(?b$(PV-^AYFCFtrqlPp#_51*)sBOe<g!D?FDmF$zbl#zKx>jQK|R5P_;+O5h?cc1igMXxS~ zk1gPx+!aun7M)4eG^ekO^QJ{q&9P-EG9Gc7%=IEH-&P|+5{jVOWaM6!mpabdJaCrw zImiISfq&&rql{{Hz1(gtP-CV210&00U!L(<9<9}1Hbg^bLxs|L-Q!l{>$!Yi4%{Kj z1aF^H?p)??PWvkqUN4t)_b@mF6rBCVqQWyl%O?{_`zpm;laoO~Ie#l%ralhz2)8wg z$X56*!*v6jXcVDr!p~~!rQP@x)gU#vTO{VB$XgGAfZL`6@+IXK z>j*?SEvf;r!=AUSm?>x3K;~qu-QI`x?={3}6M#1Fv+LA4GM1|c5@!GE|G~H=|JazF ziw2LOGmv3b>fj%0W*JV6fmi$+0#C>`ly2ugz>E9gt`7wGG{Fo@I^C;v7+gGA z&)=p7iy>xlz@W6x^k@Qxz595?_-5yWwUGTMy7y$2RBWTn6nP%sp)KWxZnzMLl;nv2w%B<3!S;$RPD7 z^@+xHQBNGd+(rB;DfK-|rS`5Lhjf(q0}#j52)d%imJPIR1BX&?eO6b~CR+1MAFAbL zoCxYMXiP8hPek5f_Tccv84guOEus;A8SdVMWne;exJq%U=G{KBvhM3da*g>UmWKDl za^QX_HLay>?Z_aW=Fvx3SiB(m9v4~dUHk-Y;;eVVg=6hBaCdrlN!j`v*Mk*(ba)Er z{~n+rb!E4TrQNxk1MoyvoQ{sTpQ!6-U6CP7yw@r)dxDJ{ztX-80`lYv4kk<{hv`Wg z!gF-vGXoQciE9Q#l0y~e(RmpoCuhCkiBl&Ly@R{2gbPO#tth7O=8!Z*@Y`K=RkKKg zOO#M{KTP%SsEa%5Rh!Z+E^BQXoA-vn6Q}3657@hCVt3J)6@g(RNkEA{=35N#`Xk^lmfAX zgwCHMrdz>GE3hgVVi=;4zatg$3t?hvVoWsF-W5*4Wk4^TZH|T5U;^$btD*kD!q^Rt z3gJqn%g*#is6$kLIU8_6vxfGA5I@1*X?CU6O(c1rc7}X2PSj}zOvc9)CSlHx5d5_E z?EY(?WZ&v8cFxgbGO@ITjkz5(>iiaPnTZ-@|v%BI~ zJJ?MdZ!%_ZT_DxniHuw~fP-)ay#T8!+xaxgp4k32WY68Ddlh&=&YetFph`GE7J|{7 zVSgVVjqC(x8&&^%R-6Y2xT>O&L6ORg{Rc)gwpsIgg=ZVqV}^Y8@C_h(BJXg0%_1Lf zadRgOr@-oz0}$>=Zs)-VOPpnDL)9Co*07yw*&-wp%vNwb}jMwc!Z5WkgrfTF1NLv(;C&CUP!}%{+QG3I-;-Mm@FnS=26U zkrPO?-^X}T0EJikK#hd%BlWR%kzp`#YCpGZHpy_u*EqhdCbHgl$)7mv@mMX1V6u=s z(OdWyX2cm??|nfXsdRx81rJ}U-WOy9VvHbY+26pAvqe)I4ydy5+i!C-6XVk%R<;EI z6IWTu=zq@Mnn)bzsGqj z%Wn=9icfGFbI``x`2z>vHPr_jN%6H{xo?bBtwZb~EwB-_o^h42A3mFvaz|jS_VaQ; zbtp?w+B;N*iz>Jn3YEB2yGAO1eyv}04jXc3DWtLFpccM>XItULv;t&Qea+0Bx5V$0 z_dTcbqOy4zRCOh!#1n+`S_zr2YdV*YZz&W4D;IUbv?MeBan4f+xjb4CW%H|$-h-jxXofkHH?F3+=mHS94%s<*6Ir1Sw^*ITT=Fe!ki+B}RYb=TipU8uOtc&HrXq-jMsib4)ctFul2H?s=4(S`p zU!ZDlMr3QsRuk1M-i4g75VH8gKJ~^f%1VK=er`>WD!Z=-TQE*y*kAwRv9m2&En{*T zz?l}%j>PVeMUo;D!iD-EXI(h}KBO-F%@I9lZ? z=z5NKBxg4a46cKn13t{oN$CBG;AdN3{85-Nv%u1JyM1M;rpGD%j$XZ@!bZ=cQ`|9x zp%Ba|y+{W#D*&5GNQ4!}7bl847%_fzZUM7CDN>SURYBETemM^ie{JA|>e99I=^Z+T z-?L}9YT%6fAd5#TJU$Zy^%!~NMuB1pw41Lx>ABCAcB0j-*)#LZ34bS7#LKmvle#F7 zLU^JL(58P)b2do&N;*keNjfd`{p(~U>GgiY_m}>I*Mrx+IPn9o;%2r=-KWXmgBWDg zxHQ>n0Ozcq#l_H9YQKxu@2V?75TW)n&i;B`%0Y`opCu=%%(s9f8w3Rx;UGfpypv(& z(qtP0fZF6+U@xY(09Uv5rK}}fBUHpEDkeN)3)8LGQElsUK_`kghFl-Zo>;Hlgi=EV zn=P-^=#mz^0~c9|*NV-EgN0`!dF_x@d}0=GWmwu}*{_zcUq~p_@+1%gKkv=pHm;4b zhb^rz3>aV#@wu~Bescb{eFh&MNdtM!5J;@;x(#A=ocXR2g`CrBkcDU|N^u~<@vvg7cuA7|dHJ;=q1xYjsoY(O?;1ok)5kNZJgdBdAi|{89JK4Nn@LOuVsNsVwKb%NJf`7`BHf3wlMr&G=IBM7Hs$~7~eT^eU=qjds zYPxqjIqJ<(EMfy52m|Y(w#iU9`G=41(0RgUxqA_sKz0+Z7Q<)-J1K9hp2Vm-+~-~2v6q}knDgj+e!0aTrB}F zTbTcBeKR@{doiovkPC#*KQZW(+ZXF8!Dgz1gvhS4vsGoMW_(>OqO4fjwXu1oHT)It zm;I~)>Q`o4Qaj8mPcDFvEmTDr|rG#-51bpj0trzizG(&78)a+%kyVYue3{Nyabqh8Ln z!<4tfV*f-hT}w3=41-#kBmU{|r^+g-0k*+8Ty}P?)7?~-5kgJ;Vjbw2=agfo5NB7i zosTXn{#h;t%WosZ?C?sF>zLfesw7ifj)Nigs9;t4aFbL%9@3Y@M_uUWo+Z$_)6_JX_oO8@MGa@tN zCl#g8yJssT!ZU^8X8uv~b}$;86Q>xuWtEsocMPF3%2u*5u7u&|4g>|}oQLzLB#5cU zHgqbI1uaxwN5Cfx!MOSLr<4T~z0NknP_S<2x>6?#9HJv7rIWID&ieW~c$uyQzRt{^%#}MVJ^%5SOUtr@u(9H_4~D3^D|0g%9`N?-8;9(7=CT+M4!^Ei z=6;CsVNTqo$KFJ%xWOuNgLwy{CsaYcy6$kB&3#Rq(rM7#;bDr{YVRLT#d3C5b7yMh z@d7Ai%je)cBn9!Q76g}fn5dDDI-0tmPig2VntkCz-jEaHaEmXXo9k}G^R(*j%y?v# zUP%zDz$j!pq=#1dRc?w4z0Q@1Dox(S*$Vk?Ylq2oy*0C5 z8twXiPp5YUQ|L=Trr<%FZk=QVUyjNgiNV| z>s+yQ;~Dg<>&grJ6m1p_U)ImVr~nSWMR3*g#&?9YB79VgILG*KJbcEef#tOJIu~!& z@uA9zf*dcRioCr?u}Hyn8pJK6$zhWcl40#>v`_y&gP>YE7^U;rHeC|gq6A9xA)qgM z%YG6?G49UlxB(2>CHK}CLfw!C$t(R?r*d@<&?G>R7UltnV}-w4O*?*$mosjQGI8E$ zoX)y#9LrI^Eln3JKUAXkR-5`?6Jt5UyD(sfXNvmhH&su%y=6&&0BJkBg zgGRWzK1CNS@*={s5r%~$zkTiJPBC$Hdn{)XrZTRJSTHyNJQvOxU^e&fP$oZK352YE zsUeVp`6&!$4JK%`zv0;X9VC@bzg$!Wih(FNXp0MW zd~EE7!mHH8A!V;h?%FrJlbC6zn?}Ymgna6Lb{Fj{ zxV&XH*4FMNM0i9?5trZ6JP5t`#texton}!*xMt$zi}U>J;rHi00Hj}m;O$A(-P4fGeDAawtE|9ONXU7z@FaPQHJ=iZ>(&!jH+pks3+ zg>0D!pY@pN^@GxJ2Em_-P+7Qc9Rv%a9u7gn_N(t|;uCDtO4|OU{!BO>Mf@u>K}=i~ zv5o^vVwc9(9v|vIfC#_^t`KI$(rz2Fk@a%VSkD8`b1b#LTX7E9l=H>Y3EaGEjY%c~ ziC0)XZUn6?zM8`nYan|hIE<8QX!T<(?@S;imJ$RjznjW{;k*j;4~-MFiwjHL`YH0Y zl+OCGz9%bk;`6xHfFxGmwVYM5Hlf)wU#UMhKV&h&;G?~Ojo1?fZ8YB+OgN^P@;8Dr zs<(_~i?8%;?=8Y74Fp~$q%(hx1eeAYt&|;n=FOr$?J-{jYtUpbd(`+? z`+#he^B15zQ&)2!+&%uOv@Wvn(Fu~#yYR98p|k-a1nK zHzTNMis)zkFz(df2lI3HQUh5P|*g(r8e1@&L4dXY7N@pC##9kEsQO zQ$%WH3~tV)9*j)MSWYNji*VYHrLK-CtR86g;hu-y$wXjf?!STu7uS=^ZAn1(xG=v@rn z;T86fL=496f~w1(vM@0(obMI(6NoYo(hrhNt2Bmj8~p+vAex&6QgIXgEFPdDLYteW zG=*^&2I>hLs3)Lt|Cde@?)k&p;OqJ|vj&o{-(wf~w3^Nx?!aFmb~vjuV(UI2YjBKr zSieOp0E6_~=`3Yp@u4XQDDfW=?S{m{%9Mtx1hRXsk{*Cm2*?4Y2QRt7`=2!LB(Fr? zuYE_S43>w|>pyP58n8EP-J>qTd`?I}UYnt3N0ICGN(emN*epSfjU7uU9>Rg0ZUOZD z0jtk%^jd>fs;gO%{)6DBGvGBhLWXl;3Y!bseM=S>z-v896c}Silm&!S=eEz1WreW$gg?#(_unf0gpjbOm-m^@64{KqGj z>|^kv2nKf+47Ax%Ma!kes0`Ijnm0nn`KIw*+yQJxW7}R5PtNNX^BnZ(C!fCf;MI`I6ObZ*fd^{K1DQwH{vZ}9*J=<#TECCLcC7t8HESu2npiF;e z%w7zZ*z2YS%VgDzJtoGmdWNjIk33Q*(AsB}n%`|udnv4#HwsdzVr*cjqh^*|2w)XyL&EJHfpOmNwRpsa z4cjLSBpR_AAV|j{iQ0?0(W0?fidAsDuRhwX8L@(y%*$h-03D)U16->VrJ-8wPKtQx zvh65(^dT)bKsU}`;LH+8!@mKxdj;ubFg#jG9<#$67p>AIm251p^QLWL@Agw`klRMVoq0^Wae1`$P5cAJ1TRQOmDZWd0 z+g#xCPwj4f(eW=`I6HaTDyT#MNxa+FV_L$*)Du2h1n;bG+Xvh!rQ8)e^=fR0gL#GI3qEVkE>R>tN9hvE8kDQDxhqmKN~Zdq~gF zm4lXIW+x1nPsqWzf3m1Ink;swzF>nfDVSC$N8-2r-a96zK$g(ha_tWIV243gB%x?44(M3;pA0pAC44Iv z3flE_!2zJT#yR-YZqE2)71~)ZCku;LW0;9z-!xMFs)dE&oG0fPCIR-PuA?e*r-7$> zzI=!%1NS-gY!-V<^B#VDou5dt1P?z7SM7J8wPnd48zx)zU_7@ zx6ABO)#hXlyYzp^Uk3X}Bu1KP0^uCY@S+Si$IJtV&B# zcN8FtG!Jjif6Um`Ncl16NR-$z6MXbGrY_zV@R(Qu{euF5vtNm?F)VUBxM!#{Tp60k zZn4d2gYf5QAzdjaT&ncmoENnre9R6#GuJ9D>-okq@y#gQF!G8;$}g-sSux|qBMzg~ zIivOSk=t%BQ<$5&4^36aQiQ(%r|~-xh*3$SIPoLwR@|gL!Yyzko<8-(a~>_ffq>2v2bH zTXsRZ=?^KW_31A>d^2yN!kn~KNrfk-I%ugKe*qV77$+iDpI?gTSQGnx(P79VOzHmSbV!8K&7yHPF#?9K;`?C7g0m`hx?EeQ9<CJoA z|Bw(Q`e+DossFyc7vOXw!tBBs45-yN8e@VWrBa;bX+lhj!roU z+<>xSk%v zt7GV@Or*KaO}`SZeM)d5v2ZR}fsODZn);*vX$TEG2gcoF@9p@MhATVhm~N^-{oya= z$qiXRyUbw}9OSB)R=1U5Ud?clc3QO6Y*%2aUf<~{+^DWFq;iB)>u^5-PT`?9;a2`= z_}ObEU&REiXxn<-4j&hOb|xqy;w{0w#6SBN(1%a*tD08GtMo;+>_<1wiOf)pv?04! zngjhq|Ge{9Yz`w-@f>!C<6ZtKHje9^16us< zvBQ`lOXSljKj<2WNV3n#HB(XE8(54T1|-tk?2<`BHP&0vZ}j>0sBDK`DOG;#6l-gY zTaFg7j@U1UwH}AzR0B@jD=BQZzV&V(w%kki3&@?kxNEKumGTVL4`(6LR(od@s$vsE zT4epHUb;_iul?;mA`#IRlEuE1=cH_hvD14foXk0G@=KxI#8biMEIf47O!9>DO(C#c zl17m`?|gSpW0%18nW=mL)l8q5S!Z8#=>$cmvv*>jtbBa8Vza+lr=Qbzx3`~{Y{7yg z;l=sIOh~4qvHgvPrYe$U5eZ&%qu?EWq4YLnW- zp{|JnDV&v{9W`&|-ONb6;mmRL2r)|@9i<+BX8U%>-shnbgh=|_QKi8fI$(^gh8L6e z%LaRWsn`8?oY*plJrxW`J47YV&~+wlt*-4<)z&%4_$u^M)l?#Tq` z0bsslDd%PCX>JoqFr#6X>sv>)BTkaqO6a4P+Npm)i>*OUGFH?&lET9AR(&dPlKs1I z%FH0{#sTVxB{Yq1+Uq70eK9TL8TR%vXUWipopM1l<6MNZ1X_~-+%H8(&=Xwo=ND(k zm6FXF=hlXXnOivfyE2QEtrdw$X_FeiN9gMI;k(3PHA3q1adC$M^=H^e?R}14e`E=6 zif)ylS?P|*(4b7G+?fc=1W9buJB{~4U{}3cQuRh1&;&d8xjgvs#zpqL=k|hsA>j%( zuK61}oC1V%ny!SMp2w7%YN(Cf(0H1HKyYS*dC&>ds>0^@5-bj}91$lmV5c*U;DSlZ z;Z^Dm-dB1L+W5u9rR;m2p`muUB5=4#_T+O-gxgiQ#ox>3WOiThiK zc|^oj&K1K??B$v^TJ8&LSGw5df~PukxfgeTvuYRY<;H_2f-irl2`$zZy*9fC61o!u zDo{{S$j_0N+8)C0=#spfx1(fbgY>Zm*buk7wa+AUIMA0#iXAci0U{*=cjjBw2w#D7@qL2^u}+4*}};S~m$*fS4_ z7*e~bE=eAQ0O$QLVba-}vgudY?$ZRjc9$%Lf=D^h=RF9~sX1{j2sA%o7eA(HQd}I+}U76nVZ?D;7i$`^}9COwF0#HQ2siz})_Z2JDuGRnf%vpjr;T?Z&fZ2AT zp%jBB--@8=t6^2JZV7HFrO8w(2#07rgB(=D%7i(RKq(B0;0zq;OW(c#H}IduC_p9z zBpC;fc(?ZWJ4>d+PIV17jz{6yk}jA-R(h&QV8U0V8O&b+1=#nY@LB4E_e9WGtmWbI z^d@ScFzRf3q!X^Wn1sDo83KY+sf80~JY1B2V%|cIZuB%gloIJ%vN` z$|`suc^)Jll0MW$bWr97N?8l**>fGlRf2Q8|&IM$? zD$RjvzZ#v0$bFEOB&L4Q90wMCu-s>F&wi%*=PI2L2=o~1DH}Gr#@xx$Hw|{sbO3>k z&effLbe#8=`*}Tg$=<+*i9a0cvMh}Cta|rZj8_vYmqV58Kv$TWg1>462aK zsLYlf-EGLaHxUvX;nYVH`+QpnNu}frg(HTFk^>eL!a+)G6xYfkstsd|iin*~zJ7`7$;)L6Ax-!#@xi@i-P;wfOzbe+1z$R{W z9duA}aLzEtBBvrt{02L+Xiz6hd{esZn9W2BsBQHZ0JC!9g9qzT($ zf|Nz4-hI{8w9vfKY9Vi^s%>m1I>jpQ$!=tQX=G#F!Vy(Wh)P%8nI=ImYN*(LyyQ`U zkA!AOREklLrQhtxOg>Qz#4SOlO1t0|qN&hCi{Z!(vT0BCyg=afh#I6m6hxK1i%AYy z@GsbNbAf{`>M-Ry3F>4Voe-CxWzuIR>@F5)#e5v|;#XTKqsC>6R~zb2_s%esCJ(<4 zF*!0_e@xJeZNFeA=0Qu4-(JHC-&0#>WNIC9{5bF{@TWN+t8HZj zd9^0B2}04#R*)V>sUlC3v>{0xeP`{16j!7x&goH)_MdLE0?nWku>mmp`rzB(SEB`|3 znp4(_klUoJpS6e;XGCADpfQU0zFHB)pA&wtKhZ_!Oh{Hj)=IDXfp`Iz!idD!NvMd1 zYezV&r#7h-z7#huAktWlzLE}??WVY2&ZQnN!Ov-|db{-nvo-AM?dv0(P+(V286LpQ zm&6^4+!q`3Uc4r0=8s3>T)rrfE{z8EkN$=#Ov+f}F$V)~!&<-B1jnE?!5mNGichck z1o(x6?hx`H`RkD4MmKe;z4bE={3)iw@kdMTg}IwEFo!9C;}9yIp0G1~dtxq~TCtJP z5#=|S^ThYG$G?ExkLdf{#=ihc)fQ7DIc6jj1^2uMPhSa8*?!mc~HA5O@VsWCM7CQx4(8>kvQr$4*Y+dCOn3e|l)@b^Tu&k@s zSu<~N&p4-f270PiCq>RCaFjJ_dox(j*mwNdh@sk=Abb=?e||qM0l2kCp<8wzj`4S_ zDf0HztMB^{be#(Bght3kG9;m7-m;fVh?Pz|?$#kqRCcNCgH# zi0-COYR+2GcDk;!aDk;?jZmpz$rprN3KhbrsfHh%vPqS}`^sNxVIM1>G(D8_vlIgp zSSy$C0-F%L*!#~B@v z-3%@5yyX!OGsrbQJjl)@gQfpG#(U&YoX|_)SYJyi4*z3qAr(0O@S_B6*23}|{XbY% zWU0?4MmM3Kk|;MOO4lK|^C%00G}nXmwvzZv(MNBuMEQl7`A#ON(Aw@Wfd@&>Z z$h*FmLf%K(+8~C@iG|?yvf>&vm*q17E}y^z?W(f(kb*%UJw0jS{Kv-oABL``|1e@u>Z=OblJ!r=wRTYtxnJKnn$raD-ky&| z#%DecowxnN%K3+%EtS6wJ|jHiY8OnXXxT(noN_}yGLziB9mHCkK^FDp=$3%BGT<5s z^ck7?kNO1th$gcSSCE6MFPRu-)lc>O{`JMYhvZ-d_`JnWXENqESi?iIcKi|xWghAE zj_1H%XWhz#M%jI!dd&SWFqY1!v4!M2iF65g1@?X+&Wo51eAZ+SZrCPx9-(Uh3wAuh zWK=?^Ld}gQ?pF1Ki(8)H<#V~BTl%g+0xcLEEal`jchVf?N_e#oCAv1t`rQk&;jM=s zf7`ENYRZF?^&mzY`y9mwy*0OvyH%nfv1)eUv~`hsP232Udu&+C{Ay+4FW@+UB@DNs zNjhg9G*Qyj?sLhRchz0vE7wIs+>yYpRvsVO7mfHy+{knrX5Toe0jT!x2u5w2+POMnV5BvOU_%kV32D;LzCfEH2(8O*jwK`frh@|a$ zf)n$E=;T;?Ag)CBtCX?UubV^I?sP3OI24GPIR{u&GtF9p9Z47MawEMK99?0HvIQ+G zeU;p$btW)GCZmd(m0F@$$icwnf#KTwLs@9S;p~g54iWF}?q4jBvJBL@EYgJ7219K^6Qe%*?FJE^!X?ETrY0Z zRhe0IXNMMc-@CT}Z{#VQ!k`5LrQ^q#N}^vk zeGFk0TXPxQ7SVJ%8_q7V1b=pjimiyO_+YZzp2f%Q&Tm+<&jWyso8%1j`W&2hC@;t# z7}IIQh==7+kFtUM5uaJ3*;9Q!P&X__GWm5~vSO5#K02cP=|NLWHq}G<5w=D*!?-I* zB3yy5B*WJyk2G@u)1ONJ^)ZlQ(hTEruNvF{TtvL}`=Yv00rmNiCMUA}{}c+vk&>%d z?{aUw#@fD)vTEk-aCl^=`yZAO|4^VL=W_)*O>Wfe4bZ#v5;G&*0vzl-(w0jeSin6A zwu7u~e{(?uGtO-uX_xjDtvjKy&thL;%KMC@R4Lj{mXVKz4V?46OSNTV5?6J3F}bV> zt0pSmJFHnayRe@@W_WzZYp%6emx;Eqhu?C5x|6In+t;+cQ}8CYyjuNv9Puty$r$5~ z6uG5ZzPM$pXo-+CODawM6%1VC82AuJHE%ZMet~T?rm4hu zc8D$U{D836wMxdzEfY++X95=_p<*2CF7R~1!es2C0pLXE!`zgVWd$SMQfhn~Re(uP zwSn74LU*uuvP07bgyd|^Q+9(KL@87FVdWKXP|4_|{!(Ru?^Evwlkm{15-3dL@0S3p z9k0KDM|zw8aZfpT?sz3MJl#^9z$cqS&-idtDAk-Y4jD5=RU1w6>AjC#hfSG+Q0?d> ze?-(l`xnryK0N2~7y;_1F5A~#`#q;Z?84TC^|7sUxtk#^0sXJcWCqT#w;#}k^HnU&baz#wysTlmU zYnc(aK!4A7AlUdJaa@UlJc>)6AIAv1{sVT?Qm|FkkgL@`g zO_?Y}GzoqvnfuJz^Yz1kx|WP8w`#i#h_}Q{?VU*Qt%0oNOlw$&rs`s+XkYFiklW%( zwm9(*uG=orppexVRfMt2XaPeE{m4&de z2-f9kl^YUX{dJ=ALmeHNrrrP*upkBWp8YUN{?l!wj4R@Y7M2kHHZ*J<$QM^$V?SXDNU*S` zRAke@ZEpjlh6_B=x_D0*e~bL@9jI$L67+j#yA<%Mhx*JhS#=prpy~UlGR_g0p=_*G z+IMbMqKU4hp7!s=VRI$2y98+|2c9PMN3m|MQ%&rCnzV^QjR8rW{-jLob?cqJ{oK?} z~Yl~h(t#((y;@nylV1V=b+X)pTkxqF!52FomFnbD$cKU8pep;nUt`S zOtEa*zoAf=|IcJ8!Lsf7aiB=Ha-ds-Jt}iC?); zkc-wX*ka#prqF63OatRy#Z>k7jv5B4{#QV=Na>D}>lk{JAA7w;=JGA~j131LN7Wsk;4&SrfjG z*yKsvG^v&&WyvI->6acpCM1^NTU8uextxI3q#(1}zLp1~a8&@(GxoHnt2(AW%poMP z3%Ue#-y~La3i(iP-7D2M*u$p~TiWBIJYL=gia*#y73gYRU{keHGhsT9T!K;Ms_%H< zSI9M@{8s5<;U4lC@{ z!ZJop@bq0CGrx;`n7_B$%h0S(|D2U!2vL{rjirb0UsEngoXzpNl}XA`_5NM#hsD4m z`S`5CQ;b#}D_1ojs#G4eiRB}4E<9r|i-!&k74P8oB6G0=f*XVX3#elFtoxWi4g?~P zPDfsvygdg@ku*l0@4oQ{OySplN7)#BV*Qv9ar+MgQ~pvX>aQKJN75E9G6x#L7qt&E zrTfTBvbU&zG9xdH13SS2dtm

_q}$K5zq$NZKQ>Oy05rmPnc-FLvK>K}>h>=LSgV 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 { const CACHED = { + hasNew: true, videoById: new Map(), }; @@ -39,8 +42,10 @@ export default { if (json?.itemList) { json.itemList.forEach((_) => { - if (_.video.playAddr || _.imagePost?.images?.length) + if (_.video.playAddr || _.imagePost?.images?.length) { CACHED.videoById.set(_.video.id, _); + CACHED.hasNew = true; + } if (_.imagePost?.images?.length) console.log(_); }); @@ -56,6 +61,7 @@ export default { json.data.forEach((_) => { if (_.type === 1) { CACHED.videoById.set(_.item.video.id, _.item); + CACHED.hasNew = true; } }); } @@ -169,9 +175,13 @@ export default { `, created() { setInterval(() => { - this.videos = Array.from(CACHED.videoById.values()) - // inject index - .map((v, i) => ({ ...v, index: i + 1 })); + if (CACHED.hasNew) { + this.videos = Array.from(CACHED.videoById.values()) + // inject index + .map((v, i) => ({ ...v, index: i + 1 })); + + CACHED.hasNew = false; + } }, 1000); }, data() { @@ -384,7 +394,7 @@ export default { ); }, scrollToVeryEnd() { - setTimeout(() => scrollToVeryEnd(), 100); + setTimeout(() => scrollToVeryEnd(false), 100); }, scrollToTop(e) { e.target.parentElement.scrollTo({ top: 0, behavior: "smooth" }); diff --git a/scripts/tiktok_batchDownload.png b/scripts/tiktok_batchDownload.png new file mode 100644 index 0000000000000000000000000000000000000000..b29983e69a569fb62791d4bccfb2be0120ada83a GIT binary patch literal 105514 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 WGqUSGv^*~W0000 Date: Sun, 28 Jul 2024 01:33:16 +0700 Subject: [PATCH 44/47] refactor --- popup/index.js | 2 +- popup/recommend.js | 8 +++----- scripts/bypass_learnAnything.js | 2 -- scripts/fb_blockSeenStory.js | 2 +- scripts/fb_getPostReactionCount.js | 2 -- scripts/fb_stopNewFeed.js | 2 +- scripts/fb_toggleNewFeed.js | 2 -- scripts/github_HTMLPreview.js | 3 --- scripts/github_openRepoPages.js | 2 -- scripts/guland_VIP.js | 2 ++ scripts/insta_getFollowForOther.js | 2 -- scripts/pip_anything.js | 6 +++--- scripts/screenshotVisiblePage.js | 4 ---- scripts/showImageOnHoverLink.js | 3 --- scripts/youtube_changeCountry.js | 3 --- scripts/youtube_getVideoCaption.js | 2 -- scripts/youtube_getVideoThumbnail.js | 2 -- 17 files changed, 11 insertions(+), 38 deletions(-) diff --git a/popup/index.js b/popup/index.js index 73ac9701..b12838cd 100644 --- a/popup/index.js +++ b/popup/index.js @@ -1018,7 +1018,7 @@ async function initShowDonate() { let count = (await Storage.get("openPopupCount")) || 0; count++; Storage.set("openPopupCount", count); - if (!clickedDonate && count > 0 && count % 10 === 0) { + if (count > 0 && count % 10 === 0 && !clickedDonate) { const res = await Swal.fire({ icon: "info", title: t({ diff --git a/popup/recommend.js b/popup/recommend.js index 1d40572e..4fc8e136 100644 --- a/popup/recommend.js +++ b/popup/recommend.js @@ -123,7 +123,7 @@ export const Recommend = { en: "Support youtube, tiktok, instagram, twitter/x, bilibili, twitch, vimeo, soundcloud, dailymotion, pinterest, reddit, tumblr, ...", vi: "Hỗ trợ youtube, tiktok, instagram, twitter/x, bilibili, twitch, vimeo, soundcloud, dailymotion, pinterest, reddit, tumblr, ...", }, - badges: [BADGES.recommend, BADGES.new], + badges: [BADGES.recommend, BADGES.hot], buttons: [ { icon: '', @@ -333,7 +333,7 @@ export const Recommend = { en: "View all videos you watched on facebook", vi: "Xem lại những video bạn đã xem trên facebook", }, - badges: [BADGES.new], + badges: [BADGES.hot], popupScript: { onClick: () => window.open( @@ -352,7 +352,6 @@ export const Recommend = { en: "View pass events that you have joined on facebook.", vi: "Xem tất cả sự kiện bạn từng tham gia trên facebook.", }, - badges: [BADGES.new], popupScript: { onClick: () => window.open("https://www.facebook.com/events/past"), }, @@ -368,7 +367,6 @@ export const Recommend = { en: "View your friend's birthdays each month on facebook", vi: "Xem từng tháng có những sinh nhật nào của bạn bè trên facebook.", }, - badges: [BADGES.new], popupScript: { onClick: () => window.open("https://www.facebook.com/events/birthdays"), }, @@ -400,7 +398,7 @@ export const Recommend = { en: "Your fb account has been hacked? Facebook can help you.", vi: "Tài khoản fb của bạn bị hack? Facebook có thể giúp bạn.", }, - badges: [BADGES.new], + badges: [BADGES.hot], popupScript: { onClick: () => window.open("https://fb.com/hacked"), }, diff --git a/scripts/bypass_learnAnything.js b/scripts/bypass_learnAnything.js index 31126eae..b75a5c27 100644 --- a/scripts/bypass_learnAnything.js +++ b/scripts/bypass_learnAnything.js @@ -1,5 +1,4 @@ import { UfsGlobal } from "./content-scripts/ufs_global.js"; -import { BADGES } from "./helpers/badge.js"; export default { icon: "https://learn-anything.xyz/favicon.ico", @@ -12,7 +11,6 @@ export default { vi: "Xem nội dung web learn-anything.xyz không cần đăng ký member", img: "/scripts/bypass_LearnAnything.png", }, - badges: [BADGES.new], changeLogs: { "2024-07-01": "init", }, diff --git a/scripts/fb_blockSeenStory.js b/scripts/fb_blockSeenStory.js index 2351dbdd..c00c91e4 100644 --- a/scripts/fb_blockSeenStory.js +++ b/scripts/fb_blockSeenStory.js @@ -12,7 +12,7 @@ export default { en: "Block 'Seen' story in facebook. Your friend will not know that you have seen his/her stories.", vi: "Chặn 'Đã xem' cho story facebook. Bạn bè sẽ không biết được bạn đã xem story của họ.", }, - badges: [BADGES.new], + badges: [BADGES.hot], changeLogs: { "2024-05-31": "init", }, diff --git a/scripts/fb_getPostReactionCount.js b/scripts/fb_getPostReactionCount.js index 8171a36a..4095b072 100644 --- a/scripts/fb_getPostReactionCount.js +++ b/scripts/fb_getPostReactionCount.js @@ -1,7 +1,6 @@ import { UfsGlobal } from "./content-scripts/ufs_global.js"; import { fetchGraphQl, getFbdtsg } from "./fb_GLOBAL.js"; import { hookXHR } from "./libs/ajax-hook/index.js"; -import { BADGES } from "./helpers/badge.js"; export default { icon: '', @@ -14,7 +13,6 @@ export default { vi: "Hiện tổng lượt thích bài viết khi đưa chuột vào xem lượt thích.", img: "/scripts/fb_getPostReactionCount.jpg", }, - badges: [BADGES.new], changeLogs: { "2024-06-25": "init", }, diff --git a/scripts/fb_stopNewFeed.js b/scripts/fb_stopNewFeed.js index 4a58211c..d696f7d6 100644 --- a/scripts/fb_stopNewFeed.js +++ b/scripts/fb_stopNewFeed.js @@ -21,7 +21,7 @@ export default { changeLogs: { "2024-06-12": "init", }, - badges: [BADGES.new], + badges: [BADGES.hot], whiteList: ["https://www.facebook.com/*"], diff --git a/scripts/fb_toggleNewFeed.js b/scripts/fb_toggleNewFeed.js index 1d4b1684..c0300177 100644 --- a/scripts/fb_toggleNewFeed.js +++ b/scripts/fb_toggleNewFeed.js @@ -1,5 +1,4 @@ import { UfsGlobal } from "./content-scripts/ufs_global.js"; -import { BADGES } from "./helpers/badge.js"; export default { icon: '', @@ -11,7 +10,6 @@ export default { en: "Hide facebook new feed (home page) for better focus to work", vi: "Ẩn dòng thời gian facebook (trang chủ) để tập trung làm việc", }, - badges: [BADGES.hot], infoLink: "https://www.facebook.com/groups/j2team.community/posts/1919935575005220/", whiteList: ["https://*.facebook.com/*"], diff --git a/scripts/github_HTMLPreview.js b/scripts/github_HTMLPreview.js index f5dd9d21..b84bccb6 100644 --- a/scripts/github_HTMLPreview.js +++ b/scripts/github_HTMLPreview.js @@ -1,5 +1,3 @@ -import { BADGES } from "./helpers/badge.js"; - export default { icon: '', name: { @@ -10,7 +8,6 @@ export default { en: "Preview github's HTML file in any repo without download code.", vi: "Xem trước giao diện file HTML trên bất kỳ repo github nào mà không cần tải code về.", }, - badges: [BADGES.new], changeLogs: { "2024-06-14": "init", }, diff --git a/scripts/github_openRepoPages.js b/scripts/github_openRepoPages.js index 328e1ae7..ee0b892b 100644 --- a/scripts/github_openRepoPages.js +++ b/scripts/github_openRepoPages.js @@ -1,5 +1,4 @@ import { getRepoNameFromUrl } from "./github_goToAnyCommit.js"; -import { BADGES } from "./helpers/badge.js"; export default { icon: '', @@ -15,7 +14,6 @@ export default { username.github.io/repo
github.com/username/repo
`, }, - badges: [BADGES.new], changeLogs: { "2024-06-03": "init", }, diff --git a/scripts/guland_VIP.js b/scripts/guland_VIP.js index fa3172ba..5a71ed65 100644 --- a/scripts/guland_VIP.js +++ b/scripts/guland_VIP.js @@ -1,4 +1,5 @@ import { UfsGlobal } from "./content-scripts/ufs_global.js"; +import { BADGES } from "./helpers/badge.js"; export default { icon: "https://guland.vn/bds/img/apple-touch-icon.png", @@ -11,6 +12,7 @@ export default { vi: "Xem quy hoạch đất không bị làm phiền bởi popup vip tại Guland.vn", img: "/scripts/guland_VIP.png", }, + badges: [BADGES.new], infoLink: "https://guland.vn", changeLogs: { "2024-07-14": "init", diff --git a/scripts/insta_getFollowForOther.js b/scripts/insta_getFollowForOther.js index 17d5372c..25bb46b7 100644 --- a/scripts/insta_getFollowForOther.js +++ b/scripts/insta_getFollowForOther.js @@ -1,5 +1,4 @@ import { UfsGlobal } from "./content-scripts/ufs_global.js"; -import { BADGES } from "./helpers/badge.js"; export default { icon: '', @@ -11,7 +10,6 @@ export default { en: "Know about your (or your friends's) following / followers on instagram. Export to json file", vi: "Biết bạn bè của bạn (hoặc chính bạn) đang follow những ai / được ai follow trên instagram. Tải về file json", }, - badges: [BADGES.new], whiteList: ["https://www.instagram.com/*"], popupScript: { diff --git a/scripts/pip_anything.js b/scripts/pip_anything.js index cec9c341..a18c1b5e 100644 --- a/scripts/pip_anything.js +++ b/scripts/pip_anything.js @@ -1,4 +1,4 @@ -import { UfsGlobal } from "./content-scripts/ufs_global.js"; +import { BADGES } from "./helpers/badge.js"; export default { icon: '', @@ -11,9 +11,9 @@ export default { vi: "Xem bất kỳ giao diện nào trong cửa sổ nổi (Picture in picture), không chỉ mỗi video, click chọn phần tử từ website để xem trong cửa sổ nổi.", img: "", }, - + badges: [BADGES.beta], changeLogs: { - date: "description", + "2024-07-20": "init", }, popupScript: { diff --git a/scripts/screenshotVisiblePage.js b/scripts/screenshotVisiblePage.js index 55788a42..8ec9400d 100644 --- a/scripts/screenshotVisiblePage.js +++ b/scripts/screenshotVisiblePage.js @@ -1,5 +1,4 @@ import { UfsGlobal } from "./content-scripts/ufs_global.js"; -import { BADGES } from "./helpers/badge.js"; import { attachDebugger, detachDebugger, @@ -47,9 +46,6 @@ export default { }, }, ], - - badges: [BADGES.new], - changeLogs: { "2024-06-10": "init", }, diff --git a/scripts/showImageOnHoverLink.js b/scripts/showImageOnHoverLink.js index 9f8d7482..01208651 100644 --- a/scripts/showImageOnHoverLink.js +++ b/scripts/showImageOnHoverLink.js @@ -1,5 +1,3 @@ -import { BADGES } from "./helpers/badge.js"; - export default { icon: '', name: { @@ -10,7 +8,6 @@ export default { en: "Show preview image when you hover mouse over an image link", vi: "Xem trước hình ảnh khi bạn đưa chuột qua link hình ảnh", }, - badges: [BADGES.new], changeLogs: { "2024-06-19": "init", }, diff --git a/scripts/youtube_changeCountry.js b/scripts/youtube_changeCountry.js index a1cdfdf5..ce126cd4 100644 --- a/scripts/youtube_changeCountry.js +++ b/scripts/youtube_changeCountry.js @@ -1,5 +1,3 @@ -import { BADGES } from "./helpers/badge.js"; - export default { icon: '', name: { @@ -10,7 +8,6 @@ export default { en: "Change youtube country to view content in other country", vi: "Đổi quốc gia youtube để xem nội dung youtube bên các nước khác", }, - badges: [BADGES.new], changeLogs: { "2024-07-07": "init", }, diff --git a/scripts/youtube_getVideoCaption.js b/scripts/youtube_getVideoCaption.js index cac5d971..0b6cb8e2 100644 --- a/scripts/youtube_getVideoCaption.js +++ b/scripts/youtube_getVideoCaption.js @@ -1,6 +1,5 @@ import { UfsGlobal } from "./content-scripts/ufs_global.js"; import { runScriptInCurrentTab } from "./helpers/utils.js"; -import { BADGES } from "./helpers/badge.js"; export default { icon: '', @@ -13,7 +12,6 @@ export default { vi: "- Bấm để tải về tất cả phụ đề của video youtube đang xem
- Bật tự chạy để hiển thị phụ đề thời gian thực", img: "/scripts/youtube_getVideoCaption.png", }, - badges: [BADGES.new], changeLogs: { "2024-07-04": "init", }, diff --git a/scripts/youtube_getVideoThumbnail.js b/scripts/youtube_getVideoThumbnail.js index 65b63df6..38393b67 100644 --- a/scripts/youtube_getVideoThumbnail.js +++ b/scripts/youtube_getVideoThumbnail.js @@ -1,4 +1,3 @@ -import { BADGES } from "./helpers/badge.js"; import { getIdFromYoutubeURL } from "./youtube_downloadVideo.js"; export default { @@ -11,7 +10,6 @@ export default { en: "Get largest thumbnail of playing youtube video", vi: "Tải về hình thumbnail độ phân giải lớn nhất của video youtube đang xem", }, - badges: [BADGES.new], changeLogs: { "2024-07-04": "init", }, From ce912f170877f219c4be3a3b603cdc35207909e3 Mon Sep 17 00:00:00 2001 From: HoangTran <99.hoangtran@gmail.com> Date: Sun, 28 Jul 2024 01:48:58 +0700 Subject: [PATCH 45/47] update --- md/CHANGELOGS.md | 18 ++++++++++++++++++ scripts/tiktok_batchDownload.js | 1 + 2 files changed, 19 insertions(+) diff --git a/md/CHANGELOGS.md b/md/CHANGELOGS.md index b39999ae..e2cf4225 100644 --- a/md/CHANGELOGS.md +++ b/md/CHANGELOGS.md @@ -1,5 +1,23 @@ ## Change logs +

+ v1.7 - 28/07/2024 + +- Tiktok + - NEW batch download [source](/scripts/tiktok_batchDownload.js) + +- Google + - Download private video [source](/scripts/ggdrive_downloadVideo.js) + +- More + - Magnify image: support config min-size for hover [source](/scripts/magnify_image.js) + - Fix Medium VIP [source](/scripts/medium_readFullArticle.js) + - Guland VIP [source](/scripts/guland_VIP.js) + - Picture in picture anything (beta) [source](/scripts/pip_anything.js) + - Optimize performance UfsGlobal [source](/scripts/content-scripts/ufs_global.js) + +
+
v1.69 - 14/07/2024 diff --git a/scripts/tiktok_batchDownload.js b/scripts/tiktok_batchDownload.js index 9e078cc9..bfb6dcdb 100644 --- a/scripts/tiktok_batchDownload.js +++ b/scripts/tiktok_batchDownload.js @@ -438,6 +438,7 @@ export default { const dir = await UfsGlobal.Utils.chooseFolderToDownload(folderName); onProgressItem?.(0, data.length); + UfsGlobal.Extension.trackEvent("tiktok_batchDownload-download"); for (let i = 0; i < data.length; ++i) { try { onProgressItem?.(i + 1, data.length); From 821b9b70a593070caca017bc8f5557fbba1c74dd Mon Sep 17 00:00:00 2001 From: HoangTran <99.hoangtran@gmail.com> Date: Sun, 28 Jul 2024 01:49:46 +0700 Subject: [PATCH 46/47] fix typo --- popup/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/popup/index.js b/popup/index.js index b12838cd..d02ace66 100644 --- a/popup/index.js +++ b/popup/index.js @@ -705,8 +705,8 @@ function initSettings() { smoothScrollRow.innerHTML = `
${t({ From bce8f03dc81cdcabd27daca19df3fc5678784e17 Mon Sep 17 00:00:00 2001 From: HoangTran <99.hoangtran@gmail.com> Date: Sun, 28 Jul 2024 01:50:06 +0700 Subject: [PATCH 47/47] up ver --- manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.json b/manifest.json index e698f238..7a70224c 100644 --- a/manifest.json +++ b/manifest.json @@ -4,7 +4,7 @@ "name": "Useful Scripts", "description": "Scripts that can make your life faster and better", "homepage_url": "https://github.com/HoangTran0410/useful-script", - "version": "1.69", + "version": "1.7", "icons": { "16": "./assets/icon16.png", "32": "./assets/icon32.png",