Greasy Fork

来自缓存

Greasy Fork is available in English.

IPA提取助手

从 itms-services 链接中提取 IPA 下载直链或显示错误提示,并显示在原始按钮旁边

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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
// ==/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 });
})();