Greasy Fork

Mangadex API v5 reader

5/8/2021, 10:46:50 AM

目前为 2021-05-11 提交的版本。查看 最新版本

// ==UserScript==
// @name        Mangadex API v5 reader
// @namespace   Violentmonkey Scripts
// @match       https://mangadex.org/index.html
// @grant       none
// @version     1.3
// @author      -
// @description 5/8/2021, 10:46:50 AM
// ==/UserScript==

window.sessionToken = undefined;
const login = async(username,password)=>{
  let response = await fetch("https://api.mangadex.org/auth/login",{method:"POST",body:JSON.stringify({
    username,
    password
  })});
  let data = await response.json();
  if(data.result==="ko") alert("wrong password!");
  window.sessionToken = data.token.session;
};

let cache = new Map();
let fetchJSON = async(url)=>{
  
  if(cache.has(url)) return cache.get(url);
  
	let response = await fetch(url,sessionToken?{
    headers: {
      Authorization: sessionToken
    }
  }:undefined);
  try{
	  let data = await response.json();
    cache.set(url,data);
    return data;
  }  catch(e) {
    cache.set(url,null);
    return null
  }
};

async function* listIterator(urlstring, params, limit=100) {
  let i=0;
  params.limit = limit;
  while(true) {
    let url = new URL(urlstring);
    params.offset = i;
    url.search = new URLSearchParams(params);
    let result = await fetchJSON(url.toString());
    if(result===null || result.results.length === 0) return;
    for(let e of result.results) {
      yield e;
    }
    i+=limit;
  }
}

let getImageUrlsFromChapterID = async(chapterID)=>{
	let chapter = await fetchJSON(`https://api.mangadex.org/chapter/${chapterID}`);
  let server = await fetchJSON(`https://api.mangadex.org/at-home/server/${chapterID}`);
  console.log(chapter)
  let urls = chapter.data.attributes.dataSaver.map(s=>`${server.baseUrl}/data-saver/${chapter.data.attributes.hash}/${s}`)
  return urls;
};
let getChaptersFromMangaID = async(mangaID)=>{
  let chapters = [];
  for await (let result of listIterator(`https://api.mangadex.org/manga/${mangaID}/feed`,{"locales[]":["en"]})) {
    chapters.push(result)
  }
  let string = (c)=>(c.data.attributes.volume||"0")+"."+(c.data.attributes.chapter||"0");
  return chapters.sort((a,b)=>string(a).localeCompare(string(b), undefined, {numeric: true}));
  // return chapters;
	// let chapters = await fetchJSON(`https://api.mangadex.org/manga/${mangaID}/feed?locales[]=en`);
  // return chapters.results;
};
let getGroupNamesFromChapter = async(chapter)=>{
  let groupIDs = chapter.relationships.filter(e=>e.type==="scanlation_group").map(e=>e.id);
  let groups = await Promise.all(groupIDs.map(id=>fetchJSON(`https://api.mangadex.org/group/${id}`)));
  return groups.map(g=>g.data.attributes.name);
};
let search = async(title)=>{
  let matches = [];
  for await (let result of listIterator("https://api.mangadex.org/manga",{title})) {
    let resultTitle = result.data.attributes.title.en;
    if(resultTitle.toLowerCase().indexOf(title.toLowerCase())>=0) matches.push(result);
  }
  return matches;
};

const displayChapter = async(chapter)=>{
  searchResults.innerHTML = "";
  
  let imageIDs = await getImageUrlsFromChapterID(chapter.data.id);
  let mangaID = chapter.relationships.filter(e=>e.type==="manga").map(e=>e.id)[0];
  const links = ()=>{
    let manga = document.createElement("div");
    manga.innerText = "manga";
    manga.addEventListener("click", ()=>{
      displayManga(mangaID);
    });
    searchResults.appendChild(manga);
    
    let chapterNumber = document.createElement("div");
    chapterNumber.innerText = " chapter: "+chapter.data.attributes.chapter;
    searchResults.appendChild(chapterNumber);
  };
  
  links();
  
  for(let e of imageIDs) {
    let image = document.createElement("img");
    image.src = e;
    searchResults.appendChild(image);
  }
  
  links();
};

const displayManga = async(id)=>{
  searchResults.innerHTML = "";
  let chapters = await getChaptersFromMangaID(id);
  
  for(let e of chapters) {
    let chapter = document.createElement("div");
    
    let names = await getGroupNamesFromChapter(e);
    chapter.innerText = `${e.data.attributes.volume||0}.${e.data.attributes.chapter||0} ${e.data.attributes.title} by ${names.join(" ")}`;
    chapter.addEventListener("click",()=>{
      displayChapter(e);
    });
    searchResults.appendChild(chapter);
  }
};

const displayMangaList = async(mangaList)=>{
  searchResults.innerHTML = "";
  for(let e of mangaList) {
    let id = e.data.id;
    let title = e.data.attributes.title.en;
    let lastChapter = "";
    if(e.data.attributes.lastChapter && e.data.attributes.lastChapter!=="0") lastChapter = `[last chapter: ${e.data.attributes.lastVolume||0}.${e.data.attributes.lastChapter}]`;
    
    let manga = document.createElement("div");
    manga.innerText = `${title} ${lastChapter}`;
    manga.addEventListener("click",()=>{
      displayManga(id);
    });
    searchResults.appendChild(manga);
  }
};

let div = document.createElement("div");
div.innerHTML = `
<style>
body {
  margin: 0px;
  padding: 0px;
}
img:not([src^="data:"]) {
  width: 100%;
}
input#search {
  width: 100%;
}
</style>
<input id="username" placeholder="username" type="text" name="username">
<input id="password" placeholder="password" type="password" name="password">
<button id="show-follows">show follows</button>
<br/>
<input id="search" type="search" placeholder="manga title">
<div id="search-results"></div>
`;
document.body.insertBefore(div,document.body.childNodes[0])
let searchResults = document.querySelector("#search-results");

document.querySelector("#search").addEventListener("input",async(e)=>{
  console.log(e.target.value);
  let query = e.target.value;
  let result = await search(query);
  
  displayMangaList(result);
});
document.querySelector("#show-follows").addEventListener("click",async(e)=>{
  let username = document.querySelector("#username").value;
  let password = document.querySelector("#password").value;
  await login(username,password);
  
  let response = await fetch("https://api.mangadex.org/user/follows/manga?limit=100",{
    headers: {
      Authorization:sessionToken
    }
  });
  let data = await response.json();
  console.log(data);
  displayMangaList(data.results);
});