Greasy Fork

Greasy Fork is available in English.

MAM Ratio Protect

Warns about downloading based on resulting Ratio Loss due to forgetting to buy w/FL

当前为 2021-05-01 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name MAM Ratio Protect
// @namespace yyyzzz999
// @author yyyzzz999
// @description Warns about downloading based on resulting Ratio Loss due to forgetting to buy w/FL
// @include https://www.myanonamouse.net/t/*
// @version 1.80
// @description (5/1/21)
// @grant none
// @run-at document-end
// ==/UserScript==
// Many Thanks to GardenShade, yyywwwyyyhhh & 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!
// Release downloadURL:  http://greasyfork.icu/en/scripts/416189-mam-ratio-protect

//let xhr;
let rcRow; //Used in body and functions

//let tHash = ""; //Hexcode hash for this torrent

let DEBUG =0; // Debugging mode on (1) or off (0) added in (v1.54) verbose (2) (v1.6)

/* 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: http://greasyfork.icu/en/scripts/418820-mam-user-page-re-title
// and http://greasyfork.icu/en/scripts/418992-mam-request-page-re-title
// and http://greasyfork.icu/en/scripts/417852-mam-site-store-fix

/* 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","KB","MB","GB","TB","PB","EB","ZB","YB"][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
			`<div class="torDetRow" id="Mrp_row"><div class="torDetLeft">${label}</div><div class="torDetRight ${rowClass}"><span id="mp_foobar"></span></div></div>`);
            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";
					dlLabel.innerHTML = ""; //Clear ratio loss
					//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);

/* if (DEBUG) console.log("Declaring fetchUD()"); //THIS CODE BLOCK IS NO LONGER USED IN 1.8
function fetchUD() { //Fetch ratio Uploaded numerator, and Downloaded denominator, then show calculations
		if (DEBUG) console.log("Fetching user data");
		fetch('https://www.myanonamouse.net/jsonLoad.php') //Greasemonkey/Violentmonky seem to need FQDN instead of partial URL (v1.65)
		.then(response => response.json())
		.then(data => {
			//console.log(data);
			//if (DEBUG) console.log("U/D:",data.uploaded,data.downloaded);
			// 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', 'KB', 'MB', 'GB', 'TB'];  // I want to be around when we start sharing TB+ collections! ;-)
			let tsize = x[0] * Math.pow(1024, sizes.indexOf(x[1]));
			// 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 rU3 = tsize*rcur;
            if (DEBUG) console.log("recovU = " + recovU + " recovU2 = " + recovU2 + " rU3 = " + rU3  );
			rcRow.innerHTML = "<b>" + formatBytes(recovU) + "</b>&nbsp; Uploaded Needed to Restore Current Ratio.<br><b>" +
			comma(Math.floor(125*recovU/268435456)) + "</b> Bonus Points to purchase.<br>" +
            "Contributing to the vault gives you on average almost one FL wedge per day at a cost of as little as 200 bonus points each. ";
            if (DEBUG) rcRow.innerHTML += "<br>Calculations based on Up/Down ratio= " + data.uploaded + "/" + data.downloaded +
            ", approximately " + comma(Math.floor(recovU)) + " future upload bytes, and a download size of " + comma(tsize) + " bytes.";
			}) // .textContent above prob. should be replaced with innerHTML for formatting and links etc.
		.catch((error) => {
		console.error('Error:', error);
		rcRow.innerHTML = "Try at most one reload to resolve error: " + error
		+ '<br>and then <a href="http://greasyfork.icu/en/scripts/416189-mam-ratio-protect/feedback" target="_blank">report this exact error message</a>'
		+ " along with the browser and userscript manager you are using including their version numbers." ; // v1.65
// Produced on 404: SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
		});
} */

// Main Program

// 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);
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
//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("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
// Seeding or downloading TRUE if "Actively Seeding" etc.
let seeding = document.getElementById('DLhistory');
if (DEBUG) console.log("seeding= " + seeding);

// 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 code if the new ratio exists and we found the current one
if(rnew && rcur){
    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); // For debugging

  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 = '<button id="Mrp_btn">View Calculations</button>';
	//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', 'KB', 'MB', 'GB', 'TB'];  // I want to be around when we start sharing TB+ collections! ;-)
			let tsize = x[0] * Math.pow(1024, sizes.indexOf(x[1]));
			// 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 = "<b>" + formatBytes(recovU) + "</b>&nbsp; Uploaded Needed to Restore Current Ratio.<br><b>" +
			comma(Math.floor(125*recovU/268435456)) + "</b> Bonus Points to purchase.<br>" +
            "Contributing 2000 bonus points to each vault gives you on average almost one FL wedge per day.";
            if (DEBUG) rcRow.innerHTML += "<br>Calculations based on ratio= " + rcur +
            ", approximately " + comma(Math.floor(recovU)) + " future upload bytes, and a download size of " + comma(tsize) + " bytes.";


  }

    // 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";
    }

    // Change this 1 number to your I never want to dl w/o FL ratio loss amount
    if(rdiff > 1){
        dlBtn.style.backgroundColor="Red";
        // Disable link to prevent download
//        dlBtn.style.pointerEvents="none";
//	Uncomment (remove //) above line to disable the download button
        // maybe hide the button, and add the Ratio Loss warning in its place?
        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";
    }
}