Erome Video Cloner

clone videos in an erome album, play multiple & side-by-side!

作者のサイトでサポートを受ける。または、このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name         Erome Video Cloner
// @namespace    http://tampermonkey.net/
// @version      0.9.900
// @description  clone videos in an erome album, play multiple & side-by-side!
// @author       throwinglove23
// @license      MIT
// @match        https://www.erome.com/a/*
// @match        http://www.erome.com/a/*
// @match        https://www.erome.com/*
// @exclude      http://www.erome.com/a/*/edit
// @exclude      https://www.erome.com/a/*/edit
// @icon         https://www.erome.com/favicon-32x32.png
// @grant        none
// @supportURL   https://github.com/wflover23/erome-multi-tool
// ==/UserScript==
/* jshint esversion: 8 */

function mainFunction() {
    'use strict';
    // removing default seek functions
    document.onkeydown = null;
    window.addEventListener('keydown', function(e) {
        // avoiding space which scrolls down to allow pausing
      if(e.keyCode == 32 && e.target.tagName != 'INPUT') {
         
        e.preventDefault();
      }
});
    const userRow = document.querySelector('.username');
    const cleanseBtn = document.createElement('button');
    // if (userRow == null || document.querySelector('video') == null)
    // {
    //     return;
    // }
    cleanseBtn.classList.add('btn', 'btn-grey', 'btn-sm', 'url-btn');
    cleanseBtn.textContent = 'SHOW URLS';
    userRow.appendChild(cleanseBtn);
    cleanseBtn.addEventListener('click', urlShow);
    if (document.querySelectorAll('.video').length>0)
    {
        const infoRow = document.querySelector('.user-info.text-right');
        const spanItem = document.createElement('span');
        //spanItem.classList.add('btn', 'mr-5', 'btn-grey', 'cloner');
        const cloneIcon = document.createElement('i');
        if (document.querySelectorAll('#legend a').length > 0)
            {
                const legendBtn = document.createElement('a');
                legendBtn.classList.add('btn', 'btn-grey', 'btn-sm', 'mr-5');
                legendBtn.href='#legend';
                legendBtn.textContent = 'GO TO LEGEND';
                spanItem.append(legendBtn);
            }
        infoRow.insertBefore(spanItem, infoRow.firstElementChild);
   
        //spanItem.addEventListener('click', cloneFunction);
    }
    // adding transitions to legend links if exists
    replaceAdWithButtons();

    // Your code here...
}

function seekChaptersN()
{
    const video = document.querySelector('video');
    video.addEventListener('keydown', seekChapters);
    video.setAttribute('data-chapter', 0);
   

    function seekChapters(event)
        {
            if (event.key == 'n')
            {
                const chapterArea = document.querySelector('.vjs-control.vjs-chapters-button .vjs-menu-content');
                if (chapterArea.childElementCount > 1)
                    {
                        const childrenChapterCount = chapterArea.childElementCount;
                        const chapters = Array.from(chapterArea.querySelectorAll('.vjs-menu-item'));
                        const currentChapter = video.dataset.chapter;
                        // skipping title child from chapter
                        if (currentChapter < childrenChapterCount -1)
                       
                        {
                            chapters[+currentChapter].click();
                            video.dataset.chapter++;
                        }
                        else
                        {
                            video.currentTime = 0;
                            video.dataset.chapter=0;
                        }
                    }
            }
        }
}


function copySourceFromAlbum()
{
    const albums = Array.from(document.getElementsByClassName('album'));
    albums.forEach(async function(album)
        {
/*             const hr = album.querySelector('.album-link').href;
            const hdr = await fetch(hr);
            const data = await hdr.text();
            var parser = new DOMParser();
            var doc = parser.parseFromString(data, 'text/html');
            album.querySelector('.album-thumbnail').style.objectFit = 'cover';
            album.style.maxHeight = '200px';
            if (doc.querySelector('source') == null)
            {
                return;
            }
            const vidSrc = doc.querySelector('source').src;
            async function copyFxn()
            {
                await navigator.clipboard.writeText(vidSrc);
            }
            album.insertAdjacentHTML("afterbegin", '<button class="copy-link-btn btn btn-sm btn-pink" style="margin-left: 2px;width: fit-content;position: relative;z-index: 30;bottom: 61px;font-size: 1.5rem;padding: 0;">COPY</button>');
            album.querySelector('.copy-link-btn').onclick = copyFxn;
            const btnCopy = album.querySelector('.copy-link-btn');
            copyOnHover(btnCopy); */
        });
}

