import datetime
from django.core.validators import MinValueValidator, MaxValueValidator
from django.db import models
from ptools.base import BaseEntity, DownloaderCategory
# Create your models here.
# 支持的站点
class Site(BaseEntity):
# 站点设置
identity = models.IntegerField(verbose_name='认证ID', unique=True, help_text='唯一值,自行适配站点的请填写的尽量大')
url = models.URLField(verbose_name='站点网址', default='', help_text='请保留网址结尾的"/"', unique=True)
name = models.CharField(max_length=32, verbose_name='站点名称')
nickname = models.CharField(max_length=16, verbose_name='简称', default='', help_text='英文,用于刷流')
logo = models.URLField(verbose_name='站点logo', default='', help_text='站点logo图标')
tracker = models.CharField(verbose_name='tracker', default='', help_text='tracker网址关键字', max_length=32)
# 功能支持
sign_in_support = models.BooleanField(verbose_name="签到支持", default=True)
get_torrent_support = models.BooleanField(verbose_name="拉取种子", default=True)
get_userinfo_support = models.BooleanField(verbose_name="站点数据", default=True)
search_support = models.BooleanField(verbose_name="搜索支持", default=False)
search_params = models.CharField(verbose_name='搜索参数',
default='{"免费":"spstate=2","2X":"spstate=3",'
'"2X免费":"spstate=4","50%":"spstate=5","2X 50%":"spstate=6",}',
help_text='字典格式:{"accept":"application/json","c":"d"}',
max_length=128)
# 主要页面
page_index = models.CharField(verbose_name='首页', default='index.php', max_length=64)
page_default = models.CharField(verbose_name='默认搜索页面', default='torrents.php?incldead=1', 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)
page_search = models.CharField(verbose_name='搜索链接', default='torrents.php?incldead=1&search={}', max_length=64)
page_message = models.CharField(verbose_name='消息页面', default='messages.php', max_length=64)
page_hr = models.CharField(verbose_name='HR考核页面', default='myhr.php?hrtype=1&userid={}', max_length=64)
page_leeching = models.CharField(verbose_name='当前下载信息',
default='getusertorrentlistajax.php?userid={}&type=leeching',
max_length=64)
page_uploaded = models.CharField(verbose_name='发布种子信息',
default='getusertorrentlistajax.php?userid={}&type=uploaded',
max_length=64)
page_seeding = models.CharField(verbose_name='当前做种信息',
default='getusertorrentlistajax.php?userid={}&type=seeding',
max_length=64)
page_completed = models.CharField(verbose_name='完成种子信息',
default='getusertorrentlistajax.php?userid={}&type=completed',
max_length=64)
page_mybonus = models.CharField(verbose_name='魔力值页面',
default='mybonus.php',
max_length=64)
page_viewfilelist = models.CharField(verbose_name='文件列表链接',
default='viewfilelist.php?id={}',
max_length=64)
page_viewpeerlist = models.CharField(verbose_name='当前用户列表',
default='viewpeerlist.php?id={}',
max_length=64)
sign_in_method = models.CharField(verbose_name='签到请求方法',
default='get',
help_text='get或post,请使用小写字母,默认get',
max_length=5)
sign_in_captcha = models.BooleanField(verbose_name='签到验证码',
default=False,
help_text='有签到验证码的站点请开启', )
sign_in_params = models.CharField(verbose_name='签到请求参数',
default='{}',
help_text='默认无参数',
max_length=128,
blank=True,
null=True)
sign_in_headers = models.CharField(verbose_name='签到请求头',
default='{}',
help_text='字典格式:{"accept":"application/json","c":"d"},默认无参数',
max_length=128)
# HR及其他
hr = models.BooleanField(verbose_name='H&R', default=False, help_text='站点是否开启HR')
hr_rate = models.IntegerField(verbose_name='HR分享率', default=2, help_text='站点要求HR种子的分享率,最小:1')
hr_time = models.IntegerField(verbose_name='HR时间', default=10, help_text='站点要求HR种子最短做种时间,单位:小时')
sp_full = models.FloatField(verbose_name='满魔', default=100, help_text='时魔满魔')
limit_speed = models.IntegerField(verbose_name='上传速度限制',
default=100,
help_text='站点盒子限速,家宽用户无需理会,单位:MB/S')
# xpath规则
torrents_rule = models.CharField(verbose_name='种子行信息',
default='//table[@class="torrents"]/tr',
max_length=128)
name_rule = models.CharField(verbose_name='种子名称',
default='.//td[@class="embedded"]/a/b/text()',
max_length=128)
title_rule = models.CharField(verbose_name='种子标题',
default='.//a[contains(@href,"detail")]/parent::td/text()[last()]',
max_length=128)
detail_url_rule = models.CharField(
verbose_name='种子详情',
default='.//td[@class="embedded"]/a[contains(@href,"detail")]/@href',
max_length=128)
category_rule = models.CharField(
verbose_name='分类',
default='.//td[@class="rowfollow nowrap"][1]/a[1]/img/@title',
max_length=128)
poster_rule = models.CharField(
verbose_name='海报',
default='.//table/tr/td[1]/img/@src',
max_length=128)
magnet_url_rule = models.CharField(
verbose_name='主页下载链接',
default='.//td/a[contains(@href,"download.php?id=")]/@href',
max_length=128)
size_rule = models.CharField(verbose_name='文件大小',
default='.//td[5]/text()',
max_length=128)
hr_rule = models.CharField(
verbose_name='H&R',
default='.//table/tr/td/img[@class="hitandrun"]/@title',
max_length=128)
sale_rule = models.CharField(
verbose_name='促销信息',
default='.//img[contains(@class,"free")]/@alt',
max_length=128
)
sale_expire_rule = models.CharField(
verbose_name='促销时间',
default='.//img[contains(@class,"free")]/following-sibling::font/span/@title',
max_length=128)
release_rule = models.CharField(
verbose_name='发布时间',
default='.//td[4]/span/@title',
max_length=128)
seeders_rule = models.CharField(
verbose_name='做种人数',
default='.//a[contains(@href,"#seeders")]/text()',
max_length=128)
leechers_rule = models.CharField(
verbose_name='下载人数',
default='.//a[contains(@href,"#leechers")]/text()',
max_length=128)
completers_rule = models.CharField(
verbose_name='完成人数',
default='.//a[contains(@href,"viewsnatches")]//text()',
max_length=128)
detail_title_rule = models.CharField(
verbose_name='详情页种子标题',
default='//h1/text()[1]',
max_length=128)
detail_subtitle_rule = models.CharField(
verbose_name='详情页种子副标题',
default='//td[contains(text(),"副标题")]/following-sibling::td/text()[1]',
max_length=128)
detail_download_url_rule = models.CharField(
verbose_name='详情页种子链接',
default='//a[@class="index" and contains(@href,"download.php")]/@href',
max_length=128)
detail_size_rule = models.CharField(
verbose_name='详情页种子大小',
default='//td//b[contains(text(),"大小")]/following::text()[1]',
max_length=128)
detail_category_rule = models.CharField(
verbose_name='详情页种子类型',
default='//td/b[contains(text(),"类型")]/following-sibling::text()[1]',
max_length=128)
detail_area_rule = models.CharField(
verbose_name='详情页种子地区',
default='//h1/following::td/b[contains(text(),"地区")]/text()',
max_length=128)
detail_count_files_rule = models.CharField(
verbose_name='详情页文件数',
default='//td/b[contains(text(),"文件数")]/following-sibling::text()[1]',
max_length=128)
# HASH RULE
detail_hash_rule = models.CharField(
verbose_name='详情页种子HASH',
default='//td/b[contains(text(),"Hash")]/following-sibling::text()[1]',
max_length=128)
detail_free_rule = models.CharField(
verbose_name='详情页促销标记',
default='//td//b[contains(text(),"大小")]/following::text()[1]',
max_length=128)
detail_free_expire_rule = models.CharField(
verbose_name='详情页促销时间',
default='//h1/b/font[contains(@class,"free")]/parent::b/following-sibling::b/span/@title',
max_length=128)
detail_douban_rule = models.CharField(
verbose_name='详情页豆瓣信息',
help_text='提取做种列表中文件大小计算总量',
default='//td/a[starts-with(@href,"https://movie.douban.com/subject/")][1]',
max_length=128)
detail_year_publish_rule = models.CharField(
verbose_name='详情页豆瓣信息',
help_text='提取做种列表中文件大小计算总量',
default='year_current_publish: //td/b[contains(text(),"发行版年份")]/text()',
max_length=128)
remark = models.TextField(verbose_name='备注', default='', null=True, blank=True)
# 状态信息XPath
invitation_rule = models.CharField(
verbose_name='邀请资格',
default='//span/a[contains(@href,"invite.php?id=")]/following-sibling::text()[1]',
max_length=128)
time_join_rule = models.CharField(
verbose_name='注册时间',
default='//td[contains(text(),"加入")]/following-sibling::td/span/@title',
max_length=128)
latest_active_rule = models.CharField(
verbose_name='最后活动时间',
default='//td[contains(text(),"最近动向")]/following-sibling::td/span/@title',
max_length=128)
uploaded_rule = models.CharField(
verbose_name='上传量',
default='//font[@class="color_uploaded"]/following-sibling::text()[1]',
max_length=128)
downloaded_rule = models.CharField(
verbose_name='下载量',
default='//font[@class="color_downloaded"]/following-sibling::text()[1]',
max_length=128)
ratio_rule = models.CharField(
verbose_name='分享率',
default='//font[@class="color_ratio"][1]/following-sibling::text()[1]',
max_length=128)
my_sp_rule = models.CharField(
verbose_name='魔力值',
default='//a[@href="mybonus.php"]/following-sibling::text()[1]',
max_length=128)
hour_sp_rule = models.CharField(
verbose_name='时魔',
default='//div[contains(text(),"每小时能获取")]/text()[1]',
max_length=128)
my_bonus_rule = models.CharField(
verbose_name='保种积分',
default='//font[@class="color_bonus" and contains(text(),"积分")]/following-sibling::text()[1]',
max_length=128)
my_level_rule = models.CharField(
verbose_name='用户等级',
default='//table[@id="info_block"]//span/a[contains(@class,"_Name") and contains(@href,"userdetails.php?id=")]/@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='//table[@id="info_block"]//span/a[contains(@class,"_Name") and contains(@href,"userdetails.php?id=")]/@href',
max_length=128
)
my_hr_rule = models.CharField(
verbose_name='H&R',
default='//a[@href="myhr.php"]//text()',
max_length=128)
leech_rule = models.CharField(
verbose_name='下载数量',
default='//img[@class="arrowdown"]/following-sibling::text()[1]',
max_length=128)
publish_rule = models.CharField(verbose_name='发种数量',
default='//p/preceding-sibling::b/text()[1]',
max_length=128)
seed_rule = models.CharField(verbose_name='做种数量',
default='//img[@class="arrowup"]/following-sibling::text()[1]',
max_length=128)
seed_vol_rule = models.CharField(verbose_name='做种大小',
default='//tr/td[3]',
help_text='需对数据做处理',
max_length=128)
mailbox_rule = models.CharField(verbose_name='邮件规则',
default='//a[@href="messages.php"]/font[contains(text(),"条")]/text()[1]',
help_text='获取新邮件',
max_length=128)
message_title = models.CharField(verbose_name='邮件标题',
default='//img[@alt="Unread"]/parent::td/following-sibling::td/a[1]//text()',
help_text='获取邮件标题',
max_length=128)
notice_rule = models.CharField(verbose_name='公告规则',
default='//a[@href="index.php"]/font[contains(text(),"条")]/text()[1]',
help_text='获取新公告',
max_length=128)
notice_title = models.CharField(verbose_name='公告标题',
default='//td[@class="text"]/div/a//text()',
help_text='获取公告标题',
max_length=128)
notice_content = models.CharField(verbose_name='公告内容',
default='//td[@class="text"]/div/a/following-sibling::div',
help_text='获取公告内容',
max_length=128)
full_site_free = models.CharField(verbose_name='站免规则',
default='//td/b/a/font[contains(text(),"全站") and contains(text(),"Free")]/text()',
help_text='站免信息',
max_length=128)
class Meta:
verbose_name = '站点信息'
verbose_name_plural = verbose_name
ordering = ['name', ]
def __str__(self):
return self.name
class UserLevelRule(BaseEntity):
site = models.ForeignKey(verbose_name='站 点', to=Site, to_field='identity', on_delete=models.CASCADE)
level_id = models.IntegerField(verbose_name='等级id', default=1)
level = models.CharField(verbose_name='等 级', default='User', max_length=24, help_text='请去除空格')
days = models.IntegerField(verbose_name='时 间', default=0, help_text='原样输入,单位:周')
uploaded = models.CharField(verbose_name='上 传', default=0, help_text='原样输入,例:50GB,1.5TB', max_length=12)
downloaded = models.CharField(verbose_name='下 载', default=0, help_text='原样输入,例:50GB,1.5TB', max_length=12)
bonus = models.FloatField(verbose_name='魔 力', default=0)
score = models.IntegerField(verbose_name='积 分', default=0)
ratio = models.FloatField(verbose_name='分享率', default=0)
torrents = models.IntegerField(verbose_name='发 种', help_text='发布种子数', default=0)
leeches = models.IntegerField(verbose_name='吸血数', help_text='完成种子数', default=0)
seeding_delta = models.FloatField(verbose_name='做种时间', help_text='累计做种时间', default=0)
rights = models.TextField(verbose_name='权 利', max_length=256,
help_text='当前等级所享有的权利与义务')
def __str__(self):
return f'{self.site.nickname}/{self.level}'
class Meta:
unique_together = ('site', 'level_id', 'level',)
verbose_name = '升级进度'
verbose_name_plural = verbose_name
class MySite(BaseEntity):
site = models.OneToOneField(verbose_name='站点', to=Site, on_delete=models.CASCADE)
sort_id = models.IntegerField(verbose_name='排序', default=1)
# 用户信息
user_id = models.CharField(verbose_name='用户ID', max_length=16,
help_text='请填写数字UID,'
'* az,cz,ez,莫妮卡、普斯特请填写用户名')
passkey = models.CharField(max_length=128, verbose_name='PassKey', blank=True, null=True)
cookie = models.TextField(verbose_name='COOKIE', help_text='与UA搭配使用效果更佳,请和UA在同一浏览器提取')
expires = models.DateTimeField(verbose_name='COOKIE有效期', help_text='COOKIE有效期',
default=datetime.datetime.strptime('2023-01-01 12:30:00', '%Y-%m-%d %H:%M:%S'))
user_agent = models.TextField(verbose_name='User-Agent', help_text='请填写你获取cookie的浏览器的User-Agent',
default='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 Edg/106.0.1370.42')
# 用户设置
hr = models.BooleanField(verbose_name='开启HR下载', default=False, help_text='是否下载HR种子')
sign_in = models.BooleanField(verbose_name='开启签到', default=True, help_text='是否开启签到')
get_info = models.BooleanField(verbose_name='抓取信息', default=True, help_text='是否抓取站点数据')
search = models.BooleanField(verbose_name='开启搜索', default=True, help_text='是否开启搜索')
# 用户数据 自动拉取
# invitation = models.IntegerField(verbose_name='邀请资格', default=0)
time_join = models.DateTimeField(verbose_name='注册时间', blank=True, null=True, help_text='请务必填写此项!')
latest_active = models.DateTimeField(verbose_name='最近活动时间', blank=True, null=True)
# sp_hour = models.FloatField(verbose_name='时魔', default=0)
my_level = models.CharField(verbose_name='用户等级', max_length=16, default='')
my_hr = models.CharField(verbose_name='H&R', max_length=16, default='')
# leech = models.IntegerField(verbose_name='当前下载', default=0)
# seed = models.IntegerField(verbose_name='当前做种', default=0)
mail = models.IntegerField(verbose_name='新邮件', default=0)
# publish = models.IntegerField(verbose_name='发布种子', default=0)
def __str__(self):
return self.site.name
class Meta:
verbose_name = '我的站点'
verbose_name_plural = verbose_name
# 站点信息
class SiteStatus(BaseEntity):
# 获取日期,只保留当天最新数据
site = models.ForeignKey(verbose_name='站点名称', to=MySite, on_delete=models.CASCADE)
# 签到,有签到功能的访问签到页面,无签到的访问个人主页
uploaded = models.IntegerField(verbose_name='上传量', default=0)
downloaded = models.IntegerField(verbose_name='下载量', default=0)
ratio = models.FloatField(verbose_name='分享率', default=0)
my_sp = models.FloatField(verbose_name='魔力值', default=0)
my_bonus = models.FloatField(verbose_name='做种积分', default=0)
seed_vol = models.IntegerField(verbose_name='做种体积', default=0)
leech = models.IntegerField(verbose_name='当前下载', default=0)
seed = models.IntegerField(verbose_name='当前做种', default=0)
sp_hour = models.FloatField(verbose_name='时魔', default=0)
publish = models.IntegerField(verbose_name='发布种子', default=0)
invitation = models.IntegerField(verbose_name='邀请资格', default=0)
class Meta:
verbose_name = '我的数据'
verbose_name_plural = verbose_name
def __str__(self):
return self.site.site.name
class SignIn(BaseEntity):
site = models.ForeignKey(verbose_name='站点名称', to=MySite, on_delete=models.CASCADE)
sign_in_today = models.BooleanField(verbose_name='签到', default=False)
sign_in_info = models.CharField(verbose_name='信息', default='', max_length=256)
class Meta:
verbose_name = '签到'
verbose_name_plural = verbose_name
def __str__(self):
return self.site.site.name
class Downloader(BaseEntity):
# 下载器名称
name = models.CharField(max_length=12, verbose_name='名称')
# 下载器类别 tr qb de
category = models.CharField(max_length=128, choices=DownloaderCategory.choices,
default=DownloaderCategory.qBittorrent,
verbose_name='下载器')
# 用户名
username = models.CharField(max_length=16, verbose_name='用户名')
# 密码
password = models.CharField(max_length=128, verbose_name='密码')
# host
host = models.CharField(max_length=32, verbose_name='HOST')
# port
port = models.IntegerField(default=8999, verbose_name='端口', validators=[
MaxValueValidator(65535),
MinValueValidator(1001)
])
# 预留空间
reserved_space = models.IntegerField(default=30, verbose_name='预留磁盘空间', validators=[
MinValueValidator(1),
MaxValueValidator(512)
], help_text='单位GB,最小为1G,最大512G')
class Meta:
verbose_name = '下载器'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
# 种子信息
class TorrentInfo(BaseEntity):
site = models.ForeignKey(to=Site, on_delete=models.CASCADE, verbose_name='所属站点', null=True)
name = models.CharField(max_length=256, verbose_name='种子名称', default='')
title = models.CharField(max_length=256, verbose_name='标题', default='')
category = models.CharField(max_length=128, verbose_name='分类', default='')
poster_url = models.URLField(max_length=512, verbose_name='海报链接', default='')
detail_url = models.URLField(max_length=512, verbose_name='种子详情', default='')
magnet_url = models.URLField(verbose_name='下载链接')
download_url = models.URLField(verbose_name='种子链接', unique=True, max_length=255)
size = models.IntegerField(verbose_name='文件大小', default=0)
state = models.BooleanField(max_length=16, verbose_name='推送状态', default=False)
save_path = models.FilePathField(verbose_name='保存路径', default='/downloads/brush')
hr = models.BooleanField(verbose_name='H&R考核', default=True, help_text='绿色为通过或无需HR考核')
sale_status = models.CharField(verbose_name='优惠状态', default='无促销', max_length=16)
sale_expire = models.CharField(verbose_name='到期时间', default='无限期', max_length=32)
on_release = models.CharField(verbose_name='发布时间', default='', max_length=32)
seeders = models.CharField(verbose_name='做种人数', default='0', max_length=8)
leechers = models.CharField(verbose_name='下载人数', default='0', max_length=8)
completers = models.CharField(verbose_name='完成人数', default='0', max_length=8)
downloader = models.ForeignKey(to=Downloader,
on_delete=models.CASCADE,
verbose_name='下载器',
blank=True, null=True)
hash_string = models.CharField(max_length=128, verbose_name='Info_hash', default='')
viewfilelist = models.CharField(max_length=128, verbose_name='文件列表', default='')
viewpeerlist = models.FloatField(max_length=128, verbose_name='下载总进度', default=0)
peer_list_speed = models.FloatField(max_length=128, verbose_name='平均上传速度', default=0)
class Meta:
verbose_name = '种子管理'
verbose_name_plural = verbose_name
def __str__(self):
return self.name