Khinsider AntiRedirect

Makes khinsider stop redirecting you when clicking download on a soundtrack. Also directly downloads the soundtracks instead of redirecting and includes a right click save as directly from the album's page.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(Tôi đã có Trình quản lý tập lệnh người dùng, hãy cài đặt nó!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Khinsider AntiRedirect
// @namespace    https://downloads.khinsider.com/game-soundtracks/*
// @version      0.5
// @description  Makes khinsider stop redirecting you when clicking download on a soundtrack. Also directly downloads the soundtracks instead of redirecting and includes a right click save as directly from the album's page.
// @author       realcoloride
// @license      MIT
// @match        https://downloads.khinsider.com/game-soundtracks/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=khinsider.com
// @connect      vgmdownloads.com
// @connect      vgmsite.com
// @connect      vgmtreasurechest.com
// @grant        GM.xmlHttpRequest
// ==/UserScript==

(function() {
    const FLAC_DOWNLOAD_COLOR = "#2bb7c7";
    const songlist = document.getElementById('songlist');
    let pass = true;

    let tbody;

    try {
        tbody = songlist.getElementsByTagName("tbody")[0];
    } catch (error) {pass = false;}

    if (!pass) return;

    const songElements = tbody.getElementsByTagName("tr");
    const excludedElements = [
        'songlist_header',
        'songlist_footer'
    ]

    function extractUrl(htmlString) {
        const regex = /<audio\s+id="audio"\s+.*?\bsrc="(.*?)"/;
        const match = regex.exec(htmlString);
        return match ? match[1] : null;
    }

    function getButtonFromHref(hrefElement) {
        return hrefElement.querySelector("i");
    }

    async function downloadFile(button, url, filename) {
        setButtonToLoading(button);

        function finish() {
            setButtonToDownloadIcon(button);
            if (button.disabled) button.disabled = false
        }
 
        GM.xmlHttpRequest({
            method: 'GET',
            headers: {},
            url: url,
            responseType: 'blob',
            onload: async function(response) {
                if (response.status == 403) { 
                    alert("Download failed, please try again later or refresh the page."); 
                    return; 
                }
                
                const blob = response.response;
                const link = document.createElement('a');
 
                link.href = URL.createObjectURL(blob);
                link.setAttribute('download', filename);
                link.click();
 
                URL.revokeObjectURL(link.href);
                
                setButtonToDownloadIcon(button);
                button.disabled = false;

                finish();
            },
            onerror: function(error) {
                console.error('Download Error:', error);
                alert("Download failed, please try again later or refresh the page."); 
                
                finish();
            }
        });
    }

    let stylesInjected = false;


    function loadCSS(css) {
        const style = document.createElement('style');
        style.innerHTML = css;
        document.head.appendChild(style);
    }

    function injectStyles() {
        if (stylesInjected) return;
        stylesInjected = true;

        loadCSS(`
            @keyframes rotating {
            from { transform: rotate(0deg); }
            to { transform: rotate(-360deg); }
        }`);
    }

    function setButtonToLoading(button) {
        button.innerText = "loop";
        button.setAttribute("style", "animation: 2s linear infinite rotating");
    }
    function setButtonToDownloadIcon(button) {
        button.innerText = "get_app";
        button.setAttribute("style", "");
    }

    async function doesSongUrlExist(url) {
        try {
            return (await GM.xmlHttpRequest({method: "GET", url})).status == 200;
        } catch (error) {
            return false;
        }
    }

    function inject(trackElement) {
        const playlistDownloadSong = trackElement.getElementsByClassName('playlistDownloadSong')[0];
        const hrefElement = playlistDownloadSong.getElementsByTagName('a')[0];
        setButtonToLoading(getButtonFromHref(hrefElement));

        playlistDownloadSong.style = "display: flex";

        const songLink = hrefElement.href;

        // dispatch click event
        hrefElement.removeAttribute('href');

        hrefElement.style.cursor = 'pointer';
        hrefElement.setAttribute('alt', 'Download track (MP3)');
        hrefElement.setAttribute('title', 'Download track (MP3)');

        const xhr = new XMLHttpRequest();
        xhr.open("GET", songLink);
        xhr.send();
        xhr.responseType = "text";

        function prepareHrefElement(url) {
            const newHrefElement = hrefElement.cloneNode(true);
            setButtonToDownloadIcon(getButtonFromHref(newHrefElement));
            newHrefElement.setAttribute('href', url);
            newHrefElement.setAttribute('download', '');

            newHrefElement.addEventListener('click', async function(event) {
                event.preventDefault();

                const filename = decodeURIComponent(url.substring(url.lastIndexOf('/')+1).replace(/%20/g, " "));
                await downloadFile(getButtonFromHref(newHrefElement), url, filename);
            });

            playlistDownloadSong.appendChild(newHrefElement);

            return newHrefElement;
        }

        async function finish(url) {
            prepareHrefElement(url);

            const flacUrl = url.replace(/\.mp3$/, ".flac");
            if (!await doesSongUrlExist(flacUrl)) return;

            const flacHrefElement = prepareHrefElement(flacUrl);
            flacHrefElement.setAttribute('alt', 'Download track (FLAC)');
            flacHrefElement.setAttribute('title', 'Download track (FLAC)');
            getButtonFromHref(flacHrefElement).style = `color:${FLAC_DOWNLOAD_COLOR}`;
        }

        xhr.onload = async () => {
            if (xhr.readyState == 4 && xhr.status == 200) {
                const body = xhr.response;
                const url = extractUrl(body);

                finish(url);
                hrefElement.remove();
            } else console.log('Something errored with the request');
        }
    }

    injectStyles();
    for (let i = 0; i < songElements.length; i++) {
        const trackElement = songElements[i];
        let passing = true;

        for (let j = 0; j < excludedElements.length; j++) {
            if (excludedElements[j] == trackElement.id)
                passing = false;
        }

        if (passing) inject(trackElement);
    }
})();