function copyOnHover(btn)
{
        btn.style.opacity=0;
      btn.closest('.album').addEventListener('mouseover', function()
        {
            btn.style.opacity=1;
        });
        btn.closest('.album').addEventListener('mouseout', function()
        {
            btn.style.opacity=0;
        });
}

function checkForChapters()
{
    if (document.querySelector('video') == null)
    {
        return;
    }
    const legend = document.querySelector('#legend');
    // if no text nodes/chapters found in legend, return ;
    if (!legend || legend.children.length < 2)
    {
        return;
    }
    const nodeTypes = Array.from(legend.childNodes).filter(function(item)
                                                       {
                                                           return item.nodeType === 3 && item.textContent.trim() != '';
                                                       })
                                                       .map(item => item.textContent.trim());
    const startingIndex = nodeTypes.indexOf('CHAPTERS BELOW');
    if (startingIndex == -1)
    {
        return;
    }
    else
    {
        document.querySelector('#bubble').style.width = 'min-content';
        document.querySelector('#bubble').insertAdjacentHTML("beforeend", '<button class="btn btn-sm btn-pink chapter-btn mb-5">ADD CHAPTERS</button>');
        const chapterBtn = document.querySelector('.chapter-btn');
        chapterBtn.addEventListener('click', function()
        {
            parseChapters(nodeTypes.slice(startingIndex+1));
            chapterBtn.textContent == 'REMOVE CHAPTERS' ? chapterBtn.textContent = 'ADD CHAPTERS' : chapterBtn.textContent = 'REMOVE CHAPTERS';
        });
    }
}

function parseChapters(chapterInfo)
{
    document.querySelector('.vjs-chapters-button.vjs-control').classList.toggle('vjs-hidden');
    let listTimes = [];
    let listNames = [];
    // https://stackoverflow.com/questions/9640266/convert-hhmmss-string-to-seconds-only-in-javascript
    function timeToSeconds(str)
    {
        var timeElements = str.split(':'),
            seconds = 0, minutes = 1;
   
        while (timeElements.length > 0)
        {
            seconds += minutes * parseInt(timeElements.pop(), 10);
            minutes *= 60;
        }
   
        return seconds;
    }
    for (let i = 0; i<chapterInfo.length;i++)
        {
            listTimes.push(timeToSeconds(chapterInfo[i].split('-')[0]));
            listNames.push(chapterInfo[i].split('-')[1]);
        }
    const chapterBar = document.querySelector('.vjs-chapters-button .vjs-menu-content');
    if (chapterBar.children.length > 1)
    {
        chapterBar.innerHTML = '<li class="vjs-menu-title" tabindex="-1">Chapters</li>';
    }
    for (let i = 0; i< listTimes.length;i++)
        {
            chapterBar.insertAdjacentHTML("beforeend", `<li class="vjs-menu-item" onclick='function moveTo(){document.querySelector("video").currentTime=${listTimes[i]}} moveTo();' tabindex="-1">${listNames[i]}</li>`);
        }
    seekChaptersN();
   
}

