1. 支持使用PTPP的cookie备份文件导入站点

This commit is contained in:
ngfchl
2022-09-10 15:10:21 +08:00
parent ca7c8d86b2
commit 0fd9ae19d6
14 changed files with 374 additions and 223 deletions

View File

@@ -11,5 +11,6 @@ urlpatterns = [
path(r'update', views.update_page, name='update_page'),
path(r'do_restart', views.do_restart, name='do_restart'),
path(r'do_update', views.do_update, name='do_update'),
path(r'import_from_ptpp', views.import_from_ptpp, name='import_from_ptpp'),
path(r'do_sql', views.do_sql, name='do_sql'),
]

View File

@@ -12,7 +12,7 @@ from pt_site import views as tasks
from pt_site.UtilityTool import FileSizeConvert
from pt_site.models import SiteStatus, MySite, Site
from pt_site.views import scheduler, pt_spider
from ptools.base import CommonResponse
from ptools.base import CommonResponse, StatusCodeEnum
def add_task(request):
@@ -78,23 +78,80 @@ def test_notify(request):
def do_sql(request):
# with open('main_pt_site_site.sql', encoding='utf-8') as file_obj:
# contents = file_obj.readlines()
# with connection.cursor() as cursor:
# for statement in contents:
# res1 = cursor.execute(statement)
# print(threading.main_thread().getName())
try:
print(0)
except Exception as e:
print(e)
# autoreload.start_django(au)
# django_main_thread
return JsonResponse('ok', safe=False)
def import_from_ptpp(request):
if request.method == 'GET':
return render(request, 'auto_pt/import_ptpp.html')
else:
# print(request.body)
data_list = json.loads(request.body).get('ptpp')
datas = json.loads(data_list)
print('content', len(datas))
# success_messages = []
# error_messages = []
message_list = []
# print(datas)
cookies = []
cookie = {
'domain': '',
'cookies': ''
}
for index, data in enumerate(datas):
domain = data.get('domain').lstrip('.').lstrip('www.')
# print('domain', domain)
value = data.get('name') + '=' + data.get('value') + ';'
# print('value', value)
if not cookie.get('domain'):
cookie['domain'] = domain
cookie['cookies'] = value
elif domain in cookie.get('domain'):
# new_value = cookie['cookies'] + value
# cookie['cookies'] = new_value
cookie['cookies'] += value
# print('new_value', cookie['cookies'])
else:
cookie['cookies'] = cookie['cookies'].lstrip(';')
cookies.append(cookie)
# print(len(cookies))
# cookie = {}
# cookie['domain'] = domain
# cookie['cookies'] = value
cookie = {'domain': domain, 'cookies': value}
if index == len(datas) - 1:
cookies.append(cookie)
# print('cookie:', cookie)
print('cookies,', len(cookies))
for data in cookies:
try:
print(data)
res = pt_spider.get_uid_and_passkey(data)
msg = res.msg
print(msg)
if res.code == StatusCodeEnum.OK.code:
message_list.append({
'msg': msg,
'tag': 'success'
})
else:
# error_messages.append(msg)
message_list.append({
'msg': msg,
'tag': 'error'
})
except Exception as e:
message = '{} 站点导入失败!{} \n'.format(data.get('domain'), str(e))
message_list.append({
'msg': message,
'tag': 'warning'
})
raise
return JsonResponse(CommonResponse.success(data={
'messages': message_list
}).to_dict(), safe=False)
def get_git_logs(master='', n=10):
# 获取最新的10条更新记录
# master='' 本地 master='origin/master' 远程

View File

