From 2d15933c53b952f6bc59bfaea9291e92ab8c00c4 Mon Sep 17 00:00:00 2001 From: Tarcisio Gruppi Date: Sat, 8 Jan 2022 13:00:28 -0300 Subject: [PATCH] added option to use a command line script as image uploader (#2100) --- .../prefComponents/imageUploader/index.vue | 46 ++++++++++++++- .../prefComponents/imageUploader/services.js | 8 +++ src/renderer/store/preferences.js | 3 +- src/renderer/util/fileSystem.js | 56 +++++++++++++++++-- 4 files changed, 103 insertions(+), 10 deletions(-) diff --git a/src/renderer/prefComponents/imageUploader/index.vue b/src/renderer/prefComponents/imageUploader/index.vue index 5e6f0d84..e0906194 100644 --- a/src/renderer/prefComponents/imageUploader/index.vue +++ b/src/renderer/prefComponents/imageUploader/index.vue @@ -53,6 +53,19 @@ Set as default + +
The script will be executed with the image file path as its only argument and it should output any valid value for the src attribute of a HTMLImageElement.
+
+
+ Shell script location +
+ +
+
+ Save + Set as default +
+
@@ -62,6 +75,7 @@ import { shell } from 'electron' import services, { isValidService } from './services.js' import legalNoticesCheckbox from './legalNoticesCheckbox' +import { isFileExecutable } from '../../util/fileSystem' export default { components: { @@ -76,6 +90,7 @@ export default { repo: '', branch: '' }, + cliScript: '', uploadServices: services, legalNoticesErrorStates: { smms: false, @@ -99,8 +114,19 @@ export default { return this.$store.state.preferences.githubToken } }, + prefCliScript: { + get: function () { + return this.$store.state.preferences.cliScript + } + }, githubDisable () { return !this.githubToken || !this.github.owner || !this.github.repo + }, + cliScriptDisable () { + if (!this.cliScript) { + return true + } + return !isFileExecutable(this.cliScript) } }, watch: { @@ -114,6 +140,7 @@ export default { this.$nextTick(() => { this.github = this.imageBed.github this.githubToken = this.prefGithubToken + this.cliScript = this.prefCliScript if (services.hasOwnProperty(this.currentUploader)) { services[this.currentUploader].agreedToLegalNotices = true @@ -143,11 +170,22 @@ export default { value: this.githubToken }) } - if (withNotice) { + if (type === 'cliScript') { + this.$store.dispatch('SET_USER_DATA', { + type: 'cliScript', + value: this.cliScript + }) + } + if (withNotice && type === 'github') { new Notification('Save Image Uploader', { body: 'The Github configration has been saved.' }) } + if (withNotice && type === 'cliScript') { + new Notification('Save Image Uploader', { + body: 'The command line script configuration has been saved' + }) + } }, setCurrentUploader (value) { const service = services[value] @@ -162,10 +200,12 @@ export default { return } // Save the setting before set it as default uploader. - if (value === 'github') { + if (value === 'github' || value === 'cliScript') { this.save(value, false) } - this.legalNoticesErrorStates[value] = false + if (this.legalNoticesErrorStates[value] !== undefined) { + this.legalNoticesErrorStates[value] = false + } const type = 'currentUploader' this.$store.dispatch('SET_USER_DATA', { type, value }) diff --git a/src/renderer/prefComponents/imageUploader/services.js b/src/renderer/prefComponents/imageUploader/services.js index a733cc28..9b824728 100644 --- a/src/renderer/prefComponents/imageUploader/services.js +++ b/src/renderer/prefComponents/imageUploader/services.js @@ -35,6 +35,14 @@ const services = { // Currently a non-persistent value agreedToLegalNotices: false + }, + + cliScript: { + name: 'command line script', + isGdprCompliant: true, + privacyUrl: '', + tosUrl: '', + agreedToLegalNotices: true } } diff --git a/src/renderer/store/preferences.js b/src/renderer/store/preferences.js index 25259719..fb5aeb41 100644 --- a/src/renderer/store/preferences.js +++ b/src/renderer/store/preferences.js @@ -94,7 +94,8 @@ const state = { repo: '', branch: '' } - } + }, + cliScript: '' } const getters = {} diff --git a/src/renderer/util/fileSystem.js b/src/renderer/util/fileSystem.js index ef36a84b..18d5ae26 100644 --- a/src/renderer/util/fileSystem.js +++ b/src/renderer/util/fileSystem.js @@ -6,6 +6,9 @@ import dayjs from 'dayjs' import { Octokit } from '@octokit/rest' import { ensureDirSync } from 'common/filesystem' import { isImageFile } from 'common/filesystem/paths' +import cp from 'child_process' +import { tmpdir } from 'os' +import { unlink, writeFileSync, statSync, constants } from 'fs' import { isWindows, dataURItoBlob } from './index' import axios from '../axios' @@ -104,6 +107,7 @@ export const uploadImage = async (pathname, image, preferences) => { const { currentUploader } = preferences const { owner, repo, branch } = preferences.imageBed.github const token = preferences.githubToken + const cliScript = preferences.cliScript const isPath = typeof image === 'string' const MAX_SIZE = 5 * 1024 * 1024 let re @@ -164,6 +168,25 @@ export const uploadImage = async (pathname, image, preferences) => { }) } + const uploadByCliScript = (filepath, name = null) => { + let isPath = true + if (typeof filepath !== 'string') { + isPath = false + const data = new Uint8Array(filepath) + filepath = path.join(tmpdir(), name || +new Date()) + writeFileSync(filepath, data) + } + cp.execFile(cliScript, [filepath], (err, data) => { + if (!isPath) { + unlink(filepath) + } + if (err) { + return rj(err) + } + re(data.trim()) + }) + } + const notification = () => { rj('Cannot upload more than 5M image, the image will be copied to the image folder') } @@ -177,6 +200,10 @@ export const uploadImage = async (pathname, image, preferences) => { if (size > MAX_SIZE) { notification() } else { + if (currentUploader === 'cliScript') { + uploadByCliScript(imagePath) + return promise + } const imageFile = await fs.readFile(imagePath) const blobFile = new Blob([imageFile]) if (currentUploader === 'smms') { @@ -196,17 +223,34 @@ export const uploadImage = async (pathname, image, preferences) => { } else { const reader = new FileReader() reader.onload = async () => { - const blobFile = dataURItoBlob(reader.result, image.name) - if (currentUploader === 'smms') { - uploadToSMMS(blobFile) - } else { - uploadByGithub(reader.result, image.name) + switch (currentUploader) { + case 'cliScript': + uploadByCliScript(reader.result, image.name) + break + + case 'smms': + uploadToSMMS(dataURItoBlob(reader.result, image.name)) + break + + default: + uploadByGithub(reader.result, image.name) } } - reader.readAsDataURL(image) + const readerFunction = currentUploader === 'cliScript' ? 'readAsArrayBuffer' : 'readAsDataURL' + reader[readerFunction](image) } } return promise } + +export const isFileExecutable = (filepath) => { + try { + const stat = statSync(filepath) + return stat.isFile() && (stat.mode & (constants.S_IXUSR | constants.S_IXGRP | constants.S_IXOTH)) !== 0 + } catch (err) { + // err ignored + return false + } +}