plugin-jm-server 0.2.0__tar.gz → 0.2.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 (61) hide show
  1. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/PKG-INFO +64 -33
  2. plugin_jm_server-0.2.2/README.md +160 -0
  3. plugin_jm_server-0.2.2/src/plugin_jm_server/__init__.py +3 -0
  4. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/app.py +84 -2
  5. plugin_jm_server-0.2.2/src/plugin_jm_server/driver.py +88 -0
  6. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/files.py +20 -15
  7. plugin_jm_server-0.2.2/src/plugin_jm_server/message.py +135 -0
  8. plugin_jm_server-0.2.2/src/plugin_jm_server/static/css/h5/m_message.css +476 -0
  9. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/css/index_spa.css +87 -6
  10. plugin_jm_server-0.2.2/src/plugin_jm_server/static/css/message.css +516 -0
  11. plugin_jm_server-0.2.2/src/plugin_jm_server/static/js/common.js +195 -0
  12. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/js/index.js +7 -3
  13. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/js/index_spa.js +286 -2
  14. plugin_jm_server-0.2.2/src/plugin_jm_server/static/js/message.js +301 -0
  15. plugin_jm_server-0.2.0/src/plugin_jm_server/templates/m_index.html → plugin_jm_server-0.2.2/src/plugin_jm_server/templates/index.html +37 -8
  16. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/templates/index_spa.html +34 -8
  17. plugin_jm_server-0.2.0/src/plugin_jm_server/templates/index.html → plugin_jm_server-0.2.2/src/plugin_jm_server/templates/m_index.html +27 -4
  18. plugin_jm_server-0.2.2/src/plugin_jm_server/templates/m_message.html +84 -0
  19. plugin_jm_server-0.2.2/src/plugin_jm_server/templates/message.html +91 -0
  20. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server.egg-info/PKG-INFO +64 -33
  21. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server.egg-info/SOURCES.txt +6 -0
  22. plugin_jm_server-0.2.0/README.md +0 -129
  23. plugin_jm_server-0.2.0/src/plugin_jm_server/__init__.py +0 -3
  24. plugin_jm_server-0.2.0/src/plugin_jm_server/driver.py +0 -41
  25. plugin_jm_server-0.2.0/src/plugin_jm_server/static/js/common.js +0 -78
  26. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/setup.cfg +0 -0
  27. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/setup.py +0 -0
  28. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/css/all.min.css +0 -0
  29. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/css/bootstrap.css +0 -0
  30. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/css/download_error.css +0 -0
  31. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/css/h5/m_download_error.css +0 -0
  32. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/css/h5/m_index.css +0 -0
  33. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/css/h5/m_login.css +0 -0
  34. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/css/h5/m_upload.css +0 -0
  35. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/css/h5/normalize.css +0 -0
  36. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/css/index.css +0 -0
  37. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/css/login.css +0 -0
  38. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/css/style_photo.css +0 -0
  39. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/css/upload.css +0 -0
  40. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/img/bg.jpg +0 -0
  41. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/img/m_bg.jpeg +0 -0
  42. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/js/h5/m_index.js +0 -0
  43. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/js/h5/m_upload.js +0 -0
  44. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/js/jquery-3.7.1.min.js +0 -0
  45. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/js/jquery.album-0.2.js +0 -0
  46. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/js/jquery.lazyload-2.0.js +0 -0
  47. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/js/jquery.photo-0.3.js +0 -0
  48. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/js/upload.js +0 -0
  49. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/webfonts/fa-regular-400.woff2 +0 -0
  50. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/static/webfonts/fa-solid-900.woff2 +0 -0
  51. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/templates/download_error.html +0 -0
  52. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/templates/jm_view.html +0 -0
  53. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/templates/login.html +0 -0
  54. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/templates/m_download_error.html +0 -0
  55. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/templates/m_jm_view.html +0 -0
  56. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/templates/m_login.html +0 -0
  57. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/templates/m_upload.html +0 -0
  58. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server/templates/upload.html +0 -0
  59. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server.egg-info/dependency_links.txt +0 -0
  60. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server.egg-info/requires.txt +0 -0
  61. {plugin_jm_server-0.2.0 → plugin_jm_server-0.2.2}/src/plugin_jm_server.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plugin_jm_server
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: plugin_jm_server, a plugin for jmcomic that can be used to view comics in a web browser.
5
5
  Home-page: https://github.com/hect0x7/plugin-jm-server
