diff --git a/README.en.md b/README.en.md new file mode 100644 index 0000000..eb34c9c --- /dev/null +++ b/README.en.md @@ -0,0 +1,77 @@ +

🖼️ Pixiv Daily Ranking Widget

+

+ 中文 +

+ Want to add a Pixiv Daily Ranking Widget to your website? Now, it only takes one line code to do it! + DEMO +

+ +## ✨ Features +- Easy to use by only one `HTML` code +- Adaptive width and height. Recommended width `240px`, height `380px` or more +- Click the image redirect to detail page +- Automatic daily update +- Built-in multi image-hosting support, on-demand image loading, extremely low resource consumption +- Provide API service, including ranking update date, thumbnail url and detail page url, etc. + +## 🤔 How to use +Just add this line to your page +```html + +``` + +Take `Wordpress` as an example. First go to `wp-admin`, click **Appearance** -> **Widgets** +Add a **Text** or **Custom HTML** widget to the right in the appropriate place and fill in the above code + +[Advance Usage](doc/advance-usage.en.md) + +## 🛠️ How to deploy +Want to customize the code yourself? Think the service I provide is too slow? + +You can also easily have your own widget! +> Requires PHP version >= 5.6 + +[Deployment Documentation](doc/deploy.en.md) + +## 🔌 API +[Ranking data (already uploaded to the image-hosting)](https://cloud.mokeyjay.com/pixiv/?r=api/pixiv-json) (recommended) +[Ranking data (pixiv url)](https://cloud.mokeyjay.com/pixiv/?r=api/source-json) + +`data` is the ranking data; `date` is the ranking date (maybe yesterday or the day before yesterday, because the official update time is not necessarily) + +Both api automatically return the corresponding cross-domain header based on the `Origin` or `Referer` in the request header. They can be called directly by the front-end + +> The `image` and `url` keys are for compatibility with users of 4.x and earlier versions, you can ignore them + +## 🆙 Upgrade Guide +1. [Download ZIP](https://github.com/mokeyjay/pixiv-daily-ranking-widget/releases/latest) +2. Unzip and overwrite the `app` and `index.php` to your server + +### From 4.x to 5.0 +1. Check the `image_hosting` item's comment from [config.php](config.php#L90), select the appropriate option and fill it in your `config.php` +2. Delete all files in 'storage/app', then let the program refresh the ranking data + +## 🌟 Changelog +### Added +- api service support cross-domain +- scheduled job to clearing historical logs +- support 8 free image-hosting +### Optimization +- Picture display effect +- Left and right arrows will auto hidden now +- Show works title and author name when hovering +- Upgrade front-end dependency package to the latest, reduce dependency +- Improve logging function +- Enrich interface data to fit more scenarios +- Replace Baidu statistics with Google statistics +- Update default UA +### Fix +- the project url is not correctly retrieved in some cases +- download invalid picture in some cases +### Other +- Remove invalid Alibaba image-hosting + +[History](doc/log.en.md) + +## 👨‍💻 About author +[mokeyjay](https://www.mokeyjay.com), IT and ACG lover diff --git a/README.md b/README.md index c810afb..6cba587 100644 --- a/README.md +++ b/README.md @@ -1,178 +1,75 @@ -## Pixiv每日排行榜小挂件 -![Pixiv每日排行榜小挂件效果图](https://i.loli.net/2019/05/04/5ccd5293141b4.jpg) -## 简介 -骚年,你是`ACG`或绘画爱好者吗?你希望在你的博客或网站中添加一个 **`Pixiv`每日排行榜** 的展示功能吗?现在,无需在茫茫互联网中寻找适合自己站点的插件了,只需要几个文件或是一行代码即可实现! - -## 特色 -- 一行`HTML`代码即可调用,方便快捷 -- 适合放在大部分博客或网站的侧边栏 -- 自适应宽高。推荐宽度`240px`、高度`380px` +

🖼️ Pixiv 每日排行榜小挂件

+

+ English +

+ 想要在你的网站页面中添加一个 Pixiv 每日排行榜 的展示功能吗?现在,只需要一行代码即可实现! + 在线预览 +

