AnimeWorld Skipper

Salta anime intro in AnimeWorld

安装此脚本
作者推荐脚本

您可能也喜欢BetterAnimeWorld

安装此脚本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         AnimeWorld Skipper
// @namespace    https://github.com/pizidavi
// @icon         https://static.animeworld.ac/assets/images/favicon/android-icon-192x192.png
// @description  Salta anime intro in AnimeWorld
// @author       pizidavi
// @version      1.1.1
// @copyright    2023, PIZIDAVI
// @license      MIT
// @require      https://cdn.jsdelivr.net/gh/soufianesakhi/node-creation-observer-js@edabdee1caaee6af701333a527a0afd95240aa3b/release/node-creation-observer-latest.min.js
// @require      https://cdn.jsdelivr.net/gh/sizzlemctwizzle/GM_config@2207c5c1322ebb56e401f03c2e581719f909762a/gm_config.min.js
// @require      https://gf.qytechs.cn/scripts/457460-aniskip/code/AniSkip.js
// @require      https://gf.qytechs.cn/scripts/401626-notify-library/code/Notify%20Library.js
// @match        https://www.animeworld.ac/play/*
// @connect      api.aniskip.com
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_xmlhttpRequest
// @grant        GM_registerMenuCommand
// @grant        GM_getResourceText
// @run-at       document-body
// ==/UserScript==

/* global GM_config, NodeCreationObserver, AniSkip, Notify */

