柠檬音乐转种插件

2024/12/23 21:39:27

// ==UserScript==
// @name        柠檬音乐转种插件
// @namespace   Violentmonkey Scripts
// @match       https://dicmusic.com/torrents.php?id=*
// @match       https://lemonhd.club/*
// @match       https://redacted.sh/torrents.php*
// @match       https://orpheus.network/torrents.php*
// @grant       none
// @version     1.14
// @author      Posase
// @author      Posase
// @description 2024/12/23 21:39:27
// @license MIT
// ==/UserScript==

var torrent = {};
var db, targetWindow, canSend, offsetY = 10;  // 初始偏移量;
const redLogUrl = "https://redacted.sh/torrents.php?action=loglist&torrentid="
const orpLogUrl = "https://orpheus.network/torrents.php?action=viewlog&torrentid="
const searchUrl = "https://lemonhd.club/music_torrents.php?search_type=title&search="

const dbName = "nmzz", tabName = "torrent_info"

function showMessage(text, fontColor = "#fff") {
    const messageDiv = document.createElement("div");
    messageDiv.textContent = text;
    messageDiv.style.position = "fixed";
    messageDiv.style.top = `${offsetY}px`;
    messageDiv.style.left = "10px";  // 可以根据需要调整左边距
    messageDiv.style.backgroundColor = "rgba(0, 0, 0, 0.7)";
    messageDiv.style.color = fontColor ;
    messageDiv.style.padding = "10px";
    messageDiv.style.borderRadius = "5px";
    messageDiv.style.transition = "top 0.5s ease";

    document.body.appendChild(messageDiv);

    // 每显示一个提示框,就增加偏移量,避免重叠
    offsetY += 50;  // 根据提示框的高度调整偏移量

    // 自动消失
    setTimeout(() => {
        messageDiv.remove();
    }, 3000);  // 3秒后消失

    // 提示框消失后调整位置
    setTimeout(() => {
        offsetY -= 50;  // 释放空位,准备下一个提示框
    }, 3000);
}

function openDB(version = 2) {
  const request = indexedDB.open(dbName, version);
  request.onupgradeneeded = function(event) {
    db = event.target.result;
    if (!db.objectStoreNames.contains(tabName)) {
      const objectStore = db.createObjectStore(tabName, { keyPath: 'torrent_id' });
      // objectStore.createIndex('name', 'name', { unique: false }); // 创建索引
      console.log('对象存储创建成功');
    } else {
      console.log('对象存储已经存在');
    }
  };

  request.onsuccess = function(event) {
    db = event.target.result;
    console.log('数据库打开成功', db);
    if(window.opener){
      window.opener.postMessage(true, "*");
    }
  };

  request.onerror = function(event) {
    console.log('打开数据库失败', event.target.error);
  };
}

function insert(db, storeName, data) {
  console.log(data)
  var request = db.transaction([tabName], 'readwrite')
    .objectStore(tabName)
    .put(data);
  request.onsuccess = function (event) {
    showMessage('数据获取成功: ' + data.torrent_id, 'yellow');
    console.log('数据写入成功');
  };
  request.onerror = function (event) {
    console.log('数据写入失败');
  }
}

function selectAll(db, tabName) {
  return new Promise((resolve, reject) => {
    const transaction = db.transaction(tabName, 'readonly'); // 创建只读事务
    const objectStore = transaction.objectStore(tabName);

    const result = [];
    const cursorRequest = objectStore.openCursor();
    cursorRequest.onsuccess = function(event) {
      const cursor = event.target.result;
      if (cursor) {
        result.push(cursor.value);
        cursor.continue();
      } else {
        resolve(result);
      }
    }
  });
}

function clearDb(db, storeName) {
  console.log(db)
  const transaction = db.transaction(storeName, 'readwrite'); // 'myStore' 是你要清空的对象存储名称
  const objectStore = transaction.objectStore(storeName);
  const clearRequest = objectStore.clear();

  clearRequest.onsuccess = function() {
    console.log("所有数据已删除");
  };

  clearRequest.onerror = function(event) {
    console.error("删除数据失败", event);
  };
}

function checkFinish() {
  if(canSend && torrent.file_num == 3) {
    torrent.file_num = 0;
    console.log(torrent)
    targetWindow.postMessage(torrent, "*");
  }
  console.log("当前已下载文件数: ", torrent.file_num);
}

function fetchFile(name, url) {
  fetch(url)
    .then(response => {
      if (!response.ok) throw new Error("网络响应失败");
      return response.blob();
    })
    .then(data => {
      torrent[name] = data;
      torrent.file_num += 1;
      checkFinish();
    })
    .catch(error => {
      console.error("下载失败:", error);
      throw error;
    });
}

