├── .MD ├── *nix.md ├── baidu.md ├── browser │ ├── chrome.md │ ├── firefox.md │ └── safari.md ├── macOS.md └── win.md ├── .gitignore ├── README.md ├── api.py ├── axel-2.5.tar.gz ├── axel-patch ├── Makefile ├── Makefile.patch ├── axel.c ├── axel.c.patch ├── axel.h ├── conf.c ├── conf.c.patch ├── conf.h ├── conf.h.patch ├── httppost.c ├── httppost.h ├── text.c └── text.c.patch ├── axeldown.py ├── build.sh ├── conf ├── config.py ├── css └── main.css ├── db.py ├── donate ├── alipay.png └── wechatpay.png ├── index.html ├── js ├── jquery-1.7.1.min.js └── main.js └── screenshot ├── ad.jpg ├── admenu.jpg ├── build.jpg ├── dmacos.jpg ├── downlinux.jpg ├── dwin.jpg ├── preview.jpg └── run.jpg /.MD/*nix.md: -------------------------------------------------------------------------------- 1 | ## 下载和编译: 2 | 3 | ``` bash 4 | $ git clone https://github.com/lihaoyun6/axeldown-core.git 5 | $ cd axeldown-core 6 | $ chmod a+x build.sh 7 | $ ./build.sh 8 | ``` 9 | ![clone](https://github.com/lihaoyun6/axeldown-core/blob/master/screenshot/build.jpg) 10 | 11 | ## 环境准备: 12 | 13 | 因为项目基于web.py模块提供服务, 故需要先安装web.py 14 | 15 | ``` bash 16 | $ sudo easy_install web.py 17 | ``` 18 | 19 | 启动运行: 20 | 21 | ``` bash 22 | $ cd axeldown-core 23 | $ python axeldown.py [自定义端口] 24 | ``` 25 | 例如 26 | ``` bash 27 | $ python axeldown.py 2333 28 | ``` 29 | (不使用自定义端口时, 默认在8080端口开启服务) 30 | 31 | 启动服务后在浏览器中打开""即可看到管理界面 32 | 33 | ![run](https://github.com/lihaoyun6/axeldown-core/blob/master/screenshot/run.jpg) 34 | 35 | 默认下载目录为当前用户的家目录, 如需永久更改请使用"设置"按钮进行更改, "新建"界面设置下载目录仅对当前任务生效 36 | 37 | PS: 下载过程中可以关闭浏览器, 但不要关闭终端窗口 38 | 39 | ## 下载百度云文件 40 | 41 | 首先保证下载服务已经开启, 然后[点此查看](baidu.md)百度云下载任务导出教程    42 | 43 | ## 打赏 44 |
45 | 支付宝 46 | 微信 47 |
48 | -------------------------------------------------------------------------------- /.MD/baidu.md: -------------------------------------------------------------------------------- 1 | # 百度云直接调用Axel下载教程   2 | 3 | ## 安装插件与脚本 4 | 5 | [Safari浏览器安装向导](./browser/safari.md) 6 | [Chrome浏览器安装向导](./browser/chrome.md) 7 | [Firefox浏览器安装向导](./browser/firefox.md) 8 | 9 | ## 使用脚本 10 | 11 | 安装好插件和脚本后, 再打开百度云分享或自己的文件管理页面, 会看到页面上多了一个"AX-下载"按钮   12 | 13 | ![axmain](https://github.com/lihaoyun6/ax-baiduyunpan/blob/master/screenshot/axdmain.jpg)   14 | 15 | ![axmain](https://github.com/lihaoyun6/ax-baiduyunpan/blob/master/screenshot/axdmain2.jpg)   16 | 17 | ```` 18 | •"AX-压缩下载"仅作为另一种链接提取方式的保留选项, 一般用户可以无视 19 | ```` 20 | 21 | 点击"AX-下载"按钮会展开一个对话框 22 | 23 | "发送到Axeldown下载"按钮会将解析出的链接发送至指定的下载服务器端口, 可以设置当前任务使用的线程数量   24 | 25 | ![axdown](https://github.com/lihaoyun6/ax-baiduyunpan/blob/master/screenshot/axdurl.jpg)   26 | 27 | 发送下载任务需要授予跨站访问权限, 弹出此窗口时, 请点击"允许域名"即可   28 | 29 | ![xss](https://github.com/lihaoyun6/ax-baiduyunpan/blob/master/screenshot/xss.jpg)   30 | 31 | 通过"AX-下载">"下载设置"可以自定义Axeldown服务器地址和端口以及默认下载线程数(默认为http://127.0.0.1:2333).   32 | 33 | ![axconf](https://github.com/lihaoyun6/ax-baiduyunpan/blob/master/screenshot/axdconf.jpg)   34 | 35 | ```` 36 | •发送下载任务需要保证Axeldown服务已经开启, 并同意脚本的跨站请求.     37 | •脚本支持自动解析单选/多选模式下的文件名, 以及单选/多选模式下的文件夹打包文件名, 无需手动指定下载文件名.   38 | ```` 39 | 40 | ## 修复解压   41 | 42 | 通过多选方式打包下载的文件, 如果大小超过4G, 很有可能导致无法正常解压. 43 | 44 | macOS平台可以点击Axeldown管理界面中的"设置">"修复解压"并选择要解压的打包zip文件来进行解压  45 | 46 | Linux以及Unix平台可以使用[此项目](https://github.com/ccloli/baidupan-zip-extract)来修复并解压此种特殊zip文件(需nodejs环境) 47 | 48 | ## 打赏 49 |
50 | 支付宝 51 | 微信 52 |
53 | -------------------------------------------------------------------------------- /.MD/browser/chrome.md: -------------------------------------------------------------------------------- 1 | ## Chrome浏览器安装插件 2 | 3 | 1. [点击此处](http://tampermonkey.net)打开油猴插件官网, 并点击图中所示按钮下载插件 4 | 5 | ![tamp](https://github.com/lihaoyun6/ax-baiduyunpan/blob/master/screenshot/c1.png) 6 | 7 | 2. 待弹出安装提示时, 点击"添加扩展程序" 8 | 9 | ![tamp](https://github.com/lihaoyun6/ax-baiduyunpan/blob/master/screenshot/c2.png) 10 | 11 | 当浏览器右上角出现此图标时代表已完成插件安装 12 | 13 | ![tamp](https://github.com/lihaoyun6/ax-baiduyunpan/blob/master/screenshot/c3.png) 14 | 15 | ## Chrome浏览器安装脚本 16 | 17 | 1. [点击此处](https://greasyfork.org/zh-CN/scripts/38418-ax-百度云盘)打开脚本安装页面, 并点击图中所示按钮安装脚本 18 | 19 | ![tamp](https://github.com/lihaoyun6/ax-baiduyunpan/blob/master/screenshot/s5.png) 20 | 21 | 2. 待弹出安装页面时, 点击"安装" 22 | 23 | ![tamp](https://github.com/lihaoyun6/ax-baiduyunpan/blob/master/screenshot/s6.png) 24 | 25 | 至此浏览器用户脚本安装完成, [点击此处](../baidu.md)返回百度云导出教程 26 | -------------------------------------------------------------------------------- /.MD/browser/firefox.md: -------------------------------------------------------------------------------- 1 | ## Firefox浏览器安装插件 2 | 3 | 1. [点击此处](http://tampermonkey.net)打开油猴插件官网, 并点击图中所示按钮下载插件 4 | 5 | ![tamp](https://github.com/lihaoyun6/ax-baiduyunpan/blob/master/screenshot/f1.png) 6 | 7 | 2. 自动跳转到火狐插件商店后, 点击图中所示按钮安装插件 8 | 9 | ![tamp](https://github.com/lihaoyun6/ax-baiduyunpan/blob/master/screenshot/f2.png) 10 | 11 | 3. 待Firefox弹出确认提示时点击"添加" 12 | 13 | ![tamp](https://github.com/lihaoyun6/ax-baiduyunpan/blob/master/screenshot/f3.png) 14 | 15 | 至此浏览器油猴插件安装完成 16 | 17 | ## Firefox浏览器安装用户脚本 18 | 19 | 1. [点击此处](https://greasyfork.org/zh-CN/scripts/38418-ax-百度云盘)打开脚本安装页面, 并点击图中所示按钮安装脚本 20 | 21 | ![tamp](https://github.com/lihaoyun6/ax-baiduyunpan/blob/master/screenshot/s5.png) 22 | 23 | 2. 待弹出安装页面时, 点击"安装" 24 | 25 | ![tamp](https://github.com/lihaoyun6/ax-baiduyunpan/blob/master/screenshot/s6.png) 26 | 27 | 至此浏览器用户脚本安装完成, [点击此处](../baidu.md)返回百度云导出教程 28 | -------------------------------------------------------------------------------- /.MD/browser/safari.md: -------------------------------------------------------------------------------- 1 | ## Safari浏览器安装插件 2 | 3 | 1. [点击此处](http://tampermonkey.net)打开油猴插件官网, 并点击图中所示按钮下载插件 4 | 5 | ![tamp](https://github.com/lihaoyun6/ax-baiduyunpan/blob/master/screenshot/s1.png) 6 | 7 | 2. 双击打开下载好的插件文件 8 | 9 | ![tamp](https://github.com/lihaoyun6/ax-baiduyunpan/blob/master/screenshot/s2.png) 10 | 11 | 3. 待Safari弹出提示框后点击"信任" 12 | 13 | ![tamp](https://github.com/lihaoyun6/ax-baiduyunpan/blob/master/screenshot/s3.png) 14 | 15 | 至此浏览器油猴插件安装完成 16 | 17 | ![tamp](https://github.com/lihaoyun6/ax-baiduyunpan/blob/master/screenshot/s4.png) 18 | 19 | ## Safari浏览器安装用户脚本 20 | 21 | 1. [点击此处](https://greasyfork.org/zh-CN/scripts/38418-ax-百度云盘)打开脚本安装页面, 并点击图中所示按钮安装脚本 22 | 23 | ![tamp](https://github.com/lihaoyun6/ax-baiduyunpan/blob/master/screenshot/s5.png) 24 | 25 | 2. 待弹出安装页面时, 点击"安装" 26 | 27 | ![tamp](https://github.com/lihaoyun6/ax-baiduyunpan/blob/master/screenshot/s6.png) 28 | 29 | 至此浏览器用户脚本安装完成, [点击此处](../baidu.md)返回百度云导出教程 30 | -------------------------------------------------------------------------------- /.MD/macOS.md: -------------------------------------------------------------------------------- 1 | ## 下载安装: 2 | 3 | [点此前往](https://github.com/lihaoyun6/axeldown-core/releases/latest)并下载最新版本的Axeldown 4 | 5 | ## 启动运行: 6 | 7 | 打开下载好的dmg文件, 将Axeldown程序拖动安装至"应用程序"文件夹 8 | 9 | ![ad](https://github.com/lihaoyun6/axeldown-core/blob/master/screenshot/ad.jpg) 10 | 11 | 在Finder或Launchpad中打开Axeldown即可运行全部的下载服务 12 | 13 | 启动Axeldown后, 会在屏幕顶部的"菜单栏"显示一个图标 14 | 15 | ![admenu](https://github.com/lihaoyun6/axeldown-core/blob/master/screenshot/admenu.jpg) 16 | 17 | ```` 18 | •如果首次启动时界面一片空白, 可以右键单击Axeldown的菜单栏图标, 即可退出程序, 再次启动应该就没问题了 19 | ```` 20 | 21 | 默认下载到当前用户的"下载"文件夹, 如需永久更改请使用"设置"按钮进行更改, "新建"界面设置下载目录仅对当前任务生效 22 | 23 | 点击"窗口化"按钮即可在默认浏览器中打下载管理面板, 根据喜好决定具体使用哪种控制方法 24 | 25 | ## 下载百度云文件 26 | 27 | 首先保证下载服务已经开启, 然后[点此查看](baidu.md)百度云下载任务导出教程  28 | 29 | ## 打赏 30 |
31 | 支付宝 32 | 微信 33 |
34 | -------------------------------------------------------------------------------- /.MD/win.md: -------------------------------------------------------------------------------- 1 | ## 请注意 2 | 3 | 此版Windows平台预编译版本使用了cygwin提供的桥接api来初步实现免移植的os.fork()函数使用 4 | 5 | (已使用Pyinstaller进行打包, 无需安装cygwin即可运行) 6 | 7 | 不过也有许多适配性的bug, 比如某些链接下载时无法显示进度与速度, 部分情况下下载进度可以超过100%等 8 | 9 | - 仅供测试以及预览axeldown在Windows中的运行效果之用. 不推荐普通用户日常使用!! 10 | 11 | 欢迎fork, 欢迎移植 12 | 13 | # 使用方法 14 | 15 | [点此](https://github.com/lihaoyun6/axeldown-core/releases/tag/1.2)前往下载页面, 并下载"Axeldown_beta_win.zip" 16 | 17 | 解压后进入"axeldown"目录, 并双击运行"axeldown.exe"即可开启服务, 服务默认运行在8080端口 18 | 19 | (需要自定义服务端口请使用cmd调用axeldown.exe, 并将指定的端口号作为第一个输入参数) 20 | 21 | 然后在浏览器中打开""即可看到管理界面 22 | 23 | ![dwin](https://github.com/lihaoyun6/axeldown-core/blob/master/screenshot/dwin.jpg) 24 | 25 | ## 下载百度云文件 26 | 27 | 首先保证下载服务已经开启, 然后[点此](baidu.md)查看百度云下载任务导出教程  28 | -------------------------------------------------------------------------------- /.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 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 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 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # axeldown-core 2 | 3 | 基于axel-webm的优化项目. 通过webui调用axel进行下载. 支持http与ftp下载, 支持多线程加速百度云下载   4 | 5 | ![preview](screenshot/preview.jpg)   6 | ## 使用方法 7 | 8 | [macOS版教程](.MD/macOS.md) 9 | [linux/Unix版教程](.MD/*nix.md) 10 | [Windows测试版说明](.MD/win.md) 11 | 12 | ## 打赏 13 |
14 | 支付宝 15 | 微信 16 |
17 | -------------------------------------------------------------------------------- /api.py: -------------------------------------------------------------------------------- 1 | import os, shutil, subprocess, time, urlparse, json, datetime, json, sys 2 | import logging as log 3 | import db, config 4 | 5 | config_file = open("conf", "rb") 6 | conf = config.Config(json.load(config_file)) 7 | config_file.close() 8 | 9 | STATE_WAITING=1 10 | STATE_DOWNLOADING=2 11 | STATE_PAUSED=3 12 | STATE_COMPLETED=4 13 | STATE_ERROR=5 14 | 15 | class APIError(Exception): 16 | ERROR_REQUEST_DATA_INVALID = 11 17 | 18 | messages = { 19 | ERROR_REQUEST_DATA_INVALID: 'request data invalid', 20 | } 21 | 22 | def __init__(self, code): 23 | self.errno = code 24 | self.message = self.messages[code] 25 | 26 | class API(object): 27 | def __init__(self, curdir='.'): 28 | self.workdir = os.path.abspath(curdir) 29 | 30 | def serve(self, data): 31 | try: 32 | action = data['action'] 33 | if action == 'tasks': 34 | options = data["options"] if data.has_key("options") else {} 35 | ret = self.tasks(options) 36 | elif action == 'pause': 37 | ids = data["ids"] 38 | ret = self.pause(ids) 39 | elif action == 'remove': 40 | ids = data["ids"] 41 | ret = self.remove(ids) 42 | elif action == 'create': 43 | options = data["options"] 44 | ret = self.create(options) 45 | elif action == 'cconfig': 46 | options = data["options"] 47 | ret = self.cconfig(options) 48 | elif action == 'resume': 49 | ids = data["ids"] 50 | for tid in ids: 51 | ret = self.resume(tid) 52 | else: 53 | return dict(success=True, result="") 54 | elif action == 'sort': 55 | ids = data["ids"] 56 | ret = self.sort(ids) 57 | elif action == 'maxspeed': 58 | tid = data["tid"] 59 | ret = self.maxspeed(tid); 60 | elif action == "config": 61 | conf = data["config"] 62 | ret = self.config(conf) 63 | elif action == "rconfig": 64 | ret = self.rconfig() 65 | else: 66 | raise APIError(APIError.ERROR_REQUEST_DATA_INVALID) 67 | 68 | if ret is None: 69 | return dict(success=True) 70 | return dict(success=True, result=ret) 71 | except KeyError: 72 | import traceback; traceback.print_exc() 73 | raise APIError(APIError.ERROR_REQUEST_DATA_INVALID) 74 | except APIError, e: 75 | return dict(success=False, errno=e.errno, errmsg=e.message) 76 | except Exception, e: 77 | # TODO log error 78 | import traceback; traceback.print_exc() 79 | return dict(success=False, errno=0, errmsg="unkown error, please check out the log") 80 | 81 | def download_last(self): 82 | tasks = db.select_tasks(state=STATE_DOWNLOADING) 83 | for task in tasks: 84 | log.debug('start to download %s' % task['id']) 85 | self._start(task['id']) 86 | self.download_more() 87 | 88 | def download_more(self): 89 | tasks = db.select_tasks(state=STATE_DOWNLOADING) 90 | if len(tasks) < conf.task_queue_size: 91 | tasks = db.select_tasks(state=STATE_WAITING) 92 | for task in tasks: 93 | log.debug('start to download %s' % task['id']) 94 | self._start(task['id']) 95 | 96 | def create(self, options): 97 | # just create a new task and then load_more() 98 | url = options["url"] 99 | output = options["output"] if options.has_key("output") and options["output"] else \ 100 | os.path.basename(urlparse.urlparse(url)[2]) 101 | if not options.has_key("immediately") or options["immediately"]: state = STATE_WAITING 102 | else: state = STATE_PAUSED 103 | thsize = options["thsize"] if options.has_key("thsize") else conf.default_thread_size 104 | if not thsize.strip(): 105 | thsize = conf.default_thread_size 106 | maxspeed = options["maxspeed"] if options.has_key("maxspeed") else 0 107 | headers = options["headers"] if options.has_key("headers") else "" 108 | #subdir = options["subdir"] if options.has_key("subdir") else "" 109 | conf.downloads = os.path.expanduser(conf.downloads) 110 | downloads = options["downloads"] if options.has_key("downloads") else conf.downloads 111 | downloads = os.path.expanduser(downloads) 112 | if not downloads.strip(): 113 | downloads = conf.downloads 114 | ua = options["ua"] if options.has_key("ua") else conf.user_agent 115 | if not ua.strip(): 116 | ua = conf.user_agent 117 | tid = db.insert_task(url=url, output=output, state=state, thsize=thsize, maxspeed=maxspeed, headers=headers, downloads=downloads, ua=ua) 118 | 119 | if state == STATE_WAITING: 120 | self.download_more() 121 | 122 | def cconfig(self, options): 123 | with open('conf', 'w') as conf_file: 124 | json.dump(options, conf_file) 125 | 126 | def rconfig(self): 127 | with open('conf', 'r') as f: 128 | data = json.load(f) 129 | return data 130 | 131 | def _start(self, tid): 132 | # start a task existed 133 | tasks = db.select_tasks(id=tid) 134 | if not tasks: 135 | return 136 | 137 | db.update_tasks(tid, state=STATE_DOWNLOADING) 138 | 139 | #create axel task 140 | #print "=====" 141 | #print os.fork() 142 | if os.fork(): # old process 143 | return # as 200 OK 144 | else: # sub process 145 | # get the options 146 | task = tasks[0] 147 | url = task["url"] 148 | output = task["output"] 149 | thsize = task["thsize"] 150 | maxspeed = task["maxspeed"] 151 | headers = task["headers"] 152 | #subdir = task["subdir"] 153 | downloads = task["downloads"] 154 | ua = task["ua"] 155 | 156 | force_download = conf.force_download 157 | output_file = os.path.join(downloads, output) 158 | if os.path.exists(output_file) and not os.path.exists(output_file + '.st'): 159 | if force_download: 160 | os.remove(output_file) 161 | else: 162 | print 'file completed already, skip download' 163 | exit() 164 | os.system("mkdir -p %s" % os.path.join(downloads)) 165 | args = [os.path.join(os.getcwd(), "axel"), "-a", "-n", str(thsize), "-s", str(maxspeed), "-U", str(ua)] 166 | for header in headers.splitlines(): 167 | args.append("-H") 168 | args.append(header) 169 | args.append("-o") 170 | args.append(output_file) 171 | args.append(url) 172 | axel_process = subprocess.Popen(args, shell=False, stdout=subprocess.PIPE, cwd=downloads) 173 | #print(args) 174 | 175 | last_update_time = 0 176 | while 1: 177 | try: 178 | line = axel_process.stdout.readline() 179 | if not line: 180 | break 181 | line = line.strip() 182 | except: 183 | returncode = axel_process.poll() 184 | if returncode is not None: 185 | # axel completed 186 | if returncode: 187 | db.update_tasks(tid, state=STATE_ERROR, errmsg="Error, axel exit with code: %s" % returncode) 188 | break 189 | this_update_time = time.time() 190 | if line.startswith(":"): 191 | done, total, thdone, speed, left, update_time = line[1:].split("|") 192 | if done != total and last_update_time > 0 and this_update_time - last_update_time < 1: 193 | continue 194 | elif line.startswith("HTTP/1."): 195 | db.update_tasks(tid, state=STATE_ERROR, errmsg=line) 196 | break 197 | else: 198 | continue 199 | last_update_time = this_update_time 200 | tasks = db.select_tasks(id=tid) 201 | if tasks: 202 | task = tasks[0] 203 | state = task["state"] 204 | if state == STATE_DOWNLOADING: 205 | try: 206 | if done == total: 207 | #completed 208 | db.update_tasks(tid, state=STATE_COMPLETED, left=0) 209 | os.system("mkdir -p %s" % os.path.join(downloads)) 210 | #os.rename(output_file, os.path.join(conf.downloads, subdir, output)) 211 | break 212 | db.update_tasks(tid, speed=speed, done=done, total=total, left=left) 213 | continue 214 | except Exception, e: 215 | import traceback 216 | traceback.print_exc() 217 | db.update_tasks(tid, state=STATE_ERROR, errmsg="Error, axel exit with code: %s" % e) 218 | try: 219 | axel_process.terminate() 220 | except: 221 | pass 222 | else: 223 | #paused 224 | axel_process.terminate() 225 | else: 226 | #deleted 227 | axel_process.terminate() 228 | try: 229 | os.remove(output_file) 230 | except: 231 | pass 232 | try: 233 | os.remove(output_file + ".st") 234 | except: 235 | pass 236 | returncode = axel_process.poll() 237 | if returncode is not None: 238 | # axel completed 239 | if returncode: 240 | db.update_tasks(tid, state=STATE_ERROR, errmsg="Error, axel exit with code: %s" % returncode) 241 | self.download_more() 242 | sys.exit() 243 | 244 | def resume(self, tid): 245 | # set state to waiting and load_more() 246 | tid = int(tid) 247 | tasks = db.select_tasks(id=tid) 248 | if tasks: 249 | task = tasks[0] 250 | url = task["url"] 251 | output = task["output"] 252 | state = task["state"] 253 | thsize = task["thsize"] 254 | maxspeed = task["maxspeed"] 255 | headers = task["headers"] 256 | #subdir = task["subdir"] 257 | downloads = task["downloads"] 258 | ua = task["ua"] 259 | if state not in (STATE_WAITING, STATE_PAUSED, STATE_ERROR): 260 | return 261 | if state != STATE_WAITING: 262 | db.update_tasks(tid, state=STATE_WAITING) 263 | self.download_more() 264 | 265 | def tasks(self, options): 266 | tasks = db.select_tasks(**options) 267 | for task in tasks: 268 | if task['state'] == STATE_DOWNLOADING and task['update_time'] and task['speed']: 269 | nowt = datetime.datetime.now() 270 | parts = task['update_time'].split('.') 271 | dt = datetime.datetime.strptime(parts[0], "%Y-%m-%d %H:%M:%S") 272 | update_time = dt.replace(microsecond=int(parts[1])) 273 | interval = nowt - update_time 274 | interval_seconds = interval.seconds + interval.microseconds*1.0/1000/1000 275 | if interval_seconds > 2 and interval_seconds * task['speed'] > conf.buffer_size: 276 | speed = conf.buffer_size * 1.0 / interval_seconds 277 | task['speed'] = 0 if speed < 1024 else speed 278 | return tasks 279 | 280 | def pause(self, ids): 281 | db.update_tasks(ids, **{'state': STATE_PAUSED}) 282 | 283 | def remove(self, ids): 284 | db.delete_tasks(ids) 285 | 286 | def sort(self, ids): 287 | orders = [] 288 | for order in range(len(ids)): 289 | db.update_tasks(ids[order], **{'order':order}) 290 | 291 | def maxspeed(self, tid): 292 | return 0 293 | if not conf.total_maxspeed: 294 | return 0 295 | if not conf.total_max: 296 | return 0 297 | tasks = db.select_tasks(state="!=5") 298 | total_speed = 0 299 | for task in tasks: 300 | if task['id'] == tid: 301 | continue 302 | speed = task['speed'] 303 | if speed: 304 | total_speed += task['speed'] 305 | if total_speed > conf.total_max: 306 | return 1 307 | else: 308 | return conf.total_max - total_speed 309 | 310 | def config(self, conf): 311 | try: 312 | dict_conf = json.loads(conf) 313 | conf = json.dumps() 314 | except: 315 | raise APIError(ERROR_REQUEST_DATA_INVALID) 316 | with open('conf', 'wb') as configfile: 317 | configfile.write(conf) 318 | 319 | -------------------------------------------------------------------------------- /axel-2.5.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihaoyun6/axeldown-core/a0dec7dfbbc23f5e0bc9d10bf94c0d651dabc934/axel-2.5.tar.gz -------------------------------------------------------------------------------- /axel-patch/Makefile: -------------------------------------------------------------------------------- 1 | ########################### 2 | ## Makefile for Axel ## 3 | ## ## 4 | ## Copyright 2001 Lintux ## 5 | ########################### 6 | 7 | 8 | ### DEFINITIONS 9 | 10 | -include Makefile.settings 11 | 12 | .SUFFIXES: .po .mo 13 | 14 | # Add your translation here.. 15 | MOFILES = nl.mo de.mo ru.mo zh_CN.mo 16 | 17 | 18 | all: $(OUTFILE) 19 | install: install-bin install-etc install-man 20 | uninstall: uninstall-bin uninstall-etc uninstall-man 21 | 22 | ifdef I18N 23 | all: $(MOFILES) 24 | install: install-i18n 25 | uninstall: uninstall-i18n 26 | endif 27 | 28 | clean: 29 | rm -f *.o $(OUTFILE) search core *.mo 30 | 31 | distclean: clean 32 | rm -f Makefile.settings config.h axel-*.tar axel-*.tar.gz axel-*.tar.bz2 33 | 34 | install-man: 35 | mkdir -p $(DESTDIR)$(MANDIR)/man1/ 36 | cp axel.1 $(DESTDIR)$(MANDIR)/man1/axel.1 37 | mkdir -p $(DESTDIR)$(MANDIR)/zh_CN/man1/ 38 | cp axel_zh_CN.1 $(DESTDIR)$(MANDIR)/zh_CN/man1/axel.1 39 | 40 | uninstall-man: 41 | rm -f $(MANDIR)/man1/axel.1 42 | rm -f $(MANDIR)/zh_CN/man1/axel.1 43 | 44 | install-etc: 45 | mkdir -p $(DESTDIR)$(ETCDIR)/ 46 | cp axelrc.example $(DESTDIR)$(ETCDIR)/axelrc 47 | 48 | uninstall-etc: 49 | rm -f $(ETCDIR)/axelrc 50 | 51 | ### MAIN PROGRAM 52 | 53 | $(OUTFILE): axel.o conf.o conn.o ftp.o http.o search.o tcp.o text.o httppost.o 54 | $(CC) *.o -o $(OUTFILE) $(LFLAGS) 55 | ifndef DEBUG 56 | -$(STRIP) $(OUTFILE) 57 | endif 58 | 59 | .c.o: 60 | $(CC) -c $*.c -o $*.o -Wall $(CFLAGS) 61 | 62 | install-bin: 63 | mkdir -p $(DESTDIR)$(BINDIR)/ 64 | cp $(OUTFILE) $(DESTDIR)$(BINDIR)/$(OUTFILE) 65 | 66 | uninstall-bin: 67 | rm -f $(BINDIR)/$(OUTFILE) 68 | 69 | tar: 70 | version=$$(sed -n 's/#define AXEL_VERSION_STRING[ \t]*"\([^"]*\)"/\1/p' < axel.h) && \ 71 | tar --create --transform "s#^#axel-$${version}/#" "--file=axel-$${version}.tar" --exclude-vcs -- *.c *.h *.po *.1 configure Makefile axelrc.example gui API CHANGES COPYING CREDITS README && \ 72 | gzip --best < "axel-$${version}.tar" > "axel-$${version}.tar.gz" && \ 73 | bzip2 --best < "axel-$${version}.tar" > "axel-$${version}.tar.bz2" 74 | 75 | 76 | ### I18N FILES 77 | 78 | %.po: 79 | -@mv $@ $@.bak 80 | xgettext -k_ -o$@ *.[ch] 81 | @if [ -e $@.bak ]; then \ 82 | echo -n Merging files...; \ 83 | msgmerge -vo $@.combo $@.bak $@; \ 84 | rm -f $@ $@.bak; \ 85 | mv $@.combo $@; \ 86 | fi 87 | 88 | .po.mo: $@.po 89 | msgfmt -vo $@ $*.po 90 | 91 | install-i18n: 92 | @echo Installing locale files... 93 | @for i in $(MOFILES); do \ 94 | mkdir -p $(DESTDIR)$(LOCALE)/`echo $$i | cut -d. -f1`/LC_MESSAGES/; \ 95 | cp $$i $(DESTDIR)$(LOCALE)/`echo $$i | cut -d. -f1`/LC_MESSAGES/axel.mo; \ 96 | done 97 | 98 | uninstall-i18n: 99 | cd $(LOCALE); find . -name axel.mo -exec 'rm' '{}' ';' 100 | -------------------------------------------------------------------------------- /axel-patch/Makefile.patch: -------------------------------------------------------------------------------- 1 | 53c53 2 | < $(OUTFILE): axel.o conf.o conn.o ftp.o http.o search.o tcp.o text.o 3 | --- 4 | > $(OUTFILE): axel.o conf.o conn.o ftp.o http.o search.o tcp.o text.o httppost.o 5 | -------------------------------------------------------------------------------- /axel-patch/axel.c: -------------------------------------------------------------------------------- 1 | /********************************************************************\ 2 | * Axel -- A lighter download accelerator for Linux and other Unices. * 3 | * * 4 | * Copyright 2001 Wilmer van der Gaast * 5 | \********************************************************************/ 6 | 7 | /* Main control */ 8 | 9 | /* 10 | This program is free software; you can redistribute it and/or modify 11 | it under the terms of the GNU General Public License as published by 12 | the Free Software Foundation; either version 2 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License with 21 | the Debian GNU/Linux distribution in file /usr/doc/copyright/GPL; 22 | if not, write to the Free Software Foundation, Inc., 59 Temple Place, 23 | Suite 330, Boston, MA 02111-1307 USA 24 | */ 25 | 26 | #include "axel.h" 27 | 28 | /* Axel */ 29 | static void save_state( axel_t *axel ); 30 | static void *setup_thread( void * ); 31 | static void axel_message( axel_t *axel, char *format, ... ); 32 | static void axel_divide( axel_t *axel ); 33 | 34 | static int read_max_speed_from_http_api( int tid ); 35 | 36 | static char *buffer = NULL; 37 | 38 | static read_max_speed_from_http_api( int tid ) 39 | { 40 | char *json; 41 | char *tpl = "{\"action\":\"maxspeed\", \"tid\":%d}"; 42 | int tidlen = 0; 43 | if (tid < 10) tidlen = 1; 44 | else if (tid < 100) tidlen = 2; 45 | else if (tid < 1000) tidlen = 3; 46 | else tid = 4; 47 | json = (char *)malloc(tidlen+strlen(tpl)-1); 48 | sprintf(json, tpl, tid); 49 | char *c = (char *)malloc(10); 50 | int ret = http_post("localhost", 8080, "/api", json, c); 51 | int max_speed = atoi(c); 52 | free(json); 53 | free(c); 54 | return max_speed; 55 | } 56 | 57 | /* Create a new axel_t structure */ 58 | axel_t *axel_new( conf_t *conf, int count, void *url ) 59 | { 60 | search_t *res; 61 | axel_t *axel; 62 | url_t *u; 63 | char *s; 64 | int i; 65 | 66 | axel = malloc( sizeof( axel_t ) ); 67 | memset( axel, 0, sizeof( axel_t ) ); 68 | *axel->conf = *conf; 69 | axel->conn = malloc( sizeof( conn_t ) * axel->conf->num_connections ); 70 | memset( axel->conn, 0, sizeof( conn_t ) * axel->conf->num_connections ); 71 | 72 | // Read max_speed from python http api 73 | int max_speed = read_max_speed_from_http_api( axel->conf->tid ); 74 | 75 | if( max_speed > 0 ) 76 | { 77 | if( (float) max_speed / axel->conf->buffer_size < 0.5 ) 78 | { 79 | if( axel->conf->verbose >= 2 ) 80 | axel_message( axel, _("Buffer resized for this speed.") ); 81 | axel->conf->buffer_size = max_speed; 82 | } 83 | axel->delay_time = (int) ( (float) 1000000 / max_speed * axel->conf->buffer_size * axel->conf->num_connections ); 84 | } 85 | if( buffer == NULL ) 86 | buffer = malloc( max( MAX_STRING, axel->conf->buffer_size ) ); 87 | 88 | if( count == 0 ) 89 | { 90 | axel->url = malloc( sizeof( url_t ) ); 91 | axel->url->next = axel->url; 92 | strncpy( axel->url->text, (char *) url, MAX_STRING ); 93 | } 94 | else 95 | { 96 | res = (search_t *) url; 97 | u = axel->url = malloc( sizeof( url_t ) ); 98 | for( i = 0; i < count; i ++ ) 99 | { 100 | strncpy( u->text, res[i].url, MAX_STRING ); 101 | if( i < count - 1 ) 102 | { 103 | u->next = malloc( sizeof( url_t ) ); 104 | u = u->next; 105 | } 106 | else 107 | { 108 | u->next = axel->url; 109 | } 110 | } 111 | } 112 | 113 | axel->conn[0].conf = axel->conf; 114 | if( !conn_set( &axel->conn[0], axel->url->text ) ) 115 | { 116 | axel_message( axel, _("Could not parse URL.\n") ); 117 | axel->ready = -1; 118 | return( axel ); 119 | } 120 | 121 | axel->conn[0].local_if = axel->conf->interfaces->text; 122 | axel->conf->interfaces = axel->conf->interfaces->next; 123 | 124 | strncpy( axel->filename, axel->conn[0].file, MAX_STRING ); 125 | http_decode( axel->filename ); 126 | if( *axel->filename == 0 ) /* Index page == no fn */ 127 | strncpy( axel->filename, axel->conf->default_filename, MAX_STRING ); 128 | if( ( s = strchr( axel->filename, '?' ) ) != NULL && axel->conf->strip_cgi_parameters ) 129 | *s = 0; /* Get rid of CGI parameters */ 130 | 131 | if( !conn_init( &axel->conn[0] ) ) 132 | { 133 | axel_message( axel, axel->conn[0].message ); 134 | axel->ready = -1; 135 | return( axel ); 136 | } 137 | 138 | /* This does more than just checking the file size, it all depends 139 | on the protocol used. */ 140 | if( !conn_info( &axel->conn[0] ) ) 141 | { 142 | axel_message( axel, axel->conn[0].message ); 143 | axel->ready = -1; 144 | return( axel ); 145 | } 146 | s = conn_url( axel->conn ); 147 | strncpy( axel->url->text, s, MAX_STRING ); 148 | if( ( axel->size = axel->conn[0].size ) != INT_MAX ) 149 | { 150 | // if( axel->conf->verbose > 0 ) 151 | // axel_message( axel, _("File size: %lld bytes"), axel->size ); 152 | } 153 | 154 | /* Wildcards in URL --> Get complete filename */ 155 | if( strchr( axel->filename, '*' ) || strchr( axel->filename, '?' ) ) 156 | strncpy( axel->filename, axel->conn[0].file, MAX_STRING ); 157 | 158 | return( axel ); 159 | } 160 | 161 | /* Open a local file to store the downloaded data */ 162 | int axel_open( axel_t *axel ) 163 | { 164 | int i, fd; 165 | int r; 166 | long long int j; 167 | 168 | if( axel->conf->verbose > 0 ) 169 | // axel_message( axel, _("Opening output file %s"), axel->filename ); 170 | snprintf( buffer, MAX_STRING, "%s.st", axel->filename ); 171 | 172 | axel->outfd = -1; 173 | 174 | /* Check whether server knows about RESTart and switch back to 175 | single connection download if necessary */ 176 | if( !axel->conn[0].supported ) 177 | { 178 | axel_message( axel, _("Server unsupported, " 179 | "starting from scratch with one connection.") ); 180 | axel->conf->num_connections = 1; 181 | axel->conn = realloc( axel->conn, sizeof( conn_t ) ); 182 | axel_divide( axel ); 183 | } 184 | else if( ( fd = open( buffer, O_RDONLY ) ) != -1 ) 185 | { 186 | r = read( fd, &axel->conf->num_connections, sizeof( axel->conf->num_connections ) ); 187 | 188 | axel->conn = realloc( axel->conn, sizeof( conn_t ) * axel->conf->num_connections ); 189 | memset( axel->conn + 1, 0, sizeof( conn_t ) * ( axel->conf->num_connections - 1 ) ); 190 | 191 | axel_divide( axel ); 192 | 193 | r = read( fd, &axel->bytes_done, sizeof( axel->bytes_done ) ); 194 | for( i = 0; i < axel->conf->num_connections; i ++ ) 195 | r = read( fd, &axel->conn[i].currentbyte, sizeof( axel->conn[i].currentbyte ) ); 196 | 197 | /* axel_message( axel, _("State file found: %lld bytes downloaded, %lld to go."), 198 | axel->bytes_done, axel->size - axel->bytes_done ); 199 | */ 200 | 201 | close( fd ); 202 | 203 | if( ( axel->outfd = open( axel->filename, O_WRONLY, 0666 ) ) == -1 ) 204 | { 205 | axel_message( axel, _("Error opening local file") ); 206 | return( 0 ); 207 | } 208 | } 209 | 210 | /* If outfd == -1 we have to start from scrath now */ 211 | if( axel->outfd == -1 ) 212 | { 213 | axel_divide( axel ); 214 | 215 | if( ( axel->outfd = open( axel->filename, O_CREAT | O_WRONLY, 0666 ) ) == -1 ) 216 | { 217 | axel_message( axel, _("Error opening local file") ); 218 | return( 0 ); 219 | } 220 | 221 | /* And check whether the filesystem can handle seeks to 222 | past-EOF areas.. Speeds things up. :) AFAIK this 223 | should just not happen: */ 224 | if( lseek( axel->outfd, axel->size, SEEK_SET ) == -1 && axel->conf->num_connections > 1 ) 225 | { 226 | /* But if the OS/fs does not allow to seek behind 227 | EOF, we have to fill the file with zeroes before 228 | starting. Slow.. */ 229 | axel_message( axel, _("Crappy filesystem/OS.. Working around. :-(") ); 230 | lseek( axel->outfd, 0, SEEK_SET ); 231 | memset( buffer, 0, axel->conf->buffer_size ); 232 | j = axel->size; 233 | while( j > 0 ) 234 | { 235 | r = write( axel->outfd, buffer, min( j, axel->conf->buffer_size ) ); 236 | j -= axel->conf->buffer_size; 237 | } 238 | } 239 | } 240 | 241 | return( 1 ); 242 | } 243 | 244 | /* Start downloading */ 245 | void axel_start( axel_t *axel ) 246 | { 247 | int i; 248 | 249 | /* HTTP might've redirected and FTP handles wildcards, so 250 | re-scan the URL for every conn */ 251 | for( i = 0; i < axel->conf->num_connections; i ++ ) 252 | { 253 | conn_set( &axel->conn[i], axel->url->text ); 254 | axel->url = axel->url->next; 255 | axel->conn[i].local_if = axel->conf->interfaces->text; 256 | axel->conf->interfaces = axel->conf->interfaces->next; 257 | axel->conn[i].conf = axel->conf; 258 | if( i ) axel->conn[i].supported = 1; 259 | } 260 | 261 | // if( axel->conf->verbose > 0 ) 262 | // axel_message( axel, _("Starting download") ); 263 | 264 | for( i = 0; i < axel->conf->num_connections; i ++ ) 265 | if( axel->conn[i].currentbyte <= axel->conn[i].lastbyte ) 266 | { 267 | if( axel->conf->verbose >= 2 ) 268 | { 269 | axel_message( axel, _("Connection %i downloading from %s:%i using interface %s"), 270 | i, axel->conn[i].host, axel->conn[i].port, axel->conn[i].local_if ); 271 | } 272 | 273 | axel->conn[i].state = 1; 274 | if( pthread_create( axel->conn[i].setup_thread, NULL, setup_thread, &axel->conn[i] ) != 0 ) 275 | { 276 | axel_message( axel, _("pthread error!!!") ); 277 | axel->ready = -1; 278 | } 279 | else 280 | { 281 | axel->conn[i].last_transfer = gettime(); 282 | } 283 | } 284 | 285 | /* The real downloading will start now, so let's start counting */ 286 | axel->start_time = gettime(); 287 | axel->ready = 0; 288 | } 289 | 290 | /* Main 'loop' */ 291 | void axel_do( axel_t *axel ) 292 | { 293 | fd_set fds[1]; 294 | int hifd, i; 295 | long long int remaining,size; 296 | struct timeval timeval[1]; 297 | 298 | /* Create statefile if necessary */ 299 | if( gettime() > axel->next_state ) 300 | { 301 | save_state( axel ); 302 | axel->next_state = gettime() + axel->conf->save_state_interval; 303 | } 304 | 305 | /* Wait for data on (one of) the connections */ 306 | FD_ZERO( fds ); 307 | hifd = 0; 308 | for( i = 0; i < axel->conf->num_connections; i ++ ) 309 | { 310 | if( axel->conn[i].enabled ) 311 | FD_SET( axel->conn[i].fd, fds ); 312 | hifd = max( hifd, axel->conn[i].fd ); 313 | } 314 | if( hifd == 0 ) 315 | { 316 | /* No connections yet. Wait... */ 317 | usleep( 100000 ); 318 | goto conn_check; 319 | } 320 | else 321 | { 322 | timeval->tv_sec = 0; 323 | timeval->tv_usec = 100000; 324 | /* A select() error probably means it was interrupted 325 | by a signal, or that something else's very wrong... */ 326 | if( select( hifd + 1, fds, NULL, NULL, timeval ) == -1 ) 327 | { 328 | axel->ready = -1; 329 | return; 330 | } 331 | } 332 | 333 | /* Handle connections which need attention */ 334 | for( i = 0; i < axel->conf->num_connections; i ++ ) 335 | if( axel->conn[i].enabled ) { 336 | if( FD_ISSET( axel->conn[i].fd, fds ) ) 337 | { 338 | axel->conn[i].last_transfer = gettime(); 339 | size = read( axel->conn[i].fd, buffer, axel->conf->buffer_size ); 340 | if( size == -1 ) 341 | { 342 | if( axel->conf->verbose ) 343 | { 344 | axel_message( axel, _("Error on connection %i! " 345 | "Connection closed"), i ); 346 | } 347 | axel->conn[i].enabled = 0; 348 | conn_disconnect( &axel->conn[i] ); 349 | continue; 350 | } 351 | else if( size == 0 ) 352 | { 353 | if( axel->conf->verbose ) 354 | { 355 | /* Only abnormal behaviour if: */ 356 | if( axel->conn[i].currentbyte < axel->conn[i].lastbyte && axel->size != INT_MAX ) 357 | { 358 | axel_message( axel, _("Connection %i unexpectedly closed"), i ); 359 | } 360 | else 361 | { 362 | axel_message( axel, _("Connection %i finished"), i ); 363 | } 364 | } 365 | if( !axel->conn[0].supported ) 366 | { 367 | axel->ready = 1; 368 | } 369 | axel->conn[i].enabled = 0; 370 | conn_disconnect( &axel->conn[i] ); 371 | continue; 372 | } 373 | /* remaining == Bytes to go */ 374 | remaining = axel->conn[i].lastbyte - axel->conn[i].currentbyte + 1; 375 | if( remaining < size ) 376 | { 377 | if( axel->conf->verbose ) 378 | { 379 | axel_message( axel, _("Connection %i finished"), i ); 380 | } 381 | axel->conn[i].enabled = 0; 382 | conn_disconnect( &axel->conn[i] ); 383 | size = remaining; 384 | /* Don't terminate, still stuff to write! */ 385 | } 386 | /* This should always succeed.. */ 387 | lseek( axel->outfd, axel->conn[i].currentbyte, SEEK_SET ); 388 | if( write( axel->outfd, buffer, size ) != size ) 389 | { 390 | 391 | axel_message( axel, _("Write error!") ); 392 | axel->ready = -1; 393 | return; 394 | } 395 | axel->conn[i].currentbyte += size; 396 | axel->bytes_done += size; 397 | } 398 | else 399 | { 400 | if( gettime() > axel->conn[i].last_transfer + axel->conf->connection_timeout ) 401 | { 402 | if( axel->conf->verbose ) 403 | axel_message( axel, _("Connection %i timed out"), i ); 404 | conn_disconnect( &axel->conn[i] ); 405 | axel->conn[i].enabled = 0; 406 | } 407 | } } 408 | 409 | if( axel->ready ) 410 | return; 411 | 412 | conn_check: 413 | /* Look for aborted connections and attempt to restart them. */ 414 | for( i = 0; i < axel->conf->num_connections; i ++ ) 415 | { 416 | if( !axel->conn[i].enabled && axel->conn[i].currentbyte < axel->conn[i].lastbyte ) 417 | { 418 | if( axel->conn[i].state == 0 ) 419 | { 420 | // Wait for termination of this thread 421 | pthread_join(*(axel->conn[i].setup_thread), NULL); 422 | 423 | conn_set( &axel->conn[i], axel->url->text ); 424 | axel->url = axel->url->next; 425 | /* axel->conn[i].local_if = axel->conf->interfaces->text; 426 | axel->conf->interfaces = axel->conf->interfaces->next; */ 427 | if( axel->conf->verbose >= 2 ) 428 | axel_message( axel, _("Connection %i downloading from %s:%i using interface %s"), 429 | i, axel->conn[i].host, axel->conn[i].port, axel->conn[i].local_if ); 430 | 431 | axel->conn[i].state = 1; 432 | if( pthread_create( axel->conn[i].setup_thread, NULL, setup_thread, &axel->conn[i] ) == 0 ) 433 | { 434 | axel->conn[i].last_transfer = gettime(); 435 | } 436 | else 437 | { 438 | axel_message( axel, _("pthread error!!!") ); 439 | axel->ready = -1; 440 | } 441 | } 442 | else 443 | { 444 | if( gettime() > axel->conn[i].last_transfer + axel->conf->reconnect_delay ) 445 | { 446 | pthread_cancel( *axel->conn[i].setup_thread ); 447 | axel->conn[i].state = 0; 448 | } 449 | } 450 | } 451 | } 452 | 453 | /* Calculate current average speed and finish_time */ 454 | axel->bytes_per_second = (int) ( (double) ( axel->bytes_done - axel->start_byte ) / ( gettime() - axel->start_time ) ); 455 | axel->finish_time = (int) ( axel->start_time + (double) ( axel->size - axel->start_byte ) / axel->bytes_per_second ); 456 | 457 | /* Check speed. If too high, delay for some time to slow things 458 | down a bit. I think a 5% deviation should be acceptable. */ 459 | 460 | // Read max_speed from api 461 | int max_speed = read_max_speed_from_http_api( axel->conf->tid ); 462 | 463 | if( axel->conf->max_speed > 0 ) 464 | { 465 | if( (float) axel->bytes_per_second / axel->conf->max_speed > 1.05 ) 466 | axel->delay_time += 10000; 467 | else if( ( (float) axel->bytes_per_second / axel->conf->max_speed < 0.95 ) && ( axel->delay_time >= 10000 ) ) 468 | axel->delay_time -= 10000; 469 | else if( ( (float) axel->bytes_per_second / axel->conf->max_speed < 0.95 ) ) 470 | axel->delay_time = 0; 471 | usleep( axel->delay_time ); 472 | } 473 | 474 | /* Ready? */ 475 | if( axel->bytes_done == axel->size ) 476 | axel->ready = 1; 477 | } 478 | 479 | /* Close an axel connection */ 480 | void axel_close( axel_t *axel ) 481 | { 482 | int i; 483 | message_t *m; 484 | 485 | /* Terminate any thread still running */ 486 | for( i = 0; i < axel->conf->num_connections; i ++ ) 487 | /* don't try to kill non existing thread */ 488 | if ( *axel->conn[i].setup_thread != 0 ) 489 | pthread_cancel( *axel->conn[i].setup_thread ); 490 | 491 | /* Delete state file if necessary */ 492 | if( axel->ready == 1 ) 493 | { 494 | snprintf( buffer, MAX_STRING, "%s.st", axel->filename ); 495 | unlink( buffer ); 496 | } 497 | /* Else: Create it.. */ 498 | else if( axel->bytes_done > 0 ) 499 | { 500 | save_state( axel ); 501 | } 502 | 503 | /* Delete any message not processed yet */ 504 | while( axel->message ) 505 | { 506 | m = axel->message; 507 | axel->message = axel->message->next; 508 | free( m ); 509 | } 510 | 511 | /* Close all connections and local file */ 512 | close( axel->outfd ); 513 | for( i = 0; i < axel->conf->num_connections; i ++ ) 514 | conn_disconnect( &axel->conn[i] ); 515 | 516 | free( axel->conn ); 517 | free( axel ); 518 | } 519 | 520 | /* time() with more precision */ 521 | double gettime() 522 | { 523 | struct timeval time[1]; 524 | 525 | gettimeofday( time, 0 ); 526 | return( (double) time->tv_sec + (double) time->tv_usec / 1000000 ); 527 | } 528 | 529 | /* Save the state of the current download */ 530 | void save_state( axel_t *axel ) 531 | { 532 | int fd, i; 533 | char fn[MAX_STRING+4]; 534 | 535 | /* No use for such a file if the server doesn't support 536 | resuming anyway.. */ 537 | if( !axel->conn[0].supported ) 538 | return; 539 | 540 | snprintf( fn, MAX_STRING, "%s.st", axel->filename ); 541 | if( ( fd = open( fn, O_CREAT | O_TRUNC | O_WRONLY, 0666 ) ) == -1 ) 542 | { 543 | return; /* Not 100% fatal.. */ 544 | } 545 | int r = write( fd, &axel->conf->num_connections, sizeof( axel->conf->num_connections ) ); 546 | r = write( fd, &axel->bytes_done, sizeof( axel->bytes_done ) ); 547 | for( i = 0; i < axel->conf->num_connections; i ++ ) 548 | { 549 | r = write( fd, &axel->conn[i].currentbyte, sizeof( axel->conn[i].currentbyte ) ); 550 | } 551 | close( fd ); 552 | } 553 | 554 | /* Thread used to set up a connection */ 555 | void *setup_thread( void *c ) 556 | { 557 | conn_t *conn = c; 558 | int oldstate; 559 | 560 | /* Allow this thread to be killed at any time. */ 561 | pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldstate ); 562 | pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, &oldstate ); 563 | 564 | if( conn_setup( conn ) ) 565 | { 566 | conn->last_transfer = gettime(); 567 | if( conn_exec( conn ) ) 568 | { 569 | conn->last_transfer = gettime(); 570 | conn->enabled = 1; 571 | conn->state = 0; 572 | return( NULL ); 573 | } 574 | } 575 | 576 | conn_disconnect( conn ); 577 | conn->state = 0; 578 | return( NULL ); 579 | } 580 | 581 | /* Add a message to the axel->message structure */ 582 | static void axel_message( axel_t *axel, char *format, ... ) 583 | { 584 | message_t *m = malloc( sizeof( message_t ) ), *n = axel->message; 585 | va_list params; 586 | 587 | memset( m, 0, sizeof( message_t ) ); 588 | va_start( params, format ); 589 | vsnprintf( m->text, MAX_STRING, format, params ); 590 | va_end( params ); 591 | 592 | if( axel->message == NULL ) 593 | { 594 | axel->message = m; 595 | } 596 | else 597 | { 598 | while( n->next != NULL ) 599 | n = n->next; 600 | n->next = m; 601 | } 602 | } 603 | 604 | /* Divide the file and set the locations for each connection */ 605 | static void axel_divide( axel_t *axel ) 606 | { 607 | int i; 608 | 609 | axel->conn[0].currentbyte = 0; 610 | axel->conn[0].lastbyte = axel->size / axel->conf->num_connections - 1; 611 | for( i = 1; i < axel->conf->num_connections; i ++ ) 612 | { 613 | #ifdef DEBUG 614 | printf( "Downloading %lld-%lld using conn. %i\n", axel->conn[i-1].currentbyte, axel->conn[i-1].lastbyte, i - 1 ); 615 | #endif 616 | axel->conn[i].currentbyte = axel->conn[i-1].lastbyte + 1; 617 | axel->conn[i].lastbyte = axel->conn[i].currentbyte + axel->size / axel->conf->num_connections; 618 | } 619 | axel->conn[axel->conf->num_connections-1].lastbyte = axel->size - 1; 620 | #ifdef DEBUG 621 | printf( "Downloading %lld-%lld using conn. %i\n", axel->conn[i-1].currentbyte, axel->conn[i-1].lastbyte, i - 1 ); 622 | #endif 623 | } 624 | -------------------------------------------------------------------------------- /axel-patch/axel.c.patch: -------------------------------------------------------------------------------- 1 | 33a34,35 2 | > static int read_max_speed_from_http_api( int tid ); 3 | > 4 | 35a38,54 5 | > static read_max_speed_from_http_api( int tid ) 6 | > { 7 | > char *json; 8 | > char *tpl = "{\"action\":\"maxspeed\", \"tid\":%d}"; 9 | > int tidlen = 0; 10 | > if (tid < 10) tidlen = 1; 11 | > else if (tid < 100) tidlen = 2; 12 | > else if (tid < 1000) tidlen = 3; 13 | > else tid = 4; 14 | > json = (char *)malloc(tidlen+strlen(tpl)-1); 15 | > sprintf(json, tpl, tid); 16 | > char *c = (char *)malloc(10); 17 | > int ret = http_post("localhost", 8080, "/api", json, c); 18 | > int max_speed = atoi(c); 19 | > return max_speed; 20 | > } 21 | > 22 | 50c69,73 23 | < if( axel->conf->max_speed > 0 ) 24 | --- 25 | > 26 | > // Read max_speed from python http api 27 | > int max_speed = read_max_speed_from_http_api( axel->conf->tid ); 28 | > 29 | > if( max_speed > 0 ) 30 | 52c75 31 | < if( (float) axel->conf->max_speed / axel->conf->buffer_size < 0.5 ) 32 | --- 33 | > if( (float) max_speed / axel->conf->buffer_size < 0.5 ) 34 | 56c79 35 | < axel->conf->buffer_size = axel->conf->max_speed; 36 | --- 37 | > axel->conf->buffer_size = max_speed; 38 | 58c81 39 | < axel->delay_time = (int) ( (float) 1000000 / axel->conf->max_speed * axel->conf->buffer_size * axel->conf->num_connections ); 40 | --- 41 | > axel->delay_time = (int) ( (float) 1000000 / max_speed * axel->conf->buffer_size * axel->conf->num_connections ); 42 | 125,126c148,149 43 | < if( axel->conf->verbose > 0 ) 44 | < axel_message( axel, _("File size: %lld bytes"), axel->size ); 45 | --- 46 | > // if( axel->conf->verbose > 0 ) 47 | > // axel_message( axel, _("File size: %lld bytes"), axel->size ); 48 | 139a163 49 | > int r; 50 | 143c167 51 | < axel_message( axel, _("Opening output file %s"), axel->filename ); 52 | --- 53 | > // axel_message( axel, _("Opening output file %s"), axel->filename ); 54 | 160c184 55 | < read( fd, &axel->conf->num_connections, sizeof( axel->conf->num_connections ) ); 56 | --- 57 | > r = read( fd, &axel->conf->num_connections, sizeof( axel->conf->num_connections ) ); 58 | 167c191 59 | < read( fd, &axel->bytes_done, sizeof( axel->bytes_done ) ); 60 | --- 61 | > r = read( fd, &axel->bytes_done, sizeof( axel->bytes_done ) ); 62 | 169c193 63 | < read( fd, &axel->conn[i].currentbyte, sizeof( axel->conn[i].currentbyte ) ); 64 | --- 65 | > r = read( fd, &axel->conn[i].currentbyte, sizeof( axel->conn[i].currentbyte ) ); 66 | 171c195 67 | < axel_message( axel, _("State file found: %lld bytes downloaded, %lld to go."), 68 | --- 69 | > /* axel_message( axel, _("State file found: %lld bytes downloaded, %lld to go."), 70 | 172a197 71 | > */ 72 | 208c233 73 | < write( axel->outfd, buffer, min( j, axel->conf->buffer_size ) ); 74 | --- 75 | > r = write( axel->outfd, buffer, min( j, axel->conf->buffer_size ) ); 76 | 234,235c259,260 77 | < if( axel->conf->verbose > 0 ) 78 | < axel_message( axel, _("Starting download") ); 79 | --- 80 | > // if( axel->conf->verbose > 0 ) 81 | > // axel_message( axel, _("Starting download") ); 82 | 431a457,460 83 | > 84 | > // Read max_speed from api 85 | > int max_speed = read_max_speed_from_http_api( axel->conf->tid ); 86 | > 87 | 514,515c543,544 88 | < write( fd, &axel->conf->num_connections, sizeof( axel->conf->num_connections ) ); 89 | < write( fd, &axel->bytes_done, sizeof( axel->bytes_done ) ); 90 | --- 91 | > int r = write( fd, &axel->conf->num_connections, sizeof( axel->conf->num_connections ) ); 92 | > r = write( fd, &axel->bytes_done, sizeof( axel->bytes_done ) ); 93 | 518c547 94 | < write( fd, &axel->conn[i].currentbyte, sizeof( axel->conn[i].currentbyte ) ); 95 | --- 96 | > r = write( fd, &axel->conn[i].currentbyte, sizeof( axel->conn[i].currentbyte ) ); 97 | -------------------------------------------------------------------------------- /axel-patch/axel.h: -------------------------------------------------------------------------------- 1 | /********************************************************************\ 2 | * Axel -- A lighter download accelerator for Linux and other Unices. * 3 | * * 4 | * Copyright 2001 Wilmer van der Gaast * 5 | \********************************************************************/ 6 | 7 | /* Main include file */ 8 | 9 | /* 10 | This program is free software; you can redistribute it and/or modify 11 | it under the terms of the GNU General Public License as published by 12 | the Free Software Foundation; either version 2 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License with 21 | the Debian GNU/Linux distribution in file /usr/doc/copyright/GPL; 22 | if not, write to the Free Software Foundation, Inc., 59 Temple Place, 23 | Suite 330, Boston, MA 02111-1307 USA 24 | */ 25 | 26 | #include "config.h" 27 | #include "httpget.h" 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #ifndef NOGETOPTLONG 36 | #define _GNU_SOURCE 37 | #include 38 | #endif 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | 58 | /* Internationalization */ 59 | #ifdef I18N 60 | #define PACKAGE "axel" 61 | #define _( x ) gettext( x ) 62 | #include 63 | #include 64 | #else 65 | #define _( x ) x 66 | #endif 67 | 68 | /* Compiled-in settings */ 69 | #define MAX_STRING 1024 70 | #define MAX_ADD_HEADERS 10 71 | #define MAX_REDIR 5 72 | #define AXEL_VERSION_STRING "2.4" 73 | #define DEFAULT_USER_AGENT "Axel " AXEL_VERSION_STRING " (" ARCH ")" 74 | 75 | typedef struct 76 | { 77 | void *next; 78 | char text[MAX_STRING]; 79 | } message_t; 80 | 81 | typedef message_t url_t; 82 | typedef message_t if_t; 83 | 84 | #include "conf.h" 85 | #include "tcp.h" 86 | #include "ftp.h" 87 | #include "http.h" 88 | #include "conn.h" 89 | #include "search.h" 90 | 91 | #define min( a, b ) ( (a) < (b) ? (a) : (b) ) 92 | #define max( a, b ) ( (a) > (b) ? (a) : (b) ) 93 | 94 | typedef struct 95 | { 96 | conn_t *conn; 97 | conf_t conf[1]; 98 | char filename[MAX_STRING]; 99 | double start_time; 100 | int next_state, finish_time; 101 | long long bytes_done, start_byte, size; 102 | int bytes_per_second; 103 | int delay_time; 104 | int outfd; 105 | int ready; 106 | message_t *message; 107 | url_t *url; 108 | } axel_t; 109 | 110 | axel_t *axel_new( conf_t *conf, int count, void *url ); 111 | int axel_open( axel_t *axel ); 112 | void axel_start( axel_t *axel ); 113 | void axel_do( axel_t *axel ); 114 | void axel_close( axel_t *axel ); 115 | 116 | double gettime(); 117 | -------------------------------------------------------------------------------- /axel-patch/conf.c: -------------------------------------------------------------------------------- 1 | /********************************************************************\ 2 | * Axel -- A lighter download accelerator for Linux and other Unices. * 3 | * * 4 | * Copyright 2001 Wilmer van der Gaast * 5 | \********************************************************************/ 6 | 7 | /* Configuration handling file */ 8 | 9 | /* 10 | This program is free software; you can redistribute it and/or modify 11 | it under the terms of the GNU General Public License as published by 12 | the Free Software Foundation; either version 2 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License with 21 | the Debian GNU/Linux distribution in file /usr/doc/copyright/GPL; 22 | if not, write to the Free Software Foundation, Inc., 59 Temple Place, 23 | Suite 330, Boston, MA 02111-1307 USA 24 | */ 25 | 26 | #include "axel.h" 27 | 28 | /* Some nifty macro's.. */ 29 | #define get_config_string( name ) \ 30 | if( strcmp( key, #name ) == 0 ) \ 31 | { \ 32 | st = 1; \ 33 | strcpy( conf->name, value ); \ 34 | } 35 | #define get_config_number( name ) \ 36 | if( strcmp( key, #name ) == 0 ) \ 37 | { \ 38 | st = 1; \ 39 | sscanf( value, "%i", &conf->name ); \ 40 | } 41 | 42 | int parse_interfaces( conf_t *conf, char *s ); 43 | 44 | int conf_loadfile( conf_t *conf, char *file ) 45 | { 46 | int i, line = 0; 47 | FILE *fp; 48 | char s[MAX_STRING], key[MAX_STRING], value[MAX_STRING]; 49 | 50 | fp = fopen( file, "r" ); 51 | if( fp == NULL ) 52 | return( 1 ); /* Not a real failure */ 53 | 54 | while( !feof( fp ) ) 55 | { 56 | int st; 57 | 58 | line ++; 59 | 60 | *s = 0; 61 | fscanf( fp, "%100[^\n#]s", s ); 62 | fscanf( fp, "%*[^\n]s" ); 63 | fgetc( fp ); /* Skip newline */ 64 | if( strchr( s, '=' ) == NULL ) 65 | continue; /* Probably empty? */ 66 | sscanf( s, "%[^= \t]s", key ); 67 | for( i = 0; s[i]; i ++ ) 68 | if( s[i] == '=' ) 69 | { 70 | for( i ++; isspace( (int) s[i] ) && s[i]; i ++ ); 71 | break; 72 | } 73 | strcpy( value, &s[i] ); 74 | for( i = strlen( value ) - 1; isspace( (int) value[i] ); i -- ) 75 | value[i] = 0; 76 | 77 | st = 0; 78 | 79 | /* Long live macros!! */ 80 | get_config_string( default_filename ); 81 | get_config_string( http_proxy ); 82 | get_config_string( no_proxy ); 83 | get_config_number( strip_cgi_parameters ); 84 | get_config_number( save_state_interval ); 85 | get_config_number( connection_timeout ); 86 | get_config_number( reconnect_delay ); 87 | get_config_number( num_connections ); 88 | get_config_number( buffer_size ); 89 | get_config_number( max_speed ); 90 | get_config_number( verbose ); 91 | get_config_number( alternate_output ); 92 | 93 | get_config_number( search_timeout ); 94 | get_config_number( search_threads ); 95 | get_config_number( search_amount ); 96 | get_config_number( search_top ); 97 | 98 | /* Option defunct but shouldn't be an error */ 99 | if( strcmp( key, "speed_type" ) == 0 ) 100 | st = 1; 101 | 102 | if( strcmp( key, "interfaces" ) == 0 ) 103 | st = parse_interfaces( conf, value ); 104 | 105 | if( !st ) 106 | { 107 | fprintf( stderr, _("Error in %s line %i.\n"), file, line ); 108 | return( 0 ); 109 | } 110 | get_config_number( add_header_count ); 111 | for(i=0;iadd_header_count;i++) 112 | get_config_string( add_header[i] ); 113 | get_config_string( user_agent ); 114 | } 115 | 116 | fclose( fp ); 117 | return( 1 ); 118 | } 119 | 120 | int conf_init( conf_t *conf ) 121 | { 122 | char s[MAX_STRING], *s2; 123 | int i; 124 | 125 | /* Set defaults */ 126 | memset( conf, 0, sizeof( conf_t ) ); 127 | strcpy( conf->default_filename, "default" ); 128 | *conf->http_proxy = 0; 129 | *conf->no_proxy = 0; 130 | conf->strip_cgi_parameters = 1; 131 | conf->save_state_interval = 10; 132 | conf->connection_timeout = 45; 133 | conf->reconnect_delay = 5; 134 | conf->num_connections = 4; 135 | conf->buffer_size = 5120; 136 | conf->max_speed = 0; 137 | conf->verbose = 1; 138 | conf->alternate_output = 0; 139 | 140 | conf->search_timeout = 10; 141 | conf->search_threads = 3; 142 | conf->search_amount = 15; 143 | conf->search_top = 3; 144 | conf->add_header_count = 0; 145 | strncpy( conf->user_agent, DEFAULT_USER_AGENT, MAX_STRING ); 146 | 147 | conf->interfaces = malloc( sizeof( if_t ) ); 148 | memset( conf->interfaces, 0, sizeof( if_t ) ); 149 | conf->interfaces->next = conf->interfaces; 150 | 151 | if( ( s2 = getenv( "http_proxy" ) ) != NULL ) 152 | strncpy( conf->http_proxy, s2, MAX_STRING ); 153 | else if( ( s2 = getenv( "HTTP_PROXY" ) ) != NULL ) 154 | strncpy( conf->http_proxy, s2, MAX_STRING ); 155 | 156 | if( !conf_loadfile( conf, ETCDIR "/axelrc" ) ) 157 | return( 0 ); 158 | 159 | if( ( s2 = getenv( "HOME" ) ) != NULL ) 160 | { 161 | sprintf( s, "%s/%s", s2, ".axelrc" ); 162 | if( !conf_loadfile( conf, s ) ) 163 | return( 0 ); 164 | } 165 | 166 | /* Convert no_proxy to a 0-separated-and-00-terminated list.. */ 167 | for( i = 0; conf->no_proxy[i]; i ++ ) 168 | if( conf->no_proxy[i] == ',' ) 169 | conf->no_proxy[i] = 0; 170 | conf->no_proxy[i+1] = 0; 171 | 172 | return( 1 ); 173 | } 174 | 175 | int parse_interfaces( conf_t *conf, char *s ) 176 | { 177 | char *s2; 178 | if_t *iface; 179 | 180 | iface = conf->interfaces->next; 181 | while( iface != conf->interfaces ) 182 | { 183 | if_t *i; 184 | 185 | i = iface->next; 186 | free( iface ); 187 | iface = i; 188 | } 189 | free( conf->interfaces ); 190 | 191 | if( !*s ) 192 | { 193 | conf->interfaces = malloc( sizeof( if_t ) ); 194 | memset( conf->interfaces, 0, sizeof( if_t ) ); 195 | conf->interfaces->next = conf->interfaces; 196 | return( 1 ); 197 | } 198 | 199 | s[strlen(s)+1] = 0; 200 | conf->interfaces = iface = malloc( sizeof( if_t ) ); 201 | while( 1 ) 202 | { 203 | while( ( *s == ' ' || *s == '\t' ) && *s ) s ++; 204 | for( s2 = s; *s2 != ' ' && *s2 != '\t' && *s2; s2 ++ ); 205 | *s2 = 0; 206 | if( *s < '0' || *s > '9' ) 207 | get_if_ip( s, iface->text ); 208 | else 209 | strcpy( iface->text, s ); 210 | s = s2 + 1; 211 | if( *s ) 212 | { 213 | iface->next = malloc( sizeof( if_t ) ); 214 | iface = iface->next; 215 | } 216 | else 217 | { 218 | iface->next = conf->interfaces; 219 | break; 220 | } 221 | } 222 | 223 | return( 1 ); 224 | } 225 | -------------------------------------------------------------------------------- /axel-patch/conf.c.patch: -------------------------------------------------------------------------------- 1 | 133c133 2 | < conf->reconnect_delay = 20; 3 | --- 4 | > conf->reconnect_delay = 5; 5 | -------------------------------------------------------------------------------- /axel-patch/conf.h: -------------------------------------------------------------------------------- 1 | /********************************************************************\ 2 | * Axel -- A lighter download accelerator for Linux and other Unices. * 3 | * * 4 | * Copyright 2001 Wilmer van der Gaast * 5 | \********************************************************************/ 6 | 7 | /* Configuration handling include file */ 8 | 9 | /* 10 | This program is free software; you can redistribute it and/or modify 11 | it under the terms of the GNU General Public License as published by 12 | the Free Software Foundation; either version 2 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License with 21 | the Debian GNU/Linux distribution in file /usr/doc/copyright/GPL; 22 | if not, write to the Free Software Foundation, Inc., 59 Temple Place, 23 | Suite 330, Boston, MA 02111-1307 USA 24 | */ 25 | 26 | typedef struct 27 | { 28 | char default_filename[MAX_STRING]; 29 | char http_proxy[MAX_STRING]; 30 | char no_proxy[MAX_STRING]; 31 | int strip_cgi_parameters; 32 | int save_state_interval; 33 | int connection_timeout; 34 | int reconnect_delay; 35 | int num_connections; 36 | int buffer_size; 37 | int max_speed; 38 | int tid; 39 | int verbose; 40 | int alternate_output; 41 | 42 | if_t *interfaces; 43 | 44 | int search_timeout; 45 | int search_threads; 46 | int search_amount; 47 | int search_top; 48 | 49 | int add_header_count; 50 | char add_header[MAX_ADD_HEADERS][MAX_STRING]; 51 | 52 | char user_agent[MAX_STRING]; 53 | } conf_t; 54 | 55 | int conf_loadfile( conf_t *conf, char *file ); 56 | int conf_init( conf_t *conf ); 57 | -------------------------------------------------------------------------------- /axel-patch/conf.h.patch: -------------------------------------------------------------------------------- 1 | 37a38 2 | > int tid; 3 | -------------------------------------------------------------------------------- /axel-patch/httppost.c: -------------------------------------------------------------------------------- 1 | #include "httppost.h" 2 | #define HOST "coding.debuntu.org" 3 | #define PAGE "/" 4 | #define PORT 80 5 | #define USERAGENT "HTMLGET 1.0" 6 | 7 | #define BUFSIZE 1200 8 | char *get_ip(char *host) 9 | { 10 | struct hostent *hent; 11 | int iplen = 15; //XXX.XXX.XXX.XXX 12 | char *ip = (char *)malloc(iplen+1); 13 | memset(ip, 0, iplen+1); 14 | if((hent = gethostbyname(host)) == NULL) { 15 | //Can't get IP 16 | return NULL; 17 | } 18 | if(inet_ntop(AF_INET, (void *)hent->h_addr_list[0], ip, iplen) == NULL) { 19 | //Can't resolve host 20 | return NULL; 21 | } 22 | //fprintf(stdout, "%s", ip); 23 | return ip; 24 | } 25 | 26 | int http_post(char *host, int port, char *page, char *data, char *response) 27 | { 28 | struct sockaddr_in *remote; 29 | int sock; 30 | int tmpres; 31 | char *ip; 32 | char *post; 33 | char buf[BUFSIZE+1]; 34 | 35 | if((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){ 36 | return NULL; 37 | } 38 | ip = get_ip(host);//"123.125.114.14"; 39 | remote = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in *)); 40 | remote->sin_family = AF_INET; 41 | tmpres = inet_pton(AF_INET, ip, (void *)(&(remote->sin_addr.s_addr))); 42 | if( tmpres < 0) { 43 | //Can't set remote->sin_addr.s_addr 44 | return NULL; 45 | }else if(tmpres == 0) { 46 | return NULL; 47 | } 48 | remote->sin_port = htons(port); 49 | 50 | if(connect(sock, (struct sockaddr *)remote, sizeof(struct sockaddr)) < 0){ 51 | //Could not connect 52 | return NULL; 53 | } 54 | post = build_post_query(host, page, data); 55 | 56 | //Send the query to the server 57 | int sent = 0; 58 | while(sent < strlen(post)) { 59 | tmpres = send(sock, post+sent, strlen(post)-sent, 0); 60 | if(tmpres == -1){ 61 | return NULL; 62 | //Can't send query 63 | } 64 | sent += tmpres; 65 | } 66 | //now it is time to receive the page 67 | memset(buf, 0, sizeof(buf)); 68 | int htmlstart = 0; 69 | char * htmlcontent; 70 | size_t alllen = 0; 71 | while((tmpres = recv(sock, buf, BUFSIZE, 0)) > 0){ 72 | if(htmlstart == 0){ 73 | htmlcontent = strstr(buf, "\r\n\r\n"); 74 | if(htmlcontent != NULL){ 75 | htmlstart = 1; 76 | htmlcontent += 4; 77 | alllen += buf + tmpres - htmlcontent; 78 | memcpy(response, htmlcontent, alllen); 79 | } 80 | }else{ 81 | memcpy(response+alllen, buf, tmpres); 82 | alllen += tmpres; 83 | } 84 | memset(buf, 0, tmpres); 85 | } 86 | response[alllen] = '\0'; 87 | if(htmlstart){ 88 | free(post); 89 | free(remote); 90 | free(ip); 91 | close(sock); 92 | //fprintf(stdout, "%s", htmlcontent); 93 | return 1; 94 | } 95 | //Error receiving data 96 | return NULL; 97 | } 98 | 99 | char *build_post_query(char *host, char *page, char *data) 100 | { 101 | char *query; 102 | char *postpage = page; 103 | char *tpl = "POST /%s HTTP/1.0\r\nHost: %s\r\nUser-Agent: %s\r\nContent-Length: %d\r\n\r\n%s"; 104 | if(postpage[0] == '/'){ 105 | postpage += 1; 106 | } 107 | query = (char *)malloc(strlen(host)+strlen(postpage)+strlen(USERAGENT)+strlen(data)+strlen(tpl)-9+3); 108 | sprintf(query, tpl, postpage, host, USERAGENT, strlen(data), data); 109 | return query; 110 | } 111 | 112 | /* 113 | int main(int argc, char **argv) 114 | { 115 | char *host = "baidu.com"; 116 | char *page = "/"; 117 | char *c = http_post(host, page, ""); 118 | fprintf(stdout, "%s", c); 119 | return 0; 120 | } 121 | */ 122 | -------------------------------------------------------------------------------- /axel-patch/httppost.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int http_post(char *host, int port, char *page, char *data, char *response); 9 | char *build_post_query(char *host, char *page, char *data); 10 | -------------------------------------------------------------------------------- /axel-patch/text.c: -------------------------------------------------------------------------------- 1 | /********************************************************************\ 2 | * Axel -- A lighter download accelerator for Linux and other Unices. * 3 | * * 4 | * Copyright 2001 Wilmer van der Gaast * 5 | \********************************************************************/ 6 | 7 | /* Text interface */ 8 | 9 | /* 10 | This program is free software; you can redistribute it and/or modify 11 | it under the terms of the GNU General Public License as published by 12 | the Free Software Foundation; either version 2 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License with 21 | the Debian GNU/Linux distribution in file /usr/doc/copyright/GPL; 22 | if not, write to the Free Software Foundation, Inc., 59 Temple Place, 23 | Suite 330, Boston, MA 02111-1307 USA 24 | */ 25 | 26 | #include "axel.h" 27 | 28 | static void stop( int signal ); 29 | static char *size_human( long long int value ); 30 | static char *time_human( int value ); 31 | static void print_commas( long long int bytes_done ); 32 | static void print_alternate_output( axel_t *axel ); 33 | static void print_help(); 34 | static void print_version(); 35 | static void print_messages( axel_t *axel ); 36 | 37 | int run = 1; 38 | 39 | #ifdef NOGETOPTLONG 40 | #define getopt_long( a, b, c, d, e ) getopt( a, b, c ) 41 | #else 42 | static struct option axel_options[] = 43 | { 44 | /* name has_arg flag val */ 45 | { "max-speed", 1, NULL, 's' }, 46 | { "num-connections", 1, NULL, 'n' }, 47 | { "output", 1, NULL, 'o' }, 48 | { "search", 2, NULL, 'S' }, 49 | { "no-proxy", 0, NULL, 'N' }, 50 | { "quiet", 0, NULL, 'q' }, 51 | { "verbose", 0, NULL, 'v' }, 52 | { "help", 0, NULL, 'h' }, 53 | { "version", 0, NULL, 'V' }, 54 | { "alternate", 0, NULL, 'a' }, 55 | { "header", 1, NULL, 'H' }, 56 | { "user-agent", 1, NULL, 'U' }, 57 | { NULL, 0, NULL, 0 } 58 | }; 59 | #endif 60 | 61 | /* For returning string values from functions */ 62 | static char string[MAX_STRING]; 63 | 64 | 65 | int main( int argc, char *argv[] ) 66 | { 67 | char fn[MAX_STRING] = ""; 68 | int do_search = 0; 69 | search_t *search; 70 | conf_t conf[1]; 71 | axel_t *axel; 72 | int i, j, cur_head = 0; 73 | char *s; 74 | 75 | #ifdef I18N 76 | setlocale( LC_ALL, "" ); 77 | bindtextdomain( PACKAGE, LOCALE ); 78 | textdomain( PACKAGE ); 79 | #endif 80 | 81 | if( !conf_init( conf ) ) 82 | { 83 | return( 1 ); 84 | } 85 | 86 | opterr = 0; 87 | 88 | j = -1; 89 | while( 1 ) 90 | { 91 | int option; 92 | 93 | option = getopt_long( argc, argv, "s:n:o:S::NqvhVaH:U:", axel_options, NULL ); 94 | if( option == -1 ) 95 | break; 96 | 97 | switch( option ) 98 | { 99 | case 'U': 100 | strncpy( conf->user_agent, optarg, MAX_STRING); 101 | break; 102 | case 'H': 103 | strncpy( conf->add_header[cur_head++], optarg, MAX_STRING ); 104 | break; 105 | case 's': 106 | if( !sscanf( optarg, "%i", &conf->max_speed ) ) 107 | { 108 | print_help(); 109 | return( 1 ); 110 | } 111 | break; 112 | case 'n': 113 | if( !sscanf( optarg, "%i", &conf->num_connections ) ) 114 | { 115 | print_help(); 116 | return( 1 ); 117 | } 118 | break; 119 | case 'o': 120 | strncpy( fn, optarg, MAX_STRING ); 121 | break; 122 | case 'S': 123 | do_search = 1; 124 | if( optarg != NULL ) 125 | if( !sscanf( optarg, "%i", &conf->search_top ) ) 126 | { 127 | print_help(); 128 | return( 1 ); 129 | } 130 | break; 131 | case 'a': 132 | conf->alternate_output = 1; 133 | break; 134 | case 'N': 135 | *conf->http_proxy = 0; 136 | break; 137 | case 'h': 138 | print_help(); 139 | return( 0 ); 140 | case 'v': 141 | if( j == -1 ) 142 | j = 1; 143 | else 144 | j ++; 145 | break; 146 | case 'V': 147 | print_version(); 148 | return( 0 ); 149 | case 'q': 150 | close( 1 ); 151 | conf->verbose = -1; 152 | if( open( "/dev/null", O_WRONLY ) != 1 ) 153 | { 154 | fprintf( stderr, _("Can't redirect stdout to /dev/null.\n") ); 155 | return( 1 ); 156 | } 157 | break; 158 | default: 159 | print_help(); 160 | return( 1 ); 161 | } 162 | } 163 | conf->add_header_count = cur_head; 164 | if( j > -1 ) 165 | conf->verbose = j; 166 | 167 | if( argc - optind == 0 ) 168 | { 169 | print_help(); 170 | return( 1 ); 171 | } 172 | else if( strcmp( argv[optind], "-" ) == 0 ) 173 | { 174 | s = malloc( MAX_STRING ); 175 | if (scanf( "%1024[^\n]s", s) != 1) { 176 | fprintf( stderr, _("Error when trying to read URL (Too long?).\n") ); 177 | return( 1 ); 178 | } 179 | } 180 | else 181 | { 182 | s = argv[optind]; 183 | if( strlen( s ) > MAX_STRING ) 184 | { 185 | fprintf( stderr, _("Can't handle URLs of length over %d\n" ), MAX_STRING ); 186 | return( 1 ); 187 | } 188 | } 189 | 190 | printf( _("Initializing download: %s\n"), s ); 191 | if( do_search ) 192 | { 193 | search = malloc( sizeof( search_t ) * ( conf->search_amount + 1 ) ); 194 | memset( search, 0, sizeof( search_t ) * ( conf->search_amount + 1 ) ); 195 | search[0].conf = conf; 196 | if( conf->verbose ) 197 | printf( _("Doing search...\n") ); 198 | i = search_makelist( search, s ); 199 | if( i < 0 ) 200 | { 201 | fprintf( stderr, _("File not found\n" ) ); 202 | return( 1 ); 203 | } 204 | if( conf->verbose ) 205 | printf( _("Testing speeds, this can take a while...\n") ); 206 | j = search_getspeeds( search, i ); 207 | search_sortlist( search, i ); 208 | if( conf->verbose ) 209 | { 210 | printf( _("%i usable servers found, will use these URLs:\n"), j ); 211 | j = min( j, conf->search_top ); 212 | printf( "%-60s %15s\n", "URL", "Speed" ); 213 | for( i = 0; i < j; i ++ ) 214 | printf( "%-70.70s %5i\n", search[i].url, search[i].speed ); 215 | printf( "\n" ); 216 | } 217 | axel = axel_new( conf, j, search ); 218 | free( search ); 219 | if( axel->ready == -1 ) 220 | { 221 | print_messages( axel ); 222 | axel_close( axel ); 223 | return( 1 ); 224 | } 225 | } 226 | else if( argc - optind == 1 ) 227 | { 228 | axel = axel_new( conf, 0, s ); 229 | if( axel->ready == -1 ) 230 | { 231 | print_messages( axel ); 232 | axel_close( axel ); 233 | return( 1 ); 234 | } 235 | } 236 | else 237 | { 238 | search = malloc( sizeof( search_t ) * ( argc - optind ) ); 239 | memset( search, 0, sizeof( search_t ) * ( argc - optind ) ); 240 | for( i = 0; i < ( argc - optind ); i ++ ) 241 | strncpy( search[i].url, argv[optind+i], MAX_STRING ); 242 | axel = axel_new( conf, argc - optind, search ); 243 | free( search ); 244 | if( axel->ready == -1 ) 245 | { 246 | print_messages( axel ); 247 | axel_close( axel ); 248 | return( 1 ); 249 | } 250 | } 251 | print_messages( axel ); 252 | if( s != argv[optind] ) 253 | { 254 | free( s ); 255 | } 256 | 257 | if( *fn ) 258 | { 259 | struct stat buf; 260 | 261 | if( stat( fn, &buf ) == 0 ) 262 | { 263 | if( S_ISDIR( buf.st_mode ) ) 264 | { 265 | size_t fnlen = strlen(fn); 266 | size_t axelfnlen = strlen(axel->filename); 267 | 268 | if (fnlen + 1 + axelfnlen + 1 > MAX_STRING) { 269 | fprintf( stderr, _("Filename too long!\n")); 270 | return ( 1 ); 271 | } 272 | 273 | fn[fnlen] = '/'; 274 | memcpy(fn+fnlen+1, axel->filename, axelfnlen); 275 | fn[fnlen + 1 + axelfnlen] = '\0'; 276 | } 277 | } 278 | sprintf( string, "%s.st", fn ); 279 | if( access( fn, F_OK ) == 0 ) if( access( string, F_OK ) != 0 ) 280 | { 281 | fprintf( stderr, _("No state file, cannot resume!\n") ); 282 | return( 1 ); 283 | } 284 | if( access( string, F_OK ) == 0 ) if( access( fn, F_OK ) != 0 ) 285 | { 286 | printf( _("State file found, but no downloaded data. Starting from scratch.\n" ) ); 287 | unlink( string ); 288 | } 289 | strcpy( axel->filename, fn ); 290 | } 291 | else 292 | { 293 | /* Local file existence check */ 294 | i = 0; 295 | s = axel->filename + strlen( axel->filename ); 296 | while( 1 ) 297 | { 298 | sprintf( string, "%s.st", axel->filename ); 299 | if( access( axel->filename, F_OK ) == 0 ) 300 | { 301 | if( axel->conn[0].supported ) 302 | { 303 | if( access( string, F_OK ) == 0 ) 304 | break; 305 | } 306 | } 307 | else 308 | { 309 | if( access( string, F_OK ) ) 310 | break; 311 | } 312 | sprintf( s, ".%i", i ); 313 | i ++; 314 | } 315 | } 316 | 317 | if( !axel_open( axel ) ) 318 | { 319 | print_messages( axel ); 320 | return( 1 ); 321 | } 322 | print_messages( axel ); 323 | axel_start( axel ); 324 | print_messages( axel ); 325 | 326 | if( conf->alternate_output ) 327 | { 328 | putchar('\n'); 329 | } 330 | else 331 | { 332 | if( axel->bytes_done > 0 ) /* Print first dots if resuming */ 333 | { 334 | putchar( '\n' ); 335 | print_commas( axel->bytes_done ); 336 | } 337 | } 338 | axel->start_byte = axel->bytes_done; 339 | 340 | /* Install save_state signal handler for resuming support */ 341 | signal( SIGINT, stop ); 342 | signal( SIGTERM, stop ); 343 | 344 | while( !axel->ready && run ) 345 | { 346 | long long int prev, done; 347 | 348 | prev = axel->bytes_done; 349 | axel_do( axel ); 350 | 351 | if( conf->alternate_output ) 352 | { 353 | if( !axel->message && prev != axel->bytes_done ) 354 | print_alternate_output( axel ); 355 | } 356 | else 357 | { 358 | /* The infamous wget-like 'interface'.. ;) */ 359 | done = ( axel->bytes_done / 1024 ) - ( prev / 1024 ); 360 | if( done && conf->verbose > -1 ) 361 | { 362 | for( i = 0; i < done; i ++ ) 363 | { 364 | i += ( prev / 1024 ); 365 | if( ( i % 50 ) == 0 ) 366 | { 367 | if( prev >= 1024 ) 368 | printf( " [%6.1fKB/s]", (double) axel->bytes_per_second / 1024 ); 369 | if( axel->size < 10240000 ) 370 | printf( "\n[%3lld%%] ", min( 100, 102400 * i / axel->size ) ); 371 | else 372 | printf( "\n[%3lld%%] ", min( 100, i / ( axel->size / 102400 ) ) ); 373 | } 374 | else if( ( i % 10 ) == 0 ) 375 | { 376 | putchar( ' ' ); 377 | } 378 | putchar( '.' ); 379 | i -= ( prev / 1024 ); 380 | } 381 | fflush( stdout ); 382 | } 383 | } 384 | 385 | if( axel->message ) 386 | { 387 | if(conf->alternate_output==1) 388 | { 389 | /* clreol-simulation */ 390 | putchar( '\r' ); 391 | for( i = 0; i < 79; i++ ) /* linewidth known? */ 392 | putchar( ' ' ); 393 | putchar( '\r' ); 394 | } 395 | else 396 | { 397 | putchar( '\n' ); 398 | } 399 | print_messages( axel ); 400 | if( !axel->ready ) 401 | { 402 | if(conf->alternate_output!=1) 403 | print_commas( axel->bytes_done ); 404 | else 405 | print_alternate_output(axel); 406 | } 407 | } 408 | else if( axel->ready ) 409 | { 410 | putchar( '\n' ); 411 | } 412 | } 413 | 414 | strcpy( string + MAX_STRING / 2, 415 | size_human( axel->bytes_done - axel->start_byte ) ); 416 | 417 | printf( _("\nDownloaded %s in %s. (%.2f KB/s)\n"), 418 | string + MAX_STRING / 2, 419 | time_human( gettime() - axel->start_time ), 420 | (double) axel->bytes_per_second / 1024 ); 421 | 422 | i = axel->ready ? 0 : 2; 423 | 424 | axel_close( axel ); 425 | 426 | return( i ); 427 | } 428 | 429 | /* SIGINT/SIGTERM handler */ 430 | void stop( int signal ) 431 | { 432 | run = 0; 433 | } 434 | 435 | /* Convert a number of bytes to a human-readable form */ 436 | char *size_human( long long int value ) 437 | { 438 | if( value == 1 ) 439 | sprintf( string, _("%lld byte"), value ); 440 | else if( value < 1024 ) 441 | sprintf( string, _("%lld bytes"), value ); 442 | else if( value < 10485760 ) 443 | sprintf( string, _("%.1f kilobytes"), (float) value / 1024 ); 444 | else 445 | sprintf( string, _("%.1f megabytes"), (float) value / 1048576 ); 446 | 447 | return( string ); 448 | } 449 | 450 | /* Convert a number of seconds to a human-readable form */ 451 | char *time_human( int value ) 452 | { 453 | if( value == 1 ) 454 | sprintf( string, _("%i second"), value ); 455 | else if( value < 60 ) 456 | sprintf( string, _("%i seconds"), value ); 457 | else if( value < 3600 ) 458 | sprintf( string, _("%i:%02i seconds"), value / 60, value % 60 ); 459 | else 460 | sprintf( string, _("%i:%02i:%02i seconds"), value / 3600, ( value / 60 ) % 60, value % 60 ); 461 | 462 | return( string ); 463 | } 464 | 465 | /* Part of the infamous wget-like interface. Just put it in a function 466 | because I need it quite often.. */ 467 | void print_commas( long long int bytes_done ) 468 | { 469 | int i, j; 470 | 471 | printf( " " ); 472 | j = ( bytes_done / 1024 ) % 50; 473 | if( j == 0 ) j = 50; 474 | for( i = 0; i < j; i ++ ) 475 | { 476 | if( ( i % 10 ) == 0 ) 477 | putchar( ' ' ); 478 | putchar( ',' ); 479 | } 480 | fflush( stdout ); 481 | } 482 | 483 | static void print_alternate_output(axel_t *axel) 484 | { 485 | long long int done=axel->bytes_done; 486 | long long int total=axel->size; 487 | int i; 488 | double now = gettime(); 489 | 490 | putchar(':'); 491 | printf("%lld|%lld|", done, total); 492 | for(i=0;iconf->num_connections;i++) 493 | { 494 | printf("%lld", axel->conn[i].currentbyte); 495 | if (i < axel->conf->num_connections-1) 496 | putchar(','); 497 | } 498 | printf( "|%d|", axel->bytes_per_second ); 499 | if(donefinish_time > now) 502 | printf("%lf|", axel->finish_time - now); 503 | else 504 | printf("0|"); 505 | } 506 | else 507 | printf("0|"); 508 | 509 | struct timeval tim; 510 | gettimeofday(&tim, NULL); 511 | double t1=tim.tv_sec+(tim.tv_usec/1000000.0); 512 | 513 | printf("%f", t1); 514 | putchar('\n'); 515 | 516 | fflush( stdout ); 517 | } 518 | 519 | void print_help() 520 | { 521 | #ifdef NOGETOPTLONG 522 | printf( _("Usage: axel [options] url1 [url2] [url...]\n" 523 | "\n" 524 | "-s x\tSpecify maximum speed (bytes per second)\n" 525 | "-n x\tSpecify maximum number of connections\n" 526 | "-o f\tSpecify local output file\n" 527 | "-S [x]\tSearch for mirrors and download from x servers\n" 528 | "-H x\tAdd header string\n" 529 | "-U x\tSet user agent\n" 530 | "-N\tJust don't use any proxy server\n" 531 | "-q\tLeave stdout alone\n" 532 | "-v\tMore status information\n" 533 | "-a\tAlternate progress indicator\n" 534 | "-h\tThis information\n" 535 | "-V\tVersion information\n" 536 | "\n" 537 | "Visit http://axel.alioth.debian.org/ to report bugs\n") ); 538 | #else 539 | printf( _("Usage: axel [options] url1 [url2] [url...]\n" 540 | "\n" 541 | "--max-speed=x\t\t-s x\tSpecify maximum speed (bytes per second)\n" 542 | "--num-connections=x\t-n x\tSpecify maximum number of connections\n" 543 | "--output=f\t\t-o f\tSpecify local output file\n" 544 | "--search[=x]\t\t-S [x]\tSearch for mirrors and download from x servers\n" 545 | "--header=x\t\t-H x\tAdd header string\n" 546 | "--user-agent=x\t\t-U x\tSet user agent\n" 547 | "--no-proxy\t\t-N\tJust don't use any proxy server\n" 548 | "--quiet\t\t\t-q\tLeave stdout alone\n" 549 | "--verbose\t\t-v\tMore status information\n" 550 | "--alternate\t\t-a\tAlternate progress indicator\n" 551 | "--help\t\t\t-h\tThis information\n" 552 | "--version\t\t-V\tVersion information\n" 553 | "\n" 554 | "Visit http://axel.alioth.debian.org/ to report bugs\n") ); 555 | #endif 556 | } 557 | 558 | void print_version() 559 | { 560 | printf( _("Axel version %s (%s)\n"), AXEL_VERSION_STRING, ARCH ); 561 | printf( "\nCopyright 2001-2002 Wilmer van der Gaast.\n" ); 562 | } 563 | 564 | /* Print any message in the axel structure */ 565 | void print_messages( axel_t *axel ) 566 | { 567 | message_t *m; 568 | 569 | while( axel->message ) 570 | { 571 | printf( "%s\n", axel->message->text ); 572 | m = axel->message; 573 | axel->message = axel->message->next; 574 | free( m ); 575 | } 576 | } 577 | -------------------------------------------------------------------------------- /axel-patch/text.c.patch: -------------------------------------------------------------------------------- 1 | 487c487 2 | < int i,j=0; 3 | --- 4 | > int i; 5 | 490,491c490,491 6 | < printf("\r[%3ld%%] [", min(100,(long)(done*100./total+.5) ) ); 7 | < 8 | --- 9 | > putchar(':'); 10 | > printf("%lld|%lld|", done, total); 11 | 494,509c494,496 12 | < for(;j<((double)axel->conn[i].currentbyte/(total+1)*50)-1;j++) 13 | < putchar('.'); 14 | < 15 | < if(axel->conn[i].currentbyteconn[i].lastbyte) 16 | < { 17 | < if(now <= axel->conn[i].last_transfer + axel->conf->connection_timeout/2 ) 18 | < putchar(i+'0'); 19 | < else 20 | < putchar('#'); 21 | < } else 22 | < putchar('.'); 23 | < 24 | < j++; 25 | < 26 | < for(;j<((double)axel->conn[i].lastbyte/(total+1)*50);j++) 27 | < putchar(' '); 28 | --- 29 | > printf("%lld", axel->conn[i].currentbyte); 30 | > if (i < axel->conf->num_connections-1) 31 | > putchar(','); 32 | 511,518c498 33 | < 34 | < if(axel->bytes_per_second > 1048576) 35 | < printf( "] [%6.1fMB/s]", (double) axel->bytes_per_second / (1024*1024) ); 36 | < else if(axel->bytes_per_second > 1024) 37 | < printf( "] [%6.1fKB/s]", (double) axel->bytes_per_second / 1024 ); 38 | < else 39 | < printf( "] [%6.1fB/s]", (double) axel->bytes_per_second ); 40 | < 41 | --- 42 | > printf( "|%d|", axel->bytes_per_second ); 43 | 521,531c501,504 44 | < int seconds,minutes,hours,days; 45 | < seconds=axel->finish_time - now; 46 | < minutes=seconds/60;seconds-=minutes*60; 47 | < hours=minutes/60;minutes-=hours*60; 48 | < days=hours/24;hours-=days*24; 49 | < if(days) 50 | < printf(" [%2dd%2d]",days,hours); 51 | < else if(hours) 52 | < printf(" [%2dh%02d]",hours,minutes); 53 | < else 54 | < printf(" [%02d:%02d]",minutes,seconds); 55 | --- 56 | > if (axel->finish_time > now) 57 | > printf("%lf|", axel->finish_time - now); 58 | > else 59 | > printf("0|"); 60 | 532a506,514 61 | > else 62 | > printf("0|"); 63 | > 64 | > struct timeval tim; 65 | > gettimeofday(&tim, NULL); 66 | > double t1=tim.tv_sec+(tim.tv_usec/1000000.0); 67 | > 68 | > printf("%f", t1); 69 | > putchar('\n'); 70 | -------------------------------------------------------------------------------- /axeldown.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import web, logging as log, pprint, json, db 5 | from api import * 6 | import traceback 7 | 8 | f=pprint.pformat 9 | 10 | files = {} 11 | 12 | class Router(object): 13 | def POST(self, uri): 14 | if uri == 'api': 15 | try: 16 | print web.data() 17 | json_data = json.loads(web.data()) 18 | except Exception, e: 19 | traceback.print_exc() 20 | raise web.badrequest() 21 | 22 | try: 23 | return json.dumps(API().serve(json_data)) 24 | except Exception, e: 25 | traceback.print_exc() 26 | raise web.internalerror(e.message) 27 | else: 28 | raise web.notfound() 29 | 30 | def GET(self, uri): 31 | dirs = uri.split('/') 32 | if dirs[0] in ['', 'js', 'css', 'img', 'donate', 'index2.html', 'favicon.ico']: 33 | filename = uri 34 | if dirs[0] == '': 35 | filename = 'index.html' 36 | try: 37 | with open(filename) as staticfile: 38 | filecontent = staticfile.read() 39 | files[filename] = filecontent 40 | return filecontent 41 | except IOError, e: 42 | if e.errno == 2: 43 | raise web.notfound() 44 | if files.has_key(filename): 45 | return files[filename] 46 | else: 47 | try: 48 | with open(filename) as staticfile: 49 | filecontent = staticfile.read() 50 | files[filename] = filecontent 51 | return filecontent 52 | except IOError, e: 53 | if e.errno == 2: 54 | raise web.notfound() 55 | else: 56 | raise web.notfound() 57 | 58 | urls = ( 59 | "/(.*)", Router 60 | ) 61 | 62 | app = web.application(urls, globals()) 63 | 64 | #log.basicConfig(level=log.DEBUG, filename='webm.log') 65 | #log.basicConfig(level=log.DEBUG) 66 | if __name__ == "__main__": 67 | try: 68 | db.select_tasks(id=1) 69 | except: 70 | db.reset_database() 71 | API().download_last() 72 | try: 73 | app.run() 74 | except: 75 | print "timeout" 76 | pass 77 | 78 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo '创建临时目录...' 3 | rm -rf build 4 | mkdir -p build 5 | cd build 6 | echo '解压axel源码...' 7 | tar -xzf ../axel-2.5.tar.gz 8 | axel=axel-2.5 9 | patch=../axel-patch 10 | echo '开始修补源码..' 11 | diff $axel/text.c $patch/text.c > $patch/text.c.patch 12 | diff $axel/conf.c $patch/conf.c > $patch/conf.c.patch 13 | patch $axel/text.c $patch/text.c.patch 14 | patch $axel/conf.c $patch/conf.c.patch 15 | cd $axel/ 16 | export CFLAGS='-w' 17 | echo '编译'$axel'源码...' 18 | ./configure --debug=0 --i18n=0 19 | make 20 | echo '清理临时目录...' 21 | cp axel ../../ 22 | cd ../../ 23 | rm -rf ./screenshot 24 | rm -rf ./.MD 25 | rm -rf ./build 26 | rm -rf ./axel-patch 27 | rm ./axel-2.5.tar.gz 28 | rm $0 29 | -------------------------------------------------------------------------------- /conf: -------------------------------------------------------------------------------- 1 | {"task_queue_size": "10", "default_thread_size": "5", "downloads": "~/", "user_agent": "", "total_maxspeed": "", "buffer_size": "5120", "force_download": false} -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | class Config(dict): 2 | def __getattr__(self, name): 3 | return self[name] if self.has_key(name) else None 4 | def __setattr__(self, name, value): 5 | self[name] = value -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | *{margin:0;padding:0;font-family: Arial;} 2 | body{padding:0;font-size:12px;} 3 | 4 | .clear{clear:both;} 5 | .hidden{display:none;} 6 | 7 | table#list{border-collapse:collapse;} 8 | table#list td{padding:5px 5px 5px 10px;} 9 | table#list th{color:#333;font-size:14px;padding:5px;text-shadow: 1px 1px 0 #AAA;} 10 | table#list thead{cursor:pointer;background-color:#CCCCCC;} 11 | table#list tbody tr.even{background-color:#DDDDDD;} 12 | table#list tbody tr.odd{background-color:#F3F3F3;} 13 | table#list tbody tr.url{background-color:#FFFFFF;display:none;} 14 | table#list tbody tr.completed td.filename{font-weight:bold;} 15 | table#list tbody tr.downloading{color:green;} 16 | table#list tbody tr.paused{color:gray;} 17 | table#list tbody tr.error{color:red;} 18 | table#list tbody tr td.center{text-align:center;} 19 | table#list tbody tr td.filename{cursor:pointer;} 20 | table#list tbody tr td.state.E{cursor:pointer;} 21 | 22 | #toolbar{padding-left:5px;} 23 | #toolbar button{float:left;margin-right:5px;} 24 | #msg{color:red;font-size:14px;padding:0 10px 0 30px;border-radius:3px;text-align:center;display:none;float:left;position:relative;left:10px;top:3px;height:27px;line-height:27px;} 25 | button{ 26 | background: -moz-linear-gradient(center top , #EDEDED 5%, #DFDFDF 100%) repeat scroll 0 0 #EDEDED; 27 | border: 1px solid #DCDCDC; 28 | border-radius: 5px; 29 | box-shadow: 0 1px 0 0 #FFFFFF inset; 30 | color: #555; 31 | display: inline-block; 32 | font-size: 15px; 33 | font-weight: bold; 34 | padding: 3px 15px; 35 | text-decoration: none; 36 | text-shadow: 1px 1px 0 #FFFFFF; 37 | margin:3px 0; 38 | cursor:pointer; 39 | } 40 | 41 | #create{z-index:1000;position:absolute;border:7px solid gray;border-radius:10px;display:none;background:white;} 42 | #create .actions{float:right;padding-right:5px;padding-bottom:3px;} 43 | #create button{margin-left:5px;} 44 | 45 | #cconfig{z-index:1000;position:absolute;border:7px solid gray;border-radius:10px;display:none;background:white;} 46 | #cconfig .actions{float:right;padding-right:5px;padding-bottom:3px;} 47 | #cconfig button{margin-left:5px;} 48 | 49 | #donate{z-index:1000;position:absolute;border:7px solid gray;border-radius:10px;display:none;background:white;} 50 | #donate .actions{float:right;padding-right:5px;padding-bottom:3px;} 51 | #donate button{margin-left:5px;} 52 | 53 | #background{left:0;top:0;position:absolute;display:none;} 54 | #list thead td{font-weight:bold;} 55 | 56 | input[type="text"]{height:20px;line-height:20px;} 57 | 58 | div.progress{background:#99CCCC;height:25px;line-height:25px;} 59 | span.filename{padding:0 10px;float:left;} 60 | #states{float:right;margin-top:14px;margin-right:5px;} 61 | #states span{cursor:pointer;padding:5px 10px 5px 10px;background:#E5E5E5;border-radius:3px 3px 0 0;font-weight:bold;} 62 | #states span.current{background:#CCC;} 63 | -------------------------------------------------------------------------------- /db.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sqlite3, datetime, logging as log 4 | 5 | DB_FILE='Tasks.db' 6 | SQL_CREATE="""create table tasks( 7 | id integer primary key autoincrement, 8 | url text not null, 9 | output text not null, 10 | `order` integer, 11 | total long, 12 | done long, 13 | state long, 14 | current integer, 15 | average integer, 16 | left integer, 17 | used integer, 18 | thsize integer, 19 | thdone text, 20 | maxspeed integer, 21 | headers text, 22 | speed integer, 23 | update_time timestamp default current_timestamp, 24 | errmsg text, 25 | downloads text, 26 | ua text) 27 | """ 28 | 29 | def reset_database(): 30 | dbc = sqlite3.connect(DB_FILE) 31 | cursor = dbc.cursor() 32 | cursor.execute("drop table if exists tasks") 33 | cursor.execute(SQL_CREATE) 34 | dbc.commit() 35 | dbc.close() 36 | 37 | def select_tasks(**kwargs): 38 | dbc = sqlite3.connect(DB_FILE) 39 | cursor = dbc.cursor() 40 | SQL = "select * from tasks" 41 | 42 | 43 | if kwargs: 44 | SQL += " where " 45 | for key, value in kwargs.items(): 46 | if type(value) is int: 47 | SQL += " `%s` = %s " % (key, value) 48 | else: 49 | SQL += " `%s` %s " % (key, value) 50 | 51 | SQL += " order by `order`, `id`" 52 | log.debug(SQL) 53 | cursor.execute(SQL) 54 | dbc.commit() 55 | tasks = [] 56 | for row in cursor: 57 | tasks.append({"id": row[0], 58 | "url": row[1], 59 | "output": row[2], 60 | "order": row[3], 61 | "total": row[4], 62 | "done": row[5], 63 | "state": row[6], 64 | "current": row[7], 65 | "average": row[8], 66 | "left": row[9], 67 | "used": row[10], 68 | "thsize": row[11], 69 | "thdone": row[12], 70 | "maxspeed": row[13], 71 | "headers": row[14], 72 | "speed": row[15], 73 | "update_time": row[16], 74 | "errmsg": row[17], 75 | "downloads": row[18], 76 | "ua": row[19]}) 77 | dbc.close() 78 | return tasks 79 | 80 | def insert_task(**kwargs): 81 | if not kwargs: 82 | return 83 | 84 | dbc = sqlite3.connect(DB_FILE) 85 | cursor = dbc.cursor() 86 | 87 | columns = list() 88 | values = list() 89 | for key, value in kwargs.items(): 90 | columns.append("`%s`" % key) 91 | values.append("%s" % value if type(value) is int else "'%s'" % value) 92 | columns.append("`update_time`") 93 | values.append("'%s'" % datetime.datetime.now()) 94 | columns = ", ".join(columns) 95 | values = ", ".join(values) 96 | SQL = "insert into tasks (%s) values (%s)" % (columns, values) 97 | log.debug(SQL) 98 | cursor.execute(SQL) 99 | dbc.commit() 100 | tid = cursor.lastrowid 101 | dbc.close() 102 | return tid 103 | 104 | def delete_tasks(ids): 105 | if not ids: 106 | return 107 | 108 | dbc = sqlite3.connect(DB_FILE) 109 | cursor = dbc.cursor() 110 | 111 | SQL = "delete from tasks where " 112 | SQL += " id = %s " % ids if type(ids) is int else \ 113 | " id in (%s) " % ", ".join([str(did) for did in ids]) 114 | 115 | log.debug(SQL) 116 | cursor.execute(SQL) 117 | dbc.commit() 118 | dbc.close() 119 | 120 | def update_tasks(dids, **kwargs): 121 | if not dids or not kwargs: 122 | return 123 | 124 | dbc = sqlite3.connect(DB_FILE) 125 | cursor = dbc.cursor() 126 | 127 | SQL = "update tasks set " 128 | 129 | sets = [] 130 | for key, value in kwargs.items(): 131 | sets.append(" `%s` = %s " % (key, value) if type(value) is int else \ 132 | " `%s` = '%s' " % (key, value)) 133 | sets.append(" `update_time` = '%s' " % datetime.datetime.now()) 134 | SQL += ",".join(sets) 135 | 136 | SQL += " where id = %s " % dids if type(dids) is int else \ 137 | " where id in (%s) " % ", ".join([str(did) for did in dids]) 138 | 139 | log.debug(SQL) 140 | cursor.execute(SQL) 141 | dbc.commit() 142 | dbc.close() 143 | 144 | def test_select(): 145 | return select_tasks(**{'output':"like \'%CentOS%\'"}) 146 | 147 | def test_create(): 148 | return insert_task(**{'url': 'http://mirrors.163.com/centos/6.2/isos/x86_64/CentOS-6.2-x86_64-LiveCD.iso', 'output':'CentOS-6.2-x86_64-LiveCD.iso'}) 149 | 150 | def test_update(): 151 | return update_task([1,2], **{'state':3}) 152 | 153 | def test_delete(): 154 | return delete_task([1, 2]) 155 | 156 | def test(): 157 | #print 'test create new task' 158 | #print test_create() 159 | 160 | #print 'test update tasks' 161 | #print test_update() 162 | 163 | print 'test delete task' 164 | print test_delete() 165 | 166 | print 'test select tasks:' 167 | print test_select() 168 | 169 | if __name__ == '__main__': 170 | reset_database() 171 | -------------------------------------------------------------------------------- /donate/alipay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihaoyun6/axeldown-core/a0dec7dfbbc23f5e0bc9d10bf94c0d651dabc934/donate/alipay.png -------------------------------------------------------------------------------- /donate/wechatpay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihaoyun6/axeldown-core/a0dec7dfbbc23f5e0bc9d10bf94c0d651dabc934/donate/wechatpay.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Axeldown 10 | 11 | 12 |
13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |
23 |
24 | 所有任务 25 | 下载中 26 | 已完成 27 | 暂停中 28 | 等待中 29 | 发生错误 30 |
31 |
32 |
33 |
34 |
35 |
36 | 37 | 38 | 39 | 42 | 45 | 48 | 51 | 54 | 57 | 58 | 59 | 60 | 61 |
40 | 选中 41 | 43 | 状态 44 | 46 | 文件名 47 | 49 | 进度 50 | 52 | 速度 53 | 55 | 剩余时间 56 |
62 |
63 |
64 | 65 | 66 | 69 | 70 | 71 | 74 | 75 | 76 | 79 | 80 | 81 | 84 | 85 | 86 | 89 | 90 | 91 | 94 | 95 | 96 | 99 | 100 | 101 | 104 | 105 | 108 | 109 | 110 | 113 | 114 | 115 | 118 | 119 | 120 | 123 | 124 |
67 | 下载地址 68 |
72 | 73 |
77 | *线程数 78 |
82 | 83 |
87 | *文件名 88 |
92 | 93 |
97 | *下载目录 98 |
102 | 103 |
106 | *User Agent 107 |
111 | 112 |
116 | *请求头 117 |
121 | 122 |
125 | 126 | 127 | 130 | 133 | 134 |
128 | 建立任务后立即下载 129 | 131 | 132 |
135 |
136 | 137 | 138 |
139 |
140 | 153 |
154 | 155 | 156 | 159 | 160 | 161 | 164 | 165 | 166 | 169 | 170 | 171 | 174 | 175 | 176 | 179 | 180 | 181 | 184 | 185 | 186 | 189 | 190 | 191 | 194 | 195 | 198 | 199 | 200 | 203 | 204 | 214 |
157 | User-Agent 158 |
162 | 163 |
167 | 最大下载速度 168 |
172 | 173 |
177 | 默认下载位置 178 |
182 | 183 |
187 | 默认下载线程数 188 |
192 | 193 |
196 | 最大同时任务数 197 |
201 | 202 |
215 | 216 | 217 | 220 | 223 | 224 |
218 | 强制覆盖已存在文件 219 | 221 | 222 |
225 |
226 | 227 |
228 |
229 |
230 |
231 | 232 | 233 | -------------------------------------------------------------------------------- /js/jquery-1.7.1.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery v1.7.1 jquery.com | jquery.org/license */ 2 | (function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!ck[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cl||(cl=c.createElement("iframe"),cl.frameBorder=cl.width=cl.height=0),b.appendChild(cl);if(!cm||!cl.createElement)cm=(cl.contentWindow||cl.contentDocument).document,cm.write((c.compatMode==="CSS1Compat"?"":"")+""),cm.close();d=cm.createElement(a),cm.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cl)}ck[a]=e}return ck[a]}function cu(a,b){var c={};f.each(cq.concat.apply([],cq.slice(0,b)),function(){c[this]=a});return c}function ct(){cr=b}function cs(){setTimeout(ct,0);return cr=f.now()}function cj(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ci(){try{return new a.XMLHttpRequest}catch(b){}}function cc(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;g=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.1",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
a",d=q.getElementsByTagName("*"),e=q.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=q.getElementsByTagName("input")[0],b={leadingWhitespace:q.firstChild.nodeType===3,tbody:!q.getElementsByTagName("tbody").length,htmlSerialize:!!q.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:q.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete q.test}catch(s){b.deleteExpando=!1}!q.addEventListener&&q.attachEvent&&q.fireEvent&&(q.attachEvent("onclick",function(){b.noCloneEvent=!1}),q.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),q.appendChild(i),k=c.createDocumentFragment(),k.appendChild(q.lastChild),b.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,k.removeChild(i),k.appendChild(q),q.innerHTML="",a.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",q.style.width="2px",q.appendChild(j),b.reliableMarginRight=(parseInt((a.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(q.attachEvent)for(o in{submit:1,change:1,focusin:1})n="on"+o,p=n in q,p||(q.setAttribute(n,"return;"),p=typeof q[n]=="function"),b[o+"Bubbles"]=p;k.removeChild(q),k=g=h=j=q=i=null,f(function(){var a,d,e,g,h,i,j,k,m,n,o,r=c.getElementsByTagName("body")[0];!r||(j=1,k="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",m="visibility:hidden;border:0;",n="style='"+k+"border:5px solid #000;padding:0;'",o="
"+""+"
",a=c.createElement("div"),a.style.cssText=m+"width:0;height:0;position:static;top:0;margin-top:"+j+"px",r.insertBefore(a,r.firstChild),q=c.createElement("div"),a.appendChild(q),q.innerHTML="
t
",l=q.getElementsByTagName("td"),p=l[0].offsetHeight===0,l[0].style.display="",l[1].style.display="none",b.reliableHiddenOffsets=p&&l[0].offsetHeight===0,q.innerHTML="",q.style.width=q.style.paddingLeft="1px",f.boxModel=b.boxModel=q.offsetWidth===2,typeof q.style.zoom!="undefined"&&(q.style.display="inline",q.style.zoom=1,b.inlineBlockNeedsLayout=q.offsetWidth===2,q.style.display="",q.innerHTML="
",b.shrinkWrapBlocks=q.offsetWidth!==2),q.style.cssText=k+m,q.innerHTML=o,d=q.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,i={doesNotAddBorder:e.offsetTop!==5,doesAddBorderForTableAndCells:h.offsetTop===5},e.style.position="fixed",e.style.top="20px",i.fixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",i.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,i.doesNotIncludeMarginInBodyOffset=r.offsetTop!==j,r.removeChild(a),q=a=null,f.extend(b,i))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;h=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/\bhover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")}; 3 | f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&i.push({elem:this,matches:d.slice(e)});for(j=0;j0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
","
"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function() 4 | {for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||!bc.test("<"+a.nodeName)?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");b===c?bh.appendChild(o):U(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return br.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bq,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bq.test(g)?g.replace(bq,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,b){var c,d,e;b=b.replace(bs,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b)));return c}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bt.test(f)&&bu.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bD=/%20/g,bE=/\[\]$/,bF=/\r?\n/g,bG=/#.*$/,bH=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bI=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bJ=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bK=/^(?:GET|HEAD)$/,bL=/^\/\//,bM=/\?/,bN=/)<[^<]*)*<\/script>/gi,bO=/^(?:select|textarea)/i,bP=/\s+/,bQ=/([?&])_=[^&]*/,bR=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bS=f.fn.load,bT={},bU={},bV,bW,bX=["*/"]+["*"];try{bV=e.href}catch(bY){bV=c.createElement("a"),bV.href="",bV=bV.href}bW=bR.exec(bV.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bS)return bS.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bN,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bO.test(this.nodeName)||bI.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bF,"\r\n")}}):{name:b.name,value:c.replace(bF,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b_(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b_(a,b);return a},ajaxSettings:{url:bV,isLocal:bJ.test(bW[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bX},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bZ(bT),ajaxTransport:bZ(bU),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cb(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cc(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bH.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bG,"").replace(bL,bW[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bP),d.crossDomain==null&&(r=bR.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bW[1]&&r[2]==bW[2]&&(r[3]||(r[1]==="http:"?80:443))==(bW[3]||(bW[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bT,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bK.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bM.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bQ,"$1_="+x);d.url=y+(y===d.url?(bM.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bX+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bU,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)ca(g,a[g],c,e);return d.join("&").replace(bD,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cd=f.now(),ce=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cd++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ce.test(b.url)||e&&ce.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ce,l),b.url===j&&(e&&(k=k.replace(ce,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cf=a.ActiveXObject?function(){for(var a in ch)ch[a](0,1)}:!1,cg=0,ch;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ci()||cj()}:ci,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cf&&delete ch[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cg,cf&&(ch||(ch={},f(a).unload(cf)),ch[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var ck={},cl,cm,cn=/^(?:toggle|show|hide)$/,co=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cp,cq=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cr;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | String.prototype.fs=function() { 2 | segments=this.split('%s'); 3 | ret=''; 4 | for (var i in arguments) { 5 | ret += segments[i] + arguments[i]; 6 | } 7 | return ret + segments[segments.length-1]; 8 | } 9 | String.prototype.trim=function() { 10 | return this.replace(/^\s+|\s+$/g,""); 11 | } 12 | String.prototype.ltrim=function() { 13 | return this.replace(/^\s+/,""); 14 | } 15 | String.prototype.rtrim=function() { 16 | return this.replace(/\s+$/,""); 17 | }; 18 | String.prototype.empty=function() { 19 | return this == ''; 20 | }; 21 | 22 | Number.prototype.human=function() { 23 | if (this < 1024) 24 | return parseInt(this) + 'B'; 25 | else if (this < 1024 * 1024) 26 | return parseInt(this/1024*10)/10 + 'KB'; 27 | else if (this < 1024 * 1024 * 1024) 28 | return parseInt(this/1024/1024*10)/10 + 'MB'; 29 | else if (this < 1024 * 1024 * 1024 * 1024) 30 | return parseInt(this/1024/1024/1024*10)/10 + 'GB'; 31 | else 32 | return parseInt(this/1024/1024/1024/1024*10)/10 + 'TB'; 33 | } 34 | Number.prototype.time=function() { 35 | if (this < 60) 36 | return parseInt(this) + ' S'; 37 | else if (this < 3600) 38 | return parseInt(this/60*10)/10 + ' M'; 39 | else if (this < 3600*24) 40 | return parseInt(this/3600*10)/10 + ' H'; 41 | else 42 | return parseInt(this/3600/24*10)/10 + ' D'; 43 | } 44 | 45 | 46 | $(function() { 47 | var pdata = {current:'A', states:['A', 'D', 'C', 'W', 'P', 'E']}; 48 | $.extend({'alert': function(msg){ 49 | $('#msg').text(msg).show(); 50 | }, 51 | 'unalert': function(){ 52 | $('#msg').hide(); 53 | }}); 54 | function api(action, data, callback) { 55 | if (data == undefined) 56 | data = new Object; 57 | data['action'] = action; 58 | $.ajax({ 59 | url:'/api', 60 | type:'POST', 61 | data:JSON.stringify(data), 62 | dataType:'json', 63 | success:function(ret) { 64 | callback(ret); 65 | }, 66 | error:function(ret){ 67 | console.error(ret); 68 | $.alert("内部错误,请刷新页面, 若无效需重启Axeldown"); 69 | }, 70 | timeout:function(ret){ 71 | console.error("request timeout"); 72 | $.alert("请求超时,请重启Axeldown"); 73 | } 74 | }); 75 | } 76 | 77 | function init() { 78 | $('#toolbar .create').click(open_create_dialog); 79 | $('#toolbar .cconfig').click(open_cconfig_dialog); 80 | $('#toolbar .donate').click(open_donate_dialog); 81 | $('#create button.create').click(function(){ 82 | var urls = $.trim($('#create textarea[name="url"]').val()); 83 | if (!urls) { 84 | $.alert('请输入下载地址!'); 85 | setTimeout(function(){ 86 | $.unalert(); 87 | }, 5000); 88 | close_create_dialog(); 89 | return; 90 | } 91 | var headers = $('#create textarea[name="headers"]').val(); 92 | var output = $('#create input[name="filename"]').val(); 93 | var thsize = $('#create input[name="thsize"]').val(); 94 | var downloads = $('#create input[name="downloads"]').val(); 95 | var ua = $('#create input[name="ua"]').val(); 96 | var immediately = $('#create input[name="immediately"]').attr('checked') == 'checked'; 97 | urls = urls.split('\n'); 98 | if (urls.length > 1) 99 | output = ""; 100 | for (var i in urls) { 101 | var url = urls[i]; 102 | var parts = url.split('\t'); 103 | console.log(parts); 104 | if (parts.length > 1) { 105 | url = parts[0]; 106 | output = parts[1]; 107 | } 108 | var options = {options:{url:url, headers:headers, output:output, downloads:downloads, immediately:immediately, thsize:thsize, ua:ua}}; 109 | api('create', options, function() { 110 | setTimeout(refresh, 1000); 111 | }); 112 | } 113 | close_create_dialog(); 114 | refresh(); 115 | }); 116 | 117 | $('#cconfig button.save').click(function(){ 118 | var cpath = $('#cconfig input[name="cpath"]').val(); 119 | if (!cpath) { 120 | $.alert('请输入默认下载位置!'); 121 | setTimeout(function(){ 122 | $.unalert(); 123 | }, 5000); 124 | close_cconfig_dialog(); 125 | return; 126 | } 127 | var cua = $('#cconfig textarea[name="cua"]').val(); 128 | var cmaxspeed = $('#cconfig input[name="cmaxspeed"]').val(); 129 | var cthread = $('#cconfig input[name="cthread"]').val(); 130 | var cqueue = $('#cconfig input[name="cqueue"]').val(); 131 | if (!cqueue) { 132 | var cqueue = "10"; 133 | } 134 | var cbuffer = $('#cconfig input[name="cbuffer"]').val(); 135 | if (!cbuffer) { 136 | var cbuffer = "5120"; 137 | } 138 | var cforce = $('#cconfig input[name="cforce"]').attr('checked') == 'checked'; 139 | var options = {options:{force_download:cforce, total_maxspeed:cmaxspeed, downloads:cpath, default_thread_size:cthread, buffer_size:cbuffer, task_queue_size:cqueue, user_agent:cua}}; 140 | api('cconfig', options, function() { 141 | setTimeout(refresh, 1000); 142 | }); 143 | close_cconfig_dialog(); 144 | refresh(); 145 | }); 146 | 147 | $('#toolbar button.pause').click(function(){ 148 | var ids = []; 149 | $('#list tr:visible input[type="checkbox"]:checked').each(function(i){ 150 | if ($(this).parent().parent().hasClass('D')) 151 | ids.push($(this).val()); 152 | }); 153 | if (ids.length) 154 | pause(ids); 155 | }); 156 | $('#toolbar button.remove').click(function(){ 157 | var ids = []; 158 | $('#list tr:visible input[type="checkbox"]:checked').each(function(i){ 159 | ids.push($(this).val()); 160 | }); 161 | if (ids.length) 162 | remove(ids); 163 | }); 164 | $('#toolbar button.resume').click(function(){ 165 | var ids = []; 166 | $('#list tr:visible input[type="checkbox"]:checked').each(function(i){ 167 | ids.push($(this).val()); 168 | }); 169 | if (ids.length) 170 | resume(ids); 171 | }); 172 | $('#toolbar button.download').click(function(){ 173 | var ids = []; 174 | $('#list tr:visible input[type="checkbox"]:checked').each(function(i){ 175 | ids.push($(this).val()); 176 | }); 177 | if (ids.length) 178 | download(ids); 179 | }); 180 | $('#create button.cancel').click(close_create_dialog); 181 | $('#cconfig button.cancel').click(close_cconfig_dialog); 182 | $('#donate button.cancel').click(close_donate_dialog); 183 | $('#list td.filename').live('click', function(){ 184 | if($(this).parent().next().css('display') != 'none') { 185 | $(this).parent().next().hide('fast'); 186 | }else{ 187 | $(this).parent().next().show('fast'); 188 | } 189 | }); 190 | $('#list thead td').click(function(){ 191 | // 192 | }); 193 | $('#list th.select').click(function(){ 194 | if($(this).find('select').length) 195 | return; 196 | var selector = $(''); 205 | $('#list td input[type="checkbox"]').attr('checked', 'checked'); 206 | var td = $(this); 207 | selector.change(function(){ 208 | var selected = $(this).val(); 209 | if (selected == 'A') 210 | $('#list td input[type="checkbox"]').attr('checked', 'checked'); 211 | else if (selected == 'N') 212 | $('#list td input[type="checkbox"]').removeAttr('checked'); 213 | else{ 214 | $('#list td input[type="checkbox"]').removeAttr('checked'); 215 | $('#list tr.%s'.fs(selected)).find('td input[type="checkbox"]').attr('checked', 'checked'); 216 | } 217 | td.html('选中'); 218 | }); 219 | $(this).html('').append(selector); 220 | return true; 221 | }); 222 | $('#list td.select').live('click', function() { 223 | var selector = $(this).find('input'); 224 | if (selector.attr('checked')) 225 | selector.removeAttr('checked'); 226 | else 227 | selector.attr('checked', 'checked'); 228 | }); 229 | $('#list td.select input').live('click', function(event) { 230 | event.stopPropagation() 231 | }); 232 | $(window).resize(function(){ 233 | reset_progress_length(); 234 | }); 235 | $('#states span').click(function(){ 236 | $('#states span').removeClass('current'); 237 | var cssClass = $(this).attr('class'); 238 | $(this).addClass('current'); 239 | pdata.current = cssClass; 240 | $('#list tbody tr').each(function(){ 241 | var cc = $(this).attr('class'); 242 | for (var i in pdata.states) { 243 | var s = pdata.states[i]; 244 | if ($.inArray(s, cc.split(' '))>=0) { 245 | if (cssClass == 'A' || s == cssClass) { 246 | $(this).show(); 247 | } else { 248 | $(this).hide(); 249 | } 250 | break; 251 | } 252 | } 253 | }); 254 | }); 255 | 256 | refresh(); 257 | setInterval(refresh, 1000); 258 | } 259 | 260 | function refresh() { 261 | api('tasks', null, function(return_content){ 262 | states = {1:'W', 2:'D', 3:'P', 4:'C', 5:'E'} 263 | var tasks = return_content['result']; 264 | var trs = ""; 265 | $.each(tasks, function(i) { 266 | var task = tasks[i]; 267 | var errmsg_title = states[task.state] == 'E' ? 'title="%s"'.fs(task.errmsg) : ''; 268 | if (states[task.state] == 'C') 269 | var tr = '
'.fs(task.id, errmsg_title, i % 2 ? 'odd' : 'even', pdata.current!='C' && pdata.current!='A' ? 'hidden' : ''); 270 | else if (states[task.state] == 'D') 271 | var tr = ''.fs(task.id, errmsg_title, i % 2 ? 'odd' : 'even', pdata.current!='D' && pdata.current!='A' ? 'hidden' : ''); 272 | else if (states[task.state] == 'P') 273 | var tr = ''.fs(task.id, errmsg_title, i % 2 ? 'odd' : 'even', pdata.current!='P' && pdata.current!='A' ? 'hidden' : ''); 274 | else if (states[task.state] == 'W') 275 | var tr = ''.fs(task.id, errmsg_title, i % 2 ? 'odd' : 'even', pdata.current!='W' && pdata.current!='A' ? 'hidden' : ''); 276 | else 277 | var tr = ''.fs(task.id, errmsg_title, i % 2 ? 'odd' : 'even', pdata.current!='E' && pdata.current!='A' ? 'hidden' : ''); 278 | if($('#list input[value="%s"]:checked'.fs(task.id)).length) { 279 | tr += ''.fs(task.id); 280 | } else { 281 | tr += ''.fs(task.id); 282 | } 283 | //tr += ''.fs(task.id); 284 | tr += ''.fs(states[task.state], states[task.state]); 285 | tr += ''.fs(task.downloads ? task.downloads : task.downloads, task.output); 286 | var total = task.total ? task.total.human() : ''; 287 | if (states[task.state] == 'C') 288 | var done = total; 289 | else 290 | var done = task.done ? task.done.human() : '' 291 | if (!done && !total) 292 | tr += ''; 293 | else 294 | tr += ''.fs(states[task.state] == 'C' ? 1 : task.done/task.total, done, total); 295 | if (states[task.state] != 'D') 296 | speed = ''; 297 | else if (task.speed == 0) 298 | speed = 0; 299 | else 300 | speed = task.speed ? task.speed.human() + '/s' : ''; 301 | tr += ''.fs(speed); 302 | if (states[task.state] != 'D') 303 | left = ''; 304 | else 305 | left = task.left !== null ? task.left.time() : ''; 306 | tr += ''.fs(left); 307 | tr += ''; 308 | 309 | if ($('#list tr#url-%s'.fs(task.id)).length && $('#list tr#url-%s'.fs(task.id)).css('display') != 'none') 310 | tr += ''; 311 | else 312 | tr += ''; 313 | trs += tr; 314 | }); 315 | $('#wrap table tbody').html(trs); 316 | reset_progress_length(); 317 | }); 318 | } 319 | 320 | function reset_progress_length(){ 321 | $('#list .progress').each(function(i){ 322 | var percent = $(this).attr('percent'); 323 | var width = $(this).parent().width(); 324 | $(this).width(width * percent); 325 | $(this).find('span.filename').width(width); 326 | }); 327 | } 328 | 329 | function open_create_dialog() { 330 | var $d = $(document); 331 | var ww = $d.width(); 332 | var wh = $d.height(); 333 | 334 | var $c = $('#create'); 335 | var cw = $c.width(); 336 | var ch = $c.height() 337 | 338 | var left = (ww - cw) / 2; 339 | var top = (wh - ch) / 2; 340 | if (left < 0) 341 | left = 0; 342 | if (top < 0) 343 | top = 0; 344 | $c.css({left:left,top:top,display:'block'}); 345 | 346 | $('#background').css({display:'block',width:ww,height:wh}); 347 | rconfig(); 348 | } 349 | 350 | function open_cconfig_dialog() { 351 | var $d = $(document); 352 | var ww = $d.width(); 353 | var wh = $d.height(); 354 | 355 | var $c = $('#cconfig'); 356 | var cw = $c.width(); 357 | var ch = $c.height() 358 | 359 | var left = (ww - cw) / 2; 360 | var top = (wh - ch) / 2; 361 | if (left < 0) 362 | left = 0; 363 | if (top < 0) 364 | top = 0; 365 | $c.css({left:left,top:top,display:'block'}); 366 | 367 | $('#background').css({display:'block',width:ww,height:wh}); 368 | rconfig(); 369 | } 370 | 371 | function open_donate_dialog() { 372 | var $d = $(document); 373 | var ww = $d.width(); 374 | var wh = $d.height(); 375 | 376 | var $c = $('#donate'); 377 | var cw = $c.width(); 378 | var ch = $c.height() 379 | 380 | var left = (ww - cw) / 2; 381 | var top = (wh - ch) / 2; 382 | if (left < 0) 383 | left = 0; 384 | if (top < 0) 385 | top = 0; 386 | $c.css({left:left,top:top,display:'block'}); 387 | 388 | $('#background').css({display:'block',width:ww,height:wh}); 389 | rconfig(); 390 | } 391 | 392 | function close_create_dialog() { 393 | $('#create').hide(); 394 | $('#background').hide(); 395 | } 396 | 397 | function close_cconfig_dialog() { 398 | $('#cconfig').hide(); 399 | $('#background').hide(); 400 | } 401 | 402 | function close_donate_dialog() { 403 | $('#donate').hide(); 404 | $('#background').hide(); 405 | } 406 | 407 | function pause(ids) { 408 | api('pause', {ids:ids}, function() { 409 | $('#list input[type="checkbox"]:checked').removeAttr('checked'); 410 | refresh(); 411 | }); 412 | } 413 | 414 | function remove(ids) { 415 | api('remove', {ids:ids}, function() { 416 | $('#list input[type="checkbox"]:checked').removeAttr('checked'); 417 | refresh(); 418 | }); 419 | } 420 | 421 | function resume(ids) { 422 | api('resume', {ids:ids}, function() { 423 | $('#list input[type="checkbox"]:checked').removeAttr('checked'); 424 | refresh(); 425 | }); 426 | } 427 | 428 | function download(ids) { 429 | for (var i in ids) { 430 | var id = ids[i]; 431 | var filename = $('#task-%s td.filename'.fs(id)).text(); 432 | var url = '/download/%s'.fs(filename); 433 | document.location.href = url; 434 | } 435 | } 436 | 437 | function rconfig() { 438 | api('rconfig', null, function(return_content){ 439 | var rconfig = return_content['result']; 440 | console.error(rconfig); 441 | $.each(rconfig, function(key, val) { 442 | $('#' + key).val(val); 443 | $("input[id="+key+"]").attr("checked",val); 444 | $('#' + key + "1").val(val); 445 | $("input[id="+key+"1]").attr("checked",val); 446 | if (key == 'user_agent') { 447 | if (!val) { 448 | $('#user_agent1').val(navigator.userAgent); 449 | } 450 | } 451 |   }); 452 | }); 453 | } 454 | 455 | function settop() { 456 | } 457 | 458 | function setbottom () { 459 | } 460 | 461 | init(); 462 | }); 463 | -------------------------------------------------------------------------------- /screenshot/ad.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihaoyun6/axeldown-core/a0dec7dfbbc23f5e0bc9d10bf94c0d651dabc934/screenshot/ad.jpg -------------------------------------------------------------------------------- /screenshot/admenu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihaoyun6/axeldown-core/a0dec7dfbbc23f5e0bc9d10bf94c0d651dabc934/screenshot/admenu.jpg -------------------------------------------------------------------------------- /screenshot/build.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihaoyun6/axeldown-core/a0dec7dfbbc23f5e0bc9d10bf94c0d651dabc934/screenshot/build.jpg -------------------------------------------------------------------------------- /screenshot/dmacos.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihaoyun6/axeldown-core/a0dec7dfbbc23f5e0bc9d10bf94c0d651dabc934/screenshot/dmacos.jpg -------------------------------------------------------------------------------- /screenshot/downlinux.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihaoyun6/axeldown-core/a0dec7dfbbc23f5e0bc9d10bf94c0d651dabc934/screenshot/downlinux.jpg -------------------------------------------------------------------------------- /screenshot/dwin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihaoyun6/axeldown-core/a0dec7dfbbc23f5e0bc9d10bf94c0d651dabc934/screenshot/dwin.jpg -------------------------------------------------------------------------------- /screenshot/preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihaoyun6/axeldown-core/a0dec7dfbbc23f5e0bc9d10bf94c0d651dabc934/screenshot/preview.jpg -------------------------------------------------------------------------------- /screenshot/run.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihaoyun6/axeldown-core/a0dec7dfbbc23f5e0bc9d10bf94c0d651dabc934/screenshot/run.jpg --------------------------------------------------------------------------------
%s%s%s%s
%s / %s
%s%s
线程数:' + task.thsize + '   下载地址:'+ task.url + '
线程数:' + task.thsize + '   下载地址:'+ task.url + '