(function () {
  'use strict';

  //* GM_config
  GM_config.init({
    id: GM.info.script.name.toLowerCase().replace(/\s/g, '-'),
    title: `${GM.info.script.name} v${GM.info.script.version} Settings`,
    fields: {
      AniSkipUserId: {
        label: 'AniSkip User Id',
        section: ['AniSkip', 'Ottieni su: https://www.uuidgenerator.net/'],
        labelPos: 'left',
        type: 'text',
        title: 'Il tuo AniSkip userId',
        size: 70,
        default: ''
      },
      ManualSeekTime: {
        label: 'Secondi saltati con Salto Manuale (s)',
        section: ['Settings', 'Script settings'],
        type: 'int',
        default: 90
      },
      SeekWheel: {
        label: 'Millisecondi saltati con rotella del mouse su input nel Menu (ms)',
        type: 'int',
        default: 50
      },
      SubmittedSkip: {
        label: 'Times Skip inviati',
        section: ['Stats', '(solo lettura)'],
        type: 'int',
        default: 0,
        save: false
      }
    },
    events: {
      init: () => { },
      open: () => {
        GM_config.set('SubmittedSkip', GM_getValue('SubmittedSkip', 0));
      },
      save: () => {
        alert('Impostazioni salvate');
        GM_config.close()
        setTimeout(window.location.reload(false), 500);
      }
    }
  });
  GM_registerMenuCommand('Configure', () => GM_config.open());

  //* Const
  const CSS = `
  /* Skip Button */
  #player { display: flex; }
  .aws-btn {
    display: none;
    position: absolute;
    bottom: 6em;
    right: 2em;
    padding: .5em 1em .4em;
    font-size: 14px;
    border: 1px solid #bdc3c7;
    border-radius: 2px;
    color: #bdc3c7;
    background-color: rgba(0, 0, 0, .7);
    opacity: .35;
    z-index: 2147483648;
  }
  .aws-btn:hover {
    opacity: .75;
  }
  .aws-btn.active {
    display: block;
  }
  /* Menu */
  .aws-container {
    position: absolute;
    top: 1em;
    right: 1em;
    border: none;
  }
  .aws-container summary {
    cursor: pointer;
    color: white;
    background-color: rgba(35, 35, 35, 0.5);
    border-radius: 5px;
    padding: 1px 5px;
    user-select: none;
    opacity: 0.5;
    transition: opacity 0.3s;
  }
  .aws-container[open] summary,
  .aws-container summary:hover {
    opacity: 0.8;
  }
  .aws-container summary::-webkit-details-marker {
    display: none;
  }
  .aws-menu {
    position: absolute;
    right: 0;
    padding: 10px;
    border-radius: 3px;
    color: white;
    background-color: rgba(20, 20, 20, 0.95);
    z-index: 2;
    min-width: 300px;
  }
  .aws-menu h4,
  .aws-menu h5 {
    margin-top: 0;
    margin-bottom: 3px;
  }
  .aws-menu h5 {
    margin-bottom: 0;
  }
  .aws-menu hr {
    margin: 10px 0;
    padding: 0;
  }
  #aws-message {
    text-align: center;
    margin-bottom: 0;
  }
  #aws-reload-list {
    fill: white;
    width: 11px;
    margin-right: 2px;
  }
  .aws-skip-list {
    overflow-y: auto;
    max-height: 250px;
  }
  .aws-skip-list .skip-item {
    display: flex;
    align-items: center;
    padding: 5px;
    border-bottom: 1px solid #555;
  }
  .aws-skip-list .skip-item:last-child {
    border-bottom: none;
  }
  .aws-skip-list .skip-item .icon {
    display: block;
    height: 25px;
    width: 25px;
    margin-right: 10px;
    float: left;
    background-color: #bbb;
    border: 1px solid white;
    border-radius: 50%;
  }
  .aws-skip-list .skip-item .icon.op {
    background-color: green
  }
  .aws-skip-list .skip-item .icon.ed {
    background-color: gold
  }
  .aws-skip-list .skip-item .icon.mixed-op {
    background-color: #90ee90
  }
  .aws-skip-list .skip-item .icon.mixed-ed {
    background-color: #ff0
  }
  .aws-skip-list .skip-item .icon.recap {
    background-color: #add8e6
  }
  .aws-skip-list .skip-item .info .name {
    font-weight: 400;
    font-size: 1.1rem;
  }
  .aws-skip-list .skip-item .info p {
    margin: 0;
    font-size: .9rem;
  }
  .aws-skip-list .skip-item .action {
    display: flex;
    margin-left: auto;
  }
  .aws-skip-list .skip-item .action button {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 25px;
    aspect-ratio: 1;
    margin-right: 4px;
    padding: 0;
    border-radius: 25%;
  }
  .aws-skip-list .skip-item .action button:last-child {
    margin-right: 0;
  }
  .aws-skip-list .skip-item .action button svg {
    width: 12px;
  }
  `;
  const MENU = `
  <details id="aws" class="aws-container">
    <summary title="Apri/Chiudi menu" role="button">AW Skipper</summary>
    <div class="aws-menu">
      <h4>AnimeWorld Skipper</h4>
      <hr />
      <!-- Form -->
      <form>
        <h5>Add Skip Time</h5>
        <select class="form-control" name="aws-skip-type" style="margin-bottom:5px" required>
          <option value hidden selected>Seleziona una tipologia</option>
          <option value="op">Opening</option>
          <option value="ed">Ending</option>
          <option value="mixed-op">Mixed Epening</option>
          <option value="mixed-ed">Mixed Ending</option>
          <option value="recap">Recap</option>
        </select>
        <div class="row">
          <div class="col-sm-4" style="padding-right:0;">
            <input
              type="number"
              class="form-control"
              name="aws-start"
              value="0.000"
              placeholder="Start time"
              step="0.001"
              min="0"
              required
              data-aws-wheel
            >
            <small data-aws-goTo="now" role="button">+Ora</small>
          </div>
          <div class="col-sm-4" style="padding-right:0;">
            <input
              type="number"
              class="form-control"
              name="aws-end"
              value="0.000"
              placeholder="End time"
              step="0.001"
              min="0"
              required
              data-aws-wheel
            >
            <small data-aws-goTo="now" role="button">+Ora</small>
            <small data-aws-goTo="+90" role="button">+90s</small>
            <small data-aws-goTo="end" style="margin-left:auto;" role="button">+Fine</small>
          </div>
          <div class="col-sm-4">
            <button
              class="btn btn-block btn-primary"
              type="submit"
              disabled
            >Salva</button>
          </div>
        </div>
      </form>
      <!-- End form -->
      <hr />
      <!-- Skip list -->
      <h5>
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" id="aws-reload-list" role="button">
          <path d="M463.5 224H472c13.3 0 24-10.7 24-24V72c0-9.7-5.8-18.5-14.8-22.2s-19.3-1.7-26.2 5.2L413.4 96.6c-87.6-86.5-228.7-86.2-315.8 1c-87.5 87.5-87.5 229.3 0 316.8s229.3 87.5 316.8 0c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0c-62.5 62.5-163.8 62.5-226.3 0s-62.5-163.8 0-226.3c62.2-62.2 162.7-62.5 225.3-1L327 183c-6.9 6.9-8.9 17.2-5.2 26.2s12.5 14.8 22.2 14.8H463.5z" />
        </svg>
        Skip Times
      </h5>
      <div id="aws-skip-list" class="aws-skip-list"><!-- Items --></div>
      <p id="aws-message" style="text-align:center;">Waiting for video metadata</p>
      <!-- End skip list -->
    </div>
  </details>
  `;
  const TEMPLATE_ITEM = `
  <div class="skip-item">
    <span class="icon"></span>
    <div class="info">
      <span class="name"></span>
      <p class="times">
        <span class="time-start" role="button" title="Go to start"></span>
        -
        <span class="time-end" role="button" title="Go to end"></span>
      </p>
    </div>
    <div class="action">
      <button class="btn btn-dark" data-aws-vote="upvote">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
          <path d="M313.4 32.9c26 5.2 42.9 30.5 37.7 56.5l-2.3 11.4c-5.3 26.7-15.1 52.1-28.8 75.2H464c26.5 0 48 21.5 48 48c0 25.3-19.5 46-44.3 47.9c7.7 8.5 12.3 19.8 12.3 32.1c0 23.4-16.8 42.9-38.9 47.1c4.4 7.2 6.9 15.8 6.9 24.9c0 21.3-13.9 39.4-33.1 45.6c.7 3.3 1.1 6.8 1.1 10.4c0 26.5-21.5 48-48 48H294.5c-19 0-37.5-5.6-53.3-16.1l-38.5-25.7C176 420.4 160 390.4 160 358.3V320 272 247.1c0-29.2 13.3-56.7 36-75l7.4-5.9c26.5-21.2 44.6-51 51.2-84.2l2.3-11.4c5.2-26 30.5-42.9 56.5-37.7zM32 192H96c17.7 0 32 14.3 32 32V448c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32V224c0-17.7 14.3-32 32-32z" fill="black"></path>
        </svg>
      </button>
      <button class="btn" data-aws-vote="downvote">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
          <path d="M313.4 479.1c26-5.2 42.9-30.5 37.7-56.5l-2.3-11.4c-5.3-26.7-15.1-52.1-28.8-75.2H464c26.5 0 48-21.5 48-48c0-25.3-19.5-46-44.3-47.9c7.7-8.5 12.3-19.8 12.3-32.1c0-23.4-16.8-42.9-38.9-47.1c4.4-7.3 6.9-15.8 6.9-24.9c0-21.3-13.9-39.4-33.1-45.6c.7-3.3 1.1-6.8 1.1-10.4c0-26.5-21.5-48-48-48H294.5c-19 0-37.5 5.6-53.3 16.1L202.7 73.8C176 91.6 160 121.6 160 153.7V192v48 24.9c0 29.2 13.3 56.7 36 75l7.4 5.9c26.5 21.2 44.6 51 51.2 84.2l2.3 11.4c5.2 26 30.5 42.9 56.5 37.7zM32 320H96c17.7 0 32-14.3 32-32V64c0-17.7-14.3-32-32-32H32C14.3 32 0 46.3 0 64V288c0 17.7 14.3 32 32 32z"/>
        </svg>
      </button>
    </div>
  </div>
  `;

  const aniskip = new AniSkip({
    userId: GM_config.get('AniSkipUserId'),
    providerName: 'AnimeWorld'
  });
  const seekWheel = GM_config.get('SeekWheel', 50) / 1000;

  //* Script
  NodeCreationObserver.init('AnimeWorldSkipper');
  NodeCreationObserver.onCreation('#player iframe', function (iframe) {
    const malId = document.querySelector('#mal-button').getAttribute('href').split('/').at(-1);
    const episodeNumber = document.querySelector('div.server ul a.active').getAttribute('data-base').split('-')[0];

    iframe.addEventListener('load', function () {
      const content = this.contentDocument;
      const video = content?.querySelector('video');
      if (!video) return;

      injectStyle(CSS, content.head);
      const menu = injectHTML(MENU, video.parentNode);
      menu.querySelector('form [name="aws-start"]').onchange = function () {
        this.value = (parseFloat(this.value) || 0).toFixed(3);
        video.currentTime = this.value;
      };
      menu.querySelector('form [name="aws-end"]').onchange = function () {
        this.value = (parseFloat(this.value) || 0).toFixed(3);
        video.currentTime = this.value;
      };

      const loadskipdata = () => {
        const videoLength = video.duration;

        content.querySelectorAll('.aws-btn').forEach(element => {
          element.remove();
        });
        menu.querySelector('#aws-message').textContent = 'Loading...';
        menu.querySelector('#aws-skip-list').innerHTML = '';
        menu.querySelector('#aws-reload-list').onclick = () => {
          video.dispatchEvent(new Event('loadskipdata'));
        };

        menu.querySelector('form [name="aws-start"]').max = videoLength.toFixed(3);
        menu.querySelector('form [name="aws-end"]').max = videoLength.toFixed(3);
        menu.querySelector('form button[type="submit"]').disabled = false;

        menu.querySelectorAll('form [data-aws-goTo]').forEach(element => {
          const action = element.getAttribute('data-aws-goTo');
          element.onclick = function () {
            const value = (action === 'now' ? video.currentTime : action === '+90' ? video.currentTime+90 : videoLength).toFixed(3);
            this.parentNode.querySelector('input').value = value;
            this.parentNode.querySelector('input').dispatchEvent(new Event('change'));
          };
        });
        menu.querySelectorAll('form [data-aws-wheel]').forEach(element => {
          element.onwheel = function (e) {
            e.preventDefault();
            e.stopPropagation();
            let newValue = (parseFloat(this.value) || 0) + (e.deltaY >= 0 ? -seekWheel : seekWheel);
            if (newValue < 0)
              newValue = 0;
            else if (newValue > videoLength)
              newValue = videoLength;
            if (this.value !== newValue.toFixed(3)) {
              this.value = newValue.toFixed(3);
              this.dispatchEvent(new Event('change'));
            }
          };
        });

        aniskip.getSkipTimes(malId, episodeNumber, videoLength)
          .then(data => {
          console.log(data)
          menu.querySelector('#aws-message').textContent = '';

          data.forEach(item => {
            // Add skip-btn to video
            const skipButton = createSkipButton({
              id: item.skipId,
              text: 'Salta ' + aniskip.CategoryTitle[item.skipType]
            });
            skipButton.onclick = () => {
              video.currentTime = item.interval.endTime;
              video.focus();
            };
            skipButton.setAttribute('data-time-start', item.interval.startTime);
            skipButton.setAttribute('data-time-end', item.interval.endTime);
            video.parentNode.appendChild(skipButton);

            // Add skip-item to menu
            const skipItem = injectHTML(TEMPLATE_ITEM, menu.querySelector('#aws-skip-list'));
            skipItem.querySelector('.icon').classList.add(item.skipType);
            skipItem.querySelector('.name').textContent = aniskip.CategoryTitle[item.skipType];

            skipItem.querySelector('.times .time-start').textContent = item.interval.startTime.toFixed(1);
            skipItem.querySelector('.times .time-start').onclick = () => {
              video.currentTime = item.interval.startTime;
            };
            skipItem.querySelector('.times .time-end').textContent = item.interval.endTime.toFixed(1);
            skipItem.querySelector('.times .time-end').onclick = () => {
              video.currentTime = item.interval.endTime;
            };
            skipItem.querySelectorAll('.action [data-aws-vote]').forEach(element => {
              const action = element.getAttribute('data-aws-vote');
              element.onclick = function () {
                this.disabled = true;
                aniskip.vote(action, item.skipId)
                  .then(data => {
                  new Notify({
                    text: data.message || 'Votato!',
                    type: 'success'
                  }).show();
                })
                  .catch(data => {
                  console.error(data)
                  new Notify({
                    text: data.message || 'Errore',
                    type: 'error'
                  }).show();
                })
                  .finally(() => {
                  video.dispatchEvent(new Event('loadskipdata'));
                });
              }
            });
          });
        })
          .catch(response => {
          console.log(response)
          menu.querySelector('#aws-message').textContent = response.message || 'No skip time';

          // Add skip-btn to video
          const skipButton = createSkipButton({
            id: 'manual-skip',
            text: 'Salto Manuale',
            class: 'aws-btn manual active',
          });
          skipButton.onclick = function () {
            const seekTime = GM_config.get('ManualSeekTime', 90);
            const form = menu.querySelector('form');
            const currentTime = video.currentTime - 0.4;
            form.querySelector('[name="aws-start"]').value = currentTime.toFixed(3);
            form.querySelector('[name="aws-end"]').value = (currentTime + seekTime).toFixed(3);
            menu.setAttribute('open', true);
            video.pause();

            video.currentTime += seekTime;
            video.focus();
            this.remove();
          };
          video.parentNode.appendChild(skipButton);
        });

        menu.querySelector('form').onsubmit = function (e) {
          e.preventDefault();
          e.stopPropagation();
          const form = this;
          const skipType = form.querySelector('[name="aws-skip-type"]').value;
          const timeStart = parseFloat(form.querySelector('[name="aws-start"]').value);
          const timeEnd = parseFloat(form.querySelector('[name="aws-end"]').value);
          if (
            !skipType ||
            (!timeStart && timeStart !== 0) ||
            (!timeEnd && timeEnd !== 0) ||
            timeStart >= timeEnd ||
            timeStart < 0 ||
            timeEnd > videoLength
          ) {
            alert('Campi non validi');
            return;
          }

          form.querySelector('button[type="submit"]').disabled = true;
          aniskip.createSkipTime(
            malId,
            episodeNumber,
            {
              skipType: skipType,
              startTime: timeStart,
              endTime: timeEnd,
              episodeLength: videoLength
            }
          ).then(data => {
            form.querySelector('button[type="submit"]').disabled = false;
            form.reset();
            GM_setValue('SubmittedSkip', GM_getValue('SubmittedSkip', 0) + 1);
            video.dispatchEvent(new Event('loadskipdata'));
          }).catch(error => console.error(error));
        };
      };

      video.addEventListener('loadskipdata', loadskipdata);
      if (video.readyState > 0) {
        loadskipdata();
        video.play();
      } else
        video.onloadedmetadata = loadskipdata;

      // Show/Hide skipButton base of currentTime
      video.ontimeupdate = () => {
        content.querySelectorAll('.aws-btn:not(.manual)').forEach(element => {
          const startTime = element.getAttribute('data-time-start');
          const endTime = element.getAttribute('data-time-end');
          const active = video.currentTime >= startTime && video.currentTime < endTime;
          element.classList.toggle('active', active);
        });
        if (video.currentTime > video.duration / 3)
          content.querySelector('#manual-skip')?.remove();
      };

      video.oncanplay = function () {
        this.play();
        this.focus();
        this.oncanplay = null; // only at video start
      };

      video.onmouseenter = function () {
        if (!this.paused && !this.ended)
          this.focus();
      };

      content.onkeydown = e => {
        if (e.keyCode === 13) {
          const btn = content.querySelector('.aws-btn.active');
          if (btn) {
            e.preventDefault();
            btn.click();
          }
        }
      };

    }); // end iframe.addEventListener
  }); // end NodeCreationObserver.onCreation

  injectMeta('AnimeWorldSkipper');


  //* Functions
  function createSkipButton(options = {}) {
    return createElement('button', {
      text: options.text ?? 'Salta',
      class: options.class ?? 'aws-btn',
      ...options
    });
  }

  function createElement(tagName, options) {
    const element = document.createElement(tagName);
    element.textContent = options?.text;
    element.id = options?.id ?? '';
    element.classList = options?.class ?? '';
    element.title = options?.title ?? '';
    element.value = options?.value ?? '';
    element.style = options?.style ?? '';
    element.innerHTML = options?.html ?? element.innerHTML;
    return element;
  }

  function injectHTML(html, parent = document.body) {
    const tag = document.createElement('div');
    tag.innerHTML = html;
    return parent.appendChild(tag.childElementCount === 1 ? tag.firstElementChild : tag);
  }

  function injectStyle(style, parent = document.head) {
    const tag = document.createElement('style');
    tag.innerText = style;
    parent.appendChild(tag);
  }

  function injectMeta(name, content = '', parent = document.head) {
    const tag = document.createElement('meta');
    tag.name = name;
    tag.content = content;
    parent.appendChild(tag);
  }

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址