Sleazy Fork is available in English.

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();