Difference between revisions of "MediaWiki:Gadget-BulkUpload/Runtime.js"

From Nookipedia, the Animal Crossing wiki
(Rm console log)
m (fixing licensing template name)
 
(33 intermediate revisions by one other user not shown)
Line 1: Line 1:
 +
/**
 +
* Turns Special:BulkUpload into a bulk upload form. Filenames are kept as-is and all files will have the same information.
 +
* Created by SuperHamster on Nookipedia (https://nookipedia.com/wiki/User:SuperHamster)
 +
* Licensed under CC BY-SA 3.0 (https://creativecommons.org/licenses/by-sa/3.0/)
 +
**/
 +
 
(function() {
 
(function() {
    "use strict";
+
"use strict";
    document.title = "Bulk upload form - Animal Crossing Wiki - Nookipedia";
+
 
    $('#firstHeading').html('Bulk upload form');
+
// Set title of page as it appears in the browser tab::
    $('#bodyContent').empty();
+
const PAGETITLE = "Bulk upload form - Animal Crossing Wiki - Nookipedia";
    $('#bodyContent').append('<p>Use this form to upload multiple files at once. Filenames are kept as-is and all files will have the same information. To cancel an upload that is in progress, simply navigate away from this page.</p>');
+
    var htmlUploadForm = `<fieldset>
+
// List of warnings that always result in upload failure, even if ignorewarnings is checked:
<legend>Files and info</legend>
+
const FATALWARNINGS = ["fileexists-no-change", "verification-error", "abusefilter-disallowed"];
<label for="bulkUploadFiles">Select files:</label><br>
+
<input type="file" id="bulkUploadFiles" multiple/><br><br>
+
// Milliseconds to wait between uploads, to avoid rate limiting:
<label for="bulkUploadInfo">File description</legend><br>
+
const SLEEPTIME = 500;
<textarea id="bulkUploadInfo" cols="80" rows="12">
+
// Milliseconds to give each file to upload before moving to the next file:
== Summary ==
+
const TIMEOUT = 30000;
 +
 +
// Default file description that is loaded in the form:
 +
const DEFAULTDESCRIPTION = `== Summary ==
 
{{File Info
 
{{File Info
 
|description = Describe the file (THIS IS NOT OPTIONAL)
 
|description = Describe the file (THIS IS NOT OPTIONAL)
 
|source = Where you found the file (THIS IS NOT OPTIONAL)
 
|source = Where you found the file (THIS IS NOT OPTIONAL)
|edits = Describe any edits made (if applicable)
 
|other-versions = Link to similar version(s) (if applicable)
 
|source-file-name = Name of the original source file (if applicable)
 
 
}}
 
}}
  
 
== Licensing ==
 
== Licensing ==
{{Fair use}}
+
{{Fairuse}}`;
</textarea><br><br>
+
 
 +
document.title = PAGETITLE;
 +
$('#firstHeading').html('Bulk upload form');
 +
$('#bodyContent').empty();
 +
$('#bodyContent').append('<p>Use this form to upload multiple files at once. Filenames are kept as-is and all files will have the same information.</p><p>Uploads through this tool are marked as bot edits in recent changes. To cancel an upload that is in progress, simply navigate away from this page.</p>');
 +
var htmlUploadForm = `<fieldset>
 +
<legend>Files and info</legend>
 +
<label for="bulkUploadFiles">Select files:</label><br>
 +
<input type="file" id="bulkUploadFiles" multiple/><br><br>
 +
<label for="bulkUploadInfo">File description</legend><br>
 +
<textarea id="bulkUploadInfo" cols="80" rows="12">` + DEFAULTDESCRIPTION + `</textarea><br><br>
 
<input type="checkbox" id="bulkUploadIgnoreWarnings"><label for="bulkUploadIgnoreWarnings">Ignore warnings (check to override existing files)</label><br><br>
 
<input type="checkbox" id="bulkUploadIgnoreWarnings"><label for="bulkUploadIgnoreWarnings">Ignore warnings (check to override existing files)</label><br><br>
 
<button id="bulkUploadButton" type="button">Upload files</button>
 
<button id="bulkUploadButton" type="button">Upload files</button>
Line 32: Line 48:
 
</fieldset>
 
</fieldset>
 
`;
 
`;
    $('#bodyContent').append(htmlUploadForm);
+
$('#bodyContent').append(htmlUploadForm);
    document.getElementById('bulkUploadButton').addEventListener("click", function () {
+
document.getElementById('bulkUploadButton').addEventListener("click", function () {
        const api = new mw.Api();
+
const api = new mw.Api();
        var completed = 0;
+
var started = 0;
        document.getElementById('bulkUploadButton').disabled = true;
+
var completed = 0;
        $('#bulkUploadStatus').empty();
+
var timedOut = [];
        var files = $('#bulkUploadFiles')[0].files;
+
document.getElementById('bulkUploadButton').disabled = true;
        for (var i = 0; i < files.length; i++) {
+
$('#bulkUploadStatus').empty();
            var file = files[i];
+
 
            var uploadParams = {};
+
function timeout(ms) {
            if($('#bulkUploadIgnoreWarnings').is(':checked')) {
+
return new Promise(resolve => { setTimeout(() => resolve('timed out'), ms)});
                uploadParams = {
+
}
                    filename: file.name,
+
 
                    text: $('#bulkUploadInfo').val(),
+
async function uploadFile(file, uploadParams) {
                    ignorewarnings: "yes",
+
started++;
                    format: "json"
+
$('#bulkUploadStatus').append('Starting upload for ' + file.name + '...<br>');
                };
+
let uploadPromise = api.upload(file, uploadParams).always(response => {
            } else {
+
completed++;
                uploadParams = {
+
var status = "";
                    filename: file.name,
+
if (timedOut.includes(file.name)) {
                    text: $('#bulkUploadInfo').val(),
+
timedOut = timedOut.filter(item => item !== file.name);
                    format: "json"
+
}
                };
+
if (typeof response === 'object') {
            }
+
if (response.hasOwnProperty('upload') && upload.result == 'Success') {
            api.upload(file, Object.freeze(uploadParams)).always( (response) => {
+
status = 'Upload succeeded for ' + file.name;
                JSON.stringify(response);
+
} else {
                completed++;
+
status = 'Upload <span style="color: red;">failed</span> for ' + file.name + ' due to unknown error';
                if (response.hasOwnProperty('upload')) {
+
}
                    $('#bulkUploadStatus').append('Upload succeeded for ' + file.name + ' (' + completed + ' out of ' + files.length + ')<br>');
+
} else if (typeof response === 'string') {
                } else {
+
if (FATALWARNINGS.includes(response)) {
                    $('#bulkUploadStatus').append('Upload failed for ' + file.name + ' (' + completed + ' out of ' + files.length + ')<br>');
+
status = 'Upload <span style="color: red;">failed</span> for ' + file.name + ' with error "' + response + '"';
                }
+
} else {
                if (completed == files.length) {
+
if ($('#bulkUploadIgnoreWarnings').is(':checked')) {
                    $('#bulkUploadStatus').append('<span style="color: green;">Done!</span>');
+
status = 'Upload succeeded for ' + file.name + ' with warning "' + response + '"';
                    document.getElementById('bulkUploadButton').disabled = false;
+
} else {
                }
+
status = 'Upload <span style="color: red;">failed</span> for ' + file.name + ' with error "' + response + '"';
            });
+
}
        }
+
}
    });
+
} else {
 +
status = 'Upload <span style="color: red;">failed</span> for ' + file.name + ' due to unknown error';
 +
}
 +
$('#bulkUploadStatus').append(status + ' (' + completed + ' out of ' + files.length + ')<br>');
 +
if (completed == files.length) {
 +
$('#bulkUploadStatus').append('<span style="color: green;">Done!</span><br>');
 +
document.getElementById('bulkUploadButton').disabled = false;
 +
}
 +
});
 +
 +
try {
 +
var onTimeout = timeout(TIMEOUT);
 +
await Promise.race([uploadPromise, onTimeout]).then((value) => {
 +
if (value === 'timed out') {
 +
timedOut.push(file.name);
 +
$('#bulkUploadStatus').append(file.name + ' is taking a long time to upload; moving to the next file.<br>');
 +
}
 +
if (started == files.length && timedOut.length > 0) {
 +
$('#bulkUploadStatus').append('The following uploads are still running in the background and may be stuck; recommend retrying: ' + timedOut + '<br>')
 +
}
 +
}).catch((value) => {
 +
if (value === 'timed out') {
 +
timedOut.push(file.name);
 +
$('#bulkUploadStatus').append(file.name + ' is taking a long time to upload; moving to the next file.<br>');
 +
}
 +
if (started == files.length && timedOut.length > 0) {
 +
$('#bulkUploadStatus').append('The following uploads are still running in the background and may be stuck; recommend retrying: ' + timedOut + '<br>')
 +
}
 +
})
 +
} catch (e) {
 +
console.error(e);
 +
} finally {
 +
await timeout(SLEEPTIME);
 +
return true;
 +
}
 +
}
 +
 
 +
async function iterateFiles() {
 +
// Remove duplicates:
 +
const uniqueFiles = [...new Set(files)];
 +
 
 +
// Iterate through files and set upload params:
 +
for (const file of uniqueFiles) {
 +
var uploadParams = {};
 +
if($('#bulkUploadIgnoreWarnings').is(':checked')) {
 +
uploadParams = {
 +
filename: file.name,
 +
text: $('#bulkUploadInfo').val(),
 +
comment: "Uploaded via bulk upload gadget",
 +
ignorewarnings: "yes",
 +
format: "json"
 +
};
 +
} else {
 +
uploadParams = {
 +
filename: file.name,
 +
text: $('#bulkUploadInfo').val(),
 +
comment: "Uploaded via bulk upload gadget",
 +
format: "json"
 +
};
 +
}
 +
 
 +
// Call upload func, wait until it completes or times out before going to next file
 +
const response = await uploadFile(file, uploadParams);
 +
}
 +
}
 +
 
 +
var files = $('#bulkUploadFiles')[0].files;
 +
iterateFiles();
 +
});
 
})();
 
})();