@@ -17,7 +17,7 @@ from wechat_push import WechatPush
from wxpusher import WxPusher
from auto_pt.models import Notify, OCR
from pt_site.models import MySite, SignIn, TorrentInfo, SiteStatus
from pt_site.models import MySite, SignIn, TorrentInfo, SiteStatus, Site
from ptools.base import TorrentBaseInfo, PushConfig, CommonResponse, StatusCodeEnum
@@ -94,6 +94,9 @@ class PtSpider:
for i in list_mid:
# 以第一个选中的字符分割1次
list2 = i.split('=', 1)
print(list2)
if list2[0] == '':
continue
dist_dict[list2[0]] = list2[1]
return dist_dict
@@ -225,6 +228,40 @@ class PtSpider:
msg=StatusCodeEnum.OCR_ACCESS_ERR.errmsg + str(e)
)
def get_uid_and_passkey(self, cookie: dict):
site = Site.objects.filter(url__contains=cookie.get('domain')).first()
# print('查询站点信息:',site)
if not site:
return CommonResponse.error(msg='尚未支持此站点:' + cookie.get('domain'))
my_site = MySite.objects.filter(site=site).first()
# print('查询我的站点:',my_site)
# 如果有更新cookie如果没有继续创建
if not my_site:
my_site = MySite(
site=site,
cookie=cookie.get('cookies')
)
url = site.url + site.page_control_panel
# print(my_site.cookie)
print(url)
response = self.send_request(my_site=my_site, url=url)
# print(response.content.decode('utf8'))
user_details = self.parse(response, site.my_uid_rule)
print(user_details)
user_details_href = user_details[0]
passkey = self.parse(response, site.my_passkey_rule)[0]
uid = re.sub(r'\D', '', user_details_href)
print(uid, passkey)
my_site.user_id = uid
my_site.passkey = passkey
my_site.save()
print(site.name, uid, passkey)
return CommonResponse.success(msg=site.name + ' 信息导入成功!')
else:
my_site.cookie = cookie.get('cookies').rstrip(';')
my_site.save()
return CommonResponse.success(msg=site.name + ' 信息更新成功!')
def sign_in_hdsky(self, my_site: MySite, captcha=False):
"""HDSKY签到"""
site = my_site.site

View File