function urlShow()
{
    if (document.querySelector('video') == null)
    {
        return;
    }
    const urlBtn = document.querySelector('.url-btn');
    urlBtn.textContent == 'SHOW URLS' ? urlBtn.textContent = 'HIDE URLS': urlBtn.textContent = 'SHOW URLS';
    const vids = document.querySelectorAll('.video');
    if (document.querySelector('.form-url') == null)
    {
        const area = document.querySelector('.clearfix').previousElementSibling.parentElement;
        area.insertAdjacentHTML("afterbegin", "<form class='form-url hidden'><input type='url' required id='video-input' style='width:430px' placeholder='enter video url(s) separated by comma ,'><button class='btn btn-sm btn-pink btn-add-url'>ADD</button></form>");
        const form = document.querySelector('.form-url').addEventListener('submit', function(e)
               {
                    e.preventDefault();
                });
        const btn = document.querySelector('.btn-add-url');
        btn.addEventListener('click', addVideo);
    }
    vids.forEach(vid =>
    {
        const src = vid.querySelector('video').firstElementChild.src;
        if (vid.firstChild.tagName != 'A')
        {
            const anc = document.createElement('a');
            anc.classList.add('video-url', 'hidden');
            anc.textContent = 'COPY LINK';
            vid.insertAdjacentElement("afterbegin", anc);
            anc.onclick = async function()
            {
                await navigator.clipboard.writeText(src);
            };
        }
    });

    function addVideo()
        {
            const input = document.getElementById('video-input');

            if (input.value == '' || input.value.length < 50 || input.value == null)
            {
                console.log("no valid input");
                return;
            }
            else
            {
                let vidURLs = input.value.split(',');
                vidURLs.forEach(url => createVid(url));
            }
        }

    function createVid(sourceURL)
        {
            let posterURL1 = sourceURL.replace('v', 's');
            let posterURL2 = posterURL1.replace('_720p', '');
            let posterURL = posterURL2.replace('mp4', 'jpg');
            const mediaDiv = document.createElement('div');
            const videoDiv = document.createElement('div');
            mediaDiv.classList.add('media-group');
            videoDiv.classList.add('video');
   
            const mainMedia = document.querySelector('.media-group');
            mainMedia.parentElement.appendChild(mediaDiv);
            mediaDiv.appendChild(videoDiv);
            const video = document.createElement('video');
            video.classList.add('video-js', 'vjs-16-9');
            video.controls = true;
            video.poster = posterURL;
       
            const src = document.createElement('source');
            src.setAttribute('src', sourceURL);
            video.appendChild(src);
            videoDiv.appendChild(video);
            var player = videojs(video);
            video.preload="none";
            allowPToPause(video);


            document.querySelector('.sidebyside-btn').click();
            document.querySelector('.sidebyside-btn').click();
        }


    if (urlBtn.textContent == 'SHOW URLS')
    {
        const links = document.querySelectorAll('.video-url');
        links.forEach(anc =>
        {
            anc.classList.add('hidden');
        });
    }
    else
    {
        const links = document.querySelectorAll('.video-url');
        links.forEach(anc =>
        {
            anc.classList.remove('hidden');
        });
    }
    document.querySelector('.form-url').classList.toggle('hidden');
                                               
}


function addTransitionToID()
{
    const legendLinks = document.querySelectorAll('.comments a');
    legendLinks.forEach(link =>
    {
            const idText = link.textContent.trim();
            const parsedInt = parseInt(idText.substring(1), 10);
            const textInLink = link.nextSibling;
            const idEl = document.getElementById(parsedInt);
            if (Number.isNaN(parsedInt))
            {
                return;
            }
            if (textInLink.nodeType == 3)
            {
                if (idEl) {
                 idEl.title=textInLink?.textContent?.trim();
                }
            }
    });
    legendLinks.forEach(link => link.onclick= function()
        {
            const idText = link.textContent.trim();
            const parsedInt = parseInt(idText.substring(1), 10);
            const idEl = document.getElementById(parsedInt);
            if (!Number.isNaN(parsedInt))
            {
                link.href=`#${parsedInt}`;
            }
            idEl.style.transition = 'all 0.3s ease-in-out';
            idEl.style.opacity = '0.4';
            function backTo(elem)
                {
                    elem.style.opacity='1';
                }
            setTimeout(backTo, 400, idEl);
        });
}

function addProperID()
{
    let count = 0;
    document.querySelectorAll('.media-group img.img-front, .media-group video').forEach
    (item =>
        {
            count++;
            item.closest('.media-group').removeAttribute('id');
            item.closest('.media-group').id=count;
        }
    );
}

