diff --git a/.dockerignore b/.dockerignore
index 7348d97..6620795 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -5,7 +5,6 @@ logs/*
YYeTsFE/node_modules/*
.github/*
assets/*
-scripts/*
conf/*
tests/*
yyetsweb/yyets.sqlite
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
index baa0ca1..fd8df4a 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -6,14 +6,13 @@ tools/worker/public/js/search.js -linguist-vendored
tools/worker/public/404.html linguist-vendored
tools/worker/public/resource.html linguist-vendored
-yyetsweb/css/** linguist-vendored
-yyetsweb/fonts/* linguist-vendored
-yyetsweb/img/* linguist-vendored
-yyetsweb/js/* linguist-vendored
-yyetsweb/404.html linguist-vendored
-yyetsweb/resource.html linguist-vendored
+yyetsweb/templates/css/** linguist-vendored
+yyetsweb/templates/fonts/* linguist-vendored
+yyetsweb/templates/img/* linguist-vendored
+yyetsweb/templates/js/* linguist-vendored
+yyetsweb/templates/404.html linguist-vendored
+yyetsweb/templates/resource.html linguist-vendored
-yyetsweb/js/common.js -linguist-vendored
+yyetsweb/templates/js/common.js -linguist-vendored
tests/data/* linguist-vendored
-yyetsweb/craw_data/*.html linguist-vendored
\ No newline at end of file
diff --git a/.github/workflows/builder.yaml b/.github/workflows/builder.yaml
index 8c5bc7e..9669ace 100644
--- a/.github/workflows/builder.yaml
+++ b/.github/workflows/builder.yaml
@@ -42,7 +42,7 @@ jobs:
REACT_APP_DOMAIN: ${{ secrets.REACT_APP_DOMAIN }}
REACT_APP_GA: ${{ secrets.REACT_APP_GA }}
GENERATE_SOURCEMAP: ${{ secrets.GENERATE_SOURCEMAP }}
- run: bash hooks/pre_build
+ run: bash scripts/pre_build.sh
- name: Lower case
id: string
@@ -54,7 +54,7 @@ jobs:
uses: docker/build-push-action@v2
with:
context: .
- platforms: linux/amd64,linux/arm64
+ platforms: linux/amd64, linux/arm64
push: true
tags: ${{ steps.string.outputs.lowercase }}
cache-from: type=local,src=/tmp/.buildx-cache
diff --git a/.gitignore b/.gitignore
index 3b2a44c..164f82b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -127,3 +127,11 @@ logs/*
/yyetsweb/yyets.sqlite
/yyetsweb/yyetsweb
/yyetsweb/assets.go
+/yyetsweb/templates/static/*
+/yyetsweb/templates/sponsor/*
+/yyetsweb/templates/svg/*
+/yyetsweb/templates/index.css
+/yyetsweb/templates/logo*
+/yyetsweb/templates/*.json
+
+
diff --git a/API.md b/API.md
index 3229b28..445c33a 100644
--- a/API.md
+++ b/API.md
@@ -7,16 +7,30 @@
- [x] 联合搜索,当本地数据库搜索不到数据时,会返回extra字段
- [x] 最新评论
- [x] 公告
-- [ ] 评论通知(浏览器通知)
+- [x] 最新更新资源
+- [x] API变更:登录时需要验证码
+- [x] API变更:like API变更 PATCH `/api/user/` --> PATCH `/api/like/`
+- [x] 删除评论(admin only)
+- [ ] 添加下载地址到已有资源
+- [ ] 新增资源
+- [ ] 删除资源、删除已有资源的下载
+- [ ] 对评论的反应
+- [ ] 更改用户信息(添加邮箱)
+- [ ] 分类
+- [ ] 评论通知(浏览器通知,暂时隐藏了)
# BE
- [x] 联合搜索:字幕侠、new字幕组、追新番
- [x] grafana面板
- [x] 豆瓣接口
-- [ ] 用户体系(添加邮箱,邮件支持,找回密码)
-- [ ] 评论通知,需要新接口
-- [ ] 添加资源API
+- [x] 评论通知:站内通知
+- [x] 添加邮箱
+- [x] 邮件通知
+- [x] 添加下载地址到已有资源
+- [x] 删除资源
+- [x] 新建资源
+- [ ] 找回密码
# 资源
@@ -25,6 +39,11 @@
* GET `/api/resource?id=10004`
数据结构参考 [sample.json](yyetsweb/js/sample.json)
+**对于非官方、由用户提交的下载,与 `files` `dateline` 同级会有一个 `creator` 用于标明是谁创建的**
+**对于非官方、由用户提交的资源,与 `cnname` `enname` 同级会有一个 `creator` 用于标明是谁创建的**
+
+如果没有,那么就是官方资源
+
## 搜索
* GET `/api/resource?keyword=逃避`
@@ -115,6 +134,119 @@
}
```
+## 添加下载地址到已有资源
+
+比如更新S01E05
+
+* PATCH `http://127.0.0.1:8888/api/resource`
+
+```json
+{
+ "resource_id": 39894,
+ "season_num": "1,对于电影纪录片等,应该是0或者101",
+ "items": {
+ "MP4": [
+ {
+ "episode": "12",
+ "name": "第五集.mp4",
+ "size": "9.43GB",
+ "dateline": "1628399290 单位秒",
+ "files": [
+ {
+ "way": "1",
+ "way_cn": "电驴",
+ "address": "ed2k://|filszpwzec5|/",
+ "passwd": ""
+ },
+ {
+ "way": "2",
+ "way_cn": "磁力",
+ "address": "magnet:37",
+ "passwd": ""
+ }
+ ]
+ }
+ ]
+ },
+ "formats": [
+ "MP4"
+ ]
+}
+```
+
+返回201
+
+## 创建新资源
+
+仅登录用户可用,用于创建新的资源,不包括 `data.list`。
+
+* POST `http://127.0.0.1:8888/api/resource`
+
+```json
+{
+ "status": 1,
+ "info": "OK",
+ "data": {
+ "info": {
+ "id": "设置成任意值即可",
+ "cnname": "中文名",
+ "enname": "英文名",
+ "aliasname": "别名",
+ "channel": "movie/tv",
+ "channel_cn": "电影/美剧",
+ "area": "法国",
+ "show_type": "",
+ "expire": "1610401225",
+ "views": 0
+ },
+ "list": [
+ ]
+ }
+}
+```
+
+返回
+
+```json
+{
+ "status": true,
+ "message": "success",
+ "id": 50623
+}
+```
+
+## 删除资源
+
+仅管理员可用,分成两种情况,一种是删除某个id全部资源,如39894;另外一种是删除这个资源下的某集下载
+
+* POST `http://127.0.0.1:8888/api/resource`
+
+均返回 202
+
+### 删除全部
+
+```json
+{
+ "resource_id": 39894
+}
+```
+
+### 删除部分资源
+
+会尽可能的匹配并删除对应的行
+
+```json
+{
+ "resource_id": 39894,
+ "meta": {
+ "episode": "1",
+ "name": "超235678-213.mp4",
+ "size": "1.43GB",
+ "dateline": "1628399290"
+ }
+}
+```
+
# Top
获取top信息,每类返回15条访问量最高的数据
@@ -213,7 +345,11 @@
## 登录或注册
-* POST `/api/user`,提交json,字段 `username`, `password`
+* POST `/api/user`,提交json,字段 `username`, `password`,`captcha_id` 和 `captcha`
+
+当验证码失效时,会返回303 See Other
+
+返回json
## 获取当前登录用户信息
@@ -235,12 +371,56 @@
"group": [
"admin"
],
- "comments_like": [
- "60c46d6a6d7c5dd22d69fd3b"
- ],
- "comments_dislike": [
- "60c46d6a6d7c5dd22d69fd3b"
- ]
+ "email": {
+ "verified": false,
+ "address": "123@qq.com"
+ }
+}
+```
+
+## 更改用户信息
+
+* PATCH `http://127.0.0.1:8888/api/user`
+
+* 目前只支持修改email字段,会发送验证邮件,1800秒之内只能验证一次,有效期24小时
+
+暂不支持取消绑定
+
+```json
+{
+ "email": "123@qq.com"
+}
+```
+
+response
+
+```json
+{
+ "status_code": 429,
+ "status": false,
+ "message": "try again in 1797s"
+}
+```
+
+## 验证邮件
+
+* POST `http://127.0.0.1:8888/api/user/email`
+
+10次错误会被加到黑名单,账号注销
+
+```json
+{
+ "code": "83216"
+}
+```
+
+response
+
+```json
+{
+ "status": true,
+ "status_code": 201,
+ "message": "success"
}
```
@@ -251,7 +431,7 @@
# 添加或删除收藏
-* PATCH `/api/user`,提交json,字段 `resource_id`
+* PATCH `/api/like`,提交json,字段 `resource_id`
# 评论
@@ -547,28 +727,50 @@
}
```
-## 点赞或踩评论
+## 对评论的反应
-* PATCH `/api/comment`
+仅对登录用户可用
-verb 为`like` 或 `dislike`
+* POST `/api/comment/reaction` 添加反应 201
+* DELETE `/api/comment/reaction` 删除反应 202
+
+verb 为任意字符串,包括emoji
```json
{
- "comment_id": "60c46d6a6d7c5dd22d69fd3b",
- "verb": "dislike/like"
+ "comment_id": "60c46d6a6d7c5dd22d69fd3b,父评论子评论均可",
+ "verb": "😍👍"
}
```
返回:
-* 201 成功
+* 201 成功添加反应
+* 202 成功删除反应
* 404 评论没找到
-* 422 已经赞/踩过了
-* 400 请求参数错误
-用户曾经点赞的记录会在 `GET /api/user` 返回
+用户添加的反应,在返回评论时可以看到,会额外多一个字段 `reactions`,子评论同理
+
+```json
+{
+ "reactions": [
+ {
+ "verb": "😍",
+ "users": [
+ "user2"
+ ]
+ },
+ {
+ "verb": "🤔",
+ "users": [
+ "user3",
+ "da"
+ ]
+ }
+ ]
+}
+```
# metrics
@@ -757,4 +959,274 @@ verb 为`like` 或 `dislike`
}
]
}
-```
\ No newline at end of file
+```
+
+# 通知
+
+只有登录用户可以获取,只有楼主能够获取到通知,其他楼层的人获取不到。
+
+## 获取通知
+
+* GET `http://127.0.0.1:8888/api/notification`
+
+支持URL参数page和size,默认1和5,size是已读和未读共享的,优先返回未读数据
+
+```json
+{
+ "username": "user1",
+ "unread_item": [
+ {
+ "username": "user3",
+ "date": "2021-08-14 20:23:02",
+ "browser": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:86.1) Gecko/20100101 Firefox/86.1",
+ "content": "@user2 u3 to u2",
+ "resource_id": 233,
+ "type": "child",
+ "id": "6117b5a6598f80ca3ebb13ed",
+ "reply_to_content": "@user1 ajnkwa"
+ },
+ {
+ "username": "user3",
+ "date": "2021-08-14 20:22:37",
+ "browser": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:86.1) Gecko/20100101 Firefox/86.1",
+ "content": "@user1 u3",
+ "resource_id": 233,
+ "type": "child",
+ "id": "6117b58dce422260bcbb13ec",
+ "reply_to_content": "hello"
+ }
+ ],
+ "read_item": [
+ {
+ "username": "user2",
+ "date": "2021-08-14 20:10:13",
+ "browser": "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:86.1) Gecko/20100101 Firefox/86.1",
+ "content": "@user1 ajnkwa",
+ "resource_id": 233,
+ "type": "child",
+ "id": "6117b2a59195e6b1ab86eb30",
+ "reply_to_content": "hello"
+ }
+ ],
+ "unread_count": 2,
+ "read_count": 1
+}
+```
+
+# 已读、未读消息
+
+* PATCH `http://127.0.0.1:8888/api/notification`
+ json body
+
+```json
+{
+ "comment_id": "61013c839633a80254ef2e38",
+ "verb": "unread"
+}
+```
+
+verb只可以是 `read` 和 `unread`
+comment_id 是评论的id
+
+# 分类
+
+最灵活的API! 推荐组合方式: 国家、分类
+
+* GET `/api/category`
+
+参数 `douban=True` 会返回豆瓣信息,默认不返回 支持如下参数,均为可选参数,可自由组合,URL参数可以URL编码,也可以不编码:
+
+## 分页参数
+
+* page 默认1
+* size 默认15
+
+## channel
+
+大分类,电影、电视剧、公开课,详细分类可以使用 `channel_cn`
+
+```python
+[('movie', 12057), ('tv', 5428), ('openclass', 35), ('discuss', 1)]
+```
+
+## channel_cn
+
+推荐值:
+
+```python
+[('电影', 12057), ('美剧', 2119), ('日剧', 1215), ('英剧', 641), ('纪录片', 314), ('韩剧', 212), ('动画', 126), ('泰剧', 112),
+ ('加剧', 82), ('西剧', 62), ('澳剧', 61)]
+
+```
+
+全部可选值:
+
+```python
+[('电影', 12057), ('美剧', 2119), ('日剧', 1215), ('英剧', 641), ('纪录片', 314), ('韩剧', 212), ('动画', 126), ('泰剧', 112),
+ ('加剧', 82), ('西剧', 62), ('澳剧', 61), ('真人秀', 51), ('法剧', 50), ('德剧', 41), ('公开课', 35), ('其剧', 34), ('越剧', 23),
+ ('巴剧', 21), ('俄剧', 21), ('意剧', 19), ('墨剧', 16), ('印剧', 16), ('土剧', 16), ('\x00剧', 12), ('电视电影', 12), ('脱口秀', 10),
+ ('挪威剧', 9), ('丹麦剧', 8), ('综艺', 7), ('葡萄牙剧', 6), ('颁奖礼', 5), ('以色列剧', 4), ('新剧', 4), ('菲律宾剧', 4), ('动漫', 4), ('瑞典剧', 4),
+ ('新西兰剧', 4), ('神剧', 3), ('短视频', 3), ('舞台剧', 3), ('MV', 3), ('演讲', 3), ('颁奖典礼', 3), ('比利时剧', 3), ('南非剧', 3), ('电视剧', 3),
+ ('晨间剧', 2), ('短片', 2), ('荷兰剧', 2), ('巴西电视剧', 2), ('爱尔兰剧', 2), ('汽车三贱客', 2), ('芬兰剧', 2), ('大剧', 2), ('美剧 律政', 1),
+ ('美剧/英剧', 1), ('小镇疑云(美版)', 1), ('动画片', 1), ('埃剧', 1), ('探案', 1), ('纪录', 1), ('演唱会', 1), ('冰岛剧', 1), ('深夜剧', 1),
+ ('萌剧', 1), ('律政/剧情', 1), ('2013年BBC历史记录片', 1), ('催眠剧', 1), ('波兰剧', 1), ('幼教', 1), ('约旦', 1), ('闹剧', 1), ('浪漫/喜剧', 1),
+ ('悬疑/罪案', 1), ('BBC世界杯专题纪录片', 1), ('克罗地亚剧', 1), ('台剧', 1), ('墨西哥剧', 1), ('惊悚', 1), ('阿拉伯剧', 1), ('委内瑞拉电视剧', 1),
+ ('音乐会', 1), ('巴西剧', 1), ('新闻', 1), ('土耳其剧', 1), ('约旦剧', 1), ('发布会', 1), ('丹麦瑞典合拍', 1), ('捷克剧', 1), ('越南剧', 1),
+ ('剧情', 1), ('墨西哥电视剧', 1), ('韩综', 1), ('花絮', 1), ('', 1)]
+
+```
+
+## area
+
+**推荐使用**
+推荐值
+
+```python
+[('美国', 9057), ('日本', 2233), ('英国', 1637), ('法国', 902), ('韩国', 763), ('其他', 535), ('德国', 402), ('加拿大', 313),
+ ('西班牙', 280), ('印度', 247), ('俄罗斯', 234), ('泰国', 191), ('澳大利亚', 182), ('意大利', 150), ('', 109), ('越南', 60), ('巴西', 54),
+ ('大陆', 52), ('墨西哥', 40), ('土耳其', 35), ('新加坡', 23), ('香港', 20), ('埃及', 1), ('台湾', 1)]
+
+```
+
+## show_type
+
+不推荐使用!数据缺失非常严重
+
+```python
+[('', 16751), ('纪录片', 314), ('动画', 126), ('日剧', 110), ('真人秀', 51), ('电视电影', 12), ('脱口秀', 10), ('挪威剧', 9), ('美剧', 8),
+ ('丹麦剧', 8), ('综艺', 7), ('葡萄牙剧', 6), ('颁奖礼', 5), ('以色列剧', 4), ('菲律宾剧', 4), ('动漫', 4), ('瑞典剧', 4), ('新西兰剧', 4),
+ ('神剧', 3), ('英剧', 3), ('短视频', 3), ('舞台剧', 3), ('MV', 3), ('演讲', 3), ('颁奖典礼', 3), ('比利时剧', 3), ('南非剧', 3), ('晨间剧', 2),
+ ('短片', 2), ('荷兰剧', 2), ('新剧', 2), ('巴西电视剧', 2), ('爱尔兰剧', 2), ('汽车三贱客', 2), ('芬兰剧', 2), ('泰剧', 1), ('美剧 律政', 1),
+ ('美剧/英剧', 1), ('小镇疑云(美版)', 1), ('动画片', 1), ('探案', 1), ('纪录', 1), ('演唱会', 1), ('冰岛剧', 1), ('深夜剧', 1), ('萌剧', 1),
+ ('律政/剧情', 1), ('2013年BBC历史记录片', 1), ('催眠剧', 1), ('波兰剧', 1), ('幼教', 1), ('约旦', 1), ('闹剧', 1), ('浪漫/喜剧', 1), ('韩剧', 1),
+ ('悬疑/罪案', 1), ('西剧', 1), ('BBC世界杯专题纪录片', 1), ('克罗地亚剧', 1), ('墨西哥剧', 1), ('惊悚', 1), ('阿拉伯剧', 1), ('委内瑞拉电视剧', 1),
+ ('音乐会', 1), ('巴西剧', 1), ('新闻', 1), ('土耳其剧', 1), ('约旦剧', 1), ('发布会', 1), ('丹麦瑞典合拍', 1), ('捷克剧', 1), ('越南剧', 1),
+ ('剧情', 1), ('墨西哥电视剧', 1), ('韩综', 1), ('花絮', 1)]
+
+```
+
+## 请求范例
+
+全部使用 `size=1&douban=True`做为范例,响应结构如下
+
+注意,由于并不是所有的资源都有豆瓣信息,因此有些可能douban字段为 `{}`
+
+```json
+{
+ "data": [
+ {
+ "id": 30552,
+ "cnname": "极限战队",
+ "enname": "Ultraforce",
+ "aliasname": "极端力量",
+ "channel": "tv",
+ "channel_cn": "美剧",
+ "area": "美国",
+ "show_type": "",
+ "expire": "1610397126",
+ "views": 0,
+ "year": [
+ 2013
+ ],
+ "douban": {
+ "name": "极限战队",
+ "doubanId": 1295384,
+ "doubanLink": "https://movie.douban.com/subject/1295384/",
+ "posterLink": "https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2512733819.jpg",
+ "posterData": "base64编码的图片",
+ "resourceId": 30552,
+ "rating": "7.9",
+ "actors": [
+ "卡斯帕·范·迪恩",
+ "迪娜·迈耶",
+ "丹妮丝·理查兹",
+ "杰克·布塞",
+ "尼尔·帕特里克·哈里斯",
+ "克兰西·布朗",
+ "塞斯·吉列姆",
+ "帕特里克·茂顿",
+ "迈克尔·艾恩塞德",
+ "露·麦克拉纳罕",
+ "马绍尔·贝尔",
+ "埃里克·布鲁斯科特尔",
+ "马特·莱文",
+ "布蕾克·林斯利",
+ "安东尼·瑞维瓦",
+ "布兰达·斯特朗",
+ "迪恩·诺里斯",
+ "克里斯托弗·柯里",
+ "莱诺尔·卡斯多夫",
+ "罗伯特·斯莫特",
+ "斯蒂芬·福特",
+ "罗伯特·大卫·豪尔",
+ "艾米·斯马特",
+ "蒂莫西·奥门德森",
+ "代尔·戴"
+ ],
+ "directors": [
+ "保罗·范霍文"
+ ],
+ "genre": [
+ "动作",
+ "科幻",
+ "惊悚",
+ "冒险"
+ ],
+ "releaseDate": "1997-11-07",
+ "episodeCount": "",
+ "episodeDuration": "129 分钟",
+ "writers": [
+ "爱德华·诺麦尔",
+ "罗伯特·A·海因莱因"
+ ],
+ "year": "1997",
+ "introduction": "高中生瑞科(卡斯帕•凡•迪恩CasperVanDien饰)毕业后,在女友卡门(丹妮丝•理查兹DeniseRichards饰)的鼓动下,违背了父亲的意志,加入了机械化步兵学院,卡门亦加入了海军学院。在他们参加训练不久后,地球遭到了来自外星球的昆虫袭击。瑞科的亲人均惨遭杀害,卡门将拍摄到的影像传送给了瑞科。悲愤交加的瑞科率领部下投入到了对抗外星昆虫的战斗中。然而,军队低估了这些昆虫的实力。在一次遭遇战中,10万军队惨遭杀戮,只剩瑞科、卡门等几人侥幸逃生。瑞科亲眼目睹了恐怖的杀戮场面,意外获知了这些昆虫变得如此聪明、强大的秘密。瑞科意识到必须制造更先进的武器才能对付这些昆虫,人类的反击开始了!"
+ }
+ }
+ ],
+ "count": 9057
+}
+```
+
+* 日剧 `http://127.0.0.1:8888/api/category?channel_cn=日剧`
+* 国家为"美国"的资源 `http://127.0.0.1:8888/api/category?area=美国`
+* 美国的纪录片 `http://127.0.0.1:8888/api/category?&area=美国&channel_cn=纪录片`
+* 日本的电影 `http://127.0.0.1:8888/api/category?size=1&area=日本&channel=movie` 或 `channel_cn=电影`
+* 动漫 `http://127.0.0.1:8888/api/category?size=1&channel_cn=动漫`
+
+# 最新资源
+
+* GET `/api/resource/latest`
+
+可选URL参数 size,最大100,超过100无效。如 `http://127.0.0.1:8888/api/resource/latest?size=5` 即为获取最新5条数据
+
+```json
+{
+ "data": [
+ {
+ "name": "速度与激情9-F9 (2021) (1080p) [BluRay] [HD FULL].avi 1.52 GB",
+ "timestamp": "1623415867",
+ "size": "1.52GB",
+ "resource_id": 39894,
+ "res_name": "速度与激情9",
+ "date": "2021-06-11 20:51:07"
+ },
+ {
+ "name": "洛基-E01",
+ "timestamp": "1623415867",
+ "size": "788.53MB",
+ "resource_id": 41382,
+ "res_name": "洛基",
+ "date": "2021-06-11 20:51:07"
+ },
+ {
+ "name": "致命女人-EP01",
+ "timestamp": "1623415867",
+ "size": "790MB",
+ "resource_id": 38413,
+ "res_name": "致命女人",
+ "date": "2021-06-11 20:51:07"
+ }
+ ]
+}
+```
diff --git a/Dockerfile b/Dockerfile
index 962c916..81ea3ec 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -20,10 +20,8 @@ COPY YYeTsFE/package.json /YYeTsBot/YYeTsFE/
COPY YYeTsFE/yarn.lock /YYeTsBot/YYeTsFE/
RUN yarn --network-timeout 1000000
COPY YYeTsFE /YYeTsBot/YYeTsFE/
-COPY .git/modules /YYeTsBot/.git/modules/
-COPY hooks/dev_robots.sh /tmp/
-RUN echo "gitdir: ../.git/modules/YYeTsFE" > .git
-RUN if [ "$env" = "dev" ]; then echo "dev build"; yarn build; sh /tmp/dev_robots.sh; rm /tmp/dev_robots.sh; else echo "prod build"; yarn run release; fi
+COPY scripts/dev_robots.sh /tmp/
+RUN if [ "$env" = "dev" ]; then echo "dev build"; yarn build; sh /tmp/dev_robots.sh; else echo "prod build"; yarn run release; fi
FROM runner
@@ -32,7 +30,7 @@ COPY --from=pybuilder /root/.local /usr/local
COPY --from=pybuilder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=pybuilder /usr/share/zoneinfo /usr/share/zoneinfo
RUN true
-COPY --from=nodebuilder /YYeTsBot/YYeTsFE/build /YYeTsBot/yyetsweb
+COPY --from=nodebuilder /YYeTsBot/YYeTsFE/build /YYeTsBot/yyetsweb/templates/
ENV TZ=Asia/Shanghai
WORKDIR /YYeTsBot/yyetsbot
diff --git a/YYeTsFE b/YYeTsFE
index 5c85689..ec00c3e 160000
--- a/YYeTsFE
+++ b/YYeTsFE
@@ -1 +1 @@
-Subproject commit 5c85689ad0060c7894e4229ad09fb9fb64ab72db
+Subproject commit ec00c3ee0b6b3e8aeac66de6993bfe5b014a8e4e
diff --git a/conf/yyets.env b/conf/yyets.env
new file mode 100644
index 0000000..f82e9d1
--- /dev/null
+++ b/conf/yyets.env
@@ -0,0 +1,6 @@
+mongo=mongo
+redis=redis
+email_user=username
+email_password=passord
+email_host=mailhog
+email_port=1025
diff --git a/docker-compose.yml b/docker-compose.yml
index 7cd3c7c..46d85d2 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -15,6 +15,19 @@ services:
logging:
driver: none
+ yyets-web:
+ image: bennythink/yyetsbot
+ restart: unless-stopped
+ environment:
+ - mongo=mongo
+ - redis=redis
+ working_dir: /YYeTsBot/yyetsweb/
+ volumes:
+ - ./data:/YYeTsBot/yyetsweb/data/
+ command: [ "python3","server.py","-h=0.0.0.0" ]
+ ports:
+ - "127.0.0.1:8888:8888"
+
socat:
image: alpine/socat
restart: always
@@ -32,6 +45,12 @@ services:
env_file:
- env/yyets.env
+ mailhog:
+ image: cd2team/mailhog
+ restart: unless-stopped
+ ports:
+ - "8025:8025"
+
nginx:
restart: always
image: nginx:alpine
@@ -45,15 +64,3 @@ services:
- "443:443"
environment:
TZ: Asia/Shanghai
-
- yyets-web:
- image: bennythink/yyetsbot
- restart: unless-stopped
- environment:
- - mongo=mongo
- working_dir: /YYeTsBot/yyetsweb/
- volumes:
- - ./data:/YYeTsBot/yyetsweb/data/
- command: [ "python3","server.py","-h=0.0.0.0" ]
- ports:
- - "127.0.0.1:8888:8888"
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 73593bc..d01da31 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,7 +5,7 @@ tgbot-ping
redis==3.5.3
apscheduler==3.6.3
pymongo==3.11.2
-tornado==6.0.4
+tornado==6.1
redis==3.5.3
captcha==0.3
passlib==1.7.4
diff --git a/hooks/dev_robots.sh b/scripts/dev_robots.sh
similarity index 100%
rename from hooks/dev_robots.sh
rename to scripts/dev_robots.sh
diff --git a/scripts/install.sh b/scripts/install.sh
index 0c8f9db..39971cb 100644
--- a/scripts/install.sh
+++ b/scripts/install.sh
@@ -23,7 +23,7 @@ function prepare() {
function prepare_compose() {
echo "[2/5] 下载docker-compose.yaml"
curl -o docker-compose.yaml https://raw.githubusercontent.com/tgbot-collection/YYeTsBot/master/docker-compose.yml
- sed -i '18,47d' docker-compose.yaml
+ sed -i '31,67d' docker-compose.yaml
sed -i 's/127.0.0.1/0.0.0.0/' docker-compose.yaml
}
diff --git a/hooks/pre_build b/scripts/pre_build.sh
similarity index 100%
rename from hooks/pre_build
rename to scripts/pre_build.sh
diff --git a/tests/.coveragerc b/tests/.coveragerc
deleted file mode 100644
index 7483f5f..0000000
--- a/tests/.coveragerc
+++ /dev/null
@@ -1,5 +0,0 @@
-[run]
-omit =
- */site-packages/*
- */distutils/*
- tests/*
\ No newline at end of file
diff --git a/tests/data/yyets_search.html b/tests/data/yyets_search.html
deleted file mode 100644
index 119e308..0000000
--- a/tests/data/yyets_search.html
+++ /dev/null
@@ -1,474 +0,0 @@
-
-
-
-
-
-
-
-
-abc,搜索结果页
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
电视剧
-
-
-
-
发布:1544957285
-
更新:1593172855
-
-
-
-
-
-
-
-
电影
-
-
-
-
发布:1362308260
-
更新:1432680927
-
-
-
-
-
-
-
-
电影
-
-
-
-
发布:1432555399
-
更新:1432619677
-
-
-
-
-
-
-
-
电影
-
-
-
-
发布:1354548684
-
更新:1354627584
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/tests/data/zimuxia_result.html b/tests/data/zimuxia_result.html
deleted file mode 100644
index b365878..0000000
--- a/tests/data/zimuxia_result.html
+++ /dev/null
@@ -1,236 +0,0 @@
-
-
-
-
-
-
-天国与地狱 | 字幕组 FIX字幕侠 做国内最好的字幕组 天国与地狱FIX字幕侠百度网盘,FIX字幕侠天国与地狱,天国与地狱字幕下载
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
天国与地狱
-
天国与地狱
-
-
导演 :
平川雄一朗 / 青山贵洋 / 松木彩
-
编剧 :
森下佳子
-
主演 : 绫濑遥 / 高桥一生 / 北村一辉 / 柄本佑 / 沟端淳平 / 更多…
-
类型: 悬疑 / 犯罪
-
官方网站: www.tbs.co.jp/tengokutojigoku_tbs/
-
制片国家/地区: 日本
-
语言: 日语
-
首播: 2021-01-17(日本)
-
单集片长: 54分钟
-
-
【剧情简介】
-
绫濑遥将饰演本剧女主角、东京警视厅搜查一课的正义女警望月彩子,不幸与高桥一生饰演的高智商杀人犯日高互换灵魂,两个人从说话、生活到思维方式都发了逆转,日子变得一团糟,而柄本佑饰演的邻家自由业者渡边,和北村一辉饰演的彩子上司河原,也参与其中。本剧编剧为森下佳子(《乱世花道》《天皇的御厨》),导演为平川雄一朗(《天皇的御厨》)、松木彩(《半泽直树2》)和青山贵洋,每周日晚9点播出。
-
-
-
【资源下载】
-
-
E01 UC网盘
115网盘 百度网盘 磁力下载 电驴下载 提取码:FIXX
-E02 UC网盘 115网盘 百度网盘 磁力下载 电驴下载 提取码:FIXX
-E03 UC网盘 115网盘 百度网盘 磁力下载 电驴下载 提取码:FIXX
-E04 UC网盘 115网盘 百度网盘 磁力下载 电驴下载 提取码:FIXX
-E05 UC网盘 115网盘 百度网盘 磁力下载 电驴下载 提取码:FIXX
-E06 UC网盘 115网盘 百度网盘 磁力下载 电驴下载 提取码:FIXX
-E07 UC网盘 115网盘 百度网盘 磁力下载 电驴下载 提取码:FIXX
-
E08 UC网盘 115网盘 百度网盘 磁力下载 电驴下载 提取码:FIXX
-E09 UC网盘 115网盘 百度网盘 磁力下载 电驴下载 提取码:FIXX
-
E10 UC网盘 115网盘 百度网盘 磁力下载 电驴下载 提取码:FIXX
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/tests/data/zimuxia_search.html b/tests/data/zimuxia_search.html
deleted file mode 100644
index 877baae..0000000
--- a/tests/data/zimuxia_search.html
+++ /dev/null
@@ -1,259 +0,0 @@
-
-
-
-
-
-
-You searched for 逃避 | 字幕组 FIX字幕侠 做国内最好的字幕组
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
逃避可耻但有用 导演: 金子文紀 / 土井裕泰 / 石井康晴 编剧: 野木亜紀子 主演: 新垣结衣 / 星野源 […]
-
-Read More
-›
-
-
-
-
-
-
-
-
-
-
-
-
亢奋 导演: 奥古斯丁·弗里泽尔 编剧: 萨姆·李文森 主演: 赞达亚 / 茉德·阿帕图 / Brian Br […]
-
-Read More
-›
-
-
-
-
-
-
-
-
-
-
-
-
第二十二条军规 导演: 乔治·克鲁尼 / 格兰特·赫斯洛夫 / 艾伦·库拉斯 编剧: 卢克·戴维斯 / 大卫· […]
-
-Read More
-›
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/tests/mongo_test_data.json b/tests/mongo_test_data.json
deleted file mode 100644
index bacd44f..0000000
--- a/tests/mongo_test_data.json
+++ /dev/null
@@ -1,1053 +0,0 @@
-[
- {
- "status": 1,
- "info": "OK",
- "data": {
- "info": {
- "id": 34812,
- "cnname": "逃避可耻却有用",
- "enname": "NIGERUHA HAJIDAGA YAKUNITATSU",
- "aliasname": "逃避虽可耻但有用 / 雇佣妻子(港) / 月薪娇妻(台) / 逃跑是可耻但是有用 / 逃避虽可耻但很有用 / 逃避可耻但有用",
- "channel": "tv",
- "channel_cn": "日剧",
- "area": "日本",
- "show_type": "",
- "expire": "1610399344",
- "views": 202
- },
- "list": [
- {
- "season_num": "101",
- "season_cn": "单剧",
- "items": {
- "APP": [
- {
- "itemid": "554385",
- "episode": "12",
- "name": "yyets://N=逃避可耻却有用 人类加油!新春特别篇!!.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ganbare.Jinrui.Shinshun.Special.SP.Chi_Jap.HDTVrip.1280X720.mp4|S=1505489064|H=b7aae378e30689eab20125e3bff3bac1d16a043e|",
- "size": "",
- "yyets_trans": 0,
- "dateline": "1609622857",
- "files": [
- {
- "way": "102",
- "way_cn": "百度云",
- "address": "https://pan.baidu.com/s/149tnE0DWW68jU5nMbdFH3w",
- "passwd": "x28a"
- },
- {
- "way": "115",
- "way_cn": "微云",
- "address": "https://share.weiyun.com/Y2ooLgRe",
- "passwd": ""
- }
- ]
- },
- {
- "itemid": "298235",
- "episode": "11",
- "name": "yyets://N=逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep11.Final.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4|S=733784950|H=c464b9e32a999b417324b53cd92d2fbfa89b597a|",
- "size": "",
- "yyets_trans": 0,
- "dateline": "1491891741",
- "files": null
- },
- {
- "itemid": "298234",
- "episode": "10",
- "name": "yyets://N=逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep10.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4|S=733930391|H=3c572ede5b41a2368d0f64071ea733592876344a|",
- "size": "",
- "yyets_trans": 0,
- "dateline": "1491891741",
- "files": null
- },
- {
- "itemid": "298233",
- "episode": "9",
- "name": "yyets://N=逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep09.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4|S=629132305|H=129e9e73d44ccf575afc70eecdd171732fc89329|",
- "size": "",
- "yyets_trans": 0,
- "dateline": "1491891741",
- "files": null
- },
- {
- "itemid": "298232",
- "episode": "8",
- "name": "yyets://N=逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep08.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4|S=629207765|H=47e4573d0d72fa7d2394a377f467d0b352fce08f|",
- "size": "",
- "yyets_trans": 0,
- "dateline": "1491891741",
- "files": null
- },
- {
- "itemid": "298231",
- "episode": "7",
- "name": "yyets://N=逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep07.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4|S=629119647|H=bb2ff32f008d3d2e58371f34b83292ddde7b51e8|",
- "size": "",
- "yyets_trans": 0,
- "dateline": "1491891741",
- "files": null
- },
- {
- "itemid": "298230",
- "episode": "6",
- "name": "yyets://N=逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep06.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4|S=629239487|H=83f72df4cd440c22c8c5ab94b559773eb472c46d|",
- "size": "",
- "yyets_trans": 0,
- "dateline": "1491891741",
- "files": null
- },
- {
- "itemid": "298229",
- "episode": "5",
- "name": "yyets://N=逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep05.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4|S=629262958|H=30bb5f65bff74ebe60d848edd4748b3c3e7e76c7|",
- "size": "",
- "yyets_trans": 0,
- "dateline": "1491891741",
- "files": null
- },
- {
- "itemid": "298228",
- "episode": "4",
- "name": "yyets://N=逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep04.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4|S=629037391|H=3274e38c1ba20493a0bf62d4f7c65bec651dc3d2|",
- "size": "",
- "yyets_trans": 0,
- "dateline": "1491891741",
- "files": null
- },
- {
- "itemid": "298227",
- "episode": "3",
- "name": "yyets://N=逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep03.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4|S=628416378|H=9ac2a719d66b3626011c2d3366367a9d56c20e26|",
- "size": "",
- "yyets_trans": 0,
- "dateline": "1491891741",
- "files": null
- },
- {
- "itemid": "298226",
- "episode": "2",
- "name": "yyets://N=逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep02.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4|S=629086728|H=d21a081cc6a32daa85310ca6aad81e378f0b736e|",
- "size": "",
- "yyets_trans": 0,
- "dateline": "1491891741",
- "files": null
- },
- {
- "itemid": "298225",
- "episode": "1",
- "name": "yyets://N=逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep01.Chi_Jap.HDTVrip.1280X720-ZhuixinFanV2.mp4|S=733967923|H=fa1a42ee8066da0aa2b66ca470814489160ad214|",
- "size": "",
- "yyets_trans": 0,
- "dateline": "1491891741",
- "files": null
- }
- ],
- "HR-HDTV": [
- {
- "itemid": "554384",
- "episode": "12",
- "name": "逃避可耻却有用 人类加油!新春特别篇!!.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ganbare.Jinrui.Shinshun.Special.SP.Chi_Jap.HDTVrip.1280X720.mp4",
- "size": "1.4GB",
- "yyets_trans": 0,
- "dateline": "1609622857",
- "files": [
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:4a6be139e640db770dbe266471e5cc81357c205e&tr=http://tr.cili001.com:8070/announce&tr=udp://p4p.arenabg.com:1337&tr=udp://tracker.opentrackr.org:1337/announce&tr=udp://open.demonii.com:1337",
- "passwd": ""
- },
- {
- "way": "9",
- "way_cn": "网盘",
- "address": "https://pan.baidu.com/s/149tnE0DWW68jU5nMbdFH3w",
- "passwd": "x28a"
- }
- ]
- },
- {
- "itemid": "284973",
- "episode": "11",
- "name": "逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep11.Final.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4",
- "size": "699.79MB",
- "yyets_trans": 0,
- "dateline": "1482282868",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep11.Final.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4|733784950|51a18ead729a833f58264700b963113a|h=ncojnutlufhohfrl42xr7t2m6lqdwzjf|/",
- "passwd": ""
- },
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:78930bae5efe391ee6bcc4c7dcfa237b05a493f0&tr=http://tracker.openbittorrent.com/announce&tr=udp://tracker.openbittorrent.com:80/announce&tr=udp://tr.cili001.com:6666/announce&tr=http://tracker.publicbt.com/announce&tr=udp://open.demonii.com:1337&tr=udp://tracker.opentrackr.org:1337/announce&tr=http://tr.cili001.com:6666/announce",
- "passwd": ""
- },
- {
- "way": "9",
- "way_cn": "网盘",
- "address": "http://pan.baidu.com/s/1jHAM8aq",
- "passwd": ""
- }
- ]
- },
- {
- "itemid": "284430",
- "episode": "10",
- "name": "逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep10.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4",
- "size": "699.93MB",
- "yyets_trans": 0,
- "dateline": "1481704074",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep10.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4|733930391|15b6440bdd991d04a7ff6ba9d22e07d6|h=aodlfl4jgt7paj6v4vbolfjeho5d744t|/",
- "passwd": ""
- },
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:1e50cd628d6829127534e5b9411d505efaea98f8&tr=http://tracker.openbittorrent.com/announce&tr=udp://tracker.openbittorrent.com:80/announce&tr=udp://tr.cili001.com:6666/announce&tr=http://tracker.publicbt.com/announce&tr=udp://open.demonii.com:1337&tr=udp://tracker.opentrackr.org:1337/announce&tr=http://tr.cili001.com:6666/announce",
- "passwd": ""
- },
- {
- "way": "9",
- "way_cn": "网盘",
- "address": "http://pan.baidu.com/s/1nvPP4db",
- "passwd": ""
- },
- {
- "way": "12",
- "way_cn": "诚通网盘",
- "address": "http://ZiMuZuUSTV.ctfile.com/fs/DFw163629975",
- "passwd": ""
- }
- ]
- },
- {
- "itemid": "283661",
- "episode": "9",
- "name": "逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep09.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4",
- "size": "599.99MB",
- "yyets_trans": 0,
- "dateline": "1481098725",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep09.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4|629132305|9d5152f4f21f1a1bf053ce33abd41ad5|h=mwd7hctmq5ziym7ugc2vptn6ieazici3|/",
- "passwd": ""
- },
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:7d047723d5697c68361b05eb20da1c50d224425e&tr=http://tracker.openbittorrent.com/announce&tr=udp://tracker.openbittorrent.com:80/announce&tr=udp://tr.cili001.com:6666/announce&tr=http://tracker.publicbt.com/announce&tr=udp://open.demonii.com:1337&tr=udp://tracker.opentrackr.org:1337/announce&tr=http://tr.cili001.com:6666/announce",
- "passwd": ""
- },
- {
- "way": "9",
- "way_cn": "网盘",
- "address": "http://pan.baidu.com/s/1nu9tJM5",
- "passwd": ""
- },
- {
- "way": "12",
- "way_cn": "诚通网盘",
- "address": "http://ZiMuZuUSTV.ctfile.com/fs/9K9162939027",
- "passwd": ""
- }
- ]
- },
- {
- "itemid": "282897",
- "episode": "8",
- "name": "逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep08.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4",
- "size": "600.06MB",
- "yyets_trans": 0,
- "dateline": "1480497463",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep08.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4|629207765|1ba73a37879514eabd5ab2adf938be40|h=scdffp3xfc4e36fdhslh5p3sr76oh5a6|/",
- "passwd": ""
- },
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:3ac5f75b4ad9e59f1a9752067eaa8ed9117a0931&tr.1=http://tracker.openbittorrent.com/announce&tr.2=udp://tracker.openbittorrent.com:80/announce&tr.3=udp://tr.cili001.com:6666/announce&tr.4=http://tracker.publicbt.com/announce&tr.5=udp://open.demonii.com:1337&tr.6=udp://tracker.opentrackr.org:1337/announce&tr.7=http://tr.cili001.com:6666/announce",
- "passwd": ""
- },
- {
- "way": "9",
- "way_cn": "网盘",
- "address": "http://pan.baidu.com/s/1kUGjaMb",
- "passwd": ""
- },
- {
- "way": "12",
- "way_cn": "诚通网盘",
- "address": "http://ZiMuZuUSTV.ctfile.com/fs/Ula162377565",
- "passwd": ""
- }
- ]
- },
- {
- "itemid": "282123",
- "episode": "7",
- "name": "逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep07.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4",
- "size": "599.98MB",
- "yyets_trans": 0,
- "dateline": "1479886413",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep07.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4|629119647|1565719ed7d08c10a7bac9a740e3bdda|h=5pho2gfiivlv3rkhrh7q4dvsietozz5r|/",
- "passwd": ""
- },
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:b8283af2cdca0b1b751f716423611e4795fbee77&tr.1=http://tracker.openbittorrent.com/announce&tr.2=udp://tracker.openbittorrent.com:80/announce&tr.3=udp://tr.cili001.com:6666/announce&tr.4=http://tracker.publicbt.com/announce&tr.5=udp://open.demonii.com:1337&tr.6=udp://tracker.opentrackr.org:1337/announce&tr.7=http://tr.cili001.com:6666/announce",
- "passwd": ""
- },
- {
- "way": "9",
- "way_cn": "网盘",
- "address": "http://pan.baidu.com/s/1qYfaU3E",
- "passwd": ""
- },
- {
- "way": "12",
- "way_cn": "诚通网盘",
- "address": "http://ZiMuZuUSTV.ctfile.com/fs/AtK161811663",
- "passwd": ""
- }
- ]
- },
- {
- "itemid": "281371",
- "episode": "6",
- "name": "逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep06.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4",
- "size": "600.09MB",
- "yyets_trans": 0,
- "dateline": "1479281049",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep06.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4|629239487|9fc21b8ce21f0699f035404ad4baae6a|h=4yse343oq7vymc4qe6qtjyge2yfukf7q|/",
- "passwd": ""
- },
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:0186d5447834e8051af6551faa4cfde44476aff5&tr.1=http://tracker.openbittorrent.com/announce&tr.2=udp://tracker.openbittorrent.com:80/announce&tr.3=udp://tr.cili001.com:6666/announce&tr.4=http://tracker.publicbt.com/announce&tr.5=udp://open.demonii.com:1337&tr.6=udp://tracker.opentrackr.org:1337/announce&tr.7=http://tr.cili001.com:6666/announce",
- "passwd": ""
- },
- {
- "way": "9",
- "way_cn": "网盘",
- "address": "http://pan.baidu.com/s/1slzXo3z",
- "passwd": ""
- },
- {
- "way": "12",
- "way_cn": "诚通网盘",
- "address": "http://ZiMuZuUSTV.ctfile.com/fs/4ia161194698",
- "passwd": ""
- }
- ]
- },
- {
- "itemid": "280656",
- "episode": "5",
- "name": "逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep05.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4",
- "size": "600.11MB",
- "yyets_trans": 0,
- "dateline": "1478670362",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep05.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4|629262958|ba41b35dcfec2716f90ab664048f7e09|h=yrdanlqzxpqrsldgoi5aobjqhldxm6vq|/",
- "passwd": ""
- },
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:f52c3ba04703cd4298e7a6a09ee87f520f700f7b&tr.1=http://tracker.openbittorrent.com/announce&tr.2=udp://tracker.openbittorrent.com:80/announce&tr.3=udp://tr.cili001.com:6666/announce&tr.4=http://tracker.publicbt.com/announce&tr.5=udp://open.demonii.com:1337&tr.6=udp://tracker.opentrackr.org:1337/announce&tr.7=http://tr.cili001.com:6666/announce",
- "passwd": ""
- },
- {
- "way": "9",
- "way_cn": "网盘",
- "address": "http://pan.baidu.com/s/1c1Rl1BU",
- "passwd": ""
- },
- {
- "way": "12",
- "way_cn": "诚通网盘",
- "address": "http://ZiMuZuUSTV.ctfile.com/fs/fMW160612929",
- "passwd": ""
- }
- ]
- },
- {
- "itemid": "280068",
- "episode": "4",
- "name": "逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep04.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4",
- "size": "599.9MB",
- "yyets_trans": 0,
- "dateline": "1478065113",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep04.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4|629037391|07d320c3db4ad737e398cc7ba08c5216|h=yzfnqwlsvw2yloobhvkjf6yil6gc5kag|/",
- "passwd": ""
- },
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:46ec914334a77f968d1c789b2f4a9fee8648435a&tr.1=http://tracker.openbittorrent.com/announce&tr.2=udp://tracker.openbittorrent.com:80/announce&tr.3=udp://tr.cili001.com:6666/announce&tr.4=http://tracker.publicbt.com/announce&tr.5=udp://open.demonii.com:1337&tr.6=udp://tracker.opentrackr.org:1337/announce&tr.7=http://tr.cili001.com:6666/announce",
- "passwd": ""
- },
- {
- "way": "9",
- "way_cn": "网盘",
- "address": "http://pan.baidu.com/s/1pKED1gv",
- "passwd": ""
- },
- {
- "way": "12",
- "way_cn": "诚通网盘",
- "address": "http://ZiMuZuUSTV.ctfile.com/fs/cOt160028088",
- "passwd": ""
- }
- ]
- },
- {
- "itemid": "279384",
- "episode": "3",
- "name": "逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep03.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4",
- "size": "599.3MB",
- "yyets_trans": 0,
- "dateline": "1477483732",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep03.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4|628416378|8fdc3bd1714d5dab282f5e0f7cdf25be|h=on5tdwesk4maldu4txop3eutgihi2yyu|/",
- "passwd": ""
- },
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:157d2fc48b9efbe8f846643652a7c2f3d7f14989&tr.1=http://tracker.openbittorrent.com/announce&tr.2=udp://tracker.openbittorrent.com:80/announce&tr.3=udp://tr.cili001.com:6666/announce&tr.4=http://tracker.publicbt.com/announce&tr.5=udp://open.demonii.com:1337&tr.6=udp://tracker.opentrackr.org:1337/announce&tr.7=http://tr.cili001.com:6666/announce",
- "passwd": ""
- },
- {
- "way": "9",
- "way_cn": "网盘",
- "address": "http://pan.baidu.com/s/1ge3yfL5",
- "passwd": ""
- },
- {
- "way": "12",
- "way_cn": "诚通网盘",
- "address": "http://ZiMuZuUSTV.ctfile.com/fs/Ov3159483996",
- "passwd": ""
- }
- ]
- },
- {
- "itemid": "278649",
- "episode": "2",
- "name": "逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep02.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4",
- "size": "599.94MB",
- "yyets_trans": 0,
- "dateline": "1476868310",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep02.Chi_Jap.HDTVrip.1280X720-ZhuixinFan.mp4|629086728|3812eb6a3ef37b3ead6ad9c3a26efa24|h=jogqyi4cauhueoppsgcgdnprpbu7om3g|/",
- "passwd": ""
- },
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:1e5a9180c2be2aa6a1e932479dae91ce082ca22d&tr.1=http://tracker.openbittorrent.com/announce&tr.2=udp://tracker.openbittorrent.com:80/announce&tr.3=udp://tr.cili001.com:6666/announce&tr.4=http://tracker.publicbt.com/announce&tr.5=udp://open.demonii.com:1337&tr.6=udp://tracker.opentrackr.org:1337/announce&tr.7=http://tr.cili001.com:6666/announce",
- "passwd": ""
- },
- {
- "way": "9",
- "way_cn": "网盘",
- "address": "http://pan.baidu.com/s/1gfHRfnt",
- "passwd": ""
- },
- {
- "way": "12",
- "way_cn": "诚通网盘",
- "address": "http://ZiMuZuUSTV.ctfile.com/fs/2Tb158731035",
- "passwd": ""
- }
- ]
- },
- {
- "itemid": "277958",
- "episode": "1",
- "name": "逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep01.Chi_Jap.HDTVrip.1280X720-ZhuixinFanV2.mp4",
- "size": "699.97MB",
- "yyets_trans": 0,
- "dateline": "1476238571",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep01.Chi_Jap.HDTVrip.1280X720-ZhuixinFanV2.mp4|733967923|7116a9dfb86fceed3f5b71604e48745f|h=ku7saiivihhc26hcbx4cjsplvkum62ho|/",
- "passwd": ""
- },
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:2a331028531d0254a2b1f30e55edf99b6b716e49&tr.1=http://tracker.openbittorrent.com/announce&tr.2=udp://tracker.openbittorrent.com:80/announce&tr.3=udp://tr.cili001.com:6666/announce&tr.4=http://tracker.publicbt.com/announce&tr.5=udp://open.demonii.com:1337&tr.6=udp://tracker.opentrackr.org:1337/announce&tr.7=http://tr.cili001.com:6666/announce",
- "passwd": ""
- },
- {
- "way": "9",
- "way_cn": "网盘",
- "address": "http://pan.baidu.com/s/1hrOugEK",
- "passwd": ""
- },
- {
- "way": "12",
- "way_cn": "诚通网盘",
- "address": "http://ZiMuZuUSTV.ctfile.com/fs/Dde158319090",
- "passwd": ""
- }
- ]
- }
- ],
- "MP4": [
- {
- "itemid": "554387",
- "episode": "12",
- "name": "逃避可耻却有用 人类加油!新春特别篇!!.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ganbare.Jinrui.Shinshun.Special.SP.Chi_Jap.HDTVrip.1280X720.mp4",
- "size": "1.4GB",
- "yyets_trans": 0,
- "dateline": "1609622857",
- "files": [
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:4a6be139e640db770dbe266471e5cc81357c205e&tr=http://tr.cili001.com:8070/announce&tr=udp://p4p.arenabg.com:1337&tr=udp://tracker.opentrackr.org:1337/announce&tr=udp://open.demonii.com:1337",
- "passwd": ""
- }
- ]
- },
- {
- "itemid": "284971",
- "episode": "11",
- "name": "逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep11.Final.Chi_Jap.HDTVrip.852X480-ZhuixinFan.mp4",
- "size": "273.97MB",
- "yyets_trans": 0,
- "dateline": "1482265302",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep11.Final.Chi_Jap.HDTVrip.852X480-ZhuixinFan.mp4|287277713|c5e3cda7312d5c959b054ce1fe2d9648|h=yons5tync72soz55fil76fhasuyef2p5|/",
- "passwd": ""
- },
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:e643ee71dcfc70ef5c768f4801937bee3474ab7c&tr=http://tracker.openbittorrent.com/announce&tr=udp://tracker.openbittorrent.com:80/announce&tr=udp://tr.cili001.com:6666/announce&tr=http://tracker.publicbt.com/announce&tr=udp://open.demonii.com:1337&tr=udp://tracker.opentrackr.org:1337/announce&tr=http://tr.cili001.com:6666/announce",
- "passwd": ""
- },
- {
- "way": "9",
- "way_cn": "网盘",
- "address": "http://pan.baidu.com/s/1nu7sDDJ",
- "passwd": ""
- }
- ]
- },
- {
- "itemid": "284424",
- "episode": "10",
- "name": "逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep10.Chi_Jap.HDTVrip.852X480-ZhuixinFan.mp4",
- "size": "250.46MB",
- "yyets_trans": 0,
- "dateline": "1481694187",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep10.Chi_Jap.HDTVrip.852X480-ZhuixinFan.mp4|262629609|5ddda8755130865fd0b506b897dd3933|h=56ncsvze3suc4pszccf3frhbw24u454u|/",
- "passwd": ""
- },
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:64c03d502e04f592fa7dc86ea9b13ccce52c53e5&tr=http://tracker.openbittorrent.com/announce&tr=udp://tracker.openbittorrent.com:80/announce&tr=udp://tr.cili001.com:6666/announce&tr=http://tracker.publicbt.com/announce&tr=udp://open.demonii.com:1337&tr=udp://tracker.opentrackr.org:1337/announce&tr=http://tr.cili001.com:6666/announce",
- "passwd": ""
- },
- {
- "way": "9",
- "way_cn": "网盘",
- "address": "http://pan.baidu.com/s/1cvdJye",
- "passwd": ""
- },
- {
- "way": "12",
- "way_cn": "诚通网盘",
- "address": "http://ZiMuZuUSTV.ctfile.com/fs/mcp163620606",
- "passwd": ""
- }
- ]
- },
- {
- "itemid": "283576",
- "episode": "9",
- "name": "逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep09.Chi_Jap.HDTVrip.852X480-ZhuixinFan.mp4",
- "size": "197.99MB",
- "yyets_trans": 0,
- "dateline": "1481091708",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep09.Chi_Jap.HDTVrip.852X480-ZhuixinFan.mp4|207609828|c3bf94d4f2e63139d7196f0297aa3882|h=lixjdvvffgkozmcbujjkeusdmx5uz6h4|/",
- "passwd": ""
- },
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:f331279acc58e85d679418c7a11ebeefb4b02b42&tr=http://tracker.openbittorrent.com/announce&tr=udp://tracker.openbittorrent.com:80/announce&tr=udp://tr.cili001.com:6666/announce&tr=http://tracker.publicbt.com/announce&tr=udp://open.demonii.com:1337&tr=udp://tracker.opentrackr.org:1337/announce&tr=http://tr.cili001.com:6666/announce",
- "passwd": ""
- },
- {
- "way": "9",
- "way_cn": "网盘",
- "address": "http://pan.baidu.com/s/1miyMEbm",
- "passwd": ""
- },
- {
- "way": "12",
- "way_cn": "诚通网盘",
- "address": "http://ZiMuZuUSTV.ctfile.com/fs/pU0162934266",
- "passwd": ""
- }
- ]
- },
- {
- "itemid": "282890",
- "episode": "8",
- "name": "逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep08.Chi_Jap.HDTVrip.852X480-ZhuixinFan.mp4",
- "size": "193.81MB",
- "yyets_trans": 0,
- "dateline": "1480490078",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep08.Chi_Jap.HDTVrip.852X480-ZhuixinFan.mp4|203226404|0b2c976116eead8cee3a086d112ce738|h=b56bqcmprjcnkhwez5siuz2dzzvfljo4|/",
- "passwd": ""
- },
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:2fc115a1a26ed12f8d2f540e9f8699b989193e04&tr.1=http://tracker.openbittorrent.com/announce&tr.2=udp://tracker.openbittorrent.com:80/announce&tr.3=udp://tr.cili001.com:6666/announce&tr.4=http://tracker.publicbt.com/announce&tr.5=udp://open.demonii.com:1337&tr.6=udp://tracker.opentrackr.org:1337/announce&tr.7=http://tr.cili001.com:6666/announce",
- "passwd": ""
- },
- {
- "way": "9",
- "way_cn": "网盘",
- "address": "http://pan.baidu.com/s/1kUIkQ7X",
- "passwd": ""
- },
- {
- "way": "12",
- "way_cn": "诚通网盘",
- "address": "http://ZiMuZuUSTV.ctfile.com/fs/NyN162369651",
- "passwd": ""
- }
- ]
- },
- {
- "itemid": "282120",
- "episode": "7",
- "name": "逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep07.Chi_Jap.HDTVrip.852X480-ZhuixinFan.mp4",
- "size": "205.1MB",
- "yyets_trans": 0,
- "dateline": "1479881031",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep07.Chi_Jap.HDTVrip.852X480-ZhuixinFan.mp4|215062889|ca0ffa335b3a595e41f71d3c8f277040|h=a65hvn5lzc5c6loc746tf3yexszea4eh|/",
- "passwd": ""
- },
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:2d47c371eba87c52e7618a782b627a95fffed7b4&tr.1=http://tracker.openbittorrent.com/announce&tr.2=udp://tracker.openbittorrent.com:80/announce&tr.3=udp://tr.cili001.com:6666/announce&tr.4=http://tracker.publicbt.com/announce&tr.5=udp://open.demonii.com:1337&tr.6=udp://tracker.opentrackr.org:1337/announce&tr.7=http://tr.cili001.com:6666/announce",
- "passwd": ""
- },
- {
- "way": "9",
- "way_cn": "网盘",
- "address": "http://pan.baidu.com/s/1bo9Dwv1",
- "passwd": ""
- },
- {
- "way": "12",
- "way_cn": "诚通网盘",
- "address": "http://ZiMuZuUSTV.ctfile.com/fs/8Tk161811666",
- "passwd": ""
- }
- ]
- },
- {
- "itemid": "281368",
- "episode": "6",
- "name": "逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep06.Chi_Jap.HDTVrip.852X480-ZhuixinFan.mp4",
- "size": "225.23MB",
- "yyets_trans": 0,
- "dateline": "1479275434",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep06.Chi_Jap.HDTVrip.852X480-ZhuixinFan.mp4|236167107|5d1ca5827f3cf88cb39bf55df64e017b|h=k2bzvh2zpyljsq6b2g5xbtci3s63q5xy|/",
- "passwd": ""
- },
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:043c0fe675011d2d6a1fcba243e130864e5ab461&tr.1=http://tracker.openbittorrent.com/announce&tr.2=udp://tracker.openbittorrent.com:80/announce&tr.3=udp://tr.cili001.com:6666/announce&tr.4=http://tracker.publicbt.com/announce&tr.5=udp://open.demonii.com:1337&tr.6=udp://tracker.opentrackr.org:1337/announce&tr.7=http://tr.cili001.com:6666/announce",
- "passwd": ""
- },
- {
- "way": "9",
- "way_cn": "网盘",
- "address": "http://pan.baidu.com/s/1qYQxCBi",
- "passwd": ""
- },
- {
- "way": "12",
- "way_cn": "诚通网盘",
- "address": "http://ZiMuZuUSTV.ctfile.com/fs/aXo161179959",
- "passwd": ""
- }
- ]
- },
- {
- "itemid": "280651",
- "episode": "5",
- "name": "逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep05.Chi_Jap.HDTVrip.852X480-ZhuixinFan.mp4",
- "size": "214.19MB",
- "yyets_trans": 0,
- "dateline": "1478665773",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep05.Chi_Jap.HDTVrip.852X480-ZhuixinFan.mp4|224589979|fc95f51dd77b80abcfc8d2be7955a0fe|h=wgrml4u6cey6iiyxgtx4ml6yrzqdx7ap|/",
- "passwd": ""
- },
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:fb7c0b0c634af47bd5249873eecef81edaefbb5e&tr.1=http://tracker.openbittorrent.com/announce&tr.2=udp://tracker.openbittorrent.com:80/announce&tr.3=udp://tr.cili001.com:6666/announce&tr.4=http://tracker.publicbt.com/announce&tr.5=udp://open.demonii.com:1337&tr.6=udp://tracker.opentrackr.org:1337/announce&tr.7=http://tr.cili001.com:6666/announce",
- "passwd": ""
- },
- {
- "way": "9",
- "way_cn": "网盘",
- "address": "http://pan.baidu.com/s/1miLLJBi",
- "passwd": ""
- },
- {
- "way": "12",
- "way_cn": "诚通网盘",
- "address": "http://ZiMuZuUSTV.ctfile.com/fs/zRI160610232",
- "passwd": ""
- }
- ]
- },
- {
- "itemid": "280038",
- "episode": "4",
- "name": "逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep04.Chi_Jap.HDTVrip.852X480-ZhuixinFan.mp4",
- "size": "191.02MB",
- "yyets_trans": 0,
- "dateline": "1478060356",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep04.Chi_Jap.HDTVrip.852X480-ZhuixinFan.mp4|200298651|b9e1eef644649f12ef4684f92fdb0379|h=5cvm2z6kxke24ti7gqpgcwesvsfejkvr|/",
- "passwd": ""
- },
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:4edc1466dea534e178214ed701731f6a376996b1&tr.1=http://tracker.openbittorrent.com/announce&tr.2=udp://tracker.openbittorrent.com:80/announce&tr.3=udp://tr.cili001.com:6666/announce&tr.4=http://tracker.publicbt.com/announce&tr.5=udp://open.demonii.com:1337&tr.6=udp://tracker.opentrackr.org:1337/announce&tr.7=http://tr.cili001.com:6666/announce",
- "passwd": ""
- },
- {
- "way": "9",
- "way_cn": "网盘",
- "address": "http://pan.baidu.com/s/1hrZk4cc",
- "passwd": ""
- },
- {
- "way": "12",
- "way_cn": "诚通网盘",
- "address": "http://ZiMuZuUSTV.ctfile.com/fs/DuN160026666",
- "passwd": ""
- }
- ]
- },
- {
- "itemid": "279352",
- "episode": "3",
- "name": "逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep03.Chi_Jap.HDTVrip.852X480-ZhuixinFan.mp4",
- "size": "230.2MB",
- "yyets_trans": 0,
- "dateline": "1477463950",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep03.Chi_Jap.HDTVrip.852X480-ZhuixinFan.mp4|241387154|923cd649dd5d48840a7f1c42d8ba3700|h=czevlgexn5os63rxulctjubd2u65ukf6|/",
- "passwd": ""
- },
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:0f71cd2401057ef47cdc6ede0dcb1ebaec2e1618&tr.1=http://tracker.openbittorrent.com/announce&tr.2=udp://tracker.openbittorrent.com:80/announce&tr.3=udp://tr.cili001.com:6666/announce&tr.4=http://tracker.publicbt.com/announce&tr.5=udp://open.demonii.com:1337&tr.6=udp://tracker.opentrackr.org:1337/announce&tr.7=http://tr.cili001.com:6666/announce",
- "passwd": ""
- },
- {
- "way": "9",
- "way_cn": "网盘",
- "address": "http://pan.baidu.com/s/1gfPtce3",
- "passwd": ""
- },
- {
- "way": "12",
- "way_cn": "诚通网盘",
- "address": "http://ZiMuZuUSTV.ctfile.com/fs/Xsc159444636",
- "passwd": ""
- }
- ]
- },
- {
- "itemid": "278647",
- "episode": "2",
- "name": "逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep02.Chi_Jap.HDTVrip.852X480-ZhuixinFan.mp4",
- "size": "200.22MB",
- "yyets_trans": 0,
- "dateline": "1476861702",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep02.Chi_Jap.HDTVrip.852X480-ZhuixinFan.mp4|209947733|ed7626c02262f287b92f2472c5ed1a29|h=f5dwdur3fhjifrztundnr7psdhzzqihv|/",
- "passwd": ""
- },
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:f5e244402d31013208dafea5e0231084cd4b3ed3&tr.1=http://tracker.openbittorrent.com/announce&tr.2=udp://tracker.openbittorrent.com:80/announce&tr.3=udp://tr.cili001.com:6666/announce&tr.4=http://tracker.publicbt.com/announce&tr.5=udp://open.demonii.com:1337&tr.6=udp://tracker.opentrackr.org:1337/announce&tr.7=http://tr.cili001.com:6666/announce",
- "passwd": ""
- },
- {
- "way": "9",
- "way_cn": "网盘",
- "address": "http://pan.baidu.com/s/1o8Rgrm6",
- "passwd": ""
- },
- {
- "way": "12",
- "way_cn": "诚通网盘",
- "address": "http://ZiMuZuUSTV.ctfile.com/fs/POv158726721",
- "passwd": ""
- }
- ]
- },
- {
- "itemid": "277951",
- "episode": "1",
- "name": "逃避可耻却有用.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep01.Chi_Jap.HDTVrip.852X480-ZhuixinFanV2.mp4",
- "size": "304.92MB",
- "yyets_trans": 0,
- "dateline": "1476216447",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8.NIGERUHA.HAJIDAGA.YAKUNITATSU.Ep01.Chi_Jap.HDTVrip.852X480-ZhuixinFanV2.mp4|319730163|56c0ff1e0c00f4ae8b2636d6fa0c20ee|h=teyixuxqajhhehxiupbozfst3r5b5ytk|/",
- "passwd": ""
- },
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:e4dcb4f8fc0f99416107da0beb6697ce77b8b66b&tr.1=http://tracker.openbittorrent.com/announce&tr.2=udp://tracker.openbittorrent.com:80/announce&tr.3=udp://tr.cili001.com:6666/announce&tr.4=http://tracker.publicbt.com/announce&tr.5=udp://open.demonii.com:1337&tr.6=udp://tracker.opentrackr.org:1337/announce&tr.7=http://tr.cili001.com:6666/announce",
- "passwd": ""
- },
- {
- "way": "9",
- "way_cn": "网盘",
- "address": "http://pan.baidu.com/s/1kUTD9cj",
- "passwd": ""
- },
- {
- "way": "12",
- "way_cn": "诚通网盘",
- "address": "http://ZiMuZuUSTV.ctfile.com/fs/07i158318898",
- "passwd": ""
- }
- ]
- }
- ]
- },
- "formats": [
- "APP",
- "HR-HDTV",
- "MP4"
- ]
- }
- ]
- }
- },
- {
- "status": 1,
- "info": "OK",
- "data": {
- "info": {
- "id": 29540,
- "cnname": "无法逃避",
- "enname": "Inescapable",
- "aliasname": "无法避免",
- "channel": "movie",
- "channel_cn": "电影",
- "area": "加拿大",
- "show_type": "",
- "expire": "1610396227",
- "views": 2
- },
- "list": [
- {
- "season_num": "0",
- "season_cn": "正片",
- "items": {
- "720P": [
- {
- "itemid": "123134",
- "episode": "0",
- "name": "无法逃避.Inescapable.2012.720p.BluRay.DTS.x264-HDWing.mkv",
- "size": "4.37GB",
- "yyets_trans": 0,
- "dateline": "1364314150",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|Inescapable.2012.720p.BluRay.DTS.x264-HDWing.mkv|4687769606|FEB821FA30195604DD120F1D9036F68C|h=U3MAAOU35DLMQ6IF3A55CT6X6ZNQGFXK|/",
- "passwd": ""
- }
- ]
- }
- ]
- },
- "formats": [
- "720P"
- ]
- }
- ]
- }
- },
- {
- "status": 1,
- "info": "OK",
- "data": {
- "info": {
- "id": 37089,
- "cnname": "逃避者",
- "enname": "Shirkers",
- "aliasname": "",
- "channel": "movie",
- "channel_cn": "电影",
- "area": "美国",
- "show_type": "",
- "expire": "1610400512",
- "views": 2
- },
- "list": [
- {
- "season_num": "0",
- "season_cn": "正片",
- "items": {
- "APP": [
- {
- "itemid": "396362",
- "episode": "0",
- "name": "yyets://N=逃避者.shirkers.2018.中文字幕.WEBrip.AAC.1080p.x264-VINEnc.mp4|S=2184526436|H=cf0fd5c26b3734518ee0528fc29f688f494afe94|",
- "size": "",
- "yyets_trans": 0,
- "dateline": "1540784598",
- "files": [
- {
- "way": "115",
- "way_cn": "微云",
- "address": "https://share.weiyun.com/5mWnx0N",
- "passwd": ""
- }
- ]
- }
- ],
- "MP4": [
- {
- "itemid": "396286",
- "episode": "0",
- "name": "逃避者.shirkers.2018.中文字幕.WEBrip.AAC.1080p.x264-VINEnc.mp4",
- "size": "2.03GB",
- "yyets_trans": 0,
- "dateline": "1540724633",
- "files": [
- {
- "way": "1",
- "way_cn": "电驴",
- "address": "ed2k://|file|%E9%80%83%E9%81%BF%E8%80%85.shirkers.2018.%E4%B8%AD%E6%96%87%E5%AD%97%E5%B9%95.WEBrip.AAC.1080p.x264-VINEnc.mp4|2184526436|70674c6ceddff0f97b8d59a827d5fb0c|h=tkktn7gbrof5hp354xx6lw7g7ugkpotb|/",
- "passwd": ""
- },
- {
- "way": "2",
- "way_cn": "磁力",
- "address": "magnet:?xt=urn:btih:cc51e109b0abbb35d5db230d2a8ef1b5a6350de6&tr=udp://9.rarbg.to:2710/announce&tr=udp://9.rarbg.me:2710/announce&tr=http://tr.cili001.com:8070/announce&tr=http://tracker.trackerfix.com:80/announce&tr=udp://open.demonii.com:1337&tr=udp://tracker.opentrackr.org:1337/announce&tr=udp://p4p.arenabg.com:1337&tr=wss://tracker.openwebtorrent.com&tr=wss://tracker.btorrent.xyz&tr=wss://tracker.fastcast.nz",
- "passwd": ""
- }
- ]
- }
- ]
- },
- "formats": [
- "APP",
- "MP4"
- ]
- }
- ]
- }
- }
-]
\ No newline at end of file
diff --git a/tests/test_bot.py b/tests/test_bot.py
deleted file mode 100644
index a79afc6..0000000
--- a/tests/test_bot.py
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/local/bin/python3
-# coding: utf-8
-
-# YYeTsBot - test_bot.py
-# 1/31/21 14:05
-#
-
-__author__ = "Benny "
-
-import unittest
-import sys
-from unittest import mock
-
-from telebot.types import Message
-
-sys.path.append("../yyetsbot")
-import yyetsbot
-
-
-@mock.patch("yyetsbot.bot")
-class TestStartHandler(unittest.TestCase):
- @classmethod
- def setUpClass(cls) -> None:
- jsonstring = r'{"message_id":1,"from":{"id":108929734,"first_name":"Frank","last_name":"Wang","username":"eternnoir","is_bot":true},"chat":{"id":1734,"first_name":"F","type":"private","last_name":"Wa","username":"oir"},"date":1435296025,"text":"/start"}'
- cls.message = Message.de_json(jsonstring)
-
- @classmethod
- def tearDownClass(cls) -> None:
- pass
-
- def test_start(self, b):
- yyetsbot.send_welcome(self.message)
- self.assertEqual(1, b.send_message.call_count)
- self.assertEqual(1, b.send_chat_action.call_count)
- self.assertEqual(self.message.chat.id, b.send_message.call_args.args[0])
- self.assertIn("欢迎使用", b.send_message.call_args.args[1])
diff --git a/tests/test_fansub.py b/tests/test_fansub.py
deleted file mode 100644
index f9ff772..0000000
--- a/tests/test_fansub.py
+++ /dev/null
@@ -1,115 +0,0 @@
-# coding: utf-8
-
-import unittest
-import os
-import sys
-
-import requests_mock
-from unittest import mock
-
-sys.path.append("../yyetsbot")
-import yyetsbot as _
-
-from fansub import BaseFansub, YYeTsOnline, YYeTsOffline
-
-
-class TestBaseFunsub(unittest.TestCase):
- @classmethod
- def setUpClass(cls) -> None:
- cls.ins = BaseFansub()
- cls.cookie_jar = dict(name="hello")
- cls.ins.cookie_file = "test_cookies.dump" # generate on tests/test_cookies.dump
-
- @classmethod
- def tearDownClass(cls) -> None:
- cls().ins.redis.flushall()
- os.unlink(cls().ins.cookie_file)
-
- def test_save_cookies(self):
- self.ins.__save_cookies__(self.cookie_jar)
- exists = os.path.exists(self.ins.cookie_file)
- self.assertTrue(exists)
-
- def test_load_cookies(self):
- self.test_save_cookies()
- cookie = self.ins.__load_cookies__()
- self.assertEqual(cookie, self.cookie_jar)
-
- def test_get_from_cache(self):
- value = self.ins.__get_from_cache__("http://test.url", "__hash__")
- self.assertEqual(value, self.ins.__hash__())
-
- def test_save_to_cache(self):
- # never expire
- url = "http://test2.url"
- self.ins.__save_to_cache__(url, self.cookie_jar)
- cache_copy = self.ins.__get_from_cache__(url, "never mind method")
- self.assertEqual(cache_copy, self.cookie_jar)
-
-
-class TestYYeTsTestOnline(unittest.TestCase):
- @classmethod
- def setUpClass(cls) -> None:
- cls.ins = YYeTsOnline()
- cls.cookie_jar = dict(name="hello yyets")
- cls.ins.cookie_file = "test_cookies.dump" # generate on tests/test_cookies.dump
- cls.ins.url = "http://www.rrys2020.com/resource/1988"
- with open("data/yyets_search.html") as f:
- cls.search_html = f.read()
-
- @classmethod
- def tearDownClass(cls) -> None:
- cls().ins.redis.flushall()
- # os.unlink(cls().ins.cookie_file)
-
- def test_get_id(self):
- self.assertEqual(self.ins.id, "1988")
-
- @requests_mock.mock()
- def test_get_search_html(self, m):
- m.get('http://www.rrys2020.com/search?keyword=abc&type=resource', text=self.search_html)
- response = self.ins.__get_search_html__("abc")
- self.assertEqual(self.search_html, response)
-
- @requests_mock.mock()
- def test_search_preview(self, m):
- kw = "abc"
- m.get(f'http://www.rrys2020.com/search?keyword={kw}&type=resource', text=self.search_html)
- results = self.ins.search_preview(kw)
- results.pop("source")
- for name in results.values():
- self.assertIn(kw, name.lower())
-
- # TODO....
- def test_search_result(self):
- pass
-
-
-class TestYYeTsTestOffline(unittest.TestCase):
- @classmethod
- def setUpClass(cls) -> None:
- cls.ins = YYeTsOffline(db="test")
-
- @classmethod
- def tearDownClass(cls) -> None:
- cls().ins.mongo.close()
-
- def test_search_preview(self):
- kw = "逃避"
- results = self.ins.search_preview(kw)
- self.assertEqual(results["source"], self.ins.label)
- results.pop("source")
- self.assertEqual(3, len(results))
- for name in results.values():
- self.assertIn(kw, name)
-
- def test_search_result(self):
- url = "http://www.rrys2020.com/resource/34812"
- results = self.ins.search_result(url)
- self.assertIn(str(results['all']['data']['info']['id']), url)
- self.assertIn("逃避可耻", results["cnname"])
- self.assertIn("34812", results["share"])
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/yyetsweb/Makefile b/yyetsweb/Makefile
index dc7bd79..9e01b63 100644
--- a/yyetsweb/Makefile
+++ b/yyetsweb/Makefile
@@ -8,7 +8,8 @@ default:
asset:
- go-bindata -o assets.go resource.html index.html search.html js/... css/... fonts/... img/...
+ cd templates; go-bindata -o assets.go resource.html index.html search.html js/... css/... fonts/... img/...
+ mv templates/assets.go ./
dev:
go-bindata -o assets.go index.html
diff --git a/yyetsweb/Mongo.py b/yyetsweb/Mongo.py
index 4385657..6403f47 100644
--- a/yyetsweb/Mongo.py
+++ b/yyetsweb/Mongo.py
@@ -7,13 +7,17 @@
__author__ = "Benny "
+import base64
import contextlib
+import json
import logging
import os
import pathlib
+import random
import re
import sys
import time
+import uuid
from datetime import date, timedelta
from http import HTTPStatus
from urllib.parse import unquote
@@ -24,14 +28,17 @@ from bs4 import BeautifulSoup
from bson.objectid import ObjectId
from passlib.handlers.pbkdf2 import pbkdf2_sha256
from retry import retry
+from tqdm import tqdm
from database import (AnnouncementResource, BlacklistResource, CaptchaResource,
- CommentChildResource, CommentNewestResource,
+ CategoryResource, CommentChildResource,
+ CommentNewestResource, CommentReactionResource,
CommentResource, DoubanReportResource, DoubanResource,
- GrafanaQueryResource, MetricsResource, NameResource,
- OtherResource, Redis, ResourceResource, TopResource,
- UserLikeResource, UserResource)
-from utils import ts_date
+ GrafanaQueryResource, LikeResource, MetricsResource,
+ NameResource, NotificationResource, OtherResource, Redis,
+ ResourceLatestResource, ResourceResource, TopResource,
+ UserEmailResource, UserResource)
+from utils import send_mail, ts_date
lib_path = pathlib.Path(__file__).parent.parent.joinpath("yyetsbot").resolve().as_posix()
sys.path.append(lib_path)
@@ -147,6 +154,7 @@ class CommentMongoResource(CommentResource, Mongo):
.sort("_id", pymongo.DESCENDING).limit(self.inner_size).skip((self.inner_page - 1) * self.inner_size)
children_data = list(children_data)
self.get_user_group(children_data)
+ self.add_reactions(children_data)
item["children"] = []
if children_data:
@@ -162,6 +170,16 @@ class CommentMongoResource(CommentResource, Mongo):
group = user.get("group", ["user"])
comment["group"] = group
+ def add_reactions(self, data):
+ for comment in data:
+ cid = comment.get("id") or comment.get("_id")
+ cid = str(cid)
+ reactions = self.db["reactions"].find_one({"comment_id": cid},
+ projection={"_id": False, "comment_id": False}) or {}
+ for verb, users in reactions.items():
+ if users:
+ comment.setdefault("reactions", []).append({"verb": verb, "users": users})
+
def get_comment(self, resource_id: int, page: int, size: int, **kwargs) -> dict:
self.inner_page = kwargs.get("inner_page", 1)
self.inner_size = kwargs.get("inner_size", 5)
@@ -174,6 +192,8 @@ class CommentMongoResource(CommentResource, Mongo):
self.find_children(data)
self.convert_objectid(data)
self.get_user_group(data)
+ self.add_reactions(data)
+
return {
"data": data,
"count": count,
@@ -227,6 +247,30 @@ class CommentMongoResource(CommentResource, Mongo):
)
returned["status_code"] = HTTPStatus.CREATED
returned["message"] = "评论成功"
+
+ # notification
+ if parent_comment_id:
+ # find username
+
+ self.db["notification"].find_one_and_update(
+ {"username": exists["username"]},
+ {"$push": {"unread": inserted_id}},
+ upsert=True
+ )
+ # send email
+ # TODO unsubscribe
+ parent_comment = self.db["comment"].find_one({"_id": ObjectId(parent_comment_id)})
+ if resource_id == 233:
+ link = f"https://yyets.dmesg.app/discuss#{parent_comment_id}"
+ else:
+ link = f"https://yyets.dmesg.app/resource?id={resource_id}#{parent_comment_id}"
+ user_info = self.db["users"].find_one({"username": parent_comment["username"], "email.verified": True})
+ if user_info:
+ subject = "[人人影视下载分享站] 你的评论有了新的回复"
+ pt_content = content.split("")[-1]
+ body = f"{username} 您好, 你的评论 {parent_comment['content']} 有了新的回复: {pt_content}" \
+ f" 你可以点此链接 查看 请勿回复此邮件"
+ send_mail(user_info["email"]["address"], subject, body)
return returned
def delete_comment(self, comment_id):
@@ -252,29 +296,36 @@ class CommentMongoResource(CommentResource, Mongo):
return returned
- def react_comment(self, username, comment_id, verb):
- if verb not in ("like", "dislike"):
- return {"status": False,
- "message": "verb could only be like or dislike",
- "status_code": HTTPStatus.BAD_REQUEST}
- result = self.db["users"].find_one({"username": username, f"comments_{verb}": {"$in": [comment_id]}})
- if result:
- return {"status": False, "message": "too many reactions", "status_code": HTTPStatus.UNPROCESSABLE_ENTITY}
+class CommentReactionMongoResource(CommentReactionResource, Mongo):
+ def react_comment(self, username, data):
+ # {"comment_id":"da23","😊":["user1","user2"]}
+ comment_id = data["comment_id"]
+ verb = data["verb"]
+ method = data["method"]
if not self.db["comment"].find_one({"_id": ObjectId(comment_id)}):
return {"status": False, "message": "Where is your comments?", "status_code": HTTPStatus.NOT_FOUND}
- self.db["users"].update_one({"username": username},
- {"$push": {f"comments_{verb}": comment_id}}
- )
-
- self.db["comment"].update_one({"_id": ObjectId(comment_id)},
- {"$inc": {verb: 1}}
- )
-
+ if method == "POST":
+ self.db["reactions"].update_one({"comment_id": comment_id},
+ {
+ "$addToSet": {verb: username}
+ },
+ upsert=True
+ )
+ code = HTTPStatus.CREATED
+ elif method == "DELETE":
+ self.db["reactions"].update_one({"comment_id": comment_id},
+ {
+ "$pull": {verb: username}
+ }
+ )
+ code = HTTPStatus.ACCEPTED
+ else:
+ code = HTTPStatus.BAD_REQUEST
return {"status": True, "message": "success",
- "status_code": HTTPStatus.CREATED}
+ "status_code": code}
class CommentChildMongoResource(CommentChildResource, CommentMongoResource, Mongo):
@@ -450,6 +501,69 @@ class ResourceMongoResource(ResourceResource, Mongo):
return returned
+ def patch_resource(self, new_data: dict):
+ rid = new_data["resource_id"]
+ new_data.pop("resource_id")
+ old_data = self.db["yyets"].find_one(
+ {"data.info.id": rid},
+ )
+ new_data["season_cn"] = self.convert_season(new_data["season_num"])
+ # 1. totally empty resource:
+ if len(old_data["data"]["list"]) == 0:
+ new_data["season_cn"] = self.convert_season(new_data["season_num"])
+ old_data["data"]["list"].append(new_data)
+ else:
+ for season in old_data["data"]["list"]:
+ if new_data["season_num"] in [season["season_num"], int(season["season_num"])]:
+ user_format = new_data["formats"][0]
+ for u in new_data["items"][user_format]:
+ season["items"][user_format].append(u)
+
+ self.db["yyets"].find_one_and_replace(
+ {"data.info.id": rid},
+ old_data
+ )
+
+ def add_resource(self, new_data: dict):
+ rid = self.get_appropriate_id()
+ new_data["data"]["info"]["id"] = rid
+ self.db["yyets"].insert_one(new_data)
+ return {"status": True, "message": "success", "id": rid}
+
+ def delete_resource(self, data: dict):
+ rid = data["resource_id"]
+ meta = data.get("meta")
+ if meta:
+ db_data = self.db["yyets"].find_one({"data.info.id": rid})
+ for season in db_data["data"]["list"]:
+ for episode in season["items"].values():
+ for v in episode:
+ if v["episode"] == meta["episode"] and v["name"] == meta["name"] and \
+ v["size"] == meta["size"] and v["dateline"] == meta["dateline"]:
+ episode.remove(v)
+ # replace it
+ self.db["yyets"].find_one_and_replace({"data.info.id": rid}, db_data)
+
+ else:
+ self.db["yyets"].delete_one({"data.info.id": rid})
+
+ def get_appropriate_id(self):
+ col = self.db["yyets"]
+ random_id = random.randint(50000, 80000)
+ data = col.find_one({"data.info.id": random_id}, projection={"_id": True})
+ if data:
+ return self.get_appropriate_id()
+ else:
+ return random_id
+
+ @staticmethod
+ def convert_season(number: [int, str]):
+ pass
+ if number in (0, "0"):
+ return "正片"
+ else:
+ return f"第{number}季"
+
class TopMongoResource(TopResource, Mongo):
projection = {'_id': False, 'data.info': True}
@@ -479,7 +593,7 @@ class TopMongoResource(TopResource, Mongo):
return all_data
-class UserLikeMongoResource(UserLikeResource, Mongo):
+class LikeMongoResource(LikeResource, Mongo):
projection = {'_id': False, 'data.info': True}
def get_user_like(self, username: str) -> list:
@@ -488,35 +602,6 @@ class UserLikeMongoResource(UserLikeResource, Mongo):
.sort("data.info.views", pymongo.DESCENDING)
return list(data)
-
-class UserMongoResource(UserResource, Mongo):
- def login_user(self, username: str, password: str, ip: str, browser: str) -> dict:
- data = self.db["users"].find_one({"username": username})
- returned_value = {"status_code": 0, "message": ""}
-
- if data:
- # try to login
- stored_password = data["password"]
- if pbkdf2_sha256.verify(password, stored_password):
- returned_value["status_code"] = HTTPStatus.OK
- else:
- returned_value["status_code"] = HTTPStatus.FORBIDDEN
- returned_value["message"] = "用户名或密码错误"
-
- else:
- hash_value = pbkdf2_sha256.hash(password)
- try:
- self.db["users"].insert_one(dict(username=username, password=hash_value,
- date=ts_date(), ip=ip, browser=browser)
- )
- returned_value["status_code"] = HTTPStatus.CREATED
-
- except Exception as e:
- returned_value["status_code"] = HTTPStatus.INTERNAL_SERVER_ERROR
- returned_value["message"] = str(e)
-
- return returned_value
-
def add_remove_fav(self, resource_id: int, username: str) -> dict:
returned = {"status_code": 0, "message": ""}
like_list: list = self.db["users"].find_one({"username": username}).get("like", [])
@@ -533,9 +618,58 @@ class UserMongoResource(UserResource, Mongo):
self.db["users"].update_one({"username": username}, {'$set': value})
return returned
+
+class UserMongoResource(UserResource, Mongo):
+ def login_user(self, username: str, password: str, captcha: str, captcha_id: str, ip: str, browser: str) -> dict:
+ # verify captcha in the first place.
+ redis = Redis().r
+ correct_captcha = redis.get(captcha_id)
+ if correct_captcha is None:
+ return {"status_code": HTTPStatus.BAD_REQUEST, "message": "验证码已过期", "status": False}
+ elif correct_captcha.lower() == captcha.lower():
+ redis.expire(captcha_id, 0)
+ else:
+ return {"status_code": HTTPStatus.FORBIDDEN, "message": "验证码错误", "status": False}
+ # check user account is locked.
+
+ data = self.db["users"].find_one({"username": username}) or {}
+ if data.get("status", {}).get("disable"):
+ return {"status_code": HTTPStatus.FORBIDDEN,
+ "status": False,
+ "message": data.get("status", {}).get("reason")}
+
+ returned_value = {"status_code": 0, "message": ""}
+
+ if data:
+ # try to login
+ stored_password = data["password"]
+ if pbkdf2_sha256.verify(password, stored_password):
+ returned_value["status_code"] = HTTPStatus.OK
+ else:
+ returned_value["status_code"] = HTTPStatus.FORBIDDEN
+ returned_value["message"] = "用户名或密码错误"
+
+ else:
+ # register
+ hash_value = pbkdf2_sha256.hash(password)
+ try:
+ self.db["users"].insert_one(dict(username=username, password=hash_value,
+ date=ts_date(), ip=ip, browser=browser)
+ )
+ returned_value["status_code"] = HTTPStatus.CREATED
+
+ except Exception as e:
+ returned_value["status_code"] = HTTPStatus.INTERNAL_SERVER_ERROR
+ returned_value["message"] = str(e)
+
+ returned_value["username"] = data.get("username")
+ returned_value["group"] = data.get("group", ["user"])
+ return returned_value
+
def get_user_info(self, username: str) -> dict:
projection = {"_id": False, "password": False}
data = self.db["users"].find_one({"username": username}, projection)
+ data.update(group=data.get("group", ["user"]))
return data
def update_user_last(self, username: str, now_ip: str) -> None:
@@ -543,6 +677,43 @@ class UserMongoResource(UserResource, Mongo):
{"$set": {"lastDate": (ts_date()), "lastIP": now_ip}}
)
+ def update_user_info(self, username: str, data: dict) -> dict:
+ redis = Redis().r
+ valid_fields = ["email"]
+ valid_data = {}
+ for field in valid_fields:
+ if data.get(field):
+ valid_data[field] = data[field]
+
+ if valid_data.get("email") and not re.findall(r"\S@\S", valid_data.get("email")):
+ return {"status_code": HTTPStatus.BAD_REQUEST, "status": False, "message": "email format error "}
+ elif valid_data.get("email"):
+ # rate limit
+ user_email = valid_data.get("email")
+ timeout_key = f"timeout-{user_email}"
+ if redis.get(timeout_key):
+ return {"status_code": HTTPStatus.TOO_MANY_REQUESTS,
+ "status": False,
+ "message": f"try again in {redis.ttl(timeout_key)}s"}
+
+ verify_code = random.randint(10000, 99999)
+ valid_data["email"] = {"verified": False, "address": user_email}
+ # send email confirm
+ subject = "[人人影视下载分享站] 请验证你的邮箱"
+ body = f"{username} 您好, 请输入如下验证码完成你的邮箱认证。验证码有效期为24小时。 " \
+ f"如果您未有此请求,请忽略此邮件。 验证码: {verify_code}"
+
+ redis.set(timeout_key, username, ex=1800)
+ redis.hset(user_email, mapping={"code": verify_code, "wrong": 0})
+ redis.expire(user_email, 24 * 3600)
+ send_mail(user_email, subject, body)
+
+ self.db["users"].update_one(
+ {"username": username},
+ {"$set": valid_data}
+ )
+ return {"status_code": HTTPStatus.CREATED, "status": True, "message": "success"}
+
class DoubanMongoResource(DoubanResource, Mongo):
@@ -663,3 +834,164 @@ class DoubanReportMongoResource(DoubanReportResource, Mongo):
{"resource_id": resource_id},
{"$push": {"content": content}}, upsert=True).matched_count
return dict(count=count)
+
+
+class NotificationMongoResource(NotificationResource, Mongo):
+ def get_notification(self, username, page, size):
+ # .sort("_id", pymongo.DESCENDING).limit(size).skip((page - 1) * size)
+ notify = self.db["notification"].find_one({"username": username}, projection={"_id": False})
+ if not notify:
+ return {}
+
+ # size is shared
+ unread = notify.get("unread", [])
+ id_list = []
+ for item in unread[(page - 1) * size:size * page]:
+ id_list.append(item)
+ notify["unread_item"] = self.get_content(id_list)
+
+ size = size - len(unread)
+ read = notify.get("read", [])
+ id_list = []
+ for item in read[(page - 1) * size:size * page]:
+ id_list.append(item)
+ notify["read_item"] = self.get_content(id_list)
+
+ notify.pop("unread", None)
+ notify.pop("read", None)
+ notify["unread_count"] = len(unread)
+ notify["read_count"] = len(read)
+ return notify
+
+ def get_content(self, id_list):
+ comments = self.db["comment"].find({"_id": {"$in": id_list}},
+ projection={"ip": False, "parent_id": False}
+ ).sort("_id", pymongo.DESCENDING)
+ comments = list(comments)
+ for comment in comments:
+ comment["id"] = str(comment["_id"])
+ comment.pop("_id")
+ reply_to_id = re.findall(r'"(.*)"', comment["content"])[0]
+ rtc = self.db["comment"].find_one({"_id": ObjectId(reply_to_id)},
+ projection={"content": True, "_id": False})
+ comment["reply_to_content"] = rtc["content"]
+
+ return comments
+
+ def update_notification(self, username, verb, comment_id):
+ if verb == "read":
+ v1, v2 = "read", "unread"
+ else:
+ v1, v2 = "unread", "read"
+ self.db["notification"].find_one_and_update(
+ {"username": username},
+ {
+ "$push": {v1: ObjectId(comment_id)},
+ "$pull": {v2: ObjectId(comment_id)}
+ }
+ )
+
+ return {}
+
+
+class UserEmailMongoResource(UserEmailResource, Mongo):
+ def verify_email(self, username, code):
+ r = Redis().r
+ email = self.db["users"].find_one({"username": username})["email"]["address"]
+ verify_data = r.hgetall(email)
+ wrong_count = int(verify_data["wrong"])
+ MAX = 10
+ if wrong_count >= MAX:
+ self.db["users"].update_one({"username": username},
+ {"$set": {"status": {"disable": True, "reason": "verify email crack"}}}
+ )
+ return {"status": False, "status_code": HTTPStatus.FORBIDDEN, "message": "Account locked. Please stay away"}
+ correct_code = verify_data["code"]
+
+ if correct_code == code:
+ r.expire(email, 0)
+ r.expire(f"timeout-{email}", 0)
+ self.db["users"].update_one({"username": username},
+ {"$set": {"email.verified": True}}
+ )
+ return {"status": True, "status_code": HTTPStatus.CREATED, "message": "success"}
+ else:
+ r.hset(email, "wrong", wrong_count + 1)
+ return {"status": False,
+ "status_code": HTTPStatus.FORBIDDEN,
+ "message": f"verification code is incorrect. You have {MAX - wrong_count} attempts remaining"}
+
+
+class CategoryMongoResource(CategoryResource, Mongo):
+ def get_category(self, query: dict):
+ page, size, douban = query["page"], query["size"], query["douban"]
+ query.pop("page")
+ query.pop("size")
+ query.pop("douban")
+ query_dict = {}
+ for key, value in query.items():
+ query_dict[f"data.info.{key}"] = value
+ logging.info("Query dict %s", query_dict)
+ projection = {"_id": False, "data.list": False}
+ data = self.db["yyets"].find(query_dict, projection=projection).limit(size).skip((page - 1) * size)
+ count = self.db["yyets"].count_documents(query_dict)
+ f = []
+ for item in data:
+ if douban:
+ douban_data = self.db["douban"].find_one({"resourceId": item["data"]["info"]["id"]},
+ projection=projection)
+ if douban_data:
+ douban_data["posterData"] = base64.b64encode(douban_data["posterData"]).decode("u8")
+ item["data"]["info"]["douban"] = douban_data
+ else:
+ item["data"]["info"]["douban"] = {}
+ f.append(item["data"]["info"])
+ return dict(data=f, count=count)
+
+
+class ResourceLatestMongoResource(ResourceLatestResource, Mongo):
+ @staticmethod
+ def get_latest_resource() -> dict:
+ redis = Redis().r
+ key = "latest-resource"
+ latest = redis.get(key)
+ if latest:
+ logging.info("Cache hit for latest resource")
+ latest = json.loads(latest)
+ latest["data"] = latest["data"][:100]
+ else:
+ logging.warning("Cache miss for latest resource")
+ latest = ResourceLatestMongoResource().query_db()
+ redis.set(key, json.dumps(latest, ensure_ascii=False))
+ return latest
+
+ def query_db(self) -> dict:
+ col = self.db["yyets"]
+ projection = {"_id": False, "status": False, "info": False}
+ episode_data = {}
+ for res in tqdm(col.find(projection=projection), total=col.count()):
+ for season in res["data"]["list"]:
+ for item in season["items"].values():
+ for single in item:
+ ts = single["dateline"]
+ res_name = res["data"]["info"]["cnname"]
+ name = "{}-{}".format(res_name, single["name"])
+ size = single["size"]
+ episode_data[name] = {"timestamp": ts, "size": size, "resource_id": res["data"]["info"]["id"],
+ "res_name": res_name, "date": ts_date(int(ts))}
+
+ sorted_res: list = sorted(episode_data.items(), key=lambda x: x[1]["timestamp"], reverse=True)
+ limited_res = dict(sorted_res[:100])
+ ok = []
+ for k, v in limited_res.items():
+ t = {"name": k}
+ t.update(v)
+ ok.append(t)
+ return dict(data=ok)
+
+ def refresh_latest_resource(self):
+ redis = Redis().r
+ logging.info("Getting new resources...")
+ latest = self.query_db()
+ redis.set("latest-resource", json.dumps(latest, ensure_ascii=False))
+ logging.info("latest-resource data refreshed.")
diff --git a/yyetsweb/README.md b/yyetsweb/README.md
index bbc922b..ed4cdd7 100644
--- a/yyetsweb/README.md
+++ b/yyetsweb/README.md
@@ -30,10 +30,26 @@ mongorestore --gzip --archive=yyets_mongo.gz
use zimuzu;
db.getCollection('yyets').createIndex({"data.info.id": 1});
-
db.getCollection('yyets').createIndex({"data.info.views" : -1});
-
db.getCollection('yyets').createIndex({"data.info.area" : 1});
-
db.getCollection('yyets').getIndexes();
+
+db.getCollection('douban').createIndex({"resourceId" : 1});
+db.getCollection('douban').getIndexes();
+
+db.getCollection('users').createIndex({"username" : 1}, { unique: true });
+db.getCollection('users').getIndexes();
+
+db.getCollection('comment').createIndex({"resource_id" : 1});
+db.getCollection('comment').getIndexes();
+
+db.getCollection('reactions').createIndex({"comment_id" : 1});
+db.getCollection('reactions').getIndexes();
+
+db.getCollection('metrics').createIndex({"date" : 1});
+db.getCollection('metrics').getIndexes();
+
+db.getCollection('notification').createIndex({"username" : 1});
+db.getCollection('notification').getIndexes();
+
```
diff --git a/yyetsweb/craw_data/douban.py b/yyetsweb/craw_data/douban.py
deleted file mode 100644
index edac331..0000000
--- a/yyetsweb/craw_data/douban.py
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/local/bin/python3
-# coding: utf-8
-
-# YYeTsBot - douban.py
-# 7/10/21 22:59
-#
-
-__author__ = "Benny "
-
-import re
-
-from bs4 import BeautifulSoup
-
-from Mongo import DoubanMongoResource
-
-with open("douban_detail.html") as f:
- detail_html = f.read()
-soup = BeautifulSoup(detail_html, 'html.parser')
-
-
-douban = DoubanMongoResource()
-rid = 27238
-douban.find_douban(rid)
diff --git a/yyetsweb/craw_data/douban_detail.html b/yyetsweb/craw_data/douban_detail.html
deleted file mode 100644
index 1020d67..0000000
--- a/yyetsweb/craw_data/douban_detail.html
+++ /dev/null
@@ -1,3151 +0,0 @@
-
-
-
-
-
-
-
-
-
- 遇见恶魔 (豆瓣)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 遇见恶魔 Meeting Evil
- (2012)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 喜欢这部电影的人也喜欢
- · · · · · ·
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 遇见恶魔的话题 · · · · · ·
- ( 全部 条 )
-
-
-
-
-
-
-
-
-
-
-
-
-
什么是话题
-
-
无论是一部作品、一个人,还是一件事,都往往可以衍生出许多不同的话题。将这些话题细分出来,分别进行讨论,会有更多收获。
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 遇见恶魔的影评 · · · · · ·
- ( 全部 14 条 )
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 2014-01-26 12:59:01
-
-
-
-
-
-
-
-
-
-
-
-
- 杀手是主角老婆雇佣的,而主角最后会杀了他的老婆。 不得不说导演的拍摄手法很隐晦,不是简单就能体会到的。影片开始主角就已经知道老婆和那个修游泳池的偷情,镜头里主角纠结的趴在铁锹上,看着那个并不是新挖的坑,给结尾做了很好的铺垫。 塞缪尔杰克逊饰演的杀...
-
- (
展开 )
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 不笑生李奕
-
-
-
- 2018-03-19 11:16:31
-
-
-
-
-
-
-
-
-
-
-
-
这篇影评可能有剧透
-
- Samuel L. Jackson是我个人认为最好的演员,因为他真的是演什么像什么,而且几乎每一部电影都是可圈可点的好作品,而且经常有非常经典的形象。被解放的姜戈里面狡猾卑微,充满奴性的老管家,低俗小说里大彻大悟的黑帮杀手,星战前传里正气凛然的长老,甚至可以说他就是昆丁的...
-
- (
展开 )
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 迪斯维瑟
-
-
-
- 2013-07-16 11:33:50
-
-
-
-
-
-
-
-
-
-
-
-
- 好片 故事递进的很有层次,一层层的铺垫,在结局打开所有谜团 牵着狗的小女孩是神来之笔 画龙点睛的恰到好处,各种暗示就像是在玩解谜游戏 塞缪尔·杰克逊扮演的杀手不断地告诉主角: 我和你很像 直到恶魔被杀,灯被关掉 男主角吹起了杀手的口哨............... 哇~~~~赞!!!
-
- (
展开 )
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 米修
-
-
- 2020-10-14 22:20:44
-
-
-
-
-
-
-
-
-
-
-
-
- 2008金融危机下,男主没有办法保住房产、工作,无奈之下想雇佣杀手杀死自己,用保险金帮助妻儿度过难关,保住住宅,大环境之下,妻子出轨、自己出轨都抵不过让儿女活下去欲望,凶手告诉男主是妻子雇凶的时候,说是妻子让别人联系杀手的,其实是男主自己,杀手误认为而已,因为...
-
- (
展开 )
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- '﹎|零¨ヰ
-
-
-
- 2020-02-08 19:31:07
-
-
-
-
-
-
-
-
-
-
-
-
- "Be sober,be vigilant ;because your adversary the Devil walks abot like a roaring lion,seeking whom he may devour." 要清醒 要警觉 因为你们的仇敌魔鬼如同吼叫的狮子 到处游荡 寻找可下手的人——“彼得前书” 5:8 这种人本性上就是希望被愚弄 一旦发现这一点就能让他...
-
- (
展开 )
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 尽在今夜
-
-
-
- 2012-04-11 15:30:39
-
-
-
-
-
-
-
-
-
-
-
-
- 草泥马,这么认真地拍一部烂片。到昨晚为之IMDB上竟没有一个人愿意评分,还不允许给零分,老子被迫给了1分,都觉得给高了。看完刚好午夜宣布不厚落马,真是活见鬼了,恶魔加噩梦。
-
- (
展开 )
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 糯隐糯现段朕朕
-
-
-
- 2016-01-22 17:21:42
-
-
-
-
-
-
-
-
-
-
-
-
- 杀手跟主角有一样的遭遇,小女孩和狗是杀手的幻觉,这是杀手当杀手以前的生活, 狗就是他自己,也是他眼里的男一, 女孩就是他以前的老婆,也是男一的老婆, build a new life, 他帮主角build a new life, __________________________________________ 警察第一次到...
-
- (
展开 )
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- X白
-
-
-
- 2015-05-21 22:23:35
-
-
-
-
-
-
-
-
-
-
-
-
- 开始觉得没什么意思,纯粹因为神盾局长才看的片子,快进看了一部分突然发现这片子很有趣又从头看了一遍。疯子杀手是碰见不爽的就干掉,包括奚落男主的人。颇有点水浒传里好汉们一言不合,拔刀相向的意思。男主从一个小受,不断受到刺激,最后骗走警察,关灯以后吹起杀手的口哨...
-
- (
展开 )
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 毛毛熊
-
-
-
- 2013-02-04 10:44:22
-
-
-
-
-
-
-
-
-
-
-
-
这篇影评可能有剧透
-
- 个人与个人的理解能力都会不同,我谅解那些骂个没完还给一分的哥们姐们。如果你不是影迷,偶尔看一看欧美片,对这个片子看不懂给一分的没啥。老美的思维就是很跳跃的,在国际比赛里天朝孩子的想象力评分为垫底,相信大伙在网上看到过,这是教育的问题吧。这个片子开始让人很纳...
-
- (
展开 )
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 那一年
-
-
-
- 2017-01-19 20:30:38
-
-
-
-
-
-
-
-
-
-
-
-
这篇影评可能有剧透
-
- 难道没人想到是男主买凶杀自己吗?男主对杀手说过,他爱自己妻子,谁伤害他妻子他会杀死谁。 因为清楚知道自己彻底破产,能够为家人摆脱困境的唯一办法就是自己的人寿保险。为了所爱的家人他愿意牺牲自己,为此,他为家人安排好了后路,这也是影片一开始他在面临各种财务绝境时...
-
- (
展开 )
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
-
- 更多影评
- 14篇
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 在哪儿看这部电影
- · · · · · ·
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 以下片单推荐
- · · · · · ·
-
- (
- 全部
- )
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 谁在看这部电影
- · · · · · ·
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
陈十二
-
- 7月6日
- 想看
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
订阅遇见恶魔的评论:
- feed: rss 2.0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/yyetsweb/craw_data/douban_search.html b/yyetsweb/craw_data/douban_search.html
deleted file mode 100644
index 615ac4d..0000000
--- a/yyetsweb/craw_data/douban_search.html
+++ /dev/null
@@ -1,642 +0,0 @@
-
-
-
-
-
-
-
-
-
- 搜索: 逃避可耻却有用
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
搜索: 逃避可耻却有用
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 相关电影:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- [电影]
- 逃避虽可耻但有用
- 可播放
-
-
-
-
-
- 8.4
- (154381人评价)
-
- 原名:逃げるは恥だが役に立つ / 金子文纪 / 新垣结衣 / 2016
-
-
-
-
森山实栗(新垣结衣 饰)自研究生毕业之后就一直仕途不顺,最近更是惨遭解雇,处于“无业游民”的状态之下,日子过得十分凄惨。经由父亲的介绍,无处可去的实栗来到了名...
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 8.4
- (26905人评价)
-
- 原名:逃げるは恥だが役に立つ ガンバレ人類! 新春スペシャル!! / 金子文纪 / 新垣结衣 / 2021
-
-
-
-
森山实栗(新垣结衣 饰)和津崎平匡(星野源 饰)之间的事实婚姻愉快而又轻松的进行着,在日常生活的摩擦中,他们逐渐接受了彼此不同的生活步调,关系越来越亲密融洽。...
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 8.9
- (1179人评价)
-
- 原名:逃げるは恥だが役に立つ ムズキュン!特別編 / 金子文纪 / 新垣结衣 / 2020
-
-
-
-
本特别篇为2016年10月首播的《逃避虽可耻但有用》原剧的再剪辑版,在首播版的基础上加入了一些未公开镜头。
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 豆瓣还没有这个电影,我来添加
- · · · · · ·
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/yyetsweb/database.py b/yyetsweb/database.py
index 68129a8..a1278b7 100644
--- a/yyetsweb/database.py
+++ b/yyetsweb/database.py
@@ -20,6 +20,7 @@ import fakeredis
import redis
from captcha.image import ImageCaptcha
+captcha_ex = 60 * 10
predefined_str = re.sub(r"[1l0oOI]", "", string.ascii_letters + string.digits)
@@ -114,10 +115,7 @@ class OtherResource():
class UserResource:
- def login_user(self, username: str, password: str, ip: str, browser: str) -> dict:
- pass
-
- def add_remove_fav(self, resource_id: int, username: str) -> str:
+ def login_user(self, username: str, password: str, captcha: str, captcha_id: str, ip: str, browser: str) -> dict:
pass
def get_user_info(self, username: str) -> dict:
@@ -126,6 +124,9 @@ class UserResource:
def update_user_last(self, username: str, now_ip: str) -> None:
pass
+ def update_user_info(self, username: str, data: dict) -> dict:
+ pass
+
class TopResource:
@@ -136,10 +137,13 @@ class TopResource:
pass
-class UserLikeResource:
+class LikeResource:
def get_user_like(self, username: str) -> list:
pass
+ def add_remove_fav(self, resource_id: int, username: str) -> str:
+ pass
+
class NameResource:
def get_names(self, is_readable: [str, bool]) -> dict:
@@ -157,7 +161,10 @@ class CommentResource:
def delete_comment(self, comment_id: str):
pass
- def react_comment(self, username, comment_id, verb):
+
+class CommentReactionResource:
+
+ def react_comment(self, username, data):
pass
@@ -178,7 +185,7 @@ class CaptchaResource:
chars = "".join([random.choice(predefined_str) for _ in range(4)])
image = ImageCaptcha()
data = image.generate(chars)
- self.redis.r.set(captcha_id, chars, ex=60 * 10)
+ self.redis.r.set(captcha_id, chars, ex=captcha_ex)
return f"data:image/png;base64,{base64.b64encode(data.getvalue()).decode('ascii')}"
def verify_code(self, user_input, captcha_id) -> dict:
@@ -207,6 +214,15 @@ class ResourceResource:
def search_resource(self, keyword: str) -> dict:
pass
+ def patch_resource(self, data: dict):
+ pass
+
+ def add_resource(self, data: dict):
+ pass
+
+ def delete_resource(self, data: dict):
+ pass
+
class GrafanaQueryResource:
def get_grafana_data(self, date_series) -> str:
@@ -242,3 +258,30 @@ class DoubanReportResource:
def get_error(self) -> dict:
pass
+
+
+class NotificationResource:
+
+ def get_notification(self, username, page, size):
+ pass
+
+ def update_notification(self, username, verb, comment_id):
+ pass
+
+
+class UserEmailResource:
+
+ def verify_email(self, username, code):
+ pass
+
+
+class CategoryResource:
+
+ def get_category(self, query: dict):
+ pass
+
+
+class ResourceLatestResource:
+ @staticmethod
+ def get_latest_resource() -> dict:
+ pass
diff --git a/yyetsweb/fonts/test.txt b/yyetsweb/fonts/test.txt
deleted file mode 100644
index 274c005..0000000
--- a/yyetsweb/fonts/test.txt
+++ /dev/null
@@ -1 +0,0 @@
-1234
\ No newline at end of file
diff --git a/yyetsweb/go.mod b/yyetsweb/go.mod
index 370f8ca..fd83f3e 100644
--- a/yyetsweb/go.mod
+++ b/yyetsweb/go.mod
@@ -4,6 +4,7 @@ go 1.16
require (
github.com/gin-gonic/gin v1.7.2
+ github.com/go-bindata/go-bindata v3.1.2+incompatible // indirect
github.com/mattn/go-sqlite3 v1.14.8
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 // indirect
)
diff --git a/yyetsweb/go.sum b/yyetsweb/go.sum
index 1a3484d..9a4e0c6 100644
--- a/yyetsweb/go.sum
+++ b/yyetsweb/go.sum
@@ -5,6 +5,8 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.7.2 h1:Tg03T9yM2xa8j6I3Z3oqLaQRSmKvxPd6g/2HJ6zICFA=
github.com/gin-gonic/gin v1.7.2/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
+github.com/go-bindata/go-bindata v3.1.2+incompatible h1:5vjJMVhowQdPzjE1LdxyFF7YFTXg5IgGVW4gBr5IbvE=
+github.com/go-bindata/go-bindata v3.1.2+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
diff --git a/yyetsweb/handler.py b/yyetsweb/handler.py
index 0d690d8..d0c6eb9 100644
--- a/yyetsweb/handler.py
+++ b/yyetsweb/handler.py
@@ -7,6 +7,7 @@
__author__ = "Benny "
+import contextlib
import importlib
import json
import logging
@@ -14,6 +15,7 @@ import os
import re
import sys
import time
+import uuid
from concurrent.futures import ThreadPoolExecutor
from datetime import date, timedelta
from hashlib import sha1
@@ -35,6 +37,9 @@ else:
logging.info("%s Running with %s. %s", "#" * 10, adapter, "#" * 10)
+static_path = os.path.join(os.path.dirname(__file__), 'templates')
+index = os.path.join(static_path, "index.html")
+
class BaseHandler(web.RequestHandler):
executor = ThreadPoolExecutor(200)
@@ -43,6 +48,9 @@ class BaseHandler(web.RequestHandler):
def __init__(self, application, request, **kwargs):
super().__init__(application, request, **kwargs)
+ self.json = {}
+ with contextlib.suppress(ValueError):
+ self.json: dict = json.loads(self.request.body)
self.instance = getattr(self.adapter_module, self.class_name)()
def write_error(self, status_code, **kwargs):
@@ -87,8 +95,6 @@ class IndexHandler(BaseHandler):
@run_on_executor()
def send_index(self):
- root_path = os.path.dirname(__file__)
- index = os.path.join(root_path, "index.html")
with open(index, encoding="u8") as f:
html = f.read()
return html
@@ -110,35 +116,27 @@ class UserHandler(BaseHandler):
@run_on_executor()
def login_user(self):
- data = json.loads(self.request.body)
+ data = self.json
username = data["username"]
password = data["password"]
+ captcha = data.get("captcha")
+ captcha_id = data.get("captcha_id", "")
ip = AntiCrawler(self).get_real_ip()
browser = self.request.headers['user-agent']
- response = self.instance.login_user(username, password, ip, browser)
+ response = self.instance.login_user(username, password, captcha, captcha_id, ip, browser)
if response["status_code"] in (HTTPStatus.CREATED, HTTPStatus.OK):
self.set_login(username)
- returned_value = ""
else:
- self.set_status(HTTPStatus.FORBIDDEN)
- returned_value = response["message"]
+ self.set_status(response["status_code"])
- return returned_value
+ return response
@run_on_executor()
- def add_remove_fav(self):
- data = json.loads(self.request.body)
- resource_id = int(data["resource_id"])
- username = self.get_current_user()
- if username:
- response = self.instance.add_remove_fav(resource_id, username)
- self.set_status(response["status_code"])
- else:
- response = {"message": "请先登录"}
- self.set_status(HTTPStatus.UNAUTHORIZED)
-
- return response["message"]
+ def update_info(self):
+ result = self.instance.update_user_info(self.current_user, self.json)
+ self.set_status(result.get("status_code", HTTPStatus.IM_A_TEAPOT))
+ return result
@run_on_executor()
def get_user_info(self) -> dict:
@@ -147,7 +145,7 @@ class UserHandler(BaseHandler):
data = self.instance.get_user_info(username)
else:
self.set_status(HTTPStatus.UNAUTHORIZED)
- data = {}
+ data = {"message": "Please try to login"}
return data
@gen.coroutine
@@ -156,13 +154,6 @@ class UserHandler(BaseHandler):
self.write(resp)
@gen.coroutine
- @web.authenticated
- def patch(self):
- resp = yield self.add_remove_fav()
- self.write(resp)
-
- @gen.coroutine
- @web.authenticated
def get(self):
resp = yield self.get_user_info()
self.write(resp)
@@ -173,6 +164,12 @@ class UserHandler(BaseHandler):
now_ip = AntiCrawler(self).get_real_ip()
self.instance.update_user_last(username, now_ip)
+ @gen.coroutine
+ @web.authenticated
+ def patch(self):
+ resp = yield self.update_info()
+ self.write(resp)
+
class ResourceHandler(BaseHandler):
class_name = f"Resource{adapter}Resource"
@@ -216,11 +213,80 @@ class ResourceHandler(BaseHandler):
resp = "error"
self.write(resp)
+ # patch and post are available to every login user
+ # these are rare operations, so no gen.coroutine and run_on_executor
+ @web.authenticated
+ def patch(self):
+ if self.instance.is_admin(self.get_current_user()):
+ # may consider add admin restrictions
+ pass
+ for item in self.json["items"].values():
+ for i in item:
+ i["creator"] = self.get_current_user()
+ i["itemid"] = uuid.uuid4().hex
+ self.instance.patch_resource(self.json)
+ self.set_status(HTTPStatus.CREATED)
+ self.write({})
-class UserLikeHandler(BaseHandler):
- class_name = f"UserLike{adapter}Resource"
+ @web.authenticated
+ def post(self):
+ self.json["data"]["list"] = []
+ self.json["data"]["info"]["creator"] = self.get_current_user()
+ self.set_status(HTTPStatus.CREATED)
+ resp = self.instance.add_resource(self.json)
+ self.write(resp)
- # from Mongo import UserLikeMongoResource
+ @web.authenticated
+ def delete(self):
+ if not self.instance.is_admin(self.get_current_user()):
+ self.set_status(HTTPStatus.FORBIDDEN)
+ self.write({"status": False, "message": "admin only"})
+ return
+ self.instance.delete_resource(self.json)
+ self.set_status(HTTPStatus.ACCEPTED)
+ self.write({})
+
+
+class ResourceLatestHandler(BaseHandler):
+ class_name = f"ResourceLatest{adapter}Resource"
+
+ # from Mongo import ResourceLatestMongoResource
+ # instance = ResourceLatestMongoResource()
+ @run_on_executor()
+ def get_latest(self):
+ size = int(self.get_query_argument("size", "100"))
+ result = self.instance.get_latest_resource()
+ result["data"] = result["data"][:size]
+ return result
+
+ @gen.coroutine
+ def get(self):
+ resp = yield self.get_latest()
+ self.write(resp)
+
+
+#
+# class ResourceLatestHandler(BaseHandler):
+# from concurrent.futures import ProcessPoolExecutor
+#
+# class_name = f"ResourceLatest{adapter}Resource"
+# executor = ProcessPoolExecutor(200)
+#
+# # from Mongo import ResourceLatestMongoResource
+# # instance = ResourceLatestMongoResource()
+#
+# @gen.coroutine
+# def get(self):
+# # This returns a concurrent.futures.Future
+# fut = self.executor.submit(self.instance.get_latest_resource)
+# ret = yield fut
+# self.write(ret)
+
+
+class LikeHandler(BaseHandler):
+ class_name = f"Like{adapter}Resource"
+
+ # from Mongo import LikeMongoResource
# instance = UserLikeMongoResource()
@run_on_executor()
@@ -234,6 +300,26 @@ class UserLikeHandler(BaseHandler):
resp = yield self.like_data()
self.write(resp)
+ @run_on_executor()
+ def add_remove_fav(self):
+ data = self.json
+ resource_id = int(data["resource_id"])
+ username = self.get_current_user()
+ if username:
+ response = self.instance.add_remove_fav(resource_id, username)
+ self.set_status(response["status_code"])
+ else:
+ response = {"message": "请先登录"}
+ self.set_status(HTTPStatus.UNAUTHORIZED)
+
+ return response["message"]
+
+ @gen.coroutine
+ @web.authenticated
+ def patch(self):
+ resp = yield self.add_remove_fav()
+ self.write(resp)
+
class NameHandler(BaseHandler):
class_name = f"Name{adapter}Resource"
@@ -281,7 +367,7 @@ class CommentHandler(BaseHandler):
@run_on_executor()
def add_comment(self):
- payload = json.loads(self.request.body)
+ payload = self.json
captcha = payload["captcha"]
captcha_id = payload["id"]
content = payload["content"]
@@ -301,7 +387,7 @@ class CommentHandler(BaseHandler):
def delete_comment(self):
# need resource_id & id
# payload = {"id": "obj_id"}
- payload = json.loads(self.request.body)
+ payload = self.json
username = self.get_current_user()
comment_id = payload["comment_id"]
@@ -313,16 +399,6 @@ class CommentHandler(BaseHandler):
self.set_status(HTTPStatus.UNAUTHORIZED)
return {"count": 0, "message": "You're unauthorized to delete comment."}
- @run_on_executor()
- def comment_reaction(self):
- payload = json.loads(self.request.body)
- username = self.get_current_user()
- comment_id = payload["comment_id"]
- verb = payload["verb"]
- result = self.instance.react_comment(username, comment_id, verb)
- self.set_status(result.get("status_code") or HTTPStatus.IM_A_TEAPOT)
- return result
-
@gen.coroutine
def get(self):
resp = yield self.get_comment()
@@ -340,9 +416,30 @@ class CommentHandler(BaseHandler):
resp = yield self.delete_comment()
self.write(resp)
+
+class CommentReactionHandler(BaseHandler):
+ class_name = f"CommentReaction{adapter}Resource"
+
+ # from Mongo import CommentReactionMongoResource
+ # instance = CommentReactionMongoResource()
+
+ @run_on_executor()
+ def comment_reaction(self):
+ self.json.update(method=self.request.method)
+ username = self.get_current_user()
+ result = self.instance.react_comment(username, self.json)
+ self.set_status(result.get("status_code"))
+ return result
+
@gen.coroutine
@web.authenticated
- def patch(self):
+ def post(self):
+ resp = yield self.comment_reaction()
+ self.write(resp)
+
+ @gen.coroutine
+ @web.authenticated
+ def delete(self):
resp = yield self.comment_reaction()
self.write(resp)
@@ -412,7 +509,7 @@ class AnnouncementHandler(BaseHandler):
self.set_status(HTTPStatus.FORBIDDEN)
return {"message": "只有管理员可以设置公告"}
- payload = json.loads(self.request.body)
+ payload = self.json
content = payload["content"]
real_ip = AntiCrawler(self).get_real_ip()
browser = self.request.headers['user-agent']
@@ -437,7 +534,7 @@ class CaptchaHandler(BaseHandler, CaptchaResource):
@run_on_executor()
def verify_captcha(self):
- data = json.loads(self.request.body)
+ data = self.json
captcha_id = data.get("id", None)
userinput = data.get("captcha", None)
if captcha_id is None or userinput is None:
@@ -477,7 +574,7 @@ class MetricsHandler(BaseHandler):
@run_on_executor()
def set_metrics(self):
- payload = json.loads(self.request.body)
+ payload = self.json
metrics_type = payload["type"]
self.instance.set_metrics(metrics_type)
@@ -550,7 +647,7 @@ class GrafanaQueryHandler(BaseHandler):
return time.mktime(time.strptime(text, "%Y-%m-%d"))
def post(self):
- payload = json.loads(self.request.body)
+ payload = self.json
start = payload["range"]["from"].split("T")[0]
end = payload["range"]["to"].split("T")[0]
date_series = self.generate_date_series(start, end)
@@ -589,7 +686,7 @@ class BlacklistHandler(BaseHandler):
class NotFoundHandler(BaseHandler):
def get(self): # for react app
- self.render("index.html")
+ self.render(index)
class DBDumpHandler(BaseHandler):
@@ -694,7 +791,7 @@ class DoubanReportHandler(BaseHandler):
@run_on_executor()
def report_error(self):
- data = json.loads(self.request.body)
+ data = self.json
user_captcha = data["captcha_id"]
captcha_id = data["id"]
content = data["content"]
@@ -713,3 +810,80 @@ class DoubanReportHandler(BaseHandler):
def get(self):
resp = yield self.get_error()
self.write(resp)
+
+
+class NotificationHandler(BaseHandler):
+ class_name = f"Notification{adapter}Resource"
+
+ # from Mongo import NotificationResource
+ # instance = NotificationResource()
+
+ @run_on_executor()
+ def get_notification(self):
+ username = self.get_current_user()
+ size = int(self.get_argument("size", "5"))
+ page = int(self.get_argument("page", "1"))
+
+ return self.instance.get_notification(username, page, size)
+
+ @run_on_executor()
+ def update_notification(self):
+ username = self.get_current_user()
+ verb = self.json["verb"]
+ comment_id = self.json["comment_id"]
+ if verb not in ["read", "unread"]:
+ self.set_status(HTTPStatus.BAD_REQUEST)
+ return {"status": False, "message": "verb: read or unread"}
+ self.set_status(HTTPStatus.CREATED)
+ return self.instance.update_notification(username, verb, comment_id)
+
+ @gen.coroutine
+ @web.authenticated
+ def get(self):
+ resp = yield self.get_notification()
+ self.write(resp)
+
+ @gen.coroutine
+ @web.authenticated
+ def patch(self):
+ resp = yield self.update_notification()
+ self.write(resp)
+
+
+class UserEmailHandler(BaseHandler):
+ class_name = f"UserEmail{adapter}Resource"
+
+ # from Mongo import UserEmailResource
+ # instance = UserEmailResource()
+
+ @run_on_executor()
+ def verify_email(self):
+ result = self.instance.verify_email(self.get_current_user(), self.json["code"])
+ self.set_status(result.get("status_code"))
+ return result
+
+ @gen.coroutine
+ @web.authenticated
+ def post(self):
+ resp = yield self.verify_email()
+ self.write(resp)
+
+
+class CategoryHandler(BaseHandler):
+ class_name = f"Category{adapter}Resource"
+
+ from Mongo import CategoryResource
+ instance = CategoryResource()
+
+ @run_on_executor()
+ def get_data(self):
+ self.json = {k: self.get_argument(k) for k in self.request.arguments}
+ self.json["size"] = int(self.json.get("size", 15))
+ self.json["page"] = int(self.json.get("page", 1))
+ self.json["douban"] = self.json.get("douban", False)
+ return self.instance.get_category(self.json)
+
+ @gen.coroutine
+ def get(self):
+ resp = yield self.get_data()
+ self.write(resp)
diff --git a/yyetsweb/server.py b/yyetsweb/server.py
index 04fd516..1a07438 100644
--- a/yyetsweb/server.py
+++ b/yyetsweb/server.py
@@ -17,14 +17,17 @@ from tornado import httpserver, ioloop, options, web
from tornado.log import enable_pretty_logging
from handler import (AnnouncementHandler, BlacklistHandler, CaptchaHandler,
- CommentChildHandler, CommentHandler, CommentNewestHandler,
+ CategoryHandler, CommentChildHandler, CommentHandler,
+ CommentNewestHandler, CommentReactionHandler,
DBDumpHandler, DoubanHandler, DoubanReportHandler,
GrafanaIndexHandler, GrafanaQueryHandler,
- GrafanaSearchHandler, IndexHandler, MetricsHandler,
- NameHandler, NotFoundHandler, ResourceHandler, TopHandler,
- UserHandler, UserLikeHandler)
+ GrafanaSearchHandler, IndexHandler, LikeHandler,
+ MetricsHandler, NameHandler, NotFoundHandler,
+ NotificationHandler, ResourceHandler,
+ ResourceLatestHandler, TopHandler, UserEmailHandler,
+ UserHandler)
from migration.douban_sync import sync_douban
-from Mongo import OtherMongoResource
+from Mongo import OtherMongoResource, ResourceLatestMongoResource
enable_pretty_logging()
@@ -34,14 +37,18 @@ if os.getenv("debug"):
class RunServer:
root_path = os.path.dirname(__file__)
- static_path = os.path.join(root_path, '')
+ static_path = os.path.join(root_path, 'templates')
handlers = [
+ (r'/', IndexHandler),
(r'/api/resource', ResourceHandler),
+ (r'/api/resource/latest', ResourceLatestHandler),
(r'/api/top', TopHandler),
- (r'/api/like', UserLikeHandler),
+ (r'/api/like', LikeHandler),
(r'/api/user', UserHandler),
+ (r'/api/user/email', UserEmailHandler),
(r'/api/name', NameHandler),
(r'/api/comment', CommentHandler),
+ (r'/api/comment/reaction', CommentReactionHandler),
(r'/api/comment/child', CommentChildHandler),
(r'/api/comment/newest', CommentNewestHandler),
(r'/api/captcha', CaptchaHandler),
@@ -52,9 +59,11 @@ class RunServer:
(r'/api/blacklist', BlacklistHandler),
(r'/api/db_dump', DBDumpHandler),
(r'/api/announcement', AnnouncementHandler),
- (r'/', IndexHandler),
(r'/api/douban', DoubanHandler),
(r'/api/douban/report', DoubanReportHandler),
+ (r'/api/notification', NotificationHandler),
+ (r'/api/category', CategoryHandler),
+
(r'/(.*\.html|.*\.js|.*\.css|.*\.png|.*\.jpg|.*\.ico|.*\.gif|.*\.woff2|.*\.gz|.*\.zip|'
r'.*\.svg|.*\.json|.*\.txt)',
web.StaticFileHandler,
@@ -89,6 +98,7 @@ if __name__ == "__main__":
scheduler = BackgroundScheduler(timezone=timez)
scheduler.add_job(OtherMongoResource().reset_top, 'cron', hour=0, minute=0, day=1)
scheduler.add_job(sync_douban, 'cron', hour=0, minute=0, day=1)
+ scheduler.add_job(ResourceLatestMongoResource().refresh_latest_resource, 'cron', minute=0)
scheduler.start()
options.define("p", default=8888, help="running port", type=int)
options.define("h", default='127.0.0.1', help="listen address", type=str)
diff --git a/yyetsweb/404.html b/yyetsweb/templates/404.html
similarity index 100%
rename from yyetsweb/404.html
rename to yyetsweb/templates/404.html
diff --git a/yyetsweb/css/3rd/animate.css b/yyetsweb/templates/css/3rd/animate.css
similarity index 100%
rename from yyetsweb/css/3rd/animate.css
rename to yyetsweb/templates/css/3rd/animate.css
diff --git a/yyetsweb/css/3rd/icons.css b/yyetsweb/templates/css/3rd/icons.css
similarity index 100%
rename from yyetsweb/css/3rd/icons.css
rename to yyetsweb/templates/css/3rd/icons.css
diff --git a/yyetsweb/css/3rd/widgets.css b/yyetsweb/templates/css/3rd/widgets.css
similarity index 100%
rename from yyetsweb/css/3rd/widgets.css
rename to yyetsweb/templates/css/3rd/widgets.css
diff --git a/yyetsweb/css/aYin.css b/yyetsweb/templates/css/aYin.css
similarity index 100%
rename from yyetsweb/css/aYin.css
rename to yyetsweb/templates/css/aYin.css
diff --git a/yyetsweb/css/bootstrap.min.css b/yyetsweb/templates/css/bootstrap.min.css
similarity index 100%
rename from yyetsweb/css/bootstrap.min.css
rename to yyetsweb/templates/css/bootstrap.min.css
diff --git a/yyetsweb/css/data.json b/yyetsweb/templates/css/data.json
similarity index 100%
rename from yyetsweb/css/data.json
rename to yyetsweb/templates/css/data.json
diff --git a/yyetsweb/css/down-list-20180530.css b/yyetsweb/templates/css/down-list-20180530.css
similarity index 100%
rename from yyetsweb/css/down-list-20180530.css
rename to yyetsweb/templates/css/down-list-20180530.css
diff --git a/yyetsweb/css/font-awesome.min.css b/yyetsweb/templates/css/font-awesome.min.css
similarity index 100%
rename from yyetsweb/css/font-awesome.min.css
rename to yyetsweb/templates/css/font-awesome.min.css
diff --git a/yyetsweb/css/index.json b/yyetsweb/templates/css/index.json
similarity index 100%
rename from yyetsweb/css/index.json
rename to yyetsweb/templates/css/index.json
diff --git a/yyetsweb/css/jquery.mCustomScrollbar.css b/yyetsweb/templates/css/jquery.mCustomScrollbar.css
similarity index 100%
rename from yyetsweb/css/jquery.mCustomScrollbar.css
rename to yyetsweb/templates/css/jquery.mCustomScrollbar.css
diff --git a/yyetsweb/css/normalize.min.css b/yyetsweb/templates/css/normalize.min.css
similarity index 100%
rename from yyetsweb/css/normalize.min.css
rename to yyetsweb/templates/css/normalize.min.css
diff --git a/yyetsweb/css/noty.css b/yyetsweb/templates/css/noty.css
similarity index 100%
rename from yyetsweb/css/noty.css
rename to yyetsweb/templates/css/noty.css
diff --git a/yyetsweb/favicon.ico b/yyetsweb/templates/favicon.ico
similarity index 100%
rename from yyetsweb/favicon.ico
rename to yyetsweb/templates/favicon.ico
diff --git a/yyetsweb/fonts/fontawesome-webfont.woff2 b/yyetsweb/templates/fonts/fontawesome-webfont.woff2
similarity index 100%
rename from yyetsweb/fonts/fontawesome-webfont.woff2
rename to yyetsweb/templates/fonts/fontawesome-webfont.woff2
diff --git a/yyetsweb/help.html b/yyetsweb/templates/help.html
similarity index 100%
rename from yyetsweb/help.html
rename to yyetsweb/templates/help.html
diff --git a/yyetsweb/img/11bcd4d0f2daf8b02fecc72bc8ca38ab.png b/yyetsweb/templates/img/11bcd4d0f2daf8b02fecc72bc8ca38ab.png
similarity index 100%
rename from yyetsweb/img/11bcd4d0f2daf8b02fecc72bc8ca38ab.png
rename to yyetsweb/templates/img/11bcd4d0f2daf8b02fecc72bc8ca38ab.png
diff --git a/yyetsweb/img/200-wrangler-ferris.gif b/yyetsweb/templates/img/200-wrangler-ferris.gif
similarity index 100%
rename from yyetsweb/img/200-wrangler-ferris.gif
rename to yyetsweb/templates/img/200-wrangler-ferris.gif
diff --git a/yyetsweb/img/404-wrangler-ferris.gif b/yyetsweb/templates/img/404-wrangler-ferris.gif
similarity index 100%
rename from yyetsweb/img/404-wrangler-ferris.gif
rename to yyetsweb/templates/img/404-wrangler-ferris.gif
diff --git a/yyetsweb/img/afdian.png b/yyetsweb/templates/img/afdian.png
similarity index 100%
rename from yyetsweb/img/afdian.png
rename to yyetsweb/templates/img/afdian.png
diff --git a/yyetsweb/img/default-green.png b/yyetsweb/templates/img/default-green.png
similarity index 100%
rename from yyetsweb/img/default-green.png
rename to yyetsweb/templates/img/default-green.png
diff --git a/yyetsweb/img/grid16.png b/yyetsweb/templates/img/grid16.png
similarity index 100%
rename from yyetsweb/img/grid16.png
rename to yyetsweb/templates/img/grid16.png
diff --git a/yyetsweb/img/yyetsTrans.png b/yyetsweb/templates/img/yyetsTrans.png
similarity index 100%
rename from yyetsweb/img/yyetsTrans.png
rename to yyetsweb/templates/img/yyetsTrans.png
diff --git a/yyetsweb/index.html b/yyetsweb/templates/index.html
similarity index 99%
rename from yyetsweb/index.html
rename to yyetsweb/templates/index.html
index a1aecd1..e19b300 100644
--- a/yyetsweb/index.html
+++ b/yyetsweb/templates/index.html
@@ -280,4 +280,4 @@
}
-
- 遇见恶魔的短评 - · · · · · · - - ( - 全部 251 条 - ) - -
- - -- - 1 - - - 有用 - - - - - 披着人皮的鬼 - 看过 - - - 2012-04-12 - - -
-- - 呆着一切不合理都是合理的心态看这是一部很有诚意的片子 若干细节理解不能和渣字幕有关 -
-- - 5 - - - 有用 - - - - - cherrie - 看过 - - - 2012-10-22 - - -
-- - 坏进骨子里的 是那些道貌岸然的假圣人。 -
-- - 0 - - - 有用 - - - - - Joey - 看过 - - - 2012-08-30 - - -
-- - wtf -
-- - 0 - - - 有用 - - - - - Avalon - 看过 - - - 2012-04-15 - - -
-- - 人不错,故事没劲! -
-- - 0 - - - 有用 - - - - - 杨子虚 - 看过 - - - 2013-12-29 - - -
-- - 此片被严重低估……至少风格化的片子很我对胃口 -
-