update bunch of features
This commit is contained in:
Benny
2021-08-15 12:28:19 +08:00
committed by BennyThink
parent 1a33526114
commit c44a20c64d
71 changed files with 1282 additions and 6170 deletions

View File

@@ -5,7 +5,6 @@ logs/*
YYeTsFE/node_modules/*
.github/*
assets/*
scripts/*
conf/*
tests/*
yyetsweb/yyets.sqlite

15
.gitattributes vendored
View File

@@ -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

View File

@@ -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
View File

@@ -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
View File

@@ -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和5size是已读和未读共享的优先返回未读数据
```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"
}
]
}
```

View File

@@ -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

Submodule YYeTsFE updated: 5c85689ad0...ec00c3ee0b

6
conf/yyets.env Normal file
View File

@@ -0,0 +1,6 @@
mongo=mongo
redis=redis
email_user=username
email_password=passord
email_host=mailhog
email_port=1025

View File

@@ -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"

View File

@@ -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

View File

@@ -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
}

View File

@@ -1,5 +0,0 @@
[run]
omit =
*/site-packages/*
*/distutils/*
tests/*

View File

@@ -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>&nbsp;&nbsp;<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">&nbsp;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>

View File

@@ -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字幕侠 做国内最好的字幕组 &raquo; Feed" href="https://www.zimuxia.cn/feed" />
<link rel="alternate" type="application/rss+xml" title="字幕组 FIX字幕侠 做国内最好的字幕组 &raquo; 评论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&#038;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">绫濑遥 / 高桥一生 / 北村一辉 / 柄本佑 / 沟端淳平 / 更多&#8230;</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&amp;#" target="_blank">115网盘</a> <a href="https://pan.baidu.com/s/10la5ThmjXLwKNTcP2y4P7A" target="_blank">百度网盘</a> <a href="magnet:?xt=urn:btih:84754072F7D436C718A24CB73598946DEF430FB5&amp;dn=Tengokutojigoku.E01.720p.FIX%e5%ad%97%e5%b9%95%e4%be%a0&amp;tr=http%3a%2f%2ft.nyaatracker.com%2fannounce&amp;tr=http%3a%2f%2fshare.camoe.cn%3a8080%2fannounce&amp;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>

View File

@@ -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字幕侠 做国内最好的字幕组 &raquo; Feed" href="https://www.zimuxia.cn/feed" />
<link rel="alternate" type="application/rss+xml" title="字幕组 FIX字幕侠 做国内最好的字幕组 &raquo; 评论Feed" href="https://www.zimuxia.cn/comments/feed" />
<link rel="alternate" type="application/rss+xml" title="字幕组 FIX字幕侠 做国内最好的字幕组 &raquo; “逃避”的搜索结果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>逃避可耻但有用 导演: 金子文紀 / 土井裕泰 / 石井康晴 编剧: 野木亜紀子 主演: 新垣结衣 / 星野源 [&hellip;]</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">&rsaquo;</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 [&hellip;]</p>
<a href="https://www.zimuxia.cn/portfolio/%e4%ba%a2%e5%a5%8b" class="read-more">
Read More
<span class="more-arrow">&rsaquo;</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>第二十二条军规 导演: 乔治·克鲁尼 / 格兰特·赫斯洛夫 / 艾伦·库拉斯 编剧: 卢克·戴维斯 / 大卫· [&hellip;]</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">&rsaquo;</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>

File diff suppressed because it is too large Load Diff

View File

@@ -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])

View File

@@ -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()

View File

@@ -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

View File

@@ -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.")

View File

@@ -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();
```

View File

@@ -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)

File diff suppressed because it is too large Load Diff

View File

@@ -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='&nbsp;<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="{&quot;from&quot;:&quot;top-nav-click-main&quot;,&quot;uid&quot;:&quot;0&quot;}">豆瓣</a>
</li>
<li class="">
<a href="https://book.douban.com" target="_blank" data-moreurl-dict="{&quot;from&quot;:&quot;top-nav-click-book&quot;,&quot;uid&quot;:&quot;0&quot;}">读书</a>
</li>
<li class="">
<a href="https://movie.douban.com" target="_blank" data-moreurl-dict="{&quot;from&quot;:&quot;top-nav-click-movie&quot;,&quot;uid&quot;:&quot;0&quot;}">电影</a>
</li>
<li class="">
<a href="https://music.douban.com" target="_blank" data-moreurl-dict="{&quot;from&quot;:&quot;top-nav-click-music&quot;,&quot;uid&quot;:&quot;0&quot;}">音乐</a>
</li>
<li class="">
<a href="https://www.douban.com/location" target="_blank" data-moreurl-dict="{&quot;from&quot;:&quot;top-nav-click-location&quot;,&quot;uid&quot;:&quot;0&quot;}">同城</a>
</li>
<li class="">
<a href="https://www.douban.com/group" target="_blank" data-moreurl-dict="{&quot;from&quot;:&quot;top-nav-click-group&quot;,&quot;uid&quot;:&quot;0&quot;}">小组</a>
</li>
<li class="">
<a href="https://read.douban.com&#47;?dcs=top-nav&amp;dcm=douban" target="_blank" data-moreurl-dict="{&quot;from&quot;:&quot;top-nav-click-read&quot;,&quot;uid&quot;:&quot;0&quot;}">阅读</a>
</li>
<li class="">
<a href="https://douban.fm&#47;?from_=shire_top_nav" target="_blank" data-moreurl-dict="{&quot;from&quot;:&quot;top-nav-click-fm&quot;,&quot;uid&quot;:&quot;0&quot;}">FM</a>
</li>
<li class="">
<a href="https://time.douban.com&#47;?dt_time_source=douban-web_top_nav" target="_blank" data-moreurl-dict="{&quot;from&quot;:&quot;top-nav-click-time&quot;,&quot;uid&quot;:&quot;0&quot;}">时间</a>
</li>
<li class="">
<a href="https://market.douban.com&#47;?utm_campaign=douban_top_nav&amp;utm_source=douban&amp;utm_medium=pc_web" target="_blank" data-moreurl-dict="{&quot;from&quot;:&quot;top-nav-click-market&quot;,&quot;uid&quot;:&quot;0&quot;}">豆品</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&amp;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&amp;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&amp;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&amp;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&amp;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&amp;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&amp;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&amp;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&amp;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&amp;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&amp;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&amp;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&amp;query=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8&amp;cat_id=1002&amp;type=search&amp;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>
&nbsp;<a href="https://www.douban.com/link2/?url=https%3A%2F%2Fmovie.douban.com%2Fsubject%2F26816519%2F&amp;query=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8&amp;cat_id=1002&amp;type=search&amp;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&amp;query=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8&amp;cat_id=1002&amp;type=search&amp;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>
&nbsp;<a href="https://www.douban.com/link2/?url=https%3A%2F%2Fmovie.douban.com%2Fsubject%2F35211578%2F&amp;query=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8&amp;cat_id=1002&amp;type=search&amp;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&amp;query=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8&amp;cat_id=1002&amp;type=search&amp;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>
&nbsp;<a href="https://www.douban.com/link2/?url=https%3A%2F%2Fmovie.douban.com%2Fsubject%2F35071038%2F&amp;query=%E9%80%83%E9%81%BF%E5%8F%AF%E8%80%BB%E5%8D%B4%E6%9C%89%E7%94%A8&amp;cat_id=1002&amp;type=search&amp;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="#">&#8593;回顶部</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()+"&timestamp="+(+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>
豆瓣还没有这个电影,我来添加
&nbsp;&middot;&nbsp;&middot;&nbsp;&middot;&nbsp;&middot;&nbsp;&middot;&nbsp;&middot;
</h2>
</div>
<div class="bd">
<p class="pl">
<a href="https://movie.douban.com/new_subject?cat=1002&amp;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">&gt;&nbsp;添加电影</a>
</p>
<p class="pl">
<a href="https://movie.douban.com/celebrities/new?step=1&amp;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">&gt;&nbsp;添加影人</a>
</p>
</div>
</div>
<div class="search_report">
<a href="javascript:;" id="search-report-btn">&gt; 对结果不满意?让我们知道</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">
&copy; 20052021 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&amp;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>

View File

@@ -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

View File

@@ -1 +0,0 @@
1234

View File

@@ -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
)

View File

@@ -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=

View File

@@ -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)

View File

@@ -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)

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -280,4 +280,4 @@
}
</script>
</html>
</html>

View File

@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View File

@@ -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')