瀏覽代碼

added "filenameMaxLength" and "filenameReplacementCharacter" options

Gildas 6 年之前
父節點
當前提交
1abcbc935b

+ 3 - 1
extension/core/bg/config.js

@@ -46,6 +46,8 @@ singlefile.extension.core.bg.config = (() => {
 		confirmInfobarContent: false,
 		confirmFilename: false,
 		filenameConflictAction: "uniquify",
+		filenameMaxLength: 192,
+		filenameReplacementCharacter: "_",
 		contextMenuEnabled: true,
 		tabMenuEnabled: true,
 		browserActionMenuEnabled: true,
@@ -374,7 +376,7 @@ singlefile.extension.core.bg.config = (() => {
 			saveAs: true
 		};
 		try {
-			await singlefile.extension.core.bg.downloads.download(downloadInfo);
+			await singlefile.extension.core.bg.downloads.download(downloadInfo, "_");
 		} finally {
 			URL.revokeObjectURL(url);
 		}

+ 16 - 11
extension/core/bg/downloads.js

@@ -69,7 +69,12 @@ singlefile.extension.core.bg.downloads = (() => {
 				saveToClipboard(message);
 			} else {
 				try {
-					await downloadPage(message, { confirmFilename: message.confirmFilename, incognito: sender.tab.incognito, filenameConflictAction: message.filenameConflictAction });
+					await downloadPage(message, { 
+						confirmFilename: message.confirmFilename, 
+						incognito: sender.tab.incognito, 
+						filenameConflictAction: message.filenameConflictAction,
+						filenameReplacementCharacter: message.filenameReplacementCharacter
+					});
 				} catch (error) {
 					console.error(error); // eslint-disable-line no-console
 					singlefile.extension.ui.bg.main.onError(sender.tab.id, {});
@@ -91,10 +96,10 @@ singlefile.extension.core.bg.downloads = (() => {
 		if (options.incognito) {
 			downloadInfo.incognito = true;
 		}
-		await download(downloadInfo);
+		await download(downloadInfo, options.filenameReplacementCharacter);
 	}
 
-	async function download(downloadInfo) {
+	async function download(downloadInfo, replacementCharacter) {
 		let downloadId;
 		try {
 			downloadId = await browser.downloads.download(downloadInfo);
@@ -103,20 +108,20 @@ singlefile.extension.core.bg.downloads = (() => {
 				const errorMessage = error.message.toLowerCase();
 				const invalidFilename = errorMessage.includes(ERROR_INVALID_FILENAME_GECKO) || errorMessage.includes(ERROR_INVALID_FILENAME_CHROMIUM);
 				if (invalidFilename && downloadInfo.filename.startsWith(".")) {
-					downloadInfo.filename = "_" + downloadInfo.filename;
-					return download(downloadInfo);
+					downloadInfo.filename = replacementCharacter + downloadInfo.filename;
+					return download(downloadInfo, replacementCharacter);
 				} else if (invalidFilename && downloadInfo.filename.includes(",")) {
-					downloadInfo.filename = downloadInfo.filename.replace(/,/g, "_");
-					return download(downloadInfo);
+					downloadInfo.filename = downloadInfo.filename.replace(/,/g, replacementCharacter);
+					return download(downloadInfo, replacementCharacter);
 				} else if (invalidFilename && !downloadInfo.filename.match(/^[\x00-\x7F]+$/)) { // eslint-disable-line  no-control-regex
-					downloadInfo.filename = downloadInfo.filename.replace(/[^\x00-\x7F]+/g, "_"); // eslint-disable-line  no-control-regex
-					return download(downloadInfo);
+					downloadInfo.filename = downloadInfo.filename.replace(/[^\x00-\x7F]+/g, replacementCharacter); // eslint-disable-line  no-control-regex
+					return download(downloadInfo, replacementCharacter);
 				} else if ((errorMessage.includes(ERROR_INCOGNITO_GECKO) || errorMessage.includes(ERROR_INCOGNITO_GECKO_ALT)) && downloadInfo.incognito) {
 					delete downloadInfo.incognito;
-					return download(downloadInfo);
+					return download(downloadInfo, replacementCharacter);
 				} else if (errorMessage == ERROR_CONFLICT_ACTION_GECKO && downloadInfo.conflictAction) {
 					delete downloadInfo.conflictAction;
-					return download(downloadInfo);
+					return download(downloadInfo, replacementCharacter);
 				} else if (errorMessage.includes(ERROR_DOWNLOAD_CANCELED_GECKO)) {
 					return {};
 				} else {

+ 8 - 1
extension/core/content/content-main.js

@@ -171,7 +171,14 @@ this.singlefile.extension.core.content.main = this.singlefile.extension.core.con
 	async function downloadPage(page, options) {
 		if (options.backgroundSave) {
 			for (let blockIndex = 0; blockIndex * MAX_CONTENT_SIZE < page.content.length; blockIndex++) {
-				const message = { method: "downloads.download", confirmFilename: options.confirmFilename, filenameConflictAction: options.filenameConflictAction, filename: page.filename, saveToClipboard: options.saveToClipboard };
+				const message = {
+					method: "downloads.download",
+					confirmFilename: options.confirmFilename,
+					filenameConflictAction: options.filenameConflictAction,
+					filename: page.filename,
+					saveToClipboard: options.saveToClipboard,
+					filenameReplacementCharacter: options.filenameReplacementCharacter
+				};
 				message.truncated = page.content.length > MAX_CONTENT_SIZE;
 				if (message.truncated) {
 					message.finished = (blockIndex + 1) * MAX_CONTENT_SIZE > page.content.length;

+ 19 - 10
lib/single-file/single-file-core.js

@@ -448,15 +448,24 @@ this.singlefile.lib.core = this.singlefile.lib.core || (() => {
 				this.stats.add("discarded", "HTML bytes", size - contentSize);
 			}
 			let filename = await ProcessorHelper.evalTemplate(this.options.filenameTemplate, this.options, content) || "";
-			filename = filename.replace(/[~\\?%*:|"<>\x00-\x1f\x7F]+/g, "_"); // eslint-disable-line no-control-regex
-			filename = filename.replace(/\.\.\//g, "").replace(/^\/+/, "").replace(/\/+/g, "/").replace(/\/$/, "").replace(/\.$/, "").replace(/\.\//g, "._").replace(/\/\./g, "/_");
+			const replacementCharacter = this.options.filenameReplacementCharacter;
+			filename = filename
+				.replace(/[~\\?%*:|"<>\x00-\x1f\x7F]+/g, replacementCharacter); // eslint-disable-line no-control-regex
+			filename = filename
+				.replace(/\.\.\//g, "")
+				.replace(/^\/+/, "")
+				.replace(/\/+/g, "/")
+				.replace(/\/$/, "")
+				.replace(/\.$/, "")
+				.replace(/\.\//g, "." + replacementCharacter)
+				.replace(/\/\./g, "/" + replacementCharacter);
 			if (!this.options.backgroundSave) {
-				filename = filename.replace(/\//g, "_");
+				filename = filename.replace(/\//g, replacementCharacter);
 			}
-			if (util.getContentSize(filename) > 192) {
+			if (util.getContentSize(filename) > this.options.filenameMaxLength) {
 				const extensionMatch = filename.match(/(\.[^.]{3,4})$/);
 				const extension = extensionMatch && extensionMatch[0] && extensionMatch[0].length > 1 ? extensionMatch[0] : "";
-				filename = await util.truncateText(filename, 192 - extension.length);
+				filename = await util.truncateText(filename, this.options.filenameMaxLength - extension.length);
 				filename = filename + "…" + extension;
 			}
 			if (!filename) {
@@ -1249,7 +1258,7 @@ this.singlefile.lib.core = this.singlefile.lib.core || (() => {
 			template = await Util.evalTemplateVariable(template, "url-username", () => url.username || "No username", dontReplaceSlash);
 			template = await Util.evalTemplateVariable(template, "tab-id", () => String(options.tabId || "No tab id"), dontReplaceSlash);
 			template = await Util.evalTemplateVariable(template, "tab-index", () => String(options.tabIndex || "No tab index"), dontReplaceSlash);
-			template = await Util.evalTemplateVariable(template, "url-last-segment", () => decodeURI(Util.getLastSegment(url)) || "No last segment", dontReplaceSlash);
+			template = await Util.evalTemplateVariable(template, "url-last-segment", () => decodeURI(Util.getLastSegment(url, options.filenameReplacementCharacter)) || "No last segment", dontReplaceSlash);
 			if (content) {
 				template = await Util.evalTemplateVariable(template, "digest-sha-256", async () => util.digest("SHA-256", content), dontReplaceSlash);
 				template = await Util.evalTemplateVariable(template, "digest-sha-384", async () => util.digest("SHA-384", content), dontReplaceSlash);
@@ -1672,19 +1681,19 @@ this.singlefile.lib.core = this.singlefile.lib.core || (() => {
 			return attributeNames;
 		}
 
-		static async evalTemplateVariable(template, variableName, valueGetter, dontReplaceSlash) {
+		static async evalTemplateVariable(template, variableName, valueGetter, dontReplaceSlash, replacementCharacter) {
 			const replaceRegExp = new RegExp("{\\s*" + variableName + "\\s*}", "g");
 			if (template && template.match(replaceRegExp)) {
 				let value = await valueGetter();
 				if (!dontReplaceSlash) {
-					value = value.replace(/\/+/g, "_");
+					value = value.replace(/\/+/g, replacementCharacter);
 				}
 				return template.replace(replaceRegExp, value);
 			}
 			return template;
 		}
 
-		static getLastSegment(url) {
+		static getLastSegment(url, replacementCharacter) {
 			let lastSegmentMatch = url.pathname.match(/\/([^/]+)$/), lastSegment = lastSegmentMatch && lastSegmentMatch[0];
 			if (!lastSegment) {
 				lastSegmentMatch = url.href.match(/([^/]+)\/?$/);
@@ -1695,7 +1704,7 @@ this.singlefile.lib.core = this.singlefile.lib.core || (() => {
 				lastSegment = lastSegmentMatch && lastSegmentMatch[0];
 			}
 			if (!lastSegment) {
-				lastSegment = url.hostname.replace(/\/+/g, "_").replace(/\/$/, "");
+				lastSegment = url.hostname.replace(/\/+/g, replacementCharacter).replace(/\/$/, "");
 			}
 			lastSegmentMatch = lastSegment.match(/(.*)\.[^.]+$/);
 			if (lastSegmentMatch && lastSegmentMatch[1]) {