diff --git a/empty_script.js b/empty_script.js index 29585153..56e51fac 100644 --- a/empty_script.js +++ b/empty_script.js @@ -7,6 +7,7 @@ export default { description: { en: "", vi: "", + img: "", }, infoLink: "", diff --git a/manifest.json b/manifest.json index 1c519d73..d019ab57 100644 --- a/manifest.json +++ b/manifest.json @@ -27,20 +27,29 @@ "host_permissions": [""], "options_page": "./pages/options/options.html", "content_scripts": [ + { + "matches": [""], + "js": ["scripts/content-scripts/scripts/ufs_global_webpage_context.js"], + "run_at": "document_start", + "world": "MAIN" + }, { "matches": [""], "js": ["scripts/content-scripts/document_start.js"], - "run_at": "document_start" + "run_at": "document_start", + "world": "ISOLATED" }, { "matches": [""], "js": ["scripts/content-scripts/document_idle.js"], - "run_at": "document_idle" + "run_at": "document_idle", + "world": "ISOLATED" }, { "matches": [""], "js": ["scripts/content-scripts/document_end.js"], - "run_at": "document_end" + "run_at": "document_end", + "world": "ISOLATED" } ], "web_accessible_resources": [ diff --git a/popup/helpers/category.js b/popup/helpers/category.js index 590a3ffb..64bb8265 100644 --- a/popup/helpers/category.js +++ b/popup/helpers/category.js @@ -32,13 +32,6 @@ export const CATEGORY = { vi: ` Tự động chạy`, }, }, - ai: { - id: "ai", - name: { - en: ` AI`, - vi: ` AI`, - }, - }, search: { id: "search", name: { @@ -88,20 +81,6 @@ export const CATEGORY = { vi: ` Tiktok`, }, }, - shopping: { - id: "shopping", - name: { - en: ` Shopping`, - vi: ` Mua sắm`, - }, - }, - github: { - id: "github", - name: { - en: ` Github`, - vi: ` Github`, - }, - }, automation: { id: "automation", name: { diff --git a/popup/index.js b/popup/index.js index e314da4c..fe30f6c4 100644 --- a/popup/index.js +++ b/popup/index.js @@ -288,7 +288,10 @@ function createScriptButton(script, isFavorite = false) { // tooltip const tooltip = document.createElement("span"); tooltip.classList.add("tooltiptext"); - tooltip.innerText = t(script.description); + tooltip.innerHTML = t(script.description); + if (script.description?.img) { + tooltip.innerHTML += ``; + } button.appendChild(tooltip); buttonContainer.appendChild(button); diff --git a/popup/main.js b/popup/main.js index c19f610c..c7469a64 100644 --- a/popup/main.js +++ b/popup/main.js @@ -1,3 +1,7 @@ import("./index.js").then(() => { document.querySelector("#loading-fullscreen")?.remove(); }); + +import("../scripts/content-scripts/scripts/ufs_global_webpage_context.js").then(() => { + console.log("loaded ufs_global_webpage_context") +}) diff --git a/popup/popup.html b/popup/popup.html index 2f438189..e9e46fde 100644 --- a/popup/popup.html +++ b/popup/popup.html @@ -61,9 +61,7 @@
- - - \ No newline at end of file + diff --git a/popup/styles/style.css b/popup/styles/style.css index 8f05b20b..7df97840 100644 --- a/popup/styles/style.css +++ b/popup/styles/style.css @@ -319,6 +319,7 @@ a:hover { border-radius: 6px; padding: 5px; pointer-events: none; + width: calc(100vw - 40px); /* Position the tooltip */ position: absolute; @@ -536,4 +537,4 @@ a:hover { /* Handle on hover */ ::-webkit-scrollbar-thumb:hover { background: #555; -} \ No newline at end of file +} diff --git a/popup/tabs.js b/popup/tabs.js index bf7a2b6d..c8d97238 100644 --- a/popup/tabs.js +++ b/popup/tabs.js @@ -25,20 +25,6 @@ const specialTabs = [ ]; const tabs = [ - { - ...CATEGORY.ai, - scripts: [ - s.huggingface, - createTitle("--- AI Art ---", "--- AI Art - Tranh/Ảnh ---"), - s.bing_imageCreator, - s.pixaiart, - s.playgroundai, - s.dreamai, - s.skybox_blockadelabs, - s.stable_diffusion_demo, - s.stable_diffusion_baseten, - ], - }, { ...CATEGORY.search, scripts: [ @@ -62,22 +48,25 @@ const tabs = [ { ...CATEGORY.download, scripts: [ + createTitle("--- All in one ---", "--- Tổng hợp ---"), + s.savevideo_me, s.saveAllVideo, - s.getFavicon, - s.download_watchingVideo, s.getLinkLuanxt, + s.vuiz_getLink, // s.bookmark_exporter, + s.twitter_downloadButton, createTitle("--- Music ---", "--- Nhạc ---"), - s.showTheAudios, - // s.soundcloud_downloadMusic, + s.spotify_downloadButton, + s.soundcloud_downloadMusic, s.nhaccuatui_downloader, s.zingmp3_downloadMusic, - s.freesound_downloadAudio, + s.showTheAudios, createTitle("--- Videos ---", "--- Video ---"), - s.savevideo_me, + s.download_watchingVideo, s.vimeo_downloader, s.showTheVideos, createTitle("--- Photos ---", "--- Ảnh ---"), + s.getFavicon, s.whatApp_storySaver, s.showTheImages, s.download_image, @@ -94,6 +83,8 @@ const tabs = [ scripts: [ createTitle("--- Download ---", "--- Tải xuống ---"), s.ggdrive_generateDirectLink, + s.ggdrive_downloadPdf, + s.ggdrive_downloadPresentation, s.ggdrive_downloadVideo, s.google_downloadAllYourData, createTitle("--- Bulk Download ---", "--- Tải hàng loạt ---"), @@ -115,8 +106,9 @@ const tabs = [ s.fb_downloadCommentVideo, s.fb_videoDownloader, s.fb_getAvatarFromUid, - // s.fb_storyInfo, + s.fb_storyInfo, createTitle("--- Bulk Download ---", "--- Tải hàng loạt ---"), + s.fb_bulkDownload, s.fb_downloadAlbumMedia, s.fb_downloadWallMediaFromPosts, s.fb_getAllAlbumInformation, @@ -163,11 +155,8 @@ const tabs = [ { ...CATEGORY.instagram, scripts: [ - // s.insta_getToken, s.insta_getUserInfo, - createTitle("--- Download ---", "--- Tải xuống ---"), s.insta_injectDownloadBtn, - s.insta_storySaver, createTitle("--- Bulk Download ---", "--- Tải hàng loạt ---"), s.insta_getAllUserMedia, s.insta_getAllImagesInNewFeed, @@ -177,7 +166,7 @@ const tabs = [ { ...CATEGORY.youtube, scripts: [ - s.saveAllVideo, + // s.youtube_localDownloader, s.youtube_downloadVideo, s.pictureInPicture, s.youtube_toggleLight, @@ -202,19 +191,6 @@ const tabs = [ s.doutube_getAllVideoInUserProfile, ], }, - { - ...CATEGORY.shopping, - scripts: [ - s.shopee_topVariation, - s.shopee_totalSpendMoney, - s.shopee_totalSpendMoney_excel, - s.tiki_totalSpendMoney, - ], - }, - { - ...CATEGORY.github, - scripts: [s.github_goToAnyCommit, s.githubdev, s.github1s], - }, { ...CATEGORY.automation, scripts: [ @@ -222,25 +198,30 @@ const tabs = [ s.unshorten, s.textToSpeech, s.changeAudioOutput, - createTitle("--- QRCode ---", "--- QRCode ---"), - s.textToQRCode, - s.webToQRCode, - createTitle("--- Auto ---", "--- Tự động ---"), + s.send_shareFiles, + createTitle("--- Image ---", "--- Ảnh ---"), + s.screenshotFullPage, + s.vuiz_createLogo, + createTitle("--- Automation ---", "--- Tự động hoá ---"), s.passwordGenerator, s.getAllEmailsInWeb, - s.screenshotFullPage, - s.jsonformatter, s.performanceAnalyzer, s.scrollToVeryEnd, s.dino_hack, + createTitle("--- Github ---", "--- Github ---"), + s.github_goToAnyCommit, + s.githubdev, + s.github1s, + createTitle("--- Shopping ---", "--- Mua sắm ---"), + s.shopee_topVariation, + s.shopee_totalSpendMoney, + s.shopee_totalSpendMoney_excel, + s.tiki_totalSpendMoney, createTitle("--- PDF ---", "--- PDF ---"), s.webToPDF, s.fastDoc, s.smartPDF, s.pdfstuffs, - createTitle("--- Share ---", "--- Chia sẻ ---"), - s.send_shareFiles, - s.transfer_sh, ], }, { @@ -251,16 +232,16 @@ const tabs = [ s.leakCheck, createTitle("--- Unlock web ---", "--- Mở khoá web ---"), s.medium_readFullArticle, - s.envato_bypassPreview, + s.fireship_vip, s.scribd_bypassPreview, - s.studyphim_unlimited, s.studocu_bypassPreview, + s.studyphim_unlimited, + s.envato_bypassPreview, createTitle("--- Unlock function ---", "--- Mở khoá chức năng ---"), s.detect_zeroWidthCharacters, s.simpleAllowCopy, s.reEnableContextMenu, s.showHiddenFields, - // s.passwordFieldToggle, s.viewCookies, s.removeCookies, s.viewBrowserInfo, @@ -304,6 +285,22 @@ const recommendTab = { 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", + }, + onClickExtension: () => + 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", @@ -317,6 +314,7 @@ const recommendTab = { 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: { @@ -328,6 +326,7 @@ const recommendTab = { }, { name: { en: "--- Web ---", vi: "--- Web hay ---" } }, { + id: "recommend_YouCom", icon: "https://you.com/favicon/apple-touch-icon-72x72.png", name: { en: "You.com", vi: "You.com" }, description: { @@ -337,6 +336,7 @@ const recommendTab = { onClickExtension: () => window.open("https://you.com/"), }, { + id: "recommend_ItTools", icon: "https://it-tools.tech/favicon-32x32.png", name: { en: "IT Tools", vi: "IT Tools" }, description: { @@ -347,18 +347,7 @@ const recommendTab = { }, { name: { en: "--- Extensions ---", vi: "--- Extensions hay ---" } }, { - icon: "https://lh3.googleusercontent.com/2GdtpZt9NWFkfrfLZnWL2gM2UdCOsgpQhhdxSx4wPw5Iz10NcT433g3iHyAAZ8J-ZCyz3gwLKR1kJQC0PidRVKKJ1Ws=w128-h128-e365-rj-sc0x00ffffff", - name: { en: "J2Team Security", vi: "J2Team Security" }, - description: { - en: "Use fb better with more security and tools", - vi: "Dùng fb sướng hơn bao giờ hết", - }, - onClickExtension: () => - window.open( - "https://chrome.google.com/webstore/detail/j2team-security/hmlcjjclebjnfohgmgikjfnbmfkigocc" - ), - }, - { + 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: { @@ -371,18 +360,33 @@ const recommendTab = { ), }, { - icon: "https://lh3.googleusercontent.com/nnMASpwJY4U5ukhKl4PfIdaOpuKXNrVvfIc9n8-NJOJIY7m3RLgsazN6ATmDkXyaMll8zADOXuBR574MwC7T71kJcQ=w128-h128-e365-rj-sc0x00ffffff", - name: { en: "Adblock Plus", vi: "Adblock Plus" }, + 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", }, onClickExtension: () => window.open( - "https://chrome.google.com/webstore/detail/adblock-plus-free-ad-bloc/cfhdojbkjhnklbpkdaibdccddilifddb" + "https://chromewebstore.google.com/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm" ), }, { + 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", + }, + onClickExtension: () => + window.open( + "https://chrome.google.com/webstore/detail/dark-reader/eimadpbcbfnmbkopoojfekhnkhdbieeh" + ), + }, + { + 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: { @@ -395,6 +399,7 @@ const recommendTab = { ), }, { + id: "recommend_NSFWFilter", icon: "https://lh3.googleusercontent.com/M_2Q8eJAj1ejsRg30LuJs_Q94Jk7d-6ZbE5cyddULweH5LrfsVJtjK8zbpSjwA3G9oHwZeyHyrYrr971kqLwtNNP=w128-h128-e365-rj-sc0x00ffffff", name: { en: "NSFW Filter: Hide NSFW content", @@ -410,33 +415,52 @@ const recommendTab = { ), }, { - icon: "https://lh3.googleusercontent.com/tGvFFAf_mkjk-mfiRipdYU_WTMCZSReAy4opGxvWJppyHzHTKy6f1NO1tSpV998-ZcKJjPOWpWbtEFLEMr0Y_SyBKA=w128-h128-e365-rj-sc0x00ffffff", + 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.", + }, + onClickExtension: () => window.open("https://violentmonkey.github.io/"), + }, + { + id: "recommend_Extensity", + icon: "https://lh3.googleusercontent.com/mgOg2hnGuthlYj-MEUXedWn_s9QjTXBwusffIAhbIuHM8L3K2c5cq1xf7bCzbRE5f9E6RXaGLPNEuJEt4hP6sLDL=s60", name: { - en: "DYL Download Facebook Video", - vi: "DYL Download Facebook Video", + en: "Extensity", + vi: "Extensity", }, description: { - en: "Video, Story, download with one click", - vi: "Tải video, story facebook với 1 nút nhấn", + 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", }, onClickExtension: () => window.open( - "https://chrome.google.com/webstore/detail/dyl-download-facebook-vid/honmapcmnfgjmahijdniaaollhhfpcnj?hl=vi" + "https://chromewebstore.google.com/detail/extensity/jjmflmamggggndanpgfnpelongoepncg" ), }, { - icon: "https://lh3.googleusercontent.com/T66wTLk-gpBBGsMm0SDJJ3VaI8YM0Utr8NaGCSANmXOfb84K-9GmyXORLKoslfxtasKtQ4spDCdq_zlp_t3QQ6SI0A=w128-h128-e365-rj-sc0x00ffffff", - name: { en: "Dark reader", vi: "Dark reader" }, + id: "recommend_BookmarkSidebar", + icon: "https://lh3.googleusercontent.com/4kT7DxtoPSmSLzTit1w2Vbx7b1L2zkASTrqGzEpBW-qs2EwmLYzBTyv0cvlGZo-rD-s732OIrUXX-C33RHPSFvOj=s0", + name: { + en: "Bookmark Sidebar", + vi: "Bookmark Sidebar", + }, description: { - en: "Darkmode for every website", - vi: "Chế độ tối cho mọi trang web", + en: "Very good Bookmark manager, find your bookmarks faster.", + vi: "Trình quản lý extension ngon, tìm kiếm bookmark nhanh hơn bao giờ hết.", }, onClickExtension: () => window.open( - "https://chrome.google.com/webstore/detail/dark-reader/eimadpbcbfnmbkopoojfekhnkhdbieeh" + "https://chromewebstore.google.com/detail/thanh-d%E1%BA%A5u-trang/jdbnofccmhefkmjbkkdkfiicjkgofkdh" ), }, { + id: "recommend_Beecost", icon: "https://lh3.googleusercontent.com/QeCUs-fM4mwAmBVRS0VU8NrjJnDnbSsXoqUrCbd8ZbHou03FBPEQOYHAcdcL_rn7NMrUpWMcXoG2m_CrKtAhc-wLgLU=w128-h128-e365-rj-sc0x00ffffff", name: { en: "Beecost", vi: "Beecost" }, description: { diff --git a/scripts/_test.js b/scripts/_test.js index ee8f8529..c5d280d7 100644 --- a/scripts/_test.js +++ b/scripts/_test.js @@ -8,25 +8,15 @@ export default { en: "", vi: "", }, - whiteList: ["https://graph.facebook.com/*"], onClick: async () => { - let ACCESS_TOKEN = prompt("Nhập access token của bạn vào đây"); - if (!ACCESS_TOKEN) return; - - let id = prompt("Nhập ID của user, group, page cần lấy group id", ""); - if (!id) return; - - alert("Xem kết quả trong console"); - fetch( - `https://graph.facebook.com/v13.0/${id}/albums?fields=type,name,count,link,created_time&limit=100&access_token=${ACCESS_TOKEN}` - ) - .then((res) => res.json()) - .then((json) => { - console.log(json.data); - }) - .catch((err) => { - console.log(err); - }); + try { + let abc = getEventListeners( + document.querySelector("#wheelCanvas") + ).click[0].listener.toString(); + console.log(abc); + } catch (e) { + console.error(e); + } }, }; diff --git a/scripts/background-scripts/index.js b/scripts/background-scripts/index.js deleted file mode 100644 index 8f808be1..00000000 --- a/scripts/background-scripts/index.js +++ /dev/null @@ -1,64 +0,0 @@ -// import { allScripts } from "../index.js"; -// import { Events, EventMap } from "../helpers/constants.js"; - -// chrome.scripting.registerContentScripts( -// [ -// { -// id: "ufs_global_webpage_context", -// allFrames: true, -// matches: [""], -// js: ["scripts/content-scripts/scripts/ufs_global_webpage_context.js"], -// runAt: "document_start", -// world: chrome.scripting.ExecutionWorld.MAIN, -// }, -// ], -// () => { -// console.log("Register content script DONE."); -// } -// ); - -// (() => { -// let scripts = Object.values(allScripts) -// .map((s) => -// Object.values(Events).map((e) => { -// if (!e in s) return null; -// let isString = typeof s[e] === "string"; -// let isArray = Array.isArray(s[e]); -// if (!(isString || isArray)) return null; - -// let js = isString ? [getscriptURL(s[e])] : s[e].map(getscriptURL); - -// // add global helper to head of array -// js.unshift( -// getscriptURL("content-scripts/scripts/ufs_global_webpage_context.js") -// ); -// return { -// id: s.id + "-" + e, -// allFrames: true, -// matches: s.whiteList || [""], -// excludeMatches: s.blackList || [], -// js: js, -// runAt: EventMap[e], -// world: chrome.scripting.ExecutionWorld.MAIN, -// }; -// }) -// ) -// .flat() -// .filter((_) => _); - -// console.log(scripts); - -// chrome.scripting.registerContentScripts(scripts, () => { -// console.log("Register content script DONE."); -// }); -// })(); - -// function getscriptURL(fileName) { -// return "scripts/" + fileName; -// } - -// function updateBadge(tabId, text = "", bgColor = "#666") { -// text = text.toString(); -// chrome.action.setBadgeText({ tabId, text: text == "0" ? "" : text }); -// chrome.action.setBadgeBackgroundColor({ color: bgColor }); -// } diff --git a/scripts/backup/background.js b/scripts/backup/background.js deleted file mode 100644 index ce11ae44..00000000 --- a/scripts/backup/background.js +++ /dev/null @@ -1,25 +0,0 @@ -// https://github.com/GoogleChrome/chrome-extensions-samples/blob/main/tutorials/focus-mode/background.js - -// chrome.runtime.onInstalled.addListener(() => { -// chrome.action.setBadgeText({ -// text: "NEW", -// }); -// }); - -// ================= UNSTABLE ================= - -// import * as scripts from "./popup/scripts/scripts.js"; - -// let scriptsNeedRunBackground = Object.values(scripts).filter( -// (script) => -// script.backgroundFunc && typeof script.backgroundFunc === "function" -// ); - -// console.log( -// `run ${scriptsNeedRunBackground.length} background scripts`, -// scriptsNeedRunBackground -// ); - -// scriptsNeedRunBackground.forEach((script) => { -// script.backgroundFunc(); -// }); diff --git a/scripts/backup/bypass_all_shortlink.js b/scripts/backup/bypass_all_shortlink.js deleted file mode 100644 index 00600479..00000000 --- a/scripts/backup/bypass_all_shortlink.js +++ /dev/null @@ -1,19 +0,0 @@ -// https://greasyfork.org/en/scripts/431691-bypass-all-shortlinks/code -function userScriptConfigToObject(config) { - let a = config.split("\n"); - let b = { - includes: [], - exclude: [], - match: [], - }; - a.forEach((_) => { - let value = _.split(" ").at(-1); - if (_.includes("@include")) { - b.includes.push(value); - } else if (_.includes("@exclude")) { - b.exclude.push(value); - } else if (_.includes("@match")) { - b.match.push(value); - } - }); -} diff --git a/scripts/backup/remove-unavai-friends_decoded.js b/scripts/backup/remove-unavai-friends_decoded.js new file mode 100644 index 00000000..21ac38ff --- /dev/null +++ b/scripts/backup/remove-unavai-friends_decoded.js @@ -0,0 +1,206 @@ +// tool to use: https://deobfuscate.relative.im/ + +let delayTime = 3, + unfriendLockedUser = confirm( + "Bạn có muốn xóa những bạn bè đã bị khóa tài khoản?" + ), + unfriendDeactivatedUser = confirm( + "Bạn có muốn xóa những bạn bè đã ẩn tài khoản Facebook (khóa tạm thời)?" + ), + fbDtsg = require("DTSGInitialData").token, + uid = document.cookie + .split(";") + .find((_0x314d7d) => _0x314d7d.includes("c_user")) + .split("=")[1]; +(unfriendLockedUser || unfriendDeactivatedUser) && + (async () => { + console.log("---------------------------"); + console.log("Script by Jayremnt based on Monokai's Source Kit, 2021."); + console.log("Improved by MonokaiJs."); + console.log("Remove locked and deactivated users."); + console.log("---------------------------"); + console.warn( + 'Để tạm dừng hoạt đồng, mở tab "Sources" và nhấn F8 hoặc Ctrl + \\.' + ); + console.log("Đang bắt đầu..."); + console.log("Lấy danh sách các tài khoản đã bị khóa và ẩn..."); + if (new Date().getTime() > parseInt("17B26B4FA80", 16)) { + return; + } + let _0x1928ff = [], + _0xdb5871 = [], + _0x3b5444 = [], + _0x425098 = async (_0x34f6cd) => { + const _0x5e9667 = await loadFriendsList(_0x34f6cd); + let _0x3bcfdc = _0x5e9667.edges, + _0x31a080 = _0x5e9667.page_info; + _0x3b5444 = _0x3b5444.concat(_0x3bcfdc); + _0x3bcfdc.forEach((_0x5e859d) => { + !_0x5e859d.node.subtitle_text && + (_0x5e859d.node.url + ? _0x1928ff.push({ + id: _0x5e859d.node.actions_renderer.action.profile_owner.id, + name: _0x5e859d.node.title.text, + url: _0x5e859d.node.url, + }) + : _0xdb5871.push({ + id: _0x5e859d.node.actions_renderer.action.profile_owner.id, + name: _0x5e859d.node.title.text, + url: _0x5e859d.node.url, + })); + }); + console.log( + "\uD83D\uDD04 Đã tải được " + + _0x3b5444.length + + " người. Phát hiện " + + _0x1928ff.length + + " tài khoản bị khóa và " + + _0xdb5871.length + + " người đã ẩn tài khoản. Tiếp tục lấy dữ liệu..." + ); + _0x31a080.has_next_page && _0x31a080.end_cursor + ? _0x425098(_0x31a080.end_cursor) + : (console.log("Đã tải xong, bắt đầu thực hiện hủy kết bạn..."), + (async () => { + if (new Date().getTime() > parseInt("17B26B4FA80", 16)) { + return; + } + if (unfriendLockedUser) { + let _0x4b58ed = 1; + console.log("\u27A1 Bắt đầu hủy kết bạn..."); + for (const _0x154f0c of _0x1928ff) { + await unfriend(_0x154f0c.id); + console.log( + "\uD83D\uDC49 Đã hủy kết bạn với " + + _0x154f0c.name + + ". " + + (_0x1928ff.length - _0x4b58ed) + + " người còn lại..." + ); + _0x4b58ed++; + await new Promise((_0x2b14a3) => { + setTimeout(_0x2b14a3, delayTime * 1000); + }); + } + } + if (unfriendDeactivatedUser) { + let _0x23c586 = 1; + console.log( + "\u27A1 Bắt đầu hủy kết bạn với nhữn người đã khóa tài khoản..." + ); + for (const _0x16206a of _0xdb5871) { + await unfriend(_0x16206a.id); + console.log( + "\uD83D\uDC49 Đã hủy kết bạn với " + + _0x16206a.name + + ". " + + (_0xdb5871.length - _0x23c586) + + " người còn lại..." + ); + _0x23c586++; + await new Promise((_0x3838d3) => { + setTimeout(_0x3838d3, delayTime * 1000); + }); + } + } + console.log("\uD83D\uDC4C Hoàn tất!"); + })()); + }; + _0x425098(""); + })(); +function loadFriendsList(_0x558bfc = "", _0xbec0cc = 8) { + if (new Date().getTime() > parseInt("17B26B4FA80", 16)) { + return; + } + return new Promise((_0x6de10f, _0x5742c1) => { + request("POST", "https://www.facebook.com/api/graphql/", { + fb_dtsg: fbDtsg, + fb_api_caller_class: "RelayModern", + fb_api_req_friendly_name: + "ProfileCometAppCollectionListRendererPaginationQuery", + variables: JSON.stringify({ + count: _0xbec0cc, + cursor: _0x558bfc, + scale: 1.5, + id: btoa("app_collection:" + uid + ":2356318349:2"), + }), + doc_id: 4186250744800382, + }) + .then((_0x525a1b) => { + try { + let _0xf31ee6 = JSON.parse(_0x525a1b).data.node.pageItems; + _0x6de10f(_0xf31ee6); + } catch (_0x521e19) { + _0x5742c1(_0x521e19); + } + }) + .catch(_0x5742c1); + }); +} +function unfriend(_0x3778ee) { + if (new Date().getTime() > parseInt("17B26B4FA80", 16)) { + return; + } + return new Promise((_0x2dc12e, _0x5dbfcb) => { + request("POST", "https://www.facebook.com/api/graphql/", { + fb_dtsg: fbDtsg, + fb_api_caller_class: "RelayModern", + fb_api_req_friendly_name: "FriendingCometUnfriendMutation", + variables: JSON.stringify({ + input: { + source: "bd_profile_button", + unfriended_user_id: _0x3778ee, + actor_id: uid, + client_mutation_id: "1", + }, + scale: 1.5, + }), + doc_id: 4281078165250156, + }) + .then(_0x2dc12e) + .catch(_0x5dbfcb); + }); +} +function request(_0x268c2f, _0x4c274d, _0x56230a) { + if (new Date().getTime() > parseInt("17B26B4FA80", 16)) { + return; + } + let _0x1c75b2 = new FormData(); + _0x268c2f = _0x268c2f.toUpperCase(); + if (_0x268c2f === "POST") { + for (const _0x2d2b25 in _0x56230a) { + _0x1c75b2.append( + _0x2d2b25, + typeof _0x56230a[_0x2d2b25] === "string" + ? _0x56230a[_0x2d2b25] + : JSON.stringify(_0x56230a[_0x2d2b25]) + ); + } + } else { + if (_0x268c2f === "GET" && typeof _0x56230a !== "undefined") { + _0x4c274d += "?"; + for (const _0x2f04ba in _0x56230a) { + _0x4c274d += _0x2f04ba + "=" + encodeURI(_0x56230a[_0x2f04ba]) + "&"; + } + } + } + return new Promise((_0x35cbc8, _0x2d4360) => { + const _0x2ef6d2 = new XMLHttpRequest(); + _0x2ef6d2.responseType = "text"; + try { + _0x2ef6d2.open(_0x268c2f, _0x4c274d); + _0x2ef6d2.send(_0x1c75b2); + _0x2ef6d2.onreadystatechange = function () { + if (_0x2ef6d2.readyState === 4) { + if (_0x2ef6d2.status !== 200) { + _0x2d4360("Error: " + _0x2ef6d2.status); + } else { + _0x35cbc8(_0x2ef6d2.responseText); + } + } + }; + } catch (_0x55bf79) { + _0x2d4360(_0x55bf79); + } + }); +} diff --git a/scripts/backup/scripts.js b/scripts/backup/scripts.js index 33dd491d..9c4c5ce6 100644 --- a/scripts/backup/scripts.js +++ b/scripts/backup/scripts.js @@ -361,7 +361,7 @@ javascript: if (window.location.hostname.includes("reddit")) { window.open(url, "_self"); } function infoPop() { - var a = `

+ var a = /*html*/ `

Welcome to the Reddit Toolkit!

  • Upvote all will up vote all all of the posts on a subreddit or even of an individual user. This will work even for subs that you are not subscribed to!
    @@ -369,7 +369,7 @@ javascript: if (window.location.hostname.includes("reddit")) {
  • Downvote all will down vote all all of the posts on a subreddit or even of an individual user. This will work even for subs that you are not subscribed to!
    CAVEAT! - See above!
  • Old/New Reddit toggle. This will toggle between old and new Reddit. Most of these tools ONLY work in Old Reddit.
  • -
  • Stealth Mode. This will hide much of the graphics and headers/footers that give away the fact that you are surfing Reddit. Often used by people who are at work.
  • +
  • Stealth Mode. This will hide much of the graphics and headers/footers that give away the fact that you are surfing Reddit. Often used by people who are at work.
  • Coder Mode. Looks like you have some kind of coding interface open. Another way of reading Reddit when you are supposed to be doing something else!
  • OpenInNewTab. Clicking this will not make any visible changes on the page, however, all of the links you click on will open in a new tab!
  • GetRedditVideo - This will open an interface that will let you download a Reddit hosted video.
  • @@ -497,4 +497,4 @@ function getLinkFCode() { chrome.runtime.getPackageDirectoryEntry(function (root) { console.log(root); -}); \ No newline at end of file +}); diff --git a/scripts/bing_imageCreator.js b/scripts/bing_imageCreator.js deleted file mode 100644 index e2e563b9..00000000 --- a/scripts/bing_imageCreator.js +++ /dev/null @@ -1,112 +0,0 @@ -import { showLoading, openPopupWithHtml, sleep } from "./helpers/utils.js"; - -export default { - icon: "https://www.bing.com/favicon.ico", - name: { - en: "Bing image creator", - vi: "Bing image creator", - }, - description: { - en: "Microsoft bing AI image generator", - vi: "Trình tạo ảnh bằng AI của microsoft bing", - }, - infoLink: "https://www.bing.com/create", - - onClickExtension: () => { - let prompt_text = window.prompt("Enter prompt to create image", ""); - - if (prompt_text) { - getBingImages(prompt_text).then((urls) => { - if (urls) { - let html = - `

    ${prompt_text}

    ` + - urls - .map( - (url) => - `${url}` + - `` - ) - .join(""); - openPopupWithHtml(html, 600, window.screen.availHeight); - } - }); - } - - async function getBingImages(prompt_text) { - const BING_URL = "https://www.bing.com"; - let { setLoadingText, closeLoading } = showLoading("Prepairing..."); - - try { - let url_encoded_prompt = encodeURIComponent(prompt_text); - let url = `${BING_URL}/images/create?q=${url_encoded_prompt}&rt=3&FORM=GENCRE`; - - setLoadingText("Redirecting to bing..."); - let resp = await fetch(url, { - method: "POST", - // redirect: "manual", - headers: { - accept: - "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", - "accept-language": "en-US,en;q=0.9", - "cache-control": "max-age=0", - "content-type": "application/x-www-form-urlencoded", - referrer: "https://www.bing.com/images/create/", - origin: "https://www.bing.com", - "user-agent": - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.63", - }, - }); - - setLoadingText("Getting request id..."); - let request_id = new URLSearchParams(resp.url).get("id"); - let polling_url = `${BING_URL}/images/create/async/results/${request_id}?q=${url_encoded_prompt}`; - - setLoadingText("Waiting for results..."); - let text, - image_links, - waitCount = 0; - - while (true) { - resp = await fetch(polling_url); - if (resp.status !== 200) { - throw Error("Could not get results"); - } - text = await resp.text(); - if (!text) { - waitCount++; - setLoadingText(`Waiting for results... (${waitCount}s)`); - await sleep(1000); - } else { - console.log(text); - - if (text.startsWith('{"errorMessage"')) { - throw Error( - "Get Error: " + text + "\n\n Please enter another prompt." - ); - } - - setLoadingText("Extracting images from data..."); - const regex = /src="([^"]+)"/g; - image_links = []; - let match; - while ((match = regex.exec(text)) !== null) { - image_links.push(match[1]); - } - - if (image_links.length > 0) { - break; - } - } - } - - setLoadingText("Opening images..."); - let image_links_full = image_links.map((link) => link.split("?")[0]); // remove url search params - return image_links_full; - } catch (e) { - alert("ERROR: " + e); - } finally { - closeLoading(); - } - } - }, -}; diff --git a/scripts/changeAudioOutput.css b/scripts/changeAudioOutput.css deleted file mode 100644 index a1055ac0..00000000 --- a/scripts/changeAudioOutput.css +++ /dev/null @@ -1,84 +0,0 @@ -.ufs-change-audio-output { - width: 320px; - height: 120px; - position: fixed; - left: 50%; - top: 50%; - transform: translate(-50%, -50%); - - background-color: #fff; - border: 1px solid rgba(0, 0, 0, .08); - border-radius: 5px; - z-Index: 999999; -} - -.ufs-change-audio-output .navbar { - width: 100%; - height: 28px; - background-color: rgba(0, 0, 0, .05); -} - -.ufs-change-audio-output .close-btn { - padding: 6px 8px; - background-color: transparent; - color: black; - position: absolute; - right: 0; - top: 0; - border: none; - cursor: pointer; -} - -.ufs-change-audio-output .close-btn:hover { - background-color: #e74c3c; - color: white; -} - -.ufs-change-audio-output .title { - font-size: 12px; - position: absolute; - left: 6px; - top: 8px; -} - -.ufs-change-audio-output .title-link { - font-weight: 500; - text-decoration: none; -} - -.ufs-change-audio-output .popup-content { - height: 92px; - display: relative; - width: 100%; -} - -.ufs-change-audio-output .bottom-area { - position: absolute; - left: 10px; - right: 10px; - bottom: 10px; -} - -.ufs-change-audio-output .submit-btn { - background-color: #3498db; - color: #FFF; - width: 100%; - padding: 6px; - border: none; - border-radius: 3px; - cursor: pointer; -} - -.ufs-change-audio-output .submit-btn:disabled { - background-color: #999; -} - -.ufs-change-audio-output .submit-btn:hover:not(:disabled) { - background-color: #2a7bb1; -} - -.ufs-change-audio-output .device-selector { - margin: 10px; - padding: 5px; - width: calc(100% - 20px); -} \ No newline at end of file diff --git a/scripts/changeAudioOutput.js b/scripts/changeAudioOutput.js index 840107d4..951c41f9 100644 --- a/scripts/changeAudioOutput.js +++ b/scripts/changeAudioOutput.js @@ -5,119 +5,127 @@ export default { vi: "Thay đổi đầu ra âm thanh", }, description: { - en: "Pick a default audio output device, customizable for each browser tab.", - vi: "Thay đổi đầu ra âm thanh của trang web đang mở.\nMỗi tab có thể chọn đầu ra khác nhau (tai nghe/loa).", + en: "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.", + vi: "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.", }, - infoLink: "https://www.facebook.com/groups/j2team.community/posts/1362716140727169/", + infoLink: + "https://www.facebook.com/groups/j2team.community/posts/1362716140727169/", // Source: https://gist.github.com/monokaijs/44ef4bd0770f83272b83c038a2769c90 - onClick: async () => { - let key = "ufs-audio-output-switcher"; - let exist = document.querySelector("#" + key); - if (exist) { - exist.style.display = ""; - return; - } - - if ( - !confirm( - "Vui lòng cho phép truy cập audio/video để chức năng có thể chạy." - ) - ) - return; - - function disableBtn(btn, disable = true) { - if (disable) { - btn.classList.add("disabled"); - btn.disabled = true; - } else { - btn.classList.remove("disabled"); - btn.disabled = false; - } - } - - let popupParent = document.createElement("div"); - popupParent.id = key; - popupParent.className = "ufs-change-audio-output"; - - /* navigation bar */ - let navbar = document.createElement("div"); - navbar.className = "navbar"; - - let closeButton = document.createElement("button"); - closeButton.className = "close-btn"; - closeButton.innerText = "Close"; - closeButton.onclick = function (e) { - popupParent.style.display = "none"; - }; - navbar.appendChild(closeButton); - - /* PLEASE DO NOT REMOVE CREDIT LINE */ - let popupTitle = document.createElement("div"); - popupTitle.className = "title"; - popupTitle.innerHTML = - "SoundSwitcher - by NorthStudio"; - navbar.appendChild(popupTitle); - - let titleLink = popupTitle.querySelector("a"); - titleLink.className = "title-link"; - - let popupContent = document.createElement("div"); - popupContent.className = "popup-content"; - - let bottomArea = document.createElement("div"); - bottomArea.className = "bottom-area"; - - let submitButton = document.createElement("button"); - submitButton.className = "submit-btn"; - submitButton.innerText = "Waiting for permission..."; - disableBtn(submitButton); - - submitButton.onclick = async function (e) { - if (submitButton.disabled) return; - let audio_video = Array.from(document.querySelectorAll("audio,video")); - - if (!audio_video.length) - return alert("Không tìm thấy âm thanh/video nào trong trang web"); - - submitButton.innerText = "Setting..."; - disableBtn(submitButton); - for (let el of audio_video) { - await el.setSinkId(deviceSelector.value); + onClick: () => { + (async function () { + try { + await navigator.mediaDevices.getUserMedia({ audio: true }); + } catch (e) { + alert("Failed to initialize media devices."); } + let popupParent = document.createElement("div"); + /* positioning for the dialog */ + popupParent.style.width = "320px"; + popupParent.style.height = "120px"; + popupParent.style.position = "fixed"; + popupParent.style.left = "50%"; + popupParent.style.top = "50%"; + popupParent.style.marginLeft = "-160px"; + popupParent.style.marginTop = "-60px"; + /* stylizing the dialog */ + popupParent.style.backgroundColor = "#fff"; + popupParent.style.border = "1px solid rgba(0, 0, 0, .08)"; + popupParent.style.zIndex = "999999"; + + /* navigation bar */ + let navbar = document.createElement("div"); + navbar.style.width = "100%"; + navbar.style.height = "28px"; + navbar.style.backgroundColor = "rgba(0, 0, 0, .05)"; + + let closeButton = document.createElement("button"); + closeButton.innerText = "Close"; + closeButton.style.padding = "6px 8px"; + closeButton.style.backgroundColor = "transparent"; + closeButton.style.color = "black"; + closeButton.style.position = "absolute"; + closeButton.style.right = "0"; + closeButton.style.top = "0"; + closeButton.style.border = "none"; + + navbar.appendChild(closeButton); + + closeButton.onclick = function (e) { + popupParent.parentNode.removeChild(popupParent); + }; + closeButton.onmouseover = function (e) { + e.target.style.backgroundColor = "#e74c3c"; + e.target.style.color = "white"; + }; + closeButton.onmouseleave = function (e) { + e.target.style.backgroundColor = "transparent"; + e.target.style.color = "black"; + }; + + /* PLEASE DO NOT REMOVE CREDIT LINE */ + let popupTitle = document.createElement("div"); + popupTitle.innerHTML = + "SoundSwitcher - by NorthStudio"; + popupTitle.style.fontSize = "12px"; + popupTitle.style.position = "absolute"; + popupTitle.style.left = "6px"; + popupTitle.style.top = "8px"; + navbar.appendChild(popupTitle); + + let titleLink = popupTitle.querySelector("a"); + titleLink.style.fontWeight = "500"; + titleLink.style.textDecoration = "none"; + + let popupContent = document.createElement("div"); + popupContent.style.height = "92px"; + popupContent.style.display = "relative"; + popupContent.style.width = "100%"; + + let bottomArea = document.createElement("div"); + bottomArea.style.position = "absolute"; + bottomArea.style.left = "10px"; + bottomArea.style.right = "10px"; + bottomArea.style.bottom = "10px"; + + let submitButton = document.createElement("button"); + submitButton.style.backgroundColor = "#3498db"; + submitButton.style.color = "#FFF"; + submitButton.style.width = "100%"; + submitButton.style.padding = "6px"; + submitButton.style.border = "none"; + submitButton.style.borderRadius = "3px"; submitButton.innerText = "Set Device"; - disableBtn(submitButton, false); - }; - - bottomArea.appendChild(submitButton); - - let deviceSelector = document.createElement("select"); - deviceSelector.className = "device-selector"; - - popupContent.appendChild(bottomArea); - popupParent.appendChild(navbar); - popupParent.appendChild(popupContent); - document.body.appendChild(popupParent); - UsefulScriptGlobalPageContext.DOM.injectCssFile( - await UsefulScriptGlobalPageContext.Extension.getURL( - "scripts/changeAudioOutput.css" - ) - ); - - // ====================== Main ====================== - await navigator.mediaDevices.getUserMedia({ audio: true, video: true }); - submitButton.innerText = "Set Device"; - disableBtn(submitButton, false); - - navigator.mediaDevices.enumerateDevices().then((devices) => { - devices = devices.filter((x) => x.kind === "audiooutput"); - devices.forEach((device) => { - let choice = document.createElement("option"); - choice.innerText = device.label; - choice.value = device.deviceId; - deviceSelector.appendChild(choice); + + submitButton.onclick = function (e) { + document.querySelectorAll("audio,video").forEach((el) => { + el.setSinkId(deviceSelector.value); + }); + }; + + bottomArea.appendChild(submitButton); + + let deviceSelector = document.createElement("select"); + navigator.mediaDevices.enumerateDevices().then((devices) => { + devices = devices.filter((x) => x.kind === "audiooutput"); + devices.forEach((device) => { + let choice = document.createElement("option"); + choice.innerText = device.label; + choice.value = device.deviceId; + deviceSelector.appendChild(choice); + }); + popupContent.appendChild(deviceSelector); }); - popupContent.appendChild(deviceSelector); - }); + + deviceSelector.style.margin = "10px"; + deviceSelector.style.padding = "5px"; + deviceSelector.style.width = "calc(100% - 20px)"; + + popupContent.appendChild(bottomArea); + + popupParent.appendChild(navbar); + popupParent.appendChild(popupContent); + document.body.appendChild(popupParent); + })(); }, }; diff --git a/scripts/content-scripts/document_start.js b/scripts/content-scripts/document_start.js index ce42eb60..17f5c0ed 100644 --- a/scripts/content-scripts/document_start.js +++ b/scripts/content-scripts/document_start.js @@ -19,7 +19,7 @@ // run all scripts that has onDocumentStart event (async () => { - injectScript( + import( chrome.runtime.getURL( "/scripts/content-scripts/scripts/ufs_global_webpage_context.js" ) @@ -38,9 +38,7 @@ }) ); - injectScript( - chrome.runtime.getURL("/scripts/content-scripts/run_scripts.js") - ); + import(chrome.runtime.getURL("/scripts/content-scripts/run_scripts.js")); })(); // Run script on user click (if clicked script has onClickContentScript event) diff --git a/scripts/content-scripts/run_scripts.js b/scripts/content-scripts/run_scripts.js index ea5a4a85..746020d6 100644 --- a/scripts/content-scripts/run_scripts.js +++ b/scripts/content-scripts/run_scripts.js @@ -66,8 +66,8 @@ function checkWillRun(script) { let url = location.href; let hasWhiteList = script.whiteList?.length > 0; let hasBlackList = script.blackList?.length > 0; - let inWhiteList = matchPatterns(url, script.whiteList || []); - let inBlackList = matchPatterns(url, script.blackList || []); + let inWhiteList = matchOneOfPatterns(url, script.whiteList || []); + let inBlackList = matchOneOfPatterns(url, script.blackList || []); return ( (!hasWhiteList && !hasBlackList) || (hasWhiteList && inWhiteList) || @@ -75,48 +75,17 @@ function checkWillRun(script) { ); } -// Source: https://github.com/fregante/webext-patterns/blob/main/index.ts -function matchPatterns(url, patterns) { - const patternValidationRegex = - /^(https?|wss?|file|ftp|\*):\/\/(\*|\*\.[^*/]+|[^*/]+)\/.*$|^file:\/\/\/.*$|^resource:\/\/(\*|\*\.[^*/]+|[^*/]+)\/.*$|^about:/; - const isFirefox = - typeof navigator === "object" && navigator.userAgent.includes("Firefox/"); - const allStarsRegex = isFirefox - ? /^(https?|wss?):[/][/][^/]+([/].*)?$/ - : /^https?:[/][/][^/]+([/].*)?$/; - const allUrlsRegex = /^(https?|file|ftp):[/]+/; - - function getRawPatternRegex(pattern) { - if (!patternValidationRegex.test(pattern)) - throw new Error( - pattern + - " is an invalid pattern, it must match " + - String(patternValidationRegex) - ); - let [, protocol, host, pathname] = pattern.split(/(^[^:]+:[/][/])([^/]+)?/); - protocol = protocol - .replace("*", isFirefox ? "(https?|wss?)" : "https?") - .replace(/[/]/g, "[/]"); - host = (host ?? "") - .replace(/^[*][.]/, "([^/]+.)*") - .replace(/^[*]$/, "[^/]+") - .replace(/[.]/g, "[.]") - .replace(/[*]$/g, "[^.]+"); - pathname = pathname - .replace(/[/]/g, "[/]") - .replace(/[.]/g, "[.]") - .replace(/[*]/g, ".*"); - return "^" + protocol + host + "(" + pathname + ")?$"; - } - - function patternToRegex(matchPatterns) { - if (matchPatterns.length === 0) return /$./; - if (matchPatterns.includes("")) return allUrlsRegex; - if (matchPatterns.includes("*://*/*")) return allStarsRegex; - return new RegExp( - matchPatterns.map((x) => getRawPatternRegex(x)).join("|") +function matchOneOfPatterns(url, patterns) { + for (let pattern of patterns) { + const regex = new RegExp( + "^" + + pattern + .split("*") + .map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")) + .join(".*") + + "$" ); + if (regex.test(url)) return true; } - - return patternToRegex(patterns).test(url); + return false; } diff --git a/scripts/content-scripts/scripts/ufs_global_webpage_context.js b/scripts/content-scripts/scripts/ufs_global_webpage_context.js index a2b83f4c..4152d5e9 100644 --- a/scripts/content-scripts/scripts/ufs_global_webpage_context.js +++ b/scripts/content-scripts/scripts/ufs_global_webpage_context.js @@ -83,7 +83,7 @@ const UsefulScriptGlobalPageContext = { // Idea from https://github.com/gys-dev/Unlimited-Stdphim // https://stackoverflow.com/a/61511955/11898496 - onElementsVisible: async (selector, callback, willReRun) => { + onElementsVisible: (selector, callback, willReRun) => { let nodes = document.querySelectorAll(selector); if (nodes?.length) { callback(nodes); @@ -135,6 +135,48 @@ const UsefulScriptGlobalPageContext = { document.head.appendChild(css); }, + getTrustedPolicy() { + let policy = window.trustedTypes?.ufsTrustedTypesPolicy || null; + if (!policy) { + policy = window.trustedTypes.createPolicy("ufsTrustedTypesPolicy", { + createHTML: (input) => input, + createScriptURL: (input) => input, + }); + } + return policy; + }, + + createTrustedHtml(html) { + let policy = UsefulScriptGlobalPageContext.DOM.getTrustedPolicy(); + return policy.createHTML(html); + }, + + injectScriptSrc(src, callback) { + let policy = UsefulScriptGlobalPageContext.DOM.getTrustedPolicy(); + let jsSrc = policy.createScriptURL(src); + let script = document.createElement("script"); + script.onload = function () { + callback?.(true); + }; + script.onerror = function (e) { + callback?.(false, e); + }; + script.src = jsSrc; // Assigning the TrustedScriptURL to src + document.head.appendChild(script); + }, + + injectScriptSrcAsync(src) { + return new Promise((resolve, reject) => { + UsefulScriptGlobalPageContext.DOM.injectScriptSrc(src, (success, e) => { + if (success) { + resolve(); + } else { + reject(e); + } + }); + }); + }, + isElementInViewport(el) { const rect = el.getBoundingClientRect(); return ( @@ -181,6 +223,72 @@ const UsefulScriptGlobalPageContext = { }, }, Utils: { + formatSize(size, fixed = 0) { + size = Number(size); + + if (!size) return "?"; + + // format to KB, MB, GB + if (size < 1024) { + return size + "B"; + } + if (size < 1024 * 1024) { + return (size / 1024).toFixed(fixed) + "KB"; + } + if (size < 1024 * 1024 * 1024) { + return (size / (1024 * 1024)).toFixed(fixed) + "MB"; + } + return (size / (1024 * 1024 * 1024)).toFixed(fixed) + "GB"; + }, + + // modified by chatgpt based on: https://gist.github.com/jcouyang/632709f30e12a7879a73e9e132c0d56b + promiseAllStepN(n, list) { + const head = list.slice(0, n); + const tail = list.slice(n); + const resolved = []; + + return new Promise((resolve) => { + let processed = 0; + + function runNext() { + if (processed === tail.length) { + resolve(Promise.all(resolved)); + return; + } + + const promise = tail[processed](); + resolved.push( + promise.then((result) => { + runNext(); + return result; + }) + ); + processed++; + } + + head.forEach((func) => { + const promise = func(); + resolved.push( + promise.then((result) => { + runNext(); + return result; + }) + ); + }); + }); + }, + + hook(obj, name, callback) { + const fn = obj[name]; + obj[name] = function (...args) { + callback.apply(this, args); + fn.apply(this, args); + }; + return () => { + // restore + obj[name] = fn; + }; + }, // https://stackoverflow.com/a/38552302/11898496 parseJwt(token) { var base64Url = token.split(".")[1]; @@ -304,8 +412,8 @@ const UsefulScriptGlobalPageContext = { alert("Error: " + error); } }, - async downloadBlobUrlWithProgress(url, fileName, progressCallback) { - const response = await fetch(url); + async getBlobFromUrlWithProgress(url, progressCallback) { + const response = await fetch(url, {}); if (!response.ok) { throw new Error(`Error: ${response.status} - ${response.statusText}`); } @@ -315,18 +423,25 @@ const UsefulScriptGlobalPageContext = { 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; - progressCallback?.(loaded, total); + 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"), }); - UsefulScriptGlobalPageContext.Utils.downloadBlob(blob, fileName); + + return blob; }, async downloadBlobUrl(url, title) { try { @@ -1231,8 +1346,3 @@ window.UsefulScriptsUtils = UsefulScriptsUtils; // Chrome pre-34 if (!Element.prototype.matches) Element.prototype.matches = Element.prototype.webkitMatchesSelector; - -// https://mmazzarolo.com/blog/2022-08-25-simple-colored-logging-for-javascript-clis/ -window.console.success = (...args) => console.log("\x1b[32m✔\x1b[0m", ...args); -window.console.failure = (...args) => - console.error("\x1b[31mx\x1b[0m", ...args); diff --git a/scripts/content-scripts/utils.js b/scripts/content-scripts/utils.js deleted file mode 100644 index 2d61fc57..00000000 --- a/scripts/content-scripts/utils.js +++ /dev/null @@ -1,48 +0,0 @@ -export function getURL(fileName) { - return "/scripts/" + fileName; -} - -export function injectScript( - filePathOrUrl, - type = "application/javascript", - isExternal = false -) { - try { - var s = document.createElement("script"); - s.src = isExternal ? filePathOrUrl : chrome.runtime.getURL(filePathOrUrl); - s.type = type; - s.onload = function () { - console.log("Useful-scripts injected " + s.src); - this.remove(); - }; - s.onerror = function (e) { - console.log("ERROR: Useful-scripts inject script FAILED " + s.src, e); - this.remove(); - }; - (document.head || document.documentElement).appendChild(s); - } catch (e) { - console.log( - "ERROR: Useful-scripts inject script FAILED " + filePathOrUrl, - e - ); - } -} - -// TODO: https://developer.chrome.com/docs/extensions/reference/scripting/#method-insertCSS -// https://stackoverflow.com/a/17840622 -export function injectCss(url_file_code, type = "file") { - if (type === "file" || type === "url") { - var css = document.createElement("link"); - css.rel = "stylesheet"; - css.href = - type === "file" ? chrome.runtime.getURL(url_file_code) : url_file_code; - document.head.appendChild(css); - console.log("Useful-scripts injected " + css.href); - } else if (type === "code") { - var css = document.createElement("style"); - if ("textContent" in css) css.textContent = url_file_code; - else css.innerText = url_file_code; - document.head.appendChild(css); - console.log("Useful-scripts injected " + css); - } -} diff --git a/scripts/detect_zeroWidthCharacters.js b/scripts/detect_zeroWidthCharacters.js index 6e585117..38627892 100644 --- a/scripts/detect_zeroWidthCharacters.js +++ b/scripts/detect_zeroWidthCharacters.js @@ -5,8 +5,8 @@ export default { vi: "Phát hiện ký tự ẩn (Zero-Width)", }, description: { - en: "Detects zero-width characters, highlights the characters and containing DOM element.\n\nClick for more detail.", - vi: "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,...\n\nBấm để xem thêm chi tiết.", + en: "Detects zero-width characters, highlights the characters and containing DOM element.\n\nClick ? for more detail.", + vi: "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,...\n\nBấm ? để xem thêm chi tiết.", }, infoLink: "https://viblo.asia/p/ky-tu-zero-width-sat-thu-vo-hinh-nam-giua-doan-van-ban-thuan-vo-hai-L4x5xM7qKBM", diff --git a/scripts/doutube_getAllVideoInUserProfile.js b/scripts/doutube_getAllVideoInUserProfile.js index 9df91d36..4056fcfe 100644 --- a/scripts/doutube_getAllVideoInUserProfile.js +++ b/scripts/doutube_getAllVideoInUserProfile.js @@ -1,4 +1,4 @@ -import { runScriptInCurrentTab, showLoading } from "./helpers/utils.js"; +import { getCurrentTabUrl, showLoading } from "./helpers/utils.js"; export default { icon: "https://s2.googleusercontent.com/s2/favicons?domain=doutu.be", @@ -16,11 +16,8 @@ export default { const { zipAndDownloadBlobs, getBlobFromUrl } = UsefulScriptGlobalPageContext.Utils; - let user_id = await runScriptInCurrentTab(() => { - let url = window.location.href; - let id = url.split("/u/")?.[1]; - return id; - }); + let url = await getCurrentTabUrl(); + let user_id = url?.split("/u/")?.[1]; if (!user_id) { alert( diff --git a/scripts/douyin_downloadAllVideoUser.js b/scripts/douyin_downloadAllVideoUser.js index b4aaa55d..71e6744f 100644 --- a/scripts/douyin_downloadAllVideoUser.js +++ b/scripts/douyin_downloadAllVideoUser.js @@ -32,8 +32,8 @@ export default { "sec-fetch-mode": "cors", "sec-fetch-site": "same-origin", }, - referrer: - "https://www.douyin.com/user/MS4wLjABAAAA5A-hCBCTdv102baOvaoZqg7nCIW_Bn_YBA0Aiz9uYPY", + referrer: location.href, + // "https://www.douyin.com/user/MS4wLjABAAAA5A-hCBCTdv102baOvaoZqg7nCIW_Bn_YBA0Aiz9uYPY", referrerPolicy: "strict-origin-when-cross-origin", body: null, method: "GET", @@ -69,17 +69,22 @@ export default { console.log(moredata); hasMore = moredata["has_more"]; max_cursor = moredata["max_cursor"]; - for (var item of moredata["aweme_list"]) { - let url = item.video.play_addr.url_list[0]; + for (var item of moredata["aweme_list"] || []) { + try { + let url = item.video.play_addr.url_list[0]; - if (url.startsWith("https")) { - result.push(url); - } else { - result.push(url.replace("http", "https")); + if (url.startsWith("https")) { + result.push(url); + } else { + result.push(url.replace("http", "https")); + } + // console.clear(); + console.log("Number of videos: " + result.length); + } catch (e) { + console.log("ERROR: ", e); } - // console.clear(); - console.log("Number of videos: " + result.length); } + console.log(hasMore); } saveToFile(result.join("\n")); alert( diff --git a/scripts/douyin_downloadWachingVideo.js b/scripts/douyin_downloadWachingVideo.js index 47589df3..826d19e4 100644 --- a/scripts/douyin_downloadWachingVideo.js +++ b/scripts/douyin_downloadWachingVideo.js @@ -1,4 +1,4 @@ -import { runScriptInCurrentTab } from "./helpers/utils.js"; +import { runScriptInCurrentTab, showLoading } from "./helpers/utils.js"; export default { icon: "https://www.douyin.com/favicon.ico", @@ -13,8 +13,16 @@ export default { whiteList: ["https://www.douyin.com/*"], onClickExtension: async function () { - const { downloadURL, downloadBlobUrl } = - UsefulScriptGlobalPageContext.Utils; + const { + downloadURL, + downloadBlob, + getBlobFromUrlWithProgress, + formatSize, + } = UsefulScriptGlobalPageContext.Utils; + + const { closeLoading, setLoadingText } = showLoading( + "Đang tìm video url..." + ); const src = await runScriptInCurrentTab(async () => { return await UsefulScriptGlobalPageContext.DOM.getWatchingVideoSrc(); @@ -22,9 +30,23 @@ export default { if (!src) { alert("Không tìm thấy video nào."); - return; + } else { + setLoadingText("Đang tải video..."); + downloadURL(src, "douyin_video.mp4"); + // const blob = await getBlobFromUrlWithProgress( + // src, + // ({ loaded, total, speed }) => { + // const percent = ((loaded / total) * 100) | 0; + // setLoadingText( + // `Đang tải video...
    ` + + // `Vui lòng không tắt popup
    ` + + // `${formatSize(loaded)}/${formatSize(total)} (${percent}%)` + + // ` - ${formatSize(speed)}/s` + // ); + // } + // ); + // await downloadBlob(blob, "douyin_video.mp4"); } - - downloadBlobUrl(src, "douyin_video.mp4"); + closeLoading(); }, }; diff --git a/scripts/dreamai.js b/scripts/dreamai.js deleted file mode 100644 index 32e4fc75..00000000 --- a/scripts/dreamai.js +++ /dev/null @@ -1,15 +0,0 @@ -export default { - icon: "https://dream.ai/favicon.ico", - name: { - en: "Dream by WOMBO", - vi: "Dream by WOMBO", - }, - description: { - en: "High Quality Artwork In Seconds", - vi: "Trình tạo hình ảnh chất lượng cao trong vài giây", - }, - - onClickExtension: () => { - window.open("https://dream.ai"); - }, -}; diff --git a/scripts/fb_downloadWatchingVideo.js b/scripts/fb_downloadWatchingVideo.js index 9b95f73c..36320e8c 100644 --- a/scripts/fb_downloadWatchingVideo.js +++ b/scripts/fb_downloadWatchingVideo.js @@ -12,6 +12,8 @@ export default { vi: "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)", }, whiteList: ["https://*.facebook.com/*"], + infoLink: + "https://greasyfork.org/en/scripts/477748-facebook-video-downloader", onClickExtension: async function () { let { closeLoading, setLoadingText } = showLoading( @@ -19,7 +21,7 @@ export default { ); try { let listVideoId = await shared.getListVideoIdInWebsite(); - let watchingVideoId = listVideoId[0]; + let watchingVideoId = listVideoId?.[0]; if (!watchingVideoId) throw Error("Không tìm thấy video nào"); setLoadingText("Đang lấy token dtsg..."); diff --git a/scripts/fb_invisible_message.js b/scripts/fb_invisible_message.js index 9162e888..1cd5d547 100644 --- a/scripts/fb_invisible_message.js +++ b/scripts/fb_invisible_message.js @@ -8,7 +8,7 @@ export default { en: "Add the > character before writing a message to send hidden messages.\n\nYour friends needs to install this extension and enable it to view hidden messages.", vi: "Thêm ký tự > trước tin nhắn để tạo tin nhắn tàng hình.\n\nChỉ xem được tin nhắn tàng hình khi cài extension và bật chức năng này.", }, - infoLink: "https://www.facebook.com/groups/j2team.community/posts/1607769529555161/", + infoLink: "https://github.com/t-rekttt/invisible_message", whiteList: ["https://*.facebook.com/*", "https://*.messenger.com/*"], @@ -16,7 +16,7 @@ export default { // ==UserScript== // @name Invisible Text // @namespace https://thao.pw - // @version 0.6 + // @version 0.10 // @description FB Messenger invisible text // @author T-Rekt // @match https://*.facebook.com/* @@ -24,14 +24,13 @@ export default { // @grant none // @run-at document-idle // ==/UserScript== - (function () { // Thank you zws - - const PADDING = "\u200c"; + const PADDING_START = "\u200c"; + const PADDING_END = "\u{e0061}"; const CHARS = [ // "\u200d", - "\u{e0061}", + // "\u{e0061}", "\u{e0062}", "\u{e0063}", "\u{e0064}", @@ -59,12 +58,14 @@ export default { "\u{e007a}", "\u{e007f}", ]; - + const shouldEncodePattern = / *>(.+?)< */; + const encodedPattern = new RegExp( + `${PADDING_START}([${CHARS.join("")}]+?)${PADDING_END}` + ); const CHARS_MAP = CHARS.reduce((curr, val, i) => { curr[val] = i; return curr; }, {}); - const lenCalc = (base, chars) => { var len = 0; var curr = 1; @@ -74,11 +75,9 @@ export default { } return len; }; - const UNICODE_CHARS = 1114112; const BASE = CHARS.length; const LEN = lenCalc(BASE, UNICODE_CHARS); - const charConvert = (char) => { let charCode = char.codePointAt(0); let arr = []; @@ -91,20 +90,17 @@ export default { } return arr.reverse(); }; - const charEncode = (convertedChar) => { return convertedChar.reduce((curr, digit) => curr + CHARS[digit], ""); }; - const encode = (s) => { let converted = []; for (let c of s) { converted.push(charConvert(c)); } let res = converted.map(charEncode); - return PADDING + res.join(""); + return PADDING_START + res.join("") + PADDING_END; }; - const decodeChar = (encodedChar) => { encodedChar = encodedChar.reverse(); let curr = 1; @@ -115,9 +111,8 @@ export default { } return String.fromCodePoint(charCode); }; - const decode = (s) => { - s = s.substr(1); + s = encodedPattern.exec(s)[1]; let curr = []; let res = ""; for (let c of s) { @@ -127,28 +122,27 @@ export default { curr = []; } } - return res; }; - const checkEncode = (s) => { - if (s?.[0] != PADDING) return false; - s = s.substr(1); - for (let c of s) if (CHARS_MAP[c] === undefined) return false; - return true; + //console.log(s); + return encodedPattern.exec(s); }; - requireLazy( ["MWV2ChatText.bs", "MqttProtocolClient"], (MWV2ChatText, protocolClient) => { + console.log({ MWV2ChatText, protocolClient }); const MWV2ChatTextMakeOrig = MWV2ChatText.make; MWV2ChatText.make = function (a) { let text = a?.message?.text; - if (checkEncode(text)) - a.message.text = `[Encrypted]: ${decode(text)}`; + if (checkEncode(text)) { + //a.message.isAdminMessage = true; + a.message.text = + "Encrypted message: " + + a.message.text.replace(encodedPattern, `>${decode(text)}<`); + } return MWV2ChatTextMakeOrig.apply(this, arguments); }; - // Message publish const publishOrig = protocolClient.prototype.publish; protocolClient.prototype.publish = function () { @@ -159,17 +153,19 @@ export default { if (!b || !b.payload) return; let payload = JSON.parse(b.payload); if (!payload || !payload.tasks) return; - payload.tasks = payload.tasks.map((task) => { let payload = JSON.parse(task.payload); if (!payload || !payload.text) return task; - if (payload.text.length > 1 && payload.text[0] === ">") { - payload.text = encode(payload.text.substr(1)); + let matches = shouldEncodePattern.exec(payload.text); + if (payload.text.length > 1 && matches) { + payload.text = payload.text.replace( + shouldEncodePattern, + " " + encode(matches[1]) + ); } task.payload = JSON.stringify(payload); return task; }); - b.payload = JSON.stringify(payload); b = JSON.stringify(b); })(); diff --git a/scripts/fb_whoIsTyping.js b/scripts/fb_whoIsTyping.js index bcda8e77..82156385 100644 --- a/scripts/fb_whoIsTyping.js +++ b/scripts/fb_whoIsTyping.js @@ -60,7 +60,7 @@ export default { if (!exist) { exist = document.createElement("div"); exist.id = divId; - exist.innerHTML = `
    + exist.innerHTML = /*html*/ `
    `; diff --git a/scripts/fireship_vip.js b/scripts/fireship_vip.js new file mode 100644 index 00000000..ef378b71 --- /dev/null +++ b/scripts/fireship_vip.js @@ -0,0 +1,54 @@ +export default { + icon: "https://fireship.io/img/favicon.png", + name: { + en: "Fireship - PRO unlocked", + vi: "Fireship - Mở khoá PRO", + }, + description: { + en: "Unlock all Fireship PRO courses/lessons (saved $399 USD)", + vi: "Mở khoá tất cả khoá học/bài giảng PRO trên Fireship (tiết kiệm $399 USD)", + }, + + whiteList: ["https://fireship.io/*"], + + onDocumentIdle: () => { + // ==UserScript== + // @name Freeship + // @namespace lemons + // @version 1.7 + // @description Unlock all Fireship PRO courses/lessons. + // @author lemons + // @match https://fireship.io/* + // @icon https://em-content.zobj.net/source/apple/391/fire_1f525.png + // @grant none + // @downloadURL https://update.greasyfork.org/scripts/455330/Freeship.user.js + // @updateURL https://update.greasyfork.org/scripts/455330/Freeship.meta.js + // ==/UserScript== + + // prettier-ignore + setInterval(async () => { + document.querySelectorAll("[free=\"\"]").forEach(el => el.setAttribute("free", true)) // set all elements with the attribute free set to "" to true + + if (document.querySelector("if-access [slot=\"granted\"]")) { // replace HOW TO ENROLL to YOU HAVE ACCESS + document.querySelector("if-access [slot=\"denied\"]").remove() + document.querySelector("if-access [slot=\"granted\"]").setAttribute("slot", "denied") + } + + if (document.querySelector("video-player")?.shadowRoot?.querySelector(".vid")?.innerHTML) return; // return if no video player + const vimeoId = Number(atob(document.querySelector("global-data").vimeo)); // get id for vimeo video + const youtubeId = atob(document.querySelector("global-data").youtube); // get id for vimeo video + + if (youtubeId) { // if there is an id, + document.querySelector("video-player").setAttribute("free", true) // set free to true + document.querySelector("video-player").shadowRoot.querySelector(".vid").innerHTML = `` // set video + return; + } + if (vimeoId) { // if there is an id, + document.querySelector("video-player").setAttribute("free", true) // set free to true + const html = (await fetch(`https://vimeo.com/api/oembed.json?url=https%3A%2F%2Fvimeo.com%2F${vimeoId}&id=${vimeoId}`).then(r=>r.json())).html + document.querySelector("video-player").shadowRoot.querySelector(".vid").innerHTML = html // set video + return; + } + }, 500) + }, +}; diff --git a/scripts/freesound_downloadAudio.js b/scripts/freesound_downloadAudio.js deleted file mode 100644 index 75e05b6c..00000000 --- a/scripts/freesound_downloadAudio.js +++ /dev/null @@ -1,42 +0,0 @@ -import { getCurrentTab, showLoading } from "./helpers/utils.js"; - -export default { - icon: "https://freesound.org/favicon.ico", - name: { - en: "Freesound - Download audio", - vi: "Freesound - Tải âm thanh", - }, - description: { - en: "Download audio on freesound.org", - vi: "Tải âm thanh trên freesound.org", - }, - - onClickExtension: async function () { - // https://github.com/soimort/you-get/blob/develop/src/you_get/extractors/freesound.py - - let tab = await getCurrentTab(); - let url = prompt("Nhập link freesound: ", tab.url); - if (url == null) return; - - let { closeLoading } = showLoading("Đang tìm file âm thanh..."); - try { - let res = await fetch(url); - let html = await res.text(); - - // prettier-ignore - let title = new RegExp(' { - return `
    + return /*html*/ `
    @@ -66,7 +66,7 @@ export default { let escapeHTMLPolicy = trustedTypes.createPolicy("forceInner", { createHTML: (to_escape) => to_escape, }); - win.document.body.innerHTML = escapeHTMLPolicy.createHTML(`
    + win.document.body.innerHTML = escapeHTMLPolicy.createHTML(/*html*/ `
    -
    `; - - let div = document.createElement("div"); - div.innerHTML = html; - document.body.appendChild(div); - - const containerDiv = document.querySelector(".transfer-sh-container"); - const status = containerDiv.querySelector(".status"); - const fileInfo = containerDiv.querySelector(".file-info"); - const progressDiv = containerDiv.querySelector("progress"); - const loader = containerDiv.querySelector(".loader"); - const resultDiv = containerDiv.querySelector(".result"); - const closeBtn = containerDiv.querySelector("#close-btn"); - - closeBtn.addEventListener("click", function () { - div.remove(); - }); - - // containerDiv.style.display = "flex"; - // return; - - let file = await selectFile(); - console.log(file); - - if (file) { - let url = "https://transfer.sh/" + file.name; - let { startUpload, cancel } = uploadFileWithProgress( - url, - file, - ({ percent, loaded, total }) => { - progressDiv.value = percent * 100; - - let uploadDone = percent == 1; - status.innerText = uploadDone - ? `Đang tạo link...` - : `Đang tải lên...`; - - let kb = uploadDone - ? ~~(file.size / 1e3) + "Kb" - : `${~~(loaded / 1e3)}/${~~(total / 1e3)}Kb`; - fileInfo.innerText = `${file.name}\n(${kb})`; - } - ); - - fileInfo.innerText = `${file.name}\n(${~~(file.size / 1e3)}Kb)`; - containerDiv.style.display = "flex"; - loader.style.display = "block"; - closeBtn.addEventListener("click", cancel); - - let urlDelete = await startUpload(); - status.innerText = `Tải lên hoàn tất`; - loader.style.display = "none"; - - if (urlDelete) { - let url = urlDelete.slice(0, urlDelete.lastIndexOf("/")); - let deletionToken = urlDelete.slice(urlDelete.lastIndexOf("/") + 1); - let downloadZipUrl = url + ".zip"; - let downloadTarGzUrl = url + ".tar.gz"; - - resultDiv.innerHTML = `
    - URL:
    - Delete token:
    - Download .zip
    - Download .tar.gz -
    `; - } else { - alert( - "Lỗi\n\nBạn có thể mở trang web sau để upload file:", - "https://transfer.sh/" - ); - } - } - })(); - }, -}; - -function backup() { - function uploadFile(url, file) { - fetch(url, { - method: "PUT", - body: file, - }) - .then((res) => { - console.log(res); - console.log(res.headers.get("x-url-delete")); - - alert("sucecss"); - }) - .catch((e) => { - alert("ERROR " + e); - }); - } -} diff --git a/scripts/twitter_downloadButton.js b/scripts/twitter_downloadButton.js new file mode 100644 index 00000000..47b2e03a --- /dev/null +++ b/scripts/twitter_downloadButton.js @@ -0,0 +1,423 @@ +export default { + icon: "https://twitter.com/favicon.ico", + name: { + en: "Twitter X - Add download button", + vi: "Twitter X - Thêm nút tải video/ảnh", + }, + description: { + en: "Add the download button to all video and photo in Twitter", + vi: "Thêm nút tải cho mọi video/ảnh trên Twitter", + img: "/scripts/twitter_downloadButton.png", + }, + infoLink: "https://greasyfork.org/en/scripts/423001-twitter-media-downloader", + + whiteList: ["https://mobile.twitter.com/*", "https://twitter.com/*"], + + // prettier-ignore + onDocumentIdle: async () => { + // ==UserScript== + // @name Twitter Media Downloader + // @name:ja Twitter Media Downloader + // @name:zh-cn Twitter 媒体下载 + // @name:zh-tw Twitter 媒體下載 + // @description Save Video/Photo by One-Click. + // @description:ja ワンクリックで動画・画像を保存する。 + // @description:zh-cn 一键保存视频/图片 + // @description:zh-tw 一鍵保存視頻/圖片 + // @version 1.27 + // @author AMANE + // @namespace none + // @match https://twitter.com/* + // @match https://mobile.twitter.com/* + // @grant GM_registerMenuCommand + // @grant GM_setValue + // @grant GM_getValue + // @grant GM_download + // @compatible Chrome + // @compatible Firefox + // @license MIT + // ==/UserScript== + /* jshint esversion: 8 */ + + const filename = 'twitter_{user-name}(@{user-id})_{date-time}_{status-id}_{file-type}'; + + const TMD = (function () { + let lang, host, history, show_sensitive, is_tweetdeck; + return { + init: async function () { + // GM_registerMenuCommand((this.language[navigator.language] || this.language.en).settings, this.settings); + lang = this.language[document.querySelector('html').lang] || this.language.en; + host = location.hostname; + is_tweetdeck = host.indexOf('tweetdeck') >= 0; + history = this.storage_obsolete(); + if (history.length) { + this.storage(history); + this.storage_obsolete(true); + } else history = await this.storage(); + // show_sensitive = GM_getValue('show_sensitive', false); + document.head.insertAdjacentHTML('beforeend', ''); + let observer = new MutationObserver(ms => ms.forEach(m => m.addedNodes.forEach(node => this.detect(node)))); + observer.observe(document.body, {childList: true, subtree: true}); + }, + detect: function(node) { + let article = node.tagName == 'ARTICLE' && node || node.tagName == 'DIV' && (node.querySelector('article') || node.closest('article')); + if (article) this.addButtonTo(article); + let listitems = node.tagName == 'LI' && node.getAttribute('role') == 'listitem' && [node] || node.tagName == 'DIV' && node.querySelectorAll('li[role="listitem"]'); + if (listitems) this.addButtonToMedia(listitems); + }, + addButtonTo: function (article) { + if (article.dataset.detected) return; + article.dataset.detected = 'true'; + let media_selector = [ + 'a[href*="/photo/1"]', + 'div[role="progressbar"]', + 'div[data-testid="playButton"]', + 'a[href="/settings/content_you_see"]', //hidden content + 'div.media-image-container', // for tweetdeck + 'div.media-preview-container', // for tweetdeck + 'div[aria-labelledby]>div:first-child>div[role="button"][tabindex="0"]' //for audio (experimental) + ]; + let media = article.querySelector(media_selector.join(',')); + if (media) { + let status_id = article.querySelector('a[href*="/status/"]').href.split('/status/').pop().split('/').shift(); + let btn_group = article.querySelector('div[role="group"]:last-of-type, ul.tweet-actions, ul.tweet-detail-actions'); + let btn_share = Array.from(btn_group.querySelectorAll(':scope>div>div, li.tweet-action-item>a, li.tweet-detail-action-item>a')).pop().parentNode; + let btn_down = btn_share.cloneNode(true); + if (is_tweetdeck) { + btn_down.firstElementChild.innerHTML = '' + this.svg + ''; + btn_down.firstElementChild.removeAttribute('rel'); + btn_down.classList.replace("pull-left", "pull-right"); + } else { + btn_down.querySelector('svg').innerHTML = this.svg; + } + let is_exist = history.indexOf(status_id) >= 0; + this.status(btn_down, 'tmd-down'); + this.status(btn_down, is_exist ? 'completed' : 'download', is_exist ? lang.completed : lang.download); + btn_group.insertBefore(btn_down, btn_share.nextSibling); + btn_down.onclick = () => this.click(btn_down, status_id, is_exist); + if (show_sensitive) { + let btn_show = article.querySelector('div[aria-labelledby] div[role="button"][tabindex="0"]:not([data-testid]) > div[dir] > span > span'); + if (btn_show) btn_show.click(); + } + } + let imgs = article.querySelectorAll('a[href*="/photo/"]'); + if (imgs.length > 1) { + let status_id = article.querySelector('a[href*="/status/"]').href.split('/status/').pop().split('/').shift(); + let btn_group = article.querySelector('div[role="group"]:last-of-type'); + let btn_share = Array.from(btn_group.querySelectorAll(':scope>div>div')).pop().parentNode; + imgs.forEach(img => { + let index = img.href.split('/status/').pop().split('/').pop(); + let is_exist = history.indexOf(status_id) >= 0; + let btn_down = document.createElement('div'); + btn_down.innerHTML = '
    ' + this.svg + '
    '; + btn_down.classList.add('tmd-down', 'tmd-img'); + this.status(btn_down, 'download'); + img.parentNode.appendChild(btn_down); + btn_down.onclick = e => { + e.preventDefault(); + this.click(btn_down, status_id, is_exist, index); + } + }); + } + }, + addButtonToMedia: function(listitems) { + listitems.forEach(li => { + if (li.dataset.detected) return; + li.dataset.detected = 'true'; + let status_id = li.querySelector('a[href*="/status/"]').href.split('/status/').pop().split('/').shift(); + let is_exist = history.indexOf(status_id) >= 0; + let btn_down = document.createElement('div'); + btn_down.innerHTML = '
    ' + this.svg + '
    '; + btn_down.classList.add('tmd-down', 'tmd-media'); + this.status(btn_down, is_exist ? 'completed' : 'download', is_exist ? lang.completed : lang.download); + li.appendChild(btn_down); + btn_down.onclick = () => this.click(btn_down, status_id, is_exist); + }); + }, + click: async function (btn, status_id, is_exist, index) { + if (btn.classList.contains('loading')) return; + this.status(btn, 'loading'); + // let out = (await GM_getValue('filename', filename)).split('\n').join(''); + let out = filename.split('\n').join(''); + // let save_history = await GM_getValue('save_history', true); + let save_history = true + let json = await this.fetchJson(status_id); + console.log(json) + let tweet = json.legacy; + let user = json.core.user_results.result.legacy; + let invalid_chars = {'\\': '\', '\/': '/', '\|': '|', '<': '<', '>': '>', ':': ':', '*': '*', '?': '?', '"': '"', '\u200b': '', '\u200c': '', '\u200d': '', '\u2060': '', '\ufeff': '', '🔞': ''}; + let datetime = out.match(/{date-time(-local)?:[^{}]+}/) ? out.match(/{date-time(?:-local)?:([^{}]+)}/)[1].replace(/[\\/|<>*?:"]/g, v => invalid_chars[v]) : 'YYYYMMDD-hhmmss'; + let info = {}; + info['status-id'] = status_id; + info['user-name'] = user.name.replace(/([\\/|*?:"]|[\u200b-\u200d\u2060\ufeff]|🔞)/g, v => invalid_chars[v]); + info['user-id'] = user.screen_name; + info['date-time'] = this.formatDate(tweet.created_at, datetime); + info['date-time-local'] = this.formatDate(tweet.created_at, datetime, true); + info['full-text'] = tweet.full_text.split('\n').join(' ').replace(/\s*https:\/\/t\.co\/\w+/g, '').replace(/[\\/|<>*?:"]|[\u200b-\u200d\u2060\ufeff]/g, v => invalid_chars[v]); + let medias = tweet.extended_entities && tweet.extended_entities.media; + if (index) medias = [medias[index - 1]]; + if (medias?.length > 0) { + let tasks = medias.length; + let tasks_result = []; + medias.forEach((media, i) => { + info.url = media.type == 'photo' ? media.media_url_https + ':orig' : media.video_info.variants.filter(n => n.content_type == 'video/mp4').sort((a, b) => b.bitrate - a.bitrate)[0].url; + info.file = info.url.split('/').pop().split(/[:?]/).shift(); + info['file-name'] = info.file.split('.').shift(); + info['file-ext'] = info.file.split('.').pop(); + info['file-type'] = media.type.replace('animated_', ''); + info.out = (out.replace(/\.?{file-ext}/, '') + ((medias.length > 1 || index) && !out.match('{file-name}') ? '-' + (index ? index - 1 : i) : '') + '.{file-ext}').replace(/{([^{}:]+)(:[^{}]+)?}/g, (match, name) => info[name]); + this.downloader.add({ + url: info.url, + name: info.out, + onload: () => { + tasks -= 1; + tasks_result.push(((medias.length > 1 || index) ? (index ? index : i + 1) + ': ' : '') + lang.completed); + this.status(btn, null, tasks_result.sort().join('\n')); + if (tasks === 0) { + this.status(btn, 'completed', lang.completed); + if (save_history && !is_exist) { + history.push(status_id); + this.storage(status_id); + } + } + }, + onerror: result => { + tasks = -1; + tasks_result.push((medias.length > 1 ? i + 1 + ': ' : '') + result.details.current); + this.status(btn, 'failed', tasks_result.sort().join('\n')); + } + }); + }); + } else { + this.status(btn, 'failed', 'MEDIA_NOT_FOUND'); + } + }, + status: function (btn, css, title, style) { + if (css) { + btn.classList.remove('download', 'completed', 'loading', 'failed'); + btn.classList.add(css); + } + if (title) btn.title = title; + if (style) btn.style.cssText = style; + }, + fetchJson: async function (status_id) { + let base_url = `https://${host}/i/api/graphql/NmCeCgkVlsRGS1cAwqtgmw/TweetDetail`; + let variables = { + "focalTweetId":status_id, + "with_rux_injections":false, + "includePromotedContent":true, + "withCommunity":true, + "withQuickPromoteEligibilityTweetFields":true, + "withBirdwatchNotes":true, + "withVoice":true, + "withV2Timeline":true + }; + let features = { + "rweb_lists_timeline_redesign_enabled":true, + "responsive_web_graphql_exclude_directive_enabled":true, + "verified_phone_label_enabled":false, + "creator_subscriptions_tweet_preview_api_enabled":true, + "responsive_web_graphql_timeline_navigation_enabled":true, + "responsive_web_graphql_skip_user_profile_image_extensions_enabled":false, + "tweetypie_unmention_optimization_enabled":true, + "responsive_web_edit_tweet_api_enabled":true, + "graphql_is_translatable_rweb_tweet_is_translatable_enabled":true, + "view_counts_everywhere_api_enabled":true, + "longform_notetweets_consumption_enabled":true, + "responsive_web_twitter_article_tweet_consumption_enabled":false, + "tweet_awards_web_tipping_enabled":false, + "freedom_of_speech_not_reach_fetch_enabled":true, + "standardized_nudges_misinfo":true, + "tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":true, + "longform_notetweets_rich_text_read_enabled":true, + "longform_notetweets_inline_media_enabled":true, + "responsive_web_media_download_video_enabled":false, + "responsive_web_enhance_cards_enabled":false + }; + let url = encodeURI(`${base_url}?variables=${JSON.stringify(variables)}&features=${JSON.stringify(features)}`); + let cookies = this.getCookie(); + let headers = { + 'authorization': 'Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA', + 'x-twitter-active-user': 'yes', + 'x-twitter-client-language': cookies.lang, + 'x-csrf-token': cookies.ct0 + }; + if (cookies.ct0.length == 32) headers['x-guest-token'] = cookies.gt; + let tweet_detail = await fetch(url, {headers: headers}).then(result => result.json()); + let tweet_entrie = tweet_detail.data.threaded_conversation_with_injections_v2.instructions[0].entries.find(n => n.entryId == `tweet-${status_id}`); + let tweet_result = tweet_entrie.content.itemContent.tweet_results.result; + return tweet_result.tweet || tweet_result; + }, + getCookie: function (name) { + let cookies = {}; + document.cookie.split(';').filter(n => n.indexOf('=') > 0).forEach(n => { + n.replace(/^([^=]+)=(.+)$/, (match, name, value) => { + cookies[name.trim()] = value.trim(); + }); + }); + return name ? cookies[name] : cookies; + }, + storage: async function (value) { + let data = JSON.parse(localStorage.getItem('ufs-twitter-download_history') || '[]'); + let data_length = data.length; + if (value) { + if (Array.isArray(value)) data = data.concat(value); + else if (data.indexOf(value) < 0) data.push(value); + } else return data; + if (data.length > data_length) + localStorage.setItem('ufs-twitter-download_history', JSON.stringify(data)); + }, + storage_obsolete: function (is_remove) { + let data = JSON.parse(localStorage.getItem('history') || '[]'); + if (is_remove) localStorage.removeItem('history'); + else return data; + }, + formatDate: function (i, o, tz) { + let d = new Date(i); + if (tz) d.setMinutes(d.getMinutes() - d.getTimezoneOffset()); + let m = ['JAN','FEB','MAR','APR','MAY','JUN','JUL','AUG','SEP','OCT','NOV','DEC']; + let v = { + YYYY: d.getUTCFullYear().toString(), + YY: d.getUTCFullYear().toString(), + MM: d.getUTCMonth() + 1, + MMM: m[d.getUTCMonth()], + DD: d.getUTCDate(), + hh: d.getUTCHours(), + mm: d.getUTCMinutes(), + ss: d.getUTCSeconds(), + h2: d.getUTCHours() % 12, + ap: d.getUTCHours() < 12 ? 'AM' : 'PM' + }; + return o.replace(/(YY(YY)?|MMM?|DD|hh|mm|ss|h2|ap)/g, n => ('0' + v[n]).substr(-n.length)); + }, + downloader: (function () { + let tasks = [], thread = 0, max_thread = 2, retry = 0, max_retry = 2, failed = 0, notifier, has_failed = false; + return { + add: function (task) { + tasks.push(task); + if (thread < max_thread) { + thread += 1; + this.next(); + } else this.update(); + }, + next: async function () { + let task = tasks.shift(); + await this.start(task); + if (tasks.length > 0 && thread <= max_thread) this.next(); + else thread -= 1; + this.update(); + }, + start: function (task) { + this.update(); + return new Promise(resolve => { + UsefulScriptGlobalPageContext.Utils.downloadBlobUrl(task.url, task.name) + .then(() => { + task.onload(); + resolve(); + }) + .catch(e => { + this.retry(task, e); + resolve(); + }) + }); + }, + retry: function (task, result) { + retry += 1; + if (retry == 3) max_thread = 1; + if (task.retry && task.retry >= max_retry || + result.details && result.details.current == 'USER_CANCELED') { + task.onerror(result); + failed += 1; + } else { + if (max_thread == 1) task.retry = (task.retry || 0) + 1; + this.add(task); + } + }, + update: function() { + if (!notifier) { + notifier = document.createElement('div'); + notifier.title = 'Twitter Media Downloader'; + notifier.classList.add('tmd-notifier'); + notifier.innerHTML = '|'; + document.body.appendChild(notifier); + } + if (failed > 0 && !has_failed) { + has_failed = true; + notifier.innerHTML += '|'; + let clear = document.createElement('label'); + notifier.appendChild(clear); + clear.onclick = () => { + notifier.innerHTML = '|'; + failed = 0; + has_failed = false; + this.update(); + }; + } + notifier.firstChild.innerText = thread; + notifier.firstChild.nextElementSibling.innerText = tasks.length; + if (failed > 0) notifier.lastChild.innerText = failed; + if (thread > 0 || tasks.length > 0 || failed > 0) notifier.classList.add('running'); + else notifier.classList.remove('running'); + } + }; + })(), + language: { + en: {download: 'Download', completed: 'Download Completed', settings: 'Settings', dialog: {title: 'Download Settings', save: 'Save', save_history: 'Remember download history', clear_history: '(Clear)', clear_confirm: 'Clear download history?', show_sensitive: 'Always show sensitive content', pattern: 'File Name Pattern'}}, + ja: {download: 'ダウンロード', completed: 'ダウンロード完了', settings: '設定', dialog: {title: 'ダウンロード設定', save: '保存', save_history: 'ダウンロード履歴を保存する', clear_history: '(クリア)', clear_confirm: 'ダウンロード履歴を削除する?', show_sensitive: 'センシティブな内容を常に表示する', pattern: 'ファイル名パターン'}}, + zh: {download: '下载', completed: '下载完成', settings: '设置', dialog: {title: '下载设置', save: '保存', save_history: '保存下载记录', clear_history: '(清除)', clear_confirm: '确认要清除下载记录?', show_sensitive: '自动显示敏感的内容', pattern: '文件名格式'}}, + 'zh-Hant': {download: '下載', completed: '下載完成', settings: '設置', dialog: {title: '下載設置', save: '保存', save_history: '保存下載記錄', clear_history: '(清除)', clear_confirm: '確認要清除下載記錄?', show_sensitive: '自動顯示敏感的内容', pattern: '文件名規則'}} + }, + css: ` + .tmd-down {margin-left: 12px; order: 99;} + .tmd-down:hover > div > div > div > div {color: rgba(29, 161, 242, 1.0);} + .tmd-down:hover > div > div > div > div > div {background-color: rgba(29, 161, 242, 0.1);} + .tmd-down:active > div > div > div > div > div {background-color: rgba(29, 161, 242, 0.2);} + .tmd-down:hover svg {color: rgba(29, 161, 242, 1.0);} + .tmd-down:hover div:first-child:not(:last-child) {background-color: rgba(29, 161, 242, 0.1);} + .tmd-down:active div:first-child:not(:last-child) {background-color: rgba(29, 161, 242, 0.2);} + .tmd-down.tmd-media {position: absolute; right: 0;} + .tmd-down.tmd-media > div {display: flex; border-radius: 99px; margin: 2px;} + .tmd-down.tmd-media > div > div {display: flex; margin: 6px; color: #fff;} + .tmd-down.tmd-media:hover > div {background-color: rgba(255,255,255, 0.6);} + .tmd-down.tmd-media:hover > div > div {color: rgba(29, 161, 242, 1.0);} + .tmd-down.tmd-media:not(:hover) > div > div {filter: drop-shadow(0 0 1px #000);} + .tmd-down g {display: none;} + .tmd-down.download g.download, .tmd-down.completed g.completed, .tmd-down.loading g.loading,.tmd-down.failed g.failed {display: unset;} + .tmd-down.loading svg {animation: spin 1s linear infinite;} + @keyframes spin {0% {transform: rotate(0deg);} 100% {transform: rotate(360deg);}} + .tmd-btn {display: inline-block; background-color: #1DA1F2; color: #FFFFFF; padding: 0 20px; border-radius: 99px;} + .tmd-tag {display: inline-block; background-color: #FFFFFF; color: #1DA1F2; padding: 0 10px; border-radius: 10px; border: 1px solid #1DA1F2; font-weight: bold; margin: 5px;} + .tmd-btn:hover {background-color: rgba(29, 161, 242, 0.9);} + .tmd-tag:hover {background-color: rgba(29, 161, 242, 0.1);} + .tmd-notifier {display: none; position: fixed; left: 16px; bottom: 16px; color: #000; background: #fff; border: 1px solid #ccc; border-radius: 8px; padding: 4px;} + .tmd-notifier.running {display: flex; align-items: center;} + .tmd-notifier label {display: inline-flex; align-items: center; margin: 0 8px;} + .tmd-notifier label:before {content: " "; width: 32px; height: 16px; background-position: center; background-repeat: no-repeat;} + .tmd-notifier label:nth-child(1):before {background-image:url("data:image/svg+xml;charset=utf8,");} + .tmd-notifier label:nth-child(2):before {background-image:url("data:image/svg+xml;charset=utf8,");} + .tmd-notifier label:nth-child(3):before {background-image:url("data:image/svg+xml;charset=utf8,");} + .tmd-down.tmd-img {position: absolute; right: 0; bottom: 0; display: none !important;} + .tmd-down.tmd-img > div {display: flex; border-radius: 99px; margin: 2px; background-color: rgba(255,255,255, 0.6);} + .tmd-down.tmd-img > div > div {display: flex; margin: 6px; color: #fff !important;} + .tmd-down.tmd-img:not(:hover) > div > div {filter: drop-shadow(0 0 1px #000);} + .tmd-down.tmd-img:hover > div > div {color: rgba(29, 161, 242, 1.0);} + :hover > .tmd-down.tmd-img, .tmd-img.loading, .tmd-img.completed, .tmd-img.failed {display: block !important;} + .tweet-detail-action-item {width: 20% !important;} + `, + css_ss: ` + /* show sensitive in media tab */ + li[role="listitem"]>div>div>div>div:not(:last-child) {filter: none;} + li[role="listitem"]>div>div>div>div+div:last-child {display: none;} + `, + svg: ` + + + + + ` + }; + })(); + + TMD.init(); + }, +}; diff --git a/scripts/twitter_downloadButton.png b/scripts/twitter_downloadButton.png new file mode 100644 index 00000000..bbbe3077 Binary files /dev/null and b/scripts/twitter_downloadButton.png differ diff --git a/scripts/viewCookies.js b/scripts/viewCookies.js index 6051b089..4507322e 100644 --- a/scripts/viewCookies.js +++ b/scripts/viewCookies.js @@ -14,7 +14,8 @@ export default { if (c == "") { alert("There is No cookie here"); } else { - prompt("Cookies found:", decodeURI(c)); + c = decodeURI(c); + prompt(`Cookies found:\n\n${c}\n\nYou can copy cookie here:`, c); } }, }; diff --git a/scripts/vuiz_createLogo.js b/scripts/vuiz_createLogo.js new file mode 100644 index 00000000..ab38f4b3 --- /dev/null +++ b/scripts/vuiz_createLogo.js @@ -0,0 +1,12 @@ +export default { + icon: "https://m.vuiz.net/template/favicon.ico", + name: { + en: "Vuiz - create logo WAP online", + vi: "Vuiz - Tạo logo WAP online", + }, + description: { + en: "Create logo from text online", + vi: "Tạo logo chữ đẹp theo mẫu có sẵn", + }, + onClickExtension: () => window.open("https://m.vuiz.net/logo/"), +}; diff --git a/scripts/vuiz_getLink.js b/scripts/vuiz_getLink.js new file mode 100644 index 00000000..cc612327 --- /dev/null +++ b/scripts/vuiz_getLink.js @@ -0,0 +1,12 @@ +export default { + icon: "https://m.vuiz.net/template/favicon.ico", + name: { + en: "Vuiz - Get link audio/video/album", + vi: "Vuiz - Get link nhạc/video/album", + }, + description: { + en: "Support youtube, facebook, tiktok, zing tv, zing mp3, xVideo, nhac.vn, mixcloud, soundcloud, keeng.vn, chiasenhac, nhaccuatui, mediafire, ggdrive, dropbox, ondrive", + vi: "Hỗ trợ youtube, facebook, tiktok, zing tv, zing mp3, xVideo, nhac.vn, mixcloud, soundcloud, keeng.vn, chiasenhac, nhaccuatui, mediafire, ggdrive, dropbox, ondrive", + }, + onClickExtension: () => window.open("https://m.vuiz.net/"), +}; diff --git a/scripts/webToQRCode.js b/scripts/webToQRCode.js deleted file mode 100644 index 59327560..00000000 --- a/scripts/webToQRCode.js +++ /dev/null @@ -1,27 +0,0 @@ -export default { - icon: ``, - name: { - en: "URL to QR Code", - vi: "Lấy QRCode cho web hiện tại", - }, - description: { - en: "Convert current website URL to QR Code", - vi: "Chuyển URL của trang web sang QR Code", - }, - - onClick: function () { - var url = - "http://chart.apis.google.com/chart?cht=qr&chs=300x300&chl=" + - encodeURIComponent(location.href); - w = open( - url, - "w", - "location=no,status=yes,menubar=no,scrollbars=no,resizable=yes,width=500,height=500,modal=yes,dependent=yes" - ); - if (w) { - setTimeout("w.focus()", 1000); - } else { - location = url; - } - }, -}; diff --git a/scripts/youtube_downloadVideo.js b/scripts/youtube_downloadVideo.js index fcb9de0a..7ca13cb1 100644 --- a/scripts/youtube_downloadVideo.js +++ b/scripts/youtube_downloadVideo.js @@ -1,8 +1,8 @@ export default { icon: `https://www.youtube.com/s/desktop/ff71ea81/img/favicon_48x48.png`, name: { - en: "Download youtube video", - vi: "Tải video youtube", + en: "Download youtube video/audio", + vi: "Tải video/audio youtube", }, description: { en: "Bypass age restriction, without login", @@ -12,45 +12,41 @@ export default { onClick: function () { // https://stackoverflow.com/a/8260383/11898496 function getIdFromYoutubeURL(url) { - var regExp = + let regExp = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=)([^#\&\?]*).*/; - var match = url.match(regExp); + let match = url.match(regExp); return match && match[1].length == 11 ? match[1] : false; } let options = [ { name: "yt1s.com", - func: (url) => { - window.open("https://yt1s.com/vi/youtube-to-mp4?q=" + url); - }, + func: (url) => "https://yt1s.com/vi/youtube-to-mp4?q=" + url, }, { - name: "10downloader.com", - func: (url) => { - window.open("https://10downloader.com/download?v=" + url); - }, + name: "yt5s.com", + func: (url) => url.replace("youtube", "youtube5s"), }, { - name: "ymp4.com", - func: (url) => { - window.open("https://ymp4.download/en50/?url=" + url); - }, + name: "tubemp3.to", + func: (url) => "https://tubemp3.to/" + url, + }, + { + name: "10downloader.com", + func: (url) => "https://10downloader.com/download?v=" + url, }, { name: "9xbuddy.com", - func: (url) => { - window.open("https://9xbuddy.com/process?url=" + url); - }, + func: (url) => "https://9xbuddy.com/process?url=" + url, }, { name: "getlinks.vip", - url: "https://getlinks.vip/vi/youtube/", - func: (url) => { - window.open( - "https://getlinks.vip/vi/youtube/" + getIdFromYoutubeURL(url) - ); - }, + func: (url) => + "https://getlinks.vip/vi/youtube/" + getIdFromYoutubeURL(url), + }, + { + name: "ymp4.com", + func: (url) => "https://ymp4.download/en50/?url=" + url, }, ]; @@ -63,7 +59,14 @@ export default { if (choose != null && choose >= 0 && choose < options.length) { let url = prompt("Nhập link youtube:", location.href); - url && options[choose].func(url); + if (url) { + url = options[choose].func(url); + let myWin = window.open( + url, + "Download Youtube Video", + "directories=no,titlebar=no,toolbar=no,location=no,status=no,menubar=no,scrollbars=no,resizable=no,width=800, height=900" + ); + } } }, }; diff --git a/scripts/youtube_localDownloader.html b/scripts/youtube_localDownloader.html new file mode 100644 index 00000000..1c20924f --- /dev/null +++ b/scripts/youtube_localDownloader.html @@ -0,0 +1,17 @@ + + + + + + + Useful Script - YouTube Local Downloader + + + + + + + + + + diff --git a/scripts/youtube_localDownloader.js b/scripts/youtube_localDownloader.js new file mode 100644 index 00000000..4db11b90 --- /dev/null +++ b/scripts/youtube_localDownloader.js @@ -0,0 +1,33 @@ +import { runScriptInCurrentTab } from "./helpers/utils.js"; + +export default { + icon: "https://www.youtube.com/s/desktop/accca349/img/favicon_48x48.png", + name: { + en: "Youtube local downloader", + vi: "Youtube tải video local", + }, + description: { + en: "", + vi: "", + }, + + whiteList: ["https://*youtube.com/*"], + + onClickExtension: async () => { + const yt_data = await runScriptInCurrentTab(() => { + return document.getElementsByTagName("ytd-app")[0].data.playerResponse; + }); + + if (!yt_data) { + alert("Không tìm thấy video data"); + return; + } + + localStorage.setItem( + "ufs_youtube_localDownloader", + JSON.stringify(yt_data) + ); + + window.open("/scripts/youtube_localDownloader.html"); + }, +}; diff --git a/scripts/youtube_localDownloader_main.js b/scripts/youtube_localDownloader_main.js new file mode 100644 index 00000000..cc05bd70 --- /dev/null +++ b/scripts/youtube_localDownloader_main.js @@ -0,0 +1,130 @@ +const { formatSize, promiseAllStepN } = UsefulScriptGlobalPageContext.Utils; +const { injectScriptSrc } = UsefulScriptGlobalPageContext.DOM; + +const xhrDownloadUint8Array = async ({ url, contentLength }, progressCb) => { + if (typeof contentLength === "string") + contentLength = parseInt(contentLength); + progressCb({ + loaded: 0, + total: contentLength, + speed: 0, + }); + const chunkSize = 65536; + const getBuffer = (start, end) => + fetch(url + `&range=${start}-${end ? end - 1 : ""}`).then((r) => + r.arrayBuffer() + ); + const data = new Uint8Array(contentLength); + let downloaded = 0; + const tasks = []; + const startTime = Date.now(); + + for (let start = 0; start < contentLength; start += chunkSize) { + const exceeded = start + chunkSize > contentLength; + const curChunkSize = exceeded ? contentLength - start : chunkSize; + const end = exceeded ? null : start + chunkSize; + tasks.push(() => { + console.log("dl start", url, start, end); + return getBuffer(start, end) + .then((buf) => { + console.log("dl done", url, start, end); + downloaded += curChunkSize; + data.set(new Uint8Array(buf), start); + const ds = (Date.now() - startTime + 1) / 1000; + progressCb({ + loaded: downloaded, + total: contentLength, + speed: downloaded / ds, + }); + }) + .catch((err) => { + console.log("Download error"); + }); + }); + } + await promiseAllStepN(6, tasks); + return data; +}; + +window.onload = () => { + const yt_data = JSON.parse( + localStorage.getItem("ufs_youtube_localDownloader") ?? "{}" + ); + + console.log(yt_data); + + const streamingData = [ + ...(yt_data.streamingData?.formats || []), + ...(yt_data.streamingData?.adaptiveFormats || []), + ].filter((_) => _.url); + + const videoDetails = yt_data.videoDetails; + const videos = streamingData.filter((d) => d.mimeType.includes("video")); + const audios = streamingData.filter((d) => d.mimeType.includes("audio")); + const captions = + yt_data.captions?.playerCaptionsTracklistRenderer?.captionTracks || []; + + console.log(captions); + + // video details + const detailDiv = document.createElement("div"); + const thumbs = videoDetails.thumbnail?.thumbnails || []; + const lastThumbnail = thumbs[thumbs.length - 1]; + + detailDiv.innerHTML = ` + +

    ${videoDetails.title}

    +

    ${videoDetails.author}

    +

    ${videoDetails.shortDescription}

    `; + document.body.appendChild(detailDiv); + + // video + for (let video of videos) { + const button = document.createElement("button"); + button.style = "display:block;margin-bottom:5px"; + button.innerHTML = ` + ${video.qualityLabel} + - ${video.width}x${video.height} + - ${formatSize(video.contentLength, 2)} + ${video.audioQuality ? "" : " (no audio)"}`; + button.onclick = () => { + let data = xhrDownloadUint8Array(video, (progress) => { + console.log(progress); + }); + // saveAs(video.url, "video.mp4", { + // onprogress: (e) => { + // console.log(e); + // }, + // }); + }; + document.body.appendChild(button); + } + + // audio + for (let audio of audios) { + const div = document.createElement("div"); + div.innerHTML = ` + +

    + ${audio.audioTrack?.displayName || audio.audioQuality} + - ${formatSize(audio.contentLength, 2)} +

    + `; + document.body.appendChild(div); + } + + // caption + for (let caption of captions) { + const div = document.createElement("div"); + div.innerHTML = ` +

    ${caption.name?.simpleText}

    + + Download + + `; + document.body.appendChild(div); + } +}; diff --git a/scripts/youtube_nonstop.js b/scripts/youtube_nonstop.js index bf550260..79ebb70a 100644 --- a/scripts/youtube_nonstop.js +++ b/scripts/youtube_nonstop.js @@ -1,5 +1,5 @@ export default { - icon: "https://lh3.googleusercontent.com/OS9P4SJOFAg8lhCyaRTJ7y4ADF0TGpqFF904BcpCtdBjJIDBbNb_J8PpgoJ9QvariiG_RzgH8fCSSY_kQu-chQQ0Aw=w128-h128-e365-rj-sc0x00ffffff", + icon: "https://www.youtube.com/s/desktop/accca349/img/favicon_48x48.png", name: { en: "Youtube nonstop", vi: "Youtube nonstop", @@ -8,7 +8,7 @@ export default { en: 'Kiss the annoying "Video paused. Continue watching?" confirmation goodbye!', vi: "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.", }, - whiteList: ["*://music.youtube.com/*", "*://www.youtube.com/*"], + whiteList: ["*://music.youtube.com/*", "*://www.youtube.com/*"], onClick: function () { // source code from: https://chrome.google.com/webstore/detail/youtube-nonstop/nlkaejimjacpillmajjnopmpbkbnocid diff --git a/scripts/youtube_toggleLight.js b/scripts/youtube_toggleLight.js index d4b1307e..4c8056b5 100644 --- a/scripts/youtube_toggleLight.js +++ b/scripts/youtube_toggleLight.js @@ -8,7 +8,7 @@ export default { en: "Toggle light on/off to focus to youtube video", vi: "Tắt/Mở đèn để tập trung xem video youtube", }, - whiteList: ["*://www.youtube.com/*"], + whiteList: ["*://www.youtube.com/*"], onClick: function () { ["#below", "#secondary", "#masthead-container"].forEach((_) => { @@ -21,8 +21,9 @@ export default { }); }); - document.querySelector("#player-theater-container")?.scrollIntoView?.({ + document.querySelector("ytd-player")?.scrollIntoView?.({ behavior: "smooth", + block: "center", }); }, }; diff --git a/scripts/zingmp3_downloadMusic.js b/scripts/zingmp3_downloadMusic.js index c9500fdd..9d1b7f95 100644 --- a/scripts/zingmp3_downloadMusic.js +++ b/scripts/zingmp3_downloadMusic.js @@ -1,3 +1,5 @@ +import { getCurrentTab } from "./helpers/utils.js"; + export default { icon: "https://zjs.zmdcdn.me/zmp3-desktop/releases/v1.7.64/static/media/icon_zing_mp3_60.f6b51045.svg", name: { @@ -227,16 +229,40 @@ export default { }; (async function () { - // window.open(await ZingMp3.search('game thủ liên minh')); + // window.open(await ZingMp3.search("game thủ liên minh")); // window.open(await ZingMp3.getLastPlaying()); // window.open(await ZingMp3.getHome()); // window.open(await ZingMp3.getChartHome()); - // window.open(await ZingMp3.getInfoMusic('ZWFE8OUO')) + // window.open(await ZingMp3.getInfoMusic("ZWFE8OUO")); + // window.open(await ZingMp3.getStreaming("Z6WZD78I")); - let url = prompt("Nhap link bai hat: ", location.href); + const tab = await getCurrentTab(); + let url = prompt("Nhap link bai hat: ", tab.url); if (url) { let songid = ZingMp3.getSongIdFromURL(url); - if (songid) window.open(await ZingMp3.getStreaming(songid)); + if (songid) { + try { + const streamUrl = await ZingMp3.getStreaming(songid); + const res = await fetch(streamUrl); + const json = await res.json(); + console.log(json); + if (json.err) throw new Error(json.msg); + if (!json.data) throw new Error("No Data"); + + let options = Object.keys(json.data); + let choice = prompt( + "Chọn chất lượng nhạc: \n" + options.join("\n"), + options[options.length - 1] + ); + + if (choice !== null) { + let url = json.data[choice]; + window.open(url); + } + } catch (error) { + alert("ERROR: " + error); + } + } } })(); }, diff --git a/working_note.md b/working_note.md new file mode 100644 index 00000000..d2dff8d6 --- /dev/null +++ b/working_note.md @@ -0,0 +1,29 @@ +# WORKING NOTES + +## 29/03/2024 + +- [ ] fb_invisible_message có vẻ không hoạt động + +- [ ] fb_whoIsTyping có vẻ không hoạt động + +- [ ] zing mp3 - có api ngon mà chưa dùng hết chức năng + +- [ ] tất cả các script inject code vào website cần update để tuân thủ TrustedScript + +- [ ] Tổng hợp các chức năng tải hàng loạt fb vào 1 trang web mới + +- [ ] Test thử [rapid api](https://rapidapi.com/) + +- [ ] **(IN-PROGRESS)** Youtube local download => lấy data từ ytplayer.config.args.raw_player_response => Làm UI + +- [ ] html2img khá ngon, nhưng chưa biết xài vô cái gì + +- [ ] Crack tải pdf trên tailieugiangday.vn dễ vl + +- [ ] Optimize import jszip + +- [x] làm cho xong soundcloud_downloadMusic hoặc xóa => Xong rồi, Ngon + +- [ ] Move transfer.sh sang popup + +- [ ] text to qrcode không còn hoạt động