6
6
  Author: hect0x7
@@ -44,20 +44,35 @@ Dynamic: summary
44
44
 
45
45
  * 双页阅读插件:https://sleazyfork.org/zh-CN/scripts/374903-comicread
46
46
 
47
- # 想法起源
47
+ # 效果图(文件浏览、整章看图)
48
48
 
49
- 想法起源:https://github.com/hect0x7/JMComic-Crawler-Python/issues/192
49
+ ## 1. 电脑浏览器访问
50
50
 
51
- 基于原项目:https://github.com/AiCorein/Flask-Files-Server
51
+ ### 登录页
52
+ ![](images/3.png)
52
53
 
53
- 为了方便修改,将原项目中的文件复制到本项目中,然后进行修改
54
+ ### 文件夹列表页
55
+ ![](images/4.png)
54
56
 
57
+ ### 看本模式
58
+ ![](images/5.png)
55
59
 
56
- # 可优化点
60
+ ### 文件夹&看本 二合一模式
61
+ ![](images/8.png)
57
62
 
58
- 1. 前端优化。目前项目架构是`前后端不分离架构`,前端使用原始的html三件套。可以优化为前后端分离架构,基于Vue等开发框架,复用组件库,优化界面。
63
+ ## 2. 手机浏览器访问
59
64
 
60
- 2. 后端优化。支持提供调用jmcomic功能的后端接口。
65
+ ### 登录页
66
+ ![](images/1.jpeg)
67
+
68
+ ### 文件夹列表页
69
+ ![](images/2.png)
70
+
71
+ ### 看本模式
72
+ ![](images/7.jpeg)
73
+
74
+ ### 局域网消息(方便手机和电脑互传消息)
75
+ ![](images/9.png)
61
76
 
62
77
 
63
78
  # 使用方式
@@ -70,7 +85,6 @@ pip install plugin_jm_server
70
85
  ```
71
86
 
72
87
 
73
-
74
88
  ## 2. 运行代码
75
89
 
76
90
  * **HTTP版**
@@ -135,31 +149,48 @@ op.wait_all_plugins_finish()
135
149
  ```
136
150
 
137
151
 
138
- # 效果图(文件浏览、整章看图)
139
-
140
- ## 1. 电脑浏览器访问
141
-
142
- ### 登录页
143
- ![](images/3.png)
144
-
145
- ### 文件夹列表页
146
- ![](images/4.png)
147
- ![](images/9.png)
148
-
149
- ### 看本模式
150
- ![](images/5.png)
152
+ ## 前端页面与功能介绍
153
+
154
+ 本项目提供了一套完整的 Web 前端界面,用于管理和浏览本地图片资源。主要包含以下三个核心页面及其功能:
155
+
156
+ ### 1. 登录页
157
+ 为了保护个人隐私,系统提供了简单的访问控制。
158
+ * **密码验证**:启动时可配置密码,访问时需校验通过。
159
+ * **设备自适应**:根据访问设备(手机/电脑)自动适配最佳的登录布局。
160
+
161
+ ### 2. 文件夹浏览页(主页)
162
+ 这是资源管理的核心入口,支持高效的文件查找与导航。目前最新版本采用了 SPA(单页应用)架构进行全面增强:
163
+ * **智能路径导航**:
164
+ * **路径补全**:输入路径时自动联想下级文件夹,支持键盘上下键选择及回车跳转。
165
+ * **层级跳转**:输入框支持“..”快速返回上一级,并提供“确认”按钮进行精准跳转。
166
+ * **可视化展示**:
167
+ * **文件夹预览**:鼠标悬停在文件夹上时,会自动展示该文件夹内的首张图片作为封面预览。
168
+ * **类型区分**:通过文件夹角标(图标)清晰区分普通文件夹与特殊资源文件夹。
169
+ * **辅助功能**:
170
+ * **书签管理**:支持将常看目录加入左侧书签栏,方便下次快速访问。
171
+ * **本地交互**:支持通过网页菜单直接打开服务器端对应的本地文件夹窗口(Explorer)。
172
+
173
+ ### 3. 图片阅读页(看本模式)
174
+ 专为漫画阅读优化的沉浸式浏览页面。
175
+ * **加载优化**:采用图片懒加载机制,流畅浏览含有数百张图片的大型章节,节省系统资源。
176
+ * **多端适配**:
177
+ * **PC端**:适合大屏浏览,支持浮动工具栏进行页面跳转、全屏切换等。
178
+ * **移动端**:适配触摸操作,提供更好的手机阅读体验。
179
+ * **阅读辅助**:
180
+ * **浮动菜单**:提供“回到顶部”、“加载全部图片”及“在资源管理器打开”等便捷操作。
181
+ * **键盘支持**:支持常用快捷键进行页面滚动和翻页。
182
+
183
+ ### 4. 局域网消息中心 (v0.2.2+ New)
184
+ 本项目最新版本引入了即时通讯与增强的局域网接入支持。
185
+ * **实时互动**:支持服务器与内网设备之间互发即时消息,采用暗色毛玻璃视觉风格。
186
+ * **全站通知**:后台实时轮询新消息,支持浏览器原生桌面通知及精美的网页侧边栏滑入提醒。
187
+ * **接入优化**:自动识别服务器在局域网内的真实物理 IP,主页提供一键复制功能。
188
+ * **非侵入式反馈**:全站移除 alert 阻塞弹窗,所有交互操作结果均通过通知条反馈。
151
189
 
