audioz-utils

Batch downloading, post hiding, etc. for AudioZ

当前为 2024-09-20 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name           audioz-utils
// @author         Metaller
// @description    Batch downloading, post hiding, etc. for AudioZ
// @grant          GM_xmlhttpRequest
// @match          https://audioz.download/*
// @run-at         document-end
// @license        MIT
// @version        1.0.0
// @namespace https://greasyfork.org/users/753942
// ==/UserScript==
function getTextArrayFromInput(input) {
  return input.trim().split(",").map((category) => category.trim()).filter((category) => category !== "" && category !== ",");
}
function shouldFilterPostByCategory(post, filteredCategories) {
  const categories = post.querySelectorAll("header > span > a");
  for (const category of categories)
    if (shouldFilterCategory(category, filteredCategories))
      return !0;
  return !1;
}
function shouldFilterCategory(category, filteredCategories) {
  const href = category.getAttribute("href");
  if (href === null)
    return !1;
  for (const filteredCategory of filteredCategories)
    if (href.includes(filteredCategory))
      return !0;
  return !1;
}
function shouldFilterPostByText(post, filteredTexts) {
  const innerText = post.innerText;
  for (const filteredText of filteredTexts)
    if (innerText.includes(filteredText))
      return !0;
  return !1;
}
function createInputForm({ title, inputFields, idPrefix }) {
  const inputForm = document.createElement("div");
  inputForm.style.fontSize = "14px";
  const inputFieldsHtml = inputFields.map(
    (field) => `
    <label for="${idPrefix}-${field.storageKey}" style="margin-bottom: 5px;">${field.title}:</label>
    <textarea id="${idPrefix}-${field.storageKey}" type="text" title="Separate the entries with ','" style="font-size: 12px; width: 100%; background: none; overflow: hidden">
${field.values.join(", ")},
    </textarea>
  `
  ).join("");
  inputForm.innerHTML = `
    <div style="padding-left: 5px; display: inline-flex; flex-direction: column; align-items: center; width: 100%; font-family: tradegothic,century gothic,CenturyGothic,AppleGothic,sans-serif">
        <h4 style="color: #997e33; align-self: start; padding-left: 10px; margin-bottom: 5px;">${title}</h4>
        <form id="hiddenPostsForm" class="post neon nBrown" style="margin-top: 5px; display: flex; flex-direction: column; width: 95%; font-size: 12px; word-wrap: break-word;">
            ${inputFieldsHtml}
            <input id="${idPrefix}-save-btn" type="button" style="font-size: 12px; margin-bottom: 10px;" class="fbutton doaddcomment" value="Save">
            <div class="ostats_area" id="${idPrefix}-console">
                <input type="button" style="font-size: 10px; width: 100%;" class="fbutton doaddcomment" value="Refresh the page"></input>
            </div>
        </form>
    <div/>
    `;
  const saveButton = inputForm.querySelector(`#${idPrefix}-save-btn`);
  if (!(saveButton instanceof HTMLInputElement))
    return;
  const message = inputForm.querySelector(`#${idPrefix}-console`);
  if (!(message instanceof HTMLElement))
    return;
  const messageButton = message.firstElementChild;
  if (!(messageButton instanceof HTMLInputElement))
    return;
  saveButton.onclick = () => {
    for (const field of inputFields) {
      const inputElement = inputForm.querySelector(`#${idPrefix}-${field.storageKey}`);
      inputElement && window.localStorage.setItem(field.storageKey, inputElement.value);
    }
    message.style.display = "";
  }, message.style.display = "none", messageButton.onclick = () => {
    message.style.display = "none", window.location.reload();
  };
  const menu = document.getElementById("StickyNav");
  menu == null || menu.appendChild(inputForm);
}
const categoryStorageKey$1 = "AudiozUtils_HiddenPosts_categories", textStorageKey$1 = "AudiozUtils_HiddenPosts_texts";
function hidePosts() {
  hidePostsByCategory(
    getTextArrayFromInput(window.localStorage.getItem(categoryStorageKey$1) ?? "samples/loop"),
    getTextArrayFromInput(window.localStorage.getItem(textStorageKey$1) ?? "REQ")
  );
}
function hidePostsByCategory(filteredCategories, filteredTexts) {
  var _a;
  const posts = document.querySelectorAll("article"), postsToHide = [];
  for (const post of posts) {
    if (((_a = post.parentElement) == null ? void 0 : _a.id) === "inside")
      return;
    (shouldFilterPostByCategory(post, filteredCategories) || shouldFilterPostByText(post, filteredTexts)) && postsToHide.push(post);
  }
  createPostContainer$1(postsToHide), createInputForm({
    title: "Hidden Posts",
    idPrefix: "hidden-posts",
    inputFields: [
      {
        title: "Hidden Categories",
        storageKey: categoryStorageKey$1,
        values: filteredCategories
      },
      {
        title: "Hidden Words",
        storageKey: textStorageKey$1,
        values: filteredTexts
      }
    ]
  });
}
function createPostContainer$1(posts) {
  if (posts.length === 0)
    return;
  const container = document.createElement("div");
  container.setAttribute("id", "hidden"), container.className = "post neon nBrown";
  const containerTitle = document.createElement("h3");
  containerTitle.innerHTML = `
  <span>hidden posts<span>
  `, container.appendChild(containerTitle);
  for (const originalPost of posts) {
    const originalPostLink = originalPost.querySelector("article > a");
    if (originalPostLink === null)
      continue;
    const originalTitle = originalPostLink.firstElementChild.innerText, postSummary = originalPostLink.cloneNode();
    postSummary.style.fontSize = "14px", postSummary.innerText = originalTitle, container.appendChild(postSummary), originalPost.style.display = "none";
  }
  const main2 = document.querySelector("main"), nav = (main2 == null ? void 0 : main2.lastElementChild) ?? void 0;
  main2 !== null && nav !== void 0 && main2.insertBefore(container, nav);
}
const downloadHosterStorageKey = "AudiozUtils_DownloadLinks_host", categoryStorageKey = "AudiozUtils_DownloadLinks_categories", textStorageKey = "AudiozUtils_DownloadLinks_texts";
function extractDownloadLinks() {
  const selectedHoster = getTextArrayFromInput(window.localStorage.getItem(downloadHosterStorageKey) ?? "katfile"), filteredCategories = getTextArrayFromInput(window.localStorage.getItem(categoryStorageKey) ?? ""), filteredTexts = getTextArrayFromInput(window.localStorage.getItem(textStorageKey) ?? "");
  createInputForm({
    title: "Download Links",
    idPrefix: "download-links",
    inputFields: [
      {
        title: "Hosts",
        storageKey: downloadHosterStorageKey,
        values: selectedHoster
      },
      {
        title: "Selected Categories",
        storageKey: categoryStorageKey,
        values: filteredCategories
      },
      {
        title: "Selected Words",
        storageKey: textStorageKey,
        values: filteredTexts
      }
    ]
  });
  const posts = document.querySelectorAll("article");
  return processPosts(Array.from(posts), selectedHoster, filteredCategories, filteredTexts);
}
async function processPosts(posts, hosts, filteredCategories, filteredTexts) {
  const foundLinks = /* @__PURE__ */ new Set(), { progressElm, startElm, container } = createDownloadLinksSection(foundLinks);
  await Promise.all(
    posts.map(async (post) => {
      const postLink = post.querySelector("a.permalink");
      if (!(postLink instanceof HTMLAnchorElement))
        return;
      const postProgressElm = addPostProgressMessage(postLink, progressElm);
      let found = !1;
      try {
        found = await processPost(
          foundLinks,
          postLink,
          container,
          progressElm,
          hosts,
          filteredCategories,
          filteredTexts
        );
      } catch (error) {
        console.error(`Error processing post ${postLink.href}`, error), found = !1;
      }
      postProgressElm.remove(), !(found || postLink.href === window.location.href) && addPostErrorMessage(postLink, post, progressElm);
    })
  ), startElm.remove();
}
function createDownloadLinksSection(foundLinks) {
  const container = createPostContainer();
  addCopyLinksButton(foundLinks, container);
  const { progressElm, startElm } = addProgressElement(container), main2 = document.querySelector("main"), header = (main2 == null ? void 0 : main2.querySelector("header")) ?? void 0;
  return main2 !== null && header !== void 0 && header.appendChild(container), { progressElm, startElm, container };
}
function addProgressElement(container) {
  const progressElm = document.createElement("div"), startElm = document.createElement("p");
  return startElm.innerHTML = "Extracting download links... (grant permissions if prompted)", progressElm.appendChild(startElm), container.appendChild(progressElm), { progressElm, startElm };
}
function addCopyLinksButton(foundLinks, container) {
  const copyButton = document.createElement("button");
  copyButton.innerText = "Copy all download links", copyButton.style.marginTop = "10px", copyButton.addEventListener("click", () => {
    const downloadLinks = [...foundLinks].map((link) => link.downloadLink).join(`
`);
    navigator.clipboard.writeText(downloadLinks).catch((err) => {
      console.error("Failed to copy text: ", err);
    });
  }), container.appendChild(copyButton);
}
function addPostProgressMessage(postLink, progressElm) {
  const postProgressElm = document.createElement("p");
  return postProgressElm.innerHTML = `Processing ${postLink.href}...`, postProgressElm.style.fontSize = "8px", progressElm.appendChild(postProgressElm), postProgressElm;
}
function addPostErrorMessage(postLink, post, progressElm) {
  var _a;
  const noPeeplink = document.createElement("div");
  noPeeplink.style.display = "flex", noPeeplink.style.alignItems = "center";
  const a = document.createElement("a");
  a.href = postLink.href, a.innerText = ((_a = post.querySelector("h2")) == null ? void 0 : _a.innerText) ?? postLink.href, a.style.fontSize = "12px", a.style.color = "red", a.style.marginRight = "10px", noPeeplink.appendChild(a);
  const noPeeplinkText = document.createElement("p");
  noPeeplinkText.innerText = "Error or no download link matched to the given hosts.", noPeeplinkText.style.color = "red", noPeeplinkText.style.fontSize = "12px", noPeeplink.appendChild(noPeeplinkText), noPeeplink.style.height = "18px", progressElm.appendChild(noPeeplink);
}
async function processPost(foundLinks, postLink, container, progressElm, hosts, filteredCategories, filteredTexts) {
  const postElement = await fetchPostElement(postLink);
  if (postElement === null)
    return !1;
  filteredCategories.length > 0 && shouldFilterPostByCategory(postElement, filteredCategories), filteredTexts.length > 0 && shouldFilterPostByText(postElement, filteredTexts);
  const peeplinks = postElement.querySelectorAll("a[href*='peeplink']");
  let found = !1;
  for (const peeplink of peeplinks) {
    if (!(peeplink instanceof HTMLAnchorElement)) {
      addNoPeeplinkMessage(postLink, progressElm);
      continue;
    }
    if (peeplink.href !== "http://peeplink.in/" && (found = await fetchAndProcessPeeplink(peeplink, hosts, postElement, container, progressElm, postLink, foundLinks), found))
      break;
  }
  return found;
}
function addPeeplinkLink(peeplink, container, progressElm) {
  const downloadLink = peeplink.cloneNode();
  downloadLink.style.fontSize = "14px", downloadLink.innerText = peeplink.href, container.insertBefore(downloadLink, progressElm);
}
async function fetchAndProcessPeeplink(peeplink, hosts, postElement, container, progressElm, postLink, foundLinks) {
  return typeof GM > "u" ? (addNoGMMessage(progressElm), addPeeplinkLink(peeplink, container, progressElm), !0) : await new Promise((resolve) => {
    GM.xmlHttpRequest({
      method: "GET",
      url: peeplink.href,
      onload: (response) => {
        let found = !1;
        for (const host of hosts) {
          const dlLink = new DOMParser().parseFromString(response.responseText, "text/html").querySelector(`a[href*='${host}']`);
          if (dlLink === null)
            continue;
          const { postTitle, downloadLink } = addDownloadLinkButton(dlLink, postElement, host, container, progressElm), audioDownloadInfo = addRemmoveButton(postLink, postTitle, dlLink, downloadLink, foundLinks);
          foundLinks.add(audioDownloadInfo), found = !0;
          break;
        }
        resolve(found);
      }
    });
  });
}
async function fetchPostElement(postLink) {
  if (postLink.href === window.location.href)
    return window.document.querySelector("article");
  const postText = await (await fetchWithRetry(postLink.href)).text();
  return new DOMParser().parseFromString(postText, "text/html").querySelector("article");
}
function addDownloadLinkButton(dlLink, postElement, host, container, progressElm) {
  const downloadLink = dlLink.cloneNode();
  downloadLink.style.fontSize = "14px";
  const postTitle = postElement.querySelector("h1");
  return postTitle !== null && (downloadLink.title = postTitle.innerText), downloadLink.innerText = `(${host}) ${((postTitle == null ? void 0 : postTitle.innerText) ?? "").substring(0, 100)}`, container.insertBefore(downloadLink, progressElm), { postTitle, downloadLink };
}
function addRemmoveButton(postLink, postTitle, dlLink, downloadLink, foundLinks) {
  const removeButton = document.createElement("span");
  removeButton.innerText = "❌", removeButton.title = "Remove this download link", removeButton.style.marginLeft = "10px", removeButton.style.fontSize = "x-small", removeButton.className = "fbutton", removeButton.style.display = "inline-block";
  const audioDownloadInfo = {
    postLink: postLink.href,
    title: (postTitle == null ? void 0 : postTitle.innerText) ?? "",
    downloadLink: dlLink.href
  };
  return removeButton.addEventListener("click", (e) => {
    downloadLink.remove(), removeButton.remove(), foundLinks.delete(audioDownloadInfo), e.preventDefault();
  }), downloadLink.appendChild(removeButton), audioDownloadInfo;
}
function addNoPeeplinkMessage(postLink, progressElm) {
  if (postLink.href === window.location.href)
    return;
  const noPeeplink = document.createElement("p");
  noPeeplink.innerHTML = `No peeplink found in ${postLink.href}`, noPeeplink.style.color = "red", progressElm.appendChild(noPeeplink);
}
function addNoGMMessage(progressElm) {
  const noGMAPI = document.createElement("p");
  noGMAPI.innerHTML = "GM API is not available. Please install Tampermonkey or Violentmonkey.", noGMAPI.style.color = "red", progressElm.appendChild(noGMAPI);
}
function createPostContainer() {
  const container = document.createElement("section");
  container.setAttribute("id", "download-links"), container.className = "feed neon nBrown";
  const containerTitle = document.createElement("h3");
  return containerTitle.innerHTML = `
  <span>Download Links<span>
  `, container.appendChild(containerTitle), container.style.marginTop = "20px", container;
}
async function fetchWithRetry(url, options = {}, retries = 3, delay = 1e3) {
  for (let attempt = 0; attempt < retries; attempt++)
    try {
      const response = await fetch(url, options);
      if (!response.ok)
        throw new Error(`HTTP error! status: ${response.status}`);
      return response;
    } catch (error) {
      if (attempt < retries - 1)
        console.warn(`Fetch attempt ${attempt + 1} failed. Retrying in ${delay}ms...`, error), await new Promise((resolve) => setTimeout(resolve, delay));
      else
        throw console.error(`Fetch failed after ${retries} attempts`, error), error;
    }
  throw new Error("Fetch failed");
}
function main() {
  return hidePosts(), extractDownloadLinks();
}
main().catch(console.error);
//# sourceMappingURL=audioz-utils.js.map