nonebot-plugin-randpic 2.0.1__tar.gz → 2.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (18) hide show
  1. {nonebot_plugin_randpic-2.0.1 → nonebot_plugin_randpic-2.0.2}/PKG-INFO +3 -3
  2. {nonebot_plugin_randpic-2.0.1 → nonebot_plugin_randpic-2.0.2}/README.md +2 -2
  3. {nonebot_plugin_randpic-2.0.1 → nonebot_plugin_randpic-2.0.2}/nonebot_plugin_randpic/ali_oss.py +1 -1
  4. {nonebot_plugin_randpic-2.0.1 → nonebot_plugin_randpic-2.0.2}/nonebot_plugin_randpic/service.py +2 -3
  5. {nonebot_plugin_randpic-2.0.1 → nonebot_plugin_randpic-2.0.2}/nonebot_plugin_randpic/web.py +7 -18
  6. {nonebot_plugin_randpic-2.0.1 → nonebot_plugin_randpic-2.0.2}/pyproject.toml +1 -1
  7. {nonebot_plugin_randpic-2.0.1 → nonebot_plugin_randpic-2.0.2}/LICENSE +0 -0
  8. {nonebot_plugin_randpic-2.0.1 → nonebot_plugin_randpic-2.0.2}/nonebot_plugin_randpic/__init__.py +0 -0
  9. {nonebot_plugin_randpic-2.0.1 → nonebot_plugin_randpic-2.0.2}/nonebot_plugin_randpic/compress.py +0 -0
  10. {nonebot_plugin_randpic-2.0.1 → nonebot_plugin_randpic-2.0.2}/nonebot_plugin_randpic/config.py +0 -0
  11. {nonebot_plugin_randpic-2.0.1 → nonebot_plugin_randpic-2.0.2}/nonebot_plugin_randpic/drivers/fastapi.py +0 -0
  12. {nonebot_plugin_randpic-2.0.1 → nonebot_plugin_randpic-2.0.2}/nonebot_plugin_randpic/handlers.py +0 -0
  13. {nonebot_plugin_randpic-2.0.1 → nonebot_plugin_randpic-2.0.2}/nonebot_plugin_randpic/journal.py +0 -0
  14. {nonebot_plugin_randpic-2.0.1 → nonebot_plugin_randpic-2.0.2}/nonebot_plugin_randpic/library.py +0 -0
  15. {nonebot_plugin_randpic-2.0.1 → nonebot_plugin_randpic-2.0.2}/nonebot_plugin_randpic/models.py +0 -0
  16. {nonebot_plugin_randpic-2.0.1 → nonebot_plugin_randpic-2.0.2}/nonebot_plugin_randpic/progress.py +0 -0
  17. {nonebot_plugin_randpic-2.0.1 → nonebot_plugin_randpic-2.0.2}/nonebot_plugin_randpic/repository.py +0 -0
  18. {nonebot_plugin_randpic-2.0.1 → nonebot_plugin_randpic-2.0.2}/nonebot_plugin_randpic/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nonebot-plugin-randpic
3
- Version: 2.0.1
3
+ Version: 2.0.2
4
4
  Summary: 一个通过指令随机发送你存储的图片的nonebot2插件
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -105,7 +105,7 @@ Bot 运行期间请通过指令增删图片,直接修改文件需重启才能
105
105
 
106
106
  使用 FastAPI 驱动时,Bot 启动后即可通过 `http://<host>:<port>/randpic/` 查看图库,无需配置 OSS。默认每页 100 张图片,可通过 `randpic_web_page_size` 调整。图片使用 `loading="lazy"`,按需加载。
107
107
 
108
- 增删图片后本地页面立即重新发布;已打开的网页在可见时每 15 秒检查一次目录版本,发现变化会自动刷新。没有 FastAPI 服务的驱动仍可使用聊天指令和 OSS 同步。
108
+ 增删图片后本地页面立即重新发布,浏览器手动刷新即可查看最新内容。没有 FastAPI 服务的驱动仍可使用聊天指令和 OSS 同步。
109
109
 
110
110
  #### 阿里云 OSS
111
111
 
@@ -122,7 +122,7 @@ Bot 运行期间请通过指令增删图片,直接修改文件需重启才能
122
122
 
123
123
  启动、添加和删除后会自动同步;`上传oss` 可手动全量重新发布、重试未完成任务。默认最多 16 个并发请求,普通终端中以单行动态进度显示完成数、百分比和成功/失败数,最多每秒刷新 5 次;其他插件的默认 NoneBot 日志显示在进度行上方。同步结束后清除动态行,只保留一条结果汇总,错误详情仍单独记录。输出重定向、非交互控制台或已移除 NoneBot 默认日志处理器时仅输出汇总。
124
124
 
