├── LICENSE ├── README.md └── Quicksave.plugin.js /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UNMAINTAINED 2 | 3 | # Quicksave-BD-plugin 4 | This is a plugin for BetterDiscord. 5 | 6 | ### What does it do? 7 | Adds saving buttons to images. These will download the images from their original urls and save them on disk. 8 | 9 |  10 |  11 | 12 | ### How to install? 13 | 1. Make sure you have native Discord application installed with [BetterDiscord](https://betterdiscord.net) 14 | 2. Locate the BetterDiscord plugin folder 15 | * %appdata%/BetterDiscord/plugins 16 | * or Settings>BetterDiscord>Plugins>Open Plugin Folder 17 | 3. Copy file 'Quicksave.plugin.js' to the plugin folder 18 | 4. Reload/restart Discord (CTRL+R) 19 | 5. Go to Settings>BetterDiscord>Plugins 20 | 6. Enable this plugin 21 | 7. Go to the plugin settings and set a download directory. 22 | 23 | 24 | ### Notes 25 | - The plugin will download images from their original URLs. This is done because Discord rehosted images are usually compressed. 26 | - This will reveal your IP address to the server when downloading. 27 | - The plugin wont check the files itself in case of viruses or stuff like that. 28 | - MIT license. Use at your own risk. 29 | -------------------------------------------------------------------------------- /Quicksave.plugin.js: -------------------------------------------------------------------------------- 1 | //META{"name":"Quicksave"}*// 2 | 3 | 'use strict'; 4 | class Quicksave { 5 | getAuthor() { return "kosshi";} 6 | getName() { return "Quicksave";} 7 | getVersion() { return "0.2.5";} 8 | getDescription(){ return "Lets you save images fast.";} 9 | load() {} // Called when the plugin is loaded in to memory 10 | start() { 11 | this.settingsVersion = 6; 12 | 13 | this.extensionWhitelist = 14 | `png, gif, jpg, jpeg, jpe, jif, jfif, jfi, bmp, apng, webp`; 15 | 16 | this.namingMethods = { 17 | original:"Keep Original", 18 | random: "Random" 19 | }; 20 | 21 | BdApi.injectCSS("quicksave-style", ` 22 | .thumbQuicksave { 23 | z-index: 9000!important; 24 | 25 | background-color: rgba(51, 51, 51, .8); 26 | 27 | position: absolute; 28 | display: block; 29 | 30 | padding: 3px 9px; 31 | margin: 5px; 32 | 33 | border-radius: 3px; 34 | 35 | font-family: inherit; 36 | color: #FFF; 37 | font-weight: 500; 38 | font-size: 14px; 39 | opacity: 0; 40 | } 41 | 42 | .imageWrapper-2p5ogY:hover .thumbQuicksave { 43 | opacity: 0.8; 44 | } 45 | 46 | .thumbQuicksave:hover { 47 | opacity: 1 !important; 48 | } 49 | 50 | #qs_button { 51 | padding-left: 10px; 52 | } 53 | `); 54 | 55 | this.injectThumbIcons(); 56 | } 57 | 58 | stop() { 59 | clearTimeout(this.injectionTimeout); 60 | BdApi.clearCSS("quicksave-style"); 61 | } 62 | 63 | accessSync(dir){ 64 | var fs = require('fs'); 65 | try { 66 | fs.accessSync(dir, fs.F_OK); 67 | return true; 68 | } catch (e) { 69 | return false; 70 | } 71 | } 72 | 73 | 74 | observer(e) { 75 | // MutationObserver, function is bound by BetterDiscord. 76 | // We use this to see if user opens an image, if so, add a button. 77 | 78 | var fs = require('fs'); 79 | 80 | // IMAGE OPEN BUTTON 81 | 82 | if( e.addedNodes.length > 0 83 | && e.addedNodes[0].className=='backdrop-1wrmKB da-backdrop' 84 | ){ 85 | 86 | let modal = document.querySelector('div.modal-1UGdnR'); 87 | 88 | // Check if the added element actually has image modal, loading or not 89 | if( 90 | // !modal.querySelector('.imagePlaceholder-jWw28v') && 91 | !modal.querySelector('.imageWrapper-2p5ogY') 92 | ) 93 | return; 94 | 95 | // Element that has the "Open Original" button as a child 96 | let buttonParent = document.querySelector( 97 | '.inner-1JeGVc > div' 98 | ); 99 | 100 | if(!buttonParent) return; 101 | console.log('buttonparent'); 102 | let settings = this.loadSettings(); 103 | 104 | let button = document.createElement('a'); 105 | 106 | // This class gives the styling of the Open Original buttom 107 | // These will break every other update now it seems 108 | button.className = 109 | "anchor-3Z-8Bb downloadLink-1ywL9o size14-3iUx6q weightMedium-2iZe9B da-anchor da-downloadLink da-weightMedium"; 110 | 111 | button.id = "qs_button"; 112 | button.href = "#"; 113 | 114 | // Should the access be checked all the time like this? 115 | fs.access(settings.direcotry, fs.W_OK, (err)=>{ 116 | if (err){ 117 | button.innerHTML = 118 | "Can't Quicksave: Go to plugin settings and "+ 119 | "set the download directory!"; 120 | }else{ 121 | button.onclick = this.saveCurrentImage.bind(this); 122 | button.innerHTML = "Quicksave"; 123 | } 124 | 125 | buttonParent.appendChild(button); 126 | }); 127 | } 128 | } 129 | 130 | injectThumbIcons() { 131 | var fs = require('fs'); 132 | let list = document.querySelectorAll("img"); 133 | for (let i = 0; i < list.length; i++) { 134 | let elem = list[i].parentElement; 135 | //console.log(elem); 136 | 137 | if( !elem.href 138 | || !elem.classList.contains('imageWrapper-2p5ogY') 139 | || elem.querySelector('.thumbQuicksave') 140 | ) continue; 141 | 142 | let div = document.createElement('div'); 143 | div.innerHTML = "Save"; 144 | div.className = "thumbQuicksave"; 145 | 146 | let settings = this.loadSettings(); 147 | fs.access(settings.direcotry, fs.W_OK, (err)=>{ 148 | if (err) 149 | div.innerHTML = "Dir Error"; 150 | else 151 | div.onclick = (e)=>{ 152 | // Prevent parent from opening the image 153 | e.stopPropagation(); 154 | e.preventDefault(); 155 | 156 | this.saveThumbImage(e); 157 | }; 158 | 159 | // appendChild but as the first child 160 | elem.insertAdjacentElement('afterbegin', div); 161 | }); 162 | } 163 | 164 | // Originally this code was in mutationobserver, but that wasn't reliable. 165 | // Now we use this timeout loop with global img search. Not optimal but 166 | // works very well (and maybe even better perfomance wise?) 167 | this.injectionTimeout = setTimeout(this.injectThumbIcons.bind(this), 2000); 168 | } 169 | 170 | saveSettings (button) { 171 | var settings = this.loadSettings(); 172 | var dir = document.getElementById('qs_directory').value; 173 | 174 | var plugin = BdApi.getPlugin('Quicksave'); 175 | var err = document.getElementById('qs_err'); 176 | 177 | if(dir.slice(-1)!='/') dir+='/'; 178 | 179 | if( plugin.accessSync(dir) ){ 180 | 181 | settings.direcotry = dir; 182 | settings.fnLength = document.getElementById('qs_fnLength') .value; 183 | settings.namingmethod = document.getElementById('qs_namingmethod').value; 184 | 185 | bdPluginStorage.set(this.getName(), 'config', JSON.stringify(settings)); 186 | 187 | plugin.stop(); 188 | plugin.start(); 189 | 190 | err.innerHTML = ""; 191 | button.innerHTML = "Saved and applied!"; 192 | } else { 193 | err.innerHTML = "Error: Invalid directory!"; 194 | return; 195 | } 196 | setTimeout(function(){button.innerHTML = "Save and apply";},1000); 197 | } 198 | 199 | defaultSettings() { 200 | return { 201 | version: this.settingsVersion, 202 | direcotry: "none", 203 | namingmethod: "original", 204 | fnLength: 4 205 | }; 206 | } 207 | 208 | resetSettings(button) { 209 | var settings = this.defaultSettings(); 210 | bdPluginStorage.set(this.getName(), 'config', JSON.stringify(settings)); 211 | this.stop(); 212 | this.start(); 213 | button.innerHTML = "Settings resetted!"; 214 | setTimeout(function(){button.innerHTML = "Reset settings";},1000); 215 | } 216 | 217 | loadSettings() { 218 | // Loads settings from localstorage 219 | var settings = (bdPluginStorage.get(this.getName(), 'config')) ? 220 | JSON.parse( bdPluginStorage.get(this.getName(), 'config')) : 221 | {version:"0"}; 222 | 223 | if(settings.version != this.settingsVersion){ 224 | console.log( 225 | `[${this.getName()}] Settings were outdated/invalid/nonexistent.`+ 226 | ` Using default settings.` 227 | ); 228 | settings = this.defaultSettings(); 229 | bdPluginStorage.set(this.getName(), 'config', JSON.stringify(settings)); 230 | } 231 | return settings; 232 | } 233 | 234 | getSettingsPanel () { 235 | var settings = this.loadSettings(); 236 | // rip column rules 237 | var html = 238 | ` 239 |
Quicksave directory
241 |
242 |
243 |
File naming method
244 | 251 |