function fetchName(item, className) {
    if(item.className.includes(className))
        return fetchName(item.previousElementSibling, className)
    return item;
}

function fetchRedLog(torrent_id) {
  const url = redLogUrl + torrent_id
  console.log(url)
  fetch(url).then(rep => {
    console.log(rep.ok)
    return rep.text()
  }).then(htmlString => {
    console.log(htmlString)
    const parser = new DOMParser();
    const doc = parser.parseFromString(htmlString, 'text/html');
    const textContent = doc.body.textContent || doc.body.innerText;
    const textList = textContent.split(/Score:.*?\)/).filter(text => text.trim() != '')
    console.log(textList.map(text => text.length))
    console.log('日志长度', textList.length)
    torrent.log_num = textList.length;
    torrent.log_list = textList;
    torrent.file_num += 1;
    checkFinish()
  });
}

function fetchRedTorrent(btn) {
    // 获取种子id
    let str = btn.previousElementSibling.href
    const torrent_id = str.substring(str.lastIndexOf("=") + 1);
    console.log(torrent_id)
    let torrentInfo = btn.parentElement.nextElementSibling;
    // 下载文件
    torrent = {file_num: 0, torrent_id: torrent_id, from: location.hostname, href: torrentInfo.href}
    fetchFile('json', 'https://redacted.sh/ajax.php?action=torrent&id=' + torrent_id)
    fetchFile('torrent', btn.parentElement.childNodes[0].href)

    // 获取日志
    if(torrentInfo.text.includes("Log")){
        fetchRedLog(torrent_id)
    } else {
        torrent.log_list = []
        torrent.file_num += 1
        torrent.log_num = 0
        checkFinish()
    }
}

function createBtn() {
    let btn = document.createElement("a")
    btn.className = "tooltip button_pl"
    btn.textContent = "ZZ"
    btn.className = "nmzz"
    btn.addEventListener('click', function() {

        // 搜索该曲目
        let name = "名字查找错误"
        if(window.location.href.startsWith("https://redacted.sh/torrents.php?")) {
            name = document.querySelector("#content > div > div.header > h2 > span").textContent
        } else {
            let tr = btn.parentElement.parentElement.parentElement
            name = fetchName(tr, "edition").querySelector('a[class][dir="ltr"]').textContent
        }
        targetWindow = window.open(searchUrl + name, '_blank');
        canSend = false;
        fetchRedTorrent(btn);
    });
    return btn;
}

function addRedactedListener() {
  console.log("add listener.")
  window.addEventListener('message', function(event) {
    const message = event.data;
    console.log('Received message:', message);
    canSend = true;
    checkFinish()
  });
}

function redacted() {
  addRedactedListener()
  let boxList = document.querySelectorAll(".torrent_action_buttons")
  boxList.forEach((box) => {
    box.appendChild(document.createTextNode(' | '))
    box.appendChild(createBtn())
  });
}

function addSListener() {
  console.log("start addSListener")
  window.addEventListener('message', function(event) {
    console.log(event.origin)
    if(event.origin != location.host) {
      const message = event.data;
      console.log(message)
      clearDb(db, tabName)
      insert(db, tabName, message)
    }
  });
}

function uploadFile(fileInput, fileList) {
  const dataTransfer = new DataTransfer();
  fileList.forEach(data => {
    const blob = new Blob([data.text], { type: 'text/plain' });
    const file = new File([blob], data.name, { type: "text/plain" });
    dataTransfer.items.add(file);
  });
  fileInput.files = dataTransfer.files;
  const event = new Event('change');
  fileInput.dispatchEvent(event);
}

function refreshTorrent(spec) {
  console.log("refresh")
  spec.textContent = ""
  selectAll(db, tabName)
    .then(list => {
      if(list.length == 0) {
        spec.textContent = "无种子数据"
      } else {
        result = list[0]
        spec.textContent = "Torrent: " + result.torrent_id + ' LogNum: ' + result.log_num + ' from ' + result.from
      }
    });
}

function fillTorrent(result) {
  let torrentInput = document.querySelector('input[name="torrent_file"]')
  let jsonInput = document.querySelector("#upload-form > table > tbody > tr:nth-child(3) > td.rowfollow > input[type=file]")
  let logInput = document.querySelector('input[name="log_files[]"]')
  let srcInput = document.querySelector('.src_link')

  let torrent_id = result.torrent_id;
  document.querySelector('input[name="uplver"]').checked = true
  document.querySelector('input[name="read_flag"]').checked = true
  document.querySelector("input.src_link").value = result.href
  uploadFile(jsonInput, [{name: torrent_id + '.json', text: result.json}])
  uploadFile(torrentInput, [{name: torrent_id + '.torrent', text: result.torrent}])
  uploadFile(logInput, result.log_list.map((log,inx) => {return {name: torrent_id + inx + '.log', text: log};}))
}