Latest revision as of 15:40, May 30, 2022

/**
 * Turns Special:BulkUpload into a bulk upload form. Filenames are kept as-is and all files will have the same information.
 * Created by SuperHamster on Nookipedia (https://nookipedia.com/wiki/User:SuperHamster)
 * Licensed under CC BY-SA 3.0 (https://creativecommons.org/licenses/by-sa/3.0/)
 **/

(function() {
	"use strict";

	// Set title of page as it appears in the browser tab::
	const PAGETITLE = "Bulk upload form - Animal Crossing Wiki - Nookipedia";
	
	// List of warnings that always result in upload failure, even if ignorewarnings is checked:
	const FATALWARNINGS = ["fileexists-no-change", "verification-error", "abusefilter-disallowed"];
	
	// Milliseconds to wait between uploads, to avoid rate limiting:
	const SLEEPTIME = 500;
	// Milliseconds to give each file to upload before moving to the next file:
	const TIMEOUT = 30000;
	
	// Default file description that is loaded in the form:
	const DEFAULTDESCRIPTION = `== Summary ==
{{File Info
|description = Describe the file (THIS IS NOT OPTIONAL)
|source = Where you found the file (THIS IS NOT OPTIONAL)
}}

== Licensing ==
{{Fairuse}}`;

	document.title = PAGETITLE;
	$('#firstHeading').html('Bulk upload form');
	$('#bodyContent').empty();
	$('#bodyContent').append('<p>Use this form to upload multiple files at once. Filenames are kept as-is and all files will have the same information.</p><p>Uploads through this tool are marked as bot edits in recent changes. To cancel an upload that is in progress, simply navigate away from this page.</p>');
	var htmlUploadForm = `<fieldset>
<legend>Files and info</legend>
<label for="bulkUploadFiles">Select files:</label><br>
<input type="file" id="bulkUploadFiles" multiple/><br><br>
<label for="bulkUploadInfo">File description</legend><br>
<textarea id="bulkUploadInfo" cols="80" rows="12">` + DEFAULTDESCRIPTION + `</textarea><br><br>
<input type="checkbox" id="bulkUploadIgnoreWarnings"><label for="bulkUploadIgnoreWarnings">Ignore warnings (check to override existing files)</label><br><br>
<button id="bulkUploadButton" type="button">Upload files</button>
</fieldset>
<br>
<fieldset>
<legend>Upload status</legend>
<span id="bulkUploadStatus">Waiting for user to start upload.</span>
</fieldset>
`;
	$('#bodyContent').append(htmlUploadForm);
	document.getElementById('bulkUploadButton').addEventListener("click", function () {
		const api = new mw.Api();
		var started = 0;
		var completed = 0;
		var timedOut = [];
		document.getElementById('bulkUploadButton').disabled = true;
		$('#bulkUploadStatus').empty();

		function timeout(ms) {
			return new Promise(resolve => { setTimeout(() => resolve('timed out'), ms)});
		}

		async function uploadFile(file, uploadParams) {
			started++;
			$('#bulkUploadStatus').append('Starting upload for ' + file.name + '...<br>');
			let uploadPromise = api.upload(file, uploadParams).always(response => {
				completed++;
				var status = "";
				if (timedOut.includes(file.name)) {
					timedOut = timedOut.filter(item => item !== file.name);
				}
				if (typeof response === 'object') {
					if (response.hasOwnProperty('upload') && upload.result == 'Success') {
						status = 'Upload succeeded for ' + file.name;
					} else {
						status = 'Upload <span style="color: red;">failed</span> for ' + file.name + ' due to unknown error';
					}
				} else if (typeof response === 'string') {
					if (FATALWARNINGS.includes(response)) {
						status = 'Upload <span style="color: red;">failed</span> for ' + file.name + ' with error "' + response + '"';
					} else {
						if ($('#bulkUploadIgnoreWarnings').is(':checked')) {
							status = 'Upload succeeded for ' + file.name + ' with warning "' + response + '"';
						} else {
							status = 'Upload <span style="color: red;">failed</span> for ' + file.name + ' with error "' + response + '"';
						}
					}
				} else {
					status = 'Upload <span style="color: red;">failed</span> for ' + file.name + ' due to unknown error';
				}
				$('#bulkUploadStatus').append(status + ' (' + completed + ' out of ' + files.length + ')<br>');
				if (completed == files.length) {
					$('#bulkUploadStatus').append('<span style="color: green;">Done!</span><br>');
					document.getElementById('bulkUploadButton').disabled = false;
				}
			});
			
			try {
				var onTimeout = timeout(TIMEOUT);
				await Promise.race([uploadPromise, onTimeout]).then((value) => {
					if (value === 'timed out') {
						timedOut.push(file.name);
						$('#bulkUploadStatus').append(file.name + ' is taking a long time to upload; moving to the next file.<br>');
					}
					if (started == files.length && timedOut.length > 0) {
						$('#bulkUploadStatus').append('The following uploads are still running in the background and may be stuck; recommend retrying: ' + timedOut + '<br>')
					}
				}).catch((value) => {
					if (value === 'timed out') {
						timedOut.push(file.name);
						$('#bulkUploadStatus').append(file.name + ' is taking a long time to upload; moving to the next file.<br>');
					}
					if (started == files.length && timedOut.length > 0) {
						$('#bulkUploadStatus').append('The following uploads are still running in the background and may be stuck; recommend retrying: ' + timedOut + '<br>')
					}
				})
			} catch (e) {
				console.error(e);
			} finally {
				await timeout(SLEEPTIME);
				return true;
			}
		}

		async function iterateFiles() {
			// Remove duplicates:
			const uniqueFiles = [...new Set(files)];

			// Iterate through files and set upload params:
			for (const file of uniqueFiles) {
				var uploadParams = {};
				if($('#bulkUploadIgnoreWarnings').is(':checked')) {
					uploadParams = {
						filename: file.name,
						text: $('#bulkUploadInfo').val(),
						comment: "Uploaded via bulk upload gadget",
						ignorewarnings: "yes",
						format: "json"
					};
				} else {
					uploadParams = {
						filename: file.name,
						text: $('#bulkUploadInfo').val(),
						comment: "Uploaded via bulk upload gadget",
						format: "json"
					};
				}

				// Call upload func, wait until it completes or times out before going to next file
				const response = await uploadFile(file, uploadParams);
			}
		}

		var files = $('#bulkUploadFiles')[0].files;
		iterateFiles();
	});
})();