125
- 失败任务存入数据库,后台约每 30 秒重试,重启后继续处理。OSS 的图片、页面和目录版本依次同步,网络故障期间云端可能暂时落后于本地图。`randpic_oss_no_upload_list` 只影响 OSS,本地网页仍显示所有配置的分类;此前已同步的分类被排除后,其受本插件跟踪的云端对象会清理。未被数据库跟踪的历史对象不保证自动清理。
125
+ 失败任务存入数据库,后台约每 30 秒重试,重启后继续处理。OSS 的图片和页面依次同步,网络故障期间云端可能暂时落后于本地图。`randpic_oss_no_upload_list` 只影响 OSS,本地网页仍显示所有配置的分类;此前已同步的分类被排除后,其受本插件跟踪的云端对象会清理。未被数据库跟踪的历史对象不保证自动清理。
126
126
 
127
127
  ## 💿 安装
128
128
 
@@ -74,7 +74,7 @@ Bot 运行期间请通过指令增删图片,直接修改文件需重启才能
74
74
 
75
75
  使用 FastAPI 驱动时,Bot 启动后即可通过 `http://<host>:<port>/randpic/` 查看图库,无需配置 OSS。默认每页 100 张图片,可通过 `randpic_web_page_size` 调整。图片使用 `loading="lazy"`,按需加载。
76
76
 
77
- 增删图片后本地页面立即重新发布;已打开的网页在可见时每 15 秒检查一次目录版本,发现变化会自动刷新。没有 FastAPI 服务的驱动仍可使用聊天指令和 OSS 同步。
77
+ 增删图片后本地页面立即重新发布,浏览器手动刷新即可查看最新内容。没有 FastAPI 服务的驱动仍可使用聊天指令和 OSS 同步。
78
78
 
79
79
  #### 阿里云 OSS
80
80
 
@@ -91,7 +91,7 @@ Bot 运行期间请通过指令增删图片,直接修改文件需重启才能
91
91
 
92
92
  启动、添加和删除后会自动同步;`上传oss` 可手动全量重新发布、重试未完成任务。默认最多 16 个并发请求,普通终端中以单行动态进度显示完成数、百分比和成功/失败数,最多每秒刷新 5 次;其他插件的默认 NoneBot 日志显示在进度行上方。同步结束后清除动态行,只保留一条结果汇总,错误详情仍单独记录。输出重定向、非交互控制台或已移除 NoneBot 默认日志处理器时仅输出汇总。
93
93
 
94
- 失败任务存入数据库,后台约每 30 秒重试,重启后继续处理。OSS 的图片、页面和目录版本依次同步,网络故障期间云端可能暂时落后于本地图。`randpic_oss_no_upload_list` 只影响 OSS,本地网页仍显示所有配置的分类;此前已同步的分类被排除后,其受本插件跟踪的云端对象会清理。未被数据库跟踪的历史对象不保证自动清理。
94
+ 失败任务存入数据库,后台约每 30 秒重试,重启后继续处理。OSS 的图片和页面依次同步,网络故障期间云端可能暂时落后于本地图。`randpic_oss_no_upload_list` 只影响 OSS,本地网页仍显示所有配置的分类;此前已同步的分类被排除后,其受本插件跟踪的云端对象会清理。未被数据库跟踪的历史对象不保证自动清理。
95
95
 
96
96
  ## 💿 安装
97
97
 
@@ -44,7 +44,7 @@ class OSSUploaderV2:
44
44
  result = await asyncio.wait_for(self.client.put_object(oss.PutObjectRequest(
45
45
  bucket=self.bucket, key=oss_key, body=data,
46
46
  content_type=mimetypes.guess_type(oss_key)[0] or 'application/octet-stream',
47
- cache_control='no-cache' if oss_key.endswith(('.html', '.json')) else 'public, max-age=31536000, immutable',
47
+ cache_control='no-cache' if oss_key.endswith('.html') else 'public, max-age=31536000, immutable',
48
48
  )), timeout=60)
49
49
  if 200 <= result.status_code < 300:
50
50
  return True
@@ -226,10 +226,9 @@ class RandpicService:
226
226
 
227
227
  await asyncio.gather(*(worker() for _ in range(min(self.config.randpic_upload_concurrency, len(items)))))
228
228
 
229
- # 先同步图片,再同步页面,最后更新用于浏览器自动刷新的目录版本。
230
- await phase([job for job in jobs if not job.key.endswith(('.html', '.json'))])
229
+ # 先同步图片,再同步页面。
230
+ await phase([job for job in jobs if not job.key.endswith('.html')])
231
231
  await phase([job for job in jobs if job.key.endswith('.html')])
232
- await phase([job for job in jobs if job.key.endswith('.json')])
233
232
  pending = len(await run_blocking(self.repository.sync_jobs))
234
233
  elapsed = asyncio.get_running_loop().time() - started