function playerSeekables()
{
    var btn = document.createElement('button');
    var backwardIcon = document.createElement('i');
    backwardIcon.classList.add('fas', 'fa-forward', 'fa-flip-horizontal');
    btn.append(backwardIcon);
    btn.title = "Back 10 seconds";
    btn.addEventListener('click', seekNeg);
        function seekNeg()
        {
            this.closest('.video').querySelector('video').currentTime -= 10;
        }
    function seekPos()
        {
            this.closest('.video').querySelector('video').currentTime += 10;
        }
    var btn2 = document.createElement('button');
    var forwardIcon = document.createElement('i');
    forwardIcon.classList.add('fas', 'fa-forward');
    btn2.append(forwardIcon);
    btn2.title = 'Forward 10 seconds';
    btn2.onclick = seekPos;
    var flipIcon = document.createElement('i');
    flipIcon.classList.add('fas','fa-sort', 'fa-rotate-90', 'fa-2x', 'ml-10');
    const flipBtn = document.createElement('button');
    flipBtn.append(flipIcon);
    flipBtn.classList.add('mirror-btn');
    flipBtn.onclick = flip;

    return {btn, btn2, flipBtn};
   
}

function flip()
        {
            let flipper = this.closest('.mirror-btn');
            if (this.closest('.mirror-btn').classList.contains('flipped'))
                {
                    this.closest('.video').querySelector('video').style.transform = '';
                    flipper.classList.toggle('flipped');
                    return;
                }
            this.closest('.video').querySelector('video').style.transform = 'rotateY(170deg)';
            flipper.classList.add('flipped');
        }

function replaceAdWithButtons()
{
    const ad = document.getElementById('bubble');
    ad.removeAttribute('href');
    ad.textContent = '';
    ad.style.display='block';
    const sbsBtn = document.createElement('button');
    sbsBtn.textContent = 'SBS: OFF';
    const clonerBtn = document.createElement('button');
    clonerBtn.textContent = 'CLONER: OFF';
    sbsBtn.classList.add('btn', 'btn-sm', 'btn-pink', 'sidebyside-btn', 'mr-5', 'mb-5');
    clonerBtn.classList.add('btn', 'btn-sm', 'btn-pink', 'cloner', 'mb-5');
    sbsBtn.addEventListener('click', sideBySide);
    clonerBtn.addEventListener('click', cloneFunction);
   
    ad.append(sbsBtn, clonerBtn);
}

function allowPToPause(videoToAllow)
{
    videoToAllow.parentElement.onkeydown = function(event){
        if(event.key == 'p' || event.key == ' ')
        {
            videoToAllow.parentElement.querySelector('.vjs-play-control').click();
        }
        else if (event.key == 'ArrowRight')
        {
            videoToAllow.currentTime+=10;
        }
        else if (event.key == 'ArrowLeft')
        {
            videoToAllow.currentTime-=10;
        }
        else if (event.key == 'r')
        {
            const rng = Math.random() * (videoToAllow.duration - 1);
            videoToAllow.currentTime = rng;
        }
        else if (event.key == 'x')
        {
            if (videoToAllow.parentElement.querySelector('.mirror-btn')!= null)
            {
               videoToAllow.parentElement.querySelector('.mirror-btn').click();
               return;
            }
            if (videoToAllow.classList.contains('flipped'))
            {
                videoToAllow.style.transform = '';
                videoToAllow.classList.remove('flipped');
            }
            else
            {
                videoToAllow.style.transform = 'rotateY(173deg)';
                videoToAllow.classList.add('flipped');
            }
        }
        else if (event.key == 'f')
        {
            videoToAllow.parentElement.querySelector('.vjs-fullscreen-control').click();
        }
        else if (!isNaN(event.key) && event.key != ' ')
        {
            videoToAllow.currentTime = (videoToAllow.duration/10) * (event.key);
        }
        else if (event.key == 'z')
        {
            if (!videoToAllow.classList.contains('zoomed'))
            {
                videoToAllow.style.scale = 1.6;
            }
            else
            {
                videoToAllow.style.scale= 1;
            }
            videoToAllow.classList.toggle('zoomed');
        }
        else if (event.key == 't')
        {
            if (!document.querySelector('.sea-css'))
            {
                let css = document.createElement('link');
                css.href='https://unpkg.com/@videojs/themes@1/dist/sea/index.css';
                css.classList.add('sea-css');
                css.rel='stylesheet';
                document.head.appendChild(css);

                css = document.createElement('link');
                css.href='https://unpkg.com/@videojs/themes@1/dist/forest/index.css';
                css.classList.add('sea-css');
                css.rel='stylesheet';
                document.head.appendChild(css);

                css = document.createElement('link');
                css.href='https://unpkg.com/@videojs/themes@1/dist/fantasy/index.css';
                css.classList.add('sea-css');
                css.rel='stylesheet';
                document.head.appendChild(css);

                css = document.createElement('link');
                css.href='https://unpkg.com/@videojs/themes@1/dist/city/index.css';
                css.classList.add('sea-css');
                css.rel='stylesheet';
                document.head.appendChild(css);
            }
            const themes = ["vjs-theme-city","vjs-theme-fantasy", "vjs-theme-sea", "vjs-theme-forest"]
            const random = Math.floor(Math.random() * themes.length);
            videoToAllow.parentElement.classList.remove("vjs-theme-city")
            videoToAllow.parentElement.classList.remove("vjs-theme-fantasy")
            videoToAllow.parentElement.classList.remove("vjs-theme-sea")
            videoToAllow.parentElement.classList.remove("vjs-theme-forest")
            videoToAllow.parentElement.classList.toggle(themes[random]);
        }
    };
}