function checkTorrent(item) {
  let url = new URL(item.parentElement.parentElement.parentElement.querySelector('a[title="种子链接"]').href);
  let params = new URLSearchParams(url.search);
  let albumid = params.get('albumid');
  let torrentid = params.get('torrentid');
  console.log(albumid, torrentid)
  fetch("https://lemonhd.club/music_details.php?method=check", {
    "headers": {
      "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
      "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
      "cache-control": "max-age=0",
      "content-type": "application/x-www-form-urlencoded",
      "priority": "u=0, i",
      "sec-ch-ua": "\"Microsoft Edge\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"",
      "sec-ch-ua-mobile": "?0",
      "sec-ch-ua-platform": "\"Windows\"",
      "sec-fetch-dest": "document",
      "sec-fetch-mode": "navigate",
      "sec-fetch-site": "same-origin",
      "sec-fetch-user": "?1",
      "upgrade-insecure-requests": "1"
    },
    "referrer": "https://lemonhd.club/music_details.php?albumid=" + albumid,
    "referrerPolicy": "strict-origin-when-cross-origin",
    "body": "albumid=" + albumid + "&torrentid=" + torrentid + "&status=pass&reason=",
    "method": "POST",
    "mode": "cors",
    "credentials": "include"
  })
  .then(response => response.text()) // 如果重定向返回 HTML 页面,使用 text() 获取响应
  .then(data => {
    item.className = "check_status pass"
    showMessage('专辑: ' + albumid + '种子: ' + torrentid + ' 审核完成!', 'yellow')
  })
  .catch(error => console.error('Error:', error));
}

function lemonhd() {
  openDB()
  addSListener()

  // 添加转种功能
  let uploadForm = document.querySelector("#upload-form > table > tbody > tr:nth-child(2) > td.rowfollow")
  if(uploadForm){
    let refresh = document.createElement("input")
    let button = document.createElement("input")
    let spec = document.createElement("span")
    spec.className = "descr-txt"
    spec.id = 'zzdesc'
    refresh.type = "button"
    refresh.value = "刷新数据"
    button.type = "button"
    button.value = "填写数据"
    uploadForm.appendChild(refresh)
    uploadForm.appendChild(button)
    uploadForm.appendChild(spec)

    refresh.addEventListener('click', function() {
      refreshTorrent(spec)
    });
    button.addEventListener('click', function() {
      selectAll(db, tabName)
        .then(list => {
          if(list.length == 0) {
            spec.textContent = "获取数据失败"
          } else {
            let result = list[0]
            spec.textContent = "当前填写种子: " + result.torrent_id
            fillTorrent(result)
          }
        });
    });
  }

  // 添加一键审核功能
  if(location.href.startsWith("https://lemonhd.club/music_details.php?albumid=")) {
    console.log('可一键审核')
    let btn = document.createElement("a")
    btn.textContent = "一键审核"
    btn.addEventListener('click', () => {
      console.log('开始一键审核')
      let list = document.querySelectorAll('img.check_status.uncheck');
      list.forEach((item) => {
        checkTorrent(item);
      });
    });
    document.querySelector("div.nav-list").appendChild(btn)
  }

  let select = document.querySelector("select[name=search_type]")
  if(select) {
    select.selectedIndex = 3
  }
}

function addDicListener() {
  console.log("add listener.")
  document.querySelectorAll('div.linkbox > a.brackets').forEach((btn) => {
    if(btn.text == '查看Log') {
      // 打开所有log
      btn.click()
    }
  });
  window.addEventListener('message', function(event) {
    const message = event.data;
    console.log('Received message:', message);
    canSend = true;
    checkFinish()
  });
}

function fetchDicTorrent(btn) {
  let dl = btn.parentElement.querySelector('a');
  let url = new URL(dl.href)
  let torrent_id = url.searchParams.get('id')
  console.log(torrent_id)

  // 下载文件
  torrent = {file_num: 0, torrent_id: torrent_id, from: location.hostname, href: location.href}
  console.log(btn.previousElementSibling.href)
  console.log(dl.href)
  fetchFile('json', btn.previousElementSibling.href)
  fetchFile('torrent', dl.href)

  let logList = btn.parentElement.parentElement.parentElement.nextElementSibling.querySelectorAll('.log_section > td > a:nth-child(1)')
  console.log(logList)
  if(logList.length > 0) {
    torrent.log_num = logList.length
    torrent.log_list = []
    for(let i = 0; i < logList.length; ++i) {
      fetch(logList[i].href)
        .then(response => {
          if (!response.ok) throw new Error("网络响应失败");
          return response.blob();
        })
        .then(data => {
         console.log(data)
          torrent.log_list.push(data);
          if(torrent.log_list.length == torrent.log_num) {
            torrent.file_num += 1;
            checkFinish();
          }
        })
        .catch(error => {
          console.error("下载失败:", error);
          throw error;
        });
    }
  } else {
    torrent.log_list = []
    torrent.file_num += 1
    torrent.log_num = 0
    checkFinish()
  }
}

