// ==UserScript==
// @name MAM Ratio Protect BETA
// @namespace yyyzzz999
// @author yyyzzz999
// @description (5/22/22) Warns about downloading based on resulting Ratio Loss due to forgetting to buy w/FL
// @match https://www.myanonamouse.net/t/*
// @version 1.94
// @license MIT
// @grant none
// @run-at document-end
// @downloadURL none
// ==/UserScript==
// Many Thanks to GardenShade, yyywwwyyyhhh, sora12192, Onigiri & ooglyboogly for advice, testing, and code contributions!
/*jshint esversion: 6 */
/*eslint no-multi-spaces:0 */ //stop pestering me 'cause I learned to type with double spaces!
/*eslint curly: ["error", "multi-line"]*/ // Don't nag about missing {} after if's that do only one thing.
// Release downloadURL: https://greasyfork.org/en/scripts/416189-mam-ratio-protect
//let xhr;
let rcRow; //Used in body and functions
//let tHash = ""; //Hexcode hash for this torrent
let DEBUG =1; // Debugging mode on (1) or off (0) added in (v1.54) verbose (2) (v1.6)
let CICON =1; // Custome Favorite icons is ON by default, change this to 0 to keep the default icon on all tabs
/* Easter Egg bonus features, uncomment to use! */
//Hide banner on book pages if not using MAM+
//document.getElementById("msb").style.display = "none";
//Re-title tab to make it easier to find a book tab! v1.66
document.title=document.title.replace('Details for torrent "', '');
//This makes Bookmark titles easier to read as well!
// See also: https://greasyfork.org/en/scripts/418820-mam-user-page-re-title
// and https://greasyfork.org/en/scripts/418992-mam-request-page-re-title
// and https://greasyfork.org/en/scripts/417852-mam-site-store-fix
// and https://greasyfork.org/en/scripts/441976-mam-banner-shrink
/* End Easter Eggs */
// Functions:
//https://blog.abelotech.com/posts/number-currency-formatting-javascript/
function comma(num) { // add commas to a number
return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,')
}
function formatBytes(a,b=2){if(0===a)return"0 Bytes";
const c=0>b?0:b,d=Math.floor(Math.log(a)/Math.log(1024));
return parseFloat((a/Math.pow(1024,d)).toFixed(c))+" "+["Bytes","KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"][d]}
/** hacked from MAM+ 4.2.20 Line 222
Always inserted line at beginning or end of page block until removed parentElement before insertAdjacentHTML
* Add a new TorDetRow and return the inner div
* @param tar The row to be targetted
* @param label The name to be displayed for the new row
* @param rowClass The row's classname (should start with mp_)
*/
function myaddTorDetailsRow(tar, label, rowClass) {
if (tar == null ) { //why originally === assignment?
throw new Error(`myAdd Tor Details Row: empty node @ ${tar}`);
}
else {
tar.insertAdjacentHTML('beforebegin', // changed from afterbegin so I can position before .torDetBottom as nth child changes w/MAM+
// and Torrent: ratio line has no id or unique CSS
`
${label}
`);
return document.querySelector(`.${rowClass} #mp_foobar`);
}
}
/*
https://stackoverflow.com/questions/629671/how-can-i-intercept-xmlhttprequests-from-a-greasemonkey-script
Spy on all AJAX request/response to see when FL is purchased
This works in Basilisk Scratchpad, Tampermonkey w/Firefox, but not Greasmonkey 3.9 in Basilisk
Problem: it also looks at our own AJAX request so we return early if we see the user data object
*/
let ResultObj; // I scoped this out of the function in case I want to reference it later in other contexts/functions
// This is where we react after FL purchase
(function(open) {
XMLHttpRequest.prototype.open = function() {
this.addEventListener("readystatechange", function() {
if (DEBUG >2) console.log(this.readyState);
if (DEBUG >2) console.log(this.responseText);
if (this.readyState == 4 && this.status == 200) {
ResultObj = JSON.parse(this.responseText);
if ( ResultObj.uid ) return; //if getting user data in this script w/ fetch('/jsonLoad.php')
if (DEBUG >1) console.log("Response: " + this.responseText);
if (DEBUG) console.log("xhr status: " + ResultObj.success);
if (ResultObj.success && ResultObj.type == "personal FL") { // Return download button etc. to normal after FL purchase (v1.6)
// bookmark operations return {"success": true, "action": "add"} Specific FL test in v1.63!
dlBtn.innerHTML = "Download";
dlBtn.style.backgroundColor="dodgerblue";
dlBtn.style.color="white"; // V1.9 restore white text color showing button active again
dlBtn.style.pointerEvents = 'auto' //NEW in V1.9 restore DL link
dlLabel.innerHTML = ""; //Clear ratio loss
if (CICON) favi("5"); // Purple means FL purchased, but not yet downloading or seeding
//document.getElementById("Mrp_row").style.visibility = "hidden"; //Wastes space
document.getElementById("Mrp_row").style.display = "none"; //Restore w/ "block"
if (DEBUG) console.log("Seedbonus: " + ResultObj.seedbonus);
if( document.getElementById("tmFW") && ResultObj.FLleft) {// If FL Wedges: are displayed in top menu & we have new value so update it
document.getElementById("tmFW").textContent=ResultObj.textContent="FL Wedges: " + ResultObj.FLleft; }
// Why doesn't MAM already do this?
}
}
}, false);
open.apply(this, arguments);
};
})(XMLHttpRequest.prototype.open);
// Function to change the FAVICON to one matching a filename fn
function favi(fn,un) {
un = un || 164109;
link.href = 'https://cdn.myanonamouse.net/imagebucket/' + un + '/' + fn + '.png' ;
// link.href = 'https://cdn.myanonamouse.net/imagebucket/164109/' + fn + '.png' ;
/* Many filenames are simply the CGA color number 1-15 (low blue to white)
https://en.wikipedia.org/wiki/Web_colors
It would be easy to change my user number to a custom one by parsing a number out of the string,
or better yet by adding an optional 2nd argument (fn,un).
un = un || 164109;
Site security requires the favicon to be hosted on MAM.
Use: https://www.myanonamouse.net/bitbucket-upload.php
to store your own custom icons 32x32 or 16x16 if you prefer.
*/
}
// Main Program
// Set up for custom Favicons, and load a simple default. Link global variable set up once, then used in favi()
if (CICON) { // If changing icons is set to TRUE or 1 etc.
var link = document.querySelector("link[rel~='icon']");
if (!link) {
link = document.createElement('link');
link.rel = 'icon';
document.getElementsByTagName('head')[0].appendChild(link);
}
favi("tm_32x32"); //Simple black mouse w/transparent background
// Source icon: https://www.shareicon.net/data/512x512/2016/01/06/699292_mouse_512x512.png
}
// The download text area
let dlBtn = document.getElementById("tddl");
// The unused label area above the download text
let dlLabel = document.querySelector("#download .torDetInnerTop");
// Would become ratio
let rnew = 0; // FALSE
if (document.getElementById("ratio").textContent.split(" ")[1].match(/become/) ) {
rnew = document.getElementById("ratio").textContent.split(" ")[2].replace(/,/g,""); // (v1.54)
// VIP expires date caused rdiff to be NaN in v1.53 bcause text crept in in v1.52
}
if (DEBUG) console.log("rnew= " + rnew);
if (rnew) rnew = rnew.replace(/[A-z]/g,""); // (v.1.71) to address no space before "This can't be made site FL due to no author set" t/73005 //v1.92 added if, replace doesn't work on 0
//let rnew = document.getElementById("ratio").textContent.split("become ")[1].replace(/,/g,""); // broke w/On list for next FL pick
//let rnew = document.getElementById("ratio").textContent.match(/\d*\,?\d+\.\d+/)[0].replace(/,/g,"''); //breaks at 1,000,000bp
if (DEBUG) console.log("After trim rnew= " + rnew); // For debugging
// Current ratio - Version 1.52 and earlier broke when a ratio has a comma
let rcur = document.getElementById("tmR").textContent.replace(/,/g,"").trim();
if (DEBUG) console.log("rcur= " + rcur ); // For debugging
const vipstat = document.querySelector("#ratio .torDetInnerBottomSpan").textContent;
// VIP status is in the div with class=torDetInnerBottomSpan in another div with id=ratio
if (CICON && (vipstat.search("VIP expires") > -1 || vipstat.search("torrent is freeleech") > -1 ) ) favi("mouseclock");
// TODO parse out when a VIP expires, and add the expiration date to the tab title so it shows up on mouseover!
// Seeding or downloading TRUE if "Actively Seeding" etc.
let seeding = document.getElementById('DLhistory');
if (seeding && CICON) favi("13");
if (seeding && DEBUG) console.log("seeding.textContent= " + seeding.textContent); //v1.92 object was not useful in console
// Available FL wedges - for future use... (>v1.6)
// Only visible if set on MAM in Preferences, Style, Main Menu, Top Menu
//let wedgeAvail = document.getElementById('tmFW').textContent.split(":")[1].trim(); //only works if added to top menu
// Probably more robust way of finding current FL, so use if avail.
//if(wedgeAvail) console.log("tm wedgeAvail= " + wedgeAvail);
// ELSE try finding in drop downs with Rel XPath //a[contains(text(),'FL Wedges: ')] or
// CSS ul:nth-child(1) li.mmUserStats ul.hidden:nth-child(2) li:nth-child(7) > a:nth-child(1)
// let wedgeAvail = document.querySelector('[aria-labelledby="userMenu"] li:nth-of-type(7)').textContent.split(':')[1].trim();
// or document.querySelector("ul:nth-child(1) li.mmUserStats ul.hidden:nth-child(2) li:nth-child(7) > a:nth-child(1)")
// Only run the ratio loss code if the new ratio exists and we found the current one
if(rnew && rcur && !seeding){ // Need to skip this whole section for correct icon colors, should have in the 1st place
// let rdiff = 0;
let rdiff = rcur-rnew; // Loss in ratio after download (if error in old browser use var instead of let)
if (DEBUG) console.log("rdiff= " + rdiff); // To see how far the script is progressing as well as the values
if (seeding == null ) { // if NOT already seeding, downloading or VIP expires (v1.54)
dlLabel.innerHTML = `Ratio loss ${rdiff.toFixed(4)}`; //changed from toPrecision(5) (v1.54)
dlLabel.style.fontWeight = "normal"; //To distinguish from BOLD Titles
// Add line under Torrent: detail for Cost data "Cost to Restore Ratio"
rcRow = myaddTorDetailsRow(document.querySelector("div[class='torDetBottom']"), 'Cost to Restore Ratio', 'mp_rcRow');
if (DEBUG) console.log("rcRow.innerHTML= " + rcRow.innerHTML);
// Create button that will load data and insert calculations (and slow ajax calls to server)
//rcRow.innerHTML = '';
//document.getElementById("Mrp_btn").addEventListener("click", fetchUD);
// Always show calculations when there is a ratio loss
// Calculate & Display cost of download w/o FL
let x=document.querySelector("div[id='size'] span").textContent.split(/\s+/); //Why didn't split(" ") work?
if (DEBUG) console.log("x = " + x + ", x[0]= " + x[0] + ", x[1]= " + x[1] );
const sizes = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB']; // V1.9 MAM changed KB to KiB etc. Spring 2022
// I want to be around when we start sharing TB+ collections! ;-)
let tsize = x[0].replace(/,/g,"") * Math.pow(1024, sizes.indexOf(x[1])); //Remove commas in V1.9 (only 323984 seemed to need this)
// Convert human notation to bytes, only used once so didn't make it a function
if (DEBUG) console.log("tsize = " + tsize);
//let recovU = (data.uploaded*(data.downloaded+tsize))/data.downloaded - data.uploaded;
//let recovU2 = tsize*data.uploaded/data.downloaded; // yyywwwyyyhhh proposed change
let recovU = tsize*rcur;
if (DEBUG) console.log("recovU = " + recovU );
rcRow.innerHTML = "" + formatBytes(recovU) + " Uploaded Needed to Restore Current Ratio. " +
comma(Math.floor(125*recovU/268435456)) + " Bonus Points to purchase. " +
"That's enough Bonus Points to obtain approximately one FL wedge per day for " + comma(Math.floor(5*recovU/2147483648)) + " days." ;
//" " + "Contributing 2000 bonus points to each vault gives you on average almost one FL wedge per day.";
if (DEBUG) { rcRow.innerHTML += " Calculations based on ratio= " + rcur +
", approximately " + comma(Math.floor(recovU)) + " future upload bytes, and a download size of " + comma(tsize) + " bytes.";}
}
if (CICON && rdiff <= 0.3) favi("DodgerBlue");
// Change this .3 number to your "trivial ratio loss" amount
// These changes will always happen if the ratio conditions are met
if(rdiff > 0.3){
dlBtn.style.backgroundColor="SpringGreen";
dlBtn.style.color="black";
if (CICON) favi("SpringGreen"); //
}
// Change this 1 number to your I never want to dl w/o FL ratio loss amount
if(rdiff > 1){
dlBtn.style.backgroundColor="Red";
if (CICON) favi("12");
// Disable link to prevent download
dlBtn.style.pointerEvents="none";
// Comment (add // to) the above line to not disable the download button when RED V1.9 new default
// maybe hide the button, and add the Ratio Loss warning in its place? Nah!
dlBtn.innerHTML = "FL Recommended";
dlLabel.style.fontWeight = "bold";
// Change this .5 number to your "I need to think about using a FL ratio loss" amount
}else if(rdiff > 0.5){
dlBtn.style.backgroundColor="Orange";
if (CICON) favi("Orange");
}
}
// if (seeding && CICON) favi("13"); color got changed back in debugging
if (DEBUG) console.log("MAM Ratio Protect BETA 1.94 Completed Normally.");