// ==UserScript== // @name iOS App Download Link Extractor // @namespace https://github.com/WangONC/ios-app-download-link-extractor // @version 0.9.0 // @description Extracts the IPA download link from itms-services URLs or displays an error message, shown next to the original button. // @author WangONC // @source https://github.com/WangONC/ios-app-download-link-extractor // @match *://*/* // @grant GM.xmlHttpRequest // @license MIT // @name:en iOS App Download Link Extractor // @description:en Extracts the IPA download link from itms-services URLs or displays an error message, shown next to the original button. // @name:zh-CN IPA提取助手 // @description:zh-CN 从 itms-services 链接中提取 IPA 下载直链或显示错误提示,并显示在原始按钮旁边 // @name:zh-TW IPA提取助手 // @description:zh-TW 從 itms-services 連結中提取 IPA 下載直鍊或顯示錯誤提示,並顯示在原始按鈕旁邊 // @name:ar مُستخرج رابط تنزيل تطبيقات iOS // @description:ar يستخرج رابط تنزيل IPA من عناوين itms-services أو يعرض رسالة خطأ، تُظهر بجانب الزر الأصلي // @name:bg Екстрактор на връзки за изтегляне на iOS приложения // @description:bg Извлича връзката за изтегляне на IPA от itms-services URL-и или показва съобщение за грешка до оригиналния бутон // @name:ckb دەرهێنەری بەستەری داگرتنی ئەپی iOS // @description:ckb بەستەری داگرتنی IPA لە itms-services URLەکان دەرهێنەرە یان پەیامی هەڵە نیشان دەدات، کە لە تەنیشتی دوگمەی سەرەکی دەردەکەوێت // @name:cs Extraktor odkazů ke stažení aplikací pro iOS // @description:cs Extrahuje odkaz na stažení IPA z adres itms-services nebo zobrazí chybovou zprávu vedle původního tlačítka // @name:da iOS App Download Link Extractor // @description:da Uddrager IPA-downloadlinket fra itms-services URL'er eller viser en fejlmeddelelse ved siden af den originale knap // @name:de iOS App Download Link Extractor // @description:de Extrahiert den IPA-Download-Link aus itms-services URLs oder zeigt eine Fehlermeldung neben dem ursprünglichen Button an // @name:el Εξαγωγέας Συνδέσμων Λήψης Εφαρμογών iOS // @description:el Εξάγει τον σύνδεσμο λήψης IPA από URLs itms-services ή εμφανίζει ένα μήνυμα σφάλματος δίπλα στο αρχικό κουμπί // @name:eo Eltiraĵo de Elŝuta Ligilo por iOS-Aplikoj // @description:eo Eltiras la IPA-elŝutan ligilon el itms-services URL-oj aŭ montras erarmesaĝon apud la originala butono // @name:es Extractor de Enlaces de Descarga de Aplicaciones iOS // @description:es Extrae el enlace de descarga de IPA de las URLs de itms-services o muestra un mensaje de error junto al botón original // @name:es-419 Extractor de Enlaces de Descarga de Aplicaciones iOS // @description:es-419 Extrae el enlace de descarga de IPA de las URLs de itms-services o muestra un mensaje de error junto al botón original // @name:fi iOS-sovelluksen latauslinkin poimija // @description:fi Puraisee IPA-latauslinkin itms-services-URL-osoitteista tai näyttää virheilmoituksen alkuperäisen painikkeen vieressä // @name:fr Extracteur de liens de téléchargement d'applications iOS // @description:fr Extrait le lien de téléchargement IPA des URL itms-services ou affiche un message d'erreur à côté du bouton d'origine // @name:fr-CA Extracteur de liens de téléchargement d'applications iOS // @description:fr-CA Extrait le lien de téléchargement IPA des URL itms-services ou affiche un message d'erreur à côté du bouton d'origine // @name:he מחלץ קישורי הורדה לאפליקציות iOS // @description:he מחלץ את קישור ההורדה של IPA מכתובות itms-services או מציג הודעת שגיאה ליד הכפתור המקורי // @name:hr Izvlači poveznice za preuzimanje iOS aplikacija // @description:hr Izvlači poveznicu za preuzimanje IPA iz itms-services URL-ova ili prikazuje poruku o grešci pored originalnog gumba // @name:hu iOS App Letöltési Link Kivonó // @description:hu Kinyeri az IPA letöltési linket az itms-services URL-ekből vagy hibaüzenetet jelenít meg az eredeti gomb mellett // @name:id Pengekstrak Tautan Unduhan Aplikasi iOS // @description:id Mengekstrak tautan unduhan IPA dari URL itms-services atau menampilkan pesan kesalahan di samping tombol asli // @name:it Estrattore di Link di Download per App iOS // @description:it Estrae il link di download IPA dagli URL itms-services o visualizza un messaggio di errore accanto al pulsante originale // @name:ja iOSアプリダウンロードリンク抽出ツール // @description:ja itms-services URLからIPAダウンロードリンクを抽出するか、エラーメッセージを元のボタンの横に表示します // @name:ka iOS აპლიკაციის ჩამოტვირთვის ბმულის ამომღები // @description:ka ამოიღებს IPA-ს ჩამოტვირთვის ბმულს itms-services URL-ებიდან ან აჩვენებს შეცდომის შეტყობინებას ორიგინალური ღილაკის გვერდით // @name:ko iOS 앱 다운로드 링크 추출기 // @description:ko itms-services URL에서 IPA 다운로드 링크를 추출하거나 오류 메시지를 원래 버튼 옆에 표시합니다 // @name:mr iOS अॅप डाउनलोड लिंक एक्स्ट्रॅक्टर // @description:mr itms-services URL मधून IPA डाउनलोड लिंक काढून किंवा त्रुटी संदेश दाखवून, मूळ बटणाजवळ दर्शविलेला // @name:nb iOS App Nedlastingslenke Ekstraktor // @description:nb Trekker ut IPA-nedlastingslenken fra itms-services URL-er eller viser en feilmelding ved siden av den opprinnelige knappen // @name:nl iOS App Download Link Extractor // @description:nl Extraheert de IPA-downloadlink van itms-services URL's of toont een foutmelding naast de originele knop // @name:pl Ekstraktor linków do pobierania aplikacji iOS // @description:pl Wyciąga link do pobrania IPA z adresów URL itms-services lub wyświetla komunikat o błędzie obok oryginalnego przycisku // @name:pt Extrator de Link de Download de Aplicativos iOS // @description:pt Extrai o link de download do IPA de URLs itms-services ou exibe uma mensagem de erro ao lado do botão original // @name:pt-BR Extrator de Link de Download de Aplicativos iOS // @description:pt-BR Extrai o link de download do IPA de URLs itms-services ou exibe uma mensagem de erro ao lado do botão original // @name:ro Extragător de Linkuri de Descărcare pentru Aplicații iOS // @description:ro Extrage linkul de descărcare IPA din URL-urile itms-services sau afișează un mesaj de eroare lângă butonul original // @name:ru Экстрактор ссылок на скачивание приложений iOS // @description:ru Извлекает ссылку на скачивание IPA из URL-адресов itms-services или отображает сообщение об ошибке рядом с оригинальной кнопкой // @name:sk Extraktor odkazov na stiahnutie aplikácií pre iOS // @description:sk Extrahuje odkaz na stiahnutie IPA z adries itms-services alebo zobrazí chybovú správu vedľa pôvodného tlačidla // @name:sr Екстрактор веза за преузимање iOS апликација // @description:sr Екстрахује везу за преузимање IPA из itms-services URL-ова или приказује поруку о грешци поред оригиналног дугмета // @name:sv iOS App Nedladdningslänk Extraktor // @description:sv Extraherar IPA-nedladdningslänken från itms-services URL:er eller visar ett felmeddelande bredvid den ursprungliga knappen // @name:th ตัวดึงลิงค์ดาวน์โหลดแอป iOS // @description:th ดึงลิงค์ดาวน์โหลด IPA จาก URL itms-services หรือแสดงข้อความผิดพลาดข้างปุ่มต้นฉบับ // @name:tr iOS Uygulama İndirme Bağlantısı Çıkarıcı // @description:tr itms-services URL'lerinden IPA indirme bağlantısını çıkarır veya orijinal butonun yanında bir hata mesajı gösterir // @name:uk Екстрактор посилань для завантаження додатків iOS // @description:uk Витягує посилання на завантаження IPA з URL-адрес itms-services або відображає повідомлення про помилку поруч з оригінальною кнопкою // @name:ug iOS ئەپ تۆۋەنلىك ئۇلىنىشىنى چىقىرىش // @description:ug itms-services URL لىرىدىن IPA چۈشۈرۈش ئۇلىنىشىنى چىقىرىپ ياكى ئەسلى كۇنۇپكا يېنىدا خاتالىق ئۇچۇرىنى كۆرسىتىدۇ // @name:vi Trích xuất liên kết tải xuống ứng dụng iOS // @description:vi Trích xuất liên kết tải IPA từ URL itms-services hoặc hiển thị thông báo lỗi bên cạnh nút gốc // @downloadURL https://update.greasyfork.org/scripts/528616/IPA%E6%8F%90%E5%8F%96%E5%8A%A9%E6%89%8B.user.js // @updateURL https://update.greasyfork.org/scripts/528616/IPA%E6%8F%90%E5%8F%96%E5%8A%A9%E6%89%8B.meta.js // ==/UserScript== (function() { 'use strict'; const MAX_RETRIES = 3; // 最大重试次数 const RETRY_INTERVAL = 1000; // 重试间隔 const TIMEOUT = 500; // 超时时间 function fetchPlist(plistUrl, retryCount, link) { GM.xmlHttpRequest({ method: 'GET', url: plistUrl, timeout: TIMEOUT, // 设置超时时间 onload: function(response) { if (response.status === 200) { let xmlText = response.responseText; let parser = new DOMParser(); let xmlDoc = parser.parseFromString(xmlText, 'text/xml'); let downloadUrl = extractDownloadUrl(xmlDoc); if (downloadUrl) { let downloadLink = document.createElement('a'); downloadLink.href = downloadUrl; downloadLink.target = '_blank'; // 新窗口打开,方便下载 downloadLink.textContent = 'Download IPA'; downloadLink.style.marginLeft = '13px'; downloadLink.classList.add('ipa-download-link'); // 添加类名以便识别 link.insertAdjacentElement('afterend', downloadLink); } else { showError(link, 'Unable to parse plist file'); } } else { showError(link, `Request failed: ${response.status}`); } }, onerror: function() { if (retryCount > 0) { setTimeout(() => { fetchPlist(plistUrl, retryCount - 1, link); // 递归重试 console.log(`当前重试 ${retryCount - 1}`); }, RETRY_INTERVAL); } else { showError(link, 'Network Error'); } }, ontimeout: function() { if (retryCount > 0) { setTimeout(() => { fetchPlist(plistUrl, retryCount - 1, link); console.log(`当前重试 ${retryCount - 1}`); }, RETRY_INTERVAL); } else { showError(link, 'Request timed out'); } } }); } // 处理页面中的链接 function processLinks() { let links = document.querySelectorAll('a[href^="itms-services://?action=download-manifest&url="]'); if (links.length === 0) { // console.log('No matching links found'); return; } for (let link of links) { // 检查新添加的链接 if (link.nextElementSibling && (link.nextElementSibling.classList.contains('ipa-download-link') || link.nextElementSibling.classList.contains('ipa-error-link'))) { continue; } // 检查是否是已经处理过的链接 if (link.getAttribute('ipa-data-processed') === 'true') { continue; } // 标记该链接为已处理 link.setAttribute('ipa-data-processed', 'true'); try { let href = link.href; let query = href.split('?')[1]; if (!query) throw new Error('Invalid link format'); let params = new URLSearchParams(query); let plistUrl = params.get('url'); if (!plistUrl) throw new Error('No "url" parameter found'); // 解码 plistUrl 以处理 URL 编码 plistUrl = decodeURIComponent(plistUrl); fetchPlist(plistUrl, MAX_RETRIES, link); } catch (e) { showError(link, e.message); } } } // 提取下载链接的核心函数 function extractDownloadUrl(xmlDoc) { let dict = xmlDoc.querySelector('plist > dict'); if (!dict) return null; let keys = Array.from(dict.children).filter(el => el.tagName === 'key'); let itemsKey = keys.find(key => key.textContent === 'items'); if (!itemsKey) return null; let itemsArray = itemsKey.nextElementSibling; if (!itemsArray || itemsArray.tagName !== 'array') return null; let firstItem = itemsArray.querySelector('dict'); if (!firstItem) return null; let assetsKey = Array.from(firstItem.children).find(el => el.tagName === 'key' && el.textContent === 'assets'); if (!assetsKey) return null; let assetsArray = assetsKey.nextElementSibling; if (!assetsArray || assetsArray.tagName !== 'array') return null; let softwarePackageDict = Array.from(assetsArray.children).find(dict => { let kindKey = Array.from(dict.children).find(key => key.tagName === 'key' && key.textContent === 'kind'); if (kindKey && kindKey.nextElementSibling.textContent === 'software-package') { return true; } return false; }); if (!softwarePackageDict) return null; let urlKey = Array.from(softwarePackageDict.children).find(el => el.tagName === 'key' && el.textContent === 'url'); if (!urlKey) return null; return urlKey.nextElementSibling.textContent; } // 显示错误提示的函数 function showError(link, message) { let errorLink = document.createElement('a'); errorLink.textContent = message; errorLink.style.color = 'red'; errorLink.style.marginLeft = '13px'; errorLink.style.pointerEvents = 'none'; // 防止点击 errorLink.classList.add('ipa-error-link'); // 添加类名以便识别 link.insertAdjacentElement('afterend', errorLink); } document.addEventListener('DOMContentLoaded', function() { processLinks(); }); window.addEventListener('load', function() { processLinks(); }); // 监听动态内容加载 const observer = new MutationObserver(() => { processLinks(); }); observer.observe(document.body, { childList: true, subtree: true, attributes: true }); })();