152
- ### 文件夹&看本 二合一模式
153
- ![](images/8.png)
154
-
155
- ## 2. 手机浏览器访问
156
-
157
- ### 登录页
158
- ![](images/1.jpeg)
190
+ # 想法起源
159
191
 
160
- ### 文件夹列表页
161
- ![](images/2.png)
192
+ 想法起源:https://github.com/hect0x7/JMComic-Crawler-Python/issues/192
162
193
 
163
- ### 看本模式
164
- ![](images/7.jpeg)
194
+ 基于原项目:https://github.com/AiCorein/Flask-Files-Server
165
195
 
196
+ 为了方便修改,将原项目中的文件复制到本项目中,然后进行修改
@@ -0,0 +1,160 @@
1
+ # plugin-jm-server
2
+
3
+ “离线版”禁漫天堂
4
+
5
+ 该项目会在本地起一个`文件服务器`,支持用浏览器查看文件夹内的图片,界面模仿禁漫的章节观看页面,并适配手机端。
6
+
7
+ 用浏览器的好处就是支持很多浏览器插件和脚本,例如:
8
+
9
+ * 双页阅读插件:https://sleazyfork.org/zh-CN/scripts/374903-comicread
10
+
11
+ # 效果图(文件浏览、整章看图)
12
+
13
+ ## 1. 电脑浏览器访问
14
+
15
+ ### 登录页
16
+ ![](images/3.png)
17
+
18
+ ### 文件夹列表页
19
+ ![](images/4.png)
20
+
21
+ ### 看本模式
22
+ ![](images/5.png)
23
+
24
+ ### 文件夹&看本 二合一模式
25
+ ![](images/8.png)
26
+
27
+ ## 2. 手机浏览器访问
28
+
29
+ ### 登录页
30
+ ![](images/1.jpeg)
31
+
32
+ ### 文件夹列表页
33
+ ![](images/2.png)
34
+
35
+ ### 看本模式
36
+ ![](images/7.jpeg)
37
+
38
+ ### 局域网消息(方便手机和电脑互传消息)
39
+ ![](images/9.png)
40
+
41
+
42
+ # 使用方式
43
+
44
+
45
+ ## 1. pip安装
46
+
47
+ ```shell
48
+ pip install plugin_jm_server
49
+ ```
50
+
51
+
52
+ ## 2. 运行代码
53
+
54
+ * **HTTP版**
55
+
56
+ ```python
57
+ from plugin_jm_server import *
58
+
59
+ # http
60
+ server = JmServer(
61
+ 'D:/',
62
+ 'password',
63
+ )
64
+ server.run(
65
+ host='0.0.0.0',
66
+ port=80,
67
+ )
68
+ ```
69
+
70
+ * **HTTPS版**
71
+
72
+ ```python
73
+ from plugin_jm_server import *
74
+
75
+ # https
76
+ server = JmServer(
77
+ 'D:/',
78
+ 'password',
79
+ )
80
+ server.run(
81
+ host='0.0.0.0',
82
+ port=443,
83
+ ssl_context='adhoc',
84
+ )
85
+ ```
86
+
87
+ * 作为jmcomic的插件
88
+
89
+ ```yml
90
+ # 配置文件
91
+ plugins:
92
+ after_init:
93
+ # after_album:
94
+ - plugin: jm_server
95
+ kwargs:
96
+ password: ''
97
+
98
+ ```
99
+
100
+ ```python
101
+ # 代码
102
+ from jmcomic import *
103
+
104
+ op = create_option('op.yml')
105
+ op.download_album(123)
106
+ # 运行到这里,虽然主线程代码执行完毕,但程序不会退出,因为服务器线程还没有结束。
107
+ # 控制台会输出提示信息如下(atexit时输出的):
108
+ # “主线程执行完毕,但插件jm_server的服务器线程仍运行中,可按下ctrl+c结束程序”
109
+ # 此时需要你主动按下ctrl+c终止程序。
110
+
111
+ # python 3.12 特别注意:需要插入下面这行代码,服务器才能继续处理请求
112
+ op.wait_all_plugins_finish()
113
+ ```
114
+
115
+
116
+ ## 前端页面与功能介绍
117
+
118
+ 本项目提供了一套完整的 Web 前端界面,用于管理和浏览本地图片资源。主要包含以下三个核心页面及其功能:
119
+
120
+ ### 1. 登录页
121
+ 为了保护个人隐私,系统提供了简单的访问控制。
122
+ * **密码验证**:启动时可配置密码,访问时需校验通过。
123
+ * **设备自适应**:根据访问设备(手机/电脑)自动适配最佳的登录布局。
124
+
125
+ ### 2. 文件夹浏览页(主页)
126
+ 这是资源管理的核心入口,支持高效的文件查找与导航。目前最新版本采用了 SPA(单页应用)架构进行全面增强:
127
+ * **智能路径导航**:
128
+ * **路径补全**:输入路径时自动联想下级文件夹,支持键盘上下键选择及回车跳转。
129
+ * **层级跳转**:输入框支持“..”快速返回上一级,并提供“确认”按钮进行精准跳转。
130
+ * **可视化展示**:
131
+ * **文件夹预览**:鼠标悬停在文件夹上时,会自动展示该文件夹内的首张图片作为封面预览。
132
+ * **类型区分**:通过文件夹角标(图标)清晰区分普通文件夹与特殊资源文件夹。
133
+ * **辅助功能**:
134
+ * **书签管理**:支持将常看目录加入左侧书签栏,方便下次快速访问。
135
+ * **本地交互**:支持通过网页菜单直接打开服务器端对应的本地文件夹窗口(Explorer)。
136
+
137
+ ### 3. 图片阅读页(看本模式)
138
+ 专为漫画阅读优化的沉浸式浏览页面。
139
+ * **加载优化**:采用图片懒加载机制,流畅浏览含有数百张图片的大型章节,节省系统资源。
140
+ * **多端适配**:
141
+ * **PC端**:适合大屏浏览,支持浮动工具栏进行页面跳转、全屏切换等。
142
+ * **移动端**:适配触摸操作,提供更好的手机阅读体验。
143
+ * **阅读辅助**:
144
+ * **浮动菜单**:提供“回到顶部”、“加载全部图片”及“在资源管理器打开”等便捷操作。
145
+ * **键盘支持**:支持常用快捷键进行页面滚动和翻页。
146
+
147
+ ### 4. 局域网消息中心 (v0.2.2+ New)
148
+ 本项目最新版本引入了即时通讯与增强的局域网接入支持。
149
+ * **实时互动**:支持服务器与内网设备之间互发即时消息,采用暗色毛玻璃视觉风格。
150
+ * **全站通知**:后台实时轮询新消息,支持浏览器原生桌面通知及精美的网页侧边栏滑入提醒。
151
+ * **接入优化**:自动识别服务器在局域网内的真实物理 IP,主页提供一键复制功能。
152
+ * **非侵入式反馈**:全站移除 alert 阻塞弹窗,所有交互操作结果均通过通知条反馈。
153
+
154
+ # 想法起源
155
+
156
+ 想法起源:https://github.com/hect0x7/JMComic-Crawler-Python/issues/192
157
+
158
+ 基于原项目:https://github.com/AiCorein/Flask-Files-Server
159
+
160
+ 为了方便修改,将原项目中的文件复制到本项目中,然后进行修改
@@ -0,0 +1,3 @@
1
+ __version__ = '0.2.2'
2
+
3
+ from .app import *
@@ -11,6 +11,8 @@ from flask import render_template, send_from_directory
11
11
  from flask import request, session, redirect, flash, jsonify
