Greasy Fork

Greasy Fork is available in English.

Anisongs

Adds Anisongs to anime entries on AniList

当前为 2018-12-11 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name Anisongs
// @namespace Morimasa
// @author Morimasa
// @description Adds Anisongs to anime entries on AniList
// @match https://anilist.co/*
// @version 1.02
// @grant GM_xmlhttpRequest
// ==/UserScript==

const options = {
  cacheName: 'anison',
  cacheSize: 100,
  cacheLife: 604800000, // 1 week in ms
  class: 'anisongs'
}
let last = {id:0};
let target;

const style = document.createElement('style');
style.innerHTML = `
.update-spinner {
    animation: 2s spin linear infinite;
}
`
document.head.appendChild(style);

const insert = (list, parent) => {
  if (list===undefined || list.length===0){
    let node = document.createElement('div');
    node.classList = 'no-reviews';
    node.setAttribute('data-v-3dd773fc','');
    node.innerText = 'No songs to show (つ﹏<)・゚。';
    parent.appendChild(node);
    return;
  }
  else{
    list.forEach((title, i)=>{
      let node = document.createElement('p');
      node.innerText = `${i+1}. ${title}`;
      node.setAttribute('data-v-4e418c9e','');
      node.classList = "tag";
      parent.appendChild(node);
    })
  }
}

const createTargetDiv = (text, target, pos) => {
  let el = document.createElement('div');
  el.appendChild(document.createElement('h2'));
  el.children[0].innerText = text;
  el.classList = options.class;
  let btn = document.createElement('SPAN');
  btn.addEventListener('click', manualUpdate);
  btn.innerHTML='<svg aria-hidden="true" role="img" xmlns="http://www.w3.org/2000/svg" class="svg-inline--fa" viewBox="0 0 512 512"><path fill="currentColor" d="M55.89,262.818c-3-26-0.5-51.1,6.3-74.3c22.6-77.1,93.5-133.8,177.6-134.8v-50.4c0-2.8,3.5-4.3,5.8-2.6l103.7,76.2c1.7,1.3,1.7,3.9,0,5.1l-103.6,76.2c-2.4,1.7-5.8,0.2-5.8-2.6v-50.3c-55.3,0.9-102.5,35-122.8,83.2c-7.7,18.2-11.6,38.3-10.5,59.4c1.5,29,12.4,55.7,29.6,77.3c9.2,11.5,7,28.3-4.9,37c-11.3,8.3-27.1,6-35.8-5C74.19,330.618,59.99,298.218,55.89,262.818zM355.29,166.018c17.3,21.5,28.2,48.3,29.6,77.3c1.1,21.2-2.9,41.3-10.5,59.4c-20.3,48.2-67.5,82.4-122.8,83.2v-50.3c0-2.8-3.5-4.3-5.8-2.6l-103.7,76.2c-1.7,1.3-1.7,3.9,0,5.1l103.6,76.2c2.4,1.7,5.8,0.2,5.8-2.6v-50.4c84.1-0.9,155.1-57.6,177.6-134.8c6.8-23.2,9.2-48.3,6.3-74.3c-4-35.4-18.2-67.8-39.5-94.4c-8.8-11-24.5-13.3-35.8-5C348.29,137.718,346.09,154.518,355.29,166.018z"></path></svg>';
  btn.style.float='right';
  btn.style.cursor='pointer';
  el.children[0].appendChild(btn);
  target.insertBefore(el, target.children[pos]);
  return el;
}

const placeData = data => {
  let op = createTargetDiv('Openings', target, 2)
  let ed = createTargetDiv('Endings', target, 3)
  insert(data.opening_themes, op);
  insert(data.ending_themes, ed);
}

const handleData = data => {
  cleaner(target)
  let resp = JSON.parse(data.responseText);
  placeData(resp);
  try{
    if (Object.keys(JSON.parse(localStorage[options.cacheName])).length>options.cacheSize) clearCache();
  }
  catch{}
  addCache(last.id, {opening_themes: resp.opening_themes, ending_themes: resp.ending_themes, time: +new Date()});
}

const getMal = id => {
  if (id===null) return console.info("No MAL id in API")
  GM_xmlhttpRequest({
    method: "GET",
    url: `https://api.jikan.moe/v3/anime/${id}/`,
    headers: {
        "Accept": "application/json"
    },
    onload: handleData
  })
}

const getMalId = () => {
    const query = `query($id:Int){Media(id:$id){idMal}}`
    let vars = {id: parseInt(window.location.pathname.split("/")[2])};
	const options = {
		method: 'POST',
		body: JSON.stringify({query: query, variables: vars}),
		headers: new Headers({
			'Content-Type': 'application/json'
		})
	};
	return fetch('https://graphql.anilist.co/', options)
	.then(res => res.json())
	.then(res => getMal(res.data.Media.idMal))
	.catch(error => console.error(`Error: ${error}`));
}

const addCache = (id, data) => {
  let cache = JSON.parse(localStorage.getItem(options.cacheName)) || {};
  cache[id] = data
  localStorage.setItem(options.cacheName, JSON.stringify(cache));
}

const getCache = id => {
  let cache = localStorage.getItem(options.cacheName);
  if (cache===null)
    return {time:0}
  else
    return JSON.parse(cache)[id] || {time:0};
}

const clearCache = () => {
  delete localStorage[options.cacheName]
}

const cleaner = (target) => {
  let el = target.querySelectorAll(`.${options.class}`);
  el.forEach((e)=>{
    target.removeChild(e)
  })
}

const manualUpdate = () => {
  getMalId();
  document.querySelectorAll('.anisongs span').forEach(e=>{
    e.classList='update-spinner';
  })
}

let observer = new MutationObserver(() => {
    if (window.location.href.includes('/anime/', 18)) {
      let currentid = window.location.href.split("/")[4];
      let location = window.location.pathname.split("/").pop();
      if (location!=='') last.id=0;
      target = document.querySelectorAll('.grid-section-wrap')[1];
      if(last.id!==currentid && location==='' && target!==undefined){
        last.id = currentid;
        let cache = getCache(currentid);
        let TTLpassed = (cache.time + options.cacheLife)<+new Date();
        if (TTLpassed){
          getMalId();
          console.info("OP/ED loaded from API");
        }
        else{
          placeData(cache);
          console.info("OP/ED loaded from Cache");
        }
      }
   }
  else
    last.id = 0;
});
observer.observe(document.getElementById('app'), {childList: true, subtree: true});