项目地址
DefensiveSniper/Amateur
此项目仅为蒟蒻本人学习、接触知识而随手创建的,目前没有任何实际意义。
Flask
打包flask
pyinstaller --onefile --noconsole --name=flask_server --add-data "templates;templates" --add-data "static;static" --add-data "func;func" --add-data "base;base" --add-data "libs;libs" --add-data "cache;cache" --add-data "tools;tools" --collect-binaries azure.cognitiveservices.speech --collect-all dns --collect-all eventlet flask_server.py
或者:
pyi-makespec flask_server.py
pyinstaller flask_server.spec
在 flask_server.spec
里面添加参数
解释:
--onefile
:打包成一个独立的文件--noconsole
:隐藏终端窗口,避免黑框弹出--add-data "templates;templates"
:添加数据,防止找不到关联文件--name=flask_server
:生成flask_server.exe
成功后,dist/
目录下会生成 flask_server.exe
Electron
打包 Electron
安装 Electron 及构建工具
npm install
npm install -g electron electron-builder wait-on concurrently
修改 package.json
在 package.json
里,添加:
"build": {
"appId": "com.yourapp.id",
"productName": "AmateurApp",
"win": {
"target": "nsis",
"icon": "static/images/logo.ico"
}
}
执行打包,onlyWindows
electron-builder --win --x64
ps:记得改版本号
运行
npm run start
ps:记得在main.js中变动调试代码
笔记
前端FormData
内容的获取
表单用 FormData
时,只会收集带有 name
属性的表单项,和 id
没关系。
<form id="douyin-download-form" style="display: flex; flex-direction: column; gap: 22px;">
<label>
<span style="font-weight:500;">sec_user_id:</span>
<input type="text" name="sec_user_id" id="sec_user_id" class="input-field" placeholder="请输入sec_user_id" required>
</label>
</form>
const formData = new FormData(form);
const data = Object.fromEntries(formData.entries());
若<input>
中的name
属性缺失,那么data
的数据则为空
eventlet
猴子补丁
保证在monkey_patch
之后才import
其它内容,否则requests、threading、socketio、Flask
内部依赖的socket
等都无法被eventlet
控制。
猴子补丁一定要在最最最最前面打,不然所有异步、websocket、定时操作全都可能崩溃。
import eventlet
eventlet.monkey_patch()
import .....
request请求返回的三种结构
response.text
是 字符串,拿到的是网页或接口响应的“文本内容”。
response.json()
是直接解析成 Python 的字典/列表(前提是返回内容本身就是标准 JSON 格式)。
response.content
是 二进制内容,比如图片、文件等。
BUG
PyInstaller打包
临时目录问题
File "func\get_a_bogus.py", line 11, in <module>
douyin_sign_obj = execjs.compile(open('libs/douyin.js', encoding='utf-8-sig').read())
FileNotFoundError: [Errno 2] No such file or directory: 'libs/douyin.js'
在 --onefile
模式下,所有文件是打包到一个 .exe
中的,运行时会被解压到临时目录(sys._MEIPASS
),而不是在真实磁盘上的 libs/
路径中。
在 .exe
中运行时,找的是 当前目录下的 libs/douyin.js
,但它实际在临时目录中,所以失败了。
import sys
import os
def resource_path(relative_path):
if hasattr(sys, '_MEIPASS'):
# 打包后运行,从临时目录加载
return os.path.join(sys._MEIPASS, relative_path)
return os.path.abspath(relative_path)
douyin_js_path = resource_path("libs/douyin.js")
with open(douyin_js_path, encoding='utf-8-sig') as f:
douyin_sign_obj = execjs.compile(f.read())
ValueError: Invalid async_mode specified
Flask-SocketIO
在初始化时会检查 async_mode
,如果你没有指定,它会自动选择一个可用的。但在 PyInstaller 打包后,如果某些异步库(如 eventlet
或 gevent
)未被正确包含或安装,就会导致这个错误。
在stack overflow中佬的回答解决了这个问题,即在打包的py文件中加入:
from engineio.async_drivers import gevent,eventlet
AttributeError: ‘NoneType’ object has no attribute ‘write’
这个错误的根本原因是:eventlet.wsgi.server()
的 log
参数为 None
,而它内部却尝试调用 log.write(...)
eventlet.wsgi.server()
中有一个 log
参数,用于记录 WSGI 启动/退出等日志。
- 如果你没有传入这个参数(或 PyInstaller 打包后出了问题),
serv.log
会是None
; - 然后程序调用了
serv.log.write(...)
,于是就报错了。
将
socketio.run(app, port=6969, log_output=True)
#或者
socketio.run(app, port=6969, log=sys.stdout)
改为
# 安全日志对象(兼容 eventlet)
logger = logging.getLogger("eventlet")
logger.setLevel(logging.INFO)
logger.addHandler(logging.StreamHandler())
# 使用底层的 eventlet 方式启动,显式传 log
import eventlet
import eventlet.wsgi
listener = eventlet.listen(('0.0.0.0', 6969))
eventlet.wsgi.server(listener, app, log=logger)
日志
2025.2.6 AmateurApp1.0.0
2025.2.6 AmateurApp1.0.1 修复了应用伪关闭的问题
2025.2.7 AmateurApp1.1.0 添加了AI交流,支持DeepSeek和OPENAI的多种模型
2025.2.8 AmateurApp1.1.2 在ai交流页面添加“新聊天”按钮,优化“配置”设置
2025.2.16 AmateurApp1.1.3 添加了AI交流上下文
2025.7.7 AmateurApp1.1.4 添加了聊天记录功能
2025.7.24 AmateurApp1.1.5 添加了抖音主页视频下载功能,优化了UI,修复了一些BUG(使用Trae SOLO模式)不过AI交流的流式输出有些问题
2025.8.6 AmateurApp1.1.6 添加了抖音分享链接视频下载功能
2025.8.15 对项目进行重构,去除electron,保留flask和前端,使用tauri构建桌面