12
12
 
13
13
  from .files import FileManager
14
+ from .message import MessageManager
15
+ from .driver import get_lan_ip
14
16
 
15
17
 
16
18
  # noinspection PyMethodMayBeStatic
@@ -65,6 +67,9 @@ class JmServer:
65
67
  for k, v in env.items():
66
68
  os.environ[k] = v
67
69
 
70
+ # 初始化消息管理器
71
+ self.message_manager = MessageManager()
72
+
68
73
  def __hook_jm_logging(self):
69
74
  import jmcomic
70
75
  def executor_log(topic: str, msg: str):
@@ -142,7 +147,9 @@ class JmServer:
142
147
  data={
143
148
  'currentPath': path,
144
149
  'defaultPath': self.file_manager.default_path,
145
- 'drivers': self.file_manager.DRIVERS_LIST
150
+ 'drivers': self.file_manager.DRIVERS_LIST,
151
+ 'lan_ip': get_lan_ip(),
152
+ 'port': self.extra.get('port', self.DEFAULT_PORT)
146
153
  },
147
154
  randomArg=self.url_random_arg())
148
155
 
@@ -187,6 +194,19 @@ class JmServer:
187
194
  'images': images
188
195
  })
189
196
 
197
+ def api_open_file(self):
198
+ """
199
+ [New] API: Open file/folder in Explorer
200
+ """
201
+ if not self.verify():
202
+ return abort(403)
203
+
204
+ path = request.args.get('path', None)
205
+ if not path:
206
+ return jsonify({'error': 'Path required'}), 400
207
+
208
+ return self.open_directory(path) or jsonify({'status': 'ok'})
209
+
190
210
  def jm_view(self):
