MAM Audible Upload

Adds button to copy audiobook data as json to Audible page and opens MAM upload

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name         MAM Audible Upload
// @namespace    https://greasyfork.org/en/users/886084
// @version      1.0.1
// @license      MIT
// @description  Adds button to copy audiobook data as json to Audible page and opens MAM upload
// @author       DrBlank
// @include      https://www.audible.com/pd/*
// @include      https://www.audible.in/pd/*
// @include      https://www.audible.tld/pd/*
// @grant        none
// ==/UserScript==

const RIPPER = "Libation"; // yours can be InAudible or Blank if Encoded
const CHAPTERIZED = true; // yours will be false if not properly ripped

const AVAILABLE_CATEGORIES = [
  "Art",
  "Biographical",
  "Business",
  "Crafts",
  "Fantasy",
  "Food",
  "History",
  "Horror",
  "Humor",
  "Instructional",
  "Juvenile",
  "Language",
  "Medical",
  "Mystery",
  "Nature",
  "Philosophy",
  "Recreation",
  "Romance",
  "Self-Help",
  "Western",
  "Young Adult",
  "Historical Fiction",
  "Literary Classics",
  "Science Fiction",
  "True Crime",
  "Urban Fantasy",
  "Action/Adventure",
  "Computer/Internet",
  "Crime/Thriller",
  "Home/Garden",
  "Math/Science/Tech",
  "Travel/Adventure",
  "Pol/Soc/Relig",
  "General Fiction",
  "General Non-Fic",
];

function getTitle() {
  let title = document.getElementsByTagName("h1")[0].innerText;
  return title;
}

function getSubtitle() {
  let sLoggedIn = document.querySelector(".subtitle");
  let sLoggedOut = document.querySelector("span.bc-size-medium");
  let subtitle = "";
  if (sLoggedIn) {
    subtitle = sLoggedIn.innerText;
  } else if (sLoggedOut) {
    subtitle = sLoggedOut.innerText;
  }

  if (!subtitle) return "";

  let series = getSeriesName().toLowerCase();
  let isSubtitleSeries = Boolean(
    series && subtitle.toLocaleLowerCase().includes(series)
  );
  if (isSubtitleSeries) return "";

  return subtitle;
}

function getTitleAndSubtitle() {
  let subtitle = getSubtitle();
  if (subtitle) {
    return `${getTitle()}: ${subtitle}`;
  }
  return getTitle();
}

function getAuthors() {
  let authorElements = document.querySelectorAll(".authorLabel a");
  let authors = [];
  for (let index = 0; index < authorElements.length; index++) {
    authors[index] = authorElements[index].innerHTML.replace(
      / - (foreword|afterword|translator|editor)/gi,
      ""
    );
  }
  return authors;
}

function getNarrators() {
  let narratorElements = document.querySelectorAll(".narratorLabel a");
  let narrators = [];
  for (let index = 0; index < narratorElements.length; index++) {
    narrators[index] = narratorElements[index].innerHTML;
  }
  return narrators;
}

function getSeriesName() {
  let series = "";
  let seriesElement = document.querySelector(".seriesLabel");
  if (seriesElement) {
    series = seriesElement.querySelector("a").innerHTML;
  }
  return series;
}

function getSeriesBookNumber() {
  let bookNumber = "";
  if (!getSeriesName()) {
    return "";
  }
  let seriesElement = document.querySelector(".seriesLabel");
  let patt = /Book\s*?(\d+\.?\d*-?\d*\.?\d*)/g;
  bookNumber = patt.exec(seriesElement.innerHTML);

  if (!bookNumber) {
    return "";
  }
  return bookNumber[1];
}

function getLanguage() {
  let languageElement = document.querySelector(".languageLabel");
  let patt = /\s*(\w+)$/g;
  let matches = patt.exec(languageElement.innerHTML.trim());
  return matches[1];
}

function getRunTime() {
  let runtimeElement = document.querySelector(".runtimeLabel").textContent;
  /* clean up unnecessary parts of string */
  let patt = new RegExp("Length:\n\\s+(\\d[^\n]+)");
  let matches = patt.exec(runtimeElement);
  /* The first capture group contains the actual runtime */
  return matches[1];
}

function getBookCover() {
  return document.querySelector(".bc-image-inset-border").src;
}

function getAudibleCategory() {
  let categoryElement = document.querySelector(".categoriesLabel");
  if (categoryElement) return categoryElement.innerText;

  categoryElement = document.querySelector("nav.bc-breadcrumb");
  if (categoryElement) return categoryElement.innerText;

  return "";
}