+ +## ✨ 特色 +- 一行 `HTML` 代码即可调用,方便快捷 +- 自适应宽高。推荐宽度 `240px`、高度 `380px` 或以上 - 点击图片可跳转到对应作品详情页 - 每日自动更新,无需人工干预 - 内置多图床支持、按需加载图片,极低资源消耗 -- 提供API服务,含有排行榜更新日期、缩略图url及详情页url - -## 开源地址 -[Github](https://github.com/mokeyjay/Pixiv-daily-ranking-widget) - -## 如何部署 -### 方案一:使用[超能小紫](https://www.mokeyjay.com)提供的服务 -> 感谢 [rixCloud](https://rixcloud.app/mokeyjay) 提供代理支持 +- 提供 API 服务,含有排行榜更新日期、缩略图 url 及详情页 url 等 -该方案适用于动手能力较差或较懒或没有特殊需求的用户。且已配置数个图床,访问速度较快 -服务地址:[https://cloud.mokeyjay.com/pixiv](https://cloud.mokeyjay.com/pixiv) - -以`Wordpress`为例,首先进入 后台 -> 外观 -> 小工具 -向右边适当的位置添加一个 **文本** 或 **自定义HTML** 小工具,标题随意,内容为 +## 🤔 如何使用 +将这行代码添加到网页上即可 ```html - + ``` -点击保存按钮即可回到博客首页预览效果咯~ -如果你了解`CSS`的话,还可以随意修改`iframe`的`style`属性 -推荐宽度`240px`、高度`380px` (因为P站缩略图最大就是这个尺寸) - -#### 自定义背景颜色 -默认为透明背景色,一般情况下不必特意去改背景颜色 - -如果你还是要改,可以参考下面的示例: -例如将上面`iframe`的`src`属性的值改为`https://cloud.mokeyjay.com/pixiv/?color=f00`试试看? -正常情况下背景颜色会变成**红色**,即`#f00`。如果颜色没有改变,可能是缓存问题,刷新几次即可 -`color`的值就是CSS内使用的颜色值,可为3或6位16进制字符。无需 **#** 号 -#### 自定义Top数量 -你还可以通过`limit`参数限制图片数量 -例如`https://cloud.mokeyjay.com/pixiv/?color=f00&limit=10` -则可以得到背景为红色的Top10画册 -> `limit`参数的范围必须在`1-500` 之间 - -#### API服务 -[图床缩略图URL+详情页URL](https://cloud.mokeyjay.com/pixiv/storage/app/pixiv.json)(推荐) -[Pixiv原始缩略图URL+详情页URL](https://cloud.mokeyjay.com/pixiv/storage/app/source.json) -内容很简单,相信大家看了就知道可以怎么用了,不再赘述 - - -### 方案二:自行架设服务 -适用于动手能力较强或需要深度自定义的用户 -> 需要PHP版本 >= 5.4 - -首先[下载源代码](https://github.com/mokeyjay/Pixiv-daily-ranking-widget/releases/latest),解压 -使用专业编辑器(例如`Sublime`、`Notepad++`等,切忌使用记事本)编辑`config.php`,根据实际情况修改相应配置 -> 由于Pixiv已经被墙,如果你想要将此项目部署在国内,请务必配置 `proxy` 配置项 - -> 每个配置项的说明都以注释的形式标注在文件内。如果你看不懂,那就说明你比较适合**方案一** - -最后一步,给予`storage`目录读写权限 -具体使用方法参考**方案一** - -### 注意事项 -- 推荐使用方案一,由我本人维护,如有问题第一时间更新 -- 方案二反馈问题之前,请先将 `log_level` 设为 `['ERROR', 'DEBUG']` ,并再次重现问题后,带着 `logs` 来反馈 -- 本项目免费开源,仅供学习交流。请勿用于任何商业用途,作者不承担任何责任 - -## 更新日志 -### 4.4.2 -- 更新 Curl 类的默认 UA -- 删除已经失效的图床 -- 更新阿里巴巴图床 -- 添加 `disable_web_job` 开关,详见 `config.php` - -### 4.4.1 -- 更新 Curl 类的默认 UA -- 修复部分情况下获取项目URL错误 -- 修复获取排行榜数据失败时会无限重试的问题 -- 优化是否需要更新数据的判断机制 -- 删除已经失效的 alibaba 图床 - -### 4.4 -- 改用官方 ajax 接口获取排行数据 -- 添加 阿里巴巴、百度、今日头条 图床接口 -- 更新 smms 图床接口到 v2 版本 -- 删除已被废弃的 img.sb 图床接口 -- 删除已被封锁的 京东 图床接口 -- 删除 loading 页面的统计代码 -- 改用综合排行榜数据,而非仅限于插画 -- 图片数量限制扩充到 500,达到官方上限 -- service 配置项的取值不再影响 limit -- 其他优化、bug 修复 - -### 4.3 -- 修复Pixiv排行榜页面代码改版导致的无法更新 -- 跳转至详情页时使用 https - -### 4.2 -- 修复Pixiv排行榜页面代码改版导致的无法更新 -- 添加获取排行榜失败时的最大重试次数 - -### 4.1 -- 现已支持透明背景 - -### 4.0 -- 几乎重写了所有代码的船新版本,更多新特性与bug等你来发掘! -- 由于pixiv全面开启反盗链,为了迎合此变化。已将`download`和`url_cache`这两个不再有存在意义的开关去除。现在会强制下载缩略图,然后再根据配置上传到各个图床或存储在服务器本地 -> 碎碎念:原本这个项目只是随便搞搞,没想到后面功能越堆越多,代码也越来越丑。作为本辣鸡github上最高star的项目实在是丢人。好在我花了几天时间撸了这个4.0,总算是不那么丢人了 -> 还有就是添加了多图床的支持,每个月能节省几百G的流量了嘤 - -### 3.0 -- 添加`$download_proxy`配置项 -- 由于Pixiv的图片url添加了防盗链无法被直接显示,因此`$download`配置项默认开启 - 为了更好的显示效果,自行部署的用户建议配置一个定时任务,每天0点触发`download.php` - -### 2.9 -- 修复因Pixiv改动导致挂掉的问题 - -### 2.8 -- 尝试优化更新锁,防止高并发下重复更新 -- 从 Conf::$download 中独立出配置项 Conf::$url_cache,现在可以仅缓存图片url而不缓存缩略图了 -- 添加贴图库图床支持 - -> 贴图库免费版并不是很好用且不支持https,建议优先使用sm.ms,贴图库仅作为备用 -> 由于之前更新锁在高并发下有些问题无法很好的发挥作用,导致我的服务器IP因重复上传被sm.ms图床封了。而我个人也无力支撑高昂的CDN费用。因此即日起**方案一**不再提供CDN加速,改为直接从P站获取图片 -> 方案一目前由360网站卫士提供CDN支持 - -### 2.7 -- 添加图片压缩功能,降低服务器带宽压力(需要GD库) -- 修复sm.ms图床支持,降低失败概率 -- 添加sm.ms图床上传日志 - -> 如果开启`$enable_smms`出现问题,反馈时请带上日志文件 - -### 2.6 -- 添加sm.ms图床支持。一键启用即可大幅降低服务器带宽压力、节省流量。感谢[@Showfom](https://sb.sb/)提供图床 - -> 我才不告诉你是因为方案一每天跑掉我几G流量,心疼不已才加的这个功能呢 -> 如果连续3次上传失败,则从服务器本地读取图片,确保访问正常 - -### 2.5 -- 修复因Pixiv改版导致挂掉的问题 -- Pixiv原生支持https啦!可喜可贺 - -### 2.4 -- 修复特定情况下URL的`limit`参数无效的问题 -- 修复**方案一**缓存问题 -- 修复上面效果图SSL证书问题 - -### 2.3 -- 更换了前端库引用地址,修复移动宽带下加载慢的问题 -- 添加协议自适应,修复在关闭缓存或缓存还没全部完成时影响小绿锁的问题 -- 以上更新来自@灵乌路空 的友情PR,我们一起对她PRPR以示感激吧 -- 超能小紫的方案一服务现已支持HTTPS。咬牙忍痛上了收费CDN,请大家且用且珍惜 -- 要是被滥用到我吃不消费用的话可能会暂停服务噢~ -- 如果访问量较高的话建议还是自行搭建服务,谢谢各位的支持与谅解 - -### 2.2 -- 优化下载线程以支持自行部署HTTPS - -### 2.1 -- 规划2.0时脑子抽了,非要把所有逻辑都局限在一个文件里。虽然各方面确实有所提升,但在一些情况下照样会出现那些老问题。例如缩略图下载失败啊、PHP超时导致下载中断之类。因此在我测试并意识到这一点时,赶紧开始了新版本的开发 光速打脸 -- 去除自动更新锁机制,缩略图已存在并且有效时不再重复下载。防止因网络波动或超时导致的缩略图下载失败 - -### 2.0 -- 整体重构,各机制大幅优化 -- 添加自动更新锁机制,避免高访问量时并发更新浪费资源 -- 全新的伪多线程自动更新机制,后台更新不影响使用 -- 更新失败重试,避免因为网络问题导致的部分图片获取失败 - -## 初衷 -前几天跟朋友聊天,朋友说希望能在自己博客侧边栏中显示[Pixiv](http://www.pixiv.net/)的每日排行榜。我自己也是个`ACG`爱好者,被他这么一说也想弄一个。昨晚终于有空,花了半个多小时写完。[自己博客](https://www.mokeyjay.com)用上了感觉不错,完善了一下加了点功能开源出来福利各位 -## 关于作者 -[超能小紫](https://www.mokeyjay.com),常用ID`mokeyjay`。热爱IT与ACG的学渣 \ No newline at end of file +以 `Wordpress` 为例。首先进入后台,点击 外观 -> 小工具 +向右边适当的位置添加一个 **文本** 或 **自定义HTML** 小工具,内容填写上述代码即可 + +[高级用法](doc/advance-usage.md) + +## 🛠️ 如何部署 +想要自己定制代码?嫌我提供的服务太慢? +你也可以轻松拥有完全属于自己的小挂件! +> 需要 PHP 版本 >= 5.6 + +[部署文档](doc/deploy.md) + +## 🔌 API +[排行榜数据(已上传至图床)](https://cloud.mokeyjay.com/pixiv/?r=api/pixiv-json)(推荐) +[排行榜数据(pixiv url)](https://cloud.mokeyjay.com/pixiv/?r=api/source-json) + +其中 `data` 为排行榜数据;`date` 为排行榜日期(可能是昨天或者前天,因为官方更新时间不一定) +这两个接口都会自动根据请求头的 `Origin` 或者 `Referer` 返回对应跨域头。可供前端直接调用 + +> `image` 和 `url` 两个键是为了兼容 4.x 及之前版本的用户,无需理会 + +## 🆙 升级指南 +1. [下载源代码](https://github.com/mokeyjay/Pixiv-daily-ranking-widget/releases/latest) +2. 解压缩,将其中的 `app` 和 `index.php` 覆盖到线上环境 + +### 从 4.x 升级到 5.0 +1. 查看 [config.php](config.php#L90) 中 `image_hosting` 配置项的注释说明,选择适合你的图床配置,填写到你线上环境的 `config.php` 中 +2. 删除 `storage/app` 下的所有文件,让程序重新获取排行榜数据 + +## 🌟 更新日志 +### 新增 +- 支持跨域请求的数据接口 +- clear-log 任务用于清除历史日志文件 +- 共计 8 个国内外公开、免费的图床 +### 优化 +- 图片显示效果 +- 左右翻页箭头现在会自动隐藏了 +- 鼠标悬浮时显示作品标题及作者名称 +- 升级前端依赖包至最新、减少依赖 +- 完善日志记录功能 +- 丰富接口数据以适应更多场景 +- 将百度统计更换为谷歌统计 +- 更新默认 UA +### 修复 +- 部分情况下无法正确获取项目 url 的问题 +- 部分情况下可能会下载到空白图片的问题 +### 其他 +- 去掉失效的阿里巴巴图床 + +[历史更新日志](doc/log.md) + +## 👨‍💻 关于作者 +常用 ID [mokeyjay](https://www.mokeyjay.com),热爱 IT 与 ACG 的学渣 \ No newline at end of file diff --git a/app/App.php b/app/App.php index 676dad5..0595b19 100644 --- a/app/App.php +++ b/app/App.php @@ -4,10 +4,8 @@ use app\Jobs\Job; use app\Libs\Config; -use app\Libs\Lock; -use app\Libs\Pixiv; -use app\Libs\Storage; -use app\Libs\Tools; +use app\Libs\Log; +use app\Libs\Str; class App { @@ -19,8 +17,8 @@ protected static function init() Config::init(); // 注册全局错误捕捉 - set_exception_handler(function (\Exception $exception) { - Tools::log($exception->getMessage(), 'ERROR'); + set_exception_handler(function ($exception) { + Log::write($exception->getMessage(), 'ERROR'); http_response_code(500); die; }); @@ -30,11 +28,7 @@ public static function run() { self::init(); - $opt = getopt('j:'); - if (!empty($_GET['job']) || isset($opt['j'])) { - self::job(); - return; - } + self::job(); self::route(); } @@ -46,33 +40,59 @@ public static function run() protected static function job() { $opt = getopt('j:'); - $jobName = isset($_GET['job']) ? $_GET['job'] : $opt['j']; - $jobName = ucfirst(strtolower($jobName)); + if (empty($_GET['job']) && empty($opt['j'])) { + return; + } + + $jobName = !empty($_GET['job']) ? $_GET['job'] : $opt['j']; $job = Job::make($jobName); + + if (!empty($_GET['job']) && $job->onlyActivateByCli) { + throw new \Exception("任务 {$jobName} 只能通过 cli 触发"); + } + if (!$job) { throw new \Exception("任务 {$jobName} 加载失败"); } set_time_limit(0); if ($job->run()) { - Tools::log("任务 {$jobName} 执行完毕"); + Log::write("任务 {$jobName} 执行完毕"); echo "任务 {$jobName} 执行完毕"; } else { throw new \Exception("任务 {$jobName} 执行失败:{$job->getErrorMsg()}"); } + + exit; } /** - * 调用控制器 + * 路由 */ protected static function route() { $route = isset($_GET['r']) ? $_GET['r'] : 'index'; $route = explode('/', $route); - $method = array_pop($route) ?: 'index'; - $controller = 'app\\Controllers\\' . ucfirst(array_pop($route) ?: 'index') . 'Controller'; + $controller = Str::studly(array_shift($route) ?: 'index'); + $method = lcfirst(Str::studly(array_pop($route) ?: 'index')); + + $class = "app\\Controllers\\{$controller}Controller"; + if (!class_exists($class)) { + Log::write("控制器不存在:{$class}", Log::LEVEL_ERROR); + + http_response_code(404); + die; + } + + $controller = new $class; + if (!is_callable([$controller, $method])) { + Log::write("无法调用此方法:{$class}->{$method}()", Log::LEVEL_ERROR); + + http_response_code(404); + die; + } - (new $controller)->$method(); + $controller->$method(); } } \ No newline at end of file diff --git a/app/Controllers/ApiController.php b/app/Controllers/ApiController.php new file mode 100644 index 0000000..81a1f65 --- /dev/null +++ b/app/Controllers/ApiController.php @@ -0,0 +1,51 @@ +setAutoCorsHeader(); + } + + /** + * 自动设置跨域头 + * @return void + */ + private function setAutoCorsHeader() + { + if (!isset($_SERVER['HTTP_ORIGIN']) && !isset($_SERVER['HTTP_REFERER'])) { + return; + } + + $origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : $_SERVER['HTTP_REFERER']; + + header('Access-Control-Allow-Origin: ' . $origin); + } + + /** + * 源数据(未使用图床) + * @return void + */ + public function sourceJson() + { + echo json_encode(Storage::getJson('source') ?: []); + } + + /** + * 处理后的数据(已使用图床) + * @return void + */ + public function pixivJson() + { + echo json_encode(Storage::getJson('pixiv') ?: []); + } +} \ No newline at end of file diff --git a/app/Controllers/IndexController.php b/app/Controllers/IndexController.php index 5d5b215..24ab713 100644 --- a/app/Controllers/IndexController.php +++ b/app/Controllers/IndexController.php @@ -5,8 +5,8 @@ use app\Libs\Config; use app\Libs\Lock; use app\Libs\Pixiv; +use app\Libs\Request; use app\Libs\Storage; -use app\Libs\Tools; class IndexController extends Controller { @@ -15,7 +15,7 @@ public function index() $pixivJson = Storage::getJson('pixiv'); if ($pixivJson === false || !Pixiv::checkDate($pixivJson)) { if (Lock::create('refresh', 600) && Config::$disable_web_job === false) { - Tools::runRefreshThread(); + Request::execRefreshThread(); } } diff --git a/app/ImageHosting/Alibaba.php b/app/ImageHosting/Alibaba.php deleted file mode 100644 index d808492..0000000 --- a/app/ImageHosting/Alibaba.php +++ /dev/null @@ -1,39 +0,0 @@ - Curl::getCurlFile($path), - 'scene' => 'aeMessageCenterImageRule', - 'name' => 'image.jpg' - ]; - $result = Curl::post('https://kfupload.alibaba.com/mupload', $data, [ - CURLOPT_USERAGENT => 'iAliexpress/8.25.0 (iPhone; iOS 14.5; Scale/3.00)', - ]); - - Tools::log('[阿里巴巴图床]上传:' . json_encode($data)); - Tools::log('[阿里巴巴图床]返回:' . $result); - - $json = json_decode($result, true); - if(isset($json['code']) && $json['code'] == 0 && isset($json['url'])){ - return $json['url']; - } - - Tools::log('[阿里巴巴图床]上传失败', 'ERROR'); - return false; - } -} \ No newline at end of file diff --git a/app/ImageHosting/Baidu.php b/app/ImageHosting/Baidu.php index a325925..c013a8b 100644 --- a/app/ImageHosting/Baidu.php +++ b/app/ImageHosting/Baidu.php @@ -3,7 +3,7 @@ namespace app\ImageHosting; use app\Libs\Curl; -use app\Libs\Tools; +use app\Libs\Log; /** * 百度图床 @@ -31,15 +31,15 @@ public function upload($path) ], ]); - Tools::log('[百度图床]上传:' . json_encode($data)); - Tools::log('[百度图床]返回:' . $result); + Log::write('[百度图床]上传:' . json_encode($data)); + Log::write('[百度图床]返回:' . $result); $json = json_decode($result, true); if(isset($json['errno']) && $json['errno'] == 0 && isset($json['ret']['org_url'])){ return $json['ret']['org_url']; } - Tools::log('[百度图床]上传失败', 'ERROR'); + Log::write('[百度图床]上传失败', 'ERROR'); return false; } } \ No newline at end of file diff --git a/app/ImageHosting/Catbox.php b/app/ImageHosting/Catbox.php new file mode 100644 index 0000000..04d3cc9 --- /dev/null +++ b/app/ImageHosting/Catbox.php @@ -0,0 +1,35 @@ + 'fileupload', + 'userhash' => '', + 'fileToUpload' => Curl::getCurlFile($path), + ]; + $result = Curl::post('https://catbox.moe/user/api.php', $data); + + Log::write('[猫盒图床]上传:' . json_encode($data)); + Log::write('[猫盒图床]返回:' . $result); + + if (stripos($result, 'files.catbox.moe') !== false) { + return $result; + } + + Log::write('[猫盒图床]上传失败', 'ERROR'); + return false; + } +} \ No newline at end of file diff --git a/app/ImageHosting/Chkaja.php b/app/ImageHosting/Chkaja.php new file mode 100644 index 0000000..4dc061a --- /dev/null +++ b/app/ImageHosting/Chkaja.php @@ -0,0 +1,41 @@ + Curl::getCurlFile($path), + 'viewpassword' => '', + 'viewtips' => '', + 'Timelimit' => '', + 'Countlimit' => '', + 'submit' => 'submit', + 'MAX_FILE_SIZE' => '8388608', + ]; + $result = Curl::post('https://img.chkaja.com/ajaximg.php', $data); + + Log::write('[愛上傳图床]上传:' . json_encode($data)); + Log::write('[愛上傳图床]返回:' . $result); + + $url = preg_match('|https://img.chkaja.com/(.*?)\.jpg|', $result, $matches) ? $matches[0] : false; + + if (!empty($url)) { + return $url; + } + + Log::write('[愛上傳图床]上传失败', 'ERROR'); + return false; + } +} \ No newline at end of file diff --git a/app/ImageHosting/ImageHosting.php b/app/ImageHosting/ImageHosting.php index d36d16b..7502238 100644 --- a/app/ImageHosting/ImageHosting.php +++ b/app/ImageHosting/ImageHosting.php @@ -3,6 +3,7 @@ namespace app\ImageHosting; use app\Factory; +use app\Libs\Str; /** * 抽象 图床类 @@ -18,7 +19,7 @@ abstract class ImageHosting extends Factory */ public static function make($name, array $config = []) { - $name = '\\app\\ImageHosting\\' . ucfirst(strtolower($name)); + $name = '\\app\\ImageHosting\\' . Str::studly($name); return parent::make($name, $config); } diff --git a/app/ImageHosting/Imgstop.php b/app/ImageHosting/Imgstop.php new file mode 100644 index 0000000..4743ed8 --- /dev/null +++ b/app/ImageHosting/Imgstop.php @@ -0,0 +1,51 @@ + Curl::getCurlFile($path), + // 7=携程 15=易车,随机选一个 + 'strategy_id' => [7, 15][mt_rand(0, 1)], + ]; + $opt = [ + CURLOPT_HTTPHEADER => [ + 'x-csrf-token: ' . $csrfToken, + ], + CURLOPT_COOKIE => $result['cookie'], + ]; + $result = Curl::post('https://imgs.top/upload', $data, $opt); + + Log::write('[映画の图床]上传:' . json_encode(compact('data', 'opt'))); + Log::write('[映画の图床]返回:' . $result); + + $json = json_decode($result, true); + if (!empty($json['data']['imgurl'])) { + return $json['data']['imgurl']; + } + + Log::write('[映画の图床]上传失败', 'ERROR'); + return false; + } +} \ No newline at end of file diff --git a/app/ImageHosting/Imgtg.php b/app/ImageHosting/Imgtg.php new file mode 100644 index 0000000..ecbf46b --- /dev/null +++ b/app/ImageHosting/Imgtg.php @@ -0,0 +1,54 @@ + Curl::getCurlFile($path), + 'type' => 'file', + 'action' => 'upload', + 'timestamp' => substr(microtime(true) * 1000, 0, 13), + 'auth_token' => $authToken, + 'nsfw' => '0', + ]; + $result = Curl::post('https://imgtg.com/json', $data, [ + CURLOPT_HTTPHEADER => [ + 'accept: application/json', + 'referer: https://imgtg.com', + 'origin: https://imgtg.com', + ], + CURLOPT_COOKIE => $result['cookie'], + ]); + + Log::write('[Imgtg 图床]上传:' . json_encode($data)); + Log::write('[Imgtg 图床]返回:' . $result); + + $json = json_decode($result, true); + if(!empty($json['image']['image']['url'])){ + return $json['image']['image']['url']; + } + + Log::write('[Imgtg 图床]上传失败', 'ERROR'); + return false; + } +} \ No newline at end of file diff --git a/app/ImageHosting/Imgurl.php b/app/ImageHosting/Imgurl.php new file mode 100644 index 0000000..8ee2cd9 --- /dev/null +++ b/app/ImageHosting/Imgurl.php @@ -0,0 +1,34 @@ + Curl::getCurlFile($path), + ]; + $result = Curl::post('https://www.imgurl.org/upload/aws_s3', $data); + + Log::write('[Imgurl 图床]上传:' . json_encode($data)); + Log::write('[Imgurl 图床]返回:' . $result); + + $json = json_decode($result, true); + if (!empty($json['url'])) { + return $json['url']; + } + + Log::write('[Imgurl 图床]上传失败', 'ERROR'); + return false; + } +} \ No newline at end of file diff --git a/app/ImageHosting/Local.php b/app/ImageHosting/Local.php index 1390d0a..1f8a2db 100644 --- a/app/ImageHosting/Local.php +++ b/app/ImageHosting/Local.php @@ -3,7 +3,7 @@ namespace app\ImageHosting; use app\Libs\Config; -use app\Libs\Tools; +use app\Libs\Log; /** * 本地存储 @@ -17,13 +17,14 @@ public function upload($path) { $fileName = pathinfo($path, PATHINFO_BASENAME); $file = STORAGE_PATH . 'images/' . $fileName; - Tools::log('[本地]目标:' . $path); - Tools::log('[本地]存储到:' . $file); + Log::write('[本地]目标:' . $path); + Log::write('[本地]存储到:' . $file); - if (file_put_contents($file, file_get_contents($path)) === false) { - Tools::log('[本地]存储失败', 'ERROR'); + if (!file_put_contents($file, file_get_contents($path))) { + Log::write('[本地]存储失败', 'ERROR'); return false; } + return Config::$url . 'storage/images/' . $fileName; } } \ No newline at end of file diff --git a/app/ImageHosting/Pngcm.php b/app/ImageHosting/Pngcm.php new file mode 100644 index 0000000..f97960a --- /dev/null +++ b/app/ImageHosting/Pngcm.php @@ -0,0 +1,37 @@ + Curl::getCurlFile($path), + 'name' => pathinfo($path, PATHINFO_BASENAME), + 'uuid' => 'o_1g840' . Str::random(21), + ]; + $result = Curl::post('https://png.cm/application/upload.php', $data); + + Log::write('[Pngcm 图床]上传:' . json_encode($data)); + Log::write('[Pngcm 图床]返回:' . $result); + + $json = json_decode($result, true); + if (isset($json['code']) && $json['code'] == 200 && !empty($json['url'])) { + return $json['url']; + } + + Log::write('[Pngcm 图床]上传失败', 'ERROR'); + return false; + } +} \ No newline at end of file diff --git a/app/ImageHosting/Saoren.php b/app/ImageHosting/Saoren.php new file mode 100644 index 0000000..f647a6c --- /dev/null +++ b/app/ImageHosting/Saoren.php @@ -0,0 +1,37 @@ + Curl::getCurlFile($path), + 'name' => pathinfo($path, PATHINFO_BASENAME), + 'uuid' => 'o_1g840' . Str::random(21), + ]; + $result = Curl::post('https://tu.sao.ren/file.php', $data); + + Log::write('[骚人图床]上传:' . json_encode($data)); + Log::write('[骚人图床]返回:' . $result); + + $json = json_decode($result, true); + if (isset($json['code']) && $json['code'] == 200 && !empty($json['url'])) { + return $json['url']; + } + + Log::write('[骚人图床]上传失败', 'ERROR'); + return false; + } +} \ No newline at end of file diff --git a/app/ImageHosting/Smms.php b/app/ImageHosting/Smms.php index bcb0a4c..a1fddeb 100644 --- a/app/ImageHosting/Smms.php +++ b/app/ImageHosting/Smms.php @@ -4,7 +4,7 @@ use app\Libs\Config; use app\Libs\Curl; -use app\Libs\Tools; +use app\Libs\Log; /** * sm.ms图床 @@ -18,7 +18,7 @@ class Smms extends ImageHosting public function upload($path) { if (empty(Config::$image_hosting_extend['smms']['token'])) { - Tools::log('[Smms图床]上传失败:请先配置 smms 的 token', 'ERROR'); + Log::write('[Smms图床]上传失败:请先配置 smms 的 token', 'ERROR'); return false; } @@ -30,8 +30,8 @@ public function upload($path) ]; $result = Curl::post('https://sm.ms/api/v2/upload', $data, $header); - Tools::log('[Smms图床]上传:' . json_encode($data)); - Tools::log('[Smms图床]返回:' . $result); + Log::write('[Smms图床]上传:' . json_encode($data)); + Log::write('[Smms图床]返回:' . $result); $result = json_decode($result, true); if (isset($result['code'])) { @@ -42,7 +42,7 @@ public function upload($path) } } - Tools::log('[Smms图床]上传失败:' . $result['msg'], 'ERROR'); + Log::write('[Smms图床]上传失败:' . $result['msg'], 'ERROR'); return false; } } \ No newline at end of file diff --git a/app/ImageHosting/Tietuku.php b/app/ImageHosting/Tietuku.php index b21994a..8bdf159 100644 --- a/app/ImageHosting/Tietuku.php +++ b/app/ImageHosting/Tietuku.php @@ -4,7 +4,7 @@ use app\Libs\Config; use app\Libs\Curl; -use app\Libs\Tools; +use app\Libs\Log; /** * tietuku图床 @@ -18,7 +18,7 @@ class Tietuku extends ImageHosting public function upload($path) { if (empty(Config::$image_hosting_extend['tietuku']['token'])) { - Tools::log('[Tietuku图床]上传失败:请先配置 tietuku 的 token', 'ERROR'); + Log::write('[Tietuku图床]上传失败:请先配置 tietuku 的 token', 'ERROR'); return false; } @@ -28,12 +28,12 @@ public function upload($path) ]; $result = Curl::post('http://up.imgapi.com/', $data); - Tools::log('[Tietuku图床]上传:' . json_encode($data)); - Tools::log('[Tietuku图床]返回:' . $result); + Log::write('[Tietuku图床]上传:' . json_encode($data)); + Log::write('[Tietuku图床]返回:' . $result); $result = json_decode($result, true); if (isset($result['code'])) { - Tools::log('[Tietuku图床]上传失败:' . $result['info'], 'ERROR'); + Log::write('[Tietuku图床]上传失败:' . $result['info'], 'ERROR'); return false; } return $result['linkurl']; diff --git a/app/ImageHosting/Tsesze.php b/app/ImageHosting/Tsesze.php new file mode 100644 index 0000000..94b6574 --- /dev/null +++ b/app/ImageHosting/Tsesze.php @@ -0,0 +1,34 @@ + Curl::getCurlFile($path), + ]; + $result = Curl::post('https://www.268608.com/upload/localhost', $data); + + Log::write('[Tsesze 图床]上传:' . json_encode($data)); + Log::write('[Tsesze 图床]返回:' . $result); + + $json = json_decode($result, true); + if (!empty($json['url'])) { + return $json['url']; + } + + Log::write('[Tsesze 图床]上传失败', 'ERROR'); + return false; + } +} \ No newline at end of file diff --git a/app/Jobs/ClearLog.php b/app/Jobs/ClearLog.php new file mode 100644 index 0000000..3affee4 --- /dev/null +++ b/app/Jobs/ClearLog.php @@ -0,0 +1,42 @@ + 0 && is_numeric($opt['n'])) ? intval($opt['n']) : 7; + + $time = strtotime("-{$n} days"); + $path = STORAGE_PATH . 'logs/'; + $deleteNum = 0; + + if ($dh = opendir($path)) { + while (($file = readdir($dh)) !== false) { + if (in_array($file, ['.', '..', '.gitignore'])) { + continue; + } + + $file = $path . $file; + if (filemtime($file) < $time) { + $deleteNum++; + Storage::deleteFile($file); + } + } + } + + Log::write("共计清除日志文件 {$deleteNum} 个"); + + return true; + } +} \ No newline at end of file diff --git a/app/Jobs/Job.php b/app/Jobs/Job.php index 9cf94e0..24b7c7c 100644 --- a/app/Jobs/Job.php +++ b/app/Jobs/Job.php @@ -3,6 +3,7 @@ namespace app\Jobs; use app\Factory; +use app\Libs\Str; /** * 抽象 任务类 @@ -11,6 +12,9 @@ */ abstract class Job extends Factory { + // 是否只能通过 cli 触发 + public $onlyActivateByCli = false; + /** * @param string $name * @param array $config @@ -18,7 +22,7 @@ abstract class Job extends Factory */ public static function make($name, array $config = []) { - $name = '\\app\\Jobs\\' . ucfirst(strtolower($name)); + $name = '\\app\\Jobs\\' . Str::studly($name); return parent::make($name, $config); } diff --git a/app/Jobs/Refresh.php b/app/Jobs/Refresh.php index e6e54cb..ee38585 100644 --- a/app/Jobs/Refresh.php +++ b/app/Jobs/Refresh.php @@ -1,147 +1,160 @@ -needRefresh($ranking, $pixivJson)){ - Tools::log('排行榜尚未更新,半小时后再试'); - Lock::forceCreate('refresh', 1800); - return true; - } - - $images = Pixiv::getImages(); - if($images === false) { - throw new \Exception('【致命错误】无法获取Pixiv排行榜图片列表'); - } - - $pixivJson = [ - 'image' => [], - 'url' => [], - ]; - - $enableCompress = Config::$compress && function_exists('imagecreatefromjpeg'); - - $imageHostingInstances = []; - foreach (Config::$image_hosting as $ihName) { - $imageHostingInstances[] = ImageHosting::make($ihName); - } - - $proxy = Config::$proxy; - - // 开始获取图片 - foreach ($images['image'] as $i => $imageUrl) { - // 缓存数量限制 - if ($i >= Config::$limit) { - break; - } - // 最多尝试下载3次 - Config::$proxy = $proxy; - for ($ii = 1; $ii <= 3; $ii++) { - $tmpfile = Pixiv::downloadImage($imageUrl); - if ($tmpfile) { - break; - } else { - Tools::log("图片 {$imageUrl} 下载失败,重试第 {$ii} 次"); - sleep(3); - } - } - if (!$tmpfile) { - throw new \Exception("图片 {$imageUrl} 下载失败"); - } - // 压缩图片 - if ($enableCompress) { - $image = imagecreatefromjpeg($tmpfile); - if ($image) { - imagejpeg($image, $tmpfile, 95); - imagedestroy($image); - unset($image); - } - } - // 上传到图床 - Config::$proxy = null; - foreach ($imageHostingInstances as $imageHosting) { - $url = $imageHosting->upload($tmpfile); - if ($url != false) { - Storage::deleteFile($tmpfile); - break; - } - } - - $pixivJson['image'][] = $url ?: $images['image'][$i]; // 如上传失败则使用原图url(虽然原图url也显示不出来) - $pixivJson['url'][] = $images['url'][$i]; - } - - $pixivJson['date'] = $images['date']; - Storage::saveJson('pixiv', $pixivJson); - Lock::remove('refresh'); - - Config::$clear_overdue && Storage::clearOverdueImages(); - return true; - - } catch (\Exception $e) { - - // 是否超过最大重试次数 - $refreshCount = (int)Storage::get('refreshCount'); - if ($refreshCount > 10) { - // 超过10次(5小时)都无法获取到pixiv排行榜 - // 直接锁定一整天,明天再试,降低无意义的资源损耗 - $expire = mktime(23, 59, 59) - time(); - Lock::forceCreate('refresh', $expire); - Storage::remove('refreshCount'); - } else { - // 半小时后再试 - Lock::forceCreate('refresh', 1800); - Storage::save('refreshCount', $refreshCount + 1); - } - - $this->errorMsg = $e->getMessage(); - return false; - } - } - - /** - * 是否需要刷新数据 - * @param array $ranking pixiv 接口返回的排行榜数据 - * @param array $pixivJson pixiv.json 的内容 - * @return bool - * @throws \Exception - */ - private function needRefresh($ranking, $pixivJson) - { - if (!isset($pixivJson['date'])) { - return true; - } - - // $ranking['date'] 的格式为 20200310 - if ($ranking && isset($ranking['date']) && preg_match('|^\d{8}$|', $ranking['date'])) { - - return $pixivJson['date'] != date('Y-m-d', strtotime($ranking['date'])); - } - - throw new \Exception('排行榜日期数据异常!数据:' . json_encode($ranking)); - } +needRefresh($ranking, $pixivJson)){ + Log::write('排行榜尚未更新,半小时后再试'); + Lock::forceCreate('refresh', 1800); + + return true; + } + + $images = Pixiv::getImages(); + if($images === false) { + throw new \Exception('【致命错误】无法获取Pixiv排行榜图片列表'); + } + + $enableCompress = Config::$compress && function_exists('imagecreatefromjpeg'); + + $imageHostingInstances = []; + foreach (Config::$image_hosting as $ihName) { + $imageHostingInstances[] = ImageHosting::make($ihName); + } + + $proxy = Config::$proxy; + + // 开始获取图片 + $pixivJson = []; + foreach ($images['data'] as $i => $data) { + // 缓存数量限制 + if ($i >= Config::$limit) { + break; + } + + Log::write("开始获取第 " . ($i + 1) . " 张图:{$data['url']}"); + + // 最多尝试下载 3 次 + Config::$proxy = $proxy; + for ($ii = 1; $ii <= 3; $ii++) { + $tmpfile = Pixiv::downloadImage($data['url']); + if ($tmpfile && getimagesize($tmpfile)) { + break; + } else { + Log::write("图片 {$data['url']} 下载失败,重试第 {$ii} 次"); + sleep(mt_rand(3, 30)); + } + } + if (!$tmpfile) { + throw new \Exception("图片 {$data['url']} 下载失败"); + } + + // 压缩图片 + if ($enableCompress) { + $image = imagecreatefromjpeg($tmpfile); + if ($image) { + imagejpeg($image, $tmpfile, 95); + $bytes = filesize($tmpfile); + Log::write('压缩后图片大小: ' . $bytes . ' 字节'); + imagedestroy($image); + unset($image); + } + + if ($bytes < 1000) { + throw new \Exception("图片 {$data['url']} 下载失败"); + } + } + + // 上传到图床 + Config::$proxy = null; // 上传过程中禁用代理 + foreach ($imageHostingInstances as $imageHosting) { + $url = $imageHosting->upload($tmpfile); + if ($url != false) { + Storage::deleteFile($tmpfile); + break; + } + } + + $url = $url ?: $data['url']; // 如上传失败则使用原图url(虽然原图url也显示不出来) + $data['url'] = $url; + + $pixivJson['data'][] = $data; + $pixivJson['image'][] = $url; + $pixivJson['url'][] = "artworks/{$data['id']}"; + } + + $pixivJson['date'] = $images['date']; + Storage::saveJson('pixiv', $pixivJson); + Lock::remove('refresh'); + + Config::$clear_overdue && Storage::clearOverdueImages(); + + return true; + + } catch (\Exception $e) { + + // 是否超过最大重试次数 + $refreshCount = (int)Storage::get('refreshCount'); + if ($refreshCount > 10) { + // 超过 10 次(5小时)都无法获取到 pixiv 排行榜 + // 直接锁定一整天,明天再试,降低无意义的资源损耗 + $expire = mktime(23, 59, 59) - time(); + Lock::forceCreate('refresh', $expire); + Storage::remove('refreshCount'); + } else { + // 半小时后再试 + Lock::forceCreate('refresh', 1800); + Storage::save('refreshCount', $refreshCount + 1); + } + + $this->errorMsg = $e->getMessage(); + return false; + } + } + + /** + * 是否需要刷新数据 + * @param array $ranking pixiv 接口返回的排行榜数据 + * @param array $pixivJson pixiv.json 的内容 + * @return bool + * @throws \Exception + */ + private function needRefresh($ranking, $pixivJson) + { + if (!isset($pixivJson['date'])) { + return true; + } + + // $ranking['date'] 的格式为 20200310 + if ($ranking && isset($ranking['date']) && preg_match('|^\d{8}$|', $ranking['date'])) { + + return $pixivJson['date'] != date('Y-m-d', strtotime($ranking['date'])); + } + + throw new \Exception('排行榜日期数据异常!数据:' . json_encode($ranking)); + } } \ No newline at end of file diff --git a/app/Libs/Config.php b/app/Libs/Config.php index 519bc07..0fcae11 100644 --- a/app/Libs/Config.php +++ b/app/Libs/Config.php @@ -35,7 +35,7 @@ public static function init() // 获取本项目url if (self::$url == '' && !IS_CLI) { - self::$url = Tools::getCurrentURL(); + self::$url = Request::getCurrentUrl(); } // 是否对外提供服务,是则获取url参数 @@ -48,23 +48,25 @@ public static function init() } } - Config::$image_hosting = (array)Config::$image_hosting; - try { if (!is_writable(STORAGE_PATH)) { throw new \Exception(STORAGE_PATH . ' 目录无法写入'); } + if (!is_array(Config::$image_hosting) || count(Config::$image_hosting) < 1) { + throw new \Exception('image_hosting 配置项至少要有一个值'); + } + if (self::$limit < 1) { throw new \Exception('limit 配置项不得小于1'); } if (IS_CLI && self::$url == '' && in_array('local', self::$image_hosting)) { - throw new \Exception('在cli模式下使用local本地图床时,必须配置url项,否则可能会生成错误的缩略图url'); + throw new \Exception('在 cli 模式下使用 local 本地图床时,必须配置 url 项,否则可能会生成错误的缩略图 url'); } } catch (\Exception $e) { - Tools::log($e->getMessage(), 'ERROR'); + Log::write($e->getMessage(), Log::LEVEL_ERROR); echo '错误:' . $e->getMessage(); die; diff --git a/app/Libs/Curl.php b/app/Libs/Curl.php index dea2127..49db94b 100644 --- a/app/Libs/Curl.php +++ b/app/Libs/Curl.php @@ -13,9 +13,10 @@ class Curl * get请求 * @param string $url 请求目标 * @param array $opt curl参数 - * @return bool|string + * @param bool $includeCookie 同时返回 cookie + * @return bool|string|array $includeCookie 为 true 时返回 ['cookie' => '', 'html' => ''] 数组 */ - public static function get($url, $opt = []) + public static function get($url, $opt = [], $includeCookie = false) { $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); @@ -23,10 +24,10 @@ public static function get($url, $opt = []) curl_setopt($ch, CURLOPT_TIMEOUT, 30); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); - curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36 Edg/91.0.864.48'); - if (Config::$proxy) { - curl_setopt($ch, CURLOPT_PROXY, Config::$proxy); - } + curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36 Edg/103.0.1264.49'); + + Config::$proxy && curl_setopt($ch, CURLOPT_PROXY, Config::$proxy); + $includeCookie && curl_setopt($ch, CURLOPT_HEADER, true); if (count($opt)) { curl_setopt_array($ch, $opt); @@ -34,14 +35,35 @@ public static function get($url, $opt = []) $data = curl_exec($ch); curl_close($ch); + + if ($includeCookie) { + $data = explode("\r\n\r\n", $data); + + $header = array_shift($data); + $html = implode("\r\n\r\n", $data); + + preg_match_all("/set\-cookie:([^\r\n]*)/i", $header, $matches); + + $cookie = ''; + if (!empty($matches[1])) { + $cookieItem = []; + foreach ($matches[1] as $item) { + $cookieItem[] = explode(';', trim($item))[0]; + } + $cookie = implode('; ', $cookieItem); + } + + return compact('cookie', 'html'); + } + return $data; } /** * post请求 - * @param string $url - * @param array $postData - * @param array $opt + * @param string $url + * @param array|string $postData + * @param array $opt * @return bool|string */ public static function post($url, $postData, $opt = []) diff --git a/app/Libs/Log.php b/app/Libs/Log.php new file mode 100644 index 0000000..0dd586e --- /dev/null +++ b/app/Libs/Log.php @@ -0,0 +1,36 @@ + {$message}\n"; + if (IS_CLI) { + echo $content . "\n"; + } + + $file = STORAGE_PATH . 'logs/' . date('Ymd') . (IS_CLI ? '-cli' : '') . '.log'; + + return file_put_contents($file, $content, FILE_APPEND) !== false; + } +} \ No newline at end of file diff --git a/app/Libs/Pixiv.php b/app/Libs/Pixiv.php index 987e5e0..b9c12bf 100644 --- a/app/Libs/Pixiv.php +++ b/app/Libs/Pixiv.php @@ -1,118 +1,137 @@ - [ - 'Referer: https://www.pixiv.net/ranking.php?mode=daily', - ], - ]); - $json = json_decode($response, true); - if (!isset($json['contents'])) { - Tools::log('获取排行榜数据失败!接口返回值:' . $response, Tools::LOG_LEVEL_ERROR); - return false; - } - - return $json; - } - - /** - * 获取图片url列表 - * @return array|false - * @throws \Exception - */ - public static function getImages() - { - $source = Storage::getJson('source'); - if (is_array($source) && self::checkDate($source)) { - return $source; - } - - // 兼容旧格式 - $source = [ - 'image' => [], - 'url' => [], - ]; - - $picNum = 0; - for ($page = 1; $page <= 10; $page++) { - - $json = self::getRanking($page); - if($json === false){ - return false; - } - - foreach ($json['contents'] as $item) { - $source['image'][] = $item['url']; - $source['url'][] = "artworks/{$item['illust_id']}"; - $picNum++; - - if ($picNum >= Config::$limit) { - break 2; - } - } - } - - $source['date'] = date('Y-m-d', strtotime($json['date'])); - Storage::saveJson('source', $source); - - return $source; - } - - /** - * 下载Pixiv缩略图。成功返回临时文件名 - * @param string $url - * @return string 临时文件名 - */ - public static function downloadImage($url) - { - $fileName = pathinfo($url, PATHINFO_BASENAME); - // 如果 storage 里存了有,就不再重新下载了 - $image = Storage::getImage($fileName); - if ($image == false) { - $image = Curl::get($url, [ - CURLOPT_HTTPHEADER => [ - 'Referer: https://www.pixiv.net/ranking.php?mode=daily', - ], - ]); - } - - if ($image) { - $file = explode('/', $url); - $file = array_pop($file); - $file = sys_get_temp_dir() . '/' . $file; - return file_put_contents($file, $image) !== false ? $file : false; - } - return false; - } - - /** - * 检查传入数组的 date 值是否有效(即大于等于昨天)。返回 true 为未过期 - * @param array $data - * @return bool - */ - public static function checkDate(array $data) - { - if(isset($data['date'])){ - $yesterday = date('Y-m-d', strtotime('-1 day')); - return $data['date'] >= $yesterday; - } - - return false; - } + [ + 'Referer: https://www.pixiv.net/ranking.php?mode=daily', + ], + ]); + $json = json_decode($response, true); + if (!isset($json['contents'])) { + Log::write('获取排行榜数据失败!接口返回值:' . $response, Log::LEVEL_ERROR); + return false; + } + + return $json; + } + + /** + * 获取图片url列表 + * @return array|false + * @throws \Exception + */ + public static function getImages() + { + $source = Storage::getJson('source'); + if (is_array($source) && self::checkDate($source)) { + return $source; + } + + $picNum = 0; + $sourceJson = []; + for ($page = 1; $page <= 10; $page++) { + + $json = self::getRanking($page); + if($json === false){ + return false; + } + + foreach ($json['contents'] as $item) { + $sourceJson['data'][] = [ + 'id' => $item['illust_id'], + 'url' => $item['url'], + 'title' => $item['title'], + 'tags' => $item['tags'], + 'width' => $item['width'], + 'height' => $item['height'], + 'page_count' => $item['illust_page_count'], + 'rank' => $item['rank'], + 'yesterday_rank' => $item['yes_rank'], + 'user_id' => $item['user_id'], + 'user_name' => $item['user_name'], + 'uploaded_at' => $item['illust_upload_timestamp'], + ]; + // image 和 url 是为了兼容 5.x 之前的旧版本 + $sourceJson['image'][] = $item['url']; + $sourceJson['url'][] = "artworks/{$item['illust_id']}"; + $picNum++; + + if ($picNum >= Config::$limit) { + break 2; + } + } + } + + $sourceJson['date'] = date('Y-m-d', strtotime($json['date'])); + Storage::saveJson('source', $sourceJson); + + return $sourceJson; + } + + /** + * 下载 Pixiv 缩略图。成功返回临时文件名 + * @param string $url + * @return string 临时文件名 + */ + public static function downloadImage($url) + { + $fileName = pathinfo($url, PATHINFO_BASENAME); + // 如果 storage 里存了有,就不再重新下载了 + $image = Storage::getImage($fileName); + if ($image === false) { + $image = Curl::get($url, [ + CURLOPT_HTTPHEADER => [ + 'Referer: https://www.pixiv.net/ranking.php?mode=daily', + ], + ]); + + Log::write('下载到数据包大小: ' . strlen($image) . ' 字节'); + } + + if ($image) { + $file = explode('/', $url); + $file = array_pop($file); + $file = sys_get_temp_dir() . '/' . $file; + + $bytes = file_put_contents($file, $image); + Log::write("写入文件 {$file} 大小:{$bytes} 字节"); + + return $bytes > 0 ? $file : false; + } + + return false; + } + + /** + * 检查传入数组的 date 值是否有效(即大于等于昨天)。返回 true 为未过期 + * @param array $data + * @return bool + */ + public static function checkDate(array $data) + { + if(isset($data['date'])){ + $yesterday = date('Y-m-d', strtotime('-1 day')); + + return $data['date'] >= $yesterday; + } + + return false; + } } \ No newline at end of file diff --git a/app/Libs/Tools.php b/app/Libs/Request.php similarity index 52% rename from app/Libs/Tools.php rename to app/Libs/Request.php index 333f37b..45130d9 100644 --- a/app/Libs/Tools.php +++ b/app/Libs/Request.php @@ -3,44 +3,38 @@ namespace app\Libs; /** - * 工具类 - * Class Tools - * @package app\Libs + * 请求类 */ -class Tools +class Request { - const LOG_LEVEL_ERROR = 'ERROR'; - /** - * 写日志 - * @param string|array $message - * @param string $level - * @return bool + * 获取当前请求的协议 + * @return string */ - public static function log($message, $level = 'DEBUG') + public static function getScheme() { - $level = strtoupper($level); - if (is_array(Config::$log_level) && in_array($level, Config::$log_level)) { - $file = STORAGE_PATH . 'logs/' . date('Ymd') . (IS_CLI ? '-cli' : '') . '.log'; - $message = is_array($message) ? json_encode($message) : $message; - $content = "[{$level}] " . date('Y-m-d H:i:s') . " --> {$message}\n"; - if (IS_CLI) { - echo $content . "\n"; - } + if (isset($_SERVER['REQUEST_SCHEME'])) { + return $_SERVER['REQUEST_SCHEME']; + } - return file_put_contents($file, $content, FILE_APPEND) !== false; + if ( + (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off') || + (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') || + (!empty($_SERVER['HTTP_FRONT_END_HTTPS']) && strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) !== 'off') + ) { + return 'https'; } - return true; + return 'http'; } /** - * 获取当前 url (不含 query 参数及文件名) + * 获取当前 url (以 / 结尾。不含 query 参数及文件名) * @return string */ - public static function getCurrentURL() + public static function getCurrentUrl() { - $url = self::getRequestScheme() . '://' . $_SERVER['HTTP_HOST']; + $url = self::getScheme() . '://' . $_SERVER['HTTP_HOST']; if (!in_array($_SERVER['SERVER_PORT'], ['80', '443'])) { $url .= ':' . $_SERVER['SERVER_PORT']; } @@ -59,31 +53,15 @@ public static function getCurrentURL() /** * 执行刷新线程 */ - public static function runRefreshThread() + public static function execRefreshThread() { + if (Config::$disable_web_job) { + return; + } + + Config::$proxy = null; Curl::get(Config::$url . 'index.php?job=refresh', [ CURLOPT_TIMEOUT => 1, ]); } - - /** - * 获取当前请求的协议 - * @return string - */ - public static function getRequestScheme() - { - if (isset($_SERVER['REQUEST_SCHEME'])) { - return $_SERVER['REQUEST_SCHEME']; - } - - if ( - (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off') || - (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') || - (!empty($_SERVER['HTTP_FRONT_END_HTTPS']) && strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) !== 'off') - ) { - return 'https'; - } - - return 'http'; - } } \ No newline at end of file diff --git a/app/Libs/Storage.php b/app/Libs/Storage.php index 0cc7099..ae592b2 100644 --- a/app/Libs/Storage.php +++ b/app/Libs/Storage.php @@ -34,7 +34,7 @@ public static function get($file) } $content = @file_get_contents($file); if ($content === false) { - Tools::log("读取 {$file} 文件失败"); + Log::write("读取 {$file} 文件失败"); return false; } @@ -52,7 +52,7 @@ public static function remove($file) } /** - * 清除过期地图片 + * 清除过期的图片 */ public static function clearOverdueImages() { @@ -70,11 +70,11 @@ public static function clearOverdueImages() } } } - Tools::log("共计清除过期图片 {$deleteNum} 张"); + Log::write("共计清除过期图片 {$deleteNum} 张"); } /** - * 获取图片内容。文件不存在或无效时返回false + * 获取图片内容。文件不存在或无效时返回 false * @param string $name * @return mixed|false */ diff --git a/app/Libs/Str.php b/app/Libs/Str.php new file mode 100644 index 0000000..41eb7fc --- /dev/null +++ b/app/Libs/Str.php @@ -0,0 +1,42 @@ + - + @@ -12,48 +12,138 @@ Pixiv 每日排行榜 Top<?=Config::$limit?> 小挂件 - + + + -