191
211
  """
192
212
  以禁漫章节的模式观看指定文件夹下的图片
@@ -260,7 +280,9 @@ class JmServer:
260
280
  "files": self.file_manager.get_files_data(path),
261
281
  "drivers": self.file_manager.DRIVERS_LIST,
262
282
  "currentPath": path,
263
- "defaultPath": self.file_manager.default_path
283
+ "defaultPath": self.file_manager.default_path,
284
+ "lan_ip": get_lan_ip(),
285
+ "port": self.extra.get('port', self.DEFAULT_PORT)
264
286
  },
265
287
  randomArg=self.url_random_arg())
266
288
 
@@ -393,6 +415,60 @@ class JmServer:
393
415
  subprocess.Popen(f'explorer /select,"{path}"')
394
416
  return ''
395
417
 
418
+ # ===== 消息功能 =====
419
+
420
+ def message_page(self):
421
+ """
422
+ 消息页面(支持 PC/移动端自适应)
423
+ """
424
+ if not self.verify():
425
+ return redirect('/login')
426
+
427
+ return render_template(
428
+ self.url_format(self.mobile_check(), 'message.html'),
429
+ randomArg=self.url_random_arg()
430
+ )
431
+
432
+ def api_get_messages(self):
433
+ """
434
+ API: 获取消息列表(支持增量拉取)
435
+ """
436
+ if not self.verify():
437
+ return abort(403)
438
+
439
+ since_id = request.args.get('since_id', 0, type=int)
440
+ limit = request.args.get('limit', 50, type=int)
441
+
442
+ messages = self.message_manager.get_messages(since_id=since_id, limit=limit)
443
+ return jsonify({'messages': messages})
444
+
445
+ def api_send_message(self):
446
+ """
447
+ API: 发送消息
448
+ """
449
+ if not self.verify():
450
+ return abort(403)
451
+
452
+ data = request.get_json(silent=True)
453
+ if not data or not data.get('content'):
454
+ return jsonify({'error': '消息内容不能为空'}), 400
455
+
456
+ sender = data.get('sender', '').strip() or '匿名'
457
+ content = data.get('content', '').strip()
458
+ sender_ip = request.remote_addr or ''
459
+
460
+ # 判断是否为服务器本机发送
461
+ is_local = sender_ip in ('127.0.0.1', '::1', 'localhost')
462
+ if is_local and sender == '服务器':
463
+ msg = self.message_manager.send_server_message(content)
464
+ else:
465
+ msg = self.message_manager.send_message(sender, content, sender_ip)
466
+
467
+ if msg:
468
+ return jsonify({'status': 'ok', 'message': msg})
469
+ else:
470
+ return jsonify({'error': '发送失败'}), 400
471
+
396
472
  def register_routes(self):
397
473
  # 添加路由
398
474
  self.app.add_url_rule('/jm_view', 'jm_view', self.jm_view, methods=['GET'])
@@ -409,6 +485,12 @@ class JmServer:
409
485
  self.app.add_url_rule("/spa", 'spa_view', self.spa_view, methods=['GET'])
410
486
  self.app.add_url_rule("/api/list_files", 'api_list_files', self.api_list_files, methods=['GET'])
411
487
  self.app.add_url_rule("/api/album_images", 'api_album_images', self.api_album_images, methods=['GET'])
488
+ self.app.add_url_rule("/api/open_file", 'api_open_file', self.api_open_file, methods=['GET'])
489
+
490
+ # 消息功能路由
491
+ self.app.add_url_rule("/message", 'message_page', self.message_page, methods=['GET'])
492
+ self.app.add_url_rule("/api/messages", 'api_get_messages', self.api_get_messages, methods=['GET'])
493
+ self.app.add_url_rule("/api/messages", 'api_send_message', self.api_send_message, methods=['POST'])
412
494
 
413
495
  def run(self, **kwargs):
414
496
  kwargs.setdefault('port', self.DEFAULT_PORT)
@@ -0,0 +1,88 @@
1
+ import os
2
+ import re
3
+
4
+
5
+ def get_winDriver():
6
+ """
7
+ Windows操作系统下,返回全部驱动器卷标['C:\','D:\']
8
+ """
9
+ import psutil
10
+ # 返回驱动器卷标列表
11
+ driver_list = sorted([driver.device for driver in psutil.disk_partitions(True)])
12
+
13
+ i = 0
14
+ num = len(driver_list)
15
+ while num != 0:
16
+ # 重新格式化分隔符
17
+ driver_name = driver_list[i]
18
+ driver_name = driver_name.strip('\\')
19
+ driver_name += '/'
20
+
21
+ # 测试各驱动器是否可访问,目的是筛除未就绪驱动器,如空光驱
22
+ try:
23
+ os.listdir(driver_name)
24
+ driver_list[i] = driver_name
25
+ i += 1
26
+ except PermissionError as e:
27
+ del driver_list[i]
28
+ mobj = re.match(r'\[WinError (\d+)\]', str(e))
29
+ # ERROR_NOT_READY, ERROR_ACCESS_DENIED
30
+ if mobj is not None and mobj.group(1) not in {'21', '5'}:
31
+ print(f'Drive {driver_name} unexpectedly unavailable: {e}')
32
+ finally:
33
+ num -= 1
34
+
35
+ # 返回列表
36
+ return driver_list
37
+
38
+
39
+ def get_lan_ip():
40
+ """
41
+ 通过本地接口获取局域网 IP 地址(无需外部连接)
42
+ 优先使用 psutil 扫描活动网卡
43
+ """
44
+ import socket
45
+ try:
46
+ import psutil
47
+
48
+ # 获取所有网卡信息
49
+ addrs = psutil.net_if_addrs()
50
+ stats = psutil.net_if_stats()
51
+
52
+ # 定义私有地址段前缀
53
+ private_prefixes = ('192.168.', '10.', '172.16.', '172.17.', '172.18.', '172.19.',
54
+ '172.20.', '172.21.', '172.22.', '172.23.', '172.24.', '172.25.',
55
+ '172.26.', '172.27.', '172.28.', '172.29.', '172.30.', '172.31.')
56
+
57
+ # 1. 优先寻找处于 up 状态且有私有 IP 的物理网卡
58
+ for interface, snics in addrs.items():
59
+ # 跳过禁用的网卡
60
+ if interface in stats and not stats[interface].isup:
61
+ continue
62
+
63
+ for snic in snics:
64
+ # 必须是 IPv4
65
+ if snic.family == socket.AF_INET:
66
+ ip = snic.address
67
+ # 排除回环地址,且必须符合私有地址段
68
+ if not ip.startswith('127.') and any(ip.startswith(prefix) for prefix in private_prefixes):
69
+ return ip
70
+
71
+ # 2. 如果没找到私有地址,尝试获取主机名的 IP
72
+ host_ip = socket.gethostbyname(socket.gethostname())
73
+ if not host_ip.startswith('127.'):
74
+ return host_ip
75
+
76
+ except Exception:
77
+ # 最后的保底兜底逻辑
78
+ try:
79
+ return socket.gethostbyname(socket.gethostname())
80
+ except:
81
+ pass
82
+
83
+ return '127.0.0.1'
84
+
85
+
86
+ if __name__ == "__main__":
87
+ paths = get_winDriver()
88
+ print(paths)
@@ -21,9 +21,8 @@ class FileManager:
21
21
  return ['/']
22
22
 
23
23
  def get_jm_view_images(self, path):
24
- images_data = []
25
-
26
- give_up_sort = False
24
+ sortable_images = []
25
+ unsortable_images = []
27
26
 
28
27
  for f in self.files_of_dir_safe(path):
29
28
  if not self.is_image_file(f):
@@ -32,20 +31,22 @@ class FileManager:
32
31
  name = common.of_file_name(f)
33
32
  try:
34
33
  index = int(name[:name.index('.')])
34
+ sortable_images.append({
35
+ 'filename': name,
36
+ 'data_original': f'/view_file?path={f}',
37
+ 'index': index,
38
+ })
35
39
  except ValueError:
36
- give_up_sort = True
37
- index = None
38
-
39
- images_data.append({
40
- 'filename': name,
41
- 'data_original': f'/view_file?path={f}',
42
- 'index': index,
43
- })
40
+ unsortable_images.append({
41
+ 'filename': name,
42
+ 'data_original': f'/view_file?path={f}',
43
+ 'index': None,
44
+ })
44
45
 
45
- if give_up_sort is False:
46
- images_data.sort(key=lambda item: item['index'])
46
+ sortable_images.sort(key=lambda item: item['index'])
47
+ unsortable_images.sort(key=lambda item: item['filename'])
47
48
 
48
- return images_data
49
+ return sortable_images + unsortable_images
49
50
 
50
51
  @staticmethod
51
52
  def is_image_file(filename):
@@ -92,10 +93,14 @@ class FileManager:
92
93
  target_path = self.get_target_path(file_path)
93
94
  return self.build_one_path_info(target_path, 'Link')
94
95
 
96
+ quoted_path = quote(file_path)
97
+ quoted_name = quote(name)
98
+
95
99
  return {
96
100
  "name": name,
97
101
  'path': file_path,
98
- 'href': f'/?path={file_path}' if the_type == 'dir' else f'./download_file/{name}',
102
+ 'quoted_path': quoted_path,
103
+ 'href': f'/?path={quoted_path}' if the_type == 'dir' else f'./download_file/{quoted_name}',
99
104
  "size": size,
100
105
  "ctime": time_str,
101
106
  "type": the_type,