diff --git a/tinypng/Dockerfile b/tinypng/Dockerfile new file mode 100644 index 0000000..68e2fd3 --- /dev/null +++ b/tinypng/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.7-alpine +LABEL org.opencontainers.image.authors="stille@ioiox.com" + +WORKDIR / + +ADD . / + +RUN pip install --no-cache-dir tinify + +ENTRYPOINT ["/bin/sh", "-c", "/tinypng.sh"] diff --git a/tinypng/README.md b/tinypng/README.md new file mode 100644 index 0000000..3ef2aa9 --- /dev/null +++ b/tinypng/README.md @@ -0,0 +1,28 @@ +# tinypng + +GitHub [stilleshan/dockerfiles](https://github.com/stilleshan/dockerfiles) +Docker [stilleshan/tinypng](https://hub.docker.com/r/stilleshan/tinypng) +> *docker image support for X86 and ARM* + +## 简介 +使用 TinyPNG 免费压缩图片 docker 小程序,方便本地批量压缩,支持多个 **API key** 配置。 + +> [TinyPNG](https://tinypng.com) 是一个非常强大,高画质,高压缩比的在线压缩图片的网站,并提供免费的 API key 用于本地批量压缩。 + +## 使用 +### 获取 API key +访问 [TinyPNG Developer API](https://tinypng.com/developers) 获取免费 API key,免费用户每个 key 每月可以压缩 500 张图片,可以创建多个免费账户来获取多个 key 配合本程序使用。 + +### 准备目录及 key 文件 +1. 在需要压缩照片的目录中创建`api_key.txt`并将`key`粘贴进去,多个`key`时,每行一个`key`。 +2. 确保该目录下没有`Output`文件夹,`Output`文件夹是由本 docker 压缩图片下载后生成。 +3. 图片支持 jpg / jpeg / png 格式,并支持多层子目录。 + +### docker +```shell +docker run --rm -v /your/pic/path:/pic stilleshan/tinypng +# 修改 /your/pic/path 为需要压缩的图片目录 +``` + +## 参考 +- Python 来源于 [haoma2012/PythonProject](https://github.com/haoma2012/PythonProject) diff --git a/tinypng/tinypng.py b/tinypng/tinypng.py new file mode 100755 index 0000000..39cd0f3 --- /dev/null +++ b/tinypng/tinypng.py @@ -0,0 +1,113 @@ +# -*- coding:utf-8 -*- +# !/usr/bin/env python3 +# 使用tinypng API压缩项目图片 +import tinify +import os +import time +import shutil + +# tinify.key ="Your Developer API Key" # AppKey +# 设置代理 +# tinify.proxy = "" + +os.mkdir("/Output") + +# 压缩图片的key +online_key_list = [ + "NtkTWRRNjqfF8qllPlrt6hMMHb3Wbb24", # 可以继续添加 防止一个key不够 +] + +# 获取key +online_key_list_iter = iter(online_key_list) +online_key = next(online_key_list_iter) + +# 需要压缩图片的路径 +fromPath = "/pic" # source dir path +# 压缩后下载的图片路径 +toPath = "/Output" # dest dir path + + +tinifyAPi = tinify.tinify + + +# 在线压缩 +def compress_online(sourcefile, outputfile): + global online_key + compresskey = online_key + + tinify.key = compresskey + rs = False + try: + source = tinifyAPi.from_file(sourcefile) + source.to_file(outputfile) + print('压缩图片...' + outputfile) + rs = True + pass + except tinify.AccountError: + # Verify your API key and account limit. + # 如果key值无效 换一个key继续压缩 + print("key值无效 换一个继续。。。") + online_key = next(online_key_list_iter) + compress_online(sourcefile, outputfile, name) # 递归方法 继续读取 + rs = True + + except tinify.ClientError: + # Check your source image and request options. + print("Check your source image and request options.") + rs = False + pass + except tinify.ServerError: + # Temporary issue with the Tinify API. + # print("Temporary issue with the Tinify API. %s" % e.message) + print("Temporary issue with the Tinify API.") + + rs = False + pass + except tinify.ConnectionError: + # A network connection error occurred. + print("网络故障。。。休息1秒继续") + time.sleep(1) + compress_online(sourcefile, outputfile, name) # 递归方法 继续读取 + rs = True + pass + except Exception: + # Something else went wrong, unrelated to the Tinify API. + print("Something else went wrong, unrelated to the Tinify API.") + rs = False + pass + + return rs + + +# root, dirs, files参数的含义:目录的路径(String);root目录下所有子目录的名字(List);root目录下非目录的名字 +# os.walk(top,topdown=True,onerror=None)函数中各参数的含义:需要遍历的顶级目录的路径;默认值是“True”表示首先返回顶级目录下的文件,然后再遍历子目录中的文件;默认值为"None",表示忽略文件遍历时的错误 +for root, dirs, files in os.walk(fromPath): + newToPath = toPath + if len(root) > len(fromPath): # 比较root和fromPath的字符长度 + innerPath = root[len(fromPath):] # 字符串切割,将root字符串从第len(fromPath)个位置开始截取,不包括len(fromPath)这个位置的字符 + if innerPath[0] == '/': # 判断innerPath的第一个字符是不是/符号 + innerPath = innerPath[1:] # 字符串切割,例如innerPath的值为\test,那么innerPath[1:]之后的值为test + newToPath = os.path.join(toPath, innerPath) # 将toPath目录和innerPath文件或文件夹拼接之后的路径赋值给newToPath + + for name in files: + newFromFilePath = os.path.join(root, name) + newToFilePath = os.path.join(newToPath, name) + fileName, fileSuffix = os.path.splitext(name) # 分解文件名的扩展名 + if fileSuffix == '.png' or fileSuffix == '.jpg' or fileSuffix == '.jpeg' or fileSuffix == '.PNG' or fileSuffix == '.JPG' or fileSuffix == '.JPEG': + # source = tinify.from_file(newFromFilePath) + # source.to_file(newToFilePath) + # 在线压缩 + if not compress_online(newFromFilePath, newToFilePath): + print("压缩失败,检查报错信息") + exit() + pass + else: + pass + + for dirName in dirs: + if os.path.exists(os.path.join(newToPath, dirName)): + pass + else: + os.mkdir(os.path.join(newToPath, dirName)) + +shutil.move("/Output","/pic") diff --git a/tinypng/tinypng.sh b/tinypng/tinypng.sh new file mode 100755 index 0000000..974d6e4 --- /dev/null +++ b/tinypng/tinypng.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +# fonts color +Green="\033[32m" +Red="\033[31m" +Yellow="\033[33m" +GreenBG="\033[42;37m" +RedBG="\033[41;37m" +Font="\033[0m" +# fonts color + +if [ ! -d /pic ]; then + echo -e "${Red}未挂载目录,请重新执行.${Font}" + exit 0 +fi + +if [ -d /pic/Output ]; then + echo -e "${Red}Output 目录已存在,当前已暂停执行.${Font}" + echo -e "${Red}请将 Output 目录移除或备份至其他目录.${Font}" + echo -e "${Red}否则将会导致重复压缩已输出图片,浪费 API 次数.${Font}" + exit 0 +fi + +if [ -f /pic/api_key.txt ]; then + sed -i '17d' /tinypng.py + LINE=17 + for APIKEY in $(cat /pic/api_key.txt) + do + sed -i "${LINE}i\ \"${APIKEY}\"," /tinypng.py + LINE=$(($LINE+1)) + done +fi + +python /tinypng.py + +if [ ! -f /pic/api_key.txt ]; then + echo -e "${Red}未检测到 api_key.txt${Font}" + echo -e "${Red}已使用内置公开的 key 压缩图片,由于额度有限,图片压缩有可能失败.${Font}" + echo -e "${Green}建议自行免费申请 API key 配置使用更加稳定.${Font}" +fi