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.
@@ -0,0 +1,3 @@
1
+ __version__ = '0.1.0'
2
+
3
+ from .app import *
@@ -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,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel (0.42.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ plugin_jm_server