Stream recorder for adult cam sites that do not support HLS

Stream recorder for adult cam sites that do not support HLS/m3u8

// ==UserScript==
// @name         Stream recorder for adult cam sites that do not support HLS
// @namespace    Everywhere
// @version      1.0.2
// @description  Stream recorder for adult cam sites that do not support HLS/m3u8
// @author       Ladroop
// @license	     MIT
// @copyright    2024 Ladroop
// @match        https://www.livejasmin.com/*
// @match        https://www.xcams.com/*
// @match        https://www.secretfriends.com/*
// @match        https://cherry.tv/*
// @match        https://www.manyvids.com/*
// @match        https://www.soulcams.com/profile/*
// @match        https://cameraprive.com/*
// @noframes
// @run-at       document-end
// @grant        none
// ==/UserScript==

// based on the code of baptx
// https://gist.github.com/baptx/8a1549d91996a37378faca159b3adc17?permalink_comment_id=4975895#file-webrtc_video_blob_record-js

(function() {
    'use strict';

    var recbutton='<svg fill="#000000" version="1.1" id="recbutton" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="50px" height="50px" viewBox="0 0 45.187 45.188" xml:space="preserve" style="cursor:pointer">'+
        '<g><g>'+
		'<path d="M0,18.961h4.842V6.052c0-0.446,0.362-0.807,0.807-0.807H18.56V0.404H5.649C2.535,0.404,0,2.939,0,6.053V18.961z"/>'+
		'<path d="M39.539,0.403h-12.91v4.841h12.91c0.445,0,0.807,0.362,0.807,0.807v12.91h4.842V6.052    C45.189,2.938,42.654,0.403,39.539,0.403z"/>'+
		'<path d="M4.842,39.136V26.225H0v12.911c0,3.113,2.535,5.648,5.649,5.648H18.56v-4.842H5.649    C5.204,39.942,4.842,39.58,4.842,39.136z"/>'+
		'<path d="M40.346,39.136c0,0.446-0.362,0.807-0.807,0.807H26.628v4.842h12.91c3.115,0,5.648-2.536,5.648-5.65V26.225h-4.842    L40.346,39.136L40.346,39.136z"/>'+
		'<circle fill="#FF0000" cx="22.594" cy="22.594" r="6.455"/>'+
        '</g></g></svg>';

    var stopbutton='<svg fill="#000000" version="1.1" id="stopbutton" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="50px" height="50px" viewBox="0 0 152.013 152.013" xml:space="preserve" style="cursor:pointer">'+
        '<g><g>'+
		'<path d="M0,20.358v43.429h16.288V20.358c0-1.5,1.22-2.715,2.716-2.715h43.432V1.355H19C8.523,1.358,0,9.881,0,20.358z"/>'+
		'<path d="M133.013,1.358H89.578v16.288h43.435c1.495,0,2.71,1.22,2.71,2.715v43.429h16.29V20.358    C152.013,9.881,143.491,1.358,133.013,1.358z"/>'+
		'<path d="M16.288,131.657V88.224H0v43.434c0,10.472,8.526,19,19.003,19h43.432v-16.286H19    C17.504,134.371,16.288,133.156,16.288,131.657z"/>'+
		'<path d="M135.723,131.657c0,1.499-1.218,2.711-2.71,2.711H89.578v16.289h43.435c10.479,0,19-8.531,19-19.007V88.224h-16.29    V131.657z"/>'+
        '</g></g></svg>';

    var status="stop";
    var count=0;
    var exclude=false;
    var recName="";
    var location=document.location.href;
    var observer= new MutationObserver(pagechange);
    var observerConfig = {subtree: true, characterData: true, childList: true };

    var site=location.split("/")[2].split(".");
    site=site[site.length-2];

    var mediaRecorder;
    var recordedBlobs;
    var stream;
    var video;
    var vidObj=0;
    var mozAudioBug=false;

    startup();

// if site is not here it will set name to "sitename-video" , use video element 0 and run on all pages where this element is found.
// mozAudioBug is set when original sound mutes(mozilla bug)
    function getrecname(){
        var name="video";
        exclude=false;
        if (site=="livejasmin"){if (location.indexOf("chat")==-1){exclude=true;}name=location.split("/")[location.split("/").length-1];vidObj=1;mozAudioBug=true;}
        if (site=="cherry"){name=location.split("/")[3];}
        if (site=="secretfriends"){name=location.split("/")[4].split("?")[0];}
        if (site=="xcams"){name=location.split("/")[5];}
        if (site=="manyvids"){if (location.split("/")[4]!="cam"){exclude=true;}name=location.split("/")[5];}
        if (site=="soulcams"){name=location.split("/")[4];mozAudioBug=true;}
        if (site=="cameraprive"){name=location.split("/")[5];}
        recName=site+"-"+name;
    }

    function pagechange(){
        if (location != document.location.href){
            location = document.location.href;
            if (status=="rec"){stoprecord();}
            document.getElementById("popitup1").style.display="none";
            setTimeout(findvideo,8000);
        }
    }

    function startup(){
        observer.observe(document.getElementsByTagName("head")[0],observerConfig);
        setTimeout(findvideo,8000);
        makepopitup();
    }

    function findvideo(){
        getrecname();
        if (exclude){return;}
        if (document.getElementsByTagName("video").length > vidObj){
            video=document.getElementsByTagName("video")[vidObj];
            document.getElementById("popitup1").style.display="block";
            getStream();
        }
    }

    function getStream(){
        stream = video.captureStream ? video.captureStream() : video.mozCaptureStream();
        if (!video.captureStream) {
            if (mozAudioBug){
                var ctx = new AudioContext();
                var dest = ctx.createMediaStreamSource(stream);
                dest.connect(ctx.destination);
            }
        }
    }

    function startDownload() {
        var blob = new Blob(recordedBlobs, {type: 'video/webm codecs="vp8" opus'});
        var url = window.URL.createObjectURL(blob);
        var a = document.createElement('a');
        a.style.display = 'none';
        a.href = url;
        a.download = recName+'.webm';
        document.body.appendChild(a);
        a.click();
        setTimeout(function(){
            document.body.removeChild(a);
            window.URL.revokeObjectURL(url);
            recordedBlobs = [];
        }, 100);
    }

    function handleDataAvailable(event) {
        msg("Recording "+count);
        count++;
        if (event.data && event.data.size > 0) {
            recordedBlobs.push(event.data);
        }
    }

    function startRecording() {
        count=0;
        recordedBlobs = [];
        try {
            mediaRecorder = new MediaRecorder(stream);
        } catch (e) {
            console.log(e);
            msg("Not supported");
            status="stop";
            setTimeout(function(){msg("Stopped");},3000);
            return;
        }
        mediaRecorder.onstop = mediaRecorderStopped;
        mediaRecorder.ondataavailable = handleDataAvailable;
        try {
            mediaRecorder.start(1000);
        } catch (e) {
            console.log(e);
            msg("Error");
            status="stop";
            setTimeout(function(){msg("Stopped");},3000);
            return;
        }
    }

    function mediaRecorderStopped(){
        msg("Stopped");
        status="stop";
        startDownload();
    }

    function stopRecording() {
        msg("Stopped");
        mediaRecorder.stop();
    }

    function msg(message){
        document.getElementById("message123").innerHTML=message;
    }

    function stoprecord(){
        if (status=="stop"){return;}
        status="stop";
        stopRecording();
    }

    function startrecord(){
        if (status!="stop"){return;}
        status="rec";
        startRecording();
    }

    function makepopitup(){
        var popstyle="color:black;z-index:100000;top:100px;left:10px;box-shadow:0px 0px 32px rgba(0, 0, 0, 0.32);border-radius:4px;border:1px solid rgb(221, 221, 221);background-color:rgb(200, 200, 200);position:fixed; display:none; height: auto; width:auto; padding: 5px 5px 5px 5px;";
        var newelem=document.createElement('span');
        newelem.setAttribute("style", popstyle);
        newelem.id="popitup1";
        var newdiv=document.createElement('div');
        newdiv.style.width="100%";
        newdiv.innerHTML=recbutton+"&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp"+stopbutton;
        newelem.appendChild(newdiv);
        newdiv=document.createElement('div');
        newdiv.style.width="100%";
        newdiv.style.height="25px";
        newdiv.style.padding="3px 3px 3px 3px";
        newdiv.style.color="black";
        newdiv.id="message123";
        newdiv.innerHTML="Stopped";
        newdiv.style.cursor="move";
        newdiv.addEventListener("mousedown",dragMouseDown);
        newelem.appendChild(newdiv);
        document.getElementsByTagName("body")[0].appendChild(newelem);
        document.getElementById("recbutton").addEventListener("click",startrecord);
        document.getElementById("stopbutton").addEventListener("click",stoprecord);
    }

    var pos1=0;
    var pos2=0;
    var pos3=0;
    var pos4=0;

    function dragMouseDown(e) {
        e = e || window.event;
        e.preventDefault();
        pos3 = e.clientX;
        pos4 = e.clientY;
        document.onmouseup = closeDragElement;
        document.onmousemove = elementDrag;
    }

    function elementDrag(e) {
        e = e || window.event;
        e.preventDefault();
        pos1 = pos3 - e.clientX;
        pos2 = pos4 - e.clientY;
        pos3 = e.clientX;
        pos4 = e.clientY;
        var x =parseInt(document.getElementById("popitup1").style.left);
        var y =parseInt(document.getElementById("popitup1").style.top);
        if ((pos3>=110)&&(pos3<=window.innerWidth-150)){
            document.getElementById("popitup1").style.left = (x - pos1) + "px";
        }
        if ((y-pos2>-50)&&(pos4<=window.innerHeight-20)){
            document.getElementById("popitup1").style.top = (y - pos2) + "px";
        }
    }

    function closeDragElement() {
        document.onmouseup = null;
        document.onmousemove = null;
    }

})();