Replies: 10 comments
-
|
下面是之前的代码,并没有查询、分类等功能,几乎只能简单的记录 Py 脚本
import os
import json
import time
import shutil
import html
import hashlib
from datetime import datetime
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
# 配置路径(根据实际环境修改)
SYNC_FILE = "SyncClipboard.json" # 主同步文件路径
HISTORY_FILE = "clipboard_history.json" # 历史记录文件
HISTORY_IMAGES_DIR = "history_images" # 历史图片存储目录
FILE_DIR = "file" # 原始图片存储目录
HTML_FILE = "clipboard_history.html" # 生成的网页文件
HISTORY_FILES_DIR = "history_files" # 新增历史文件夹
os.makedirs(HISTORY_IMAGES_DIR, exist_ok=True)
os.makedirs(HISTORY_FILES_DIR, exist_ok=True)
def file_md5(path):
hash_md5 = hashlib.md5()
with open(path, "rb") as f:
for chunk in iter(lambda: f.read(4096), b""):
hash_md5.update(chunk)
return hash_md5.hexdigest()
class ClipboardHandler(FileSystemEventHandler):
def __init__(self):
self.last_content = None
def on_modified(self, event):
if os.path.basename(event.src_path) == SYNC_FILE:
self.process_clipboard()
def process_clipboard(self):
try:
with open(SYNC_FILE, "r", encoding="utf-8") as f:
content = f.read().strip()
if not content or content == self.last_content:
return
self.last_content = content
data = json.loads(content)
timestamp = datetime.now().isoformat()
# 备份图片或文件
if data["Type"] in ("Image", "File") and data["File"]:
src_path = os.path.join(FILE_DIR, data["File"])
dst_path = os.path.join(HISTORY_FILES_DIR, data["File"])
history_file = data["File"]
if os.path.exists(src_path):
# 判断是否已存在且内容相同
if os.path.exists(dst_path):
if file_md5(src_path) == file_md5(dst_path):
# 内容相同,直接用原有文件名
history_file = data["File"]
else:
# 内容不同,重命名备份
name, ext = os.path.splitext(data["File"])
new_name = f"{name}_{int(time.time())}{ext}"
dst_path = os.path.join(HISTORY_FILES_DIR, new_name)
shutil.copy2(src_path, dst_path)
history_file = new_name
else:
shutil.copy2(src_path, dst_path)
history_file = data["File"]
data["HistoryFile"] = history_file # 关键:每次都赋值
# 添加到历史记录
history_entry = {
"timestamp": timestamp,
"data": data
}
self.save_to_history(history_entry)
self.generate_html()
except Exception as e:
print(f"处理错误: {str(e)}")
def save_to_history(self, entry):
history = []
if os.path.exists(HISTORY_FILE):
try:
with open(HISTORY_FILE, "r", encoding="utf-8") as f:
history = json.load(f)
except:
pass
history.append(entry)
with open(HISTORY_FILE, "w", encoding="utf-8") as f:
json.dump(history, f, ensure_ascii=False, indent=2)
def generate_html(self):
if not os.path.exists(HISTORY_FILE):
return
with open(HISTORY_FILE, "r", encoding="utf-8") as f:
history = json.load(f)
history.reverse()
html_content = f"""
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>剪贴板历史记录</title>
<style>
body {{ font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }}
.entry {{ border: 1px solid #ddd; border-radius: 5px; padding: 15px; margin-bottom: 15px; }}
.timestamp {{ color: #666; font-size: 0.9em; margin-bottom: 5px; }}
.text-content {{ white-space: pre-wrap; background-color: #f9f9f9; padding: 10px; border-radius: 4px; }}
.image-content {{ max-width: 100%; margin-top: 10px; }}
.type-label {{ display: inline-block; padding: 2px 6px; background: #eee; border-radius: 3px; font-size: 0.8em; }}
.download-btn {{ display: inline-block; margin-top: 10px; padding: 8px 12px; background: #007bff; color: white; text-decoration: none; border-radius: 4px; }}
.download-btn:hover {{ background: #0056b3; }}
</style>
<!-- 引入 marked.js 用于 Markdown 渲染 -->
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
</head>
<body>
<h1>剪贴板历史记录</h1>
<div id="history">
"""
for idx, entry in enumerate(history):
data = entry["data"]
timestamp = datetime.fromisoformat(entry["timestamp"]).strftime("%Y-%m-%d %H:%M:%S")
type_label = f'<span class="type-label">{data["Type"]}</span>'
content_html = ""
if data["Type"] == "Text":
text_content = data["Clipboard"]
# 简单判断格式
if text_content.strip().startswith("<") and text_content.strip().endswith(">"):
# 可能是 HTML
content_html = f'<div class="text-content">{text_content}</div>'
elif any(sym in text_content for sym in ['#', '*', '-', '`', '[', ']']):
# 可能是 Markdown,前端渲染
content_html = f'<div class="text-content" id="md-{idx}">{html.escape(text_content)}</div>'
content_html += f"""
<script>
document.getElementById('md-{idx}').innerHTML = marked.parse(document.getElementById('md-{idx}').innerText);
</script>
"""
else:
# 普通文本
content_html = f'<div class="text-content">{html.escape(text_content)}</div>'
elif data["Type"] in ("Image", "File") and data.get("HistoryFile"):
file_path = os.path.join(HISTORY_FILES_DIR, data["HistoryFile"])
file_name = os.path.basename(data["HistoryFile"])
if data["Type"] == "Image":
content_html = (
f'<img src="{file_path}" class="image-content" alt="剪贴板图片"><br>'
f'<a href="{file_path}" download class="download-btn">{file_name} 下载图片</a>'
)
else:
content_html = (
f'<a href="{file_path}" download class="download-btn">{file_name} 下载文件</a>'
)
html_content += f"""
<div class="entry">
<div class="timestamp">{timestamp} {type_label}</div>
{content_html}
</div>
"""
html_content += """
</div>
</body>
</html>
"""
with open(HTML_FILE, "w", encoding="utf-8") as f:
f.write(html_content)
def main():
event_handler = ClipboardHandler()
observer = Observer()
observer.schedule(event_handler, path=".", recursive=False)
observer.start()
print(f"监控已启动,剪贴板历史将保存到: {HISTORY_FILE}")
print(f"网页界面: {HTML_FILE}")
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()
if __name__ == "__main__":
# 首次运行生成HTML
handler = ClipboardHandler()
handler.generate_html()
main() |
Beta Was this translation helpful? Give feedback.
-
|
添加一个使用说明: clipboard_history.html渲染后如下图: 将以上网页通过任意web服务提供访问即可。 |
Beta Was this translation helpful? Give feedback.
-
|
借助AI实现想要的功能,很厉害,很有行动力,给你点赞 |
Beta Was this translation helpful? Give feedback.
-
已经完成了基本的数据库与监控部分,发现一个问题,似乎在我复制文件的时候,它的clipboard值是MD5值吗?如果是的话,我就无需重新计算一遍了 |
Beta Was this translation helpful? Give feedback.
-
|
单个文件的话,是的 |
Beta Was this translation helpful? Give feedback.
-
|
??多个文件居然还有一个 group 的类型,,疏忽了 请问多个文件的情况下,我有方法去获取他的包含了哪些文件的内容吗? 同时我想知道我可以在哪里查看到 json 定义的所有可能键值吗? |
Beta Was this translation helpful? Give feedback.
-
暂时没有办法,因为我不是web前/后端的专业开发者,并且为了足够简单,当前API定义的十分简陋
在桌面客户端打开服务器开关并开启设置里的诊断模式后,访问/swagger/index.html |
Beta Was this translation helpful? Give feedback.
-
|
完蛋,我看不懂这个诊断模式[笑哭],我尝试研究研究吧。 感谢大佬 |
Beta Was this translation helpful? Give feedback.
-
|
目前有一个初步的版本,尝试一下打包,结果几次都是失败的 https://github.com/youli42/SyncClipboardWebHistory 可以监控和记录历史记录,以及提供文件和截图的历史下载,前提是使用 webdav 的同步方式 |
Beta Was this translation helpful? Give feedback.
-
请问这两个自定义字段的功能可以添加吗?如果有这种功能之后检索就方便多了,非常感谢大佬。 |
Beta Was this translation helpful? Give feedback.



Uh oh!
There was an error while loading. Please reload this page.
-
描述你想添加的功能 | Describe the feature you'd like
并不是希望将剪贴板历史记录集成到该应用,而是希望开发一个关联的软件。
具体来说,使用webdav同步剪贴板后,发现剪贴板以json的形式直接储存,于是诞生了“使用一个应用程序监控json,并储存历史记录,方便查阅“的想法。
但是很显然,我几乎没有任何开发经验,目前只能借助AI,学习进度缓慢。下面会放一个py脚本示例,是一个勉强可用的脚本,将监控历史记录,并生成一个网页,方便webdav直接通过浏览器查看。此后会尝试完善,但是py并不适合长期使用。
只希望可以抛砖引玉
同时,希望可以在json中添加自定义字段:"From":"name", "Tag":"string",From记录设备来源,Tag允许在上传时添加自定义标签,方便查询
描述你正在使用的替代方案 | Describe alternatives you are using
No response
Beta Was this translation helpful? Give feedback.
All reactions