├── data
└── README.md
├── upload
└── README.md
├── templates
├── footer.html
├── show
│ ├── any.html
│ ├── audio.html
│ ├── image.html
│ ├── video.html
│ ├── code.html
│ └── video2.html
├── head.html
├── readme.html
├── password.html
├── admin
│ ├── login.html
│ ├── install_1.html
│ ├── setpass.html
│ ├── edit.html
│ ├── cache.html
│ ├── setting.html
│ ├── upload.html
│ ├── install_0.html
│ ├── layout.html
│ ├── manage.html
│ └── upload_local.html
├── layout.html
├── header.html
├── _macro.html
└── index.html
├── requirements.txt
├── supervisord.conf.sample
├── static
├── js
│ └── hashmap.js
├── css
│ └── theme.css
└── img
│ └── bg-bottom.svg
├── config.py.sample
├── README.md
├── .gitignore
├── admin.py
├── run.py
└── function.py
/data/README.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/upload/README.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/templates/footer.html:
--------------------------------------------------------------------------------
1 |
2 | {{tj_code|safe}}
3 |
4 |
--------------------------------------------------------------------------------
/templates/show/any.html:
--------------------------------------------------------------------------------
1 | {%extends 'layout.html'%}
2 |
3 | {%block content%}
4 | {{content}}
5 | {%endblock content%}
--------------------------------------------------------------------------------
/templates/head.html:
--------------------------------------------------------------------------------
1 | {%if head%}
2 |
3 | {{head|safe}}
4 |
5 | {%endif%}
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | flask
2 | flask_sqlalchemy
3 | redis
4 | pymongo
5 | gunicorn
6 | humanize
7 | python_dateutil
8 | supervisor
9 | requests
10 | markdown
11 | shelljob
12 | eventlet
13 | flask_caching
14 | flask_limiter
15 | Flask-PyMongo
16 |
--------------------------------------------------------------------------------
/templates/readme.html:
--------------------------------------------------------------------------------
1 | {%if readme%}
2 |
3 |
4 | face
5 | README.md
6 |
7 | {{readme|safe}}
8 |
9 |
10 | {%endif%}
11 |
--------------------------------------------------------------------------------
/supervisord.conf.sample:
--------------------------------------------------------------------------------
1 | [unix_http_server]
2 | file=/tmp/supervisor.sock
3 |
4 | [supervisord]
5 | logfile=/tmp/supervisord.log
6 | logfile_maxbytes=50MB
7 | logfile_backups=10
8 | loglevel=info
9 | pidfile=/tmp/supervisord.pid
10 | nodaemon=false
11 | minfds=1024
12 | minprocs=200
13 |
14 | [inet_http_server] ; inet (TCP) server disabled by default
15 | port=*:9001
16 |
17 | [rpcinterface:supervisor]
18 | supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
19 |
20 | [supervisorctl]
21 | serverurl=unix:///tmp/supervisor.sock
22 |
23 |
24 |
25 |
26 | [program:pyone]
27 | command = gunicorn -k eventlet -b 0.0.0.0:34567 run:app
28 | directory = /root/PyOne
29 | autorestart = true
30 |
31 |
32 |
--------------------------------------------------------------------------------
/static/js/hashmap.js:
--------------------------------------------------------------------------------
1 | function HashMap(){var length=0;var obj=new Object();this.isEmpty=function(){return length==0};this.containsKey=function(key){return(key)?(key in obj):false};this.containsValue=function(value){for(key in obj){if(obj[key]==value){return true}}return false};this.get=function(key){return(this.containsKey(key))?obj[key]:null};this.remove=function(key){if(this.containsKey(key)&&delete obj[key]){length--}};this.put=function(key,value){if(!this.containsKey(key)){length++}obj[key]=value};this.values=function(){var _values=new Array();for(key in obj){_values.push(obj[key])}return _values};this.keySet=function(){var ks=new Array();for(key in obj){ks.push(key)}return ks};this.size=function(){return this.length};this.clear=function(){this.length=0;this.obj=new Object()};this.toJSON=function(){return JSON.stringify(obj,null,2)}};
2 |
--------------------------------------------------------------------------------
/templates/show/audio.html:
--------------------------------------------------------------------------------
1 | {%extends 'layout.html'%}
2 |
3 | {%block content%}
4 |
5 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
20 |
21 | file_download
22 | {%endblock content%}
--------------------------------------------------------------------------------
/templates/password.html:
--------------------------------------------------------------------------------
1 | {%extends 'layout.html'%}
2 |
3 | {%block content%}
4 |
21 | {%endblock content%}
22 |
--------------------------------------------------------------------------------
/templates/admin/login.html:
--------------------------------------------------------------------------------
1 | {%extends 'admin/layout.html'%}
2 |
3 | {%block content%}
4 |
21 | {%endblock content%}
22 |
--------------------------------------------------------------------------------
/templates/show/image.html:
--------------------------------------------------------------------------------
1 | {%extends 'layout.html'%}
2 |
3 | {%block content%}
4 |
5 |
6 |

