Compare commits

...

272 Commits

Author SHA1 Message Date
iyuu.cn
afe1a863ac 移除Arm64v8.Dockerfile 2020-07-30 21:00:10 +08:00
iyuu.cn
2a7947b9fa bash+ 2020-07-30 15:19:18 +08:00
iyuu.cn
fa6a38b7b3 移除swoft/alphp:base 2020-07-30 14:44:35 +08:00
iyuu.cn
02eb245ae0 更新Arm64v8.Dockerfile,测试自动编译。 2020-07-30 14:34:14 +08:00
iyuu.cn
3fc4e14b93 优化docker的Readme.md注释 2020-07-29 19:12:16 +08:00
iyuu.cn
6a5d375044 修复编码错误。 2020-07-29 16:00:43 +08:00
iyuu.cn
da0cb81ab2 修复笔误。 2020-07-29 15:28:48 +08:00
iyuu.cn
41ebed71c7 本地编译更改基础镜像为arm64v8/alpine:3.12,并使用国内镜像源 2020-07-29 15:26:12 +08:00
iyuu.cn
05426489f0 新增Docker自动Builds文件Arm64v8.Dockerfile; 2020-07-29 15:25:07 +08:00
iyuu.cn
8489b07b35 精简和优化readme中相关项目说明 2020-07-29 15:20:22 +08:00
iyuu.cn
3e28685731 Dockerfile使用官方源 2020-07-29 03:02:53 +08:00
iyuu.cn
2d5ab5ae9b 新增Dockerfile 2020-07-29 02:14:37 +08:00
iyuu.cn
fa61ad21e0 优化hdsky下载url后的显示效果。 2020-07-29 00:37:23 +08:00
iyuu.cn
1f95e62d95 v1.10.0 2020-07-29 00:32:47 +08:00
iyuu.cn
a84b193d14 调整hdsky流控参数20/20,适配天空辅种。 2020-07-29 00:29:54 +08:00
iyuu.cn
e2dddfdead 新增AMD64平台docker构建脚本 2020-07-28 08:33:40 +08:00
iyuu.cn
f78e9c737c 新增Docker构建amd64镜像 2020-07-28 06:49:26 +08:00
iyuu.cn
8813afbd73 新增docker镜像Arm64v8 2020-07-28 03:27:59 +08:00
iyuu.cn
8b1bd9cdb5 fix 2020-07-25 20:37:57 +08:00
iyuu.cn
5418c02227 修复缺少isset($configALL['notify_on_change'])判断警告 2020-07-24 16:03:59 +08:00
iyuu.cn
a9d9fb637d 当配置不存在时,生成配置后再延时提示。 2020-07-24 14:01:03 +08:00
iyuu.cn
4247457cb6 更新readme.md相关项目 2020-07-24 13:27:34 +08:00
iyuu.cn
0047450161 更新readme.md相关项目;修改微信通知支持条件触发:有变化才发送通知(辅种成功 + 失败 > 0)。 2020-07-24 13:16:24 +08:00
iyuu.cn
15f9905004 合并hxsf提交,修改buile.sh 2020-07-24 13:07:05 +08:00
David
63b2bfc2bc Merge pull request #17 from hxsf/master
some features
2020-07-24 13:50:37 +08:00
hxsf
c5c2feb9d3 add notify_on_change feature 2020-07-24 13:39:22 +08:00
hxsf
6c06d9fb6a add new DockerFile and docker-compose example 2020-07-24 13:36:33 +08:00
iyuu.cn
6578397ee3 更新相关项目4个。 2020-07-23 19:53:01 +08:00
iyuu.cn
29c2e43405 首次运行生成config.php;提高瓷器、城市兼容性。 2020-07-23 18:47:06 +08:00
iyuu.cn
18cdd7abed 新增pthome流控参数 2020-07-22 21:21:59 +08:00
iyuu.cn
4fbf795811 增强客户端空密码检查 2020-07-22 03:28:42 +08:00
iyuu.cn
88ebc12ce5 微信报表新增失败详情提示 2020-07-21 19:42:17 +08:00
iyuu.cn
08b4b58083 新增hddolby流控参数 2020-07-21 19:24:04 +08:00
iyuu.cn
ca02231431 加入github徽章 2020-07-21 16:43:21 +08:00
iyuu.cn
7d638eba40 新增hdhome流控参数 2020-07-19 17:20:27 +08:00
iyuu.cn
c35c23583c 新增Ourbits、SSD、hdchina、hdsky、moecat、pt等站点的流控规则 2020-07-18 14:34:07 +08:00
iyuu.cn
b2b9000a86 优化代码结构 2020-07-17 23:02:21 +08:00
iyuu.cn
b80a5b89cc 更新Readme.md 2020-07-17 20:39:45 +08:00
iyuu.cn
c2e5b4d00b 新增移动做种微信报告 2020-07-17 19:55:53 +08:00
iyuu.cn
207320ede7 fix 2020-07-16 20:59:41 +08:00
iyuu.cn
6e1653fc1e fix 2020-07-14 16:44:28 +08:00
iyuu.cn
32ec80d552 提高对旧配置的兼容性 2020-07-14 02:13:20 +08:00
iyuu.cn
2b989f9e0e 新增瓷器、城市cookie过期微信通知 2020-07-13 10:19:36 +08:00
iyuu.cn
ff16781da9 fix 2020-07-13 08:47:00 +08:00
iyuu.cn
6a01b492ce 更新公告栏 2020-07-13 07:58:39 +08:00
iyuu.cn
de41bc0131 1.优化App.Api.Sites接口带版本号请求;2.新增异步间隔流控算法:各站独立、执行时间最优;3.config.sample.php各站点新增url_replace、url_join、limitRule参数。 2020-07-13 07:32:15 +08:00
iyuu.cn
6b83e9081b fix:优化代码 2020-07-12 21:29:41 +08:00
iyuu.cn
87f06ec5c4 接口域名部署SSL证书,支持双协议http/https。如:https://api.iyuu.cn 2020-07-12 19:39:19 +08:00
iyuu.cn
8a051d4d1f fix:优化转移做种时过滤器、选择器提示顺序。 2020-07-11 20:39:09 +08:00
iyuu.cn
0e32e625a4 新增功能:转移过滤器、选择器;可以只转移指定路径的种子,也可以排除指令路径的种子,按需转移。 2020-07-11 18:09:04 +08:00
iyuu.cn
a09dacbd20 优化微信通知提示 2020-07-11 02:46:59 +08:00
iyuu.cn
e3a9c32174 微信通知增加辅种缓存位置提示。 2020-07-10 19:33:51 +08:00
iyuu.cn
c8a94bfcff 优化代码结构,美化微信通知消息,删除多余的运行提示信息等 2020-07-10 19:17:12 +08:00
iyuu.cn
fd87d07539 更新md文档 2020-07-03 17:20:25 +08:00
iyuu.cn
df7100e4e9 更新QQ群信息:859882209、931954050 2020-07-03 17:19:51 +08:00
iyuu.cn
44f5b82650 新增HDfans站点 2020-07-03 13:01:03 +08:00
iyuu.cn
f5a33843e6 v1.8.3 2020-06-18 08:45:40 +08:00
iyuu.cn
7428144aa8 移除SSD站点辅种计数器。 2020-06-18 08:44:57 +08:00
iyuu.cn
cc325dc128 删除无用.cmd批处理 2020-06-10 23:09:20 +08:00
iyuu.cn
3d675ed4bd qBittorrent下载器增加root_folder配置项:是否创建子文件夹 2020-06-10 10:31:40 +08:00
iyuu.cn
eabb51c54a fix 2020-06-01 21:51:48 +08:00
iyuu.cn
1e1a7bfc59 $configALL[$siteName]['count'] 初始化。 2020-06-01 21:48:38 +08:00
iyuu.cn
005788827d SSD加入辅种计数器(限制每次辅种10个),降低初次辅种过多ban禁IP的可能性。 2020-06-01 21:45:57 +08:00
iyuu.cn
ffb600c334 新增上报错误种子403状态码,例如:兼容杜比等站已删除种子返回403状态码的情况。 2020-05-18 12:52:57 +08:00
iyuu.cn
290bfbe5c9 优化build.sh编译脚本 2020-05-12 18:41:52 +08:00
iyuu.cn
6ecf4f1d54 更新README.MD 2020-05-06 16:37:54 +08:00
iyuu.cn
c809e8d052 v1.7.9 2020-05-06 12:38:27 +08:00
iyuu.cn
8d7dffbf3d 新增站点:海胆haidan 2020-05-06 12:37:43 +08:00
iyuu.cn
cc50804b40 优化URL获取的代码结构 2020-05-01 09:43:35 +08:00
iyuu.cn
4444aec4fb v1.7.8 2020-04-26 22:15:06 +08:00
iyuu.cn
68e128e5cd 小钢炮安装脚本添加/BT_backup目录挂载至docker 2020-04-26 22:14:14 +08:00
iyuu.cn
ec7032698d 更改文件名与Readme.md 2020-04-17 11:57:06 +08:00
iyuu.cn
5308c691b6 更新Readme.md 2020-04-11 13:59:04 +08:00
iyuu.cn
c7f4859cb7 新增支持站点伊甸园hdbd。 2020-04-11 10:16:25 +08:00
iyuu.cn
dc651174b8 v1.7.6 2020-04-07 23:19:28 +08:00
iyuu.cn
a95e579cf6 v1.7.6 2020-04-07 23:00:29 +08:00
iyuu.cn
4811937f42 优化注释 2020-04-06 15:03:19 +08:00
iyuu.cn
f9674c1898 微信报表,增加脚本版本号显示 2020-04-06 15:03:04 +08:00
iyuu.cn
2fdacc6936 更新教程 2020-04-06 15:02:06 +08:00
iyuu.cn
a2034f2840 新增合作站点MoeCat 2020-04-03 22:27:08 +08:00
iyuu.cn
4894464fdd 优化部分php环境CURLOPT_SSL_VERIFYHOST参数设置为false后,卡顿问题。 2020-04-03 21:03:58 +08:00
iyuu.cn
c90acbec6c 路径转换方法:提高替换模式的兼容性。 2020-04-03 12:19:03 +08:00
iyuu.cn
c4a8cef4ce 修改build.sh脚本,新增小钢炮专用脚本 2020-03-27 14:59:26 +08:00
iyuu.cn
cc7eb9b06a 添加、修改教程。 2020-03-27 14:58:41 +08:00
iyuu.cn
ea3f3deaa8 更新小钢炮docker脚本环境构建教程,新增小钢炮专用创建脚本。 2020-03-27 14:23:38 +08:00
iyuu.cn
44c0bcdeb4 没用$path == $key判断,是为了提高兼容性 2020-03-27 11:41:10 +08:00
iyuu.cn
d015cd56a6 fix 2020-03-26 23:49:56 +08:00
iyuu.cn
4cf1222b37 注释curl证书验证,本设置会引起部分站点的https访问失败,返回Empty reply from server错误 2020-03-26 18:31:08 +08:00
iyuu.cn
b5f4537df7 新增站点HDRoute 2020-03-25 23:49:17 +08:00
iyuu.cn
1e428a04b7 优化精简路径转换返回 2020-03-25 19:20:35 +08:00
iyuu.cn
4c7fd2cf3d 更新readme.md接口说明的部分 2020-03-25 19:02:13 +08:00
iyuu.cn
43b8bc050b 精简示例配置 2020-03-25 18:49:24 +08:00
iyuu.cn
1c1ecd33b8 提示当前脚本路径。 2020-03-25 17:44:56 +08:00
iyuu.cn
e201505dca 优化git_pull.sh脚本:自动拉取最新代码,然后执行辅种脚本。 2020-03-25 13:49:58 +08:00
iyuu.cn
e02d32b129 v1.7.1 2020-03-25 12:43:09 +08:00
iyuu.cn
01a9b71e9f 为避免多php版本带来困扰,cmd批处理内php采用环境变量调用。 2020-03-25 12:31:30 +08:00
iyuu.cn
f89b5f7325 v1.7.0 2020-03-23 13:19:45 +08:00
iyuu.cn
5880f563e8 增加通用备份脚本,可以添加进计划任务。 2020-03-23 13:05:03 +08:00
iyuu.cn
61eb8f69c9 简化辅种输出。 2020-03-22 11:29:47 +08:00
iyuu.cn
b4ca455a75 优化输出提示,未填写passkey的站点自动跳过,不提示。 2020-03-22 11:24:52 +08:00
iyuu.cn
02205e6ce3 修复transmission往qBittorrent转移种子,错误的问题。 2020-03-21 19:33:50 +08:00
iyuu.cn
818794a19b 错误提示后面增加换行符。 2020-03-19 19:58:10 +08:00
iyuu.cn
88bc79ff58 版本升级至v1.6.7 2020-03-16 15:00:00 +08:00
iyuu.cn
f9164b6e53 修改php文件夹名称,让脚本更通用 2020-03-16 14:50:33 +08:00
iyuu.cn
687a83f80d 添加公告 2020-03-15 23:20:07 +08:00
iyuu.cn
253cff0e23 增加Windows从gitee安装脚本批处理。 2020-03-15 13:12:50 +08:00
iyuu.cn
ed1d4fbe11 新增合作站点pthome,可以对使用接口的用户进行认证绑定。 2020-03-15 12:42:27 +08:00
iyuu.cn
99c2655fd4 调整配置顺序 2020-03-15 00:54:58 +08:00
iyuu.cn
7adbadd700 修复一处错误。 2020-03-14 22:55:31 +08:00
iyuu.cn
6e4c530128 减少sleep等待时间为1秒。 2020-03-14 17:21:20 +08:00
iyuu.cn
9d183d742d 优化完善转移做种客户端时,出现的错误提示。 2020-03-14 17:20:46 +08:00
iyuu.cn
f5bc84e23d 优化合作站点登录逻辑,设置登录缓存,减少请求。 2020-03-14 17:20:02 +08:00
iyuu.cn
77443896dd 更新教程 2020-03-13 23:40:04 +08:00
iyuu.cn
657f092eef 新增公告 2020-03-13 23:35:06 +08:00
iyuu.cn
59b2d5b4ca 简化docker编译文件 2020-03-13 23:30:00 +08:00
iyuu.cn
b64abe55d5 新增3篇安装教程 2020-03-13 21:29:23 +08:00
iyuu.cn
df6761bbc3 composer dump-autoload 2020-03-13 21:28:58 +08:00
iyuu.cn
880775b815 新增hdhome合作站点用户id认证配置 2020-03-13 14:51:25 +08:00
iyuu.cn
0a29e7ed2f 修复瓷器辅种显示乱码不美观的问题 2020-03-12 08:08:01 +08:00
iyuu.cn
75e66e3b0b 新增合作站点hdhome,可以对使用接口的用户进行认证绑定。 2020-03-12 00:00:53 +08:00
iyuu.cn
948e68f55b 版本号升级到:v1.6.0 2020-03-11 23:46:42 +08:00
iyuu.cn
c35bbe0c91 新增合作站点hddolby,可以对使用接口的用户进行认证绑定。 2020-03-11 23:42:39 +08:00
iyuu.cn
fb68d21589 更新最简配置wiki 2020-03-10 14:38:28 +08:00
iyuu.cn
fa31319399 增加composer一键安装批处理 2020-03-09 13:48:36 +08:00
iyuu.cn
9597182628 新增3种安装方式,并提供安装命令 2020-03-09 13:48:02 +08:00
iyuu.cn
61d3ca843d 修复因不支持站点辅种缓存引起的错误提示。 2020-03-09 13:37:59 +08:00
iyuu.cn
4a63353d27 增加git更新批处理 2020-03-03 16:43:12 +08:00
iyuu.cn
07b0b2626d 紧急修复:qBittorrent的paused自动暂停参数为字符串的bug(此问题为上次转移做种配置项添加自定义后留下来的问题。) 2020-03-02 00:32:05 +08:00
iyuu.cn
755bb1d526 优化git自动更新sh脚本,防止因手动更改文件导致更新失败。 2020-03-01 13:39:09 +08:00
iyuu.cn
9a39306990 人性化的加入vendor目录。 2020-03-01 13:36:03 +08:00
iyuu.cn
43a90f40d3 优化做种客户端转移配置的提示,更清晰易懂。 2020-02-29 21:46:57 +08:00
iyuu.cn
fa60001758 修改示例配置的注释,以免混淆 2020-02-26 21:46:36 +08:00
iyuu.cn
80ca5e9690 添加git自动更新命令 2020-02-26 17:33:45 +08:00
iyuu.cn
550e53d5e0 转移种子,设置自动开始开关 2020-02-26 16:07:51 +08:00
iyuu.cn
ccd0acd7f6 添加更新历史 2020-02-26 16:07:36 +08:00
iyuu.cn
b788ef1245 新增站点:葡萄pt 2020-02-25 23:30:15 +08:00
iyuu.cn
17e9a494ee 升级docker制作脚本,每次制作都是最新版。 2020-02-25 21:03:54 +08:00
iyuu.cn
0ccc4f6342 优化过滤transmission连接URL兼容性(修复网址后有斜杠,无法连接的问题) 2020-02-24 14:47:36 +08:00
iyuu.cn
93208b3dc3 从composer创建工程 2020-02-23 19:49:21 +08:00
iyuu.cn
082700cf5d 发布包到composer,v1.5.3 2020-02-23 19:35:47 +08:00
iyuu.cn
d2269aa1ef git@github.com:ledccn/IYUUAutoReseed.git 2020-02-23 19:26:36 +08:00
iyuu.cn
95a81499c3 创建composer包:ledccn/IYUUAutoReseed 2020-02-23 19:19:37 +08:00
iyuu.cn
2f4ad383a7 创建composer包:iyuu/IYUUAutoReseed 2020-02-23 19:12:21 +08:00
iyuu.cn
9b62e84cda 加入git自动更新脚本 2020-02-23 16:25:38 +08:00
iyuu.cn
125394ee0b 优化提高Windows平台转移做种客户端的兼容性 2020-02-23 01:19:25 +08:00
iyuu.cn
efa11d280d 更新教程 2020-02-22 20:56:13 +08:00
iyuu.cn
18f860713f 新增公告:转移做种常见错误 2020-02-22 16:35:17 +08:00
iyuu.cn
264aef754e 更新文档、新增支持站点天雪v1.5.2 2020-02-21 21:24:22 +08:00
iyuu.cn
c178046435 新增站点天雪,添加配置即可辅种。 2020-02-21 21:08:17 +08:00
iyuu.cn
397106cad9 更新公告 2020-02-21 19:07:14 +08:00
iyuu.cn
cc3a4ec1b8 删除海豚音乐配置 2020-02-21 18:14:05 +08:00
iyuu.cn
d87029c4f4 修复bug 修订版本号+1 2020-02-21 08:59:27 +08:00
iyuu.cn
7f2889ba1e 修正海豚音乐torrent_pass、authkey正确名称
紧急更新v1.5.1
2020-02-21 08:40:29 +08:00
iyuu.cn
185157a00c 添加部分,更新历史 2020-02-21 07:23:42 +08:00
iyuu.cn
9ad36efed8 更新开发计划 2020-02-21 07:23:17 +08:00
iyuu.cn
d0fc64beb1 更新wiki教程 2020-02-21 07:23:03 +08:00
iyuu.cn
426e47337d 版本号升级至1.5.0
新增海豚音乐
2020-02-21 06:46:53 +08:00
iyuu.cn
37809f7bfe 新增海豚音乐示例配置 2020-02-21 06:42:54 +08:00
iyuu.cn
721aaf217b 新增海豚音乐dicmusic,更新捐赠名单 2020-02-21 06:42:35 +08:00
iyuu.cn
aa18b23dbb 增加转移做种搜索目录命令find / -name BT_backup 2020-02-20 21:03:31 +08:00
iyuu.cn
4d452e3909 优化大于一万条做种,防止精简配置,导致超时设置无效。 2020-02-20 18:09:50 +08:00
iyuu.cn
741ccb4fb4 优化大于一万条做种时辅种失败问题。 2020-02-20 17:49:50 +08:00
iyuu.cn
e336970fca 小钢炮使用docker版qb 4.1.6 简明教程 2020-02-20 17:48:24 +08:00
iyuu.cn
0321deee01 小钢炮qBittorrent连接失败的处理办法 2020-02-20 17:05:57 +08:00
iyuu.cn
08ef3769c2 删除示例配置圣城cnscg 2020-02-20 16:33:31 +08:00
iyuu.cn
1355398f76 修复扩展参数extra_options错误 2020-02-20 16:13:15 +08:00
iyuu.cn
53533df4b9 新增两个API接口 2020-02-20 09:20:16 +08:00
iyuu.cn
53902ee878 优化https判断 2020-02-20 06:52:31 +08:00
iyuu.cn
60430f6550 代码格式化 2020-02-19 13:33:58 +08:00
iyuu.cn
9445547378 修复transmission添加任务后,ID无法显示的bug 2020-02-19 02:45:27 +08:00
iyuu.cn
8ba1ef1ff4 新增docker容器安装使用方法 2020-02-18 21:23:19 +08:00
iyuu.cn
ca148bac6b 更新忽略列表 2020-02-18 18:02:40 +08:00
iyuu.cn
3393eae7e7 新增做种列表备份功能,便于紧急恢复。 2020-02-18 16:02:16 +08:00
iyuu.cn
06c00f88dc 增加版本号,用于服务器端限定 2020-02-18 15:08:57 +08:00
iyuu.cn
854121884b 新增转移成功跳校验、转移成功删除当前做种功能。 2020-02-18 14:48:35 +08:00
iyuu.cn
da88f3e7fc 新增种子删除上报功能! 2020-02-17 16:54:27 +08:00
iyuu.cn
3abc02d660 修改示例配置 2020-02-17 16:46:59 +08:00
iyuu.cn
339f1304c6 优化日志记录 2020-02-16 20:51:39 +08:00
iyuu.cn
c4df9899c8 修复transmission添加任务成功后,获取名字和id 2020-02-16 15:10:15 +08:00
iyuu.cn
6494351ff3 修复transmission返回值判断错误 2020-02-16 14:39:23 +08:00
iyuu.cn
e01dbfd601 审查代码,删除无用接口 2020-02-16 01:26:40 +08:00
iyuu.cn
940f948f43 增加客户端文件是否存在判断 2020-02-15 22:12:12 +08:00
iyuu.cn
958bef0dfa windows大小写不敏感,新增 2020-02-15 19:24:58 +08:00
iyuu.cn
ea74b96293 windows大小写不敏感,删除 2020-02-15 19:24:10 +08:00
iyuu.cn
94948610e4 优化瓷器cookie有效性检查 2020-02-15 19:20:12 +08:00
iyuu.cn
0f1605ffca 修改transmission首字母小写 2020-02-15 16:05:05 +08:00
iyuu.cn
33ff8a991a 优化工厂模式创建客户端实例
增加新功能:本地做种客户端转移
2020-02-15 09:20:41 +08:00
iyuu.cn
877616214c 新增本地转移做种客户端示例配置 2020-02-15 09:19:20 +08:00
iyuu.cn
aa4a8c76fe 升级php版本号 2020-02-12 14:50:05 +08:00
iyuu.cn
f6870a4e95 增加瓷器的第一次下载种子检测 2020-02-12 14:16:38 +08:00
iyuu.cn
e073f165ac 优化提示信息。echo对隐私信息脱敏 2020-02-09 23:20:20 +08:00
iyuu.cn
6144b8b301 更新Readme.md 2020-02-08 11:59:30 +08:00
iyuu.cn
658d764c26 修复pathReplace方法返回值错误 2020-02-08 11:27:10 +08:00
iyuu.cn
2ce1920c51 后端API迁移至新架构,升级版本号到1.0.0 2020-02-07 18:31:51 +08:00
iyuu.cn
ea158ddd55 规范代码 2020-02-01 21:47:26 +08:00
IYUU
76c0cefca2 变更忽略列表 2020-01-31 20:55:27 +08:00
IYUU
a69a2c2955 变更忽略列表 2020-01-31 20:51:52 +08:00
IYUU
7dc0c54c56 排除列表 2020-01-31 20:50:24 +08:00
IYUU
19197d36ce 整合vendor依赖目录 2020-01-31 15:16:18 +08:00
IYUU
8561fac8c4 修改示例配置IP为本机 2020-01-31 12:28:07 +08:00
IYUU
e4911871b8 新增Windows辅种一键脚本 2020-01-31 11:18:00 +08:00
IYUU
a328f68178 完善逻辑 2020-01-31 03:00:32 +08:00
IYUU
ce65b5d1ff 规范换行符 2020-01-31 01:07:18 +08:00
IYUU
dff0c17b33 调整顺序 2020-01-31 00:35:43 +08:00
IYUU
7dcd3e55f4 更新https支持 2020-01-30 22:00:21 +08:00
IYUU
9027262f57 更新文档 2020-01-30 21:52:34 +08:00
IYUU
31d0e6ee03 优化辅种CCFBits下载种子方法。 2020-01-30 17:33:34 +08:00
IYUU
076e688e1c 修复CCFBits自动评论 2020-01-30 16:41:36 +08:00
IYUU
c649c05a3a 新增CCFBits站点支持 2020-01-30 13:57:10 +08:00
IYUU
4896e08635 CCFBits辅种时智能启用评论 2020-01-30 13:22:48 +08:00
IYUU
3b3289274b 完善转移功能:当启用转移功能时,可以设置是否辅种被转移客户端。 2020-01-30 11:33:46 +08:00
IYUU
bf65f7039d 示例配置内增加教程 2020-01-29 15:11:02 +08:00
IYUU
0a7c2120a1 修复str_replace($key, '', $path, 1);执行错误的bug 2020-01-29 14:43:30 +08:00
IYUU
04a10295c2 更新文档 2020-01-29 00:42:52 +08:00
IYUU
3e75aec9d0 减少依赖包 2020-01-29 00:08:58 +08:00
IYUU
63ce263afb 新增做种客户端移动,新增北邮人辅种 2020-01-28 23:57:18 +08:00
iyuu.cn
19df3f3cfb 格式化代码 2020-01-22 22:56:35 +08:00
iyuu.cn
66490efad7 优化错误提示,优化辅种匹配算法 2020-01-22 20:00:21 +08:00
iyuu.cn
9044d3575e 更新参数 2020-01-22 14:19:17 +08:00
iyuu.cn
69ba1ebe56 wiki加入RSS订阅使用教程 2020-01-22 12:10:32 +08:00
iyuu.cn
978295b44d 更新示例配置,支持RSS订阅 2020-01-22 12:05:08 +08:00
iyuu.cn
f3b4deae4c 标准化格式 2020-01-22 10:47:33 +08:00
iyuu.cn
fcb2384949 增加乱码解决方法 2020-01-22 09:37:21 +08:00
iyuu.cn
33b55d7598 新增更新历史信息 2020-01-22 08:50:58 +08:00
iyuu.cn
db8290fe89 新增RSS订阅新种命令 2020-01-22 08:50:35 +08:00
iyuu.cn
81083220c5 修正站点鉴权帮助文档 2020-01-22 08:50:13 +08:00
iyuu.cn
39cb00d648 新增常见问题 2020-01-22 08:49:40 +08:00
iyuu.cn
db9010cee7 新增排除文件。 2020-01-21 17:53:17 +08:00
iyuu.cn
ad6f798946 新增json支持的站点列表本地化 2020-01-21 11:00:46 +08:00
iyuu.cn
36d07a25db 新增在线查询支持站点表格显示 2020-01-21 02:18:51 +08:00
iyuu.cn
7c35291b15 新增Windows系统composer install安装依赖包命令 2020-01-21 01:17:57 +08:00
iyuu.cn
7d531a26c8 Transmission兼容支持https链接 2020-01-20 14:09:04 +08:00
iyuu.cn
385a122f02 更新捐赠名单 2020-01-19 12:56:45 +08:00
iyuu.cn
f75feeca44 更新数据清理 2020-01-19 12:35:41 +08:00
iyuu.cn
d8fc340691 修改hdbug站点示例配置信息 2020-01-18 23:11:31 +08:00
iyuu.cn
2345124ce0 更新数据 2020-01-18 21:25:31 +08:00
iyuu.cn
f9592c6b0d 新增IYUU入口文件 2020-01-18 20:13:21 +08:00
iyuu.cn
fd695e4209 更新数据清理时间 2020-01-18 12:16:56 +08:00
iyuu.cn
8ddb143485 新增wlog日志助手函数 2020-01-18 12:16:25 +08:00
iyuu.cn
ef2668ca54 完善各种日志的输出 2020-01-18 12:16:07 +08:00
iyuu.cn
51f3fa39bc 检查代码缩进规范 2020-01-18 10:27:27 +08:00
iyuu.cn
84b3f23c27 完善下载最新源码的教程 2020-01-17 21:35:36 +08:00
iyuu.cn
f260065fb9 合并代码,优化hash缓存日志信息。 2020-01-17 19:47:30 +08:00
iyuu.cn
9615f177d0 加入wiki 2020-01-17 19:46:27 +08:00
iyuu.cn
f86c0a8392 修复添加种子元数据时,传入扩展参数失效的bug 2020-01-17 15:18:10 +08:00
iyuu.cn
c222663547 修改php版本要求,规范返回值 2020-01-17 13:15:10 +08:00
iyuu.cn
8167c7444a 优化前置检查逻辑 2020-01-17 13:14:35 +08:00
iyuu.cn
a70626d73b 增加排除目录 2020-01-17 13:14:14 +08:00
David
38e25850cf Merge pull request #8 from Rhilip/master
代码结构优化,来自Rhilip
2020-01-17 12:09:04 +08:00
Rhilip
255c7c4a2f 使用ClientInterface优化 2020-01-17 11:20:27 +08:00
Rhilip
32e14f9471 增加IDE注释 2020-01-17 11:13:19 +08:00
Rhilip
b0607dbcf0 RPC和AutoReseed文件重复 2020-01-17 11:09:12 +08:00
Rhilip
27104bd9a2 迁移文件 2020-01-17 10:59:48 +08:00
Rhilip
86b7dcb552 移除不支持的uTorrent 2020-01-17 10:33:07 +08:00
Rhilip
5f80ec199b 移动到helper.php 2020-01-17 10:32:49 +08:00
Rhilip
a49c63af09 未使用到Bencode库 2020-01-17 10:26:35 +08:00
Rhilip
32c04f1621 merge处理 2020-01-17 10:16:41 +08:00
Rhilip
e35cb261d1 Merge remote-tracking branch 'raw/master'
# Conflicts:
#	app/config/config.sample.php
#	wiki/常见问题.md
2020-01-17 10:13:28 +08:00
iyuu.cn
3430a4cf7b 修复伊甸园配置项的key 2020-01-16 23:26:06 +08:00
iyuu.cn
5123d31ca6 修复瓷器、城市辅种逻辑错误。重复检测失效的问题。 2020-01-16 23:25:44 +08:00
iyuu.cn
2fbe0ab29c 新增伊甸园配置项 2020-01-16 22:30:03 +08:00
iyuu.cn
f610ef0fca 完善配置注释 2020-01-16 22:22:59 +08:00
iyuu.cn
dafcb50d7a 新增常见问题 2020-01-16 22:18:14 +08:00
iyuu.cn
61a552c6ae 完善配置备注 2020-01-16 22:17:51 +08:00
iyuu.cn
c9a8039afa 新增瓷器人机检测,触发人机智能跳过后续辅种。 2020-01-16 14:16:45 +08:00
iyuu.cn
d862eea662 新增404数据清理公示 2020-01-16 14:16:05 +08:00
iyuu.cn
7b15f4f4df 404数据清理时间 2020-01-16 02:40:04 +08:00
iyuu.cn
feff490fd6 更新免责声明 2020-01-16 02:29:39 +08:00
Rhilip
7315524056 使用php-cs-fixer更新代码格式 2020-01-15 21:44:00 +08:00
Rhilip
7a41449855 从git中移除wiki和vendor目录 2020-01-15 21:41:28 +08:00
iyuu.cn
57f19e47f9 新增数据同步看板 2020-01-15 20:50:19 +08:00
iyuu.cn
f9ebca31e2 新增免责声明 2020-01-15 20:50:04 +08:00
81 changed files with 4971 additions and 24000 deletions

5
.gitignore vendored
View File

@@ -1,5 +0,0 @@
/app/torrent
/app/config/config.php
/php-7.2.12-nts
/*.bat
/*.sh

7
8.git更新与辅种.cmd Normal file
View File

@@ -0,0 +1,7 @@
@echo off
chcp 65001
git fetch --all
git reset --hard origin/master
#git pull
php %cd%\iyuu.php
pause

4
9.执行辅种.cmd Normal file
View File

@@ -0,0 +1,4 @@
@echo off
chcp 65001
php %cd%\iyuu.php
pause

105
Dockerfile Normal file
View File

@@ -0,0 +1,105 @@
FROM swoft/alphp:base
#FROM swoft/alphp:cli
#FROM alpine:latest
LABEL maintainer="david <367013672@qq.com>" version="1.0"
##
# ---------- env settings ----------
##
# --build-arg timezone=Asia/Shanghai
ARG timezone
# prod pre test dev
ARG app_env=prod
# default use www-data user
# ARG add_user=www-data
ENV APP_ENV=${app_env:-"prod"} \
TIMEZONE=${timezone:-"Asia/Shanghai"}
##
# ---------- building ----------
##
RUN set -ex \
# change apk source repo
# && sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/' /etc/apk/repositories \
&& apk update \
&& apk add --no-cache \
# Install base packages ('ca-certificates' will install 'nghttp2-libs')
# ca-certificates \
# curl \
# tar \
# xz \
# libressl \
# openssh \
# openssl \
git \
tzdata \
# pcre \
# install php7 and some extensions
php7 \
# php7-common \
# php7-bcmath \
php7-curl \
# php7-ctype \
php7-dom \
# php7-fileinfo \
# php7-gettext \
# php7-gd \
# php7-iconv \
# php7-imagick \
php7-json \
php7-mbstring \
#php7-mongodb \
# php7-mysqlnd \
# php7-openssl \
# php7-opcache \
# php7-pdo \
# php7-pdo_mysql \
# php7-pdo_sqlite \
# php7-phar \
# php7-posix \
# php7-redis \
php7-simplexml \
# php7-sockets \
# php7-sodium \
# php7-sqlite \
# php7-session \
# php7-sysvshm \
# php7-sysvmsg \
# php7-sysvsem \
# php7-tokenizer \
php7-zip \
# php7-zlib \
php7-xml \
&& git clone https://gitee.com/ledc/IYUUAutoReseed.git /var/www \
&& cp /var/www/config/config.sample.php /var/www/config/config.php \
&& ln -sf /var/www/config/config.php /config.php \
&& apk del --purge *-dev \
&& rm -rf /var/cache/apk/* /tmp/* /usr/share/man /usr/share/php7 \
# ---------- some config,clear work ----------
&& cd /etc/php7 \
# - config PHP
&& { \
echo "upload_max_filesize=100M"; \
echo "post_max_size=108M"; \
echo "memory_limit=1024M"; \
echo "date.timezone=${TIMEZONE}"; \
} | tee conf.d/99-overrides.ini \
# - config timezone
&& ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \
&& echo "${TIMEZONE}" > /etc/timezone \
# ---------- some config work ----------
# - ensure 'www-data' user exists(82 is the standard uid/gid for "www-data" in Alpine)
# && addgroup -g 82 -S ${add_user} \
# && adduser -u 82 -D -S -G ${add_user} ${add_user} \
# # - create user dir
# && mkdir -p /data \
# && chown -R ${add_user}:${add_user} /data \
&& echo -e "\033[42;37m Build Completed :).\033[0m\n"
EXPOSE 9000
# VOLUME ["/var/www", "/data"]
WORKDIR /var/www

981
app/AutoReseed.php Normal file
View File

@@ -0,0 +1,981 @@
<?php
namespace IYUU;
use Curl\Curl;
use IYUU\Client\AbstractClient;
use IYUU\Library\IFile;
use IYUU\Library\Oauth;
use IYUU\Library\Table;
/**
* IYUUAutoReseed自动辅种类
*/
class AutoReseed
{
// 版本号
const VER = '1.10.1';
// RPC连接
private static $links = [];
// 客户端配置
private static $clients = [];
// 站点列表
private static $sites = [];
// 不辅种的站点 'pt','hdchina'
private static $noReseed = [];
// cookie检查
private static $cookieCheck = ['hdchina','hdcity','hdsky'];
// 缓存路径
public static $cacheDir = TORRENT_PATH.'cache'.DS;
public static $cacheHash = TORRENT_PATH.'cachehash'.DS;
public static $cacheMove = TORRENT_PATH.'cachemove'.DS;
// API接口配置
public static $apiUrl = 'http://api.iyuu.cn';
public static $endpoints = array(
'login' => '/user/login',
'sites' => '/api/sites',
'infohash'=> '/api/infohash',
'notify' => '/api/notify',
'hash' => '/api/hash',
);
// curl
private static $curl = null;
// 退出状态码
public static $ExitCode = 0;
// 客户端转移做种 格式:['客户端key', '移动参数move']
private static $move = null;
// 微信消息体
private static $wechatMsg = array(
'hashCount' => 0, // 提交给服务器的hash总数
'sitesCount' => 0, // 可辅种站点总数
'reseedCount' => 0, // 返回的总数据
'reseedSuccess' => 0, // 成功:辅种成功(会加入缓存,哪怕种子在校验中,下次也会过滤)
'reseedError' => 0, // 错误:辅种失败(可以重试)
'reseedRepeat' => 0, // 重复:客户端已做种
'reseedSkip' => 0, // 跳过因未设置passkey而跳过
'reseedPass' => 0, // 忽略:因上次成功添加、存在缓存,而跳过
'MoveSuccess' => 0, // 移动成功
'MoveError' => 0, // 移动失败
);
// 错误通知消息体
private static $errNotify = array(
'sign' => '',
'site' => '',
'sid' => 0,
'torrent_id'=> 0,
'error' => '',
);
// 初始化
public static function init()
{
global $configALL;
echo "正在初始化运行参数,版本号:".self::VER.PHP_EOL;
sleep(mt_rand(1, 3));
self::backup('config', $configALL);
self::$curl = new Curl();
self::$curl->setOpt(CURLOPT_SSL_VERIFYPEER, false);
self::$curl->setOpt(CURLOPT_SSL_VERIFYHOST, 2);
// 合作站点鉴权绑定
Oauth::login(self::$apiUrl . self::$endpoints['login']);
// 显示支持站点列表
self::ShowTableSites();
self::$clients = isset($configALL['default']['clients']) && $configALL['default']['clients'] ? $configALL['default']['clients'] : array();
if (empty(self::$clients)) {
die('全局客户端为空!');
}
// 递归删除上次历史记录
IFile::rmdir(self::$cacheDir, true);
// 建立目录
IFile::mkdir(self::$cacheDir);
IFile::mkdir(self::$cacheHash);
IFile::mkdir(self::$cacheMove);
// 连接全局客户端
self::links();
}
/**
* 显示支持站点列表
*/
private static function ShowTableSites()
{
$list = [
'gitee源码仓库https://gitee.com/ledc/IYUUAutoReseed',
'github源码仓库https://github.com/ledccn/IYUUAutoReseed',
'教程https://gitee.com/ledc/IYUUAutoReseed/tree/master/wiki',
'问答社区http://wenda.iyuu.cn',
'【IYUU自动辅种交流】QQ群859882209、931954050'.PHP_EOL,
'正在连接IYUUAutoReseed服务器查询支持列表……'.PHP_EOL
];
foreach ($list as $value) {
echo $value.PHP_EOL;
}
$res = self::$curl->get(self::$apiUrl.self::$endpoints['sites'].'?sign='.Oauth::getSign().'&version='.self::VER);
$rs = json_decode($res->response, true);
$sites = isset($rs['data']['sites']) && $rs['data']['sites'] ? $rs['data']['sites'] : false;
// 数据写入本地
if ($sites) {
self::$sites = array_column($sites, null, 'id');
$json = array_column($sites, null, 'site');
ksort($json);
$json = json_encode($json, JSON_UNESCAPED_UNICODE);
$myfile = ROOT_PATH.DS.'config'.DS.'sites.json';
$file_pointer = @fopen($myfile, "w");
$worldsnum = @fwrite($file_pointer, $json);
@fclose($file_pointer);
} else {
if (isset($rs['msg']) && $rs['msg']) {
die($rs['msg'].PHP_EOL);
}
die('远端服务器无响应,请稍后再试!!!');
}
$data = [];
$i = $j = $k = 0; // i列、j序号、k行
foreach ($sites as $v) {
// 控制多少列
if ($i > 4) {
$k++;
$i = 0;
}
$i++;
$j++;
$data[$k][] = $j.". ".$v['site'];
}
echo "IYUUAutoReseed自动辅种脚本目前支持以下站点".PHP_EOL;
// 输出支持站点表格
$table = new Table();
$table->setRows($data);
echo($table->render());
}
/**
* 连接远端RPC下载器
*/
private static function links()
{
foreach (self::$clients as $k => $v) {
// 跳过未配置的客户端
if (empty($v['username']) || empty($v['password'])) {
self::$links[$k] = array();
echo "clients_".$k." 用户名或密码未配置,已跳过".PHP_EOL.PHP_EOL;
continue;
}
try {
// 传入配置,创建客户端实例
$client = AbstractClient::create($v);
self::$links[$k]['rpc'] = $client;
self::$links[$k]['type'] = $v['type'];
self::$links[$k]['BT_backup'] = isset($v['BT_backup']) && $v['BT_backup'] ? $v['BT_backup'] : '';
self::$links[$k]['root_folder'] = isset($v['root_folder']) ? $v['root_folder'] : 1;
$result = $client->status();
print $v['type'].''.$v['host']." Rpc连接 [{$result}] \n";
// 检查转移做种 (self::$move为空移动配置为真)
if (is_null(self::$move) && isset($v['move']) && $v['move']) {
self::$move = array($k,$v['move']);
}
} catch (\Exception $e) {
die('[连接错误] ' . $e->getMessage() . PHP_EOL);
}
}
}
/**
* @brief 添加下载任务
* @param string $torrent 种子元数据
* @param string $save_path 保存路径
* @return bool
*/
public static function add($rpcKey, $torrent, $save_path = '', $extra_options = array())
{
try {
$is_url = false;
if ((strpos($torrent, 'http://')===0) || (strpos($torrent, 'https://')===0) || (strpos($torrent, 'magnet:?xt=urn:btih:')===0)) {
$is_url = true;
}
// 下载服务器类型
$type = self::$links[$rpcKey]['type'];
// 判断
switch ($type) {
case 'transmission':
$extra_options['paused'] = isset($extra_options['paused']) ? $extra_options['paused'] : true;
if ($is_url) {
$result = self::$links[$rpcKey]['rpc']->add($torrent, $save_path, $extra_options); // 种子URL添加下载任务
} else {
$result = self::$links[$rpcKey]['rpc']->add_metainfo($torrent, $save_path, $extra_options); // 种子元数据添加下载任务
}
if (isset($result['result']) && $result['result'] == 'success') {
$id = $name = '';
if (isset($result['arguments']['torrent-duplicate'])) {
$id = $result['arguments']['torrent-duplicate']['id'];
$name = $result['arguments']['torrent-duplicate']['name'];
} elseif (isset($result['arguments']['torrent-added'])) {
$id = $result['arguments']['torrent-added']['id'];
$name = $result['arguments']['torrent-added']['name'];
}
print "名字:" .$name . PHP_EOL;
print "********RPC添加下载任务成功 [" .$result['result']. "] (id=" .$id. ")".PHP_EOL.PHP_EOL;
return true;
} else {
$errmsg = isset($result['result']) ? $result['result'] : '未知错误,请稍后重试!';
if (strpos($errmsg, 'http error 404: Not Found') !== false) {
self::sendNotify('404');
}
print "-----RPC添加种子任务失败 [{$errmsg}]" . PHP_EOL.PHP_EOL;
}
break;
case 'qBittorrent':
$extra_options['autoTMM'] = 'false'; //关闭自动种子管理
#$extra_options['skip_checking'] = 'true'; //跳校验
// 添加任务校验后是否暂停
if (isset($extra_options['paused'])) {
$extra_options['paused'] = $extra_options['paused'] ? 'true' : 'false';
} else {
$extra_options['paused'] = 'true';
}
// 是否创建根目录
$extra_options['root_folder'] = self::$links[$rpcKey]['root_folder'] ? 'true' : 'false';
if ($is_url) {
$result = self::$links[$rpcKey]['rpc']->add($torrent, $save_path, $extra_options); // 种子URL添加下载任务
} else {
$extra_options['name'] = 'torrents';
$rand = mt_rand(10, 42949672);
$extra_options['filename'] = intval($rand).'.torrent';
$result = self::$links[$rpcKey]['rpc']->add_metainfo($torrent, $save_path, $extra_options); // 种子元数据添加下载任务
}
if ($result === 'Ok.') {
print "********RPC添加下载任务成功 [{$result}]".PHP_EOL.PHP_EOL;
return true;
} else {
print "-----RPC添加种子任务失败 [{$result}]".PHP_EOL.PHP_EOL;
}
break;
default:
echo '[下载器类型错误] '.$type. PHP_EOL. PHP_EOL;
break;
}
} catch (\Exception $e) {
echo '[添加下载任务出错] ' . $e->getMessage() . PHP_EOL;
}
return false;
}
/**
* 辅种或转移,总入口
*/
public static function call()
{
if (self::$move!==null) {
self::move();
}
self::reseed();
self::wechatMessage();
exit(self::$ExitCode);
}
/**
* IYUUAutoReseed辅种
*/
private static function reseed()
{
global $configALL;
// 支持站点数量
self::$wechatMsg['sitesCount'] = count(self::$sites);
$sites = self::$sites;
// 遍历客户端 开始
foreach (self::$links as $k => $v) {
if (empty($v)) {
echo "clients_".$k." 用户名或密码未配置,已跳过".PHP_EOL.PHP_EOL;
continue;
}
// 过滤无需辅种的客户端
if (self::$move!==null && self::$move[0]!=$k && self::$move[1]==2) {
echo "clients_".$k." 根据设置无需辅种,已跳过!";
continue;
}
echo "正在从下载器 clients_".$k." 获取种子哈希……".PHP_EOL;
$hashArray = self::$links[$k]['rpc']->getList();
if (empty($hashArray)) {
// 失败
continue;
}
self::backup('clients_'.$k, $hashArray);
$infohash_Dir = $hashArray['hashString'];
unset($hashArray['hashString']);
// 签名
$hashArray['sign'] = Oauth::getSign();
$hashArray['timestamp'] = time();
$hashArray['version'] = self::VER;
// 写请求日志
wlog($hashArray, 'hashString'.$k);
self::$wechatMsg['hashCount'] +=count($infohash_Dir);
// 此处优化大于一万条做种时,设置超时
if (count($infohash_Dir) > 5000) {
$connecttimeout = isset($configALL['default']['CONNECTTIMEOUT']) && $configALL['default']['CONNECTTIMEOUT']>60 ? $configALL['default']['CONNECTTIMEOUT'] : 60;
$timeout = isset($configALL['default']['TIMEOUT']) && $configALL['default']['TIMEOUT']>600 ? $configALL['default']['TIMEOUT'] : 600;
self::$curl->setOpt(CURLOPT_CONNECTTIMEOUT, $connecttimeout);
self::$curl->setOpt(CURLOPT_TIMEOUT, $timeout);
}
// P($infohash_Dir); // 调试:打印目录对应表
echo "正在向服务器提交 clients_".$k." 种子哈希……".PHP_EOL;
$res = self::$curl->post(self::$apiUrl . self::$endpoints['infohash'], $hashArray);
$res = json_decode($res->response, true);
// 写返回日志
wlog($res, 'reseed'.$k);
$reseed = isset($res['data']) && $res['data'] ? $res['data'] : array();
if (empty($reseed)) {
echo "clients_".$k." 没有查询到可辅种数据".PHP_EOL.PHP_EOL;
continue;
}
// 判断返回值
if (empty($res['msg'])) {
echo "clients_".$k." 辅种数据下载成功!!!".PHP_EOL.PHP_EOL;
echo '【提醒】未配置passkey的站点都会跳过'.PHP_EOL.PHP_EOL;
} else {
$errmsg = isset($res['msg']) && $res['msg'] ? $res['msg'] : '远端服务器无响应,请稍后重试!';
echo '-----辅种失败,原因:' .$errmsg.PHP_EOL.PHP_EOL;
continue;
}
// 遍历当前客户端可辅种数据
foreach ($reseed as $info_hash => $vv) {
// 当前种子哈希对应的目录
$downloadDir = $infohash_Dir[$info_hash];
foreach ($vv['torrent'] as $id => $value) {
// 匹配的辅种数据累加
self::$wechatMsg['reseedCount']++;
// 站点id
$sid = $value['sid'];
// 种子id
$torrent_id = $value['torrent_id'];
// 站点名
if (empty($sites[$sid]['site'])) {
echo '-----当前站点不受支持,已跳过。' .PHP_EOL.PHP_EOL;
self::$wechatMsg['reseedSkip']++;
continue;
}
$siteName = $sites[$sid]['site'];
// 错误通知
self::setNotify($siteName, $sid, $torrent_id);
// 页面规则
$download_page = str_replace('{}', $torrent_id, $sites[$sid]['download_page']);
// 协议
$protocol = $sites[$sid]['is_https'] == 0 ? 'http://' : 'https://';
$_url = $protocol . $sites[$sid]['base_url']. '/' .$download_page;
/**
* 前置检测
*/
// 配置与passkey检测
if (empty($configALL[$siteName]) || empty($configALL[$siteName]['passkey'])) {
//echo '-------因当前' .$siteName. "站点未设置passkey已跳过".PHP_EOL.PHP_EOL;
self::$wechatMsg['reseedSkip']++;
continue;
} else {
echo "clients_".$k."正在辅种... {$siteName}".PHP_EOL;
}
// cookie检测
if (in_array($siteName, self::$cookieCheck) && empty($configALL[$siteName]['cookie'])) {
echo '-------因当前' .$siteName. '站点未设置cookie已跳过' .PHP_EOL.PHP_EOL;
self::$wechatMsg['reseedSkip']++;
continue;
}
// 流控检测
if (isset($configALL[$siteName]['limit'])) {
echo "-------因当前" .$siteName. "站点触发流控,已跳过!! {$_url}".PHP_EOL.PHP_EOL;
// 流控日志
if ($siteName == 'hdchina') {
$details_page = str_replace('{}', $torrent_id, 'details.php?id={}&hit=1');
$_url = 'https://' .$sites[$sid]['base_url']. '/' .$details_page;
}
wlog('clients_'.$k.PHP_EOL.$downloadDir.PHP_EOL."-------因当前" .$siteName. "站点触发流控,已跳过!! {$_url}".PHP_EOL.PHP_EOL, 'reseedLimit');
self::$wechatMsg['reseedSkip']++;
continue;
}
// 重复做种检测
if (isset($value['info_hash']) && isset($infohash_Dir[$value['info_hash']])) {
echo '-------与客户端现有种子重复:'.$_url.PHP_EOL.PHP_EOL;
self::$wechatMsg['reseedRepeat']++;
continue;
}
// 历史添加检测
if (is_file(self::$cacheHash . $value['info_hash'].'.txt')) {
echo '-------当前种子上次辅种已成功添加,已跳过! '.$_url.PHP_EOL.PHP_EOL;
self::$wechatMsg['reseedPass']++;
continue;
}
// 检查站点是否可以辅种
if (in_array($siteName, self::$noReseed)) {
echo '-------已跳过不辅种的站点:'.$_url.PHP_EOL.PHP_EOL;
self::$wechatMsg['reseedPass']++;
// 写入日志文件,供用户手动辅种
wlog('clients_'.$k.PHP_EOL.$downloadDir.PHP_EOL.$_url.PHP_EOL.PHP_EOL, $siteName);
continue;
}
// 操作站点流控的配置
if (isset($configALL[$siteName]['limitRule']) && $configALL[$siteName]['limitRule']) {
$limitRule = $configALL[$siteName]['limitRule'];
if (isset($limitRule['count']) && isset($limitRule['sleep'])) {
if ($limitRule['count'] <= 0) {
echo '-------当前站点辅种数量已满足规则,保障账号安全已跳过:'.$_url.PHP_EOL.PHP_EOL;
self::$wechatMsg['reseedPass']++;
continue;
} else {
// 异步间隔流控算法:各站独立、执行时间最优
$lastTime = isset($limitRule['time']) ? $limitRule['time'] : 0; // 最近一次辅种成功的时间
if ($lastTime) {
$interval = time() - $lastTime; // 间隔时间
if ($interval < $limitRule['sleep']) {
$t = $limitRule['sleep'] - $interval + mt_rand(1, 5);
do {
echo microtime(true)." 为账号安全,辅种进程休眠 {$t} 秒后继续...".PHP_EOL;
sleep(1);
} while (--$t > 0);
}
}
}
} else {
echo '-------当前站点流控规则错误缺少count或sleep参数请重新配置'.$_url.PHP_EOL.PHP_EOL;
self::$wechatMsg['reseedPass']++;
continue;
}
}
/**
* 种子URL组合方式区分
*/
$url = self::getTorrentUrl($siteName, $_url);
$reseedPass = false; // 标志:跳过辅种
// 特殊站点:种子元数据推送给下载器
switch ($siteName) {
case 'hdchina':
$cookie = $configALL[$siteName]['cookie'];
$userAgent = $configALL['default']['userAgent'];
// 拼接详情页URL
$details_page = str_replace('{}', $value['torrent_id'], 'details.php?id={}&hit=1');
$details_url = $protocol .$sites[$sid]['base_url']. '/' .$details_page;
print "种子详情页:".$details_url.PHP_EOL;
$details_html = download($details_url, $cookie, $userAgent);
// 删种检查
if (strpos($details_html, '没有该ID的种子') != false) {
echo '种子已被删除!'.PHP_EOL;
self::sendNotify('404');
$reseedPass = true;
break;
}
// 提取种子下载地址
$offset = strpos($details_html, str_replace('{hash}', '', $sites[$sid]['download_page']));
if ($offset === false) {
ff($siteName. '站点cookie已过期请更新后重新辅种');
echo 'cookie已过期请更新后重新辅种已加入排除列表'.PHP_EOL;
$t = 15;
do {
echo microtime(true)." cookie已过期请更新后重新辅种已加入排除列表{$t}秒后继续...".PHP_EOL;
sleep(1);
} while (--$t > 0);
$configALL[$siteName]['cookie'] = '';
$reseedPass = true;
break;
}
// 种子地址
$regex = "/download.php\?hash\=(.*?)[\"|\']/i"; // 提取种子hash的正则表达式
preg_match($regex, $details_html, $matchs);
$download_page = str_replace('{hash}', $matchs[1], $sites[$sid]['download_page']);
$_url = $protocol . $sites[$sid]['base_url']. '/' . $download_page;
print "种子下载页:".$_url.PHP_EOL;
$url = download($_url, $cookie, $userAgent);
if (strpos($url, '第一次下载提示') != false) {
echo "当前站点触发第一次下载提示,已加入排除列表".PHP_EOL;
echo "请进入瓷器详情页点右上角蓝色框下载种子成功后更新cookie".PHP_EOL;
$t = 30;
do {
echo microtime(true)." 请进入瓷器详情页点右上角蓝色框下载种子成功后更新cookie{$t}秒后继续...".PHP_EOL;
sleep(1);
} while (--$t > 0);
ff($siteName. '站点,辅种时触发第一次下载提示!');
self::$noReseed[] = $siteName;
$reseedPass = true;
}
if (strpos($url, '系统检测到过多的种子下载请求') != false) {
echo "当前站点触发人机验证,已加入排除列表".PHP_EOL;
ff($siteName. '站点,辅种时触发人机验证!');
$configALL[$siteName]['limit'] = 1;
self::$noReseed[] = $siteName;
$reseedPass = true;
}
break;
case 'hdcity':
$cookie = $configALL[$siteName]['cookie'];
$userAgent = $configALL['default']['userAgent'];
print "种子:".$_url.PHP_EOL;
if (isset($configALL[$siteName]['cuhash'])) {
// 已获取cuhash
# code...
} else {
// 获取cuhash
$html = download($protocol .$sites[$sid]['base_url']. '/pt', $cookie, $userAgent);
// 提取种子下载地址
$offset = strpos($html, 'cuhash=');
if ($offset === false) {
ff($siteName. '站点cookie已过期请更新后重新辅种');
echo 'cookie已过期请更新后重新辅种'.PHP_EOL;
$configALL[$siteName]['cookie'] = '';
$reseedPass = true;
break;
}
$regex = "/cuhash\=(.*?)[\"|\']/i"; // 提取种子cuhash的正则表达式
preg_match($regex, $html, $matchs);
$configALL[$siteName]['cuhash'] = $matchs[1];
}
$url = str_replace('{cuhash}', $configALL[$siteName]['cuhash'], $_url);
// 城市下载种子时会302转向
$url = download($url, $cookie, $userAgent);
if (strpos($url, 'Non-exist torrent id!') != false) {
echo '种子已被删除!'.PHP_EOL;
self::sendNotify('404');
// 标志:跳过辅种
$reseedPass = true;
}
break;
case 'hdsky':
$cookie = $configALL[$siteName]['cookie'];
$userAgent = $configALL['default']['userAgent'];
// 拼接详情页URL
$details_page = str_replace('{}', $value['torrent_id'], 'details.php?id={}&hit=1');
$details_url = $protocol .$sites[$sid]['base_url']. '/' .$details_page;
print "种子详情页:".$details_url.PHP_EOL;
$details_html = download($details_url, $cookie, $userAgent);
// 删种检查
if (strpos($details_html, '没有该ID的种子') != false) {
echo '种子已被删除!'.PHP_EOL;
self::sendNotify('404');
$reseedPass = true;
break;
}
// 提取种子下载地址
$remove = 'id={}&passkey={passkey}';
$offset = strpos($details_html, str_replace($remove, '', $sites[$sid]['download_page']));
if ($offset === false) {
ff($siteName. '站点cookie已过期请更新后重新辅种');
echo 'cookie已过期请更新后重新辅种已加入排除列表'.PHP_EOL;
$t = 15;
do {
echo microtime(true)." cookie已过期请更新后重新辅种已加入排除列表{$t}秒后继续...".PHP_EOL;
sleep(1);
} while (--$t > 0);
$configALL[$siteName]['cookie'] = '';
$reseedPass = true;
break;
}
// 种子地址
$regex = '/download.php\?(.*?)["|\']/i';
if (preg_match($regex, $details_html, $matchs)) {
$download_page = str_replace($remove, '', $sites[$sid]['download_page']).str_replace('&amp;', '&', $matchs[1]);
}
$_url = $protocol . $sites[$sid]['base_url']. '/' . $download_page;
print "种子下载页:".$_url.PHP_EOL;
$url = download($_url, $cookie, $userAgent);
if (strpos($url, '第一次下载提示') != false) {
echo "当前站点触发第一次下载提示,已加入排除列表".PHP_EOL;
echo "请进入种子详情页下载种子成功后更新cookie".PHP_EOL;
$t = 30;
do {
echo microtime(true)." 请进入种子详情页下载种子成功后更新cookie{$t}秒后继续...".PHP_EOL;
sleep(1);
} while (--$t > 0);
ff($siteName. '站点,辅种时触发第一次下载提示!');
self::$noReseed[] = $siteName;
$reseedPass = true;
}
break;
default:
// 默认站点推送给下载器种子URL链接
break;
}
// 检查switch内是否异常
if ($reseedPass) {
continue;
}
// 把拼接的种子URL推送给下载器
echo '推送种子:' . $_url . PHP_EOL;
$ret = false;
// 成功返回true
$ret = self::add($k, $url, $downloadDir);
// 按站点规范日志内容
switch ($siteName) {
case 'hdchina':
case 'hdsky':
$url = $details_url;
break;
case 'hdcity':
$url = $_url;
break;
default:
break;
}
$log = 'clients_'.$k.PHP_EOL.$downloadDir.PHP_EOL.$url.PHP_EOL.PHP_EOL;
if ($ret) {
// 成功的种子
// 操作流控参数
if (isset($configALL[$siteName]['limitRule']) && $configALL[$siteName]['limitRule']) {
$limitRule = $configALL[$siteName]['limitRule'];
if ($limitRule['count']) {
$configALL[$siteName]['limitRule']['count']--;
$configALL[$siteName]['limitRule']['time'] = time();
}
}
wlog($log, $value['info_hash'], self::$cacheHash); // 添加成功的种子以infohash为文件名写入缓存
wlog($log, 'reseedSuccess');
// 成功累加
self::$wechatMsg['reseedSuccess']++;
} else {
// 失败的种子
wlog($log, 'reseedError');
// 失败累加
self::$wechatMsg['reseedError']++;
}
}
// 当前种子辅种 结束
}
// 当前客户端辅种 结束
}
// 按客户端循环辅种 结束
}
/**
* IYUUAutoReseed做种客户端转移
*/
private static function move()
{
global $configALL;
//遍历客户端
foreach (self::$links as $k => $v) {
if (self::$move[0] == $k) {
echo "clients_".$k."是目标转移客户端,避免冲突,已跳过!".PHP_EOL.PHP_EOL;
continue;
}
if (empty(self::$links[$k])) {
echo "clients_".$k." 用户名或密码未配置,已跳过".PHP_EOL.PHP_EOL;
continue;
}
echo "正在从下载器 clients_".$k." 获取种子哈希……".PHP_EOL;
$move = []; // 客户端做种列表 传址
$hashArray = self::$links[$k]['rpc']->getList($move);
if (empty($hashArray)) {
// 失败
continue;
} else {
$infohash_Dir = $hashArray['hashString'];
// 写日志
wlog($hashArray, 'move'.$k);
}
// 前置过滤移除转移成功的hash
$rs = self::hashFilter($infohash_Dir);
if ($rs) {
echo "clients_".$k." 全部转移成功,本次无需转移!".PHP_EOL.PHP_EOL;
continue;
}
//遍历当前客户端种子
foreach ($infohash_Dir as $info_hash => $downloadDir) {
// 调用路径过滤
if (self::pathFilter($downloadDir)) {
continue;
}
// 做种实际路径与相对路径之间互转
echo '转换前:'.$downloadDir.PHP_EOL;
$downloadDir = self::pathReplace($downloadDir);
echo '转换后:'.$downloadDir.PHP_EOL;
if (is_null($downloadDir)) {
echo 'IYUU自动转移做种客户端--使用教程 https://www.iyuu.cn/archives/351/'.PHP_EOL;
die("全局配置的move数组内路径转换参数配置错误请重新配置".PHP_EOL);
}
// 种子目录:脚本要能够读取到
$path = self::$links[$k]['BT_backup'];
// 待删除种子
$torrentDelete = '';
// 获取种子原文件的实际路径
switch ($v['type']) {
case 'transmission':
// 优先使用API提供的种子路径
$torrentPath = $move[$info_hash]['torrentFile'];
$torrentDelete = $move[$info_hash]['id'];
// API提供的种子路径不存在时使用配置内指定的BT_backup路径
if (!is_file($torrentPath)) {
$torrentPath = str_replace("\\", "/", $torrentPath);
$torrentPath = $path . strrchr($torrentPath, '/');
}
break;
case 'qBittorrent':
if (empty($path)) {
echo 'IYUU自动转移做种客户端--使用教程 https://www.iyuu.cn/archives/351/'.PHP_EOL;
die("clients_".$k." 未设置种子的BT_backup目录无法完成转移");
}
$torrentPath = $path .DS. $info_hash . '.torrent';
$torrentDelete = $info_hash;
break;
default:
# code...
break;
}
if (!is_file($torrentPath)) {
echo 'IYUU自动转移做种客户端--使用教程 https://www.iyuu.cn/archives/351/'.PHP_EOL;
die("clients_".$k." 的种子文件{$torrentPath}不存在,无法完成转移!");
}
echo '存在种子:'.$torrentPath.PHP_EOL;
$torrent = file_get_contents($torrentPath);
// 正式开始转移
echo "种子已推送给下载器,正在转移做种...".PHP_EOL;
// 目标下载器类型
$rpcKey = self::$move[0];
$type = self::$links[$rpcKey]['type'];
$extra_options = array();
// 转移后,是否开始?
$extra_options['paused'] = isset($configALL['default']['move']['paused']) && $configALL['default']['move']['paused'] ? true : false;
if ($type == 'qBittorrent') {
if (isset($configALL['default']['move']['skip_check']) && $configALL['default']['move']['skip_check'] === 1) {
$extra_options['skip_checking'] = "true"; //转移成功,跳校验
}
} else {
}
// 添加转移任务成功返回true
$ret = self::add(self::$move[0], $torrent, $downloadDir, $extra_options);
/**
* 转移成功的种子写日志
*/
$log = $info_hash.PHP_EOL.$torrentPath.PHP_EOL.$downloadDir.PHP_EOL.PHP_EOL;
if ($ret) {
//转移成功时,删除做种,不删资源
if (isset($configALL['default']['move']['delete_torrent']) && $configALL['default']['move']['delete_torrent'] === 1) {
self::$links[$k]['rpc']->delete($torrentDelete);
}
// 转移成功的种子以infohash为文件名写入缓存
wlog($log, $info_hash, self::$cacheMove);
wlog($log, 'MoveSuccess'.$k);
self::$wechatMsg['MoveSuccess']++;
} else {
// 失败的种子
wlog($log, 'MoveError'.$k);
self::$wechatMsg['MoveError']++;
}
}
}
}
/**
* 过滤已转移的种子hash
*/
private static function hashFilter(&$infohash_Dir = array())
{
foreach ($infohash_Dir as $info_hash => $dir) {
if (is_file(self::$cacheMove . $info_hash.'.txt')) {
unset($infohash_Dir[$info_hash]);
echo '-------当前种子上次已成功转移,前置过滤已跳过! ' .PHP_EOL.PHP_EOL;
}
}
return empty($infohash_Dir) ? true : false;
}
/**
* 实际路径与相对路径之间互相转换
*/
private static function pathReplace($path = '')
{
global $configALL;
$type = $configALL['default']['move']['type'];
$pathArray = $configALL['default']['move']['path'];
$path = rtrim($path, DIRECTORY_SEPARATOR); // 提高Windows转移兼容性
switch ($type) {
case 1: // 减
foreach ($pathArray as $key => $val) {
if (strpos($path, $key)===0) {
return substr($path, strlen($key));
}
}
break;
case 2: // 加
foreach ($pathArray as $key => $val) {
if (strpos($path, $key)===0) { // 没用$path == $key判断是为了提高兼容性
return $val . $path;
}
}
break;
case 3: // 替换
foreach ($pathArray as $key => $val) {
if (strpos($path, $key)===0) { // 没用$path == $key判断是为了提高兼容性
return $val . substr($path, strlen($key));
}
}
break;
default: // 不变
return $path;
break;
}
return null;
}
/**
* 处理转移种子时所设置的过滤器、选择器
* @return bool true 过滤 | false 不过滤
*/
private static function pathFilter(&$path = '')
{
global $configALL;
$path = rtrim($path, DIRECTORY_SEPARATOR); // 提高Windows转移兼容性
// 转移过滤器、选择器 David/2020年7月11日
$path_filter = isset($configALL['default']['move']['path_filter']) && !empty($configALL['default']['move']['path_filter']) ? $configALL['default']['move']['path_filter'] : null;
$path_selector = isset($configALL['default']['move']['path_selector']) && !empty($configALL['default']['move']['path_selector']) ? $configALL['default']['move']['path_selector'] : null;
if (\is_null($path_filter) && \is_null($path_selector)) {
return false;
} elseif (\is_null($path_filter)) {
//选择器
if (\is_array($path_selector)) {
foreach ($path_selector as $pathName) {
if (strpos($path, $pathName)===0) { // 没用$path == $key判断是为了提高兼容性
return false;
}
}
echo '已跳过!转移选择器未匹配到:'.$path.PHP_EOL;
return true;
}
} elseif (\is_null($path_selector)) {
//过滤器
if (\is_array($path_filter)) {
foreach ($path_filter as $pathName) {
if (strpos($path, $pathName)===0) { // 没用$path == $key判断是为了提高兼容性
echo '已跳过!转移过滤器匹配到:'.$path.PHP_EOL;
return true;
}
}
return false;
}
} else {
//同时设置过滤器、选择器
if (\is_array($path_filter) && \is_array($path_selector)) {
//先过滤器
foreach ($path_filter as $pathName) {
if (strpos($path, $pathName)===0) {
echo '已跳过!转移过滤器匹配到:'.$path.PHP_EOL;
return true;
}
}
//后选择器
foreach ($path_selector as $pathName) {
if (strpos($path, $pathName)===0) {
return false;
}
}
echo '已跳过!转移选择器未匹配到:'.$path.PHP_EOL;
return true;
}
}
return false;
}
/**
* 获取站点种子的URL
* @param string $site
* @param string $url
* @return string
*/
private static function getTorrentUrl($site = '', $url = '')
{
global $configALL;
// 兼容旧配置
if (isset($configALL[$site]['passkey']) && $configALL[$site]['passkey']) {
if (empty($configALL[$site]['url_replace'])) {
$configALL[$site]['url_replace'] = array('{passkey}' => $configALL[$site]['passkey']);
}
if (empty($configALL[$site]['url_join'])) {
$configALL[$site]['url_join'] = array();
if (in_array($site, array('m-team','mocat','hdbd'))) {
if (isset($configALL[$site]['ip_type'])) {
$configALL[$site]['url_join'][] = $configALL[$site]['ip_type'].'=1';
}
$configALL[$site]['url_join'][] = 'https=1';
}
}
}
// 通用操作:替换
if (isset($configALL[$site]['url_replace']) && $configALL[$site]['url_replace']) {
$url = strtr($url, $configALL[$site]['url_replace']);
}
// 通用操作:拼接
if (isset($configALL[$site]['url_join']) && $configALL[$site]['url_join']) {
$url = $url.(strpos($url, '?') === false ? '?' : '&').implode('&', $configALL[$site]['url_join']);
}
return $url;
}
/**
* 微信模板消息拼接方法
*/
private static function wechatMessage()
{
global $configALL;
if (isset($configALL['notify_on_change']) && $configALL['notify_on_change'] && self::$wechatMsg['reseedSuccess'] == 0 && self::$wechatMsg['reseedError'] == 0) {
return;
}
$br = PHP_EOL;
$text = 'IYUU自动辅种-统计报表';
$desp = '### 版本号:'. self::VER . $br;
$desp .= '**支持站点:'.self::$wechatMsg['sitesCount']. '** [当前支持自动辅种的站点数量]' .$br;
$desp .= '**总做种:'.self::$wechatMsg['hashCount'] . '** [客户端做种的hash总数]' .$br;
$desp .= '**返回数据:'.self::$wechatMsg['reseedCount']. '** [服务器返回的可辅种数据]' .$br;
$desp .= '**成功:'.self::$wechatMsg['reseedSuccess']. '** [会把hash加入辅种缓存]' .$br;
$desp .= '**失败:'.self::$wechatMsg['reseedError']. '** [种子下载失败或网络超时引起]' .$br;
$desp .= '**重复:'.self::$wechatMsg['reseedRepeat']. '** [客户端已做种]' .$br;
$desp .= '**跳过:'.self::$wechatMsg['reseedSkip']. '** [未设置passkey]' .$br;
$desp .= '**忽略:'.self::$wechatMsg['reseedPass']. '** [成功添加存在缓存]' .$br;
// 失败详情
if (self::$wechatMsg['reseedError']) {
$desp .= '**失败详情,见 ./torrent/cache/reseedError.txt**'.$br;
}
// 重新辅种
$desp .= '**如需重新辅种,请删除 ./torrent/cachehash 辅种缓存。**'.$br;
// 移动做种
if (self::$wechatMsg['MoveSuccess'] || self::$wechatMsg['MoveError']) {
$desp .= $br.'----------'.$br;
$desp .= '**移动成功:'.self::$wechatMsg['MoveSuccess']. '** [会把hash加入移动缓存]' .$br;
$desp .= '**移动失败:'.self::$wechatMsg['MoveError']. '** [解决错误提示,可以重试]' .$br;
$desp .= '**如需重新移动,请删除 ./torrent/cachemove 移动缓存。**'.$br;
}
$desp .= $br.'*此消息将在3天后过期*。';
return ff($text, $desp);
}
/**
* 错误的种子通知服务器
*/
private static function sendNotify($error = '')
{
self::$errNotify['error'] = $error;
$notify = http_build_query(self::$errNotify);
self::$errNotify = array(
'sign' => '',
'site' => '',
'sid' => 0,
'torrent_id'=> 0,
'error' => '',
);
$res = self::$curl->get(self::$apiUrl.self::$endpoints['notify'].'?'.$notify);
$res = json_decode($res->response, true);
if (isset($res['data']['success']) && $res['data']['success']) {
echo '感谢您的参与,失效种子上报成功!!'.PHP_EOL;
}
return true;
}
/**
* 设置通知主体
*/
private static function setNotify($siteName = '', $sid = 0, $torrent_id = 0)
{
self::$errNotify = array(
'sign' => Oauth::getSign(),
'site' => $siteName,
'sid' => $sid,
'torrent_id'=> $torrent_id,
);
}
/**
* 备份功能
*/
private static function backup($key = '', $array = [])
{
$json = json_encode($array, JSON_UNESCAPED_UNICODE);
$myfile = ROOT_PATH.DS.'config'.DS.$key.'.json';
$file_pointer = @fopen($myfile, "w");
$worldsnum = @fwrite($file_pointer, $json);
@fclose($file_pointer);
return $worldsnum;
}
}

View File

@@ -1,141 +0,0 @@
<?php
/**
* Created by PhpStorm.
* User: Rhilip
* Date: 2019/4/21
* Time: 21:12
*/
class Bencode
{
/**
* Decodes a BEncoded string to the following values:
* - Dictionary (starts with d, ends with e)
* - List (starts with l, ends with e
* - Integer (starts with i, ends with e
* - String (starts with number denoting number of characters followed by : and then the string)
*
* @see https://wiki.theory.org/index.php/BitTorrentSpecification
*
* @param string $data
* @param int $pos
* @return mixed
*/
public static function decode($data, &$pos = 0)
{
$start_decode = ($pos === 0);
if ($data[$pos] === 'd') {
$pos++;
$return = [];
while ($data[$pos] !== 'e') {
$key = self::decode($data, $pos);
$value = self::decode($data, $pos);
if ($key === null || $value === null) {
break;
}
if (!is_string($key)) {
throw new RuntimeException('Invalid key type, must be string: ' . gettype($key));
}
$return[$key] = $value;
}
ksort($return);
$pos++;
} elseif ($data[$pos] === 'l') {
$pos++;
$return = [];
while ($data[$pos] !== 'e') {
$value = self::decode($data, $pos);
$return[] = $value;
}
$pos++;
} elseif ($data[$pos] === 'i') {
$pos++;
$digits = strpos($data, 'e', $pos) - $pos;
$return = substr($data, $pos, $digits);
if ($return === '-0') {
throw new RuntimeException('Cannot have integer value -0');
}
$multiplier = 1;
if ($return[0] === '-') {
$multiplier = -1;
$return = substr($return, 1);
}
if (!ctype_digit($return)) {
throw new RuntimeException('Cannot have non-digit values in integer number: ' . $return);
}
$return = $multiplier * ((int)$return);
$pos += $digits + 1;
} else {
$digits = strpos($data, ':', $pos) - $pos;
$len = (int)substr($data, $pos, $digits);
$pos += ($digits + 1);
$return = substr($data, $pos, $len);
$pos += $len;
}
if ($start_decode) {
if ($pos !== strlen($data)) {
throw new RuntimeException('Could not fully decode bencode string');
}
}
return $return;
}
/**
* @param mixed $data
* @return string
*/
public static function encode($data)
{
if (is_array($data)) {
$return = '';
$check = -1;
$list = true;
foreach ($data as $key => $value) {
if ($key !== ++$check) {
$list = false;
break;
}
}
if ($list) {
$return .= 'l';
foreach ($data as $value) {
$return .= self::encode($value);
}
} else {
$return .= 'd';
foreach ($data as $key => $value) {
$return .= self::encode(strval($key));
$return .= self::encode($value);
}
}
$return .= 'e';
} elseif (is_integer($data)) {
$return = 'i' . $data . 'e';
} else {
$return = strlen($data) . ':' . $data;
}
return $return;
}
/**
* Given a path to a file, decode the contents of it
*
* @param string $path
* @return mixed
*/
public static function load($path)
{
if (is_file($path)) {
return self::decode(file_get_contents($path));
}
return null;
}
/**
* Given a path for a file, encode the contents of it
*
* @param string $path
* @param $data
* @return mixed
*/
public static function dump($path, $data)
{
return file_put_contents($path, self::encode($data));
}
}

View File

@@ -1,313 +0,0 @@
<?php
/**
* 调试函数
*/
function p($data, $echo=true){
$str='******************************'."\n";
// 如果是boolean或者null直接显示文字否则print
if (is_bool($data)) {
$show_data=$data ? 'true' : 'false';
}elseif (is_null($data)) {
$show_data='null';
}else{
$show_data=print_r($data,true);
}
$str.=$show_data;
$str.="\n".'******************************'."\n";
if($echo){
echo $str;
return null;
}
return $str;
}
/**
* 微信推送Server酱
*/
function sc($text='', $desp='')
{
global $configALL;
$token = $configALL['sc.ftqq.com'];
$desp = ($desp=='')?date("Y-m-d H:i:s") :$desp;
$postdata = http_build_query(array(
'text' => $text,
'desp' => $desp
));
$opts = array('http' => array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postdata
));
$context = stream_context_create($opts);
$result = file_get_contents('http://sc.ftqq.com/'.$token.'.send', false, $context);
return $result;
}
/**
* 微信推送 爱语飞飞
*/
function ff($text='', $desp='')
{
global $configALL;
$token = $configALL['iyuu.cn'];
$desp = ($desp=='')?date("Y-m-d H:i:s") :$desp;
$postdata = http_build_query(array(
'text' => $text,
'desp' => $desp
));
$opts = array('http' => array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postdata
));
$context = stream_context_create($opts);
$result = file_get_contents('http://iyuu.cn/'.$token.'.send', false, $context);
return $result;
}
/**
* 微信推送 爱语飞飞
* @param array $torrent 种子数组
Array
(
[id] => 118632
[h1] => CCTV5+ 2019 ATP Men's Tennis Final 20191115B HDTV 1080i H264-HDSTV
[title] => 央视体育赛事频道 2019年ATP男子网球年终总决赛 单打小组赛 纳达尔VS西西帕斯 20191115[优惠剩余时间4时13分]
[details] => https://xxx.me/details.php?id=118632
[download] => https://xxx.me/download.php?id=118632
[filename] => 118632.torrent
[type] => 0
[sticky] => 1
[time] => Array
(
[0] => "2019-11-16 20:41:53">4时13分
[1] => "2019-11-16 14:41:53">1时<br />46分
)
[comments] => 0
[size] => 5232.64MB
[seeders] => 69
[leechers] => 10
[completed] => 93
[percentage] => 100%
[owner] => 匿名
)
*/
function send($site = '', $torrent = array())
{
$br = "\r\n";
$text = $site. ' 免费:' .$torrent['filename']. ',添加成功';
$desp = '主标题:'.$torrent['h1'] . $br;
if ( isset($torrent['title']) ) {
$desp .= '副标题:'.$torrent['title']. $br;
}
if ( isset($torrent['size']) ) {
$desp .= '大小:'.$torrent['size']. $br;
}
if ( isset($torrent['seeders']) ) {
$desp .= '做种数:'.$torrent['seeders']. $br;
}
if ( isset($torrent['leechers']) ) {
$desp .= '下载数:'.$torrent['leechers']. $br;
}
if ( isset($torrent['owner']) ) {
$desp .= '发布者:'.$torrent['owner']. $br;
}
return ff($text, $desp);
}
/**
* @brief 下载种子
* @param string $url 种子URL
* @param string $cookies 模拟登陆的cookie
* @return mixed 返回的数据
*/
function download($url, $cookies, $useragent, $method = 'GET')
{
$header = array(
"Content-Type:application/x-www-form-urlencoded",
'User-Agent: '.$useragent);
$ch = curl_init();
if($method === 'POST'){
curl_setopt($ch, CURLOPT_POST, true );
}
if(stripos($url, 'https://') !== FALSE) {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_SSLVERSION, 1);
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER,$header);
curl_setopt($ch, CURLOPT_COOKIE,$cookies);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT,60);
curl_setopt($ch, CURLOPT_TIMEOUT,600);
$data = curl_exec($ch);
$status = curl_getinfo($ch);
curl_close($ch);
if (isset($status['http_code']) && $status['http_code'] == 200) {
return $data;
}
if (isset($status['http_code']) && $status['http_code'] == 302) {
return download($status['redirect_url'], $cookies, $useragent);
}
return $data;
}
/**
* @brief 文件大小格式化为MB
* @param string $from 文件大小
* @return int 单位MB
*/
function convertToMB($from){
$number=substr($from,0,-2);
switch(strtoupper(substr($from,-2))){
case "KB":
return $number/1024;
case "MB":
return $number;
case "GB":
return $number*pow(1024,1);
case "TB":
return $number*pow(1024,2);
case "PB":
return $number*pow(1024,3);
default:
return $from;
}
}
/**
* @brief 种子过滤器
* @param string $site 站点标识
* @param array $torrent 种子数组
* Array
(
[id] => 118632
[h1] => CCTV5+ 2019 ATP Men's Tennis Final 20191115B HDTV 1080i H264-HDSTV
[title] => 央视体育赛事频道 2019年ATP男子网球年终总决赛 单打小组赛 纳达尔VS西西帕斯 20191115[优惠剩余时间4时13分]
[details] => https://xxx.me/details.php?id=118632
[download] => https://xxx.me/download.php?id=118632
[filename] => 118632.torrent
[type] => 0
[sticky] => 1
[time] => Array
(
[0] => "2019-11-16 20:41:53">4时13分
[1] => "2019-11-16 14:41:53">1时<br />46分
)
[comments] => 0
[size] => 5232.64MB
[seeders] => 69
[leechers] => 10
[completed] => 93
[percentage] => 100%
[owner] => 匿名
)
* @return bool 或 string false不过滤
*/
function filter($site = '', $torrent = array()){
global $configALL;
$config = $configALL[$site];
$filter = array();
// 读取配置
if (isset($configALL['default']['filter']) || isset($config['filter'])) {
$filter = isset($config['filter']) && $config['filter'] ? $config['filter'] : $configALL['default']['filter'];
}else {
return false;
}
$filename = $torrent['filename'];
// 兼容性
if ( empty($torrent['size']) ) {
return false;
}
// 大小过滤
$size = convertToMB($torrent['size']);
$min = isset($filter['size']['min']) ? convertToMB($filter['size']['min']) : 0;
$max = isset($filter['size']['max']) ? convertToMB($filter['size']['max']) : 2097152; //默认 2097152MB = 2TB
if ($min > $size || $size > $max) {
return $filename. ' ' .$size. 'MB被大小过滤';
}
// 兼容性
if ( empty($torrent['seeders']) ) {
return false;
}
// 种子数过滤
$seeders = $torrent['seeders'];
$min = isset($filter['seeders']['min']) ? $filter['seeders']['min'] : 1; //默认 1
$max = isset($filter['seeders']['max']) ? $filter['seeders']['max'] : 3; //默认 3
if ($min > $seeders || $seeders > $max) {
return $filename. ' 当前做种' .$seeders. '人,被过滤';
}
// 兼容性
if ( empty($torrent['leechers']) ) {
return false;
}
// 下载数过滤
$leechers = $torrent['leechers'];
$min = isset($filter['leechers']['min']) ? $filter['leechers']['min'] : 0; //默认
$max = isset($filter['leechers']['max']) ? $filter['leechers']['max'] : 30000; //默认
if ($min > $leechers || $leechers > $max) {
return $filename. ' 当前下载' .$leechers. '人,被过滤';
}
// 兼容性
if ( empty($torrent['completed']) ) {
return false;
}
// 完成数过滤
$completed = $torrent['completed'];
$min = isset($filter['completed']['min']) ? $filter['completed']['min'] : 0; //默认
$max = isset($filter['completed']['max']) ? $filter['completed']['max'] : 30000; //默认
if ($min > $completed || $completed > $max) {
return $filename. ' 已完成数' .$completed. '人,被过滤';
}
return false;
}
/**
* 奇数
*/
function oddFilter($var)
{
// 返回$var最后一个二进制位
// 为1则保留奇数的二进制的最后一位肯定是1
return($var & 1);
}
/**
* 偶数
*/
function evenFilter($var)
{
// 返回$var最后一个二进制位
// 为0则保留偶数的二进制的最后一位肯定是0
return(!($var & 1));
}
/**
* 发布员签名
* 注意同时配置iyuu.cn与secret时优先使用secret。
*/
function sign( $timestamp ){
global $configALL;
// 爱语飞飞
$token = isset($configALL['iyuu.cn']) && $configALL['iyuu.cn'] ? $configALL['iyuu.cn'] : '';
// 鉴权
$token = isset($configALL['secret']) && $configALL['secret'] ? $configALL['secret'] : $token;
return sha1($timestamp . $token);
}
/**
* @brief 分离token中的用户uid
* token算法IYUU + uid + T + sha1(openid+time+盐)
* @param string $token 用户请求token
*/
function getUid($token){
//验证是否IYUU开头strpos($token,'T')<15,token总长度小于60(40+10+5)
return (strlen($token)<60)&&(strpos($token,'IYUU')===0)&&(strpos($token,'T')<15) ? substr($token,4,strpos($token,'T')-4): false;
}

View File

@@ -1,332 +0,0 @@
<?php
/**
* @brief 文件处理
* @version 0.6
*/
/**
* @class IFile
* @brief IFile 文件处理类
*/
class IFile
{
private $resource = null; //文件资源句柄
/**
* @brief 构造函数,打开资源流,并独占锁定
* @param String $fileName 文件路径名
* @param String $mode 操作方式默认为读操作可供选择的项为r,r+,w+,w+,a,a+
* @note $mod'r' 只读方式打开,将文件指针指向文件头
* 'r+' 读写方式打开,将文件指针指向文件头
* 'w' 写入方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
* 'w+' 读写方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
* 'a' 写入方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建之。
* 'a+' 读写方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建之。
*/
function __construct($fileName,$mode='r')
{
$dirName = dirname($fileName);
$baseName = basename($fileName);
//检查并创建文件夹
self::mkdir($dirName);
$this->resource = fopen($fileName,$mode.'b');
if($this->resource)
{
flock($this->resource,LOCK_EX);
}
}
/**
* @brief 获取文件内容
* @return String 文件内容
*/
public function read()
{
$content = null;
while(!feof($this->resource))
{
$content.= fread($this->resource,1024);
}
return $content;
}
/**
* @brief 文件写入操作
* @param String $content 要写入的文件内容
* @return Int or false 写入的字符数; false:写入失败;
*/
public function write($content)
{
$worldsnum = fwrite($this->resource,$content);
$this->save();
return is_bool($worldsnum) ? false : $worldsnum;
}
/**
* @brief 清空目录下的所有文件
* @return bool false:失败; true:成功;
*/
public static function clearDir($dir)
{
if($dir[0] != '.' && is_dir($dir) && is_writable($dir))
{
$dirRes = opendir($dir);
while( false !== ($fileName = readdir($dirRes)) )
{
if($fileName[0] !== '.')
{
$fullpath = $dir.'/'.$fileName;
if(is_file($fullpath))
{
self::unlink($fullpath);
}
else
{
self::clearDir($fullpath);
rmdir($fullpath);
}
}
}
closedir($dirRes);
return true;
}
else
{
return false;
}
}
/**
* @brief 获取文件信息
* @param String $fileName 文件路径
* @return array or null array:文件信息; null:文件不存在;
*/
public static function getInfo($fileName)
{
if(is_file($fileName))
return stat($fileName);
else
return null;
}
/**
* @brief 创建文件夹
* @param String $path 路径
* @param int $chmod 文件夹权限
* @note $chmod 参数不能是字符串(加引号)否则linux会出现权限问题
*/
public static function mkdir($path,$chmod=0777)
{
return is_dir($path) or (self::mkdir(dirname($path),$chmod) and mkdir($path,$chmod));
}
/**
* @brief 复制文件
* @param String $from 源文件路径
* @param String $to 目标文件路径
* @param String $mod 操作模式c:复制(默认); x:剪切(删除$from文件)
* @return bool 操作结果 true:成功; false:失败;
*/
public static function copy($from,$to,$mode = 'c')
{
$dir = dirname($to);
//创建目录
self::mkdir($dir);
copy($from,$to);
if(is_file($to))
{
if($mode == 'x')
{
self::unlink($from);
}
return true;
}
else
{
return false;
}
}
/**
* @brief 删除文件
* @param String $fileName 文件路径
* @return bool 操作结果 false:删除失败;
*/
public static function unlink($fileName)
{
if(is_file($fileName) && is_writable($fileName))
{
return unlink($fileName);
}
else
return false;
}
/**
* @brief 删除$dir文件夹 或者 其下所有文件
* @param String $dir 文件路径
* @param bool $recursive 是否强制删除如果强制删除则递归删除该目录下的全部文件默认为false
* @return bool true:删除成功; false:删除失败;
*/
public static function rmdir($dir,$recursive = false)
{
if(is_dir($dir) && is_writable($dir))
{
//强制删除
if($recursive == true)
{
self::clearDir($dir);
return self::rmdir($dir,false);
}
//非强制删除
else
{
if(rmdir($dir))
{
return true;
}
else
{
return false;
}
}
}
}
/**
* @brief 获取文件类型
* @param String $fileName 文件名
* @return String $filetype 文件类型
* @note 如果文件不存在返回false,如果文件后缀名不在识别列表之内返回NULL对于docx及elsx格式文档识别在会出现识别为ZIP格式的错误这是office2007的bug目前尚未修复请谨慎使用
*/
public static function getFileType($fileName)
{
$filetype = null;
if(!is_file($fileName))
{
return false;
}
$fileRes = fopen($fileName,"rb");
if(!$fileRes)
{
return false;
}
$bin= fread($fileRes, 2);
fclose($fileRes);
if($bin != null)
{
$strInfo = unpack("C2chars", $bin);
$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);
$typelist = self::getTypeList();
foreach($typelist as $val)
{
if(strtolower($val[0]) == strtolower($typeCode))
{
if($val[0] == 8075)
{
return array('zip','docx','xlsx');
}
else
{
return $val[1];
}
}
}
}
return $filetype;
}
/**
* @brief 获取文件类型映射关系
* @return array 文件类型映射关系数组
*/
public static function getTypeList()
{
return array(
array('255216','jpg'),
array('13780','png'),
array('7173','gif'),
array('6677','bmp'),
array('6063','xml'),
array('60104','html'),
array('208207','xls/doc'),
array('8075','zip'),
array('8075','docx'),
array('8075','xlsx'),
array("8297","rar"),
);
}
/**
* @brief 获取文件大小
* @param String $fileName 文件名
* @return Int 文件大小的字节数,如果文件无效则返回 NULL
*/
public static function getFileSize($fileName)
{
return is_file($fileName) ? filesize($fileName):null;
}
/**
* @brief 检测文件夹是否为空
* @param String $dir 路径地址
* @return bool true:$dir为空目录; false:$dir为非空目录;
*/
public static function isEmptyDir($dir)
{
if(is_dir($dir))
{
$isEmpty = true;
$dirRes = opendir($dir);
while(false !== ($fileName = readdir($dirRes)))
{
if($fileName!='.' && $fileName!='..')
{
$isEmpty = false;
break;
}
}
closedir($dirRes);
return $isEmpty;
}
}
/**
* @brief 释放文件锁定
*/
public function save()
{
flock($this->resource,LOCK_UN);
}
/**
* @brief 获取文件扩展名
* @param String $fileName 文件名
* @return String 文件后缀名
*/
public static function getFileSuffix($fileName)
{
$fileInfoArray = pathinfo($fileName);
return $fileInfoArray['extension'];
}
/**
* @brief 析构函数,释放文件连接句柄
*/
function __destruct()
{
if(is_resource($this->resource))
{
fclose($this->resource);
}
}
}

View File

@@ -1,71 +0,0 @@
<?php
/**
* IYUU用户注册、认证
*/
use Curl\Curl;
class Oauth{
// 合作的站点
public static $sites = ['ourbits'];
// 爱语飞飞token
public static $token = '';
// 合作站点用户id
public static $user_id = 0;
// 合作站点密钥
public static $passkey = '';
// 合作站名字
public static $site = '';
/**
* 初始化配置
*/
public static function init(){
global $configALL;
foreach (self::$sites as $name) {
if (isset($configALL[$name]['passkey']) && $configALL[$name]['passkey'] && isset($configALL[$name]['id']) && $configALL[$name]['id'] ) {
self::$token = self::getSign();
self::$user_id = $configALL[$name]['id'];
self::$passkey = sha1( $configALL[$name]['passkey'] ); // 避免泄露用户passkey秘钥
self::$site = $name;
return true;
}
}
echo "-----缺少合作站点登录参数token, user_id, passkey, site \n";
echo "-----当前正在使用测试接口,功能可能会受到限制! \n\n";
return false;
}
/**
* 从配置文件内读取爱语飞飞token作为鉴权参数
*/
public static function getSign(){
global $configALL;
// 爱语飞飞
$token = isset($configALL['iyuu.cn']) && $configALL['iyuu.cn'] ? $configALL['iyuu.cn'] : '';
if (empty($token) || strlen($token)<46) {
echo "缺少辅种接口请求参数爱语飞飞token \n";
echo "请访问https://iyuu.cn 用微信扫码申请并填入配置文件config.php内。\n\n";
exit(1);
}
return $token;
}
/**
* 用户注册与登录
* 作用在服务器端实现微信用户与合作站点用户id的关联
* 参数爱语飞飞token + 合作站点用户id + sha1(合作站点密钥passkey) + 合作站点标识
*/
public static function login($apiUrl = ''){
$is_oauth = self::init();
if ( $is_oauth ) {
$curl = new Curl();
$curl->setOpt(CURLOPT_SSL_VERIFYPEER, false);
$data = [
'token' => self::$token,
'id' => self::$user_id,
'passkey'=> self::$passkey,
'site' => self::$site,
];
$res = $curl->get($apiUrl, $data);
p($res->response);
return true;
}
return false;
}
}

View File

@@ -1,368 +0,0 @@
<?php
/**
* Rpc操作类
*/
class Rpc
{
/**
* 版本号
* @var string
*/
const VER = '0.0.1';
// 下载种子的请求类型 GET POST
public static $method = 'GET';
// RPC连接池
public static $links = array();
/**
* cookie
*/
public static $cookies = '';
/**
* 浏览器 User-Agent
*/
public static $userAgent = '';
/**
* passkey
*/
public static $passkey = '';
/**
* 客户端配置
*/
public static $clients = '';
/**
* 监控目录
*/
public static $watch = '';
/**
* 种子存放路径
*/
public static $torrentDir = '';
/**
* 工作模式
*/
public static $workingMode = '';
// 站点标识
public static $site = '';
/**
* 负载均衡 控制变量
*/
public static $RPC_Key = 0;
/**
* 退出状态码
*/
public static $ExitCode = 0;
/**
* 初始化
*/
public static function init($site = '', $method = 'GET'){
global $configALL;
self::$site = $site;
self::$method = strtoupper($method);
$config = $configALL[$site];
self::$cookies = $config['cookie'];
self::$userAgent = isset($config['userAgent']) && $config['userAgent'] ? $config['userAgent'] : $configALL['default']['userAgent'];
self::$clients = isset($config['clients']) && $config['clients'] ? $config['clients'] : $configALL['default']['clients'];
self::$workingMode = isset($config['workingMode']) && $config['workingMode'] ? $config['workingMode'] : 0;
$watch = isset($config['watch']) && $config['watch'] ? $config['watch'] : $configALL['default']['watch'];
self::$watch = rtrim($watch,'/') . DS;
self::$torrentDir = TORRENT_PATH . $site . DS;
// 建立目录
IFile::mkdir(self::$torrentDir);
self::links();
}
/**
* 连接远端RPC服务器
*
* @param string
* @return array
*/
public static function links()
{
if(self::$workingMode === 1 && empty(self::$links)){
foreach ( self::$clients as $k => $v ){
// 跳过未配置的客户端
if (empty($v['username']) || empty( $v['password'])) {
unset(self::$clients[$k]);
echo "clients_".$k." 用户名或密码未配置,已跳过 \n\n";
continue;
}
try
{
switch($v['type']){
case 'transmission':
self::$links[$k]['rpc'] = new TransmissionRPC($v['host'], $v['username'], $v['password']);
$result = self::$links[$k]['rpc']->sstats();
print $v['type'].''.$v['host']." Rpc连接成功 [{$result->result}] \n";
break;
case 'qBittorrent':
self::$links[$k]['rpc'] = new qBittorrent($v['host'], $v['username'], $v['password']);
$result = self::$links[$k]['rpc']->appVersion();
print $v['type'].''.$v['host']." Rpc连接成功 [{$result}] \n";
break;
case 'uTorrent':
self::$links[$k]['rpc'] = new uTorrent($v['host'], $v['username'], $v['password']);
$result = self::$links[$k]['rpc']->getBuild();
print $v['type'].''.$v['host']." Rpc连接 [{$result}] \n";
break;
default:
echo '[ERROR] '.$v['type'];
exit(1);
break;
}
self::$links[$k]['type'] = $v['type'];
self::$links[$k]['downloadDir'] = $v['downloadDir'];
} catch (Exception $e) {
echo '[ERROR] ' . $e->getMessage() . PHP_EOL;
exit(1);
}
}
}
return true;
}
/**
* @brief 添加下载任务
* @param string $torrent 种子元数据
* @param string $save_path 保存路径
* @return bool
*/
public static function add($torrent, $save_path = '', $extra_options = array())
{
switch( (int)self::$workingMode ){
case 0: // watch默认工作模式
// 复制到watch目录
copy($torrent,$save_path);
if(is_file($save_path)){
print "********watch模式下载任务添加成功 \n\n";
return true;
}else {
print "-----watch模式下载任务添加失败!!! \n\n";
}
break;
case 1: //负载均衡模式
try
{
$is_url = false;
if( (strpos($torrent,'http://')===0) || (strpos($torrent,'https://')===0) || (strpos($torrent,'magnet:?xt=urn:btih:')===0) ){
$is_url = true;
}
// 负载均衡
$rpcKey = self::$RPC_Key;
echo '选中:负载均衡'.$rpcKey."\n";
self::rpcSelect();
// 调试
#p($result);
// 下载服务器类型 判断
$type = self::$links[$rpcKey]['type'];
switch($type){
case 'transmission':
if( $is_url ){
echo 'add';
$result = self::$links[$rpcKey]['rpc']->add( $torrent, self::$links[$rpcKey]['downloadDir'], $extra_options ); // 种子URL添加下载任务
}else{
echo 'add_metainfo';
$result = self::$links[$rpcKey]['rpc']->add_metainfo( $torrent, self::$links[$rpcKey]['downloadDir'], $extra_options ); // 种子文件添加下载任务
}
$id = $name = '';
if( isset($result->arguments->torrent_duplicate) ){
$id = $result->arguments->torrent_duplicate->id;
$name = $result->arguments->torrent_duplicate->name;
}elseif( isset($result->arguments->torrent_added) ){
$id = $result->arguments->torrent_added->id;
$name = $result->arguments->torrent_added->name;
}
if(!$id){
print "-----RPC添加种子任务失败 [{$result->result}] \n\n";
}else{
print "********RPC添加下载任务成功 [{$result->result}] (id=$id) \n\n";
// 新添加的任务,开始
self::$links[$rpcKey]['rpc']->start( $id );
return true;
}
break;
case 'qBittorrent':
if( $is_url ){
echo 'add';
$result = self::$links[$rpcKey]['rpc']->add( $torrent, self::$links[$rpcKey]['downloadDir'], $extra_options ); // 种子URL添加下载任务
}else{
echo 'add_metainfo';
$result = self::$links[$rpcKey]['rpc']->add_metainfo( $torrent, self::$links[$rpcKey]['downloadDir'], $extra_options ); // 种子文件添加下载任务
}
if ($result === 'Ok.') {
print "********RPC添加下载任务成功 [{$result}] \n\n";
return true;
} else {
print "-----RPC添加种子任务失败 [{$result}] \n\n";
}
break;
default:
echo '[ERROR] '.$type;
break;
}
} catch (Exception $e) {
die('[ERROR] ' . $e->getMessage() . PHP_EOL);
}
break;
case 2:
echo "\n\n";
# 暂未开放
break;
default:
echo "\n\n";
break;
}
return false;
}
/**
* 负载均衡 选择算法
*
* @param
* @return
*/
public static function rpcSelect()
{
$clientsConut = count(self::$clients);
if( $clientsConut > 1 ){
if( $clientsConut > (self::$RPC_Key+1) ){
self::$RPC_Key++;
}else{
self::$RPC_Key = 0;
}
}
}
/**
* @brief 种子处理函数
* @param array $data 种子数组
* Array
(
[id] => 118632
[h1] => CCTV5+ 2019 ATP Men's Tennis Final 20191115B HDTV 1080i H264-HDxxx
[title] => 央视体育赛事频道 2019年ATP男子网球年终总决赛 单打小组赛 纳达尔VS西西帕斯 20191115[优惠剩余时间4时13分]
[details] => https://XXX.me/details.php?id=118632
[download] => https://XXX.me/download.php?id=118632
[filename] => 118632.torrent
[type] => 0
[sticky] => 1
[time] => Array
(
[0] => "2019-11-16 20:41:53">4时13分
[1] => "2019-11-16 14:41:53">1时<br />46分
)
[comments] => 0
[size] => 5232.64MB
[seeders] => 69
[leechers] => 10
[completed] => 93
[percentage] => 100%
[owner] => 匿名
)
* @return
*/
public static function call($data = array())
{
foreach ($data as $key => $value) {
// 控制台打印
echo '主标题:'.$value['h1']."\n";
echo '副标题:'.$value['title']."\n";
echo '详情页:'.$value['details']."\n";
if ( $value['type'] != 0 ) {
echo "-----非免费,已忽略! \n\n";
continue;
}
if ( isset($value['hr']) && ($value['hr'] == 1) ) {
echo "-----HR种子已忽略 \n\n";
continue;
}
// 下载任务的可选参数
$extra_options = array();
// 保存的文件名
$filename = $value['id'] . '.torrent';
// 默认watch工作模式复制到此目录
$to = self::$watch . $filename;
// 种子完整存放路径
$torrentFile = self::$torrentDir . $filename;
if(is_file($torrentFile)){
$fileSize = filesize($torrentFile); //失败会返回false 或 00代表上次下载失败
if ( !empty($fileSize) ) {
//种子已经存在
echo '-----存在旧种子:'.$filename."\n\n";
continue;
}
// 删除下载错误的文件
IFile::unlink($torrentFile);
}
// 调用过滤函数
$isFilter = filter(self::$site, $value);
if ( is_string( $isFilter ) ) {
echo "-----" .$isFilter. "\n\n";
continue;
}
//种子不存在
echo '正在下载新种子... '.$value['download']." \n";
// 创建文件、下载种子以二进制写入
$content = '';
$content = download($value['download'], self::$cookies, self::$userAgent, self::$method);
#p($content);
// 文件句柄
$resource = fopen($torrentFile, "wb");
// 成功返回写入字节数失败返回false
$worldsnum = fwrite($resource, $content);
// 关闭
fclose($resource);
// 判断
if(is_bool($worldsnum)){
print "种子下载失败!!! \n\n";
IFile::unlink($torrentFile);
continue;
}else{
print "成功下载种子" . $filename . ',共计:' . $worldsnum . "字节 \n";
sleep(mt_rand(2,10));
$ret = false;
$rpcKey = self::$RPC_Key;
switch( (int)self::$workingMode ){
case 0: //默认工作模式
$ret = self::add($torrentFile, $to);
break;
case 1: //负载均衡模式
$type = self::$links[$rpcKey]['type'];
// 下载服务器类型
switch ($type) {
case 'transmission':
# code...
break;
case 'qBittorrent':
$extra_options['name'] = 'torrents';
$extra_options['filename'] = $filename;
$extra_options['autoTMM'] = 'false'; //关闭自动种子管理
break;
default:
# code...
break;
}
// 种子文件添加下载任务
$ret = self::add($content, $to, $extra_options);
break;
case 2:
echo "\n\n";
# 暂未开放
break;
default:
echo "\n\n";
break;
}
global $configALL;
if( isset($configALL['iyuu.cn']) && ($ret === true) ){
send(self::$site, $value);
}
}
}
return true;
}
}

View File

@@ -1,708 +0,0 @@
<?php
/**
* Transmission bittorrent client/daemon RPC communication class
* Copyright (C) 2010 Johan Adriaans <johan.adriaans@gmail.com>,
* Bryce Chidester <bryce@cobryce.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* PHP version specific information
* version_compare() (PHP 4 >= 4.1.0, PHP 5)
* ctype_digit() (PHP 4 >= 4.0.4, PHP 5)
* stream_context_create (PHP 4 >= 4.3.0, PHP 5)
* PHP Class support (PHP 5) (PHP 4 might work, untested)
*/
/**
* A friendly little version check...
*/
if ( version_compare( PHP_VERSION, TransmissionRPC::MIN_PHPVER, '<' ) )
die( "The TransmissionRPC class requires PHP version {TransmissionRPC::TRANSMISSIONRPC_MIN_PHPVER} or above." . PHP_EOL );
/**
* Transmission bittorrent client/daemon RPC communication class
*
* Usage example:
* <code>
* $rpc = new TransmissionRPC($rpc_url);
* $result = $rpc->add_file( $url_or_path_to_torrent, $target_folder );
* </code>
*
*/
class TransmissionRPC
{
/**
* User agent used in all http communication
*/
const HTTP_UA = 'TransmissionRPC for PHP/0.3';
/**
* Minimum PHP version required
* 5.2.10 implemented the required http stream ignore_errors option
*/
const MIN_PHPVER = '5.2.10';
/**
* The URL to the bittorent client you want to communicate with
* the port (default: 9091) can be set in you Transmission preferences
* @var string
*/
public $url = '';
/**
* If your Transmission RPC requires authentication, supply username here
* @var string
*/
public $username = '';
/**
* If your Transmission RPC requires authentication, supply password here
* @var string
*/
public $password = '';
/**
* Return results as an array, or an object (default)
* @var bool
*/
public $return_as_array = false;
/**
* Print debugging information, default is off
* @var bool
*/
public $debug = false;
/**
* Transmission RPC version
* @var int
*/
protected $rpc_version = 0;
/**
* Transmission uses a session id to prevent CSRF attacks
* @var string
*/
protected $session_id = '';
/**
* Default values for stream context
* @var array
*/
private $default_context_opts = array( 'http' => array(
'user_agent' => self::HTTP_UA,
'timeout' => '60', // Don't want to be too slow
'ignore_errors' => true, // Leave the error parsing/handling to the code
)
);
/**
* Constants for torrent status
*/
const TR_STATUS_STOPPED = 0;
const TR_STATUS_CHECK_WAIT = 1;
const TR_STATUS_CHECK = 2;
const TR_STATUS_DOWNLOAD_WAIT = 3;
const TR_STATUS_DOWNLOAD = 4;
const TR_STATUS_SEED_WAIT = 5;
const TR_STATUS_SEED = 6;
const RPC_LT_14_TR_STATUS_CHECK_WAIT = 1;
const RPC_LT_14_TR_STATUS_CHECK = 2;
const RPC_LT_14_TR_STATUS_DOWNLOAD = 4;
const RPC_LT_14_TR_STATUS_SEED = 8;
const RPC_LT_14_TR_STATUS_STOPPED = 16;
/**
* Start one or more torrents
*
* @param int|array ids A list of transmission torrent ids
*/
public function start ( $ids )
{
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
$request = array( "ids" => $ids );
return $this->request( "torrent-start", $request );
}
/**
* Stop one or more torrents
*
* @param int|array ids A list of transmission torrent ids
*/
public function stop ( $ids )
{
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
$request = array( "ids" => $ids );
return $this->request( "torrent-stop", $request );
}
/**
* Reannounce one or more torrents
*
* @param int|array ids A list of transmission torrent ids
*/
public function reannounce ( $ids )
{
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
$request = array( "ids" => $ids );
return $this->request( "torrent-reannounce", $request );
}
/**
* Verify one or more torrents
*
* @param int|array ids A list of transmission torrent ids
*/
public function verify ( $ids )
{
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
$request = array( "ids" => $ids );
return $this->request( "torrent-verify", $request );
}
/**
* Get information on torrents in transmission, if the ids parameter is
* empty all torrents will be returned. The fields array can be used to return certain
* fields. Default fields are: "id", "name", "status", "doneDate", "haveValid", "totalSize".
* See https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt for available fields
*
* @param array fields An array of return fields
* @param int|array ids A list of transmission torrent ids
*
Request:
{
"arguments": {
"fields": [ "id", "name", "totalSize" ],
"ids": [ 7, 10 ]
},
"method": "torrent-get",
"tag": 39693
}
Response:
{
"arguments": {
"torrents": [
{
"id": 10,
"name": "Fedora x86_64 DVD",
"totalSize": 34983493932,
},
{
"id": 7,
"name": "Ubuntu x86_64 DVD",
"totalSize", 9923890123,
}
]
},
"result": "success",
"tag": 39693
}
*/
public function get ( $ids = array(), $fields = array() )
{
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
if ( count( $fields ) == 0 ) $fields = array( "id", "name", "status", "doneDate", "haveValid", "totalSize" ); // Defaults
$request = array(
"fields" => $fields,
"ids" => $ids
);
return $this->request( "torrent-get", $request );
}
/**
* Set properties on one or more torrents, available fields are:
* "bandwidthPriority" | number this torrent's bandwidth tr_priority_t
* "downloadLimit" | number maximum download speed (in K/s)
* "downloadLimited" | boolean true if "downloadLimit" is honored
* "files-wanted" | array indices of file(s) to download
* "files-unwanted" | array indices of file(s) to not download
* "honorsSessionLimits" | boolean true if session upload limits are honored
* "ids" | array torrent list, as described in 3.1
* "location" | string new location of the torrent's content
* "peer-limit" | number maximum number of peers
* "priority-high" | array indices of high-priority file(s)
* "priority-low" | array indices of low-priority file(s)
* "priority-normal" | array indices of normal-priority file(s)
* "seedRatioLimit" | double session seeding ratio
* "seedRatioMode" | number which ratio to use. See tr_ratiolimit
* "uploadLimit" | number maximum upload speed (in K/s)
* "uploadLimited" | boolean true if "uploadLimit" is honored
* See https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt for more information
*
* @param array arguments An associative array of arguments to set
* @param int|array ids A list of transmission torrent ids
*/
public function set ( $ids = array(), $arguments = array() )
{
// See https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt for available fields
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
if ( !isset( $arguments['ids'] ) ) $arguments['ids'] = $ids; // Any $ids given in $arguments overrides the method parameter
return $this->request( "torrent-set", $arguments );
}
/**
* Add a new torrent
*
* Available extra options:
* key | value type & description
* ---------------------+-------------------------------------------------
* "download-dir" | string path to download the torrent to
* "filename" | string filename or URL of the .torrent file
* "metainfo" | string base64-encoded .torrent content
* "paused" | boolean if true, don't start the torrent
* "peer-limit" | number maximum number of peers
* "bandwidthPriority" | number torrent's bandwidth tr_priority_t
* "files-wanted" | array indices of file(s) to download
* "files-unwanted" | array indices of file(s) to not download
* "priority-high" | array indices of high-priority file(s)
* "priority-low" | array indices of low-priority file(s)
* "priority-normal" | array indices of normal-priority file(s)
*
* Either "filename" OR "metainfo" MUST be included.
* All other arguments are optional.
*
* @param torrent_location The URL or path to the torrent file
* @param save_path Folder to save torrent in
* @param extra options Optional extra torrent options
*/
public function add_file ( $torrent_location, $save_path = '', $extra_options = array() )
{
if(!empty($save_path)) $extra_options['download-dir'] = $save_path;
$extra_options['filename'] = $torrent_location;
return $this->request( "torrent-add", $extra_options );
}
/**
* Add a torrent using the raw torrent data
*
* @param torrent_metainfo The raw, unencoded contents (metainfo) of a torrent
* @param save_path Folder to save torrent in
* @param extra options Optional extra torrent options
*/
public function add_metainfo ( $torrent_metainfo, $save_path = '', $extra_options = array() )
{
$extra_options['download-dir'] = $save_path;
$extra_options['metainfo'] = base64_encode( $torrent_metainfo );
return $this->request( "torrent-add", $extra_options );
}
/* Add a new torrent using a file path or a URL (For backwards compatibility)
* @param torrent_location The URL or path to the torrent file
* @param save_path Folder to save torrent in
* @param extra options Optional extra torrent options
*/
public function add ( $torrent_location, $save_path = '', $extra_options = array() )
{
return $this->add_file( $torrent_location, $save_path, $extra_options );
}
/**
* Remove torrent from transmission
*
* @param bool delete_local_data Also remove local data?
* @param int|array ids A list of transmission torrent ids
*/
public function remove ( $ids, $delete_local_data = false )
{
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
$request = array(
"ids" => $ids,
"delete-local-data" => $delete_local_data
);
return $this->request( "torrent-remove", $request );
}
/**
* Move local storage location
*
* @param int|array ids A list of transmission torrent ids
* @param string target_location The new storage location
* @param string move_existing_data Move existing data or scan new location for available data
*/
public function move ( $ids, $target_location, $move_existing_data = true )
{
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
$request = array(
"ids" => $ids,
"location" => $target_location,
"move" => $move_existing_data
);
return $this->request( "torrent-set-location", $request );
}
/**
* 3.7. Renaming a Torrent's Path
*
* Method name: "torrent-rename-path"
*
* For more information on the use of this function, see the transmission.h
* documentation of tr_torrentRenamePath(). In particular, note that if this
* call succeeds you'll want to update the torrent's "files" and "name" field
* with torrent-get.
*
* Request arguments:
*
* string | value type & description
* ---------------------------------+-------------------------------------------------
* "ids" | array the torrent torrent list, as described in 3.1
* | (must only be 1 torrent)
* "path" | string the path to the file or folder that will be renamed
* "name" | string the file or folder's new name
* Response arguments: "path", "name", and "id", holding the torrent ID integer
*
* @param int|array ids A 1-element list of transmission torrent ids
* @param string path The path to the file or folder that will be renamed
* @param string name The file or folder's new name
*/
public function rename ( $ids, $path, $name )
{
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $id to an array if only a single id was passed
if ( count( $ids ) !== 1 ) {
throw new TransmissionRPCException( 'A single id is accepted', TransmissionRPCException::E_INVALIDARG );
}
$request = array(
"ids" => $ids,
"path" => $path,
"name" => $name
);
return $this->request( "torrent-rename-path", $request );
}
/**
* Retrieve session statistics
*
* @returns array of statistics
*/
public function sstats ( )
{
return $this->request( "session-stats", array() );
}
/**
* Retrieve all session variables
*
* @returns array of session information
*/
public function sget ( )
{
return $this->request( "session-get", array() );
}
/**
* Set session variable(s)
*
* @param array of session variables to set
*/
public function sset ( $arguments )
{
return $this->request( "session-set", $arguments );
}
/**
* Return the interpretation of the torrent status
*
* @param int The integer "torrent status"
* @returns string The translated meaning
*/
public function getStatusString ( $intstatus )
{
if($this->rpc_version < 14){
if( $intstatus == self::RPC_LT_14_TR_STATUS_CHECK_WAIT )
return "Waiting to verify local files";
if( $intstatus == self::RPC_LT_14_TR_STATUS_CHECK )
return "Verifying local files";
if( $intstatus == self::RPC_LT_14_TR_STATUS_DOWNLOAD )
return "Downloading";
if( $intstatus == self::RPC_LT_14_TR_STATUS_SEED )
return "Seeding";
if( $intstatus == self::RPC_LT_14_TR_STATUS_STOPPED )
return "Stopped";
}else{
if( $intstatus == self::TR_STATUS_CHECK_WAIT )
return "Waiting to verify local files";
if( $intstatus == self::TR_STATUS_CHECK )
return "Verifying local files";
if( $intstatus == self::TR_STATUS_DOWNLOAD )
return "Downloading";
if( $intstatus == self::TR_STATUS_SEED )
return "Seeding";
if( $intstatus == self::TR_STATUS_STOPPED )
return "Stopped";
if( $intstatus == self::TR_STATUS_SEED_WAIT )
return "Queued for seeding";
if( $intstatus == self::TR_STATUS_DOWNLOAD_WAIT )
return "Queued for download";
}
return "Unknown";
}
/**
* Here be dragons (Internal methods)
*/
/**
* Clean up the request array. Removes any empty fields from the request
*
* @param array array The request associative array to clean
* @returns array The cleaned array
*/
protected function cleanRequestData ( $array )
{
if ( !is_array( $array ) || count( $array ) == 0 ) return null; // Nothing to clean
setlocale( LC_NUMERIC, 'en_US.utf8' ); // Override the locale - if the system locale is wrong, then 12.34 will encode as 12,34 which is invalid JSON
foreach ( $array as $index => $value )
{
if( is_object( $value ) ) $array[$index] = $value->toArray(); // Convert objects to arrays so they can be JSON encoded
if( is_array( $value ) ) $array[$index] = $this->cleanRequestData( $value ); // Recursion
if( empty( $value ) && $value !== 0 ) // Remove empty members
{
unset( $array[$index] );
continue; // Skip the rest of the tests - they may re-add the element.
}
if( is_numeric( $value ) ) $array[$index] = $value+0; // Force type-casting for proper JSON encoding (+0 is a cheap way to maintain int/float/etc)
if( is_bool( $value ) ) $array[$index] = ( $value ? 1 : 0); // Store boolean values as 0 or 1
if( is_string( $value ) ) {
if ( mb_detect_encoding($value,"auto") !== 'UTF-8' ) {
$array[$index] = mb_convert_encoding($value, "UTF-8");
//utf8_encode( $value ); // Make sure all data is UTF-8 encoded for Transmission
}
}
}
return $array;
}
/**
* Clean up the result object. Replaces all minus(-) characters in the object properties with underscores
* and converts any object with any all-digit property names to an array.
*
* @param object The request result to clean
* @returns array The cleaned object
*/
protected function cleanResultObject ( $object )
{
// Prepare and cast object to array
$return_as_array = false;
$array = $object;
if ( !is_array( $array ) ) $array = (array) $array;
foreach ( $array as $index => $value )
{
if( is_array( $array[$index] ) || is_object( $array[$index] ) )
{
$array[$index] = $this->cleanResultObject( $array[$index] ); // Recursion
}
if ( strstr( $index, '-' ) )
{
$valid_index = str_replace( '-', '_', $index );
$array[$valid_index] = $array[$index];
unset( $array[$index] );
$index = $valid_index;
}
// Might be an array, check index for digits, if so, an array should be returned
if ( ctype_digit( (string) $index ) ) { $return_as_array = true; }
if ( empty( $value ) ) unset( $array[$index] );
}
// Return array cast to object
return $return_as_array ? $array : (object) $array;
}
/**
* 执行 rpc 请求
*
* @param $method 请求类型/方法, 详见 $this->allowMethods
* @param array $arguments 附加参数, 可选
* @return mixed
*/
protected function request($method, $arguments = array())
{
// Check the parameters
if ( !is_scalar( $method ) )
throw new TransmissionRPCException( 'Method name has no scalar value', TransmissionRPCException::E_INVALIDARG );
if ( !is_array( $arguments ) )
throw new TransmissionRPCException( 'Arguments must be given as array', TransmissionRPCException::E_INVALIDARG );
$arguments = $this->cleanRequestData( $arguments ); // Sanitize input
// Grab the X-Transmission-Session-Id if we don't have it already
if( !$this->session_id )
if( !$this->GetSessionID() )
throw new TransmissionRPCException( 'Unable to acquire X-Transmission-Session-Id', TransmissionRPCException::E_SESSIONID );
$data = array(
'method' => $method,
'arguments' => $arguments
);
$header = array(
'Content-Type: application/json',
'Authorization: Basic '.base64_encode(sprintf("%s:%s", $this->username, $this->password)),
'X-Transmission-Session-Id: '.$this->session_id
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, $this->username.':'.$this->password);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60);
curl_setopt($ch, CURLOPT_TIMEOUT, 600);
$content = curl_exec($ch);
curl_close($ch);
if (!$content) $content = json_encode(array('result' => 'failed'));
return $this->return_as_array ? json_decode( $content, true ) : $this->cleanResultObject( json_decode( $content ) ); // Return the sanitized result
}
/**
* Performs an empty GET on the Transmission RPC to get the X-Transmission-Session-Id
* and store it in $this->session_id
*
* @return string
*/
public function GetSessionID()
{
if( !$this->url )
throw new TransmissionRPCException( "Class must be initialized before GetSessionID() can be called.", TransmissionRPCException::E_INVALIDARG );
// Setup the context
$contextopts = $this->default_context_opts; // Start with the defaults
// Make sure it's blank/empty (reset)
$this->session_id = null;
// Setup authentication (if provided)
if ( $this->username && $this->password )
$contextopts['http']['header'] = sprintf( "Authorization: Basic %s\r\n", base64_encode( $this->username.':'.$this->password ) );
if( $this->debug ) echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Stream context created with options:".
PHP_EOL . print_r( $contextopts, true );
$context = stream_context_create( $contextopts ); // Create the context for this request
if ( ! $fp = @fopen( $this->url, 'r', false, $context ) ) // Open a filepointer to the data, and use fgets to get the result
throw new TransmissionRPCException( 'Unable to connect to '.$this->url, TransmissionRPCException::E_CONNECTION );
// Check the response (headers etc)
$stream_meta = stream_get_meta_data( $fp );
fclose( $fp );
if( $this->debug ) echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Stream meta info: ".
PHP_EOL . print_r( $stream_meta, true );
if( $stream_meta['timed_out'] )
throw new TransmissionRPCException( "Timed out connecting to {$this->url}", TransmissionRPCException::E_CONNECTION );
if( substr( $stream_meta['wrapper_data'][0], 9, 3 ) == "401" )
throw new TransmissionRPCException( "Invalid username/password.", TransmissionRPCException::E_AUTHENTICATION );
elseif( substr( $stream_meta['wrapper_data'][0], 9, 3 ) == "409" ) // This is what we're hoping to find
{
// Loop through the returned headers and extract the X-Transmission-Session-Id
foreach( $stream_meta['wrapper_data'] as $header )
{
if( strpos( $header, 'X-Transmission-Session-Id: ' ) === 0 )
{
if( $this->debug ) echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Session-Id header: ".
PHP_EOL . print_r( $header, true );
$this->session_id = trim( substr( $header, 27 ) );
break;
}
}
if( ! $this->session_id ) { // Didn't find a session_id
throw new TransmissionRPCException( "Unable to retrieve X-Transmission-Session-Id", TransmissionRPCException::E_SESSIONID );
}
} else {
throw new TransmissionRPCException( "Unexpected response from Transmission RPC: ".$stream_meta['wrapper_data'][0] );
}
return $this->session_id;
}
/**
* Takes the connection parameters
*
* TODO: Sanitize username, password, and URL
*
* @param string $url
* @param string $username
* @param string $password
*/
public function __construct( $url = 'http://localhost:9091/transmission/rpc', $username = null, $password = null, $return_as_array = false )
{
// server URL
$this->url = $url;
// Username & password
$this->username = $username;
$this->password = $password;
// Get the Transmission RPC_version
$this->rpc_version = self::sget()->arguments->rpc_version;
// Return As Array
$this->return_as_array = $return_as_array;
// Reset X-Transmission-Session-Id so we (re)fetch one
$this->session_id = null;
}
}
/**
* This is the type of exception the TransmissionRPC class will throw
*/
class TransmissionRPCException extends Exception
{
/**
* Exception: Invalid arguments
*/
const E_INVALIDARG = -1;
/**
* Exception: Invalid Session-Id
*/
const E_SESSIONID = -2;
/**
* Exception: Error while connecting
*/
const E_CONNECTION = -3;
/**
* Exception: Error 401 returned, unauthorized
*/
const E_AUTHENTICATION = -4;
/**
* Exception constructor
*/
public function __construct( $message = null, $code = 0, Exception $previous = null )
{
// PHP version 5.3.0 and above support Exception linking
if ( version_compare( PHP_VERSION, '5.3.0', '>=' ) )
parent::__construct( $message, $code, $previous );
else
parent::__construct( $message, $code );
}
}
?>

View File

@@ -1,255 +0,0 @@
<?php
define("UTORRENT_TORRENT_HASH", 0);
define("UTORRENT_TORRENT_STATUS", 1);
define("UTORRENT_TORRENT_NAME", 2);
define("UTORRENT_TORRENT_SIZE", 3);
define("UTORRENT_TORRENT_PROGRESS", 4);
define("UTORRENT_TORRENT_DOWNLOADED", 5);
define("UTORRENT_TORRENT_UPLOADED", 6);
define("UTORRENT_TORRENT_RATIO", 7);
define("UTORRENT_TORRENT_UPSPEED", 8);
define("UTORRENT_TORRENT_DOWNSPEED", 9);
define("UTORRENT_TORRENT_ETA", 10);
define("UTORRENT_TORRENT_LABEL", 11);
define("UTORRENT_TORRENT_PEERS_CONNECTED", 12);
define("UTORRENT_TORRENT_PEERS_SWARM", 13);
define("UTORRENT_TORRENT_SEEDS_CONNECTED", 14);
define("UTORRENT_TORRENT_SEEDS_SWARM", 15);
define("UTORRENT_TORRENT_AVAILABILITY", 16);
define("UTORRENT_TORRENT_QUEUE_POSITION", 17);
define("UTORRENT_TORRENT_REMAINING", 18);
define("UTORRENT_FILEPRIORITY_HIGH", 3);
define("UTORRENT_FILEPRIORITY_NORMAL", 2);
define("UTORRENT_FILEPRIORITY_LOW", 1);
define("UTORRENT_FILEPRIORITY_SKIP", 0);
define("UTORRENT_TYPE_INTEGER", 0);
define("UTORRENT_TYPE_BOOLEAN", 1);
define("UTORRENT_TYPE_STRING", 2);
define("UTORRENT_STATUS_STARTED", 1);
define("UTORRENT_STATUS_CHECKED", 2);
define("UTORRENT_STATUS_START_AFTER_CHECK", 4);
class uTorrent {
// class static variables
private static $base = "%s/gui/%s";
// member variables
public $host;
public $user;
public $pass;
protected $token;
protected $guid;
// constructor
function __construct($host = "", $user = "", $pass = "") {
$this->host = rtrim($host,'/');
$this->user = $user;
$this->pass = $pass;
if (!$this->getToken()) {
//handle error here, don't know how to best do this yet
die('could not get token');
}
}
// performs request
private function makeRequest($request, $decode = true, $options = array()) {
$request = preg_replace('/^\?/', '?token='.$this->token . '&', $request);
$ch = curl_init();
curl_setopt_array($ch, $options);
curl_setopt($ch, CURLOPT_URL, sprintf(self::$base, $this->host, $request));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, $this->user.":".$this->pass);
curl_setopt($ch, CURLOPT_COOKIE, "GUID=".$this->guid);
$req = curl_exec($ch);
curl_close($ch);
return ($decode ? json_decode($req, true) : $req);
}
// implodes given parameter with glue, whether it is an array or not
private function paramImplode($glue, $param) {
return $glue.implode($glue, is_array($param) ? $param : array($param));
}
// gets token, returns true on success
private function getToken() {
$url = sprintf(self::$base, $this->host, 'token.html');
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, $this->user.":".$this->pass);
curl_setopt($ch, CURLOPT_HEADER, true);
$output = curl_exec($ch);
$info = curl_getinfo($ch);
curl_close($ch);
$headers = substr($output, 0, $info['header_size']);
if (preg_match("@Set-Cookie: GUID=([^;]+);@i", $headers, $matches)) {
$this->guid = $matches[1];
}
if (preg_match('/<div id=\'token\'.+>(.*)<\/div>/', $output, $m)) {
$this->token = $m[1];
return true;
}
return false;
}
// returns the uTorrent build number
public function getBuild(){
$json = $this->makeRequest("?");
return $json['build'];
}
// returns an array of files for the specified torrent hash
// TODO:
// - (when implemented in API) allow multiple hashes to be specified
public function getFiles($hash) {
$json = $this->makeRequest("?action=getfiles&hash=".$hash);
return $json['files'];
}
// returns an array of all labels
public function getLabels(){
$json = $this->makeRequest("?list=1");
return $json['label'];
}
// returns an array of the properties for the specified torrent hash
// TODO:
// - (when implemented in API) allow multiple hashes to be specified
public function getProperties($hash) {
$json = $this->makeRequest("?action=getprops&hash=".$hash);
return $json['props'];
}
// returns an array of all settings
public function getSettings() {
$json = $this->makeRequest("?action=getsettings");
return $json['settings'];
}
// returns an array of all torrent jobs and related information
public function getTorrents() {
$json = $this->makeRequest("?list=1");
return $json['torrents'];
}
/**
* Get all the RSS favourites/filters
* @return model\Filter[]
*/
public function getRSSFilters() {
$json = $this->makeRequest("?list=1");
$filters = array();
foreach ($json['rssfilters'] as $filter) {
$filters[] = model\Filter::fromData($filter);
}
return $filters;
}
/**
* Update an RSS filter as retrieved from getRSSFilters
* @param \uTorrent\model\Filter $filter
*/
public function setRSSFilter(model\Filter $filter) {
$request = array_merge(array('action' => 'filter-update'), $filter->toParams());
return $this->makeRequest('?'.http_build_query($request));
}
/**
* Add a new RSS filter
* Requires a utorrent > 2.2.1 (not sure which version exactly)
* @param \uTorrent\model\Filter $filter
* @return int ID of the new filter
*/
public function addRSSFilter(model\Filter $filter) {
$filter->filterId = -1;
$resp = $this->setRSSFilter($filter);
if (!empty($resp['filter_ident'])) {
return $resp['filter_ident'];
} else {
return 0;
}
}
// returns true if WebUI server is online and enabled, false otherwise
public function is_online() {
return is_array($this->makeRequest("?"));
}
// sets the properties for the specified torrent hash
// TODO:
// - allow multiple hashes, properties, and values to be set simultaneously
public function setProperties($hash, $property, $value) {
$this->makeRequest("?action=setprops&hash=".$hash."&s=".$property."&v=".$value, false);
}
// sets the priorities for the specified files in the specified torrent hash
public function setPriority($hash, $files, $priority) {
$this->makeRequest("?action=setprio&hash=".$hash."&p=".$priority.$this->paramImplode("&f=", $files), false);
}
// sets the settings
// TODO:
// - allow multiple settings and values to be set simultaneously
public function setSetting($setting, $value) {
$this->makeRequest("?action=setsetting&s=".$setting."&v=".$value, false);
}
// add a file to the list
public function torrentAdd($filename, &$estring = false) {
$split = explode(":", $filename, 2);
if (count($split) > 1 && (stristr("|http|https|file|magnet|", "|".$split[0]."|") !== false)) {
$this->makeRequest("?action=add-url&s=".urlencode($filename), false);
}
elseif (file_exists($filename)) {
$json = $this->makeRequest("?action=add-file", true, array(CURLOPT_POSTFIELDS => array("torrent_file" => "@".realpath($filename))));
if (isset($json['error'])) {
if ($estring !== false) $estring = $json['error'];
return false;
}
return true;
}
else {
if ($estring !== false) $estring = "File doesn't exist!";
return false;
}
}
// force start the specified torrent hashes
public function torrentForceStart($hash) {
$this->makeRequest("?action=forcestart".$this->paramImplode("&hash=", $hash), false);
}
// pause the specified torrent hashes
public function torrentPause($hash) {
$this->makeRequest("?action=pause".$this->paramImplode("&hash=", $hash), false);
}
// recheck the specified torrent hashes
public function torrentRecheck($hash) {
$this->makeRequest("?action=recheck".$this->paramImplode("&hash=", $hash), false);
}
// start the specified torrent hashes
public function torrentStart($hash) {
$this->makeRequest("?action=start".$this->paramImplode("&hash=", $hash), false);
}
// stop the specified torrent hashes
public function torrentStop($hash) {
$this->makeRequest("?action=stop".$this->paramImplode("&hash=", $hash), false);
}
// remove the specified torrent hashes (and data, if $data is set to true)
public function torrentRemove($hash, $data = false) {
$this->makeRequest("?action=".($data ? "removedata" : "remove").$this->paramImplode("&hash=", $hash), false);
}
}

View File

@@ -0,0 +1,66 @@
<?php
/**
* Created by PhpStorm.
* User: David <367013672@qq.com>
* Date: 2020-2-14
* Time: 21:31:49
*/
namespace IYUU\Client;
abstract class AbstractClient
{
/**
* 公共方法:创建客户端实例
*/
public static function create($config = array())
{
$type = $config['type'];
$host = $config['host'];
$username = $config['username'];
$password = $config['password'];
$file = __DIR__ . DIRECTORY_SEPARATOR . $type . DIRECTORY_SEPARATOR . $type .'.php';
if (!is_file($file)) {
die($file.' 文件不存在');
}
$className = "\IYUU\Client\\" . $type . "\\" . $type;
if (class_exists($className)) {
echo $type." 客户端正在实例化!".PHP_EOL;
return new $className($host, $username, $password);
} else {
die($className.' 客户端不存在');
}
}
/**
* 查询Bittorrent客户端状态
*
* @return string
*/
abstract public function status();
/**
* 获取种子列表
* @return array(
'hash' => string json,
'sha1' => string,
'hashString '=> array
)
*/
abstract public function getList(&$move = array());
/**
* 添加种子连接
*/
abstract public function add($torrent_url, $save_path = '', $extra_options = array());
/**
* 添加种子原数据
*/
abstract public function add_metainfo($torrent_url, $save_path = '', $extra_options = array());
/**
* 删除种子
*/
abstract public function delete($hash, $deleteFiles = false);
}

View File

@@ -1,9 +1,13 @@
<?php
namespace IYUU\Client\qBittorrent;
use Curl\Curl;
use IYUU\Client\AbstractClient;
/**
* https://github.com/qbittorrent/qBittorrent/wiki/Web-API-Documentation
*/
class qBittorrent
class qBittorrent extends AbstractClient
{
private $debug;
private $url;
@@ -35,7 +39,7 @@ class qBittorrent
'1' => null,
'2' => '/api/v2/app/setPreferences'
],
'defaultSavePath' => [
'defaultSavePath' => [
'1' => null,
'2' => '/api/v2/app/defaultSavePath'
],
@@ -72,18 +76,18 @@ class qBittorrent
public function __construct($url='', $username='', $password='', $api_version = 2, $debug = false)
{
$this->debug = $debug;
$this->url = rtrim($url,'/');
$this->url = rtrim($url, '/');
$this->username = $username;
$this->password = $password;
$this->api_version = $api_version;
$this->curl = new Curl();
$this->curl->setOpt(CURLOPT_SSL_VERIFYPEER, false); // 禁止验证证书
$this->curl->setOpt(CURLOPT_SSL_VERIFYHOST, false); // 不检查证书
$this->curl->setOpt(CURLOPT_SSL_VERIFYHOST, 2); // 不检查证书
$this->curl->setOpt(CURLOPT_CONNECTTIMEOUT, 60); // 超时
$this->curl->setOpt(CURLOPT_TIMEOUT, 600); // 超时
// Authenticate and get cookie, else throw exception
if (!$this->authenticate()) {
throw new \Exception("Unable to authenticate with Web Api.");
throw new \Exception("qBittorrent Unable to authenticate with Web Api.");
}
}
@@ -130,37 +134,35 @@ class qBittorrent
*/
public function add($torrent_url, $save_path = '', $extra_options = array())
{
if(!empty($save_path)) $extra_options['savepath'] = $save_path;
if (!empty($save_path)) {
$extra_options['savepath'] = $save_path;
}
$extra_options['urls'] = $torrent_url;
#$extra_options['skip_checking'] = 'true'; //跳校验
// 关键 上传文件流 multipart/form-data【严格按照api文档编写】
$post_data = $this->buildUrls($extra_options);
#p($post_data);
// 设置请求头
$this->curl->setHeader('Content-Type','multipart/form-data; boundary='.$this->delimiter);
$this->curl->setHeader('Content-Length',strlen($post_data));
$this->curl->setHeader('Content-Type', 'multipart/form-data; boundary='.$this->delimiter);
$this->curl->setHeader('Content-Length', strlen($post_data));
return $this->postData('torrent_add', $post_data);
}
public function add_metainfo($torrent_metainfo, $save_path = '', $extra_options = array())
public function add_metainfo($torrent_metainfo, $save_path = '', $extra_options = array())
{
if(!empty($save_path)) $extra_options['savepath'] = $save_path;
if (!empty($save_path)) {
$extra_options['savepath'] = $save_path;
}
$extra_options['torrents'] = $torrent_metainfo;
#$extra_options['skip_checking'] = 'true'; //跳校验
// 关键 上传文件流 multipart/form-data【严格按照api文档编写】
$post_data = $this->buildData($extra_options);
#p($post_data);
// 设置请求头
$this->curl->setHeader('Content-Type','multipart/form-data; boundary='.$this->delimiter);
$this->curl->setHeader('Content-Length',strlen($post_data));
$this->curl->setHeader('Content-Type', 'multipart/form-data; boundary='.$this->delimiter);
$this->curl->setHeader('Content-Length', strlen($post_data));
return $this->postData('torrent_add', $post_data);
}
public function torrentDelete($hash='', $deleteFiles = false)
{
return $this->postData('torrent_delete', ['hashes' => $hash, 'deleteFiles' => $deleteFiles ? 'true':'false']);
}
public function torrentDeleteAll($deleteFiles = false)
{
$torrents = json_decode($this->torrentList());
@@ -250,14 +252,15 @@ class qBittorrent
* 拼接种子urls multipart/form-data
* https://github.com/qbittorrent/qBittorrent/wiki/Web-API-Documentation#add-new-torrent
*/
private function buildUrls($param){
private function buildUrls($param)
{
$this->delimiter = uniqid();
$eol = "\r\n";
$data = '';
// 拼接文件流
foreach ($param as $name => $content) {
$data .= "--" . $this->delimiter . $eol;
$data .= 'Content-Disposition: form-data; name' . '="' .$name. '"' . "\r\n\r\n";
$data .= 'Content-Disposition: form-data; name="' .$name. '"' . $eol . $eol;
$data .= $content . $eol;
}
$data .= "--" . $this->delimiter . "--" . $eol;
@@ -267,22 +270,81 @@ class qBittorrent
* 拼接种子上传文件流 multipart/form-data
* https://github.com/qbittorrent/qBittorrent/wiki/Web-API-Documentation#add-new-torrent
*/
private function buildData($param){
private function buildData($param)
{
$this->delimiter = uniqid();
$eol = "\r\n";
$data = '';
$torrents = $param['torrents'];
unset($param['torrents']);
// 拼接文件流
$data .= "--" . $this->delimiter . $eol
. 'Content-Disposition: form-data; ';
foreach ($param as $name => $content) {
$data.= $name . '="' . $content.'"; ';
$data .= "--" . $this->delimiter . $eol;
$data .= 'Content-Disposition: form-data; name="' .$param['name']. '"; filename="'.$param['filename'].'"' . $eol;
$data .= 'Content-Type: application/x-bittorrent' . $eol . $eol;
$data .= $param['torrents'] . $eol;
unset($param['name']);
unset($param['filename']);
unset($param['torrents']);
if (!empty($param)) {
foreach ($param as $name => $content) {
$data .= "--" . $this->delimiter . $eol;
$data .= 'Content-Disposition: form-data; name="' . $name . '"' . $eol . $eol;
$data .= $content . $eol;
}
}
$data .= $eol;
$data .= 'Content-Type: application/x-bittorrent'."\r\n\r\n";
$data .= $torrents . $eol;
$data .= "--" . $this->delimiter . "--" . $eol;
return $data;
}
/**
* 抽象方法,子类实现
*/
public function status()
{
return $this->appVersion();
}
/**
* 抽象方法,子类实现
*/
public function getList(&$move = array())
{
$result = $this->getData('torrent_list');
$res = json_decode($result, true);
if (empty($res)) {
echo "获取种子列表失败可能qBittorrent暂时无响应请稍后重试".PHP_EOL;
return array();
}
// 过滤,只保留正常做种
$res = array_filter($res, function ($v) {
if (isset($v['state']) && in_array($v['state'], array('uploading','stalledUP','pausedUP','queuedUP','checkingUP','forcedUP'))) {
return true;
}
return false;
}, ARRAY_FILTER_USE_BOTH);
if (empty($res)) {
echo "未获取到正常做种数据,请多保种,然后重试!".PHP_EOL;
return array();
}
// 提取数组hashString
$info_hash = array_column($res, 'hash');
// 升序排序
sort($info_hash);
$json = json_encode($info_hash, JSON_UNESCAPED_UNICODE);
// 去重 应该从文件读入,防止重复提交
$sha1 = sha1($json);
// 组装返回数据
$hashArray['hash'] = $json;
$hashArray['sha1'] = $sha1;
// 变换数组hashString为键
$hashArray['hashString'] = array_column($res, "save_path", 'hash');
return $hashArray;
}
/**
* 抽象方法,子类实现
*/
public function delete($hash='', $deleteFiles = false)
{
return $this->postData('torrent_delete', ['hashes' => $hash, 'deleteFiles' => $deleteFiles ? 'true':'false']);
}
}

View File

@@ -0,0 +1,48 @@
<?php
/**
* Created by PhpStorm.
* User: Rhilip
* Date: 1/17/2020
* Time: 2020
*/
namespace IYUU\Client\transmission;
/**
* This is the type of exception the TransmissionRPC class will throw
*/
class TransmissionRPCException extends \Exception
{
/**
* Exception: Invalid arguments
*/
const E_INVALIDARG = -1;
/**
* Exception: Invalid Session-Id
*/
const E_SESSIONID = -2;
/**
* Exception: Error while connecting
*/
const E_CONNECTION = -3;
/**
* Exception: Error 401 returned, unauthorized
*/
const E_AUTHENTICATION = -4;
/**
* Exception constructor
*/
public function __construct($message = null, $code = 0, \Exception $previous = null)
{
// PHP version 5.3.0 and above support Exception linking
if (version_compare(PHP_VERSION, '5.3.0', '>=')) {
parent::__construct($message, $code, $previous);
} else {
parent::__construct($message, $code);
}
}
}

View File

@@ -0,0 +1,759 @@
<?php
/**
* Transmission bittorrent client/daemon RPC communication class
* Copyright (C) 2010 Johan Adriaans <johan.adriaans@gmail.com>,
* Bryce Chidester <bryce@cobryce.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* PHP version specific information
* version_compare() (PHP 4 >= 4.1.0, PHP 5)
* ctype_digit() (PHP 4 >= 4.0.4, PHP 5)
* stream_context_create (PHP 4 >= 4.3.0, PHP 5)
* PHP Class support (PHP 5) (PHP 4 might work, untested)
*/
namespace IYUU\Client\transmission;
use IYUU\Client\AbstractClient;
/**
* A friendly little version check...
*/
if (version_compare(PHP_VERSION, '5.2.10', '<')) {
die("The TransmissionRPC class requires PHP version 5.2.10 or above." . PHP_EOL);
}
/**
* Transmission bittorrent client/daemon RPC communication class
*
* Usage example:
* <code>
* $rpc = new TransmissionRPC($rpc_url);
* $result = $rpc->add_file( $url_or_path_to_torrent, $target_folder );
* </code>
*
*/
class transmission extends AbstractClient
{
/**
* User agent used in all http communication
*/
const HTTP_UA = 'TransmissionRPC for PHP/0.3';
/**
* The URL to the bittorent client you want to communicate with
* the port (default: 9091) can be set in you Transmission preferences
* @var string
*/
public $url = '';
/**
* If your Transmission RPC requires authentication, supply username here
* @var string
*/
public $username = '';
/**
* If your Transmission RPC requires authentication, supply password here
* @var string
*/
public $password = '';
/**
* Print debugging information, default is off
* @var bool
*/
public $debug = false;
/**
* Transmission RPC version
* @var int
*/
protected $rpc_version = 0;
/**
* Transmission uses a session id to prevent CSRF attacks
* @var string
*/
protected $session_id = '';
/**
* Default values for stream context
* @var array
*/
private $default_context_opts = array(
'http' => array(
'user_agent' => self::HTTP_UA,
'timeout' => '60', // Don't want to be too slow
'ignore_errors' => true, // Leave the error parsing/handling to the code
),
"ssl"=>array(
"verify_peer"=>false,
"verify_peer_name"=>false,
)
);
/**
* Constants for torrent status
*/
const TR_STATUS_STOPPED = 0;
const TR_STATUS_CHECK_WAIT = 1;
const TR_STATUS_CHECK = 2;
const TR_STATUS_DOWNLOAD_WAIT = 3;
const TR_STATUS_DOWNLOAD = 4;
const TR_STATUS_SEED_WAIT = 5;
const TR_STATUS_SEED = 6;
const RPC_LT_14_TR_STATUS_CHECK_WAIT = 1;
const RPC_LT_14_TR_STATUS_CHECK = 2;
const RPC_LT_14_TR_STATUS_DOWNLOAD = 4;
const RPC_LT_14_TR_STATUS_SEED = 8;
const RPC_LT_14_TR_STATUS_STOPPED = 16;
/**
* Takes the connection parameters
*
* @param string $url
* @param string $username
* @param string $password
*/
public function __construct($url = 'http://127.0.0.1:9091/transmission/rpc', $username = null, $password = null)
{
$this->url = rtrim($url, '/');
$this->username = $username;
$this->password = $password;
}
/**
* Start one or more torrents
*
* @param int|array ids A list of transmission torrent ids
* @return mixed
* @throws TransmissionRPCException
*/
public function start($ids)
{
if (!is_array($ids)) {
$ids = array($ids);
}
$request = array("ids" => $ids);
return $this->request("torrent-start", $request);
}
/**
* Stop one or more torrents
*
* @param int|array ids A list of transmission torrent ids
* @return mixed
* @throws TransmissionRPCException
*/
public function stop($ids)
{
if (!is_array($ids)) {
$ids = array($ids);
}
$request = array("ids" => $ids);
return $this->request("torrent-stop", $request);
}
/**
* Reannounce one or more torrents
*
* @param int|array ids A list of transmission torrent ids
* @return mixed
* @throws TransmissionRPCException
*/
public function reannounce($ids)
{
if (!is_array($ids)) {
$ids = array($ids);
}
$request = array("ids" => $ids);
return $this->request("torrent-reannounce", $request);
}
/**
* Verify one or more torrents
*
* @param int|array ids A list of transmission torrent ids
* @return mixed
* @throws TransmissionRPCException
*/
public function verify($ids)
{
if (!is_array($ids)) {
$ids = array($ids);
}
$request = array("ids" => $ids);
return $this->request("torrent-verify", $request);
}
/**
* Get information on torrents in transmission, if the ids parameter is
* empty all torrents will be returned. The fields array can be used to return certain
* fields. Default fields are: "id", "name", "status", "doneDate", "haveValid", "totalSize".
* See https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt for available fields
*
* @param array fields An array of return fields
* @param int|array ids A list of transmission torrent ids
*
* Request:
* {
* "arguments": {
* "fields": [ "id", "name", "totalSize" ],
* "ids": [ 7, 10 ]
* },
* "method": "torrent-get",
* "tag": 39693
* }
*
* Response:
* {
* "arguments": {
* "torrents": [
* {
* "id": 10,
* "name": "Fedora x86_64 DVD",
* "totalSize": 34983493932,
* },
* {
* "id": 7,
* "name": "Ubuntu x86_64 DVD",
* "totalSize", 9923890123,
* }
* ]
* },
* "result": "success",
* "tag": 39693
* }
* @return mixed
* @throws TransmissionRPCException
*/
public function get($ids = array(), $fields = array())
{
if (!is_array($ids)) {
$ids = array($ids);
} // Convert $ids to an array if only a single id was passed
if (count($fields) == 0) {
$fields = array("id", "name", "status", "doneDate", "haveValid", "totalSize");
} // Defaults
$request = array(
"fields" => $fields,
"ids" => $ids
);
return $this->request("torrent-get", $request);
}
/**
* Set properties on one or more torrents, available fields are:
* "bandwidthPriority" | number this torrent's bandwidth tr_priority_t
* "downloadLimit" | number maximum download speed (in K/s)
* "downloadLimited" | boolean true if "downloadLimit" is honored
* "files-wanted" | array indices of file(s) to download
* "files-unwanted" | array indices of file(s) to not download
* "honorsSessionLimits" | boolean true if session upload limits are honored
* "ids" | array torrent list, as described in 3.1
* "location" | string new location of the torrent's content
* "peer-limit" | number maximum number of peers
* "priority-high" | array indices of high-priority file(s)
* "priority-low" | array indices of low-priority file(s)
* "priority-normal" | array indices of normal-priority file(s)
* "seedRatioLimit" | double session seeding ratio
* "seedRatioMode" | number which ratio to use. See tr_ratiolimit
* "uploadLimit" | number maximum upload speed (in K/s)
* "uploadLimited" | boolean true if "uploadLimit" is honored
* See https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt for more information
*
* @param array arguments An associative array of arguments to set
* @param int|array ids A list of transmission torrent ids
* @return mixed
* @throws TransmissionRPCException
*/
public function set($ids = array(), $arguments = array())
{
// See https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt for available fields
if (!is_array($ids)) {
$ids = array($ids);
} // Convert $ids to an array if only a single id was passed
if (!isset($arguments['ids'])) {
$arguments['ids'] = $ids;
} // Any $ids given in $arguments overrides the method parameter
return $this->request("torrent-set", $arguments);
}
/**
* Add a new torrent
*
* Available extra options:
* key | value type & description
* ---------------------+-------------------------------------------------
* "download-dir" | string path to download the torrent to
* "filename" | string filename or URL of the .torrent file
* "metainfo" | string base64-encoded .torrent content
* "paused" | boolean if true, don't start the torrent
* "peer-limit" | number maximum number of peers
* "bandwidthPriority" | number torrent's bandwidth tr_priority_t
* "files-wanted" | array indices of file(s) to download
* "files-unwanted" | array indices of file(s) to not download
* "priority-high" | array indices of high-priority file(s)
* "priority-low" | array indices of low-priority file(s)
* "priority-normal" | array indices of normal-priority file(s)
*
* Either "filename" OR "metainfo" MUST be included.
* All other arguments are optional.
*
* @param string $torrent_location The URL or path to the torrent file
* @param string $save_path Folder to save torrent in
* @param array $extra_options Optional extra torrent options
* @return mixed
* @throws TransmissionRPCException
*/
public function add_file($torrent_location, $save_path = '', $extra_options = array())
{
if (!empty($save_path)) {
$extra_options['download-dir'] = $save_path;
}
$extra_options['filename'] = $torrent_location;
return $this->request("torrent-add", $extra_options);
}
/* Add a new torrent using a file path or a URL (For backwards compatibility)
* @param torrent_location The URL or path to the torrent file
* @param save_path Folder to save torrent in
* @param extra options Optional extra torrent options
*/
public function add($torrent_location, $save_path = '', $extra_options = array())
{
return $this->add_file($torrent_location, $save_path, $extra_options);
}
/**
* Add a torrent using the raw torrent data
*
* @param string $torrent_metainfo The raw, unencoded contents (metainfo) of a torrent
* @param string $save_path Folder to save torrent in
* @param array $extra_options Optional extra torrent options
* @return mixed
* @throws TransmissionRPCException
*/
public function add_metainfo($torrent_metainfo, $save_path = '', $extra_options = array())
{
if (!empty($save_path)) {
$extra_options['download-dir'] = $save_path;
}
$extra_options['metainfo'] = base64_encode($torrent_metainfo);
return $this->request("torrent-add", $extra_options);
}
/**
* Remove torrent from transmission
*
* @param bool delete_local_data Also remove local data?
* @param int|array ids A list of transmission torrent ids
* @return mixed
* @throws TransmissionRPCException
*/
public function delete($ids, $delete_local_data = false)
{
if (!is_array($ids)) {
$ids = array($ids);
} // Convert $ids to an array if only a single id was passed
$request = array(
"ids" => $ids,
"delete-local-data" => $delete_local_data
);
return $this->request("torrent-remove", $request);
}
/**
* Move local storage location
*
* @param int|array ids A list of transmission torrent ids
* @param string target_location The new storage location
* @param string move_existing_data Move existing data or scan new location for available data
* @return mixed
* @throws TransmissionRPCException
*/
public function move($ids, $target_location, $move_existing_data = true)
{
if (!is_array($ids)) {
$ids = array($ids);
} // Convert $ids to an array if only a single id was passed
$request = array(
"ids" => $ids,
"location" => $target_location,
"move" => $move_existing_data
);
return $this->request("torrent-set-location", $request);
}
/**
* 3.7. Renaming a Torrent's Path
*
* Method name: "torrent-rename-path"
*
* For more information on the use of this function, see the transmission.h
* documentation of tr_torrentRenamePath(). In particular, note that if this
* call succeeds you'll want to update the torrent's "files" and "name" field
* with torrent-get.
*
* Request arguments:
*
* string | value type & description
* ---------------------------------+-------------------------------------------------
* "ids" | array the torrent torrent list, as described in 3.1
* | (must only be 1 torrent)
* "path" | string the path to the file or folder that will be renamed
* "name" | string the file or folder's new name
* Response arguments: "path", "name", and "id", holding the torrent ID integer
*
* @param int|array ids A 1-element list of transmission torrent ids
* @param string path The path to the file or folder that will be renamed
* @param string name The file or folder's new name
* @return mixed
* @throws TransmissionRPCException
*/
public function rename($ids, $path, $name)
{
if (!is_array($ids)) {
$ids = array($ids);
} // Convert $id to an array if only a single id was passed
if (count($ids) !== 1) {
throw new TransmissionRPCException('A single id is accepted', TransmissionRPCException::E_INVALIDARG);
}
$request = array(
"ids" => $ids,
"path" => $path,
"name" => $name
);
return $this->request("torrent-rename-path", $request);
}
/**
* Retrieve session statistics
*
* @returns array of statistics
*/
public function sstats()
{
return $this->request("session-stats", array());
}
/**
* Retrieve all session variables
*
* @returns array of session information
*/
public function sget()
{
return $this->request("session-get", array());
}
/**
* Set session variable(s)
*
* @param array of session variables to set
* @return mixed
* @throws TransmissionRPCException
*/
public function sset($arguments)
{
return $this->request("session-set", $arguments);
}
/**
* Return the interpretation of the torrent status
*
* @param int The integer "torrent status"
* @returns string The translated meaning
* @return string
*/
public function getStatusString($intstatus)
{
if ($this->rpc_version < 14) {
if ($intstatus == self::RPC_LT_14_TR_STATUS_CHECK_WAIT) {
return "Waiting to verify local files";
}
if ($intstatus == self::RPC_LT_14_TR_STATUS_CHECK) {
return "Verifying local files";
}
if ($intstatus == self::RPC_LT_14_TR_STATUS_DOWNLOAD) {
return "Downloading";
}
if ($intstatus == self::RPC_LT_14_TR_STATUS_SEED) {
return "Seeding";
}
if ($intstatus == self::RPC_LT_14_TR_STATUS_STOPPED) {
return "Stopped";
}
} else {
if ($intstatus == self::TR_STATUS_CHECK_WAIT) {
return "Waiting to verify local files";
}
if ($intstatus == self::TR_STATUS_CHECK) {
return "Verifying local files";
}
if ($intstatus == self::TR_STATUS_DOWNLOAD) {
return "Downloading";
}
if ($intstatus == self::TR_STATUS_SEED) {
return "Seeding";
}
if ($intstatus == self::TR_STATUS_STOPPED) {
return "Stopped";
}
if ($intstatus == self::TR_STATUS_SEED_WAIT) {
return "Queued for seeding";
}
if ($intstatus == self::TR_STATUS_DOWNLOAD_WAIT) {
return "Queued for download";
}
}
return "Unknown";
}
/**
* Here be dragons (Internal methods)
*/
/**
* Clean up the request array. Removes any empty fields from the request
*
* @param array array The request associative array to clean
* @returns array The cleaned array
* @return array|null
*/
protected function cleanRequestData($array)
{
if (!is_array($array) || count($array) == 0) {
return null;
} // Nothing to clean
setlocale(LC_NUMERIC, 'en_US.utf8'); // Override the locale - if the system locale is wrong, then 12.34 will encode as 12,34 which is invalid JSON
foreach ($array as $index => $value) {
if (is_object($value)) {
$array[$index] = $value->toArray();
} // Convert objects to arrays so they can be JSON encoded
if (is_array($value)) {
$array[$index] = $this->cleanRequestData($value);
} // Recursion
if (empty($value) && $value !== 0) { // Remove empty members
unset($array[$index]);
continue; // Skip the rest of the tests - they may re-add the element.
}
if (is_numeric($value)) {
$array[$index] = $value + 0;
} // Force type-casting for proper JSON encoding (+0 is a cheap way to maintain int/float/etc)
if (is_bool($value)) {
$array[$index] = ($value ? 1 : 0);
} // Store boolean values as 0 or 1
if (is_string($value)) {
$type = mb_detect_encoding($value, "auto");
if ($type !== 'UTF-8') {
$array[$index] = mb_convert_encoding($value, "UTF-8");
//utf8_encode( $value ); // Make sure all data is UTF-8 encoded for Transmission
}
}
}
return $array;
}
/**
* 执行 rpc 请求
*
* @param string $method 请求类型/方法, 详见 $this->allowMethods
* @param array $arguments 附加参数, 可选
* @return array
* @throws TransmissionRPCException
*/
protected function request($method, $arguments = array())
{
// Check the parameters
if (!is_scalar($method)) {
throw new TransmissionRPCException('Method name has no scalar value', TransmissionRPCException::E_INVALIDARG);
}
if (!is_array($arguments)) {
throw new TransmissionRPCException('Arguments must be given as array', TransmissionRPCException::E_INVALIDARG);
}
$arguments = $this->cleanRequestData($arguments); // Sanitize input
// Grab the X-Transmission-Session-Id if we don't have it already
if (!$this->session_id) {
if (!$this->GetSessionID()) {
throw new TransmissionRPCException('Unable to acquire X-Transmission-Session-Id', TransmissionRPCException::E_SESSIONID);
}
}
$data = array(
'method' => $method,
'arguments' => $arguments
);
$header = array(
'Content-Type: application/json',
'Authorization: Basic ' . base64_encode(sprintf("%s:%s", $this->username, $this->password)),
'X-Transmission-Session-Id: ' . $this->session_id
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, $this->username . ':' . $this->password);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60);
curl_setopt($ch, CURLOPT_TIMEOUT, 600);
$content = curl_exec($ch);
curl_close($ch);
if (!$content) {
$content = array('result' => 'failed');
}
return json_decode($content, true);
}
/**
* Performs an empty GET on the Transmission RPC to get the X-Transmission-Session-Id
* and store it in $this->session_id
*
* @return string
* @throws TransmissionRPCException
*/
public function GetSessionID()
{
if (!$this->url) {
throw new TransmissionRPCException("Class must be initialized before GetSessionID() can be called.", TransmissionRPCException::E_INVALIDARG);
}
// Setup the context
$contextopts = $this->default_context_opts; // Start with the defaults
// Make sure it's blank/empty (reset)
$this->session_id = null;
// Setup authentication (if provided)
if ($this->username && $this->password) {
$contextopts['http']['header'] = sprintf("Authorization: Basic %s\r\n", base64_encode($this->username . ':' . $this->password));
}
if ($this->debug) {
echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Stream context created with options:" .
PHP_EOL . print_r($contextopts, true);
}
$context = stream_context_create($contextopts); // Create the context for this request
if (!$fp = @fopen($this->url, 'r', false, $context)) { // Open a filepointer to the data, and use fgets to get the result
throw new TransmissionRPCException('Unable to connect to ' . $this->url, TransmissionRPCException::E_CONNECTION);
}
// Check the response (headers etc)
$stream_meta = stream_get_meta_data($fp);
fclose($fp);
if ($this->debug) {
echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Stream meta info: " .
PHP_EOL . print_r($stream_meta, true);
}
if ($stream_meta['timed_out']) {
throw new TransmissionRPCException("Timed out connecting to {$this->url}", TransmissionRPCException::E_CONNECTION);
}
if (substr($stream_meta['wrapper_data'][0], 9, 3) == "401") {
throw new TransmissionRPCException("Invalid username/password.", TransmissionRPCException::E_AUTHENTICATION);
} elseif (substr($stream_meta['wrapper_data'][0], 9, 3) == "409") { // This is what we're hoping to find
// Loop through the returned headers and extract the X-Transmission-Session-Id
foreach ($stream_meta['wrapper_data'] as $header) {
if (strpos($header, 'X-Transmission-Session-Id: ') === 0) {
if ($this->debug) {
echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Session-Id header: " .
PHP_EOL . print_r($header, true);
}
$this->session_id = trim(substr($header, 27));
break;
}
}
if (!$this->session_id) { // Didn't find a session_id
throw new TransmissionRPCException("Unable to retrieve X-Transmission-Session-Id", TransmissionRPCException::E_SESSIONID);
}
} else {
throw new TransmissionRPCException("Unexpected response from Transmission RPC: " . $stream_meta['wrapper_data'][0]);
}
return $this->session_id;
}
/**
* 抽象方法,子类实现
*/
public function status()
{
$rs = $this->sstats();
return isset($rs['result']) ? $rs['result'] : 'error';
}
/**
* 抽象方法,子类实现
*/
public function getList(&$move = array())
{
$ids = array();
$fields = array( "id", "status", "name", "hashString", "downloadDir", "torrentFile" );
$res = $this->get($ids, $fields);
if (isset($res['result']) && $res['result'] == 'success') {
// 成功
} else {
// 失败
echo "获取种子列表失败可能transmission暂时无响应请稍后重试".PHP_EOL;
return array();
}
if (empty($res['arguments']['torrents'])) {
echo "未获取到正常做种数据,请多保种,然后重试!".PHP_EOL;
return array();
}
$res = $res['arguments']['torrents'];
// 过滤,只保留正常做种
$res = array_filter($res, function ($v) {
return isset($v['status']) && $v['status']===6;
}, ARRAY_FILTER_USE_BOTH);
if (empty($res)) {
echo "未获取到正常做种数据,请多保种,然后重试!".PHP_EOL;
return array();
}
// 提取数组hashString
$info_hash = array_column($res, 'hashString');
// 升序排序
sort($info_hash);
$json = json_encode($info_hash, JSON_UNESCAPED_UNICODE);
// 去重 应该从文件读入,防止重复提交
$sha1 = sha1($json);
// 组装返回数据
$hashArray['hash'] = $json;
$hashArray['sha1'] = $sha1;
// 变换数组hashString为键
$hashArray['hashString'] = array_column($res, "downloadDir", 'hashString');
$move = array_column($res, null, 'hashString');
return $hashArray;
}
}

295
app/Library/IFile.php Normal file
View File

@@ -0,0 +1,295 @@
<?php
namespace IYUU\Library;
/**
* @class IFile
* @brief IFile 文件处理类
*/
class IFile
{
private $resource = null; //文件资源句柄
/**
* @brief 构造函数,打开资源流,并独占锁定
* @param String $fileName 文件路径名
* @param String $mode 操作方式默认为读操作可供选择的项为r,r+,w+,w+,a,a+
* @note $mod'r' 只读方式打开,将文件指针指向文件头
* 'r+' 读写方式打开,将文件指针指向文件头
* 'w' 写入方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
* 'w+' 读写方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
* 'a' 写入方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建之。
* 'a+' 读写方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建之。
*/
public function __construct($fileName, $mode='r')
{
$dirName = dirname($fileName);
$baseName = basename($fileName);
//检查并创建文件夹
self::mkdir($dirName);
$this->resource = fopen($fileName, $mode.'b');
if ($this->resource) {
flock($this->resource, LOCK_EX);
}
}
/**
* @brief 获取文件内容
* @return String 文件内容
*/
public function read()
{
$content = null;
while (!feof($this->resource)) {
$content.= fread($this->resource, 1024);
}
return $content;
}
/**
* @brief 文件写入操作
* @param String $content 要写入的文件内容
* @return Int or false 写入的字符数; false:写入失败;
*/
public function write($content)
{
$worldsnum = fwrite($this->resource, $content);
$this->save();
return is_bool($worldsnum) ? false : $worldsnum;
}
/**
* @brief 清空目录下的所有文件
* @return bool false:失败; true:成功;
*/
public static function clearDir($dir)
{
if ($dir[0] != '.' && is_dir($dir) && is_writable($dir)) {
$dirRes = opendir($dir);
while (false !== ($fileName = readdir($dirRes))) {
if ($fileName[0] !== '.') {
$fullpath = $dir.'/'.$fileName;
if (is_file($fullpath)) {
self::unlink($fullpath);
} else {
self::clearDir($fullpath);
rmdir($fullpath);
}
}
}
closedir($dirRes);
return true;
} else {
return false;
}
}
/**
* @brief 获取文件信息
* @param String $fileName 文件路径
* @return array or null array:文件信息; null:文件不存在;
*/
public static function getInfo($fileName)
{
if (is_file($fileName)) {
return stat($fileName);
} else {
return null;
}
}
/**
* @brief 创建文件夹
* @param String $path 路径
* @param int $chmod 文件夹权限
* @note $chmod 参数不能是字符串(加引号)否则linux会出现权限问题
*/
public static function mkdir($path, $chmod=0777)
{
return is_dir($path) or (self::mkdir(dirname($path), $chmod) and mkdir($path, $chmod));
}
/**
* @brief 复制文件
* @param String $from 源文件路径
* @param String $to 目标文件路径
* @param String $mod 操作模式c:复制(默认); x:剪切(删除$from文件)
* @return bool 操作结果 true:成功; false:失败;
*/
public static function copy($from, $to, $mode = 'c')
{
$dir = dirname($to);
//创建目录
self::mkdir($dir);
copy($from, $to);
if (is_file($to)) {
if ($mode == 'x') {
self::unlink($from);
}
return true;
} else {
return false;
}
}
/**
* @brief 删除文件
* @param String $fileName 文件路径
* @return bool 操作结果 false:删除失败;
*/
public static function unlink($fileName)
{
if (is_file($fileName) && is_writable($fileName)) {
return unlink($fileName);
} else {
return false;
}
}
/**
* @brief 删除$dir文件夹 或者 其下所有文件
* @param String $dir 文件路径
* @param bool $recursive 是否强制删除如果强制删除则递归删除该目录下的全部文件默认为false
* @return bool true:删除成功; false:删除失败;
*/
public static function rmdir($dir, $recursive = false)
{
if (is_dir($dir) && is_writable($dir)) {
//强制删除
if ($recursive == true) {
self::clearDir($dir);
return self::rmdir($dir, false);
}
//非强制删除
else {
if (rmdir($dir)) {
return true;
} else {
return false;
}
}
}
}
/**
* @brief 获取文件类型
* @param String $fileName 文件名
* @return String $filetype 文件类型
* @note 如果文件不存在返回false,如果文件后缀名不在识别列表之内返回NULL对于docx及elsx格式文档识别在会出现识别为ZIP格式的错误这是office2007的bug目前尚未修复请谨慎使用
*/
public static function getFileType($fileName)
{
$filetype = null;
if (!is_file($fileName)) {
return false;
}
$fileRes = fopen($fileName, "rb");
if (!$fileRes) {
return false;
}
$bin= fread($fileRes, 2);
fclose($fileRes);
if ($bin != null) {
$strInfo = unpack("C2chars", $bin);
$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);
$typelist = self::getTypeList();
foreach ($typelist as $val) {
if (strtolower($val[0]) == strtolower($typeCode)) {
if ($val[0] == 8075) {
return array('zip','docx','xlsx');
} else {
return $val[1];
}
}
}
}
return $filetype;
}
/**
* @brief 获取文件类型映射关系
* @return array 文件类型映射关系数组
*/
public static function getTypeList()
{
return array(
array('255216','jpg'),
array('13780','png'),
array('7173','gif'),
array('6677','bmp'),
array('6063','xml'),
array('60104','html'),
array('208207','xls/doc'),
array('8075','zip'),
array('8075','docx'),
array('8075','xlsx'),
array("8297","rar"),
);
}
/**
* @brief 获取文件大小
* @param String $fileName 文件名
* @return Int 文件大小的字节数,如果文件无效则返回 NULL
*/
public static function getFileSize($fileName)
{
return is_file($fileName) ? filesize($fileName):null;
}
/**
* @brief 检测文件夹是否为空
* @param String $dir 路径地址
* @return bool true:$dir为空目录; false:$dir为非空目录;
*/
public static function isEmptyDir($dir)
{
if (is_dir($dir)) {
$isEmpty = true;
$dirRes = opendir($dir);
while (false !== ($fileName = readdir($dirRes))) {
if ($fileName!='.' && $fileName!='..') {
$isEmpty = false;
break;
}
}
closedir($dirRes);
return $isEmpty;
}
}
/**
* @brief 释放文件锁定
*/
public function save()
{
flock($this->resource, LOCK_UN);
}
/**
* @brief 获取文件扩展名
* @param String $fileName 文件名
* @return String 文件后缀名
*/
public static function getFileSuffix($fileName)
{
$fileInfoArray = pathinfo($fileName);
return $fileInfoArray['extension'];
}
/**
* @brief 析构函数,释放文件连接句柄
*/
public function __destruct()
{
if (is_resource($this->resource)) {
fclose($this->resource);
}
}
}

100
app/Library/Oauth.php Normal file
View File

@@ -0,0 +1,100 @@
<?php
namespace IYUU\Library;
use Curl\Curl;
/**
* IYUU用户注册、认证
*/
class Oauth
{
// 合作的站点
public static $sites = ['ourbits','hddolby','hdhome','pthome','moecat'];
// 爱语飞飞token
public static $token = '';
// 合作站点用户id
public static $user_id = 0;
// 合作站点密钥
public static $passkey = '';
// 合作站名字
public static $site = '';
// 登录缓存路径
public static $SiteLoginCache = ROOT_PATH.DS.'config'.DS.'siteLoginCache_{}.json';
/**
* 从配置文件内读取爱语飞飞token作为鉴权参数
*/
public static function getSign()
{
global $configALL;
// 爱语飞飞
$token = isset($configALL['iyuu.cn']) && $configALL['iyuu.cn'] ? $configALL['iyuu.cn'] : '';
if (empty($token) || strlen($token)<46) {
echo "缺少辅种接口请求参数爱语飞飞token \n";
echo "请访问https://iyuu.cn 用微信扫码申请并填入配置文件config.php内。\n\n";
exit(1);
}
return $token;
}
/**
* 用户注册与登录
* 作用在服务器端实现微信用户与合作站点用户id的关联
* 参数爱语飞飞token + 合作站点用户id + sha1(合作站点密钥passkey) + 合作站点标识
*/
public static function login($apiUrl = '', $sites = array())
{
global $configALL;
// 云端下发合作的站点标识
self::$sites = $sites ? $sites : self::$sites;
$ret = false;
self::$token = self::getSign();
foreach (self::$sites as $name) {
if (is_file(str_replace('{}', $name, self::$SiteLoginCache))) {
// 存在鉴权缓存
$ret = true;
continue;
}
if (isset($configALL[$name]['passkey']) && $configALL[$name]['passkey'] && isset($configALL[$name]['id']) && $configALL[$name]['id']) {
self::$user_id = $configALL[$name]['id'];
self::$passkey = sha1($configALL[$name]['passkey']); // 避免泄露用户passkey秘钥
self::$site = $name;
$curl = new Curl();
$curl->setOpt(CURLOPT_SSL_VERIFYPEER, false);
$data = [
'token' => self::$token,
'id' => self::$user_id,
'passkey'=> self::$passkey,
'site' => self::$site,
];
$res = $curl->get($apiUrl, $data);
p($res->response);
$rs = json_decode($res->response, true);
if (isset($rs['ret']) && $rs['ret'] == 200 && isset($rs['data']['success']) && $rs['data']['success']) {
self::setSiteLoginCache($name, $rs);
$ret = true;
} else {
$msg = isset($rs['msg']) && $rs['msg'] ? $rs['msg'] : '远端服务器无响应,请稍后重试!';
$msg = isset($rs['data']['errmsg']) && $rs['data']['errmsg'] ? $rs['data']['errmsg'] : $msg;
echo $msg . PHP_EOL;
}
} else {
echo $name.'合作站点参数配置不完整请同时填写passkey和用户id。' . PHP_EOL;
echo '合作站点鉴权配置请查阅https://www.iyuu.cn/archives/337/'. PHP_EOL. PHP_EOL;
}
}
return $ret;
}
/**
* 写鉴权成功缓存
* @desc 作用:减少对服务器请求,跳过鉴权提示信息;
*/
private static function setSiteLoginCache($key = '', $array = [])
{
$json = json_encode($array, JSON_UNESCAPED_UNICODE);
$myfile = str_replace('{}', $key, self::$SiteLoginCache);
$file_pointer = @fopen($myfile, "w");
$worldsnum = @fwrite($file_pointer, $json);
@fclose($file_pointer);
}
}

269
app/Library/Table.php Normal file
View File

@@ -0,0 +1,269 @@
<?php
namespace IYUU\Library;
/**
* Created by PhpStorm.
* User: 大卫
* Date: 2020-1-19
* Time: 17:44
*/
class Table
{
const ALIGN_LEFT = 1;
const ALIGN_RIGHT = 0;
const ALIGN_CENTER = 2;
/**
* 头信息数据
* @var array
*/
protected $header = [];
/**
* 头部对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER
* @var int
*/
protected $headerAlign = 1;
/**
* 表格数据(二维数组)
* @var array
*/
protected $rows = [];
/**
* 单元格对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER
* @var int
*/
protected $cellAlign = 1;
/**
* 单元格宽度信息
* @var array
*/
protected $colWidth = [];
/**
* 表格输出样式
* @var string
*/
protected $style = 'default';
/**
* 表格样式定义
* @var array
*/
protected $format = [
'compact' => [],
'default' => [
'top' => ['+', '-', '+', '+'],
'cell' => ['|', ' ', '|', '|'],
'middle' => ['+', '-', '+', '+'],
'bottom' => ['+', '-', '+', '+'],
'cross-top' => ['+', '-', '-', '+'],
'cross-bottom' => ['+', '-', '-', '+'],
],
'markdown' => [
'top' => [' ', ' ', ' ', ' '],
'cell' => ['|', ' ', '|', '|'],
'middle' => ['|', '-', '|', '|'],
'bottom' => [' ', ' ', ' ', ' '],
'cross-top' => ['|', ' ', ' ', '|'],
'cross-bottom' => ['|', ' ', ' ', '|'],
],
'borderless' => [
'top' => ['=', '=', ' ', '='],
'cell' => [' ', ' ', ' ', ' '],
'middle' => ['=', '=', ' ', '='],
'bottom' => ['=', '=', ' ', '='],
'cross-top' => ['=', '=', ' ', '='],
'cross-bottom' => ['=', '=', ' ', '='],
],
'box' => [
'top' => ['┌', '─', '┬', '┐'],
'cell' => ['│', ' ', '│', '│'],
'middle' => ['├', '─', '┼', '┤'],
'bottom' => ['└', '─', '┴', '┘'],
'cross-top' => ['├', '─', '┴', '┤'],
'cross-bottom' => ['├', '─', '┬', '┤'],
],
'box-double' => [
'top' => ['╔', '═', '╤', '╗'],
'cell' => ['║', ' ', '│', '║'],
'middle' => ['╠', '─', '╪', '╣'],
'bottom' => ['╚', '═', '╧', '╝'],
'cross-top' => ['╠', '═', '╧', '╣'],
'cross-bottom' => ['╠', '═', '╤', '╣'],
],
];
/**
* 设置表格头信息 以及对齐方式
* @access public
* @param array $header 要输出的Header信息
* @param int $align 对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER
* @return void
*/
public function setHeader($header, $align = self::ALIGN_LEFT)
{
$this->header = $header;
$this->headerAlign = $align;
$this->checkColWidth($header);
}
/**
* 设置输出表格数据 及对齐方式
* @access public
* @param array $rows 要输出的表格数据(二维数组)
* @param int $align 对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER
* @return void
*/
public function setRows($rows, $align = self::ALIGN_LEFT)
{
$this->rows = $rows;
$this->cellAlign = $align;
foreach ($rows as $row) {
$this->checkColWidth($row);
}
}
/**
* 检查列数据的显示宽度
* @access public
* @param mixed $row 行数据
* @return void
*/
protected function checkColWidth($row)
{
if (is_array($row)) {
foreach ($row as $key => $cell) {
if (!isset($this->colWidth[$key]) || strlen($cell) > $this->colWidth[$key]) {
$this->colWidth[$key] = strlen($cell);
}
}
}
}
/**
* 增加一行表格数据
* @access public
* @param mixed $row 行数据
* @param bool $first 是否在开头插入
* @return void
*/
public function addRow($row, $first = false)
{
if ($first) {
array_unshift($this->rows, $row);
} else {
$this->rows[] = $row;
}
$this->checkColWidth($row);
}
/**
* 设置输出表格的样式
* @access public
* @param string $style 样式名
* @return void
*/
public function setStyle($style)
{
$this->style = isset($this->format[$style]) ? $style : 'default';
}
/**
* 输出分隔行
* @access public
* @param string $pos 位置
* @return string
*/
protected function renderSeparator($pos)
{
$style = $this->getStyle($pos);
$array = [];
foreach ($this->colWidth as $width) {
$array[] = str_repeat($style[1], $width + 2);
}
return $style[0] . implode($style[2], $array) . $style[3] . PHP_EOL;
}
/**
* 输出表格头部
* @access public
* @return string
*/
protected function renderHeader()
{
$style = $this->getStyle('cell');
$content = $this->renderSeparator('top');
foreach ($this->header as $key => $header) {
$array[] = ' ' . str_pad($header, $this->colWidth[$key], $style[1], $this->headerAlign);
}
if (!empty($array)) {
$content .= $style[0] . implode(' ' . $style[2], $array) . ' ' . $style[3] . PHP_EOL;
if ($this->rows) {
$content .= $this->renderSeparator('middle');
}
}
return $content;
}
protected function getStyle($style)
{
if ($this->format[$this->style]) {
$style = $this->format[$this->style][$style];
} else {
$style = [' ', ' ', ' ', ' '];
}
return $style;
}
/**
* 输出表格
* @access public
* @param array $dataList 表格数据
* @return string
*/
public function render($dataList = [])
{
if ($dataList) {
$this->setRows($dataList);
}
// 输出头部
$content = $this->renderHeader();
$style = $this->getStyle('cell');
if ($this->rows) {
foreach ($this->rows as $row) {
if (is_string($row) && '-' === $row) {
$content .= $this->renderSeparator('middle');
} elseif (is_scalar($row)) {
$content .= $this->renderSeparator('cross-top');
$array = str_pad($row, 3 * (count($this->colWidth) - 1) + array_reduce($this->colWidth, function ($a, $b) {
return $a + $b;
}));
$content .= $style[0] . ' ' . $array . ' ' . $style[3] . PHP_EOL;
$content .= $this->renderSeparator('cross-bottom');
} else {
$array = [];
foreach ($row as $key => $val) {
$array[] = ' ' . str_pad($val, $this->colWidth[$key], ' ', $this->cellAlign);
}
$content .= $style[0] . implode(' ' . $style[2], $array) . ' ' . $style[3] . PHP_EOL;
}
}
}
$content .= $this->renderSeparator('bottom');
return $content;
}
}

View File

@@ -1,352 +0,0 @@
<?php
/**
* 技术讨论及后续更新请加入QQ群
群名称IYUU自动辅种交流
QQ群号859882209
* 手动配置方法请查看https://www.iyuu.cn/archives/324/
*/
return array(
// 1.爱语飞飞 微信通知配置
'iyuu.cn' => 'IYUU',
// 2.server酱 微信通知配置
'sc.ftqq.com' => '',
// 3.发布员鉴权
'secret' => '',
// 4.全局默认配置
'default' => array(
// 5.【必须配置】浏览器UA打开http://demo.iyuu.cn 复制过来即可
'userAgent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36',
// 6.【自动辅种必须配置】全局客户端设置(条目不够可以复制,用不到的请删除)
'clients' => array(
// 全局客户端设置 开始
array(
'type' => 'transmission', // 支持transmission、qBittorrent
'host' => 'http://127.0.0.1:9091/transmission/rpc',
'username' => '',
'password' => '',
//'move' =>array(
// 'type' => 2, // 0保持不变1减2加 3直接替换
// 'path' =>array(
// '/sda1' => '/volume1',
// ),
//),
),
// (条目不够可以复制,用不到的请删除)
array(
'type' => 'qBittorrent', // 支持transmission、qBittorrent
'host' => 'http://www.baidu.com:8083',
'username' => '',
'password' => '',
),
// 全局客户端设置 结束
),
'CONNECTTIMEOUT'=> 60,
'TIMEOUT' => 600,
),
/**
* 以下为各站点的独立配置(互不影响、互不冲突)
* 自动辅种需要配置各站的passkey没有配置passkey的站点会自动跳过
*/
// m-team 序号1
'm-team' => array(
// 14.m-team的cookie 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => 'tp=',
// 15.m-team的passkey 【必须配置】
'passkey' => '',
// 种子Tracker的IP地址选择 可选ipv4ipv6
'ip_type' => 'ipv4',
),
// keepfrds 序号2
'keepfrds' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// ourbits 序号3
'ourbits' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'id' => 0, // 用户ID
'is_vip' => 0, // 是否具有VIP或特殊权限0 普通1 VIP
),
// HDSky 序号4
'hdsky' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// pter 序号5
'pter' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// tjupt 序号6
'tjupt' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// hdhome 序号7
'hdhome' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// btschool 序号8
'btschool' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// PTHome 序号9
'pthome' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// hddolby 序号10
'hddolby' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// TorrentCCF 序号11
'torrentccf' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// PTMSG 序号12
'ptmsg' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// MoeCat 序号13
'moecat' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
// 种子Tracker的IP地址选择 可选ipv4ipv6
'ip_type' => 'ipv4',
),
// totheglory 序号14
'ttg' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// nanyangpt 序号15
'nanyangpt' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// springsunday.net 序号16
'ssd' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// yingk 序号17
'yingk' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// hdcity 序号18
'hdcity' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置 cuhash
'passkey' => '',
),
// 52pt.site 序号19
'52pt' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// brobits.cc 序号20
'brobits' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// www.beitai.pt 序号21
'beitai' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// pt.eastgame.org 序号22
'eastgame' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// pt.soulvoice.club 序号23
'soulvoice' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// chdbits 序号24
'chdbits' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// leaguehd 序号25
'leaguehd' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// ptsbao.club 序号26
'ptsbao' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// hdchina 序号27
'hdchina' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// hdarea 序号28
'hdarea' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// hdtime 序号29
'hdtime' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// 1ptba 序号30
'1ptba' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// hd4fans 序号31
'hd4fans' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// hdbug 序号32
'hdbug' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// opencd 序号33
'opencd' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// hdstreet 序号34
'hdstreet' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// joyhd 序号35
'joyhd' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// dmhy 序号36
'dmhy' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// upxin 序号37
'upxin' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// oshen 序号38
'oshen' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// discfan 序号39
'discfan' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// hdzone 序号40
'hdzone' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// cnscg 序号41
'cnscg' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// nicept 序号42
'nicept' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
),
// 配置文件结束
);

View File

@@ -1 +0,0 @@
<?php return '20191224.1010';

458
app/helper.php Normal file
View File

@@ -0,0 +1,458 @@
<?php
use IYUU\Library\IFile;
use IYUU\Library\Table;
/**
* 调试函数
* @param $data
* @param bool $echo
* @return string|null
*/
function p($data, $echo=true)
{
$str='******************************'."\n";
// 如果是boolean或者null直接显示文字否则print
if (is_bool($data)) {
$show_data=$data ? 'true' : 'false';
} elseif (is_null($data)) {
$show_data='null';
} else {
$show_data=print_r($data, true);
}
$str.=$show_data;
$str.="\n".'******************************'."\n";
if ($echo) {
echo $str;
return null;
}
return $str;
}
/**
* 微信推送Server酱
* @param string $text
* @param string $desp
* @return false|string
*/
function sc($text='', $desp='')
{
global $configALL;
$token = $configALL['sc.ftqq.com'];
$desp = ($desp=='')?date("Y-m-d H:i:s") :$desp;
$postdata = http_build_query(array(
'text' => $text,
'desp' => $desp
));
$opts = array('http' => array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postdata
));
$context = stream_context_create($opts);
$result = file_get_contents('http://sc.ftqq.com/'.$token.'.send', false, $context);
return $result;
}
/**
* 微信推送 爱语飞飞
* @param string $text
* @param string $desp
* @return false|string
*/
function ff($text='', $desp='')
{
global $configALL;
$token = $configALL['iyuu.cn'];
$desp = ($desp=='')?date("Y-m-d H:i:s") :$desp;
$postdata = http_build_query(array(
'text' => $text,
'desp' => $desp
));
$opts = array('http' => array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postdata
));
$context = stream_context_create($opts);
$result = file_get_contents('http://iyuu.cn/'.$token.'.send', false, $context);
return $result;
}
/**
* 微信推送 爱语飞飞
* @param string $site
* @param array $torrent 种子数组
* Array
(
[id] => 118632
[h1] => CCTV5+ 2019 ATP Men's Tennis Final 20191115B HDTV 1080i H264-HDSTV
[title] => 央视体育赛事频道 2019年ATP男子网球年终总决赛 单打小组赛 纳达尔VS西西帕斯 20191115[优惠剩余时间4时13分]
[details] => https://xxx.me/details.php?id=118632
[download] => https://xxx.me/download.php?id=118632
[filename] => 118632.torrent
[type] => 0
[sticky] => 1
[time] => Array
(
[0] => "2019-11-16 20:41:53">4时13分
[1] => "2019-11-16 14:41:53">1时<br />46分
)
[comments] => 0
[size] => 5232.64MB
[seeders] => 69
[leechers] => 10
[completed] => 93
[percentage] => 100%
[owner] => 匿名
)
* @return false|string
*/
function send($site = '', $torrent = array())
{
$br = "\r\n";
$text = $site. ' 免费:' .$torrent['filename']. ',添加成功';
$desp = '主标题:'.$torrent['h1'] . $br;
if (isset($torrent['title'])) {
$desp .= '副标题:'.$torrent['title']. $br;
}
if (isset($torrent['size'])) {
$desp .= '大小:'.$torrent['size']. $br;
}
if (isset($torrent['seeders'])) {
$desp .= '做种数:'.$torrent['seeders']. $br;
}
if (isset($torrent['leechers'])) {
$desp .= '下载数:'.$torrent['leechers']. $br;
}
if (isset($torrent['owner'])) {
$desp .= '发布者:'.$torrent['owner']. $br;
}
return ff($text, $desp);
}
/**
* @brief 下载种子
* @param string $url 种子URL
* @param string $cookies 模拟登陆的cookie
* @param $useragent
* @param string $method
* @return mixed 返回的数据
*/
function download($url, $cookies='', $useragent='', $method = 'GET')
{
$header = array(
"Content-Type:application/x-www-form-urlencoded",
'User-Agent: '.$useragent);
$ch = curl_init();
if ($method === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
}
if (stripos($url, 'https://') === 0) {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSLVERSION, 1);
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_COOKIE, $cookies);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60);
curl_setopt($ch, CURLOPT_TIMEOUT, 600);
$data = curl_exec($ch);
$status = curl_getinfo($ch);
curl_close($ch);
if (isset($status['http_code']) && $status['http_code'] == 200) {
return $data;
}
if (isset($status['http_code']) && $status['http_code'] == 302) {
return download($status['redirect_url'], $cookies, $useragent);
}
return $data;
}
/**
* @brief 文件大小格式化为MB
* @param string $from 文件大小
* @return int 单位MB
*/
function convertToMB($from)
{
$number=substr($from, 0, -2);
switch (strtoupper(substr($from, -2))) {
case "KB":
return $number/1024;
case "MB":
return $number;
case "GB":
return $number*pow(1024, 1);
case "TB":
return $number*pow(1024, 2);
case "PB":
return $number*pow(1024, 3);
default:
return $from;
}
}
/**
* 字节数Byte转换为KB、MB、GB、TB
*
*/
function getFilesize($num)
{
$p = 0;
$format='bytes';
if ($num>0 && $num<1024) {
$p = 0;
return number_format($num).' '.$format;
}
if ($num>=1024 && $num<pow(1024, 2)) {
$p = 1;
$format = 'KB';
}
if ($num>=pow(1024, 2) && $num<pow(1024, 3)) {
$p = 2;
$format = 'MB';
}
if ($num>=pow(1024, 3) && $num<pow(1024, 4)) {
$p = 3;
$format = 'GB';
}
if ($num>=pow(1024, 4) && $num<pow(1024, 5)) {
$p = 3;
$format = 'TB';
}
$num /= pow(1024, $p);
return number_format($num, 2).$format;
}
/**
* @brief 种子过滤器
* @param string $site 站点标识
* @param array $torrent 种子数组
* Array
(
[id] => 118632
[h1] => CCTV5+ 2019 ATP Men's Tennis Final 20191115B HDTV 1080i H264-HDSTV
[title] => 央视体育赛事频道 2019年ATP男子网球年终总决赛 单打小组赛 纳达尔VS西西帕斯 20191115[优惠剩余时间4时13分]
[details] => https://xxx.me/details.php?id=118632
[download] => https://xxx.me/download.php?id=118632
[filename] => 118632.torrent
[type] => 0
[sticky] => 1
[time] => Array
(
[0] => "2019-11-16 20:41:53">4时13分
[1] => "2019-11-16 14:41:53">1时<br />46分
)
[comments] => 0
[size] => 5232.64MB
[seeders] => 69
[leechers] => 10
[completed] => 93
[percentage] => 100%
[owner] => 匿名
)
* @return bool 或 string false不过滤
*/
function filter($site = '', $torrent = array())
{
global $configALL;
$config = $configALL[$site];
$filter = array();
// 读取配置
if (isset($configALL['default']['filter']) || isset($config['filter'])) {
$filter = isset($config['filter']) && $config['filter'] ? $config['filter'] : $configALL['default']['filter'];
} else {
return false;
}
$filename = $torrent['filename'];
// 兼容性
if (empty($torrent['size'])) {
return false;
}
// 大小过滤
$size = convertToMB($torrent['size']);
$min = isset($filter['size']['min']) ? convertToMB($filter['size']['min']) : 0;
$max = isset($filter['size']['max']) ? convertToMB($filter['size']['max']) : 2097152; //默认 2097152MB = 2TB
if ($min > $size || $size > $max) {
return $filename. ' ' .$size. 'MB被大小过滤';
}
// 兼容性
if (empty($torrent['seeders'])) {
return false;
}
// 种子数过滤
$seeders = $torrent['seeders'];
$min = isset($filter['seeders']['min']) ? $filter['seeders']['min'] : 1; //默认 1
$max = isset($filter['seeders']['max']) ? $filter['seeders']['max'] : 3; //默认 3
if ($min > $seeders || $seeders > $max) {
return $filename. ' 当前做种' .$seeders. '人,被过滤';
}
// 兼容性
if (empty($torrent['leechers'])) {
return false;
}
// 下载数过滤
$leechers = $torrent['leechers'];
$min = isset($filter['leechers']['min']) ? $filter['leechers']['min'] : 0; //默认
$max = isset($filter['leechers']['max']) ? $filter['leechers']['max'] : 30000; //默认
if ($min > $leechers || $leechers > $max) {
return $filename. ' 当前下载' .$leechers. '人,被过滤';
}
// 兼容性
if (empty($torrent['completed'])) {
return false;
}
// 完成数过滤
$completed = $torrent['completed'];
$min = isset($filter['completed']['min']) ? $filter['completed']['min'] : 0; //默认
$max = isset($filter['completed']['max']) ? $filter['completed']['max'] : 30000; //默认
if ($min > $completed || $completed > $max) {
return $filename. ' 已完成数' .$completed. '人,被过滤';
}
return false;
}
/**
* 日志记录函数
*/
function wlog($data='', $name = '', $path = '')
{
// 数据转换
if (is_bool($data)) {
$show_data=$data ? 'true' : 'false';
} elseif (is_null($data)) {
$show_data='null';
} else {
$show_data=print_r($data, true);
}
// 写入日志
$dir = $path===''? TORRENT_PATH . 'cache' . DS : $path;
IFile::mkdir($dir);
$myfile = $dir.$name.'.txt';
$file_pointer = @fopen($myfile, "a");
$worldsnum = @fwrite($file_pointer, $show_data);
@fclose($file_pointer);
return $worldsnum;
}
//PHP stdClass Object转array
function object_array($array)
{
if (is_object($array)) {
$array = (array)$array;
}
if (is_array($array)) {
foreach ($array as $key=>$value) {
$array[$key] = object_array($value);
}
}
return $array;
}
/**
* 奇数
*/
function oddFilter($var)
{
// 返回$var最后一个二进制位
// 为1则保留奇数的二进制的最后一位肯定是1
return($var & 1);
}
/**
* 偶数
*/
function evenFilter($var)
{
// 返回$var最后一个二进制位
// 为0则保留偶数的二进制的最后一位肯定是0
return(!($var & 1));
}
/**
* 发布员签名
* 注意同时配置iyuu.cn与secret时优先使用secret。
*/
function sign($timestamp)
{
global $configALL;
// 爱语飞飞
$token = isset($configALL['iyuu.cn']) && $configALL['iyuu.cn'] ? $configALL['iyuu.cn'] : '';
// 鉴权
$token = isset($configALL['secret']) && $configALL['secret'] ? $configALL['secret'] : $token;
return sha1($timestamp . $token);
}
/**
* @brief 分离token中的用户uid
* token算法IYUU + uid + T + sha1(openid+time+盐)
* @param string $token 用户请求token
*/
function getUid($token)
{
//验证是否IYUU开头strpos($token,'T')<15,token总长度小于60(40+10+5)
return (strlen($token)<60)&&(strpos($token, 'IYUU')===0)&&(strpos($token, 'T')<15) ? substr($token, 4, strpos($token, 'T')-4): false;
}
/**
* 显示支持的站点列表
*/
function ShowTableSites($dir = 'Protocols', $filter = array())
{
// 过滤的文件
switch ($dir) {
case 'Protocols':
$filter = ['axxxx','decodeBase'];
break;
case 'Rss':
$filter = ['AbstractRss'];
break;
default:
# code...
break;
}
$data = [];
$i = $j = $k = 0; //i列、j序号、k行
foreach (glob(APP_PATH.$dir.DS.'*.php') as $key => $start_file) {
$start_file = str_replace("\\", "/", $start_file);
$offset = strripos($start_file, '/');
if ($offset===false) {
$start_file = substr($start_file, 0, -4);
} else {
$start_file = substr($start_file, $offset+1, -4);
}
// 过滤示例、过滤解码接口
if (in_array($start_file, $filter)) {
continue;
}
// 控制多少列
if ($i > 4) {
$k++;
$i = 0;
}
$i++;
$j++;
$data[$k][] = $j.". ".$start_file;
}
//输出表格
$table = new Table();
$table->setRows($data);
echo($table->render());
}
/**
* 是否win平台
* @return bool
*/
function isWin()
{
return (DIRECTORY_SEPARATOR == '\\') ? true : false;
}

View File

@@ -1,42 +0,0 @@
<?php
//----------------------------------
// 公共入口文件
//----------------------------------
// 定义目录
defined('ROOT_PATH') or define("ROOT_PATH", dirname(__DIR__));
defined('APP_PATH') or define('APP_PATH', __DIR__);
define('DS', DIRECTORY_SEPARATOR);
define('TORRENT_PATH', APP_PATH.DS.'torrent'.DS);
// 严格开发模式
error_reporting( E_ALL );
#ini_set('display_errors', 1);
// 永不超时
ini_set('max_execution_time', 0);
set_time_limit(0);
// 内存限制,如果外面设置的内存比 /etc/php/php-cli.ini 大,就不要设置了
if (intval(ini_get("memory_limit")) < 1024)
{
ini_set('memory_limit', '1024M');
}
if( PHP_SAPI != 'cli' )
{
exit("You must run the CLI environment\n");
}
// 设置时区
date_default_timezone_set('Asia/Shanghai');
// 系统配置
if( file_exists( APP_PATH."/config/config.php" ) )
{
// 配置(全局变量)
$configALL = require_once APP_PATH."/config/config.php";
}else{
// 示例配置
$configALL = require_once APP_PATH . '/config/config.sample.php';
}
require_once ROOT_PATH . '/vendor/autoload.php';

24
backup.sh Normal file
View File

@@ -0,0 +1,24 @@
#!/bin/sh
# 传入的种子备份参数
if [ $1 ]; then
AppName=$1
fi
if [ $2 ]; then
torrentDir=$2
fi
# 脚本当前目录
pwddir=$(cd $(dirname $0); pwd)
# 当前日期
DATE=$(date +%Y%m%d)
# 备份在当前目录
backupdir=$pwddir"/"$AppName$DATE
echo "种子备份目录:"$backupdir
mkdir $backupdir -p
# 种子目录
torrentDir=$torrentDir"/*"
# 备份
cp -rf $torrentDir $backupdir
# 成功提示
echo "ok";

View File

@@ -1,10 +1,24 @@
{
"require": {
"owner888/phpspider": "^2.1",
"curl/curl": "^2.2"
},
"autoload": {
"classmap":["app/Class"],
"files": ["app/Class/Function.php"]
"name": "ledccn/iyuuautoreseed",
"description": "IYUU自动辅种工具目前能对国内大部分的PT站点自动辅种支持下载器集群支持多盘位支持多下载目录支持远程连接等。",
"type": "project",
"keywords": ["iyuu", "reseed"],
"homepage": "https://github.com/ledccn/IYUUAutoReseed",
"authors": [
{
"name": "David"
}
],
"require": {
"ext-json": "*",
"ext-mbstring": "*",
"ext-curl": "*",
"curl/curl": "^2.2"
},
"autoload": {
"psr-4": {
"IYUU\\": "app/"
},
"files": ["app/helper.php"]
}
}

60
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "32806a4860870f6306b69cf349584387",
"content-hash": "10281f19c929443b7db18d1ab159ec63",
"packages": [
{
"name": "curl/curl",
@@ -66,58 +66,6 @@
"dot"
],
"time": "2018-12-04T19:47:03+00:00"
},
{
"name": "owner888/phpspider",
"version": "v2.1.6",
"source": {
"type": "git",
"url": "https://github.com/owner888/phpspider.git",
"reference": "e6021148adec201418c16ba26f39bc013ba5b4d9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/owner888/phpspider/zipball/e6021148adec201418c16ba26f39bc013ba5b4d9",
"reference": "e6021148adec201418c16ba26f39bc013ba5b4d9",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=5.5.0"
},
"suggest": {
"ext-pcntl、ext-redis": "For better performance. "
},
"type": "library",
"autoload": {
"psr-4": {
"phpspider\\": "./"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Seatle Yang",
"email": "seatle@foxmail.com",
"homepage": "http://www.phpspider.org",
"role": "Developer"
}
],
"description": "The PHPSpider Framework.",
"homepage": "http://www.phpspider.org",
"keywords": [
"framework",
"phpspider"
],
"time": "2018-08-15T08:04:29+00:00"
}
],
"packages-dev": [],
@@ -126,6 +74,10 @@
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform": {
"ext-json": "*",
"ext-mbstring": "*",
"ext-curl": "*"
},
"platform-dev": []
}

659
config/config.sample.php Normal file
View File

@@ -0,0 +1,659 @@
<?php
/**
* 技术讨论及后续更新请加入QQ群
群名称IYUU自动辅种交流
QQ群号859882209、931954050
* IYUU自动辅种工具-【安装篇】如何下载最新源码? https://www.iyuu.cn/archives/338/
* IYUU自动辅种工具-【安装篇】Windows之git https://www.iyuu.cn/archives/367/
* IYUU自动辅种工具-【安装篇】群晖Linux之git https://www.iyuu.cn/archives/372/
* IYUU自动辅种工具-【安装篇】小钢炮手把手教程 https://www.iyuu.cn/archives/386/
* IYUU自动辅种工具--最简配置(所有平台通用教程) https://www.iyuu.cn/archives/324/
* IYUU自动辅种工具--合作站点鉴权配置说明 https://www.iyuu.cn/archives/337/
* IYUU自动下载种子--之RSS订阅使用教程 https://www.iyuu.cn/archives/349/
* IYUU自动转移做种客户端--使用教程 https://www.iyuu.cn/archives/351/
脚本仓库GIT下载法
git clone https://gitee.com/ledc/IYUUAutoReseed.git
cd IYUUAutoReseed
php ./iyuu.php
*/
return array(
// 有变化才发送通知(辅种成功 + 失败 > 0
'notify_on_change' => false,
// 1.【必须配置】爱语飞飞 微信通知请访问https://iyuu.cn 用微信扫码申请
'iyuu.cn' => 'IYUU',
// 2.全局默认配置
'default' => array(
// 3.【必须配置】浏览器UA打开http://demo.iyuu.cn 复制过来即可
'userAgent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.1303.189 Safari/537.36',
// 4.【自动辅种必须配置】全局客户端设置(条目不够可以复制)
'clients' => array(
// 全局客户端设置 开始
# 开始
array(
'type' => 'transmission', // 支持transmission、qBittorrent
'host' => 'http://127.0.0.1:9091/transmission/rpc', // 警告注意transmission/rpc这段别动你只需要修改 127.0.0.1:9091
'username' => 'null',
'password' => 'null',
'BT_backup' => '/var/lib/transmission/torrents', // 移动做种:如果脚本与当前客户端不在一台机器,必须配置
'move' => 0, // 0不移动1移动并辅种2移动且只在当前客户端辅种
),
# 结束
# 开始
array(
'type' => 'qBittorrent', // 支持transmission、qBittorrent
'host' => 'http://127.0.0.1:8083',
'username' => 'admin',
'password' => '',
'root_folder'=> 1, // 0不创建根目录1创建根目录
'BT_backup' => 'C:\Users\ASUS\AppData\Local\qBittorrent\BT_backup', // 移动做种必须配置Linux搜索方法find / -name BT_backup
'move' => 0, // 0不移动1移动并辅种2移动且只在当前客户端辅种
),
# 结束
// 全局客户端设置 结束
),
// 5.移动做种必须配置
'move' =>array(
'type' => 0, // 0保持不变1减2加3替换
'path' =>array(
// 当前路径 => 目标路径
'/downloads' => '/volume1',
),
'path_filter'=> array(), //转移过滤器:不转移此路径内文件
'path_selector' => array(), //转移选择器:只转移此路径内文件(为空时,全转移) 【优先级:过滤器 选择器】
'paused' => 0, //转移成功自动开始任务0开始1暂停
'skip_check' => 0, //转移成功跳校验0不跳、1跳校验
'delete_torrent' => 0, //转移成功删除当前做种0不删除、1删除
),
// 6.RSS工作模式
'workingMode' => 0,
// 7.监控目录
'watch' => '/volume1/downloads',
// 8.RSS过滤参数配置
'filter' => array(
'size'=>array(
'min' => '1GB',
'max' => '280GB',
),
'seeders'=>array(
'min' => 1,
'max' => 3,
),
'leechers'=>array(
'min' => 0,
'max' => 10000,
),
'completed'=>array(
'min' => 0,
'max' => 10000,
),
),
'CONNECTTIMEOUT'=> 60,
'TIMEOUT' => 600,
),
// 9.server酱 微信通知配置
'sc.ftqq.com' => '',
// 10.发布员鉴权
'secret' => '',
/**
* 以下为各站点的独立配置(互不影响、互不冲突)
* 自动辅种需要配置各站的passkey没有配置passkey的站点会自动跳过
*/
// ourbits
'ourbits' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'id' => 0, // 用户ID(不是用户名)
'url_replace' => array(),
'url_join' => array(
//'ipv6=1', // 种子Tracker的IP地址选择 可选ipv4ipv6
//'https=1',
),
'limitRule' => array(
'count' => 20, // 每次辅种20个
'sleep' => 15, // 最少休眠15秒
),
'workingMode' => 0,
'watch' => '/root/downloads',
'filter' => array(
'size'=>array(
'min' => '1GB',
'max' => '280GB',
),
),
),
// hddolby
'hddolby' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'id' => 0, // 用户ID(不是用户名)
'url_replace' => array(),
'url_join' => array(),
'limitRule' => array(
'count' => 20, // 每次辅种20个
'sleep' => 5, // 最少休眠5秒
),
),
// hdhome
'hdhome' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'id' => 0, // 用户ID(不是用户名)
'url_replace' => array(),
'url_join' => array(),
'limitRule' => array(
'count' => 20, // 每次辅种20个
'sleep' => 5, // 最少休眠5秒
),
),
// PTHome
'pthome' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'id' => 0, // 用户ID(不是用户名)
'url_replace' => array(),
'url_join' => array(),
'limitRule' => array(
'count' => 20, // 每次辅种20个
'sleep' => 5, // 最少休眠5秒
),
),
// MoeCat
'moecat' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'id' => 0, // 用户ID(不是用户名)
'url_replace' => array(),
'url_join' => array(
//'ipv6=1', // 种子Tracker的IP地址选择 可选ipv4ipv6
'https=1',
),
'limitRule' => array(
'count' => 20, // 每次辅种20个
'sleep' => 15, // 最少休眠15秒
),
),
// m-team
'm-team' => array(
// 14.m-team的cookie 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => 'tp=',
// 15.m-team的passkey 【必须配置】
'passkey' => '',
'url_replace' => array(),
'url_join' => array(
//'ipv6=1', // 种子Tracker的IP地址选择 可选ipv4ipv6
'https=1',
),
'clients' => array(
array(
'type' => 'transmission', // 支持transmission、qBittorrent
'host' => 'http://127.0.0.1:9091/transmission/rpc', // 警告注意transmission/rpc这段别动你只需要修改 127.0.0.1:9091
'username' => '',
'password' => '',
'downloadDir'=> '',
),
),
'workingMode' => 1,
'watch' => '',
'filter' => array(
'size'=>array(
'min' => '1GB',
'max' => '280GB',
),
),
),
// keepfrds
'keepfrds' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
'workingMode' => 1,
'watch' => '',
'filter' => array(
'size'=>array(
'min' => '1GB',
'max' => '280GB',
),
),
),
// pter
'pter' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// tjupt
'tjupt' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// btschool
'btschool' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// HDSky
'hdsky' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'limitRule' => array(
'count' => 20, // 每次辅种50个
'sleep' => 20, // 最少休眠15秒
),
'url_replace' => array(),
'url_join' => array(),
),
// TorrentCCF
'torrentccf' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// PTMSG
'ptmsg' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// totheglory
'ttg' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
// 如果需要rss订阅必须配置
'url_replace' => array(),
'url_join' => array(),
'rss' => '',
),
// nanyangpt
'nanyangpt' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// springsunday.net
'ssd' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
'limitRule' => array(
'count' => 20, // 每次辅种20个
'sleep' => 15, // 最少休眠15秒
),
),
// yingk
'yingk' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// hdcity
'hdcity' => array(
// 必须配置
'cookie' => '',
// 如果需要自动辅种必须配置cuhash
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// 52pt.site
'52pt' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// brobits
'brobits' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// beitai
'beitai' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// eastgame
'eastgame' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// soulvoice
'soulvoice' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// chdbits
'chdbits' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// leaguehd
'leaguehd' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// ptsbao
'ptsbao' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// hdchina
'hdchina' => array(
// 必须配置
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
'limitRule' => array(
'count' => 10, // 每次辅种10个
'sleep' => 5, // 最少休眠15秒
),
),
// hdarea
'hdarea' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// hdtime
'hdtime' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// 1ptba
'1ptba' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// hd4fans
'hd4fans' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// hddisk.life
'hdbug' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// opencd皇后
'opencd' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// hdstreet
'hdstreet' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// joyhd
'joyhd' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// dmhy幼儿园
'dmhy' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// hdu
'upxin' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// oshen
'oshen' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// discfan港知堂
'discfan' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// hdzone
'hdzone' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// nicept老师
'nicept' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// hdbd伊甸园
'hdbd' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(
//'ipv6=1', // 种子Tracker的IP地址选择 可选ipv4ipv6
//'https=1',
),
),
// byr北邮
'byr' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// CCFBits
'ccfbits' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// hdbits
'hdbits' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// PTPBD
'ptpbd' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// HD-T
'hd-torrents' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// skyeysnow天雪
'skyeysnow' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// pt.sjtu葡萄
'pt' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
'limitRule' => array(
'count' => 20, // 每次辅种20个
'sleep' => 20, // 最少休眠20秒
),
),
// hdroute
'hdroute' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// haidan
'haidan' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// hdfans
'hdfans' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'url_replace' => array(),
'url_join' => array(),
),
// 配置结束,后面的一行不能删除,必须保留!!!
);

105
docker/AMD64/Dockerfile Normal file
View File

@@ -0,0 +1,105 @@
FROM swoft/alphp:base
#FROM swoft/alphp:cli
#FROM alpine:latest
LABEL maintainer="david <367013672@qq.com>" version="1.0"
##
# ---------- env settings ----------
##
# --build-arg timezone=Asia/Shanghai
ARG timezone
# prod pre test dev
ARG app_env=prod
# default use www-data user
# ARG add_user=www-data
ENV APP_ENV=${app_env:-"prod"} \
TIMEZONE=${timezone:-"Asia/Shanghai"}
##
# ---------- building ----------
##
RUN set -ex \
# change apk source repo
&& sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/' /etc/apk/repositories \
&& apk update \
&& apk add --no-cache \
# Install base packages ('ca-certificates' will install 'nghttp2-libs')
# ca-certificates \
# curl \
# tar \
# xz \
# libressl \
# openssh \
# openssl \
git \
tzdata \
# pcre \
# install php7 and some extensions
php7 \
# php7-common \
# php7-bcmath \
php7-curl \
# php7-ctype \
php7-dom \
# php7-fileinfo \
# php7-gettext \
# php7-gd \
# php7-iconv \
# php7-imagick \
php7-json \
php7-mbstring \
#php7-mongodb \
# php7-mysqlnd \
# php7-openssl \
# php7-opcache \
# php7-pdo \
# php7-pdo_mysql \
# php7-pdo_sqlite \
# php7-phar \
# php7-posix \
# php7-redis \
php7-simplexml \
# php7-sockets \
# php7-sodium \
# php7-sqlite \
# php7-session \
# php7-sysvshm \
# php7-sysvmsg \
# php7-sysvsem \
# php7-tokenizer \
php7-zip \
# php7-zlib \
php7-xml \
&& git clone https://gitee.com/ledc/IYUUAutoReseed.git /var/www \
&& cp /var/www/config/config.sample.php /var/www/config/config.php \
&& ln -sf /var/www/config/config.php /config.php \
&& apk del --purge *-dev \
&& rm -rf /var/cache/apk/* /tmp/* /usr/share/man /usr/share/php7 \
# ---------- some config,clear work ----------
&& cd /etc/php7 \
# - config PHP
&& { \
echo "upload_max_filesize=100M"; \
echo "post_max_size=108M"; \
echo "memory_limit=1024M"; \
echo "date.timezone=${TIMEZONE}"; \
} | tee conf.d/99-overrides.ini \
# - config timezone
&& ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \
&& echo "${TIMEZONE}" > /etc/timezone \
# ---------- some config work ----------
# - ensure 'www-data' user exists(82 is the standard uid/gid for "www-data" in Alpine)
# && addgroup -g 82 -S ${add_user} \
# && adduser -u 82 -D -S -G ${add_user} ${add_user} \
# # - create user dir
# && mkdir -p /data \
# && chown -R ${add_user}:${add_user} /data \
&& echo -e "\033[42;37m Build Completed :).\033[0m\n"
EXPOSE 9000
# VOLUME ["/var/www", "/data"]
WORKDIR /var/www

4
docker/AMD64/build.sh Normal file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
docker build -t iyuu:latest .
docker run -it -v /root/config.php:/config.php --network bridge --name IYUUAutoReseed --restart always -d iyuu:latest
docker exec -it IYUUAutoReseed php iyuu.php

25
docker/Arm64v8/Dockerfile Normal file
View File

@@ -0,0 +1,25 @@
# FROM arm64v8/alpine
# FROM arm64v8/alpine:latest
FROM arm64v8/alpine:3.12
ENV TZ Asia/Shanghai
ENV cron="0 9 * * 0"
RUN set -ex \
&& sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/' /etc/apk/repositories \
# && sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.cn/' /etc/apk/repositories \
&& apk update \
&& apk add --no-cache \
tzdata \
php7 php7-curl php7-json php7-mbstring php7-dom php7-simplexml php7-xml php7-zip \
git \
&& git clone https://gitee.com/ledc/IYUUAutoReseed.git /IYUU \
&& cp /IYUU/config/config.sample.php /IYUU/config/config.php \
&& ln -sf /IYUU/config/config.php /config.php \
&& echo "${TZ}" > /etc/timezone \
&& ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime
WORKDIR /IYUU
CMD ["sh", "-c", "/usr/bin/php /IYUU/iyuu.php ; /usr/sbin/crond ; (crontab -l ;echo \"$cron /usr/bin/php /IYUU/iyuu.php &> /dev/null\") | crontab - ; tail -f /dev/null"]

View File

@@ -0,0 +1,4 @@
#!/bin/sh
docker build -t iyuu:latest .
docker run -it -v /root/config.php:/config.php -v /var/lib/transmission/torrents:/torrents -v /var/lib/qbittorrent/.local/share/data/qBittorrent/BT_backup:/BT_backup --network bridge --name IYUUAutoReseed --restart always -d iyuu:latest
docker exec -it IYUUAutoReseed php iyuu.php

4
docker/Arm64v8/build.sh Normal file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
docker build -f Dockerfile -t iyuu:latest .
docker run -it -v /root/config.php:/config.php --network bridge --name IYUUAutoReseed --restart always -d iyuu:latest
docker exec -it IYUUAutoReseed php iyuu.php

3
docker/Arm64v8/iyuu.sh Normal file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
export PATH=$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/bin
docker exec IYUUAutoReseed php iyuu.php

137
docker/Readme.md Normal file
View File

@@ -0,0 +1,137 @@
# 使用方法:
### 1.拉取镜像、创建容器,运行
#### ARM平台通用方法
```
docker run -d \
--name IYUUAutoReseed \
-e cron='0 9 * * 0' \
-v /root/config.php:/config.php \
--restart=always \
iyuucn/iyuuautoreseed:arm64v8
```
#### 小钢炮方法:
```
docker run -d \
--name IYUUAutoReseed \
-e cron='0 8 * * 0' \
-v /root/config.php:/config.php \
-v /var/lib/transmission/torrents:/torrents \
-v /var/lib/qbittorrent/.local/share/data/qBittorrent/BT_backup:/BT_backup \
--restart always \
iyuucn/iyuuautoreseed:arm64v8
```
#### AMD64平台MAC OS、台式、服务器、NAS等
```
docker run -d \
--name IYUUAutoReseed \
-e cron='0 9 * * 0' \
-v /root/config.php:/config.php \
--restart=always \
iyuucn/iyuuautoreseed:latest
```
**命令解释**
| 参数 | 解释 |
| ----------- | ------------------------------------------------------------ |
| `--name` | 容器名字 |
| `-e` | 环境变量,定时任务执行时间 |
| `-v` | 本地目录或文件:容器目录文件,资源挂载到容器。<br />请把你的配置文件放在/root/config.php会把你的配置映射进容器内。 |
| `--restart` | 启动模式 |
------
### 2.停止
```
docker stop IYUUAutoReseed
```
### 3.运行
```
docker start IYUUAutoReseed
```
### 4.删除容器
```
docker rm IYUUAutoReseed
```
### 5.删除镜像
```
docker rmi iyuucn/iyuuautoreseed:arm64v8
```
------
#### 功能
IYUU自动辅种工具功能分为两大块自动辅种、自动转移。
- 自动辅种目前能对国内大部分的PT站点自动辅种支持下载器集群支持多盘位支持多下载目录支持远程连接等
- 自动转移:可以实现各下载器之间自动转移做种客户端,让下载器各司其职(专职的保种、专职的下载)。
#### 原理
IYUU自动辅种工具英文名IYUUAutoReseed是一款PHP语言编写的Private Tracker辅种脚本通过计划任务或常驻内存按指定频率调用transmission、qBittorrent下载软件的API接口提取正在做种的info_hash提交到辅种服务器API接口辅种过程和PT站没有任何交互根据API接口返回的数据拼接种子连接提交给下载器自动辅种各个站点。
#### 优势
- 全程自动化,无需人工干预;
- 支持多盘位,多做种目录,多下载器,支持远程下载器;
- 辅种精确度高,精度可配置;
- 支持微信通知,消息即时达;
- 自动对合集包,进行拆包辅种(暂未开发)
- 安全:所有隐私信息只在本地存储,绝不发送给第三方。
- 拥有专业的问答社区和交流群
#### 支持的下载器
1. transmission
2. qBittorrent
#### 运行环境
具备PHP运行环境的所有平台例如Linux、Windows、MacOS
官方下载的记得开启curl、json、mbstring这3个扩展。
1. Windows安装php环境https://www.php.net/downloads
#### 源码仓库
- github仓库https://github.com/ledccn/IYUUAutoReseed
- 码云仓库https://gitee.com/ledc/IYUUAutoReseed
#### 使用方法
- 博客https://www.iyuu.cn/
#### 接口开发文档
如果您懂得其他语言的开发可以基于接口做成任何您喜欢的样子比如手机APP二进制包Windows的GUI程序浏览器插件等。欢迎分享您的作品
实时更新的接口文档http://api.iyuu.cn/docs.php
#### 需求提交/错误反馈
- QQ群859882209[2000人.入门群]931954050[1000人.进阶群]
- 问答社区http://wenda.iyuu.cn
- 博客https://www.iyuu.cn/
- issues https://gitee.com/ledc/IYUUAutoReseed/issues

9
git_pull.sh Normal file
View File

@@ -0,0 +1,9 @@
#!/bin/sh
#脚本功能从git拉取最新代码然后执行辅种
pwddir=$(cd $(dirname $0); pwd)
echo $pwddir
cd $(dirname $0)
git fetch --all
git reset --hard origin/master
git pull
php ./iyuu.php

81
init.php Normal file
View File

@@ -0,0 +1,81 @@
<?php
/**
IIIIIIIIIIYYYYYYY YYYYYYYUUUUUUUU UUUUUUUUUUUUUUUU UUUUUUUU
I::::::::IY:::::Y Y:::::YU::::::U U::::::UU::::::U U::::::U
I::::::::IY:::::Y Y:::::YU::::::U U::::::UU::::::U U::::::U
II::::::IIY::::::Y Y::::::YUU:::::U U:::::UUUU:::::U U:::::UU
I::::I YYY:::::Y Y:::::YYY U:::::U U:::::U U:::::U U:::::U
I::::I Y:::::Y Y:::::Y U:::::D D:::::U U:::::D D:::::U
I::::I Y:::::Y:::::Y U:::::D D:::::U U:::::D D:::::U
I::::I Y:::::::::Y U:::::D D:::::U U:::::D D:::::U
I::::I Y:::::::Y U:::::D D:::::U U:::::D D:::::U
I::::I Y:::::Y U:::::D D:::::U U:::::D D:::::U
I::::I Y:::::Y U:::::D D:::::U U:::::D D:::::U
I::::I Y:::::Y U::::::U U::::::U U::::::U U::::::U
II::::::II Y:::::Y U:::::::UUU:::::::U U:::::::UUU:::::::U
I::::::::I YYYY:::::YYYY UU:::::::::::::UU UU:::::::::::::UU
I::::::::I Y:::::::::::Y UU:::::::::UU UU:::::::::UU
IIIIIIIIII YYYYYYYYYYYYY UUUUUUUUU UUUUUUUUU
*/
// 定义目录
defined('ROOT_PATH') or define("ROOT_PATH", __DIR__);
define('DS', DIRECTORY_SEPARATOR);
defined('APP_PATH') or define('APP_PATH', ROOT_PATH.DS.'app'.DS);
define('TORRENT_PATH', ROOT_PATH.DS.'torrent'.DS);
// 严格开发模式
error_reporting(E_ALL);
ini_set('display_errors', 1);
// 永不超时
ini_set('max_execution_time', 0);
set_time_limit(0);
// 内存限制,如果外面设置的内存比 /etc/php/php-cli.ini 大,就不要设置了
if (intval(ini_get("memory_limit")) < 1024) {
ini_set('memory_limit', '1024M');
}
if (PHP_SAPI != 'cli') {
exit("You must run the CLI environment\n");
}
// 设置时区
date_default_timezone_set('Asia/Shanghai');
echo microtime(true).' 环境变量初始化完成!'.PHP_EOL;
// 系统配置
if (file_exists(ROOT_PATH."/config/config.php")) {
// 配置(全局变量)
$configALL = require_once ROOT_PATH . "/config/config.php";
} else {
// 第一次会生成
@copy(ROOT_PATH . '/config/config.sample.php', ROOT_PATH . '/config/config.php');
// 示例配置
$configALL = require_once ROOT_PATH . '/config/config.sample.php';
echo microtime(true).' 缺少config.php已载入config.sample.php示例配置。'.PHP_EOL;
echo microtime(true).' 请编辑配置文件config.php以免后续版本升级覆盖配置'.PHP_EOL;
$t = 30;
do {
echo microtime(true)." 请编辑配置文件config.php{$t}秒后退出...".PHP_EOL;
sleep(1);
} while (--$t > 0);
exit;
}
echo microtime(true).' 全局配置载入完成!'.PHP_EOL;
// 读取支持列表
if (is_file(ROOT_PATH . "/config/sites.json")) {
$sitesJson = file_get_contents(ROOT_PATH . "/config/sites.json");
$configALL['sitesALL'] = json_decode($sitesJson, true);
echo microtime(true).' 支持站点JSON载入完成'.PHP_EOL;
}
echo microtime(true).' 正在加载composer包管理器...'.PHP_EOL;
require_once ROOT_PATH . '/vendor/autoload.php';
echo microtime(true).' composer依赖载入完成'.PHP_EOL;
echo microtime(true).' 当前脚本运行环境:'.PHP_OS.PHP_EOL;
global $argv;
$start_file = str_replace("\\", "/", trim($argv[0]));
if (substr($start_file, -8)==="init.php") {
require_once __DIR__ . '/iyuu.php';
}

View File

@@ -1,612 +0,0 @@
<?php
/**
* IYUUAutoReseed自动辅种
*/
use Curl\Curl;
require_once __DIR__ . '/app/init.php';
iyuuAutoReseed::init();
$hashArray = iyuuAutoReseed::get();
if ( iyuuAutoReseed::$move != null ) {
echo "种子移动完毕,请重新编辑配置,再尝试辅种! \n\n";
exit;
}
iyuuAutoReseed::call($hashArray);
iyuuAutoReseed::wechatMessage();
/**
* IYUUAutoReseed自动辅种类
*/
class iyuuAutoReseed
{
/**
* 版本号
* @var string
*/
const VER = '0.1.0';
/**
* RPC连接池
* @var array
*/
public static $links = array();
/**
* 客户端配置
* @var array
*/
public static $clients = array();
/**
* 不辅种的站点 'ourbits','hdchina'
* @var array
*/
public static $noReseed = [];
/**
* 缓存路径
* @var string
*/
public static $cacheDir = TORRENT_PATH.'cache'.DS;
public static $cacheHash = TORRENT_PATH.'cachehash'.DS;
/**
* API接口配置
* @var string
* @var array
*/
public static $apiUrl = 'http://iyuu.cn:2122';
public static $endpoints = array(
'add' => '/api/add',
'update' => '/api/update',
'reseed' => '/api/reseed',
'login' => '/login',
);
/**
* 退出状态码
* @var int
*/
public static $ExitCode = 0;
/**
* 客户端转移做种 状态码[请把transmission配置为第一个客户端]
* @var array
*/
public static $move = null;
/**
* 微信消息体
* @var array
*/
public static $wechatMsg = array(
'hashCount' => 0, // 提交给服务器的hash总数
'sitesCount' => 0, // 可辅种站点总数
'reseedCount' => 0, // 返回的总数据
'reseedSuccess' => 0, // 成功:辅种成功(会加入缓存,哪怕种子在校验中,下次也会过滤)
'reseedError' => 0, // 错误:辅种失败(可以重试)
'reseedRepeat' => 0, // 重复:客户端已做种
'reseedSkip' => 0, // 跳过因未设置passkey而跳过
'reseedPass' => 0, // 忽略:因上次成功添加、存在缓存,而跳过
);
/**
* 初始化
* @return void
*/
public static function init(){
global $configALL;
self::$clients = isset($configALL['default']['clients']) && $configALL['default']['clients'] ? $configALL['default']['clients'] : array();
echo "程序正在初始化运行参数... \n";
// 递归删除上次历史记录
IFile::rmdir(self::$cacheDir, true);
// 建立目录
IFile::mkdir(self::$cacheDir);
IFile::mkdir(self::$cacheHash);
// 连接全局客户端
self::links();
// 合作站点自动注册鉴权
Oauth::login(self::$apiUrl . self::$endpoints['login']);
}
/**
* 连接远端RPC服务器
*
* @param string
* @return bool
*/
public static function links()
{
if(empty(self::$links)){
foreach ( self::$clients as $k => $v ){
// 跳过未配置的客户端
if (empty($v['username']) || empty( $v['password'])) {
unset(self::$clients[$k]);
echo "clients_".$k." 用户名或密码未配置,已跳过 \n\n";
continue;
}
try
{
switch($v['type']){
case 'transmission':
self::$links[$k]['rpc'] = new TransmissionRPC($v['host'], $v['username'], $v['password']);
$result = self::$links[$k]['rpc']->sstats();
print $v['type'].''.$v['host']." Rpc连接 [{$result->result}] \n";
break;
case 'qBittorrent':
self::$links[$k]['rpc'] = new qBittorrent($v['host'], $v['username'], $v['password']);
$result = self::$links[$k]['rpc']->appVersion();
print $v['type'].''.$v['host']." Rpc连接 [{$result}] \n";
break;
default:
echo '[ERROR] '.$v['type'];
exit(1);
break;
}
self::$links[$k]['type'] = $v['type'];
// 检查是否转移种子的做种客户端?
if ( isset($v['move']) && $v['move'] ) {
self::$move = array($k,$v['type']);
}
} catch (Exception $e) {
echo '[ERROR] ' . $e->getMessage() . PHP_EOL;
exit(1);
}
}
}
return true;
}
/**
* 从客户端获取种子的哈希列表
* @var array
*/
public static function get(){
$hashArray = array();
foreach ( self::$clients as $k => $v ){
$result = array();
$res = $info_hash = array();
$json = $sha1 = '';
try
{
switch($v['type']){
case 'transmission':
$ids = $fields = array();
#$fields = array( "id", "status", "name", "hashString", "downloadDir", "torrentFile" );
$fields = array( "id", "status", "hashString", "downloadDir");
$result = self::$links[$k]['rpc']->get($ids, $fields);
if ( empty($result->result) || $result->result != 'success' ){
// 获取种子列表 失败
echo "获取种子列表失败原因可能是transmission暂时无响应请稍后重试 \n";
break;
}
if( empty($result->arguments) ){
echo "未获取到需要辅种的数据,请多多保种,然后重试! \n";
break;
}
// 对象转数组
$res = object_array($result->arguments->torrents);
// 过滤,只保留正常做种
$res = array_filter($res, "filterStatus");
// 提取数组hashString
$info_hash = array_column($res, 'hashString');
// 升序排序
sort($info_hash);
// 微信模板消息 统计
self::$wechatMsg['hashCount'] += count($info_hash);
$json = json_encode($info_hash, JSON_UNESCAPED_UNICODE);
// 去重 应该从文件读入,防止重复提交
$sha1 = sha1( $json );
if ( isset($hashArray['sha1']) && (in_array($sha1, $hashArray['sha1']) != false) ) {
break;
}
// 组装返回数据
$hashArray['hash']['clients_'.$k] = $json;
$hashArray['sha1'][] = $sha1;
// 变换数组hashString为键
self::$links[$k]['hash'] = array_column($res, "downloadDir", 'hashString');
#p(self::$links[$k]['hash']);exit;
break;
case 'qBittorrent':
$result = self::$links[$k]['rpc']->torrentList();
$res = json_decode($result,true);
if ( empty($res) ) {
echo "未获取到需要辅种的数据,请多多保种,然后重试! \n";
break;
}
#p($res);exit;
// 过滤,只保留正常做种
$res = array_filter($res, "qbfilterStatus");
// 提取数组hashString
$info_hash = array_column($res, 'hash');
// 升序排序
sort($info_hash);
// 微信模板消息 统计
self::$wechatMsg['hashCount'] += count($info_hash);
$json = json_encode($info_hash, JSON_UNESCAPED_UNICODE);
// 去重 应该从文件读入,防止重复提交
$sha1 = sha1( $json );
if ( isset($hashArray['sha1']) && (in_array($sha1, $hashArray['sha1']) != false) ) {
break;
}
// 组装返回数据
$hashArray['hash']['clients_'.$k] = $json;
$hashArray['sha1'][] = $sha1;
// 变换数组hash为键
self::$links[$k]['hash'] = array_column($res, "save_path", 'hash');
#p(self::$links[$k]['hash']);exit;
break;
default:
echo '[ERROR] '.$v['type'];
exit(1);
break;
}
// 是否执行转移种子做种客户端?
if ( self::$move != null && (empty($v['move'])) ) {
self::move($res, $v['type']);
}
} catch (Exception $e) {
echo '[ERROR] ' . $e->getMessage() . PHP_EOL;
exit(1);
}
}
return $hashArray;
}
/**
* @brief 添加下载任务
* @param string $torrent 种子元数据
* @param string $save_path 保存路径
* @return bool
*/
public static function add($rpcKey, $torrent, $save_path = '', $extra_options = array())
{
try
{
$is_url = false;
if( (strpos($torrent,'http://')===0) || (strpos($torrent,'https://')===0) || (strpos($torrent,'magnet:?xt=urn:btih:')===0) ){
$is_url = true;
}
// 下载服务器类型
$type = self::$links[$rpcKey]['type'];
// 判断
switch( $type ){
case 'transmission':
$extra_options['paused'] = true;
if( $is_url ){
$result = self::$links[$rpcKey]['rpc']->add( $torrent, $save_path, $extra_options ); // 种子URL添加下载任务
} else{
$result = self::$links[$rpcKey]['rpc']->add_metainfo( $torrent, $save_path, $extra_options ); // 种子元数据添加下载任务
}
if(isset($result->result) && $result->result == 'success'){
$id = $name = '';
if( isset($result->arguments->torrent_duplicate) ){
$id = $result->arguments->torrent_duplicate->id;
$name = $result->arguments->torrent_duplicate->name;
}elseif( isset($result->arguments->torrent_added) ){
$id = $result->arguments->torrent_added->id;
$name = $result->arguments->torrent_added->name;
}
print "********RPC添加下载任务成功 [{$result->result}] (id=$id) \n";
if( $is_url ){
print "种子:".$torrent. "\n";
}
print "名字:".$name."\n\n";
return true;
}else{
$errmsg = isset($result->result) ? $result->result : '未知错误,请稍后重试!';
print "-----RPC添加种子任务失败 [{$errmsg}] \n";
if( $is_url ){
print "种子:".$torrent. "\n";
}
}
break;
case 'qBittorrent':
$extra_options['paused'] = 'true';
$extra_options['autoTMM'] = 'false'; //关闭自动种子管理
if( $is_url ){
$result = self::$links[$rpcKey]['rpc']->add( $torrent, $save_path, $extra_options ); // 种子URL添加下载任务
} else{
$extra_options['name'] = 'torrents';
$extra_options['filename'] = rand(1,4294967200).'.torrent';
$result = self::$links[$rpcKey]['rpc']->add_metainfo( $torrent, $save_path, $extra_options ); // 种子元数据添加下载任务
}
if ($result === 'Ok.') {
print "********RPC添加下载任务成功 [{$result}] \n\n";
return true;
} else {
print "-----RPC添加种子任务失败 [{$result}] \n\n";
}
break;
default:
echo '[ERROR] '.$type;
break;
}
} catch (Exception $e) {
echo '[ERROR] ' . $e->getMessage() . PHP_EOL;
}
return false;
}
/**
* 正常做种的种子在各下载器的互相转移
*/
public static function move($torrent=array(), $type = 'qBittorrent'){
switch($type){
case 'transmission':
break;
case 'qBittorrent':
foreach ($torrent as $k => $v) {
// 路径转换
#$v['save_path'] = '/volume3' . $v['save_path']; // docker路径转换
self::add(self::$move[0], $v['magnet_uri'], $v['save_path'] );
}
break;
default:
echo '[ERROR] '.$type;
break;
}
}
/**
* @brief 提交种子hash给远端API用来获取辅种数据
* @param array $hashArray 种子hash数组
* @return
*/
public static function call($hashArray = array())
{
global $configALL;
$resArray = $sites = array();
$curl = new Curl();
$curl->setOpt(CURLOPT_SSL_VERIFYPEER, false);
// 签名
$hashArray['timestamp'] = time();
// 爱语飞飞token
$hashArray['sign'] = Oauth::getSign();
$hashArray['version'] = self::VER;
// 写日志
if (true) {
// 文件句柄
$resource = fopen(self::$cacheDir.'hashString.txt', "wb");
// 成功返回写入字节数失败返回false
$worldsnum = fwrite($resource, p($hashArray, false));
fclose($resource);
}
// 发起请求
echo "正在提交辅种信息…… \n";
$res = $curl->post(self::$apiUrl . self::$endpoints['reseed'], $hashArray);
$resArray = json_decode($res->response, true);
// 写日志
if(true){
// 文件句柄
$resource = fopen(self::$cacheDir.'reseed.txt', "wb");
// 成功返回写入字节数失败返回false
$worldsnum = fwrite($resource, p($resArray, false));
fclose($resource);
}
// 判断返回值
if ( isset($resArray['errmsg']) && ($resArray['errmsg'] == 'ok') ) {
echo "辅种信息提交成功!!! \n\n";
}else{
$errmsg = isset($resArray['errmsg']) ? $resArray['errmsg'] : '远端服务器无响应,请稍后重试!';
echo '-----辅种失败,原因:' .$errmsg. " \n\n";
exit(1);
}
// 可辅种站点信息列表
$sites = $resArray['sites'];
self::$wechatMsg['sitesCount'] = count($sites);
#p($sites);
// 按客户端循环辅种 开始
foreach (self::$links as $k => $v) {
$reseed = $infohash_Dir = array();
// info_hash 对应的下载目录
$infohash_Dir = self::$links[$k]['hash'];
if (empty($resArray['clients_'.$k])) {
echo "clients_".$k."没有查询到可辅种数据 \n\n";
continue;
}
#p($infohash_Dir);
// 当前客户端辅种数据
$reseed = $resArray['clients_'.$k];
foreach ($reseed as $info_hash => $vv) {
// 当前种子哈希对应的目录
$downloadDir = $infohash_Dir[$info_hash];
foreach ($vv['torrent'] as $id => $value) {
// 匹配的辅种数据累加
self::$wechatMsg['reseedCount']++;
// 站点id
$sitesID = $value['sid'];
$url = $_url = '';
$download_page = $details_url = '';
// 页面规则
$download_page = str_replace('{}', $value['torrent_id'], $sites[$sitesID]['download_page']);
$_url = 'https://' .$sites[$sitesID]['base_url']. '/' .$download_page;
if ( empty($configALL[$sites[$sitesID]['site']]['passkey']) ) {
echo '-------因当前' .$sites[$sitesID]['site']. '站点未设置passkey已跳过' . "\n\n";
self::$wechatMsg['reseedSkip']++;
continue;
}
// 种子URL组合方式区分
switch ($sites[$sitesID]['site']) {
case 'ttg':
$url = $_url."/". $configALL[$sites[$sitesID]['site']]['passkey'];
break;
case 'm-team':
$ip_type = '';
if (isset($configALL[$sites[$sitesID]['site']]['ip_type'])) {
$ip_type = $configALL[$sites[$sitesID]['site']]['ip_type'] == 'ipv6' ? '&ipv6=1' : '';
}
$url = $_url."&passkey=". $configALL[$sites[$sitesID]['site']]['passkey'] . $ip_type. "&https=1";
break;
case 'moecat':
$ip_type = '';
if (isset($configALL[$sites[$sitesID]['site']]['ip_type'])) {
$ip_type = $configALL[$sites[$sitesID]['site']]['ip_type'] == 'ipv6' ? '&ipv6=1' : '';
}
$url = $_url."&passkey=". $configALL[$sites[$sitesID]['site']]['passkey'] . $ip_type. "&https=1";
break;
case 'hdchina':
if ( empty($configALL[$sites[$sitesID]['site']]['cookie']) ) {
echo '-------因当前' .$sites[$sitesID]['site']. '站点未设置cookie已跳过' . "\n\n";
self::$wechatMsg['reseedSkip']++;
break;
}
$cookie = isset($configALL[$sites[$sitesID]['site']]['cookie']) ? $configALL[$sites[$sitesID]['site']]['cookie'] : '';
$userAgent = $configALL['default']['userAgent'];
// 拼接URL
$details_page = str_replace('{}', $value['torrent_id'], 'details.php?id={}&hit=1');
$details_url = 'https://' .$sites[$sitesID]['base_url']. '/' .$details_page;
$details_html = download($details_url, $cookie, $userAgent);
print "种子详情页:".$details_url. "\n";
// 提取种子下载地址
$download_page = str_replace('{}', '', $sites[$sitesID]['download_page']);
$offset = strpos($details_html, $download_page);
$urlTemp = substr($details_html, $offset, 50);
// 种子地址
$_url = substr($urlTemp,0,strpos($urlTemp,'">'));
$_url = 'https://' .$sites[$sitesID]['base_url']. '/' . $_url;
print "种子下载页:".$_url. "\n";
$url = download($_url, $cookie, $userAgent);
break;
case 'hdcity':
if ( empty($configALL[$sites[$sitesID]['site']]['cookie']) ) {
echo '-------因当前' .$sites[$sitesID]['site']. '站点未设置cookie已跳过' . "\n\n";
self::$wechatMsg['reseedSkip']++;
break;
}
$cookie = isset($configALL[$sites[$sitesID]['site']]['cookie']) ? $configALL[$sites[$sitesID]['site']]['cookie'] : '';
$userAgent = $configALL['default']['userAgent'];
print "种子:".$_url. "\n";
if ( isset($configALL[$sites[$sitesID]['site']]['cuhash']) ) {
// 已获取cuhash
# code...
}else {
// 获取cuhash
$html = download('https://' .$sites[$sitesID]['base_url']. '/pt', $cookie, $userAgent);
// 提取种子下载地址
$offset = strpos($html,'cuhash=');
$len = strlen('cuhash=');
$cuhashTemp = substr($html,$offset+$len,40);
$configALL[$sites[$sitesID]['site']]['cuhash'] = substr($cuhashTemp,0,strpos($cuhashTemp,'"'));
}
$url = $_url."&cuhash=". $configALL[$sites[$sitesID]['site']]['cuhash'];
// 城市下载种子时会302转向
$url = download($url, $cookie, $userAgent);
break;
default:
$url = $_url."&passkey=". $configALL[$sites[$sitesID]['site']]['passkey'];
break;
}
/**
* 检查站点是否可以辅种
*/
// 判断是否具有VIP或特殊权限
$is_vip = isset($configALL[$sites[$sitesID]['site']]['is_vip']) && $configALL[$sites[$sitesID]['site']]['is_vip'] ? 1 : 0;
if ( (in_array($sites[$sitesID]['site'], self::$noReseed)==false) || $is_vip ) {
/**
* 可以辅种
*/
if ( isset($infohash_Dir[$value['info_hash']]) ) {
// 与客户端现有种子重复
echo '-------与客户端现有种子重复:'.$_url."\n\n";
self::$wechatMsg['reseedRepeat']++;
continue;
}else{
// 判断上次是否成功添加?
if ( is_file(self::$cacheHash . $value['info_hash'].'.txt') ) {
echo '-------当前种子上次辅种已成功添加,已跳过!'.$_url."\n\n";
self::$wechatMsg['reseedPass']++;
continue;
}
// 把拼接的种子URL推送给下载器
$ret = false;
// 成功返回true
$ret = self::add($k, $url, $downloadDir);
// 添加成功的种子以infohash为文件名写入缓存
if ($ret) {
// 成功的种子
// 文件句柄
$resource = fopen(self::$cacheHash . $value['info_hash'].'.txt', "wb");
// 成功返回写入字节数失败返回false
$worldsnum = fwrite($resource, $url);
fclose($resource);
self::$wechatMsg['reseedSuccess']++;
continue;
}else{
// 失败的种子
// 站点类型判断
switch ($sites[$sitesID]['site']) {
case 'hdcity':
echo '当前' .$sites[$sitesID]['site']. '站点是配置cuhash(不是passkey),添加成功说明配置正确!如果添加任务失败,请查阅常见问题!!' . "\n";
break;
default:
break;
}
// 失败累加
self::$wechatMsg['reseedError']++;
continue;
}
}
}else{
/**
* 不辅种
*/
echo '-------已跳过不辅种的站点:'.$_url."\n\n";
// 写入日志文件,供用户手动辅种
if ( !isset($infohash_Dir[$value['info_hash']]) ) {
// 站点类型判断
switch ($sites[$sitesID]['site']) {
case 'hdchina':
$url = $_url;
break;
default:
break;
}
// 文件句柄
$resource = fopen(self::$cacheDir . $sites[$sitesID]['site'].'.txt', 'a');
// 成功返回写入字节数失败返回false
$worldsnum = fwrite($resource, 'clients_'.$k."\n".$downloadDir."\n".$url."\n".$details_url."\n\n");
fclose($resource);
}
}
}
}
}
// 按客户端循环辅种 结束
}
/**
*
*/
public static function wechatMessage(){
$br = "\r\n";
$text = 'IYUU自动辅种-统计报表';
$desp = '总做种:'.self::$wechatMsg['hashCount'] . ' [客户端正在做种的hash总数]' .$br;
$desp .= '返回数据:'.self::$wechatMsg['reseedCount']. ' [服务器返回的可辅种数据]' .$br;
$desp .= '支持站点:'.self::$wechatMsg['sitesCount']. ' [当前支持自动辅种的站点数量]' .$br;
$desp .= '成功:'.self::$wechatMsg['reseedSuccess']. ' [辅种成功会把hash加入缓存]' .$br;
$desp .= '失败:'.self::$wechatMsg['reseedError']. ' [下载器下载种子失败或网络超时引起,可以重试]' .$br;
$desp .= '重复:'.self::$wechatMsg['reseedRepeat']. ' [客户端已做种]' .$br;
$desp .= '跳过:'.self::$wechatMsg['reseedSkip']. ' [未设置passkey]' .$br;
$desp .= '忽略:'.self::$wechatMsg['reseedPass']. ' [成功添加存在缓存]' .$br;
return ff($text, $desp);
}
}
/**
* transmission过滤函数只保留正常做种
*/
function filterStatus( $v ){
return isset($v['status']) && $v['status']===6;
}
/**
* qBittorrent过滤函数只保留正常做种
*/
function qbfilterStatus( $v ){
if( isset($v['state']) && in_array($v['state'], array('uploading','stalledUP','pausedUP','queuedUP','checkingUP','forcedUP')) ){
return true;
}
return false;
}
//PHP stdClass Object转array
function object_array($array) {
if(is_object($array)) {
$array = (array)$array;
}
if(is_array($array)) {
foreach($array as $key=>$value) {
$array[$key] = object_array($value);
}
}
return $array;
}
// 对象转数组
function object2array(&$object) {
return json_decode( json_encode( $object ), true );
}

31
iyuu.php Normal file
View File

@@ -0,0 +1,31 @@
<?php
/**
_____ _____ _____ _____
/\ \ |\ \ /\ \ /\ \
/::\ \ |:\____\ /::\____\ /::\____\
\:::\ \ |::| | /:::/ / /:::/ /
\:::\ \ |::| | /:::/ / /:::/ /
\:::\ \ |::| | /:::/ / /:::/ /
\:::\ \ |::| | /:::/ / /:::/ /
/::::\ \ |::| | /:::/ / /:::/ /
____ /::::::\ \ |::|___|______ /:::/ / _____ /:::/ / _____
/\ \ /:::/\:::\ \ /::::::::\ \ /:::/____/ /\ \ /:::/____/ /\ \
/::\ \/:::/ \:::\____\ /::::::::::\____\|:::| / /::\____\|:::| / /::\____\
\:::\ /:::/ \::/ / /:::/~~~~/~~ |:::|____\ /:::/ /|:::|____\ /:::/ /
\:::\/:::/ / \/____/ /:::/ / \:::\ \ /:::/ / \:::\ \ /:::/ /
\::::::/ / /:::/ / \:::\ \ /:::/ / \:::\ \ /:::/ /
\::::/____/ /:::/ / \:::\ /:::/ / \:::\ /:::/ /
\:::\ \ \::/ / \:::\__/:::/ / \:::\__/:::/ /
\:::\ \ \/____/ \::::::::/ / \::::::::/ /
\:::\ \ \::::::/ / \::::::/ /
\:::\____\ \::::/ / \::::/ /
\::/ / \::/____/ \::/____/
\/____/ ~~ ~~
*/
require_once __DIR__ . '/init.php';
echo '当前脚本路径:'.__FILE__.PHP_EOL;
use IYUU\AutoReseed;
AutoReseed::init();
AutoReseed::call();

125
readme.md
View File

@@ -1,39 +1,98 @@
## 免责声明
在使用本工具前,请认真阅读《免责声明》全文如下:
使用IYUUAutoReseed自动辅种工具本身是非常安全的IYUU脚本辅种时不会跟PT站点的服务器产生任何交互只是会把下载种子链接推送给下载器由下载器去站点下载种子。理论上任何站点、任何技术都无法检测你是否使用了IYUUAutoReseed。危险来自于包括但不限于以下几点
第一建议不要自己手动跳校验任何因为跳校验ban号别怪我没提醒出事后请不要怪到IYUU的头上
第二官方首发资源、其他一切首发资源的种子IYUUAutoReseed自动辅种工具也无法在出种前辅种如果因为你个人的作弊而被ban号跟IYUU无关
第三您使用IYUU工具造成的一切损失与IYUU无关。如不接受此条款请不要使用IYUUAutoReseed并立刻删除已经下载的源码。
![stars](https://img.shields.io/github/stars/ledccn/IYUUAutoReseed)![forks](https://img.shields.io/github/forks/ledccn/IYUUAutoReseed)![release](https://img.shields.io/github/release/ledccn/IYUUAutoReseed.svg)
## 获取脚本,三种方式皆可
1. 通过git命令安装
`git clone https://github.com/ledccn/IYUUAutoReseed.git`
2. 通过composer命令安装
`composer create-project ledccn/iyuuautoreseed:dev-master`
3. 直接下载zip源码包
`https://github.com/ledccn/IYUUAutoReseed/archive/master.zip`
## 功能
IYUU自动辅种工具目前能对国内大部分的PT站点自动辅种支持下载器集群支持多盘位支持多下载目录支持远程连接等。
IYUU自动辅种工具功能分为两大块自动辅种、自动转移。
- 自动辅种目前能对国内大部分的PT站点自动辅种支持下载器集群支持多盘位支持多下载目录支持远程连接等
- 自动转移:可以实现各下载器之间自动转移做种客户端,让下载器各司其职(专职的保种、专职的下载)。
## 原理
IYUU自动辅种工具英文名IYUUAutoReseed是一款PHP语言编写的Private Tracker辅种脚本通过计划任务或常驻内存按指定频率调用transmission、qBittorrent下载软件的API接口提取正在做种的info_hash提交到服务器API接口根据API接口返回的数据拼接种子连接提交给下载器自动辅种各个站点。
IYUU自动辅种工具英文名IYUUAutoReseed是一款PHP语言编写的Private Tracker辅种脚本通过计划任务或常驻内存按指定频率调用transmission、qBittorrent下载软件的API接口提取正在做种的info_hash提交到辅种服务器API接口辅种过程和PT站没有任何交互根据API接口返回的数据拼接种子连接提交给下载器自动辅种各个站点。
## 优势
- 全程自动化,无需人工干预;
- 支持多盘位,多做种目录,多下载器,支持远程连接下载器;
- 支持多盘位,多做种目录,多下载器,支持远程下载器;
- 辅种精确度高,精度可配置;
- 支持微信通知,消息即时达;
- 自动对合集包,进行拆包辅种(暂未开发)
- 安全:所有隐私信息只在本地存储,绝不发送给第三方。
- 拥有专业的问答社区和交流群
## 支持的下载器
1. transmission
2. qBittorrent
## 支持自动辅种的站点
学校、杜比、家园、天空、朋友、馒头、萌猫、我堡、猫站、铂金家、烧包、北洋、TCCF、南洋、TTG、映客、城市、52pt、brobits、备胎、SSD、CHD、ptmsg、leaguehd、聆音、瓷器、hdarea、eastgame(TLF)、1ptba、hdtime、hd4fans、opencd、hdbug、hdstreet、joyhd、u2、upxin(HDU)、oshen、discfan(GZT)、cnscg圣城。
学校、杜比、家园、天空、朋友、馒头、萌猫、我堡、猫站、铂金家、烧包、北洋、TCCF、南洋、TTG、映客、城市、52pt、brobits、备胎、SSD、CHD、ptmsg、leaguehd、聆音、瓷器、hdarea、eastgame(TLF)、1ptba、hdtime、hd4fans、opencd、hdbug、hdstreet、joyhd、u2、upxin(HDU)、oshen、discfan(GZT)、cnscg圣城(已删除)、北邮、CCFBits、dicmusic、天雪、葡萄、HDRoute、伊甸园hdbd、海胆haidan、HDfans
## 运行环境
所有具备PHP运行环境的所有平台官方下载的记得开启crul、fileinfo、mbstring这3个扩展。
例如Linux、Windows、MacOS
1. Windows下安装php环境https://www.php.net/downloads
具备PHP运行环境的所有平台例如Linux、Windows、MacOS
官方下载的记得开启curl、json、mbstring这3个扩展。
1. Windows安装php环境https://www.php.net/downloads
## 下载源码
- github仓库https://github.com/ledccn/IYUUAutoReseed
- 码云仓库https://gitee.com/ledc/IYUUAutoReseed
## 使用方法
详见Wiki https://gitee.com/ledc/IYUUAutoReseed/wikis
- 详见Wikihttps://gitee.com/ledc/IYUUAutoReseed/tree/master/wiki
- 博客https://www.iyuu.cn/
## 接口开发文档
如果您懂得其他语言的开发可以基于接口做成任何您喜欢的样子比如手机APP二进制包Windows的GUI程序浏览器插件等。欢迎分享您的作品
实时更新的接口文档http://api.iyuu.cn/docs.php
## 相关项目
| 项目名| 简介|
| - | --- |
| [IYUU GUI](https://github.com/Rhilip/IYUU-GUI) | 这是一个基于IYUU提供的API产生一个可视化操作项目。目的是为了降低直接上手PHP版IYUUAutoReseed的困难。 |
| [IYUU-Fly](https://github.com/PlexPt/iyuu-fly) | 带GUI的iyuu自动辅种程序。 |
| [goreseed](https://github.com/gaoluhua99/goreseed) | golang编写调用IYUU接口的CLI辅种程序。 |
| [IYUUAutoReseed-web](https://github.com/goveeta/IYUUAutoReseed-web) | |
| [AutoPT](https://github.com/lyssssssss/AutoPT) | 此程序用于自动下载PT免费种子并自动辅种和一体化管理。开发目的为了释放双手专注观影 |
| [flexget_qbittorrent_mod](https://github.com/IvonWei/flexget_qbittorrent_mod) | Flexget qBittorrent插件实现全自动化辅种删除种免费种筛选签到等。|
## 需求提交/错误反馈
- 点击链接加入群聊【IYUU自动辅种交流】[https://jq.qq.com/?_wv=1027&k=5JOfOlM][1]
- QQ群859882209
- QQ群859882209[2000人.入门群]931954050[1000人.进阶群]
- 问答社区http://wenda.iyuu.cn
- 博客https://www.iyuu.cn/
- issues https://gitee.com/ledc/IYUUAutoReseed/issues
## 捐助开发者
@@ -41,11 +100,9 @@ IYUU自动辅种工具英文名IYUUAutoReseed是一款PHP语言编
如果喜欢,请帮忙在[Github](https://github.com/ledccn/IYUUAutoReseed)或[码云](https://gitee.com/ledc/IYUUAutoReseed)给个Star也可以对IYUUAutoReseed进行[捐赠](https://gitee.com/ledc/IYUUAutoReseed#%E6%8D%90%E5%8A%A9%E5%BC%80%E5%8F%91%E8%80%85)哦 ^_^。
**您所有的打赏将用于服务器续期,增加服务的延续性。**
**您所有的打赏将用于服务器维护及续期,增加服务的延续性。**
![微信打赏.png][2]
## 捐赠者列表
感谢以下捐赠者,排名不分先后!
@@ -87,6 +144,43 @@ IYUU自动辅种工具英文名IYUUAutoReseed是一款PHP语言编
| 寒山先生 | ¥100元 | 2020年1月9日11:51 |
| 王浩淼 | ¥50元 | 2020年1月9日11:53 |
| 寒山先生 | ¥100元 | 2020年1月11日11:47 |
| 天空(感谢远程指导小意思) | ¥20元 | 2020年1月14日23:11 |
| 寒山先生 | ¥200元 | 2020年1月18日12:37 |
| 小城流水 | ¥5元 | 2020年1月22日22:14 |
| 国旗(未署名) | ¥8.8元 | 2020年1月22日23:28 |
| Alpha | ¥10.81元 | 2020年1月24日20:23 |
| 羽生 | ¥88.88元 | 2020年1月24日21:06 |
| 当下丶 | ¥100元 | 2020年1月28日1:45 |
| 陈君政 | ¥10元 | 2020年2月3日11:32 |
| 不寐夜游 | ¥10元 | 2020年2月8日17:17 |
| Jack | ¥10元 | 2020年2月13日08:05 |
| 陈伟平 | ¥28.88元 | 2020年2月13日12:35 |
| PhalApi Pro商业授权 | ¥-950元 | 2020年2月14日21:56 |
| jonnaszheng | ¥10元 | 2020年2月15日10:25 |
| weekendsd54zdk | ¥10元 | 2020年2月17日14:31 |
| 寒山先生 | ¥200元 | 2020年2月17日17:00 |
| PLC组态远程服务 | ¥8.88元 | 2020年2月18日02:14 |
| JeSsiE杰西 | ¥66元 | 2020年2月20日19:38 |
| 黄叶梓(炮王) | ¥10元 | 2020年2月20日21:10 |
| 里奥龙 | ¥88.8元 | 2020年2月20日21:48 |
| 寒山先生 | ¥200元 | 2020年2月21日17:32 |
| 李永超 | ¥10元 | 2020年2月22日16:24 |
| Always | ¥5元 | 2020年2月22日21:31 |
| 车站 | ¥30元 | 2020年2月22日21:32 |
| 寒山先生 | ¥200元 | 2020年2月23日22:21 |
| 莫凡 | ¥10元 | 2020年2月24日19:43 |
| 未署名 | ¥200元 | 2020年2月25日14:36 |
| 锦年 | ¥6.66元 | 2020年2月25日19:00 |
| 金力 | ¥10元 | 2020年2月26日22:45 |
| 飞翔鱼 | ¥100元 | 2020年2月24日17:58 |
| 团 | ¥1元 | 2020年2月29日1:12 |
| 沙鸥 | ¥10元 | 2020年2月29日17:03 |
| lsy | ¥229.5元 | 2020年3月1日15:15 |
| 慧宇 | ¥30元 | 2020年3月3日16:39 |
| sz贺贺 | ¥100元 | 2020年3月7日14:40 |
| 一介凡人 | ¥8.88元 | 2020年3月9日22:34 |
| | | |
补充说明:
@@ -94,8 +188,3 @@ IYUU自动辅种工具英文名IYUUAutoReseed是一款PHP语言编
2. 所捐赠的资源不属于任何个人,而应作为项目或者开发团队的所需开销;
3. 如果捐赠了却不希望您的名字出现在这里,可以联系我们进行相应处理;
4. 更新有延时,如未能及时更新,可联系我。
[1]: https://jq.qq.com/?_wv=1027&k=5JOfOlM
[2]: https://www.iyuu.cn/usr/uploads/2019/12/801558607.png

2
vendor/autoload.php vendored
View File

@@ -4,4 +4,4 @@
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit902220bdd481fe56c25750cdf0255dd6::getLoader();
return ComposerAutoloaderInitd8553673db02b2a444a853f28e16196e::getLoader();

View File

@@ -6,12 +6,4 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Bencode' => $baseDir . '/app/Class/Bencode.php',
'IFile' => $baseDir . '/app/Class/IFile.php',
'Oauth' => $baseDir . '/app/Class/Oauth.php',
'Rpc' => $baseDir . '/app/Class/Rpc.php',
'TransmissionRPC' => $baseDir . '/app/Class/TransmissionRPC.php',
'TransmissionRPCException' => $baseDir . '/app/Class/TransmissionRPC.php',
'qBittorrent' => $baseDir . '/app/Class/qBittorrent.php',
'uTorrent' => $baseDir . '/app/Class/uTorrent.php',
);

View File

@@ -6,5 +6,5 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'818e4acb2b433594ec9a26bacb71084d' => $baseDir . '/app/Class/Function.php',
'45702aba72a3d88d5dd1a153f5231b73' => $baseDir . '/app/helper.php',
);

View File

@@ -6,5 +6,5 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'phpspider\\' => array($vendorDir . '/owner888/phpspider'),
'IYUU\\' => array($baseDir . '/app'),
);

View File

@@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit902220bdd481fe56c25750cdf0255dd6
class ComposerAutoloaderInitd8553673db02b2a444a853f28e16196e
{
private static $loader;
@@ -19,15 +19,15 @@ class ComposerAutoloaderInit902220bdd481fe56c25750cdf0255dd6
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit902220bdd481fe56c25750cdf0255dd6', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInitd8553673db02b2a444a853f28e16196e', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit902220bdd481fe56c25750cdf0255dd6', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInitd8553673db02b2a444a853f28e16196e', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit902220bdd481fe56c25750cdf0255dd6::getInitializer($loader));
call_user_func(\Composer\Autoload\ComposerStaticInitd8553673db02b2a444a853f28e16196e::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
@@ -48,19 +48,19 @@ class ComposerAutoloaderInit902220bdd481fe56c25750cdf0255dd6
$loader->register(true);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInit902220bdd481fe56c25750cdf0255dd6::$files;
$includeFiles = Composer\Autoload\ComposerStaticInitd8553673db02b2a444a853f28e16196e::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire902220bdd481fe56c25750cdf0255dd6($fileIdentifier, $file);
composerRequired8553673db02b2a444a853f28e16196e($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequire902220bdd481fe56c25750cdf0255dd6($fileIdentifier, $file)
function composerRequired8553673db02b2a444a853f28e16196e($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;

View File

@@ -4,23 +4,23 @@
namespace Composer\Autoload;
class ComposerStaticInit902220bdd481fe56c25750cdf0255dd6
class ComposerStaticInitd8553673db02b2a444a853f28e16196e
{
public static $files = array (
'818e4acb2b433594ec9a26bacb71084d' => __DIR__ . '/../..' . '/app/Class/Function.php',
'45702aba72a3d88d5dd1a153f5231b73' => __DIR__ . '/../..' . '/app/helper.php',
);
public static $prefixLengthsPsr4 = array (
'p' =>
'I' =>
array (
'phpspider\\' => 10,
'IYUU\\' => 5,
),
);
public static $prefixDirsPsr4 = array (
'phpspider\\' =>
'IYUU\\' =>
array (
0 => __DIR__ . '/..' . '/owner888/phpspider',
0 => __DIR__ . '/../..' . '/app',
),
);
@@ -34,24 +34,12 @@ class ComposerStaticInit902220bdd481fe56c25750cdf0255dd6
),
);
public static $classMap = array (
'Bencode' => __DIR__ . '/../..' . '/app/Class/Bencode.php',
'IFile' => __DIR__ . '/../..' . '/app/Class/IFile.php',
'Oauth' => __DIR__ . '/../..' . '/app/Class/Oauth.php',
'Rpc' => __DIR__ . '/../..' . '/app/Class/Rpc.php',
'TransmissionRPC' => __DIR__ . '/../..' . '/app/Class/TransmissionRPC.php',
'TransmissionRPCException' => __DIR__ . '/../..' . '/app/Class/TransmissionRPC.php',
'qBittorrent' => __DIR__ . '/../..' . '/app/Class/qBittorrent.php',
'uTorrent' => __DIR__ . '/../..' . '/app/Class/uTorrent.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit902220bdd481fe56c25750cdf0255dd6::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit902220bdd481fe56c25750cdf0255dd6::$prefixDirsPsr4;
$loader->prefixesPsr0 = ComposerStaticInit902220bdd481fe56c25750cdf0255dd6::$prefixesPsr0;
$loader->classMap = ComposerStaticInit902220bdd481fe56c25750cdf0255dd6::$classMap;
$loader->prefixLengthsPsr4 = ComposerStaticInitd8553673db02b2a444a853f28e16196e::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitd8553673db02b2a444a853f28e16196e::$prefixDirsPsr4;
$loader->prefixesPsr0 = ComposerStaticInitd8553673db02b2a444a853f28e16196e::$prefixesPsr0;
}, null, ClassLoader::class);
}

View File

@@ -1,52 +0,0 @@
# phpspider -- PHP蜘蛛爬虫框架
《我用爬虫一天时间“偷了”知乎一百万用户只为证明PHP是世界上最好的语言 》所使用的程序
phpspider是一个爬虫开发框架。使用本框架你不用了解爬虫的底层技术实现爬虫被网站屏蔽、有些网站需要登录或验证码识别才能爬取等问题。简单几行PHP代码就可以创建自己的爬虫利用框架封装的多进程Worker类库代码更简洁执行效率更高速度更快。
demo目录下有一些特定网站的爬取规则只要你安装了PHP环境代码就可以在命令行下直接跑。 对爬虫感兴趣的开发者可以加QQ群一起讨论147824717。
下面以糗事百科为例, 来看一下我们的爬虫长什么样子:
```
$configs = array(
'name' => '糗事百科',
'domains' => array(
'qiushibaike.com',
'www.qiushibaike.com'
),
'scan_urls' => array(
'http://www.qiushibaike.com/'
),
'content_url_regexes' => array(
"http://www.qiushibaike.com/article/\d+"
),
'list_url_regexes' => array(
"http://www.qiushibaike.com/8hr/page/\d+\?s=\d+"
),
'fields' => array(
array(
// 抽取内容页的文章内容
'name' => "article_content",
'selector' => "//*[@id='single-next-link']",
'required' => true
),
array(
// 抽取内容页的文章作者
'name' => "article_author",
'selector' => "//div[contains(@class,'author')]//h2",
'required' => true
),
),
);
$spider = new phpspider($configs);
$spider->start();
```
爬虫的整体框架就是这样, 首先定义了一个$configs数组, 里面设置了待爬网站的一些信息, 然后通过调用```$spider = new phpspider($configs);```和```$spider->start();```来配置并启动爬虫.
#### 运行界面如下:
![](http://www.epooll.com/zhihu/pachong.gif)
更多详细内容,移步到:
[开发文档](http://doc.phpspider.org)

View File

@@ -1,77 +0,0 @@
<?php
/**
* This file is part of phpspider.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author seatle<seatle@foxmail.com>
* @copyright seatle<seatle@foxmail.com>
* @link http://www.phpspider.org/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace phpspider;
/**
* autoloader.
*/
class autoloader
{
/**
* Autoload root path.
*
* @var string
*/
protected static $_autoload_root_path = '';
/**
* Set autoload root path.
*
* @param string $root_path
* @return void
*/
public static function set_root_path($root_path)
{
self::$_autoload_root_path = $root_path;
}
/**
* Load files by namespace.
*
* @param string $name
* @return boolean
*/
public static function load_by_namespace($name)
{
$class_path = str_replace('\\', DIRECTORY_SEPARATOR, $name);
if (strpos($name, 'phpspider\\') === 0)
{
$class_file = __DIR__ . substr($class_path, strlen('phpspider')) . '.php';
}
else
{
if (self::$_autoload_root_path)
{
$class_file = self::$_autoload_root_path . DIRECTORY_SEPARATOR . $class_path . '.php';
}
if (empty($class_file) || !is_file($class_file))
{
$class_file = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . "$class_path.php";
}
}
if (is_file($class_file))
{
require_once($class_file);
if (class_exists($name, false))
{
return true;
}
}
return false;
}
}
spl_autoload_register('\phpspider\autoloader::load_by_namespace');

View File

@@ -1,38 +0,0 @@
{
"name": "owner888/phpspider",
"type": "library",
"keywords": [
"framework",
"phpspider"
],
"homepage": "http://www.phpspider.org",
"license": "MIT",
"description": "The PHPSpider Framework.",
"authors": [
{
"name": "Seatle Yang",
"email": "seatle@foxmail.com",
"homepage": "http://www.phpspider.org",
"role": "Developer"
}
],
"support": {
"email": "seatle@foxmail.com",
"issues": "https://github.com/owner888/phpspider/issues",
"forum": "http://wenda.phpspider.org/",
"wiki": "http://doc.phpspider.org/",
"source": "https://github.com/owner888/phpspider"
},
"require": {
"php": ">=5.5.0"
},
"suggest": {
"ext-pcntl、ext-redis": "For better performance. "
},
"autoload": {
"psr-4": {
"phpspider\\": "./"
}
},
"minimum-stability": "dev"
}

View File

@@ -1,64 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | PHPSpider [ A PHP Framework For Crawler ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 https://doc.phpspider.org All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Seatle Yang <seatle@foxmail.com>
// +----------------------------------------------------------------------
//----------------------------------
// PHPSpider缓存类文件
//----------------------------------
class cache
{
// 多进程下面不能用单例模式
//protected static $_instance;
/**
* 获取实例
*
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2016-04-10 22:55
*/
public static function init()
{
if(extension_loaded('Redis'))
{
$_instance = new Redis();
}
else
{
$errmsg = "extension redis is not installed";
log::add($errmsg, "Error");
return null;
}
// 这里不能用pconnect会报错Uncaught exception 'RedisException' with message 'read error on connection'
$_instance->connect($GLOBALS['config']['redis']['host'], $GLOBALS['config']['redis']['port'], $GLOBALS['config']['redis']['timeout']);
// 验证
if ($GLOBALS['config']['redis']['pass'])
{
if ( !$_instance->auth($GLOBALS['config']['redis']['pass']) )
{
$errmsg = "Redis Server authentication failed!!";
log::add($errmsg, "Error");
return null;
}
}
// 不序列化的话不能存数组用php的序列化方式其他语言又不能读取所以这里自己用json序列化了性能还比php的序列化好1.4倍
//$_instance->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // don't serialize data
//$_instance->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); // use built-in serialize/unserialize
//$_instance->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY); // use igBinary serialize/unserialize
$_instance->setOption(Redis::OPT_PREFIX, $GLOBALS['config']['redis']['prefix'] . ":");
return $_instance;
}
}

View File

@@ -1,55 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | PHPSpider [ A PHP Framework For Crawler ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 https://doc.phpspider.org All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Seatle Yang <seatle@foxmail.com>
// +----------------------------------------------------------------------
//----------------------------------
// PHPSpider公共入口文件
//----------------------------------
//namespace phpspider\core;
// Display errors.
ini_set('display_errors', 'on');
// Reporting all.
error_reporting(E_ALL);
// 永不超时
ini_set('max_execution_time', 0);
set_time_limit(0);
// 内存限制,如果外面设置的内存比 /etc/php/php-cli.ini 大,就不要设置了
if (intval(ini_get("memory_limit")) < 1024)
{
ini_set('memory_limit', '1024M');
}
if( PHP_SAPI != 'cli' )
{
exit("You must run the CLI environment\n");
}
// Date.timezone
if (!ini_get('date.timezone'))
{
date_default_timezone_set('Asia/Shanghai');
}
//核心库目录
define('CORE', dirname(__FILE__));
define('PATH_ROOT', CORE."/../");
define('PATH_DATA', CORE."/../data");
define('PATH_LIBRARY', CORE."/../library");
//系统配置
//if( file_exists( PATH_ROOT."/config/inc_config.php" ) )
//{
//require PATH_ROOT."/config/inc_config.php";
//}

View File

@@ -1,579 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | PHPSpider [ A PHP Framework For Crawler ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 https://doc.phpspider.org All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Seatle Yang <seatle@foxmail.com>
// +----------------------------------------------------------------------
//----------------------------------
// PHPSpider数据库类文件
//----------------------------------
namespace phpspider\core;
class db
{
private static $configs = array();
private static $rsid;
private static $links = array();
private static $link_name = 'default';
private static $autocommiting = false;
public static function _init()
{
// 获取配置
$config = self::$link_name == 'default' ? self::_get_default_config() : self::$configs[self::$link_name];
// 创建连接
if (empty(self::$links[self::$link_name]) || empty(self::$links[self::$link_name]['conn']))
{
// 第一次连接初始化fail和pid
if (empty(self::$links[self::$link_name]))
{
self::$links[self::$link_name]['fail'] = 0;
self::$links[self::$link_name]['pid'] = function_exists('posix_getpid') ? posix_getpid() : 0;
//echo "progress[".self::$links[self::$link_name]['pid']."] create db connect[".self::$link_name."]\n";
}
self::$links[self::$link_name]['conn'] = mysqli_connect($config['host'], $config['user'], $config['pass'], $config['name'], $config['port']);
if(mysqli_connect_errno())
{
self::$links[self::$link_name]['fail']++;
$errmsg = 'Mysql Connect failed['.self::$links[self::$link_name]['fail'].']: ' . mysqli_connect_error();
echo util::colorize(date("H:i:s") . " {$errmsg}\n\n", 'fail');
log::add($errmsg, "Error");
// 连接失败5次中断进程
if (self::$links[self::$link_name]['fail'] >= 5)
{
exit(250);
}
self::_init($config);
}
else
{
mysqli_query(self::$links[self::$link_name]['conn'], " SET character_set_connection=utf8, character_set_results=utf8, character_set_client=binary, sql_mode='' ");
}
}
else
{
$curr_pid = function_exists('posix_getpid') ? posix_getpid() : 0;
// 如果父进程已经生成资源就释放重新生成,因为多进程不能共享连接资源
if (self::$links[self::$link_name]['pid'] != $curr_pid)
{
self::clear_link();
}
}
}
/**
* 重新设置连接
* 传空的话就等于关闭数据库再连接
* 在多进程环境下如果主进程已经调用过了,子进程一定要调用一次 clear_link否则会报错
* Error while reading greeting packet. PID=19615这是两个进程互抢一个连接句柄引起的
*
* @param array $config
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2016-03-29 00:51
*/
public static function clear_link()
{
if(self::$links)
{
foreach(self::$links as $k=>$v)
{
@mysqli_close($v['conn']);
unset(self::$links[$k]);
}
}
// 注意,只会连接最后一个,不过貌似也够用了啊
self::_init();
}
/**
* 改变链接为指定配置的链接(如果不同时使用多个数据库,不会涉及这个操作)
* @parem $link_name 链接标识名
* @parem $config 多次使用时, 这个数组只需传递一次
* config 格式与 $GLOBALS['config']['db'] 一致
* @return void
*/
public static function set_connect($link_name, $config = array())
{
self::$link_name = $link_name;
if (!empty($config))
{
self::$configs[self::$link_name] = $config;
}
else
{
if (empty(self::$configs[self::$link_name]))
{
throw new Exception("You not set a config array for connect!");
}
}
}
/**
* 还原为默认连接(如果不同时使用多个数据库,不会涉及这个操作)
* @parem $config 指定配置默认使用inc_config.php的配置
* @return void
*/
public static function set_connect_default()
{
$config = self::_get_default_config();
self::set_connect('default', $config);
}
/**
* 获取默认配置
*/
protected static function _get_default_config()
{
if (empty(self::$configs['default']))
{
if (!is_array($GLOBALS['config']['db']))
{
exit('db.php _get_default_config()' . '没有mysql配置');
}
self::$configs['default'] = $GLOBALS['config']['db'];
}
return self::$configs['default'];
}
/**
* 返回查询游标
* @return rsid
*/
protected static function _get_rsid($rsid = '')
{
return $rsid == '' ? self::$rsid : $rsid;
}
public static function autocommit($mode = false)
{
if ( self::$autocommiting )
{
return true;
}
self::$autocommiting = true;
self::_init();
return mysqli_autocommit(self::$links[self::$link_name]['conn'], $mode);
}
public static function begin_tran()
{
return self::autocommit(false);
}
public static function commit()
{
mysqli_commit(self::$links[self::$link_name]['conn']);
self::autocommit(true);
return true;
}
public static function rollback()
{
mysqli_rollback(self::$links[self::$link_name]['conn']);
self::autocommit(true);
return true;
}
public static function query($sql)
{
$sql = trim($sql);
// 初始化数据库
self::_init();
self::$rsid = @mysqli_query(self::$links[self::$link_name]['conn'], $sql);
if (self::$rsid === false)
{
// 不要每次都ping浪费流量浪费性能执行出错了才重新连接
$errno = mysqli_errno(self::$links[self::$link_name]['conn']);
if ($errno == 2013 || $errno == 2006)
{
$errmsg = mysqli_error(self::$links[self::$link_name]['conn']);
log::add($errmsg, "Error");
@mysqli_close(self::$links[self::$link_name]['conn']);
self::$links[self::$link_name]['conn'] = null;
return self::query($sql);
}
$errmsg = "Query SQL: ".$sql;
log::add($errmsg, "Warning");
$errmsg = "Error SQL: ".mysqli_error(self::$links[self::$link_name]['conn']);
log::add($errmsg, "Warning");
$backtrace = debug_backtrace();
array_shift($backtrace);
$narr = array('class', 'type', 'function', 'file', 'line');
$err = "debug_backtrace\n";
foreach($backtrace as $i => $l)
{
foreach($narr as $k)
{
if( !isset($l[$k]) )
{
$l[$k] = '';
}
}
$err .= "[$i] in function {$l['class']}{$l['type']}{$l['function']} ";
if($l['file']) $err .= " in {$l['file']} ";
if($l['line']) $err .= " on line {$l['line']} ";
$err .= "\n";
}
log::add($err);
return false;
}
else
{
return self::$rsid;
}
}
public static function fetch($rsid = '')
{
$rsid = self::_get_rsid($rsid);
$row = mysqli_fetch_array($rsid, MYSQLI_ASSOC);
return $row;
}
public static function get_one($sql)
{
if (!preg_match("/limit/i", $sql))
{
$sql = preg_replace("/[,;]$/i", '', trim($sql)) . " limit 1 ";
}
$rsid = self::query($sql);
if ($rsid === false)
{
return array();
}
$row = self::fetch($rsid);
self::free($rsid);
return $row;
}
public static function get_all($sql)
{
$rsid = self::query($sql);
if ($rsid === false)
{
return array();
}
while ( $row = self::fetch($rsid) )
{
$rows[] = $row;
}
self::free($rsid);
return empty($rows) ? false : $rows;
}
public static function free($rsid)
{
return mysqli_free_result($rsid);
}
public static function insert_id()
{
return mysqli_insert_id(self::$links[self::$link_name]['conn']);
}
public static function affected_rows()
{
return mysqli_affected_rows(self::$links[self::$link_name]['conn']);
}
public static function insert($table = '', $data = null, $return_sql = false)
{
$items_sql = $values_sql = "";
foreach ($data as $k => $v)
{
$v = stripslashes($v);
$v = addslashes($v);
$items_sql .= "`$k`,";
$values_sql .= "\"$v\",";
}
$sql = "Insert Ignore Into `{$table}` (" . substr($items_sql, 0, -1) . ") Values (" . substr($values_sql, 0, -1) . ")";
if ($return_sql)
{
return $sql;
}
else
{
if (self::query($sql))
{
return mysqli_insert_id(self::$links[self::$link_name]['conn']);
}
else
{
return false;
}
}
}
public static function insert_batch($table = '', $set = NULL, $return_sql = FALSE)
{
if (empty($table) || empty($set))
{
return false;
}
$set = self::strsafe($set);
$fields = self::get_fields($table);
$keys_sql = $vals_sql = array();
foreach ($set as $i=>$val)
{
ksort($val);
$vals = array();
foreach ($val as $k => $v)
{
// 过滤掉数据库没有的字段
if (!in_array($k, $fields))
{
continue;
}
// 如果是第一个数组把key当做插入条件
if ($i == 0 && $k == 0)
{
$keys_sql[] = "`$k`";
}
$vals[] = "\"$v\"";
}
$vals_sql[] = implode(",", $vals);
}
$sql = "Insert Ignore Into `{$table}`(".implode(", ", $keys_sql).") Values (".implode("), (", $vals_sql).")";
if ($return_sql) return $sql;
$rt = self::query($sql);
$insert_id = self::insert_id();
$return = empty($insert_id) ? $rt : $insert_id;
return $return;
}
public static function update_batch($table = '', $set = NULL, $index = NULL, $where = NULL, $return_sql = FALSE)
{
if (empty($table) || is_null($set) || is_null($index))
{
// 不要用exit会中断程序
return false;
}
$set = self::strsafe($set);
$fields = self::get_fields($table);
$ids = array();
foreach ($set as $val)
{
ksort($val);
// 去重其实不去也可以因为相同的when只会执行第一个后面的就直接跳过不执行了
$key = md5($val[$index]);
$ids[$key] = $val[$index];
foreach (array_keys($val) as $field)
{
if ($field != $index)
{
$final[$field][$key] = 'When `'.$index.'` = "'.$val[$index].'" Then "'.$val[$field].'"';
}
}
}
//$ids = array_values($ids);
// 如果不是数组而且不为空,就转数组
if (!is_array($where) && !empty($where))
{
$where = array($where);
}
$where[] = $index.' In ("'.implode('","', $ids).'")';
$where = empty($where) ? "" : " Where ".implode(" And ", $where);
$sql = "Update `".$table."` Set ";
$cases = '';
foreach ($final as $k => $v)
{
// 过滤掉数据库没有的字段
if (!in_array($k, $fields))
{
continue;
}
$cases .= '`'.$k.'` = Case '."\n";
foreach ($v as $row)
{
$cases .= $row."\n";
}
$cases .= 'Else `'.$k.'` End, ';
}
$sql .= substr($cases, 0, -2);
// 其实不带 Where In ($index) 的条件也可以的
$sql .= $where;
if ($return_sql) return $sql;
$rt = self::query($sql);
$insert_id = self::affected_rows();
$return = empty($affected_rows) ? $rt : $affected_rows;
return $return;
}
public static function update($table = '', $data = array(), $where = null, $return_sql = false)
{
$sql = "UPDATE `{$table}` SET ";
foreach ($data as $k => $v)
{
$v = stripslashes($v);
$v = addslashes($v);
$sql .= "`{$k}` = \"{$v}\",";
}
if (!is_array($where))
{
$where = array($where);
}
// 删除空字段,不然array("")会成为WHERE
foreach ($where as $k => $v)
{
if (empty($v))
{
unset($where[$k]);
}
}
$where = empty($where) ? "" : " Where " . implode(" And ", $where);
$sql = substr($sql, 0, -1) . $where;
if ($return_sql)
{
return $sql;
}
else
{
if (self::query($sql))
{
return mysqli_affected_rows(self::$links[self::$link_name]['conn']);
}
else
{
return false;
}
}
}
public static function delete($table = '', $where = null, $return_sql = false)
{
// 小心全部被删除了
if (empty($where))
{
return false;
}
$where = 'Where ' . (!is_array($where) ? $where : implode(' And ', $where));
$sql = "Delete From `{$table}` {$where}";
if ($return_sql)
{
return $sql;
}
else
{
if (self::query($sql))
{
return mysqli_affected_rows(self::$links[self::$link_name]['conn']);
}
else
{
return false;
}
}
}
public static function ping()
{
if (!mysqli_ping(self::$links[self::$link_name]['conn']))
{
@mysqli_close(self::$links[self::$link_name]['conn']);
self::$links[self::$link_name]['conn'] = null;
self::_init();
}
}
public static function strsafe($array)
{
$arrays = array();
if(is_array($array)===true)
{
foreach ($array as $key => $val)
{
if(is_array($val)===true)
{
$arrays[$key] = self::strsafe($val);
}
else
{
//先去掉转义,避免下面重复转义了
$val = stripslashes($val);
//进行转义
$val = addslashes($val);
//处理addslashes没法处理的 _ % 字符
//$val = strtr($val, array('_'=>'\_', '%'=>'\%'));
$arrays[$key] = $val;
}
}
return $arrays;
}
else
{
$array = stripslashes($array);
$array = addslashes($array);
//$array = strtr($array, array('_'=>'\_', '%'=>'\%'));
return $array;
}
}
// 这个是给insert、update、insert_batch、update_batch用的
public static function get_fields($table)
{
// $sql = "SHOW COLUMNS FROM $table"; //和下面的语句效果一样
$rows = self::get_all("Desc `{$table}`");
$fields = array();
foreach ($rows as $k => $v)
{
// 过滤自增主键
// if ($v['Key'] != 'PRI')
if ($v['Extra'] != 'auto_increment')
{
$fields[] = $v['Field'];
}
}
return $fields;
}
public static function table_exists($table_name)
{
$sql = "SHOW TABLES LIKE '" . $table_name . "'";
$rsid = self::query($sql);
$table = self::fetch($rsid);
if (empty($table))
{
return false;
}
return true;
}
}

View File

@@ -1,101 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | PHPSpider [ A PHP Framework For Crawler ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 https://doc.phpspider.org All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Seatle Yang <seatle@foxmail.com>
// +----------------------------------------------------------------------
//----------------------------------
// PHPSpider公共入口文件
//----------------------------------
// 严格开发模式
error_reporting( E_ALL );
//ini_set('display_errors', 1);
// 永不超时
ini_set('max_execution_time', 0);
set_time_limit(0);
// 内存限制,如果外面设置的内存比 /etc/php/php-cli.ini 大,就不要设置了
if (intval(ini_get("memory_limit")) < 1024)
{
ini_set('memory_limit', '1024M');
}
if( PHP_SAPI != 'cli' )
{
exit("You must run the CLI environment\n");
}
// 设置时区
date_default_timezone_set('Asia/Shanghai');
// 引入PATH_DATA
require_once __DIR__ . '/constants.php';
// 核心库目录
define('CORE', dirname(__FILE__));
define('PATH_ROOT', CORE."/../");
define('PATH_DATA', CORE."/../data");
define('PATH_LIBRARY', CORE."/../library");
// 系统配置
if( file_exists( PATH_ROOT."/config/inc_config.php" ) )
{
require PATH_ROOT."/config/inc_config.php";
}
require CORE.'/log.php';
require CORE.'/requests.php';
require CORE.'/selector.php';
require CORE.'/util.php';
require CORE.'/db.php';
require CORE.'/cache.php';
require CORE."/worker.php";
require CORE."/phpspider.php";
// 启动的时候生成data目录
util::path_exists(PATH_DATA);
util::path_exists(PATH_DATA."/lock");
util::path_exists(PATH_DATA."/log");
util::path_exists(PATH_DATA."/cache");
util::path_exists(PATH_DATA."/status");
function autoload($classname) {
set_include_path(PATH_ROOT.'/library/');
spl_autoload($classname); //replaces include/require
}
spl_autoload_extensions('.php');
spl_autoload_register('autoload');
/**
* 自动加载类库处理
* @return void
*/
//function __autoload( $classname )
//{
//$classname = preg_replace("/[^0-9a-z_]/i", '', $classname);
//if( class_exists ( $classname ) ) {
//return true;
//}
//$classfile = $classname.'.php';
//try
//{
//if ( file_exists ( PATH_LIBRARY.'/'.$classfile ) )
//{
//require PATH_LIBRARY.'/'.$classfile;
//}
//else
//{
//throw new Exception ( 'Error: Cannot find the '.$classname );
//}
//}
//catch ( Exception $e )
//{
//log::error($e->getMessage().'|'.$classname);
//exit();
//}
//}

View File

@@ -1,119 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | PHPSpider [ A PHP Framework For Crawler ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 https://doc.phpspider.org All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Seatle Yang <seatle@foxmail.com>
// +----------------------------------------------------------------------
//----------------------------------
// PHPSpider日志类文件
//----------------------------------
namespace phpspider\core;
// 引入PATH_DATA
require_once __DIR__ . '/constants.php';
class log
{
public static $log_show = false;
public static $log_type = false;
public static $log_file = "data/phpspider.log";
public static $out_sta = "";
public static $out_end = "";
public static function note($msg)
{
self::$out_sta = self::$out_end = "";
self::msg($msg, 'note');
}
public static function info($msg)
{
self::$out_sta = self::$out_end = "";
self::msg($msg, 'info');
}
public static function warn($msg)
{
self::$out_sta = self::$out_end = "";
if (!util::is_win())
{
self::$out_sta = "\033[33m";
self::$out_end = "\033[0m";
}
self::msg($msg, 'warn');
}
public static function debug($msg)
{
self::$out_sta = self::$out_end = "";
if (!util::is_win())
{
self::$out_sta = "\033[36m";
self::$out_end = "\033[0m";
}
self::msg($msg, 'debug');
}
public static function error($msg)
{
self::$out_sta = self::$out_end = "";
if (!util::is_win())
{
self::$out_sta = "\033[31m";
self::$out_end = "\033[0m";
}
self::msg($msg, 'error');
}
public static function msg($msg, $log_type)
{
if ($log_type != 'note' && self::$log_type && strpos(self::$log_type, $log_type) === false)
{
return false;
}
if ($log_type == 'note')
{
$msg = self::$out_sta. $msg . "\n".self::$out_end;
}
else
{
$msg = self::$out_sta.date("Y-m-d H:i:s")." [{$log_type}] " . $msg .self::$out_end. "\n";
}
if(self::$log_show)
{
echo $msg;
}
file_put_contents(self::$log_file, $msg, FILE_APPEND | LOCK_EX);
}
/**
* 记录日志 XXX
* @param string $msg
* @param string $log_type Note|Warning|Error
* @return void
*/
public static function add($msg, $log_type = '')
{
if ($log_type != '')
{
$msg = date("Y-m-d H:i:s")." [{$log_type}] " . $msg . "\n";
}
if(self::$log_show)
{
echo $msg;
}
//file_put_contents(PATH_DATA."/log/".strtolower($log_type).".log", $msg, FILE_APPEND | LOCK_EX);
file_put_contents(PATH_DATA."/log/error.log", $msg, FILE_APPEND | LOCK_EX);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,998 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | PHPSpider [ A PHP Framework For Crawler ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 https://doc.phpspider.org All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Seatle Yang <seatle@foxmail.com>
// +----------------------------------------------------------------------
// +----------------------------------------------------------------------
// | GET请求
// | requests::get('http://www.test.com');
// | SERVER
// | $_GET
// +----------------------------------------------------------------------
// | POST请求
// | $data = array('name'=>'request');
// | requests::post('http://www.test.com', $data);
// | SERVER
// | $_POST
// +----------------------------------------------------------------------
// | POST RESTful请求
// | $data = array('name'=>'request');
// | $data_string = json_encode($data);
// | requests::set_header("Content-Type", "application/json");
// | requests::post('http://www.test.com', $data_string);
// | SERVER
// | file_get_contents('php://input')
// +----------------------------------------------------------------------
// | POST 文件上传
// | $data = array('file1'=>''./data/phpspider.log'');
// | requests::post('http://www.test.com', null, $data);
// | SERVER
// | $_FILES
// +----------------------------------------------------------------------
// | 代理
// | requests::set_proxy(array('223.153.69.150:42354'));
// | $html = requests::get('https://www.test.com');
// +----------------------------------------------------------------------
//----------------------------------
// PHPSpider请求类文件
//----------------------------------
namespace phpspider\core;
if (!function_exists('curl_file_create'))
{
function curl_file_create($filename, $mimetype = '', $postname = '')
{
return "@$filename;filename="
. ($postname ?: basename($filename))
. ($mimetype ? ";type=$mimetype" : '');
}
}
class requests
{
const VERSION = '2.0.1';
protected static $ch = null;
/**** Public variables ****/
/* user definable vars */
public static $timeout = 15;
public static $encoding = null;
public static $input_encoding = null;
public static $output_encoding = null;
public static $cookies = array(); // array of cookies to pass
// $cookies['username'] = "seatle";
public static $rawheaders = array(); // array of raw headers to send
public static $domain_cookies = array(); // array of cookies for domain to pass
public static $hosts = array(); // random host binding for make request faster
public static $headers = array(); // headers returned from server sent here
public static $useragents = array("requests/2.0.0"); // random agent we masquerade as
public static $client_ips = array(); // random ip we masquerade as
public static $proxies = array(); // random proxy ip
public static $raw = ""; // head + body content returned from server sent here
public static $head = ""; // head content
public static $content = ""; // The body before encoding
public static $text = ""; // The body after encoding
public static $info = array(); // curl info
public static $history = 302; // http request status before redirect. ex:30x
public static $status_code = 0; // http request status
public static $error = ""; // error messages sent here
/**
* set timeout
* $timeout 为数组时会分别设置connect和read
*
* @param init or array $timeout
* @return
*/
public static function set_timeout($timeout)
{
self::$timeout = $timeout;
}
/**
* 设置代理
* 如果代理有多个,请求时会随机使用
*
* @param mixed $proxies
* array (
* 'socks5://user1:pass2@host:port',
* 'socks5://user2:pass2@host:port'
*)
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2016-09-18 10:17
*/
public static function set_proxy($proxy)
{
self::$proxies = is_array($proxy) ? $proxy : array($proxy);
}
/**
* 删除代理
* 因为每个链接信息里面都有代理信息,有的链接需要,有的不需要,所以必须提供一个删除功能
*
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2018-07-16 17:59
*/
public static function del_proxy()
{
self::$proxies = array();
}
/**
* 自定义请求头部
* 请求头内容可以用 requests::$rawheaders 来获取
* 比如获取Content-Typerequests::$rawheaders['Content-Type']
*
* @param string $headers
* @return void
*/
public static function set_header($key, $value)
{
self::$rawheaders[$key] = $value;
}
/**
* 设置全局COOKIE
*
* @param string $cookie
* @return void
*/
public static function set_cookie($key, $value, $domain = '')
{
if (empty($key))
{
return false;
}
if (!empty($domain))
{
self::$domain_cookies[$domain][$key] = $value;
}
else
{
self::$cookies[$key] = $value;
}
return true;
}
/**
* 批量设置全局cookie
*
* @param mixed $cookies
* @param string $domain
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2017-08-03 18:06
*/
public static function set_cookies($cookies, $domain = '')
{
$cookies_arr = explode(';', $cookies);
if (empty($cookies_arr))
{
return false;
}
foreach ($cookies_arr as $cookie)
{
$cookie_arr = explode('=', $cookie, 2);
$key = $cookie_arr[0];
$value = empty($cookie_arr[1]) ? '' : $cookie_arr[1];
if (!empty($domain))
{
self::$domain_cookies[$domain][$key] = $value;
}
else
{
self::$cookies[$key] = $value;
}
}
return true;
}
/**
* 获取单一Cookie
*
* @param mixed $name cookie名称
* @param string $domain 不传则取全局cookie就是手动set_cookie的cookie
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2017-08-03 18:06
*/
public static function get_cookie($name, $domain = '')
{
if (!empty($domain) && !isset(self::$domain_cookies[$domain]))
{
return '';
}
$cookies = empty($domain) ? self::$cookies : self::$domain_cookies[$domain];
return isset($cookies[$name]) ? $cookies[$name] : '';
}
/**
* 获取Cookie数组
*
* @param string $domain 不传则取全局cookie就是手动set_cookie的cookie
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2017-08-03 18:06
*/
public static function get_cookies($domain = '')
{
if (!empty($domain) && !isset(self::$domain_cookies[$domain]))
{
return array();
}
return empty($domain) ? self::$cookies : self::$domain_cookies[$domain];
}
/**
* 删除Cookie
*
* @param string $domain 不传则删除全局Cookie
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2017-08-03 18:06
*/
public static function del_cookie($key, $domain = '')
{
if (empty($key))
{
return false;
}
if (!empty($domain) && !isset(self::$domain_cookies[$domain]))
{
return false;
}
if (!empty($domain))
{
if (isset(self::$domain_cookies[$domain][$key]))
{
unset(self::$domain_cookies[$domain][$key]);
}
}
else
{
if (isset(self::$cookies[$key]))
{
unset(self::$cookies[$key]);
}
}
return true;
}
/**
* 删除Cookie
*
* @param string $domain 不传则删除全局Cookie
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2017-08-03 18:06
*/
public static function del_cookies($domain = '')
{
if (!empty($domain) && !isset(self::$domain_cookies[$domain]))
{
return false;
}
if ( empty($domain) )
{
self::$cookies = array();
}
else
{
if (isset(self::$domain_cookies[$domain]))
{
unset(self::$domain_cookies[$domain]);
}
}
return true;
}
/**
* 设置随机的user_agent
*
* @param string $useragent
* @return void
*/
public static function set_useragent($useragent)
{
self::$useragents = is_array($useragent) ? $useragent : array($useragent);
}
/**
* set referer
*
*/
public static function set_referer($referer)
{
self::$rawheaders['Referer'] = $referer;
}
/**
* 设置伪造IP
* 传入数组则为随机IP
* @param string $ip
* @return void
*/
public static function set_client_ip($ip)
{
self::$client_ips = is_array($ip) ? $ip : array($ip);
}
/**
* 删除伪造IP
*
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2018-07-16 17:59
*/
public static function del_client_ip()
{
self::$client_ips = array();
}
/**
* 设置中文请求
*
* @param string $lang
* @return void
*/
public static function set_accept_language($lang = 'zh-CN')
{
self::$rawheaders['Accept-Language'] = $lang;
}
/**
* 设置Hosts
* 负载均衡到不同的服务器如果对方使用CDN采用这个是最好的了
*
* @param string $hosts
* @return void
*/
public static function set_hosts($host, $ips = array())
{
$ips = is_array($ips) ? $ips : array($ips);
self::$hosts[$host] = $ips;
}
/**
* 分割返回的header和body
* header用来判断编码和获取Cookie
* body用来判断编码得到编码前和编码后的内容
*
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2017-08-03 18:06
*/
public static function split_header_body()
{
$head = $body = '';
$head = substr(self::$raw, 0, self::$info['header_size']);
$body = substr(self::$raw, self::$info['header_size']);
// http header
self::$head = $head;
// The body before encoding
self::$content = $body;
//$http_headers = array();
//// 解析HTTP数据流
//if (!empty(self::$raw))
//{
//self::get_response_cookies($domain);
//// body里面可能有 \r\n\r\n但是第一个一定是HTTP Header去掉后剩下的就是body
//$array = explode("\r\n\r\n", self::$raw);
//foreach ($array as $k=>$v)
//{
//// post 方法会有两个http headerHTTP/1.1 100 Continue、HTTP/1.1 200 OK
//if (preg_match("#^HTTP/.*? 100 Continue#", $v))
//{
//unset($array[$k]);
//continue;
//}
//if (preg_match("#^HTTP/.*? \d+ #", $v))
//{
//$header = $v;
//unset($array[$k]);
//$http_headers = self::get_response_headers($v);
//}
//}
//$body = implode("\r\n\r\n", $array);
//}
// 设置了输出编码的转码,注意: xpath只支持utf-8iso-8859-1 不要转他本身就是utf-8
$body = self::encoding($body); //自动转码
// 转码后
self::$encoding = self::$output_encoding;
// The body after encoding
self::$text = $body;
return array($head, $body);
}
/**
* 获得域名相对应的Cookie
*
* @param mixed $header
* @param mixed $domain
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2017-08-03 18:06
*/
public static function get_response_cookies($header, $domain)
{
// 解析Cookie并存入 self::$cookies 方便调用
preg_match_all("/.*?Set\-Cookie: ([^\r\n]*)/i", $header, $matches);
$cookies = empty($matches[1]) ? array() : $matches[1];
// 解析到Cookie
if (!empty($cookies))
{
$cookies = implode(';', $cookies);
$cookies = explode(';', $cookies);
foreach ($cookies as $cookie)
{
$cookie_arr = explode('=', $cookie, 2);
// 过滤 httponly、secure
if (count($cookie_arr) < 2)
{
continue;
}
$cookie_name = !empty($cookie_arr[0]) ? trim($cookie_arr[0]) : '';
if (empty($cookie_name))
{
continue;
}
// 过滤掉domain路径
if (in_array(strtolower($cookie_name), array('path', 'domain', 'expires', 'max-age')))
{
continue;
}
self::$domain_cookies[$domain][trim($cookie_arr[0])] = trim($cookie_arr[1]);
}
}
}
/**
* 获得response header
* 此方法占时没有用到
*
* @param mixed $header
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2017-08-03 18:06
*/
public static function get_response_headers($header)
{
$headers = array();
$header_lines = explode("\n", $header);
if (!empty($header_lines))
{
foreach ($header_lines as $line)
{
$header_arr = explode(':', $line, 2);
$key = empty($header_arr[0]) ? '' : trim($header_arr[0]);
$val = empty($header_arr[1]) ? '' : trim($header_arr[1]);
if (empty($key) || empty($val))
{
continue;
}
$headers[$key] = $val;
}
}
self::$headers = $headers;
return self::$headers;
}
/**
* 获取编码
* @param $string
* @return string
*/
public static function get_encoding($string)
{
$encoding = mb_detect_encoding($string, array('UTF-8', 'GBK', 'GB2312', 'LATIN1', 'ASCII', 'BIG5', 'ISO-8859-1'));
return strtolower($encoding);
}
/**
* 移除页面head区域代码
* @param $html
* @return mixed
*/
private static function _remove_head($html)
{
return preg_replace('/<head.+?>.+<\/head>/is', '<head></head>', $html);
}
/**
* 简单的判断一下参数是否为一个URL链接
* @param string $str
* @return boolean
*/
private static function _is_url($url)
{
//$pattern = '/^http(s)?:\\/\\/.+/';
$pattern = "/\b(([\w-]+:\/\/?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|\/)))/";
if (preg_match($pattern, $url))
{
return true;
}
return false;
}
/**
* 初始化 CURL
*
*/
public static function init()
{
if (!is_resource ( self::$ch ))
{
self::$ch = curl_init ();
curl_setopt( self::$ch, CURLOPT_RETURNTRANSFER, true );
curl_setopt( self::$ch, CURLOPT_HEADER, false );
curl_setopt( self::$ch, CURLOPT_USERAGENT, "phpspider-requests/".self::VERSION );
// 如果设置了两个时间,就分开设置
if (is_array(self::$timeout))
{
curl_setopt( self::$ch, CURLOPT_CONNECTTIMEOUT, self::$timeout[0] );
curl_setopt( self::$ch, CURLOPT_TIMEOUT, self::$timeout[1]);
}
else
{
curl_setopt(self::$ch, CURLOPT_CONNECTTIMEOUT, ceil(self::$timeout / 2));
curl_setopt(self::$ch, CURLOPT_TIMEOUT, self::$timeout);
}
curl_setopt(self::$ch, CURLOPT_MAXREDIRS, 5); //maximum number of redirects allowed
// 在多线程处理场景下使用超时选项时会忽略signals对应的处理函数但是无耐的是还有小概率的crash情况发生
curl_setopt( self::$ch, CURLOPT_NOSIGNAL, true);
}
return self::$ch;
}
/**
* get 请求
*/
public static function get($url, $fields = array(), $allow_redirects = true, $cert = NULL)
{
self::init ();
return self::request($url, 'get', $fields, NULL, $allow_redirects, $cert);
}
/**
* post 请求
* $fields 有三种类型:1、数组2、http query3、json
* 1、array('name'=>'yangzetao')
* 2、http_build_query(array('name'=>'yangzetao'))
* 3、json_encode(array('name'=>'yangzetao'))
* 前两种是普通的post可以用$_POST方式获取
* 第三种是post stream( json rpc其实就是webservice )
* 虽然是post方式但是只能用流方式 http://input 后者 $HTTP_RAW_POST_DATA 获取
*
* @param mixed $url
* @param array $fields
* @param mixed $proxies
* @static
* @access public
* @return void
*/
public static function post($url, $fields = array(), $files = array(), $allow_redirects = true, $cert = NULL)
{
self::init ();
return self::request($url, 'POST', $fields, $files, $allow_redirects, $cert);
}
public static function put($url, $fields = array(), $allow_redirects = true, $cert = NULL)
{
self::init ();
return self::request($url, 'PUT', $fields, $allow_redirects, $cert);
}
public static function delete($url, $fields = array(), $allow_redirects = true, $cert = NULL)
{
self::init ();
return self::request($url, 'DELETE', $fields, $allow_redirects, $cert);
}
// 响应HTTP头域里的元信息
// 此方法被用来获取请求实体的元信息而不需要传输实体主体entity-body
// 此方法经常被用来测试超文本链接的有效性,可访问性,和最近的改变。.
public static function head($url, $fields = array(), $allow_redirects = true, $cert = NULL)
{
self::init ();
self::request($url, 'HEAD', $fields, $allow_redirects, $cert);
}
public static function options($url, $fields = array(), $allow_redirects = true, $cert = NULL)
{
self::init ();
return self::request($url, 'OPTIONS', $fields, $allow_redirects, $cert);
}
public static function patch($url, $fields = array(), $allow_redirects = true, $cert = NULL)
{
self::init ();
return self::request($url, 'PATCH', $fields, $allow_redirects, $cert);
}
/**
* request
*
* @param mixed $url 请求URL
* @param string $method 请求方法
* @param array $fields 表单字段
* @param array $files 上传文件
* @param mixed $cert CA证书
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2017-08-03 18:06
*/
public static function request($url, $method = 'GET', $fields = array(), $files = array(), $allow_redirects = true, $cert = NULL)
{
$method = strtoupper($method);
if(!self::_is_url($url))
{
self::$error = "You have requested URL ({$url}) is not a valid HTTP address";
return false;
}
// 如果是 get 方式,直接拼凑一个 url 出来
if ($method == 'GET' && !empty($fields))
{
$url = $url.(strpos($url, '?') === false ? '?' : '&').http_build_query($fields);
}
$parse_url = parse_url($url);
if (empty($parse_url) || empty($parse_url['host']) || !in_array($parse_url['scheme'], array('http', 'https')))
{
self::$error = "No connection adapters were found for '{$url}'";
return false;
}
$scheme = $parse_url['scheme'];
$domain = $parse_url['host'];
// 随机绑定 hosts做负载均衡
if (self::$hosts)
{
if (isset(self::$hosts[$domain]))
{
$hosts = self::$hosts[$domain];
$key = rand(0, count($hosts)-1);
$ip = $hosts[$key];
$url = str_replace($domain, $ip, $url);
self::$rawheaders['Host'] = $domain;
}
}
curl_setopt( self::$ch, CURLOPT_URL, $url );
if ($method != 'GET')
{
// 如果是 post 方式
if ($method == 'POST')
{
//curl_setopt( self::$ch, CURLOPT_POST, true );
$tmpheaders = array_change_key_case(self::$rawheaders, CASE_LOWER);
// 有些RESTful服务只接受JSON形态的数据
// CURLOPT_POST会把上傳的文件类型设为 multipart/form-data
// 把CURLOPT_POSTFIELDS的内容按multipart/form-data 的形式编码
// CURLOPT_CUSTOMREQUEST可以按指定内容上传
if ( isset($tmpheaders['content-type']) && $tmpheaders['content-type'] == 'application/json' )
{
curl_setopt( self::$ch, CURLOPT_CUSTOMREQUEST, $method );
}
else
{
curl_setopt( self::$ch, CURLOPT_POST, true );
}
$file_fields = array();
if (!empty($files))
{
foreach ($files as $postname => $file)
{
$filepath = realpath($file);
// 如果文件不存在
if (!file_exists($filepath))
{
continue;
}
$filename = basename($filepath);
$type = self::get_mimetype($filepath);
$file_fields[$postname] = curl_file_create($filepath, $type, $filename);
// curl -F "name=seatle&file=@/absolute/path/to/image.png" htt://localhost/uploadfile.php
//$cfile = '@'.realpath($filename).";type=".$type.";filename=".$filename;
}
}
}
else
{
self::$rawheaders['X-HTTP-Method-Override'] = $method;
curl_setopt( self::$ch, CURLOPT_CUSTOMREQUEST, $method );
}
if ( $method == 'POST' )
{
// 不是上传文件的用http_build_query, 能实现更好的兼容性,更小的请求数据包
if ( empty($file_fields) )
{
// post方式
if ( is_array($fields) )
{
$fields = http_build_query($fields);
}
}
else
{
// 有post数据
if ( is_array($fields) && !empty($fields) )
{
// 某些server可能会有问题
$fields = array_merge($fields, $file_fields);
}
else
{
$fields = $file_fields;
}
}
// 不能直接传数组不知道是什么Bug会非常慢
curl_setopt( self::$ch, CURLOPT_POSTFIELDS, $fields );
}
}
$cookies = self::get_cookies();
$domain_cookies = self::get_cookies($domain);
$cookies = array_merge($cookies, $domain_cookies);
// 是否设置了cookie
if (!empty($cookies))
{
foreach ($cookies as $key=>$value)
{
$cookie_arr[] = $key.'='.$value;
}
$cookies = implode('; ', $cookie_arr);
curl_setopt(self::$ch, CURLOPT_COOKIE, $cookies);
}
if (!empty(self::$useragents))
{
$key = rand(0, count(self::$useragents) - 1);
self::$rawheaders['User-Agent'] = self::$useragents[$key];
}
if (!empty(self::$client_ips))
{
$key = rand(0, count(self::$client_ips) - 1);
self::$rawheaders['CLIENT-IP'] = self::$client_ips[$key];
self::$rawheaders['X-FORWARDED-FOR'] = self::$client_ips[$key];
}
if (self::$rawheaders)
{
$http_headers = array();
foreach (self::$rawheaders as $k=>$v)
{
$http_headers[] = $k.': '.$v;
}
curl_setopt( self::$ch, CURLOPT_HTTPHEADER, $http_headers );
}
curl_setopt( self::$ch, CURLOPT_ENCODING, 'gzip' );
// 关闭验证
if ($scheme == 'https')
{
curl_setopt(self::$ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt(self::$ch, CURLOPT_SSL_VERIFYHOST, false);
}
if (self::$proxies)
{
$key = rand(0, count(self::$proxies) - 1);
$proxy = self::$proxies[$key];
curl_setopt( self::$ch, CURLOPT_PROXY, $proxy );
}
// header + bodyheader 里面有 cookie
curl_setopt( self::$ch, CURLOPT_HEADER, true );
// 请求跳转后的内容
if ($allow_redirects)
{
curl_setopt( self::$ch, CURLOPT_FOLLOWLOCATION, true);
}
self::$raw = curl_exec ( self::$ch );
// 真实url
//$location = curl_getinfo( self::$ch, CURLINFO_EFFECTIVE_URL);
self::$info = curl_getinfo( self::$ch );
//print_r(self::$info);
self::$status_code = self::$info['http_code'];
if (self::$raw === false)
{
self::$error = 'Curl error: ' . curl_error( self::$ch );
//trigger_error(self::$error, E_USER_WARNING);
}
// 关闭句柄
curl_close( self::$ch );
// 请求成功之后才把URL存起来
list($header, $text) = self::split_header_body();
self::$history = self::get_history($header);
self::$headers = self::get_response_headers($header);
self::get_response_cookies($header, $domain);
//$data = substr($data, 10);
//$data = gzinflate($data);
return $text;
}
public static function get_history($header)
{
$status_code = 0;
$lines = explode("\n", $header);
foreach ($lines as $line)
{
$line = trim($line);
if (preg_match("#^HTTP/.*? (\d+) Found#", $line, $out))
{
$status_code = empty($out[1]) ? 0 : intval($out[1]);
}
}
return $status_code;
}
// 获取 mimetype
public static function get_mimetype($filepath)
{
$fp = finfo_open(FILEINFO_MIME);
$mime = finfo_file($fp, $filepath);
finfo_close($fp);
$arr = explode(';', $mime);
$type = empty($arr[0]) ? '' : $arr[0];
return $type;
}
/**
* 拼凑文件和表单
* 占时没有用到
*
* @param mixed $post_fields
* @param mixed $file_fields
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2017-08-03 18:06
*/
public static function get_postfile_form($post_fields, $file_fields)
{
// 构造post数据
$data = '';
$delimiter = '-------------' . uniqid();
// 表单数据
foreach ($post_fields as $name => $content)
{
$data .= '--'.$delimiter."\r\n";
$data .= 'Content-Disposition: form-data; name = "'.$name.'"';
$data .= "\r\n\r\n";
$data .= $content;
$data .= "\r\n";
}
foreach ($file_fields as $input_name => $file)
{
$data .= '--'.$delimiter."\r\n";
$data .= 'Content-Disposition: form-data; name = "'.$input_name.'";'.
' filename="'.$file['filename'].'"'."\r\n";
$data .= "Content-Type: {$file['type']}\r\n";
$data .= "\r\n";
$data .= $file['content'];
$data .= "\r\n";
}
// 结束符
$data .= '--'.$delimiter."--\r\n";
//return array(
//CURLOPT_HTTPHEADER => array(
//'Content-Type:multipart/form-data;boundary=' . $delimiter,
//'Content-Length:' . strlen($data)
//),
//CURLOPT_POST => true,
//CURLOPT_POSTFIELDS => $data,
//);
return array($delimiter, $data);
}
/**
* html encoding transform
*
* @param string $html
* @param string $in
* @param string $out
* @param string $content
* @param string $mode
* auto|iconv|mb_convert_encoding
* @return string
*/
public static function encoding($html, $in = null, $out = null, $mode = 'auto')
{
$valid = array(
'auto',
'iconv',
'mb_convert_encoding',
);
if (isset(self::$output_encoding))
{
$out = self::$output_encoding;
}
if ( ! isset($out))
{
$out = 'UTF-8';
}
if ( ! in_array($mode, $valid))
{
throw new Exception('invalid mode, mode='.$mode);
}
$if = function_exists('mb_convert_encoding');
$if = $if && ($mode == 'auto' || $mode == 'mb_convert_encoding');
if (function_exists('iconv') && ($mode == 'auto' || $mode == 'iconv'))
{
$func = 'iconv';
}
elseif ($if)
{
$func = 'mb_convert_encoding';
}
else
{
throw new Exception('charsetTrans failed, no function');
}
$pattern = '/(<meta[^>]*?charset=([\"\']?))([a-z\d_\-]*)(\2[^>]*?>)/is';
if ( ! isset($in))
{
$n = preg_match($pattern, $html, $in);
if ($n > 0)
{
$in = $in[3];
}
else
{
$in = null;
}
if (empty($in) and function_exists('mb_detect_encoding'))
{
$in = mb_detect_encoding($html, array('UTF-8', 'GBK', 'GB2312', 'LATIN1', 'ASCII', 'BIG5', 'ISO-8859-1'));
}
}
if (isset($in))
{
if ($in == 'ISO-8859-1')
{
$in = 'UTF-8';
}
$old = error_reporting(error_reporting() & ~E_NOTICE);
$html = call_user_func($func, $in, $out.'//IGNORE', $html);
error_reporting($old);
$html = preg_replace($pattern, "\\1$out\\4", $html, 1);
}
return $html;
}
}

View File

@@ -1,588 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | PHPSpider [ A PHP Framework For Crawler ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 https://doc.phpspider.org All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Seatle Yang <seatle@foxmail.com>
// +----------------------------------------------------------------------
//----------------------------------
// PHPSpider选择器类文件
//----------------------------------
namespace phpspider\core;
use phpspider\library\phpquery;
use DOMDocument;
use DOMXpath;
use Exception;
class selector
{
/**
* 版本号
* @var string
*/
const VERSION = '1.0.2';
public static $dom = null;
public static $dom_auth = '';
public static $xpath = null;
public static $error = null;
public static function select($html, $selector, $selector_type = 'xpath')
{
if (empty($html) || empty($selector))
{
return false;
}
$selector_type = strtolower($selector_type);
if ($selector_type == 'xpath')
{
return self::_xpath_select($html, $selector);
}
elseif ($selector_type == 'regex')
{
return self::_regex_select($html, $selector);
}
elseif ($selector_type == 'css')
{
return self::_css_select($html, $selector);
}
}
public static function remove($html, $selector, $selector_type = 'xpath')
{
if (empty($html) || empty($selector))
{
return false;
}
$remove_html = "";
$selector_type = strtolower($selector_type);
if ($selector_type == 'xpath')
{
$remove_html = self::_xpath_select($html, $selector, true);
}
elseif ($selector_type == 'regex')
{
$remove_html = self::_regex_select($html, $selector, true);
}
elseif ($selector_type == 'css')
{
$remove_html = self::_css_select($html, $selector, true);
}
$html = str_replace($remove_html, "", $html);
return $html;
}
/**
* xpath选择器
*
* @param mixed $html
* @param mixed $selector
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2016-10-26 12:53
*/
private static function _xpath_select($html, $selector, $remove = false)
{
if (!is_object(self::$dom))
{
self::$dom = new DOMDocument();
}
// 如果加载的不是之前的HTML内容替换一下验证标识
if (self::$dom_auth != md5($html))
{
self::$dom_auth = md5($html);
@self::$dom->loadHTML('<?xml encoding="UTF-8">'.$html);
self::$xpath = new DOMXpath(self::$dom);
}
//libxml_use_internal_errors(true);
//self::$dom->loadHTML('<?xml encoding="UTF-8">'.$html);
//$errors = libxml_get_errors();
//if (!empty($errors))
//{
//print_r($errors);
//exit;
//}
$elements = @self::$xpath->query($selector);
if ($elements === false)
{
self::$error = "the selector in the xpath(\"{$selector}\") syntax errors";
// 不应该返回false因为isset(false)为true更不能通过 !$values 去判断,因为!0为true所以这里只能返回null
//return false;
return null;
}
$result = array();
if (!is_null($elements))
{
foreach ($elements as $element)
{
// 如果是删除操作,取一整块代码
if ($remove)
{
$content = self::$dom->saveXml($element);
}
else
{
$nodeName = $element->nodeName;
$nodeType = $element->nodeType; // 1.Element 2.Attribute 3.Text
//$nodeAttr = $element->getAttribute('src');
//$nodes = util::node_to_array(self::$dom, $element);
//echo $nodes['@src']."\n";
// 如果是img标签直接取src值
if ($nodeType == 1 && in_array($nodeName, array('img')))
{
$content = $element->getAttribute('src');
}
// 如果是标签属性,直接取节点值
elseif ($nodeType == 2 || $nodeType == 3 || $nodeType == 4)
{
$content = $element->nodeValue;
}
else
{
// 保留nodeValue里的html符号给children二次提取
$content = self::$dom->saveXml($element);
//$content = trim(self::$dom->saveHtml($element));
$content = preg_replace(array("#^<{$nodeName}.*>#isU","#</{$nodeName}>$#isU"), array('', ''), $content);
}
}
$result[] = $content;
}
}
if (empty($result))
{
return null;
}
// 如果只有一个元素就直接返回string否则返回数组
return count($result) > 1 ? $result : $result[0];
}
/**
* css选择器
*
* @param mixed $html
* @param mixed $selector
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2016-10-26 12:53
*/
private static function _css_select($html, $selector, $remove = false)
{
$selector = self::css_to_xpath($selector);
//echo $selector."\n";
//exit("\n");
return self::_xpath_select($html, $selector, $remove);
// 如果加载的不是之前的HTML内容替换一下验证标识
//if (self::$dom_auth['css'] != md5($html))
//{
//self::$dom_auth['css'] = md5($html);
//phpQuery::loadDocumentHTML($html);
//}
//if ($remove)
//{
//return phpQuery::pq($selector)->remove();
//}
//else
//{
//return phpQuery::pq($selector)->html();
//}
}
/**
* 正则选择器
*
* @param mixed $html
* @param mixed $selector
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2016-10-26 12:53
*/
private static function _regex_select($html, $selector, $remove = false)
{
if(@preg_match_all($selector, $html, $out) === false)
{
self::$error = "the selector in the regex(\"{$selector}\") syntax errors";
return null;
}
$count = count($out);
$result = array();
// 一个都没有匹配到
if ($count == 0)
{
return null;
}
// 只匹配一个,就是只有一个 ()
elseif ($count == 2)
{
// 删除的话取匹配到的所有内容
if ($remove)
{
$result = $out[0];
}
else
{
$result = $out[1];
}
}
else
{
for ($i = 1; $i < $count; $i++)
{
// 如果只有一个元素,就直接返回好了
$result[] = count($out[$i]) > 1 ? $out[$i] : $out[$i][0];
}
}
if (empty($result))
{
return null;
}
return count($result) > 1 ? $result : $result[0];
}
public static function find_all($html, $selector)
{
}
public static function css_to_xpath($selectors)
{
$queries = self::parse_selector($selectors);
$delimiter_before = false;
$xquery = '';
foreach($queries as $s)
{
// TAG
$is_tag = preg_match('@^[\w|\||-]+$@', $s) || $s == '*';
if ($is_tag)
{
$xquery .= $s;
}
// ID
else if ($s[0] == '#')
{
if ($delimiter_before)
{
$xquery .= '*';
}
// ID用精确查询
$xquery .= "[@id='".substr($s, 1)."']";
}
// CLASSES
else if ($s[0] == '.')
{
if ($delimiter_before)
{
$xquery .= '*';
}
// CLASS用模糊查询
$xquery .= "[contains(@class,'".substr($s, 1)."')]";
}
// ATTRIBUTES
else if ($s[0] == '[')
{
if ($delimiter_before)
{
$xquery .= '*';
}
// strip side brackets
$attr = trim($s, '][');
// attr with specifed value
if (mb_strpos($s, '='))
{
$value = null;
list($attr, $value) = explode('=', $attr);
$value = trim($value, "'\"");
if (self::is_regexp($attr))
{
// cut regexp character
$attr = substr($attr, 0, -1);
$xquery .= "[@{$attr}]";
}
else
{
$xquery .= "[@{$attr}='{$value}']";
}
}
// attr without specified value
else
{
$xquery .= "[@{$attr}]";
}
}
// ~ General Sibling Selector
else if ($s[0] == '~')
{
}
// + Adjacent sibling selectors
else if ($s[0] == '+')
{
}
// PSEUDO CLASSES
else if ($s[0] == ':')
{
}
// DIRECT DESCENDANDS
else if ($s == '>')
{
$xquery .= '/';
$delimiter_before = 2;
}
// ALL DESCENDANDS
else if ($s == ' ')
{
$xquery .= '//';
$delimiter_before = 2;
}
// ERRORS
else
{
exit("Unrecognized token '$s'");
}
$delimiter_before = $delimiter_before === 2;
}
return $xquery;
}
/**
* @access private
*/
public static function parse_selector($query)
{
$query = trim( preg_replace( '@\s+@', ' ', preg_replace('@\s*(>|\\+|~)\s*@', '\\1', $query) ) );
$queries = array();
if ( !$query )
{
return $queries;
}
$special_chars = array('>',' ');
$special_chars_mapping = array();
$strlen = mb_strlen($query);
$class_chars = array('.', '-');
$pseudo_chars = array('-');
$tag_chars = array('*', '|', '-');
// split multibyte string
// http://code.google.com/p/phpquery/issues/detail?id=76
$_query = array();
for ( $i=0; $i<$strlen; $i++ )
{
$_query[] = mb_substr($query, $i, 1);
}
$query = $_query;
// it works, but i dont like it...
$i = 0;
while( $i < $strlen )
{
$c = $query[$i];
$tmp = '';
// TAG
if ( self::is_char($c) || in_array($c, $tag_chars) )
{
while(isset($query[$i]) && (self::is_char($query[$i]) || in_array($query[$i], $tag_chars)))
{
$tmp .= $query[$i];
$i++;
}
$queries[] = $tmp;
}
// IDs
else if ( $c == '#' )
{
$i++;
while( isset($query[$i]) && (self::is_char($query[$i]) || $query[$i] == '-') )
{
$tmp .= $query[$i];
$i++;
}
$queries[] = '#'.$tmp;
}
// SPECIAL CHARS
else if ( in_array($c, $special_chars) )
{
$queries[] = $c;
$i++;
// MAPPED SPECIAL MULTICHARS
// } else if ( $c.$query[$i+1] == '//') {
// $return[] = ' ';
// $i = $i+2;
}
// MAPPED SPECIAL CHARS
else if ( isset($special_chars_mapping[$c]))
{
$queries[] = $special_chars_mapping[$c];
$i++;
}
// COMMA
else if ( $c == ',' )
{
$i++;
while( isset($query[$i]) && $query[$i] == ' ')
{
$i++;
}
}
// CLASSES
else if ($c == '.')
{
while( isset($query[$i]) && (self::is_char($query[$i]) || in_array($query[$i], $class_chars)))
{
$tmp .= $query[$i];
$i++;
}
$queries[] = $tmp;
}
// ~ General Sibling Selector
else if ($c == '~')
{
$space_allowed = true;
$tmp .= $query[$i++];
while( isset($query[$i])
&& (self::is_char($query[$i])
|| in_array($query[$i], $class_chars)
|| $query[$i] == '*'
|| ($query[$i] == ' ' && $space_allowed)
))
{
if ($query[$i] != ' ')
{
$space_allowed = false;
}
$tmp .= $query[$i];
$i++;
}
$queries[] = $tmp;
}
// + Adjacent sibling selectors
else if ($c == '+')
{
$space_allowed = true;
$tmp .= $query[$i++];
while( isset($query[$i])
&& (self::is_char($query[$i])
|| in_array($query[$i], $class_chars)
|| $query[$i] == '*'
|| ($space_allowed && $query[$i] == ' ')
))
{
if ($query[$i] != ' ')
$space_allowed = false;
$tmp .= $query[$i];
$i++;
}
$queries[] = $tmp;
}
// ATTRS
else if ($c == '[')
{
$stack = 1;
$tmp .= $c;
while( isset($query[++$i]))
{
$tmp .= $query[$i];
if ( $query[$i] == '[')
{
$stack++;
}
else if ( $query[$i] == ']')
{
$stack--;
if (! $stack )
{
break;
}
}
}
$queries[] = $tmp;
$i++;
}
// PSEUDO CLASSES
else if ($c == ':')
{
$stack = 1;
$tmp .= $query[$i++];
while( isset($query[$i]) && (self::is_char($query[$i]) || in_array($query[$i], $pseudo_chars)))
{
$tmp .= $query[$i];
$i++;
}
// with arguments ?
if ( isset($query[$i]) && $query[$i] == '(')
{
$tmp .= $query[$i];
$stack = 1;
while( isset($query[++$i]))
{
$tmp .= $query[$i];
if ( $query[$i] == '(')
{
$stack++;
}
else if ( $query[$i] == ')')
{
$stack--;
if (! $stack )
{
break;
}
}
}
$queries[] = $tmp;
$i++;
}
else
{
$queries[] = $tmp;
}
}
else
{
$i++;
}
}
if (isset($queries[0]))
{
if (isset($queries[0][0]) && $queries[0][0] == ':')
{
array_unshift($queries, '*');
}
if ($queries[0] != '>')
{
array_unshift($queries, ' ');
}
}
return $queries;
}
public static function is_char($char)
{
return preg_match('@\w@', $char);
}
/**
* 模糊匹配
* ^ 前缀字符串
* * 包含字符串
* $ 后缀字符串
* @access private
*/
protected static function is_regexp($pattern)
{
return in_array(
$pattern[ mb_strlen($pattern)-1 ],
array('^','*','$')
);
}
}

View File

@@ -1,936 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | PHPSpider [ A PHP Framework For Crawler ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 https://doc.phpspider.org All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Seatle Yang <seatle@foxmail.com>
// +----------------------------------------------------------------------
//----------------------------------
// PHPSpider实用函数集合类文件
//----------------------------------
namespace phpspider\core;
// 引入PATH_DATA
require_once __DIR__ . '/constants.php';
class util
{
/**
* 文件锁
* 如果没有锁,就加一把锁并且执行逻辑,然后删除锁
* if (!util::lock('statistics_offer'))
* {
* util::lock('statistics_offer');
* ...
* util::unlock('statistics_offer');
* }
* 否则输出锁存在
* else
* {
* echo "process has been locked\n";
* }
*
* @param mixed $lock_name
* @param int $lock_timeout
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2016-02-18 14:28
*/
public static function lock($lock_name, $lock_timeout = 600)
{
$lock = util::get_file(PATH_DATA."/lock/{$lock_name}.lock");
if ($lock)
{
$time = time() - $lock;
// 还没到10分钟说明进程还活着
if ($time < $lock_timeout)
{
return true;
}
unlink(PATH_DATA."/lock/{$lock_name}.lock");
}
util::put_file(PATH_DATA."/lock/{$lock_name}.lock", time());
return false;
}
public static function unlock($lock_name)
{
unlink(PATH_DATA."/lock/{$lock_name}.lock");
}
public static function time2second($time, $is_log = true)
{
if(is_numeric($time))
{
$value = array(
"years" => 0, "days" => 0, "hours" => 0,
"minutes" => 0, "seconds" => 0,
);
if($time >= 31556926)
{
$value["years"] = floor($time/31556926);
$time = ($time%31556926);
}
if($time >= 86400)
{
$value["days"] = floor($time/86400);
$time = ($time%86400);
}
if($time >= 3600)
{
$value["hours"] = floor($time/3600);
$time = ($time%3600);
}
if($time >= 60)
{
$value["minutes"] = floor($time/60);
$time = ($time%60);
}
$value["seconds"] = floor($time);
//return (array) $value;
//$t = $value["years"] ."y ". $value["days"] ."d ". $value["hours"] ."h ". $value["minutes"] ."m ".$value["seconds"]."s";
if ($is_log)
{
$t = $value["days"] ."d ". $value["hours"] ."h ". $value["minutes"] ."m ".$value["seconds"]."s";
}
else
{
$t = $value["days"] ." days ". $value["hours"] ." hours ". $value["minutes"] ." minutes";
}
return $t;
}
else
{
return false;
}
}
public static function get_days($day_sta, $day_end = true, $range = 86400)
{
if ($day_end === true) $day_end = date('Y-m-d');
return array_map(function ($time) {
return date('Y-m-d', $time);
}, range(strtotime($day_sta), strtotime($day_end), $range));
}
/**
* 获取文件行数
*
* @param mixed $filepath
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2016-03-31 21:54
*/
public static function get_file_line($filepath)
{
$line = 0 ;
$fp = fopen($filepath , 'r');
if (!$fp)
{
return 0;
}
//获取文件的一行内容注意需要php5才支持该函数
while( stream_get_line($fp,8192,"\n") ){
$line++;
}
fclose($fp);//关闭文件
return $line;
}
/**
* 获得表数
*
* @param mixed $table_name 表名
* @param mixed $item_value 唯一索引
* @param int $table_num 表数量
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2015-10-22 23:25
*/
public static function get_table_num($item_value, $table_num = 100)
{
//sha1:返回一个40字符长度的16进制数字
$item_value = sha1(strtolower($item_value));
//base_convert:进制建转换下面是把16进制转成10进制方便做除法运算
//str_pad:把字符串填充为指定的长度下面是在左边加0表数量大于100就3位否则2位
$step = $table_num > 100 ? 3 : 2;
$item_value = str_pad(base_convert(substr($item_value, -2), 16, 10) % $table_num, $step, "0", STR_PAD_LEFT);
return $item_value;
}
/**
* 获得表面
*
* @param mixed $table_name 表名
* @param mixed $item_value 唯一索引
* @param int $table_num 表数量
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2015-10-22 23:25
*/
public static function get_table_name($table_name, $item_value, $table_num = 100)
{
//sha1:返回一个40字符长度的16进制数字
$item_value = sha1(strtolower($item_value));
//base_convert:进制建转换下面是把16进制转成10进制方便做除法运算
//str_pad:把字符串填充为指定的长度下面是在左边加0共3位
$step = $table_num > 100 ? 3 : 2;
$item_value = str_pad(base_convert(substr($item_value, -2), 16, 10) % $table_num, $step, "0", STR_PAD_LEFT);
return $table_name."_".$item_value;
}
// 获得当前使用内存
public static function memory_get_usage()
{
$memory = memory_get_usage();
return self::format_bytes($memory);
}
// 获得最高使用内存
public static function memory_get_peak_usage()
{
$memory = memory_get_peak_usage();
return self::format_bytes($memory);
}
// 转换大小单位
public static function format_bytes($size)
{
$unit = array('b', 'kb', 'mb', 'gb', 'tb', 'pb');
return @round($size / pow(1024, ($i = floor(log($size, 1024)))), 2) . ' ' . $unit[$i];
}
/**
* 获取数组大小
*
* @param mixed $arr 数组
* @return string
*/
public static function array_size($arr)
{
ob_start();
print_r($arr);
$mem = ob_get_contents();
ob_end_clean();
$mem = preg_replace("/\n +/", "", $mem);
$mem = strlen($mem);
return self::format_bytes($mem);
}
/**
* 数字随机数
*
* @param int $num
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2016-09-18 10:17
*/
public static function rand_num($num = 7)
{
$rand = "";
for ($i = 0; $i < $num; $i ++)
{
$rand .= mt_rand(0, 9);
}
return $rand;
}
/**
* 字母数字混合随机数
*
* @param int $num
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2016-09-18 10:17
*/
public static function rand_str($num = 10)
{
$chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
$string = "";
for ($i = 0; $i < $num; $i ++)
{
$string .= substr($chars, rand(0, strlen($chars)), 1);
}
return $string;
}
/**
* 汉字转拼音
*
* @param mixed $str 汉字
* @param int $ishead
* @param int $isclose
* @static
* @access public
* @return string
*/
public static function pinyin($str, $ishead = 0, $isclose = 1)
{
// $str = iconv("utf-8", "gbk//ignore", $str);
$str = mb_convert_encoding($str, "gbk", "utf-8");
global $pinyins;
$restr = '';
$str = trim($str);
$slen = strlen($str);
if ($slen < 2)
{
return $str;
}
if (count($pinyins) == 0)
{
$fp = fopen(PATH_DATA . '/pinyin.dat', 'r');
while (!feof($fp))
{
$line = trim(fgets($fp));
$pinyins[$line[0] . $line[1]] = substr($line, 3, strlen($line) - 3);
}
fclose($fp);
}
for ($i = 0; $i < $slen; $i ++)
{
if (ord($str[$i]) > 0x80)
{
$c = $str[$i] . $str[$i + 1];
$i ++;
if (isset($pinyins[$c]))
{
if ($ishead == 0)
{
$restr .= $pinyins[$c];
}
else
{
$restr .= $pinyins[$c][0];
}
}
else
{
// $restr .= "_";
}
}
else if (preg_match("/[a-z0-9]/i", $str[$i]))
{
$restr .= $str[$i];
}
else
{
// $restr .= "_";
}
}
if ($isclose == 0)
{
unset($pinyins);
}
return $restr;
}
/**
* 生成字母前缀
*
* @param mixed $s0
* @return char
* @author seatle <seatle@foxmail.com>
* @created time :2016-09-18 10:17
*/
public static function letter_first($s0)
{
$firstchar_ord = ord(strtoupper($s0{0}));
if (($firstchar_ord >= 65 and $firstchar_ord <= 91) or ($firstchar_ord >= 48 and $firstchar_ord <= 57)) return $s0{0};
// $s = iconv("utf-8", "gbk//ignore", $s0);
$s = mb_convert_encoding($s0, "gbk", "utf-8");
$asc = ord($s{0}) * 256 + ord($s{1}) - 65536;
if ($asc >= -20319 and $asc <= -20284) return "A";
if ($asc >= -20283 and $asc <= -19776) return "B";
if ($asc >= -19775 and $asc <= -19219) return "C";
if ($asc >= -19218 and $asc <= -18711) return "D";
if ($asc >= -18710 and $asc <= -18527) return "E";
if ($asc >= -18526 and $asc <= -18240) return "F";
if ($asc >= -18239 and $asc <= -17923) return "G";
if ($asc >= -17922 and $asc <= -17418) return "H";
if ($asc >= -17417 and $asc <= -16475) return "J";
if ($asc >= -16474 and $asc <= -16213) return "K";
if ($asc >= -16212 and $asc <= -15641) return "L";
if ($asc >= -15640 and $asc <= -15166) return "M";
if ($asc >= -15165 and $asc <= -14923) return "N";
if ($asc >= -14922 and $asc <= -14915) return "O";
if ($asc >= -14914 and $asc <= -14631) return "P";
if ($asc >= -14630 and $asc <= -14150) return "Q";
if ($asc >= -14149 and $asc <= -14091) return "R";
if ($asc >= -14090 and $asc <= -13319) return "S";
if ($asc >= -13318 and $asc <= -12839) return "T";
if ($asc >= -12838 and $asc <= -12557) return "W";
if ($asc >= -12556 and $asc <= -11848) return "X";
if ($asc >= -11847 and $asc <= -11056) return "Y";
if ($asc >= -11055 and $asc <= -10247) return "Z";
return 0; // null
}
/**
* 获得某天前的时间戳
*
* @param mixed $day
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2016-09-18 10:17
*/
public static function getxtime($day)
{
$day = intval($day);
return mktime(23, 59, 59, date("m"), date("d") - $day, date("y"));
}
/**
* 读文件
*/
public static function get_file($url, $timeout = 10)
{
if (function_exists('curl_init'))
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
$content = curl_exec($ch);
curl_close($ch);
if ($content) return $content;
}
$ctx = stream_context_create(array('http' => array('timeout' => $timeout)));
$content = @file_get_contents($url, 0, $ctx);
if ($content) return $content;
return false;
}
/**
* 写文件,如果文件目录不存在,则递归生成
*/
public static function put_file($file, $content, $flag = 0)
{
$pathinfo = pathinfo($file);
if (!empty($pathinfo['dirname']))
{
if (file_exists($pathinfo['dirname']) === false)
{
if (@mkdir($pathinfo['dirname'], 0777, true) === false)
{
return false;
}
}
}
if ($flag === FILE_APPEND)
{
// 多个php-fpm写一个文件的时候容易丢失要加锁
//return @file_put_contents($file, $content, FILE_APPEND|LOCK_EX);
return @file_put_contents($file, $content, FILE_APPEND);
}
else
{
return @file_put_contents($file, $content, LOCK_EX);
}
}
/**
* 检查路径是否存在,不存在则递归生成路径
*
* @param mixed $path 路径
* @static
* @access public
* @return bool or string
*/
public static function path_exists($path)
{
$pathinfo = pathinfo($path . '/tmp.txt');
if (!empty($pathinfo['dirname']))
{
if (file_exists($pathinfo['dirname']) === false)
{
if (mkdir($pathinfo['dirname'], 0777, true) === false)
{
return false;
}
}
}
return $path;
}
/**
* 递归删除目录
*
* @param mixed $dir
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2016-09-18 10:17
*/
public static function deldir($dir)
{
//先删除目录下的文件:
$dh = opendir($dir);
while ($file = readdir($dh))
{
if($file!="." && $file!="..")
{
$fullpath = $dir."/".$file;
if(!is_dir($fullpath))
{
unlink($fullpath);
}
else
{
self::deldir($fullpath);
}
}
}
closedir($dh);
//删除当前文件夹:
if(rmdir($dir))
{
return true;
}
else
{
return false;
}
}
/**
* 递归修改目录权限
*
* @param mixed $path 目录
* @param mixed $filemode 权限
* @return bool
*/
public static function chmodr($path, $filemode)
{
if (!is_dir($path))
{
return @chmod($path, $filemode);
}
$dh = opendir($path);
while (($file = readdir($dh)) !== false)
{
if ($file != '.' && $file != '..')
{
$fullpath = $path . '/' . $file;
if (is_link($fullpath))
{
return FALSE;
}
elseif (!is_dir($fullpath) && !@chmod($fullpath, $filemode))
{
return FALSE;
}
elseif (!self::chmodr($fullpath, $filemode))
{
return FALSE;
}
}
}
closedir($dh);
if (@chmod($path, $filemode))
{
return TRUE;
}
else
{
return FALSE;
}
}
/**
* 数组格式化为CSV
*
* @param mixed $data
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2016-07-29 11:32
*/
public static function format_csv($data)
{
foreach ($data as $k=>$v)
{
$v = str_replace(",", "", $v);
$v = str_replace("", "", $v);
$data[$k] = $v;
}
return implode(",", $data);
}
/**
* 判断是否为utf8字符串
* @parem $str
* @return bool
*/
public static function is_utf8($str)
{
if ($str === mb_convert_encoding(mb_convert_encoding($str, "UTF-32", "UTF-8"), "UTF-8", "UTF-32"))
{
return true;
}
else
{
return false;
}
}
/**
* 获取文件编码
* @param $string
* @return string
*/
public static function get_encoding($string)
{
$encoding = mb_detect_encoding($string, array('UTF-8', 'GBK', 'GB2312', 'LATIN1', 'ASCII', 'BIG5'));
return strtolower($encoding);
}
/**
* 转换数组值的编码格式
* @param array $arr
* @param string $toEncoding
* @param string $fromEncoding
* @return array
*/
public static function array_iconv($arr, $from_encoding, $to_encoding)
{
eval('$arr = '.iconv($from_encoding, $to_encoding.'//IGNORE', var_export($arr,TRUE)).';');
return $arr;
}
/**
* 从普通时间返回Linux时间截(strtotime中文处理版)
* @parem string $dtime
* @return int
*/
public static function cn_strtotime($dtime)
{
if (!preg_match("/[^0-9]/", $dtime))
{
return $dtime;
}
$dtime = trim($dtime);
$dt = Array(1970, 1, 1, 0, 0, 0);
$dtime = preg_replace("/[\r\n\t]|日|秒/", " ", $dtime);
$dtime = str_replace("", "-", $dtime);
$dtime = str_replace("", "-", $dtime);
$dtime = str_replace("", ":", $dtime);
$dtime = str_replace("", ":", $dtime);
$dtime = trim(preg_replace("/[ ]{1,}/", " ", $dtime));
$ds = explode(" ", $dtime);
$ymd = explode("-", $ds[0]);
if (!isset($ymd[1]))
{
$ymd = explode(".", $ds[0]);
}
if (isset($ymd[0]))
{
$dt[0] = $ymd[0];
}
if (isset($ymd[1])) $dt[1] = $ymd[1];
if (isset($ymd[2])) $dt[2] = $ymd[2];
if (strlen($dt[0]) == 2) $dt[0] = '20' . $dt[0];
if (isset($ds[1]))
{
$hms = explode(":", $ds[1]);
if (isset($hms[0])) $dt[3] = $hms[0];
if (isset($hms[1])) $dt[4] = $hms[1];
if (isset($hms[2])) $dt[5] = $hms[2];
}
foreach ($dt as $k => $v)
{
$v = preg_replace("/^0{1,}/", '', trim($v));
if ($v == '')
{
$dt[$k] = 0;
}
}
$mt = mktime($dt[3], $dt[4], $dt[5], $dt[1], $dt[2], $dt[0]);
if (!empty($mt))
{
return $mt;
}
else
{
return strtotime($dtime);
}
}
public static function cn_substr($string, $length = 80, $etc = '...', $count_words = true)
{
mb_internal_encoding("UTF-8");
if ($length == 0) return '';
if (strlen($string) <= $length) return $string;
preg_match_all("/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|\xe0[\xa0-\xbf][\x80-\xbf]|[\xe1-\xef][\x80-\xbf][\x80-\xbf]|\xf0[\x90-\xbf][\x80-\xbf][\x80-\xbf]|[\xf1-\xf7][\x80-\xbf][\x80-\xbf][\x80-\xbf]/", $string, $info);
if ($count_words)
{
$j = 0;
$wordscut = "";
for ($i = 0; $i < count($info[0]); $i ++)
{
$wordscut .= $info[0][$i];
if (ord($info[0][$i]) >= 128)
{
$j = $j + 2;
}
else
{
$j = $j + 1;
}
if ($j >= $length)
{
return $wordscut . $etc;
}
}
return join('', $info[0]);
}
return join("", array_slice($info[0], 0, $length)) . $etc;
}
/**
* 获取文件后缀名
*
* @param mixed $file_name 文件名
* @static
*
* @access public
* @return string
*/
public static function get_extension($file_name)
{
$ext = explode('.', $file_name);
$ext = array_pop($ext);
return strtolower($ext);
}
// 获取 Url 跳转后的真实地址
public static function getrealurl($url)
{
if (empty($url))
{
return $url;
}
$header = get_headers($url, 1);
if (empty($header[0]) || empty($header[1]))
{
return $url;
}
if (strpos($header[0], '301') || strpos($header[0], '302'))
{
if (empty($header['Location']))
{
return $url;
}
if (is_array($header['Location']))
{
return $header['Location'][count($header['Location']) - 1];
}
else
{
return $header['Location'];
}
}
else
{
return $url;
}
}
// 解压服务器用 Content-Encoding:gzip 压缩过的数据
public static function gzdecode($data)
{
$flags = ord(substr($data, 3, 1));
$headerlen = 10;
$extralen = 0;
$filenamelen = 0;
if ($flags & 4)
{
$extralen = unpack('v', substr($data, 10, 2));
$extralen = $extralen[1];
$headerlen += 2 + $extralen;
}
if ($flags & 8) // Filename
$headerlen = strpos($data, chr(0), $headerlen) + 1;
if ($flags & 16) // Comment
$headerlen = strpos($data, chr(0), $headerlen) + 1;
if ($flags & 2) // CRC at end of file
$headerlen += 2;
$unpacked = @gzinflate(substr($data, $headerlen));
if ($unpacked === FALSE) $unpacked = $data;
return $unpacked;
}
/**
* 数字金额转换为中文
* @param string|integer|float $num 目标数字
* @param boolean $sim 使用小写(默认)
* @return string
*/
public static function number2chinese($num, $sim = FALSE)
{
if (!is_numeric($num)) return '含有非数字非小数点字符!';
$char = $sim ? array('零', '一', '二', '三', '四', '五', '六', '七', '八', '九') : array('零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖');
$unit = $sim ? array('', '十', '百', '千', '', '万', '亿', '兆') : array('', '拾', '佰', '仟', '', '萬', '億', '兆');
$retval = '';
$num = sprintf("%01.2f", $num);
list ($num, $dec) = explode('.', $num);
// 小数部分
if ($dec['0'] > 0)
{
$retval .= "{$char[$dec['0']]}";
}
if ($dec['1'] > 0)
{
$retval .= "{$char[$dec['1']]}";
}
// 整数部分
if ($num > 0)
{
$retval = "" . $retval;
$f = 1;
$str = strrev(intval($num));
for ($i = 0, $c = strlen($str); $i < $c; $i ++)
{
if ($str[$i] > 0)
{
$f = 0;
}
if ($f == 1 && $str[$i] == 0)
{
$out[$i] = "";
}
else
{
$out[$i] = $char[$str[$i]];
}
$out[$i] .= $str[$i] != '0' ? $unit[$i % 4] : '';
if ($i > 1 and $str[$i] + $str[$i - 1] == 0)
{
$out[$i] = '';
}
if ($i % 4 == 0)
{
$out[$i] .= $unit[4 + floor($i / 4)];
}
}
$retval = join('', array_reverse($out)) . $retval;
}
return $retval;
}
public static function colorize($str, $status = "info")
{
$out = "";
switch ($status)
{
case 'succ':
$out = "\033[32m"; // Blue
break;
case "error":
$out = "\033[31m"; // Red
break;
case "warn":
$out = "\033[33m"; // Yellow
break;
case "note":
$out = "\033[34m"; // Green
break;
case "debug":
$out = "\033[36m"; // Green
break;
default:
$out = "\033[0m"; // info
break;
}
return $out.$str."\033[0m";
}
public static function node_to_array($dom, $node)
{
if(!is_a( $dom, 'DOMDocument' ) || !is_a( $node, 'DOMNode' ))
{
return false;
}
$array = array();
// Discard empty nodes
$localName = trim( $node->localName );
if( empty($localName))
{
return false;
}
if( XML_TEXT_NODE == $node->nodeType )
{
return $node->nodeValue;
}
foreach ($node->attributes as $attr)
{
$array['@'.$attr->localName] = $attr->nodeValue;
}
foreach ($node->childNodes as $childNode)
{
if ( (isset($childNode->childNodes->length) && 1 == $childNode->childNodes->length) &&
XML_TEXT_NODE == $childNode->firstChild->nodeType )
{
$array[$childNode->localName] = $childNode->nodeValue;
}
else
{
if( false !== ($a = self::node_to_array( $dom, $childNode)))
{
$array[$childNode->localName] = $a;
}
}
}
return $array;
}
public static function is_win()
{
return strtoupper(substr(PHP_OS,0,3))==="WIN";
}
/**
* 和 http_build_query 相反,分解出参数
*
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2016-05-16 17:29
*/
public static function http_split_query($query, $is_query = false)
{
if (!$is_query)
{
$parse_arr = parse_url($query);
if (empty($parse_arr['query']))
{
return array();
}
$query = $parse_arr['query'];
}
$query_arr = explode("&", $query);
$params = array();
foreach ($query_arr as $val)
{
$arr = explode("=", $val);
$params[$arr[0]] = $arr[1];
}
return $params;
}
}

View File

@@ -1,421 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | PHPSpider [ A PHP Framework For Crawler ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 https://doc.phpspider.org All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Seatle Yang <seatle@foxmail.com>
// +----------------------------------------------------------------------
//----------------------------------
// Worker多进程操作类
//----------------------------------
class worker
{
// worker进程数
public $count = 0;
// worker idworker进程从1开始0被master进程所使用
public $worker_id = 0;
// worker 进程ID
public $worker_pid = 0;
// 进程用户
public $user = '';
// 进程名
public $title = '';
// 每个进程是否只运行一次
public $run_once = true;
// 是否输出日志
public $log_show = false;
// master进程启动回调
public $on_start = false;
// master进程停止回调
public $on_stop = false;
// worker进程启动回调
public $on_worker_start = false;
// worker进程停止回调
public $on_worker_stop = false;
// master进程ID
protected static $_master_pid = 0;
// worker进程ID
protected static $_worker_pids = array();
// master、worker进程启动时间
public $time_start = 0;
// master、worker进程运行状态 [starting|running|shutdown|reload]
protected static $_status = "starting";
public function __construct()
{
self::$_master_pid = posix_getpid();
// 产生时钟云,添加后父进程才可以收到信号
declare(ticks = 1);
$this->install_signal();
}
/**
* 安装信号处理函数
* @return void
*/
protected function install_signal()
{
// stop
pcntl_signal(SIGINT, array($this, 'signal_handler'), false);
// reload
pcntl_signal(SIGUSR1, array($this, 'signal_handler'), false);
// status
pcntl_signal(SIGUSR2, array($this, 'signal_handler'), false);
// ignore
pcntl_signal(SIGPIPE, SIG_IGN, false);
// install signal handler for dead kids
// pcntl_signal(SIGCHLD, array($this, 'signal_handler'));
}
/**
* 卸载信号处理函数
* @return void
*/
protected function uninstall_signal()
{
// uninstall stop signal handler
pcntl_signal(SIGINT, SIG_IGN, false);
// uninstall reload signal handler
pcntl_signal(SIGUSR1, SIG_IGN, false);
// uninstall status signal handler
pcntl_signal(SIGUSR2, SIG_IGN, false);
}
/**
* 信号处理函数会被其他类调用到所以要设置为public
* @param int $signal
*/
public function signal_handler($signal) {
switch ($signal) {
// stop 2
case SIGINT:
// master进程和worker进程都会调用
$this->stop_all();
break;
// reload 30
case SIGUSR1:
echo "reload\n";
break;
// show status 31
case SIGUSR2:
echo "status\n";
break;
}
}
/**
* 运行worker实例
*/
public function run()
{
$this->time_start = microtime(true);
$this->worker_id = 0;
$this->worker_pid = posix_getpid();
$this->set_process_title($this->title);
// 这里赋值worker进程也会克隆到
if ($this->log_show)
{
log::$log_show = true;
}
if ($this->on_start)
{
call_user_func($this->on_start, $this);
}
// worker进程从1开始0被master进程所使用
for ($i = 1; $i <= $this->count; $i++)
{
$this->fork_one_worker($i);
}
$this->monitor_workers();
}
/**
* 创建一个子进程
* @param Worker $worker
* @throws Exception
*/
public function fork_one_worker($worker_id)
{
//$sockets = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
$pid = pcntl_fork();
// 主进程记录子进程pid
if($pid > 0)
{
self::$_worker_pids[$worker_id] = $pid;
}
// 子进程运行
elseif(0 === $pid)
{
$this->time_start = microtime(true);
$this->worker_id = $worker_id;
$this->worker_pid = posix_getpid();
$this->set_process_title($this->title);
$this->set_process_user($this->user);
// 清空master进程克隆过来的worker进程ID
self::$_worker_pids = array();
//$this->uninstall_signal();
// 设置worker进程的运行状态为运行中
self::$_status = "running";
// 注册进程退出回调,用来检查是否有错误(子进程里面注册)
register_shutdown_function(array($this, 'check_errors'));
// 如果设置了worker进程启动回调函数
if ($this->on_worker_start)
{
call_user_func($this->on_worker_start, $this);
}
// 停止当前worker实例
$this->stop();
// 这里用0表示正常退出
exit(0);
}
else
{
log::add("fork one worker fail", "Error");
exit;
}
}
/**
* 尝试设置运行当前进程的用户
*
* @param $user_name
*/
protected static function set_process_user($user_name)
{
// 用户名为空 或者 当前用户不是root用户
if(empty($user_name) || posix_getuid() !== 0)
{
return;
}
$user_info = posix_getpwnam($user_name);
if($user_info['uid'] != posix_getuid() || $user_info['gid'] != posix_getgid())
{
if(!posix_setgid($user_info['gid']) || !posix_setuid($user_info['uid']))
{
log::add('Can not run woker as '.$user_name." , You shuld be root", "Error");
}
}
}
/**
* 设置当前进程的名称在ps aux命令中有用
* 注意 需要php>=5.5或者安装了protitle扩展
* @param string $title
* @return void
*/
protected function set_process_title($title)
{
if (!empty($title))
{
// 需要扩展
if(extension_loaded('proctitle') && function_exists('setproctitle'))
{
@setproctitle($title);
}
// >=php 5.5
elseif (function_exists('cli_set_process_title'))
{
cli_set_process_title($title);
}
}
}
/**
* 监控所有子进程的退出事件及退出码
* @return void
*/
public function monitor_workers()
{
// 设置master进程的运行状态为运行中
self::$_status = "running";
while(1)
{
// pcntl_signal_dispatch 子进程无法接受到信号
// 如果有信号到来,尝试触发信号处理函数
//pcntl_signal_dispatch();
// 挂起进程,直到有子进程退出或者被信号打断
$status = 0;
$pid = pcntl_wait($status, WUNTRACED);
// 如果有信号到来,尝试触发信号处理函数
//pcntl_signal_dispatch();
// 子进程退出信号
if($pid > 0)
{
//echo "worker[".$pid."] stop\n";
//$this->stop();
// 如果不是正常退出是被kill等杀掉的
if($status !== 0)
{
log::add("worker {$pid} exit with status $status", "Warning");
}
// key 和 value 互换
$worker_pids = array_flip(self::$_worker_pids);
// 通过 pid 得到 worker_id
$worker_id = $worker_pids[$pid];
// 这里不unset掉是为了进程重启
self::$_worker_pids[$worker_id] = 0;
//unset(self::$_worker_pids[$pid]);
// 再生成一个worker
if (!$this->run_once)
{
$this->fork_one_worker($worker_id);
}
// 如果所有子进程都退出了,触发主进程退出函数
$all_worker_stop = true;
foreach (self::$_worker_pids as $_worker_pid)
{
// 只要有一个worker进程还存在进程ID就不算退出
if ($_worker_pid != 0)
{
$all_worker_stop = false;
}
}
if ($all_worker_stop)
{
if ($this->on_stop)
{
call_user_func($this->on_stop, $this);
}
exit(0);
}
}
// 其他信号
else
{
// worker进程接受到master进行信号退出的会到这里来
if ($this->on_stop)
{
call_user_func($this->on_stop, $this);
}
exit(0);
}
}
}
/**
* 执行关闭流程(所有进程)
* 事件触发,非正常程序执行完毕
* @return void
*/
public function stop_all()
{
// 设置master、worker进程的运行状态为关闭状态
self::$_status = "shutdown";
// master进程
if(self::$_master_pid === posix_getpid())
{
// 循环给worker进程发送关闭信号
foreach (self::$_worker_pids as $worker_pid)
{
posix_kill($worker_pid, SIGINT);
}
}
// worker进程
else
{
// 接收到master进程发送的关闭信号之后退出这里应该考虑业务的完整性不能强行exit
$this->stop();
exit(0);
}
}
/**
* 停止当前worker实例
* 正常运行结束和接受信号退出,都会调用这个方法
* @return void
*/
public function stop()
{
if ($this->on_worker_stop)
{
call_user_func($this->on_worker_stop, $this);
}
// 设置worker进程的运行状态为关闭
self::$_status = "shutdown";
}
/**
* 检查错误PHP exit之前会执行
* @return void
*/
public function check_errors()
{
// 如果当前worker进程不是正常退出
if(self::$_status != "shutdown")
{
$error_msg = "WORKER EXIT UNEXPECTED ";
$errors = error_get_last();
if($errors && ($errors['type'] === E_ERROR ||
$errors['type'] === E_PARSE ||
$errors['type'] === E_CORE_ERROR ||
$errors['type'] === E_COMPILE_ERROR ||
$errors['type'] === E_RECOVERABLE_ERROR ))
{
$error_msg .= $this->get_error_type($errors['type']) . " {$errors['message']} in {$errors['file']} on line {$errors['line']}";
}
log::add($error_msg, 'Error');
}
}
/**
* 获取错误类型对应的意义
* @param integer $type
* @return string
*/
protected function get_error_type($type)
{
switch($type)
{
case E_ERROR: // 1 //
return 'E_ERROR';
case E_WARNING: // 2 //
return 'E_WARNING';
case E_PARSE: // 4 //
return 'E_PARSE';
case E_NOTICE: // 8 //
return 'E_NOTICE';
case E_CORE_ERROR: // 16 //
return 'E_CORE_ERROR';
case E_CORE_WARNING: // 32 //
return 'E_CORE_WARNING';
case E_COMPILE_ERROR: // 64 //
return 'E_COMPILE_ERROR';
case E_COMPILE_WARNING: // 128 //
return 'E_COMPILE_WARNING';
case E_USER_ERROR: // 256 //
return 'E_USER_ERROR';
case E_USER_WARNING: // 512 //
return 'E_USER_WARNING';
case E_USER_NOTICE: // 1024 //
return 'E_USER_NOTICE';
case E_STRICT: // 2048 //
return 'E_STRICT';
case E_RECOVERABLE_ERROR: // 4096 //
return 'E_RECOVERABLE_ERROR';
case E_DEPRECATED: // 8192 //
return 'E_DEPRECATED';
case E_USER_DEPRECATED: // 16384 //
return 'E_USER_DEPRECATED';
}
return "";
}
}

View File

@@ -1,20 +0,0 @@
#!/bin/bash
if [ ! -d "$1" ] && [ ! -f "$1" ]; then
echo "file $1 not exists"
exit
fi
filename=$1
comment="add file"
if [[ $2 != "" ]]; then
comment=$2
fi
echo "start update..."
git pull
echo "start add new file..."
git add $filename
echo "start commit..."
git commit -m "$comment" $filename
git push -u origin master
echo "git commit complete..."

View File

@@ -1,129 +0,0 @@
_-o#&&*''''?d:>b\_
_o/"`'' '',, dMF9MMMMMHo_
.o&#' `"MbHMMMMMMMMMMMHo.
.o"" ' vodM*$&&HMMMMMMMMMM?.
,' $M&ood,~'`(&##MMMMMMH\
/ ,MMMMMMM#b?#bobMMMMHMMML
& ?MMMMMMMMMMMMMMMMM7MMM$R*Hk
?$. :MMMMMMMMMMMMMMMMMMM/HMMM|`*L
| |MMMMMMMMMMMMMMMMMMMMbMH' T,
$H#: `*MMMMMMMMMMMMMMMMMMMMb#]' `?
]MMH# ""*""""*#MMMMMMMMMMMMM' -
MMMMMb_ |MMMMMMMMMMMP' :
HMMMMMMMHo `MMMMMMMMMT .
?MMMMMMMMP 9MMMMMMMM] -
-?MMMMMMM |MMMMMMMMM?,d- ' {Name}
:|MMMMMM- `MMMMMMMT .M|. : {Description}
.9MMM[ &MMMMM*' `' . {Loaded}
:9MMk `MMM#" -
&M] ` .-
`&. .
`~, . ./
. _ .-
'`--._,dd###pp=""'
$$$$$AnyShIt$$$$$$
_v->#H#P? "':o<>\_
.,dP` `'' "'-o.+H6&MMMHo_
oHMH9' `?&bHMHMMMMMMHo.
oMP"' ' ooMP*#&HMMMMMMM?.
,M* - `*MSdob//`^&##MMMH\
d*' .,MMMMMMH#o>#ooMMMMMb
HM- :HMMMMMMMMMMMMMMM&HM[R\
d"Z\. 9MMMMMMMMMMMMMMMMM[HMM|:
-H - MMMMMMMMMMMMMMMMMMMbMP' :
:??Mb# `9MMMMMMMMMMMMMMMMMMH#! .
: MMMMH#, "*""""`#HMMMMMMMMMMH -
||MMMMMM6\. [MMMMMMMMMH' :
:|MMMMMMMMMMHo `9MMMMMMMM' .
. HMMMMMMMMMMP' !MMMMMMMM `
- `#MMMMMMMMM HMMMMMMM*,/ :
: ?MMMMMMMF HMMMMMM',P' : {Name}
. HMMMMR' [MMMMP' ^' - {Description}
: `HMMMT iMMH' .' {Loaded}
-.`HMH .
-:*H . '
-`\,, . .-
' . _ .-`
'`~\.__,obb#q==~'''
$$$$$AnyShIt$$$$$$
_ood>H&H&Z?#M#b-\.
.\HMMMMMR?`\M6b."`' ''``v.
.. .MMMMMMMMMMHMMM#&. ``~o.
. ,HMMMMMMMMMMMM*"'-` &b.
. .MMMMMMMMMMMMH' `"&\
- RMMMMM#H##R' 4Mb
- |7MMM' ?:: `|MMb
/ HMM__#|`"\>?v.. `MMML
. `"'#Hd| ` 9MMM:
- |\,\?HH#bbL `9MMb
: !MMMMMMMH#b, `""T
. . ,MMMMMMMMMMMbo. |
: 4MMMMMMMMMMMMMMMHo |
: ?MMMMMMMMMMMMMMM? :
-. `#MMMMMMMMMMMM: .-
: |MMMMMMMMMM? .
- JMMMMMMMT' : {Name}
`. MMMMMMH' - {Description}
-. |MMM#*` - {Loaded}
. HMH' . '
-. #H:. .-
` . .\ .-
'-..-+oodHL_,--/-`
$$$$$AnyShIt$$$$$$
.,:,#&6dHHHb&##o\_
.oHHMMMMMMMMMMMMMMMMMH*\,.
oHMMMMMMMMMMMMMMMMMMMMMMHb:'-.
.dMMMMMMMMMMMMMMMMMMMMMMMMMH|\/' .
,&HMMMMMMMMMMMMMMMMMMMMMMM/"&.,d. -.
dboMMHMMMMMMMMMMMMMMMMMMMMMML `' .
HMHMMM$Z***MMMMMMMMMMMMMMMMMM|.- .
dMM]MMMM#' `9MMMH?"`MMMMR'T' _ :
|MMMbM#'' |MM" ``MMMH. <_ .
dMMMM#& *&. .?`*" .'&: .
MMMMMH- `' -v/H .dD "' ' :
MMMM* `*M: 4MM*::-!v,_ :
MMMM `*?::" "'``"?9Mb::. :
&MMM, `"'"'|"._ "?`| - :
`MMM].H ,#dM[_H ..:
9MMi`M: . .ooHMMMMMMM, ..
9Mb `- 1MMMMMMMMMM| : {Name}
?M |MM#*#MMMM* . {Description}
-. ` |#"' ,' {Loaded}
. -" v`
-. .-
- . . `
'-*#d#HHMMMMHH#"-'
$$$$$AnyShIt$$$$$$
.-:?,Z?:&$dHH##b\_
,:bqRMMMMMMMMMMMMMMMMMHo.
.?HHHMMMMMMMMMMMMMMMMMMMMMMMHo.
-o/*M9MMMMMMMMMMMMMMMMMMMMMMMMMMMv
.:H\b\'|?#HHMMMMMMMMMMMMMMMMMMMMMM6?Z\
.?MMMHbdbbodMMMMHMMMMMMMMMMMMMMMMMMMM\':
:MMMMMMMMMMM7MMMMb?6P**#MMMMMMMMMMMMMMM_ :
\MMMMMMMMMMMMb^MMMMMM? `*MMMM*"`MMMR<' . -
.1MMMMMMMMMMMMMb]M#"" 9MR' `?MMb \. :
-MMMMMMMMMMMMMMMH##|` *&. |`*' .\ .
-?""*MMMMMMMMMMMMM' ' |?b ,]" :
: MMMMMMMMMMH' `M_|M]r\?
. `MMMMMMMMM' `$_:`'"H
- TMMMMMMMM, '"``::
: [MMMMMMMM| oH| .#M-
: `9MMMMMM' .MP . ,oMMT
. HMMMMP' `' ,MMMP {Name}
- `MMH' HH9* {Description}
'. ` ` .' {Loaded}
- . '
` . - .-
` . .-
' -==pHMMH##HH#"""

View File

@@ -1,49 +0,0 @@
<?php
ini_set("memory_limit", "10240M");
require_once __DIR__ . '/../autoloader.php';
use phpspider\core\requests;
use phpspider\core\selector;
/* Do NOT delete this comment */
/* 不要删除这段注释 */
hacked_emails::random_banner();
exit;
class hacked_emails
{
// Colors
// green - yellow - blue - red - white - magenta - cyan - reset
public static $color_g = "\033[92m";
public static $color_y = "\033[93m";
public static $color_b = "\033[94m";
public static $color_r = "\033[91m";
public static $color_w = "\033[0m";
public static $color_m = "\x1b[35m";
public static $color_c = "\x1b[36m";
public static $end = "\x1b[39m";
public static $bold = "\033[1m";
public static function random_banner()
{
$banners = file_get_contents("banners.txt");
$banners = explode('$$$$$AnyShIt$$$$$$', $banners);
$banner = $banners[count($banners)-1];
$banner_to_print = self::$color_g;
$banner_to_print .= $banner;
$banner_to_print .= self::$end;
$name = self::$color_b."Hacked Emails By ".self::$bold."@seatle -".self::$color_m." V0.1".self::$color_g;
$banner_to_print = str_replace("{Name}", $name, $banner_to_print);
$description = self::$color_c."Know the dangers of email credentials reuse attacks.".self::$color_g;
$banner_to_print = str_replace("{Description}", $description, $banner_to_print);
$loaded = self::$color_b."Loaded ".self::$color_y."14".self::$color_b." website.".self::$color_g;
$banner_to_print = str_replace("{Loaded}", $loaded, $banner_to_print);
echo $banner_to_print;
}
}
$html = requests::get('http://www.qiushibaike.com/article/118914171');
//echo $html;
//exit;
$data = selector::select($html, "div.author", "css");
echo $data;

View File

@@ -1,425 +0,0 @@
<?php
/**
* Worker多进程操作类
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author seatle<seatle@foxmail.com>
* @copyright seatle<seatle@foxmail.com>
* @link http://www.epooll.com/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
class cls_curl
{
protected static $timeout = 10;
protected static $ch = null;
protected static $useragent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36';
protected static $http_raw = false;
protected static $cookie = null;
protected static $cookie_jar = null;
protected static $cookie_file = null;
protected static $referer = null;
protected static $ip = null;
protected static $proxy = null;
protected static $headers = array();
protected static $hosts = array();
protected static $gzip = false;
protected static $info = array();
/**
* set timeout
*
* @param init $timeout
* @return
*/
public static function set_timeout($timeout)
{
self::$timeout = $timeout;
}
/**
* 设置代理
*
* @param mixed $proxy
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2016-09-18 10:17
*/
public static function set_proxy($proxy)
{
self::$proxy = $proxy;
}
/**
* set referer
*
*/
public static function set_referer($referer)
{
self::$referer = $referer;
}
/**
* 设置 user_agent
*
* @param string $useragent
* @return void
*/
public static function set_useragent($useragent)
{
self::$useragent = $useragent;
}
/**
* 设置COOKIE
*
* @param string $cookie
* @return void
*/
public static function set_cookie($cookie)
{
self::$cookie = $cookie;
}
/**
* 设置COOKIE JAR
*
* @param string $cookie_jar
* @return void
*/
public static function set_cookie_jar($cookie_jar)
{
self::$cookie_jar = $cookie_jar;
}
/**
* 设置COOKIE FILE
*
* @param string $cookie_file
* @return void
*/
public static function set_cookie_file($cookie_file)
{
self::$cookie_file = $cookie_file;
}
/**
* 获取内容的时候是不是连header也一起获取
*
* @param mixed $http_raw
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2016-09-18 10:17
*/
public static function set_http_raw($http_raw)
{
self::$http_raw = $http_raw;
}
/**
* 设置IP
*
* @param string $ip
* @return void
*/
public static function set_ip($ip)
{
self::$ip = $ip;
}
/**
* 设置Headers
*
* @param string $headers
* @return void
*/
public static function set_headers($headers)
{
self::$headers = $headers;
}
/**
* 设置Hosts
*
* @param string $hosts
* @return void
*/
public static function set_hosts($hosts)
{
self::$hosts = $hosts;
}
/**
* 设置Gzip
*
* @param string $hosts
* @return void
*/
public static function set_gzip($gzip)
{
self::$gzip = $gzip;
}
/**
* 初始化 CURL
*
*/
public static function init()
{
//if (empty ( self::$ch ))
if (!is_resource ( self::$ch ))
{
self::$ch = curl_init ();
curl_setopt( self::$ch, CURLOPT_RETURNTRANSFER, true );
curl_setopt( self::$ch, CURLOPT_CONNECTTIMEOUT, self::$timeout );
curl_setopt( self::$ch, CURLOPT_HEADER, false );
curl_setopt( self::$ch, CURLOPT_USERAGENT, self::$useragent );
curl_setopt( self::$ch, CURLOPT_TIMEOUT, self::$timeout + 5);
// 在多线程处理场景下使用超时选项时会忽略signals对应的处理函数但是无耐的是还有小概率的crash情况发生
curl_setopt( self::$ch, CURLOPT_NOSIGNAL, true);
}
return self::$ch;
}
/**
* get
*
*
*/
public static function get($url, $fields = array())
{
self::init ();
return self::http_request($url, 'get', $fields);
}
/**
* $fields 有三种类型:1、数组2、http query3、json
* 1、array('name'=>'yangzetao') 2、http_build_query(array('name'=>'yangzetao')) 3、json_encode(array('name'=>'yangzetao'))
* 前两种是普通的post可以用$_POST方式获取
* 第三种是post stream( json rpc其实就是webservice )虽然是post方式但是只能用流方式 http://input 后者 $HTTP_RAW_POST_DATA 获取
*
* @param mixed $url
* @param array $fields
* @param mixed $proxy
* @static
* @access public
* @return void
*/
public static function post($url, $fields = array())
{
self::init ();
return self::http_request($url, 'post', $fields);
}
public static function http_request($url, $type = 'get', $fields)
{
// 如果是 get 方式,直接拼凑一个 url 出来
if (strtolower($type) == 'get' && !empty($fields))
{
$url = $url . (strpos($url,"?")===false ? "?" : "&") . http_build_query($fields);
}
// 随机绑定 hosts做负载均衡
if (self::$hosts)
{
$parse_url = parse_url($url);
$host = $parse_url['host'];
$key = rand(0, count(self::$hosts)-1);
$ip = self::$hosts[$key];
$url = str_replace($host, $ip, $url);
self::$headers = array_merge( array('Host:'.$host), self::$headers );
}
curl_setopt( self::$ch, CURLOPT_URL, $url );
// 如果是 post 方式
if (strtolower($type) == 'post')
{
curl_setopt( self::$ch, CURLOPT_POST, true );
curl_setopt( self::$ch, CURLOPT_POSTFIELDS, $fields );
}
if (self::$useragent)
{
curl_setopt( self::$ch, CURLOPT_USERAGENT, self::$useragent );
}
if (self::$cookie)
{
curl_setopt( self::$ch, CURLOPT_COOKIE, self::$cookie );
}
if (self::$cookie_jar)
{
curl_setopt( self::$ch, CURLOPT_COOKIEJAR, self::$cookie_jar );
}
if (self::$cookie_file)
{
curl_setopt( self::$ch, CURLOPT_COOKIEFILE, self::$cookie_file );
}
if (self::$referer)
{
curl_setopt( self::$ch, CURLOPT_REFERER, self::$referer );
}
if (self::$ip)
{
self::$headers = array_merge( array('CLIENT-IP:'.self::$ip, 'X-FORWARDED-FOR:'.self::$ip), self::$headers );
}
if (self::$headers)
{
curl_setopt( self::$ch, CURLOPT_HTTPHEADER, self::$headers );
}
if (self::$gzip)
{
curl_setopt( self::$ch, CURLOPT_ENCODING, 'gzip' );
}
if (self::$proxy)
{
curl_setopt( self::$ch, CURLOPT_PROXY, self::$proxy );
}
if (self::$http_raw)
{
curl_setopt( self::$ch, CURLOPT_HEADER, true );
}
$data = curl_exec ( self::$ch );
self::$info = curl_getinfo(self::$ch);
if ($data === false)
{
//echo date("Y-m-d H:i:s"), ' Curl error: ' . curl_error( self::$ch ), "\n";
}
// 关闭句柄
curl_close( self::$ch );
//$data = substr($data, 10);
//$data = gzinflate($data);
return $data;
}
public static function get_info()
{
return self::$info;
}
public static function get_http_code()
{
return self::$info['http_code'];
}
}
function classic_curl($urls, $delay)
{
$queue = curl_multi_init();
$map = array();
foreach ($urls as $url)
{
// create cURL resources
$ch = curl_init();
// 设置 URL 和 其他参数
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_TIMEOUT, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_NOSIGNAL, true);
// 把当前 curl resources 加入到 curl_multi_init 队列
curl_multi_add_handle($queue, $ch);
$map[$url] = $ch;
}
$active = null;
// execute the handles
do {
$mrc = curl_multi_exec($queue, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
while ($active > 0 && $mrc == CURLM_OK) {
while (curl_multi_exec($queue, $active) === CURLM_CALL_MULTI_PERFORM);
// 这里 curl_multi_select 一直返回 -1所以这里就死循环了CPU就100%了
if (curl_multi_select($queue, 0.5) != -1)
{
do {
$mrc = curl_multi_exec($queue, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
}
}
$responses = array();
foreach ($map as $url=>$ch) {
//$responses[$url] = callback(curl_multi_getcontent($ch), $delay);
$responses[$url] = callback(curl_multi_getcontent($ch), $delay, $url);
curl_multi_remove_handle($queue, $ch);
curl_close($ch);
}
curl_multi_close($queue);
return $responses;
}
function rolling_curl($urls, $delay)
{
$queue = curl_multi_init();
$map = array();
foreach ($urls as $url) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_NOSIGNAL, true);
$cookie = '_za=36643642-e546-4d60-a771-8af8dcfbd001; q_c1=a57a2b9f10964f909b8d8969febf3ab2|1437705596000|1437705596000; _xsrf=f0304fba4e44e1d008ec308d59bab029; cap_id="YWY1YmRmODlmZGVmNDc3MWJlZGFkZDg3M2E0M2Q5YjM=|1437705596|963518c454bb6f10d96775021c098c84e1e46f5a"; z_c0="QUFCQVgtRWZBQUFYQUFBQVlRSlZUVjR6NEZVUTgtRkdjTVc5UDMwZXRJZFdWZ2JaOWctNVhnPT0=|1438164574|aed6ef3707f246a7b64da4f1e8c089395d77ff2b"; __utma=51854390.1105113342.1437990174.1438160686.1438164116.10; __utmc=51854390; __utmz=51854390.1438134939.8.5.utmcsr=zhihu.com|utmccn=(referral)|utmcmd=referral|utmcct=/people/yangzetao; __utmv=51854390.100-1|2=registration_date=20131030=1^3=entry_date=20131030=1';
curl_setopt($ch, CURLOPT_COOKIE, $cookie);
$useragent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36';
curl_setopt( $ch, CURLOPT_USERAGENT, $useragent );
curl_setopt($ch, CURLOPT_ENCODING, 'gzip');
curl_multi_add_handle($queue, $ch);
$map[(string) $ch] = $url;
}
$responses = array();
do {
while (($code = curl_multi_exec($queue, $active)) == CURLM_CALL_MULTI_PERFORM) ;
if ($code != CURLM_OK) { break; }
// a request was just completed -- find out which one
while ($done = curl_multi_info_read($queue)) {
// get the info and content returned on the request
$info = curl_getinfo($done['handle']);
$error = curl_error($done['handle']);
$results = callback(curl_multi_getcontent($done['handle']), $delay, $map[(string) $done['handle']]);
$responses[$map[(string) $done['handle']]] = compact('info', 'error', 'results');
// remove the curl handle that just completed
curl_multi_remove_handle($queue, $done['handle']);
curl_close($done['handle']);
}
// Block for data in / output; error handling is done by curl_multi_exec
if ($active > 0) {
curl_multi_select($queue, 0.5);
}
} while ($active);
curl_multi_close($queue);
return $responses;
}
function callback($data, $delay, $url) {
//echo $data;
//echo date("Y-m-d H:i:s", time()) . " --- " . $url . "\n";
if (!empty($data))
{
file_put_contents("./html2/".md5($url).".html", $data);
}
// usleep模拟现实中比较负责的数据处理逻辑(如提取, 分词, 写入文件或数据库等)
//usleep(1);
//return compact('data', 'matches');
}

View File

@@ -1,248 +0,0 @@
<?php
class cls_query
{
private static $content;
public static $debug = false;
public static function init($content)
{
self::$content = $content;
}
public static function query($query, $attr = "html")
{
$nodes = self::get_nodes($query);
$datas = self::get_datas($nodes, $attr);
return $datas;
}
protected static function is_char($char) {
return extension_loaded('mbstring') ? mb_eregi('\w', $char) : preg_match('@\w@', $char);
}
/**
* 从xpath中得到节点
*
* @param mixed $xpath
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2015-08-08 15:52
*/
private static function get_nodes($query)
{
// 把一到多个空格 替换成 一个空格
// 把 > 和 ~ 符号两边的空格去掉,因为没有用这两个符号,所以这里可以不这么做
// ul>li.className
$query = trim(
preg_replace('@\s+@', ' ',
preg_replace('@\s*(>|\\+|~)\s*@', '\\1', $query)
)
);
$nodes = array();
if (! $query)
{
return $nodes;
}
$query_arr = explode(" ", $query);
foreach ($query_arr as $k=>$v)
{
$path = $k == 0 ? $v : $path.' '.$v;
$node = array("path"=>(string)$path, "name"=>"", "id"=>"", "class"=>"", "other"=>array());
// 如果存在内容选择器
if (preg_match('@(.*?)\[(.*?)=[\'|"](.*?)[\'|"]\]@', $v, $matches) && !empty($matches[2]) && !empty($matches[3]))
{
// 把选择器过滤掉 [rel='topic']
$v = $matches[1];
$node['other'] = array(
'key'=>$matches[2],
'val'=>$matches[3],
);
}
// 如果存在 id
$id_arr = explode("#", $v);
$class_arr = explode(".", $v);
if (count($id_arr) === 2)
{
$node['name'] = $id_arr[0];
$node['id'] = $id_arr[1];
}
// 如果存在 class
elseif (count($class_arr) === 2)
{
$node['name'] = $class_arr[0];
$node['class'] = $class_arr[1];
}
// 如果没有样式
else
{
$node['name'] = $v;
}
$nodes[] = $node;
}
//print_r($nodes);
//exit;
return $nodes;
}
public static function get_datas($nodes, $attr = "html")
{
if (empty(self::$content))
{
return false;
}
$node_datas = array();
$count = count($nodes);
// 循环所有节点
foreach ($nodes as $i=>$node)
{
$is_last = $count == $i+1 ? true : false;
// 第一次
if ($i == 0)
{
$datas = array();
$datas = self::get_node_datas($node, self::$content, $attr, $is_last);
// 如果第一次都取不到数据,直接跳出循环
if(!$datas)
{
break;
}
$node_datas[$nodes[$i]['path']] = $datas;
}
else
{
$datas = array();
// 循环上一个节点的数组
foreach ($node_datas[$nodes[$i-1]['path']] as $v)
{
$datas = array_merge( $datas, self::get_node_datas($node, trim($v), $attr, $is_last) );
}
$node_datas[$nodes[$i]['path']] = $datas;
// 删除上一个节点,防止内存溢出,或者缓存到本地,再次使用?!
unset($node_datas[$nodes[$i-1]['path']]);
}
}
//print_r($datas);exit;
// 从数组中弹出最后一个元素
$node_datas = array_pop($node_datas);
//print_r($node_datas);
//exit;
return $node_datas;
}
/**
* 从节点中获取内容
* $regex = '@<meta[^>]+http-equiv\\s*=\\s*(["|\'])Content-Type\\1([^>]+?)>@i';
*
* @param mixed $node
* @param mixed $content
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2015-08-08 15:52
*/
private static function get_node_datas($node, $content, $attr = "html", $is_last = false)
{
$node_datas = $datas = array();
if (!empty($node['id']))
{
if ($node['name'])
$regex = '@<'.$node['name'].'[^>]+id\\s*=\\s*["|\']+?'.$node['id'].'\\s*[^>]+?>(.*?)</'.$node['name'].'>@is';
else
$regex = '@id\\s*=\\s*["|\']+?'.$node['id'].'\\s*[^>]+?>(.*?)<@is';
}
elseif (!empty($node['class']))
{
if ($node['name'])
$regex = '@<'.$node['name'].'[^>]+class\\s*=\\s*["|\']+?'.$node['class'].'\\s*[^>]+?>(.*?)</'.$node['name'].'>@is';
else
$regex = '@class\\s*=\\s*["|\']+?'.$node['class'].'\\s*[^>]+?>(.*?)<@is';
}
else
{
// 这里为是么是*0次到多次因为有可能是 <li>
$regex = '@<'.$node['name'].'[^>]*?>(.*?)</'.$node['name'].'>@is';
}
self::log("regex --- " . $regex);;
preg_match_all($regex, $content, $matches);
$all_datas = empty($matches[0]) ? array() : $matches[0];
$html_datas = empty($matches[1]) ? array() : $matches[1];
// 过滤掉选择器对不上的
foreach ($all_datas as $i=>$data)
{
// 如果有设置其他选择器,验证一下选择器
if (!empty($node['other']))
{
$regex = '@'.$node['other']['key'].'=[\'|"]'.$node['other']['val'].'[\'|"]@is';
self::log("regex other --- " . $regex);
// 过滤器对不上的,跳过
if (!preg_match($regex, $data, $matches))
{
continue;
}
}
// 获取节点的html内容
if ($attr != "html" && $is_last)
{
$regex = '@'.$attr.'=[\'|"](.*?)[\'|"]@is';
preg_match($regex, $data, $matches);
$node_datas[] = empty($matches[1]) ? '' : trim($matches[1]);
}
// 获取节点属性名的值
else
{
$node_datas[] = trim($html_datas[$i]);
}
}
//echo " 11111 ========================================= \n";
//print_r($node_datas);
//echo " 22222 ========================================= \n\n\n";
return $node_datas;
}
/**
* 记录日志
* @param string $msg
* @return void
*/
private static function log($msg)
{
$msg = "[".date("Y-m-d H:i:s")."] " . $msg . "\n";
if (self::$debug)
{
echo $msg;
}
}
}
//$xpath = "ul.top-nav-dropdown li";
//$xpath = "i.zg-icon";
//print_r($nodes);
//exit;
// [^>]+ 不是>的字符重复一次到多次, ? 表示不贪婪
// \s 表示空白字符
// * 表示0次或者多次
// + 表示1次或者多次
//
// 后向引用,表示表达式中,从左往右数,第一个左括号对应的括号内的内容。
// \\0 表示整个表达式
// \\1表示第1个表达式
// \\2表示第2个表达式
// $regex = '@<meta[^>]+http-equiv\\s*=\\s*(["|\'])Content-Type\\1([^>]+?)>@i';
//preg_match_all($regex, $content, $matches);
//print_r($matches);
//exit;
// 用法
//$content = file_get_contents("./test.html");
//$query = "ul#top-nav-profile-dropdown li a";
//$query = "div#zh-profile-following-topic a.link[href='/topic/19550937']";
//cls_query::init($content);
//$list = cls_query::query($query, "href");
//print_r($list);

File diff suppressed because it is too large Load Diff

View File

@@ -1,121 +0,0 @@
<?php
/**
* redis 客户端
* redis的协议可参考这个文章http://redis.cn/topics/protocol.html
*
* @version 2.7.0
* @copyright 1997-2018 The PHP Group
* @author seatle <seatle@foxmail.com>
* @created time :2018-01-03
*/
class cls_redis_client
{
private $redis_socket = false;
//private $command = '';
public function __construct($host='127.0.0.1', $port=6379, $timeout = 3)
{
$this->redis_socket = stream_socket_client("tcp://".$host.":".$port, $errno, $errstr, $timeout);
if ( !$this->redis_socket )
{
throw new Exception("{$errno} - {$errstr}");
}
}
public function __destruct()
{
fclose($this->redis_socket);
}
public function __call($name, $args)
{
$crlf = "\r\n";
array_unshift($args, $name);
$command = '*' . count($args) . $crlf;
foreach ($args as $arg)
{
$command .= '$' . strlen($arg) . $crlf . $arg . $crlf;
}
//echo $command."\n";
$fwrite = fwrite($this->redis_socket, $command);
if ($fwrite === FALSE || $fwrite <= 0)
{
throw new Exception('Failed to write entire command to stream');
}
return $this->read_response();
}
private function read_response()
{
$reply = trim(fgets($this->redis_socket, 1024));
switch (substr($reply, 0, 1))
{
case '-':
throw new Exception(trim(substr($reply, 1)));
break;
case '+':
$response = substr(trim($reply), 1);
if ($response === 'OK')
{
$response = TRUE;
}
break;
case '$':
$response = NULL;
if ($reply == '$-1')
{
break;
}
$read = 0;
$size = intval(substr($reply, 1));
if ($size > 0)
{
do
{
$block_size = ($size - $read) > 1024 ? 1024 : ($size - $read);
$r = fread($this->redis_socket, $block_size);
if ($r === FALSE)
{
throw new Exception('Failed to read response from stream');
}
else
{
$read += strlen($r);
$response .= $r;
}
}
while ($read < $size);
}
fread($this->redis_socket, 2); /* discard crlf */
break;
/* Multi-bulk reply */
case '*':
$count = intval(substr($reply, 1));
if ($count == '-1')
{
return NULL;
}
$response = array();
for ($i = 0; $i < $count; $i++)
{
$response[] = $this->read_response();
}
break;
/* Integer reply */
case ':':
$response = intval(substr(trim($reply), 1));
break;
default:
throw new RedisException("Unknown response: {$reply}");
break;
}
return $response;
}
}
//$redis = new cls_redis_client();
//var_dump($redis->auth("foobared"));
//var_dump($redis->set("name",'abc'));
//var_dump($redis->get("name"));

View File

@@ -1,179 +0,0 @@
<?php
ini_set("memory_limit", "128M");
/**
* redis 服务端
* 多进程阻塞式
* redis-benchmark -h 127.0.0.1 -p 11211 -t set -n 80000 -q
*
* @version 2.7.0
* @copyright 1997-2018 The PHP Group
* @author seatle <seatle@foxmail.com>
* @created time :2018-01-03
*/
class cls_redis_server
{
private $socket = false;
private $process_num = 3;
public $redis_kv_data = array();
public $onMessage = null;
public function __construct($host="0.0.0.0", $port=6379)
{
$this->socket = stream_socket_server("tcp://".$host.":".$port,$errno, $errstr);
if (!$this->socket) die($errstr."--".$errno);
echo "listen $host $port \r\n";
}
private function parse_resp(&$conn)
{
// 读取一行,遇到 \r\n 为一行
$line = fgets($conn);
if($line === '' || $line === false)
{
return null;
}
// 获取第一个字符作为类型
$type = $line[0];
// 去掉第一个字符,去掉结尾的 \r\n
$line = mb_substr($line, 1, -2);
switch ( $type )
{
case "*":
// 得到长度
$count = (int) $line;
$data = array();
for ($i = 1; $i <= $count; $i++)
{
$data[] = $this->parse_resp($conn);
}
return $data;
case "$":
if ($line == '-1')
{
return null;
}
// 截取的长度要加上 \r\n 两个字符
$length = $line + 2;
$data = '';
while ($length > 0)
{
$block = fread($conn, $length);
if ($length !== strlen($block))
{
throw new Exception('RECEIVING');
}
$data .= $block;
$length -= mb_strlen($block);
}
return mb_substr($data, 0, -2);
}
return $line;
}
private function start_worker_process()
{
$pid = pcntl_fork();
switch ($pid)
{
case -1:
echo "fork error : {$i} \r\n";
exit;
case 0:
while ( true )
{
echo "PID ".posix_getpid()." waiting...\n";
// 堵塞等待
$conn = stream_socket_accept($this->socket, -1);
if ( !$conn )
{
continue;
}
//"*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n"
while( true )
{
$arr = $this->parse_resp($conn);
if ( is_array($arr) )
{
if ($this->onMessage)
{
call_user_func($this->onMessage, $conn, $arr);
}
}
else if ( $arr )
{
if ($this->onMessage)
{
call_user_func($this->onMessage, $conn, $arr);
}
}
else
{
fclose($conn);
break;
}
}
}
default:
$this->pids[$pid] = $pid;
break;
}
}
public function run()
{
for($i = 1; $i <= $this->process_num; $i++)
{
$this->start_worker_process();
}
while( true )
{
foreach ($this->pids as $i => $pid)
{
if($pid)
{
$res = pcntl_waitpid($pid, $status,WNOHANG);
if ( $res == -1 || $res > 0 )
{
$this->start_worker_process();
unset($this->pids[$pid]);
}
}
}
sleep(1);
}
}
}
$server = new cls_redis_server();
$server->onMessage = function($conn, $info) use($server)
{
if ( is_array($info) )
{
$command = strtoupper($info[0]);
if ( $command == "SET" )
{
$key = $info[1];
$val = $info[2];
$server->redis_kv_data[$key] = $val;
fwrite($conn, "+OK\r\n");
}
else if ( $command == "GET" )
{
$key = $info[1];
$val = isset($server->redis_kv_data[$key]) ? $server->redis_kv_data[$key] : '';
fwrite($conn, "$".strlen($val)."\r\n".$val."\r\n");
}
else
{
fwrite($conn,"+OK\r\n");
}
}
else
{
fwrite($conn,"+OK\r\n");
}
};
$server->run();

File diff suppressed because it is too large Load Diff

View File

@@ -1,466 +0,0 @@
<?php
/**
* Curl操作类
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author seatle<seatle@foxmail.com>
* @copyright seatle<seatle@foxmail.com>
* @link http://www.epooll.com/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
class rolling_curl
{
/**
* @var float
*
* 同时运行任务数
* 例如有8个请求则会被分成两批第一批5个请求第二批3个请求
* 注意采集知乎的时候5个是比较稳定的7个以上就开始会超时了多进程就没有这样的问题因为多进程很少几率会发生并发
*/
public $window_size = 5;
/**
* @var float
*
* Timeout is the timeout used for curl_multi_select.
*/
private $timeout = 10;
/**
* @var string|array
*
* 应用在每个请求的回调函数
*/
public $callback;
/**
* @var array
*
* 设置默认的请求参数
*/
protected $options = array(
CURLOPT_SSL_VERIFYPEER => 0,
CURLOPT_RETURNTRANSFER => 1,
// 注意TIMEOUT = CONNECTTIMEOUT + 数据获取时间,所以 TIMEOUT 一定要大于 CONNECTTIMEOUT否则 CONNECTTIMEOUT 设置了就没意义
// "Connection timed out after 30001 milliseconds"
CURLOPT_CONNECTTIMEOUT => 30,
CURLOPT_TIMEOUT => 60,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_HEADER => 0,
// 在多线程处理场景下使用超时选项时会忽略signals对应的处理函数但是无耐的是还有小概率的crash情况发生
CURLOPT_NOSIGNAL => 1,
CURLOPT_USERAGENT => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36",
);
/**
* @var array
*/
private $headers = array();
/**
* @var Request[]
*
* 请求队列
*/
private $requests = array();
/**
* @var RequestMap[]
*
* Maps handles to request indexes
*/
private $requestMap = array();
public function __construct()
{
}
/**
* set timeout
*
* @param init $timeout
* @return
*/
public function set_timeout($timeout)
{
$this->options[CURLOPT_TIMEOUT] = $timeout;
}
/**
* set proxy
*
*/
public function set_proxy($proxy)
{
$this->options[CURLOPT_PROXY] = $proxy;
}
/**
* set referer
*
*/
public function set_referer($referer)
{
$this->options[CURLOPT_REFERER] = $referer;
}
/**
* 设置 user_agent
*
* @param string $useragent
* @return void
*/
public function set_useragent($useragent)
{
$this->options[CURLOPT_USERAGENT] = $useragent;
}
/**
* 设置COOKIE
*
* @param string $cookie
* @return void
*/
public function set_cookie($cookie)
{
$this->options[CURLOPT_COOKIE] = $cookie;
}
/**
* 设置COOKIE JAR
*
* @param string $cookie_jar
* @return void
*/
public function set_cookiejar($cookiejar)
{
$this->options[CURLOPT_COOKIEJAR] = $cookiejar;
}
/**
* 设置COOKIE FILE
*
* @param string $cookie_file
* @return void
*/
public function set_cookiefile($cookiefile)
{
$this->options[CURLOPT_COOKIEFILE] = $cookiefile;
}
/**
* 获取内容的时候是不是连header也一起获取
*
* @param mixed $http_raw
* @return void
* @author seatle <seatle@foxmail.com>
* @created time :2016-09-18 10:17
*/
public function set_http_raw($http_raw = false)
{
$this->options[CURLOPT_HEADER] = $http_raw;
}
/**
* 设置IP
*
* @param string $ip
* @return void
*/
public function set_ip($ip)
{
$headers = array(
'CLIENT-IP'=>$ip,
'X-FORWARDED-FOR'=>$ip,
);
$this->headers = $this->headers + $headers;
}
/**
* 设置Headers
*
* @param string $headers
* @return void
*/
public function set_headers($headers)
{
$this->headers = $this->headers + $headers;
}
/**
* 设置Hosts
*
* @param string $hosts
* @return void
*/
public function set_hosts($hosts)
{
$headers = array(
'Host'=>$hosts,
);
$this->headers = $this->headers + $headers;
}
/**
* 设置Gzip
*
* @param string $hosts
* @return void
*/
public function set_gzip($gzip)
{
if ($gzip)
{
$this->options[CURLOPT_ENCODING] = 'gzip';
}
}
public function request($url, $method = "GET", $fields = array(), $headers = array(), $options = array())
{
$this->requests[] = array('url'=>$url,'method'=>$method,'fields'=>$fields,'headers'=>$headers,'options'=>$options);
return true;
}
public function get_options($request)
{
$options = $this->options;
$headers = $this->headers;
if (ini_get('safe_mode') == 'Off' || !ini_get('safe_mode'))
{
$options[CURLOPT_FOLLOWLOCATION] = 1;
$options[CURLOPT_MAXREDIRS] = 5;
}
// 如果是 get 方式,直接拼凑一个 url 出来
if (strtolower($request['method']) == 'get' && !empty($request['fields']))
{
$url = $request['url'] . "?" . http_build_query($request['fields']);
}
// 如果是 post 方式
if (strtolower($request['method']) == 'post')
{
$options[CURLOPT_POST] = 1;
$options[CURLOPT_POSTFIELDS] = $request['fields'];
}
// append custom options for this specific request
if ($request['options'])
{
$options = $request['options'] + $options;
}
if ($request['headers'])
{
$headers = $request['headers'] + $headers;
}
// 随机绑定 hosts做负载均衡
//if (self::$hosts)
//{
//$parse_url = parse_url($url);
//$host = $parse_url['host'];
//$key = rand(0, count(self::$hosts)-1);
//$ip = self::$hosts[$key];
//$url = str_replace($host, $ip, $url);
//self::$headers = array_merge( array('Host:'.$host), self::$headers );
//}
// header 要这样拼凑
$headers_tmp = array();
foreach ($headers as $k=>$v)
{
$headers_tmp[] = $k.":".$v;
}
$headers = $headers_tmp;
$options[CURLOPT_URL] = $request['url'];
$options[CURLOPT_HTTPHEADER] = $headers;
return $options;
}
/**
* GET 请求
*
* @param string $url
* @param array $headers
* @param array $options
* @return bool
*/
public function get($url, $fields = array(), $headers = array(), $options = array())
{
return $this->request($url, 'get', $fields, $headers, $options);
}
/**
* $fields 有三种类型:1、数组2、http query3、json
* 1、array('name'=>'yangzetao') 2、http_build_query(array('name'=>'yangzetao')) 3、json_encode(array('name'=>'yangzetao'))
* 前两种是普通的post可以用$_POST方式获取
* 第三种是post stream( json rpc其实就是webservice )虽然是post方式但是只能用流方式 http://input 后者 $HTTP_RAW_POST_DATA 获取
*
* @param string $url
* @param array $fields
* @param array $headers
* @param array $options
* @return void
*/
public function post($url, $fields = array(), $headers = array(), $options = array())
{
return $this->request($url, 'post', $fields, $headers, $options);
}
/**
* Execute processing
*
* @param int $window_size Max number of simultaneous connections
* @return string|bool
*/
public function execute($window_size = null)
{
$count = sizeof($this->requests);
if ($count == 0)
{
return false;
}
// 只有一个请求
elseif ($count == 1)
{
return $this->single_curl();
}
else
{
// 开始 rolling curlwindow_size 是最大同时连接数
return $this->rolling_curl($window_size);
}
}
private function single_curl()
{
$ch = curl_init();
// 从请求队列里面弹出一个来
$request = array_shift($this->requests);
$options = $this->get_options($request);
curl_setopt_array($ch, $options);
$output = curl_exec($ch);
$info = curl_getinfo($ch);
$error = null;
if ($output === false)
{
$error = curl_error( $ch );
}
//$output = substr($output, 10);
//$output = gzinflate($output);
// 其实一个请求的时候没是么必要回调,直接返回数据就好了,不过这里算是多一个功能吧,和多请求保持一样的操作
if ($this->callback)
{
if (is_callable($this->callback))
{
call_user_func($this->callback, $output, $info, $request, $error);
}
}
else
{
return $output;
}
return true;
}
private function rolling_curl($window_size = null)
{
// 如何设置了最大任务数
if ($window_size)
$this->window_size = $window_size;
// 如果请求数 小于 任务数,设置任务数为请求数
if (sizeof($this->requests) < $this->window_size)
$this->window_size = sizeof($this->requests);
// 如果任务数小于2个不应该用这个方法的用上面的single_curl方法就好了
if ($this->window_size < 2)
exit("Window size must be greater than 1");
// 初始化任务队列
$master = curl_multi_init();
// 开始第一批请求
for ($i = 0; $i < $this->window_size; $i++)
{
$ch = curl_init();
$options = $this->get_options($this->requests[$i]);
curl_setopt_array($ch, $options);
curl_multi_add_handle($master, $ch);
// 添加到请求数组
$key = (string) $ch;
$this->requestMap[$key] = $i;
}
do {
while (($execrun = curl_multi_exec($master, $running)) == CURLM_CALL_MULTI_PERFORM) ;
// 如果
if ($execrun != CURLM_OK) { break; }
// 一旦有一个请求完成找出来因为curl底层是select所以最大受限于1024
while ($done = curl_multi_info_read($master))
{
// 从请求中获取信息、内容、错误
$info = curl_getinfo($done['handle']);
$output = curl_multi_getcontent($done['handle']);
$error = curl_error($done['handle']);
// 如果绑定了回调函数
$callback = $this->callback;
if (is_callable($callback))
{
$key = (string) $done['handle'];
$request = $this->requests[$this->requestMap[$key]];
unset($this->requestMap[$key]);
call_user_func($callback, $output, $info, $request, $error);
}
// 一个请求完了就加一个进来一直保证5个任务同时进行
if ($i < sizeof($this->requests) && isset($this->requests[$i]) && $i < count($this->requests))
{
$ch = curl_init();
$options = $this->get_options($this->requests[$i]);
curl_setopt_array($ch, $options);
curl_multi_add_handle($master, $ch);
// 添加到请求数组
$key = (string) $ch;
$this->requestMap[$key] = $i;
$i++;
}
// 把请求已经完成了得 curl handle 删除
curl_multi_remove_handle($master, $done['handle']);
}
// 当没有数据的时候进行堵塞,把 CPU 使用权交出来,避免上面 do 死循环空跑数据导致 CPU 100%
if ($running)
{
curl_multi_select($master, $this->timeout);
}
} while ($running);
// 关闭任务
curl_multi_close($master);
// 把请求清空,否则没有重新 new rolling_curl(); 直接再次导入一批url的时候就会把前面已经执行过的url又执行一轮
unset($this->requests);
return true;
}
/**
* @return void
*/
public function __destruct()
{
unset($this->window_size, $this->callback, $this->options, $this->headers, $this->requests);
}
}

View File

@@ -1,7 +0,0 @@
<?php
$arr = array('fff', 'ggg', '', '');
$arr = array_filter($arr);
print_r($arr);

View File

@@ -1,32 +0,0 @@
<?php
echo "Starting\n";
$gmworker = new GearmanWorker();
$gmworker->addServer('10.10.10.238');
$gmworker->addFunction("reverse", "reverse_fn");
print "Waiting for job...\n";
while($gmworker->work())
{
if ($gmworker->returnCode() != GEARMAN_SUCCESS)
{
echo "return_code: " . $gmworker->returnCode() . "\n";
break;
}
//break;
}
function reverse_fn($job)
{
sleep(3);
echo $job->workload()."\n";
return strrev($job->workload());
}
echo "hello\n";
?>

View File

@@ -2,7 +2,7 @@
IYUU自动辅种工具目前能对国内大部分的PT站点自动辅种支持下载器集群支持多盘位支持多下载目录支持远程连接等。
## 原理
IYUU自动辅种工具英文名iyuuAutoReseed是一款PHP语言编写的Private Tracker辅种脚本通过计划任务或常驻内存按指定频率调用transmission、qBittorrent下载软件的API接口提取正在做种的info_hash提交到服务器API接口根据API接口返回的数据拼接种子连接提交给下载器自动辅种各个站点。
IYUU自动辅种工具英文名IYUUAutoReseed是一款PHP语言编写的Private Tracker辅种脚本通过计划任务或常驻内存按指定频率调用transmission、qBittorrent下载软件的API接口提取正在做种的info_hash提交到服务器API接口根据API接口返回的数据拼接种子连接提交给下载器自动辅种各个站点。
## 运行环境
所有具备PHP运行环境的所有平台

View File

@@ -0,0 +1,177 @@
# IYUU新推出RSS订阅功能
## 功能
自动订阅站点的新种,自动添加下载任务,支持大小过滤。
## 优势
1.弥补部分下载器没有RSS订阅的缺陷
2.与下载器本身的RSS功能相比IYUU自动RSS订阅支持远程连接下载器支持下载器多盘位、支持多目录支持筛选支持下载器集群
## 需要配置什么?
1.必须配置各站的`passkey`秘钥,从各站点的`控制面板`复制到配置文件内对应站点`passkey`处即可;
2.TTG的密钥比较特殊请从RSS链接处复制并给站点配置加一个`rss`配置项详情参考示例配置文件TTG配置
前期如果你正确使用了`IYUUAutoReseed自动辅种工具`的话,本部分就非常简单了。
----------
## 如何配置`workingMode`工作模式、`watch`监控目录、`cliects`下载器?
### 第一步完善全局配置
```php
'default' => array(
// 5.【必须配置】浏览器UA打开http://demo.iyuu.cn 复制过来即可
'userAgent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36',
// 6.【自动辅种必须配置】全局客户端设置(条目不够可以复制)
'clients' => array(
// 全局客户端设置 开始
# 开始
array(
'type' => 'transmission', // 支持transmission、qBittorrent
'host' => 'http://127.0.0.1:9091/transmission/rpc', // 警告注意transmission/rpc这段别动你只需要修改 127.0.0.1:9091
'username' => '',
'password' => '',
),
# 结束
# 开始
array(
'type' => 'qBittorrent', // 支持transmission、qBittorrent
'host' => 'http://www.baidu.com:8083',
'username' => '',
'password' => '',
),
# 结束
// 全局客户端设置 结束
),
'move' =>array(
'type' => 2, // 0保持不变1减2加 3直接替换
'path' =>array(
'/sda1' => '/volume1',
),
),
'workingMode' => 0,
'watch' => '/volume1/downloads',
'filter' => array(
'size'=>array(
'min' => '10GB',
'max' => '280GB',
),
'seeders'=>array(
'min' => 1,
'max' => 3,
),
'leechers'=>array(
'min' => 0,
'max' => 10000,
),
'completed'=>array(
'min' => 0,
'max' => 10000,
),
),
'CONNECTTIMEOUT'=> 60,
'TIMEOUT' => 600,
),
```
----------
### 第二步完善站点配置示例配置m-team
本部分以馒头为例,讲解`工作模式1负载均衡`
关键地方:`clients``workingMode``watch``filter`
- 全局已经配置clients两个用来辅种站点单独配置clients一个用来下载在RSS订阅添加下载任务时**以站点单独配置的clients为准**。
- `workingMode=>1,` 代表当前站点将会工作在模式1
- `watch=>''` 因为站点工作在模式1配置了也无效**留空即可**
- 全局和站点都配置filter该站点RSS订阅添加下载任务时**过滤规则以站点配置为准**
下面是m-team的示例配置代码。
```php
'm-team' => array(
// 14.m-team的cookie 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => 'tp=',
// 15.m-team的passkey 【必须配置】
'passkey' => '',
// 种子Tracker的IP地址选择 可选ipv4ipv6
'ip_type' => 'ipv4',
'clients' => array(
array(
'type' => 'transmission', // 支持transmission、qBittorrent
'host' => 'http://127.0.0.1:9091/transmission/rpc', // 警告注意transmission/rpc这段别动你只需要修改 127.0.0.1:9091
'username' => '',
'password' => '',
),
),
'workingMode' => 1,
'watch' => '',
'filter' => array(
'size'=>array(
'min' => '1GB',
'max' => '280GB',
),
),
),
```
----------
### 第三步完善站点配置示例配置keepfrds
本部分以朋友为例讲解工作模式1负载均衡
关键地方:`clients``workingMode``watch``filter`
- 全局已经配置clients两个用来辅种**站点没有单独配置clients在RSS订阅添加下载任务时以全局配置的clients为准**。
- `workingMode=>1,` 代表当前站点将会工作在模式1
- `watch=>''` 因为站点工作在模式1配置了也无效**留空即可**
- 全局和站点都配置filter该站点RSS订阅添加下载任务时**过滤规则以站点配置为准**
下面是keepfrds的示例配置代码。
```php
'keepfrds' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'workingMode' => 1,
'watch' => '',
'filter' => array(
'size'=>array(
'min' => '1GB',
'max' => '280GB',
),
),
),
```
----------
### 第四步完善站点配置示例配置ourbits
本部分以我堡为例讲解工作模式0watch监控目录
关键地方:`workingMode``watch``filter`
- `workingMode=>0,` 代表当前站点将会工作在模式0**脚本会往指定的watch目录内下载种子**,由下载器添加下载任务;
- `watch=>'/root/downloads'` 脚本会往`/root/downloads`目录内下载种子,由下载器添加下载任务;
- 全局和站点都配置filter该站点RSS订阅添加下载任务时**过滤规则以站点配置为准**
下面是ourbits的示例配置代码。
```php
'ourbits' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'id' => 0, // 用户ID
'is_vip' => 0, // 是否具有VIP或特殊权限0 普通1 VIP
'workingMode' => 0,
'watch' => '/root/downloads',
'filter' => array(
'size'=>array(
'min' => '1GB',
'max' => '280GB',
),
),
),
```
## RSS订阅脚本名字`rss.php`
配置好后,执行命令:`php rss.php`即可查看支持RSS的站点列表。
![IYUU1.png][1]
**总结以上详细讲解了RSS订阅下载、下载免费种时的各种配置的情况请仔细阅读务必理解**
其他站点设置RSS订阅同样道理
[1]: https://www.iyuu.cn/usr/uploads/2020/01/3194879294.png

98
wiki/公告栏.md Normal file
View File

@@ -0,0 +1,98 @@
# 公告栏
### 2020年7月13日08:00:00
【更新提醒】v1.9.1
优化App.Api.Sites接口请求时携带版本号版本为空或低于v1.9.1会返回旧数据);
新增:异步间隔流控算法,适用所有站点;各站独立、执行时间最优;
【升级方法】
1.异步间隔流控使用方法参考v1.9.1版本的config.sample.php[第242-245行]把他复制到你想应用流控的站点配置项内解释count表示每次辅种的最大数量sleep表示当前站点每次下载种子最小间隔多少秒。
2.url_replace和url_join是为了最大化兼容未来各种类型站点提前准备的可以自定义URL
解释url_replace是替换下载链接时使用让下载链接支持替换任意参数。举例'url_replace'=>array('{passkey}'=>'123456789'), 下载链接https://pt.baidu.com/download.php?id={}&passkey={passkey}替换后变为https://pt.baidu.com/download.php?id={}&passkey=123456789
解释url_join是拼接下载链接是使用让下载链接支持任意自定义参数。举例
'url_join' => array('ipv6=1','https=1'), 下载链接https://pt.baidu.com/download.php?id={}&passkey=123456789拼接后变为https://pt.baidu.com/download.php?id={}&passkey=123456789&ipv6=1&https=1
### 2020年7月12日19:34:03
【新增功能】v1.9.0
转移过滤器、选择器;可以只转移指定路径的种子,也可以排除指定路径的种子,按需转移。
【升级方法】
1.主脚本可以通过git pull或覆盖更新
2.然后对照最新config.sample.php把59行60行手动加入到你的config.php对应位置。
【重要提醒】域名部署SSL证书接口域名支持双协议访问http/https如下
https://api.iyuu.cn
http://api.iyuu.cn
### 2020年4月10日17:43:50
针对多合作站点绑定的优化!
1.没有更新到v1.7.5以上版本的群友,尽快更新!
2.正在使用v1.7.5以上版本的群友请删除config目录下的siteLoginCache_*.json文件重新运行辅种即可同时绑定多个合作站点。
### 2020年4月4日00:57:36
v1.7.5版本发布新增合作站点moecat合作站点有Ourbits、 HD Dolby、HDHome、PTHome、moecat。【更新提醒】手动升级可以直接覆盖git安装的可通过git pull命令升级脚本升级后然后对比config.sample.php手动在config.php增加用户id配置项辅种时会自动进行验证。
如果有问题,可以群内@我或私聊或到问答社区http://wenda.iyuu.cn/提问,必回。
### 2020年4月1日00:56:31
IYUUAutoReseed自动辅种备忘录gitee代码仓库https://gitee.com/ledc/IYUUAutoReseed
github代码仓库https://github.com/ledccn/IYUUAutoReseed
爱语飞飞:[https://iyuu.cn](https://iyuu.cn/)
大卫博客:[https://www.iyuu.cn](https://www.iyuu.cn/)
问答社区:[http://wenda.iyuu.cn](http://wenda.iyuu.cn/)
接口文档http://api.iyuu.cn/docs.php
### 2020年3月14日22:03:02
时光如梭转眼至v1.6.3感谢一路走来陪伴的朋友们为了共同的爱好我们相聚在这里本版本上线了合作站点认证系统合作站点有Ourbits、 HD Dolby、HDHome。
【更新提醒】手动更新的可以直接覆盖升级git安装脚本的会自动升级脚本然后对比config.sample.php手动添加用户id配置项启动辅种时会自动进行验证。
如果有问题,可以群内@我或到问答社区http://wenda.iyuu.cn/提问,必回。
### 2020年3月13日14:52:32
【重要提醒】IYUUAutoReseed自动辅种工具合作站点有OurBits、HD Dolby、HDHome可以对使用辅种工具的用户进行认证与绑定。
请大家及时根据新的示例配置config.sample.php增加用户id配置项及时认证。
### 2020年3月9日22:52:11
【喜报】辅种服务器顺利迁移阿里云。辅种接口域名api.iyuu.cn
问答社区wenda.iyuu.cn
### 2020年2月22日12:28:55
转移做种常见错误:
1.Windows下运行脚本却填写Linux路径正确应该填写映射的路径
2.Docker运行脚本却填写实际路径正确应该填写Docker内可读的路径
请勿犯此类常识错误。
另外,跳校验和删除当前做种的配置(第二次转移再用)。
### 2020年2月21日18:14:21
由于与海豚管理组沟通时,支持确认度上出现问题,现调整如下: 使用IYUUAutoReseed自动辅种海豚的小伙伴私聊提供海豚UID所有收集到的UID会提交海豚官方备案提供UID不是为了ban但也请遵守海豚的规则。请在配置内删除海豚passkey、authkey。
后续情况待定。
### 2020年2月21日07:42:14
【新增站点】dicmusic海豚音乐版本升级v1.5.0
【支持辅种】学校、杜比、家园、天空、朋友、馒头、萌猫、我堡、猫站、铂金家、烧包、北洋、TCCF、南洋、TTG、映客、城市、52pt、brobits、备胎、SSD、CHD、ptmsg、leaguehd、聆音、瓷器、hdarea、eastgame(TLF)、1ptba、hdtime、hd4fans、opencd、hdbug、hdstreet、joyhd、u2、upxin(HDU)、oshen、discfan(GZT)、北邮、CCFBits、dicmusic。
【重要提醒】Api/Reseed接口是合并所有客户端发起的请求当做种超过10000时存在失败可能预备10天后停止。v1.3.0以上版本不受此影响。
【升级方法】15号之后的版本直接覆盖。
### 2020年1月9日12:06:03
使用IYUUAutoReseed自动化辅种工具本身是非常安全的辅种过程不会跟PT站点服务器产生任何关系只是会把下载种子链接推送给下载器。也就是说任何站点、任何技术都无法检测你是否使用了IYUU。建议不要自己手动跳校验任何因为跳校验ban号别怪我没提醒也请出事不要怪到IYUU或者Reseed的头上。

View File

@@ -1,4 +1,12 @@
## 重点讲解Ourbits站点的鉴权配置
## 支持用户验证的合作站点:`ourbits`,`hddolby`,`hdhome`,`pthome`,`moecat`
截止2020年7月17日20:30:53【文档更新有延迟最终以gitee、github为准】
----------
## 以讲解Ourbits站点的鉴权配置为例
博客链接https://www.iyuu.cn/archives/337/
IYUU自动辅种工具、Ourbits双方达成合作可以对使用接口的用户实现认证。
### 申请爱语飞飞微信通知token新用户访问http://iyuu.cn 申请!
@@ -6,22 +14,21 @@ IYUU自动辅种工具、Ourbits双方达成合作可以对使用接口的用
![微信通知1.png][1]
![微信通知2.png][2]
![微信通知3.png][3]
2.复制您的token令牌到`/app/config/config.php`文件内的`iyuu.cn`对应的配置字段,保存。如图:
2.复制您的token令牌到`/config/config.php`文件内的`iyuu.cn`对应的配置字段,保存。如图:
![微信通知4.png][4]
### 设置Ourbits
![编辑配置4.png][5]
`passkey`,在你的控制面板 - 密钥
`is_vip`,根据你的实际情况填写,因站点有下载种子的流控,如果你不在限制之列,可以`设置为1`
`id`,为用户中心打开后,浏览器地址栏**http://xxxxx.xxx/userdetails.php?id=`46880`**等号=后面的几个数字,如图:
![编辑配置6.png][6]
到此,配置文件编辑完毕,请记得保存。
如果提示保存格式请保存为UTF8无BOM格式。
[1]: https://www.iyuu.cn/usr/uploads/2019/12/2331433923.png
[2]: https://www.iyuu.cn/usr/uploads/2019/12/3324442680.png
[3]: https://www.iyuu.cn/usr/uploads/2019/12/3181272964.png
[4]: https://www.iyuu.cn/usr/uploads/2019/12/3669828008.png
[5]: https://www.iyuu.cn/usr/uploads/2019/12/3696916642.png
[6]: https://www.iyuu.cn/usr/uploads/2019/12/1230288911.png
[1]: https://www.iyuu.cn/usr/uploads/2019/12/2331433923.png
[2]: https://www.iyuu.cn/usr/uploads/2019/12/3324442680.png
[3]: https://www.iyuu.cn/usr/uploads/2019/12/3181272964.png
[4]: https://www.iyuu.cn/usr/uploads/2019/12/3669828008.png
[5]: https://www.iyuu.cn/usr/uploads/2019/12/3696916642.png
[6]: https://www.iyuu.cn/usr/uploads/2019/12/1230288911.png

View File

@@ -3,7 +3,13 @@ php命令与脚本路径之间是有个空格请注意请注意请注
## IYUU自动辅种命令
`php ./iyuu.cn.php`
`php ./iyuu.php`
## IYUU自动下载种子之RSS订阅
`php ./rss.php 站点名`

View File

@@ -8,35 +8,25 @@
只需配置全局客户端和各网站的passkey密钥没有配置passkey的站点在辅种时候会跳过。2019年12月28日补充辅种hdcity、hdchina需要配置cookie。
#### 问如何获取cookie呢
浏览器F12依次`Network` ==》`Doc` ==》`F5刷新` ==》左侧点页面后,查看右侧`Headers标签`,下拉`cookie`后面的一大段字符串。
#### 问IYUU自动辅种工具向服务器发送了什么实现自动辅种呢
1.文件`phpspider\app\torrent\cache\hashString.txt`是脚本发送给服务器的数据是按下载器分组的种子info_hash2.文件`phpspider\app\torrent\cache\reseed.txt`是服务器返回的可辅种数据。
1.文件`IYUUAutoReseed\torrent\cache\hashString.txt`是脚本发送给服务器的数据是按下载器分组的种子info_hash2.文件`IYUUAutoReseed\torrent\cache\reseed.txt`是服务器返回的可辅种数据。
#### 问:本次添加成功的辅种任务,下次辅种时还会重复添加吗?
答:添加成功的辅种任务,会在本地生成缓存记录,避免重复添加辅种任务,路径在:`phpspider\app\torrent\cachehash`
答:添加成功的辅种任务,会在本地生成缓存记录,避免重复添加辅种任务,路径在:`IYUUAutoReseed\torrent\cachehash`
#### 问:为什么有些站点自动跳过?
答:因为站点在下载种子时有流控或者人机验证,会导致辅种失败;但脚本会在`phpspider\app\torrent\cache`目录下生成以站点命名的手动辅种文本。
#### 问:我拥有辅种时自动跳过站点的特殊权限,如何设置为可以辅种呢?
答:在站点的独立配置区域,添加一行代码`'is_vip' => 1,`即可。例如Ourbits
```php
// ourbits
'ourbits' => array(
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
'cookie' => '',
// 如果需要自动辅种,必须配置
'passkey' => '',
'id' => 46880, // 用户ID
'is_vip' => 1, // 是否具有VIP或特殊权限0 普通1 VIP
),
```
答:因为站点在下载种子时有流控或者人机验证,会导致辅种失败;但脚本会在`IYUUAutoReseed\torrent\cache`目录下生成以站点命名的手动辅种文本。
#### 问为什么幼儿园U2辅种失败
按站内说明修改下载器所在设备的HOST或者在具备修改HOST功能的路由器内修改。
#### 问:如何升级到最新版本?
@@ -73,11 +63,33 @@
请退出登录在登录时勾选下面两个SSL的选项登录后复制cookie重新配置。
#### 问IYUU自动辅种失败怎么排查问题
1.输入种子链接,手动方法,看看种子是否还在?
2.手动下载种子,看看是否出现了下载种子的提示页?
3.瓷器、城市需要cookie辅种请检查cookie是否配置正确如果下载出现提示页面需要更新配置内的cookie、更新cookie
如果整个站都失败,请按以上步骤排查。
#### 问Windows下执行脚本时乱码
用cmd和powershell执行脚本中文输出乱码由于我是写php脚本的非常喜欢国际化的uft8编码。注册表修改有些可以有些不可以直接在控制面板修改。
控制面板\时钟和区域\区域\管理\更改系统区域设置\勾选beta版:使用unicode-utf8全球语言支持,保存重启即可仅支持win10
#### 问小钢炮qBittorrent原装v4.1.5版本无法连接的解决办法?
请按照这篇文章升级种子不用重新校验数据不丢失。小钢炮使用docker版qb 4.1.6 简明教程http://nanodm.net:8092/archives/43/
#### 问:如何反馈问题?
1、点击链接加入群聊【IYUU自动辅种交流】[https://jq.qq.com/?_wv=1027&k=5JOfOlM][1]
1、QQ群859882209[2000人.入门群]931954050[1000人.进阶群]
2、QQ群859882209
2、问答社区http://wenda.iyuu.cn
3、issues https://gitee.com/ledc/IYUUAutoReseed/issues
3、博客https://www.iyuu.cn
4、issues https://gitee.com/ledc/IYUUAutoReseed/issues

View File

@@ -6,10 +6,14 @@
| m-team IPv4、IPv6自定义配置 | 已完成 | 2019年12月25日 | 2019年12月25日 |
| 未配置客户端智能过滤 | 已完成 | 2019年12月25日 | 2019年12月25日 |
| 自动辅种结束微信通知 | 已完成 | 2019年12月25日 | 2019年12月27日 |
| 做种客户端间转移 | 已完成 | 2019年12月25日 | 2020年1月14日 |
| 做种客户端间转移(需联网) | 已完成 | 2019年12月25日 | 2020年1月14日 |
| 手动辅种按目录分组 | 已完成 | 2019年12月26日 | 2020年1月14日 |
| WEB页面生成配置 | 暂未开始 | | |
| 自动转移客户端 | 暂未开始 | | |
| 脚本docker容器化 | 暂未开始 | | |
| 自动转移客户端 | 已完成 | 2020年1月27日 | 2020年1月29日 |
| 做种客户端本地转移 | 已完成 | 2020年2月21日 | 2020年2月21日 |
| Google身份验证器谷歌动态口令 | 开发中 | 2020年2月21日 | |
| 自动更新 | 开发中 | 2020年2月21日 | |
| WEB页面生成配置 | 开发中 | 2020年2月21日 | |
| 种子删除自动同步 | 已完成 | 2020年2月21日 | 2020年5月6日 |
| 脚本docker容器化测试版 | 已完成 | 2020年2月21日 | 2020年2月21日 |
| 浏览器插件 | 暂未开始 | | |
| 合集自动拆包辅种 | 暂未开始 | | |

View File

@@ -1,3 +1,85 @@
### 更多日志请查看:
### https://gitee.com/ledc/IYUUAutoReseed/commits/master
------
### 2020年7月3日
1. 优化windows的辅种批处理采用环境变量调用
2. 新增站点HDRoute、hdbd、haidan、HDfans
3. 优化curl的连接参数
4. 新增小钢炮专用的docker脚本
5. 新增合作站点MoeCat
6. 微信报表增加版本号提示
7. qBittorrent下载器增加root_folder配置项是否创建子文件夹
### 2020年3月22日
1. 转移种子,设置自动开始开关
2. 添加git自动更新.cmd
3. 优化做种客户端转移配置的提示
4. 人性化加入vendor目录
5. 修复因不支持站点缓存引起的错误提示
6. 新增合作站点hddolby、hdhome、pthome
7. 修复瓷器辅种乱码不美观的问题
8. 简化docker编译文件
9. 优化合作站点登录缓存,检索请求
10. 优化输出提示未填写passkey的站点只提示一次后续自动跳过
11. 增加通用备份脚本
### 2020年2月26日
1. 新增葡萄pt
2. 升级docker制作脚本每次制作都是最新版。
3. 优化过滤transmission连接URL兼容性修复网址后有斜杠无法连接的问题
4. 发布包到composer支持composer安装
5. 加入git自动更新脚本
6. 优化提高Windows平台转移做种客户端的兼容性
7. 更新文档、新增支持站点天雪
### 2020年2月21日
发行版v1.5.0
1. 新增做种列表备份功能,便于紧急恢复。
2. 新增docker容器安装使用方法
3. 修复transmission添加任务后ID无法显示的bug
4. 优化https判断
5. 新增通知接口
6. 修复扩展参数extra_options错误
7. 删除示例配置圣城cnscg
8. 新增教程小钢炮qBittorrent连接失败的处理办法
9. 优化大于一万条做种,防止因精简配置,可能导致的超时设置无效。
10. 增加转移做种搜索目录命令find / -name BT_backup
11. 新增海豚音乐dicmusic
### 2020年1月30日
新增CCFBits站点辅种
### 2020年1月28日
新增:北邮站点辅种
新增:做种客户端转移功能
优化:删除用不到的依赖包
### 2020年1月21日
新增支持的站点列表json本地化
新增:在线查询支持站点控制台显示
新增Windows系统composer install安装依赖包命令
### 2020年1月17日
优化:文件结构,辅种脚本前置检查;
修复:瓷器、城市辅种时目录不对应问题。
### 2020年1月14日
更新hdbug域名删除下载免费种冗余文件。

View File

@@ -1,102 +1,162 @@
以下教程以windows为基础进行讲解其他系统同理
博客链接https://www.iyuu.cn/archives/324/
以下教程以windows为基础进行讲解通用威联通、群晖、铁威马等Linux系统
# 重要提示:请先完整阅读完再动手操作!
## 第一步 下载压缩包
从[码云仓库][1]下载最新源码解压缩到D盘的根目录下。
**特殊说明新手第一次使用建议去群内下载zip压缩包。**
## 第二步 复制一份配置文件
打开`D:\IYUUAutoReseed\app\config`目录,复制一份`config.sample.php`存为`config.php`
打开`D:\IYUUAutoReseed\config`目录,复制一份`config.sample.php`存为`config.php`这样操作后,需要升级新版本时,直接覆盖即可,不会影响到配置。
这样操作后,需要升级新版本时,直接覆盖即可,不会影响到配置。
## 第三步 编辑配置文件
提醒千万不要用windows记事本来编辑配置文件(会导致乱码)
提醒千万不要用windows记事本来编辑配置文件
推荐编辑软件:`VS code``EditPlus``SublimeText``Notepad++`保存格式选UTF8 无BOM
配置文件内容较多,新手往往很迷茫,不知道改哪里,在这里我重点强调2个步骤:
`1.编辑全局客户端; 2.编辑各站的秘钥,即passkey`
配置文件内容较多,新手往往很迷茫,不知道改哪里,在这里我重点强调3个步骤:
`1.申请爱语飞飞token2.编辑全局客户端; 3.编辑各站的密钥即passkey4.配置合作站点的passkey用户id`
其他配置,如果不懂也没有关系;先保持默认,等脚本运行起来,再修改也不迟。另外,修改时一定要细心,仔细看教程。
打开`D:\IYUUAutoReseed\app\config\config.php`文件,如下图:
![编辑配置1.png][2]
**下面开始详细步骤!!!!**
### 填写全局客户端
上图红框内的是`transmission`的示例配置,绿框是`qBittorrent`的示例配置;
IYUU自动辅种工具目前支持这两种下载器支持多盘位辅种时全自动对应资源的下载目录。
1编辑`transmission`下载器
`http://127.0.0.1:9091/transmission/rpc`是下载器的连接参数,你要修改的部分是`127.0.0.1:9091`改成你的IP与端口本机使用无需修改局域网内的机器请填写局域网IP与端口远程使用请填写DDNS的远程连接域名与端口。
username是用户名、password是密码。
如果你没有用到`transmission`下载器,请把红框的内容都删除。
2编辑`qBittorrent`下载器
方法与上一步相同只需填写ip、端口、用户名、密码即可。如果您是windows下的qBittorrent请参考下图打开`WEB用户界面`
![qb设置WEB用户界面.png][3]
因为我两个下载器都在用,编辑好后,如图:
![编辑配置2.png][4]
### 填写各站秘钥passkey
IYUU自动辅种需要您配置各站的passkey没有配置passkey的站点会自动跳过
从各站点的控制面板,找到您的`秘钥`复制粘贴过来即可。
配置好后如图:
![编辑配置3.png][5]
----------
## 第四步重点讲解Ourbits站点的配置
IYUU自动辅种工具、Ourbits双方达成合作可以对使用接口的用户实现认证。
### 申请爱语飞飞微信通知token新用户访问http://iyuu.cn 申请!
1.点击`开始使用`,出现二维码,用`微信扫码`
![微信通知1.png][6]
![微信通知2.png][7]
![微信通知3.png][8]
2.复制您的token令牌到`/app/config/config.php`文件内的`iyuu.cn`对应的配置字段,保存。如图:
![微信通知4.png][9]
![微信通知1.png][2]
![微信通知2.png][3]
![微信通知3.png][4]
2.复制您的token令牌到`/config/config.php`文件内的`iyuu.cn`对应的配置字段,保存。如图:
![微信通知4.png][5]
### 设置Ourbits
----------
### 填写全局客户端
打开`D:\IYUUAutoReseed\config\config.php`文件,如下图:
![编辑配置1.png][6]
上图红框内的是`transmission`的示例配置,绿框是`qBittorrent`的示例配置;
IYUU自动辅种工具目前支持这两种下载器支持多盘位辅种时全自动对应资源的下载目录。
#### 1编辑`transmission`下载器
`http://127.0.0.1:9091/transmission/rpc`是下载器的连接参数,你要修改的部分是`127.0.0.1:9091`改成你的IP与端口本机使用IP无需修改,端口改成你的局域网内的机器请填写局域网IP与端口远程使用请填写DDNS的远程连接域名与端口。
username是用户名、password是密码。没有用户名和密码的都填写`null`
如果你没有用到`transmission`下载器,红框内不要做任何改动,也无需删除(脚本内会自动处理)。
#### 2编辑`qBittorrent`下载器
方法与上一步相同只需填写ip、端口、用户名、密码即可。如果您是windows下的qBittorrent请打开`WEB用户界面`,设置如下图:
![qb设置WEB用户界面.png][7]
因为我两个下载器都在用,编辑好后,如图:
![编辑配置2.png][8]
----------
### 填写各站秘钥passkey
IYUU自动辅种需要您配置各站的passkey目前支持40多个站点的自动辅种没有配置passkey的站点会自动跳过。注2019年12月28日补充辅种hdcity、hdchina需要配置cookie。
从各站点的控制面板或对应地方,找到您的`密钥`复制粘贴过来即可。
配置好后如图:
![编辑配置3.png][9]
----------
## 第四步,合作站点的配置
此处配置也可以参考这里https://www.iyuu.cn/archives/337/
IYUU自动辅种工具与站点达成合作可以对使用接口的用户实现认证。以Ourbits为例进行讲解
![编辑配置4.png][10]
`passkey`,在你的控制面板 - 密钥
`is_vip`,根据你的实际情况填写,因站点有下载种子的流控,如果你不在限制之列,可以`设置为1`
`id`,为用户中心打开后,浏览器地址栏**http://xxxxx.xxx/userdetails.php?id=`46880`**等号=后面的几个数字,如图:
![编辑配置6.png][11]
到此,配置文件编辑完毕,请记得保存。
如果提示保存格式请保存为UTF8无BOM格式。
如果有多个合作站账号,请全部配置,如图:
![合作站点ID.png][12]
------
到此配置文件编辑完毕请记得保存如果提示保存格式请保存为UTF8无BOM格式。
## 群晖、铁威马、威联通等Linux环境
----------
## 群晖、铁威马、威联通等Linux的php环境
**群晖、铁威马、威联通自带php运行环境非常简单。**
经过上面步骤其实已经完成了配置只需要把脚本复制到设备内用php命令运行脚本即可。
下面是各系统运行php的命令
群晖php`php`
威联通php `/mnt/ext/opt/apache/bin/php`
铁威马php`php`
群晖php命令`php`
威联通php命令`/mnt/ext/opt/apache/bin/php`
----------
铁威马php命令`php`
**威联通补充几个命令,找到辅种脚本:**
```sh
#查看php版本
/mnt/ext/opt/apache/bin/php -v
#切换admin
sudo -i
#搜索iyuu.php或IYUUAutoReseed
find / -name iyuu.php
#根据上一行命令搜索结果,组成辅种命令:
/mnt/ext/opt/apache/bin/php 找到的路径/iyuu.php
```
----------
## Windows安装PHP运行环境
也可以去官方下载【https://www.php.net/downloads】,官方下载的记得开启`curl、fileinfo、mbstring`这3个扩展。
另外我打包了一份,下载地址:
微云链接https://share.weiyun.com/5EiXLfn 密码ezsvnb
也可以去官方下载【https://www.php.net/downloads】
**特别提醒:官方下载的记得开启`curl、json、mbstring`这3个扩展。**
**特别提醒:官方下载的记得开启`curl、json、mbstring`这3个扩展。**
**特别提醒:官方下载的记得开启`curl、json、mbstring`这3个扩展。**
![php开启扩展.png][13]
**另外我打包了一份【扩展已开启】,下载地址:**
!!!
<a href="http://www.dawei.hk:81/php-7.4.2-nts-Win32-vc15-x86.zip" target="_blank"><h3><code>http://www.dawei.hk:81/php-7.4.2-nts-Win32-vc15-x86.zip</code></h3></a>
<a href="http://www.dawei.hk:81/php-7.4.2-nts-Win32-vc15-x64.zip" target="_blank"><h3><code>http://www.dawei.hk:81/php-7.4.2-nts-Win32-vc15-x64.zip</code></h3></a>
!!!
----------
链接https://share.weiyun.com/5I13dek 密码utcjsx
链接https://share.weiyun.com/57uYFrn 密码gurkdc
下载回来是一个ZIP压缩包解压到`D:\IYUUAutoReseed\`目录内,文件结构如图:
![编辑配置7.png][12]
点击红框内`执行辅种`即可。
![编辑配置7.png][14]
重点根据你下载的版本不同、路径不同请把你解压的php环境的路径添加进系统的环境变量内。比如我下载的是`php-7.4.2-nts-Win32-vc15-x86.zip`解压到了D盘根目录下可以这样添加
![请输入图片描述][15]
然后,点击红框内`执行辅种`即可,也可以运行命令:`php iyuu.php`
> 提示新手第一次请去群内下载打包好的Windows版本双击运行即可。
如果你前期严格按照配置一步步操作,这里会正常显示跑动的辅种列表。正常如图:
![编辑配置8.png][13]
![编辑配置8.png][16]
[1]: https://gitee.com/ledc/IYUUAutoReseed
[2]: https://www.iyuu.cn/usr/uploads/2019/12/2720183833.png
[3]: https://www.iyuu.cn/usr/uploads/2019/12/405587689.png
[4]: https://www.iyuu.cn/usr/uploads/2019/12/441257656.png
[5]: https://www.iyuu.cn/usr/uploads/2019/12/890327305.png
[6]: https://www.iyuu.cn/usr/uploads/2019/12/2331433923.png
[7]: https://www.iyuu.cn/usr/uploads/2019/12/3324442680.png
[8]: https://www.iyuu.cn/usr/uploads/2019/12/3181272964.png
[9]: https://www.iyuu.cn/usr/uploads/2019/12/3669828008.png
[10]: https://www.iyuu.cn/usr/uploads/2019/12/3696916642.png
[11]: https://www.iyuu.cn/usr/uploads/2019/12/1230288911.png
[12]: https://www.iyuu.cn/usr/uploads/2019/12/3189986236.png
[13]: https://www.iyuu.cn/usr/uploads/2019/12/2523845772.png
[1]: https://gitee.com/ledc/IYUUAutoReseed
[2]: https://www.iyuu.cn/usr/uploads/2019/12/2331433923.png
[3]: https://www.iyuu.cn/usr/uploads/2019/12/3324442680.png
[4]: https://www.iyuu.cn/usr/uploads/2019/12/3181272964.png
[5]: https://www.iyuu.cn/usr/uploads/2019/12/3669828008.png
[6]: https://www.iyuu.cn/usr/uploads/2019/12/2720183833.png
[7]: https://www.iyuu.cn/usr/uploads/2019/12/405587689.png
[8]: https://www.iyuu.cn/usr/uploads/2019/12/441257656.png
[9]: https://www.iyuu.cn/usr/uploads/2019/12/890327305.png
[10]: https://www.iyuu.cn/usr/uploads/2019/12/3696916642.png
[11]: https://www.iyuu.cn/usr/uploads/2019/12/1230288911.png
[12]: https://www.iyuu.cn/usr/uploads/2020/04/712430028.png
[13]: https://www.iyuu.cn/usr/uploads/2019/12/3007415838.png
[14]: https://www.iyuu.cn/usr/uploads/2019/12/3189986236.png
[15]: https://www.iyuu.cn/usr/uploads/2020/03/686511859.png
[16]: https://www.iyuu.cn/usr/uploads/2019/12/2523845772.png