plugin-jm-server 0.1.0__py3-none-any.whl
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.
- plugin_jm_server/__init__.py +3 -0
- plugin_jm_server/app.py +240 -0
- plugin_jm_server/driver.py +43 -0
- plugin_jm_server/files.py +130 -0
- plugin_jm_server-0.1.0.dist-info/METADATA +28 -0
- plugin_jm_server-0.1.0.dist-info/RECORD +8 -0
- plugin_jm_server-0.1.0.dist-info/WHEEL +5 -0
- plugin_jm_server-0.1.0.dist-info/top_level.txt +1 -0
plugin_jm_server/app.py
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
from random import randint
|
|
4
|
+
|
|
5
|
+
import common
|
|
6
|
+
from flask import Flask, abort
|
|
7
|
+
from flask import render_template, send_from_directory
|
|
8
|
+
from flask import request, session, redirect, flash
|
|
9
|
+
|
|
10
|
+
from .files import get_files_data, DEFAULT_PATH, DRIVERS_LIST, \
|
|
11
|
+
get_current_path, get_jm_view_images
|
|
12
|
+
|
|
13
|
+
# 创建项目以及初始化一些关键信息
|
|
14
|
+
app = Flask(__name__,
|
|
15
|
+
template_folder='templates',
|
|
16
|
+
static_folder='static',
|
|
17
|
+
static_url_path='/static',
|
|
18
|
+
)
|
|
19
|
+
# 这里是预先将值存储在系统环境变量中了
|
|
20
|
+
app.secret_key = '123'
|
|
21
|
+
# 匹配移动端设备的正则表达式
|
|
22
|
+
MATCH_EXP = 'Android|webOS|iPhone|iPad|iPod|BlackBerry'
|
|
23
|
+
# 设置登录用户名和密码
|
|
24
|
+
SPECIFY_SECRET = ''
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def verify():
|
|
28
|
+
"""
|
|
29
|
+
验证登录状态
|
|
30
|
+
"""
|
|
31
|
+
if session.get('uname', None) == SPECIFY_SECRET:
|
|
32
|
+
return True
|
|
33
|
+
else:
|
|
34
|
+
return False
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def mobile_check():
|
|
38
|
+
"""
|
|
39
|
+
设备类型检查
|
|
40
|
+
"""
|
|
41
|
+
try:
|
|
42
|
+
if session.mobile == 'yes':
|
|
43
|
+
return True
|
|
44
|
+
elif session.mobile == 'no':
|
|
45
|
+
return False
|
|
46
|
+
except AttributeError:
|
|
47
|
+
if re.search(MATCH_EXP, request.headers.get('User-Agent')):
|
|
48
|
+
session.mobile = 'yes'
|
|
49
|
+
return True
|
|
50
|
+
else:
|
|
51
|
+
session.mobile = 'no'
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def url_format(device_isMobile, default_load_url):
|
|
56
|
+
"""
|
|
57
|
+
根据设备类型返回对应的资源 url
|
|
58
|
+
"""
|
|
59
|
+
if device_isMobile:
|
|
60
|
+
return './h5/m_' + default_load_url
|
|
61
|
+
else:
|
|
62
|
+
return default_load_url
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def url_random_arg():
|
|
66
|
+
"""
|
|
67
|
+
url添加一个随机参数,防止浏览器缓存
|
|
68
|
+
"""
|
|
69
|
+
return randint(100000, 1000000)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@app.route('/jm_view', methods=['GET'])
|
|
73
|
+
def jm_view():
|
|
74
|
+
"""
|
|
75
|
+
以禁漫章节的模式观看指定文件夹下的图片
|
|
76
|
+
"""
|
|
77
|
+
if not verify():
|
|
78
|
+
return redirect('/login')
|
|
79
|
+
|
|
80
|
+
# path是要阅读的文件夹
|
|
81
|
+
path = request.args.get('path', None)
|
|
82
|
+
# 从哪个文件夹打开的
|
|
83
|
+
openFromDir = request.args.get('openFromDir', get_current_path())
|
|
84
|
+
|
|
85
|
+
if path is None:
|
|
86
|
+
return redirect('/')
|
|
87
|
+
|
|
88
|
+
path = os.path.abspath(path)
|
|
89
|
+
if os.path.isfile(path):
|
|
90
|
+
path = common.of_dir_path(path)
|
|
91
|
+
|
|
92
|
+
# 文件不存在
|
|
93
|
+
if common.file_not_exists(path):
|
|
94
|
+
return abort(404)
|
|
95
|
+
|
|
96
|
+
print(f'jm_view: {path}')
|
|
97
|
+
return render_template(url_format(mobile_check(), "jm_view.html"),
|
|
98
|
+
data={
|
|
99
|
+
'title': common.of_file_name(path),
|
|
100
|
+
'images': get_jm_view_images(path),
|
|
101
|
+
'openFromDir': openFromDir,
|
|
102
|
+
},
|
|
103
|
+
randomArg=url_random_arg())
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@app.route("/view_file/", methods=['GET'])
|
|
107
|
+
def view_file():
|
|
108
|
+
"""
|
|
109
|
+
获取单个文件
|
|
110
|
+
"""
|
|
111
|
+
# 判断是否已经在登录状态上
|
|
112
|
+
if not verify():
|
|
113
|
+
# 之前没有登录过,返回登录页
|
|
114
|
+
return redirect('/login')
|
|
115
|
+
|
|
116
|
+
# 已经登录了,返回文件夹内文件信息(此时为默认路径)
|
|
117
|
+
path = request.args.get('path', None)
|
|
118
|
+
if path is None:
|
|
119
|
+
return abort(403)
|
|
120
|
+
|
|
121
|
+
return send_from_directory(os.path.dirname(path),
|
|
122
|
+
os.path.basename(path),
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@app.route('/', methods=['GET'])
|
|
127
|
+
def index():
|
|
128
|
+
"""
|
|
129
|
+
共享文件主页
|
|
130
|
+
"""
|
|
131
|
+
# 判断是否已经在登录状态上
|
|
132
|
+
if not verify():
|
|
133
|
+
# 之前没有登录过,返回登录页
|
|
134
|
+
return redirect('/login')
|
|
135
|
+
|
|
136
|
+
# 已经登录了,返回文件夹内文件信息(此时为默认路径)
|
|
137
|
+
path = request.args.get('path', DEFAULT_PATH)
|
|
138
|
+
path = os.path.abspath(path)
|
|
139
|
+
|
|
140
|
+
# 文件不存在
|
|
141
|
+
if common.file_not_exists(path):
|
|
142
|
+
return abort(404)
|
|
143
|
+
|
|
144
|
+
return render_template(url_format(mobile_check(), "index.html"),
|
|
145
|
+
data={
|
|
146
|
+
"files": get_files_data(path),
|
|
147
|
+
"drivers": DRIVERS_LIST,
|
|
148
|
+
"currentPath": path,
|
|
149
|
+
},
|
|
150
|
+
randomArg=url_random_arg())
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@app.route('/login', methods=['GET', 'POST'])
|
|
154
|
+
def login():
|
|
155
|
+
"""
|
|
156
|
+
登录页
|
|
157
|
+
"""
|
|
158
|
+
device_isMobile = mobile_check()
|
|
159
|
+
if request.method == 'GET':
|
|
160
|
+
if verify():
|
|
161
|
+
return redirect('/')
|
|
162
|
+
else:
|
|
163
|
+
# 之前没有登录过,返回一个登录页
|
|
164
|
+
return render_template(url_format(device_isMobile, 'login.html'),
|
|
165
|
+
randomArg=url_random_arg())
|
|
166
|
+
else:
|
|
167
|
+
# 先保存才能验证
|
|
168
|
+
uname = request.form.get('uname')
|
|
169
|
+
session['uname'] = uname
|
|
170
|
+
if verify():
|
|
171
|
+
# 重定向到首页
|
|
172
|
+
return redirect('/')
|
|
173
|
+
else:
|
|
174
|
+
# 登录失败的情况
|
|
175
|
+
flash("该用户名和密码不存在,请重试")
|
|
176
|
+
return redirect('/login')
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@app.route('/logout', methods=['GET', 'POST'])
|
|
180
|
+
def logout():
|
|
181
|
+
"""
|
|
182
|
+
注销
|
|
183
|
+
"""
|
|
184
|
+
if verify():
|
|
185
|
+
# 声明重定向对象
|
|
186
|
+
resp = redirect('/')
|
|
187
|
+
# 删除值
|
|
188
|
+
resp.delete_cookie('uname')
|
|
189
|
+
session.pop('uname', None)
|
|
190
|
+
return resp
|
|
191
|
+
else:
|
|
192
|
+
# 没有登录过,返回登录页
|
|
193
|
+
return redirect('/login')
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
@app.route("/download_file/<filename>")
|
|
197
|
+
def file_content(filename):
|
|
198
|
+
"""
|
|
199
|
+
下载文件
|
|
200
|
+
"""
|
|
201
|
+
if verify():
|
|
202
|
+
# 若文件存在
|
|
203
|
+
if filename in os.listdir(get_current_path()):
|
|
204
|
+
# 发送文件 参数:路径,文件名
|
|
205
|
+
return send_from_directory(get_current_path(), filename)
|
|
206
|
+
else:
|
|
207
|
+
# 否则返回错误页面
|
|
208
|
+
device_isMobile = mobile_check()
|
|
209
|
+
return render_template(url_format(device_isMobile, "download_error.html"),
|
|
210
|
+
filename=filename,
|
|
211
|
+
randomArg=url_random_arg())
|
|
212
|
+
else:
|
|
213
|
+
return redirect('/login')
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@app.route("/upload_file", methods=['GET', 'POST'])
|
|
217
|
+
def upload():
|
|
218
|
+
"""
|
|
219
|
+
上传文件
|
|
220
|
+
"""
|
|
221
|
+
if verify():
|
|
222
|
+
if request.method == "POST":
|
|
223
|
+
# 获取文件 拼接存储路径并保存
|
|
224
|
+
upload_file = request.files['file']
|
|
225
|
+
upload_file.save(os.path.join(get_current_path(), upload_file.filename))
|
|
226
|
+
|
|
227
|
+
# 返回上传成功的消息给前端
|
|
228
|
+
return '提示:上传的%s已经存储到了服务器中!' % upload_file.filename
|
|
229
|
+
|
|
230
|
+
# 如果是 GET 方法:
|
|
231
|
+
device_isMobile = mobile_check()
|
|
232
|
+
return render_template(url_format(device_isMobile, "upload.html"),
|
|
233
|
+
randomArg=url_random_arg())
|
|
234
|
+
else:
|
|
235
|
+
return redirect('/login')
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
if __name__ == '__main__':
|
|
239
|
+
# 监听在所有 IP 地址上
|
|
240
|
+
app.run(host='0.0.0.0', port=5000, debug=True)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import psutil
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_winDriver():
|
|
7
|
+
"""
|
|
8
|
+
Windows操作系统下,返回全部驱动器卷标['C:\','D:\']
|
|
9
|
+
"""
|
|
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
|
+
if '[WinError 21]' in str(e):
|
|
28
|
+
del driver_list[i]
|
|
29
|
+
# 异常类型不为 “设备未就绪” 的再次抛出异常供调试
|
|
30
|
+
else:
|
|
31
|
+
raise (PermissionError, e)
|
|
32
|
+
finally:
|
|
33
|
+
num -= 1
|
|
34
|
+
|
|
35
|
+
# 返回列表
|
|
36
|
+
return driver_list
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
if __name__ == "__main__":
|
|
40
|
+
paths = get_winDriver()
|
|
41
|
+
print(paths)
|
|
42
|
+
|
|
43
|
+
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
import common
|
|
5
|
+
|
|
6
|
+
from .driver import get_winDriver
|
|
7
|
+
|
|
8
|
+
DEFAULT_PATH = 'D:/'
|
|
9
|
+
DRIVERS_LIST = get_winDriver()
|
|
10
|
+
current_path = ''
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_jm_view_images(path):
|
|
14
|
+
images_data = []
|
|
15
|
+
|
|
16
|
+
from urllib.parse import quote
|
|
17
|
+
for f in files_of_dir_safe(path):
|
|
18
|
+
if not is_image_file(f):
|
|
19
|
+
continue
|
|
20
|
+
f = quote(f)
|
|
21
|
+
images_data.append({
|
|
22
|
+
"filename": common.of_file_name(f),
|
|
23
|
+
"data_original": f'/view_file?path={f}',
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
return images_data
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def is_image_file(filename):
|
|
30
|
+
# 获取文件名的小写形式并去掉路径
|
|
31
|
+
file_extension = filename.lower().split('.')[-1]
|
|
32
|
+
|
|
33
|
+
# 常见图片文件扩展名列表
|
|
34
|
+
image_extensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tiff', 'webp']
|
|
35
|
+
|
|
36
|
+
# 判断文件扩展名是否在图片扩展名列表中
|
|
37
|
+
return file_extension in image_extensions
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def files_of_dir_safe(dir_path):
|
|
41
|
+
try:
|
|
42
|
+
return common.files_of_dir(dir_path)
|
|
43
|
+
except OSError:
|
|
44
|
+
return []
|
|
45
|
+
|
|
46
|
+
def check_dir_can_open_jm_view(dirpath):
|
|
47
|
+
return any(f for f in files_of_dir_safe(dirpath) if is_image_file(f))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# 获取文件信息的函数
|
|
51
|
+
def get_files_data(path):
|
|
52
|
+
"""
|
|
53
|
+
获取指定路径下的所有文件、文件夹的信息
|
|
54
|
+
"""
|
|
55
|
+
global current_path
|
|
56
|
+
files = []
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
listdir = os.listdir(path)
|
|
60
|
+
except OSError:
|
|
61
|
+
# 无权限
|
|
62
|
+
return []
|
|
63
|
+
|
|
64
|
+
for file_name in listdir:
|
|
65
|
+
# 拼接路径
|
|
66
|
+
file_path = os.path.abspath(os.path.join(path, file_name))
|
|
67
|
+
|
|
68
|
+
# 判断是文件夹还是文件
|
|
69
|
+
if os.path.isfile(file_path):
|
|
70
|
+
the_type = 'file'
|
|
71
|
+
jm_view = check_dir_can_open_jm_view(common.of_dir_path(file_path))
|
|
72
|
+
else:
|
|
73
|
+
the_type = 'dir'
|
|
74
|
+
jm_view = check_dir_can_open_jm_view(file_path)
|
|
75
|
+
|
|
76
|
+
name = file_name
|
|
77
|
+
try:
|
|
78
|
+
size = os.path.getsize(file_path)
|
|
79
|
+
except OSError as e:
|
|
80
|
+
print(e)
|
|
81
|
+
continue
|
|
82
|
+
|
|
83
|
+
size = file_size_format(size, the_type)
|
|
84
|
+
# 创建时间
|
|
85
|
+
getctime = os.path.getctime(file_path)
|
|
86
|
+
try:
|
|
87
|
+
ctime = time.localtime(getctime)
|
|
88
|
+
# 封装成字典形式追加给 files 列表
|
|
89
|
+
time_str = "{}/{}/{}".format(ctime.tm_year, ctime.tm_mon, ctime.tm_mday)
|
|
90
|
+
except OSError as e:
|
|
91
|
+
print(e)
|
|
92
|
+
continue
|
|
93
|
+
|
|
94
|
+
files.append({
|
|
95
|
+
"name": name,
|
|
96
|
+
"size": size,
|
|
97
|
+
# 拼接年月日信息
|
|
98
|
+
"ctime": time_str,
|
|
99
|
+
"type": the_type,
|
|
100
|
+
'jm_view': jm_view
|
|
101
|
+
})
|
|
102
|
+
# 更新当前路径
|
|
103
|
+
current_path = path
|
|
104
|
+
|
|
105
|
+
return files
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def file_size_format(size, the_type):
|
|
109
|
+
"""
|
|
110
|
+
文件大小格式化,携带单位
|
|
111
|
+
"""
|
|
112
|
+
if the_type == 'dir':
|
|
113
|
+
return '<DIR>'
|
|
114
|
+
else:
|
|
115
|
+
if size < 1024:
|
|
116
|
+
return '%i' % size + ' B'
|
|
117
|
+
elif 1024 < size <= 1048576:
|
|
118
|
+
return '%.1f' % float(size / 1024) + ' KB'
|
|
119
|
+
elif 1048576 < size <= 1073741824:
|
|
120
|
+
return '%.1f' % float(size / 1048576) + ' MB'
|
|
121
|
+
elif 1073741824 < size <= 1099511627776:
|
|
122
|
+
return '%.1f' % float(size / 1073741824) + ' GB'
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def get_current_path():
|
|
126
|
+
return current_path
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
if __name__ == '__main__':
|
|
130
|
+
text = get_files_data('E:/')
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: plugin_jm_server
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: plugin_jm_server, a plugin for jmcomic that can be used to view comics in a web browser.
|
|
5
|
+
Home-page: https://github.com/hect0x7/plugin-jm-server
|
|
6
|
+
Author: hect0x7
|
|
7
|
+
Author-email: 93357912+hect0x7@users.noreply.github.com
|
|
8
|
+
Keywords: python,jmcomic,18comic,禁漫天堂,NSFW
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Operating System :: MacOS
|
|
18
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
19
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
20
|
+
Requires-Python: >=3.7
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Requires-Dist: jmcomic
|
|
23
|
+
Requires-Dist: flask
|
|
24
|
+
Requires-Dist: psutil
|
|
25
|
+
|
|
26
|
+
源项目:https://github.com/AiCorein/Flask-Files-Server
|
|
27
|
+
|
|
28
|
+
为了方便修改,将源项目中的文件复制到本项目中,然后进行修改
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
plugin_jm_server/__init__.py,sha256=JATfJ_2L6Zqa7Bmr0dTS_UwrtvyFe95ZkErcNqVkUCs,42
|
|
2
|
+
plugin_jm_server/app.py,sha256=UTm-nzaaPwnA151pHEdq0sM-KgkA2IprmSJi2fRjguM,6787
|
|
3
|
+
plugin_jm_server/driver.py,sha256=sWpFjS_kG5IpYD9kqKFuVayQJjsu8GelQ7othR0KzZA,1081
|
|
4
|
+
plugin_jm_server/files.py,sha256=LlSneXjvBYNlfcHqSbNLnHWWgkTYerhv6rO6pnhoUKo,3268
|
|
5
|
+
plugin_jm_server-0.1.0.dist-info/METADATA,sha256=ZPDkc-LRy71Rv782aMuEUIPARB_8rlOeaZOWHngwjzI,1142
|
|
6
|
+
plugin_jm_server-0.1.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
7
|
+
plugin_jm_server-0.1.0.dist-info/top_level.txt,sha256=FZ81r1OLOEzo0e7Ahgqw23gmcIeCsZ1XVtFHFDKTSak,17
|
|
8
|
+
plugin_jm_server-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
plugin_jm_server
|