Iwara Custom Sort

Automatically sort video results in a page on /videos, /images, /subscriptions, and sidebars using customizable sort function.

2019-02-08 기준 버전입니다. 최신 버전을 확인하세요.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

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

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name     Iwara Custom Sort
// @version  0.114
// @grant    GM.setValue
// @grant    GM.getValue
// @grant    GM.deleteValue
// @match    https://ecchi.iwara.tv/*
// @match    https://www.iwara.tv/*
// @match    http://ecchi.iwara.tv/*
// @match    http://www.iwara.tv/*
// @description  Automatically sort video results in a page on /videos, /images, /subscriptions, and sidebars using customizable sort function.
// @namespace https://greasyfork.org/users/245195
// ==/UserScript==

/* jshint esversion: 6 */

const logDebug = (...args) => {
  const debugging = true;
  if (debugging) {
  	console.log(...args);
  }
}

logDebug('Parsed.');

const parsePrefixed = (str) => {
  return Number.parseFloat(str) * (str.includes('k') ? 1000 : 1);
}

const sortVideos = (videosContainer, sortValueExpression) => {
  const videoDivs = Array.from(videosContainer.querySelectorAll('.clearfix'));
  const views = videoDivs.map(div => div.querySelector('.glyphicon-eye-open'))
    .map(div => div ? parsePrefixed(div.parentElement.textContent) : 0);
  const likes = videoDivs.map(div => div.querySelector('.glyphicon-heart'))
    .map(div => div ? parsePrefixed(div.parentElement.textContent) : 0);
  const videoEntries = Object.entries(videoDivs);
  GM.setValue('sortValue', sortValueExpression);
  const evalSortValue = (views, likes) => {
    const ratio = Math.min(likes / Math.max(1, views), 1);
    return eval(sortValueExpression);
  }
  videoEntries.sort((entryA, entryB) => {
    return evalSortValue(views[entryB[0]], likes[entryB[0]]) - evalSortValue(views[entryA[0]], likes[entryA[0]]);
  });
  videoDivs.map(div => div.parentElement)
    .forEach((div, index) => {
    div.append(videoEntries[index][1]);
  });
};

const sortAllVideos = (sortValueExpression) => {
  let gridCount = 0;
  try {
    document.querySelectorAll('.views-responsive-grid').forEach((grid) => {
      sortVideos(grid, sortValueExpression);
      gridCount++;
    });
  } catch (message) {
    alert(message);
  }
  logDebug(`${gridCount}grids sorted.`);
};

const requestMorePages = (URL, pageCount) => {
  const params = URL.searchParams;
  const page = params.has('page') ? params.get('page') : 0;
  logDebug(page, pageCount);
}

(async () => {
  const currentURL = new URL(location);
  if (/\/(videos|images|subscriptions)/.test(currentURL.pathname)) {
    const additionalPageCount = 2;
    requestMorePages(currentURL, additionalPageCount);
  }
  const sortValueInput = document.createElement('input');
  sortValueInput.type = 'text';
  sortValueInput.value = await GM.getValue('sortValue', '100 * ratio + Math.sqrt(likes) / 25');
  const uiContainer = (() => {
    const temp = document.querySelector('.list-inline');
    if (temp) {
      return temp;
    } else {
      return document.querySelector('#user-links');
    }
  })();
  uiContainer.insertAdjacentElement('afterbegin', sortValueInput);
  const sortButton = document.createElement('button');
  sortButton.innerHTML = 'Sort';
  uiContainer.insertAdjacentElement('afterbegin', sortButton);
  sortButton.addEventListener('click', () => sortAllVideos(sortValueInput.value));
  sortAllVideos(sortValueInput.value);
})();