function playerVersion(data)
{
        const mediaDiv = document.createElement('div');
        const videoDiv = document.createElement('div');
        mediaDiv.classList.add('media-group');
        videoDiv.classList.add('video');

        const mainMedia = document.querySelector('.media-group');
        mainMedia.parentElement.appendChild(mediaDiv);
        mediaDiv.appendChild(videoDiv);
        const video = document.createElement('video');
        video.classList.add('video-js', 'vjs-16-9');
        video.controls = true;
        video.poster=data.bi;
   
        const src = document.createElement('source');
        src.setAttribute('src', data.urL);
        video.appendChild(src);
        videoDiv.appendChild(video);
        var player = videojs(video);
        video.preload="none";
       
        let btn = playerSeekables().btn;
        let btn2 = playerSeekables().btn2;
        let flipper = playerSeekables().flipBtn;
        mediaDiv.querySelector('.vjs-control-bar').insertAdjacentElement("afterbegin", btn);
        mediaDiv.querySelector('.vjs-play-control').insertAdjacentElement("afterend", btn2);
        mediaDiv.querySelector('.vjs-fullscreen-control').insertAdjacentElement("beforebegin", flipper);
       
        allowPToPause(video);
}

function getVidData(vidDiv)
{
    const urL = vidDiv.querySelector('.video video source').src;
    var img = vidDiv.querySelector('.vjs-poster');
    var bi = img.style.backgroundImage.slice(4, -1).replace(/"/g, "");
   
    return {urL, bi};
}
// remove original videos and get their poster+vid url
function removeWithData(vidDiv)
{
    let data = getVidData(vidDiv);
    vidDiv.remove();
    return data;
}

function videoCleanseReplace()
{
    const vidDivs = document.getElementsByClassName('video');
    console.log("started cleansing");
    Array.from(vidDivs).forEach(vidDiv =>
    {
        let data = removeWithData(vidDiv);
        playerVersion(data);
    });
    //showing message on load
    const messageDiv = document.querySelector('#user_message');
    messageDiv.textContent = 'videos replaced';
    setTimeout(() => {
        messageDiv.style.display='none';
    }, 5000);
    // deleting empty first video section
     Array.from(document.querySelectorAll('.video-lg')).forEach(lgVid => lgVid.parentElement.remove());
   
   
}

function sideBySide()
{
    if (document.querySelectorAll('.media-group.col-sm-6').length > 0)
    {
        const sideVideos = Array.from(document.querySelectorAll('.media-group.col-sm-6'));
        sideVideos.forEach(group =>
        {
            group.classList.remove('col-sm-6');
        });
        document.querySelector('a .sidebyside-btn').textContent = 'SBS: OFF';
    }
    else
    {
        const sideVideos = Array.from(document.querySelectorAll('.media-group.col-sm-6'));
        sideVideos.forEach(group =>
        {
            group.classList.remove('col-sm-6');
        });
        const groups = Array.from(document.querySelectorAll('.media-group'));
        groups.forEach(group =>
        {
           group.classList.add('col-sm-6');
        });
        document.querySelector('a .sidebyside-btn').textContent = 'SBS: ON';
       
    }
}

function showMessage()
{
    if (document.querySelector('.video') == null)
    {
        return;
    }
    const messageDiv = document.querySelector('#user_message');
    messageDiv.textContent = 'videos replaced + multi-play ON';
    messageDiv.style.display='block';
    messageDiv.style.width="400px";
    setTimeout(() => {
        messageDiv.style.display='none';
        messageDiv.style.width="200px";
    }, 1500);
}

// adding clone button to each video
function cloneFunction()
{
    if (document.querySelector('.video video') == null)
    {
        console.log("no videos");
        return;
    }
    // pressing button again up top should clear the clone buttons if second time pressing it
    if (document.getElementsByClassName('clone-btn').length > 0)
    {
        const allButtons = document.querySelectorAll('.clone-btn');
        allButtons.forEach(button => button.remove());
        document.querySelector('a .cloner').textContent = 'CLONER: OFF';
        return;
    }
    const videoDivs = document.querySelectorAll('.video');
    videoDivs.forEach(videoDiv =>
    {
        const cloneBtn = document.createElement('button');
        cloneBtn.style.position = 'absolute';
        cloneBtn.style.top = '0px';
        cloneBtn.style.zIndex = 1;
        cloneBtn.style.left="94.75%";
        if (videoDiv.parentElement.classList.contains('col-sm-6'))
        {
            cloneBtn.style.left="89%";
        }
        cloneBtn.style.transition = 'all 0.6s ease-in-out';
        cloneBtn.style.opacity=0;
        cloneBtn.classList.add('btn','btn-sm','btn-default','clone-btn');
        cloneBtn.textContent = 'CLONE';
       
        videoDiv.addEventListener('mouseover', function()
                        {
                            cloneBtn.style.opacity=1;
                        });
        videoDiv.addEventListener('mouseout', function()
                        {
                                cloneBtn.style.opacity = 0;
                        });
       
        videoDiv.insertBefore(cloneBtn, videoDiv.firstElementChild);
        cloneBtn.onclick = cloneThis;
    });
    document.querySelector('a .cloner').textContent = 'CLONER: ON';
}

function cloneThis()
{
   
    if (document.querySelector('.video video') == null)
    {
        console.log("no videos");
        return;
    }
    else
    {
        const videoDivData = this.parentElement;
        let data = getVidData(videoDivData);
        const videoOriginal = videoDivData.querySelector('video');
       
       
        const mediaDiv = document.createElement('div');
        const videoDiv = document.createElement('div');
        mediaDiv.classList.add('media-group');
        if (videoOriginal.closest('.media-group').classList.contains('col-sm-6'))
        {
            mediaDiv.classList.add('col-sm-6');
        }
        videoDiv.classList.add('video');

        const currentMedia = this.parentElement.parentElement;
        currentMedia.insertAdjacentElement("afterend", mediaDiv);

        mediaDiv.appendChild(videoDiv);
        const video = document.createElement('video');
        video.classList.add('video-js', 'vjs-16-9');
        video.controls = true;
        video.poster=data.bi;
   
        const src = document.createElement('source');
        src.setAttribute('src', data.urL);
        video.appendChild(src);
        videoDiv.appendChild(video);
        var player = videojs(video);
        // var player = videojs(duplicateVid);
        allowPToPause(video);
        console.log("found videos");
    }
}

function showHideMenuE()
{
    document.addEventListener('keypress', function(event)
    {
       if (event.key == 'e')
       {
           document.querySelector('#bubble').classList.toggle('hidden');
       }
       else if (event.key == 's' && event.target != document.querySelector('#q') && event.target != document.querySelector('#content'))
       {
           sideBySide();
       }
       else if (event.key == 'v' && event.target != document.querySelector('#q') && event.target != document.querySelector('#content'))
       {
           if (!!document.querySelector('div.vjs-playing'))
           {
               document.querySelector('div.vjs-playing').scrollIntoView();
           }
       }
    });
   
}

function cleanOnLoad()
{
    mainFunction();
    videoCleanseReplace();
    showMessage();
    addProperID();
    addTransitionToID();
    copySourceFromAlbum();
    checkForChapters();
    showHideMenuE();
}
cleanOnLoad();