function getMAMCategory() {
  /* TODO: Implement guessing of categories */
  let audibleCategory = getAudibleCategory().toLowerCase();
  let guesses = [];
  AVAILABLE_CATEGORIES.forEach((category) => {
    if (audibleCategory.includes(category.toLowerCase())) {
      guesses.push(`Audiobooks - ${category}`);
      return;
    }

    let separators = ["/", " "];
    separators.forEach((separator) => {
      let splits = category.split(separator);
      splits.forEach((split) => {
        if (audibleCategory.includes(split.toLowerCase())) {
          guesses.push(`Audiobooks - ${category}`);
          return;
        }
      });
    });
  });
  if (guesses.length) return guesses[0];
  return "";
}

function getDescription() {
  let d = document.querySelector(
    ".productPublisherSummary>div>div>span"
  ).innerHTML;
  /* In order: Remove excess whitespace, replace double quotes, remove empty p elements, add line break after every paragraph, and every list */
  return d
    .replace(/\s+/g, " ")
    .replace(/"/g, "'")
    .replace(/<p><\/p>/g, "")
    .replace(/<\/p>/g, "</p><br>")
    .replace(/<\/ul>/g, "</ul><br>");
}

function getReleaseDate() {
  let element = document.querySelector(".releaseDateLabel");
  let patt = /\d{2}-\d{2}-\d{2}/;
  let matches = patt.exec(element.innerText);
  return matches ? matches[0] : "";
}

function getPublisher() {
  return document.querySelector(".publisherLabel>a").innerText;
}

function getAdditionalTags() {
  let raw_tags = [];
  raw_tags.push(`Duration: ${getRunTime()}`);

  if (CHAPTERIZED) raw_tags.push("Chapterized");

  if (RIPPER) raw_tags.push(`${RIPPER} True Decrypt`);

  raw_tags.push(
    `Audible Release: ${getReleaseDate()}`,
    `Publisher: ${getPublisher()}`,
    getAudibleCategory()
  );
  // let tags = `Duration: ${getRunTime()} | Chapterized | Libation True Decrypt | ${getAudibleCategory()} | Audible Release: ${getReleaseDate()} | Publisher: ${getPublisher()}`;
  // return tags;
  return raw_tags.join(" | ");
}

function getSeries() {
  let seriesName = getSeriesName();
  if (seriesName) {
    let bookNumber = getSeriesBookNumber();
    return [{ name: seriesName, number: bookNumber }];
  }
  return [];
}

function fallbackCopyTextToClipboard(text) {
  var textArea = document.createElement("textarea");
  textArea.value = text;

  // Avoid scrolling to bottom
  textArea.style.top = "0";
  textArea.style.left = "0";
  textArea.style.position = "fixed";

  document.body.appendChild(textArea);
  textArea.focus();
  textArea.select();

  try {
    var successful = document.execCommand("copy");
    var msg = successful ? "successful" : "unsuccessful";
    console.log("Fallback: Copying text command was " + msg);
  } catch (err) {
    console.error("Fallback: Oops, unable to copy", err);
  }

  document.body.removeChild(textArea);
}

function copyTextToClipboard(text) {
  if (!navigator.clipboard) {
    fallbackCopyTextToClipboard(text);
    return;
  }
  navigator.clipboard.writeText(text).then(
    function () {
      window.open("https://www.myanonamouse.net/tor/upload.php", "_blank");
    },
    function (err) {
      console.error("Async: Could not copy text: ", err);
    }
  );
}

let buttonStr = `<div id="" class="bc-row bc-spacing-top-s1">
  <div class="bc-row">
    <div class="bc-trigger bc-pub-block">
      <span class="bc-button bc-button-primary">
        <button
          id="upload-to-mam"
          class="bc-button-text"
          type="button"
          tabindex="0" title="Copy book details as JSON"
        >
          <span class="bc-text bc-button-text-inner bc-size-action-large">
            Copy as JSON
          </span>
        </button>
      </span>
    </div>
  </div>
  </div>
  `;

let foo = document.createElement("foo");
foo.innerHTML = buttonStr.trim();

let button = foo.firstChild;
document.querySelector("#adbl-common-buybox").appendChild(button);

button.addEventListener("click", function (event) {
  copyTextToClipboard(
    JSON.stringify({
      authors: getAuthors(),
      description: getDescription(),
      narrators: getNarrators(),
      tags: getAdditionalTags(),
      thumbnail: getBookCover(),
      title: getTitleAndSubtitle(),
      language: getLanguage(),
      series: getSeries(),
      category: getMAMCategory(),
    })
  );
});