235
234
  summary = (f'OSS 同步完成:成功 {succeeded},失败 {failed},待处理 {pending},'
@@ -1,7 +1,6 @@
1
- """静态网页发布:原子替换文件、分页、懒加载,以及增删后的自动刷新。"""
1
+ """静态网页发布:原子替换文件、分页与图片懒加载。"""
2
2
 
3
3
  import hashlib
4
- import json
5
4
  import math
6
5
  from html import escape
7
6
  from pathlib import Path
@@ -67,8 +66,7 @@ def _url(image: ImageRecord) -> str:
67
66
  return quote(image.filename, safe='') + '?v=' + image.content_hash[:16]
68
67
 
69
68
 
70
- def _shell(title: str, home: str, version: str, command: str, page: int, content: str) -> str:
71
- state = json.dumps({'version': version, 'command': command, 'page': page}, ensure_ascii=False).replace('<', '\\u003c')
69
+ def _shell(title: str, home: str, content: str) -> str:
72
70
  return f'''<!doctype html><html lang="zh-CN"><head><meta charset="utf-8">
73
71
  <meta name="viewport" content="width=device-width,initial-scale=1"><meta name="theme-color" content="#f5f6f2">
74
72
  <title>{escape(title)} · Randpic</title><style>{GALLERY_STYLE}
@@ -77,22 +75,13 @@ def _shell(title: str, home: str, version: str, command: str, page: int, content
77
75
  </style></head><body><div class="shell">
78
76
  <nav class="topbar"><a class="brand" href="{home}"><span class="brand-mark">✳</span><span>RANDPIC / 图片收藏馆</span></a><span class="top-note">捕捉喜欢的瞬间</span></nav>
79
77
  {content}<footer>Randpic · 收藏每一个喜欢的瞬间</footer></div>
80
- <script>(()=>{{const state={state}; const home={json.dumps(home)};
81
- setInterval(async()=>{{if(document.hidden)return;try{{
82
- const response=await fetch(home+'catalog.json',{{cache:'no-store'}});if(!response.ok)return;
83
- const next=await response.json();if(next.version===state.version)return;
84
- if(state.command && (!(state.command in next.pages)||state.page>next.pages[state.command])){{location.href=state.command in next.pages?'./':home;return;}}
85
- location.reload();}}catch(error){{}}}},15000);}})();</script></body></html>'''
78
+ </body></html>'''
86
79
 
87
80
 
88
81
  def render_catalog(groups: Mapping[str, Sequence[ImageRecord]], page_size: int) -> Dict[str, bytes]:
89
- """相同图库总是产生相同 HTML;发送次数变化不会导致网页重复刷新。"""
90
- identity = [(command, [(image.id, image.filename, image.content_hash) for image in images])
91
- for command, images in sorted(groups.items())]
92
- version = hashlib.sha256(json.dumps(identity, ensure_ascii=False).encode()).hexdigest()
82
+ """生成纯静态 HTML,只让内容实际变化的页面产生同步任务。"""
93
83
  pages = {command: max(1, math.ceil(len(images) / page_size)) for command, images in groups.items()}
94
- catalog = json.dumps({'version': version, 'pages': pages}, ensure_ascii=False, sort_keys=True).encode()
95
- files = {'catalog.json': catalog}
84
+ files: Dict[str, bytes] = {}
96
85
  total = sum(len(images) for images in groups.values())
97
86
  cards = []
98
87
  for command, images in sorted(groups.items()):
@@ -106,7 +95,7 @@ def render_catalog(groups: Mapping[str, Sequence[ImageRecord]], page_size: int)
106
95
  home = f'''<header class="hero"><p class="eyebrow">YOUR IMAGE LIBRARY</p><h1>每一张,都值得<br>被好好收藏。</h1>
107
96
  <p class="hero-copy">在这里慢慢翻看收藏的每一份灵感。</p></header><div class="section-head"><h2>全部分类</h2>
108
97
  <span class="count">{len(groups)} 个分类 · {total} 张图片</span></div><main class="folder-grid">{''.join(cards)}</main>'''
109
- files['index.html'] = _shell('图片收藏馆', './', version, '', 1, home).encode()
98
+ files['index.html'] = _shell('图片收藏馆', './', home).encode()
110
99
  for command, images in groups.items():
111
100
  for page in range(1, pages[command] + 1):
112
101
  cards = []
@@ -128,7 +117,7 @@ def render_catalog(groups: Mapping[str, Sequence[ImageRecord]], page_size: int)
128
117
  <header class="gallery-title"><p class="eyebrow">COLLECTION</p><h1>{escape(command)}</h1><p class="hero-copy">本分类共收藏 {len(images)} 张图片。</p></header>
129
118
  <main class="image-grid">{''.join(cards)}</main><nav class="pagination">{''.join(navigation)}</nav>'''
130
119
  filename = 'index.html' if page == 1 else f'page-{page}.html'
131
- files[f'{command}/{filename}'] = _shell(command, '../', version, command, page, content).encode()
120
+ files[f'{command}/{filename}'] = _shell(command, '../', content).encode()
132
121
  return files
133
122
 
134
123
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "nonebot-plugin-randpic"
3
- version = "2.0.1"
3
+ version = "2.0.2"
4
4
  description = "一个通过指令随机发送你存储的图片的nonebot2插件"
5
5
  authors = ["HuParry <hu_parry@qq.com>"]
6
6
  license = "MIT"