您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Connexion automatique, boutons de téléchargement dans les résultats de recherche, prévisualisation des images de torrent, et bien d'autres améliorations.
// ==UserScript== // @name YggTorrent Plus // @match https://www.ygg.re/* // @version 1.25.1 // @author J.H / clemente / Binouchette // @license MIT // @description Connexion automatique, boutons de téléchargement dans les résultats de recherche, prévisualisation des images de torrent, et bien d'autres améliorations. // @run-at document-end // @noframes // @grant GM.getValue // @grant GM.setValue // @grant GM.deleteValue // @grant GM_addStyle // @grant GM_getResourceURL // @grant GM_xmlhttpRequest // @namespace https://greasyfork.org/fr/scripts/497739 // @icon https://www.google.com/s2/favicons?sz=64&domain=ygg.re // ==/UserScript== (async function() { const CONFIG = { HOVER_TABLE: true, // Change la couleur de fond du torrent au survol dans les résultats de recherche LOGIN_AUTOMATICALLY: true, // Se connecte automatiquement (première connexion manuel obligatoire) PREVIEWS_IMAGES_ON_HOVER: true, // Affiche une prévisualisation de l'image du torrent au survol PREVIEWS_IMAGES_SIZE: 400, // Taille des images de prévisualisation (en pixels) DOWNLOAD_BUTTONS_IN_RESULTS: true, // Ajoute un bouton de téléchargement dans les résultats de recherche HIDE_SIDEBAR: false, // Masque la barre latérale LARGER_NFO: true, // Agrandit la fenêtre de prévisualisation du NFO SEARCH_BY_LATEST_FIRST: true, // Affiche les résultats de recherche par date de publication (du plus récent au plus ancien) KEEP_SEARCH_WHEN_CLICKING_ON_SUBCATEGORY_ICON: true, // Garde les critères de recherche lorsqu'on clique sur une catégorie }; const SELECTORS = { HOVER_TABLE_LIGNE: '.table-responsive', REGISTER_BUTTON: '.register', TORRENT_NAME_LINK: 'a[id="torrent_name"]', RESULTS_TABLE_ROW: '.results table tbody tr', INPUT_USERNAME: 'input[name="id"]', INPUT_PASSWORD: 'input[name="pass"]', INPUT_SUBMIT: 'button[type="submit"]', LOGOUT_LINK: 'a[href="https://www.ygg.re/user/logout"]', NFO_MODAL: 'nfoModal', SEARCH_FORM: 'form.search', LOGIN_FORM: '.login-form', }; const CONSTANTS = { IMAGE_MODAL_STYLE: `min-width: ${CONFIG.PREVIEWS_IMAGES_SIZE}px; max-width: ${CONFIG.PREVIEWS_IMAGES_SIZE}px;`, COOKIES_STORAGE_KEY: 'yggtorrent_credentials', MOUSEENTER_DELAY: 100, }; CONFIG.HOVER_TABLE && hoverTable(); CONFIG.DOWNLOAD_BUTTONS_IN_RESULTS && addDownloadButtonToTorrents(); CONFIG.HIDE_SIDEBAR && hideSidebar(); CONFIG.LARGER_NFO && displayLargerNfo(); CONFIG.SEARCH_BY_LATEST_FIRST && searchByLatestFirst(); CONFIG.KEEP_SEARCH_WHEN_CLICKING_ON_SUBCATEGORY_ICON && keepSearchWhenClickingOnSubcategoryIcon(); CONFIG.PREVIEWS_IMAGES_ON_HOVER && displayImageHandler(); CONFIG.LOGIN_AUTOMATICALLY && await handleLogin(); async function hoverTable() { const style = document.createElement('style'); const lignes = document.querySelector(SELECTORS.HOVER_TABLE_LIGNE); if(lignes) { style.textContent = ` .table td { padding: .35rem !important; } .table td:hover { background-color: rgba(246, 246, 246, 0.5) !important; } .table tr:hover { background-color: rgba(246, 246, 246, 0.5) !important; opacity: .8 !important; } `; document.head.appendChild(style); } } async function handleLogin() { const isNotLoggedIn = document.querySelector(SELECTORS.REGISTER_BUTTON); const savedCredentials = await GM.getValue(CONSTANTS.COOKIES_STORAGE_KEY); if (isNotLoggedIn && !savedCredentials) { await getCredentials(); } else if (isNotLoggedIn && savedCredentials) { await autoLogin(savedCredentials); } } async function getCredentials() { try { const loginForm = document.querySelector(SELECTORS.LOGIN_FORM); if (loginForm) { loginForm.addEventListener('submit', async (e) => { e.preventDefault(); const usernameInput = loginForm.querySelector(SELECTORS.INPUT_USERNAME); const passwordInput = loginForm.querySelector(SELECTORS.INPUT_PASSWORD); await saveCredentials(usernameInput.value, passwordInput.value); }); } } catch (error) { console.error('Error getting credentials:', error); } } async function autoLogin(credentials) { try { const loginForm = document.querySelector(SELECTORS.LOGIN_FORM); if (loginForm) { const usernameInput = loginForm.querySelector(SELECTORS.INPUT_USERNAME); const passwordInput = loginForm.querySelector(SELECTORS.INPUT_PASSWORD); const submitButton = loginForm.querySelector(SELECTORS.INPUT_SUBMIT); if (usernameInput && passwordInput && submitButton) { usernameInput.value = credentials.username; passwordInput.value = credentials.password; submitButton.click(); } } } catch (error) { console.error('Error during login:', error); } } const logoutLink = document.querySelector(SELECTORS.LOGOUT_LINK); if (logoutLink) { logoutLink.addEventListener('click', deleteCredentials); } async function deleteCredentials() { try { await GM.deleteValue(CONSTANTS.COOKIES_STORAGE_KEY); } catch (error) { console.error('Error deleting credentials:', error); } } async function saveCredentials(username, password) { try { await GM.setValue(CONSTANTS.COOKIES_STORAGE_KEY, { username, password }); } catch (error) { console.error('Error saving credentials:', error); } } function fetchImageSize(url) { return new Promise((resolve, reject) => { const img = new Image(); img.onload = () => resolve({ width: img.width, height: img.height, url }); img.onerror = reject; img.src = url; }); } function makeGetRequest(url) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url, onload: function(response) { resolve(response.responseText); }, onerror: function(error) { reject(error); }, }); }); } async function getImages(url) { const imageRet = []; try { const response = await makeGetRequest(url); const parser = new DOMParser(); const doc = parser.parseFromString(response, 'text/html'); if (doc.title.includes('Just a moment...')) { imageRet.push({ width: 250, height: 250, url: 'https://i.ibb.co/DQb8WVj/cflr.jpg', }); return imageRet; } const imgElements = doc.getElementsByTagName('img'); for (const imgElement of imgElements) { if (imgElement.src.includes('summer_icon') || imgElement.src.includes('yggtorrent') || imgElement.src.includes('avatars') || imgElement.src.startsWith('data:image/png;base64')) { continue; } const image = await fetchImageSize(imgElement.src); if (image.width > 250 && image.height > 250) { imageRet.push(image); break; } } } catch (error) { console.error('Error fetching images:', error); } return imageRet; } function displayImageHandler() { createImageModal(); let timeout = null; let canDisplay = false; const torrents = document.querySelectorAll(SELECTORS.RESULTS_TABLE_ROW); if (!torrents) return; torrents.forEach((torrent) => { const torrentNameLink = torrent.querySelector(SELECTORS.TORRENT_NAME_LINK); if (!torrentNameLink) return; torrentNameLink.addEventListener('mouseenter', async (e) => { timeout = setTimeout(async () => { if (e.target.id !== 'torrent_name') return; canDisplay = true; const images = await getImages(e.target.href); if (canDisplay) { displayImageModal(e, images[0]); } }, CONSTANTS.MOUSEENTER_DELAY); }); torrentNameLink.addEventListener('mousemove', (e) => { const modal = document.getElementById('imageModal'); if (modal) { const mouseY = e.clientY - 25; const outOfScreen = mouseY + document.getElementById('imageModalImage').offsetHeight - window.innerHeight; modal.style.top = outOfScreen > -25 ? mouseY - outOfScreen - 25 + 'px' : mouseY + 'px'; modal.style.left = e.clientX + 125 + 'px'; } }); torrentNameLink.addEventListener('mouseout', () => { document.getElementById('imageModal').style.display = 'none'; canDisplay = false; clearTimeout(timeout); }); }); } function createImageModal() { const modal = document.createElement('div'); modal.id = 'imageModal'; modal.classList.add('modal'); modal.style.display = 'none'; const modalImage = document.createElement('img'); modalImage.id = 'imageModalImage'; modalImage.classList.add('modal-content'); modalImage.style = CONSTANTS.IMAGE_MODAL_STYLE; modal.appendChild(modalImage); document.body.append(modal); } function displayImageModal(event, image) { const modal = document.getElementById('imageModal'); if (modal) { const mouseY = event.clientY - 25; const modalImage = document.getElementById('imageModalImage'); modalImage.src = image.url; modal.style.left = event.clientX + 125 + 'px'; modal.style.display = 'block'; const outOfScreen = mouseY + modalImage.offsetHeight - window.innerHeight; modal.style.top = outOfScreen > -25 ? mouseY - outOfScreen - 25 + 'px' : mouseY + 'px'; } } function addDownloadButtonToTorrents() { const torrents = document.querySelectorAll(SELECTORS.RESULTS_TABLE_ROW); torrents.forEach((torrent) => { const torrentId = torrent.querySelector('a[target]')?.target; if (!torrentId) return; const downloadIcon = document.createElement('span'); downloadIcon.classList.add('ico_download'); downloadIcon.classList.add('custom_ygg'); const downloadButton = document.createElement('a'); downloadButton.href = `/engine/download_torrent?id=${torrentId}`; downloadButton.append(downloadIcon); downloadButton.style = 'color: rgb(98, 219, 168); vertical-align: middle; margin-right: 10px;'; const nameLink = torrent.querySelector('td:nth-child(3) a'); if (nameLink) { nameLink.parentNode.insertBefore(downloadButton, nameLink); } }); } function hideSidebar() { const sidebar = document.getElementById('cat'); if (sidebar && sidebar.classList.contains('active')) { sidebar.querySelector('.open').click(); } } function displayLargerNfo() { const modal = document.getElementById(SELECTORS.NFO_MODAL); if (!modal) return; const modalDialog = modal.querySelector('.modal-dialog'); modalDialog.classList.remove('modal-sm'); modalDialog.classList.add('modal-lg'); const nfoDiv = document.querySelector('#nfo'); if (nfoDiv) { GM_addStyle(` @import url('https://fonts.googleapis.com/css2?family=Source+Code+Pro&display=swap&display=swap'); `); GM_addStyle(` #nfo { padding: 0 !important; } #nfo pre { font-family: 'Source Code Pro', monospace !important; font-optical-sizing: auto !important; font-weight: 500 !important; font-style: normal !important; background-color: #fff !important; color: #4d4d4d !important; margin: 0 !important; padding: 2rem !important; text-align: center !important; } `); /* nfoDiv.style.background = '#ffffff !important'; nfoDiv.style.color = '#4d4d4d !important'; nfoDiv.style.margin = 'auto !important'; */ } } function searchByLatestFirst() { const searchForm = document.querySelector(SELECTORS.SEARCH_FORM); if (!searchForm) return; const orderInput = document.createElement('input'); orderInput.name = 'order'; orderInput.value = 'desc'; orderInput.style = 'display: none'; const sortInput = document.createElement('input'); sortInput.name = 'sort'; sortInput.value = 'publish_date'; sortInput.style = 'display: none'; searchForm.append(orderInput); searchForm.append(sortInput); } function keepSearchWhenClickingOnSubcategoryIcon() { document.querySelectorAll('[class^="tag_subcat_"]').forEach((node) => { const subcategoryId = node.className.split('tag_subcat_')[1]; node.parentNode.href = `${document.URL}&sub_category=${subcategoryId}`; }); } })();