function dicmusic() {
    addDicListener()
    const tdList = document.querySelectorAll('td.td_info > span');
    tdList.forEach(td => {
        let allLinks = td.querySelectorAll('a');
        let JSLink = allLinks[allLinks.length - 1];
        console.log(JSLink)
        const newLink = document.createElement('a');
        newLink.href = "#"
        newLink.className = 'tooltip'
        newLink.textContent = 'ZZ'; // 设置链接文本
        const separator = document.createTextNode(' | ');
        JSLink.parentElement.insertBefore(separator, JSLink.nextSibling);
        JSLink.parentElement.insertBefore(newLink, separator.nextSibling);
        newLink.addEventListener('click', () => {
            // 搜索该曲目
            let name = document.querySelector('span[dir=ltr]').textContent
            targetWindow = window.open(searchUrl + name, '_blank');
            canSend = false;
            fetchDicTorrent(newLink);
        });
    });
}

function addOrpListener() {
    console.log("add listener.")
    window.addEventListener('message', function(event) {
        const message = event.data;
        console.log('Received message:', message);
        canSend = true;
        checkFinish()
    });
}

function fetchOrpLog(torrent_id) {
    const url = orpLogUrl + torrent_id
    console.log(url)
    fetch(url).then(rep => {
        console.log(rep.ok)
        return rep.text()
    }).then(htmlString => {
        const parser = new DOMParser();
        const doc = parser.parseFromString(htmlString, 'text/html');
        const textContent = doc.body.textContent || doc.body.innerText;
        console.log(textContent)

        const regex = /.*This torrent has (\d+).*View Raw Log\s*(.*)/gms;
        const match = regex.exec(textContent);
        if(match && match[1] == 1) {
            torrent.log_num = 1;
            torrent.log_list = [match[2]];
            torrent.file_num += 1;
            checkFinish()
        } else {
            console.log("日志获取失败,请联系作者")
        }
    });
}

function fetchOrpTorrent(btn) {
    let dl = btn.parentElement.querySelector('a');
    let url = new URL(dl.href)
    let torrent_id = url.searchParams.get('id')
    console.log(torrent_id)
    let torrentInfo = btn.parentElement.nextElementSibling;

    // 下载文件
    torrent = {file_num: 0, torrent_id: torrent_id, from: location.hostname, href: torrentInfo.href}
    console.log(dl.href)
    fetchFile('json', btn.previousElementSibling.href)
    fetchFile('torrent', dl.href)
    if(btn.parentElement.nextElementSibling.textContent.includes("Log")) {
        fetchOrpLog(torrent_id)
    } else {
        torrent.log_num = 0;
        torrent.log_list = [];
        torrent.file_num += 1;
        checkFinish()
    }
}

function orpheus() {
    addOrpListener()
    const tdList = document.querySelectorAll(".torrent_links_block");
    tdList.forEach(td => {
        let allLinks = td.querySelectorAll('a');
        let JSLink = allLinks[allLinks.length - 1];
        // console.log(JSLink)
        const newLink = document.createElement('a');
        newLink.href = "#"
        newLink.className = 'tooltip'
        newLink.textContent = 'ZZ'; // 设置链接文本
        const separator = document.createTextNode(' | ');
        JSLink.parentElement.insertBefore(separator, JSLink.nextSibling);
        JSLink.parentElement.insertBefore(newLink, separator.nextSibling);
        newLink.addEventListener('click', () => {
            event.preventDefault()
            // 搜索该曲目
            let name = "名字查找错误"
            if(window.location.href.startsWith("https://orpheus.network/torrents.php?")) {
                name = document.querySelector("#content > div > div.header > h2 > a:nth-child(2)").textContent
            } else {
                let tr = newLink.parentElement.parentElement.parentElement
                name = fetchName(tr, "edition").querySelector('a[title][dir="ltr"]').textContent
            }
            targetWindow = window.open(searchUrl + name, '_blank');
            canSend = false;
            fetchOrpTorrent(newLink);
        });
    });
}


(function() {
    'use strict';

    const hostname = window.location.hostname;
    console.log(hostname)

    if (hostname === 'dicmusic.com') {
        dicmusic()
    } else if (hostname === 'lemonhd.club') {
        lemonhd();
    } else if(hostname === 'redacted.sh') {
        redacted()
    } else if(hostname === 'orpheus.network') {
        orpheus()
    }
})();