@@ -1,4 +1,4 @@
# Generated by Django 4.1 on 2022-08-25 13:16
# Generated by Django 4.1 on 2022-09-09 08:11
import django.core.validators
from django.db import migrations, models
@@ -161,13 +161,16 @@ class Migration(migrations.Migration):
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="更新时间"),
),
("name", models.CharField(max_length=32, verbose_name="站点名称")),
(
"url",
models.URLField(
default="", help_text='请保留网址结尾的"/"', verbose_name="站点网址"
default="",
help_text='请保留网址结尾的"/"',
unique=True,
verbose_name="站点网址",
),
),
("name", models.CharField(max_length=32, verbose_name="站点名称")),
(
"logo",
models.URLField(
@@ -202,6 +205,12 @@ class Migration(migrations.Migration):
default="attendance.php", max_length=64, verbose_name="默认签到链接"
),
),
(
"page_control_panel",
models.CharField(
default="usercp.php", max_length=64, verbose_name="控制面板"
),
),
(
"page_detail",
models.CharField(
@@ -374,7 +383,7 @@ class Migration(migrations.Migration):
(
"title_rule",
models.CharField(
default=".//tr/td[1]/text()",
default='.//td[@class="embedded"]/a/following::text()[1]',
max_length=128,
verbose_name="种子标题",
),
@@ -390,7 +399,7 @@ class Migration(migrations.Migration):
(
"category_rule",
models.CharField(
default='.//td[@class="rowfollow nowrap"][1]/a[1]/img/@class',
default='.//td[@class="rowfollow nowrap"][1]/a[1]/img/@title',
max_length=128,
verbose_name="分类",
),
@@ -406,9 +415,9 @@ class Migration(migrations.Migration):
(
"magnet_url_rule",
models.CharField(
default='.//td/a[contains(@href,"download")]/@href',
default='.//td/a[contains(@href,"download.php?id=")]/@href',
max_length=128,
verbose_name="下载链接",
verbose_name="主页下载链接",
),
),
(
@@ -416,7 +425,7 @@ class Migration(migrations.Migration):
models.CharField(
default='.//a[contains(@href,"download.php?id=") and contains(@href,"passkey")]/@href',
max_length=128,
verbose_name="种子链接",
verbose_name="详情页种子链接",
),
),
(
@@ -436,7 +445,7 @@ class Migration(migrations.Migration):
(
"sale_rule",
models.CharField(
default='.//table/tr/td/img[contains(@class,"pro_")]/@alt',
default='.//div/img[contains(@class,"pro_")]/@alt',
max_length=128,
verbose_name="促销信息",
),
@@ -460,7 +469,7 @@ class Migration(migrations.Migration):
(
"seeders_rule",
models.CharField(
default=".//td[6]/b/a/text()",
default='.//a[contains(@href,"#seeders")]/text()',
max_length=128,
verbose_name="做种人数",
),
@@ -468,7 +477,7 @@ class Migration(migrations.Migration):
(
"leechers_rule",
models.CharField(
default=".//td[7]/b/a/text()",
default='.//a[contains(@href,"#leechers")]/text()',
max_length=128,
verbose_name="下载人数",
),
@@ -476,7 +485,7 @@ class Migration(migrations.Migration):
(
"completers_rule",
models.CharField(
default=".//td[8]/a/b/text()",
default='.//a[contains(@href,"viewsnatches.php?id=")]//text()',
max_length=128,
verbose_name="完成人数",
),
@@ -498,9 +507,9 @@ class Migration(migrations.Migration):
(
"peer_speed_rule",
models.CharField(
default=".//tr/td[5]/nobr/text()",
default=".//tr/td[7]/nobr/text()",
max_length=128,
verbose_name="平均上传速度",
verbose_name="平均下载速度",
),
),
(
@@ -584,11 +593,27 @@ class Migration(migrations.Migration):
(
"my_level_rule",
models.CharField(
default='//span[@class="medium"]/span[@class="nowrap"]/a[contains(@class,"_Name")]/@class',
default='//a[contains(@class,"_Name") and contains(@href,"userdetails.php?id=1")]/@class',
max_length=128,
verbose_name="用户等级",
),
),
(
"my_passkey_rule",
models.CharField(
default='//td[contains(text(),"密钥")]/following-sibling::td[1]/text()',
max_length=128,
verbose_name="Passkey",
),
),
(
"my_uid_rule",
models.CharField(
default='//a[contains(@class,"_Name") and contains(@href,"userdetails.php?id=")]/@href',
max_length=128,
verbose_name="用户ID",
),
),
(
"my_hr_rule",
models.CharField(
@@ -616,9 +641,10 @@ class Migration(migrations.Migration):
(
"record_count_rule",
models.CharField(
default="/html/body/b/text()",
default=".//td[3]/text()",
help_text="提取做种列表中文件大小计算总量",
max_length=128,
verbose_name="种子记录数",
verbose_name="做种大小列表",
),
),
(
@@ -633,12 +659,21 @@ class Migration(migrations.Migration):
(
"mailbox_rule",
models.CharField(
default='//a[@href="messages.php"]/following-sibling::text()[1]',
default='//a[@href="messages.php"]/font/text()',
help_text="获取新邮件",
max_length=128,
verbose_name="邮件规则",
),
),
(
"notice_rule",
models.CharField(
default='//a[@href="index.php"]/font/text()[1]',
help_text="获取新公告",
max_length=128,
verbose_name="公告规则",
),
),
(
"hash_rule",
models.CharField(
@@ -712,7 +747,12 @@ class Migration(migrations.Migration):
default="/downloads/brush", verbose_name="保存路径"
),
),
("hr", models.BooleanField(default=False, verbose_name="H&R")),
(
"hr",
models.BooleanField(
default=True, help_text="绿色为通过或无需HR考核", verbose_name="H&R考核"
),
),
(
"sale_status",
models.CharField(default="无促销", max_length=16, verbose_name="优惠状态"),
@@ -797,14 +837,8 @@ class Migration(migrations.Migration):
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="更新时间"),
),
(
"uploaded",
models.CharField(default="0", max_length=16, verbose_name="上传量"),
),
(
"downloaded",
models.CharField(default="0", max_length=16, verbose_name="下载量"),
),
("uploaded", models.IntegerField(default=0, verbose_name="上传量")),
("downloaded", models.IntegerField(default=0, verbose_name="下载量")),
("ratio", models.FloatField(default=0, verbose_name="分享率")),
("my_sp", models.FloatField(default=0, verbose_name="魔力值")),
("my_bonus", models.FloatField(default=0, verbose_name="做种积分")),
@@ -844,6 +878,10 @@ class Migration(migrations.Migration):
"sign_in_today",
models.BooleanField(default=False, verbose_name="签到"),
),
(
"sign_in_info",
models.CharField(default="", max_length=256, verbose_name="信息"),
),
(
"site",
models.ForeignKey(

View File

@@ -0,0 +1,21 @@
# Generated by Django 4.1 on 2022-09-10 14:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("pt_site", "0001_initial"),
]
operations = [
migrations.AlterField(
model_name="site",
name="my_uid_rule",
field=models.CharField(
default='//a[contains(@class,"_Name") and contains(@href,"userdetails.php?id=")]/@href',
max_length=128,
verbose_name="用户ID",
),
),
]

View File

@@ -1,20 +0,0 @@
# Generated by Django 4.1 on 2022-08-27 17:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("pt_site", "0001_initial"),
]
operations = [
migrations.AlterField(
model_name="site",
name="url",
field=models.URLField(
default="", help_text='请保留网址结尾的"/"', unique=True, verbose_name="站点网址"
),
),
]

View File

@@ -1,18 +0,0 @@
# Generated by Django 4.1 on 2022-09-02 11:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pt_site', '0002_alter_site_url'),
]
operations = [
migrations.AddField(
model_name='signin',
name='sign_in_info',
field=models.CharField(default='', max_length=256, verbose_name='信息'),
),
]

View File

@@ -1,63 +0,0 @@
# Generated by Django 4.1 on 2022-09-02 17:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pt_site', '0003_signin_sign_in_info'),
]
operations = [
migrations.AlterField(
model_name='site',
name='category_rule',
field=models.CharField(default='.//td[@class="rowfollow nowrap"][1]/a[1]/img/@title', max_length=128, verbose_name='分类'),
),
migrations.AlterField(
model_name='site',
name='completers_rule',
field=models.CharField(default='.//a[contains(@href,"viewsnatches.php?id=")]//text()', max_length=128, verbose_name='完成人数'),
),
migrations.AlterField(
model_name='site',
name='download_url_rule',
field=models.CharField(default='.//a[contains(@href,"download.php?id=") and contains(@href,"passkey")]/@href', max_length=128, verbose_name='详情页种子链接'),
),
migrations.AlterField(
model_name='site',
name='leechers_rule',
field=models.CharField(default='.//a[contains(@href,"#leechers")]/text()', max_length=128, verbose_name='下载人数'),
),
migrations.AlterField(
model_name='site',
name='magnet_url_rule',
field=models.CharField(default='.//td/a[contains(@href,"download.php?id=")]/@href', max_length=128, verbose_name='主页下载链接'),
),
migrations.AlterField(
model_name='site',
name='peer_speed_rule',
field=models.CharField(default='.//tr/td[7]/nobr/text()', max_length=128, verbose_name='平均下载速度'),
),
migrations.AlterField(
model_name='site',
name='record_count_rule',
field=models.CharField(default='.//td[3]/text()', help_text='提取做种列表中文件大小计算总量', max_length=128, verbose_name='做种大小列表'),
),
migrations.AlterField(
model_name='site',
name='sale_rule',
field=models.CharField(default='.//div/img[contains(@class,"pro_")]/@alt', max_length=128, verbose_name='促销信息'),
),
migrations.AlterField(
model_name='site',
name='seeders_rule',
field=models.CharField(default='.//a[contains(@href,"#seeders")]/text()', max_length=128, verbose_name='做种人数'),
),
migrations.AlterField(
model_name='site',
name='title_rule',
field=models.CharField(default='.//td[@class="embedded"]/a/following::text()[1]', max_length=128, verbose_name='种子标题'),
),
]

View File

@@ -1,18 +0,0 @@
# Generated by Django 4.1 on 2022-09-02 17:09
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pt_site', '0004_alter_site_category_rule_alter_site_completers_rule_and_more'),
]
operations = [
migrations.AlterField(
model_name='torrentinfo',
name='hr',
field=models.BooleanField(default=True, help_text='绿色为通过或无需HR考核', verbose_name='H&R考核'),
),
]

View File

@@ -1,33 +0,0 @@
# Generated by Django 4.1 on 2022-09-05 20:35
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("pt_site", "0005_alter_torrentinfo_hr"),
]
operations = [
migrations.AddField(
model_name="site",
name="notice_rule",
field=models.CharField(
default='//a[@href="index.php"]/font/text()[1]',
help_text="获取新公告",
max_length=128,
verbose_name="公告规则",
),
),
migrations.AlterField(
model_name="site",
name="mailbox_rule",
field=models.CharField(
default='//a[@href="messages.php"]/font/text()',
help_text="获取新邮件",
max_length=128,
verbose_name="邮件规则",
),
),
]

View File

@@ -1,23 +0,0 @@
# Generated by Django 4.1 on 2022-09-06 16:36
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pt_site', '0006_site_notice_rule_alter_site_mailbox_rule'),
]
operations = [
migrations.AlterField(
model_name='sitestatus',
name='downloaded',
field=models.IntegerField(default=0, verbose_name='下载量'),
),
migrations.AlterField(
model_name='sitestatus',
name='uploaded',
field=models.IntegerField(default=0, verbose_name='上传量'),
),
]

View File

@@ -19,6 +19,7 @@ class Site(BaseEntity):
# 主要页面
page_default = models.CharField(verbose_name='默认搜索页面', default='torrents.php', max_length=64)
page_sign_in = models.CharField(verbose_name='默认签到链接', default='attendance.php', max_length=64)
page_control_panel = models.CharField(verbose_name='控制面板', default='usercp.php', max_length=64)
page_detail = models.CharField(verbose_name='详情页面链接', default='details.php?id={}', max_length=64)
page_download = models.CharField(verbose_name='默认下载链接', default='download.php?id={}', max_length=64)
page_user = models.CharField(verbose_name='用户信息链接', default='userdetails.php?id={}', max_length=64)
@@ -183,7 +184,17 @@ class Site(BaseEntity):
max_length=128)
my_level_rule = models.CharField(
verbose_name='用户等级',
default='//span[@class="medium"]/span[@class="nowrap"]/a[contains(@class,"_Name")]/@class',
default='//a[contains(@class,"_Name") and contains(@href,"userdetails.php?id=1")]/@class',
max_length=128
)
my_passkey_rule = models.CharField(
verbose_name='Passkey',
default='//td[contains(text(),"密钥")]/following-sibling::td[1]/text()',
max_length=128
)
my_uid_rule = models.CharField(
verbose_name='用户ID',
default='//a[contains(@class,"_Name") and contains(@href,"userdetails.php?id=")]/@href',
max_length=128
)
my_hr_rule = models.CharField(

View File

@@ -204,12 +204,16 @@ SIMPLEUI_CONFIG = {
}]
}, {
'app': 'update',
'name': '更新',
'icon': 'fas fa-code',
'name': '更新&导入',
'icon': 'el-icon-attract',
'models': [{
'name': '更新',
'icon': 'far fa-surprise',
'name': '代码更新',
'icon': 'el-icon-refresh',
'url': '/tasks/update'
}, {
'name': '站点导入',
'icon': 'el-icon-s-open',
'url': '/tasks/import_from_ptpp'
}, ]
}]
}

View File

@@ -0,0 +1,157 @@
{% load static %}
<!DOCTYPE html>
<html>
<head>
{% include 'admin/includes/css-part.html' %}
<style>
#content {
background-color: dimgrey;
color: whitesmoke;
border: 2px solid dimgrey;
border-radius: 3px;
line-height: 20px;
font-size: 16px;
width: 100%;
height: 500px;
padding: 5px;
overflow-y: scroll;
word-break: break-all;
}
</style>
</head>
<body>
<div id="ptpp">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>
<input type="file" class="upload-demo" @change="showFile($event)"/>
</span>
<el-button style="float: right;" type="success" @click="do_import">导入</el-button>
{# <el-upload#}
{# :show-file-list="false"#}
{# :on-change="showFile($event)"#}
{# :auto-upload="false">#}
{# <el-button type="success">点击上传</el-button>#}
{# </el-upload>#}
</div>
<div class="text item">
<textarea id="content" readonly v-model="ptpp"></textarea>
</div>
</el-card>
</div>
{% include 'admin/includes/js-part.html' %}
<script src="{% static 'admin/simpleui-x/js/axios.min.js' %}"></script>
<script type="text/javascript">
const vm = new Vue({
el: '#ptpp',
data() {
return {
ptpp: ''
}
},
watch: {},
methods: {
do_format() {
let ptpp = JSON.parse(this.ptpp)
this.ptpp = JSON.stringify(ptpp, null, " ")
},
showFile(input) {
//支持chrome IE10
try {
if (window.FileReader) {
var file = input.target.files[0];
var reader = new FileReader();
reader.onload = ((event) => {
//显示文件
let ptpp = JSON.parse(event.target.result);
this.ptpp = JSON.stringify(ptpp, null, " ")
{#console.log(event.target.result)#}
})
console.info(file)
console.info(reader);
reader.readAsText(file);
} else {
this.$message({
type: 'warning',
message: '可能不支持您的浏览器请使用Chrome或Edge'
});
}
} catch (e) {
this.$message({
type: 'warning',
message: 'PTPP数据必须为标准JSON格式请检查数据是否有误'
});
}
},
do_import() {
var self = this;
{#console.log(this.ptpp)#}
if (this.ptpp === '') {
this.$message({
type: 'warning',
message: '数据获取失败,请检查数据文件是否有误???'
});
}
this.$confirm('确认导入数据 ', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
axios.post(
"{% url "import_from_ptpp" %}",
{
'ptpp': this.ptpp
}).then(res => {
if (res.data.code === 0) {
let messages = res.data.data.messages
Array.from(messages).forEach(item => {
var duration = 0
switch (item.tag) {
case 'success':
duration = 1500;
break;
case 'warning':
duration = 0;
break;
case 'error':
duration = 0;
break;
}
setTimeout(function () {
console.log(duration)
self.$notify({
title: '提示',
message: item.msg,
type: item.tag,
dangerouslyUseHTMLString: true,
duration: duration
});
}, 200);
})
} else {
console.log(res)
this.$message({
type: 'warning',
message: res.data.msg
});
}
})
}).catch(() => {
this.$message({
type: 'info',
message: '已取消'
});
});
}
}
})
</script>
</body>
</html>