@@ -5,7 +5,6 @@ logs/*
|
||||
YYeTsFE/node_modules/*
|
||||
.github/*
|
||||
assets/*
|
||||
scripts/*
|
||||
conf/*
|
||||
tests/*
|
||||
yyetsweb/yyets.sqlite
|
||||
15
.gitattributes
vendored
@@ -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
|
||||
4
.github/workflows/builder.yaml
vendored
@@ -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
|
||||
|
||||
8
.gitignore
vendored
@@ -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
|
||||
|
||||
|
||||
|
||||
516
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`
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
# 通知
|
||||
|
||||
只有登录用户可以获取,只有楼主能够获取到通知,其他楼层的人获取不到。
|
||||
|
||||
## 获取通知
|
||||
|
||||
* 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": "<reply value=\"6117b2a59195e6b1ab86eb30\">@user2</reply>u3 to u2",
|
||||
"resource_id": 233,
|
||||
"type": "child",
|
||||
"id": "6117b5a6598f80ca3ebb13ed",
|
||||
"reply_to_content": "<reply value=\"610135c9d1f873388feb5c78\">@user1</reply>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": "<reply value=\"610135c9d1f873388feb5c78\">@user1</reply>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": "<reply value=\"610135c9d1f873388feb5c78\">@user1</reply>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"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
2
YYeTsFE
6
conf/yyets.env
Normal file
@@ -0,0 +1,6 @@
|
||||
mongo=mongo
|
||||
redis=redis
|
||||
email_user=username
|
||||
email_password=passord
|
||||
email_host=mailhog
|
||||
email_port=1025
|
||||
@@ -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"
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
[run]
|
||||
omit =
|
||||
*/site-packages/*
|
||||
*/distutils/*
|
||||
tests/*
|
||||
474
tests/data/yyets_search.html
vendored
@@ -1,474 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<link href="http://js.jstucdn.com/style/layout.css" rel="stylesheet" type="text/css" />
|
||||
<link href="http://js.jstucdn.com/style/style.css" rel="stylesheet" type="text/css" />
|
||||
<script src="http://js.jstucdn.com/js/jquery-1.7.1.min.js" charset="utf-8"></script>
|
||||
<script src="http://js.jstucdn.com/js/global.js?v=20160922" charset="utf-8"></script>
|
||||
<title>abc,搜索结果页</title>
|
||||
<meta name="keywords" content="连载,RMVB,HR-HDTV,MP4,MKV,720P,1080P,迅雷,电驴,磁力,网盘,字幕,下载,美女在线观看">
|
||||
<meta name="description" content="ZiMuZu字幕组网站,www.zimuzu.tv是一群由海外留学生于2015年1月1号组建的字幕组分享网站,以翻译最新最快影 视剧为兴趣爱好,并且免费分享给广大网友和爱好者,欢迎大家的加入">
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
var uri = document.location.href.toLowerCase();
|
||||
var na = ['/release','/user/reg','/work','/translate'];
|
||||
var na_ga = [];
|
||||
|
||||
function is_a(){
|
||||
for(var i in na)
|
||||
if(uri.indexOf(na[i])>=0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function is_ga_a(){
|
||||
var t = na.concat(na_ga);
|
||||
for(var i in t)
|
||||
if(uri.indexOf(t[i])>=0) return false;
|
||||
return true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
//if(is_a()) document.write('<script src="https://res.iyoowi.com/iy/iy.js?cid=6BC02495&skey=1c5b2238"><\/script>');
|
||||
if (is_a()) {
|
||||
var ad = GLOBAL.GetAd(4);
|
||||
if (ad) {
|
||||
document.write(ad.item_js.code);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="http://js.jstucdn.com/js/jquery.cookie.js"></script>
|
||||
|
||||
<div class="top-box">
|
||||
<div class="header">
|
||||
<div class="w">
|
||||
<h1 class="fl"><a href="/" class="w_index"><img id="logo"></a></h1>
|
||||
<form name="search" action="/search/index" method="get" class="fl corner search-box">
|
||||
<input type="text" size="20" class="keywords" name="keyword" autocomplete='off' placeholder="請輸入搜尋關鍵詞" />
|
||||
<input type="submit" class="submit">
|
||||
<div class="search-list" style="display:none;"></div>
|
||||
<ul id="top_search_list" style="display:none;"></ul>
|
||||
<input type="hidden" name="search_type" id="search_type" value="">
|
||||
</form>
|
||||
<a href="http://www.yyets.com/jz/" target="_blank" class="zzhu">捐助我们</a>
|
||||
<span class="app_link" style="position: relative; font-size: 16px; font-weight: bold; left: 20px; top: 15px;"><a href="http://app.rrys.tv" target="_blank" style="color:#fff">客户端下载</a><span style="position: absolute;;left:0px;top:35px;display: none;"><img src="http://app.rrysapp.com/uploads/20181115/9ba002992f234b7c7c4203db5abe7222.png" style="width:180px;height: 180px;"></span></span>
|
||||
<div class="fr top-login">
|
||||
<!-- 未登录 -->
|
||||
<div class="top-login-before" style="display:none;"><div class="u"><em class="ico user-ico"></em><a href="/user/login">登入</a>/ <a href="/user/reg">註冊</a> <a href="/user/login/change_pwd">修改登录密码</a></div></div>
|
||||
<!-- 未登录 -->
|
||||
<!-- 已登录 -->
|
||||
<div class="top-login-after" style="display:none;">
|
||||
<div class="fl u">
|
||||
</div>
|
||||
<div class="fl e top_msg">
|
||||
<!-- <em class="corner" style="display:none;"></em>-->
|
||||
<div class="user-pop p-email">
|
||||
<div class="user-pop-con corner">
|
||||
<span class="ar"></span>
|
||||
<ul>
|
||||
<li class="message"><a href='/user/message'><span>私信</span></a><strong class="corner"></strong></li>
|
||||
<li class="sys_message" style="margin-bottom: 10px;"><a href="/user/sysmessage"><span>系统通知</span></a><strong class="corner"></strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fr fb">
|
||||
我要发布<em></em>
|
||||
<div class="user-pop p-release">
|
||||
<div class="user-pop-con corner">
|
||||
<span class="ar"></span>
|
||||
<ul id="post_List">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 已登录 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="notice">
|
||||
<div class="w" style="height:48px;overflow: hidden;">
|
||||
<p style="float:left;"><strong class="f0">网站公告:</strong> </p>
|
||||
<ul></ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.signin-tips{ border-radius:5px; border:1px solid #a6884c; background:#fab639; color:#fff; width:436px; height:95px; box-shadow:3px 3px 0 rgba(0,0,0,.3);z-index: 999;}
|
||||
.signin-tips .fl{ font-size:60px; font-weight:bold; width:40%; text-align:center; margin-top:36px; text-shadow:1px 1px 0 #333;}
|
||||
.signin-tips .fr{ width:60%; font-size:17px; line-height:150%; margin-top:17px; text-shadow:1px 1px 0 #666;}
|
||||
.signin-tips .fr b{ font-size:25px;}
|
||||
</style>
|
||||
<script>
|
||||
$('#logo').attr('src',GLOBAL.CONST.RES_URL+'images/logo.png');
|
||||
GLOBAL.JQUERY.SEARCHTIPS(250,function(){$('.search-list').hide()});
|
||||
var navbar_a,navbar_b;
|
||||
|
||||
$(document).ready(function(){
|
||||
//加载用户信息
|
||||
$('.top-login-before').show();
|
||||
var ginfo = $.cookie('GINFO')
|
||||
if (ginfo) {
|
||||
$.ajax({
|
||||
url: '/user/login/getCurUserTopInfo',
|
||||
async: false,
|
||||
dataType: "json",
|
||||
success: function(R){
|
||||
$('.top-login-before').hide();
|
||||
GLOBAL.InitTopUserInfo(R.data,R.status);
|
||||
}
|
||||
});
|
||||
}
|
||||
var common_data = GLOBAL.CommonData();
|
||||
load_hotkeywords(common_data.hotkeywords)
|
||||
load_announcements(common_data.announcements)
|
||||
|
||||
$('.top-login-after .fb').mouseover(function(){
|
||||
$(this).addClass('fb-selected');
|
||||
}).mouseout(function(){
|
||||
$(this).removeClass('fb-selected');
|
||||
})
|
||||
$('#logout').click(function(){
|
||||
GLOBAL.Loading();
|
||||
$.getJSON('/user/logout/ajaxLogout',function(data){
|
||||
if(data.status==1){
|
||||
window.history.go(0);
|
||||
}
|
||||
GLOBAL.Loading('hide');
|
||||
});
|
||||
return false;
|
||||
});
|
||||
$('.app_link a').hover(function(){
|
||||
$(this).next().show();
|
||||
},function(){
|
||||
$(this).next().hide();
|
||||
});
|
||||
});
|
||||
|
||||
function load_hotkeywords(hotkeywords)
|
||||
{
|
||||
var html = '<li><a href="http://www.yyets.com/jz" target="_blank" rel="nofollow"><span style="color:#ED8600">1</span>捐助人人影视</a></li>\
|
||||
';
|
||||
for(key in hotkeywords){
|
||||
key = parseInt(key);
|
||||
if (typeof hotkeywords[key].keyword !== 'undefined') html += '<li><a href="/search?keyword='+encodeURIComponent(hotkeywords[key].keyword)+'"><span class="corner50">'+(key+1)+'</span>'+hotkeywords[key].keyword+'</a></li>';
|
||||
}
|
||||
$('.search-list').html('<ul class="corner box key-result">'+html+'</ul>');
|
||||
}
|
||||
|
||||
function load_announcements(announcements) {
|
||||
var html = '';
|
||||
for(k in announcements){
|
||||
html += '<li><a target="_blank" href="/announcement/'+announcements[k].id+'"><span>'+announcements[k].title+'</span></a></li>';
|
||||
}
|
||||
$('.notice').show().find('div.w ul').html(html);
|
||||
GLOBAL.JQUERY.Scroll({obj:$('.notice ul'),direct:'up'});
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="globalNav1" style="text-align:center; margin:20px 0;">
|
||||
<a href="" target="_blank" rel="nofollow"><img src=""/></a>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
var ad = GLOBAL.GetAd(5);
|
||||
if (ad) {
|
||||
$('#globalNav1').find('a').attr('href',ad.item_img.url);
|
||||
$('#globalNav1').find('img').attr('src',ad.item_img.pic);
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="menu-box">
|
||||
<ul class="w">
|
||||
<li><a href="/" class="w_index">首页</a></li>
|
||||
<li channel="translate"><a href="/translate">任务</a></li>
|
||||
<li channel="article"><a href="/article">资讯</a></li>
|
||||
<li channel="subtitle"><a href="/subtitle">字幕</a></li>
|
||||
<li channel="resource"><a href="/resourcelist">影视库</a></li>
|
||||
<li channel='top'><a href="/html/top/week.html">排行榜</a></li>
|
||||
<li channel="today"><a href="/today">今日</a></li>
|
||||
<li channel='schedule'><a href="/tv/schedule">播出表</a></li>
|
||||
<li channel="help"><a href="/help">求档</a></li>
|
||||
<li><a href="/Announcement">公告</a></li>
|
||||
<li><a href="http://allyingshi.com/fanyi/index.html">合作</a></li>
|
||||
<li><a target="_blank" href="http://shop113823204.taobao.com/?spm=2013.1.1000126.2.5a945178SbNzAZ">人人小店</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
if(navbar_a) $('.menu-box li:first').after(navbar_a);
|
||||
if(navbar_b) $('.menu-box li:last').after(navbar_b);
|
||||
var matches = window.location.href.match(/http:\/\/[^/]+(\/.*)/);
|
||||
var group_name = matches==null?'':matches[1].replace(/\/{2,}/m,'/');
|
||||
var groups = {'article':'^/article','fav':'^/user/fav','subtitle':'^/[a-z]?subtitle','resource':'^/[a-z]?resourcelist|/[a-z]?resource/','schedule':'^/tv/schedule|/html/return|/html/new','today':'^/today','help':'^/help','top':'^/html/top/','translate':'^/translate'};
|
||||
for(key in groups)
|
||||
{
|
||||
regx = new RegExp(groups[key],'i');
|
||||
if(group_name.search(regx)!=-1)
|
||||
{
|
||||
$('.menu-box li[channel='+key+'] a').addClass('cur');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
$('.menu-box li:first a').addClass('cur');
|
||||
});
|
||||
</script>
|
||||
<div id="globalNav2" style="text-align:center; margin:15px 0;">
|
||||
<!-- <a href="http://aa.juexcx.com" target="_blank" rel="nofollow"><img src="http://image.jstucdn.com/g3/ad-img/21.jpg" width="1100" height="90"/></a> -->
|
||||
<script type="text/javascript">
|
||||
//if(is_ga_a()) document.write('<script src="http://g.ousns.net/ga-728-t.js?232" type="text/javascript"><\/script>');
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/*
|
||||
$(function () {
|
||||
var ad = GLOBAL.GetAd(6);
|
||||
if (ad) {
|
||||
$('#globalNav2').find('a').attr('href',ad.item_img.url);
|
||||
$('#globalNav2').find('img').attr('src',ad.item_img.pic);
|
||||
}
|
||||
})
|
||||
*/
|
||||
</script>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="middle-box">
|
||||
<div class="w">
|
||||
<div class="search-result-box-wrap">
|
||||
<div class="box search-result-box">
|
||||
<div class="article-tab">
|
||||
查看:<a class="search_type" type="">全部(141)</a><a class="search_type" type="resource">影视(4)</a><a class="search_type" type="subtitle">字幕(5)</a><a class="search_type" type="article">资讯(132) </a>
|
||||
</div>
|
||||
<div class="search-result">
|
||||
<div id="ad1" style="display:none;text-align:center;margin:10px 0;overflow:hidden">
|
||||
<!-- <script src="http://g.ousns.net/ga-728-m.js?232" type="text/javascript"></script> -->
|
||||
</div>
|
||||
<script>
|
||||
$(function(){
|
||||
$('#ad1').show().insertAfter('div.search-result li:eq(4)');
|
||||
});
|
||||
</script>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<div class="clearfix search-item">
|
||||
<div class="fl-img">
|
||||
<a href="/resource/37345"><img src="http://image.jstucdn.com/ftp/2018/1227/s_bf967816c2d98b2c98141ff1e6af2ea6.jpg"></a>
|
||||
<em style="background:#0d82cb;">电视剧</em>
|
||||
</div>
|
||||
<div class="fl-info">
|
||||
<div class="t f14"><a href="/resource/37345"><strong class="list_title">《ABC谋杀案》(The ABC Murders)</strong></a></div>
|
||||
<p>发布:<span class="f4 time">1544957285</span></p>
|
||||
<p>更新:<span class="f1 time">1593172855</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="clearfix search-item">
|
||||
<div class="fl-img">
|
||||
<a href="/resource/29296"><img src="http://image.jstucdn.com/ftp/2014/1010/s_6fbec82a97711981f0e3fdeaaba409d6.jpg"></a>
|
||||
<em style="background:#0d82cb;">电影</em>
|
||||
</div>
|
||||
<div class="fl-info">
|
||||
<div class="t f14"><a href="/resource/29296"><strong class="list_title">《26种死法 第一部》(The ABCs of Death)</strong>2012</a></div>
|
||||
<p>发布:<span class="f4 time">1362308260</span></p>
|
||||
<p>更新:<span class="f1 time">1432680927</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="clearfix search-item">
|
||||
<div class="fl-img">
|
||||
<a href="/resource/33595"><img src="http://image.jstucdn.com/ftp/2015/0525/s_2112135a38fc7702ae7078adc6d9b5da.jpg"></a>
|
||||
<em style="background:#0d82cb;">电影</em>
|
||||
</div>
|
||||
<div class="fl-info">
|
||||
<div class="t f14"><a href="/resource/33595"><strong class="list_title">《26种死法 第二部》(ABCs of Death 2)</strong>2014</a></div>
|
||||
<p>发布:<span class="f4 time">1432555399</span></p>
|
||||
<p>更新:<span class="f1 time">1432619677</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="clearfix search-item">
|
||||
<div class="fl-img">
|
||||
<a href="/resource/28570"><img src="http://image.jstucdn.com/ftp/2012/1203/s_fe9d4a9fd45be1556d108a938f9b6c67.jpg"></a>
|
||||
<em style="background:#0d82cb;">电影</em>
|
||||
</div>
|
||||
<div class="fl-info">
|
||||
<div class="t f14"><a href="/resource/28570"><strong class="list_title">《小鬼当家5》(美国abc电视台)</strong> </a></div>
|
||||
<p>发布:<span class="f4 time">1354548684</span></p>
|
||||
<p>更新:<span class="f1 time">1354627584</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pages pages-padding"><div><b></b></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="area-right">
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
if(is_ga_a()){
|
||||
/*
|
||||
document.write('<script src="http://g.ousns.net/ga-300.js?81" type="text/javascript"><\/script>');
|
||||
document.write('<script src="http://g.ousns.net/ga-300.js?81" type="text/javascript"><\/script>');
|
||||
document.write('<script src="http://g.ousns.net/ga-300-2.js?81" type="text/javascript"><\/script>');
|
||||
*/
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<!-- <div class="box subtitle-serch-sidebar">
|
||||
<h2 class="it">熱門關搜索鍵詞</h2>
|
||||
<ul>
|
||||
<li><a href="/search?keyword=进击的巨人">进击的巨人</a></li><li><a href="/search?keyword=甜蜜家园">甜蜜家园</a></li><li><a href="/search?keyword=曼达洛人">曼达洛人</a></li><li><a href="/search?keyword=信条">信条</a></li><li><a href="/search?keyword=良医">良医</a></li><li><a href="/search?keyword=无耻家庭">无耻家庭</a></li><li><a href="/search?keyword=心灵奇旅">心灵奇旅</a></li><li><a href="/search?keyword=法官大人">法官大人</a></li><li><a href="/search?keyword=暗黑">暗黑</a></li><li><a href="/search?keyword=老友记">老友记</a></li><li><a href="/search?keyword=生活大爆炸">生活大爆炸</a></li><li><a href="/search?keyword=黑袍纠察队">黑袍纠察队</a></li><li><a href="/search?keyword=弥留之国的爱丽丝">弥留之国的爱丽丝</a></li><li><a href="/search?keyword=顶楼">顶楼</a></li><li><a href="/search?keyword=行尸走肉">行尸走肉</a></li><li><a href="/search?keyword=半泽直树">半泽直树</a></li> </ul>
|
||||
</div> -->
|
||||
|
||||
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var keyword = 'abc';
|
||||
var query_string = 'keyword=abc&type=resource';
|
||||
var regx = new RegExp(keyword,'gi');
|
||||
var url = GLOBAL.CONST.WWW_URL+'search';
|
||||
|
||||
var build_param = function(key,value){
|
||||
match = false;
|
||||
regx = new RegExp(key+'=','gi');
|
||||
params = query_string.split('&');
|
||||
for(i=0;i<params.length;i++){
|
||||
if(params[i].match(regx)!=null){
|
||||
params[i] = key+'='+value;
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
if(match==false) params[i] = key+'='+value;
|
||||
return params.join('&');
|
||||
};
|
||||
|
||||
$('.list_title').each(function(){ //列表中的关键字标红
|
||||
_text = $(this).text();
|
||||
_text = _text.replace(regx,'<font color="red">'+keyword+'</font>');
|
||||
$(this).html(_text);
|
||||
});
|
||||
$('.time').each(function(){ //格式化列表中的UNIX时间戳
|
||||
_time = GLOBAL.Util.formatDate($(this).text());
|
||||
$(this).text(_time);
|
||||
});
|
||||
$('input[name=keyword]').val(keyword).css('color','#000'); //填充搜索框
|
||||
|
||||
$('.search_type').each(function(){ //搜索类型状态
|
||||
type = $(this).attr('type');
|
||||
if(GLOBAL.Util.getParam('type',query_string)==type){
|
||||
$(this).addClass('selected');
|
||||
$('a.search_type[type='+type+']').addClass('selected').siblings().removeClass('selected');
|
||||
$('input#type').val(type);
|
||||
}else{
|
||||
$(this).attr('href',url+'?'+build_param('type',type));
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<div style="text-align:center;margin-top:10px">
|
||||
<script type="text/javascript">
|
||||
//if(is_ga_a()) document.write('<script src="http://g.ousns.net/ga-728-m3.js?232" type="text/javascript"><\/script>');
|
||||
//tb-ssp-950x90
|
||||
// tmp document.write('<script charset="gbk" src="http://p.tanx.com/ex?i=mm_111117185_10092486_42796389"><\/script>');
|
||||
</script>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="lx167" style="width:1038px;margin:10px auto;background-color:#fff"></div>
|
||||
<script>
|
||||
//GLOBAL.JQUERY.LazyLoad($('div.lx167'),function(){
|
||||
$('div.lx167').html('<iframe src="http://www.lx167.com/index/promo_iframe" width="1038" height="147" frameborder="0" scrolling="yes"></iframe>');
|
||||
//});
|
||||
</script>
|
||||
|
||||
<script type='text/javascript' charset='utf-8' src='https://t.quandangdang.net/vtdd/cf978cl8501aq.js'></script>
|
||||
|
||||
|
||||
<script>
|
||||
/*if(GLOBAL.Util.getCookie('ad_open_new')!=1 && GLOBAL.AdUtils.is_index_page()==false){
|
||||
setTimeout(function(){
|
||||
$('body').append('<a href="https://pcshow.xyz/?utm_source=zmz2019&utm_medium=TV&utm_campaign=TV" id="new_pop_ad" target="_blank"><div style="z-index:99999;background:#fff;width:'+$(document).width()+'px;height:'+$(document).height()+'px;position:absolute;left:0;top:0;filter:alpha(opacity=0);opacity:0;"></div></a>').find('a#new_pop_ad').click(function(){
|
||||
GLOBAL.Util.setCookie('ad_open_new',1,1000*60*60*24);
|
||||
$('a#new_pop_ad').remove();
|
||||
});
|
||||
|
||||
},1000*5);
|
||||
}*/
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="footer">
|
||||
<p class="tc" style="font-size:12px;"><a href="http://allyingshi.com/fanyi/index.html" target="_blank">商业翻译洽谈</a> <a href="/announcement/63">联系我们</a> <a href="/Announcement">加入字幕组</a> <a href="http://app.rrys.tv/" target="_blank">APP手机客户端</a> <a href="/bbs/list?fid=413">意见反馈</a> <a href="/user/password/forgot">账号申诉</a> <a href="/announcement/89">网站规章制度</a> <a href="/announcement/79">其他字幕组入驻本站发布</a> <!-- <a target="_blank" href="http://rss.rrys.tv/rss/feed">RSS订阅</a> --><a target="_blank" href="http://www.yyets.com/donate-us.html">捐助我们</a> <div style="width:1px;overflow: hidden;height:1px;"><script src="http://s95.cnzz.com/z_stat.php?id=1254180690&web_id=1254180690" language="JavaScript"></script></div></p>
|
||||
<p class="tc" style="font-size:14px;"><img src="http://image.jstucdn.com/images/dibulogo.png"> YYeTs人人影视 2006-2020</p>
|
||||
</div>
|
||||
<style>
|
||||
.WB_follow_ex, .WB_follow_ex .follow_btn_line{margin:0 auto !important;}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.chat-enterbox{ width:248px; height:102px; background:#fff; border:1px solid #575757; position:fixed; left:0; bottom:0; color:#000;}
|
||||
.chat-entertitle{ height:27px; line-height:27px; text-align:center; color:#fff; font-size:14px; font-weight:bold; background:#333;}
|
||||
.chat-entercon{ position:relative; background:url(http://image.jstucdn.com/images/ico-chatenter.png) 40px 50% no-repeat; width:100%; height:75px;}
|
||||
.chat-entercon p{ position:absolute; top:10px; left:120px; font-size:16px; width:120px; line-height:180%;}
|
||||
.chat-entercon p b{ color:#ff5910;}
|
||||
</style>
|
||||
<div class="chat-enterbox">
|
||||
<div class="chat-entertitle">人人影视在线聊天室</div>
|
||||
<a target="_blank"><div class="chat-entercon">
|
||||
<p><b></b>人在线<br />一起来热聊</p>
|
||||
</div></a>
|
||||
</div>
|
||||
<script>
|
||||
let chat_domain = 'http://'+window.location.hostname.replace(/^[a-z0-9]+/gi,'chat');
|
||||
$.get(chat_domain+'/status',function(R){ $('.chat-entercon b').text(R.count); },'jsonp');
|
||||
$('div.chat-enterbox a:first').attr('href',chat_domain);
|
||||
|
||||
$(document).ready(function(){
|
||||
if($('.area-right ins').size()>0 && document.location.pathname.match(/^\/search/)!=null){
|
||||
const random = parseInt(Math.random()*10);
|
||||
if(random<4 && $.cookie('ad_alert')!=1){
|
||||
$('.middle-box .w').css({position:'relative',left:0,top:0}).append('<img style="position:absolute;left:290px;top:50px;" id="ad_alert" src="http://image.jstucdn.com/images/ad/ad_alert2.png">').find('#ad_alert').load(function(){
|
||||
setTimeout(function(){
|
||||
$('#ad_alert').remove();
|
||||
$.cookie('ad_alert',1,{expires:24*3});
|
||||
},3000);
|
||||
});
|
||||
}
|
||||
}else if($('.view-right ins').size()>0 && document.location.pathname.match(/^\/resource\/[0-9]+/)!=null){
|
||||
const random = parseInt(Math.random()*10);
|
||||
if(random<4 && $.cookie('ad_alert_resource')!=1){
|
||||
$('.middle-box .w').css({position:'relative',left:0,top:0}).append('<img style="position:absolute;left:260px;top:270px;" id="ad_alert" src="http://image.jstucdn.com/images/ad/ad_alert3.png">').find('#ad_alert').load(function(){
|
||||
setTimeout(function(){
|
||||
$('#ad_alert').remove();
|
||||
$.cookie('ad_alert_resource',1,{expires:24*3});
|
||||
},3000);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
236
tests/data/zimuxia_result.html
vendored
@@ -1,236 +0,0 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="baidu-site-verification" content="3uvZd9Aact" />
|
||||
<title>天国与地狱 | 字幕组 FIX字幕侠 做国内最好的字幕组 天国与地狱FIX字幕侠百度网盘,FIX字幕侠天国与地狱,天国与地狱字幕下载
|
||||
|
||||
|
||||
</title>
|
||||
<meta name="keywords" content="FIX字幕组,美剧下载,西部世界,code blue,unnatural,我的危险妻子,字幕组,高清美剧,日剧下载,权力的游戏,天天美剧,高清电影,高清美剧,法剧下载,德剧下载,欧美电影,英剧下载,百度网盘,迅雷下载,BT下载" />
|
||||
<meta name="description" content="FIX字幕组 美剧 英剧 欧美电影 韩影 日剧 日影 法剧 法影 德剧 纪录片 特效字幕制作 百度网盘 迅雷下载 在线观看 BT下载 天天美剧" />
|
||||
<script>
|
||||
|
||||
var _hmt = _hmt || [];
|
||||
|
||||
(function() {
|
||||
|
||||
var hm = document.createElement("script");
|
||||
|
||||
hm.src = "//hm.baidu.com/hm.js?af75f52bfdd411d166c23dc7aa879aa5";
|
||||
|
||||
var s = document.getElementsByTagName("script")[0];
|
||||
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
|
||||
})();
|
||||
|
||||
</script>
|
||||
<script>
|
||||
var _hmt = _hmt || [];
|
||||
(function() {
|
||||
var hm = document.createElement("script");
|
||||
hm.src = "https://hm.baidu.com/hm.js?353115f6f5ead3c8d60025faed7a85dc";
|
||||
var s = document.getElementsByTagName("script")[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
</script>
|
||||
|
||||
<meta property="og:image" content="http://www.zimuxia.cn/wp-content/uploads/2021/01/tengokutojigoku46.gif" />
|
||||
|
||||
<meta itemprop="image" content="http://www.zimuxia.cn/wp-content/uploads/2021/01/tengokutojigoku46.gif">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
|
||||
<link rel="alternate" type="application/rss+xml" title="字幕组 FIX字幕侠 做国内最好的字幕组 RSS" href="https://www.zimuxia.cn/feed" />
|
||||
<link rel="pingback" href="https://www.zimuxia.cn/xmlrpc.php" />
|
||||
<!--[if lt IE 9]>
|
||||
|
||||
<script src="https://www.zimuxia.cn/wp-content/themes/thestory/js/html5shiv.js"></script>
|
||||
|
||||
<![endif]-->
|
||||
|
||||
<meta name="keywords" content="天国与地狱,天国与地狱字幕下载,字幕组,中文电影字幕下载网站,中文字幕,天国与地狱百度网盘,fix,sub,美剧,美剧排行,英剧,日剧,韩剧" />
|
||||
<link rel="canonical" href="https://www.zimuxia.cn/portfolio/%e5%a4%a9%e5%9b%bd%e4%b8%8e%e5%9c%b0%e7%8b%b1" />
|
||||
|
||||
<link rel='dns-prefetch' href='//s.w.org' />
|
||||
<link rel="alternate" type="application/rss+xml" title="字幕组 FIX字幕侠 做国内最好的字幕组 » Feed" href="https://www.zimuxia.cn/feed" />
|
||||
<link rel="alternate" type="application/rss+xml" title="字幕组 FIX字幕侠 做国内最好的字幕组 » 评论Feed" href="https://www.zimuxia.cn/comments/feed" />
|
||||
<script type="text/javascript">
|
||||
window._wpemojiSettings = {"baseUrl":"https:\/\/s.w.org\/images\/core\/emoji\/2\/72x72\/","ext":".png","svgUrl":"https:\/\/s.w.org\/images\/core\/emoji\/2\/svg\/","svgExt":".svg","source":{"concatemoji":"https:\/\/www.zimuxia.cn\/wp-includes\/js\/wp-emoji-release.min.js?ver=4.6.6"}};
|
||||
!function(a,b,c){function d(a){var c,d,e,f,g,h=b.createElement("canvas"),i=h.getContext&&h.getContext("2d"),j=String.fromCharCode;if(!i||!i.fillText)return!1;switch(i.textBaseline="top",i.font="600 32px Arial",a){case"flag":return i.fillText(j(55356,56806,55356,56826),0,0),!(h.toDataURL().length<3e3)&&(i.clearRect(0,0,h.width,h.height),i.fillText(j(55356,57331,65039,8205,55356,57096),0,0),c=h.toDataURL(),i.clearRect(0,0,h.width,h.height),i.fillText(j(55356,57331,55356,57096),0,0),d=h.toDataURL(),c!==d);case"diversity":return i.fillText(j(55356,57221),0,0),e=i.getImageData(16,16,1,1).data,f=e[0]+","+e[1]+","+e[2]+","+e[3],i.fillText(j(55356,57221,55356,57343),0,0),e=i.getImageData(16,16,1,1).data,g=e[0]+","+e[1]+","+e[2]+","+e[3],f!==g;case"simple":return i.fillText(j(55357,56835),0,0),0!==i.getImageData(16,16,1,1).data[0];case"unicode8":return i.fillText(j(55356,57135),0,0),0!==i.getImageData(16,16,1,1).data[0];case"unicode9":return i.fillText(j(55358,56631),0,0),0!==i.getImageData(16,16,1,1).data[0]}return!1}function e(a){var c=b.createElement("script");c.src=a,c.type="text/javascript",b.getElementsByTagName("head")[0].appendChild(c)}var f,g,h,i;for(i=Array("simple","flag","unicode8","diversity","unicode9"),c.supports={everything:!0,everythingExceptFlag:!0},h=0;h<i.length;h++)c.supports[i[h]]=d(i[h]),c.supports.everything=c.supports.everything&&c.supports[i[h]],"flag"!==i[h]&&(c.supports.everythingExceptFlag=c.supports.everythingExceptFlag&&c.supports[i[h]]);c.supports.everythingExceptFlag=c.supports.everythingExceptFlag&&!c.supports.flag,c.DOMReady=!1,c.readyCallback=function(){c.DOMReady=!0},c.supports.everything||(g=function(){c.readyCallback()},b.addEventListener?(b.addEventListener("DOMContentLoaded",g,!1),a.addEventListener("load",g,!1)):(a.attachEvent("onload",g),b.attachEvent("onreadystatechange",function(){"complete"===b.readyState&&c.readyCallback()})),f=c.source||{},f.concatemoji?e(f.concatemoji):f.wpemoji&&f.twemoji&&(e(f.twemoji),e(f.wpemoji)))}(window,document,window._wpemojiSettings);
|
||||
</script>
|
||||
<style type="text/css">
|
||||
img.wp-smiley,
|
||||
img.emoji {
|
||||
display: inline !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
height: 1em !important;
|
||||
width: 1em !important;
|
||||
margin: 0 .07em !important;
|
||||
vertical-align: -0.1em !important;
|
||||
background: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
</style>
|
||||
<link rel='stylesheet' id='fix_tvcard_ui-css' href='https://www.zimuxia.cn/wp-content/plugins/fixtvcard-0.5/fixtvs.css?ver=0.5' type='text/css' media='screen' />
|
||||
<link rel='stylesheet' id='pexeto-pretty-photo-css' href='https://www.zimuxia.cn/wp-content/themes/thestory/css/prettyPhoto.css?ver=1.5.0' type='text/css' media='all' />
|
||||
<link rel='stylesheet' id='pexeto-stylesheet-css' href='https://www.zimuxia.cn/wp-content/themes/thestory/style.css?ver=1.5.0' type='text/css' media='all' />
|
||||
<style id='pexeto-stylesheet-inline-css' type='text/css'>
|
||||
body, .page-wrapper, #sidebar input[type="text"],
|
||||
#sidebar input[type="password"], #sidebar textarea, .comment-respond input[type="text"],
|
||||
.comment-respond textarea{background-color:#ffffff;}.header-wrapper, .pg-navigation, .mobile.page-template-template-fullscreen-slider-php #header, .mobile.page-template-template-fullscreen-slider-php .header-wrapper{background-color:#0a0707;}.dark-header #header{background-color:rgba(10,5,6,0.7);}.fixed-header-scroll #header{background-color:rgba(10,5,6,0.95);}h1,h2,h3,h4,h5,h6,.pt-price{font-family:Microsoft Yahei;}body{font-family:Microsoft Yahei;font-size:14px;}body, #footer, .sidebar-box, .services-box, .ps-content, .page-masonry .post, .services-title-box{font-size:14px;}#menu ul li a{font-family:Microsoft Yahei;font-size:18px;font-weight:bold;}.page-title h1{font-family:Microsoft Yahei;font-size:58px;}.sidebar-box .title, .footer-box .title{font-family:Microsoft Yahei;font-size:58px;}
|
||||
</style>
|
||||
<!--[if lte IE 8]>
|
||||
<link rel='stylesheet' id='pexeto-ie8-css' href='https://www.zimuxia.cn/wp-content/themes/thestory/css/style_ie8.css?ver=1.5.0' type='text/css' media='all' />
|
||||
<![endif]-->
|
||||
<script type='text/javascript' src='https://www.zimuxia.cn/wp-includes/js/jquery/jquery.js?ver=1.12.4'></script>
|
||||
<script type='text/javascript' src='https://www.zimuxia.cn/wp-includes/js/jquery/jquery-migrate.min.js?ver=1.4.1'></script>
|
||||
<script type='text/javascript' src='https://www.zimuxia.cn/wp-content/plugins/fixtvcard-0.5/assets/plugins/moment.min.js?ver=0.5'></script>
|
||||
<script type='text/javascript' src='https://www.zimuxia.cn/wp-content/plugins/fixtvcard-0.5/assets/plugins/moment-timezone.min.js?ver=0.5'></script>
|
||||
<link rel='https://api.w.org/' href='https://www.zimuxia.cn/wp-json/' />
|
||||
<link rel="EditURI" type="application/rsd+xml" title="RSD" href="https://www.zimuxia.cn/xmlrpc.php?rsd" />
|
||||
<link rel="wlwmanifest" type="application/wlwmanifest+xml" href="https://www.zimuxia.cn/wp-includes/wlwmanifest.xml" />
|
||||
<meta name="generator" content="WordPress 4.6.6" />
|
||||
<link rel='shortlink' href='https://www.zimuxia.cn/?p=7026' />
|
||||
<link rel="alternate" type="application/json+oembed" href="https://www.zimuxia.cn/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fwww.zimuxia.cn%2Fportfolio%2F%25e5%25a4%25a9%25e5%259b%25bd%25e4%25b8%258e%25e5%259c%25b0%25e7%258b%25b1" />
|
||||
<link rel="alternate" type="text/xml+oembed" href="https://www.zimuxia.cn/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fwww.zimuxia.cn%2Fportfolio%2F%25e5%25a4%25a9%25e5%259b%25bd%25e4%25b8%258e%25e5%259c%25b0%25e7%258b%25b1&format=xml" />
|
||||
<style type="text/css">.recentcomments a{display:inline !important;padding:0 !important;margin:0 !important;}</style>
|
||||
<link rel="icon" href="https://www.zimuxia.cn/wp-content/uploads/2016/03/cropped-logo2-32x32.png" sizes="32x32" />
|
||||
<link rel="icon" href="https://www.zimuxia.cn/wp-content/uploads/2016/03/cropped-logo2-192x192.png" sizes="192x192" />
|
||||
<link rel="apple-touch-icon-precomposed" href="https://www.zimuxia.cn/wp-content/uploads/2016/03/cropped-logo2-180x180.png" />
|
||||
<meta name="msapplication-TileImage" content="https://www.zimuxia.cn/wp-content/uploads/2016/03/cropped-logo2-270x270.png" />
|
||||
</head>
|
||||
<body class="single single-portfolio postid-7026 thestory fixed-header no-slider icons-style-light">
|
||||
<div id="main-container">
|
||||
<div class="page-wrapper">
|
||||
|
||||
<div class="header-wrapper">
|
||||
<header id="header">
|
||||
<div class="section-boxed section-header">
|
||||
<div id="logo-container">
|
||||
<a href="https://www.zimuxia.cn/"><img class="logo" src="https://www.zimuxia.cn/wp-content/uploads/2016/03/logo.png" alt="字幕组 FIX字幕侠 做国内最好的字幕组" /></a>
|
||||
</div>
|
||||
<div class="mobile-nav">
|
||||
<span class="mob-nav-btn">Menu</span>
|
||||
</div>
|
||||
<nav class="navigation-container">
|
||||
<div id="menu" class="nav-menu">
|
||||
<ul id="menu-fix-top" class="menu-ul"><li id="menu-item-3795" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-home menu-item-3795"><a href="https://www.zimuxia.cn">首页</a></li>
|
||||
<li id="menu-item-2470" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-2470"><a href="https://www.zimuxia.cn/%e6%88%91%e4%bb%ac%e7%9a%84%e4%bd%9c%e5%93%81">FIX作品</a></li>
|
||||
<li id="menu-item-4996" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-4996"><a target="_blank" href="https://weibo.com/5466307917/GayzHBmuh?type=comment">度盘失效修复</a></li>
|
||||
<li id="menu-item-6354" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-6354"><a target="_blank" href="https://www.zimuxia.cn/wp-content/uploads/2020/01/erweima1.jpg">关注微信</a></li>
|
||||
<li id="menu-item-2468" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-2468"><a href="https://www.zimuxia.cn/%e5%8a%a0%e5%85%a5%e6%88%91%e4%bb%ac">加入我们</a></li>
|
||||
<li id="menu-item-4027" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-4027"><a target="_blank" href="https://www.zimuxia.cn/%e5%85%b3%e4%ba%8efix">关于FIX</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="header-buttons">
|
||||
<div class="header-search">
|
||||
<div class="search-wrapper">
|
||||
<form role="search" method="get" class="searchform" action="https://www.zimuxia.cn">
|
||||
<input type="text" name="s" class="search-input placeholder" placeholder="Search" />
|
||||
<input type="submit" value="" class="search-button" />
|
||||
</form>
|
||||
</div>
|
||||
<a href="#" class="header-search-btn">Search</a></div>
|
||||
<div class="social-profiles"><ul class="social-icons">
|
||||
<li>
|
||||
<a href="https://weibo.com/fixmeiju" target="_blank" title="关注FIX字幕侠">
|
||||
<div>
|
||||
<img src="https://www.zimuxia.cn/wp-content/uploads/2015/02/sina.png" alt="" />
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul></div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="clear"></div>
|
||||
<div id="navigation-line"></div>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
<div id="content-container" class="content-boxed layout-full">
|
||||
<div id="full-width" class="content">
|
||||
<div class="content-box">
|
||||
<h2 class="content-page-title">天国与地狱</h2>
|
||||
<h2 style="text-align: center;">天国与地狱</h2>
|
||||
<div><img class="img-frame aligncenter" src="https://www.zimuxia.cn/wp-content/uploads/2021/01/tengokutojigoku588.jpg" alt="天国与地狱" width="1000" height="720" /></div>
|
||||
<div><span class="pl">导演</span>: <span class="attrs">平川雄一朗 / 青山贵洋 / 松木彩</span><br />
|
||||
<span class="pl">编剧</span>: <span class="attrs">森下佳子</span><br />
|
||||
<span class="actor"><span class="pl">主演</span>: <span class="attrs">绫濑遥 / 高桥一生 / 北村一辉 / 柄本佑 / 沟端淳平 / 更多…</span></span><br />
|
||||
<span class="pl">类型:</span> 悬疑 / 犯罪<br />
|
||||
<span class="pl">官方网站:</span> <a href="http://www.tbs.co.jp/tengokutojigoku_tbs/" target="_blank" rel="nofollow">www.tbs.co.jp/tengokutojigoku_tbs/</a><br />
|
||||
<span class="pl">制片国家/地区:</span> 日本<br />
|
||||
<span class="pl">语言:</span> 日语<br />
|
||||
<span class="pl">首播:</span> 2021-01-17(日本)<br />
|
||||
<span class="pl">单集片长:</span> 54分钟</div>
|
||||
<div></div>
|
||||
<div>【剧情简介】</div>
|
||||
<div> 绫濑遥将饰演本剧女主角、东京警视厅搜查一课的正义女警望月彩子,不幸与高桥一生饰演的高智商杀人犯日高互换灵魂,两个人从说话、生活到思维方式都发了逆转,日子变得一团糟,而柄本佑饰演的邻家自由业者渡边,和北村一辉饰演的彩子上司河原,也参与其中。本剧编剧为森下佳子(《乱世花道》《天皇的御厨》),导演为平川雄一朗(《天皇的御厨》)、松木彩(《半泽直树2》)和青山贵洋,每周日晚9点播出。</div>
|
||||
<div></div>
|
||||
<div>
|
||||
<div>【资源下载】</div>
|
||||
<div>
|
||||
<div>E01 UC网盘 <a href="https://115.com/s/swn3d213f3n?password=FIXX&#" target="_blank">115网盘</a> <a href="https://pan.baidu.com/s/10la5ThmjXLwKNTcP2y4P7A" target="_blank">百度网盘</a> <a href="magnet:?xt=urn:btih:84754072F7D436C718A24CB73598946DEF430FB5&dn=Tengokutojigoku.E01.720p.FIX%e5%ad%97%e5%b9%95%e4%be%a0&tr=http%3a%2f%2ft.nyaatracker.com%2fannounce&tr=http%3a%2f%2fshare.camoe.cn%3a8080%2fannounce&tr=http%3a%2f%2ftracker.kamigami.org%3a2710%2fannounce" target="_blank">磁力下载</a> <a href="ed2k://|file|Tengokutojigoku.E01.720p.FIX%E5%AD%97%E5%B9%95%E4%BE%A0.mp4|1148270190|0ED996E48B61F974FDCC8DA9EC05F424|h=6UMM5EHCOGEQWP234IKR7LQYXWUJJDDR|/" target="_blank">电驴下载</a> 提取码:FIXX<br />
|
||||
E02 UC网盘 115网盘 百度网盘 磁力下载 电驴下载 提取码:FIXX<br />
|
||||
E03 UC网盘 115网盘 百度网盘 磁力下载 电驴下载 提取码:FIXX<br />
|
||||
E04 UC网盘 115网盘 百度网盘 磁力下载 电驴下载 提取码:FIXX<br />
|
||||
E05 UC网盘 115网盘 百度网盘 磁力下载 电驴下载 提取码:FIXX<br />
|
||||
E06 UC网盘 115网盘 百度网盘 磁力下载 电驴下载 提取码:FIXX<br />
|
||||
E07 UC网盘 115网盘 百度网盘 磁力下载 电驴下载 提取码:FIXX</div>
|
||||
<div>E08 UC网盘 115网盘 百度网盘 磁力下载 电驴下载 提取码:FIXX<br />
|
||||
E09 UC网盘 115网盘 百度网盘 磁力下载 电驴下载 提取码:FIXX</div>
|
||||
<div>E10 UC网盘 115网盘 百度网盘 磁力下载 电驴下载 提取码:FIXX</div>
|
||||
</div>
|
||||
</div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</div>
|
||||
<footer id="footer" class="center">
|
||||
<div class="scroll-to-top"><span></span></div>
|
||||
<div class="footer-bottom">
|
||||
<div class="section-boxed">
|
||||
<span class="copyrights">
|
||||
©copyright <a href="https://www.zimuxia.cn" title="FIX字幕侠">FIX字幕侠</a>2018-2020 蜀ICP备17039209号
|
||||
</span>
|
||||
<div class="footer-nav">
|
||||
<div class="menu-fix-footer-container"><ul id="menu-fix-footer" class="menu"><li id="menu-item-2819" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-2819"><a target="_blank" href="http://subhd.com/zu/28">FIX字幕站</a></li>
|
||||
<li id="menu-item-5017" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-5017"><a target="_blank" href="https://www.zhaiiker.com/">宅客Zhaiiker</a></li>
|
||||
</ul></div>
|
||||
</div>
|
||||
<div class="social-profiles"><ul class="social-icons">
|
||||
<li>
|
||||
<a href="https://weibo.com/fixmeiju" target="_blank" title="关注FIX字幕侠">
|
||||
<div>
|
||||
<img src="https://www.zimuxia.cn/wp-content/uploads/2015/02/sina.png" alt="" />
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul></div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<link rel='stylesheet' id='ivory-search-styles-css' href='https://www.zimuxia.cn/wp-content/plugins/add-search-to-menu/public/css/ivory-search.css?ver=4.4.6' type='text/css' media='all' />
|
||||
<script type='text/javascript' src='https://www.zimuxia.cn/wp-content/plugins/add-search-to-menu/public/js/ivory-search.js?ver=4.4.6'></script>
|
||||
<script type='text/javascript' src='https://www.zimuxia.cn/wp-content/plugins/fixtvcard-0.5/app.js?ver=0.5'></script>
|
||||
<script type='text/javascript' src='https://www.zimuxia.cn/wp-includes/js/underscore.min.js?ver=1.8.3'></script>
|
||||
<script type='text/javascript' src='https://www.zimuxia.cn/wp-content/themes/thestory/js/main.js?ver=1.5.0'></script>
|
||||
<script type='text/javascript' src='https://www.zimuxia.cn/wp-content/themes/thestory/js/portfolio-gallery.js?ver=1.5.0'></script>
|
||||
<script type='text/javascript' src='https://www.zimuxia.cn/wp-includes/js/comment-reply.min.js?ver=4.6.6'></script>
|
||||
<script type='text/javascript' src='https://www.zimuxia.cn/wp-includes/js/wp-embed.min.js?ver=4.6.6'></script>
|
||||
<script type="text/javascript">var PEXETO = PEXETO || {};PEXETO.ajaxurl="https://www.zimuxia.cn/wp-admin/admin-ajax.php";PEXETO.lightboxOptions = {"theme":"pp_default","animation_speed":"normal","overlay_gallery":false,"allow_resize":true};PEXETO.disableRightClick=false;PEXETO.stickyHeader=true;jQuery(document).ready(function($){
|
||||
PEXETO.init.initSite();$(".pexeto-contact-form").each(function(){
|
||||
$(this).pexetoContactForm({"wrongCaptchaText":"The text you have entered did not match the text on the image. Please try again.","failText":"An error occurred. Message not sent.","validationErrorText":"Please complete all the fields correctly","messageSentText":"Message sent"});
|
||||
});});</script>
|
||||
<script type="text/javascript" src="https://idm-su.baidu.com/su_new.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
259
tests/data/zimuxia_search.html
vendored
@@ -1,259 +0,0 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="baidu-site-verification" content="3uvZd9Aact" />
|
||||
<title>You searched for 逃避 | 字幕组 FIX字幕侠 做国内最好的字幕组
|
||||
|
||||
|
||||
</title>
|
||||
<meta name="keywords" content="FIX字幕组,美剧下载,西部世界,code blue,unnatural,我的危险妻子,字幕组,高清美剧,日剧下载,权力的游戏,天天美剧,高清电影,高清美剧,法剧下载,德剧下载,欧美电影,英剧下载,百度网盘,迅雷下载,BT下载" />
|
||||
<meta name="description" content="FIX字幕组 美剧 英剧 欧美电影 韩影 日剧 日影 法剧 法影 德剧 纪录片 特效字幕制作 百度网盘 迅雷下载 在线观看 BT下载 天天美剧" />
|
||||
<script>
|
||||
|
||||
var _hmt = _hmt || [];
|
||||
|
||||
(function() {
|
||||
|
||||
var hm = document.createElement("script");
|
||||
|
||||
hm.src = "//hm.baidu.com/hm.js?af75f52bfdd411d166c23dc7aa879aa5";
|
||||
|
||||
var s = document.getElementsByTagName("script")[0];
|
||||
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
|
||||
})();
|
||||
|
||||
</script>
|
||||
<script>
|
||||
var _hmt = _hmt || [];
|
||||
(function() {
|
||||
var hm = document.createElement("script");
|
||||
hm.src = "https://hm.baidu.com/hm.js?353115f6f5ead3c8d60025faed7a85dc";
|
||||
var s = document.getElementsByTagName("script")[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
</script>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
|
||||
<link rel="alternate" type="application/rss+xml" title="字幕组 FIX字幕侠 做国内最好的字幕组 RSS" href="https://www.zimuxia.cn/feed" />
|
||||
<link rel="pingback" href="https://www.zimuxia.cn/xmlrpc.php" />
|
||||
<!--[if lt IE 9]>
|
||||
|
||||
<script src="https://www.zimuxia.cn/wp-content/themes/thestory/js/html5shiv.js"></script>
|
||||
|
||||
<![endif]-->
|
||||
|
||||
<meta name="robots" content="noindex,follow" />
|
||||
<link rel="canonical" href="https://www.zimuxia.cn/search/%E9%80%83%E9%81%BF" />
|
||||
|
||||
<link rel='dns-prefetch' href='//s.w.org' />
|
||||
<link rel="alternate" type="application/rss+xml" title="字幕组 FIX字幕侠 做国内最好的字幕组 » Feed" href="https://www.zimuxia.cn/feed" />
|
||||
<link rel="alternate" type="application/rss+xml" title="字幕组 FIX字幕侠 做国内最好的字幕组 » 评论Feed" href="https://www.zimuxia.cn/comments/feed" />
|
||||
<link rel="alternate" type="application/rss+xml" title="字幕组 FIX字幕侠 做国内最好的字幕组 » “逃避”的搜索结果Feed" href="https://www.zimuxia.cn/search/%E9%80%83%E9%81%BF/feed/rss2/" />
|
||||
<script type="text/javascript">
|
||||
window._wpemojiSettings = {"baseUrl":"https:\/\/s.w.org\/images\/core\/emoji\/2\/72x72\/","ext":".png","svgUrl":"https:\/\/s.w.org\/images\/core\/emoji\/2\/svg\/","svgExt":".svg","source":{"concatemoji":"https:\/\/www.zimuxia.cn\/wp-includes\/js\/wp-emoji-release.min.js?ver=4.6.6"}};
|
||||
!function(a,b,c){function d(a){var c,d,e,f,g,h=b.createElement("canvas"),i=h.getContext&&h.getContext("2d"),j=String.fromCharCode;if(!i||!i.fillText)return!1;switch(i.textBaseline="top",i.font="600 32px Arial",a){case"flag":return i.fillText(j(55356,56806,55356,56826),0,0),!(h.toDataURL().length<3e3)&&(i.clearRect(0,0,h.width,h.height),i.fillText(j(55356,57331,65039,8205,55356,57096),0,0),c=h.toDataURL(),i.clearRect(0,0,h.width,h.height),i.fillText(j(55356,57331,55356,57096),0,0),d=h.toDataURL(),c!==d);case"diversity":return i.fillText(j(55356,57221),0,0),e=i.getImageData(16,16,1,1).data,f=e[0]+","+e[1]+","+e[2]+","+e[3],i.fillText(j(55356,57221,55356,57343),0,0),e=i.getImageData(16,16,1,1).data,g=e[0]+","+e[1]+","+e[2]+","+e[3],f!==g;case"simple":return i.fillText(j(55357,56835),0,0),0!==i.getImageData(16,16,1,1).data[0];case"unicode8":return i.fillText(j(55356,57135),0,0),0!==i.getImageData(16,16,1,1).data[0];case"unicode9":return i.fillText(j(55358,56631),0,0),0!==i.getImageData(16,16,1,1).data[0]}return!1}function e(a){var c=b.createElement("script");c.src=a,c.type="text/javascript",b.getElementsByTagName("head")[0].appendChild(c)}var f,g,h,i;for(i=Array("simple","flag","unicode8","diversity","unicode9"),c.supports={everything:!0,everythingExceptFlag:!0},h=0;h<i.length;h++)c.supports[i[h]]=d(i[h]),c.supports.everything=c.supports.everything&&c.supports[i[h]],"flag"!==i[h]&&(c.supports.everythingExceptFlag=c.supports.everythingExceptFlag&&c.supports[i[h]]);c.supports.everythingExceptFlag=c.supports.everythingExceptFlag&&!c.supports.flag,c.DOMReady=!1,c.readyCallback=function(){c.DOMReady=!0},c.supports.everything||(g=function(){c.readyCallback()},b.addEventListener?(b.addEventListener("DOMContentLoaded",g,!1),a.addEventListener("load",g,!1)):(a.attachEvent("onload",g),b.attachEvent("onreadystatechange",function(){"complete"===b.readyState&&c.readyCallback()})),f=c.source||{},f.concatemoji?e(f.concatemoji):f.wpemoji&&f.twemoji&&(e(f.twemoji),e(f.wpemoji)))}(window,document,window._wpemojiSettings);
|
||||
</script>
|
||||
<style type="text/css">
|
||||
img.wp-smiley,
|
||||
img.emoji {
|
||||
display: inline !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
height: 1em !important;
|
||||
width: 1em !important;
|
||||
margin: 0 .07em !important;
|
||||
vertical-align: -0.1em !important;
|
||||
background: none !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
</style>
|
||||
<link rel='stylesheet' id='fix_tvcard_ui-css' href='https://www.zimuxia.cn/wp-content/plugins/fixtvcard-0.5/fixtvs.css?ver=0.5' type='text/css' media='screen' />
|
||||
<link rel='stylesheet' id='pexeto-pretty-photo-css' href='https://www.zimuxia.cn/wp-content/themes/thestory/css/prettyPhoto.css?ver=1.5.0' type='text/css' media='all' />
|
||||
<link rel='stylesheet' id='pexeto-stylesheet-css' href='https://www.zimuxia.cn/wp-content/themes/thestory/style.css?ver=1.5.0' type='text/css' media='all' />
|
||||
<style id='pexeto-stylesheet-inline-css' type='text/css'>
|
||||
body, .page-wrapper, #sidebar input[type="text"],
|
||||
#sidebar input[type="password"], #sidebar textarea, .comment-respond input[type="text"],
|
||||
.comment-respond textarea{background-color:#ffffff;}.header-wrapper, .pg-navigation, .mobile.page-template-template-fullscreen-slider-php #header, .mobile.page-template-template-fullscreen-slider-php .header-wrapper{background-color:#0a0707;}.dark-header #header{background-color:rgba(10,5,6,0.7);}.fixed-header-scroll #header{background-color:rgba(10,5,6,0.95);}h1,h2,h3,h4,h5,h6,.pt-price{font-family:Microsoft Yahei;}body{font-family:Microsoft Yahei;font-size:14px;}body, #footer, .sidebar-box, .services-box, .ps-content, .page-masonry .post, .services-title-box{font-size:14px;}#menu ul li a{font-family:Microsoft Yahei;font-size:18px;font-weight:bold;}.page-title h1{font-family:Microsoft Yahei;font-size:58px;}.sidebar-box .title, .footer-box .title{font-family:Microsoft Yahei;font-size:58px;}
|
||||
</style>
|
||||
<!--[if lte IE 8]>
|
||||
<link rel='stylesheet' id='pexeto-ie8-css' href='https://www.zimuxia.cn/wp-content/themes/thestory/css/style_ie8.css?ver=1.5.0' type='text/css' media='all' />
|
||||
<![endif]-->
|
||||
<script type='text/javascript' src='https://www.zimuxia.cn/wp-includes/js/jquery/jquery.js?ver=1.12.4'></script>
|
||||
<script type='text/javascript' src='https://www.zimuxia.cn/wp-includes/js/jquery/jquery-migrate.min.js?ver=1.4.1'></script>
|
||||
<script type='text/javascript' src='https://www.zimuxia.cn/wp-content/plugins/fixtvcard-0.5/assets/plugins/moment.min.js?ver=0.5'></script>
|
||||
<script type='text/javascript' src='https://www.zimuxia.cn/wp-content/plugins/fixtvcard-0.5/assets/plugins/moment-timezone.min.js?ver=0.5'></script>
|
||||
<link rel='https://api.w.org/' href='https://www.zimuxia.cn/wp-json/' />
|
||||
<link rel="EditURI" type="application/rsd+xml" title="RSD" href="https://www.zimuxia.cn/xmlrpc.php?rsd" />
|
||||
<link rel="wlwmanifest" type="application/wlwmanifest+xml" href="https://www.zimuxia.cn/wp-includes/wlwmanifest.xml" />
|
||||
<meta name="generator" content="WordPress 4.6.6" />
|
||||
<style type="text/css">.recentcomments a{display:inline !important;padding:0 !important;margin:0 !important;}</style>
|
||||
<link rel="icon" href="https://www.zimuxia.cn/wp-content/uploads/2016/03/cropped-logo2-32x32.png" sizes="32x32" />
|
||||
<link rel="icon" href="https://www.zimuxia.cn/wp-content/uploads/2016/03/cropped-logo2-192x192.png" sizes="192x192" />
|
||||
<link rel="apple-touch-icon-precomposed" href="https://www.zimuxia.cn/wp-content/uploads/2016/03/cropped-logo2-180x180.png" />
|
||||
<meta name="msapplication-TileImage" content="https://www.zimuxia.cn/wp-content/uploads/2016/03/cropped-logo2-270x270.png" />
|
||||
</head>
|
||||
<body class="search search-results thestory fixed-header contains-posts no-slider icons-style-light">
|
||||
<div id="main-container">
|
||||
<div class="page-wrapper">
|
||||
|
||||
<div class="header-wrapper">
|
||||
<header id="header">
|
||||
<div class="section-boxed section-header">
|
||||
<div id="logo-container">
|
||||
<a href="https://www.zimuxia.cn/"><img class="logo" src="https://www.zimuxia.cn/wp-content/uploads/2016/03/logo.png" alt="字幕组 FIX字幕侠 做国内最好的字幕组" /></a>
|
||||
</div>
|
||||
<div class="mobile-nav">
|
||||
<span class="mob-nav-btn">Menu</span>
|
||||
</div>
|
||||
<nav class="navigation-container">
|
||||
<div id="menu" class="nav-menu">
|
||||
<ul id="menu-fix-top" class="menu-ul"><li id="menu-item-3795" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-home menu-item-3795"><a href="https://www.zimuxia.cn">首页</a></li>
|
||||
<li id="menu-item-2470" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-2470"><a href="https://www.zimuxia.cn/%e6%88%91%e4%bb%ac%e7%9a%84%e4%bd%9c%e5%93%81">FIX作品</a></li>
|
||||
<li id="menu-item-4996" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-4996"><a target="_blank" href="https://weibo.com/5466307917/GayzHBmuh?type=comment">度盘失效修复</a></li>
|
||||
<li id="menu-item-6354" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-6354"><a target="_blank" href="https://www.zimuxia.cn/wp-content/uploads/2020/01/erweima1.jpg">关注微信</a></li>
|
||||
<li id="menu-item-2468" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-2468"><a href="https://www.zimuxia.cn/%e5%8a%a0%e5%85%a5%e6%88%91%e4%bb%ac">加入我们</a></li>
|
||||
<li id="menu-item-4027" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-4027"><a target="_blank" href="https://www.zimuxia.cn/%e5%85%b3%e4%ba%8efix">关于FIX</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="header-buttons">
|
||||
<div class="header-search">
|
||||
<div class="search-wrapper">
|
||||
<form role="search" method="get" class="searchform" action="https://www.zimuxia.cn">
|
||||
<input type="text" name="s" class="search-input placeholder" placeholder="Search" />
|
||||
<input type="submit" value="" class="search-button" />
|
||||
</form>
|
||||
</div>
|
||||
<a href="#" class="header-search-btn">Search</a></div>
|
||||
<div class="social-profiles"><ul class="social-icons">
|
||||
<li>
|
||||
<a href="https://weibo.com/fixmeiju" target="_blank" title="关注FIX字幕侠">
|
||||
<div>
|
||||
<img src="https://www.zimuxia.cn/wp-content/uploads/2015/02/sina.png" alt="" />
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul></div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="clear"></div>
|
||||
<div id="navigation-line"></div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page-title-wrapper"><div class="page-title">
|
||||
<div class="content-boxed">
|
||||
<h1>Search results for "逃避"</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="content-container" class="content-boxed layout-full">
|
||||
<div id="full-width" class="content">
|
||||
<article id="post-4338" class="blog-non-single-post theme-post-entry post-4338 portfolio type-portfolio status-publish hentry portfolio_category-fix">
|
||||
<div class="post-content no-thumbnail">
|
||||
<div class="post-title-wrapper">
|
||||
<h2 class="post-title">
|
||||
<a href="https://www.zimuxia.cn/portfolio/%e9%80%83%e8%b7%91%e5%8f%af%e8%80%bb%e4%bd%86%e6%9c%89%e7%94%a8">逃避可耻但有用</a>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
<div class="post-content-content">
|
||||
<p>逃避可耻但有用 导演: 金子文紀 / 土井裕泰 / 石井康晴 编剧: 野木亜紀子 主演: 新垣结衣 / 星野源 […]</p>
|
||||
<a href="https://www.zimuxia.cn/portfolio/%e9%80%83%e8%b7%91%e5%8f%af%e8%80%bb%e4%bd%86%e6%9c%89%e7%94%a8" class="read-more">
|
||||
Read More
|
||||
<span class="more-arrow">›</span>
|
||||
</a>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</article>
|
||||
<article id="post-5967" class="blog-non-single-post theme-post-entry post-5967 portfolio type-portfolio status-publish hentry portfolio_category-48">
|
||||
<div class="post-content no-thumbnail">
|
||||
<div class="post-title-wrapper">
|
||||
<h2 class="post-title">
|
||||
<a href="https://www.zimuxia.cn/portfolio/%e4%ba%a2%e5%a5%8b">亢奋</a>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
<div class="post-content-content">
|
||||
<p>亢奋 导演: 奥古斯丁·弗里泽尔 编剧: 萨姆·李文森 主演: 赞达亚 / 茉德·阿帕图 / Brian Br […]</p>
|
||||
<a href="https://www.zimuxia.cn/portfolio/%e4%ba%a2%e5%a5%8b" class="read-more">
|
||||
Read More
|
||||
<span class="more-arrow">›</span>
|
||||
</a>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</article>
|
||||
<article id="post-5908" class="blog-non-single-post theme-post-entry post-5908 portfolio type-portfolio status-publish hentry portfolio_category-48">
|
||||
<div class="post-content no-thumbnail">
|
||||
<div class="post-title-wrapper">
|
||||
<h2 class="post-title">
|
||||
<a href="https://www.zimuxia.cn/portfolio/%e7%ac%ac%e4%ba%8c%e5%8d%81%e4%ba%8c%e6%9d%a1%e5%86%9b%e8%a7%84">第二十二条军规</a>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
<div class="post-content-content">
|
||||
<p>第二十二条军规 导演: 乔治·克鲁尼 / 格兰特·赫斯洛夫 / 艾伦·库拉斯 编剧: 卢克·戴维斯 / 大卫· […]</p>
|
||||
<a href="https://www.zimuxia.cn/portfolio/%e7%ac%ac%e4%ba%8c%e5%8d%81%e4%ba%8c%e6%9d%a1%e5%86%9b%e8%a7%84" class="read-more">
|
||||
Read More
|
||||
<span class="more-arrow">›</span>
|
||||
</a>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</article>
|
||||
<div id="blog-pagination">
|
||||
<div class="alignleft"></div>
|
||||
<div class="alignright"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</div>
|
||||
<footer id="footer" class="center">
|
||||
<div class="scroll-to-top"><span></span></div>
|
||||
<div class="footer-bottom">
|
||||
<div class="section-boxed">
|
||||
<span class="copyrights">
|
||||
©copyright <a href="https://www.zimuxia.cn" title="FIX字幕侠">FIX字幕侠</a>2018-2020 蜀ICP备17039209号
|
||||
</span>
|
||||
<div class="footer-nav">
|
||||
<div class="menu-fix-footer-container"><ul id="menu-fix-footer" class="menu"><li id="menu-item-2819" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-2819"><a target="_blank" href="http://subhd.com/zu/28">FIX字幕站</a></li>
|
||||
<li id="menu-item-5017" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-5017"><a target="_blank" href="https://www.zhaiiker.com/">宅客Zhaiiker</a></li>
|
||||
</ul></div>
|
||||
</div>
|
||||
<div class="social-profiles"><ul class="social-icons">
|
||||
<li>
|
||||
<a href="https://weibo.com/fixmeiju" target="_blank" title="关注FIX字幕侠">
|
||||
<div>
|
||||
<img src="https://www.zimuxia.cn/wp-content/uploads/2015/02/sina.png" alt="" />
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul></div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<link rel='stylesheet' id='ivory-search-styles-css' href='https://www.zimuxia.cn/wp-content/plugins/add-search-to-menu/public/css/ivory-search.css?ver=4.4.6' type='text/css' media='all' />
|
||||
<script type='text/javascript' src='https://www.zimuxia.cn/wp-content/plugins/add-search-to-menu/public/js/ivory-search.js?ver=4.4.6'></script>
|
||||
<script type='text/javascript' src='https://www.zimuxia.cn/wp-content/plugins/fixtvcard-0.5/app.js?ver=0.5'></script>
|
||||
<script type='text/javascript' src='https://www.zimuxia.cn/wp-includes/js/underscore.min.js?ver=1.8.3'></script>
|
||||
<script type='text/javascript' src='https://www.zimuxia.cn/wp-content/themes/thestory/js/main.js?ver=1.5.0'></script>
|
||||
<script type='text/javascript' src='https://www.zimuxia.cn/wp-includes/js/wp-embed.min.js?ver=4.6.6'></script>
|
||||
<script type="text/javascript">var PEXETO = PEXETO || {};PEXETO.ajaxurl="https://www.zimuxia.cn/wp-admin/admin-ajax.php";PEXETO.lightboxOptions = {"theme":"pp_default","animation_speed":"normal","overlay_gallery":false,"allow_resize":true};PEXETO.disableRightClick=false;PEXETO.stickyHeader=true;jQuery(document).ready(function($){
|
||||
PEXETO.init.initSite();$(".pexeto-contact-form").each(function(){
|
||||
$(this).pexetoContactForm({"wrongCaptchaText":"The text you have entered did not match the text on the image. Please try again.","failText":"An error occurred. Message not sent.","validationErrorText":"Please complete all the fields correctly","messageSentText":"Message sent"});
|
||||
});});</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/usr/local/bin/python3
|
||||
# coding: utf-8
|
||||
|
||||
# YYeTsBot - test_bot.py
|
||||
# 1/31/21 14:05
|
||||
#
|
||||
|
||||
__author__ = "Benny <benny.think@gmail.com>"
|
||||
|
||||
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])
|
||||
@@ -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()
|
||||
@@ -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
|
||||
|
||||
@@ -7,13 +7,17 @@
|
||||
|
||||
__author__ = "Benny <benny.think@gmail.com>"
|
||||
|
||||
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("</reply>")[-1]
|
||||
body = f"{username} 您好,<br>你的评论 {parent_comment['content']} 有了新的回复:<br>{pt_content}" \
|
||||
f"<br>你可以<a href='{link}'>点此链接</a>查看<br><br>请勿回复此邮件"
|
||||
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} 您好,<br>请输入如下验证码完成你的邮箱认证。验证码有效期为24小时。<br>" \
|
||||
f"如果您未有此请求,请忽略此邮件。<br><br>验证码: {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.")
|
||||
|
||||
@@ -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();
|
||||
|
||||
```
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/usr/local/bin/python3
|
||||
# coding: utf-8
|
||||
|
||||
# YYeTsBot - douban.py
|
||||
# 7/10/21 22:59
|
||||
#
|
||||
|
||||
__author__ = "Benny <benny.think@gmail.com>"
|
||||
|
||||
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)
|
||||
@@ -1,642 +0,0 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" class="ua-linux ua-webkit">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta name="referrer" content="always">
|
||||
<meta name="google-site-verification" content="ok0wCgT20tBBgo9_zat2iAcimtN4Ftf5ccsh092Xeyw" />
|
||||
<title>
|
||||
搜索: 逃避可耻却有用
|
||||
</title>
|
||||
|
||||
|
||||
<meta http-equiv="Pragma" content="no-cache">
|
||||
<meta http-equiv="Expires" content="Sun, 6 Mar 2005 01:00:00 GMT">
|
||||
|
||||
<link rel="search" type="application/opensearchdescription+xml" title="豆瓣搜索" href="/opensearch">
|
||||
|
||||
<script >var _head_start = new Date();</script>
|
||||
<script src="https://img3.doubanio.com/f/shire/72ced6df41d4d158420cebdd254f9562942464e3/js/jquery.min.js"></script>
|
||||
<script src="https://img3.doubanio.com/f/shire/22ee83f45f94c7a90e73e0ee4acd18f902a6991f/js/douban.js"></script>
|
||||
<link href="https://img3.doubanio.com/f/shire/6522c42d2aba9757aeefa0c35cc0cefc9229747c/css/douban.css" rel="stylesheet" type="text/css">
|
||||
<style type="text/css">
|
||||
|
||||
|
||||
h1{display:none}.mod-search{position:relative;overflow:hidden;zoom:1;margin-bottom:28px;background-color:#f7f7f8;padding:7px}.mod-search input{-webkit-appearance:none;border:0;background:transparent}.mod-search label{position:absolute;left:12px;top:10px;line-height:30px;cursor:text;color:#bbb}.mod-search legend{display:none}.mod-search fieldset{border:0;padding:0;margin:0}.mod-search .inp{height:30px;cursor:text;border:1px solid #dcdcdc;background-color:#fff}.mod-search .inp input{background:#fff;width:94%;margin:0;text-align:left;height:30px;padding-left:6px;height:28px\9;line-height:28px\9;outline:0}.mod-search .inp-btn{position:absolute;top:6px;right:6px;width:30px;height:30px;zoom:1;overflow:hidden}.mod-search .inp-btn input{width:100%;height:100%;font-size:0;padding:35px 0 0 0;overflow:hidden;background:url(https://img3.doubanio.com/f/sns/f71f15922ebd7c0ff0ea0e7a25577529efd8981a/pics/icon/bn_srh_1.png) no-repeat 45% 45%;color:transparent;cursor:pointer}.search-cate{float:left;margin-right:35px;width:65px;font-size:0}.search-cate li{width:65px;display:inline-block;*display:inline;zoom:1;margin-bottom:12px;line-height:1.2;font-size:12px}.search-cate li a{display:block;padding:3px 0;text-align:center}.search-cate li a:link,.search-cate li a:visited{color:#111}.search-cate li a:hover{color:#fff;background-color:#8cd379;border-radius:3px}.search-cate .on a:link,.search-cate .on a:visited,.search-cate .on a:hover,.search-cate .on a:active{background:#7fc06e;color:#fff;border-radius:3px}.search-cate .new a{background:url(https://img3.doubanio.com/f/sns/e49eca1517424a941871a2667a8957fd6c72d632/pics/new_menu.gif) no-repeat right 0}.search-result{overflow:hidden;zoom:1}.search-result .no-result{font-size:12px}.no-result{margin-bottom:40px;font-size:14px;color:#999}.frm-find{margin-bottom:30px}.frm-find .item{position:relative;margin-bottom:10px;color:#666}.frm-find .item label{position:absolute;top:2px;left:2px;line-height:20px;color:#999;cursor:text}.frm-find .item input{padding:5px;border:1px solid #ccc}.frm-find .item-accounts{border:1px solid #ccc}.frm-find .item-accounts input{width:60%;border:0}.frm-find .item-accounts .mail-domain{position:absolute;top:0;right:2px}.frm-find .item-accounts .mail-domain select{padding:2px;border:0;font-size:13px;background:transparent;-webkit-appearance:none}.frm-find .item-password input{width:98%}.result .ic-movie-mark{background-color:#71b5de}.result .ic-read-mark{background-color:#825d5b}
|
||||
.search_report { float:right; margin-top:15px; margin-right:10px;}
|
||||
.search_report a:link,
|
||||
.search_report a:hover,
|
||||
.search_report a:visited,
|
||||
.search_report a:active { color:#bbb; }
|
||||
.search_report a:hover { color:#fff; background-color:#bbb; }
|
||||
.search-list-item a{color:#666}
|
||||
.search-list-item a:hover{color:#fff}
|
||||
.topic-promotion {font-size: 13px; border-bottom: 1px dashed #CCC; padding-bottom: 15px; margin-bottom: 10px}
|
||||
.topic-promotion a:hover {color: #37A; background-color: #E8E8E8}
|
||||
.topic-promotion a {display: inline-block; padding: 3px 23px; margin-right: 10px; background: #F5F5F5}
|
||||
.topic-promotion a.new-icon {background: #F5F5F5 url('https://img3.doubanio.com/f/shire/e49eca1517424a941871a2667a8957fd6c72d632/pics/new_menu.gif') no-repeat right 0}
|
||||
.topic-promotion span {font-size: 12px; color: gray}
|
||||
.topic-promotion .related-topics {margin-top: 20px}
|
||||
.result > .pic {position: relative;}
|
||||
.user-verify-icon {
|
||||
display: block;
|
||||
background: url('https://img3.doubanio.com/f/shire/8cff6c7f0bfa5a9dbe604b51f4de62f655c233ce/pics/ic_verify@2x.png') no-repeat center;
|
||||
background-size: contain;
|
||||
border-radius: 50%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
right: -4px;
|
||||
bottom: -4px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<link rel="stylesheet" href="https://img3.doubanio.com/misc/mixed_static/6078d5bcd65d3c51.css">
|
||||
<script>
|
||||
var o={},PEOPLE_ID="",currTagId="",totallen=16,newGroupNum=0,groupNameArr=[],isDuplicate=!1,mlist=$(".menu-list"),olist=$(".group-opts-list"),sglist=$(".set-group-list"),sgarrow=$(".user-group-arrow"),addGroup=$("#add-new-group"),TEMPL_NEW_TAG='<li><a href="?tag={ID}">{NAME}</a></li>',TEMPL_MENU_LIST='<li><a href="?tag=0">全部</a></li>{CUSTOM_TAGS}<li class="last"><a href="?tag=2">未分组</a></li>',TEMPL_NEW_GROUP_ITEM='<li><input type="checkbox" id="{GID}" value="{GID}" {CHECKED}><label for="{GID}">{GNAME}</label></li>',TEMPL_SET_LIST_ITEMS='{CUSTOM_ITEMS}<li class="last"><span class="create-new">新分组</span></li>',TEMPL_GROUP_OPTS='<ul class="group-opts-list"><li id="del-group">删除分组</li><li id="rename-group">改组名</li></ul>',TEMPL_FRIEND_TIPS='<span class="tips">(可看仅朋友可见内容)</span>',TEMPL_EDIT_ICON=' <em class="icon-edit"></em>',TEMPL_TIPS='<span class="tips tlimit">{TIPS}</span>',CSS_LAST_ITEM=".set-group-list .last",CSS_INPUT_CREATE=".input-create",CSS_ARROW_SELECT="arrow-select",CSS_LOADER=".gray-loader",CSS_SET_GROUP_LIST=".set-group-list",CSS_EM_TIPS=".tlimit",TXT_DUP_NAME="已有同名分组",TXT_NULL_NAME="请输入分组名",TXT_BAN_WORDS="分组名称含有被禁止的内容",isIE6=!(!$.browser.msie||"6.0"!==$.browser.version),collectGroupName=function(){var e=$("#db-timeline-hd a").toArray();$.each(e,function(e,t){groupNameArr[e]=$.trim($(t).text())})},countNum=function(){var e=$("input[type=checkbox]:checked").length;e>0?$(".sel-wrapper .user-rs").html("加入"+e+"个分组"):$(".sel-wrapper .user-rs").html("加入分组")},changeGroupShow=function(e){var t=e.parents(".user-group-arrow").prev(".user-rs"),a=e.closest(CSS_SET_GROUP_LIST).children().children(":checked"),r=a.length,_=a.toArray(),l=$.map(_,function(e){return $(e).next().text().escapeHTML()});r>0?(t.html(l.join(", ").substring(0,totallen)).show(),l.join(", ").length>=totallen&&t.append("...")):t.text("未分组")},createNewGroup=function(e,t,a,r){$.each(groupNameArr,function(t,r){return a===r||"全部"===a||"未分组"===a?(isDuplicate=!0,$(CSS_EM_TIPS,e).length?$(CSS_EM_TIPS,e).text(TXT_DUP_NAME):$(CSS_INPUT_CREATE,e).after(TEMPL_TIPS.replace("{TIPS}",TXT_DUP_NAME)),!1):void(isDuplicate=!1)}),a&&!isDuplicate?($(CSS_EM_TIPS).remove(),$.post_withck("/j/contact/newtag",{name:a,people:t},function(t){if(t.result){var a=[],_=[],l="",T="",S="",n=t.data.newtag.id,i=t.data.newtag.name;mlist.length&&($.each(t.data.all,function(e,t){a[e]=TEMPL_NEW_TAG.replace("{ID}",t.id).replace("{NAME}",t.name.escapeHTML())}),mlist.html(TEMPL_MENU_LIST.replace("{CUSTOM_TAGS}",a.join(""))),l=$(".menu-list a:contains("+currGroupName+")"),l.parent().addClass("on"),"全部"!==currGroupName&&"朋友"!==currGroupName&&"未分组"!==currGroupName&&l.append(TEMPL_EDIT_ICON).after(TEMPL_GROUP_OPTS)),$.each(t.data.all,function(e,t){_[e]=TEMPL_NEW_GROUP_ITEM.replace(/{GID}/g,t.id).replace("{GNAME}",t.name.escapeHTML()).replace("{CHECKED}",t.status?'checked="checked"':"")}),$(CSS_SET_GROUP_LIST,e).html(TEMPL_SET_LIST_ITEMS.replace("{CUSTOM_ITEMS}",_.join(""))),$(CSS_SET_GROUP_LIST+" li:first",e).append(TEMPL_FRIEND_TIPS),$(CSS_SET_GROUP_LIST).length>1&&(S=$("#"+n).parent().next().children("label").text()||"",T=TEMPL_NEW_GROUP_ITEM.replace(/{GID}/g,n).replace(/{GNAME}/g,i.escapeHTML()).replace("{CHECKED}",""),S?$(CSS_SET_GROUP_LIST+' li:contains("'+S+'")').before(T):$(CSS_LAST_ITEM).before(T),$(CSS_SET_GROUP_LIST+" input[id="+n+"]",e).eq(1).parent().remove()),newGroupNum=$(CSS_SET_GROUP_LIST+" input[type=checkbox]",e).length,newGroupNum>=r&&$(CSS_LAST_ITEM).remove();var s=$(e).children().children("li:first");$(".custom-popwin").length?countNum(s):changeGroupShow(s),collectGroupName()}else"ban"===t.msg&&($(CSS_EM_TIPS,e).length?$(CSS_EM_TIPS,e).text(TXT_BAN_WORDS):$(CSS_INPUT_CREATE,e).after(TEMPL_TIPS.replace("{TIPS}",TXT_BAN_WORDS)))},"json")):a||($(CSS_EM_TIPS,e).length?$(CSS_EM_TIPS,e).text(TXT_NULL_NAME):$(CSS_INPUT_CREATE,e).after(TEMPL_TIPS.replace("{TIPS}",TXT_NULL_NAME)))};collectGroupName(),isIE6&&$(CSS_SET_GROUP_LIST+' li:not(".last")').hover(function(){$(this).addClass("hover")},function(){$(this).removeClass("hover")});
|
||||
</script>
|
||||
|
||||
<!-- Google Adsense Search AD begin -->
|
||||
<script async="async" src="https://www.google.com/adsense/search/ads.js"></script>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
(function(g,o){g[o]=g[o]||function(){(g[o]['q']=g[o]['q']||[]).push(
|
||||
arguments)},g[o]['t']=1*new Date})(window,'_googCsa');
|
||||
</script>
|
||||
<!-- Google Adsense Search AD end -->
|
||||
<link rel="stylesheet" href="https://img3.doubanio.com/f/shire/75ba35fadc87ea9d052896f99e9de067746f9788/css/core/large.css">
|
||||
|
||||
<link rel="shortcut icon" href="https://img3.doubanio.com/favicon.ico" type="image/x-icon">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<script type="text/javascript">var _body_start = new Date();</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link href="//img3.doubanio.com/dae/accounts/resources/d3e2921/shire/bundle.css" rel="stylesheet" type="text/css">
|
||||
|
||||
|
||||
|
||||
<div id="db-global-nav" class="global-nav">
|
||||
<div class="bd">
|
||||
|
||||
<div class="top-nav-info">
|
||||
<a href="https://accounts.douban.com/passport/login?source=main" class="nav-login" rel="nofollow">登录/注册</a>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="top-nav-doubanapp">
|
||||
<a href="https://www.douban.com/doubanapp/app?channel=top-nav" class="lnk-doubanapp">下载豆瓣客户端</a>
|
||||
<div id="doubanapp-tip">
|
||||
<a href="https://www.douban.com/doubanapp/app?channel=qipao" class="tip-link">豆瓣 <span class="version">6.0</span> 全新发布</a>
|
||||
<a href="javascript: void 0;" class="tip-close">×</a>
|
||||
</div>
|
||||
<div id="top-nav-appintro" class="more-items">
|
||||
<p class="appintro-title">豆瓣</p>
|
||||
<p class="qrcode">扫码直接下载</p>
|
||||
<div class="download">
|
||||
<a href="https://www.douban.com/doubanapp/redirect?channel=top-nav&direct_dl=1&download=iOS">iPhone</a>
|
||||
<span>·</span>
|
||||
<a href="https://www.douban.com/doubanapp/redirect?channel=top-nav&direct_dl=1&download=Android" class="download-android">Android</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="global-nav-items">
|
||||
<ul>
|
||||
<li class="on">
|
||||
<a href="https://www.douban.com" data-moreurl-dict="{"from":"top-nav-click-main","uid":"0"}">豆瓣</a>
|
||||
</li>
|
||||
<li class="">
|
||||
<a href="https://book.douban.com" target="_blank" data-moreurl-dict="{"from":"top-nav-click-book","uid":"0"}">读书</a>
|
||||
</li>
|
||||
<li class="">
|
||||
<a href="https://movie.douban.com" target="_blank" data-moreurl-dict="{"from":"top-nav-click-movie","uid":"0"}">电影</a>
|
||||
</li>
|
||||
<li class="">
|
||||
<a href="https://music.douban.com" target="_blank" data-moreurl-dict="{"from":"top-nav-click-music","uid":"0"}">音乐</a>
|
||||
</li>
|
||||
<li class="">
|
||||
<a href="https://www.douban.com/location" target="_blank" data-moreurl-dict="{"from":"top-nav-click-location","uid":"0"}">同城</a>
|
||||
</li>
|
||||
<li class="">
|
||||
<a href="https://www.douban.com/group" target="_blank" data-moreurl-dict="{"from":"top-nav-click-group","uid":"0"}">小组</a>
|
||||
</li>
|
||||
<li class="">
|
||||
<a href="https://read.douban.com/?dcs=top-nav&dcm=douban" target="_blank" data-moreurl-dict="{"from":"top-nav-click-read","uid":"0"}">阅读</a>
|
||||
</li>
|
||||
<li class="">
|
||||
<a href="https://douban.fm/?from_=shire_top_nav" target="_blank" data-moreurl-dict="{"from":"top-nav-click-fm","uid":"0"}">FM</a>
|
||||
</li>
|
||||
<li class="">
|
||||
<a href="https://time.douban.com/?dt_time_source=douban-web_top_nav" target="_blank" data-moreurl-dict="{"from":"top-nav-click-time","uid":"0"}">时间</a>
|
||||
</li>
|
||||
<li class="">
|
||||
<a href="https://market.douban.com/?utm_campaign=douban_top_nav&utm_source=douban&utm_medium=pc_web" target="_blank" data-moreurl-dict="{"from":"top-nav-click-market","uid":"0"}">豆品</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
;window._GLOBAL_NAV = {
|
||||
DOUBAN_URL: "https://www.douban.com",
|
||||
N_NEW_NOTIS: 0,
|
||||
N_NEW_DOUMAIL: 0
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<script src="//img3.doubanio.com/dae/accounts/resources/d3e2921/shire/bundle.js" defer="defer"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link href="//img3.doubanio.com/dae/accounts/resources/d3e2921/sns/bundle.css" rel="stylesheet" type="text/css">
|
||||
|
||||
|
||||
|
||||
<div id="db-nav-sns" class="nav">
|
||||
<div class="nav-wrap">
|
||||
<div class="nav-primary">
|
||||
|
||||
<div class="nav-logo">
|
||||
<a href="https://www.douban.com">豆瓣社区</a>
|
||||
</div>
|
||||
|
||||
<div class="nav-search">
|
||||
<form action="https://www.douban.com/search" method="get">
|
||||
<fieldset>
|
||||
<legend>搜索:</legend>
|
||||
<label for="inp-query">搜索你感兴趣的内容和人...</label>
|
||||
<div class="inp">
|
||||
<input type="hidden" name="source" value="suggest">
|
||||
<input id="inp-query" name="q" size="22" maxlength="60" autocomplete="off" value="">
|
||||
</div>
|
||||
<div class="inp-btn"><input type="submit" value="搜索"></div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="nav-items">
|
||||
<ul>
|
||||
<li><a href="https://www.douban.com">首页</a></li>
|
||||
<li>
|
||||
<a href="https://www.douban.com/explore">
|
||||
浏览发现
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://www.douban.com/gallery">
|
||||
话题广场
|
||||
<img src="https://img3.doubanio.com/f/shire/e49eca1517424a941871a2667a8957fd6c72d632/pics/new_menu.gif" alt="new" style="position: absolute; top: -7px; right: -13px;" />
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script src="//img3.doubanio.com/dae/accounts/resources/d3e2921/sns/bundle.js" defer="defer"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div id="wrapper">
|
||||
|
||||
|
||||
|
||||
<div id="content">
|
||||
|
||||
<h1>搜索: 逃避可耻却有用</h1>
|
||||
|
||||
<div class="grid-16-8 clearfix">
|
||||
|
||||
|
||||
<div class="article">
|
||||
|
||||
|
||||
<div class="mod-search">
|
||||
<form action="https://www.douban.com/search" method="get">
|
||||
<fieldset>
|
||||
<legend>搜索:</legend>
|
||||
<input type="hidden" name="cat" value="1002">
|
||||
<div class="inp"><input placeholder="搜索你感兴趣的内容和人" autofocus name="q" size="22" maxlength="60" value="逃避可耻却有用"></div>
|
||||
<div class="inp-btn"><input type="submit" value="搜索"></div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="search-cate">
|
||||
<ul>
|
||||
<li><a href="/search?q=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8">全部</a></li>
|
||||
<li class=on><a href="/search?cat=1002&q=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8">电影</a></li>
|
||||
<li><a href="/search?cat=1001&q=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8">书籍</a></li>
|
||||
<li><a href="/search?cat=1003&q=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8">音乐</a></li>
|
||||
<li><a href="/search?cat=1019&q=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8">小组</a></li>
|
||||
<li><a href="/search?cat=1005&q=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8">成员</a></li>
|
||||
<li><a href="/search?cat=1015&q=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8">日记</a></li>
|
||||
<li><a href="/search?cat=2012&q=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8">小站</a></li>
|
||||
<li><a href="/search?cat=1011&q=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8">同城活动</a></li>
|
||||
<li><a href="/search?cat=3069&q=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8">舞台剧</a></li>
|
||||
<li><a href="/search?cat=3114&q=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8">游戏</a></li>
|
||||
<li><a href="/search?cat=3064&q=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8">移动应用</a></li>
|
||||
<li><a href="/search?cat=8018&q=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8">豆品</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<script>
|
||||
Do(function(){
|
||||
if ($.browser.msie && ($.browser.version|0) === 6) {
|
||||
return;
|
||||
}
|
||||
$(window).scroll((function(){var g=$(document);var d=$(".search-cate");var f=d.find("ul");var e=parseInt($("body").css("padding-top"))+5;var c=d.offset().top-e;var b=d.width();var a;return function(h){if(a){window.clearTimeout(a)}a=window.setTimeout(function(){if(g.scrollTop()>=c){d.css({height:1});f.css({position:"fixed",width:b,top:e})}else{d.css({height:"auto"});f.css({position:"static",width:"auto"})}},0)}})());
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="search-result">
|
||||
<!-- douban ad begin -->
|
||||
<div id="dale_search_promo"></div>
|
||||
<!-- douban ad end -->
|
||||
|
||||
|
||||
|
||||
|
||||
<h2>
|
||||
相关电影:
|
||||
|
||||
</h2>
|
||||
|
||||
<div class="result-list">
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="result">
|
||||
<div class="pic">
|
||||
<a class="nbg" href="https://www.douban.com/link2/?url=https%3A%2F%2Fmovie.douban.com%2Fsubject%2F26816519%2F&query=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8&cat_id=1002&type=search&pos=0" target="_blank" onclick="moreurl(this,{i: '0', query: '%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8', from: 'dou_search_movie', sid: 26816519, qcat: '1002'})" title="逃げるは恥だが役に立つ" ><img src="https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2400201631.webp"></a>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="title">
|
||||
<h3>
|
||||
<span>[电影]</span>
|
||||
<a href="https://www.douban.com/link2/?url=https%3A%2F%2Fmovie.douban.com%2Fsubject%2F26816519%2F&query=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8&cat_id=1002&type=search&pos=0" target="_blank" onclick="moreurl(this,{i: '0', query: '%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8', from: 'dou_search_movie', sid: 26816519, qcat: '1002'})" >逃避虽可耻但有用 </a>
|
||||
<span class="ic-mark ic-movie-mark">可播放</span>
|
||||
</h3>
|
||||
|
||||
<div class="rating-info">
|
||||
|
||||
<span class="allstar40"></span>
|
||||
<span class="rating_nums">8.4</span>
|
||||
<span>(154381人评价)</span>
|
||||
|
||||
<span class="subject-cast">原名:逃げるは恥だが役に立つ / 金子文纪 / 新垣结衣 / 2016</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<p>森山实栗(新垣结衣 饰)自研究生毕业之后就一直仕途不顺,最近更是惨遭解雇,处于“无业游民”的状态之下,日子过得十分凄惨。经由父亲的介绍,无处可去的实栗来到了名...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="gallery_topic_result">
|
||||
|
||||
|
||||
<div class="item">
|
||||
<div class="label">话题</div>
|
||||
<div class="detail">
|
||||
<h2><a href="/gallery/topic/13555/?from=search" target="_blank" onclick="moreurl(this,{query: '%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8', from: 'dou_search_topic', tid: 13555, qcat: '1002'})">《逃避虽可耻但有用》哪个角色最让你惊喜?</a></h2>
|
||||
<div class="info">9794人浏览 · 14篇文章</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<div class="label">话题</div>
|
||||
<div class="detail">
|
||||
<h2><a href="/gallery/topic/13554/?from=search" target="_blank" onclick="moreurl(this,{query: '%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8', from: 'dou_search_topic', tid: 13554, qcat: '1002'})">《逃避虽可耻但有用》有哪些精彩片段?</a></h2>
|
||||
<div class="info">722人浏览 · 0篇文章</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="result">
|
||||
<div class="pic">
|
||||
<a class="nbg" href="https://www.douban.com/link2/?url=https%3A%2F%2Fmovie.douban.com%2Fsubject%2F35211578%2F&query=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8&cat_id=1002&type=search&pos=1" target="_blank" onclick="moreurl(this,{i: '1', query: '%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8', from: 'dou_search_movie', sid: 35211578, qcat: '1002'})" title="逃げるは恥だが役に立つ ガンバレ人類! 新春スペシャル!!" ><img src="https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2629717359.webp"></a>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="title">
|
||||
<h3>
|
||||
<span>[电影]</span>
|
||||
<a href="https://www.douban.com/link2/?url=https%3A%2F%2Fmovie.douban.com%2Fsubject%2F35211578%2F&query=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8&cat_id=1002&type=search&pos=1" target="_blank" onclick="moreurl(this,{i: '1', query: '%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8', from: 'dou_search_movie', sid: 35211578, qcat: '1002'})" >逃避虽可耻但有用 新春特别篇 </a>
|
||||
</h3>
|
||||
|
||||
<div class="rating-info">
|
||||
|
||||
<span class="allstar40"></span>
|
||||
<span class="rating_nums">8.4</span>
|
||||
<span>(26905人评价)</span>
|
||||
|
||||
<span class="subject-cast">原名:逃げるは恥だが役に立つ ガンバレ人類! 新春スペシャル!! / 金子文纪 / 新垣结衣 / 2021</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<p>森山实栗(新垣结衣 饰)和津崎平匡(星野源 饰)之间的事实婚姻愉快而又轻松的进行着,在日常生活的摩擦中,他们逐渐接受了彼此不同的生活步调,关系越来越亲密融洽。...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="result">
|
||||
<div class="pic">
|
||||
<a class="nbg" href="https://www.douban.com/link2/?url=https%3A%2F%2Fmovie.douban.com%2Fsubject%2F35071038%2F&query=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8&cat_id=1002&type=search&pos=2" target="_blank" onclick="moreurl(this,{i: '2', query: '%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8', from: 'dou_search_movie', sid: 35071038, qcat: '1002'})" title="逃げるは恥だが役に立つ ムズキュン!特別編" ><img src="https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2649905266.webp"></a>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="title">
|
||||
<h3>
|
||||
<span>[电影]</span>
|
||||
<a href="https://www.douban.com/link2/?url=https%3A%2F%2Fmovie.douban.com%2Fsubject%2F35071038%2F&query=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8&cat_id=1002&type=search&pos=2" target="_blank" onclick="moreurl(this,{i: '2', query: '%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8', from: 'dou_search_movie', sid: 35071038, qcat: '1002'})" >逃避虽可耻但有用 爱的悸动!特别篇 </a>
|
||||
</h3>
|
||||
|
||||
<div class="rating-info">
|
||||
|
||||
<span class="allstar45"></span>
|
||||
<span class="rating_nums">8.9</span>
|
||||
<span>(1179人评价)</span>
|
||||
|
||||
<span class="subject-cast">原名:逃げるは恥だが役に立つ ムズキュン!特別編 / 金子文纪 / 新垣结衣 / 2020</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<p>本特别篇为2016年10月首播的《逃避虽可耻但有用》原剧的再剪辑版,在首播版的基础上加入了一些未公开镜头。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="back-to-top">
|
||||
<a href="#">↑回顶部</a>
|
||||
</div>
|
||||
<script>
|
||||
Do(function() {
|
||||
$("html").delegate(".a_search_more","click",function(c){c.preventDefault();var b=$(this);if(b.hasClass("processing")){return}var a=b.data();b.attr("title",b.text()).text("加载中...");b.addClass("processing");$.get("/j/search",a,function(d){if(d.r){b.text("加载失败...");return}b.removeClass("processing");b.text(b.attr("title"));b.data("start",parseInt(b.data("start"),10)+d.limit);$(d.items.join("")).insertBefore(b.parent());if(!d.more){b.hide()}})});
|
||||
(function(){var h=$(window);var k=$(document);var l;var a=$(".back-to-top");var b=$("#content").find(".article");var g;var i=$("#content").find(".search-result");var d=$.browser.msie&&$.browser.version==="6.0";var f=function(n){if(!f.cache){f.cache=[]}if(f.cache[n]){return}var m=new Date;(new Image()).src="/stat.html?source=group&action=back_top&iden="+n+"&month="+(m.getMonth()+1)+"&day="+m.getDate()+"×tamp="+(+new Date);f.cache[n]=1};var e=function(){return i.offset().top+i.outerHeight()};var c=function(m){if(m+g>=e()){a.css({position:"absolute",bottom:"",top:e()-a.outerHeight()})}else{if(!d){a.css({position:"fixed",top:"",bottom:0})}}a.show().find("a").stop().animate({top:0})};var j=function(){a.hide().find("a").css("top",40)};h.resize(function(){g=h.height();a.css("left",b.offset().left+b.width()+80);l=k.height()/g>3?Math.max(2*g,$(".side").height()):0});h.trigger("resize");h.scroll((function(){var m;return function(){if(m){clearTimeout(m)}setTimeout(function(){if(l===0){return}var n=k.scrollTop();if(n>l){c(n)}else{j()}},100)}})());a.find("a").click(function(m){m.preventDefault();k.scrollTop(0)});if(d){h.scroll(function(){if(k.scrollTop()+g>=e()){return}a.css("top",k.scrollTop()+g-a.height())})}})();
|
||||
});
|
||||
</script>
|
||||
|
||||
</div>
|
||||
<div class="aside">
|
||||
|
||||
|
||||
|
||||
<!-- douban ad begin -->
|
||||
<div id="dale_subject_search_top_right"></div>
|
||||
<!-- douban ad end -->
|
||||
|
||||
|
||||
|
||||
<div class="mod">
|
||||
<div class="hd">
|
||||
|
||||
<h2>
|
||||
豆瓣还没有这个电影,我来添加
|
||||
· · · · · ·
|
||||
</h2>
|
||||
|
||||
</div>
|
||||
<div class="bd">
|
||||
<p class="pl">
|
||||
<a href="https://movie.douban.com/new_subject?cat=1002&search_text=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8" rel="nofollow">> 添加电影</a>
|
||||
</p>
|
||||
<p class="pl">
|
||||
<a href="https://movie.douban.com/celebrities/new?step=1&search_text=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8" rel="nofollow">> 添加影人</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search_report">
|
||||
<a href="javascript:;" id="search-report-btn">> 对结果不满意?让我们知道</a>
|
||||
</div>
|
||||
<iframe frameborder="0" id="feedback-iframe"></iframe>
|
||||
|
||||
<script type="text/javascript">
|
||||
Do(function () {
|
||||
|
||||
$('#search-report-btn').click(function () {
|
||||
var url = 'https://help.douban.com/feedback_popup?qtype=194&tags=%E7%BB%BC%E5%90%88%2C%E7%94%B5%E5%BD%B1&extra=' +
|
||||
encodeURIComponent(JSON.stringify({
|
||||
q: '逃避可耻却有用',
|
||||
cat_id: '1002'
|
||||
}))
|
||||
var $iframe = $('#feedback-iframe')
|
||||
if ($iframe.attr('src') !== url) {
|
||||
$iframe.attr('src', url)
|
||||
}
|
||||
$('#feedback-iframe').show()
|
||||
})
|
||||
window.addEventListener('message', function (event) {
|
||||
var origin = event.origin || event.originalEvent.origin;
|
||||
if (!origin.match(/^https?:\/\/[a-z0-9-.]+\.douban\.com(?:\/|:|$)/)) {
|
||||
// 非法域名
|
||||
window.console && console.warn && console.warn('window receive message from illegal origin: ', origin, event)
|
||||
return
|
||||
}
|
||||
if (typeof event.data !== 'string') {
|
||||
return
|
||||
}
|
||||
var data = JSON.parse(event.data)
|
||||
if (data.type === 'FEEDBACK_POPUP_CLOSE') {
|
||||
$('#feedback-iframe').hide()
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
</div>
|
||||
<div class="extra">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="footer">
|
||||
|
||||
<span id="icp" class="fleft gray-link">
|
||||
© 2005-2021 douban.com, all rights reserved 北京豆网科技有限公司
|
||||
</span>
|
||||
|
||||
<a href="https://www.douban.com/hnypt/variformcyst.py" style="display: none;"></a>
|
||||
|
||||
<span class="fright">
|
||||
<a href="https://www.douban.com/about">关于豆瓣</a>
|
||||
· <a href="https://www.douban.com/jobs">在豆瓣工作</a>
|
||||
· <a href="https://www.douban.com/about?topic=contactus">联系我们</a>
|
||||
· <a href="https://www.douban.com/about/legal">法律声明</a>
|
||||
|
||||
· <a href="https://help.douban.com/?app=main" target="_blank">帮助中心</a>
|
||||
· <a href="https://www.douban.com/doubanapp/">移动应用</a>
|
||||
· <a href="https://www.douban.com/partner/">豆瓣广告</a>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<!-- COLLECTED JS -->
|
||||
|
||||
<!-- douban ad begin -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
(function (global) {
|
||||
var newNode = global.document.createElement('script'),
|
||||
existingNode = global.document.getElementsByTagName('script')[0],
|
||||
adSource = '//erebor.douban.com/',
|
||||
userId = '',
|
||||
browserId = 'kZhpqD1n6oY',
|
||||
criteria = '8:逃避可耻却有用|3:/search?cat=1002&q=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8',
|
||||
preview = '',
|
||||
debug = false,
|
||||
adSlots = ['dale_subject_search_top_right', 'dale_search_promo', 'dale_web_search_result_feed'];
|
||||
|
||||
global.DoubanAdRequest = {src: adSource, uid: userId, bid: browserId, crtr: criteria, prv: preview, debug: debug};
|
||||
global.DoubanAdSlots = (global.DoubanAdSlots || []).concat(adSlots);
|
||||
|
||||
newNode.setAttribute('type', 'text/javascript');
|
||||
newNode.setAttribute('src', '//img1.doubanio.com/YWVrMm1iYy9mL2FkanMvN2ZmNmEyM2M0ZDNjMmQxYWVkMjMwZDIwMDU5NWI2YTFkNDc5YjExYi9hZC5yZWxlYXNlLmpz');
|
||||
newNode.setAttribute('async', true);
|
||||
existingNode.parentNode.insertBefore(newNode, existingNode);
|
||||
})(this);
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- douban ad end -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Google Tag Manager -->
|
||||
<noscript><iframe src="//www.googletagmanager.com/ns.html?id=GTM-5WP579" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
|
||||
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src='//www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','GTM-5WP579');</script>
|
||||
<!-- End Google Tag Manager -->
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
var _paq = _paq || [];
|
||||
_paq.push(['trackPageView']);
|
||||
_paq.push(['enableLinkTracking']);
|
||||
(function() {
|
||||
var p=(('https:' == document.location.protocol) ? 'https' : 'http'), u=p+'://fundin.douban.com/';
|
||||
_paq.push(['setTrackerUrl', u+'piwik']);
|
||||
_paq.push(['setSiteId', '100001']);
|
||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||
g.type='text/javascript';
|
||||
g.defer=true;
|
||||
g.async=true;
|
||||
g.src=p+'://img3.doubanio.com/dae/fundin/piwik.js';
|
||||
s.parentNode.insertBefore(g,s);
|
||||
})();
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-7019765-1']);
|
||||
_gaq.push(['_setCampNameKey', 'dcn']);
|
||||
_gaq.push(['_setCampSourceKey', 'dcs']);
|
||||
_gaq.push(['_setCampMediumKey', 'dcm']);
|
||||
_gaq.push(['_setCampTermKey', 'dct']);
|
||||
_gaq.push(['_setCampContentKey', 'dcc']);
|
||||
_gaq.push(['_addOrganic', 'baidu', 'word']);
|
||||
_gaq.push(['_addOrganic', 'soso', 'w']);
|
||||
_gaq.push(['_addOrganic', '3721', 'name']);
|
||||
_gaq.push(['_addOrganic', 'youdao', 'q']);
|
||||
_gaq.push(['_addOrganic', 'so.360.cn', 'q']);
|
||||
_gaq.push(['_addOrganic', 'vnet', 'kw']);
|
||||
_gaq.push(['_addOrganic', 'sogou', 'query']);
|
||||
_gaq.push(['_addIgnoredOrganic', '豆瓣']);
|
||||
_gaq.push(['_addIgnoredOrganic', 'douban']);
|
||||
_gaq.push(['_addIgnoredOrganic', '豆瓣网']);
|
||||
_gaq.push(['_addIgnoredOrganic', 'www.douban.com']);
|
||||
_gaq.push(['_setDomainName', '.douban.com']);
|
||||
|
||||
|
||||
_gaq.push(['_setCustomVar', 1, 'responsive_view_mode', 'desktop', 3]);
|
||||
|
||||
_gaq.push(['_trackPageview']);
|
||||
_gaq.push(['_trackPageLoadTime']);
|
||||
|
||||
window._ga_init = function() {
|
||||
var ga = document.createElement('script');
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
ga.setAttribute('async', 'true');
|
||||
document.documentElement.firstChild.appendChild(ga);
|
||||
};
|
||||
if (window.addEventListener) {
|
||||
window.addEventListener('load', _ga_init, false);
|
||||
} else {
|
||||
window.attachEvent('onload', _ga_init);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- dae-web-bywater--default-65879cbd8c-clxtd-->
|
||||
|
||||
<script>_SPLITTEST=''</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
1234
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
__author__ = "Benny <benny.think@gmail.com>"
|
||||
|
||||
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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
@@ -280,4 +280,4 @@
|
||||
}
|
||||
|
||||
</script>
|
||||
</html>
|
||||
</html>
|
||||
3
yyetsweb/templates/robots.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
@@ -7,8 +7,44 @@
|
||||
|
||||
__author__ = "Benny <benny.think@gmail.com>"
|
||||
|
||||
import os
|
||||
import smtplib
|
||||
import time
|
||||
from email.header import Header
|
||||
from email.mime.text import MIMEText
|
||||
from email.utils import formataddr, parseaddr
|
||||
|
||||
|
||||
def ts_date(ts=None):
|
||||
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(ts))
|
||||
|
||||
|
||||
|
||||
def _format_addr(s):
|
||||
name, addr = parseaddr(s)
|
||||
return formataddr((Header(name, 'utf-8').encode(), addr))
|
||||
|
||||
|
||||
def send_mail(to: str, subject: str, body: str):
|
||||
user = os.getenv("email_user")
|
||||
password = os.getenv("email_password")
|
||||
host = os.getenv("email_host") or "localhost"
|
||||
port = os.getenv("email_port") or "1025" # mailhog
|
||||
from_addr = os.getenv("from_addr") or "yyets@dmesg.app"
|
||||
|
||||
msg = MIMEText(body, 'html', 'utf-8')
|
||||
msg['From'] = _format_addr('YYeTs <%s>' % from_addr)
|
||||
msg['To'] = _format_addr(to)
|
||||
msg['Subject'] = Header(subject, 'utf-8').encode()
|
||||
|
||||
if port == "1025":
|
||||
server = smtplib.SMTP(host, port)
|
||||
else:
|
||||
server = smtplib.SMTP_SSL(host, port)
|
||||
server.login(user, password)
|
||||
server.sendmail(from_addr, [to], msg.as_string())
|
||||
server.quit()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
send_mail("benny.think@gmail.com", "subj", 'aaaa<br>bbb')
|
||||
|
||||