Greasy Fork

Greasy Fork is available in English.

云听下载高音质版本

云听点播节目批量下载,实时显示下载进度,高低音质切换,文件名自动重命名,复制链接到剪贴板功能

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         云听下载高音质版本
// @version      2.4
// @description  云听点播节目批量下载,实时显示下载进度,高低音质切换,文件名自动重命名,复制链接到剪贴板功能
// @author       darkduck9
// @match        https://www.radio.cn/*
// @grant        none
// @V2.4         修复:1.无法下载当日低音质音频,2.切换音质后“全选”复选框不选中。‘复制所有’调整为‘复制所选’
// @license      MIT
// @namespace http://example.com
// ==/UserScript==

(function() {
    'use strict';
 let submenuContainer;
    let isHighQuality = false;
  if (/channelname=\d+|name=\d+/.test(window.location.href)) {
        createSubmenu(); 

         function createSubmenu() {
             if (submenuContainer) {
                 document.body.removeChild(submenuContainer);
            }

            submenuContainer = document.createElement('div');
            submenuContainer.style.position = 'fixed';
            submenuContainer.style.top = '50%';
            submenuContainer.style.right = '1rem';
            submenuContainer.style.transform = 'translateY(-50%)';
            submenuContainer.style.backgroundColor = '#00000080';
            submenuContainer.style.padding = '10px';
            submenuContainer.style.zIndex = '9999';
            submenuContainer.style.borderRadius = '10px';

             const submitButton = document.createElement('button');
            submitButton.textContent = 'Submit';

            const refreshButton = document.createElement('button');
            refreshButton.textContent = '刷新';
            refreshButton.style.color = 'white';
            refreshButton.style.fontWeight = 'bold';
            refreshButton.style.marginRight = '5px';
            refreshButton.style.cursor = 'pointer';
            refreshButton.style.backgroundColor = '#00000080';refreshButton.style.border= 'none';
             const downloadButtonsContainer = document.createElement('div');
            downloadButtonsContainer.style.height = '60vh';
            downloadButtonsContainer.style.overflow = 'auto';
            downloadButtonsContainer.style.marginTop = '10px';

            const qualityToggleButton = document.createElement('button');
            setQualityButtonText(); // Set initial button text
             const copyLinksButton = document.createElement('button');
            copyLinksButton.textContent = '复制所选链接';
            copyLinksButton.style.color = 'white';
            copyLinksButton.style.fontWeight = 'bold';
            copyLinksButton.style.marginRight = '5px';
            copyLinksButton.style.cursor = 'pointer';
            copyLinksButton.style.backgroundColor = '#00000080';copyLinksButton.style.border= 'none';
             qualityToggleButton.addEventListener('click', function() {
                isHighQuality = !isHighQuality; 
                setQualityButtonText();
                const downloadUrls = getDownloadUrls();
                displayDownloadButtons(downloadUrls);
                 const selectAllCheckbox = document.querySelector('#select-all-checkbox');
    if (selectAllCheckbox && !selectAllCheckbox.checked) {
        selectAllCheckbox.checked = true;
        const event = new Event('change');
        selectAllCheckbox.dispatchEvent(event);
    }
            });

 function setQualityButtonText() {
  qualityToggleButton.textContent = isHighQuality ? '当前低音质' : '当前高音质';
            qualityToggleButton.style.color = 'white';
            qualityToggleButton.style.fontWeight = 'bold';
            qualityToggleButton.style.marginRight = '5px';
            qualityToggleButton.style.cursor = 'pointer';
            qualityToggleButton.style.backgroundColor = '#00000080';qualityToggleButton.style.border= 'none';
  if (isHighQuality) {
    qualityToggleButton.textContent = '当前低音质';
  } else {
    qualityToggleButton.textContent = '当前高音质';
  }
}
             submitButton.addEventListener('click', function() {
                const downloadUrls = getDownloadUrls();
                displayDownloadButtons(downloadUrls);
            });

             refreshButton.addEventListener('click', function() {
                const currentQuality = isHighQuality;
createSubmenu();
submitButton.click(); 
isHighQuality = currentQuality; 
setQualityButtonText(); 
            });

             copyLinksButton.addEventListener('click', function() {
    const checkboxes = downloadButtonsContainer.querySelectorAll('input[type="checkbox"]');
    const selectedUrls = [];
    checkboxes.forEach(checkbox => {
        if (checkbox.checked) {
            selectedUrls.push(checkbox.value);
        }
    });
    const links = selectedUrls.join('\n');
    copyToClipboard(links);
});
   // Create the progress bar
      const progressBar = document.createElement('div');
      progressBar.style.width = '0%';
      progressBar.style.height = '10px';
      progressBar.style.backgroundColor = '#E9E9E9';
      progressBar.style.marginTop = '5px';
      progressBar.className = 'progress-bar'; // Add a class name for easy selection

       const fileCountIndicator = document.createElement('div');
      fileCountIndicator.style.marginTop = '5px';
      fileCountIndicator.style.fontWeight = 'bold';
      fileCountIndicator.className = 'file-count-indicator'; // Add a class name for easy selection

       submenuContainer.appendChild(progressBar);
      submenuContainer.appendChild(fileCountIndicator);
const fileSizeIndicator = document.createElement('div');
fileSizeIndicator.style.marginTop = '5px';
fileSizeIndicator.style.fontWeight = 'bold';
fileSizeIndicator.className = 'file-size-indicator'; // Add a class name for easy selection

const indicatorsContainer = document.createElement('div');
indicatorsContainer.style.display = 'flex';
indicatorsContainer.style.alignItems = 'center';
indicatorsContainer.style.marginTop = '5px';
indicatorsContainer.appendChild(fileCountIndicator);
indicatorsContainer.appendChild(fileSizeIndicator);

 submenuContainer.appendChild(indicatorsContainer);

 function cleanURL(url) {
    try {
        const parsedUrl = new URL(url);
         parsedUrl.searchParams.delete('e');
        parsedUrl.searchParams.delete('ps');
        parsedUrl.searchParams.delete('r');
        return parsedUrl.toString();
    } catch (error) {
        console.error("URL无效:", error);
        return url; 
    }
}           // Function to get download URLs
           function getDownloadUrls() {
    const downloadUrls = [];
    const trElements = document.querySelectorAll('tr');
    trElements.forEach(tr => {
        const dateTd = tr.querySelector('td:first-child');
        const linkTd = tr.querySelector('td:nth-child(2)');
        const downloadLink = tr.querySelector('td:nth-child(3) a');
        const fileNameLink = linkTd ? linkTd.querySelector('a') : null;

        if (dateTd && downloadLink && fileNameLink) {
            const onclickValue = downloadLink.getAttribute('onclick');
            if (onclickValue) {
                const date = dateTd.textContent.trim();
                const url = isHighQuality ? cleanURL(getDownloadUrlFromDownLiveRecord(onclickValue)) : cleanURL(fileNameLink.getAttribute('data-url'));
                const fileName = getFileName(date, fileNameLink.textContent.trim());
                const downloadUrl = {
                    url: url,
                    fileName: fileName
                };
                downloadUrls.push(downloadUrl);
            }
        }
    });
    return downloadUrls;
}

            // Function to get the file name
            function getFileName(date, text) {
                const httpDateMatches = text.match(/\/(\d{8})\//);
                if (httpDateMatches && httpDateMatches.length > 1) {
                    const httpDate = httpDateMatches[1];
                    const formattedDate = formatDate(httpDate);
                    return formattedDate + '_' + text;
                }
                return date + '_' + text;
            }

            // Function to format HTTP date to 'YYYY/MM/DD' format
            function formatDate(httpDate) {
                const year = httpDate.substring(0, 4);
                const month = httpDate.substring(4, 6);
                const day = httpDate.substring(6, 8);
                return year + '/' + month + '/' + day;
            }

             function getDownloadUrlFromDownLiveRecord(onclickValue) {
    if (!onclickValue) return '';
    const matches = onclickValue.match(/downLiveRecord\('([^']+)'/);
    if (matches && matches.length > 1) {
        return matches[1];
    }
    return '';
}

            // Function to display download buttons
            function displayDownloadButtons(downloadUrls) {
                 downloadButtonsContainer.innerHTML = '';

                downloadUrls.forEach(downloadUrl => {
                if (!downloadUrl.url) {
               return;
              }
                     const checkbox = document.createElement('input');
                    checkbox.type = 'checkbox';
                    checkbox.value = downloadUrl.url;
                    checkbox.checked = true; 
                     const buttonElement = document.createElement('button');
                    buttonElement.textContent = downloadUrl.fileName;
                    buttonElement.style.backgroundColor = '#00000080';
                    buttonElement.style.color = 'white';buttonElement.style.border= 'none';
                    buttonElement.style.borderBottom = '2px solid #fff';
                    buttonElement.style.cursor = 'pointer';
                    buttonElement.addEventListener('click', function() {
                        downloadFile(downloadUrl.url, downloadUrl.fileName);
                    });

                    const container = document.createElement('div');
                    container.appendChild(checkbox);
                    container.appendChild(buttonElement);
                    container.style.borderRadius = '5px';
                    container.style.marginBottom = '5px';
                    downloadButtonsContainer.appendChild(container);
                });

                if (!submenuContainer.querySelector('#select-all-checkbox')) {
                     const selectAllCheckbox = document.createElement('input');
                    selectAllCheckbox.type = 'checkbox';
                    selectAllCheckbox.id = 'select-all-checkbox';
                    selectAllCheckbox.checked = true;
                     const selectAllLabel = document.createElement('label');
                    selectAllLabel.textContent = '全选';
                    selectAllLabel.style.color = 'white';
                    selectAllLabel.htmlFor = 'select-all-checkbox';
                    selectAllCheckbox.addEventListener('change', function() {
                        const checkboxes = downloadButtonsContainer.querySelectorAll('input[type="checkbox"]');
                        checkboxes.forEach(checkbox => {
                            checkbox.checked = selectAllCheckbox.checked;
                        });
                    });
                    submenuContainer.appendChild(selectAllCheckbox);
                    submenuContainer.appendChild(selectAllLabel);
                }

                 if (!submenuContainer.querySelector('#download-selected-button')) {
                    // Create the download selected button
                    const downloadSelectedButton = document.createElement('button');
                    downloadSelectedButton.textContent = '下载所选';
                    downloadSelectedButton.style.color = 'white';
            downloadSelectedButton.style.fontWeight = 'bold';
            downloadSelectedButton.style.marginRight = '5px';
            downloadSelectedButton.style.cursor = 'pointer';
            downloadSelectedButton.style.backgroundColor = '#00000080';
            downloadSelectedButton.style.border= 'none';
                    downloadSelectedButton.id = 'download-selected-button';
                    downloadSelectedButton.addEventListener('click', function() {
                       const checkboxes = downloadButtonsContainer.querySelectorAll('input[type="checkbox"]');
                        const selectedUrls = [];
                        checkboxes.forEach(checkbox => {
                            if (checkbox.checked) {
                                selectedUrls.push(checkbox.value);
                            }
                        });
                        downloadSelectedUrls(selectedUrls);
                    });
                    submenuContainer.appendChild(downloadButtonsContainer);
                    submenuContainer.appendChild(downloadSelectedButton);
                }
                submenuContainer.appendChild(copyLinksButton);
                submenuContainer.appendChild(qualityToggleButton);
            }
function updateProgress(percentComplete, currentFileSize, downloadSpeed) {
  const progressBar = submenuContainer.querySelector('.progress-bar');
  progressBar.style.width = percentComplete + '%';
 
 const fileSizeIndicator = submenuContainer.querySelector('.file-size-indicator');
fileSizeIndicator.textContent = `已下载: ${formatFileSize(event.loaded)} / 共: ${currentFileSize}`;
fileSizeIndicator.style.color = 'white';
}

  function updateFileCount(currentFile, totalFiles) {
    const fileCountIndicator = submenuContainer.querySelector('.file-count-indicator');
    fileCountIndicator.textContent = `${currentFile}/${totalFiles}`;
    fileCountIndicator.style.color = 'white';
  }

function downloadFile(url, fileName) {
  const a = document.createElement('a');
  a.style.display = 'none';
  a.href = url;

  const fileExtension = getFileExtension(url);
  // Modify the file name with the obtained file extension
  const fullFileName = fileName + '.' + fileExtension;
  a.setAttribute('download', fullFileName);
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
}

 // Function to extract the file extension from the URL
function getFileExtension(url) {
  return url.split('.').pop();
}

function downloadSelectedUrls(urls) {
  const divs = document.getElementsByTagName('div');
  const totalFiles = urls.length;

  function getButtonInnerText(div) {
    const button = div.querySelector('button');
    if (button) {
      return button.textContent.trim();
    }
    return '';
  }

  function getCheckboxValue(div) {
    const checkbox = div.querySelector('input[type="checkbox"]');
    if (checkbox) {
      return checkbox.value;
    }
    return '';
  }

  function getDivContainingUrl(url) {
    for (let i = 0; i < divs.length; i++) {
      const div = divs[i];
      const checkbox = div.querySelector('input[type="checkbox"]');
      if (checkbox && checkbox.value === url) {
        return div;
      }
    }
    return null;
  }

  let index = 0;

  function downloadNext() {
    if (index < totalFiles) {
      const url = urls[index];
      const fileName = getFileNameFromUrl(url);
      const fileExtension = getFileExtension(url);
      const div = getDivContainingUrl(url);

      if (div) {
        const buttonText = getButtonInnerText(div);
        const checkboxValue = getCheckboxValue(div);
        const fullFileName = buttonText + '.' + fileExtension;

        if (checkboxValue === url) {
          const xhr = new XMLHttpRequest();
         const secureUrl = url.replace('http://', 'https://');
          xhr.open('GET', secureUrl);

          xhr.responseType = 'blob';

          // Add progress event listener
          xhr.addEventListener('progress', function(event) {
            if (event.lengthComputable) {
              const percentComplete = (event.loaded / event.total) * 100;
              const currentFileSize = formatFileSize(event.total);
              updateProgress(percentComplete,currentFileSize);
            }
          });

          xhr.onload = function() {
            if (xhr.status === 200) {
              const blob = new Blob([xhr.response]);
              const fileUrl = URL.createObjectURL(blob);
              const a = document.createElement('a');
              a.style.display = 'none';
              a.href = fileUrl;
              a.setAttribute('download', fullFileName);

              document.body.appendChild(a);
              a.click();
              document.body.removeChild(a);
              URL.revokeObjectURL(fileUrl);
              index++;

              updateFileCount(index, totalFiles);

              setTimeout(downloadNext, 1000); // Delay between downloads (1 second)
            }
          };

          xhr.send();
        } else {
          console.log('Checkbox value does not match URL:', url);
          index++;
          downloadNext();
        }
      } else {
        console.log('Div not found for URL:', url);
        index++;
        downloadNext();
      }
    }
  }

  downloadNext();
}
function formatFileSize(bytes) {
  const sizes = ['Bytes', 'KB', 'MB', 'GB'];
  if (bytes === 0) return '0 Bytes';
  const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
  return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${sizes[i]}`;
}

            function getFileNameFromUrl(url) {
                const matches = url.match(/\/([^/]+)$/);
                if (matches && matches.length > 1) {
                    return matches[1];
                }
                return '';
            }

            // Function to copy text to clipboard
            function copyToClipboard(text) {
                const textarea = document.createElement('textarea');
                textarea.value = text;
                document.body.appendChild(textarea);
                textarea.select();
                document.execCommand('copy');
                document.body.removeChild(textarea);
            }

            document.body.appendChild(submenuContainer);
            submenuContainer.appendChild(refreshButton);
        }

        submitButton.click();
       }
})();