├── .eslintrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── License ├── README.md ├── images ├── 1.png └── 2.png ├── logo.png ├── package.json └── src └── index.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "standard" 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | .DS_Store 4 | yarn-error.log 5 | temp.js 6 | package-lock.json 7 | test.png 8 | test2.png -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | yarn-error.log 3 | temp.js 4 | package-lock.json 5 | tsconfig.json 6 | tslint.json 7 | .vscode/ 8 | src/ 9 | .travis.yml 10 | test.png 11 | test2.png 12 | images/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: stable 3 | 4 | # Travis-CI Caching 5 | cache: 6 | directories: 7 | - node_modules 8 | yarn: true 9 | 10 | # S: Build Lifecycle 11 | install: 12 | - yarn 13 | 14 | stages: 15 | - name: deploy 16 | 17 | jobs: 18 | include: 19 | - stage: deploy 20 | script: 21 | - npm run build 22 | deploy: 23 | provider: npm 24 | email: "" 25 | api_key: "${NPM_TOKEN}" 26 | skip_cleanup: true 27 | on: 28 | branch: master 29 | branches: 30 | only: 31 | - master 32 | -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 JolyneAnasui 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 | # picgo-plugin-squoosh 2 | 3 | 为[PicGo](https://github.com/Molunerfinn/PicGo)开发的插件 4 | 5 | * 使用[@squoosh/lib](https://github.com/GoogleChromeLabs/squoosh/tree/dev/libsquoosh)压缩图片,所有处理在本地执行,[详见](https://github.com/GoogleChromeLabs/squoosh#privacy) 6 | * 使用图片md5进行重命名 7 | 8 | ![](https://raw.githubusercontent.com/JolyneAnasui/picgo-plugin-squoosh/main/images/1.png) 9 | 10 | ## 配置 11 | 12 | * 初次使用前请进行配置 13 | 14 | ![](https://raw.githubusercontent.com/JolyneAnasui/picgo-plugin-squoosh/main/images/2.png) 15 | 16 | * `开`表示压缩相应扩展名的图片,反则反之 17 | 18 | * `开`表示使用图片md5进行重命名(与压缩相互独立) 19 | 20 | * 使用squoosh的默认压缩配置,若想定制请修改`index.js`里的`DefaultEncodeOptions` 21 | 22 | -------------------------------------------------------------------------------- /images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JolyneAnasui/picgo-plugin-squoosh/14a7a52ff48e3b1c2187bac3e7fe53ee68a22737/images/1.png -------------------------------------------------------------------------------- /images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JolyneAnasui/picgo-plugin-squoosh/14a7a52ff48e3b1c2187bac3e7fe53ee68a22737/images/2.png -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JolyneAnasui/picgo-plugin-squoosh/14a7a52ff48e3b1c2187bac3e7fe53ee68a22737/logo.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "picgo-plugin-squoosh", 3 | "version": "1.1.0", 4 | "description": "使用squoosh压缩图片", 5 | "main": "src/index.js", 6 | "publishConfig": { 7 | "access": "public" 8 | }, 9 | "homepage": "https://github.com/JolyneAnasui/picgo-plugin-squoosh", 10 | "scripts": { 11 | "test": "echo \"Error: no test specified\" && exit 1", 12 | "patch": "npm version patch && git push origin master && git push origin --tags", 13 | "minor": "npm version minor && git push origin master && git push origin --tags", 14 | "major": "npm version major && git push origin master && git push origin --tags" 15 | }, 16 | "keywords": [ 17 | "picgo", 18 | "picgo-gui-plugin", 19 | "picgo-plugin" 20 | ], 21 | "author": "JolyneAnasui", 22 | "license": "MIT", 23 | "devDependencies": { 24 | "eslint": "^5.0.1", 25 | "eslint-config-standard": "^11.0.0", 26 | "eslint-plugin-import": "^2.13.0", 27 | "eslint-plugin-node": "^6.0.1", 28 | "eslint-plugin-promise": "^3.8.0", 29 | "eslint-plugin-standard": "^3.1.0" 30 | }, 31 | "dependencies": { 32 | "@squoosh/lib": "^0.4.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const {ImagePool, encoders} = require('@squoosh/lib'); 2 | const crypto = require('crypto'); 3 | 4 | const pluginConfig = ctx => { 5 | let userConfig = ctx.getConfig('picgo-plugin-squoosh'); 6 | if (!userConfig) { 7 | userConfig = {}; 8 | } 9 | return [ 10 | {name: 'md5-rename', type: 'confirm', alias: 'md5-rename'}, 11 | {name: '.jpg', type: 'confirm', alias: 'jpg'}, 12 | {name: '.png', type: 'confirm', alias: 'png'}, 13 | {name: '.webp', type: 'confirm', alias: 'webp'}, 14 | {name: '.avif', type: 'confirm', alias: 'avif'}, 15 | {name: '.jxl', type: 'confirm', alias: 'jxl'}, 16 | {name: '.wp2', type: 'confirm', alias: 'wp2'}, 17 | ]; 18 | }; 19 | 20 | const DefaultEncodeOptions = Object.fromEntries( 21 | Object.entries(encoders).map(([key, encoder]) => { 22 | const extension = `.${encoder.extension}`; 23 | return [extension, Object.fromEntries([[key, {}]])]; 24 | }) 25 | ); 26 | // DefaultEncodeOptions = { 27 | // '.jpg': { mozjpeg: {} }, 28 | // '.webp': { webp: {} }, 29 | // '.avif': { avif: {} }, 30 | // '.jxl': { jxl: {} }, 31 | // '.wp2': { wp2: {} }, 32 | // '.png': { oxipng: {} } 33 | // }; 34 | // reference: https://github.com/pekeq/gulp-libsquoosh/blob/main/index.js#L28 35 | // default settings: https://github.com/GoogleChromeLabs/squoosh/blob/dev/libsquoosh/src/codecs.ts#L284 36 | 37 | const handle = async (ctx) => { 38 | ctx.log.info('**** squoosh begin here ****') 39 | let t0 = new Date(); 40 | 41 | const userConfig = ctx.getConfig('picgo-plugin-squoosh'); 42 | if (!userConfig) throw new Error('picgo-plugin-squoosh config not found'); 43 | const settings = ctx.getConfig('settings'); 44 | if (userConfig['md5-rename'] && (settings.rename || settings.autoRename)) throw new Error('rename method conflict'); 45 | 46 | let imagePool = new ImagePool(); 47 | 48 | const jobs = ctx.output.map(async outputi => { 49 | try { 50 | if (userConfig[outputi.extname]) { 51 | let t = new Date(); 52 | let b = outputi.buffer; 53 | let ab = b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength); 54 | let image = imagePool.ingestImage(ab); 55 | let originSize = Math.round(b.byteLength / 1024); 56 | ctx.log.info(`Compressing ${outputi.fileName} ${originSize} kb`); 57 | 58 | await image.encode(DefaultEncodeOptions[outputi.extname]); 59 | let encoded = await Object.values(image.encodedWith)[0]; 60 | outputi.buffer = Buffer.from(encoded.binary); 61 | 62 | let newSize = Math.round(encoded.size / 1024); 63 | let ratio = Math.round(newSize / originSize * 100); 64 | ctx.log.success(`Finish ${outputi.fileName} ${newSize} kb ${ratio}% ${new Date().getTime() - t.getTime()} ms`); 65 | }// else throw new Error('extname debug error'); 66 | if (userConfig['md5-rename']) { 67 | let hash = crypto.createHash('md5'); 68 | hash.update(outputi.buffer); 69 | let originName = outputi.fileName; 70 | outputi.fileName = hash.digest('hex') + outputi.extname; 71 | ctx.log.info(`${originName} -> ${outputi.fileName}`); 72 | } 73 | return outputi; 74 | } catch (err) { 75 | ctx.emit('notification', { 76 | title: `${outputi.fileName} compression error`, 77 | body: err 78 | }); 79 | ctx.log.error(`${outputi.fileName} compression error`); 80 | ctx.log.error(err); 81 | } 82 | }); 83 | 84 | ctx.output = await Promise.all(jobs); 85 | ctx.output = ctx.output.filter(Boolean); 86 | imagePool.close(); 87 | 88 | ctx.log.info(`**** squoosh end here ${new Date().getTime() - t0.getTime()} ms ****`) 89 | return ctx; 90 | } 91 | 92 | module.exports = (ctx) => { 93 | const register = () => { 94 | ctx.helper.beforeUploadPlugins.register('squoosh', { 95 | handle, 96 | }) 97 | } 98 | return { 99 | register, 100 | config: pluginConfig, 101 | } 102 | } 103 | --------------------------------------------------------------------------------