Stream recorder for adult cam sites that do not support HLS

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

  1. // ==UserScript==
  2. // @name Stream recorder for adult cam sites that do not support HLS
  3. // @namespace Everywhere
  4. // @version 1.0.2
  5. // @description Stream recorder for adult cam sites that do not support HLS/m3u8
  6. // @author Ladroop
  7. // @license MIT
  8. // @copyright 2024 Ladroop
  9. // @match https://www.livejasmin.com/*
  10. // @match https://www.xcams.com/*
  11. // @match https://www.secretfriends.com/*
  12. // @match https://cherry.tv/*
  13. // @match https://www.manyvids.com/*
  14. // @match https://www.soulcams.com/profile/*
  15. // @match https://cameraprive.com/*
  16. // @noframes
  17. // @run-at document-end
  18. // @grant none
  19. // ==/UserScript==
  20.  
  21. // based on the code of baptx
  22. // https://gist.github.com/baptx/8a1549d91996a37378faca159b3adc17?permalink_comment_id=4975895#file-webrtc_video_blob_record-js
  23.  
  24. (function() {
  25. 'use strict';
  26.  
  27. 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">'+
  28. '<g><g>'+
  29. '<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"/>'+
  30. '<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"/>'+
  31. '<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"/>'+
  32. '<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"/>'+
  33. '<circle fill="#FF0000" cx="22.594" cy="22.594" r="6.455"/>'+
  34. '</g></g></svg>';
  35.  
  36. 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">'+
  37. '<g><g>'+
  38. '<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"/>'+
  39. '<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"/>'+
  40. '<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"/>'+
  41. '<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"/>'+
  42. '</g></g></svg>';
  43.  
  44. var status="stop";
  45. var count=0;
  46. var exclude=false;
  47. var recName="";
  48. var location=document.location.href;
  49. var observer= new MutationObserver(pagechange);
  50. var observerConfig = {subtree: true, characterData: true, childList: true };
  51.  
  52. var site=location.split("/")[2].split(".");
  53. site=site[site.length-2];
  54.  
  55. var mediaRecorder;
  56. var recordedBlobs;
  57. var stream;
  58. var video;
  59. var vidObj=0;
  60. var mozAudioBug=false;
  61.  
  62. startup();
  63.  
  64. // 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.
  65. // mozAudioBug is set when original sound mutes(mozilla bug)
  66. function getrecname(){
  67. var name="video";
  68. exclude=false;
  69. if (site=="livejasmin"){if (location.indexOf("chat")==-1){exclude=true;}name=location.split("/")[location.split("/").length-1];vidObj=1;mozAudioBug=true;}
  70. if (site=="cherry"){name=location.split("/")[3];}
  71. if (site=="secretfriends"){name=location.split("/")[4].split("?")[0];}
  72. if (site=="xcams"){name=location.split("/")[5];}
  73. if (site=="manyvids"){if (location.split("/")[4]!="cam"){exclude=true;}name=location.split("/")[5];}
  74. if (site=="soulcams"){name=location.split("/")[4];mozAudioBug=true;}
  75. if (site=="cameraprive"){name=location.split("/")[5];}
  76. recName=site+"-"+name;
  77. }
  78.  
  79. function pagechange(){
  80. if (location != document.location.href){
  81. location = document.location.href;
  82. if (status=="rec"){stoprecord();}
  83. document.getElementById("popitup1").style.display="none";
  84. setTimeout(findvideo,8000);
  85. }
  86. }
  87.  
  88. function startup(){
  89. observer.observe(document.getElementsByTagName("head")[0],observerConfig);
  90. setTimeout(findvideo,8000);
  91. makepopitup();
  92. }
  93.  
  94. function findvideo(){
  95. getrecname();
  96. if (exclude){return;}
  97. if (document.getElementsByTagName("video").length > vidObj){
  98. video=document.getElementsByTagName("video")[vidObj];
  99. document.getElementById("popitup1").style.display="block";
  100. getStream();
  101. }
  102. }
  103.  
  104. function getStream(){
  105. stream = video.captureStream ? video.captureStream() : video.mozCaptureStream();
  106. if (!video.captureStream) {
  107. if (mozAudioBug){
  108. var ctx = new AudioContext();
  109. var dest = ctx.createMediaStreamSource(stream);
  110. dest.connect(ctx.destination);
  111. }
  112. }
  113. }
  114.  
  115. function startDownload() {
  116. var blob = new Blob(recordedBlobs, {type: 'video/webm codecs="vp8" opus'});
  117. var url = window.URL.createObjectURL(blob);
  118. var a = document.createElement('a');
  119. a.style.display = 'none';
  120. a.href = url;
  121. a.download = recName+'.webm';
  122. document.body.appendChild(a);
  123. a.click();
  124. setTimeout(function(){
  125. document.body.removeChild(a);
  126. window.URL.revokeObjectURL(url);
  127. recordedBlobs = [];
  128. }, 100);
  129. }
  130.  
  131. function handleDataAvailable(event) {
  132. msg("Recording "+count);
  133. count++;
  134. if (event.data && event.data.size > 0) {
  135. recordedBlobs.push(event.data);
  136. }
  137. }
  138.  
  139. function startRecording() {
  140. count=0;
  141. recordedBlobs = [];
  142. try {
  143. mediaRecorder = new MediaRecorder(stream);
  144. } catch (e) {
  145. console.log(e);
  146. msg("Not supported");
  147. status="stop";
  148. setTimeout(function(){msg("Stopped");},3000);
  149. return;
  150. }
  151. mediaRecorder.onstop = mediaRecorderStopped;
  152. mediaRecorder.ondataavailable = handleDataAvailable;
  153. try {
  154. mediaRecorder.start(1000);
  155. } catch (e) {
  156. console.log(e);
  157. msg("Error");
  158. status="stop";
  159. setTimeout(function(){msg("Stopped");},3000);
  160. return;
  161. }
  162. }
  163.  
  164. function mediaRecorderStopped(){
  165. msg("Stopped");
  166. status="stop";
  167. startDownload();
  168. }
  169.  
  170. function stopRecording() {
  171. msg("Stopped");
  172. mediaRecorder.stop();
  173. }
  174.  
  175. function msg(message){
  176. document.getElementById("message123").innerHTML=message;
  177. }
  178.  
  179. function stoprecord(){
  180. if (status=="stop"){return;}
  181. status="stop";
  182. stopRecording();
  183. }
  184.  
  185. function startrecord(){
  186. if (status!="stop"){return;}
  187. status="rec";
  188. startRecording();
  189. }
  190.  
  191. function makepopitup(){
  192. 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;";
  193. var newelem=document.createElement('span');
  194. newelem.setAttribute("style", popstyle);
  195. newelem.id="popitup1";
  196. var newdiv=document.createElement('div');
  197. newdiv.style.width="100%";
  198. newdiv.innerHTML=recbutton+"&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp"+stopbutton;
  199. newelem.appendChild(newdiv);
  200. newdiv=document.createElement('div');
  201. newdiv.style.width="100%";
  202. newdiv.style.height="25px";
  203. newdiv.style.padding="3px 3px 3px 3px";
  204. newdiv.style.color="black";
  205. newdiv.id="message123";
  206. newdiv.innerHTML="Stopped";
  207. newdiv.style.cursor="move";
  208. newdiv.addEventListener("mousedown",dragMouseDown);
  209. newelem.appendChild(newdiv);
  210. document.getElementsByTagName("body")[0].appendChild(newelem);
  211. document.getElementById("recbutton").addEventListener("click",startrecord);
  212. document.getElementById("stopbutton").addEventListener("click",stoprecord);
  213. }
  214.  
  215. var pos1=0;
  216. var pos2=0;
  217. var pos3=0;
  218. var pos4=0;
  219.  
  220. function dragMouseDown(e) {
  221. e = e || window.event;
  222. e.preventDefault();
  223. pos3 = e.clientX;
  224. pos4 = e.clientY;
  225. document.onmouseup = closeDragElement;
  226. document.onmousemove = elementDrag;
  227. }
  228.  
  229. function elementDrag(e) {
  230. e = e || window.event;
  231. e.preventDefault();
  232. pos1 = pos3 - e.clientX;
  233. pos2 = pos4 - e.clientY;
  234. pos3 = e.clientX;
  235. pos4 = e.clientY;
  236. var x =parseInt(document.getElementById("popitup1").style.left);
  237. var y =parseInt(document.getElementById("popitup1").style.top);
  238. if ((pos3>=110)&&(pos3<=window.innerWidth-150)){
  239. document.getElementById("popitup1").style.left = (x - pos1) + "px";
  240. }
  241. if ((y-pos2>-50)&&(pos4<=window.innerHeight-20)){
  242. document.getElementById("popitup1").style.top = (y - pos2) + "px";
  243. }
  244. }
  245.  
  246. function closeDragElement() {
  247. document.onmouseup = null;
  248. document.onmousemove = null;
  249. }
  250.  
  251. })();