7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | file_download
23 | {%endblock content%}
--------------------------------------------------------------------------------
/config.py.sample:
--------------------------------------------------------------------------------
1 | #-*- coding=utf-8 -*-
2 | import os
3 |
4 | #限制调用域名
5 | allow_site=[u'no-referrer']
6 |
7 | #######源码目录
8 | config_dir="/root/PyOne"
9 | data_dir=os.path.join(config_dir,'data')
10 |
11 | #下载链接过期时间
12 | downloadUrl_timeout="300"
13 |
14 | #后台密码设置
15 | password="PyOne"
16 |
17 | #网站名称
18 | title="PyOne"
19 |
20 | tj_code=""""""
21 |
22 | #onedrive api设置
23 | redirect_uri='https://auth.pyone.me/' #不要修改!
24 | BaseAuthUrl='https://login.microsoftonline.com'
25 | app_url=u'https://graph.microsoft.com/'
26 |
27 | od_users={
28 | "A":{
29 | "client_id":"",
30 | "client_secret":"",
31 | "share_path":"/",
32 | "other_name":"网盘1区",
33 | "order":1
34 | },
35 | "B":{
36 | "client_id":"",
37 | "client_secret":"",
38 | "share_path":"/",
39 | "other_name":"网盘2区",
40 | "order":2
41 | },
42 | "C":{
43 | "client_id":"",
44 | "client_secret":"",
45 | "share_path":"/",
46 | "other_name":"网盘3区",
47 | "order":3
48 | }
49 | }
50 |
51 |
52 |
--------------------------------------------------------------------------------
/templates/show/video.html:
--------------------------------------------------------------------------------
1 | {%extends 'layout.html'%}
2 |
3 | {%block content%}
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
30 | file_download
31 | {%endblock content%}
--------------------------------------------------------------------------------
/templates/show/code.html:
--------------------------------------------------------------------------------
1 | {%extends 'layout.html'%}
2 |
3 | {%block content%}
4 |
9 |
12 |
13 |
14 |
15 |
16 | file_download
17 |
18 |
19 |
20 |
34 | {%endblock content%}
35 |
--------------------------------------------------------------------------------
/templates/show/video2.html:
--------------------------------------------------------------------------------
1 | {%extends 'layout.html'%}
2 |
3 | {%block content%}
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
31 | file_download
32 | {%endblock content%}
--------------------------------------------------------------------------------
/templates/layout.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | index of {{path}} - {{title}}
8 |
9 |
10 |
11 |
12 |
29 |
30 |
31 |
32 |
33 | {%include 'header.html'%}
34 | {%block content%}
35 | {%endblock content%}
36 |
37 | {%include 'footer.html'%}
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/templates/header.html:
--------------------------------------------------------------------------------
1 |
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PyOne - 基于Python的onedrive文件本地化浏览系统,使用MongoDB缓存文件
2 |
3 | ## PyOne3.0版本更新!!!支持多网盘绑定
4 |
5 | ## 适用onedrive版本 ##
6 | 1. 个人版
7 | 2. onedrive商业版
8 | 3. onedrive教育版(需要学校管理员开启权限)
9 |
10 | ## 特性 ##
11 | 1. 简单易用。只需简单设置,即可做一个onedrive文件列表分享程序
12 | 2. 功能丰富。
13 | - 可设置文件夹密码。只需在文件夹添加`.password`文件,内容为密码内容,即可在该文件夹设置密码
14 | - 可设置README。
15 | 3. 后台强大。
16 | - **防盗链设置**。
17 | - **后台上传文件**。
18 | - **后台更新文件**。
19 | - **后台设置统计代码**
20 | - **后台管理onedrive文件**。
21 | - **删除onedrive文件**
22 | - **直接在后台给文件夹添加`.password`和`README`和`HEAD`**
23 | - **直接在后台编辑文本文件**。
24 | - **上传本地文件至onedrive**(2018.10.18更新)
25 | - **支持创建文件夹**(2018.10.19更新)
26 | - **支持移动文件(仅限单文件)**(2018.10.19更新)
27 | 4. **支持绑定多网盘!!!**(2018.11.15更新)
28 |
29 | ## 适用环境 ##
30 | 1. linux环境(推荐centos7)
31 | 2. Python2.7
32 | 3. **需要安装redis,MongoDB**
33 |
34 | **推荐预先安装宝塔,再进行安装**
35 |
36 | ## 安装教程 ##
37 | 请转移到我的博客查看[安装教程](https://www.abbeyok.com/?p=174)
38 |
39 | ## 更新源码步骤 ##
40 | - PyOne旧版本升级到3.0:**请重装!!!**
41 | - PyOne3.0内小版本更新:
42 | 1. 拉最新代码
43 | ```
44 | git pull
45 | ```
46 | 5. 重新安装依赖,看是否有新增的依赖包:
47 | ```
48 | pip install -r requirements.txt
49 | ```
50 | 6. 重启网站:
51 | ```
52 | supervisorctl -c supervisord.conf restart pyone
53 | ```
54 | 7. 最好更新一下文件缓存:
55 | ```
56 | python function.py UpdateFile
57 | ```
58 |
59 | ## 提供安装服务 ##
60 | [点击购买](https://iofaka.com/?gid=4)
61 |
62 |
--------------------------------------------------------------------------------
/templates/admin/install_1.html:
--------------------------------------------------------------------------------
1 | {%extends 'admin/layout.html'%}
2 | {% import '_macro.html' as macros %}
3 |
4 | {%block content%}
5 |
6 |
7 |
系统安装 绑定账号
8 |
9 |
10 |
11 |
步骤1:绑定账号,获取code
12 |
13 |
14 |
15 |
步骤2:输入code,结束绑定
16 |
1. 绑定账号
17 |
32 |
33 |
34 |
示例,如何操作
35 |

36 |
37 |
38 |
39 | {%endblock content%}
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .nox/
42 | .coverage
43 | .coverage.*
44 | .cache
45 | nosetests.xml
46 | coverage.xml
47 | *.cover
48 | .hypothesis/
49 | .pytest_cache/
50 |
51 | # Translations
52 | *.mo
53 | *.pot
54 |
55 | # Django stuff:
56 | *.log
57 | local_settings.py
58 | db.sqlite3
59 |
60 | # Flask stuff:
61 | instance/
62 | .webassets-cache
63 |
64 | # Scrapy stuff:
65 | .scrapy
66 |
67 | # Sphinx documentation
68 | docs/_build/
69 |
70 | # PyBuilder
71 | target/
72 |
73 | # Jupyter Notebook
74 | .ipynb_checkpoints
75 |
76 | # IPython
77 | profile_default/
78 | ipython_config.py
79 |
80 | # pyenv
81 | .python-version
82 |
83 | # celery beat schedule file
84 | celerybeat-schedule
85 |
86 | # SageMath parsed files
87 | *.sage.py
88 |
89 | # Environments
90 | .env
91 | .venv
92 | env/
93 | venv/
94 | ENV/
95 | env.bak/
96 | venv.bak/
97 |
98 | # Spyder project settings
99 | .spyderproject
100 | .spyproject
101 |
102 | # Rope project settings
103 | .ropeproject
104 |
105 | # mkdocs documentation
106 | /site
107 |
108 | # mypy
109 | .mypy_cache/
110 | .dmypy.json
111 | dmypy.json
112 |
113 | # PyOne config_file
114 | config.py
115 | AppUrl
116 | *.json
117 | supervisord.conf
118 |
--------------------------------------------------------------------------------
/templates/admin/setpass.html:
--------------------------------------------------------------------------------
1 | {%extends 'admin/layout.html'%}
2 |
3 | {%block content%}
4 |
9 |
10 |
添加文件 - {{filename}}
11 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
67 | {%endblock content%}
68 |
--------------------------------------------------------------------------------
/templates/admin/edit.html:
--------------------------------------------------------------------------------
1 | {%extends 'admin/layout.html'%}
2 |
3 | {%block content%}
4 |
9 |
10 |
{{content}}
11 |
14 |
15 |
16 |
17 |
18 |
19 |
66 | {%endblock content%}
67 |
--------------------------------------------------------------------------------
/templates/admin/cache.html:
--------------------------------------------------------------------------------
1 | {%extends 'admin/layout.html'%} {%block content%}
2 |
11 |
12 |
13 |
更新缓存 更新本地文件列表
14 |
15 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
59 | {%endblock content%}
60 |
--------------------------------------------------------------------------------
/templates/admin/setting.html:
--------------------------------------------------------------------------------
1 | {%extends 'admin/layout.html'%}
2 |
3 | {%block content%}
4 |
5 |
6 |
7 |
基本设置 设置PyOne基本参数
8 |
9 |
59 |
60 | {%endblock content%}
61 |
--------------------------------------------------------------------------------
/static/css/theme.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #f2f5fa;
3 | padding-bottom: 60px;
4 | background-image: url(../img/bg-bottom.svg);
5 | background-position: 50% 100%;
6 | background-repeat: no-repeat;
7 | background-attachment: fixed
8 | }
9 |
10 | .nexmoe-item {
11 | /*margin: 20px -8px 0 !important;*/
12 | padding: 15px !important;
13 | border-radius: 5px;
14 | background-color: #fff;
15 | -webkit-box-shadow: 0 .5em 3em rgba(161, 177, 204, .4);
16 | box-shadow: 0 .5em 3em rgba(161, 177, 204, .4);
17 | background-color: #fff
18 | }
19 |
20 | .mdui-img-fluid,
21 | .mdui-video-fluid {
22 | border-radius: 5px;
23 | border: 1px solid #eee
24 | }
25 |
26 | .mdui-list {
27 | padding: 0
28 | }
29 |
30 | .mdui-list-item {
31 | margin: 0 !important;
32 | border-radius: 5px;
33 | padding: 0 10px 0 5px !important;
34 | border: 1px solid #eee;
35 | margin-bottom: 10px !important
36 | }
37 |
38 | .mdui-list-item:last-child {
39 | margin-bottom: 0 !important
40 | }
41 |
42 | .mdui-list-item:first-child {
43 | border: none
44 | }
45 |
46 | .mdui-toolbar {
47 | width: auto;
48 | /*margin-top: 60px !important*/
49 | }
50 |
51 | .mdui-appbar .mdui-toolbar {
52 | height: 56px;
53 | font-size: 16px
54 | }
55 |
56 | .mdui-toolbar>* {
57 | padding: 0 6px;
58 | margin: 0 2px;
59 | opacity: .5
60 | }
61 |
62 | .mdui-toolbar>.mdui-typo-headline {
63 | padding: 0 16px 0 0
64 | }
65 |
66 | .mdui-toolbar>i {
67 | padding: 0
68 | }
69 |
70 | .mdui-toolbar>a:hover,
71 | a.mdui-typo-headline,
72 | a.active {
73 | opacity: 1
74 | }
75 |
76 | .mdui-container {
77 | max-width: 980px
78 | }
79 |
80 | .mdui-list>.th {
81 | background-color: initial
82 | }
83 |
84 | .mdui-list-item>a {
85 | width: 100%;
86 | line-height: 48px
87 | }
88 |
89 | .mdui-toolbar>a {
90 | padding: 0 16px;
91 | line-height: 30px;
92 | border-radius: 30px;
93 | border: 1px solid #eee
94 | }
95 |
96 | .mdui-toolbar>a:last-child {
97 | opacity: 1;
98 | background-color: #1e89f2;
99 | color: #ffff
100 | }
101 |
102 | @media screen and (max-width:980px) {
103 | .mdui-list-item .mdui-text-right {
104 | display: none
105 | }
106 |
107 | .mdui-container {
108 | width: 100% !important;
109 | margin: 0
110 | }
111 |
112 | .mdui-toolbar>* {
113 | display: none
114 | }
115 |
116 | .mdui-toolbar>a:last-child,
117 | .mdui-toolbar>.mdui-typo-headline,
118 | .mdui-toolbar>i:first-child {
119 | display: block
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/templates/admin/upload.html:
--------------------------------------------------------------------------------
1 | {%extends 'admin/layout.html'%} {%block content%}
2 |
11 |
12 |
13 |
上传管理 文件上传添加和管理
14 |
15 |
42 |
43 |
44 |
45 |
46 |
47 |
64 | {%endblock content%}
65 |
--------------------------------------------------------------------------------
/templates/admin/install_0.html:
--------------------------------------------------------------------------------
1 | {%extends 'admin/layout.html'%}
2 | {% import '_macro.html' as macros %}
3 |
4 | {%block content%}
5 |
6 |
7 |
系统安装 应用ID和机密
8 |
9 |
10 |
16 |
17 |
38 |
39 |
示例,分别获取client_secret和client_id
40 |
1. 首先获取应用ID(client_id)
41 |
2. 然后登陆:https://apps.dev.microsoft.com/,查到第一步获取的client_id,并进入相关应用
42 |
3. 点击生成新密码,获取一个新client_secret
43 |

44 |

45 |

46 |
47 |
48 |
49 |
50 | {%endblock content%}
51 |
--------------------------------------------------------------------------------
/templates/_macro.html:
--------------------------------------------------------------------------------
1 | {% macro pagination_widget(pagination, endpoint) %}
2 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | {% endmacro %}
39 |
40 | {% macro Arraw(name,cur_type,sortby,order) %}
41 |
42 | {{ name }}
43 | {% if cur_type==sortby %}
44 | {% if order=='asc' %}
45 | arrow_upward
46 | {% else %}
47 | arrow_downward
48 | {% endif %}
49 | {% endif %}
50 |
51 | {% endmacro %}
52 |
53 |
54 | {% macro Admin_Arraw(name,cur_type,sortby,order) %}
55 |
56 | {{ name }}
57 | {% if cur_type==sortby %}
58 | {% if order=='asc' %}
59 | arrow_upward
60 | {% else %}
61 | arrow_downward
62 | {% endif %}
63 | {% endif %}
64 |
65 | {% endmacro %}
66 |
--------------------------------------------------------------------------------
/templates/admin/layout.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | PyOne系统管理
7 |
8 |
9 |
10 |
11 |
12 |
29 |
30 |
31 |
40 |
64 |
65 |
66 |
67 |
68 | {% for message in get_flashed_messages() %}
69 |
{{message}}
70 | {% endfor %}
71 |
72 | {%block content%}
73 | {%endblock content%}
74 |
75 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/templates/index.html:
--------------------------------------------------------------------------------
1 | {%extends 'layout.html'%}
2 | {% import '_macro.html' as macros %}
3 |
4 | {%block content%}
5 |
6 |
7 |
12 |
13 | {%include 'head.html'%}
14 |
15 |
16 |
17 | -
18 |
{{ macros.Arraw(name='文件',cur_type='name',sortby=sortby,order=order,path=path) }}
19 | {{ macros.Arraw(name='修改时间',cur_type='lastModtime',sortby=sortby,order=order,path=path) }}
20 | {{ macros.Arraw(name='文件类型',cur_type='type',sortby=sortby,order=order,path=path) }}
21 | {{ macros.Arraw(name='大小',cur_type='size',sortby=sortby,order=order,path=path) }}
22 |
23 | {%if path.split(':')[-1]!=''%}
24 | -
25 | {%if '/'.join(path_list(path)[:-1])==''%}
26 |
27 | {%else%}
28 |
29 | {%endif%}
30 |
31 | arrow_upward 返回上一层
32 |
33 |
34 |
35 |
36 |
37 | {%endif%}
38 |
39 | {%for data in items%}
40 | {%if data['type']=='folder' %}
41 | -
42 |
43 |
44 | folder_open {{data['name']}}
45 |
46 | {{data['lastModtime']}}
47 | {{data['type']}}
48 | {{data['size']}}
49 |
50 |
51 | {%else%}
52 | -
53 |
54 | {%if image_mode==1%}
55 | {%if file_ico(data)=='image'%}
56 |
57 | {%else%}
58 |
59 | {{file_ico(data)}} {{data['name']}}
60 |
61 | {{data['lastModtime']}}
62 | {{data['type']}}
63 | {{data['size']}}
64 | {%endif%}
65 | {%else%}
66 |
67 | {{file_ico(data)}} {{data['name']}}
68 |
69 | {{data['lastModtime']}}
70 | {{data['type']}}
71 | {{data['size']}}
72 | {%endif%}
73 |
74 |
75 | {%endif%}
76 | {%endfor%}
77 |
78 |
79 | {%include 'readme.html'%}
80 |
81 |
82 |
83 |
84 |
85 | {% if pagination %}
86 | {{ macros.pagination_widget(pagination, endpoint,path=path,sortby=sortby,order=order) }}
87 | {% endif %}
88 |
89 |
90 |
122 | {%endblock content%}
123 |
--------------------------------------------------------------------------------
/templates/admin/manage.html:
--------------------------------------------------------------------------------
1 | {%extends 'admin/layout.html'%}
2 | {% import '_macro.html' as macros %}
3 | {%block content%}
4 |
24 |
25 |
文件管理 仅支持更新/编辑文编;支持删除文件
26 |
27 |
149 |
150 | {% if pagination %} {{ macros.pagination_widget(pagination, endpoint,path=path,sortby=sortby,order=order) }} {% endif %}
151 |
152 |
334 | {%endblock content%}
335 |
--------------------------------------------------------------------------------
/templates/admin/upload_local.html:
--------------------------------------------------------------------------------
1 | {%extends 'admin/layout.html'%} {%block content%}
2 |
3 |
4 |
5 |
50 |
51 |
52 |
本地文件上传至onedrive 本地文件->服务器->onedrive
53 |
54 |
55 |
56 |
59 |
60 |
61 |
62 |
63 |
64 | | 序号 |
65 | 文件名称 |
66 | 文件大小 |
67 | 本地->服务器上传状态 |
68 | 服务器->onedrive上传状态 |
69 | 操作 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
选择文件或将文件拖到这里
81 |
82 |
83 |
84 |
85 |
86 |
87 |
368 | {%endblock content%}
369 |
--------------------------------------------------------------------------------
/admin.py:
--------------------------------------------------------------------------------
1 | #-*- coding=utf-8 -*-
2 | from flask import Blueprint,redirect,url_for,request,render_template,flash,session,jsonify,Response,make_response
3 | from flask_sqlalchemy import Pagination
4 | from function import *
5 | from config import *
6 | from run import FetchData,path_list,GetName,CodeType,_remote_content,rd,has_item,AddResource
7 | import os
8 | import io
9 | import re
10 | import subprocess
11 | import random
12 | import urllib
13 | from shelljob import proc
14 | import eventlet
15 |
16 | eventlet.monkey_patch()
17 |
18 |
19 | admin = Blueprint('admin', __name__,url_prefix='/admin')
20 |
21 | ############功能函数
22 | def set(key,value,user='A'):
23 | allow_key=['title','downloadUrl_timeout','allow_site','password','client_secret','client_id','share_path','other_name','tj_code']
24 | if key not in allow_key:
25 | return u'禁止修改'
26 | print 'set {}:{}'.format(key,value)
27 | config_path=os.path.join(config_dir,'config.py')
28 | with open(config_path,'r') as f:
29 | old_text=f.read()
30 | with open(config_path,'w') as f:
31 | if key in ['client_secret','client_id','share_path','other_name']:
32 | old_kv=re.findall('"{}":{{[\w\W]*}}'.format(user),old_text)[0]
33 | new_kv=re.sub('"{}":.*?,'.format(key),'"{}":"{}",'.format(key,value),old_kv,1)
34 | new_text=old_text.replace(old_kv,new_kv,1)
35 | elif key=='allow_site':
36 | value=value.split(',')
37 | new_text=re.sub('{}=.*'.format(key),'{}={}'.format(key,value),old_text)
38 | elif key=='tj_code':
39 | new_text=re.sub('{}=.*'.format(key),'{}="""{}"""'.format(key,value),old_text)
40 | else:
41 | new_text=re.sub('{}=.*'.format(key),'{}="{}"'.format(key,value),old_text)
42 | f.write(new_text)
43 |
44 |
45 | ############视图函数
46 | @admin.before_request
47 | def before_request():
48 | if request.endpoint.startswith('admin') and request.endpoint!='admin.login' and session.get('login') is None: #and request.endpoint!='admin.install'
49 | return redirect(url_for('admin.login'))
50 |
51 |
52 | ########web console
53 | @admin.route('/web_console')
54 | def web_console():
55 | g = proc.Group()
56 | action=request.args.get('action')
57 | allow_action=['UpdateFile','UploadDir','Upload']
58 | if action not in allow_action:
59 | return make_response('error')
60 | if action in ['UploadDir','Upload']:
61 | local=urllib.unquote(request.args.get('local'))
62 | remote=urllib.unquote(request.args.get('remote'))
63 | user=urllib.unquote(request.args.get('user'))
64 | cmd=["python","-u",os.path.join(config_dir,'function.py'),action,local,remote,user]
65 | elif action=='UpdateFile':
66 | type_=request.args.get('type')
67 | cmd=["python","-u",os.path.join(config_dir,'function.py'),'UpdateFile',type_]
68 | else:
69 | cmd=["python","-u",os.path.join(config_dir,'function.py'),action]
70 | p = g.run(cmd)
71 | def read_process():
72 | while g.is_pending():
73 | lines = g.readlines()
74 | for proc, line in lines:
75 | yield "data:" + line + "\n\n"
76 | yield "data:end\n\n"
77 | return Response(read_process(), mimetype= 'text/event-stream')
78 |
79 | ########admin
80 | @admin.route('/',methods=['GET','POST'])
81 | @admin.route('/setting',methods=['GET','POST'])
82 | def setting():
83 | if request.method=='POST':
84 | title=request.form.get('title','PyOne')
85 | downloadUrl_timeout=request.form.get('downloadUrl_timeout',5*60)
86 | allow_site=request.form.get('allow_site','no-referrer')
87 | tj_code=request.form.get('tj_code','')
88 | password1=request.form.get('password1')
89 | password2=request.form.get('password2')
90 | new_password=password
91 | if ((password1 is not None and password2 is None) or (password1 is None and password2 is not None)):
92 | flash(u'请输入新密码或者二次确认新密码')
93 | elif password1 is not None and password2 is not None and password1!=password2:
94 | flash(u'两次输入密码不相同')
95 | elif password1 is not None and password2 is not None and password1==password2 and password1!='':
96 | new_password=password1
97 | set('title',title)
98 | set('downloadUrl_timeout',downloadUrl_timeout)
99 | set('allow_site',allow_site)
100 | set('tj_code',tj_code)
101 | set('password',new_password)
102 | ####网盘信息处理
103 | for k,v in request.form.to_dict().items():
104 | if 'share_path' in k or 'other_name' in k:
105 | user=re.findall('\[(.*?)\]',k)[0]
106 | key=re.findall('(.*)\[',k)[0]
107 | print('setting {}\'s {}\'s value {}'.format(user,key,v))
108 | set(key,v,user)
109 | reload()
110 | return render_template('admin/setting.html')
111 | return render_template('admin/setting.html')
112 |
113 |
114 |
115 |
116 | @admin.route('/upload',methods=["POST","GET"])
117 | def upload():
118 | if request.method=='POST':
119 | user=request.form.get('user').encode('utf-8')
120 | local=request.form.get('local').encode('utf-8')
121 | remote=request.form.get('remote').encode('utf-8')
122 | if not os.path.exists(local):
123 | flash('本地目录/文件不存在')
124 | return redirect(url_for('admin.upload'))
125 | if os.path.isfile(local):
126 | action='Upload'
127 | else:
128 | action='UploadDir'
129 | return render_template('admin/upload.html',remote=remote,local=local,action=action,user=user)
130 | return render_template('admin/upload.html')
131 |
132 |
133 |
134 | @admin.route('/cache',methods=["POST","GET"])
135 | def cache():
136 | if request.method=='POST':
137 | type=request.form.get('type')
138 | return render_template('admin/cache.html',type=type,action='UpdateFile')
139 | return render_template('admin/cache.html')
140 |
141 |
142 | @admin.route('/manage',methods=["POST","GET"])
143 | def manage():
144 | if request.method=='POST':
145 | pass
146 | path=urllib.unquote(request.args.get('path','A:/'))
147 | user,n_path=path.split(':')
148 | if n_path=='':
149 | path=':'.join([user,'/'])
150 | page=request.args.get('page',1,type=int)
151 | image_mode=request.args.get('image_mode')
152 | sortby=request.args.get('sortby')
153 | order=request.args.get('order')
154 | if sortby:
155 | sortby=request.args.get('sortby')
156 | else:
157 | sortby=request.cookies.get('admin_sortby') if request.cookies.get('admin_sortby') is not None else 'lastModtime'
158 | sortby=sortby
159 | if order:
160 | order=request.args.get('order')
161 | else:
162 | order=request.cookies.get('admin_order') if request.cookies.get('admin_order') is not None else 'desc'
163 | order=order
164 | resp,total = FetchData(path=path,page=page,per_page=50,sortby=sortby,order=order)
165 | pagination=Pagination(query=None,page=page, per_page=50, total=total, items=None)
166 | if path.split(':',1)[-1]=='/':
167 | path=':'.join([path.split(':',1)[0],''])
168 | resp=make_response(render_template('admin/manage.html',pagination=pagination,items=resp,path=path,sortby=sortby,order=order,cur_user=user,endpoint='admin.manage'))
169 | resp.set_cookie('admin_sortby',str(sortby))
170 | resp.set_cookie('admin_order',str(order))
171 | return resp
172 |
173 |
174 | @admin.route('/edit',methods=["GET","POST"])
175 | def edit():
176 | if request.method=='POST':
177 | fileid=request.form.get('fileid')
178 | user=request.form.get('user')
179 | content=request.form.get('content').encode('utf-8')
180 | info={}
181 | token=GetToken(user=user)
182 | app_url=GetAppUrl()
183 | headers={'Authorization':'bearer {}'.format(token)}
184 | url=app_url+'v1.0/me/drive/items/{}/content'.format(fileid)
185 | try:
186 | r=requests.put(url,headers=headers,data=content,timeout=10)
187 | data=json.loads(r.content)
188 | if data.get('id'):
189 | info['status']=0
190 | info['msg']='修改成功'
191 | rd.delete('{}:content'.format(fileid))
192 | file=items.find_one({'id':fileid})
193 | name=file['name']
194 | path=file['path'].replace('/'+name,'').replace(name,'')
195 | if path=='':
196 | path='/'
197 | if not path.startswith('/'):
198 | path='/'+path
199 | path='{}:{}'.format(user,path)
200 | key='has_item$#$#$#$#{}$#$#$#$#{}'.format(path,name)
201 | rd.delete(key)
202 | else:
203 | info['status']=0
204 | info['msg']=data.get('error').get('message')
205 | except Exception as e:
206 | print e
207 | info['status']=0
208 | info['msg']='修改超时'
209 | return jsonify(info)
210 | fileid=request.args.get('fileid')
211 | user=request.args.get('user')
212 | name=GetName(fileid)
213 | ext=name.split('.')[-1]
214 | language=CodeType(ext)
215 | if language is None:
216 | language='Text'
217 | content=_remote_content(fileid,user)
218 | return render_template('admin/edit.html',content=content,fileid=fileid,name=name,language=language,cur_user=user)
219 |
220 | ###本地上传文件只onedrive,通过服务器中转
221 | @admin.route('/upload_local',methods=['POST','GET'])
222 | def upload_local():
223 | user,remote_folder=request.args.get('path').split(':')
224 | return render_template('admin/upload_local.html',remote_folder=remote_folder,cur_user=user)
225 |
226 | @admin.route('/checkChunk', methods=['POST'])
227 | def checkChunk():
228 | md5=request.form.get('fileMd5')
229 | fileName=request.form.get('name').encode('utf-8')
230 | chunk=request.form.get('chunk',0,type=int)
231 | filename = u'./upload/{}-{}'.format(fileName, chunk)
232 | if os.path.exists(filename):
233 | exists=True
234 | else:
235 | exists=False
236 | return jsonify({'ifExist':exists})
237 |
238 |
239 | @admin.route('/mergeChunks', methods=['POST'])
240 | def mergeChunks():
241 | fileName=request.form.get('fileName').encode('utf-8')
242 | md5=request.form.get('fileMd5')
243 | chunk = 0 # 分片序号
244 | with open(u'./upload/{}'.format(fileName), 'wb') as target_file: # 创建新文件
245 | while True:
246 | try:
247 | filename = u'./upload/{}-{}'.format(fileName, chunk)
248 | source_file = open(filename, 'rb') # 按序打开每个分片
249 | target_file.write(source_file.read()) # 读取分片内容写入新文件
250 | source_file.close()
251 | except IOError as msg:
252 | break
253 | chunk += 1
254 | os.remove(filename) # 删除该分片,节约空间
255 | return jsonify({'upload':True})
256 |
257 |
258 | @admin.route('/recv_upload', methods=['POST'])
259 | def recv_upload(): # 接收前端上传的一个分片
260 | md5=request.form.get('fileMd5')
261 | name=request.form.get('name').encode('utf-8')
262 | chunk_id=request.form.get('chunk',0,type=int)
263 | filename = '{}-{}'.format(name,chunk_id)
264 | upload_file = request.files['file']
265 | upload_file.save(u'./upload/{}'.format(filename))
266 | return jsonify({'upload_part':True})
267 |
268 |
269 | @admin.route('/to_one',methods=['GET'])
270 | def server_to_one():
271 | user=request.args.get('user')
272 | filename=request.args.get('filename').encode('utf-8')
273 | remote_folder=request.args.get('remote_folder').encode('utf-8')
274 | if remote_folder!='/':
275 | remote_folder=remote_folder+'/'
276 | local_dir=os.path.join(config_dir,'upload')
277 | filepath=urllib.unquote(os.path.join(local_dir,filename))
278 | _upload_session=Upload_for_server(filepath,remote_folder,user)
279 | def read_status():
280 | while 1:
281 | try:
282 | msg=_upload_session.next()['status']
283 | yield "data:" + msg + "\n\n"
284 | except Exception as e:
285 | msg='end'
286 | yield "data:" + msg + "\n\n"
287 | os.remove(filepath)
288 | break
289 | return Response(read_status(), mimetype= 'text/event-stream')
290 |
291 |
292 |
293 | ###本地上传文件只onedrive,通过服务器中转
294 | @admin.route('/setFile',methods=["GET","POST"])
295 | @admin.route('/setFile/',methods=["GET","POST"])
296 | def setFile(filename=None):
297 | if request.method=='POST':
298 | path=request.form.get('path')
299 | user,n_path=path.split(':')
300 | filename=request.form.get('filename')
301 | if not n_path.startswith('/'):
302 | n_path='/'+n_path
303 | remote_file=os.path.join(n_path,filename)
304 | content=request.form.get('content').encode('utf-8')
305 | info={}
306 | token=GetToken(user=user)
307 | app_url=GetAppUrl()
308 | headers={'Authorization':'bearer {}'.format(token)}
309 | url=app_url+'v1.0/me/drive/items/root:{}:/content'.format(remote_file)
310 | try:
311 | r=requests.put(url,headers=headers,data=content,timeout=10)
312 | data=json.loads(r.content)
313 | if data.get('id'):
314 | AddResource(data,user)
315 | info['status']=0
316 | info['msg']='添加成功'
317 | key='has_item$#$#$#$#{}$#$#$#$#{}'.format(path,filename)
318 | rd.delete(key)
319 | else:
320 | info['status']=0
321 | info['msg']=data.get('error').get('message')
322 | except Exception as e:
323 | info['status']=0
324 | info['msg']='超时'
325 | return jsonify(info)
326 | path=urllib.unquote(request.args.get('path'))
327 | user,n_path=path.split(':')
328 | _,fid,i=has_item(path,filename)
329 | if fid!=False:
330 | return redirect(url_for('admin.edit',fileid=fid,user=user))
331 | return render_template('admin/setpass.html',path=path,filename=filename,cur_user=user)
332 |
333 |
334 | @admin.route('/delete',methods=["POST"])
335 | def delete():
336 | ids=request.form.get('id')
337 | user=request.form.get('user')
338 | if ids is None:
339 | return jsonify({'msg':u'请选择要删除的文件','status':0})
340 | ids=ids.split('##')
341 | infos={}
342 | infos['status']=1
343 | infos['delete']=0
344 | infos['fail']=0
345 | for id in ids:
346 | file=items.find_one({'id':id})
347 | name=file['name']
348 | # path=file['path'].replace('/'+name,'').replace(name,'')
349 | if file['parent']=='':
350 | path='/'
351 | else:
352 | path=items.find_one({'id':file['parent']})['path']
353 | if not path.startswith('/'):
354 | path='/'+path
355 | path='{}:{}'.format(user,path)
356 | key='has_item$#$#$#$#{}$#$#$#$#{}'.format(path,name)
357 | rd.delete(key)
358 | kc='{}:content'.format(id)
359 | rd.delete(kc)
360 | status=DeleteRemoteFile(id,user)
361 | if status:
362 | infos['delete']+=1
363 | else:
364 | infos['fail']+=1
365 | return jsonify(infos)
366 |
367 |
368 | @admin.route('/add_folder',methods=['POST'])
369 | def AddFolder():
370 | folder_name=request.form.get('folder_name')
371 | path=request.args.get('path')
372 | user,grand_path=path.split(':')
373 | if grand_path=='' or grand_path is None:
374 | grand_path='/'
375 | else:
376 | if grand_path.startswith('/'):
377 | grand_path=grand_path[1:]
378 | result=CreateFolder(folder_name,grand_path,user)
379 | return jsonify({'result':result})
380 |
381 | @admin.route('/move_file',methods=['POST'])
382 | def MoveFileToNewFolder():
383 | fileid=request.form.get('fileid')
384 | user=request.form.get('user')
385 | new_folder_path=request.form.get('new_folder_path')
386 | if new_folder_path=='' or new_folder_path is None:
387 | new_folder_path='/'
388 | else:
389 | if new_folder_path.startswith('/'):
390 | new_folder_path=new_folder_path[1:]
391 | result=MoveFile(fileid,new_folder_path,user)
392 | return jsonify({'result':result})
393 |
394 |
395 |
396 |
397 |
398 | @admin.route('/login',methods=["POST","GET"])
399 | def login():
400 | if request.method=='POST':
401 | password1=request.form.get('password')
402 | if password1==password:
403 | session['login']='true'
404 | if len(os.listdir(os.path.join(config_dir,'data')))<=1:
405 | return redirect(url_for('admin.install',step=0,user='A'))
406 | return redirect(url_for('admin.setting'))
407 | else:
408 | return render_template('admin/login.html')
409 | return render_template('admin/login.html')
410 |
411 |
412 | @admin.route('/logout',methods=['GET','POST'])
413 | def logout():
414 | session.pop('login',None)
415 | return redirect('/')
416 |
417 | @admin.route('/reload',methods=['GET','POST'])
418 | def reload():
419 | cmd='supervisorctl -c {} restart pyone'.format(os.path.join(config_dir,'supervisord.conf'))
420 | subprocess.Popen(cmd,shell=True)
421 | flash('正在重启网站...如果更改了分享目录,请更新缓存')
422 | return redirect(url_for('admin.setting'))
423 |
424 | ###########################################安装
425 | @admin.route('/install',methods=['POST','GET'])
426 | def install():
427 | if request.method=='POST':
428 | step=request.form.get('step',type=int)
429 | user=request.form.get('user')
430 | if step==1:
431 | client_secret=request.form.get('client_secret')
432 | client_id=request.form.get('client_id')
433 | set('client_secret',client_secret,user)
434 | set('client_id',client_id,user)
435 | login_url=LoginUrl.format(client_id=client_id,redirect_uri=redirect_uri)
436 | return render_template('admin/install_1.html',client_secret=client_secret,client_id=client_id,login_url=login_url,cur_user=user)
437 | else:
438 | client_secret=request.form.get('client_secret')
439 | client_id=request.form.get('client_id')
440 | code=request.form.get('code')
441 | #授权
442 | headers['Content-Type']='application/x-www-form-urlencoded'
443 | data=AuthData.format(client_id=client_id,redirect_uri=urllib.quote(redirect_uri),client_secret=client_secret,code=code)
444 | url=OAuthUrl
445 | r=requests.post(url,data=data,headers=headers)
446 | Atoken=json.loads(r.text)
447 | if Atoken.get('access_token'):
448 | with open(os.path.join(config_dir,'data/{}_Atoken.json'.format(user)),'w') as f:
449 | json.dump(Atoken,f,ensure_ascii=False)
450 | refresh_token=Atoken.get('refresh_token')
451 | token=ReFreshToken(refresh_token,user)
452 | with open(os.path.join(config_dir,'data/{}_token.json'.format(user)),'w') as f:
453 | json.dump(token,f,ensure_ascii=False)
454 | return make_response('授权成功!点击进入首页
请在后台另开一个ssh窗口,运行:python function.py UpdateFile
进行更新数据操作
')
455 | else:
456 | return jsonify(Atoken)
457 | step=request.args.get('step',type=int)
458 | user=request.args.get('user','A')
459 | resp=render_template('admin/install_0.html',step=step,cur_user=user)
460 | return resp
461 |
462 |
463 |
--------------------------------------------------------------------------------
/run.py:
--------------------------------------------------------------------------------
1 |
2 | #-*- coding=utf-8 -*-
3 | from flask import Flask,render_template,redirect,abort,make_response,jsonify,request,url_for,Response
4 | from flask_sqlalchemy import Pagination
5 | import json
6 | from collections import OrderedDict
7 | import subprocess
8 | import hashlib
9 | import random
10 | import markdown
11 | from function import *
12 | from config import *
13 | from flask_caching import Cache
14 | from flask_limiter import Limiter
15 | from flask_limiter.util import get_remote_address
16 | from shelljob import proc
17 | import time
18 | import os
19 | import sys
20 | import eventlet
21 |
22 | eventlet.monkey_patch()
23 |
24 | reload(sys)
25 | sys.setdefaultencoding("utf-8")
26 |
27 |
28 | #######flask
29 | app=Flask(__name__)
30 | app.secret_key=os.path.join(config_dir,'PyOne'+password)
31 | cache = Cache(app, config={'CACHE_TYPE': 'redis'})
32 | limiter = Limiter(
33 | app,
34 | key_func=get_remote_address,
35 | default_limits=["200/minute", "50/second"],
36 | )
37 |
38 |
39 | ################################################################################
40 | ###################################功能函数#####################################
41 | ################################################################################
42 | def md5(string):
43 | a=hashlib.md5()
44 | a.update(string.encode(encoding='utf-8'))
45 | return a.hexdigest()
46 |
47 | def GetTotal(path='A:/'):
48 | key='total:{}'.format(path)
49 | if rd.exists(key):
50 | return int(rd.get(key))
51 | else:
52 | user,n_path=path.split(':')
53 | if n_path=='/':
54 | total=items.find({'grandid':0}).count()
55 | else:
56 | f=items.find_one({'path':path})
57 | pid=f['id']
58 | total=items.find({'parent':pid}).count()
59 | rd.set(key,total,300)
60 | return total
61 |
62 |
63 | # @cache.memoize(timeout=60*5)
64 | def FetchData(path='A:/',page=1,per_page=50,sortby='lastModtime',order='desc',dismiss=False):
65 | path=urllib.unquote(path)
66 | resp=[]
67 | if sortby not in ['lastModtime','type','size','name']:
68 | sortby='lastModtime'
69 | if sortby=='size':
70 | sortby='size_order'
71 | if order=='desc':
72 | order=DESCENDING
73 | else:
74 | order=ASCENDING
75 | try:
76 | user,n_path=path.split(':')
77 | if n_path=='/':
78 | data=items.find({'grandid':0,'user':user}).collation({"locale": "zh", 'numericOrdering':True})\
79 | .sort([('order',ASCENDING),(sortby,order)])\
80 | .limit(per_page).skip((page-1)*per_page)
81 | for d in data:
82 | item={}
83 | item['name']=d['name']
84 | item['id']=d['id']
85 | item['lastModtime']=d['lastModtime']
86 | item['size']=d['size']
87 | item['type']=d['type']
88 | if dismiss:
89 | if d['name'] not in ('README.md','README.txt','readme.md','readme.txt','.password','HEAD.md','HEAD.txt','head.md','head.txt'):
90 | resp.append(item)
91 | else:
92 | resp.append(item)
93 | total=GetTotal(path)
94 | else:
95 | f=items.find_one({'path':path})
96 | pid=f['id']
97 | if f['type']!='folder':
98 | return f,'files'
99 | data=items.find({'parent':pid}).collation({"locale": "zh", 'numericOrdering':True})\
100 | .sort([('order',ASCENDING),(sortby,order)])\
101 | .limit(per_page).skip((page-1)*per_page)
102 | for d in data:
103 | item={}
104 | item['name']=d['name']
105 | item['id']=d['id']
106 | item['lastModtime']=d['lastModtime']
107 | item['size']=d['size']
108 | item['type']=d['type']
109 | if dismiss:
110 | if d['name'] not in ('README.md','README.txt','readme.md','readme.txt','.password','HEAD.md','HEAD.txt','head.md','head.txt'):
111 | resp.append(item)
112 | else:
113 | resp.append(item)
114 | total=GetTotal(path)
115 | except:
116 | resp=[]
117 | total=0
118 | return resp,total
119 |
120 | @cache.memoize(timeout=60*5)
121 | def _thunbnail(id,user):
122 | app_url=GetAppUrl()
123 | token=GetToken(user=user)
124 | headers={'Authorization':'bearer {}'.format(token),'Content-type':'application/json'}
125 | url=app_url+'v1.0/me/drive/items/{}/thumbnails/0?select=large'.format(id)
126 | r=requests.get(url,headers=headers)
127 | data=json.loads(r.content)
128 | if data.get('large').get('url'):
129 | return data.get('large').get('url')
130 | else:
131 | return False
132 |
133 | @cache.memoize(timeout=60*5)
134 | def _getdownloadurl(id,user):
135 | app_url=GetAppUrl()
136 | token=GetToken(user=user)
137 | filename=GetName(id)
138 | ext=filename.split('.')[-1]
139 | if ext in ['webm','avi','mpg', 'mpeg', 'rm', 'rmvb', 'mov', 'wmv', 'mkv', 'asf']:
140 | downloadUrl=_thunbnail(id,user)
141 | downloadUrl=downloadUrl.replace('thumbnail','videomanifest')+'&part=index&format=dash&useScf=True&pretranscode=0&transcodeahead=0'
142 | return downloadUrl
143 | else:
144 | headers={'Authorization':'bearer {}'.format(token),'Content-type':'application/json'}
145 | url=app_url+'v1.0/me/drive/items/'+id
146 | r=requests.get(url,headers=headers)
147 | data=json.loads(r.content)
148 | if data.get('@microsoft.graph.downloadUrl'):
149 | return data.get('@microsoft.graph.downloadUrl')
150 | else:
151 | return False
152 |
153 | def GetDownloadUrl(id,user):
154 | if rd.exists('downloadUrl2:{}'.format(id)):
155 | downloadUrl,ftime=rd.get('downloadUrl2:{}'.format(id)).split('####')
156 | if time.time()-int(ftime)>=600:
157 | # print('{} downloadUrl expired!'.format(id))
158 | downloadUrl=_getdownloadurl(id,user)
159 | ftime=int(time.time())
160 | k='####'.join([downloadUrl,str(ftime)])
161 | rd.set('downloadUrl2:{}'.format(id),k)
162 | else:
163 | # print('get {}\'s downloadUrl from cache'.format(id))
164 | downloadUrl=downloadUrl
165 | else:
166 | # print('first time get downloadUrl from {}'.format(id))
167 | downloadUrl=_getdownloadurl(id,user)
168 | ftime=int(time.time())
169 | k='####'.join([downloadUrl,str(ftime)])
170 | rd.set('downloadUrl2:{}'.format(id),k)
171 | return downloadUrl
172 |
173 |
174 |
175 | # @cache.memoize(timeout=60*5)
176 | def GetReadMe(path):
177 | # README
178 | ext='Markdown'
179 | readme,_,i=has_item(path,'README.md')
180 | if readme==False:
181 | readme,_,i=has_item(path,'readme.md')
182 | if readme==False:
183 | ext='Text'
184 | readme,_,i=has_item(path,'readme.txt')
185 | if readme==False:
186 | ext='Text'
187 | readme,_,i=has_item(path,'README.txt')
188 | if readme!=False:
189 | readme=markdown.markdown(readme)
190 | return readme,ext
191 |
192 |
193 | # @cache.memoize(timeout=60*5)
194 | def GetHead(path):
195 | # README
196 | ext='Markdown'
197 | head,_,i=has_item(path,'HEAD.md')
198 | if head==False:
199 | head,_,i=has_item(path,'head.md')
200 | if head==False:
201 | ext='Text'
202 | head,_,i=has_item(path,'head.txt')
203 | if head==False:
204 | ext='Text'
205 | head,_,i=has_item(path,'HEAD.txt')
206 | if head!=False:
207 | head=markdown.markdown(head)
208 | return head,ext
209 |
210 |
211 | def CanEdit(filename):
212 | ext=filename.split('.')[-1]
213 | if ext in ["html","htm","php","css","go","java","js","json","txt","sh","md",".password"]:
214 | return True
215 | else:
216 | return False
217 |
218 | def CodeType(ext):
219 | code_type={}
220 | code_type['html'] = 'html';
221 | code_type['htm'] = 'html';
222 | code_type['php'] = 'php';
223 | code_type['css'] = 'css';
224 | code_type['go'] = 'golang';
225 | code_type['java'] = 'java';
226 | code_type['js'] = 'javascript';
227 | code_type['json'] = 'json';
228 | code_type['txt'] = 'Text';
229 | code_type['sh'] = 'sh';
230 | code_type['md'] = 'Markdown';
231 | return code_type.get(ext.lower())
232 |
233 | def file_ico(item):
234 | ext = item['name'].split('.')[-1].lower()
235 | if ext in ['bmp','jpg','jpeg','png','gif']:
236 | return "image";
237 |
238 | if ext in ['mp4','mkv','webm','avi','mpg', 'mpeg', 'rm', 'rmvb', 'mov', 'wmv', 'mkv', 'asf']:
239 | return "ondemand_video";
240 |
241 | if ext in ['ogg','mp3','wav']:
242 | return "audiotrack";
243 |
244 | return "insert_drive_file";
245 |
246 | def _remote_content(fileid,user):
247 | kc='{}:content'.format(fileid)
248 | if rd.exists(kc):
249 | return rd.get(kc)
250 | else:
251 | downloadUrl=GetDownloadUrl(fileid,user)
252 | if downloadUrl:
253 | r=requests.get(downloadUrl)
254 | r.encoding='utf-8'
255 | content=r.content
256 | rd.set(kc,content)
257 | return content
258 | else:
259 | return False
260 |
261 | # @cache.memoize(timeout=60)
262 | def has_item(path,name):
263 | if items.count()==0:
264 | return False,False,False
265 | key='has_item$#$#$#$#{}$#$#$#$#{}'.format(path,name)
266 | if rd.exists(key):
267 | values=rd.get(key)
268 | item,fid,cur=values.split('########')
269 | if item=='False':
270 | item=False
271 | if cur=='False':
272 | cur=False
273 | else:
274 | cur=True
275 | if fid=='False':
276 | fid=False
277 | return item,fid,cur
278 | else:
279 | item=False
280 | fid=False
281 | dz=False
282 | cur=False
283 | if name=='.password':
284 | dz=True
285 | try:
286 | user,n_path=path.split(':')
287 | if n_path=='/':
288 | if items.find_one({'grandid':0,'name':name,'user':user}):
289 | fid=items.find_one({'grandid':0,'name':name,'user':user})['id']
290 | item=_remote_content(fid,user).strip()
291 | else:
292 | route=n_path[1:].split('/')
293 | if name=='.password':
294 | for idx,r in enumerate(route):
295 | p=user+':/'+'/'.join(route[:idx+1])
296 | f=items.find_one({'path':p})
297 | pid=f['id']
298 | data=items.find_one({'name':name,'parent':pid})
299 | if data:
300 | fid=data['id']
301 | item=_remote_content(fid,user).strip()
302 | if idx==len(route)-1:
303 | cur=True
304 | else:
305 | f=items.find_one({'path':path})
306 | pid=f['id']
307 | data=items.find_one({'name':name,'parent':pid})
308 | if data:
309 | fid=data['id']
310 | item=_remote_content(fid,user).strip()
311 | except:
312 | item=False
313 | rd.set(key,'{}########{}########{}'.format(item,fid,cur))
314 | return item,fid,cur
315 |
316 |
317 |
318 | def verify_pass_before(path):
319 | plist=path_list(path)
320 | for i in [i for i in range(len(plist))]:
321 | n='/'.join(plist[:-i])
322 | yield n
323 |
324 | def has_verify(path):
325 | verify=False
326 | md5_p=md5(path)
327 | passwd,fid,cur=has_item(path,'.password')
328 | if fid and cur:
329 | vp=request.cookies.get(md5_p)
330 | if passwd==vp:
331 | verify=True
332 | else:
333 | for last in verify_pass_before(path):
334 | if last=='':
335 | last='/'
336 | passwd,fid,cur=has_item(last,'.password')
337 | md5_p=md5(last)
338 | vp=request.cookies.get(md5_p)
339 | if passwd==vp:
340 | verify=True
341 | return verify
342 |
343 |
344 | def path_list(path):
345 | path=urllib.unquote(path)
346 | if path.split(':',1)=='':
347 | plist=[path+'/']
348 | else:
349 | user,n_path=path.split(':',1)
350 | if n_path.startswith('/'):
351 | n_path=n_path[1:]
352 | if n_path.endswith('/'):
353 | n_path=n_path[:-1]
354 | plist=n_path.split('/')
355 | plist=['{}:/{}'.format(user,plist[0])]+plist[1:]
356 | return plist
357 |
358 | def get_od_user():
359 | config_path=os.path.join(config_dir,'config.py')
360 | with open(config_path,'r') as f:
361 | text=f.read()
362 | users=json.loads(re.findall('od_users=([\w\W]*})',text)[0])
363 | ret=[]
364 | for user,value in users.items():
365 | if value.get('client_id')!='':
366 | #userid,username,endpoint,sharepath,order,
367 | ret.append(
368 | (
369 | user,
370 | value.get('other_name'),
371 | '/{}:'.format(user),
372 | value.get('share_path'),
373 | value.get('order')
374 | )
375 | )
376 | else:
377 | ret.append(
378 | (
379 | user,
380 | '添加网盘',
381 | url_for('admin.install',step=0,user=user),
382 | value.get('share_path'),
383 | value.get('order')
384 | )
385 | )
386 | ret=sorted(ret,key=lambda x:x[-1],reverse=False)
387 | return ret
388 |
389 |
390 |
391 |
392 | ################################################################################
393 | ###################################试图函数#####################################
394 | ################################################################################
395 | @app.before_request
396 | def before_request():
397 | bad_ua=['Googlebot-Image','FeedDemon ','BOT/0.1 (BOT for JCE)','CrawlDaddy ','Java','Feedly','UniversalFeedParser','ApacheBench','Swiftbot','ZmEu','Indy Library','oBot','jaunty','YandexBot','AhrefsBot','MJ12bot','WinHttp','EasouSpider','HttpClient','Microsoft URL Control','YYSpider','jaunty','Python-urllib','lightDeckReports Bot','PHP','vxiaotou-spider','spider']
398 | global referrer
399 | try:
400 | ip = request.headers['X-Forwarded-For'].split(',')[0]
401 | except:
402 | ip = request.remote_addr
403 | try:
404 | ua = request.headers.get('User-Agent')
405 | except:
406 | ua="null"
407 | if sum([i.lower() in ua.lower() for i in bad_ua])>0:
408 | return redirect('http://www.baidu.com')
409 | # print '{}:{}:{}'.format(request.endpoint,ip,ua)
410 | referrer=request.referrer if request.referrer is not None else 'no-referrer'
411 |
412 | @app.route('/',methods=['POST','GET'])
413 | @app.route('/',methods=['POST','GET'])
414 | @limiter.limit("200/minute;50/second")
415 | def index(path='A:/'):
416 | if path=='favicon.ico':
417 | return redirect('https://onedrive.live.com/favicon.ico')
418 | if items.count()==0:
419 | if not os.path.exists(os.path.join(config_dir,'data/.install')):
420 | return redirect(url_for('admin.install',step=0,user='A'))
421 | else:
422 | #subprocess.Popen('python {} UpdateFile'.format(os.path.join(config_dir,'function.py')),shell=True)
423 | return make_response('正在更新数据!如果您是网站管理员,请在后台运行命令:python function.py UpdateFile
')
424 | #参数
425 | user,n_path=path.split(':')
426 | if n_path=='':
427 | path=':'.join([user,'/'])
428 | page=request.args.get('page',1,type=int)
429 | image_mode=request.args.get('image_mode')
430 | sortby=request.args.get('sortby')
431 | order=request.args.get('order')
432 | resp,total = FetchData(path=path,page=page,per_page=50,sortby=sortby,order=order,dismiss=True)
433 | if total=='files':
434 | return show(resp['id'],user)
435 | #是否有密码
436 | password,_,cur=has_item(path,'.password')
437 | md5_p=md5(path)
438 | has_verify_=has_verify(path)
439 | if request.method=="POST":
440 | password1=request.form.get('password')
441 | if password1==password:
442 | resp=make_response(redirect(url_for('.index',path=path)))
443 | resp.delete_cookie(md5_p)
444 | resp.set_cookie(md5_p,password)
445 | return resp
446 | if password!=False:
447 | if (not request.cookies.get(md5_p) or request.cookies.get(md5_p)!=password) and has_verify_==False:
448 | return render_template('password.html',path=path,cur_user=user)
449 | readme,ext_r=GetReadMe(path)
450 | head,ext_d=GetHead(path)
451 | #设置cookies
452 | if image_mode:
453 | image_mode=request.args.get('image_mode',type=int)
454 | else:
455 | image_mode=request.cookies.get('image_mode') if request.cookies.get('image_mode') is not None else 0
456 | image_mode=int(image_mode)
457 | if sortby:
458 | sortby=request.args.get('sortby')
459 | else:
460 | sortby=request.cookies.get('sortby') if request.cookies.get('sortby') is not None else 'lastModtime'
461 | sortby=sortby
462 | if order:
463 | order=request.args.get('order')
464 | else:
465 | order=request.cookies.get('order') if request.cookies.get('order') is not None else 'desc'
466 | order=order
467 | #参数
468 | resp,total = FetchData(path=path,page=page,per_page=50,sortby=sortby,order=order,dismiss=True)
469 | pagination=Pagination(query=None,page=page, per_page=50, total=total, items=None)
470 | if path.split(':',1)[-1]=='/':
471 | path=':'.join([path.split(':',1)[0],''])
472 | resp=make_response(render_template('index.html'
473 | ,pagination=pagination
474 | ,items=resp
475 | ,path=path
476 | ,image_mode=image_mode
477 | ,readme=readme
478 | ,ext_r=ext_r
479 | ,head=head
480 | ,ext_d=ext_d
481 | ,sortby=sortby
482 | ,order=order
483 | ,cur_user=user
484 | ,endpoint='.index'))
485 | resp.set_cookie('image_mode',str(image_mode))
486 | resp.set_cookie('sortby',str(sortby))
487 | resp.set_cookie('order',str(order))
488 | return resp
489 |
490 | @app.route('/file//')
491 | def show(fileid,user):
492 | name=GetName(fileid)
493 | ext=name.split('.')[-1].lower()
494 | path=GetPath(fileid)
495 | if request.method=='POST':
496 | url=request.url.replace(':80','').replace(':443','')
497 | if ext in ['csv','doc','docx','odp','ods','odt','pot','potm','potx','pps','ppsx','ppsxm','ppt','pptm','pptx','rtf','xls','xlsx']:
498 | downloadUrl=GetDownloadUrl(fileid,user)
499 | url = 'https://view.officeapps.live.com/op/view.aspx?src='+urllib.quote(downloadUrl)
500 | return redirect(url)
501 | elif ext in ['bmp','jpg','jpeg','png','gif']:
502 | return render_template('show/image.html',url=url,path=path,cur_user=user)
503 | elif ext in ['mp4','webm']:
504 | return render_template('show/video.html',url=url,path=path,cur_user=user)
505 | elif ext in ['mp4','webm','avi','mpg', 'mpeg', 'rm', 'rmvb', 'mov', 'wmv', 'mkv', 'asf']:
506 | return render_template('show/video2.html',url=url,path=path,cur_user=user)
507 | elif ext in ['avi','mpg', 'mpeg', 'rm', 'rmvb', 'mov', 'wmv', 'mkv', 'asf']:
508 | return render_template('show/video2.html',url=url,path=path,cur_user=user)
509 | elif ext in ['ogg','mp3','wav']:
510 | return render_template('show/audio.html',url=url,path=path,cur_user=user)
511 | elif CodeType(ext) is not None:
512 | content=_remote_content(fileid,user)
513 | return render_template('show/code.html',content=content,url=url,language=CodeType(ext),path=path,cur_user=user)
514 | else:
515 | downloadUrl=GetDownloadUrl(fileid,user)
516 | return redirect(downloadUrl)
517 | else:
518 | if 'no-referrer' in allow_site:
519 | downloadUrl=GetDownloadUrl(fileid,user)
520 | resp=redirect(downloadUrl)
521 | return resp
522 | elif sum([i in referrer for i in allow_site])>0:
523 | downloadUrl=GetDownloadUrl(fileid,user)
524 | return redirect(downloadUrl)
525 | else:
526 | return abort(404)
527 |
528 | @app.route('/robot.txt')
529 | def robot():
530 | resp="""
531 | User-agent: *
532 | Disallow: /
533 | """
534 | resp=make_response(resp)
535 | resp.headers['Content-Type'] = 'text/javascript; charset=utf-8'
536 | return resp
537 |
538 |
539 | ######################注册应用
540 | from admin import admin as admin_blueprint
541 | app.register_blueprint(admin_blueprint)
542 |
543 |
544 | ######################函数
545 | app.jinja_env.globals['FetchData']=FetchData
546 | app.jinja_env.globals['path_list']=path_list
547 | app.jinja_env.globals['CanEdit']=CanEdit
548 | app.jinja_env.globals['len']=len
549 | app.jinja_env.globals['enumerate']=enumerate
550 | app.jinja_env.globals['os']=os
551 | app.jinja_env.globals['re']=re
552 | app.jinja_env.globals['file_ico']=file_ico
553 | app.jinja_env.globals['title']=title
554 | app.jinja_env.globals['tj_code']=tj_code if tj_code is not None else ''
555 | app.jinja_env.globals['get_od_user']=get_od_user
556 | app.jinja_env.globals['allow_site']=','.join(allow_site)
557 | # app.jinja_env.globals['share_path']=od_users.get('A').get('share_path')
558 | app.jinja_env.globals['downloadUrl_timeout']=downloadUrl_timeout
559 | ################################################################################
560 | #####################################启动#######################################
561 | ################################################################################
562 | if __name__=='__main__':
563 | app.run(port=58693,debug=True)
564 |
565 |
566 |
567 |
--------------------------------------------------------------------------------
/function.py:
--------------------------------------------------------------------------------
1 | #-*- coding=utf-8 -*-
2 | import json
3 | import requests
4 | import collections
5 | import sys
6 | reload(sys)
7 | sys.setdefaultencoding('utf-8')
8 | if sys.version_info[0]==3:
9 | import urllib.parse as urllib
10 | else:
11 | import urllib
12 | import os
13 | import re
14 | import time
15 | import shutil
16 | import base64
17 | import humanize
18 | import StringIO
19 | from dateutil.parser import parse
20 | from Queue import Queue
21 | from threading import Thread
22 | from redis import Redis
23 | from config import *
24 | from pymongo import MongoClient,ASCENDING,DESCENDING
25 | ######mongodb
26 | client = MongoClient('localhost',27017)
27 | db=client.three
28 | items=db.items
29 | rd=Redis(host='localhost',port=6379)
30 |
31 | #######授权链接
32 | LoginUrl=BaseAuthUrl+'/common/oauth2/v2.0/authorize?response_type=code\
33 | &client_id={client_id}&redirect_uri={redirect_uri}&scope=offline_access%20files.readwrite.all'
34 | OAuthUrl=BaseAuthUrl+'/common/oauth2/v2.0/token'
35 | AuthData='client_id={client_id}&redirect_uri={redirect_uri}&client_secret={client_secret}&code={code}&grant_type=authorization_code'
36 | ReFreshData='client_id={client_id}&redirect_uri={redirect_uri}&client_secret={client_secret}&refresh_token={refresh_token}&grant_type=refresh_token'
37 |
38 | headers={'User-Agent':'ISV|PyOne|PyOne/2.0'}
39 |
40 | def convert2unicode(string):
41 | return string.encode('utf-8')
42 |
43 | def get_value(key,user='A'):
44 | allow_key=['client_secret','client_id']
45 | print('get user {}\'s {}'.format(user,key))
46 | if key not in allow_key:
47 | return u'禁止获取'
48 | config_path=os.path.join(config_dir,'config.py')
49 | with open(config_path,'r') as f:
50 | text=f.read()
51 | kv=re.findall('"{}":{{[\w\W]*}}'.format(user),text)[0]
52 | value=re.findall('"{}":"(.*?)"'.format(key),kv)[0]
53 | return value
54 |
55 | def GetName(id):
56 | key='name:{}'.format(id)
57 | if rd.exists(key):
58 | return rd.get(key)
59 | else:
60 | item=items.find_one({'id':id})
61 | rd.set(key,item['name'])
62 | return item['name']
63 |
64 | def GetPath(id):
65 | key='path:{}'.format(id)
66 | if rd.exists(key):
67 | return rd.get(key)
68 | else:
69 | item=items.find_one({'id':id})
70 | rd.set(key,item['path'])
71 | return item['path']
72 |
73 |
74 | ################################################################################
75 | ###################################授权函数#####################################
76 | ################################################################################
77 | def open_json(filepath):
78 | token=False
79 | with open(filepath,'r') as f:
80 | try:
81 | token=json.load(f)
82 | except:
83 | for i in range(1,10):
84 | try:
85 | token=json.loads(f.read()[:-i])
86 | except:
87 | token=False
88 | if token!=False:
89 | return token
90 | return token
91 |
92 | def ReFreshToken(refresh_token,user='A'):
93 | client_id=get_value('client_id',user)
94 | client_secret=get_value('client_secret',user)
95 | headers['Content-Type']='application/x-www-form-urlencoded'
96 | data=ReFreshData.format(client_id=client_id,redirect_uri=urllib.quote(redirect_uri),client_secret=client_secret,refresh_token=refresh_token)
97 | url=OAuthUrl
98 | r=requests.post(url,data=data,headers=headers)
99 | return json.loads(r.text)
100 |
101 |
102 | def GetToken(Token_file='token.json',user='A'):
103 | Token_file='{}_{}'.format(user,Token_file)
104 | if os.path.exists(os.path.join(data_dir,Token_file)):
105 | token=open_json(os.path.join(data_dir,Token_file))
106 | try:
107 | if time.time()>int(token.get('expires_on')):
108 | print 'token timeout'
109 | refresh_token=token.get('refresh_token')
110 | token=ReFreshToken(refresh_token,user)
111 | if token.get('access_token'):
112 | with open(os.path.join(data_dir,Token_file),'w') as f:
113 | json.dump(token,f,ensure_ascii=False)
114 | except:
115 | with open(os.path.join(data_dir,'{}_Atoken.json'.format(user)),'r') as f:
116 | Atoken=json.load(f)
117 | refresh_token=Atoken.get('refresh_token')
118 | token=ReFreshToken(refresh_token,user)
119 | token['expires_on']=str(time.time()+3599)
120 | if token.get('access_token'):
121 | with open(os.path.join(data_dir,Token_file),'w') as f:
122 | json.dump(token,f,ensure_ascii=False)
123 | return token.get('access_token')
124 | else:
125 | return False
126 |
127 |
128 | def GetAppUrl():
129 | return 'https://graph.microsoft.com/'
130 |
131 | ################################################################################
132 | ###############################onedrive操作函数#################################
133 | ################################################################################
134 | def GetExt(name):
135 | try:
136 | return name.split('.')[-1]
137 | except:
138 | return 'file'
139 |
140 | def date_to_char(date):
141 | return date.strftime('%Y/%m/%d')
142 |
143 | def Dir(path=u'A:/'):
144 | app_url=GetAppUrl()
145 | user,n_path=path.split(':')
146 | print('update {}\'s file'.format(user))
147 | if n_path=='/':
148 | BaseUrl=app_url+u'v1.0/me/drive/root/children?expand=thumbnails'
149 | # items.remove()
150 | queue=Queue()
151 | # queue.put(dict(url=BaseUrl,grandid=grandid,parent=parent,trytime=1))
152 | g=GetItemThread(queue,user)
153 | g.GetItem(BaseUrl)
154 | queue=g.queue
155 | if queue.qsize()==0:
156 | return
157 | tasks=[]
158 | for i in range(min(5,queue.qsize())):
159 | t=GetItemThread(queue,user)
160 | t.start()
161 | tasks.append(t)
162 | for t in tasks:
163 | t.join()
164 | RemoveRepeatFile()
165 | else:
166 | grandid=0
167 | parent=''
168 | if n_path.endswith('/'):
169 | n_path=n_path[:-1]
170 | if not n_path.startswith('/'):
171 | n_path='/'+n_path
172 | n_path=urllib.quote(n_path)
173 | BaseUrl=app_url+u'v1.0/me/drive/root:{}:/children?expand=thumbnails'.format(n_path)
174 | queue=Queue()
175 | # queue.put(dict(url=BaseUrl,grandid=grandid,parent=parent,trytime=1))
176 | g=GetItemThread(queue,user)
177 | g.GetItem(BaseUrl,grandid,parent,1)
178 | queue=g.queue
179 | if queue.qsize()==0:
180 | return
181 | tasks=[]
182 | for i in range(min(10,queue.qsize())):
183 | t=GetItemThread(queue,user)
184 | t.start()
185 | tasks.append(t)
186 | for t in tasks:
187 | t.join()
188 | RemoveRepeatFile()
189 |
190 | def Dir_all(path=u'A:/'):
191 | app_url=GetAppUrl()
192 | user,n_path=path.split(':')
193 | print('update {}\'s {} file'.format(user,n_path))
194 | if n_path=='/':
195 | BaseUrl=app_url+u'v1.0/me/drive/root/children?expand=thumbnails'
196 | items.remove({'user':user})
197 | queue=Queue()
198 | g=GetItemThread(queue,user)
199 | g.GetItem(BaseUrl)
200 | queue=g.queue
201 | if queue.qsize()==0:
202 | return
203 | tasks=[]
204 | for i in range(min(5,queue.qsize())):
205 | t=GetItemThread(queue,user)
206 | t.start()
207 | tasks.append(t)
208 | for t in tasks:
209 | t.join()
210 | RemoveRepeatFile()
211 | else:
212 | grandid=0
213 | parent=''
214 | if n_path.endswith('/'):
215 | n_path=n_path[:-1]
216 | if not n_path.startswith('/'):
217 | n_path='/'+n_path
218 | if items.find_one({'grandid':0,'type':'folder','user':user}):
219 | parent_id=0
220 | for idx,p in enumerate(n_path[1:].split('/')):
221 | if parent_id==0:
222 | parent_id=items.find_one({'name':p,'grandid':idx,'user':user})['id']
223 | else:
224 | parent_id=items.find_one({'name':p,'grandid':idx,'parent':parent_id})['id']
225 | items.delete_many({'parent':parent_id})
226 | grandid=idx+1
227 | parent=parent_id
228 | n_path=urllib.quote(n_path)
229 | BaseUrl=app_url+u'v1.0/me/drive/root:{}:/children?expand=thumbnails'.format(n_path)
230 | queue=Queue()
231 | g=GetItemThread(queue,user)
232 | g.GetItem(BaseUrl,grandid,parent,1)
233 | queue=g.queue
234 | if queue.qsize()==0:
235 | return
236 | tasks=[]
237 | for i in range(min(10,queue.qsize())):
238 | t=GetItemThread(queue,user)
239 | t.start()
240 | tasks.append(t)
241 | for t in tasks:
242 | t.join()
243 | RemoveRepeatFile()
244 |
245 | class GetItemThread(Thread):
246 | def __init__(self,queue,user):
247 | super(GetItemThread,self).__init__()
248 | self.queue=queue
249 | self.user=user
250 | share_path=od_users.get(user).get('share_path')
251 | if share_path=='/':
252 | self.share_path=share_path
253 | else:
254 | sp=share_path
255 | if not sp.startswith('/'):
256 | sp='/'+share_path
257 | if sp.endswith('/') and sp!='/':
258 | sp=sp[:-1]
259 | self.share_path=sp
260 |
261 | def run(self):
262 | while 1:
263 | time.sleep(0.5) #避免过快
264 | info=self.queue.get()
265 | url=info['url']
266 | grandid=info['grandid']
267 | parent=info['parent']
268 | trytime=info['trytime']
269 | self.GetItem(url,grandid,parent,trytime)
270 | if self.queue.empty():
271 | time.sleep(5) #再等5s
272 | print('waiting 5s if queue is not empty')
273 | if self.queue.empty():
274 | break
275 |
276 | def GetItem(self,url,grandid=0,parent='',trytime=1):
277 | app_url=GetAppUrl()
278 | token=GetToken(user=self.user)
279 | print(u'getting files from url {}'.format(url))
280 | header={'Authorization': 'Bearer {}'.format(token)}
281 | try:
282 | r=requests.get(url,headers=header)
283 | data=json.loads(r.content)
284 | if data.get('error'):
285 | print('error:{}! waiting 180s'.format(data.get('error').get('message')))
286 | time.sleep(180)
287 | self.queue.put(dict(url=url,grandid=grandid,parent=parent,trytime=trytime))
288 | return
289 | values=data.get('value')
290 | if len(values)>0:
291 | for value in values:
292 | item={}
293 | if value.get('folder'):
294 | folder=items.find_one({'id':value['id']})
295 | if folder is not None:
296 | if folder['size_order']==value['size']: #文件夹大小未变化,不更新
297 | print(u'path:{},origin size:{},current size:{}'.format(value['name'],folder['size_order'],value['size']))
298 | else:
299 | items.delete_one({'id':value['id']})
300 | item['type']='folder'
301 | item['user']=self.user
302 | item['order']=0
303 | item['name']=convert2unicode(value['name'])
304 | item['id']=convert2unicode(value['id'])
305 | item['size']=humanize.naturalsize(value['size'], gnu=True)
306 | item['size_order']=int(value['size'])
307 | item['lastModtime']=date_to_char(parse(value['lastModifiedDateTime']))
308 | item['grandid']=grandid
309 | item['parent']=parent
310 | grand_path=value.get('parentReference').get('path').replace('/drive/root:','')
311 | if grand_path=='':
312 | path=convert2unicode(value['name'])
313 | else:
314 | path=grand_path.replace(self.share_path,'',1)+'/'+convert2unicode(value['name'])
315 | if path.startswith('/') and path!='/':
316 | path=path[1:]
317 | if path=='':
318 | path=convert2unicode(value['name'])
319 | path='{}:/{}'.format(self.user,path)
320 | item['path']=path
321 | subfodler=items.insert_one(item)
322 | if value.get('folder').get('childCount')==0:
323 | continue
324 | else:
325 | url=app_url+'v1.0/me'+value.get('parentReference').get('path')+'/'+value.get('name')+':/children?expand=thumbnails'
326 | self.queue.put(dict(url=url,grandid=grandid+1,parent=item['id'],trytime=1))
327 | else:
328 | if items.find_one({'id':value['id']}) is not None: #文件存在
329 | continue
330 | else:
331 | item['type']=GetExt(value['name'])
332 | grand_path=value.get('parentReference').get('path').replace('/drive/root:','')
333 | if grand_path=='':
334 | path=convert2unicode(value['name'])
335 | else:
336 | path=grand_path.replace(self.share_path,'',1)+'/'+convert2unicode(value['name'])
337 | if path.startswith('/') and path!='/':
338 | path=path[1:]
339 | if path=='':
340 | path=convert2unicode(value['name'])
341 | path='{}:/{}'.format(self.user,path)
342 | item['path']=path
343 | item['user']=self.user
344 | item['name']=convert2unicode(value['name'])
345 | item['id']=convert2unicode(value['id'])
346 | item['size']=humanize.naturalsize(value['size'], gnu=True)
347 | item['size_order']=int(value['size'])
348 | item['lastModtime']=date_to_char(parse(value['lastModifiedDateTime']))
349 | item['grandid']=grandid
350 | item['parent']=parent
351 | if GetExt(value['name']) in ['bmp','jpg','jpeg','png','gif']:
352 | item['order']=3
353 | key1='name:{}'.format(value['id'])
354 | key2='path:{}'.format(value['id'])
355 | rd.set(key1,value['name'])
356 | rd.set(key2,path)
357 | elif value['name']=='.password':
358 | item['order']=1
359 | else:
360 | item['order']=2
361 | items.insert_one(item)
362 | if data.get('@odata.nextLink'):
363 | self.queue.put(dict(url=data.get('@odata.nextLink'),grandid=grandid,parent=parent,trytime=1))
364 | except Exception as e:
365 | trytime+=1
366 | print(u'error to opreate GetItem("{}","{}","{}"),try times :{}, reason: {}'.format(url,grandid,parent,trytime,e))
367 | if trytime<=3:
368 | self.queue.put(dict(url=url,grandid=grandid,parent=parent,trytime=trytime))
369 |
370 |
371 | def GetItemByPath(self,path):
372 | app_url=GetAppUrl()
373 | token=GetToken(user=self.user)
374 | if path=='' or path=='/':
375 | url=app_url+u'v1.0/me/drive/root/'
376 | if path=='/':
377 | url=app_url+u'v1.0/me/drive/root:{}:/'.format(path)
378 | header={'Authorization': 'Bearer {}'.format(token)}
379 | url=app_url+u'v1.0/me/drive/root:{}:/'.format(path)
380 | r=requests.get(url,headers=header)
381 | data=json.loads(r.content)
382 | return data
383 |
384 | def GetItemByUrl(self,url):
385 | app_url=GetAppUrl()
386 | token=GetToken(user=self.user)
387 | header={'Authorization': 'Bearer {}'.format(token)}
388 | r=requests.get(url,headers=header)
389 | data=json.loads(r.content)
390 | return data
391 |
392 | def GetRootid(user='A'):
393 | key='{}:rootid'.format(user)
394 | if rd.exists(key):
395 | return rd.get(key)
396 | else:
397 | app_url=GetAppUrl()
398 | token=GetToken(user=user)
399 | url=app_url+u'v1.0/me/drive/root/'
400 | header={'Authorization': 'Bearer {}'.format(token)}
401 | r=requests.get(url,headers=header)
402 | data=json.loads(r.content)
403 | rd.set(key,data['id'],3600)
404 | return data['id']
405 |
406 | def UpdateFile(renew='all'):
407 | if renew=='all':
408 | items.remove()
409 | rd.flushdb()
410 | for user,item in od_users.items():
411 | if item.get('client_id')!='':
412 | share_path='{}:{}'.format(user,item['share_path'])
413 | Dir_all(share_path)
414 | else:
415 | for user,item in od_users.items():
416 | if item.get('client_id')!='':
417 | share_path='{}:{}'.format(user,item['share_path'])
418 | Dir(share_path)
419 | print('update file success!')
420 |
421 |
422 | def FileExists(filename,user='A'):
423 | token=GetToken(user=user)
424 | headers={'Authorization':'bearer {}'.format(token),'Content-Type':'application/json'}
425 | search_url=app_url+"v1.0/me/drive/root/search(q='{}')".format(filename)
426 | r=requests.get(search_url,headers=headers)
427 | jsondata=json.loads(r.text)
428 | if len(jsondata['value'])==0:
429 | return False
430 | else:
431 | return True
432 |
433 | def FileInfo(fileid,user='A'):
434 | token=GetToken(user=user)
435 | headers={'Authorization':'bearer {}'.format(token),'Content-Type':'application/json'}
436 | search_url=app_url+"v1.0/me/drive/items/{}".format(fileid)
437 | r=requests.get(search_url,headers=headers)
438 | jsondata=json.loads(r.text)
439 | return jsondata
440 |
441 |
442 | ################################################上传文件
443 | def list_all_files(rootdir):
444 | import os
445 | _files = []
446 | if len(re.findall('[:#\|\?]+',rootdir))>0:
447 | newf=re.sub('[:#\|\?]+','',rootdir)
448 | shutil.move(rootdir,newf)
449 | rootdir=newf
450 | if rootdir.endswith(' '):
451 | shutil.move(rootdir,rootdir.rstrip())
452 | rootdir=rootdir.rstrip()
453 | if len(re.findall('/ ',rootdir))>0:
454 | newf=re.sub('/ ','/',rootdir)
455 | shutil.move(rootdir,newf)
456 | rootdir=newf
457 | flist = os.listdir(rootdir) #列出文件夹下所有的目录与文件
458 | for f in flist:
459 | path = os.path.join(rootdir,f)
460 | if os.path.isdir(path):
461 | _files.extend(list_all_files(path))
462 | if os.path.isfile(path):
463 | _files.append(path)
464 | return _files
465 |
466 | def _filesize(path):
467 | size=os.path.getsize(path)
468 | # print('{}\'s size {}'.format(path,size))
469 | return size
470 |
471 | def _file_content(path,offset,length):
472 | size=_filesize(path)
473 | offset,length=map(int,(offset,length))
474 | if offset>size:
475 | print('offset must smaller than file size')
476 | return False
477 | length=length if offset+length3:
514 | yield {'status':'upload fail! touch max retry time(3)'}
515 | break
516 |
517 | def _upload_part(uploadUrl, filepath, offset, length,trytime=1):
518 | size=_filesize(filepath)
519 | offset,length=map(int,(offset,length))
520 | if offset>size:
521 | print('offset must smaller than file size')
522 | return {'status':'fail','msg':'params mistake','code':1}
523 | length=length if offset+length0:
770 | newf=os.path.join(dir_,re.sub('[:/#\|]+','',fname))
771 | shutil.move(f,newf)
772 | localfiles=list_all_files(local_dir)
773 | check_file_list=[]
774 | if local_dir.endswith('/'):
775 | local_dir=local_dir[:-1]
776 | for file in localfiles:
777 | dir_,fname=os.path.dirname(file),os.path.basename(file)
778 | remote_path=remote_dir+'/'+dir_.replace(local_dir,'')+'/'+fname
779 | remote_path=remote_path.replace('//','/')
780 | check_file_list.append((remote_path,file))
781 | print(u'check repeat file')
782 | if remote_dir=='/':
783 | cloud_files=_GetAllFile()
784 | else:
785 | if remote_dir.startswith('/'):
786 | remote_dir=remote_dir[1:]
787 | if items.find_one({'grandid':0,'type':'folder','name':remote_dir.split('/')[0]}):
788 | parent_id=0
789 | parent_path=''
790 | for idx,p in enumerate(remote_dir.split('/')):
791 | if parent_id==0:
792 | parent=items.find_one({'name':p,'grandid':idx})
793 | parent_id=parent['id']
794 | parent_path='/'.join([parent_path,parent['name']])
795 | else:
796 | parent=items.find_one({'name':p,'grandid':idx,'parent':parent_id})
797 | parent_id=parent['id']
798 | parent_path='/'.join([parent_path,parent['name']])
799 | grandid=idx+1
800 | cloud_files=_GetAllFile(parent_id,parent_path)
801 | try:
802 | cloud_files=dict([(i,i) for i in cloud_files])
803 | except:
804 | cloud_files={}
805 | queue=Queue()
806 | tasks=[]
807 | for remote_path,file in check_file_list:
808 | if not cloud_files.get(base64.b64encode(remote_path)):
809 | queue.put((file,remote_path))
810 | print "check_file_list {},cloud_files {},queue {}".format(len(check_file_list),len(cloud_files),queue.qsize())
811 | print "start upload files 5s later"
812 | time.sleep(5)
813 | for i in range(min(threads,queue.qsize())):
814 | t=MultiUpload(queue,user)
815 | t.start()
816 | tasks.append(t)
817 | for t in tasks:
818 | t.join()
819 | #删除错误数据
820 | RemoveRepeatFile()
821 |
822 |
823 |
824 | ########################删除文件
825 | def DeleteLocalFile(fileid):
826 | items.remove({'id':fileid})
827 |
828 | def DeleteRemoteFile(fileid,user='A'):
829 | app_url=GetAppUrl()
830 | token=GetToken(user=user)
831 | headers={'Authorization':'bearer {}'.format(token)}
832 | url=app_url+'v1.0/me/drive/items/'+fileid
833 | r=requests.delete(url,headers=headers)
834 | if r.status_code==204:
835 | DeleteLocalFile(fileid)
836 | return True
837 | else:
838 | DeleteLocalFile(fileid)
839 | return False
840 |
841 | ########################
842 | def CreateFolder(folder_name,grand_path,user='A'):
843 | app_url=GetAppUrl()
844 | token=GetToken(user=user)
845 | if grand_path=='' or grand_path is None or grand_path=='/':
846 | url=app_url+'v1.0/me/drive/root/children'
847 | parent_id=''
848 | grandid=0
849 | else:
850 | path='{}:/{}'.format(user,grand_path)
851 | parent=items.find_one({'path':path})
852 | parent_id=parent['id']
853 | grandid=parent['grandid']+1
854 | url=app_url+'v1.0/me/drive/items/{}/children'.format(parent['id'])
855 | headers={'Authorization':'bearer {}'.format(token),'Content-Type':'application/json'}
856 | payload={
857 | "name": folder_name,
858 | "folder": {},
859 | "@microsoft.graph.conflictBehavior": "rename"
860 | }
861 | r=requests.post(url,headers=headers,data=json.dumps(payload))
862 | data=json.loads(r.content)
863 | if data.get('id'):
864 | #插入数据
865 | share_path=od_users.get(user).get('share_path')
866 | item={}
867 | item['type']='folder'
868 | item['user']=user
869 | item['name']=data.get('name')
870 | item['id']=data.get('id')
871 | item['size']=humanize.naturalsize(data.get('size'), gnu=True)
872 | item['size_order']=data.get('size')
873 | item['lastModtime']=date_to_char(parse(data.get('lastModifiedDateTime')))
874 | item['grandid']=grandid
875 | item['parent']=parent_id
876 | if grand_path=='' or grand_path is None or grand_path=='/':
877 | path=convert2unicode(data['name'])
878 | else:
879 | path=grand_path.replace(share_path,'',1)+'/'+convert2unicode(data['name'])
880 | if not path.startswith('/'):
881 | path='/'+path
882 | path='{}:{}'.format(user,path)
883 | item['path']=path
884 | item['order']=0
885 | items.insert_one(item)
886 | return True
887 | else:
888 | print(data.get('error').get('msg'))
889 | return False
890 |
891 | def MoveFile(fileid,new_folder_path,user='A'):
892 | app_url=GetAppUrl()
893 | token=GetToken(user=user)
894 | #GetRootid
895 | if new_folder_path=='' or new_folder_path is None or new_folder_path=='/':
896 | folder_id=GetRootid(user)
897 | parent=''
898 | grandid=0
899 | path=GetName(fileid)
900 | else:
901 | path='{}:/{}'.format(user,new_folder_path)
902 | parent_item=items.find_one({'path':path})
903 | folder_id=parent_item['id']
904 | parent=parent_item['id']
905 | grandid=parent_item['grandid']+1
906 | path=parent_item['path']+'/'+GetName(fileid)
907 | url=app_url+'v1.0/me/drive/items/{}'.format(fileid)
908 | headers={'Authorization':'bearer {}'.format(token),'Content-Type':'application/json'}
909 | payload={
910 | "parentReference": {
911 | "id": folder_id
912 | },
913 | "name": GetName(fileid)
914 | }
915 | r=requests.patch(url,headers=headers,data=json.dumps(payload))
916 | data=json.loads(r.content)
917 | if data.get('id'):
918 | new_value={'parent':parent,'grandid':grandid,'path':path}
919 | items.find_one_and_update({'id':fileid},{'$set':new_value})
920 | file=items.find_one({'id':fileid})
921 | filename=file['name']
922 | if file['parent']=='':
923 | path='/'
924 | else:
925 | path=items.find_one({'id':file['parent']})['path']
926 | key='has_item$#$#$#$#{}$#$#$#$#{}'.format(path,filename)
927 | rd.delete(key)
928 | return True
929 | else:
930 | print(data.get('error').get('msg'))
931 | return False
932 |
933 | def CheckTimeOut(fileid):
934 | app_url=GetAppUrl()
935 | token=GetToken()
936 | headers={'Authorization':'bearer {}'.format(token),'Content-Type':'application/json'}
937 | url=app_url+'v1.0/me/drive/items/'+fileid
938 | r=requests.get(url,headers=headers)
939 | data=json.loads(r.content)
940 | if data.get('@microsoft.graph.downloadUrl'):
941 | downloadUrl=data.get('@microsoft.graph.downloadUrl')
942 | start_time=time.time()
943 | for i in range(10000):
944 | r=requests.head(downloadUrl)
945 | print '{}\'s gone, status:{}'.format(time.time()-start_time,r.status_code)
946 | if r.status_code==404:
947 | break
948 |
949 | def RemoveRepeatFile():
950 | """
951 | db.items.aggregate([
952 | {
953 | $group:{_id:{id:'$id'},count:{$sum:1},dups:{$addToSet:'$_id'}}
954 | },
955 | {
956 | $match:{count:{$gt:1}}
957 | }
958 |
959 | ]).forEach(function(it){
960 |
961 | it.dups.shift();
962 | db.items.remove({_id: {$in: it.dups}});
963 |
964 | });
965 | """
966 | deleteData=items.aggregate([
967 | {'$group': {
968 | '_id': { 'id': "$id"},
969 | 'uniqueIds': { '$addToSet': "$_id" },
970 | 'count': { '$sum': 1 }
971 | }},
972 | { '$match': {
973 | 'count': { '$gt': 1 }
974 | }}
975 | ]);
976 | first=True
977 | try:
978 | for d in deleteData:
979 | first=True
980 | for did in d['uniqueIds']:
981 | if not first:
982 | items.delete_one({'_id':did});
983 | first=False
984 | except Exception as e:
985 | print(e)
986 | return
987 |
988 |
989 | if __name__=='__main__':
990 | func=sys.argv[1]
991 | if len(sys.argv)>2:
992 | args=sys.argv[2:]
993 | eval(func+str(tuple(args)))
994 | else:
995 | eval(func+'()')
996 |
--------------------------------------------------------------------------------
/static/img/bg-bottom.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------