NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name ZXDL // @namespace http://zoox18.com/ // @version 1.5.3 // @description View and download private videos from ZX18. refactored and bug fixed. // @author Narkuh, refactored by PSR // @license MIT // @match http*://*.zoox18.com/* // @connect pastebin.com // @grant GM_download // @grant GM_xmlhttpRequest // @require http://code.jquery.com/jquery-3.4.1.min.js // @require https://cdn.plyr.io/3.5.6/plyr.js // ==/UserScript== /***********************************************************************/ /* This program is free software. It comes without any warranty, to / /* the extent permitted by applicable law. You can redistribute it / /* and/or modify it under the terms of the Do What The Fuck You Want / /* To Public License, Version 2. / /* See http://www.wtfpl.net/ for more details. / /***********************************************************************/ // Avoid Global Scope and var with IIFE (function ($) { "use strict"; // key0, key1, key2 are derived from http request header of video. you can research with developer tool to watch network request. // These are used for sending GET request of video. Direct URL access is banned but through xmlhttp request works. const zxdl = { VIDEO_ID: window.location.pathname.split("/")[2], key0: "", key1: "", key2: "", paths: [], isDownloading: false, isVideoFound: false, isPrivateWindow: false, videoUrl: "", videoUploader: "Unknown", videoTitle: "Unknown", videoDescription: "None", linkCheckThisSession: false, }; function removeAnnoyance() { document.querySelectorAll(".img-private").forEach((element) => (element.style.filter = "brightness(1)")); document.querySelectorAll(".label-private").forEach((element) => (element.style.filter = "opacity(0.5)")); } function makeNavigationBar() { $(".navbar").after('<div class="container" id="rip-div" style="width: 560px;"></div>'); $(".top-menu > .pull-left").append( '<li id="zxdl-header"><span id="zx-ver">ZXDL 1.5.3</span><span><a data-toggle="modal" href="#zxdl-modal"><span class="caret"></span></span></a></li>' ); $("body").append( '<div class="modal fade in" id="zxdl-modal"><div class="modal-dialog zxdl-modal"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">テ�</button> <h4 class="modal-title">ZXDL</h4> </div> <div class="modal-body">Version 1.5.3 | by Lowfo and Narkuh<br><span class="link-status"></span></div></div></div></div>' ); } // Functions function formatBytes(bytes, decimalPlaces = 2) { if (bytes === 0) return "0 bytes"; const dp = 0 > decimalPlaces ? 0 : decimalPlaces, sizeIndex = Math.floor(Math.log(bytes) / Math.log(1024)); return parseFloat((bytes / Math.pow(1024, sizeIndex)).toFixed(dp)) + " " + ["bytes", "KB", "MB", "GB"][sizeIndex]; } function calculateAndShowDownloadProgress(xmlHttpRequestProgressEvent) { if (xmlHttpRequestProgressEvent.lengthComputable === false) return; $("#dl-data").html( formatBytes(xmlHttpRequestProgressEvent.loaded) + " / " + formatBytes(xmlHttpRequestProgressEvent.total) ); $("#dl-bar").attr( "aria-valuenow", Math.floor((xmlHttpRequestProgressEvent.loaded / xmlHttpRequestProgressEvent.total) * 100) ); $("#dl-bar").css( "width", Math.floor((xmlHttpRequestProgressEvent.loaded / xmlHttpRequestProgressEvent.total) * 100) + "%" ); $("#dl-bar").html(Math.floor((xmlHttpRequestProgressEvent.loaded / xmlHttpRequestProgressEvent.total) * 100) + "%"); } function showDownloadSuccess(xmlhttpRequestProgressEvent) { if (xmlhttpRequestProgressEvent.lengthComputable === false) return; $("#dl-data").html("Complete!"); $("#dl-bar").addClass("progress-bar-success"); } function showDownloadError(xmlhttpRequestProgressEvent) { if (xmlhttpRequestProgressEvent.lengthComputable === false) return; $("#dl-data").html("Oops, there was an error. Refresh page to try again"); $("#dl-bar").addClass("progress-bar-danger"); } function sendGetRequestWithCallback(url, callback) { $.ajax({ url: url, dataType: "jsonp", type: "GET", complete: function (xhr) { if (typeof callback === "function") { callback.apply(this, [xhr.status]); } }, }); } function checkMenuLinksStatus() { const OK = 200; if (!zxdl.linkCheckThisSession) { zxdl.linkCheckThisSession = true; for (let i = 0; i < zxdl.paths.length; i++) { let baseUrl = zxdl.paths[i].substring(0, zxdl.paths[i].lastIndexOf("/")); $("#zxdl-modal .link-status").append('<div class="link-' + i + '">Link ' + (i + 1) + ":</div>"); sendGetRequestWithCallback(baseUrl, function (status) { if (status === OK) { $("#zxdl-modal .link-" + i).append(' <i class="fa fa-check"></i>'); } else { $("#zxdl-modal .link-" + i).append(' <i class="fa fa-times"></i>'); } }); } } } function scanAndLoadVideoFromUrl(url) { if (zxdl.isVideoFound == false) { let videoElement = document.createElement("VIDEO"); videoElement.addEventListener("loadeddata", function () { // If video found console.log("ZXDL: Video found! " + url); zxdl.isVideoFound = true; zxdl.videoUrl = url; if (zxdl.isPrivateWindow) { $("#rip-div").html( "<h1>" + zxdl.videoTitle + '</h1><p>Uploaded by <a href="https://zoox18.com/user/' + zxdl.videoUploader + '">' + zxdl.videoUploader + '</a></p><p style="opacity:0.5">"' + zxdl.videoDescription + '"</p><p style="font-size:12px">(' + zxdl.videoUrl + ')</p><link rel="stylesheet" href="https://cdn.plyr.io/3.5.6/plyr.css" /><video style="width: 100%; height: 100%;" poster="https://www.zoox18.com/media/videos/tmb1/' + zxdl.VIDEO_ID + '/default.jpg" id="rippedvid" playsinline controls><source src="' + zxdl.videoUrl + '" type="video/mp4" /></video><div><hr><button id="zxdl_favorite" class="btn btn-primary"><i class="glyphicon glyphicon-heart"></i> Favorite</button> <button id="zxdl_download" class="btn btn-primary"><i class="glyphicon glyphicon-download"></i> Download</button><p id="status"></p><div id="dl-progress" class="well" style="display: none"></div></div>' ); $("#zxdl_favorite").click(function () { // Favorite button for private videos $("#status").html("Please wait..."); const http = new XMLHttpRequest(); const favoriteVideoBaseUrl = "https://www.zoox18.com/ajax/favorite_video"; const form = "video_id=" + zxdl.VIDEO_ID; http.open("POST", favoriteVideoBaseUrl, true); http.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); http.onreadystatechange = function () { if (http.readyState == 4 && http.status == 200) { const response = http.responseText; if (response.includes("alert-danger")) { $("#status").html( "Couldn't favorite video. Are you logged in? Is this video already in your favorites?" ); } else if (response.includes("alert-success")) { $("#status").html('<span style="color:#77b300">Added to favorites!</span>'); } else { $("#status").html("The site returned unknown data."); } } }; http.send(form); }); } else { // Replace download button on public videos $("div#share_video").append( '<button id="zxdl_download" class="btn btn-primary m-l-5"><i class="glyphicon glyphicon-download"></i></button><p id="status"></p>' ); $("#response_message").after('<div id="dl-progress" class="well" style="display: none"></div>'); $("button.btn.btn-default.dropdown-toggle").remove(); } // Initialize Download $("#zxdl_download").click(function () { if (zxdl.isDownloading === false) { zxdl.isDownloading = true; $("#dl-progress").css("display", "block"); $("#dl-progress").html( '<h4>Progress</h4><span id="dl-data">Loading...</span> <div class="progress"><div id="dl-bar" class="progress-bar progress-bar-striped" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%"></div></div>' ); GM_download({ url: url, name: zxdl.VIDEO_ID + ".mp4", onprogress: calculateAndShowDownloadProgress, onload: showDownloadSuccess, onerror: showDownloadError, }); } else { alert("You've already initiated a download. Refresh the page to try again."); } }); }); videoElement.src = url; } } async function makeAllUrlListwithKey(getLinksToo) { return new Promise((resolve) => { try { GM_xmlhttpRequest({ method: "GET", url: "https://pastebin.com/raw/pnEwmQ2f", headers: { "Cache-Control": "max-age=1200, must-revalidate", }, onload: function (response) { let json = JSON.parse(response.responseText); if (getLinksToo) { zxdl.key0 = json.key0; zxdl.key1 = json.key1; zxdl.key2 = json.key2; zxdl.paths = [ atob(json.host0) + zxdl.key0 + "/media/videos/h264/" + zxdl.VIDEO_ID + "_SD.mp4", atob(json.host1) + zxdl.key1 + "/media/videos/h264/" + zxdl.VIDEO_ID + "_SD.mp4", atob(json.host2) + zxdl.key2 + "/media/videos/iphone/" + zxdl.VIDEO_ID + ".mp4", atob(json.host2) + zxdl.key2 + "/media/videos/h264/" + zxdl.VIDEO_ID + "_SD.mp4", ]; resolve(); } if (json.revision) { $("#zx-ver").append("." + json.revision); } if (json.announcement) { $("#zxdl-header").append( '<div style="display:inline; width: 100%;font-size:11px;margin-left:10px;background: #252e51;padding: 2px 5px;color: white;"><i class="fa fa-info-circle"></i> ' + json.announcement + "</div>" ); } }, }); } catch (err) { alert( "ZXDL: There was an error retrieving links for this session. Try refreshing the page, otherwise if you keep receiving this message, please contact us" ); } }); } function isPublicWindowActive() { return $("#wrapper .container .row .col-md-8 .vcontainer ").length > 0; } function isPrivateWindowActive() { return $("#wrapper .container .row .col-xs-12 .text-danger").length > 0; } function isVideoUrl() { return window.location.pathname.split("/")[1] == "video"; } // On load async function main() { if (isPrivateWindowActive()) { zxdl.isPrivateWindow = true; zxdl.videoUploader = $(".text-danger a").text(); // You must be friends with... zxdl.videoTitle = $("meta[property='og:title']").attr("content"); // Metatag still have title zxdl.videoDescription = $("meta[property='og:description']").attr("content"); // Metatag still have discription if (isVideoUrl()) { $(".well.well-sm").remove(); // Remove notice $(".well.ad-body").remove(); // Remove sponsor block for non-ad-blockers $("#rip-div").html("<h1>Scanning for video " + zxdl.VIDEO_ID + "...</h1><p>This can take up to a minute.</p>"); await makeAllUrlListwithKey(true); zxdl.paths.forEach(scanAndLoadVideoFromUrl); } } else if (isPublicWindowActive()) { await makeAllUrlListwithKey(true); zxdl.paths.forEach(scanAndLoadVideoFromUrl); } else { await makeAllUrlListwithKey(false); } } // run makeNavigationBar(); removeAnnoyance(); $("a[href='#zxdl-modal']").click(function () { checkMenuLinksStatus(); }); window.onload = main(); })(jQuery);