Merge pull request #3362 from tridactyl/gsbabil_saveas_tidier

Add --overwrite and --cleanup options to 'saveas'
This commit is contained in:
Oliver Blanthorn 2021-03-13 12:42:48 +00:00 committed by GitHub
commit df8a745f62
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 68 additions and 15 deletions

View file

@ -2,8 +2,10 @@
* Background download-related functions
*/
import * as Messaging from "@src/lib/messaging"
import * as Native from "@src/lib/native"
import * as config from "@src/lib/config"
import * as R from "ramda"
import { getDownloadFilenameForUrl } from "@src/lib/url_util"
/** Construct an object URL string from a given data URL
@ -78,11 +80,17 @@ export async function downloadUrl(url: string, saveAs: boolean) {
*
* Note: this requires a native messenger >=0.1.9. Make sure to nativegate for this.
*
* @param url the URL to download
* @param URL the URL to download
* @param saveAs If beginning with a slash, this is the absolute path the document should be moved to. If the first character of the string is a tilda, it will be expanded to an absolute path to the user's home directory. If saveAs begins with any other character, it will be considered a path relative to where the native messenger binary is located (e.g. "$HOME/.local/share/tridactyl" on linux).
* If saveAs points to a directory, the name of the document will be inferred from the URL and the document will be placed inside the directory. If saveAs points to an already existing file, the document will be saved in the downloads directory but wont be moved to where it should be ; an error will be thrown. If any of the directories referred to in saveAs do not exist, the file will be kept in the downloads directory but won't be moved to where it should be.
* @param If true, overwrite the destination file, returns error code 1 otherwise if file exists
* @param If true, cleans up temporary downloaded source file e.g. in $HOME/Downlods/downloaded.doc when the move operation fails e.g. due to target destination exists, OS error etc.
*/
export async function downloadUrlAs(url: string, saveAs: string) {
export async function downloadUrlAs(
url: string,
saveAs: string,
overwrite: boolean,
cleanup: boolean,
) {
if (!(await Native.nativegate("0.1.9", true))) return
const urlToSave = new URL(url)
@ -125,15 +133,18 @@ export async function downloadUrlAs(url: string, saveAs: string) {
const operation = await Native.move(
downloadItem.filename,
saveAs,
overwrite,
cleanup,
)
if (operation.code !== 0) {
const code2human = n => R.defaultTo("Unknown error", {1: "File already exists", 2: "Other OS error"}[n])
if (operation.code != 0) {
reject(
new Error(
`'${downloadItem.filename}' could not be moved to '${saveAs}'. Make sure it doesn't already exist and that all directories of the path exist.`,
`${code2human(operation.code)}. '${downloadItem.filename}' could not be moved to '${saveAs}'. Error code: ${operation.code}`,
),
)
} else {
resolve(operation)
resolve(downloadItem.filename)
}
} else {
reject(

View file

@ -888,7 +888,7 @@ export async function restart() {
/** Download the current document.
*
* If you have the native messenger v>=0.1.9 installed, the function accepts one optional argument, filename, which can be:
* If you have the native messenger v>=0.1.9 installed, the function accepts an optional argument, filename, which can be:
* - An absolute path
* - A path starting with ~, which will be expanded to your home directory
* - A relative path, relative to the native messenger executable (e.g. ~/.local/share/tridactyl on linux).
@ -896,12 +896,39 @@ export async function restart() {
*
* **NB**: if a non-default save location is chosen, Firefox's download manager will say the file is missing. It is not - it is where you asked it to be saved.
*
* @param filename The name the file should be saved as.
* Flags:
* - `--overwrite`: overwrite the destination file.
* - `--cleanup`: removes the downloaded source file e.g. `$HOME/Downlods/downloaded.doc` if moving it to the desired directory fails.
*/
//#content
export async function saveas(...filename: string[]) {
if (filename.length > 0) {
return Messaging.message("download_background", "downloadUrlAs", window.location.href, filename.join(" "))
export async function saveas(...args: string[]) {
let overwrite = false
let cleanup = false
const argParse = (args: string[]): string[] => {
if (args[0] === "--overwrite") {
overwrite = true
args.shift()
argParse(args)
}
if (args[0] === "--cleanup") {
cleanup = true
args.shift()
argParse(args)
}
return args
}
const file = argParse(args).join(" ") || undefined
const requiredNativeMessengerVersion = "0.3.2"
if ((overwrite || cleanup) && !(await Native.nativegate(requiredNativeMessengerVersion, false))) {
throw new Error(`":saveas --{overwrite, cleanup}" requires native ${requiredNativeMessengerVersion} or later`)
}
if (args.length > 0) {
const filename = await Messaging.message("download_background", "downloadUrlAs", window.location.href, file, overwrite, cleanup)
return fillcmdline_tmp(10000, `Download completed: ${filename} stored in ${file}`)
} else {
return Messaging.message("download_background", "downloadUrl", window.location.href, true)
}

View file

@ -581,6 +581,7 @@ export class default_config {
q: "tabclose",
qa: "qall",
sanitize: "sanitise",
"saveas!": "saveas --cleanup --overwrite",
tutorial: "tutor",
h: "help",
unmute: "mute unmute",

View file

@ -351,10 +351,24 @@ export async function temp(content: string, prefix: string) {
})
}
export async function move(from: string, to: string) {
return sendNativeMsg("move", { from, to }).catch(e => {
throw new Error(`Failed to move '${from}' to '${to}'. ${e}.`)
})
export async function move(
from: string,
to: string,
overwrite: boolean,
cleanup: boolean,
) {
const requiredNativeMessengerVersion = "0.3.0"
if ((await nativegate(requiredNativeMessengerVersion, false))) {
return sendNativeMsg("move", { from, to, overwrite, cleanup }).catch(e => {
throw new Error(`Failed to move '${from}' to '${to}'. ${e}.`)
})
} else {
// older "saveas" scenario for native-messenger < 0.3.0
return sendNativeMsg("move", { from, to }).catch(e => {
throw new Error(`Failed to move '${from}' to '${to}'. ${e}.`)
})
}
}
export async function listDir(dir: string) {