ThisVid Download Button

Adds a download buttons to ThisVid video pages.

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

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 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.

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

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!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

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

// ==UserScript==
// @name           ThisVid Download Button
// @namespace      https://thisvid.com/
// @version        2.0.1
// @description    Adds a download buttons to ThisVid video pages.
// @author         persistentScripter
// @license        MIT
// @include        http*://thisvid.com/*
// @require        https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js
// @run-at         document-end
// ==/UserScript==

const wait = (ms) => {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

const snakeCase = (str) => {
  str = str.replace(/\W+/g, ' ').toLowerCase().split(' ').join('_');

  return str;
};

// Downloads a video via fetch() so we can save the file with the appropriate file name.
let getVid = async (fileURL) => {
  let controller = new AbortController();
  let {signal} = controller;

  const {title} = document;

  document.title = `[↓] ${title.replace(/\[↓\]/g, '')}`;

  let fileName = snakeCase(title.replace(/ThisVid\.com|at ThisVid tube/, '').trim());

  let resp = await fetch(fileURL, {
    method: 'GET',
    redirect: 'follow',
    signal,
  });

  // First request will just be a redirect. Catch it, abort, and continue.
  if (resp.redirected) {
    controller.abort();

    controller = new AbortController();
    signal = controller.signal;

    resp = await fetch(resp.url, {
      method: 'GET',
      signal,
    });
  }

  let blob = await resp.blob();

  try {
    url = window.URL.createObjectURL(blob);
  } catch (e) {
    url = resp.url;

    return url;
  }

  const a = document.createElement('a');

  document.title = `[✓] ${title.replace(/\[✓\]/g, '')}`;

  a.style.display = 'none';
  a.href = url;
  a.download = fileName;

  document.body.appendChild(a);

  a.click();

  window.URL.revokeObjectURL(url);

  return true;
};

const filterPrivateVideos = () => {
  let priv = Array.from(document.querySelectorAll('.icon-private'));

  for (let i = 0, len = priv.length; i < len; i++) {
    let item = priv[i];

    let container = item.parentNode.parentNode.parentNode;
    let vid = item.parentNode.parentNode;

    container.removeChild(vid);
  }
}

const addDownloadButton = async () => {
  let flagContainer = document.querySelector('#flagging_container');

  if (!flagContainer) return;

  let video = document.querySelector('video');
  let fileURL;

  // Video element is now inserted into DOM after video starts playing, this simulates a play click and then pauses the video.
  if (!video) {
    let playButton = document.querySelector('#kt_player > div.fp-player > div.fp-ui > div.fp-controls.fade > a.fp-play');

    playButton.click();

    await wait(500);

    video = document.querySelector('video');
  }

  video.pause();

  fileURL = video.src;

  let li = document.createElement('li');
  let a = document.createElement('a');
  let span = document.createElement('span');

  li.classList.add('share_button');
  li.appendChild(a);

  a.classList.add('__dl');
  a.href = fileURL;

  Object.assign(span.style, {
    color: '#fff',
    fontSize: '32px',
  });

  a.innerHTML += `
    <span class="tooltip">download</span>
  `;

  span.innerText = '↓';

  a.appendChild(span);

  a.addEventListener('click', async (e) => {
    e.preventDefault();
    e.stopPropagation();

    try {
      await getVid(fileURL);
    } catch (e) {
      let {title} = document;

      document.title = `[✗] ${title.replace(/\[✗\]/g, '')}`;

      window.open(fileURL);
    }
  });

  flagContainer.appendChild(li);
}

const sortVideos = () => {
  let els = Array.from(
    document.querySelectorAll('body > div.wrapper > div > div > section > div > a > span.thumb > span.percent')
  );
  let group = [];
  let globalContainer = null;

  if (!els.length) {
    els = Array.from(
      document.querySelectorAll('body > div.wrapper > div > div > section > div > div > a > span.thumb > span.percent')
    );
  }

  if (!els.length) return;

  for (let i = 0, len = els.length; i < len; i++) {
    let el = els[i];
    let int = parseInt(el.innerText.split('%')[0]);

    el = el.parentNode.parentNode;
    let container = el.parentNode;

    if (i === 0) {
      globalContainer = container;
    }

    try {
      container.removeChild(el);
    } catch (e) {
      console.log('fail', {el, container});
      continue;
    }

    group.push({
      el,
      int,
    });
  }

  group = _.orderBy(group, 'int', 'desc');

  for (let i = 0, len = group.length; i < len; i++) {
    let {el} = group[i];

    globalContainer.appendChild(el);
  }
}

window.addEventListener('load', () => {
  setTimeout(filterPrivateVideos, 100);
  setTimeout(addDownloadButton, 100);
  setTimeout(sortVideos, 200);
});